Commit 221583f6 authored by johan's avatar johan

Move srtp from ortp to mediastreamer2

parent 92d3730c
......@@ -59,6 +59,7 @@ option(ENABLE_STATIC "Build static library (default is shared library)." NO)
option(ENABLE_DEBUG_LOGS "Enable the display of traces showing the execution of the library." NO)
option(ENABLE_FIXED_POINT "Turn on fixed point computations." ${ENABLE_FIXED_POINT_DEFAULT_VALUE})
option(ENABLE_RELATIVE_PREFIX "Find resources relatively to the installation directory." NO)
option(ENABLE_SRTP "Build with the SRTP transport support." YES)
option(ENABLE_ZRTP "Build with ZRTP support." YES)
option(ENABLE_SOUND "Can be used to turn off all possible sound backends." YES)
......@@ -89,6 +90,14 @@ cmake_dependent_option(ENABLE_VPX "Build mediastreamer2 with VPX codec." YES "EN
list(APPEND CMAKE_MODULE_PATH ${CMAKE_PREFIX_PATH}/share/cmake/Modules)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
if(ENABLE_SRTP)
find_package(SRTP)
if(NOT SRTP_FOUND)
message(WARNING "Could not find SRTP library, Mediastreamer2 will be compiled without SRTP support.")
set(ENABLE_SRTP OFF CACHE BOOL "Build with the SRTP transport support." FORCE)
endif()
endif()
include(CheckIncludeFile)
include(CheckLibraryExists)
......@@ -244,6 +253,10 @@ include_directories(
if(ENABLE_VIDEO)
include_directories(src/voip)
endif()
if(SRTP_FOUND)
include_directories(${SRTP_INCLUDE_DIRS})
endif()
if(BZRTP_FOUND)
include_directories(${BZRTP_INCLUDE_DIRS})
endif()
......@@ -300,6 +313,9 @@ endif()
if(ENABLE_FIXED_POINT)
set(MS_FIXED_POINT 1)
endif()
if(SRTP_FOUND)
set(HAVE_SRTP 1)
endif()
if(ENABLE_ALSA)
set(__ALSA_ENABLED__ 1)
endif()
......
......@@ -1005,6 +1005,66 @@ case "$target_os" in
;;
esac
dnl check for libsrtp support (secure rtp)
AC_ARG_WITH(srtp,
AC_HELP_STRING([--with-srtp], [Set prefix where libsrtp can be found or "none" to disable (ex:/usr or /usr/local) [[default=/usr]]]),
[srtp_prefix=$withval],
[srtp_prefix=/usr]
)
if test "${srtp_prefix}" != "none" ; then
if test "${srtp_prefix}" != "/usr" || test "$mingw_found" = "yes" ; then
SRTP_CFLAGS="-I${srtp_prefix}/include -I${srtp_prefix}/include/srtp"
SRTP_LIBS="-L${srtp_prefix}/lib"
fi
SRTP_LIBS="$SRTP_LIBS -lsrtp"
dnl check srtp headers
have_srtp=no
CPPFLAGS_save=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $SRTP_CFLAGS"
AC_CHECK_HEADER([srtp/srtp.h],have_srtp_headers=yes)
CPPFLAGS=$CPPFLAGS_save
dnl check for srtp lib
LDFLAGS_save=$LDFLAGS
LDFLAGS="$LDFLAGS $SRTP_LIBS"
LIBS_save=$LIBS
AC_CHECK_LIB(srtp,srtp_init,have_srtp_lib=yes)
AC_CHECK_LIB(srtp,srtp_shutdown,[AC_DEFINE([HAVE_SRTP_SHUTDOWN],1,[Defined when srtp_shutdown() exists.])])
LDFLAGS=$LDFLAGS_save
LIBS=$LIBS_save
if test "$have_srtp_headers$have_srtp_lib" = "yesyes" ; then
have_srtp=yes
if test "$test_for_broken_srtp" = yes; then
LDFLAGS_save=$LDFLAGS
LDFLAGS="$LDFLAGS $SRTP_LIBS"
LIBS_save=$LIBS
AC_CHECK_LIB(srtp,sha1_update,[
AC_MSG_ERROR([This libsrtp version exports symbols conflicting with polar ssl, resulting in a bad execution path.
Please use the one from git://git.linphone.org/srtp.git
You could also track resolution of defect on https://github.com/cisco/libsrtp/issues/28
If you are not linking against polar ssl, you may prefer to skip this test with --enable-broken-srtp
])], have_srtp=yes)
LDFLAGS=$LDFLAGS_save
LIBS=$LIBS_save
fi
AC_DEFINE(HAVE_SRTP, 1, [Defined when srtp support is compiled])
else
AC_MSG_NOTICE([Could not find libsrtp headers or lib, cryto transport disabled.])
SRTP_CFLAGS=
SRTP_LIBS=
fi
AC_SUBST(SRTP_CFLAGS)
AC_SUBST(SRTP_LIBS)
else
have_srtp=no
SRTP_CFLAGS=
SRTP_LIBS=
fi
dnl check for libzrtp support (RFC 6189: Media Path Key Agreement for Unicast Secure RTP)
AC_ARG_ENABLE(zrtp,
[AS_HELP_STRING([--enable-zrtp], [Turn on or off compilation of zrtp (default=no)])],
......
......@@ -35,6 +35,7 @@ typedef struct srtp_ctx_t *MSSrtpCtx;
#include <mediastreamer2/qualityindicator.h>
#include <mediastreamer2/ice.h>
#include <mediastreamer2/zrtp.h>
#include <mediastreamer2/ms_srtp.h>
#define PAYLOAD_TYPE_FLAG_CAN_RECV PAYLOAD_TYPE_USER_FLAG_1
......@@ -157,9 +158,6 @@ struct _MediaStream {
* @addtogroup audio_stream_api
* @{
**/
MS2_PUBLIC bool_t media_stream_srtp_supported(void);
MS2_PUBLIC void media_stream_set_rtcp_information(MediaStream *stream, const char *cname, const char *tool);
MS2_PUBLIC void media_stream_get_local_rtp_stats(MediaStream *stream, rtp_stats_t *stats);
......@@ -177,9 +175,6 @@ MS2_PUBLIC void media_stream_enable_adaptive_jittcomp(MediaStream *stream, bool_
**/
MS2_PUBLIC bool_t media_stream_enable_srtp(MediaStream* stream, MSCryptoSuite suite, const char* snd_key, const char* rcv_key);
MS2_PUBLIC int media_stream_set_srtp_recv_key(MediaStream *stream, MSCryptoSuite suite, const char* key, bool_t keyisb64);
MS2_PUBLIC int media_stream_set_srtp_send_key(MediaStream *stream, MSCryptoSuite suite, const char* key, bool_t keyisb64);
/**
* @param[in] stream MediaStream object
* @return true if stream is encrypted
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006-2014 Belledonne Communications, Grenoble
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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 ms_srtp_h
#define ms_srtp_h
#if defined(HAVE_SRTP)
#if defined(ANDROID) || !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
// Android and Windows phone don't use make install
# include <srtp.h>
#else
# include <srtp/srtp.h>
#endif /* ANDROID */
#else /* HAVE_SRTP */
typedef void* srtp_t;
typedef int err_status_t;
typedef struct srtp_policy srtp_policy_t;
#endif /* HAVE_SRTP */
/*srtp defines all this stuff*/
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#include <ortp/rtpsession.h>
#include "mediastreamer2/mscommon.h"
#ifdef __cplusplus
extern "C"{
#endif
/* defined in mediastream.h */
struct _MediaStream;
enum _MSCryptoSuite;
/**
* Check if SRTP is supported
* @return true if SRTP is supported
*/
MS2_PUBLIC bool_t ms_srtp_supported(void);
/**
* Set srtp receiver key for the given media stream.
* If no srtp session exists on the stream it is created, if it already exists srtp policy is created/modified for the receiver side of the stream.
*
* @param[in/out] stream The mediastream to operate on
* @param[in] suite The srtp crypto suite to use
* @param[in] key Srtp master key and master salt in a base 64 NULL terminated string
* @return 0 on success, error code otherwise
*/
MS2_PUBLIC int media_stream_set_srtp_recv_key_b64(struct _MediaStream *stream, enum _MSCryptoSuite suite, const char* key);
/**
* Set srtp receiver key for the given media stream.
* If no srtp session exists on the stream it is created, if it already exists srtp policy is created/modified for the receiver side of the stream.
*
* @param[in/out] stream The mediastream to operate on
* @param[in] suite The srtp crypto suite to use
* @param[in] key Srtp master key and master salt
* @param[in] key_length key buffer length
* @return 0 on success, error code otherwise
*/
MS2_PUBLIC int media_stream_set_srtp_recv_key(struct _MediaStream *stream, enum _MSCryptoSuite suite, const char* key, size_t key_length);
/**
* Set srtp sender key for the given media stream.
* If no srtp session exists on the stream it is created, if it already exists srtp policy is created/modified for the sender side of the stream.
*
* @param[in/out] stream The mediastream to operate on
* @param[in] suite The srtp crypto suite to use
* @param[in] key Srtp master key and master salt in a base 64 NULL terminated string
* @return 0 on success, error code otherwise
*/
MS2_PUBLIC int media_stream_set_srtp_send_key_b64(struct _MediaStream *stream, enum _MSCryptoSuite suite, const char* key);
/**
* Set srtp sender key for the given media stream.
* If no srtp session exists on the stream it is created, if it already exists srtp policy is created/modified for the sender side of the stream.
*
* @param[in/out] stream The mediastream to operate on
* @param[in] suite The srtp crypto suite to use
* @param[in] key Srtp master key and master salt
* @param[in] key_length key buffer length
* @return 0 on success, error code otherwise
*/
MS2_PUBLIC int media_stream_set_srtp_send_key(struct _MediaStream *stream, enum _MSCryptoSuite suite, const char* key, size_t key_length);
/**
* Deallocate ressources for a srtp session
*
* @param[in/out] session SRTP session to be terminated
* @return 0 on success
*/
MS2_PUBLIC err_status_t ms_srtp_dealloc(srtp_t session);
#ifdef __cplusplus
}
#endif
#endif /* ms_srtp_h */
......@@ -25,7 +25,6 @@ if(MSVC)
find_library(LIBMINGWEX NAMES mingwex)
endif()
find_program(AWK_PROGRAM awk)
if(NOT AWK_PROGRAM)
message(FATAL_ERROR "You need the awk program to build mediastreamer2.")
......@@ -153,6 +152,7 @@ set(VOIP_SOURCE_FILES
voip/qualityindicator.c
voip/ringstream.c
voip/zrtp.c
crypto/ms_srtp.c
)
if(ENABLE_ALSA)
......@@ -339,6 +339,9 @@ endif()
if(BZRTP_FOUND)
list(APPEND VOIP_LIBS ${BZRTP_LIBRARIES})
endif()
if(SRTP_FOUND)
list(APPEND VOIP_LIBS ${SRTP_LIBRARIES})
endif()
if(GSM_FOUND)
list(APPEND VOIP_LIBS ${GSM_LIBRARIES})
endif()
......
......@@ -51,7 +51,8 @@ libmediastreamer_voip_la_LIBADD= libmediastreamer_base.la \
$(LIBV4LCONVERT_LIBS) \
$(SPANDSP_LIBS) \
$(PCAP_LIBS) \
$(OPUS_LIBS)
$(OPUS_LIBS) \
$(SRTP_LIBS)
if LIBBZRTP
......@@ -98,7 +99,8 @@ libmediastreamer_voip_la_SOURCES+= voip/private.h \
voip/bitratedriver.c \
voip/qosanalyzer.c voip/qosanalyzer.h \
voip/bitratecontrol.c \
voip/zrtp.c
voip/zrtp.c \
crypto/ms_srtp.c
else
libmediastreamer_base_la_SOURCES+= ortp-deps/logging.c \
ortp-deps/port.c \
......@@ -384,7 +386,8 @@ AM_CFLAGS=\
$(SPANDSP_CFLAGS) \
$(MSSILK_CFLAGS) \
$(PCAP_CFLAGS) \
$(OPUS_CFLAGS)
$(OPUS_CFLAGS) \
$(SRTP_FLAGS)
AM_CXXFLAGS=\
$(STRICT_OPTIONS) \
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2006-2014 Belledonne Communications, Grenoble
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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 "mediastreamer-config.h"
#endif
#include "ortp/ortp.h"
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#include "mediastreamer2/ms_srtp.h"
#include "mediastreamer2/mediastream.h"
#ifdef HAVE_SRTP
#if defined(ANDROID) || !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
// Android and Windows phone don't use make install
#include <srtp_priv.h>
#else
#include <srtp/srtp_priv.h>
#endif
#include "ortp/b64.h"
#define SRTP_PAD_BYTES (SRTP_MAX_TRAILER_LEN + 4)
/***********************************************/
/***** LOCAL FUNCTIONS *****/
/***********************************************/
static srtp_stream_ctx_t * find_other_ssrc(srtp_t srtp, uint32_t ssrc){
srtp_stream_ctx_t *stream;
for (stream=srtp->stream_list;stream!=NULL;stream=stream->next){
if (stream->ssrc!=ssrc) return stream;
}
return stream;
}
/**** Sender functions ****/
static int _process_on_send(RtpSession* session,srtp_t srtp,mblk_t *m,bool_t is_rtp){
int slen;
err_status_t err;
rtp_header_t *rtp_header=is_rtp?(rtp_header_t*)m->b_rptr:NULL;
rtcp_common_header_t *rtcp_header=!is_rtp?(rtcp_common_header_t*)m->b_rptr:NULL;
slen=msgdsize(m);
if (rtp_header && (slen>RTP_FIXED_HEADER_SIZE && rtp_header->version==2)) {
/* defragment incoming message and enlarge the buffer for srtp to write its data */
msgpullup(m,slen+SRTP_PAD_BYTES);
err=srtp_protect(srtp,m->b_rptr,&slen);
} else if (rtcp_header && (slen>RTP_FIXED_HEADER_SIZE && rtcp_header->version==2)) {
/* defragment incoming message and enlarge the buffer for srtp to write its data */
msgpullup(m,slen+SRTP_PAD_BYTES);
err=srtp_protect_rtcp(srtp,m->b_rptr,&slen);
} else {
/*ignoring non rtp/rtcp packets*/
return slen;
}
/* check return code from srtp_protect */
if (err==err_status_ok){
return slen;
}
ortp_error("srtp_protect%s() failed (%d)", is_rtp?"":"_rtcp", err);
return -1;
}
static int ms_srtp_process_on_send(RtpTransportModifier *t, mblk_t *m){
return _process_on_send(t->session,(srtp_t)t->data, m,TRUE);
}
static int ms_srtcp_process_on_send(RtpTransportModifier *t, mblk_t *m){
return _process_on_send(t->session,(srtp_t)t->data, m,FALSE);
}
/**** Receiver functions ****/
/*
* The ssrc_any_inbound feature of the libsrtp is not working good.
* It cannot be changed dynamically nor removed.
* As a result we prefer not to use it, but instead the recv stream is configured with a dummy SSRC value.
* When the first packet arrives, or when the SSRC changes, then we change the ssrc value inside the srtp stream context,
* so that the stream that was configured with the dummy SSRC value becomes now fully valid.
*/
static void update_recv_stream(RtpSession *session, srtp_t srtp, uint32_t new_ssrc){
uint32_t send_ssrc=rtp_session_get_send_ssrc(session);
srtp_stream_ctx_t *recvstream=find_other_ssrc(srtp,htonl(send_ssrc));
if (recvstream){
recvstream->ssrc=new_ssrc;
}
}
static int _process_on_receive(RtpSession* session,srtp_t srtp,mblk_t *m,bool_t is_rtp, int err){
int slen;
uint32_t new_ssrc;
err_status_t srtp_err;
/* keep NON-RTP data unencrypted */
if (is_rtp){
rtp_header_t *rtp=(rtp_header_t*)m->b_rptr;
if (err<RTP_FIXED_HEADER_SIZE || rtp->version!=2 )
return err;
new_ssrc=rtp->ssrc;
}else{
rtcp_common_header_t *rtcp=(rtcp_common_header_t*)m->b_rptr;
if (err<(sizeof(rtcp_common_header_t)+4) || rtcp->version!=2 )
return err;
new_ssrc=*(uint32_t*)(m->b_rptr+sizeof(rtcp_common_header_t));
}
slen=err;
srtp_err = is_rtp?srtp_unprotect(srtp,m->b_rptr,&slen):srtp_unprotect_rtcp(srtp,m->b_rptr,&slen);
if (srtp_err==err_status_no_ctx) {
update_recv_stream(session,srtp,new_ssrc);
slen=err;
srtp_err = is_rtp?srtp_unprotect(srtp,m->b_rptr,&slen):srtp_unprotect_rtcp(srtp,m->b_rptr,&slen);
}
if (srtp_err==err_status_ok) {
return slen;
} else {
ms_error("srtp_unprotect%s() failed (%d)", is_rtp?"":"_rtcp", srtp_err);
return -1;
}
}
static int ms_srtp_process_on_receive(RtpTransportModifier *t, mblk_t *m){
return _process_on_receive(t->session,(srtp_t)t->data, m,TRUE,msgdsize(m));
}
static int ms_srtcp_process_on_receive(RtpTransportModifier *t, mblk_t *m){
return _process_on_receive(t->session,(srtp_t)t->data, m,FALSE,msgdsize(m));
}
/**** Session management functions ****/
/**
* deallocate transport modifier ressources
* @param[in/out] tp The transport modifier to be deallocated
*/
static void ms_srtp_transport_modifier_destroy(RtpTransportModifier *tp){
ms_free(tp);
}
/**
* create a new transport modifier for srtp
* @param[in] srtp the srtp session to be used by the modifier
*/
static int ms_srtp_transport_modifier_new(srtp_t srtp, RtpTransportModifier **rtpt, RtpTransportModifier **rtcpt ){
if (rtpt) {
(*rtpt)=ms_new0(RtpTransportModifier,1);
(*rtpt)->data=srtp;
(*rtpt)->t_process_on_send=ms_srtp_process_on_send;
(*rtpt)->t_process_on_receive=ms_srtp_process_on_receive;
(*rtpt)->t_destroy=ms_srtp_transport_modifier_destroy;
}
if (rtcpt) {
(*rtcpt)=ms_new0(RtpTransportModifier,1);
(*rtcpt)->data=srtp;
(*rtcpt)->t_process_on_send=ms_srtcp_process_on_send;
(*rtcpt)->t_process_on_receive=ms_srtcp_process_on_receive;
(*rtcpt)->t_destroy=ms_srtp_transport_modifier_destroy;
}
return 0;
}
static int ms_check_srtp_session_created(struct _MediaStream *stream){
if (stream->sessions.srtp_session==NULL){
err_status_t err;
srtp_t session;
RtpTransport *rtp=NULL,*rtcp=NULL;
RtpTransportModifier *rtp_modifier, *rtcp_modifier;
err = srtp_create(&session, NULL);
if (err != 0) {
ms_error("Failed to create srtp session (%d)", err);
return -1;
}
stream->sessions.srtp_session=session;
ms_srtp_transport_modifier_new(session,&rtp_modifier,&rtcp_modifier);
rtp_session_get_transports(stream->sessions.rtp_session,&rtp,&rtcp);
/*if transports are set, we assume they are meta transporters, otherwise create them*/
if (rtp==NULL&&rtcp==NULL){
meta_rtp_transport_new(&rtp, TRUE, NULL, 0);
meta_rtp_transport_new(&rtcp, FALSE, NULL, 0);
}
meta_rtp_transport_append_modifier(rtp, rtp_modifier);
meta_rtp_transport_append_modifier(rtcp, rtcp_modifier);
rtp_session_set_transports(stream->sessions.rtp_session,rtp,rtcp);
stream->sessions.is_secured=TRUE;
}
return 0;
}
static int ms_add_srtp_stream(srtp_t srtp, MSCryptoSuite suite, uint32_t ssrc, const char* key, size_t key_length, bool_t inbound)
{
srtp_policy_t policy;
err_status_t err;
ssrc_t ssrc_conf;
memset(&policy,0,sizeof(policy));
switch(suite){
case MS_AES_128_SHA1_32:
crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp);
// srtp doc says: not adapted to rtcp...
crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtcp);
break;
case MS_AES_128_NO_AUTH:
crypto_policy_set_aes_cm_128_null_auth(&policy.rtp);
// srtp doc says: not adapted to rtcp...
crypto_policy_set_aes_cm_128_null_auth(&policy.rtcp);
break;
case MS_NO_CIPHER_SHA1_80:
crypto_policy_set_null_cipher_hmac_sha1_80(&policy.rtp);
crypto_policy_set_null_cipher_hmac_sha1_80(&policy.rtcp);
break;
case MS_AES_128_SHA1_80: /*default mode*/
crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
break;
case MS_AES_256_SHA1_80:
crypto_policy_set_aes_cm_256_hmac_sha1_80(&policy.rtp);
crypto_policy_set_aes_cm_256_hmac_sha1_80(&policy.rtcp);
break;
case MS_AES_256_SHA1_32:
crypto_policy_set_aes_cm_256_hmac_sha1_32(&policy.rtp);
crypto_policy_set_aes_cm_256_hmac_sha1_32(&policy.rtcp);
break;
case MS_CRYPTO_SUITE_INVALID:
return -1;
break;
}
/* check if key length match given policy */
if (key_length != policy.rtp.cipher_key_len) {
ms_error("Key size (%ld) doesn't match the selected srtp profile (required %d)", key_length, policy.rtp.cipher_key_len);
return -1;
}
if (!inbound)
policy.allow_repeat_tx=1; /*necessary for telephone-events*/
/*ssrc_conf.type=inbound ? ssrc_any_inbound : ssrc_specific;*/
ssrc_conf.type=ssrc_specific;
ssrc_conf.value=ssrc;
policy.ssrc = ssrc_conf;
policy.key = (uint8_t *)key;
policy.next = NULL;
err = srtp_add_stream(srtp, &policy);
if (err != err_status_ok) {
ms_error("Failed to add stream to srtp session (%d)", err);
return -1;
}
return 0;
}
/***********************************************/
/***** EXPORTED FUNCTIONS *****/
/***********************************************/
/**** Private to mediastreamer2 functions ****/
/* header declared in voip/private.h */
static int srtp_init_done=0;
err_status_t ms_srtp_init(void)
{
err_status_t st=0;
ms_message("srtp init");
if (!srtp_init_done) {
st=srtp_init();
if (st==0) {
srtp_init_done++;
}else{
ms_fatal("Couldn't initialize SRTP library.");
err_reporting_init("mediastreamer2");
}
}else srtp_init_done++;
return st;
}
void ms_srtp_shutdown(void){
srtp_init_done--;
if (srtp_init_done==0){
#ifdef HAVE_SRTP_SHUTDOWN
srtp_shutdown();
#endif
}
}