From 4eb960d6c348216f4ba946b8ae3e3b64a54c7766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20Cr=C3=AAte?= Date: Tue, 12 Mar 2013 19:47:51 -0400 Subject: [PATCH] srtp: Add SRTP plugin based on libsrtp In large part the work of Gabriel Millaire --- configure.ac | 12 + ext/Makefile.am | 8 + ext/srtp/.gitignore | 1 + ext/srtp/Makefile.am | 33 ++ ext/srtp/gstsrtp.c | 233 +++++++++ ext/srtp/gstsrtp.h | 64 +++ ext/srtp/gstsrtpdec.c | 965 +++++++++++++++++++++++++++++++++++ ext/srtp/gstsrtpdec.h | 96 ++++ ext/srtp/gstsrtpenc.c | 1134 +++++++++++++++++++++++++++++++++++++++++ ext/srtp/gstsrtpenc.h | 117 +++++ 10 files changed, 2663 insertions(+) create mode 100644 ext/srtp/.gitignore create mode 100644 ext/srtp/Makefile.am create mode 100644 ext/srtp/gstsrtp.c create mode 100644 ext/srtp/gstsrtp.h create mode 100644 ext/srtp/gstsrtpdec.c create mode 100644 ext/srtp/gstsrtpdec.h create mode 100644 ext/srtp/gstsrtpenc.c create mode 100644 ext/srtp/gstsrtpenc.h diff --git a/configure.ac b/configure.ac index 5064586429..398edc8974 100644 --- a/configure.ac +++ b/configure.ac @@ -1393,6 +1393,16 @@ AG_GST_CHECK_FEATURE(LIBMMS, [mms protocol library], libmms, [ ]) AC_SUBST(LIBMMS_LIBS) + +dnl *** libsrtp *** +translit(dnm, m, l) AM_CONDITIONAL(USE_SRTP, true) +AG_GST_CHECK_FEATURE(SRTP, [srtp library], srtp, [ + HAVE_SRTP="yes" + AG_GST_CHECK_LIBHEADER(SRTP, srtp, srtp_init, , srtp/srtp.h, + SRTP_LIBS="-lsrtp", HAVE_SRTP="no") + AC_SUBST(SRTP_LIBS) +]) + dnl *** linsys *** translit(dnm, m, l) AM_CONDITIONAL(USE_LINSYS, true) AG_GST_CHECK_FEATURE(LINSYS, [Linear Systems SDI plugin], linsys, [ @@ -2172,6 +2182,7 @@ AM_CONDITIONAL(USE_SNDFILE, false) AM_CONDITIONAL(USE_SOUNDTOUCH, false) AM_CONDITIONAL(USE_SPANDSP, false) AM_CONDITIONAL(USE_SPC, false) +AM_CONDITIONAL(USE_SRTP, false) AM_CONDITIONAL(USE_GME, false) AM_CONDITIONAL(USE_GSETTINGS, false) AM_CONDITIONAL(USE_XVID, false) @@ -2431,6 +2442,7 @@ ext/sndfile/Makefile ext/soundtouch/Makefile ext/spandsp/Makefile ext/sndio/Makefile +ext/srtp/Makefile ext/teletextdec/Makefile ext/gme/Makefile ext/gsettings/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 88a5f54f3d..c9c5f96e99 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -318,6 +318,12 @@ else SPC_DIR= endif +if USE_SRTP +SRTP_DIR=srtp +else +SRTP_DIR= +endif + if USE_GME GME_DIR=gme else @@ -409,6 +415,7 @@ SUBDIRS=\ $(SPANDSP_DIR) \ $(GME_DIR) \ $(SPC_DIR) \ + $(SRTP_DIR) \ $(TELETEXTDEC_DIR) \ $(TIMIDITY_DIR) \ $(XVID_DIR) \ @@ -462,6 +469,7 @@ DIST_SUBDIRS = \ soundtouch \ spandsp \ spc \ + srtp \ gme \ teletextdec \ timidity \ diff --git a/ext/srtp/.gitignore b/ext/srtp/.gitignore new file mode 100644 index 0000000000..f844ec49e4 --- /dev/null +++ b/ext/srtp/.gitignore @@ -0,0 +1 @@ +gstsrtp-enumtypes.[ch] diff --git a/ext/srtp/Makefile.am b/ext/srtp/Makefile.am new file mode 100644 index 0000000000..b2d51cbbfd --- /dev/null +++ b/ext/srtp/Makefile.am @@ -0,0 +1,33 @@ +plugin_LTLIBRARIES = libgstsrtp.la + +libgstsrtp_la_SOURCES = \ + gstsrtp.c \ + gstsrtp.h \ + gstsrtpdec.c \ + gstsrtpdec.h \ + gstsrtpenc.c \ + gstsrtpenc.h \ + gstsrtp-enumtypes.c \ + gstsrtp-enumtypes.h + +libgstsrtp_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(SRTP_CFLAGS) +libgstsrtp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstrtp-@GST_API_VERSION@ \ + $(GST_LIBS) \ + $(SRTP_LIBS) +libgstsrtp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstsrtp_la_LIBTOOLFLAGS = --tag=disable-static + +srtp_headers = \ + gstsrtpdec.h \ + gstsrtpenc.h + +glib_enum_headers=$(srtp_headers) +glib_enum_define=GST_SRTP +glib_gen_prefix=gst_srtp +glib_gen_basename=gstsrtp + +BUILT_SOURCES = gstsrtp-enumtypes.c gstsrtp-enumtypes.h +CLEANFILES = $(BUILT_SOURCES) + +include $(top_srcdir)/common/gst-glib-gen.mak diff --git a/ext/srtp/gstsrtp.c b/ext/srtp/gstsrtp.c new file mode 100644 index 0000000000..cf07d26a9a --- /dev/null +++ b/ext/srtp/gstsrtp.c @@ -0,0 +1,233 @@ +/* + * GStreamer - GStreamer SRTP encoder and decoder + * + * Copyright 2009-2013 Collabora Ltd. + * @author: Gabriel Millaire + * @author: Olivier Crete + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GLIB_DISABLE_DEPRECATION_WARNINGS + +#include "gstsrtp.h" + +#include + +#include + +#include "gstsrtpenc.h" +#include "gstsrtpdec.h" + +static void free_reporter_data (gpointer data); + +GPrivate current_callback = G_PRIVATE_INIT (free_reporter_data); + +struct GstSrtpEventReporterData +{ + gboolean soft_limit_reached; +}; + +static void +free_reporter_data (gpointer data) +{ + g_slice_free (struct GstSrtpEventReporterData, data); +} + + +static void +srtp_event_reporter (srtp_event_data_t * data) +{ + struct GstSrtpEventReporterData *dat = g_private_get (¤t_callback); + + if (!dat) + return; + + switch (data->event) { + case event_key_soft_limit: + dat->soft_limit_reached = TRUE; + break; + + default: + break; + } +} + +void +gst_srtp_init_event_reporter (void) +{ + struct GstSrtpEventReporterData *dat = g_private_get (¤t_callback); + + if (!dat) { + dat = g_slice_new (struct GstSrtpEventReporterData); + g_private_set (¤t_callback, dat); + } + + dat->soft_limit_reached = FALSE; + + srtp_install_event_handler (srtp_event_reporter); +} + +const gchar * +enum_nick_from_value (GType enum_gtype, gint value) +{ + GEnumClass *enum_class = g_type_class_ref (enum_gtype); + GEnumValue *enum_value; + const gchar *nick; + + if (!enum_gtype) + return NULL; + + enum_value = g_enum_get_value (enum_class, value); + nick = enum_value->value_nick; + g_type_class_unref (enum_class); + + return nick; +} + + +gint +enum_value_from_nick (GType enum_gtype, const gchar * nick) +{ + GEnumClass *enum_class = g_type_class_ref (enum_gtype); + GEnumValue *enum_value; + gint value; + + if (!enum_gtype) + return 0; + + enum_value = g_enum_get_value_by_nick (enum_class, nick); + value = enum_value->value; + g_type_class_unref (enum_class); + + return value; +} + +gboolean +gst_srtp_get_soft_limit_reached (void) +{ + struct GstSrtpEventReporterData *dat = g_private_get (¤t_callback); + + if (dat) + return dat->soft_limit_reached; + return FALSE; +} + +/* Get SSRC from RTCP buffer + */ +gboolean +rtcp_buffer_get_ssrc (GstBuffer * buf, guint32 * ssrc) +{ + gboolean ret = FALSE; + GstRTCPBuffer rtcpbuf = GST_RTCP_BUFFER_INIT; + GstRTCPPacket packet; + + /* Get SSRC from RR or SR packet (RTCP) */ + + if (!gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcpbuf)) + return FALSE; + + if (gst_rtcp_buffer_get_first_packet (&rtcpbuf, &packet)) { + do { + switch (gst_rtcp_packet_get_type (&packet)) { + case GST_RTCP_TYPE_RR: + *ssrc = gst_rtcp_packet_rr_get_ssrc (&packet); + ret = TRUE; + break; + case GST_RTCP_TYPE_SR: + gst_rtcp_packet_sr_get_sender_info (&packet, ssrc, NULL, NULL, NULL, + NULL); + ret = TRUE; + break; + default: + break; + } + } while (gst_rtcp_packet_move_to_next (&packet) && ret == FALSE); + } + + gst_rtcp_buffer_unmap (&rtcpbuf); + + return ret; +} + +void +set_crypto_policy_cipher_auth (guint cipher, guint auth, + crypto_policy_t * policy) +{ + switch (cipher) { + case GST_SRTP_CIPHER_AES_128_ICM: + policy->cipher_type = AES_ICM; + policy->cipher_key_len = 30; + break; + case GST_SRTP_CIPHER_NULL: + policy->cipher_type = NULL_CIPHER; + policy->cipher_key_len = 0; + break; + default: + g_assert_not_reached (); + } + + switch (auth) { + case GST_SRTP_AUTH_HMAC_SHA1_80: + policy->auth_type = HMAC_SHA1; + policy->auth_key_len = 20; + policy->auth_tag_len = 10; + break; + case GST_SRTP_AUTH_HMAC_SHA1_32: + policy->auth_type = HMAC_SHA1; + policy->auth_key_len = 20; + policy->auth_tag_len = 4; + break; + case GST_SRTP_AUTH_NULL: + policy->auth_type = NULL_AUTH; + policy->auth_key_len = 0; + policy->auth_tag_len = 0; + break; + } + + if (cipher == GST_SRTP_CIPHER_NULL && auth == GST_SRTP_AUTH_NULL) + policy->sec_serv = sec_serv_none; + else if (cipher == GST_SRTP_CIPHER_NULL) + policy->sec_serv = sec_serv_auth; + else if (auth == GST_SRTP_AUTH_NULL) + policy->sec_serv = sec_serv_conf; + else + policy->sec_serv = sec_serv_conf_and_auth; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + srtp_init (); + + if (!gst_srtp_enc_plugin_init (plugin)) + return FALSE; + + if (!gst_srtp_dec_plugin_init (plugin)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + srtp, + "GStreamer SRTP", + plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/") diff --git a/ext/srtp/gstsrtp.h b/ext/srtp/gstsrtp.h new file mode 100644 index 0000000000..e3f609a599 --- /dev/null +++ b/ext/srtp/gstsrtp.h @@ -0,0 +1,64 @@ +/* + * GStreamer - GStreamer SRTP encoder + * + * Copyright 2011-2013 Collabora Ltd. + * @author: Olivier Crete + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GST_SRTP_H__ +#define __GST_SRTP_H__ + +#include + +#include + +void gst_srtp_init_event_reporter (void); +gboolean gst_srtp_get_soft_limit_reached (void); + +gboolean rtcp_buffer_get_ssrc (GstBuffer * buf, guint32 * ssrc); + +const gchar *enum_nick_from_value (GType enum_gtype, gint value); +gint enum_value_from_nick (GType enum_gtype, const gchar *nick); + +void set_crypto_policy_cipher_auth (guint cipher, guint auth, + crypto_policy_t * policy); + + +#endif /* __GST_SRTP_H__ */ diff --git a/ext/srtp/gstsrtpdec.c b/ext/srtp/gstsrtpdec.c new file mode 100644 index 0000000000..0c64a7d78c --- /dev/null +++ b/ext/srtp/gstsrtpdec.c @@ -0,0 +1,965 @@ +/* + * GStreamer - GStreamer SRTP decoder + * + * Copyright 2009-2011 Collabora Ltd. + * @author: Gabriel Millaire + * @author: Olivier Crete + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-srtpdec + * + * gstrtpdec acts as a decoder that removes security from SRTP and SRTCP + * packets (encryption and authentication) and out RTP and RTCP. It + * receives packet of type 'application/x-srtp' or 'application/x-srtcp' + * on its sink pad, and outs packets of type 'application/x-rtp' or + * 'application/x-rtcp' on its sink pad. + * + * For each packet received, it checks if the internal SSRC is in the list + * of streams already in use. If this is not the case, it sends a signal to + * the user to get the needed parameters to create a new stream : master + * key, encryption and authentication mecanisms for both RTP and RTCP. If + * the user can't provide those parameters, the buffer is dropped and a + * warning is emitted. + * + * This element uses libsrtp library. The encryption and authentication + * mecanisms available are : + * + * Encryption + * - AES_128_ICM (default, maximum security) + * - STRONGHOLD_CIPHER (same as AES_128_ICM) + * - NULL + * + * Authentication + * - HMAC_SHA1 (default, maximum protection) + * - STRONGHOLD_AUTH (same as HMAC_SHA1) + * - NULL + * + * Note that for SRTP protection, authentication is mandatory (non-null) + * if encryption is used (non-null). + * + * Each packet received is first analysed (checked for valid SSRC) then + * its buffer is unprotected with libsrtp, then pushed on the source pad. + * If protection failed or the stream could not be created, the buffer + * is dropped and a warning is emitted. + * + * When the maximum usage of the master key is reached, a soft-limit + * signal is sent to the user, and new parameters (master key) are needed + * in return. If the hard limit is reached, a flag is set and every + * subsequent packet is dropped, until a new key is set and the stream + * has been updated. + * + * + * Example pipeline + * |[ + * gst-launch-0.10 udpsrc port=33333 caps="application/x-rtp,mkey=(string)bafbafbaf,..." ! rtpspeexdepay ! speexdec ! alsasink + * ]| Receive SPEEX SRTP or SRTCP packets through UDP using caps to specify + * master key and protection. It outs RTP or SRTP packets. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "gstsrtp.h" +#include "gstsrtp-enumtypes.h" + +#include "gstsrtpdec.h" + +GST_DEBUG_CATEGORY_STATIC (gst_srtp_dec_debug); +#define GST_CAT_DEFAULT gst_srtp_dec_debug + +/* Filter signals and args */ +enum +{ + SIGNAL_REQUEST_KEY = 1, + SIGNAL_CLEAR_KEYS, + SIGNAL_SOFT_LIMIT, + SIGNAL_HARD_LIMIT, + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +/* the capabilities of the inputs and outputs. + * + * describe the real formats here. + */ +static GstStaticPadTemplate rtp_sink_template = +GST_STATIC_PAD_TEMPLATE ("rtp_sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-srtp") + ); + +static GstStaticPadTemplate rtp_src_template = +GST_STATIC_PAD_TEMPLATE ("rtp_src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp") + ); + +static GstStaticPadTemplate rtcp_sink_template = +GST_STATIC_PAD_TEMPLATE ("rtcp_sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-srtcp") + ); + +static GstStaticPadTemplate rtcp_src_template = +GST_STATIC_PAD_TEMPLATE ("rtcp_src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtcp") + ); + +static guint gst_srtp_dec_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GstSrtpDec, gst_srtp_dec, GST_TYPE_ELEMENT); + +static void gst_srtp_dec_clear_streams (GstSrtpDec * filter); + +static gboolean gst_srtp_dec_sink_event_rtp (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_srtp_dec_sink_event_rtcp (GstPad * pad, GstObject * parent, + GstEvent * event); + +static gboolean gst_srtp_dec_sink_query_rtp (GstPad * pad, GstObject * parent, + GstQuery * query); +static gboolean gst_srtp_dec_sink_query_rtcp (GstPad * pad, + GstObject * parent, GstQuery * query); + + +static GstIterator *gst_srtp_dec_iterate_internal_links_rtp (GstPad * pad, + GstObject * parent); +static GstIterator *gst_srtp_dec_iterate_internal_links_rtcp (GstPad * pad, + GstObject * parent); + +static GstFlowReturn gst_srtp_dec_chain_rtp (GstPad * pad, + GstObject * parent, GstBuffer * buf); +static GstFlowReturn gst_srtp_dec_chain_rtcp (GstPad * pad, + GstObject * parent, GstBuffer * buf); + +static GstStateChangeReturn gst_srtp_dec_change_state (GstElement * element, + GstStateChange transition); + +static GstSrtpDecSsrcStream *request_key_with_signal (GstSrtpDec * filter, + guint32 ssrc, gint signal); + +struct _GstSrtpDecSsrcStream +{ + guint32 ssrc; + + GstCaps *caps; + GstBuffer *key; + guint rtp_cipher; + guint rtp_auth; + guint rtcp_cipher; + guint rtcp_auth; +}; + +/* initialize the srtpdec's class */ +static void +gst_srtp_dec_class_init (GstSrtpDecClass * klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = (GstElementClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&rtp_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&rtp_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&rtcp_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&rtcp_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, "SRTP decoder", + "Filter/Network/SRTP", + "A SRTP and SRTCP decoder", + "Gabriel Millaire "); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_srtp_dec_change_state); + klass->clear_streams = GST_DEBUG_FUNCPTR (gst_srtp_dec_clear_streams); + + /** + * GstSrtpDec::request-key: + * @gstsrtpdec: the element on which the signal is emitted + * @ssrc: The unique SSRC of the stream + * + * Signal emited to get the parameters relevant to stream + * with @ssrc. User should provide the key and the RTP and + * RTCP encryption ciphers and authentication, and return + * them wrapped in a GstCaps. + */ + gst_srtp_dec_signals[SIGNAL_REQUEST_KEY] = + g_signal_new ("request-key", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, GST_TYPE_CAPS, 1, G_TYPE_UINT); + + /** + * GstSrtpDec::clear-keys: + * @gstsrtpdec: the element on which the signal is emitted + * + * Clear the internal list of streams + */ + gst_srtp_dec_signals[SIGNAL_CLEAR_KEYS] = + g_signal_new ("clear-keys", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GstSrtpDecClass, clear_streams), NULL, NULL, NULL, + G_TYPE_NONE, 0, G_TYPE_NONE); + + /** + * GstSrtpDec::soft-limit: + * @gstsrtpdec: the element on which the signal is emitted + * @ssrc: The unique SSRC of the stream + * + * Signal emited when the stream with @ssrc has reached the + * soft limit of utilisation of it's master encryption key. + * User should provide a new key and new RTP and RTCP encryption + * ciphers and authentication, and return them wrapped in a + * GstCaps. + */ + gst_srtp_dec_signals[SIGNAL_SOFT_LIMIT] = + g_signal_new ("soft-limit", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, GST_TYPE_CAPS, 1, G_TYPE_UINT); + + /** + * GstSrtpDec::hard-limit: + * @gstsrtpdec: the element on which the signal is emitted + * @ssrc: The unique SSRC of the stream + * + * Signal emited when the stream with @ssrc has reached the + * hard limit of utilisation of it's master encryption key. + * User should provide a new key and new RTP and RTCP encryption + * ciphers and authentication, and return them wrapped in a + * GstCaps. If user could not provide those parameters or signal + * is not answered, the buffers of this stream will be dropped. + */ + gst_srtp_dec_signals[SIGNAL_HARD_LIMIT] = + g_signal_new ("hard-limit", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, GST_TYPE_CAPS, 1, G_TYPE_UINT); +} + +/* initialize the new element + * instantiate pads and add them to element + * set pad calback functions + * initialize instance structure + */ +static void +gst_srtp_dec_init (GstSrtpDec * filter) +{ + filter->rtp_sinkpad = + gst_pad_new_from_static_template (&rtp_sink_template, "rtp_sink"); + gst_pad_set_event_function (filter->rtp_sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_sink_event_rtp)); + gst_pad_set_query_function (filter->rtp_sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_sink_query_rtp)); + gst_pad_set_iterate_internal_links_function (filter->rtp_sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_iterate_internal_links_rtp)); + gst_pad_set_chain_function (filter->rtp_sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_chain_rtp)); + + filter->rtp_srcpad = + gst_pad_new_from_static_template (&rtp_src_template, "rtp_src"); + gst_pad_set_iterate_internal_links_function (filter->rtp_srcpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_iterate_internal_links_rtp)); + + gst_pad_set_element_private (filter->rtp_sinkpad, filter->rtp_srcpad); + gst_pad_set_element_private (filter->rtp_srcpad, filter->rtp_sinkpad); + + gst_element_add_pad (GST_ELEMENT (filter), filter->rtp_sinkpad); + gst_element_add_pad (GST_ELEMENT (filter), filter->rtp_srcpad); + + + filter->rtcp_sinkpad = + gst_pad_new_from_static_template (&rtcp_sink_template, "rtcp_sink"); + gst_pad_set_event_function (filter->rtcp_sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_sink_event_rtcp)); + gst_pad_set_query_function (filter->rtcp_sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_sink_query_rtcp)); + gst_pad_set_iterate_internal_links_function (filter->rtcp_sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_iterate_internal_links_rtcp)); + gst_pad_set_chain_function (filter->rtcp_sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_chain_rtcp)); + + filter->rtcp_srcpad = + gst_pad_new_from_static_template (&rtcp_src_template, "rtcp_src"); + gst_pad_set_iterate_internal_links_function (filter->rtcp_srcpad, + GST_DEBUG_FUNCPTR (gst_srtp_dec_iterate_internal_links_rtcp)); + + gst_pad_set_element_private (filter->rtcp_sinkpad, filter->rtcp_srcpad); + gst_pad_set_element_private (filter->rtcp_srcpad, filter->rtcp_sinkpad); + + gst_element_add_pad (GST_ELEMENT (filter), filter->rtcp_sinkpad); + gst_element_add_pad (GST_ELEMENT (filter), filter->rtcp_srcpad); + + filter->first_session = TRUE; +} + +static void +remove_stream_by_ssrc (GstSrtpDec * filter, guint32 ssrc) +{ + GstSrtpDecSsrcStream *stream = NULL; + + stream = g_hash_table_lookup (filter->streams, GUINT_TO_POINTER (ssrc)); + + if (stream) { + srtp_remove_stream (filter->session, ssrc); + g_hash_table_remove (filter->streams, GUINT_TO_POINTER (ssrc)); + } +} + +static GstSrtpDecSsrcStream * +find_stream_by_ssrc (GstSrtpDec * filter, guint32 ssrc) +{ + return g_hash_table_lookup (filter->streams, GUINT_TO_POINTER (ssrc)); +} + + +/* get info from buffer caps + */ +static GstSrtpDecSsrcStream * +get_stream_from_caps (GstSrtpDec * filter, GstCaps * caps, guint32 ssrc) +{ + GstSrtpDecSsrcStream *stream; + GstStructure *s; + GstBuffer *buf; + const gchar *rtp_cipher, *rtp_auth, *rtcp_cipher, *rtcp_auth; + + /* Create new stream structure and set default values */ + stream = g_slice_new0 (GstSrtpDecSsrcStream); + stream->ssrc = ssrc; + stream->key = NULL; + + /* Get info from caps */ + s = gst_caps_get_structure (caps, 0); + if (!s) + goto error; + + rtp_cipher = gst_structure_get_string (s, "srtp-cipher"); + rtp_auth = gst_structure_get_string (s, "srtp-auth"); + rtcp_cipher = gst_structure_get_string (s, "srtcp-cipher"); + rtcp_auth = gst_structure_get_string (s, "srtcp-auth"); + if (!rtp_cipher || !rtp_auth || !rtcp_cipher || !rtcp_auth) + goto error; + + if (!gst_structure_get (s, "srtp-key", GST_TYPE_BUFFER, &buf, NULL) || !buf) { + goto error; + } else { + GST_DEBUG ("Got key [%p]", buf); + stream->key = buf; + } + + stream->rtp_cipher = enum_value_from_nick (GST_TYPE_SRTP_CIPHER_TYPE, + rtp_cipher); + stream->rtp_auth = enum_value_from_nick (GST_TYPE_SRTP_AUTH_TYPE, rtp_auth); + stream->rtcp_cipher = enum_value_from_nick (GST_TYPE_SRTP_CIPHER_TYPE, + rtcp_cipher); + stream->rtcp_auth = enum_value_from_nick (GST_TYPE_SRTP_AUTH_TYPE, rtcp_auth); + + if (stream->rtcp_cipher != NULL_CIPHER && stream->rtcp_auth == NULL_AUTH) { + GST_WARNING_OBJECT (filter, + "Cannot have SRTP NULL authentication with a not-NULL encryption" + " cipher."); + goto error; + } + + return stream; + +error: + g_slice_free (GstSrtpDecSsrcStream, stream); + return NULL; +} + +/* Get SRTP params by signal + */ +static GstCaps * +signal_get_srtp_params (GstSrtpDec * filter, guint32 ssrc, gint signal) +{ + GstCaps *caps = NULL; + + g_signal_emit (filter, gst_srtp_dec_signals[signal], 0, ssrc, &caps); + + if (caps != NULL) + GST_DEBUG_OBJECT (filter, "Caps received"); + + return caps; +} + +/* Create a stream in the session + */ +static err_status_t +init_session_stream (GstSrtpDec * filter, guint32 ssrc, + GstSrtpDecSsrcStream * stream) +{ + err_status_t ret; + srtp_policy_t policy; + GstMapInfo map; + + memset (&policy, 0, sizeof (srtp_policy_t)); + + if (!stream) + return err_status_bad_param; + + GST_INFO_OBJECT (filter, "Setting RTP policy..."); + set_crypto_policy_cipher_auth (stream->rtp_cipher, stream->rtp_auth, + &policy.rtp); + GST_INFO_OBJECT (filter, "Setting RTCP policy..."); + set_crypto_policy_cipher_auth (stream->rtcp_cipher, stream->rtcp_auth, + &policy.rtcp); + + gst_buffer_map (stream->key, &map, GST_MAP_READ); + + policy.ssrc.value = ssrc; + policy.ssrc.type = ssrc_specific; + policy.key = (guchar *) map.data; + policy.next = NULL; + + /* If it is the first stream, create the session + * If not, add the stream policy to the session + */ + if (filter->first_session) + ret = srtp_create (&filter->session, &policy); + else + ret = srtp_add_stream (filter->session, &policy); + + gst_buffer_unmap (stream->key, &map); + + if (ret == err_status_ok) { + filter->first_session = FALSE; + g_hash_table_insert (filter->streams, GUINT_TO_POINTER (stream->ssrc), + stream); + } + + return ret; +} + +/* Return a stream structure for a given buffer + */ +static GstSrtpDecSsrcStream * +validate_buffer (GstSrtpDec * filter, GstBuffer * buf, guint32 * ssrc, + gboolean is_rtcp) +{ + GstSrtpDecSsrcStream *stream = NULL; + + if (!is_rtcp) { + GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT; + + if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuf)) { + GST_WARNING_OBJECT (filter, "Invalid SRTP packet"); + return NULL; + } + + *ssrc = gst_rtp_buffer_get_ssrc (&rtpbuf); + + gst_rtp_buffer_unmap (&rtpbuf); + } else if (!rtcp_buffer_get_ssrc (buf, ssrc)) { + GST_WARNING_OBJECT (filter, "No SSRC found in buffer"); + return NULL; + } + + stream = find_stream_by_ssrc (filter, *ssrc); + + if (stream) + return stream; + + return request_key_with_signal (filter, *ssrc, SIGNAL_REQUEST_KEY); +} + +/* Create new stream from params in caps + */ +static GstSrtpDecSsrcStream * +update_session_stream_from_caps (GstSrtpDec * filter, guint32 ssrc, + GstCaps * caps) +{ + GstSrtpDecSsrcStream *stream = NULL; + err_status_t err; + + g_return_val_if_fail (GST_IS_SRTP_DEC (filter), NULL); + g_return_val_if_fail (GST_IS_CAPS (caps), NULL); + + /* Remove existing stream, if any */ + remove_stream_by_ssrc (filter, ssrc); + stream = get_stream_from_caps (filter, caps, ssrc); + + if (stream) { + /* Create new session stream */ + err = init_session_stream (filter, ssrc, stream); + + if (err != err_status_ok) { + gst_buffer_unref (stream->key); + g_slice_free (GstSrtpDecSsrcStream, stream); + stream = NULL; + } + } + + return stream; +} + +static void +clear_stream (GstSrtpDecSsrcStream * stream) +{ + gst_buffer_unref (stream->key); + g_slice_free (GstSrtpDecSsrcStream, stream); +} + +/* Clear the policy list + */ +static void +gst_srtp_dec_clear_streams (GstSrtpDec * filter) +{ + guint nb = 0; + + GST_OBJECT_LOCK (filter); + + if (!filter->first_session) + srtp_dealloc (filter->session); + + nb = g_hash_table_size (filter->streams); + g_hash_table_destroy (filter->streams); + + filter->streams = NULL; + filter->first_session = TRUE; + + GST_OBJECT_UNLOCK (filter); + + GST_DEBUG_OBJECT (filter, "Cleared %d streams", nb); +} + +/* Send a signal + */ +static GstSrtpDecSsrcStream * +request_key_with_signal (GstSrtpDec * filter, guint32 ssrc, gint signal) +{ + GstCaps *caps; + GstSrtpDecSsrcStream *stream = NULL; + + caps = signal_get_srtp_params (filter, ssrc, signal); + + if (caps) { + GstSrtpDecSsrcStream *stream = + update_session_stream_from_caps (filter, ssrc, caps); + if (stream) + GST_DEBUG_OBJECT (filter, "New stream set with SSRC %d", ssrc); + else + GST_WARNING_OBJECT (filter, "Could not set stream with SSRC %d", ssrc); + gst_caps_unref (caps); + } + + return stream; +} + +static gboolean +gst_srtp_dec_sink_setcaps (GstPad * pad, GstObject * parent, + GstCaps * caps, gboolean is_rtcp) +{ + GstSrtpDec *filter = GST_SRTP_DEC (parent); + GstPad *otherpad; + GstStructure *ps; + gboolean ret = FALSE; + + g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); + + ps = gst_caps_get_structure (caps, 0); + + if (gst_structure_has_field_typed (ps, "ssrc", G_TYPE_UINT) && + gst_structure_has_field_typed (ps, "srtp-key", GST_TYPE_BUFFER) && + gst_structure_has_field_typed (ps, "srtp-cipher", G_TYPE_STRING) && + gst_structure_has_field_typed (ps, "srtp-auth", G_TYPE_STRING) && + gst_structure_has_field_typed (ps, "srtcp-cipher", G_TYPE_STRING) && + gst_structure_has_field_typed (ps, "srtcp-auth", G_TYPE_STRING)) { + guint ssrc; + + gst_structure_get_uint (ps, "ssrc", &ssrc); + + if (!update_session_stream_from_caps (filter, ssrc, caps)) { + GST_WARNING_OBJECT (pad, "Could not create session from pad caps: %" + GST_PTR_FORMAT, caps); + return FALSE; + } + } + + caps = gst_caps_copy (caps); + ps = gst_caps_get_structure (caps, 0); + gst_structure_remove_fields (ps, "srtp-key", "srtp-cipher", "srtp-auth", + "srtcp-cipher", "srtcp-auth", NULL); + + if (is_rtcp) + gst_structure_set_name (ps, "application/x-rtcp"); + else + gst_structure_set_name (ps, "application/x-rtp"); + + otherpad = gst_pad_get_element_private (pad); + + ret = gst_pad_set_caps (otherpad, caps); + + gst_caps_unref (caps); + + return ret; +} + +static gboolean +gst_srtp_dec_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event, gboolean is_rtcp) +{ + GstCaps *caps; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + gst_event_parse_caps (event, &caps); + return gst_srtp_dec_sink_setcaps (pad, parent, caps, FALSE); + default: + return gst_pad_event_default (pad, parent, event); + } +} + +static gboolean +gst_srtp_dec_sink_event_rtp (GstPad * pad, GstObject * parent, GstEvent * event) +{ + return gst_srtp_dec_sink_event (pad, parent, event, FALSE); +} + +static gboolean +gst_srtp_dec_sink_event_rtcp (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + return gst_srtp_dec_sink_event (pad, parent, event, TRUE); +} + +static gboolean +gst_srtp_dec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query, + gboolean is_rtcp) +{ + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS: + { + GstCaps *filter = NULL; + GstCaps *other_filter = NULL; + GstCaps *template_caps; + GstPad *otherpad; + GstCaps *other_caps; + GstCaps *ret; + int i; + + gst_query_parse_caps (query, &filter); + + otherpad = (GstPad *) gst_pad_get_element_private (pad); + + if (filter) { + other_filter = gst_caps_copy (filter); + + for (i = 0; i < gst_caps_get_size (other_filter); i++) { + GstStructure *ps = gst_caps_get_structure (other_filter, i); + if (is_rtcp) + gst_structure_set_name (ps, "application/x-rtcp"); + else + gst_structure_set_name (ps, "application/x-rtp"); + gst_structure_remove_fields (ps, "srtp-key", "srtp-cipher", + "srtp-auth", "srtcp-cipher", "srtcp-auth", NULL); + } + } + + + other_caps = gst_pad_peer_query_caps (otherpad, other_filter); + if (other_filter) + gst_caps_unref (other_filter); + if (!other_caps) { + goto return_template; + } + + template_caps = gst_pad_get_pad_template_caps (otherpad); + ret = gst_caps_intersect_full (other_caps, template_caps, + GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (other_caps); + gst_caps_unref (template_caps); + + ret = gst_caps_make_writable (ret); + + for (i = 0; i < gst_caps_get_size (ret); i++) { + GstStructure *ps = gst_caps_get_structure (ret, i); + if (is_rtcp) + gst_structure_set_name (ps, "application/x-srtcp"); + else + gst_structure_set_name (ps, "application/x-srtp"); + } + + gst_query_set_caps_result (query, ret); + return TRUE; + + return_template: + + ret = gst_pad_get_pad_template_caps (pad); + gst_query_set_caps_result (query, ret); + gst_caps_unref (ret); + return TRUE; + } + default: + return gst_pad_query_default (pad, parent, query); + } +} + +static gboolean +gst_srtp_dec_sink_query_rtp (GstPad * pad, GstObject * parent, GstQuery * query) +{ + return gst_srtp_dec_sink_query (pad, parent, query, FALSE); +} + +static gboolean +gst_srtp_dec_sink_query_rtcp (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + return gst_srtp_dec_sink_query (pad, parent, query, TRUE); +} + +static GstIterator * +gst_srtp_dec_iterate_internal_links (GstPad * pad, GstObject * parent, + gboolean is_rtcp) +{ + GstSrtpDec *filter = GST_SRTP_DEC (parent); + GstPad *otherpad = NULL; + GstIterator *it = NULL; + + otherpad = (GstPad *) gst_pad_get_element_private (pad); + + if (otherpad) { + GValue val = { 0 }; + + g_value_init (&val, GST_TYPE_PAD); + g_value_set_object (&val, otherpad); + it = gst_iterator_new_single (GST_TYPE_PAD, &val); + g_value_unset (&val); + } else { + GST_ELEMENT_ERROR (GST_ELEMENT_CAST (filter), CORE, PAD, (NULL), + ("Unable to get linked pad")); + } + + return it; +} + +static GstIterator * +gst_srtp_dec_iterate_internal_links_rtp (GstPad * pad, GstObject * parent) +{ + return gst_srtp_dec_iterate_internal_links (pad, parent, FALSE); +} + +static GstIterator * +gst_srtp_dec_iterate_internal_links_rtcp (GstPad * pad, GstObject * parent) +{ + return gst_srtp_dec_iterate_internal_links (pad, parent, TRUE); +} + +static GstFlowReturn +gst_srtp_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf, + gboolean is_rtcp) +{ + GstSrtpDec *filter = GST_SRTP_DEC (parent); + GstPad *otherpad; + err_status_t err = err_status_ok; + GstSrtpDecSsrcStream *stream = NULL; + GstFlowReturn ret = GST_FLOW_OK; + gint size; + guint32 ssrc = 0; + GstMapInfo map; + + GST_OBJECT_LOCK (filter); + + /* Check if this stream exists, if not create a new stream */ + + if (!(stream = validate_buffer (filter, buf, &ssrc, is_rtcp))) { + GST_OBJECT_UNLOCK (filter); + goto drop_buffer; + } + + GST_LOG_OBJECT (pad, "Received %s buffer of size %d with SSRC = %u", + is_rtcp ? "RTCP" : "RTP", gst_buffer_get_size (buf), ssrc); + + /* Change buffer to remove protection */ + buf = gst_buffer_make_writable (buf); + +unprotect: + + gst_buffer_map (buf, &map, GST_MAP_READWRITE); + size = map.size; + + gst_srtp_init_event_reporter (); + + if (is_rtcp) + err = srtp_unprotect_rtcp (filter->session, map.data, &size); + else + err = srtp_unprotect (filter->session, map.data, &size); + + gst_buffer_unmap (buf, &map); + + GST_OBJECT_UNLOCK (filter); + + if (err == err_status_ok) { + gst_buffer_set_size (buf, size); + otherpad = (GstPad *) gst_pad_get_element_private (pad); + + /* If all is well, we may have reached soft limit */ + if (gst_srtp_get_soft_limit_reached ()) + request_key_with_signal (filter, ssrc, SIGNAL_SOFT_LIMIT); + + /* Push buffer to source pad */ + ret = gst_pad_push (otherpad, buf); + + } else { /* srtp_unprotect failed */ + GST_WARNING_OBJECT (pad, + "Unable to unprotect buffer (unprotect failed code %d)", err); + + /* Signal user depending on type of error */ + switch (err) { + case err_status_key_expired: + GST_OBJECT_LOCK (filter); + + /* Update stream */ + if ((stream = find_stream_by_ssrc (filter, ssrc))) { + GST_OBJECT_UNLOCK (filter); + if (request_key_with_signal (filter, ssrc, SIGNAL_HARD_LIMIT)) { + GST_OBJECT_LOCK (filter); + goto unprotect; + } + goto drop_buffer; + } + break; + + case err_status_auth_fail: + case err_status_cipher_fail: + GST_ELEMENT_ERROR (filter, STREAM, DECRYPT, + ("Error while decryption stream"), (NULL)); + ret = GST_FLOW_ERROR; + goto drop_buffer; + + default: + goto drop_buffer; + } + } + + return ret; + + /* Drop buffer, except if gst_pad_push returned OK or an error */ + +drop_buffer: + GST_WARNING_OBJECT (pad, "Dropping buffer"); + + gst_buffer_unref (buf); + + return ret; +} + +static GstFlowReturn +gst_srtp_dec_chain_rtp (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + return gst_srtp_dec_chain (pad, parent, buf, FALSE); +} + +static GstFlowReturn +gst_srtp_dec_chain_rtcp (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + return gst_srtp_dec_chain (pad, parent, buf, TRUE); +} + +static GstStateChangeReturn +gst_srtp_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn res; + GstSrtpDec *filter; + + filter = GST_SRTP_DEC (element); + GST_OBJECT_LOCK (filter); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!filter->first_session) + gst_srtp_dec_clear_streams (filter); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + filter->streams = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) clear_stream); + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + GST_OBJECT_UNLOCK (filter); + + res = GST_ELEMENT_CLASS (gst_srtp_dec_parent_class)->change_state (element, + transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_srtp_dec_clear_streams (filter); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return res; +} + + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and other features + */ +gboolean +gst_srtp_dec_plugin_init (GstPlugin * srtpdec) +{ + GST_DEBUG_CATEGORY_INIT (gst_srtp_dec_debug, "srtpdec", 0, "SRTP dec"); + + return gst_element_register (srtpdec, "srtpdec", GST_RANK_NONE, + GST_TYPE_SRTP_DEC); +} diff --git a/ext/srtp/gstsrtpdec.h b/ext/srtp/gstsrtpdec.h new file mode 100644 index 0000000000..633818b484 --- /dev/null +++ b/ext/srtp/gstsrtpdec.h @@ -0,0 +1,96 @@ +/* + * GStreamer - GStreamer SRTP decoder + * + * Copyright 2009 Collabora Ltd. + * @author: Gabriel Millaire + * @author: Olivier Crete + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_SRTPDEC_H__ +#define __GST_SRTPDEC_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_SRTP_DEC \ + (gst_srtp_dec_get_type()) +#define GST_SRTP_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SRTP_DEC,GstSrtpDec)) +#define GST_SRTP_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SRTP_DEC,GstSrtpDecClass)) +#define GST_IS_SRTP_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SRTP_DEC)) +#define GST_IS_SRTP_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SRTP_DEC)) + +typedef struct _GstSrtpDec GstSrtpDec; +typedef struct _GstSrtpDecClass GstSrtpDecClass; +typedef struct _GstSrtpDecSsrcStream GstSrtpDecSsrcStream; + +struct _GstSrtpDec +{ + GstElement element; + + GstPad *rtp_sinkpad, *rtp_srcpad; + GstPad *rtcp_sinkpad, *rtcp_srcpad; + + gboolean ask_update; + srtp_t session; + gboolean first_session; + GHashTable *streams; +}; + +struct _GstSrtpDecClass +{ + GstElementClass parent_class; + + void (*clear_streams) (GstSrtpDec * filter); +}; + +GType gst_srtp_dec_get_type (void); + +gboolean gst_srtp_dec_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_SRTPDEC_H__ */ diff --git a/ext/srtp/gstsrtpenc.c b/ext/srtp/gstsrtpenc.c new file mode 100644 index 0000000000..f45ad6e1ab --- /dev/null +++ b/ext/srtp/gstsrtpenc.c @@ -0,0 +1,1134 @@ +/* + * GStreamer - GStreamer SRTP encoder + * + * Copyright 2009-2011 Collabora Ltd. + * @author: Gabriel Millaire + * @author: Olivier Crete + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-srtpenc + * + * gstrtpenc acts as an encoder that adds security to RTP and RTCP + * packets in the form of encryption and authentication. It outs SRTP + * and SRTCP. + * + * An application can request multiple RTP and RTCP pads to protect, + * but every sink pad requested must receive packets from the same + * source (identical SSRC). If a packet received contains a different + * SSRC, a warning is emited and the valid SSRC is forced on the packet. + * + * This element uses libsrtp library. When receiving the first packet, + * the library is initialized with a new stream (based on the SSRC). It + * uses the default RTP and RTCP encryption and authentication mechanisms, + * unless the user has set the relevant properties first. It also uses + * a master key that MUST be set by property (key) at the beginning. The + * master key must be of a maximum length of 30 characters. The + * encryption and authentication mecanisms available are : + * + * Encryption (properties rtp-cipher and rtcp-cipher) + * - AES_128_ICM (default, maximum security) + * - STRONGHOLD_CIPHER (same as AES_128_ICM) + * - NULL + * + * Authentication (properties rtp-auth and rtcp-auth) + * - HMAC_SHA1 (default, maximum protection) + * - STRONGHOLD_AUTH (same as HMAC_SHA1) + * - NULL + * + * Note that for SRTP protection, authentication is mandatory (non-null) + * if encryption is used (non-null). + * + * When requested to create a sink pad, a linked source pad is created. + * Each packet received is first analysed (checked for valid SSRC) then + * its buffer is protected with libsrtp, then pushed on the source pad. + * If protection failed or the stream could not be created, the buffer + * is dropped and a warning is emitted. The packets pushed on the source + * pad are of type 'application/x-srtp' or 'application/x-srtcp'. + * + * When the maximum usage of the master key is reached, a soft-limit + * signal is sent to the user. The user must then set a new master key + * by property. If the hard limit is reached, a flag is set and every + * subsequent packet is dropped, until a new key is set and the stream + * has been updated. + * + * + * Example pipeline + * |[ + * gst-launch-0.10 --gst-debug=srtp*:5 audiotestsrc ! speexenc ! rtpspeexpay ! srtpenc key=bafbafbaf ! udpsink port=33333 + * ]| Enc SPEEX RTP packets through srtpenc using default protection + * and costum master key, and out on UDP port 33333. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "gstsrtpenc.h" + +#include "gstsrtp.h" +#include "gstsrtp-enumtypes.h" + +GST_DEBUG_CATEGORY_STATIC (gst_srtp_enc_debug); +#define GST_CAT_DEFAULT gst_srtp_enc_debug + +/* Properties default values */ +#define DEFAULT_MASTER_KEY NULL +#define DEFAULT_RTP_CIPHER GST_SRTP_CIPHER_AES_128_ICM +#define DEFAULT_RTP_AUTH GST_SRTP_AUTH_HMAC_SHA1_80 +#define DEFAULT_RTCP_CIPHER DEFAULT_RTP_CIPHER +#define DEFAULT_RTCP_AUTH DEFAULT_RTP_AUTH +#define DEFAULT_RANDOM_KEY FALSE + +#define OBJECT_LOCK(arg) {GST_DEBUG("Locking"); GST_OBJECT_LOCK(arg);} +#define OBJECT_UNLOCK(arg) {GST_DEBUG("Unlocking"); GST_OBJECT_UNLOCK(arg);} + +/* Filter signals and args */ +enum +{ + SIGNAL_SOFT_LIMIT, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_MKEY, + PROP_RTP_CIPHER, + PROP_RTP_AUTH, + PROP_RTCP_CIPHER, + PROP_RTCP_AUTH, + PROP_RANDOM_KEY +}; + +/* the capabilities of the inputs and outputs. + * + * describe the real formats here. + */ +static GstStaticPadTemplate rtp_sink_template = +GST_STATIC_PAD_TEMPLATE ("rtp_sink_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("application/x-rtp") + ); + +static GstStaticPadTemplate rtp_src_template = +GST_STATIC_PAD_TEMPLATE ("rtp_src_%d", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("application/x-srtp") + ); + +static GstStaticPadTemplate rtcp_sink_template = +GST_STATIC_PAD_TEMPLATE ("rtcp_sink_%d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("application/x-rtcp") + ); + +static GstStaticPadTemplate rtcp_src_template = +GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("application/x-srtcp") + ); + +G_DEFINE_TYPE (GstSrtpEnc, gst_srtp_enc, GST_TYPE_ELEMENT); + +static guint gst_srtp_enc_signals[LAST_SIGNAL] = { 0 }; + +static void gst_srtp_enc_dispose (GObject * object); + +static void gst_srtp_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_srtp_enc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_srtp_enc_sink_query_rtp (GstPad * pad, GstObject * parent, + GstQuery * query); +static gboolean gst_srtp_enc_sink_query_rtcp (GstPad * pad, GstObject * parent, + GstQuery * query); + +static GstIterator *gst_srtp_enc_iterate_internal_links_rtp (GstPad * pad, + GstObject * parent); +static GstIterator *gst_srtp_enc_iterate_internal_links_rtcp (GstPad * pad, + GstObject * parent); + +static GstFlowReturn gst_srtp_enc_chain_rtp (GstPad * pad, GstObject * parent, + GstBuffer * buf); +static GstFlowReturn gst_srtp_enc_chain_rtcp (GstPad * pad, GstObject * parent, + GstBuffer * buf); + +static gboolean gst_srtp_enc_sink_event_rtp (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_srtp_enc_sink_event_rtcp (GstPad * pad, GstObject * parent, + GstEvent * event); + +static GstStateChangeReturn gst_srtp_enc_change_state (GstElement * element, + GstStateChange transition); + +static GstPad *gst_srtp_enc_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name, const GstCaps * caps); + +static void gst_srtp_enc_release_pad (GstElement * element, GstPad * pad); + +struct GstSrtpEncPads +{ + guint ssrc; + + GstPad *sinkpad; + GstPad *srcpad; +}; + + +/* initialize the srtpenc's class + */ +static void +gst_srtp_enc_class_init (GstSrtpEncClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&rtp_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&rtp_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&rtcp_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&rtcp_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, "SRTP encoder", + "Filter/Network/SRTP", + "A SRTP and SRTCP encoder", + "Gabriel Millaire "); + + + /* Install callbacks */ + gobject_class->set_property = gst_srtp_enc_set_property; + gobject_class->get_property = gst_srtp_enc_get_property; + gobject_class->dispose = gst_srtp_enc_dispose; + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_srtp_enc_request_new_pad); + gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_srtp_enc_release_pad); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_srtp_enc_change_state); + + /* Install properties */ + g_object_class_install_property (gobject_class, PROP_MKEY, + g_param_spec_boxed ("key", "Key", "Master key", + GST_TYPE_BUFFER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_PLAYING)); + g_object_class_install_property (gobject_class, PROP_RTP_CIPHER, + g_param_spec_enum ("rtp-cipher", "RTP Cipher", "RTP Cipher", + GST_TYPE_SRTP_CIPHER_TYPE, DEFAULT_RTP_CIPHER, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_RTP_AUTH, + g_param_spec_enum ("rtp-auth", "RTP Authentication", + "RTP Authentication", GST_TYPE_SRTP_AUTH_TYPE, DEFAULT_RTP_AUTH, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_RTCP_CIPHER, + g_param_spec_enum ("rtcp-cipher", "RTCP Cipher", + "RTCP Cipher", GST_TYPE_SRTP_CIPHER_TYPE, DEFAULT_RTCP_CIPHER, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_RTCP_AUTH, + g_param_spec_enum ("rtcp-auth", "RTCP Authentication", + "RTCP Authentication", GST_TYPE_SRTP_AUTH_TYPE, DEFAULT_RTCP_AUTH, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_RANDOM_KEY, + g_param_spec_boolean ("random-key", "Generate random key", + "Generate a random key if TRUE", + DEFAULT_RANDOM_KEY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstSrtpEnc::soft-limit: + * @gstsrtpenc: the element on which the signal is emitted + * @ssrc: The unique SSRC of the stream + * + * Signal emited when the stream with @ssrc has reached the soft + * limit of utilisation of it's master encryption key. User should + * provide a new key by setting the #GstSrtpEnc:key property. + */ + gst_srtp_enc_signals[SIGNAL_SOFT_LIMIT] = + g_signal_new ("soft-limit", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); +} + + +/* initialize the new element + */ +static void +gst_srtp_enc_init (GstSrtpEnc * filter) +{ + filter->key_changed = TRUE; + filter->first_session = TRUE; + filter->key = DEFAULT_MASTER_KEY; + filter->rtp_cipher = DEFAULT_RTP_CIPHER; + filter->rtp_auth = DEFAULT_RTP_AUTH; + filter->rtcp_cipher = DEFAULT_RTCP_CIPHER; + filter->rtcp_auth = DEFAULT_RTCP_AUTH; + + filter->ssrcs_set = g_hash_table_new (g_direct_hash, g_direct_equal); +} + + +/* Create stream + */ +static gboolean +check_new_stream_locked (GstSrtpEnc * filter, guint32 ssrc) +{ + err_status_t ret; + srtp_policy_t policy; + GstMapInfo map; + + memset (&policy, 0, sizeof (srtp_policy_t)); + + /* check if we already have that stream */ + if (g_hash_table_lookup (filter->ssrcs_set, GUINT_TO_POINTER (ssrc))) + return TRUE; + + if (gst_buffer_get_size (filter->key) != SRTP_MASTER_KEY_LEN) { + GST_ELEMENT_ERROR (filter, LIBRARY, SETTINGS, ("Master key size is wrong"), + ("Expected master key of %d bytes, but received %d bytes", + SRTP_MASTER_KEY_LEN, gst_buffer_get_size (filter->key))); + return FALSE; + } + + GST_DEBUG_OBJECT (filter, "Setting RTP/RTCP policy to %d / %d", + filter->rtp_cipher, filter->rtcp_cipher); + set_crypto_policy_cipher_auth (filter->rtp_cipher, filter->rtp_auth, + &policy.rtp); + set_crypto_policy_cipher_auth (filter->rtcp_cipher, filter->rtcp_auth, + &policy.rtcp); + + gst_buffer_map (filter->key, &map, GST_MAP_READ); + + policy.ssrc.value = ssrc; + policy.ssrc.type = ssrc_specific; + policy.key = (guchar *) map.data; + policy.next = NULL; + + /* If it is the first stream, create the session + * If not, add the stream to the session + */ + if (filter->first_session) + ret = srtp_create (&filter->session, &policy); + else + ret = srtp_add_stream (filter->session, &policy); + + gst_buffer_unmap (filter->key, &map); + + g_hash_table_insert (filter->ssrcs_set, GUINT_TO_POINTER (ssrc), + GUINT_TO_POINTER (1)); + + return ret == err_status_ok; +} + +/* Release ressources and set default values + */ +static void +gst_srtp_enc_reset (GstSrtpEnc * filter) +{ + GST_OBJECT_LOCK (filter); + + if (!filter->first_session) + srtp_dealloc (filter->session); + + filter->first_session = TRUE; + filter->key_changed = FALSE; + + g_hash_table_remove_all (filter->ssrcs_set); + + GST_OBJECT_UNLOCK (filter); +} + +/* Create sinkpad to receive RTP packets from encers + * and a srcpad for the RTP packets + */ +static GstPad * +create_rtp_sink (GstSrtpEnc * filter, const gchar * name) +{ + gchar *sinkpadname, *srcpadname; + struct GstSrtpEncPads *priv; + gint nb = 0; + + priv = g_slice_new0 (struct GstSrtpEncPads); + + GST_DEBUG_OBJECT (filter, "creating RTP sink pad"); + priv->sinkpad = gst_pad_new_from_static_template (&rtp_sink_template, name); + + sinkpadname = gst_pad_get_name (priv->sinkpad); + sscanf (sinkpadname, "rtp_sink_%d", &nb); + srcpadname = g_strdup_printf ("rtp_src_%d", nb); + + GST_DEBUG_OBJECT (filter, "creating RTP source pad"); + priv->srcpad = + gst_pad_new_from_static_template (&rtp_src_template, srcpadname); + g_free (srcpadname); + g_free (sinkpadname); + + gst_pad_set_element_private (priv->sinkpad, priv); + gst_pad_set_element_private (priv->srcpad, priv); + + gst_pad_set_query_function (priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_sink_query_rtp)); + gst_pad_set_iterate_internal_links_function (priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_iterate_internal_links_rtp)); + gst_pad_set_chain_function (priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_chain_rtp)); + gst_pad_set_event_function (priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_sink_event_rtp)); + gst_pad_set_active (priv->sinkpad, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (filter), priv->sinkpad); + + gst_pad_set_iterate_internal_links_function (priv->srcpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_iterate_internal_links_rtp)); + gst_pad_set_active (priv->srcpad, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (filter), priv->srcpad); + + return priv->sinkpad; +} + +/* Create sinkpad to receive RTCP packets from encers + * and a srcpad for the RTCP packets + */ +static GstPad * +create_rtcp_sink (GstSrtpEnc * filter, const gchar * name) +{ + gchar *sinkpadname, *srcpadname; + struct GstSrtpEncPads *priv; + gint nb = 0; + + priv = g_slice_new0 (struct GstSrtpEncPads); + + GST_DEBUG_OBJECT (filter, "creating RTCP sink pad"); + priv->sinkpad = gst_pad_new_from_static_template (&rtcp_sink_template, name); + + sinkpadname = gst_pad_get_name (priv->sinkpad); + sscanf (sinkpadname, "rtcp_sink_%d", &nb); + srcpadname = g_strdup_printf ("rtcp_src_%d", nb); + + GST_DEBUG_OBJECT (filter, "creating RTCP source pad"); + priv->srcpad = + gst_pad_new_from_static_template (&rtcp_src_template, srcpadname); + g_free (srcpadname); + g_free (sinkpadname); + + gst_pad_set_element_private (priv->sinkpad, priv); + gst_pad_set_element_private (priv->srcpad, priv); + + gst_pad_set_query_function (priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_sink_query_rtcp)); + gst_pad_set_iterate_internal_links_function (priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_iterate_internal_links_rtcp)); + gst_pad_set_chain_function (priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_chain_rtcp)); + gst_pad_set_event_function (priv->sinkpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_sink_event_rtcp)); + gst_pad_set_active (priv->sinkpad, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (filter), priv->sinkpad); + + gst_pad_set_iterate_internal_links_function (priv->srcpad, + GST_DEBUG_FUNCPTR (gst_srtp_enc_iterate_internal_links_rtcp)); + gst_pad_set_active (priv->srcpad, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (filter), priv->srcpad); + + return priv->sinkpad; +} + +/* Handling new pad request + */ +static GstPad * +gst_srtp_enc_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name, const GstCaps * caps) +{ + GstElementClass *klass; + GstSrtpEnc *filter; + + filter = GST_SRTP_ENC (element); + klass = GST_ELEMENT_GET_CLASS (element); + + GST_INFO_OBJECT (element, "New pad requested"); + + if (templ == gst_element_class_get_pad_template (klass, "rtp_sink_%d")) + return create_rtp_sink (filter, name); + + if (templ == gst_element_class_get_pad_template (klass, "rtcp_sink_%d")) + return create_rtcp_sink (filter, name); + + GST_ERROR_OBJECT (element, "Could not find specified template"); + return NULL; +} + +/* Dispose + */ +static void +gst_srtp_enc_dispose (GObject * object) +{ + GstSrtpEnc *filter = GST_SRTP_ENC (object); + GstIterator *it; + GValue val = { 0 }; + + GST_DEBUG_OBJECT (object, "Dispose..."); + + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (object)); + while (gst_iterator_next (it, &val) == GST_ITERATOR_OK) { + gst_srtp_enc_release_pad (GST_ELEMENT_CAST (object), + g_value_get_object (&val)); + g_value_unset (&val); + gst_iterator_resync (it); + } + gst_iterator_free (it); + + if (filter->key) + gst_buffer_unref (filter->key); + filter->key = NULL; + + if (filter->ssrcs_set) + g_hash_table_unref (filter->ssrcs_set); + filter->ssrcs_set = NULL; + + G_OBJECT_CLASS (gst_srtp_enc_parent_class)->dispose (object); +} + +static void +gst_srtp_enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstSrtpEnc *filter = GST_SRTP_ENC (object); + + GST_OBJECT_LOCK (filter); + + switch (prop_id) { + case PROP_MKEY: + if (filter->key) + gst_buffer_unref (filter->key); + filter->key = g_value_dup_boxed (value); + filter->key_changed = TRUE; + GST_INFO_OBJECT (object, "Set property: key=[%p]", filter->key); + break; + + case PROP_RTP_CIPHER: + filter->rtp_cipher = g_value_get_enum (value); + GST_INFO_OBJECT (object, "Set property: rtp cipher=%d", + filter->rtp_cipher); + break; + case PROP_RTP_AUTH: + filter->rtp_auth = g_value_get_enum (value); + GST_INFO_OBJECT (object, "Set property: rtp auth=%d", filter->rtp_auth); + break; + + case PROP_RTCP_CIPHER: + filter->rtcp_cipher = g_value_get_enum (value); + GST_INFO_OBJECT (object, "Set property: rtcp cipher=%d", + filter->rtcp_cipher); + break; + + case PROP_RTCP_AUTH: + filter->rtcp_auth = g_value_get_enum (value); + GST_INFO_OBJECT (object, "Set property: rtcp auth=%d", filter->rtcp_auth); + break; + + case PROP_RANDOM_KEY: + filter->random_key = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + GST_OBJECT_UNLOCK (filter); +} + +static void +gst_srtp_enc_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstSrtpEnc *filter = GST_SRTP_ENC (object); + GST_OBJECT_LOCK (filter); + + switch (prop_id) { + case PROP_MKEY: + if (filter->key) + g_value_set_boxed (value, filter->key); + break; + case PROP_RTP_CIPHER: + g_value_set_enum (value, filter->rtp_cipher); + break; + case PROP_RTCP_CIPHER: + g_value_set_enum (value, filter->rtcp_cipher); + break; + case PROP_RTP_AUTH: + g_value_set_enum (value, filter->rtp_auth); + break; + case PROP_RTCP_AUTH: + g_value_set_enum (value, filter->rtcp_auth); + break; + case PROP_RANDOM_KEY: + g_value_set_boolean (value, filter->random_key); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + GST_OBJECT_UNLOCK (filter); +} + +/* Returns the source pad linked with the sink pad + */ +static GstPad * +get_rtp_other_pad (GstPad * pad) +{ + struct GstSrtpEncPads *priv = gst_pad_get_element_private (pad); + + if (!priv) + return NULL; + + if (pad == priv->srcpad) + return priv->sinkpad; + else if (pad == priv->sinkpad) + return priv->srcpad; + else + g_assert_not_reached (); +} + +/* Release a sink pad and it's linked source pad + */ +static void +gst_srtp_enc_release_pad (GstElement * element, GstPad * sinkpad) +{ + struct GstSrtpEncPads *priv; + + GST_INFO_OBJECT (element, "Releasing pad %s:%s", + GST_DEBUG_PAD_NAME (sinkpad)); + + priv = gst_pad_get_element_private (sinkpad); + gst_pad_set_element_private (sinkpad, NULL); + + g_assert (priv); + + /* deactivate from source to sink */ + gst_pad_set_active (priv->srcpad, FALSE); + gst_pad_set_active (priv->sinkpad, FALSE); + + /* remove pads */ + gst_element_remove_pad (element, priv->srcpad); + gst_element_remove_pad (element, priv->sinkpad); + + g_slice_free (struct GstSrtpEncPads, priv); +} + +/* Common setcaps function + * Handles the link with other elements + */ +static gboolean +gst_srtp_enc_sink_setcaps (GstPad * pad, GstSrtpEnc * filter, + GstCaps * caps, gboolean is_rtcp) +{ + GstPad *otherpad = NULL; + GstStructure *ps = NULL; + gboolean ret = FALSE; + struct GstSrtpEncPads *priv = gst_pad_get_element_private (pad); + + g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); + + caps = gst_caps_copy (caps); + + ps = gst_caps_get_structure (caps, 0); + + GST_DEBUG_OBJECT (pad, "Sink caps: %" GST_PTR_FORMAT, caps); + + if (is_rtcp) + gst_structure_set_name (ps, "application/x-srtcp"); + else + gst_structure_set_name (ps, "application/x-srtp"); + + GST_OBJECT_LOCK (filter); + + /* Add srtp-specific params to source caps */ + gst_structure_set (ps, "srtp-key", GST_TYPE_BUFFER, filter->key, + "srtp-cipher", G_TYPE_STRING, + enum_nick_from_value (GST_TYPE_SRTP_CIPHER_TYPE, filter->rtp_cipher), + "srtp-auth", G_TYPE_STRING, + enum_nick_from_value (GST_TYPE_SRTP_AUTH_TYPE, filter->rtp_auth), + "srtcp-cipher", G_TYPE_STRING, + enum_nick_from_value (GST_TYPE_SRTP_CIPHER_TYPE, filter->rtcp_cipher), + "srtcp-auth", G_TYPE_STRING, + enum_nick_from_value (GST_TYPE_SRTP_AUTH_TYPE, filter->rtcp_auth), NULL); + + GST_OBJECT_UNLOCK (filter); + + GST_DEBUG_OBJECT (pad, "Source caps: %" GST_PTR_FORMAT, caps); + + /* Set caps on source pad */ + otherpad = priv->srcpad; + + ret = gst_pad_set_caps (otherpad, caps); + + gst_caps_unref (caps); + + return ret; +} + +static gboolean +gst_srtp_enc_sink_query (GstPad * pad, GstObject * parent, GstQuery * query, + gboolean is_rtcp) +{ + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS: + { + GstCaps *filter = NULL; + GstCaps *other_filter = NULL; + GstPad *otherpad; + GstCaps *other_caps; + GstCaps *ret; + GstCaps *template_caps; + int i; + + otherpad = get_rtp_other_pad (pad); + + gst_query_parse_caps (query, &filter); + if (filter) { + other_filter = gst_caps_copy (filter); + + for (i = 0; i < gst_caps_get_size (other_filter); i++) { + GstStructure *ps = gst_caps_get_structure (other_filter, i); + if (is_rtcp) + gst_structure_set_name (ps, "application/x-srtcp"); + else + gst_structure_set_name (ps, "application/x-srtp"); + } + } + + other_caps = gst_pad_peer_query_caps (otherpad, other_filter); + + if (other_filter) + gst_caps_unref (other_filter); + + if (!other_caps) + goto return_template; + + template_caps = gst_pad_get_pad_template_caps (otherpad); + ret = gst_caps_intersect_full (other_caps, template_caps, + GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (other_caps); + gst_caps_unref (template_caps); + + ret = gst_caps_make_writable (ret); + + for (i = 0; i < gst_caps_get_size (ret); i++) { + GstStructure *ps = gst_caps_get_structure (ret, i); + if (is_rtcp) + gst_structure_set_name (ps, "application/x-rtcp"); + else + gst_structure_set_name (ps, "application/x-rtp"); + gst_structure_remove_fields (ps, "srtp-key", "srtp-cipher", "srtp-auth", + "srtcp-cipher", "srtcp-auth", NULL); + } + + gst_query_set_caps_result (query, ret); + return TRUE; + return_template: + + ret = gst_pad_get_pad_template_caps (pad); + gst_query_set_caps_result (query, ret); + gst_caps_unref (ret); + + return TRUE; + } + default: + return gst_pad_query_default (pad, parent, query); + } +} + +static gboolean +gst_srtp_enc_sink_query_rtp (GstPad * pad, GstObject * parent, GstQuery * query) +{ + return gst_srtp_enc_sink_query (pad, parent, query, FALSE); +} + +static gboolean +gst_srtp_enc_sink_query_rtcp (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + return gst_srtp_enc_sink_query (pad, parent, query, TRUE); +} + +static GstIterator * +gst_srtp_enc_iterate_internal_links (GstPad * pad, GstObject * parent, + gboolean is_rtcp) +{ + GstSrtpEnc *filter = GST_SRTP_ENC (parent); + GstPad *otherpad = NULL; + GstIterator *it = NULL; + + otherpad = get_rtp_other_pad (pad); + + if (otherpad) { + GValue val = { 0 }; + + g_value_init (&val, GST_TYPE_PAD); + g_value_set_object (&val, otherpad); + it = gst_iterator_new_single (GST_TYPE_PAD, &val); + g_value_unset (&val); + } else { + GST_ELEMENT_ERROR (GST_ELEMENT_CAST (filter), CORE, PAD, (NULL), + ("Unable to get linked pad")); + } + + return it; +} + +static GstIterator * +gst_srtp_enc_iterate_internal_links_rtp (GstPad * pad, GstObject * parent) +{ + return gst_srtp_enc_iterate_internal_links (pad, parent, FALSE); +} + +static GstIterator * +gst_srtp_enc_iterate_internal_links_rtcp (GstPad * pad, GstObject * parent) +{ + return gst_srtp_enc_iterate_internal_links (pad, parent, TRUE); +} + + +static void +gst_srtp_enc_replace_random_key (GstSrtpEnc * filter) +{ + guint i; + GstMapInfo map; + + GST_DEBUG_OBJECT (filter, "Generating random key"); + + if (filter->key) + gst_buffer_unref (filter->key); + + filter->key = gst_buffer_new_allocate (NULL, 16 + 14, NULL); + + gst_buffer_map (filter->key, &map, GST_MAP_WRITE); + for (i = 0; i < map.size; i += 4) + GST_WRITE_UINT32_BE (map.data + i, g_random_int ()); + gst_buffer_unmap (filter->key, &map); + + filter->key_changed = TRUE; +} + +static GstFlowReturn +gst_srtp_enc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf, + gboolean is_rtcp) +{ + GstSrtpEnc *filter = GST_SRTP_ENC (parent); + GstFlowReturn ret = GST_FLOW_OK; + GstPad *otherpad = NULL; + err_status_t err = err_status_ok; + gint size_max, size; + GstBuffer *bufout = NULL; + struct GstSrtpEncPads *priv = gst_pad_get_element_private (pad); + guint32 ssrc; + gboolean do_setcaps = FALSE; + GstMapInfo mapin, mapout; + + if (!priv) + goto fail; + + if (!is_rtcp) { + GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT; + + if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuf)) { + GST_ELEMENT_ERROR (filter, STREAM, WRONG_TYPE, (NULL), + ("Could not map RTP buffer")); + ret = GST_FLOW_ERROR; + goto out; + } + + ssrc = gst_rtp_buffer_get_ssrc (&rtpbuf); + + gst_rtp_buffer_unmap (&rtpbuf); + } else if (!rtcp_buffer_get_ssrc (buf, &ssrc)) { + GST_ELEMENT_ERROR (filter, STREAM, WRONG_TYPE, (NULL), + ("No SSRC found in buffer, dropping")); + ret = GST_FLOW_ERROR; + goto out; + } + + do_setcaps = filter->key_changed; + if (filter->key_changed) + gst_srtp_enc_reset (filter); + + if (!check_new_stream_locked (filter, ssrc)) { + GST_ELEMENT_ERROR (filter, LIBRARY, INIT, + ("Could not initialize SRTP encoder"), + ("Failed to add stream to SRTP encoder")); + ret = GST_FLOW_ERROR; + goto out; + } + priv->ssrc = ssrc; + + GST_OBJECT_LOCK (filter); + + size_max = gst_buffer_get_size (buf) + SRTP_MAX_TRAILER_LEN + 10; + + /* Update source caps if asked */ + if (do_setcaps) { + GstCaps *caps; + GST_OBJECT_UNLOCK (filter); + + caps = gst_pad_get_current_caps (pad); + if (!gst_srtp_enc_sink_setcaps (pad, filter, caps, is_rtcp)) { + gst_caps_unref (caps); + ret = GST_FLOW_NOT_NEGOTIATED; + goto out; + } + gst_caps_unref (caps); + + GST_OBJECT_LOCK (filter); + } + + /* Create a bigger buffer to add protection */ + bufout = gst_buffer_new_allocate (NULL, size_max, NULL); + + gst_buffer_map (buf, &mapin, GST_MAP_READ); + gst_buffer_map (bufout, &mapout, GST_MAP_READWRITE); + + size = mapin.size; + memcpy (mapout.data, mapin.data, mapin.size); + + gst_buffer_unmap (buf, &mapin); + + gst_srtp_init_event_reporter (); + + if (is_rtcp) + err = srtp_protect_rtcp (filter->session, mapout.data, &size); + else + err = srtp_protect (filter->session, mapout.data, &size); + + gst_buffer_unmap (bufout, &mapout); + + GST_OBJECT_UNLOCK (filter); + + if (err == err_status_ok) { + /* Buffer protected */ + gst_buffer_set_size (bufout, size); + gst_buffer_copy_into (bufout, buf, GST_BUFFER_COPY_METADATA, 0, -1); + + GST_LOG_OBJECT (pad, "Encing %s buffer of size %d", + is_rtcp ? "RTCP" : "RTP", size); + + /* Push buffer to source pad */ + otherpad = get_rtp_other_pad (pad); + ret = gst_pad_push (otherpad, bufout); + bufout = NULL; + + if (ret != GST_FLOW_OK) + goto out; + + } else if (err == err_status_key_expired) { + + GST_ELEMENT_ERROR (GST_ELEMENT_CAST (filter), STREAM, ENCODE, + ("Key usage limit has been reached"), + ("Unable to protect buffer (hard key usage limit reached)")); + gst_buffer_unref (bufout); + goto fail; + + } else { + /* srtp_protect failed */ + GST_ELEMENT_ERROR (filter, LIBRARY, FAILED, (NULL), + ("Unable to protect buffer (protect failed) code %d", err)); + gst_buffer_unref (bufout); + goto fail; + } + + if (gst_srtp_get_soft_limit_reached ()) { + g_signal_emit (filter, gst_srtp_enc_signals[SIGNAL_SOFT_LIMIT], 0, ssrc); + if (filter->random_key && !filter->key_changed) + gst_srtp_enc_replace_random_key (filter); + } + +out: + + gst_buffer_unref (buf); + + return ret; + +fail: + ret = GST_FLOW_ERROR; + goto out; +} + +static GstFlowReturn +gst_srtp_enc_chain_rtp (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + return gst_srtp_enc_chain (pad, parent, buf, FALSE); +} + +static GstFlowReturn +gst_srtp_enc_chain_rtcp (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + return gst_srtp_enc_chain (pad, parent, buf, TRUE); +} + + +/* Change state + */ +static GstStateChangeReturn +gst_srtp_enc_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn res; + GstSrtpEnc *filter; + + filter = GST_SRTP_ENC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + if (!filter->key) { + if (filter->random_key) { + gst_srtp_enc_replace_random_key (filter); + } else { + GST_ERROR_OBJECT (element, "Need a key to get to READY"); + return GST_STATE_CHANGE_FAILURE; + } + } + if ((filter->rtcp_cipher != NULL_CIPHER) + && (filter->rtcp_auth == NULL_AUTH)) { + GST_ERROR_OBJECT (filter, + "RTCP authentication can't be NULL if encryption is not NULL."); + return GST_STATE_CHANGE_FAILURE; + } + if (!filter->first_session) + gst_srtp_enc_reset (filter); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + res = GST_ELEMENT_CLASS (gst_srtp_enc_parent_class)->change_state (element, + transition); + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_srtp_enc_reset (filter); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return res; +} + +static gboolean +gst_srtp_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event, + gboolean is_rtcp) +{ + GstSrtpEnc *filter = GST_SRTP_ENC (parent); + gboolean ret; + GstPad *otherpad; + + otherpad = get_rtp_other_pad (pad); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + GST_DEBUG_OBJECT (pad, "Encing event Flush stop (%d)", + GST_EVENT_TYPE (event)); + gst_srtp_enc_reset (filter); + ret = gst_pad_push_event (otherpad, event); + break; + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + ret = gst_srtp_enc_sink_setcaps (pad, filter, caps, is_rtcp); + break; + } + default: + GST_DEBUG_OBJECT (pad, "Encing event default (%d)", + GST_EVENT_TYPE (event)); + ret = gst_pad_event_default (pad, parent, event); + break; + } + + return ret; +} + +static gboolean +gst_srtp_enc_sink_event_rtp (GstPad * pad, GstObject * parent, GstEvent * event) +{ + return gst_srtp_enc_sink_event (pad, parent, event, FALSE); +} + +static gboolean +gst_srtp_enc_sink_event_rtcp (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + return gst_srtp_enc_sink_event (pad, parent, event, TRUE); +} + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and other features + */ +gboolean +gst_srtp_enc_plugin_init (GstPlugin * srtpenc) +{ + GST_DEBUG_CATEGORY_INIT (gst_srtp_enc_debug, "srtpenc", 0, "SRTP Enc"); + + return gst_element_register (srtpenc, "srtpenc", GST_RANK_NONE, + GST_TYPE_SRTP_ENC); +} diff --git a/ext/srtp/gstsrtpenc.h b/ext/srtp/gstsrtpenc.h new file mode 100644 index 0000000000..20653cadc0 --- /dev/null +++ b/ext/srtp/gstsrtpenc.h @@ -0,0 +1,117 @@ +/* + * GStreamer - GStreamer SRTP encoder + * + * Copyright 2009 Collabora Ltd. + * @author: Gabriel Millaire + * @author: Olivier Crete + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_SRTPENC_H__ +#define __GST_SRTPENC_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_SRTP_ENC \ + (gst_srtp_enc_get_type()) +#define GST_SRTP_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SRTP_ENC,GstSrtpEnc)) +#define GST_SRTP_ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SRTP_ENC,GstSrtpEncClass)) +#define GST_IS_SRTP_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SRTP_ENC)) +#define GST_IS_SRTP_ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SRTP_ENC)) + +typedef struct _GstSrtpEnc GstSrtpEnc; +typedef struct _GstSrtpEncClass GstSrtpEncClass; + +typedef enum +{ + GST_SRTP_CIPHER_NULL, + GST_SRTP_CIPHER_AES_128_ICM +} GstSrtpCipherType; + +typedef enum +{ + GST_SRTP_AUTH_NULL, + GST_SRTP_AUTH_HMAC_SHA1_32, + GST_SRTP_AUTH_HMAC_SHA1_80 +} GstSrtpAuthType; + +struct _GstSrtpEnc +{ + GstElement element; + + gboolean random_key; + + GstBuffer *key; + guint rtp_cipher; + guint rtp_auth; + guint rtcp_cipher; + guint rtcp_auth; + + srtp_t session; + gboolean first_session; + gboolean key_changed; + + gboolean hard_limit_reached; + gboolean soft_limit_reached; + + GHashTable *ssrcs_set; + + GType key_type; +}; + +struct _GstSrtpEncClass +{ + GstElementClass parent_class; +}; + +GType gst_srtp_enc_get_type (void); + +gboolean gst_srtp_enc_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_SRTPENC_H__ */