blob: d8e5b67cd60d34d124e7dda9bcda4406d721cc52 [file] [log] [blame]
/* $Id$
*
*/
#include <pjmedia/session.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pj/string.h>
typedef struct pj_media_stream_desc
{
pj_media_stream_info info;
pj_media_stream_t *enc_stream, *dec_stream;
} pj_media_stream_desc;
struct pj_media_session_t
{
pj_pool_t *pool;
pj_med_mgr_t *mediamgr;
unsigned stream_cnt;
pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA];
};
#define THIS_FILE "session.c"
#define PJ_MEDIA_SESSION_SIZE (48*1024)
#define PJ_MEDIA_SESSION_INC 1024
static const pj_str_t ID_AUDIO = { "audio", 5};
static const pj_str_t ID_VIDEO = { "video", 5};
static const pj_str_t ID_IN = { "IN", 2 };
static const pj_str_t ID_IP4 = { "IP4", 3};
static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
static void session_init (pj_media_session_t *ses)
{
pj_memset (ses, 0, sizeof(pj_media_session_t));
}
/**
* Create new session offering.
*/
PJ_DEF(pj_media_session_t*)
pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info)
{
pj_pool_factory *pf;
pj_pool_t *pool;
pj_media_session_t *session;
pj_media_stream_desc *sd;
unsigned i, codec_cnt;
pj_codec_mgr *cm;
const pj_codec_id *codecs[PJSDP_MAX_FMT];
pf = pj_med_mgr_get_pool_factory(mgr);
pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
if (!pool)
return NULL;
session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
if (!session)
return NULL;
session_init (session);
session->pool = pool;
session->mediamgr = mgr;
/* Create first stream */
sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
if (!sd)
return NULL;
sd->info.type = ID_AUDIO;
sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
sd->info.transport = ID_RTP_AVP;
pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
/* Enum audio codecs. */
sd->info.fmt_cnt = 0;
cm = pj_med_mgr_get_codec_mgr (mgr);
codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs);
if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
for (i=0; i<codec_cnt; ++i) {
if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO)
continue;
sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt;
sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate;
pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name);
++sd->info.fmt_cnt;
}
session->stream_desc[session->stream_cnt++] = sd;
return session;
}
static int sdp_check (const pjsdp_session_desc *sdp)
{
int has_conn = 0;
unsigned i;
if (sdp->conn)
has_conn = 1;
if (sdp->media_count == 0) {
PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition"));
return -1;
}
for (i=0; i<sdp->media_count; ++i) {
pjsdp_media_desc *m = sdp->media[i];
if (!m) {
pj_assert(0);
return -1;
}
if (m->desc.fmt_count == 0) {
PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream"));
return -1;
}
if (!has_conn && m->conn == NULL) {
PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media"));
return -1;
}
}
return 0;
}
/*
* Create local stream definition that matches SDP received from peer.
*/
static pj_media_stream_desc*
create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn,
const pjsdp_media_desc *m, const pj_media_sock_info *sock_info)
{
pj_media_stream_desc *sd;
sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
if (!sd) {
PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor"));
return NULL;
}
if (pj_stricmp(&conn->net_type, &ID_IN)==0 &&
pj_stricmp(&conn->addr_type, &ID_IP4)==0 &&
pj_stricmp(&m->desc.media, &ID_AUDIO)==0 &&
pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0)
{
/*
* Got audio stream.
*/
unsigned i, codec_cnt;
pj_codec_mgr *cm;
const pj_codec_id *codecs[PJSDP_MAX_FMT];
sd->info.type = ID_AUDIO;
sd->info.transport = ID_RTP_AVP;
pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
sd->info.rem_port = m->desc.port;
pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
/* Enum audio codecs. */
sd->info.fmt_cnt = 0;
cm = pj_med_mgr_get_codec_mgr (mgr);
codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs);
if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
/* Find just one codec which we can support. */
for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) {
unsigned j, fmt_i;
/* For static payload, just match payload type. */
/* Else match clock rate and encoding name. */
fmt_i = pj_strtoul(&m->desc.fmt[i]);
if (fmt_i < PJ_RTP_PT_DYNAMIC) {
for (j=0; j<codec_cnt; ++j) {
if (codecs[j]->pt == fmt_i) {
sd->info.fmt_cnt = 1;
sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
sd->info.fmt[0].pt = codecs[j]->pt;
sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name);
break;
}
}
} else {
/* Find the rtpmap for the payload type. */
const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i);
/* Don't accept the media if no rtpmap for dynamic PT. */
if (rtpmap == NULL) {
PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i]));
continue;
}
/* Check whether we can take this codec. */
for (j=0; j<codec_cnt; ++j) {
if (rtpmap->clock_rate == codecs[j]->sample_rate &&
pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0)
{
sd->info.fmt_cnt = 1;
sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
sd->info.fmt[0].pt = codecs[j]->pt;
sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
sd->info.fmt[0].encoding_name = codecs[j]->encoding_name;
break;
}
}
}
}
/* Match codec and direction. */
if (sd->info.fmt_cnt == 0 || m->desc.port == 0 ||
pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE))
{
sd->info.dir = PJ_MEDIA_DIR_NONE;
}
else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
sd->info.dir = PJ_MEDIA_DIR_ENCODING;
}
else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
sd->info.dir = PJ_MEDIA_DIR_DECODING;
}
else {
sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
}
} else {
/* Unsupported media stream. */
unsigned fmt_num;
const pjsdp_rtpmap_attr *rtpmap = NULL;
pj_strdup(pool, &sd->info.type, &m->desc.media);
pj_strdup(pool, &sd->info.transport, &m->desc.transport);
pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info));
pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
sd->info.rem_port = m->desc.port;
/* Just put one format and rtpmap, so that we don't have to make
* special exception when we convert this stream to SDP.
*/
/* Find the rtpmap for the payload type. */
fmt_num = pj_strtoul(&m->desc.fmt[0]);
rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num);
sd->info.fmt_cnt = 1;
if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) {
sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO;
sd->info.fmt[0].pt = fmt_num;
if (rtpmap) {
pj_strdup (pool, &sd->info.fmt[0].encoding_name,
&rtpmap->encoding_name);
sd->info.fmt[0].sample_rate = rtpmap->clock_rate;
}
} else {
sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN;
pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]);
}
sd->info.dir = PJ_MEDIA_DIR_NONE;
}
return sd;
}
/**
* Create new session based on peer's offering.
*/
PJ_DEF(pj_media_session_t*)
pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
const pj_media_sock_info *sock_info)
{
pj_pool_factory *pf;
pj_pool_t *pool;
pj_media_session_t *session;
unsigned i;
if (sdp_check(sdp) != 0)
return NULL;
pf = pj_med_mgr_get_pool_factory(mgr);
pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
if (!pool)
return NULL;
session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
if (!session) {
PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
pj_pool_release (pool);
return NULL;
}
session_init (session);
session->pool = pool;
session->mediamgr = mgr;
/* Enumerate each media stream and create our peer. */
for (i=0; i<sdp->media_count; ++i) {
const pjsdp_conn_info *conn;
const pjsdp_media_desc *m;
pj_media_stream_desc *sd;
m = sdp->media[i];
conn = m->conn ? m->conn : sdp->conn;
/*
* Bug:
* the sock_info below is used by more than one 'm' lines
*/
PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES)
sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info);
pj_assert (sd);
session->stream_desc[session->stream_cnt++] = sd;
}
return session;
}
/**
* Duplicate session. The new session is inactive.
*/
PJ_DEF(pj_media_session_t*)
pj_media_session_clone (const pj_media_session_t *rhs)
{
pj_pool_factory *pf;
pj_pool_t *pool;
pj_media_session_t *session;
unsigned i;
pf = pj_med_mgr_get_pool_factory(rhs->mediamgr);
pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
if (!pool) {
return NULL;
}
session = pj_pool_alloc (pool, sizeof(*session));
if (!session) {
PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
pj_pool_release (pool);
return NULL;
}
session->pool = pool;
session->mediamgr = rhs->mediamgr;
session->stream_cnt = rhs->stream_cnt;
for (i=0; i<rhs->stream_cnt; ++i) {
pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc));
const pj_media_stream_desc *sd2 = rhs->stream_desc[i];
if (!sd1) {
PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor"));
pj_pool_release (pool);
return NULL;
}
session->stream_desc[i] = sd1;
sd1->enc_stream = sd1->dec_stream = NULL;
pj_strdup (pool, &sd1->info.type, &sd2->info.type);
sd1->info.dir = sd2->info.dir;
pj_strdup (pool, &sd1->info.transport, &sd2->info.transport);
pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info));
pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr);
sd1->info.rem_port = sd2->info.rem_port;
sd1->info.fmt_cnt = sd2->info.fmt_cnt;
pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt));
}
return session;
}
/**
* Create SDP description from the session.
*/
PJ_DEF(pjsdp_session_desc*)
pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool,
pj_bool_t only_first_fmt)
{
pjsdp_session_desc *sdp;
pj_time_val tv;
unsigned i;
pj_media_sock_info *c_addr = NULL;
if (session->stream_cnt == 0) {
pj_assert(0);
return NULL;
}
sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc));
if (!sdp) {
PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor"));
return NULL;
}
pj_gettimeofday(&tv);
sdp->origin.user = pj_str("-");
sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
sdp->origin.net_type = ID_IN;
sdp->origin.addr_type = ID_IP4;
sdp->origin.addr = *pj_gethostname();
sdp->name = ID_SDP_NAME;
/* If all media addresses are the same, then put the connection
* info in the session level, otherwise put it in media stream
* level.
*/
for (i=0; i<session->stream_cnt; ++i) {
if (c_addr == NULL) {
c_addr = &session->stream_desc[i]->info.sock_info;
} else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr)
{
c_addr = NULL;
break;
}
}
if (c_addr) {
/* All addresses are the same, put connection info in session level. */
sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
if (!sdp->conn) {
PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info"));
return NULL;
}
sdp->conn->net_type = ID_IN;
sdp->conn->addr_type = ID_IP4;
pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr));
}
sdp->time.start = sdp->time.stop = 0;
sdp->attr_count = 0;
/* Create each media. */
sdp->media_count = 0;
for (i=0; i<session->stream_cnt; ++i) {
const pj_media_stream_desc *sd = session->stream_desc[i];
pjsdp_media_desc *m;
unsigned j;
unsigned fmt_cnt;
pjsdp_attr *attr;
m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc));
if (!m) {
PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor"));
return NULL;
}
sdp->media[sdp->media_count++] = m;
pj_strdup (pool, &m->desc.media, &sd->info.type);
m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port);
m->desc.port_count = 1;
pj_strdup (pool, &m->desc.transport, &sd->info.transport);
/* Add format and rtpmap for each codec. */
m->desc.fmt_count = 0;
m->attr_count = 0;
fmt_cnt = sd->info.fmt_cnt;
if (fmt_cnt > 0 && only_first_fmt)
fmt_cnt = 1;
for (j=0; j<fmt_cnt; ++j) {
pjsdp_rtpmap_attr *rtpmap;
pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++];
if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) {
pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name);
} else {
fmt->ptr = pj_pool_alloc(pool, 8);
fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr);
rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr));
if (rtpmap) {
m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap;
rtpmap->type = PJSDP_ATTR_RTPMAP;
rtpmap->payload_type = sd->info.fmt[j].pt;
rtpmap->clock_rate = sd->info.fmt[j].sample_rate;
pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name);
} else {
PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor"));
}
}
}
/* If we don't have connection info in session level, create one. */
if (sdp->conn == NULL) {
m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
if (m->conn) {
m->conn->net_type = ID_IN;
m->conn->addr_type = ID_IP4;
pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr));
} else {
PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info"));
return NULL;
}
}
/* Add additional attribute to the media stream. */
attr = pj_pool_alloc(pool, sizeof(pjsdp_attr));
if (!attr) {
PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute"));
return NULL;
}
m->attr[m->attr_count++] = attr;
switch (sd->info.dir) {
case PJ_MEDIA_DIR_NONE:
attr->type = PJSDP_ATTR_INACTIVE;
break;
case PJ_MEDIA_DIR_ENCODING:
attr->type = PJSDP_ATTR_SEND_ONLY;
break;
case PJ_MEDIA_DIR_DECODING:
attr->type = PJSDP_ATTR_RECV_ONLY;
break;
case PJ_MEDIA_DIR_ENCODING_DECODING:
attr->type = PJSDP_ATTR_SEND_RECV;
break;
}
}
return sdp;
}
/**
* Update session with SDP answer from peer.
*/
PJ_DEF(pj_status_t)
pj_media_session_update (pj_media_session_t *session,
const pjsdp_session_desc *sdp)
{
unsigned i;
unsigned count;
/* Check SDP */
if (sdp_check (sdp) != 0) {
return -1;
}
/* If the media stream count doesn't match, only update one. */
if (session->stream_cnt != sdp->media_count) {
PJ_LOG(3,(THIS_FILE, "pj_media_session_update : "
"SDP media count mismatch! (rmt=%d, lcl=%d)",
sdp->media_count, session->stream_cnt));
count = (session->stream_cnt < sdp->media_count) ?
session->stream_cnt : sdp->media_count;
} else {
count = session->stream_cnt;
}
for (i=0; i<count; ++i) {
pj_media_stream_desc *sd = session->stream_desc[i];
const pjsdp_media_desc *m = sdp->media[i];
const pjsdp_conn_info *conn;
unsigned j;
/* Check that the session is not active. */
pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL);
conn = m->conn ? m->conn : sdp->conn;
pj_assert(conn);
/* Update remote address. */
sd->info.rem_port = m->desc.port;
pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr);
/* Select one active codec according to what peer wants. */
for (j=0; j<sd->info.fmt_cnt; ++j) {
unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]);
if (sd->info.fmt[j].pt == fmt_0) {
pj_codec_id temp;
/* Put active format to the front. */
if (j == 0)
break;
pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp));
pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp));
pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp));
break;
}
}
if (j == sd->info.fmt_cnt) {
/* Peer has answered SDP with new codec, which doesn't exist
* in the offer!
* Mute this media.
*/
PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!"));
sd->info.dir = PJ_MEDIA_DIR_NONE;
continue;
}
/* Check direction. */
if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) {
sd->info.dir = PJ_MEDIA_DIR_NONE;
}
else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
sd->info.dir = PJ_MEDIA_DIR_ENCODING;
}
else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
sd->info.dir = PJ_MEDIA_DIR_DECODING;
}
else {
sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
}
}
return 0;
}
/**
* Enumerate media streams in the session.
*/
PJ_DEF(unsigned)
pj_media_session_enum_streams (const pj_media_session_t *session,
unsigned count, const pj_media_stream_info *info[])
{
unsigned i;
if (count > session->stream_cnt)
count = session->stream_cnt;
for (i=0; i<count; ++i) {
info[i] = &session->stream_desc[i]->info;
}
return session->stream_cnt;
}
/**
* Get statistics
*/
PJ_DEF(pj_status_t)
pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
pj_media_stream_stat *tx_stat,
pj_media_stream_stat *rx_stat)
{
pj_media_stream_desc *sd;
int stat_cnt = 0;
if (index >= session->stream_cnt) {
pj_assert(0);
return -1;
}
sd = session->stream_desc[index];
if (sd->enc_stream && tx_stat) {
pj_media_stream_get_stat (sd->enc_stream, tx_stat);
++stat_cnt;
} else if (tx_stat) {
pj_memset (tx_stat, 0, sizeof(*tx_stat));
}
if (sd->dec_stream && rx_stat) {
pj_media_stream_get_stat (sd->dec_stream, rx_stat);
++stat_cnt;
} else if (rx_stat) {
pj_memset (rx_stat, 0, sizeof(*rx_stat));
}
return stat_cnt ? 0 : -1;
}
/**
* Modify stream, only when stream is inactive.
*/
PJ_DEF(pj_status_t)
pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
unsigned modify_flag, const pj_media_stream_info *info)
{
pj_media_stream_desc *sd;
if (index >= session->stream_cnt) {
pj_assert(0);
return -1;
}
sd = session->stream_desc[index];
if (sd->enc_stream || sd->dec_stream) {
pj_assert(0);
return -1;
}
if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) {
sd->info.dir = info->dir;
}
return 0;
}
/**
* Activate media session.
*/
PJ_DEF(pj_status_t)
pj_media_session_activate (pj_media_session_t *session)
{
unsigned i;
pj_status_t status = 0;
for (i=0; i<session->stream_cnt; ++i) {
pj_status_t rc;
rc = pj_media_session_activate_stream (session, i);
if (status == 0)
status = rc;
}
return status;
}
/**
* Activate individual stream in media session.
*/
PJ_DEF(pj_status_t)
pj_media_session_activate_stream (pj_media_session_t *session, unsigned index)
{
pj_media_stream_desc *sd;
pj_media_stream_create_param scp;
pj_status_t status;
pj_time_val tv;
if (index < 0 || index >= session->stream_cnt) {
pj_assert(0);
return -1;
}
sd = session->stream_desc[index];
if (sd->enc_stream || sd->dec_stream) {
/* Stream already active. */
pj_assert(0);
return 0;
}
pj_gettimeofday(&tv);
/* Initialize parameter to create stream. */
pj_memset (&scp, 0, sizeof(scp));
scp.codec_id = &sd->info.fmt[0];
scp.mediamgr = session->mediamgr;
scp.dir = sd->info.dir;
scp.rtp_sock = sd->info.sock_info.rtp_sock;
scp.rtcp_sock = sd->info.sock_info.rtcp_sock;
scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in));
pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port);
scp.ssrc = tv.sec;
scp.jb_min = 1;
scp.jb_max = 15;
scp.jb_maxcnt = 16;
/* Build the stream! */
status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp);
if (status==0 && sd->enc_stream) {
status = pj_media_stream_start (sd->enc_stream);
if (status != 0)
goto on_error;
}
if (status==0 && sd->dec_stream) {
status = pj_media_stream_start (sd->dec_stream);
if (status != 0)
goto on_error;
}
return status;
on_error:
if (sd->enc_stream) {
pj_media_stream_destroy (sd->enc_stream);
sd->enc_stream = NULL;
}
if (sd->dec_stream) {
pj_media_stream_destroy (sd->dec_stream);
sd->dec_stream = NULL;
}
return status;
}
/**
* Destroy media session.
*/
PJ_DEF(pj_status_t)
pj_media_session_destroy (pj_media_session_t *session)
{
unsigned i;
if (!session)
return -1;
for (i=0; i<session->stream_cnt; ++i) {
pj_media_stream_desc *sd = session->stream_desc[i];
if (sd->enc_stream) {
pj_media_stream_destroy (sd->enc_stream);
sd->enc_stream = NULL;
}
if (sd->dec_stream) {
pj_media_stream_destroy (sd->dec_stream);
sd->dec_stream = NULL;
}
}
pj_pool_release (session->pool);
return 0;
}