Changeset 159 for pjproject/trunk/pjmedia/src/pjmedia/session.c
- Timestamp:
- Feb 8, 2006 10:43:39 PM (19 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjmedia/src/pjmedia/session.c
r121 r159 18 18 */ 19 19 #include <pjmedia/session.h> 20 #include <pjmedia/errno.h> 20 21 #include <pj/log.h> 21 22 #include <pj/os.h> … … 23 24 #include <pj/string.h> 24 25 #include <pj/assert.h> 25 26 27 typedef struct pj_media_stream_desc 28 { 29 pj_media_stream_info info; 30 pj_media_stream_t *enc_stream, *dec_stream; 31 } pj_media_stream_desc; 32 33 struct pj_media_session_t 26 #include <pj/ctype.h> 27 28 29 struct pjmedia_session 34 30 { 35 31 pj_pool_t *pool; 36 pj _med_mgr_t *mediamgr;32 pjmedia_endpt *endpt; 37 33 unsigned stream_cnt; 38 pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA]; 34 pjmedia_stream_info stream_info[PJSDP_MAX_MEDIA]; 35 pjmedia_stream *stream[PJSDP_MAX_MEDIA]; 39 36 }; 40 37 41 38 #define THIS_FILE "session.c" 42 39 43 #define PJ _MEDIA_SESSION_SIZE (48*1024)44 #define PJ _MEDIA_SESSION_INC 102440 #define PJMEDIA_SESSION_SIZE (48*1024) 41 #define PJMEDIA_SESSION_INC 1024 45 42 46 43 static const pj_str_t ID_AUDIO = { "audio", 5}; … … 50 47 static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; 51 48 static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; 52 53 static void session_init (pj_media_session_t *ses) 54 { 55 pj_memset (ses, 0, sizeof(pj_media_session_t)); 56 } 57 58 59 /** 60 * Create new session offering. 61 */ 62 PJ_DEF(pj_media_session_t*) 63 pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info) 64 { 65 pj_pool_factory *pf; 49 static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; 50 51 static const pj_str_t STR_INACTIVE = { "inactive", 8 }; 52 static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; 53 static const pj_str_t STR_SENDONLY = { "sendonly", 8 }; 54 static const pj_str_t STR_RECVONLY = { "recvonly", 8 }; 55 56 57 /* 58 * Create stream info from SDP media line. 59 */ 60 static pj_status_t create_stream_info_from_sdp(pj_pool_t *pool, 61 pjmedia_stream_info *si, 62 const pjmedia_sdp_conn *local_conn, 63 const pjmedia_sdp_conn *rem_conn, 64 const pjmedia_sdp_media *local_m, 65 const pjmedia_sdp_media *rem_m) 66 { 67 const pjmedia_sdp_attr *attr; 68 pjmedia_sdp_rtpmap *rtpmap; 69 pj_status_t status; 70 71 72 /* Validate arguments: */ 73 74 PJ_ASSERT_RETURN(pool && si && local_conn && rem_conn && 75 local_m && rem_m, PJ_EINVAL); 76 77 /* Reset: */ 78 79 pj_memset(si, 0, sizeof(*si)); 80 81 /* Media type: */ 82 83 if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) { 84 85 si->type = PJMEDIA_TYPE_AUDIO; 86 87 } else if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0) { 88 89 si->type = PJMEDIA_TYPE_VIDEO; 90 91 } else { 92 93 si->type = PJMEDIA_TYPE_UNKNOWN; 94 95 } 96 97 /* Media direction: */ 98 99 if (local_m->desc.port == 0 || 100 pj_inet_addr(&local_conn->addr).s_addr==0 || 101 pj_inet_addr(&rem_conn->addr).s_addr==0 || 102 pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) 103 { 104 /* Inactive stream. */ 105 106 si->dir = PJMEDIA_DIR_NONE; 107 108 } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { 109 110 /* Send only stream. */ 111 112 si->dir = PJMEDIA_DIR_ENCODING; 113 114 } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { 115 116 /* Recv only stream. */ 117 118 si->dir = PJMEDIA_DIR_DECODING; 119 120 } else { 121 122 /* Send and receive stream. */ 123 124 si->dir = PJMEDIA_DIR_ENCODING_DECODING; 125 126 } 127 128 129 /* Set remote address: */ 130 131 si->rem_addr.sin_family = PJ_AF_INET; 132 si->rem_addr.sin_port = pj_htons(rem_m->desc.port); 133 if (pj_inet_aton(&rem_conn->addr, &si->rem_addr.sin_addr) == 0) { 134 135 /* Invalid IP address. */ 136 return PJMEDIA_EINVALIDIP; 137 } 138 139 /* For this version of PJMEDIA, send and receive media must use 140 * the same codec. 141 */ 142 if (pj_strcmp(&local_m->desc.fmt[0], &rem_m->desc.fmt[0]) != 0) 143 return PJMEDIA_EASYMCODEC; 144 145 146 /* And codec must be numeric! */ 147 if (!pj_isdigit(*local_m->desc.fmt[0].ptr)) 148 return PJMEDIA_EINVALIDPT; 149 150 /* Find rtpmap for the first codec. 151 * For this version of PJMEDIA, we do not support static payload 152 * type without rtpmap. 153 */ 154 attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, NULL); 155 if (attr == NULL) 156 return PJMEDIA_EMISSINGRTPMAP; 157 158 status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); 159 if (status != PJ_SUCCESS) 160 return status; 161 162 /* Build codec format info: */ 163 164 si->fmt.type = si->type; 165 si->fmt.pt = pj_strtoul(&local_m->desc.fmt[0]); 166 pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); 167 si->fmt.sample_rate = rtpmap->clock_rate; 168 169 /* Leave SSRC to zero. */ 170 171 /* Leave jitter buffer parameter. */ 172 173 return PJ_SUCCESS; 174 } 175 176 177 /** 178 * Create new session. 179 */ 180 PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, 181 unsigned stream_cnt, 182 const pjmedia_sock_info skinfo[], 183 const pjmedia_sdp_session *local_sdp, 184 const pjmedia_sdp_session *rem_sdp, 185 pjmedia_session **p_session ) 186 { 66 187 pj_pool_t *pool; 67 pj_media_session_t *session; 68 pj_media_stream_desc *sd; 69 unsigned i, codec_cnt; 70 pj_codec_mgr *cm; 71 const pj_codec_id *codecs[PJSDP_MAX_FMT]; 72 73 pf = pj_med_mgr_get_pool_factory(mgr); 74 75 pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); 76 if (!pool) 77 return NULL; 78 79 session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); 80 if (!session) 81 return NULL; 82 83 session_init (session); 84 188 pjmedia_session *session; 189 int i; /* Must be signed */ 190 pj_status_t status; 191 192 /* Verify arguments. */ 193 PJ_ASSERT_RETURN(endpt && stream_cnt && skinfo && 194 local_sdp && rem_sdp && p_session, PJ_EINVAL); 195 196 /* Create pool for the session. */ 197 pool = pjmedia_endpt_create_pool( endpt, "session", 198 PJMEDIA_SESSION_SIZE, 199 PJMEDIA_SESSION_INC); 200 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); 201 202 session = pj_pool_zalloc(pool, sizeof(pjmedia_session)); 85 203 session->pool = pool; 86 session->mediamgr = mgr; 87 88 /* Create first stream */ 89 sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); 90 if (!sd) 91 return NULL; 92 93 sd->info.type = ID_AUDIO; 94 sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; 95 sd->info.transport = ID_RTP_AVP; 96 pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); 97 98 /* Enum audio codecs. */ 99 sd->info.fmt_cnt = 0; 100 cm = pj_med_mgr_get_codec_mgr (mgr); 101 codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs); 102 if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; 103 for (i=0; i<codec_cnt; ++i) { 104 if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO) 105 continue; 106 107 sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt; 108 sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate; 109 pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name); 110 ++sd->info.fmt_cnt; 111 } 112 113 session->stream_desc[session->stream_cnt++] = sd; 114 115 return session; 116 } 117 118 static int sdp_check (const pjsdp_session_desc *sdp) 119 { 120 int has_conn = 0; 204 session->endpt = endpt; 205 session->stream_cnt = stream_cnt; 206 207 /* Stream count is the lower number of stream_cnt or SDP m= lines count */ 208 if (stream_cnt < local_sdp->media_count) 209 stream_cnt = local_sdp->media_count; 210 211 /* 212 * Create streams: 213 */ 214 for (i=0; i<(int)stream_cnt; ++i) { 215 216 pjmedia_stream_info *si = &session->stream_info[i]; 217 const pjmedia_sdp_media *local_m = local_sdp->media[i]; 218 const pjmedia_sdp_media *rem_m = rem_sdp->media[i]; 219 pjmedia_sdp_conn *local_conn, *rem_conn; 220 221 /* Build stream info based on media line in local SDP */ 222 local_conn = local_m->conn ? local_m->conn : local_sdp->conn; 223 rem_conn = rem_m->conn ? rem_m->conn : rem_sdp->conn; 224 225 status = create_stream_info_from_sdp(session->pool, si, 226 local_conn, rem_conn, 227 local_m, rem_m); 228 if (status != PJ_SUCCESS) 229 return status; 230 231 /* Assign sockinfo */ 232 si->sock_info = skinfo[i]; 233 } 234 235 /* 236 * Now create the stream! 237 */ 238 for (i=0; i<(int)stream_cnt; ++i) { 239 240 status = pjmedia_stream_create(endpt, session->pool, 241 &session->stream_info[i], 242 &session->stream[i]); 243 if (status != PJ_SUCCESS) { 244 245 for ( --i; i>=0; ++i) { 246 pjmedia_stream_destroy(session->stream[i]); 247 } 248 249 pj_pool_release(session->pool); 250 return status; 251 } 252 } 253 254 255 /* Done. */ 256 257 *p_session = session; 258 return PJ_SUCCESS; 259 } 260 261 262 /** 263 * Destroy media session. 264 */ 265 PJ_DEF(pj_status_t) pjmedia_session_destroy (pjmedia_session *session) 266 { 121 267 unsigned i; 122 268 123 if (sdp->conn) 124 has_conn = 1; 125 126 if (sdp->media_count == 0) { 127 PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition")); 128 return -1; 129 } 130 131 for (i=0; i<sdp->media_count; ++i) { 132 pjsdp_media_desc *m = sdp->media[i]; 133 134 if (!m) { 135 pj_assert(0); 136 return -1; 137 } 138 139 if (m->desc.fmt_count == 0) { 140 PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream")); 141 return -1; 142 } 143 144 if (!has_conn && m->conn == NULL) { 145 PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media")); 146 return -1; 147 } 148 } 149 150 return 0; 151 } 152 153 /* 154 * Create local stream definition that matches SDP received from peer. 155 */ 156 static pj_media_stream_desc* 157 create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn, 158 const pjsdp_media_desc *m, const pj_media_sock_info *sock_info) 159 { 160 pj_media_stream_desc *sd; 161 162 sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); 163 if (!sd) { 164 PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor")); 165 return NULL; 166 } 167 168 if (pj_stricmp(&conn->net_type, &ID_IN)==0 && 169 pj_stricmp(&conn->addr_type, &ID_IP4)==0 && 170 pj_stricmp(&m->desc.media, &ID_AUDIO)==0 && 171 pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0) 172 { 173 /* 174 * Got audio stream. 175 */ 176 unsigned i, codec_cnt; 177 pj_codec_mgr *cm; 178 const pj_codec_id *codecs[PJSDP_MAX_FMT]; 179 180 sd->info.type = ID_AUDIO; 181 sd->info.transport = ID_RTP_AVP; 182 pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); 183 sd->info.rem_port = m->desc.port; 184 pj_strdup (pool, &sd->info.rem_addr, &conn->addr); 185 186 /* Enum audio codecs. */ 187 sd->info.fmt_cnt = 0; 188 cm = pj_med_mgr_get_codec_mgr (mgr); 189 codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs); 190 if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; 191 192 /* Find just one codec which we can support. */ 193 for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) { 194 unsigned j, fmt_i; 195 196 /* For static payload, just match payload type. */ 197 /* Else match clock rate and encoding name. */ 198 fmt_i = pj_strtoul(&m->desc.fmt[i]); 199 if (fmt_i < PJ_RTP_PT_DYNAMIC) { 200 for (j=0; j<codec_cnt; ++j) { 201 if (codecs[j]->pt == fmt_i) { 202 sd->info.fmt_cnt = 1; 203 sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; 204 sd->info.fmt[0].pt = codecs[j]->pt; 205 sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; 206 pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name); 207 break; 208 } 209 } 210 } else { 211 212 /* Find the rtpmap for the payload type. */ 213 const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i); 214 215 /* Don't accept the media if no rtpmap for dynamic PT. */ 216 if (rtpmap == NULL) { 217 PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i])); 218 continue; 219 } 220 221 /* Check whether we can take this codec. */ 222 for (j=0; j<codec_cnt; ++j) { 223 if (rtpmap->clock_rate == codecs[j]->sample_rate && 224 pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0) 225 { 226 sd->info.fmt_cnt = 1; 227 sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; 228 sd->info.fmt[0].pt = codecs[j]->pt; 229 sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; 230 sd->info.fmt[0].encoding_name = codecs[j]->encoding_name; 231 break; 232 } 233 } 234 } 235 } 236 237 /* Match codec and direction. */ 238 if (sd->info.fmt_cnt == 0 || m->desc.port == 0 || 239 pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) 240 { 241 sd->info.dir = PJ_MEDIA_DIR_NONE; 242 } 243 else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { 244 sd->info.dir = PJ_MEDIA_DIR_ENCODING; 245 } 246 else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { 247 sd->info.dir = PJ_MEDIA_DIR_DECODING; 248 } 249 else { 250 sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; 251 } 252 253 } else { 254 /* Unsupported media stream. */ 255 unsigned fmt_num; 256 const pjsdp_rtpmap_attr *rtpmap = NULL; 257 258 pj_strdup(pool, &sd->info.type, &m->desc.media); 259 pj_strdup(pool, &sd->info.transport, &m->desc.transport); 260 pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info)); 261 pj_strdup (pool, &sd->info.rem_addr, &conn->addr); 262 sd->info.rem_port = m->desc.port; 263 264 /* Just put one format and rtpmap, so that we don't have to make 265 * special exception when we convert this stream to SDP. 266 */ 267 268 /* Find the rtpmap for the payload type. */ 269 fmt_num = pj_strtoul(&m->desc.fmt[0]); 270 rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num); 271 272 sd->info.fmt_cnt = 1; 273 if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) { 274 sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO; 275 sd->info.fmt[0].pt = fmt_num; 276 if (rtpmap) { 277 pj_strdup (pool, &sd->info.fmt[0].encoding_name, 278 &rtpmap->encoding_name); 279 sd->info.fmt[0].sample_rate = rtpmap->clock_rate; 280 } 281 } else { 282 sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN; 283 pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]); 284 } 269 PJ_ASSERT_RETURN(session, PJ_EINVAL); 270 271 for (i=0; i<session->stream_cnt; ++i) { 285 272 286 sd->info.dir = PJ_MEDIA_DIR_NONE; 287 } 288 289 return sd; 290 } 291 292 /** 293 * Create new session based on peer's offering. 294 */ 295 PJ_DEF(pj_media_session_t*) 296 pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, 297 const pj_media_sock_info *sock_info) 298 { 299 pj_pool_factory *pf; 300 pj_pool_t *pool; 301 pj_media_session_t *session; 273 pjmedia_stream_destroy(session->stream[i]); 274 275 } 276 277 pj_pool_release (session->pool); 278 279 return PJ_SUCCESS; 280 } 281 282 283 /** 284 * Activate all stream in media session. 285 * 286 */ 287 PJ_DEF(pj_status_t) pjmedia_session_resume(pjmedia_session *session, 288 pjmedia_dir dir) 289 { 302 290 unsigned i; 303 291 304 if (sdp_check(sdp) != 0) 305 return NULL; 306 307 pf = pj_med_mgr_get_pool_factory(mgr); 308 pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); 309 if (!pool) 310 return NULL; 311 312 session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); 313 if (!session) { 314 PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); 315 pj_pool_release (pool); 316 return NULL; 317 } 318 319 session_init (session); 320 321 session->pool = pool; 322 session->mediamgr = mgr; 323 324 /* Enumerate each media stream and create our peer. */ 325 for (i=0; i<sdp->media_count; ++i) { 326 const pjsdp_conn_info *conn; 327 const pjsdp_media_desc *m; 328 pj_media_stream_desc *sd; 329 330 m = sdp->media[i]; 331 conn = m->conn ? m->conn : sdp->conn; 332 333 /* 334 * Bug: 335 * the sock_info below is used by more than one 'm' lines 336 */ 337 PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES) 338 339 sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info); 340 pj_assert (sd); 341 342 session->stream_desc[session->stream_cnt++] = sd; 343 } 344 345 return session; 346 } 347 348 /** 349 * Duplicate session. The new session is inactive. 350 */ 351 PJ_DEF(pj_media_session_t*) 352 pj_media_session_clone (const pj_media_session_t *rhs) 353 { 354 pj_pool_factory *pf; 355 pj_pool_t *pool; 356 pj_media_session_t *session; 292 PJ_ASSERT_RETURN(session, PJ_EINVAL); 293 294 for (i=0; i<session->stream_cnt; ++i) { 295 pjmedia_session_resume_stream(session, i, dir); 296 } 297 298 return PJ_SUCCESS; 299 } 300 301 302 /** 303 * Suspend receipt and transmission of all stream in media session. 304 * 305 */ 306 PJ_DEF(pj_status_t) pjmedia_session_pause(pjmedia_session *session, 307 pjmedia_dir dir) 308 { 357 309 unsigned i; 358 310 359 pf = pj_med_mgr_get_pool_factory(rhs->mediamgr); 360 pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); 361 if (!pool) { 362 return NULL; 363 } 364 365 session = pj_pool_alloc (pool, sizeof(*session)); 366 if (!session) { 367 PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); 368 pj_pool_release (pool); 369 return NULL; 370 } 371 372 session->pool = pool; 373 session->mediamgr = rhs->mediamgr; 374 session->stream_cnt = rhs->stream_cnt; 375 376 for (i=0; i<rhs->stream_cnt; ++i) { 377 pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc)); 378 const pj_media_stream_desc *sd2 = rhs->stream_desc[i]; 379 380 if (!sd1) { 381 PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor")); 382 pj_pool_release (pool); 383 return NULL; 384 } 385 386 session->stream_desc[i] = sd1; 387 sd1->enc_stream = sd1->dec_stream = NULL; 388 pj_strdup (pool, &sd1->info.type, &sd2->info.type); 389 sd1->info.dir = sd2->info.dir; 390 pj_strdup (pool, &sd1->info.transport, &sd2->info.transport); 391 pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info)); 392 pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr); 393 sd1->info.rem_port = sd2->info.rem_port; 394 sd1->info.fmt_cnt = sd2->info.fmt_cnt; 395 pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt)); 396 } 397 398 return session; 399 } 400 401 /** 402 * Create SDP description from the session. 403 */ 404 PJ_DEF(pjsdp_session_desc*) 405 pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool, 406 pj_bool_t only_first_fmt) 407 { 408 pjsdp_session_desc *sdp; 409 pj_time_val tv; 311 PJ_ASSERT_RETURN(session, PJ_EINVAL); 312 313 for (i=0; i<session->stream_cnt; ++i) { 314 pjmedia_session_pause_stream(session, i, dir); 315 } 316 317 return PJ_SUCCESS; 318 } 319 320 321 /** 322 * Suspend receipt and transmission of individual stream in media session. 323 */ 324 PJ_DEF(pj_status_t) pjmedia_session_pause_stream( pjmedia_session *session, 325 unsigned index, 326 pjmedia_dir dir) 327 { 328 PJ_ASSERT_RETURN(session && index < session->stream_cnt, PJ_EINVAL); 329 330 return pjmedia_stream_pause(session->stream[index], dir); 331 } 332 333 334 /** 335 * Activate individual stream in media session. 336 * 337 */ 338 PJ_DEF(pj_status_t) pjmedia_session_resume_stream( pjmedia_session *session, 339 unsigned index, 340 pjmedia_dir dir) 341 { 342 PJ_ASSERT_RETURN(session && index < session->stream_cnt, PJ_EINVAL); 343 344 return pjmedia_stream_resume(session->stream[index], dir); 345 } 346 347 /** 348 * Enumerate media stream in the session. 349 */ 350 PJ_DEF(pj_status_t) pjmedia_session_enum_streams(const pjmedia_session *session, 351 unsigned *count, 352 pjmedia_stream_info info[]) 353 { 410 354 unsigned i; 411 pj_media_sock_info *c_addr = NULL; 412 413 if (session->stream_cnt == 0) { 414 pj_assert(0); 415 return NULL; 416 } 417 418 sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc)); 419 if (!sdp) { 420 PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor")); 421 return NULL; 422 } 423 424 pj_gettimeofday(&tv); 425 426 sdp->origin.user = pj_str("-"); 427 sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL; 428 sdp->origin.net_type = ID_IN; 429 sdp->origin.addr_type = ID_IP4; 430 sdp->origin.addr = *pj_gethostname(); 431 432 sdp->name = ID_SDP_NAME; 433 434 /* If all media addresses are the same, then put the connection 435 * info in the session level, otherwise put it in media stream 436 * level. 437 */ 438 for (i=0; i<session->stream_cnt; ++i) { 439 if (c_addr == NULL) { 440 c_addr = &session->stream_desc[i]->info.sock_info; 441 } 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) 442 { 443 c_addr = NULL; 444 break; 445 } 446 } 447 448 if (c_addr) { 449 /* All addresses are the same, put connection info in session level. */ 450 sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); 451 if (!sdp->conn) { 452 PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info")); 453 return NULL; 454 } 455 456 sdp->conn->net_type = ID_IN; 457 sdp->conn->addr_type = ID_IP4; 458 pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr)); 459 } 460 461 sdp->time.start = sdp->time.stop = 0; 462 sdp->attr_count = 0; 463 464 /* Create each media. */ 465 sdp->media_count = 0; 466 for (i=0; i<session->stream_cnt; ++i) { 467 const pj_media_stream_desc *sd = session->stream_desc[i]; 468 pjsdp_media_desc *m; 469 unsigned j; 470 unsigned fmt_cnt; 471 pjsdp_attr *attr; 472 473 m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc)); 474 if (!m) { 475 PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor")); 476 return NULL; 477 } 478 479 sdp->media[sdp->media_count++] = m; 480 481 pj_strdup (pool, &m->desc.media, &sd->info.type); 482 m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port); 483 m->desc.port_count = 1; 484 pj_strdup (pool, &m->desc.transport, &sd->info.transport); 485 486 /* Add format and rtpmap for each codec. */ 487 m->desc.fmt_count = 0; 488 m->attr_count = 0; 489 fmt_cnt = sd->info.fmt_cnt; 490 if (fmt_cnt > 0 && only_first_fmt) 491 fmt_cnt = 1; 492 for (j=0; j<fmt_cnt; ++j) { 493 pjsdp_rtpmap_attr *rtpmap; 494 pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++]; 495 496 if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) { 497 pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name); 498 } else { 499 fmt->ptr = pj_pool_alloc(pool, 8); 500 fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr); 501 502 rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr)); 503 if (rtpmap) { 504 m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap; 505 rtpmap->type = PJSDP_ATTR_RTPMAP; 506 rtpmap->payload_type = sd->info.fmt[j].pt; 507 rtpmap->clock_rate = sd->info.fmt[j].sample_rate; 508 pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name); 509 } else { 510 PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor")); 511 } 512 } 513 } 514 515 /* If we don't have connection info in session level, create one. */ 516 if (sdp->conn == NULL) { 517 m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); 518 if (m->conn) { 519 m->conn->net_type = ID_IN; 520 m->conn->addr_type = ID_IP4; 521 pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr)); 522 } else { 523 PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info")); 524 return NULL; 525 } 526 } 527 528 /* Add additional attribute to the media stream. */ 529 attr = pj_pool_alloc(pool, sizeof(pjsdp_attr)); 530 if (!attr) { 531 PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute")); 532 return NULL; 533 } 534 m->attr[m->attr_count++] = attr; 535 536 switch (sd->info.dir) { 537 case PJ_MEDIA_DIR_NONE: 538 attr->type = PJSDP_ATTR_INACTIVE; 539 break; 540 case PJ_MEDIA_DIR_ENCODING: 541 attr->type = PJSDP_ATTR_SEND_ONLY; 542 break; 543 case PJ_MEDIA_DIR_DECODING: 544 attr->type = PJSDP_ATTR_RECV_ONLY; 545 break; 546 case PJ_MEDIA_DIR_ENCODING_DECODING: 547 attr->type = PJSDP_ATTR_SEND_RECV; 548 break; 549 } 550 } 551 552 return sdp; 553 } 554 555 /** 556 * Update session with SDP answer from peer. 557 */ 558 PJ_DEF(pj_status_t) 559 pj_media_session_update (pj_media_session_t *session, 560 const pjsdp_session_desc *sdp) 561 { 562 unsigned i; 563 unsigned count; 564 565 /* Check SDP */ 566 if (sdp_check (sdp) != 0) { 567 return -1; 568 } 569 570 /* If the media stream count doesn't match, only update one. */ 571 if (session->stream_cnt != sdp->media_count) { 572 PJ_LOG(3,(THIS_FILE, "pj_media_session_update : " 573 "SDP media count mismatch! (rmt=%d, lcl=%d)", 574 sdp->media_count, session->stream_cnt)); 575 count = (session->stream_cnt < sdp->media_count) ? 576 session->stream_cnt : sdp->media_count; 577 } else { 578 count = session->stream_cnt; 579 } 580 581 for (i=0; i<count; ++i) { 582 pj_media_stream_desc *sd = session->stream_desc[i]; 583 const pjsdp_media_desc *m = sdp->media[i]; 584 const pjsdp_conn_info *conn; 585 unsigned j; 586 587 /* Check that the session is not active. */ 588 pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL); 589 590 conn = m->conn ? m->conn : sdp->conn; 591 pj_assert(conn); 592 593 /* Update remote address. */ 594 sd->info.rem_port = m->desc.port; 595 pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr); 596 597 /* Select one active codec according to what peer wants. */ 598 for (j=0; j<sd->info.fmt_cnt; ++j) { 599 unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]); 600 if (sd->info.fmt[j].pt == fmt_0) { 601 pj_codec_id temp; 602 603 /* Put active format to the front. */ 604 if (j == 0) 605 break; 606 607 pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp)); 608 pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp)); 609 pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp)); 610 break; 611 } 612 } 613 614 if (j == sd->info.fmt_cnt) { 615 /* Peer has answered SDP with new codec, which doesn't exist 616 * in the offer! 617 * Mute this media. 618 */ 619 PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!")); 620 sd->info.dir = PJ_MEDIA_DIR_NONE; 621 continue; 622 } 623 624 /* Check direction. */ 625 if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) { 626 sd->info.dir = PJ_MEDIA_DIR_NONE; 627 } 628 else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { 629 sd->info.dir = PJ_MEDIA_DIR_ENCODING; 630 } 631 else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { 632 sd->info.dir = PJ_MEDIA_DIR_DECODING; 633 } 634 else { 635 sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; 636 } 637 } 638 639 return 0; 640 } 641 642 /** 643 * Enumerate media streams in the session. 644 */ 645 PJ_DEF(unsigned) 646 pj_media_session_enum_streams (const pj_media_session_t *session, 647 unsigned count, const pj_media_stream_info *info[]) 648 { 649 unsigned i; 650 651 if (count > session->stream_cnt) 652 count = session->stream_cnt; 653 654 for (i=0; i<count; ++i) { 655 info[i] = &session->stream_desc[i]->info; 656 } 657 658 return session->stream_cnt; 355 356 PJ_ASSERT_RETURN(session && count && *count && info, PJ_EINVAL); 357 358 if (*count > session->stream_cnt) 359 *count = session->stream_cnt; 360 361 for (i=0; i<*count; ++i) { 362 pj_memcpy(&info[i], &session->stream[i], sizeof(pjmedia_stream_info)); 363 } 364 365 return PJ_SUCCESS; 659 366 } 660 367 … … 662 369 * Get statistics 663 370 */ 664 PJ_DEF(pj_status_t) 665 pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, 666 pj_media_stream_stat *tx_stat, 667 pj_media_stream_stat *rx_stat) 668 { 669 pj_media_stream_desc *sd; 670 int stat_cnt = 0; 671 672 if (index >= session->stream_cnt) { 673 pj_assert(0); 674 return -1; 675 } 676 677 sd = session->stream_desc[index]; 678 679 if (sd->enc_stream && tx_stat) { 680 pj_media_stream_get_stat (sd->enc_stream, tx_stat); 681 ++stat_cnt; 682 } else if (tx_stat) { 683 pj_memset (tx_stat, 0, sizeof(*tx_stat)); 684 } 685 686 if (sd->dec_stream && rx_stat) { 687 pj_media_stream_get_stat (sd->dec_stream, rx_stat); 688 ++stat_cnt; 689 } else if (rx_stat) { 690 pj_memset (rx_stat, 0, sizeof(*rx_stat)); 691 } 692 693 return stat_cnt ? 0 : -1; 694 } 695 696 /** 697 * Modify stream, only when stream is inactive. 698 */ 699 PJ_DEF(pj_status_t) 700 pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, 701 unsigned modify_flag, const pj_media_stream_info *info) 702 { 703 pj_media_stream_desc *sd; 704 705 if (index >= session->stream_cnt) { 706 pj_assert(0); 707 return -1; 708 } 709 710 sd = session->stream_desc[index]; 711 712 if (sd->enc_stream || sd->dec_stream) { 713 pj_assert(0); 714 return -1; 715 } 716 717 if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) { 718 sd->info.dir = info->dir; 719 } 720 721 return 0; 722 } 723 724 /** 725 * Activate media session. 726 */ 727 PJ_DEF(pj_status_t) 728 pj_media_session_activate (pj_media_session_t *session) 729 { 730 unsigned i; 731 pj_status_t status = 0; 732 733 for (i=0; i<session->stream_cnt; ++i) { 734 pj_status_t rc; 735 rc = pj_media_session_activate_stream (session, i); 736 if (status == 0) 737 status = rc; 738 } 739 return status; 740 } 741 742 /** 743 * Activate individual stream in media session. 744 */ 745 PJ_DEF(pj_status_t) 746 pj_media_session_activate_stream (pj_media_session_t *session, unsigned index) 747 { 748 pj_media_stream_desc *sd; 749 pj_media_stream_create_param scp; 750 pj_status_t status; 751 pj_time_val tv; 752 753 if (index < 0 || index >= session->stream_cnt) { 754 pj_assert(0); 755 return -1; 756 } 757 758 sd = session->stream_desc[index]; 759 760 if (sd->enc_stream || sd->dec_stream) { 761 /* Stream already active. */ 762 pj_assert(0); 763 return 0; 764 } 765 766 pj_gettimeofday(&tv); 767 768 /* Initialize parameter to create stream. */ 769 pj_memset (&scp, 0, sizeof(scp)); 770 scp.codec_id = &sd->info.fmt[0]; 771 scp.mediamgr = session->mediamgr; 772 scp.dir = sd->info.dir; 773 scp.rtp_sock = sd->info.sock_info.rtp_sock; 774 scp.rtcp_sock = sd->info.sock_info.rtcp_sock; 775 scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in)); 776 pj_sockaddr_in_init(scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port); 777 scp.ssrc = tv.sec; 778 scp.jb_min = 1; 779 scp.jb_max = 15; 780 scp.jb_maxcnt = 16; 781 782 /* Build the stream! */ 783 status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp); 784 785 if (status==0 && sd->enc_stream) { 786 status = pj_media_stream_start (sd->enc_stream); 787 if (status != 0) 788 goto on_error; 789 } 790 if (status==0 && sd->dec_stream) { 791 status = pj_media_stream_start (sd->dec_stream); 792 if (status != 0) 793 goto on_error; 794 } 795 return status; 796 797 on_error: 798 if (sd->enc_stream) { 799 pj_media_stream_destroy (sd->enc_stream); 800 sd->enc_stream = NULL; 801 } 802 if (sd->dec_stream) { 803 pj_media_stream_destroy (sd->dec_stream); 804 sd->dec_stream = NULL; 805 } 806 return status; 807 } 808 809 /** 810 * Destroy media session. 811 */ 812 PJ_DEF(pj_status_t) 813 pj_media_session_destroy (pj_media_session_t *session) 814 { 815 unsigned i; 816 817 if (!session) 818 return -1; 819 820 for (i=0; i<session->stream_cnt; ++i) { 821 pj_media_stream_desc *sd = session->stream_desc[i]; 822 823 if (sd->enc_stream) { 824 pj_media_stream_destroy (sd->enc_stream); 825 sd->enc_stream = NULL; 826 } 827 if (sd->dec_stream) { 828 pj_media_stream_destroy (sd->dec_stream); 829 sd->dec_stream = NULL; 830 } 831 } 832 pj_pool_release (session->pool); 833 return 0; 834 } 835 371 PJ_DEF(pj_status_t) pjmedia_session_get_stat(const pjmedia_session *session, 372 unsigned *count, 373 pjmedia_stream_stat stat[]) 374 { 375 PJ_ASSERT_RETURN(session && count && *count && stat, PJ_EINVAL); 376 377 *count = 0; 378 pj_memset(stat, 0, *count * sizeof(pjmedia_stream_stat)); 379 return PJ_EINVALIDOP; 380 } 381 382 383 /** 384 * Get individual stream statistic. 385 */ 386 PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( const pjmedia_session *s, 387 unsigned index, 388 pjmedia_stream_stat *stat) 389 { 390 PJ_ASSERT_RETURN(s && index < s->stream_cnt && stat, PJ_EINVAL); 391 pj_memset(stat, 0, sizeof(pjmedia_stream_stat)); 392 return PJ_EINVALIDOP; 393 } 394 395
Note: See TracChangeset
for help on using the changeset viewer.