mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-25 03:01:03 +00:00
467 lines
15 KiB
C
467 lines
15 KiB
C
/*
|
|
* Copyright (c) 2014, Ericsson AB. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. 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.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstdtlssrtpenc.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
static GstStaticPadTemplate rtp_sink_template =
|
|
GST_STATIC_PAD_TEMPLATE ("rtp_sink_%d",
|
|
GST_PAD_SINK,
|
|
GST_PAD_REQUEST,
|
|
GST_STATIC_CAPS ("application/x-rtp;application/x-rtcp")
|
|
);
|
|
|
|
static GstStaticPadTemplate rtcp_sink_template =
|
|
GST_STATIC_PAD_TEMPLATE ("rtcp_sink_%d",
|
|
GST_PAD_SINK,
|
|
GST_PAD_REQUEST,
|
|
GST_STATIC_CAPS ("application/x-rtp;application/x-rtcp")
|
|
);
|
|
|
|
static GstStaticPadTemplate data_sink_template =
|
|
GST_STATIC_PAD_TEMPLATE ("data_sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_REQUEST,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_dtls_srtp_enc_debug);
|
|
#define GST_CAT_DEFAULT gst_dtls_srtp_enc_debug
|
|
|
|
#define gst_dtls_srtp_enc_parent_class parent_class
|
|
G_DEFINE_TYPE_WITH_CODE (GstDtlsSrtpEnc, gst_dtls_srtp_enc,
|
|
GST_TYPE_DTLS_SRTP_BIN, GST_DEBUG_CATEGORY_INIT (gst_dtls_srtp_enc_debug,
|
|
"dtlssrtpenc", 0, "DTLS Decoder"));
|
|
|
|
enum
|
|
{
|
|
SIGNAL_ON_KEY_SET,
|
|
NUM_SIGNALS
|
|
};
|
|
|
|
static guint signals[NUM_SIGNALS];
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_IS_CLIENT,
|
|
NUM_PROPERTIES
|
|
};
|
|
|
|
static GParamSpec *properties[NUM_PROPERTIES];
|
|
|
|
#define DEFAULT_IS_CLIENT FALSE
|
|
|
|
static gboolean transform_enum (GBinding *, const GValue * source_value,
|
|
GValue * target_value, GEnumClass *);
|
|
|
|
static void gst_dtls_srtp_enc_set_property (GObject *, guint prop_id,
|
|
const GValue *, GParamSpec *);
|
|
static void gst_dtls_srtp_enc_get_property (GObject *, guint prop_id,
|
|
GValue *, GParamSpec *);
|
|
|
|
static GstPad *add_ghost_pad (GstElement *, const gchar * name, GstPad *,
|
|
GstPadTemplate *);
|
|
static GstPad *gst_dtls_srtp_enc_request_new_pad (GstElement *,
|
|
GstPadTemplate *, const gchar * name, const GstCaps *);
|
|
|
|
static void on_key_received (GObject * encoder, GstDtlsSrtpEnc *);
|
|
|
|
static void gst_dtls_srtp_enc_remove_dtls_element (GstDtlsSrtpBin *);
|
|
static GstPadProbeReturn remove_dtls_encoder_probe_callback (GstPad *,
|
|
GstPadProbeInfo *, GstElement *);
|
|
|
|
static void
|
|
gst_dtls_srtp_enc_class_init (GstDtlsSrtpEncClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *element_class;
|
|
GstDtlsSrtpBinClass *dtls_srtp_bin_class;
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
element_class = (GstElementClass *) klass;
|
|
dtls_srtp_bin_class = (GstDtlsSrtpBinClass *) klass;
|
|
|
|
gobject_class->set_property =
|
|
GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_set_property);
|
|
gobject_class->get_property =
|
|
GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_get_property);
|
|
|
|
element_class->request_new_pad =
|
|
GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_request_new_pad);
|
|
|
|
dtls_srtp_bin_class->remove_dtls_element =
|
|
GST_DEBUG_FUNCPTR (gst_dtls_srtp_enc_remove_dtls_element);
|
|
|
|
signals[SIGNAL_ON_KEY_SET] =
|
|
g_signal_new ("on-key-set", G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
|
g_cclosure_marshal_generic, G_TYPE_NONE, 0);
|
|
|
|
properties[PROP_IS_CLIENT] =
|
|
g_param_spec_boolean ("is-client",
|
|
"Is client",
|
|
"Set to true if the decoder should act as "
|
|
"client and initiate the handshake",
|
|
DEFAULT_IS_CLIENT,
|
|
GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
|
|
|
gst_element_class_add_static_pad_template (element_class, &rtp_sink_template);
|
|
gst_element_class_add_static_pad_template (element_class,
|
|
&rtcp_sink_template);
|
|
gst_element_class_add_static_pad_template (element_class,
|
|
&data_sink_template);
|
|
gst_element_class_add_static_pad_template (element_class, &src_template);
|
|
|
|
gst_element_class_set_static_metadata (element_class,
|
|
"DTLS-SRTP Encoder",
|
|
"Encoder/Network/DTLS/SRTP",
|
|
"Encodes SRTP packets with a key received from DTLS",
|
|
"Patrik Oldsberg patrik.oldsberg@ericsson.com");
|
|
}
|
|
|
|
static void
|
|
gst_dtls_srtp_enc_init (GstDtlsSrtpEnc * self)
|
|
{
|
|
GstElementClass *klass = GST_ELEMENT_GET_CLASS (GST_ELEMENT (self));
|
|
static GEnumClass *cipher_enum_class, *auth_enum_class;
|
|
gboolean ret;
|
|
|
|
/*
|
|
+--------------------+ +-----------------+
|
|
rtp_sink-R-o|rtp_sink rtp_src|o-R-o| |
|
|
| srtpenc | | |
|
|
rtcp_sink-R-o|srtcp_sink rtcp_src|o-R-o| |
|
|
+--------------------+ | funnel |o---src
|
|
| |
|
|
+--------------------+ | |
|
|
data_sink-R-o| dtlsenc |o---o| |
|
|
+--------------------+ +-----------------+
|
|
*/
|
|
|
|
self->srtp_enc = gst_element_factory_make ("srtpenc", NULL);
|
|
if (!self->srtp_enc) {
|
|
GST_ERROR_OBJECT (self,
|
|
"failed to create srtp encoder, is the srtp plugin registered?");
|
|
return;
|
|
}
|
|
g_return_if_fail (self->srtp_enc);
|
|
self->bin.dtls_element = gst_element_factory_make ("dtlsenc", NULL);
|
|
if (!self->bin.dtls_element) {
|
|
GST_ERROR_OBJECT (self, "failed to create dtls encoder");
|
|
return;
|
|
}
|
|
self->funnel = gst_element_factory_make ("funnel", NULL);
|
|
if (!self->funnel) {
|
|
GST_ERROR_OBJECT (self, "failed to create funnel");
|
|
return;
|
|
}
|
|
|
|
gst_bin_add_many (GST_BIN (self), self->bin.dtls_element, self->srtp_enc,
|
|
self->funnel, NULL);
|
|
|
|
ret = gst_element_link (self->bin.dtls_element, self->funnel);
|
|
g_return_if_fail (ret);
|
|
|
|
add_ghost_pad (GST_ELEMENT (self), "src",
|
|
gst_element_get_static_pad (self->funnel, "src"),
|
|
gst_element_class_get_pad_template (klass, "src"));
|
|
|
|
g_signal_connect (self->bin.dtls_element, "on-key-received",
|
|
G_CALLBACK (on_key_received), self);
|
|
|
|
if (g_once_init_enter (&cipher_enum_class)) {
|
|
GType type = g_type_from_name ("GstSrtpCipherType");
|
|
g_assert (type);
|
|
g_once_init_leave (&cipher_enum_class, g_type_class_peek (type));
|
|
}
|
|
if (g_once_init_enter (&auth_enum_class)) {
|
|
GType type = g_type_from_name ("GstSrtpAuthType");
|
|
g_assert (type);
|
|
g_once_init_leave (&auth_enum_class, g_type_class_peek (type));
|
|
}
|
|
|
|
g_object_set (self->srtp_enc, "random-key", TRUE, NULL);
|
|
|
|
g_object_bind_property (G_OBJECT (self), "key", self->srtp_enc, "key",
|
|
G_BINDING_DEFAULT);
|
|
g_object_bind_property_full (G_OBJECT (self), "srtp-cipher", self->srtp_enc,
|
|
"rtp-cipher", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
|
|
NULL, cipher_enum_class, NULL);
|
|
g_object_bind_property_full (G_OBJECT (self), "srtcp-cipher", self->srtp_enc,
|
|
"rtcp-cipher", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
|
|
NULL, cipher_enum_class, NULL);
|
|
g_object_bind_property_full (G_OBJECT (self), "srtp-auth", self->srtp_enc,
|
|
"rtp-auth", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
|
|
NULL, auth_enum_class, NULL);
|
|
g_object_bind_property_full (G_OBJECT (self), "srtcp-auth", self->srtp_enc,
|
|
"rtcp-auth", G_BINDING_DEFAULT, (GBindingTransformFunc) transform_enum,
|
|
NULL, auth_enum_class, NULL);
|
|
}
|
|
|
|
static gboolean
|
|
transform_enum (GBinding * binding, const GValue * source_value,
|
|
GValue * target_value, GEnumClass * enum_class)
|
|
{
|
|
GEnumValue *enum_value;
|
|
const gchar *nick;
|
|
|
|
nick = g_value_get_string (source_value);
|
|
g_return_val_if_fail (nick, FALSE);
|
|
|
|
enum_value = g_enum_get_value_by_nick (enum_class, nick);
|
|
g_return_val_if_fail (enum_value, FALSE);
|
|
|
|
GST_DEBUG_OBJECT (g_binding_get_source (binding),
|
|
"transforming enum from %s to %d", nick, enum_value->value);
|
|
|
|
g_value_set_enum (target_value, enum_value->value);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_dtls_srtp_enc_set_property (GObject * object,
|
|
guint prop_id, const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_IS_CLIENT:
|
|
if (self->bin.dtls_element) {
|
|
g_object_set_property (G_OBJECT (self->bin.dtls_element), "is-client",
|
|
value);
|
|
} else {
|
|
GST_WARNING_OBJECT (self,
|
|
"tried to set is-client after disabling DTLS");
|
|
}
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_dtls_srtp_enc_get_property (GObject * object,
|
|
guint prop_id, GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_IS_CLIENT:
|
|
if (self->bin.dtls_element) {
|
|
g_object_get_property (G_OBJECT (self->bin.dtls_element), "is-client",
|
|
value);
|
|
} else {
|
|
GST_WARNING_OBJECT (self,
|
|
"tried to get is-client after disabling DTLS");
|
|
}
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static GstPad *
|
|
add_ghost_pad (GstElement * element,
|
|
const gchar * name, GstPad * target, GstPadTemplate * templ)
|
|
{
|
|
GstPad *pad;
|
|
gboolean ret;
|
|
|
|
pad = gst_ghost_pad_new_from_template (name, target, templ);
|
|
gst_object_unref (target);
|
|
target = NULL;
|
|
|
|
ret = gst_pad_set_active (pad, TRUE);
|
|
g_warn_if_fail (ret);
|
|
|
|
ret = gst_element_add_pad (element, pad);
|
|
g_warn_if_fail (ret);
|
|
|
|
return pad;
|
|
}
|
|
|
|
static GstPad *
|
|
gst_dtls_srtp_enc_request_new_pad (GstElement * element,
|
|
GstPadTemplate * templ, const gchar * name, const GstCaps * caps)
|
|
{
|
|
GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (element);
|
|
GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
|
|
GstPad *target_pad;
|
|
GstPad *ghost_pad = NULL;
|
|
guint pad_n;
|
|
gchar *srtp_src_name;
|
|
|
|
GST_DEBUG_OBJECT (element, "pad requested");
|
|
|
|
g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
|
|
g_return_val_if_fail (self->srtp_enc, NULL);
|
|
|
|
if (templ == gst_element_class_get_pad_template (klass, "rtp_sink_%d")) {
|
|
target_pad = gst_element_get_request_pad (self->srtp_enc, name);
|
|
g_return_val_if_fail (target_pad, NULL);
|
|
|
|
sscanf (GST_PAD_NAME (target_pad), "rtp_sink_%d", &pad_n);
|
|
srtp_src_name = g_strdup_printf ("rtp_src_%d", pad_n);
|
|
|
|
gst_element_link_pads (self->srtp_enc, srtp_src_name, self->funnel, NULL);
|
|
|
|
g_free (srtp_src_name);
|
|
|
|
ghost_pad = add_ghost_pad (element, name, target_pad, templ);
|
|
|
|
GST_LOG_OBJECT (self, "added rtp sink pad");
|
|
} else if (templ == gst_element_class_get_pad_template (klass,
|
|
"rtcp_sink_%d")) {
|
|
target_pad = gst_element_get_request_pad (self->srtp_enc, name);
|
|
g_return_val_if_fail (target_pad, NULL);
|
|
|
|
sscanf (GST_PAD_NAME (target_pad), "rtcp_sink_%d", &pad_n);
|
|
srtp_src_name = g_strdup_printf ("rtcp_src_%d", pad_n);
|
|
|
|
gst_element_link_pads (self->srtp_enc, srtp_src_name, self->funnel, NULL);
|
|
|
|
g_free (srtp_src_name);
|
|
|
|
ghost_pad = add_ghost_pad (element, name, target_pad, templ);
|
|
|
|
GST_LOG_OBJECT (self, "added rtcp sink pad");
|
|
} else if (templ == gst_element_class_get_pad_template (klass, "data_sink")) {
|
|
g_return_val_if_fail (self->bin.dtls_element, NULL);
|
|
target_pad = gst_element_get_request_pad (self->bin.dtls_element, "sink");
|
|
|
|
ghost_pad = add_ghost_pad (element, name, target_pad, templ);
|
|
|
|
GST_LOG_OBJECT (self, "added data sink pad");
|
|
} else {
|
|
g_warn_if_reached ();
|
|
}
|
|
|
|
if (caps && ghost_pad) {
|
|
g_object_set (ghost_pad, "caps", caps, NULL);
|
|
}
|
|
|
|
return ghost_pad;
|
|
}
|
|
|
|
static void
|
|
on_key_received (GObject * encoder, GstDtlsSrtpEnc * self)
|
|
{
|
|
GstDtlsSrtpBin *bin = GST_DTLS_SRTP_BIN (self);
|
|
GstBuffer *buffer = NULL;
|
|
guint cipher, auth;
|
|
|
|
if (!(bin->key_is_set || bin->srtp_cipher || bin->srtp_auth
|
|
|| bin->srtcp_cipher || bin->srtcp_auth)) {
|
|
g_object_get (encoder,
|
|
"encoder-key", &buffer,
|
|
"srtp-cipher", &cipher, "srtp-auth", &auth, NULL);
|
|
|
|
g_object_set (self->srtp_enc,
|
|
"rtp-cipher", cipher,
|
|
"rtcp-cipher", cipher,
|
|
"rtp-auth", auth,
|
|
"rtcp-auth", auth, "key", buffer, "random-key", FALSE, NULL);
|
|
|
|
gst_buffer_unref (buffer);
|
|
|
|
g_signal_emit (self, signals[SIGNAL_ON_KEY_SET], 0);
|
|
} else {
|
|
GST_DEBUG_OBJECT (self,
|
|
"ignoring keys received from DTLS handshake, key struct is set");
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_dtls_srtp_enc_remove_dtls_element (GstDtlsSrtpBin * bin)
|
|
{
|
|
GstDtlsSrtpEnc *self = GST_DTLS_SRTP_ENC (bin);
|
|
GstPad *dtls_sink_pad, *peer_pad;
|
|
gulong id;
|
|
guint rtp_cipher = 1, rtcp_cipher = 1, rtp_auth = 1, rtcp_auth = 1;
|
|
|
|
if (!bin->dtls_element) {
|
|
return;
|
|
}
|
|
|
|
g_object_get (self->srtp_enc,
|
|
"rtp-cipher", &rtp_cipher,
|
|
"rtcp-cipher", &rtcp_cipher,
|
|
"rtp-auth", &rtp_auth, "rtcp-auth", &rtcp_auth, NULL);
|
|
|
|
if (!rtp_cipher && !rtcp_cipher && !rtp_auth && !rtcp_auth) {
|
|
g_object_set (self->srtp_enc, "random-key", FALSE, NULL);
|
|
}
|
|
|
|
dtls_sink_pad = gst_element_get_static_pad (bin->dtls_element, "sink");
|
|
|
|
if (!dtls_sink_pad) {
|
|
gst_element_set_state (GST_ELEMENT (bin->dtls_element), GST_STATE_NULL);
|
|
gst_bin_remove (GST_BIN (self), bin->dtls_element);
|
|
bin->dtls_element = NULL;
|
|
return;
|
|
}
|
|
|
|
peer_pad = gst_pad_get_peer (dtls_sink_pad);
|
|
g_return_if_fail (peer_pad);
|
|
gst_object_unref (dtls_sink_pad);
|
|
dtls_sink_pad = NULL;
|
|
|
|
id = gst_pad_add_probe (peer_pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
|
|
(GstPadProbeCallback) remove_dtls_encoder_probe_callback,
|
|
bin->dtls_element, NULL);
|
|
g_return_if_fail (id);
|
|
bin->dtls_element = NULL;
|
|
|
|
gst_pad_push_event (peer_pad,
|
|
gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
|
|
gst_structure_new_empty ("dummy")));
|
|
|
|
gst_object_unref (peer_pad);
|
|
}
|
|
|
|
static GstPadProbeReturn
|
|
remove_dtls_encoder_probe_callback (GstPad * pad,
|
|
GstPadProbeInfo * info, GstElement * element)
|
|
{
|
|
gst_pad_remove_probe (pad, GST_PAD_PROBE_INFO_ID (info));
|
|
|
|
gst_element_set_state (GST_ELEMENT (element), GST_STATE_NULL);
|
|
gst_bin_remove (GST_BIN (GST_ELEMENT_PARENT (element)), element);
|
|
|
|
return GST_PAD_PROBE_OK;
|
|
}
|