/** * Rebuffer the frame when encoder and decoder has different ptime * (such as when different iLBC modes are used by local and remote) */ static void rebuffer(pjmedia_stream *stream, pjmedia_frame *frame) { /* How many samples are needed */ unsigned count; /* Normalize frame */ if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) frame->size = 0; /* Remove used frame from the buffer. */ if (stream->enc_buf_pos) { if (stream->enc_buf_count) { pj_memmove(stream->enc_buf, stream->enc_buf + stream->enc_buf_pos, (stream->enc_buf_count << 1)); } stream->enc_buf_pos = 0; } /* Make sure we have space to store the new frame */ pj_assert(stream->enc_buf_count + (frame->size >> 1) < stream->enc_buf_size); /* Append new frame to the buffer */ if (frame->size) { pj_memcpy(stream->enc_buf + stream->enc_buf_count, frame->buf, frame->size); stream->enc_buf_count += (frame->size >> 1); }
void CPjAudioInputEngine::MaiscBufferCopied(TInt aError, const TDesC8 &aBuffer) { lastError_ = aError; if (aError != KErrNone) { snd_perror("Error in MaiscBufferCopied()", aError); return; } if (frameRecBufLen_ || aBuffer.Length() < frameLen_) { pj_memcpy(frameRecBuf_ + frameRecBufLen_, (void*) aBuffer.Ptr(), aBuffer.Length()); frameRecBufLen_ += aBuffer.Length(); } if (frameRecBufLen_) { while (frameRecBufLen_ >= frameLen_) { pjmedia_frame f; f.type = PJMEDIA_FRAME_TYPE_AUDIO; f.buf = frameRecBuf_; f.size = frameLen_; f.timestamp.u32.lo = timeStamp_; f.bit_info = 0; // Call the callback. recCb_(userData_, &f); // Increment timestamp. timeStamp_ += parentStrm_->param.samples_per_frame; frameRecBufLen_ -= frameLen_; pj_memmove(frameRecBuf_, frameRecBuf_+frameLen_, frameRecBufLen_); } } else { pjmedia_frame f; f.type = PJMEDIA_FRAME_TYPE_AUDIO; f.buf = (void*)aBuffer.Ptr(); f.size = aBuffer.Length(); f.timestamp.u32.lo = timeStamp_; f.bit_info = 0; // Call the callback. recCb_(userData_, &f); // Increment timestamp. timeStamp_ += parentStrm_->param.samples_per_frame; } // Record next frame TPtr8 & frm = GetFrame(); TRAPD(err2, iInputStream_->ReadL(frm)); if (err2) { PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()")); } }
PJ_DEF(void) pj_scan_get_unescape( pj_scanner *scanner, const pj_cis_t *spec, pj_str_t *out) { register char *s = scanner->curptr; char *dst = s; pj_assert(pj_cis_match(spec,0)==0); /* Must not match character '%' */ pj_assert(pj_cis_match(spec,'%')==0); /* EOF is detected implicitly */ if (!pj_cis_match(spec, *s) && *s != '%') { pj_scan_syntax_err(scanner); return; } out->ptr = s; do { if (*s == '%') { if (s+3 <= scanner->end && pj_isxdigit(*(s+1)) && pj_isxdigit(*(s+2))) { *dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(s+1)) << 4) + pj_hex_digit_to_val(*(s+2))); ++dst; s += 3; } else { *dst++ = *s++; *dst++ = *s++; break; } } if (pj_cis_match(spec, *s)) { char *start = s; do { ++s; } while (pj_cis_match(spec, *s)); if (dst != start) pj_memmove(dst, start, s-start); dst += (s-start); } } while (*s == '%'); scanner->curptr = s; out->slen = (dst - out->ptr); if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) { pj_scan_skip_whitespace(scanner); } }
/* Internal function to rewrite the format string in SDP attribute rtpmap * and fmtp. */ PJ_INLINE(void) rewrite_pt(pj_pool_t *pool, pj_str_t *attr_val, const pj_str_t *old_pt, const pj_str_t *new_pt) { int len_diff = (int)(new_pt->slen - old_pt->slen); /* Note that attribute value should be null-terminated. */ if (len_diff > 0) { pj_str_t new_val; new_val.ptr = (char*)pj_pool_alloc(pool, attr_val->slen+len_diff+1); new_val.slen = attr_val->slen + len_diff; pj_memcpy(new_val.ptr + len_diff, attr_val->ptr, attr_val->slen + 1); *attr_val = new_val; } else if (len_diff < 0) { attr_val->slen += len_diff; pj_memmove(attr_val->ptr, attr_val->ptr - len_diff, attr_val->slen + 1); } pj_memcpy(attr_val->ptr, new_pt->ptr, new_pt->slen); }
static pj_bool_t tcp_on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { struct tcp_state *st = (struct tcp_state*) pj_activesock_get_user_data(asock); char *next = (char*) data; if (status != PJ_SUCCESS && status != PJ_EPENDING) { PJ_LOG(1,("", " err: status=%d", status)); st->err = PJ_TRUE; return PJ_FALSE; } while (size >= sizeof(struct tcp_pkt)) { struct tcp_pkt *tcp_pkt = (struct tcp_pkt*) next; if (tcp_pkt->signature != SIGNATURE) { PJ_LOG(1,("", " err: invalid signature at seq=%d", st->next_recv_seq)); st->err = PJ_TRUE; return PJ_FALSE; } if (tcp_pkt->seq != st->next_recv_seq) { PJ_LOG(1,("", " err: wrong sequence")); st->err = PJ_TRUE; return PJ_FALSE; } st->next_recv_seq++; next += sizeof(struct tcp_pkt); size -= sizeof(struct tcp_pkt); } if (size) { pj_memmove(data, next, size); *remainder = size; } return PJ_TRUE; }
/* * Handle incoming packet from client. This would have been called by * server upon receiving packet from a listener. */ PJ_DEF(void) pj_turn_allocation_on_rx_client_pkt(pj_turn_allocation *alloc, pj_turn_pkt *pkt) { pj_bool_t is_stun; pj_status_t status; /* Lock this allocation */ pj_lock_acquire(alloc->lock); /* Quickly check if this is STUN message */ is_stun = ((*((pj_uint8_t*)pkt->pkt) & 0xC0) == 0); if (is_stun) { /* * This could be an incoming STUN requests or indications. * Pass this through to the STUN session, which will call * our stun_on_rx_request() or stun_on_rx_indication() * callbacks. * * Note: currently it is necessary to specify the * PJ_STUN_NO_FINGERPRINT_CHECK otherwise the FINGERPRINT * attribute inside STUN Send Indication message will mess up * with fingerprint checking. */ unsigned options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; pj_size_t parsed_len = 0; if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) options |= PJ_STUN_IS_DATAGRAM; status = pj_stun_session_on_rx_pkt(alloc->sess, pkt->pkt, pkt->len, options, NULL, &parsed_len, &pkt->src.clt_addr, pkt->src_addr_len); if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) { pkt->len = 0; } else if (parsed_len > 0) { if (parsed_len == pkt->len) { pkt->len = 0; } else { pj_memmove(pkt->pkt, pkt->pkt+parsed_len, pkt->len - parsed_len); pkt->len -= parsed_len; } } if (status != PJ_SUCCESS) { alloc_err(alloc, "Error handling STUN packet", status); goto on_return; } } else { /* * This is not a STUN packet, must be ChannelData packet. */ pj_turn_channel_data *cd = (pj_turn_channel_data*)pkt->pkt; pj_turn_permission *perm; pj_ssize_t len; pj_assert(sizeof(*cd)==4); /* For UDP check the packet length */ if (alloc->transport->listener->tp_type == PJ_TURN_TP_UDP) { if (pkt->len < pj_ntohs(cd->length)+sizeof(*cd)) { PJ_LOG(4,(alloc->obj_name, "ChannelData from %s discarded: UDP size error", alloc->info)); goto on_return; } } else { pj_assert(!"Unsupported transport"); goto on_return; } perm = lookup_permission_by_chnum(alloc, pj_ntohs(cd->ch_number)); if (!perm) { /* Discard */ PJ_LOG(4,(alloc->obj_name, "ChannelData from %s discarded: ch#0x%x not found", alloc->info, pj_ntohs(cd->ch_number))); goto on_return; } /* Relay the data */ len = pj_ntohs(cd->length); pj_sock_sendto(alloc->relay.tp.sock, cd+1, &len, 0, &perm->hkey.peer_addr, pj_sockaddr_get_len(&perm->hkey.peer_addr)); /* Refresh permission */ refresh_permission(perm); } on_return: /* Release lock */ pj_lock_release(alloc->lock); }
static pj_bool_t ssl_on_data_read(pj_ssl_sock_t *ssock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { struct test_state *st = (struct test_state*) pj_ssl_sock_get_user_data(ssock); PJ_UNUSED_ARG(remainder); PJ_UNUSED_ARG(data); if (size > 0) { pj_size_t consumed; /* Set random remainder */ *remainder = pj_rand() % 100; /* Apply zero remainder if: * - remainder is less than size, or * - connection closed/error * - echo/check_eco set */ if (*remainder > size || status != PJ_SUCCESS || st->echo || st->check_echo) *remainder = 0; consumed = size - *remainder; st->recv += consumed; //printf("%.*s", consumed, (char*)data); pj_memmove(data, (char*)data + consumed, *remainder); /* Echo data when specified to */ if (st->echo) { pj_ssize_t size_ = consumed; status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, data, &size_, 0); if (status != PJ_SUCCESS && status != PJ_EPENDING) { app_perror("...ERROR pj_ssl_sock_send()", status); goto on_return; } if (status == PJ_SUCCESS) st->sent += size_; } /* Verify echoed data when specified to */ if (st->check_echo) { if (!st->check_echo_ptr) st->check_echo_ptr = st->send_str; if (pj_memcmp(st->check_echo_ptr, data, consumed)) { status = PJ_EINVAL; app_perror("...ERROR echoed data not exact", status); goto on_return; } st->check_echo_ptr += consumed; /* Echo received completely */ if (st->send_str_len == st->recv) { pj_ssl_sock_info info; char buf[64]; status = pj_ssl_sock_get_info(ssock, &info); if (status != PJ_SUCCESS) { app_perror("...ERROR pj_ssl_sock_get_info()", status); goto on_return; } pj_sockaddr_print((pj_sockaddr_t*)&info.local_addr, buf, sizeof(buf), 1); PJ_LOG(3, ("", "...%s successfully recv %d bytes echo", buf, st->recv)); st->done = PJ_TRUE; } } } if (status != PJ_SUCCESS) { if (status == PJ_EEOF) { status = PJ_SUCCESS; st->done = PJ_TRUE; } else { app_perror("...ERROR ssl_on_data_read()", status); } } on_return: st->err = status; if (st->err != PJ_SUCCESS || st->done) { pj_ssl_sock_close(ssock); if (!st->is_server) clients_num--; return PJ_FALSE; } return PJ_TRUE; }
/* * Notification from ioqueue when incoming UDP packet is received. */ static pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { pj_turn_sock *turn_sock; pj_bool_t ret = PJ_TRUE; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); pj_grp_lock_acquire(turn_sock->grp_lock); if (status == PJ_SUCCESS && turn_sock->sess && !turn_sock->is_destroying) { /* Report incoming packet to TURN session, repeat while we have * "packet" in the buffer (required for stream-oriented transports) */ unsigned pkt_len; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Incoming data, %lu bytes total buffer", size)); while ((pkt_len=has_packet(turn_sock, data, size)) != 0) { pj_size_t parsed_len; //const pj_uint8_t *pkt = (const pj_uint8_t*)data; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Packet start: %02X %02X %02X %02X", // pkt[0], pkt[1], pkt[2], pkt[3])); //PJ_LOG(5,(turn_sock->pool->obj_name, // "Processing %lu bytes packet of %lu bytes total buffer", // pkt_len, size)); parsed_len = (unsigned)size; pj_turn_session_on_rx_pkt(turn_sock->sess, data, size, &parsed_len); /* parsed_len may be zero if we have parsing error, so use our * previous calculation to exhaust the bad packet. */ if (parsed_len == 0) parsed_len = pkt_len; if (parsed_len < (unsigned)size) { *remainder = size - parsed_len; pj_memmove(data, ((char*)data)+parsed_len, *remainder); } else { *remainder = 0; } size = *remainder; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Buffer size now %lu bytes", size)); } } else if (status != PJ_SUCCESS && turn_sock->conn_type != PJ_TURN_TP_UDP) { sess_fail(turn_sock, "TCP connection closed", status); ret = PJ_FALSE; goto on_return; } on_return: pj_grp_lock_release(turn_sock->grp_lock); return ret; }
/* * Encode frame. */ static pj_status_t amr_codec_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { struct amr_data *amr_data = (struct amr_data*) codec->codec_data; unsigned char *bitstream; pj_int16_t *speech; unsigned nsamples, samples_per_frame; enum {MAX_FRAMES_PER_PACKET = 16}; pjmedia_frame frames[MAX_FRAMES_PER_PACKET]; pj_uint8_t *p; unsigned i, out_size = 0, nframes = 0; pj_size_t payload_len; unsigned dtx_cnt, sid_cnt; pj_status_t status; pj_assert(amr_data != NULL); PJ_ASSERT_RETURN(input && output, PJ_EINVAL); nsamples = input->size >> 1; samples_per_frame = amr_data->clock_rate * FRAME_LENGTH_MS / 1000; PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0, PJMEDIA_CODEC_EPCMFRMINLEN); nframes = nsamples / samples_per_frame; PJ_ASSERT_RETURN(nframes <= MAX_FRAMES_PER_PACKET, PJMEDIA_CODEC_EFRMTOOSHORT); /* Encode the frames */ speech = (pj_int16_t*)input->buf; bitstream = (unsigned char*)output->buf; while (nsamples >= samples_per_frame) { int size; if (amr_data->enc_setting.amr_nb) { #ifdef USE_AMRNB size = Encoder_Interface_Encode (amr_data->encoder, amr_data->enc_mode, speech, bitstream, 0); #else size = 0; #endif } else { #ifdef USE_AMRWB size = E_IF_encode (amr_data->encoder, amr_data->enc_mode, speech, bitstream, 0); #else size = 0; #endif } if (size == 0) { output->size = 0; output->buf = NULL; output->type = PJMEDIA_FRAME_TYPE_NONE; TRACE_((THIS_FILE, "AMR encode() failed")); return PJMEDIA_CODEC_EFAILED; } nsamples -= samples_per_frame; speech += samples_per_frame; bitstream += size; out_size += size; TRACE_((THIS_FILE, "AMR encode(): mode=%d, size=%d", amr_data->enc_mode, out_size)); } /* Pack payload */ p = (pj_uint8_t*)output->buf + output_buf_len - out_size; pj_memmove(p, output->buf, out_size); dtx_cnt = sid_cnt = 0; for (i = 0; i < nframes; ++i) { pjmedia_codec_amr_bit_info *info = (pjmedia_codec_amr_bit_info*) &frames[i].bit_info; info->frame_type = (pj_uint8_t)((*p >> 3) & 0x0F); info->good_quality = (pj_uint8_t)((*p >> 2) & 0x01); info->mode = (pj_int8_t)amr_data->enc_mode; info->start_bit = 0; frames[i].buf = p + 1; if (amr_data->enc_setting.amr_nb) { frames[i].size = (info->frame_type <= 8)? pjmedia_codec_amrnb_framelen[info->frame_type] : 0; } else { frames[i].size = (info->frame_type <= 9)? pjmedia_codec_amrwb_framelen[info->frame_type] : 0; } p += frames[i].size + 1; /* Count the number of SID and DTX frames */ if (info->frame_type == 15) /* DTX*/ ++dtx_cnt; else if (info->frame_type == 8) /* SID */ ++sid_cnt; } /* VA generates DTX frames as DTX+SID frames switching quickly and it * seems that the SID frames occur too often (assuming the purpose is * only for keeping NAT alive?). So let's modify the behavior a bit. * Only an SID frame will be sent every PJMEDIA_CODEC_MAX_SILENCE_PERIOD * milliseconds. */ if (sid_cnt + dtx_cnt == nframes) { pj_int32_t dtx_duration; dtx_duration = pj_timestamp_diff32(&amr_data->last_tx, &input->timestamp); if (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 || dtx_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD* amr_data->clock_rate/1000) { output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; output->timestamp = input->timestamp; return PJ_SUCCESS; } } payload_len = output_buf_len; status = pjmedia_codec_amr_pack(frames, nframes, &amr_data->enc_setting, output->buf, &payload_len); if (status != PJ_SUCCESS) { output->size = 0; output->buf = NULL; output->type = PJMEDIA_FRAME_TYPE_NONE; TRACE_((THIS_FILE, "Failed to pack AMR payload, status=%d", status)); return status; } output->size = payload_len; output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->timestamp = input->timestamp; amr_data->last_tx = input->timestamp; return PJ_SUCCESS; }
/* * Initialize and start SRTP session with the given parameters. */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start( pjmedia_transport *tp, const pjmedia_srtp_crypto *tx, const pjmedia_srtp_crypto *rx) { transport_srtp *srtp = (transport_srtp*) tp; srtp_policy_t tx_; srtp_policy_t rx_; err_status_t err; int cr_tx_idx = 0; int au_tx_idx = 0; int cr_rx_idx = 0; int au_rx_idx = 0; int crypto_suites_cnt; PJ_ASSERT_RETURN(tp && tx && rx, PJ_EINVAL); if (srtp->session_inited) { pjmedia_transport_srtp_stop(tp); } crypto_suites_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]); /* Get encryption and authentication method */ cr_tx_idx = au_tx_idx = get_crypto_idx(&tx->name); if (tx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) cr_tx_idx = 0; if (tx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) au_tx_idx = 0; cr_rx_idx = au_rx_idx = get_crypto_idx(&rx->name); if (rx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) cr_rx_idx = 0; if (rx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) au_rx_idx = 0; /* Check whether the crypto-suite requested is supported */ if (cr_tx_idx == -1 || cr_rx_idx == -1 || au_tx_idx == -1 || au_rx_idx == -1) return PJMEDIA_SRTP_ENOTSUPCRYPTO; /* If all options points to 'NULL' method, just bypass SRTP */ if (cr_tx_idx == 0 && cr_rx_idx == 0 && au_tx_idx == 0 && au_rx_idx == 0) { srtp->bypass_srtp = PJ_TRUE; return PJ_SUCCESS; } /* Check key length */ if (tx->key.slen != (pj_ssize_t)crypto_suites[cr_tx_idx].cipher_key_len || rx->key.slen != (pj_ssize_t)crypto_suites[cr_rx_idx].cipher_key_len) return PJMEDIA_SRTP_EINKEYLEN; /* Init transmit direction */ pj_bzero(&tx_, sizeof(srtp_policy_t)); pj_memmove(srtp->tx_key, tx->key.ptr, tx->key.slen); if (cr_tx_idx && au_tx_idx) tx_.rtp.sec_serv = sec_serv_conf_and_auth; else if (cr_tx_idx) tx_.rtp.sec_serv = sec_serv_conf; else if (au_tx_idx) tx_.rtp.sec_serv = sec_serv_auth; else tx_.rtp.sec_serv = sec_serv_none; tx_.key = (uint8_t*)srtp->tx_key; tx_.ssrc.type = ssrc_any_outbound; tx_.ssrc.value = 0; tx_.rtp.cipher_type = crypto_suites[cr_tx_idx].cipher_type; tx_.rtp.cipher_key_len = crypto_suites[cr_tx_idx].cipher_key_len; tx_.rtp.auth_type = crypto_suites[au_tx_idx].auth_type; tx_.rtp.auth_key_len = crypto_suites[au_tx_idx].auth_key_len; tx_.rtp.auth_tag_len = crypto_suites[au_tx_idx].srtp_auth_tag_len; tx_.rtcp = tx_.rtp; tx_.rtcp.auth_tag_len = crypto_suites[au_tx_idx].srtcp_auth_tag_len; tx_.next = NULL; err = srtp_create(&srtp->srtp_tx_ctx, &tx_); if (err != err_status_ok) { return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } srtp->tx_policy = *tx; pj_strset(&srtp->tx_policy.key, srtp->tx_key, tx->key.slen); srtp->tx_policy.name=pj_str(crypto_suites[get_crypto_idx(&tx->name)].name); /* Init receive direction */ pj_bzero(&rx_, sizeof(srtp_policy_t)); pj_memmove(srtp->rx_key, rx->key.ptr, rx->key.slen); if (cr_rx_idx && au_rx_idx) rx_.rtp.sec_serv = sec_serv_conf_and_auth; else if (cr_rx_idx) rx_.rtp.sec_serv = sec_serv_conf; else if (au_rx_idx) rx_.rtp.sec_serv = sec_serv_auth; else rx_.rtp.sec_serv = sec_serv_none; rx_.key = (uint8_t*)srtp->rx_key; rx_.ssrc.type = ssrc_any_inbound; rx_.ssrc.value = 0; rx_.rtp.sec_serv = crypto_suites[cr_rx_idx].service; rx_.rtp.cipher_type = crypto_suites[cr_rx_idx].cipher_type; rx_.rtp.cipher_key_len = crypto_suites[cr_rx_idx].cipher_key_len; rx_.rtp.auth_type = crypto_suites[au_rx_idx].auth_type; rx_.rtp.auth_key_len = crypto_suites[au_rx_idx].auth_key_len; rx_.rtp.auth_tag_len = crypto_suites[au_rx_idx].srtp_auth_tag_len; rx_.rtcp = rx_.rtp; rx_.rtcp.auth_tag_len = crypto_suites[au_rx_idx].srtcp_auth_tag_len; rx_.next = NULL; err = srtp_create(&srtp->srtp_rx_ctx, &rx_); if (err != err_status_ok) { srtp_dealloc(srtp->srtp_tx_ctx); return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } srtp->rx_policy = *rx; pj_strset(&srtp->rx_policy.key, srtp->rx_key, rx->key.slen); srtp->rx_policy.name=pj_str(crypto_suites[get_crypto_idx(&rx->name)].name); /* Declare SRTP session initialized */ srtp->session_inited = PJ_TRUE; PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", srtp->tx_policy.name.ptr, octet_string_hex_string(tx->key.ptr, tx->key.slen))); if (srtp->tx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name,"TX: disable%s%s", (cr_tx_idx?"":" enc"), (au_tx_idx?"":" auth"))); } PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", srtp->rx_policy.name.ptr, octet_string_hex_string(rx->key.ptr, rx->key.slen))); if (srtp->rx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", (cr_rx_idx?"":" enc"), (au_rx_idx?"":" auth"))); } return PJ_SUCCESS; }
/* * Initialize and start SRTP session with the given parameters. */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_start( pjmedia_transport *tp, const pjmedia_srtp_crypto *tx, const pjmedia_srtp_crypto *rx) { transport_srtp *srtp = (transport_srtp*) tp; srtp_policy_t tx_; srtp_policy_t rx_; err_status_t err; int cr_tx_idx = 0; int au_tx_idx = 0; int cr_rx_idx = 0; int au_rx_idx = 0; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(tp && tx && rx, PJ_EINVAL); pj_lock_acquire(srtp->mutex); if (srtp->session_inited) { pjmedia_transport_srtp_stop(tp); } /* Get encryption and authentication method */ cr_tx_idx = au_tx_idx = get_crypto_idx(&tx->name); if (tx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) cr_tx_idx = 0; if (tx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) au_tx_idx = 0; cr_rx_idx = au_rx_idx = get_crypto_idx(&rx->name); if (rx->flags & PJMEDIA_SRTP_NO_ENCRYPTION) cr_rx_idx = 0; if (rx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION) au_rx_idx = 0; /* Check whether the crypto-suite requested is supported */ if (cr_tx_idx == -1 || cr_rx_idx == -1 || au_tx_idx == -1 || au_rx_idx == -1) { status = PJMEDIA_SRTP_ENOTSUPCRYPTO; goto on_return; } /* If all options points to 'NULL' method, just bypass SRTP */ if (cr_tx_idx == 0 && cr_rx_idx == 0 && au_tx_idx == 0 && au_rx_idx == 0) { srtp->bypass_srtp = PJ_TRUE; goto on_return; } /* Check key length */ if (tx->key.slen != (pj_ssize_t)crypto_suites[cr_tx_idx].cipher_key_len || rx->key.slen != (pj_ssize_t)crypto_suites[cr_rx_idx].cipher_key_len) { status = PJMEDIA_SRTP_EINKEYLEN; goto on_return; } /* Init transmit direction */ pj_bzero(&tx_, sizeof(srtp_policy_t)); pj_memmove(srtp->tx_key, tx->key.ptr, tx->key.slen); if (cr_tx_idx && au_tx_idx) tx_.rtp.sec_serv = sec_serv_conf_and_auth; else if (cr_tx_idx) tx_.rtp.sec_serv = sec_serv_conf; else if (au_tx_idx) tx_.rtp.sec_serv = sec_serv_auth; else tx_.rtp.sec_serv = sec_serv_none; tx_.key = (uint8_t*)srtp->tx_key; tx_.ssrc.type = ssrc_any_outbound; tx_.ssrc.value = 0; tx_.rtp.cipher_type = crypto_suites[cr_tx_idx].cipher_type; tx_.rtp.cipher_key_len = crypto_suites[cr_tx_idx].cipher_key_len; tx_.rtp.auth_type = crypto_suites[au_tx_idx].auth_type; tx_.rtp.auth_key_len = crypto_suites[au_tx_idx].auth_key_len; tx_.rtp.auth_tag_len = crypto_suites[au_tx_idx].srtp_auth_tag_len; tx_.rtcp = tx_.rtp; tx_.rtcp.auth_tag_len = crypto_suites[au_tx_idx].srtcp_auth_tag_len; tx_.next = NULL; err = srtp_create(&srtp->srtp_tx_ctx, &tx_); if (err != err_status_ok) { status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); goto on_return; } srtp->tx_policy = *tx; pj_strset(&srtp->tx_policy.key, srtp->tx_key, tx->key.slen); srtp->tx_policy.name=pj_str(crypto_suites[get_crypto_idx(&tx->name)].name); /* Init receive direction */ pj_bzero(&rx_, sizeof(srtp_policy_t)); pj_memmove(srtp->rx_key, rx->key.ptr, rx->key.slen); if (cr_rx_idx && au_rx_idx) rx_.rtp.sec_serv = sec_serv_conf_and_auth; else if (cr_rx_idx) rx_.rtp.sec_serv = sec_serv_conf; else if (au_rx_idx) rx_.rtp.sec_serv = sec_serv_auth; else rx_.rtp.sec_serv = sec_serv_none; rx_.key = (uint8_t*)srtp->rx_key; rx_.ssrc.type = ssrc_any_inbound; rx_.ssrc.value = 0; rx_.rtp.sec_serv = crypto_suites[cr_rx_idx].service; rx_.rtp.cipher_type = crypto_suites[cr_rx_idx].cipher_type; rx_.rtp.cipher_key_len = crypto_suites[cr_rx_idx].cipher_key_len; rx_.rtp.auth_type = crypto_suites[au_rx_idx].auth_type; rx_.rtp.auth_key_len = crypto_suites[au_rx_idx].auth_key_len; rx_.rtp.auth_tag_len = crypto_suites[au_rx_idx].srtp_auth_tag_len; rx_.rtcp = rx_.rtp; rx_.rtcp.auth_tag_len = crypto_suites[au_rx_idx].srtcp_auth_tag_len; rx_.next = NULL; err = srtp_create(&srtp->srtp_rx_ctx, &rx_); if (err != err_status_ok) { srtp_dealloc(srtp->srtp_tx_ctx); status = PJMEDIA_ERRNO_FROM_LIBSRTP(err); goto on_return; } srtp->rx_policy = *rx; pj_strset(&srtp->rx_policy.key, srtp->rx_key, rx->key.slen); srtp->rx_policy.name=pj_str(crypto_suites[get_crypto_idx(&rx->name)].name); /* Declare SRTP session initialized */ srtp->session_inited = PJ_TRUE; /* Logging stuffs */ #if PJ_LOG_MAX_LEVEL >= 5 { char b64[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)]; int b64_len; /* TX crypto and key */ b64_len = sizeof(b64); status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, tx->key.slen, b64, &b64_len); if (status != PJ_SUCCESS) b64_len = pj_ansi_sprintf(b64, "--key too long--"); else b64[b64_len] = '\0'; PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", srtp->tx_policy.name.ptr, b64)); if (srtp->tx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name, "TX: disable%s%s", (cr_tx_idx?"":" enc"), (au_tx_idx?"":" auth"))); } /* RX crypto and key */ b64_len = sizeof(b64); status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, rx->key.slen, b64, &b64_len); if (status != PJ_SUCCESS) b64_len = pj_ansi_sprintf(b64, "--key too long--"); else b64[b64_len] = '\0'; PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", srtp->rx_policy.name.ptr, b64)); if (srtp->rx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", (cr_rx_idx?"":" enc"), (au_rx_idx?"":" auth"))); } } #endif on_return: pj_lock_release(srtp->mutex); return status; }
/* * Notification from ioqueue when incoming UDP packet is received. */ static pj_bool_t on_data_read(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status, pj_size_t *remainder) { pj_turn_sock *turn_sock; pj_bool_t ret = PJ_TRUE; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); pj_lock_acquire(turn_sock->lock); if (status == PJ_SUCCESS && turn_sock->sess) { /* Report incoming packet to TURN session, repeat while we have * "packet" in the buffer (required for stream-oriented transports) */ unsigned pkt_len; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Incoming data, %lu bytes total buffer", size)); while ((pkt_len=has_packet(turn_sock, data, size)) != 0) { pj_size_t parsed_len; //const pj_uint8_t *pkt = (const pj_uint8_t*)data; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Packet start: %02X %02X %02X %02X", // pkt[0], pkt[1], pkt[2], pkt[3])); //PJ_LOG(5,(turn_sock->pool->obj_name, // "Processing %lu bytes packet of %lu bytes total buffer", // pkt_len, size)); parsed_len = (unsigned)size; pj_turn_session_on_rx_pkt(turn_sock->sess, data, size, &parsed_len); /* parsed_len may be zero if we have parsing error, so use our * previous calculation to exhaust the bad packet. */ if (parsed_len == 0) parsed_len = pkt_len; if (parsed_len < (unsigned)size) { *remainder = size - parsed_len; pj_memmove(data, ((char*)data)+parsed_len, *remainder); } else { *remainder = 0; } size = *remainder; //PJ_LOG(5,(turn_sock->pool->obj_name, // "Buffer size now %lu bytes", size)); } /* Assigned remainder as size. Because ioqueue may skip the packet if never enter while loop. */ if (pkt_len == 0) *remainder = size; PJ_LOG(5, (__FILE__, "on_data_read() leaving still remainder=[%d].", *remainder)); } else if (status != PJ_SUCCESS && turn_sock->conn_type != PJ_TURN_TP_UDP) { // DEAN don't destroy TURN session, if connection aborted. // To avoid the situation of ip changing caused crash. if (status != 130053) { PJ_LOG(1, ("turn_sock.c", "!!! TURN DEALLOCATE !!! in on_data_read() read failed status=%d", status)); sess_fail(turn_sock, "TURN TCP connection closed", status); } ret = PJ_FALSE; goto on_return; } on_return: pj_lock_release(turn_sock->lock); return ret; }
static void JNICALL OnGetFrame(JNIEnv *env, jobject obj, jbyteArray data, jint length, jlong user_data) { and_stream *strm = *(and_stream**)&user_data; pjmedia_frame f; pj_uint8_t *Y, *U, *V; pj_status_t status; void *frame_buf, *data_buf; strm->frame_ts.u64 += strm->ts_inc; if (!strm->vid_cb.capture_cb) return; if (strm->thread_initialized == 0 || !pj_thread_is_registered()) { pj_status_t status; pj_bzero(strm->thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("and_cam", strm->thread_desc, &strm->thread); if (status != PJ_SUCCESS) return; strm->thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Android camera thread registered")); } f.type = PJMEDIA_FRAME_TYPE_VIDEO; f.size = length; f.timestamp.u64 = strm->frame_ts.u64; f.buf = data_buf = (*env)->GetByteArrayElements(env, data, 0); Y = (pj_uint8_t*)f.buf; U = Y + strm->vafp.plane_bytes[0]; V = U + strm->vafp.plane_bytes[1]; /* Convert NV21 -> I420, i.e: separate V/U interleaved data plane * into U & V planes. */ if (strm->convert_to_i420 == 1) { pj_uint8_t *src = U; pj_uint8_t *dst_u = U; pj_uint8_t *end_u = U + strm->vafp.plane_bytes[1]; pj_uint8_t *dst_v = strm->convert_buf; while (dst_u < end_u) { *dst_v++ = *src++; *dst_u++ = *src++; } pj_memcpy(V, strm->convert_buf, strm->vafp.plane_bytes[2]); } /* Convert YV12 -> I420, i.e: swap U & V planes. We also need to * strip out padding, if any. */ else if (strm->convert_to_i420 == 2) { int y_stride = ALIGN16(strm->vafp.size.w); int uv_stride = ALIGN16(strm->vafp.size.w/2); /* Strip out Y padding */ if (y_stride > strm->vafp.size.w) { int i; pj_uint8_t *src = Y + y_stride; pj_uint8_t *dst = Y + strm->vafp.size.w; for (i = 1; i < strm->vafp.size.h; ++i) { memmove(dst, src, strm->vafp.size.w); src += y_stride; dst += strm->vafp.size.w; } } /* Swap U & V planes */ if (uv_stride == strm->vafp.size.w/2) { /* No padding, note Y plane should be no padding too! */ pj_assert(y_stride == strm->vafp.size.w); pj_memcpy(strm->convert_buf, U, strm->vafp.plane_bytes[1]); pj_memmove(U, V, strm->vafp.plane_bytes[1]); pj_memcpy(V, strm->convert_buf, strm->vafp.plane_bytes[1]); } else if (uv_stride > strm->vafp.size.w/2) { /* Strip & copy V plane into conversion buffer */ pj_uint8_t *src = Y + y_stride*strm->vafp.size.h; pj_uint8_t *dst = strm->convert_buf; unsigned dst_stride = strm->vafp.size.w/2; int i; for (i = 0; i < strm->vafp.size.h/2; ++i) { memmove(dst, src, dst_stride); src += uv_stride; dst += dst_stride; } /* Strip U plane */ dst = U; for (i = 0; i < strm->vafp.size.h/2; ++i) { memmove(dst, src, dst_stride); src += uv_stride; dst += dst_stride; } /* Get V plane data from conversion buffer */ pj_memcpy(V, strm->convert_buf, strm->vafp.plane_bytes[2]); } } status = pjmedia_vid_dev_conv_resize_and_rotate(&strm->conv, f.buf, &frame_buf); if (status == PJ_SUCCESS) { f.buf = frame_buf; } (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &f); (*env)->ReleaseByteArrayElements(env, data, data_buf, JNI_ABORT); }
/* * This callback is called by UDP listener on incoming packet. This is * the first entry for incoming packet (from client) to the server. From * here, the packet may be handed over to an allocation if an allocation * is found for the client address, or handed over to owned STUN session * if an allocation is not found. */ PJ_DEF(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv, pj_turn_pkt *pkt) { pj_turn_allocation *alloc; /* Get TURN allocation from the source address */ pj_lock_acquire(srv->core.lock); alloc = (pj_turn_allocation*) pj_hash_get(srv->tables.alloc, &pkt->src, sizeof(pkt->src), NULL); pj_lock_release(srv->core.lock); /* If allocation is found, just hand over the packet to the * allocation. */ if (alloc) { pj_turn_allocation_on_rx_client_pkt(alloc, pkt); } else { /* Otherwise this is a new client */ unsigned options; pj_size_t parsed_len; pj_status_t status; /* Check that this is a STUN message */ options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK; if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) options |= PJ_STUN_IS_DATAGRAM; status = pj_stun_msg_check(pkt->pkt, pkt->len, options); if (status != PJ_SUCCESS) { /* If the first byte are not STUN, drop the packet. First byte * of STUN message is always 0x00 or 0x01. Otherwise wait for * more data as the data might have come from TCP. * * Also drop packet if it's unreasonably too big, as this might * indicate invalid data that's building up in the buffer. * * Or if packet is a datagram. */ if ((*pkt->pkt != 0x00 && *pkt->pkt != 0x01) || pkt->len > 1600 || (options & PJ_STUN_IS_DATAGRAM)) { char errmsg[PJ_ERR_MSG_SIZE]; char ip[PJ_INET6_ADDRSTRLEN+10]; pkt->len = 0; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(5,(srv->obj_name, "Non-STUN packet from %s is dropped: %s", pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3), errmsg)); } return; } /* Special handling for Binding Request. We won't give it to the * STUN session since this request is not authenticated. */ if (pkt->pkt[1] == 1) { handle_binding_request(pkt, options); return; } /* Hand over processing to STUN session. This will trigger * on_rx_stun_request() callback to be called if the STUN * message is a request. */ options &= ~PJ_STUN_CHECK_PACKET; parsed_len = 0; status = pj_stun_session_on_rx_pkt(srv->core.stun_sess, pkt->pkt, pkt->len, options, pkt->transport, &parsed_len, &pkt->src.clt_addr, pkt->src_addr_len); if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; char ip[PJ_INET6_ADDRSTRLEN+10]; pj_strerror(status, errmsg, sizeof(errmsg)); PJ_LOG(5,(srv->obj_name, "Error processing STUN packet from %s: %s", pj_sockaddr_print(&pkt->src.clt_addr, ip, sizeof(ip), 3), errmsg)); } if (pkt->transport->listener->tp_type == PJ_TURN_TP_UDP) { pkt->len = 0; } else if (parsed_len > 0) { if (parsed_len == pkt->len) { pkt->len = 0; } else { pj_memmove(pkt->pkt, pkt->pkt+parsed_len, pkt->len - parsed_len); pkt->len -= parsed_len; } } } }