gstreamer/subprojects/gst-plugins-bad/ext/dtls
2024-02-27 09:35:47 +01:00
..
gstdtlsagent.c
gstdtlsagent.h
gstdtlscertificate.c gstdtlscertificate: Define _WINSOCKAPI_ before including windows.h 2023-12-08 08:13:40 +00:00
gstdtlscertificate.h
gstdtlsconnection.c gst-plugins-bad: re-indent with GNU indent 2.2.12 2023-03-17 03:18:54 +00:00
gstdtlsconnection.h
gstdtlsdec.c
gstdtlsdec.h
gstdtlselement.c
gstdtlselements.h
gstdtlsenc.c gstdtlsenc: fix stream_id and missing group_id 2023-10-30 18:12:37 +00:00
gstdtlsenc.h
gstdtlssrtpbin.c
gstdtlssrtpbin.h
gstdtlssrtpdec.c dtls: Properly name encoder/decoder logging categories 2022-08-01 09:02:03 +00:00
gstdtlssrtpdec.h
gstdtlssrtpdemux.c
gstdtlssrtpdemux.h
gstdtlssrtpenc.c dtls: Properly name encoder/decoder logging categories 2022-08-01 09:02:03 +00:00
gstdtlssrtpenc.h
meson.build meson: Add a top-level option to enable webrtc 2023-10-19 06:38:45 +00:00
plugin.c
README docs: Use Discourse and Matrix as prefered communication channels 2024-02-27 09:35:47 +01:00

INTRODUCTION
============
This document is an attempt to describe the basics of the DTLS element.
It hasn't been written by the author(s) and so, besides being incomplete,
*IT MIGHT ALSO BE INCORRECT*. So take it with a pinch of salt.

As always, if in doubt ask on the GStreamer Matrix room:
https://matrix.to/#/#gstreamer:gstreamer.org.

THE INTERNALS
=============
This plugin provides two main elements (dtlssrtpdec and dtlssrtpenc) and a few
minor elements necessary to implement them. The two elements dtlssrtpdec and
dtlssrtpenc are the only ones you are supposed to include in, respectively, the
RX and TX pipelines of your DTLS-enabled application. This means you're not
supposed to directly include those minor elements in your pipelines.

dtlssrtpenc
-----------
This element is to be included in the TX pipeline and will initiate the DTLS
handshake if configured to be the client. Its main configuration parameters are:

  - connection-id: a string that must match the connection-id in dtlssrtpdec;
  - is-client: a boolean that indicates whether this is going to be the client
							 or the server during the DTLS handshake.

Internally this element comprises the standard srtpenc element, the dtlsenc
element and a funnel to connect both these elements to one single output.
The srtpenc can be used to encrypt SRTP/SRTCP packets while the dtlsenc can be
used to encrypt generic data, e.g. for non-SRTP applications.

NB With the current implementation the TX pipeline containing the dtlssrtpenc
	 must be created *AFTER* the RX pipeline.

dtlssrtpdec
-----------
It is to be included in the RX pipeline. Its main configuration parameters are:

  - connection-id: a string that must match the connection-id in dtlssrtpenc;
	- pem: a string that can be used to provide your own certificate *AND* private
				 key in PEM format. The private key is required to carry out the
				 handshake so do not forget it or the DTLS negotiation will fail;
  - peer_pem: a read only parameter that can be used to retrieve the
							certificate sent from the other party in PEM format once the
							handshake is completed.

Internally this element comprises a dtlssrtpdemux, a standard srtpdec element
and the dtlsdec element. The dtlssrtpdemux element switches SRT(C)P packets to
the srtpdec element and DTLS packets to the dtlsdec element and discards any
other unknown packet. So, similarly for the dtlssrtpenc case, DTLS-SRTP
applications would exercise the srtpdec element and any other non-SRTP
application would exercise the dtlsdec element.

NB With the current implementation the RX pipeline containing the dtlssrtpdec
	 must be created *BEFORE* the TX pipeline.

EXAMPLE PIPELINE
================
The following is an example usage of the DTLS plugin. It is a python script that
creates two endpoints that exchange encrypted audio using DTLS to exchange the
encryption keys.

NB In theory we would show an example gst-launch command. However that would not
	 be enough because you need two pairs of TX/RX pipelines for a proper
	 handshake and you can't use gst-launch two start 4 different pipelines.
	 This why there is a python script in here.

```
#!/usr/bin/env python3

# create two endpoints, each with tx and rx pipelines using the DTLS
# elements and let audio flowing for a while to give time for a packet capture

import time
from gi.repository import Gst, GObject, GLib
GObject.threads_init()
Gst.init(None)


def _start_pipeline(pipeline):
    pipeline.set_state(Gst.State.PLAYING)
    pipeline.get_state(Gst.CLOCK_TIME_NONE)


def _sleep_while_iterating_gloop(secs):
    """ block for secs seconds but iterate the gloop while you do """
    for _ in range(10 * secs):
        gloop = GLib.MainLoop()
        gloop_context = gloop.get_context()
        gloop_context.iteration(may_block=False)
        time.sleep(0.1)

def dtls_tx_pipeline_description(name, is_client, port):
    return ' ! '.join([
        'audiotestsrc is-live=true',
        'audio/x-raw, rate=8000, format=S16LE, channels=1',
        'opusenc frame-size=10',
        'rtpopuspay pt=103',
        '.rtp_sink_0 dtlssrtpenc connection-id={name} is-client={client} .src',
        'udpsink port={port}'
    ]).format(name=name, client=is_client, port=port)


def dtls_rx_pipeline_description(name, port):
    return ' ! '.join([
        'udpsrc port={port}',
        '.sink dtlssrtpdec connection-id={name} .rtp_src',
        'queue',
        'fakesink async=false'
    ]).format(name=name, port=port)


class Endpoint:
    def __init__(self, name, is_client, tx_port, rx_port):
        self.name = name
        tx_pipeline_description = dtls_tx_pipeline_description(
            name, is_client, tx_port
        )
        rx_pipeline_description = dtls_rx_pipeline_description(name, rx_port)
        print(rx_pipeline_description)
        print(tx_pipeline_description)

        self.rx_pipeline = Gst.parse_launch(rx_pipeline_description)
        self.tx_pipeline = Gst.parse_launch(tx_pipeline_description)

    def start(self):
				# Start RX first, otherwise it fails due to the current implementation
        self.start_rx_pipeline()
        self.start_tx_pipeline()

    def start_rx_pipeline(self):
        _start_pipeline(self.rx_pipeline)

    def start_tx_pipeline(self):
        _start_pipeline(self.tx_pipeline)

    def stop(self):
        def stop_pipeline(p):
            p.set_state(Gst.State.NULL)
            p.get_state(Gst.CLOCK_TIME_NONE)
        stop_pipeline(self.tx_pipeline)
        stop_pipeline(self.rx_pipeline)

blue = Endpoint("blue", is_client=True, tx_port=23000, rx_port=23002)
red = Endpoint("red", is_client=False, tx_port=23002, rx_port=23000)

red.start()
blue.start()

_sleep_while_iterating_gloop(3)

red.stop()
blue.stop()
```