/* * This callback is called by transport when incoming rtp is received */ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = size; err_status_t err; if (srtp->bypass_srtp) { srtp->rtp_cb(srtp->user_data, pkt, size); return; } if (size < 0 || !srtp->session_inited) { return; } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((long)pkt) & 0x03)==0, return ); if (srtp->probation_cnt > 0) --srtp->probation_cnt; pj_lock_acquire(srtp->mutex); err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (srtp->probation_cnt > 0 && (err == err_status_replay_old || err == err_status_replay_fail)) { /* Handle such condition that stream is updated (RTP seq is reinited * & SRTP is restarted), but some old packets are still coming * so SRTP is learning wrong RTP seq. While the newly inited RTP seq * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect() * will returning err_status_replay_*. Restarting SRTP can resolve * this. */ if (pjmedia_transport_srtp_start((pjmedia_transport*)srtp, &srtp->tx_policy, &srtp->rx_policy) != PJ_SUCCESS) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s", get_libsrtp_errstr(err))); } else { err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); } } if (err == err_status_ok) { srtp->rtp_cb(srtp->user_data, pkt, len); } else { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } pj_lock_release(srtp->mutex); }
static pj_status_t start_srtp(transport_srtp *srtp) { /* Make sure we have the SRTP policies */ if (srtp_crypto_empty(&srtp->tx_policy_neg) || srtp_crypto_empty(&srtp->rx_policy_neg)) { srtp->bypass_srtp = PJ_TRUE; srtp->peer_use = PJMEDIA_SRTP_DISABLED; if (srtp->session_inited) { pjmedia_transport_srtp_stop(&srtp->base); } return PJ_SUCCESS; } /* Reset probation counts */ srtp->probation_cnt = PROBATION_CNT_INIT; /* Got policy_local & policy_remote, let's initalize the SRTP */ /* Ticket #1075: media_start() is called whenever media description * gets updated, e.g: call hold, however we should restart SRTP only * when the SRTP policy settings are updated. */ if (srtp_crypto_cmp(&srtp->tx_policy_neg, &srtp->tx_policy) || srtp_crypto_cmp(&srtp->rx_policy_neg, &srtp->rx_policy)) { pj_status_t status; status = pjmedia_transport_srtp_start(&srtp->base, &srtp->tx_policy_neg, &srtp->rx_policy_neg); if (status != PJ_SUCCESS) return status; } srtp->bypass_srtp = PJ_FALSE; return PJ_SUCCESS; }
static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pjmedia_sdp_media *m_rem, *m_loc; pj_status_t status; unsigned i; PJ_ASSERT_RETURN(tp && pool && sdp_local && sdp_remote, PJ_EINVAL); if (srtp->bypass_srtp) goto BYPASS_SRTP; m_rem = sdp_remote->media[media_index]; m_loc = sdp_local->media[media_index]; if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) srtp->peer_use = PJMEDIA_SRTP_MANDATORY; else srtp->peer_use = PJMEDIA_SRTP_OPTIONAL; /* For answerer side, this function will just have to start SRTP */ /* Check remote media transport & set local media transport * based on SRTP usage option. */ if (srtp->offerer_side) { if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { if (pjmedia_sdp_media_find_attr(m_rem, &ID_CRYPTO, NULL)) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ESDPINCRYPTO; } goto BYPASS_SRTP; } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { // Regardless the answer's transport type (RTP/AVP or RTP/SAVP), // the answer must be processed through in optional mode. // Please note that at this point transport type is ensured to be // RTP/AVP or RTP/SAVP, see transport_media_create() //if (pj_stricmp(&m_rem->desc.transport, &m_loc->desc.transport)) { //DEACTIVATE_MEDIA(pool, m_loc); //return PJMEDIA_SDP_EINPROTO; //} } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP)) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SDP_EINPROTO; } } } if (srtp->offerer_side) { /* find supported crypto-suite, get the tag, and assign policy_local */ pjmedia_srtp_crypto tmp_tx_crypto; pj_bool_t has_crypto_attr = PJ_FALSE; int rem_tag; for (i=0; i<m_rem->attr_count; ++i) { if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) continue; /* more than one crypto attribute in media answer */ if (has_crypto_attr) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ESDPAMBIGUEANS; } has_crypto_attr = PJ_TRUE; status = parse_attr_crypto(srtp->pool, m_rem->attr[i], &tmp_tx_crypto, &rem_tag); if (status != PJ_SUCCESS) return status; /* our offer tag is always ordered by setting */ if (rem_tag < 1 || rem_tag > (int)srtp->setting.crypto_count) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ESDPINCRYPTOTAG; } /* match the crypto name */ if (pj_stricmp(&tmp_tx_crypto.name, &srtp->setting.crypto[rem_tag-1].name) != 0) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ECRYPTONOTMATCH; } srtp->tx_policy_neg = srtp->setting.crypto[rem_tag-1]; srtp->rx_policy_neg = tmp_tx_crypto; } if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) { /* should never reach here */ goto BYPASS_SRTP; } else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) { if (!has_crypto_attr) goto BYPASS_SRTP; } else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) { if (!has_crypto_attr) { DEACTIVATE_MEDIA(pool, m_loc); return PJMEDIA_SRTP_ESDPREQCRYPTO; } } /* At this point, we get valid rx_policy_neg & tx_policy_neg. */ } /* Reset probation counts */ srtp->probation_cnt = PROBATION_CNT_INIT; /* Got policy_local & policy_remote, let's initalize the SRTP */ status = pjmedia_transport_srtp_start(tp, &srtp->tx_policy_neg, &srtp->rx_policy_neg); if (status != PJ_SUCCESS) return status; goto PROPAGATE_MEDIA_START; BYPASS_SRTP: srtp->bypass_srtp = PJ_TRUE; srtp->peer_use = PJMEDIA_SRTP_DISABLED; PROPAGATE_MEDIA_START: return pjmedia_transport_media_start(srtp->member_tp, pool, sdp_local, sdp_remote, media_index); }
static void pcap2wav(const pj_str_t *codec, const pj_str_t *wav_filename, pjmedia_aud_dev_index dev_id, const pj_str_t *srtp_crypto, const pj_str_t *srtp_key) { const pj_str_t WAV = {".wav", 4}; struct pkt { pj_uint8_t buffer[320]; pjmedia_rtp_hdr *rtp; pj_uint8_t *payload; unsigned payload_len; } pkt0; pjmedia_codec_mgr *cmgr; const pjmedia_codec_info *ci; pjmedia_codec_param param; unsigned samples_per_frame; pj_status_t status; /* Initialize all codecs */ T( pjmedia_codec_register_audio_codecs(app.mept, NULL) ); /* Create SRTP transport is needed */ #if PJMEDIA_HAS_SRTP if (srtp_crypto->slen) { pjmedia_srtp_crypto crypto; pj_bzero(&crypto, sizeof(crypto)); crypto.key = *srtp_key; crypto.name = *srtp_crypto; T( pjmedia_transport_srtp_create(app.mept, NULL, NULL, &app.srtp) ); T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) ); } #else PJ_UNUSED_ARG(srtp_crypto); PJ_UNUSED_ARG(srtp_key); #endif /* Read first packet */ read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp, &pkt0.payload, &pkt0.payload_len, PJ_FALSE); cmgr = pjmedia_endpt_get_codec_mgr(app.mept); /* Get codec info and param for the specified payload type */ app.pt = pkt0.rtp->pt; if (app.pt >=0 && app.pt < 96) { T( pjmedia_codec_mgr_get_codec_info(cmgr, pkt0.rtp->pt, &ci) ); } else { unsigned cnt = 2; const pjmedia_codec_info *info[2]; T( pjmedia_codec_mgr_find_codecs_by_id(cmgr, codec, &cnt, info, NULL) ); if (cnt != 1) err_exit("Codec ID must be specified and unique!", 0); ci = info[0]; } T( pjmedia_codec_mgr_get_default_param(cmgr, ci, ¶m) ); /* Alloc and init codec */ T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) ); T( pjmedia_codec_init(app.codec, app.pool) ); T( pjmedia_codec_open(app.codec, ¶m) ); /* Init audio device or WAV file */ samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000; if (pj_strcmp2(wav_filename, "-") == 0) { pjmedia_aud_param aud_param; /* Open audio device */ T( pjmedia_aud_dev_default_param(dev_id, &aud_param) ); aud_param.dir = PJMEDIA_DIR_PLAYBACK; aud_param.channel_count = ci->channel_cnt; aud_param.clock_rate = ci->clock_rate; aud_param.samples_per_frame = samples_per_frame; T( pjmedia_aud_stream_create(&aud_param, NULL, &play_cb, NULL, &app.aud_strm) ); T( pjmedia_aud_stream_start(app.aud_strm) ); } else if (pj_stristr(wav_filename, &WAV)) { /* Open WAV file */ T( pjmedia_wav_writer_port_create(app.pool, wav_filename->ptr, ci->clock_rate, ci->channel_cnt, samples_per_frame, param.info.pcm_bits_per_sample, 0, 0, &app.wav) ); } else { err_exit("invalid output file", PJ_EINVAL); } /* Loop reading PCAP and writing WAV file */ for (;;) { struct pkt pkt1; pj_timestamp ts; pjmedia_frame frames[16], pcm_frame; short pcm[320]; unsigned i, frame_cnt; long samples_cnt, ts_gap; pj_assert(sizeof(pcm) >= samples_per_frame); /* Parse first packet */ ts.u64 = 0; frame_cnt = PJ_ARRAY_SIZE(frames); T( pjmedia_codec_parse(app.codec, pkt0.payload, pkt0.payload_len, &ts, &frame_cnt, frames) ); /* Decode and write to WAV file */ samples_cnt = 0; for (i=0; i<frame_cnt; ++i) { pjmedia_frame pcm_frame; pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; T( pjmedia_codec_decode(app.codec, &frames[i], (unsigned)pcm_frame.size, &pcm_frame) ); if (app.wav) { T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); } if (app.aud_strm) { T( wait_play(&pcm_frame) ); } samples_cnt += samples_per_frame; } /* Read next packet */ read_rtp(pkt1.buffer, sizeof(pkt1.buffer), &pkt1.rtp, &pkt1.payload, &pkt1.payload_len, PJ_TRUE); /* Fill in the gap (if any) between pkt0 and pkt1 */ ts_gap = pj_ntohl(pkt1.rtp->ts) - pj_ntohl(pkt0.rtp->ts) - samples_cnt; while (ts_gap >= (long)samples_per_frame) { pcm_frame.buf = pcm; pcm_frame.size = samples_per_frame * 2; if (app.codec->op->recover) { T( pjmedia_codec_recover(app.codec, (unsigned)pcm_frame.size, &pcm_frame) ); } else { pj_bzero(pcm_frame.buf, pcm_frame.size); } if (app.wav) { T( pjmedia_port_put_frame(app.wav, &pcm_frame) ); } if (app.aud_strm) { T( wait_play(&pcm_frame) ); } ts_gap -= samples_per_frame; } /* Next */ pkt0 = pkt1; pkt0.rtp = (pjmedia_rtp_hdr*)pkt0.buffer; pkt0.payload = pkt0.buffer + (pkt1.payload - pkt1.buffer); } }
/* * Create stream based on the codec, dir, remote address, etc. */ static pj_status_t create_stream( pj_pool_t *pool, pjmedia_endpt *med_endpt, const pjmedia_vid_codec_info *codec_info, pjmedia_vid_codec_param *codec_param, pjmedia_dir dir, pj_int8_t rx_pt, pj_int8_t tx_pt, pj_uint16_t local_port, const pj_sockaddr_in *rem_addr, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) pj_bool_t use_srtp, const pj_str_t *crypto_suite, const pj_str_t *srtp_tx_key, const pj_str_t *srtp_rx_key, #endif pjmedia_vid_stream **p_stream ) { pjmedia_vid_stream_info info; pjmedia_transport *transport = NULL; pj_status_t status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) pjmedia_transport *srtp_tp = NULL; #endif /* Reset stream info. */ pj_bzero(&info, sizeof(info)); /* Initialize stream info formats */ info.type = PJMEDIA_TYPE_VIDEO; info.dir = dir; info.codec_info = *codec_info; info.tx_pt = (tx_pt == -1)? codec_info->pt : tx_pt; info.rx_pt = (rx_pt == -1)? codec_info->pt : rx_pt; info.ssrc = pj_rand(); if (codec_param) info.codec_param = codec_param; /* Copy remote address */ pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in)); /* If remote address is not set, set to an arbitrary address * (otherwise stream will assert). */ if (info.rem_addr.addr.sa_family == 0) { const pj_str_t addr = pj_str("127.0.0.1"); pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0); } /* Create media transport */ status = pjmedia_transport_udp_create(med_endpt, NULL, local_port, 0, &transport); if (status != PJ_SUCCESS) return status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* Check if SRTP enabled */ if (use_srtp) { pjmedia_srtp_crypto tx_plc, rx_plc; status = pjmedia_transport_srtp_create(med_endpt, transport, NULL, &srtp_tp); if (status != PJ_SUCCESS) return status; pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto)); pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto)); tx_plc.key = *srtp_tx_key; tx_plc.name = *crypto_suite; rx_plc.key = *srtp_rx_key; rx_plc.name = *crypto_suite; status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc); if (status != PJ_SUCCESS) return status; transport = srtp_tp; } #endif /* Now that the stream info is initialized, we can create the * stream. */ status = pjmedia_vid_stream_create( med_endpt, pool, &info, transport, NULL, p_stream); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error creating stream", status); pjmedia_transport_close(transport); return status; } return PJ_SUCCESS; }
/* * This callback is called by transport when incoming rtp is received */ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = (int)size; srtp_err_status_t err; void (*cb)(void*, void*, pj_ssize_t) = NULL; void *cb_data = NULL; if (srtp->bypass_srtp) { srtp->rtp_cb(srtp->user_data, pkt, size); return; } if (size < 0) { return; } /* Give the packet to keying first by invoking its send_rtp() op. * Yes, the usage of send_rtp() is rather hacky, but it is convenient * as the signature suits the purpose and it is ready to use * (no futher registration/setting needed), and it may never be used * by any keying method in the future. */ { unsigned i; pj_status_t status; for (i=0; i < srtp->keying_cnt; i++) { if (!srtp->keying[i]->op->send_rtp) continue; status = pjmedia_transport_send_rtp(srtp->keying[i], pkt, size); if (status != PJ_EIGNORED) { /* Packet is already consumed by the keying method */ return; } } } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); if (srtp->probation_cnt > 0) --srtp->probation_cnt; pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return; } err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (srtp->probation_cnt > 0 && (err == srtp_err_status_replay_old || err == srtp_err_status_replay_fail)) { /* Handle such condition that stream is updated (RTP seq is reinited * & SRTP is restarted), but some old packets are still coming * so SRTP is learning wrong RTP seq. While the newly inited RTP seq * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect() * will return err_status_replay_*. Restarting SRTP can resolve this. */ pjmedia_srtp_crypto tx, rx; pj_status_t status; tx = srtp->tx_policy; rx = srtp->rx_policy; status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp, &tx, &rx); if (status != PJ_SUCCESS) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s", get_libsrtp_errstr(err))); } else if (!srtp->bypass_srtp) { err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); } } if (err != srtp_err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } else { cb = srtp->rtp_cb; cb_data = srtp->user_data; } pj_lock_release(srtp->mutex); if (cb) { (*cb)(cb_data, pkt, len); } }