/* Public function to parse multipart message bodies into its parts */ PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool, char *buf, pj_size_t len, const pjsip_media_type *ctype, unsigned options) { pj_str_t boundary, delim; char *curptr, *endptr; const pjsip_param *ctype_param; const pj_str_t STR_BOUNDARY = { "boundary", 8 }; pjsip_msg_body *body = NULL; PJ_ASSERT_RETURN(pool && buf && len && ctype && !options, NULL); TRACE_((THIS_FILE, "Started parsing multipart body")); /* Get the boundary value in the ctype */ boundary.ptr = NULL; boundary.slen = 0; ctype_param = pjsip_param_find(&ctype->param, &STR_BOUNDARY); if (ctype_param) { boundary = ctype_param->value; if (boundary.slen>2 && *boundary.ptr=='"') { /* Remove quote */ boundary.ptr++; boundary.slen -= 2; } TRACE_((THIS_FILE, "Boundary is specified: '%.*s'", (int)boundary.slen, boundary.ptr)); } if (!boundary.slen) { /* Boundary not found or not specified. Try to be clever, get * the boundary from the body. */ char *p=buf, *end=buf+len; PJ_LOG(4,(THIS_FILE, "Warning: boundary parameter not found or " "not specified when parsing multipart body")); /* Find the first "--". This "--" must be right after a CRLF, unless * it really appears at the start of the buffer. */ for (;;) { while (p!=end && *p!='-') ++p; if (p!=end && *(p+1)=='-' && ((p>buf && *(p-1)=='\n') || (p==buf))) { p+=2; break; } else { ++p; } } if (p==end) { /* Unable to determine boundary. Maybe this is not a multipart * message? */ PJ_LOG(4,(THIS_FILE, "Error: multipart boundary not specified and" " unable to calculate from the body")); return NULL; } boundary.ptr = p; while (p!=end && !pj_isspace(*p)) ++p; boundary.slen = p - boundary.ptr; TRACE_((THIS_FILE, "Boundary is calculated: '%.*s'", (int)boundary.slen, boundary.ptr)); } /* Build the delimiter: * delimiter = "--" boundary */ delim.slen = boundary.slen+2; delim.ptr = (char*)pj_pool_alloc(pool, (int)delim.slen); delim.ptr[0] = '-'; delim.ptr[1] = '-'; pj_memcpy(delim.ptr+2, boundary.ptr, boundary.slen); /* Start parsing the body, skip until the first delimiter. */ curptr = buf; endptr = buf + len; { pj_str_t body; body.ptr = buf; body.slen = len; curptr = pj_strstr(&body, &delim); if (!curptr) return NULL; } body = pjsip_multipart_create(pool, ctype, &boundary); for (;;) { char *start_body, *end_body; pjsip_multipart_part *part; /* Eat the boundary */ curptr += delim.slen; if (*curptr=='-' && curptr<endptr-1 && *(curptr+1)=='-') { /* Found the closing delimiter */ curptr += 2; break; } /* Optional whitespace after delimiter */ while (curptr!=endptr && IS_SPACE(*curptr)) ++curptr; /* Mandatory CRLF */ if (*curptr=='\r') ++curptr; if (*curptr!='\n') { /* Expecting a newline here */ return NULL; } ++curptr; /* We now in the start of the body */ start_body = curptr; /* Find the next delimiter */ { pj_str_t subbody; subbody.ptr = curptr; subbody.slen = endptr - curptr; curptr = pj_strstr(&subbody, &delim); if (!curptr) { /* We're really expecting end delimiter to be found. */ return NULL; } } end_body = curptr; /* The newline preceeding the delimiter is conceptually part of * the delimiter, so trim it from the body. */ if (*(end_body-1) == '\n') --end_body; if (*(end_body-1) == '\r') --end_body; /* Now that we have determined the part's boundary, parse it * to get the header and body part of the part. */ part = parse_multipart_part(pool, start_body, end_body - start_body, ctype); if (part) { pjsip_multipart_add_part(pool, body, part); } } return body; }
/* * Create relay. */ static pj_status_t create_relay(pj_turn_srv *srv, pj_turn_allocation *alloc, const pj_stun_msg *msg, const alloc_request *req, pj_turn_relay_res *relay) { enum { RETRY = 40 }; pj_pool_t *pool = alloc->pool; int retry, retry_max, sock_type; pj_ioqueue_callback icb; int af, namelen; pj_stun_string_attr *sa; pj_status_t status; pj_bzero(relay, sizeof(*relay)); relay->allocation = alloc; relay->tp.sock = PJ_INVALID_SOCKET; /* TODO: get the requested address family from somewhere */ af = alloc->transport->listener->addr.addr.sa_family; /* Save realm */ sa = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP); pj_strdup(pool, &relay->realm, &sa->value); /* Save username */ sa = (pj_stun_string_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); PJ_ASSERT_RETURN(sa, PJ_EINVALIDOP); pj_strdup(pool, &relay->user, &sa->value); /* Lifetime and timeout */ relay->lifetime = req->lifetime; pj_timer_entry_init(&relay->timer, TIMER_ID_NONE, relay, &relay_timeout_cb); resched_timeout(alloc); /* Transport type */ relay->hkey.tp_type = req->tp_type; /* Create the socket */ if (req->tp_type == PJ_TURN_TP_UDP) { sock_type = pj_SOCK_DGRAM(); } else if (req->tp_type == PJ_TURN_TP_TCP) { sock_type = pj_SOCK_STREAM(); } else { pj_assert(!"Unknown transport"); return PJ_EINVALIDOP; } status = pj_sock_socket(af, sock_type, 0, &relay->tp.sock); if (status != PJ_SUCCESS) { pj_bzero(relay, sizeof(*relay)); return status; } /* Find suitable port for this allocation */ if (req->rpp_port) { retry_max = 1; } else { retry_max = RETRY; } for (retry=0; retry<retry_max; ++retry) { pj_uint16_t port; pj_sockaddr bound_addr; pj_lock_acquire(srv->core.lock); if (req->rpp_port) { port = (pj_uint16_t) req->rpp_port; } else if (req->tp_type == PJ_TURN_TP_UDP) { port = (pj_uint16_t) srv->ports.next_udp++; if (srv->ports.next_udp > srv->ports.max_udp) srv->ports.next_udp = srv->ports.min_udp; } else if (req->tp_type == PJ_TURN_TP_TCP) { port = (pj_uint16_t) srv->ports.next_tcp++; if (srv->ports.next_tcp > srv->ports.max_tcp) srv->ports.next_tcp = srv->ports.min_tcp; } else { pj_assert(!"Invalid transport"); port = 0; } pj_lock_release(srv->core.lock); pj_sockaddr_init(af, &bound_addr, NULL, port); status = pj_sock_bind(relay->tp.sock, &bound_addr, pj_sockaddr_get_len(&bound_addr)); if (status == PJ_SUCCESS) break; } if (status != PJ_SUCCESS) { /* Unable to allocate port */ PJ_LOG(4,(THIS_FILE, "Unable to allocate relay, giving up: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } /* Init relay key */ namelen = sizeof(relay->hkey.addr); status = pj_sock_getsockname(relay->tp.sock, &relay->hkey.addr, &namelen); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "pj_sock_getsockname() failed: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } if (!pj_sockaddr_has_addr(&relay->hkey.addr)) { pj_sockaddr_copy_addr(&relay->hkey.addr, &alloc->transport->listener->addr); } if (!pj_sockaddr_has_addr(&relay->hkey.addr)) { pj_sockaddr tmp_addr; pj_gethostip(af, &tmp_addr); pj_sockaddr_copy_addr(&relay->hkey.addr, &tmp_addr); } /* Init ioqueue */ pj_bzero(&icb, sizeof(icb)); icb.on_read_complete = &on_rx_from_peer; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, relay->tp.sock, relay, &icb, &relay->tp.key); if (status != PJ_SUCCESS) { PJ_LOG(4,(THIS_FILE, "pj_ioqueue_register_sock() failed: err %d", status)); pj_sock_close(relay->tp.sock); relay->tp.sock = PJ_INVALID_SOCKET; return status; } /* Kick off pending read operation */ pj_ioqueue_op_key_init(&relay->tp.read_key, sizeof(relay->tp.read_key)); on_rx_from_peer(relay->tp.key, &relay->tp.read_key, 0); /* Done */ return PJ_SUCCESS; }
/* * Generate response digest. * Most of the parameters to generate the digest (i.e. username, realm, uri, * and nonce) are expected to be in the credential. Additional parameters (i.e. * password and method param) should be supplied in the argument. * * The resulting digest will be stored in cred->response. * The pool is used to allocate 32 bytes to store the digest in cred->response. */ static pj_status_t respond_digest( pj_pool_t *pool, pjsip_digest_credential *cred, const pjsip_digest_challenge *chal, const pj_str_t *uri, const pjsip_cred_info *cred_info, const pj_str_t *cnonce, pj_uint32_t nc, const pj_str_t *method) { const pj_str_t pjsip_AKAv1_MD5_STR = { "AKAv1-MD5", 9 }; /* Check algorithm is supported. We support MD5 and AKAv1-MD5. */ if (chal->algorithm.slen==0 || (pj_stricmp(&chal->algorithm, &pjsip_MD5_STR) || pj_stricmp(&chal->algorithm, &pjsip_AKAv1_MD5_STR))) { ; } else { PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"", chal->algorithm.slen, chal->algorithm.ptr)); return PJSIP_EINVALIDALGORITHM; } /* Build digest credential from arguments. */ pj_strdup(pool, &cred->username, &cred_info->username); pj_strdup(pool, &cred->realm, &chal->realm); pj_strdup(pool, &cred->nonce, &chal->nonce); pj_strdup(pool, &cred->uri, uri); pj_strdup(pool, &cred->algorithm, &chal->algorithm); pj_strdup(pool, &cred->opaque, &chal->opaque); /* Allocate memory. */ cred->response.ptr = (char*) pj_pool_alloc(pool, PJSIP_MD5STRLEN); cred->response.slen = PJSIP_MD5STRLEN; if (chal->qop.slen == 0) { /* Server doesn't require quality of protection. */ if ((cred_info->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { /* Call application callback to create the response digest */ return (*cred_info->ext.aka.cb)(pool, chal, cred_info, method, cred); } else { /* Convert digest to string and store in chal->response. */ pjsip_auth_create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL, uri, &chal->realm, cred_info, method); } } else if (has_auth_qop(pool, &chal->qop)) { /* Server requires quality of protection. * We respond with selecting "qop=auth" protection. */ cred->qop = pjsip_AUTH_STR; cred->nc.ptr = (char*) pj_pool_alloc(pool, 16); cred->nc.slen = pj_ansi_snprintf(cred->nc.ptr, 16, "%08u", nc); if (cnonce && cnonce->slen) { pj_strdup(pool, &cred->cnonce, cnonce); } else { pj_str_t dummy_cnonce = { "b39971", 6}; pj_strdup(pool, &cred->cnonce, &dummy_cnonce); } if ((cred_info->data_type & EXT_MASK) == PJSIP_CRED_DATA_EXT_AKA) { /* Call application callback to create the response digest */ return (*cred_info->ext.aka.cb)(pool, chal, cred_info, method, cred); } else { pjsip_auth_create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce, &pjsip_AUTH_STR, uri, &chal->realm, cred_info, method ); } } else { /* Server requires quality protection that we don't support. */ PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", chal->qop.slen, chal->qop.ptr)); return PJSIP_EINVALIDQOP; } return PJ_SUCCESS; }
/* * DirectSound capture and playback thread. */ static int dsound_dev_thread(void *arg) { pjmedia_snd_stream *strm = arg; HANDLE events[3]; unsigned eventCount; unsigned bytes_per_frame; pj_status_t status; eventCount = 0; events[eventCount++] = strm->thread_quit_event; if (strm->dir & PJMEDIA_DIR_PLAYBACK) events[eventCount++] = strm->play_strm.hEvent; if (strm->dir & PJMEDIA_DIR_CAPTURE) events[eventCount++] = strm->rec_strm.hEvent; /* Raise self priority. We don't want the audio to be distorted by * system activity. */ SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST); /* Calculate bytes per frame */ bytes_per_frame = strm->samples_per_frame * BYTES_PER_SAMPLE; /* * Loop while not signalled to quit, wait for event objects to be * signalled by DirectSound capture and play buffer. */ while (PJ_TRUE) { DWORD rc; pjmedia_dir signalled_dir; rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE); if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0+eventCount) continue; if (rc == WAIT_OBJECT_0) break; if (rc == (WAIT_OBJECT_0 + 1)) { if (events[1] == strm->play_strm.hEvent) signalled_dir = PJMEDIA_DIR_PLAYBACK; else signalled_dir = PJMEDIA_DIR_CAPTURE; } else { if (events[2] == strm->play_strm.hEvent) signalled_dir = PJMEDIA_DIR_PLAYBACK; else signalled_dir = PJMEDIA_DIR_CAPTURE; } if (signalled_dir == PJMEDIA_DIR_PLAYBACK) { struct dsound_stream *dsound_strm; /* * DirectSound has requested us to feed some frames to * playback buffer. */ dsound_strm = &strm->play_strm; status = PJ_SUCCESS; while (dsound_play_empty_size(dsound_strm) > bytes_per_frame) { /* Get frame from application. */ status = (*strm->play_cb)(strm->user_data, dsound_strm->timestamp.u32.lo, strm->buffer, bytes_per_frame); if (status != PJ_SUCCESS) break; /* Write to DirectSound buffer. */ AppWriteDataToBuffer( dsound_strm->ds.play.lpDsBuffer, dsound_strm->dwBytePos, (LPBYTE)strm->buffer, bytes_per_frame); /* Increment position. */ dsound_strm->dwBytePos += bytes_per_frame; if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize) dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize; dsound_strm->timestamp.u64 += strm->samples_per_frame; } } else { /* * DirectSound has indicated that it has some frames ready * in the capture buffer. Get as much frames as possible to * prevent overflows. */ struct dsound_stream *dsound_strm; BOOL rc; dsound_strm = &strm->rec_strm; /* Fetch while we have more than 1 frame */ while (dsound_captured_size(dsound_strm) > bytes_per_frame) { /* Capture from DirectSound buffer. */ rc = AppReadDataFromBuffer(dsound_strm->ds.capture.lpDsBuffer, dsound_strm->dwBytePos, (LPBYTE)strm->buffer, bytes_per_frame); if (!rc) { pj_bzero(strm->buffer, bytes_per_frame); } /* Call callback */ status = (*strm->rec_cb)(strm->user_data, dsound_strm->timestamp.u32.lo, strm->buffer, bytes_per_frame); /* Quit thread on error. */ if (status != PJ_SUCCESS) goto on_error; /* Increment position. */ dsound_strm->dwBytePos += bytes_per_frame; if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize) dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize; dsound_strm->timestamp.u64 += strm->samples_per_frame; } } } on_error: PJ_LOG(5,(THIS_FILE, "DirectSound: thread stopping..")); return 0; }
/* * Callback notification from STUN session when it receives STUN * requests. This callback was trigger by STUN incoming message * processing in pj_turn_allocation_on_rx_client_pkt(). */ static pj_status_t stun_on_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_rx_data *rdata, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { const pj_stun_msg *msg = rdata->msg; pj_turn_allocation *alloc; PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); alloc = (pj_turn_allocation*) pj_stun_session_get_user_data(sess); /* Refuse to serve any request if we've been shutdown */ if (alloc->relay.lifetime == 0) { /* Reject with 437 if we're shutting down */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); return PJ_SUCCESS; } if (msg->hdr.type == PJ_STUN_REFRESH_REQUEST) { /* * Handle REFRESH request */ pj_stun_lifetime_attr *lifetime; pj_stun_bandwidth_attr *bandwidth; /* Get LIFETIME attribute */ lifetime = (pj_stun_lifetime_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0); /* Get BANDWIDTH attribute */ bandwidth = (pj_stun_bandwidth_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_BANDWIDTH, 0); if (lifetime && lifetime->value==0) { /* * This is deallocation request. */ alloc->relay.lifetime = 0; /* Respond first */ send_reply_ok(alloc, rdata); /* Shutdown allocation */ PJ_LOG(4,(alloc->obj_name, "Client %s request to dealloc, shutting down", alloc->info)); alloc_shutdown(alloc); } else { /* * This is a refresh request. */ /* Update lifetime */ if (lifetime) { alloc->relay.lifetime = lifetime->value; } /* Update bandwidth */ // TODO: /* Update expiration timer */ resched_timeout(alloc); /* Send reply */ send_reply_ok(alloc, rdata); } } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) { /* * ChannelBind request. */ pj_stun_channel_number_attr *ch_attr; pj_stun_peer_addr_attr *peer_attr; pj_turn_permission *p1, *p2; ch_attr = (pj_stun_channel_number_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_CHANNEL_NUMBER, 0); peer_attr = (pj_stun_peer_addr_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0); if (!ch_attr || !peer_attr) { send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); return PJ_SUCCESS; } /* Find permission with the channel number */ p1 = lookup_permission_by_chnum(alloc, PJ_STUN_GET_CH_NB(ch_attr->value)); /* If permission is found, this is supposed to be a channel bind * refresh. Make sure it's for the same peer. */ if (p1) { if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) { /* Address mismatch. Send 400 */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address mismatch"); return PJ_SUCCESS; } /* Refresh permission */ refresh_permission(p1); /* Send response */ send_reply_ok(alloc, rdata); /* Done */ return PJ_SUCCESS; } /* If permission is not found, create a new one. Make sure the peer * has not alreadyy assigned with a channel number. */ p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) { send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address already assigned a channel number"); return PJ_SUCCESS; } /* Create permission if it doesn't exist */ if (!p2) { p2 = create_permission(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (!p2) return PJ_SUCCESS; } /* Assign channel number to permission */ p2->channel = PJ_STUN_GET_CH_NB(ch_attr->value); /* Register to hash table */ pj_assert(sizeof(p2->channel==2)); pj_hash_set(alloc->pool, alloc->ch_table, &p2->channel, sizeof(p2->channel), 0, p2); /* Update */ refresh_permission(p2); /* Reply */ send_reply_ok(alloc, rdata); return PJ_SUCCESS; } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) { /* Respond with 437 (section 6.3 turn-07) */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); } else { /* Respond with Bad Request? */ send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); } return PJ_SUCCESS; }
static int ca_thread_func (void *arg) { struct bb10_stream* stream = (struct bb10_stream *) arg; int size = stream->ca_buf_size; unsigned long nframes = stream->ca_frames; void *user_data = stream->user_data; /* Buffer to fill for PJMEDIA */ char *buf = stream->ca_buf; pj_timestamp tstamp; int result; int policy; struct sched_param param; TRACE_((THIS_FILE, "ca_thread_func: size = %d ", size)); if (pthread_getschedparam(pthread_self(), &policy, ¶m) == 0) { param.sched_priority = 18; pthread_setschedparam (pthread_self(), policy, ¶m); } pj_bzero (buf, size); tstamp.u64 = 0; /* Final init now the thread has started */ if ((result = snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE)) < 0) { TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result)); return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; //pj_bzero (buf, size); /* read the input device */ result = snd_pcm_plugin_read(stream->ca_pcm, buf,size); if(result <0 || result != size) { /* We expect result to be size (640) * It's not so we have to read the status error and "prepare" * the channel. This usually happens when output audio routing * has been changed by another thread. * We won't "continue", instead just do what we can and leave * the end of the loop to write what's in the buffer. Not entirely * correct but saves a potential underrun in PJMEDIA */ PJ_LOG (4,(THIS_FILE, "snd_pcm_plugin_read ERROR read = %d required = %d", result,size)); snd_pcm_channel_status_t status; status.channel = SND_PCM_CHANNEL_CAPTURE; if ((result = snd_pcm_plugin_status (stream->ca_pcm, &status)) < 0) { /* Should not fail but all we can do is continue */ PJ_LOG(4,(THIS_FILE, "capture: snd_pcm_plugin_status ret = %d", result)); } else { /* RIM say these are the errors that we should "prepare" * after */ if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_OVERRUN || status.status == SND_PCM_STATUS_ERROR || status.status == SND_PCM_STATUS_CHANGE) { if (snd_pcm_plugin_prepare (stream->ca_pcm, SND_PCM_CHANNEL_CAPTURE) < 0) { PJ_LOG (4,(THIS_FILE, "overrun: capture channel prepare error")); } } } } if (stream->quit) break; /* Write the capture audio data to PJMEDIA */ frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void *) buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; result = stream->ca_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; tstamp.u64 += nframes; } flush_capture(stream); TRACE_((THIS_FILE, "ca_thread_func: Stopped")); return PJ_SUCCESS; }
/* * Initialize DirectSound player device. */ static pj_status_t init_player_stream( struct dsound_stream *ds_strm, int dev_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned buffer_count) { HRESULT hr; HWND hwnd; PCMWAVEFORMAT pcmwf; DSBUFFERDESC dsbdesc; DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT]; unsigned bytes_per_frame; unsigned max_latency; unsigned i; PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL); /* Check device ID */ if (dev_id == -1) dev_id = 0; PJ_ASSERT_RETURN(dev_id>=0 && dev_id < (int)dev_count, PJ_EINVAL); /* * Create DirectSound device. */ hr = DirectSoundCreate(dev_info[dev_id].lpGuid, &ds_strm->ds.play.lpDs, NULL); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); hwnd = GetForegroundWindow(); if (hwnd == NULL) { hwnd = GetDesktopWindow(); } hr = IDirectSound_SetCooperativeLevel( ds_strm->ds.play.lpDs, hwnd, DSSCL_PRIORITY); if FAILED(hr) return PJ_RETURN_OS_ERROR(hr); /* * Set up wave format structure for initialize DirectSound play * buffer. */ init_waveformatex(&pcmwf, clock_rate, channel_count); bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE; /* Set up DSBUFFERDESC structure. */ pj_bzero(&dsbdesc, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; dsbdesc.dwBufferBytes = buffer_count * bytes_per_frame; dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; /* * Create DirectSound playback buffer. */ hr = IDirectSound_CreateSoundBuffer(ds_strm->ds.play.lpDs, &dsbdesc, &ds_strm->ds.play.lpDsBuffer, NULL); if (FAILED(hr) ) return PJ_RETURN_OS_ERROR(hr); /* * Create event for play notification. */ ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); if (ds_strm->hEvent == NULL) return pj_get_os_error(); /* * Setup notification for play. */ hr = IDirectSoundBuffer_QueryInterface( ds_strm->ds.play.lpDsBuffer, &IID_IDirectSoundNotify, (LPVOID *)&ds_strm->lpDsNotify); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); for (i=0; i<buffer_count; ++i) { dsPosNotify[i].dwOffset = i * bytes_per_frame; dsPosNotify[i].hEventNotify = ds_strm->hEvent; } hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify, buffer_count, dsPosNotify); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); hr = IDirectSoundBuffer_SetCurrentPosition(ds_strm->ds.play.lpDsBuffer, 0); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); ds_strm->dwBytePos = 0; ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame; ds_strm->timestamp.u64 = 0; /* * Play latency does not need to be on a frame boundry, it is just how far * ahead of the read pointer we set the write pointer. So we should just * use the user configured latency. However, if the latency measured in * bytes causes more buffers than we are allowed, we must cap the latency * at the time contained in 1-buffer_count. */ max_latency = (1 - buffer_count) * samples_per_frame * 1000 / clock_rate / channel_count; ds_strm->latency = PJ_MIN(max_latency, snd_output_latency); /* Done setting up play device. */ PJ_LOG(5,(THIS_FILE, " DirectSound player \"%s\" initialized (clock_rate=%d, " "channel_count=%d, samples_per_frame=%d (%dms))", dev_info[dev_id].info.name, clock_rate, channel_count, samples_per_frame, samples_per_frame * 1000 / clock_rate)); return PJ_SUCCESS; }
/* This callback is called by transport manager for the TCP factory * to create outgoing transport to the specified destination. */ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, pjsip_tpmgr *mgr, pjsip_endpoint *endpt, const pj_sockaddr *rem_addr, int addr_len, pjsip_transport **p_transport) { struct tcp_listener *listener; struct tcp_transport *tcp; pj_sock_t sock; pj_sockaddr local_addr; pj_status_t status; /* Sanity checks */ PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr && addr_len && p_transport, PJ_EINVAL); /* Check that address is a sockaddr_in or sockaddr_in6*/ PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() && addr_len == sizeof(pj_sockaddr_in)) || (rem_addr->addr.sa_family == pj_AF_INET6() && addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL); listener = (struct tcp_listener*)factory; /* Create socket */ status = pj_sock_socket(rem_addr->addr.sa_family, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) return status; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "outgoing SIP TCP socket"); /* Bind to listener's address and any port */ pj_bzero(&local_addr, sizeof(local_addr)); pj_sockaddr_cp(&local_addr, &listener->bound_addr); pj_sockaddr_set_port(&local_addr, 0); status = pj_sock_bind(sock, &local_addr, pj_sockaddr_get_len(&local_addr)); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Get the local port */ addr_len = sizeof(local_addr); status = pj_sock_getsockname(sock, &local_addr, &addr_len); if (status != PJ_SUCCESS) { pj_sock_close(sock); return status; } /* Initially set the address from the listener's address */ if (!pj_sockaddr_has_addr(&local_addr)) { pj_sockaddr_copy_addr(&local_addr, &listener->factory.local_addr); } /* Create the transport descriptor */ status = tcp_create(listener, NULL, sock, PJ_FALSE, &local_addr, rem_addr, &tcp); if (status != PJ_SUCCESS) return status; /* Start asynchronous connect() operation */ tcp->has_pending_connect = PJ_TRUE; status = pj_activesock_start_connect(tcp->asock, tcp->base.pool, rem_addr, addr_len); if (status == PJ_SUCCESS) { on_connect_complete(tcp->asock, PJ_SUCCESS); } else if (status != PJ_EPENDING) { tcp_destroy(&tcp->base, status); return status; } if (tcp->has_pending_connect) { /* Update (again) local address, just in case local address currently * set is different now that asynchronous connect() is started. */ addr_len = sizeof(local_addr); if (pj_sock_getsockname(sock, &local_addr, &addr_len)==PJ_SUCCESS) { pj_sockaddr *tp_addr = &tcp->base.local_addr; /* Some systems (like old Win32 perhaps) may not set local address * properly before socket is fully connected. */ if (pj_sockaddr_cmp(tp_addr, &local_addr) && pj_sockaddr_get_port(&local_addr) != 0) { pj_sockaddr_cp(tp_addr, &local_addr); sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name, &local_addr); } } PJ_LOG(4,(tcp->base.obj_name, "TCP transport %.*s:%d is connecting to %.*s:%d...", (int)tcp->base.local_name.host.slen, tcp->base.local_name.host.ptr, tcp->base.local_name.port, (int)tcp->base.remote_name.host.slen, tcp->base.remote_name.host.ptr, tcp->base.remote_name.port)); } /* Done */ *p_transport = &tcp->base; return PJ_SUCCESS; }
/* * This callback is called by active socket when pending accept() operation * has completed. */ static pj_bool_t on_accept_complete(pj_activesock_t *asock, pj_sock_t sock, const pj_sockaddr_t *src_addr, int src_addr_len) { struct tcp_listener *listener; struct tcp_transport *tcp; char addr[PJ_INET6_ADDRSTRLEN+10]; pjsip_tp_state_callback state_cb; pj_sockaddr tmp_src_addr; pj_status_t status; PJ_UNUSED_ARG(src_addr_len); listener = (struct tcp_listener*) pj_activesock_get_user_data(asock); PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_TRUE); PJ_LOG(4,(listener->factory.obj_name, "TCP listener %.*s:%d: got incoming TCP connection " "from %s, sock=%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), sock)); /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, listener->qos_type, &listener->qos_params, 2, listener->factory.obj_name, "incoming SIP TCP socket"); /* tcp_create() expect pj_sockaddr, so copy src_addr to temporary var, * just in case. */ pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr)); pj_sockaddr_cp(&tmp_src_addr, src_addr); /* * Incoming connection! * Create TCP transport for the new socket. */ status = tcp_create( listener, NULL, sock, PJ_TRUE, &listener->factory.local_addr, &tmp_src_addr, &tcp); if (status == PJ_SUCCESS) { /* Add a reference to prevent the transport from being destroyed while * we're operating on it. */ pjsip_transport_add_ref(&tcp->base); status = tcp_start_read(tcp); if (status != PJ_SUCCESS) { PJ_LOG(3,(tcp->base.obj_name, "New transport cancelled")); pjsip_transport_dec_ref(&tcp->base); tcp_destroy(&tcp->base, status); } else { /* Start keep-alive timer */ if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) { pj_time_val delay = {PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0}; pjsip_endpt_schedule_timer(listener->endpt, &tcp->ka_timer, &delay); tcp->ka_timer.id = PJ_TRUE; pj_gettimeofday(&tcp->last_activity); } /* Notify application of transport state accepted */ state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr); if (state_cb) { pjsip_transport_state_info state_info; pj_bzero(&state_info, sizeof(state_info)); (*state_cb)(&tcp->base, PJSIP_TP_STATE_CONNECTED, &state_info); } pjsip_transport_dec_ref(&tcp->base); } } return PJ_TRUE; }
/* * Common function to create TCP transport, called when pending accept() and * pending connect() complete. */ static pj_status_t tcp_create( struct tcp_listener *listener, pj_pool_t *pool, pj_sock_t sock, pj_bool_t is_server, const pj_sockaddr *local, const pj_sockaddr *remote, struct tcp_transport **p_tcp) { struct tcp_transport *tcp; pj_ioqueue_t *ioqueue; pj_activesock_cfg asock_cfg; pj_activesock_cb tcp_callback; const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA; char print_addr[PJ_INET6_ADDRSTRLEN+10]; pj_status_t status; PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_EINVAL); if (pool == NULL) { pool = pjsip_endpt_create_pool(listener->endpt, "tcp", POOL_TP_INIT, POOL_TP_INC); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); } /* * Create and initialize basic transport structure. */ tcp = PJ_POOL_ZALLOC_T(pool, struct tcp_transport); tcp->is_server = is_server; tcp->sock = sock; /*tcp->listener = listener;*/ pj_list_init(&tcp->delayed_list); tcp->base.pool = pool; pj_ansi_snprintf(tcp->base.obj_name, PJ_MAX_OBJ_NAME, (is_server ? "tcps%p" :"tcpc%p"), tcp); status = pj_atomic_create(pool, 0, &tcp->base.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, "tcp", &tcp->base.lock); if (status != PJ_SUCCESS) { goto on_error; } tcp->base.key.type = listener->factory.type; pj_sockaddr_cp(&tcp->base.key.rem_addr, remote); tcp->base.type_name = (char*)pjsip_transport_get_type_name( (pjsip_transport_type_e)tcp->base.key.type); tcp->base.flag = pjsip_transport_get_flag_from_type( (pjsip_transport_type_e)tcp->base.key.type); tcp->base.info = (char*) pj_pool_alloc(pool, 64); pj_ansi_snprintf(tcp->base.info, 64, "%s to %s", tcp->base.type_name, pj_sockaddr_print(remote, print_addr, sizeof(print_addr), 3)); tcp->base.addr_len = pj_sockaddr_get_len(remote); pj_sockaddr_cp(&tcp->base.local_addr, local); sockaddr_to_host_port(pool, &tcp->base.local_name, local); sockaddr_to_host_port(pool, &tcp->base.remote_name, remote); tcp->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING; tcp->base.endpt = listener->endpt; tcp->base.tpmgr = listener->tpmgr; tcp->base.send_msg = &tcp_send_msg; tcp->base.do_shutdown = &tcp_shutdown; tcp->base.destroy = &tcp_destroy_transport; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); asock_cfg.async_cnt = 1; pj_bzero(&tcp_callback, sizeof(tcp_callback)); tcp_callback.on_data_read = &on_data_read; tcp_callback.on_data_sent = &on_data_sent; tcp_callback.on_connect_complete = &on_connect_complete; ioqueue = pjsip_endpt_get_ioqueue(listener->endpt); status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, ioqueue, &tcp_callback, tcp, &tcp->asock); if (status != PJ_SUCCESS) { goto on_error; } /* Register transport to transport manager */ status = pjsip_transport_register(listener->tpmgr, &tcp->base); if (status != PJ_SUCCESS) { goto on_error; } tcp->is_registered = PJ_TRUE; /* Initialize keep-alive timer */ tcp->ka_timer.user_data = (void*)tcp; tcp->ka_timer.cb = &tcp_keep_alive_timer; pj_ioqueue_op_key_init(&tcp->ka_op_key.key, sizeof(pj_ioqueue_op_key_t)); pj_strdup(tcp->base.pool, &tcp->ka_pkt, &ka_pkt); /* Done setting up basic transport. */ *p_tcp = tcp; PJ_LOG(4,(tcp->base.obj_name, "TCP %s transport created", (tcp->is_server ? "server" : "client"))); return PJ_SUCCESS; on_error: tcp_destroy(&tcp->base, status); return status; }
/* Destroy TCP transport */ static pj_status_t tcp_destroy(pjsip_transport *transport, pj_status_t reason) { struct tcp_transport *tcp = (struct tcp_transport*)transport; if (tcp->close_reason == 0) tcp->close_reason = reason; if (tcp->is_registered) { tcp->is_registered = PJ_FALSE; pjsip_transport_destroy(transport); /* pjsip_transport_destroy will recursively call this function * again. */ return PJ_SUCCESS; } /* Mark transport as closing */ tcp->is_closing = PJ_TRUE; /* Stop keep-alive timer. */ if (tcp->ka_timer.id) { pjsip_endpt_cancel_timer(tcp->base.endpt, &tcp->ka_timer); tcp->ka_timer.id = PJ_FALSE; } /* Cancel all delayed transmits */ while (!pj_list_empty(&tcp->delayed_list)) { struct delayed_tdata *pending_tx; pj_ioqueue_op_key_t *op_key; pending_tx = tcp->delayed_list.next; pj_list_erase(pending_tx); op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key; on_data_sent(tcp->asock, op_key, -reason); } if (tcp->rdata.tp_info.pool) { pj_pool_release(tcp->rdata.tp_info.pool); tcp->rdata.tp_info.pool = NULL; } if (tcp->asock) { pj_activesock_close(tcp->asock); tcp->asock = NULL; tcp->sock = PJ_INVALID_SOCKET; } else if (tcp->sock != PJ_INVALID_SOCKET) { pj_sock_close(tcp->sock); tcp->sock = PJ_INVALID_SOCKET; } if (tcp->base.lock) { pj_lock_destroy(tcp->base.lock); tcp->base.lock = NULL; } if (tcp->base.ref_cnt) { pj_atomic_destroy(tcp->base.ref_cnt); tcp->base.ref_cnt = NULL; } if (tcp->base.pool) { pj_pool_t *pool; if (reason != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; pj_strerror(reason, errmsg, sizeof(errmsg)); PJ_LOG(4,(tcp->base.obj_name, "TCP transport destroyed with reason %d: %s", reason, errmsg)); } else { PJ_LOG(4,(tcp->base.obj_name, "TCP transport destroyed normally")); } pool = tcp->base.pool; tcp->base.pool = NULL; pj_pool_release(pool); } return PJ_SUCCESS; }
/* * This is the public API to create, initialize, register, and start the * TCP listener. */ PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( pjsip_endpoint *endpt, const pjsip_tcp_transport_cfg *cfg, pjsip_tpfactory **p_factory ) { pj_pool_t *pool; pj_sock_t sock = PJ_INVALID_SOCKET; struct tcp_listener *listener; pj_activesock_cfg asock_cfg; pj_activesock_cb listener_cb; pj_sockaddr *listener_addr; int addr_len; pj_status_t status; /* Sanity check */ PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ if (cfg->addr_name.host.slen) { pj_sockaddr tmp; status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, (pj_uint16_t)cfg->addr_name.port); if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || (cfg->af==pj_AF_INET() && tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; } } pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_LIS_INIT, POOL_LIS_INC); PJ_ASSERT_RETURN(pool, PJ_ENOMEM); listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener); listener->factory.pool = pool; listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP : PJSIP_TRANSPORT_TCP6; listener->factory.type_name = (char*) pjsip_transport_get_type_name(listener->factory.type); listener->factory.flag = pjsip_transport_get_flag_from_type(listener->factory.type); listener->qos_type = cfg->qos_type; pj_memcpy(&listener->qos_params, &cfg->qos_params, sizeof(cfg->qos_params)); pj_ansi_strcpy(listener->factory.obj_name, "tcplis"); if (listener->factory.type==PJSIP_TRANSPORT_TCP6) pj_ansi_strcat(listener->factory.obj_name, "6"); status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name, &listener->factory.lock); if (status != PJ_SUCCESS) goto on_error; /* Create socket */ status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) goto on_error; /* Apply QoS, if specified */ status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, 2, listener->factory.obj_name, "SIP TCP listener socket"); /* Bind address may be different than factory.local_addr because * factory.local_addr will be resolved below. */ pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr); /* Bind socket */ listener_addr = &listener->factory.local_addr; pj_sockaddr_cp(listener_addr, &cfg->bind_addr); status = pj_sock_bind(sock, listener_addr, pj_sockaddr_get_len(listener_addr)); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the bound address */ addr_len = pj_sockaddr_get_len(listener_addr); status = pj_sock_getsockname(sock, listener_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; /* If published host/IP is specified, then use that address as the * listener advertised address. */ if (cfg->addr_name.host.slen) { /* Copy the address */ listener->factory.addr_name = cfg->addr_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, &cfg->addr_name.host); listener->factory.addr_name.port = cfg->addr_name.port; } else { /* No published address is given, use the bound address */ /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; status = pj_gethostip(listener->bound_addr.addr.sa_family, &hostip); if (status != PJ_SUCCESS) goto on_error; pj_sockaddr_copy_addr(listener_addr, &hostip); } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, &listener->factory.addr_name, listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, sizeof(listener->factory.obj_name), "tcplis:%d", listener->factory.addr_name.port); /* Start listening to the address */ status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG); if (status != PJ_SUCCESS) goto on_error; /* Create active socket */ pj_activesock_cfg_default(&asock_cfg); if (cfg->async_cnt > MAX_ASYNC_CNT) asock_cfg.async_cnt = MAX_ASYNC_CNT; else asock_cfg.async_cnt = cfg->async_cnt; pj_bzero(&listener_cb, sizeof(listener_cb)); listener_cb.on_accept_complete = &on_accept_complete; status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg, pjsip_endpt_get_ioqueue(endpt), &listener_cb, listener, &listener->asock); /* Register to transport manager */ listener->endpt = endpt; listener->tpmgr = pjsip_endpt_get_tpmgr(endpt); listener->factory.create_transport = lis_create_transport; listener->factory.destroy = lis_destroy; listener->is_registered = PJ_TRUE; status = pjsip_tpmgr_register_tpfactory(listener->tpmgr, &listener->factory); if (status == PJSIP_ETYPEEXISTS) { /* It's not a problem if there is already a TCP factory defined. */ status = PJ_SUCCESS; } if (status != PJ_SUCCESS) { listener->is_registered = PJ_FALSE; goto on_error; } /* Start pending accept() operations */ status = pj_activesock_start_accept(listener->asock, pool); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(4,(listener->factory.obj_name, "SIP TCP listener ready for incoming connections at %.*s:%d", (int)listener->factory.addr_name.host.slen, listener->factory.addr_name.host.ptr, listener->factory.addr_name.port)); /* Return the pointer to user */ if (p_factory) *p_factory = &listener->factory; return PJ_SUCCESS; on_error: if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET) pj_sock_close(sock); lis_destroy(&listener->factory); return status; }
/* Outgoing IM callback. */ static void im_callback(void *token, pjsip_event *e) { pjsua_im_data *im_data = (pjsua_im_data*) token; if (e->type == PJSIP_EVENT_TSX_STATE) { pjsip_transaction *tsx = e->body.tsx_state.tsx; /* Ignore provisional response, if any */ if (tsx->status_code < 200) return; /* Handle authentication challenges */ if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG && (tsx->status_code == 401 || tsx->status_code == 407)) { pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; pjsip_tx_data *tdata; pjsip_auth_clt_sess auth; pj_status_t status; PJ_LOG(4,(THIS_FILE, "Resending IM with authentication")); /* Create temporary authentication session */ pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0); pjsip_auth_clt_set_credentials(&auth, pjsua_var.acc[im_data->acc_id].cred_cnt, pjsua_var.acc[im_data->acc_id].cred); pjsip_auth_clt_set_prefs(&auth, &pjsua_var.acc[im_data->acc_id].cfg.auth_pref); status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx, &tdata); if (status == PJ_SUCCESS) { pjsua_im_data *im_data2; /* Must duplicate im_data */ im_data2 = pjsua_im_data_dup(tdata->pool, im_data); /* Increment CSeq */ PJSIP_MSG_CSEQ_HDR(tdata->msg)->cseq++; /* Re-send request */ status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1, im_data2, &im_callback); if (status == PJ_SUCCESS) { /* Done */ return; } } } if (tsx->status_code/100 == 2) { PJ_LOG(4,(THIS_FILE, "Message \'%s\' delivered successfully", im_data->body.ptr)); } else { PJ_LOG(3,(THIS_FILE, "Failed to deliver message \'%s\': %d/%.*s", im_data->body.ptr, tsx->status_code, (int)tsx->status_text.slen, tsx->status_text.ptr)); } if (pjsua_var.ua_cfg.cb.on_pager_status) { pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id, &im_data->to, &im_data->body, im_data->user_data, (pjsip_status_code) tsx->status_code, &tsx->status_text); } if (pjsua_var.ua_cfg.cb.on_pager_status2) { pjsip_rx_data *rdata; if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) rdata = e->body.tsx_state.src.rdata; else rdata = NULL; pjsua_var.ua_cfg.cb.on_pager_status2(im_data->call_id, &im_data->to, &im_data->body, im_data->user_data, (pjsip_status_code) tsx->status_code, &tsx->status_text, tsx->last_tx, rdata, im_data->acc_id); } } }
/* * Create a new listener on the specified port. */ PJ_DEF(pj_status_t) pj_turn_listener_create_udp( pj_turn_srv *srv, int af, const pj_str_t *bound_addr, unsigned port, unsigned concurrency_cnt, unsigned flags, pj_turn_listener **p_listener) { pj_pool_t *pool; struct udp_listener *udp; pj_ioqueue_callback ioqueue_cb; unsigned i; pj_status_t status; /* Create structure */ pool = pj_pool_create(srv->core.pf, "udp%p", 1000, 1000, NULL); udp = PJ_POOL_ZALLOC_T(pool, struct udp_listener); udp->base.pool = pool; udp->base.obj_name = pool->obj_name; udp->base.server = srv; udp->base.tp_type = PJ_TURN_TP_UDP; udp->base.sock = PJ_INVALID_SOCKET; udp->base.destroy = &udp_destroy; udp->read_cnt = concurrency_cnt; udp->base.flags = flags; udp->tp.obj_name = udp->base.obj_name; udp->tp.info = udp->base.info; udp->tp.listener = &udp->base; udp->tp.sendto = &udp_sendto; udp->tp.add_ref = &udp_add_ref; udp->tp.dec_ref = &udp_dec_ref; /* Create socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &udp->base.sock); if (status != PJ_SUCCESS) goto on_error; /* Init bind address */ status = pj_sockaddr_init(af, &udp->base.addr, bound_addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) goto on_error; /* Create info */ pj_ansi_strcpy(udp->base.info, "UDP:"); pj_sockaddr_print(&udp->base.addr, udp->base.info+4, sizeof(udp->base.info)-4, 3); /* Bind socket */ status = pj_sock_bind(udp->base.sock, &udp->base.addr, pj_sockaddr_get_len(&udp->base.addr)); if (status != PJ_SUCCESS) goto on_error; /* Register to ioqueue */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = on_read_complete; status = pj_ioqueue_register_sock(pool, srv->core.ioqueue, udp->base.sock, udp, &ioqueue_cb, &udp->key); /* Create op keys */ udp->read_op = (struct read_op**)pj_pool_calloc(pool, concurrency_cnt, sizeof(struct read_op*)); /* Create each read_op and kick off read operation */ for (i=0; i<concurrency_cnt; ++i) { pj_pool_t *rpool = pj_pool_create(srv->core.pf, "rop%p", 1000, 1000, NULL); udp->read_op[i] = PJ_POOL_ZALLOC_T(pool, struct read_op); udp->read_op[i]->pkt.pool = rpool; on_read_complete(udp->key, &udp->read_op[i]->op_key, 0); } /* Done */ PJ_LOG(4,(udp->base.obj_name, "Listener %s created", udp->base.info)); *p_listener = &udp->base; return PJ_SUCCESS; on_error: udp_destroy(&udp->base); return status; }
/* * BB10 - tests loads the audio units and sets up the driver structure */ static pj_status_t bb10_add_dev (struct bb10_factory *af) { pjmedia_aud_dev_info *adi; int pb_result, ca_result; unsigned int handle; snd_pcm_t *pcm_handle; if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs)) return PJ_ETOOMANY; adi = &af->devs[af->dev_cnt]; TRACE_((THIS_FILE, "bb10_add_dev Enter")); if ((pb_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, &pcm_handle, &handle, (char*)"voice", SND_PCM_OPEN_PLAYBACK)) >= 0) { snd_pcm_close (pcm_handle); audio_manager_free_handle(handle); } else { TRACE_((THIS_FILE, "Try to open the device for playback - failure")); } if ((ca_result = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, &pcm_handle, &handle, (char*)"voice", SND_PCM_OPEN_CAPTURE)) >= 0) { snd_pcm_close (pcm_handle); audio_manager_free_handle(handle); } else { TRACE_((THIS_FILE, "Try to open the device for capture - failure")); } if (pb_result < 0 && ca_result < 0) { TRACE_((THIS_FILE, "Unable to open sound device", "preferred")); return PJMEDIA_EAUD_NODEV; } /* Reset device info */ pj_bzero(adi, sizeof(*adi)); /* Set device name */ strcpy(adi->name, "preferred"); /* Check the number of playback channels */ adi->output_count = (pb_result >= 0) ? 1 : 0; /* Check the number of capture channels */ adi->input_count = (ca_result >= 0) ? 1 : 0; /* Set the default sample rate */ adi->default_samples_per_sec = 8000; /* Driver name */ strcpy(adi->driver, "BB10"); ++af->dev_cnt; PJ_LOG (4,(THIS_FILE, "Added sound device %s", adi->name)); return PJ_SUCCESS; }
/* API: refresh the device list */ static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f) { struct bd_factory *wf = (struct bd_factory*)f; unsigned int i = 0; wchar_t *deviceNamep=NULL; wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME]; unsigned int captureDeviceCount = 0; wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME]; unsigned int playbackDeviceCount = 0; if(wf->pool != NULL) { pj_pool_release(wf->pool); wf->pool = NULL; } // Enumerate capture sound devices while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) != BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY) { wcscpy(captureDevName[captureDeviceCount], deviceNamep); captureDeviceCount++; } // Enumerate playback sound devices while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) != BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY) { wcscpy(playbackDevName[playbackDeviceCount], deviceNamep); playbackDeviceCount++; } // Set devices info wf->dev_count = captureDeviceCount + playbackDeviceCount; wf->pool = pj_pool_create(wf->pf, "BD_IMAD_DEVICES", 1000, 1000, NULL); wf->dev_info = (struct bddev_info*)pj_pool_calloc(wf->pool, wf->dev_count, sizeof(struct bddev_info)); // Capture device properties for(i=0;i<captureDeviceCount;i++) { wf->dev_info[i].deviceId = i; wf->dev_info[i].info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING | PJMEDIA_AUD_DEV_CAP_EC; wf->dev_info[i].info.default_samples_per_sec = BD_IMAD_DEFAULT_FREQ; strcpy(wf->dev_info[i].info.driver, "BD_IMAD"); wf->dev_info[i].info.ext_fmt_cnt = 0; wf->dev_info[i].info.input_count = BD_IMAD_MAX_CHANNELS; wf->dev_info[i].info.output_count = 0; strcpy(wf->dev_info[i].info.name, BD_IMAD_PJ_WCHARtoCHAR(captureDevName[i])); wf->dev_info[i].info.routes = 0; } // Playback device properties for(i=0;i<playbackDeviceCount;i++) { wf->dev_info[captureDeviceCount+i].deviceId = captureDeviceCount+i; wf->dev_info[captureDeviceCount+i].info.caps = PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING; wf->dev_info[captureDeviceCount+i].info.default_samples_per_sec = BD_IMAD_DEFAULT_FREQ; strcpy(wf->dev_info[captureDeviceCount+i].info.driver, "BD_IMAD"); wf->dev_info[captureDeviceCount+i].info.ext_fmt_cnt = 0; wf->dev_info[captureDeviceCount+i].info.input_count = 0; wf->dev_info[captureDeviceCount+i].info.output_count = BD_IMAD_MAX_CHANNELS; strcpy(wf->dev_info[captureDeviceCount+i].info.name, BD_IMAD_PJ_WCHARtoCHAR(playbackDevName[i])); wf->dev_info[captureDeviceCount+i].info.routes = 0; } PJ_LOG(4, (THIS_FILE, "BDIMAD found %d devices:", wf->dev_count)); for(i=0; i<wf->dev_count; i++) { PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)", i, wf->dev_info[i].info.name, wf->dev_info[i].info.input_count, wf->dev_info[i].info.output_count)); } return PJ_SUCCESS; }
/** * Play audio received from PJMEDIA */ static int pb_thread_func (void *arg) { struct bb10_stream* stream = (struct bb10_stream *) arg; int size = stream->pb_buf_size; unsigned long nframes = stream->pb_frames; void *user_data = stream->user_data; char *buf = stream->pb_buf; pj_timestamp tstamp; int result = 0; int policy; struct sched_param param; TRACE_((THIS_FILE, "pb_thread_func: size = %d ", size)); if (pthread_getschedparam(pthread_self(), &policy, ¶m) == 0) { param.sched_priority = 18; pthread_setschedparam (pthread_self(), policy, ¶m); } pj_bzero (buf, size); tstamp.u64 = 0; /* Do the final initialization now the thread has started. */ if ((result = snd_pcm_plugin_prepare(stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) { TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result)); return PJ_SUCCESS; } while (!stream->quit) { pjmedia_frame frame; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; /* pointer to buffer filled by PJMEDIA */ frame.buf = buf; frame.size = size; frame.timestamp.u64 = tstamp.u64; frame.bit_info = 0; /* Read the audio from pjmedia */ result = stream->pb_cb (user_data, &frame); if (result != PJ_SUCCESS || stream->quit) break; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero (buf, size); /* Write 640 to play unit */ result = snd_pcm_plugin_write(stream->pb_pcm,buf,size); if (result != size || result < 0) { /* either the write to output device has failed or not the * full amount of bytes have been written. This usually happens * when audio routing is being changed by another thread * Use a status variable for reading the error */ snd_pcm_channel_status_t status; status.channel = SND_PCM_CHANNEL_PLAYBACK; if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) { /* Call has failed nothing we can do except log and * continue */ PJ_LOG(4,(THIS_FILE, "underrun: playback channel status error")); } else { /* The status of the error has been read * RIM say these are expected so we can "re-prepare" the stream */ PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d", status.status)); if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN || status.status == SND_PCM_STATUS_ERROR || status.status == SND_PCM_STATUS_CHANGE) { if (snd_pcm_plugin_prepare (stream->pb_pcm, SND_PCM_CHANNEL_PLAYBACK) < 0) { PJ_LOG(4,(THIS_FILE, "underrun: playback channel prepare error")); } } } } tstamp.u64 += nframes; } flush_play(stream); TRACE_((THIS_FILE, "pb_thread_func: Stopped")); return PJ_SUCCESS; }
/* callbacks to set data */ void bdimad_CaptureCallback(void *buffer, int samples, void *user_data) { pj_status_t status = PJ_SUCCESS; pjmedia_frame frame; unsigned nsamples; struct bd_stream *strm = (struct bd_stream*)user_data; if(!strm->go) goto on_break; /* Known cases of callback's thread: * - The thread may be changed in the middle of a session, e.g: in MacOS * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("bd_CaptureCallback", strm->rec_thread_desc, &strm->rec_thread); if (status != PJ_SUCCESS) goto on_break; strm->rec_thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Recorder thread started")); } /* Calculate number of samples we've got */ nsamples = samples * strm->channel_count + strm->rec_buf_count; /* RECORD */ if (strm->fmt_id == PJMEDIA_FORMAT_L16) { if (nsamples >= strm->samples_per_frame) { /* If buffer is not empty, combine the buffer with the just incoming * samples, then call put_frame. */ if (strm->rec_buf_count) { unsigned chunk_count = 0; chunk_count = strm->samples_per_frame - strm->rec_buf_count; pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count, (pj_int16_t*)buffer, chunk_count); frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void*) strm->rec_buf; frame.size = strm->bytes_per_frame; frame.timestamp.u64 = strm->timestampCapture.u64; frame.bit_info = 0; status = (*strm->rec_cb)(strm->user_data, &frame); buffer = (pj_int16_t*) buffer + chunk_count; nsamples -= strm->samples_per_frame; strm->rec_buf_count = 0; strm->timestampCapture.u64 += strm->samples_per_frame / strm->channel_count; } /* Give all frames we have */ while (nsamples >= strm->samples_per_frame && status == 0) { frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = (void*) buffer; frame.size = strm->bytes_per_frame; frame.timestamp.u64 = strm->timestampCapture.u64; frame.bit_info = 0; status = (*strm->rec_cb)(strm->user_data, &frame); buffer = (pj_int16_t*) buffer + strm->samples_per_frame; nsamples -= strm->samples_per_frame; strm->timestampCapture.u64 += strm->samples_per_frame / strm->channel_count; } /* Store the remaining samples into the buffer */ if (nsamples && status == 0) { strm->rec_buf_count = nsamples; pjmedia_copy_samples(strm->rec_buf, (pj_int16_t*)buffer, nsamples); } } else { /* Not enough samples, let's just store them in the buffer */ pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count, (pj_int16_t*)buffer, samples * strm->channel_count); strm->rec_buf_count += samples * strm->channel_count; } } else { pj_assert(!"Frame type not supported"); } strm->timestampCapture.u64 += strm->param.samples_per_frame / strm->param.channel_count; if (status==0) return; on_break: strm->rec_thread_exited = 1; }
/* * pj_ioqueue_create() * * Create select ioqueue. */ PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, pj_size_t max_fd, pj_ioqueue_t **p_ioqueue) { pj_ioqueue_t *ioqueue; pj_status_t rc; pj_lock_t *lock; int i; /* Check that arguments are valid. */ PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && max_fd > 0, PJ_EINVAL); /* Check that size of pj_ioqueue_op_key_t is sufficient */ PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= sizeof(union operation_key), PJ_EBUG); ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); ioqueue_init(ioqueue); ioqueue->max = max_fd; ioqueue->count = 0; pj_list_init(&ioqueue->active_list); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* When safe unregistration is used (the default), we pre-create * all keys and put them in the free list. */ /* Mutex to protect key's reference counter * We don't want to use key's mutex or ioqueue's mutex because * that would create deadlock situation in some cases. */ rc = pj_mutex_create_simple(pool, NULL, &ioqueue->ref_cnt_mutex); if (rc != PJ_SUCCESS) return rc; /* Init key list */ pj_list_init(&ioqueue->free_list); pj_list_init(&ioqueue->closing_list); /* Pre-create all keys according to max_fd */ for ( i=0; i<max_fd; ++i) { pj_ioqueue_key_t *key; key = PJ_POOL_ALLOC_T(pool, pj_ioqueue_key_t); key->ref_count = 0; rc = pj_mutex_create_recursive(pool, NULL, &key->mutex); if (rc != PJ_SUCCESS) { key = ioqueue->free_list.next; while (key != &ioqueue->free_list) { pj_mutex_destroy(key->mutex); key = key->next; } pj_mutex_destroy(ioqueue->ref_cnt_mutex); return rc; } pj_list_push_back(&ioqueue->free_list, key); } #endif rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock); if (rc != PJ_SUCCESS) return rc; rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE); if (rc != PJ_SUCCESS) return rc; ioqueue->epfd = os_epoll_create(max_fd); if (ioqueue->epfd < 0) { ioqueue_destroy(ioqueue); return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); } /*ioqueue->events = pj_pool_calloc(pool, max_fd, sizeof(struct epoll_event)); PJ_ASSERT_RETURN(ioqueue->events != NULL, PJ_ENOMEM); ioqueue->queue = pj_pool_calloc(pool, max_fd, sizeof(struct queue)); PJ_ASSERT_RETURN(ioqueue->queue != NULL, PJ_ENOMEM); */ PJ_LOG(4, ("pjlib", "epoll I/O Queue created (%p)", ioqueue)); *p_ioqueue = ioqueue; return PJ_SUCCESS; }
/* callbacks to get data */ int bdimad_PlaybackCallback(void *buffer, int samples, void *user_data) { pj_status_t status = PJ_SUCCESS; pjmedia_frame frame; struct bd_stream *strm = (struct bd_stream*)user_data; unsigned nsamples_req = samples * strm->channel_count; if(!strm->go) goto on_break; /* Known cases of callback's thread: * - The thread may be changed in the middle of a session, e.g: in MacOS * it happens when plugging/unplugging headphone. * - The same thread may be reused in consecutive sessions. The first * session will leave TLS set, but release the TLS data address, * so the second session must re-register the callback's thread. */ if (strm->play_thread_initialized == 0 || !pj_thread_is_registered()) { pj_bzero(strm->play_thread_desc, sizeof(pj_thread_desc)); status = pj_thread_register("bd_PlaybackCallback", strm->play_thread_desc, &strm->play_thread); if (status != PJ_SUCCESS) goto on_break; strm->play_thread_initialized = 1; PJ_LOG(5,(THIS_FILE, "Player thread started")); } /* PLAY */ if(strm->fmt_id == PJMEDIA_FORMAT_L16) { /* Check if any buffered samples */ if (strm->play_buf_count) { /* samples buffered >= requested by sound device */ if (strm->play_buf_count >= nsamples_req) { pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, nsamples_req); strm->play_buf_count -= nsamples_req; pjmedia_move_samples(strm->play_buf, strm->play_buf + nsamples_req, strm->play_buf_count); return nsamples_req; } /* samples buffered < requested by sound device */ pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, strm->play_buf_count); nsamples_req -= strm->play_buf_count; buffer = (pj_int16_t*)buffer + strm->play_buf_count; strm->play_buf_count = 0; } /* Fill output buffer as requested */ while (nsamples_req && status == 0) { if (nsamples_req >= strm->samples_per_frame) { frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = buffer; frame.size = strm->bytes_per_frame; frame.timestamp.u64 = strm->timestampPlayback.u64; frame.bit_info = 0; status = (*strm->play_cb)(strm->user_data, &frame); if (status != PJ_SUCCESS) return 0; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); nsamples_req -= strm->samples_per_frame; buffer = (pj_int16_t*)buffer + strm->samples_per_frame; } else { frame.type = PJMEDIA_FRAME_TYPE_AUDIO; frame.buf = strm->play_buf; frame.size = strm->bytes_per_frame; frame.timestamp.u64 = strm->timestampPlayback.u64; frame.bit_info = 0; status = (*strm->play_cb)(strm->user_data, &frame); if (status != PJ_SUCCESS) return 0; if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) pj_bzero(frame.buf, frame.size); pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf, nsamples_req); strm->play_buf_count = strm->samples_per_frame - nsamples_req; pjmedia_move_samples(strm->play_buf, strm->play_buf+nsamples_req, strm->play_buf_count); nsamples_req = 0; } strm->timestampPlayback.u64 += strm->samples_per_frame / strm->channel_count; } } else { pj_assert(!"Frame type not supported"); } if(status != PJ_SUCCESS) { return 0; } strm->timestampPlayback.u64 += strm->param.samples_per_frame / strm->param.channel_count; if (status == 0) return samples; on_break: strm->play_thread_exited = 1; return 0; }
/* * Initialize DirectSound recorder device */ static pj_status_t init_capture_stream( struct dsound_stream *ds_strm, int dev_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned buffer_count) { HRESULT hr; PCMWAVEFORMAT pcmwf; DSCBUFFERDESC dscbdesc; DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT]; unsigned bytes_per_frame; unsigned i; PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL); /* Check device id */ if (dev_id == -1) dev_id = 0; PJ_ASSERT_RETURN(dev_id>=0 && dev_id < (int)dev_count, PJ_EINVAL); /* * Creating recorder device. */ hr = DirectSoundCaptureCreate(dev_info[dev_id].lpGuid, &ds_strm->ds.capture.lpDs, NULL); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); /* Init wave format to initialize buffer */ init_waveformatex( &pcmwf, clock_rate, channel_count); bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE; /* * Setup capture buffer using sound buffer structure that was passed * to play buffer creation earlier. */ pj_bzero(&dscbdesc, sizeof(DSCBUFFERDESC)); dscbdesc.dwSize = sizeof(DSCBUFFERDESC); dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ; dscbdesc.dwBufferBytes = buffer_count * bytes_per_frame; dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; hr = IDirectSoundCapture_CreateCaptureBuffer( ds_strm->ds.capture.lpDs, &dscbdesc, &ds_strm->ds.capture.lpDsBuffer, NULL); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); /* * Create event for play notification. */ ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); if (ds_strm->hEvent == NULL) return pj_get_os_error(); /* * Setup notifications for recording. */ hr = IDirectSoundCaptureBuffer_QueryInterface( ds_strm->ds.capture.lpDsBuffer, &IID_IDirectSoundNotify, (LPVOID *)&ds_strm->lpDsNotify); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); for (i=0; i<buffer_count; ++i) { dsPosNotify[i].dwOffset = i * bytes_per_frame; dsPosNotify[i].hEventNotify = ds_strm->hEvent; } hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify, buffer_count, dsPosNotify); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); hr = IDirectSoundCaptureBuffer_GetCurrentPosition( ds_strm->ds.capture.lpDsBuffer, NULL, &ds_strm->dwBytePos ); if (FAILED(hr)) return PJ_RETURN_OS_ERROR(hr); ds_strm->timestamp.u64 = 0; ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame; /* * Capture latency must always be on a frame boundry, * so compute it based off the calculated buffer_count. */ ds_strm->latency = buffer_count * samples_per_frame * 1000 / clock_rate / channel_count; /* Done setting up recorder device. */ PJ_LOG(5,(THIS_FILE, " DirectSound capture \"%s\" initialized (clock_rate=%d, " "channel_count=%d, samples_per_frame=%d (%dms))", dev_info[dev_id].info.name, clock_rate, channel_count, samples_per_frame, samples_per_frame * 1000 / clock_rate)); return PJ_SUCCESS; }
/* Internal: create BD device. */ static pj_status_t init_streams(struct bd_factory *wf, struct bd_stream *strm, const pjmedia_aud_param *prm) { unsigned ptime; enum bdIMADpj_Status errorInitAEC; wchar_t *deviceNamep=NULL; wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME]; int captureDeviceCount = 0; wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME]; int playbackDeviceCount = 0; PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL); PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL); ptime = prm->samples_per_frame * 1000 / (prm->clock_rate * prm->channel_count); strm->bytes_per_frame = (prm->clock_rate * ((prm->channel_count * prm->bits_per_sample) / 8)) * ptime / 1000; strm->timestampCapture.u64 = 0; strm->timestampPlayback.u64 = 0; //BD_IMAD_PJ bdIMADpj_CreateStructures(&strm->bdIMADpjSettingsPtr, &strm->bdIMADpjWarningPtr); strm->bdIMADpjSettingsPtr->FrameSize_ms = ptime; strm->bdIMADpjSettingsPtr->DiagnosticEnable = BD_IMAD_DIAGNOSTIC; strm->bdIMADpjSettingsPtr->DiagnosticFolderPath = bdImadPjDiagnosticFolderPath; strm->bdIMADpjSettingsPtr->validate = (void *)manage_code; if(prm->clock_rate != 8000 && prm->clock_rate != 16000 && prm->clock_rate != 32000 && prm->clock_rate != 48000) { PJ_LOG(4, (THIS_FILE, "BDIMAD support 8000 Hz, 16000 Hz, 32000 Hz and 48000 Hz " "frequency.")); } strm->bdIMADpjSettingsPtr->SamplingFrequency = prm->clock_rate; if(prm->channel_count > BD_IMAD_MAX_CHANNELS) { PJ_LOG(4, (THIS_FILE, "BDIMAD doesn't support a number of channels upper than %d.", BD_IMAD_MAX_CHANNELS)); } // Enumerate capture sound devices while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) != BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY) { wcscpy(captureDevName[captureDeviceCount], deviceNamep); captureDeviceCount++; } strm->bdIMADpjSettingsPtr->CaptureDevice = captureDevName[(int)prm->rec_id]; // Enumerate playback sound devices while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) != BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY) { wcscpy(playbackDevName[playbackDeviceCount], deviceNamep); playbackDeviceCount++; } strm->bdIMADpjSettingsPtr->PlayDevice = playbackDevName[(int)(prm->play_id-captureDeviceCount)]; strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer = &bdimad_CaptureCallback; strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer_user_data = (void*)strm; strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer = &bdimad_PlaybackCallback; strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer_user_data = (void*)strm; if(strm->bdIMADpjInstance != NULL) bdIMADpj_FreeAEC(&strm->bdIMADpjInstance); strm->bdIMADpjInstance = NULL; errorInitAEC = bdIMADpj_InitAEC(&strm->bdIMADpjInstance, &strm->bdIMADpjSettingsPtr, &strm->bdIMADpjWarningPtr); { int auxInt = (prm->ec_enabled == PJ_TRUE ? 1 : 0); bdIMADpj_setParameter(strm->bdIMADpjInstance, BD_PARAM_IMAD_PJ_AEC_ENABLE, &auxInt); auxInt = 1; //Mic control On by default bdIMADpj_setParameter(strm->bdIMADpjInstance, BD_PARAM_IMAD_PJ_MIC_CONTROL_ENABLE, &auxInt); } if(errorInitAEC != BD_PJ_OK && errorInitAEC != BD_PJ_WARN_BDIMAD_WARNING_ASSERTED) { return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(errorInitAEC); } return PJ_SUCCESS; }
/* * Handle incoming packet from peer. This function is called by * on_rx_from_peer(). */ static void handle_peer_pkt(pj_turn_allocation *alloc, pj_turn_relay_res *rel, char *pkt, pj_size_t len, const pj_sockaddr *src_addr) { pj_turn_permission *perm; /* Lookup permission */ perm = lookup_permission_by_addr(alloc, src_addr, pj_sockaddr_get_len(src_addr)); if (perm == NULL) { /* No permission, discard data */ return; } /* Send Data Indication or ChannelData, depends on whether * this permission is attached to a channel number. */ if (perm->channel != PJ_TURN_INVALID_CHANNEL) { /* Send ChannelData */ pj_turn_channel_data *cd = (pj_turn_channel_data*)rel->tp.tx_pkt; if (len > PJ_TURN_MAX_PKT_LEN) { char peer_addr[80]; pj_sockaddr_print(src_addr, peer_addr, sizeof(peer_addr), 3); PJ_LOG(4,(alloc->obj_name, "Client %s: discarded data from %s " "because it's too long (%d bytes)", alloc->info, peer_addr, len)); return; } /* Init header */ cd->ch_number = pj_htons(perm->channel); cd->length = pj_htons((pj_uint16_t)len); /* Copy data */ pj_memcpy(rel->tp.tx_pkt+sizeof(pj_turn_channel_data), pkt, len); /* Send to client */ alloc->transport->sendto(alloc->transport, rel->tp.tx_pkt, len+sizeof(pj_turn_channel_data), 0, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr)); } else { /* Send Data Indication */ pj_stun_tx_data *tdata; pj_status_t status; status = pj_stun_session_create_ind(alloc->sess, PJ_STUN_DATA_INDICATION, &tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error creating Data indication", status); return; } pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_PEER_ADDR, PJ_TRUE, src_addr, pj_sockaddr_get_len(src_addr)); pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_DATA, (const pj_uint8_t*)pkt, len); pj_stun_session_send_msg(alloc->sess, NULL, PJ_FALSE, PJ_FALSE, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr), tdata); } }
static pj_bool_t on_rx_request( pjsip_rx_data *rdata ) { pj_sockaddr hostaddr; char temp[80], hostip[PJ_INET6_ADDRSTRLEN]; pj_str_t local_uri; pjsip_dialog *dlg = NULL; pjsip_rdata_sdp_info *sdp_info; pjmedia_sdp_session *answer = NULL; pjsip_tx_data *tdata = NULL; call_t *call = NULL; unsigned i; pj_status_t status; PJ_LOG(3,(THIS_FILE, "RX %.*s from %s", (int)rdata->msg_info.msg->line.req.method.name.slen, rdata->msg_info.msg->line.req.method.name.ptr, rdata->pkt_info.src_name)); if (rdata->msg_info.msg->line.req.method.id == PJSIP_REGISTER_METHOD) { /* Let me be a registrar! */ pjsip_hdr hdr_list, *h; pjsip_msg *msg; int expires = -1; pj_list_init(&hdr_list); msg = rdata->msg_info.msg; h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); if (h) { expires = ((pjsip_expires_hdr*)h)->ivalue; pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h)); PJ_LOG(3,(THIS_FILE, " Expires=%d", expires)); } if (expires != 0) { h = (pjsip_hdr*)pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL); if (h) pj_list_push_back(&hdr_list, pjsip_hdr_clone(rdata->tp_info.pool, h)); } pjsip_endpt_respond(app.sip_endpt, &mod_sipecho, rdata, 200, NULL, &hdr_list, NULL, NULL); return PJ_TRUE; } if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) { if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { pj_str_t reason = pj_str("Go away"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 400, &reason, NULL, NULL); } return PJ_TRUE; } sdp_info = pjsip_rdata_get_sdp_info(rdata); if (!sdp_info || !sdp_info->sdp) { pj_str_t reason = pj_str("Require valid offer"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 400, &reason, NULL, NULL); } for (i=0; i<MAX_CALLS; ++i) { if (app.call[i].inv == NULL) { call = &app.call[i]; break; } } if (i==MAX_CALLS) { pj_str_t reason = pj_str("We're full"); pjsip_endpt_respond_stateless( app.sip_endpt, rdata, PJSIP_SC_BUSY_HERE, &reason, NULL, NULL); return PJ_TRUE; } /* Generate Contact URI */ status = pj_gethostip(sip_af, &hostaddr); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to retrieve local host IP", status); return PJ_TRUE; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, sip_port); local_uri = pj_str(temp); status = pjsip_dlg_create_uas_and_inc_lock( pjsip_ua_instance(), rdata, &local_uri, &dlg); if (status == PJ_SUCCESS) answer = create_answer((int)(call-app.call), dlg->pool, sdp_info->sdp); if (status == PJ_SUCCESS) status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &call->inv); if (dlg) pjsip_dlg_dec_lock(dlg); if (status == PJ_SUCCESS) status = pjsip_inv_initial_answer(call->inv, rdata, 100, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status == PJ_SUCCESS) status = pjsip_inv_answer(call->inv, 180, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status == PJ_SUCCESS) status = pjsip_inv_answer(call->inv, 200, NULL, NULL, &tdata); if (status == PJ_SUCCESS) status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 500, NULL, NULL, NULL); destroy_call(call); } else { call->inv->mod_data[mod_sipecho.id] = call; } return PJ_TRUE; }
/* * Create new allocation. */ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_transport *transport, const pj_sockaddr_t *src_addr, unsigned src_addr_len, const pj_stun_rx_data *rdata, pj_stun_session *srv_sess, pj_turn_allocation **p_alloc) { pj_turn_srv *srv = transport->listener->server; const pj_stun_msg *msg = rdata->msg; pj_pool_t *pool; alloc_request req; pj_turn_allocation *alloc; pj_stun_session_cb sess_cb; char str_tmp[80]; pj_status_t status; /* Parse ALLOCATE request */ status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len); if (status != PJ_SUCCESS) return status; pool = pj_pool_create(srv->core.pf, "alloc%p", 1000, 1000, NULL); /* Init allocation structure */ alloc = PJ_POOL_ZALLOC_T(pool, pj_turn_allocation); alloc->pool = pool; alloc->obj_name = pool->obj_name; alloc->relay.tp.sock = PJ_INVALID_SOCKET; alloc->server = transport->listener->server; alloc->bandwidth = req.bandwidth; /* Set transport */ alloc->transport = transport; pj_turn_transport_add_ref(transport, alloc); alloc->hkey.tp_type = transport->listener->tp_type; pj_memcpy(&alloc->hkey.clt_addr, src_addr, src_addr_len); status = pj_lock_create_recursive_mutex(pool, alloc->obj_name, &alloc->lock); if (status != PJ_SUCCESS) { goto on_error; } /* Create peer hash table */ alloc->peer_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Create channel hash table */ alloc->ch_table = pj_hash_create(pool, PEER_TABLE_SIZE); /* Print info */ pj_ansi_strcpy(alloc->info, pj_turn_tp_type_name(transport->listener->tp_type)); alloc->info[3] = ':'; pj_sockaddr_print(src_addr, alloc->info+4, sizeof(alloc->info)-4, 3); /* Create STUN session to handle STUN communication with client */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_send_msg = &stun_on_send_msg; sess_cb.on_rx_request = &stun_on_rx_request; sess_cb.on_rx_indication = &stun_on_rx_indication; status = pj_stun_session_create(&srv->core.stun_cfg, alloc->obj_name, &sess_cb, PJ_FALSE, &alloc->sess); if (status != PJ_SUCCESS) { goto on_error; } /* Attach to STUN session */ pj_stun_session_set_user_data(alloc->sess, alloc); /* Init authentication credential */ status = init_cred(alloc, msg); if (status != PJ_SUCCESS) { goto on_error; } /* Attach authentication credential to STUN session */ pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM, &alloc->cred); /* Create the relay resource */ status = create_relay(srv, alloc, msg, &req, &alloc->relay); if (status != PJ_SUCCESS) { goto on_error; } /* Register this allocation */ pj_turn_srv_register_allocation(srv, alloc); /* Respond to ALLOCATE request */ status = send_allocate_response(alloc, srv_sess, transport, rdata); if (status != PJ_SUCCESS) goto on_error; /* Done */ pj_sockaddr_print(&alloc->relay.hkey.addr, str_tmp, sizeof(str_tmp), 3); PJ_LOG(4,(alloc->obj_name, "Client %s created, relay addr=%s:%s", alloc->info, pj_turn_tp_type_name(req.tp_type), str_tmp)); /* Success */ *p_alloc = alloc; return PJ_SUCCESS; on_error: /* Send reply to the ALLOCATE request */ pj_strerror(status, str_tmp, sizeof(str_tmp)); pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp, transport, PJ_TRUE, src_addr, src_addr_len); /* Cleanup */ destroy_allocation(alloc); return status; }
/* main() * * If called with argument, treat argument as SIP URL to be called. * Otherwise wait for incoming calls. */ int main(int argc, char *argv[]) { struct pj_getopt_option long_options[] = { { "local-port", 1, 0, 'p' }, { "tcp", 0, 0, 't' }, { "ipv6", 0, 0, '6' }, { "help", 0, 0, 'h' } }; int c, option_index; pj_log_set_level(5); pj_init(); sip_af = pj_AF_INET(); pj_optind = 0; while ((c = pj_getopt_long(argc, argv, "p:t6h", long_options, &option_index)) != -1) { switch (c) { case 'p': sip_port = atoi(pj_optarg); break; case 't': sip_tcp = PJ_TRUE; break; case 'h': usage(); return 0; case '6': sip_af = pj_AF_INET6(); break; default: PJ_LOG(1,(THIS_FILE, "Argument \"%s\" is not valid. Use --help to see help", argv[pj_optind-1])); return -1; } } if (init_stack()) goto on_error; /* If URL is specified, then make call immediately. */ if (pj_optind != argc) { pj_sockaddr hostaddr; char hostip[PJ_INET6_ADDRSTRLEN+2]; char temp[80]; call_t *call; pj_str_t dst_uri = pj_str(argv[pj_optind]); pj_str_t local_uri; pjsip_dialog *dlg; pj_status_t status; pjsip_tx_data *tdata; if (pj_gethostip(sip_af, &hostaddr) != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Unable to retrieve local host IP")); goto on_error; } pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2); pj_ansi_sprintf(temp, "<sip:sipecho@%s:%d>", hostip, sip_port); local_uri = pj_str(temp); call = &app.call[0]; status = pjsip_dlg_create_uac( pjsip_ua_instance(), &local_uri, /* local URI */ &local_uri, /* local Contact */ &dst_uri, /* remote URI */ &dst_uri, /* remote target */ &dlg); /* dialog */ if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Unable to create UAC dialog", status); return 1; } status = pjsip_inv_create_uac( dlg, NULL, 0, &call->inv); if (status != PJ_SUCCESS) goto on_error; call->inv->mod_data[mod_sipecho.id] = call; status = pjsip_inv_invite(call->inv, &tdata); if (status != PJ_SUCCESS) goto on_error; status = pjsip_inv_send_msg(call->inv, tdata); if (status != PJ_SUCCESS) goto on_error; puts("Press ENTER to quit..."); } else { puts("Ready for incoming calls. Press ENTER to quit..."); } for (;;) { char s[10]; printf("\nMenu:\n" " h Hangup all calls\n" " l %s message logging\n" " q Quit\n", (app.enable_msg_logging? "Disable" : "Enable")); if (fgets(s, sizeof(s), stdin) == NULL) continue; if (s[0]=='q') break; switch (s[0]) { case 'l': app.enable_msg_logging = !app.enable_msg_logging; break; case 'h': hangup_all(); break; } } destroy_stack(); puts("Bye bye.."); return 0; on_error: puts("An error has occurred. run a debugger.."); return 1; }
/* * 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); }
/* * API: set capability * Currently just supporting toggle between speaker and earpiece */ static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm, pjmedia_aud_dev_cap cap, const void *value) { struct bb10_stream *stream = (struct bb10_stream*)strm; if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE && (stream->param.dir & PJMEDIA_DIR_PLAYBACK)) { pjmedia_aud_dev_route route; pj_bool_t need_restart; pj_status_t ret; PJ_ASSERT_RETURN(value, PJ_EINVAL); /* OS 10.2.1 requires pausing audio stream */ /* No longer necessary! * See https://trac.pjsip.org/repos/ticket/1743 */ need_restart = PJ_FALSE; /* need_restart = (stream->pb_thread != NULL); if (need_restart) { PJ_LOG(4,(THIS_FILE, "pausing audio stream..")); ret = bb10_stream_stop(strm); if (ret != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, ret, "Error pausing stream")); return ret; } } */ route = *((pjmedia_aud_dev_route*)value); PJ_LOG(4,(THIS_FILE, "setting audio route to %d..", route)); /* Use the initialization function which lazy-inits the * handle for routing */ if (route == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER) { ret = bb10_initialize_playback_ctrl(stream,true); } else { ret = bb10_initialize_playback_ctrl(stream,false); } if (need_restart) { PJ_LOG(4,(THIS_FILE, "resuming audio stream..")); ret = bb10_stream_start(strm); if (ret != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, ret, "Error resuming stream")); } } return ret; } else if (cap==PJMEDIA_AUD_DEV_CAP_EC && (stream->param.dir & PJMEDIA_DIR_CAPTURE)) { /* EC is always enabled. Silently ignore the request */ return PJ_SUCCESS; } TRACE_((THIS_FILE,"bb10_stream_set_cap() = PJMEDIA_EAUD_INVCAP")); return PJMEDIA_EAUD_INVCAP; }
/* Process authorization challenge */ static pj_status_t process_auth( pj_pool_t *req_pool, const pjsip_www_authenticate_hdr *hchal, const pjsip_uri *uri, pjsip_tx_data *tdata, pjsip_auth_clt_sess *sess, pjsip_cached_auth *cached_auth, pjsip_authorization_hdr **h_auth) { const pjsip_cred_info *cred; pjsip_authorization_hdr *sent_auth = NULL; pjsip_hdr *hdr; pj_status_t status; /* See if we have sent authorization header for this realm */ hdr = tdata->msg->hdr.next; while (hdr != &tdata->msg->hdr) { if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE && hdr->type == PJSIP_H_AUTHORIZATION) || (hchal->type == PJSIP_H_PROXY_AUTHENTICATE && hdr->type == PJSIP_H_PROXY_AUTHORIZATION)) { sent_auth = (pjsip_authorization_hdr*) hdr; if (pj_stricmp(&hchal->challenge.common.realm, &sent_auth->credential.common.realm )==0) { /* If this authorization has empty response, remove it. */ if (pj_stricmp(&sent_auth->scheme, &pjsip_DIGEST_STR)==0 && sent_auth->credential.digest.response.slen == 0) { /* This is empty authorization, remove it. */ hdr = hdr->next; pj_list_erase(sent_auth); continue; } else { /* Found previous authorization attempt */ break; } } } hdr = hdr->next; } /* If we have sent, see if server rejected because of stale nonce or * other causes. */ if (hdr != &tdata->msg->hdr) { pj_bool_t stale; /* Detect "stale" state */ stale = hchal->challenge.digest.stale; if (!stale) { /* If stale is false, check is nonce has changed. Some servers * (broken ones!) want to change nonce but they fail to set * stale to true. */ stale = pj_strcmp(&hchal->challenge.digest.nonce, &sent_auth->credential.digest.nonce); } if (stale == PJ_FALSE) { /* Our credential is rejected. No point in trying to re-supply * the same credential. */ PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s: " "server rejected with stale=false", sent_auth->credential.digest.username.slen, sent_auth->credential.digest.username.ptr, sent_auth->credential.digest.realm.slen, sent_auth->credential.digest.realm.ptr)); return PJSIP_EFAILEDCREDENTIAL; } cached_auth->stale_cnt++; if (cached_auth->stale_cnt >= PJSIP_MAX_STALE_COUNT) { /* Our credential is rejected. No point in trying to re-supply * the same credential. */ PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s: " "maximum number of stale retries exceeded", sent_auth->credential.digest.username.slen, sent_auth->credential.digest.username.ptr, sent_auth->credential.digest.realm.slen, sent_auth->credential.digest.realm.ptr)); return PJSIP_EAUTHSTALECOUNT; } /* Otherwise remove old, stale authorization header from the mesasge. * We will supply a new one. */ pj_list_erase(sent_auth); } /* Find credential to be used for the challenge. */ cred = auth_find_cred( sess, &hchal->challenge.common.realm, &hchal->scheme); if (!cred) { const pj_str_t *realm = &hchal->challenge.common.realm; PJ_LOG(4,(THIS_FILE, "Unable to set auth for %s: can not find credential for %.*s/%.*s", tdata->obj_name, realm->slen, realm->ptr, hchal->scheme.slen, hchal->scheme.ptr)); return PJSIP_ENOCREDENTIAL; } /* Respond to authorization challenge. */ status = auth_respond( req_pool, hchal, uri, cred, &tdata->msg->line.req.method, sess->pool, cached_auth, h_auth); return status; }
/** * Is call using a secure RTP method (SRTP/ZRTP) */ PJ_DECL(pj_str_t) call_secure_media_info(pjsua_call_id call_id) { pjsua_call *call; pj_status_t status; unsigned i; pjmedia_transport_info tp_info; pj_str_t result = pj_str(""); PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, result); PJSUA_LOCK(); if (pjsua_call_has_media(call_id)) { call = &pjsua_var.calls[call_id]; for (i = 0; i < call->med_cnt; ++i) { pjsua_call_media *call_med = &call->media[i]; PJ_LOG(4, (THIS_FILE, "Get secure for media type %d", call_med->type)); if (call_med->tp && call_med->type == PJMEDIA_TYPE_AUDIO) { pjmedia_transport_info tp_info; pjmedia_transport_info_init(&tp_info); pjmedia_transport_get_info(call_med->tp, &tp_info); if (tp_info.specific_info_cnt > 0) { unsigned j; for (j = 0; j < tp_info.specific_info_cnt; ++j) { if (tp_info.spc_info[j].type == PJMEDIA_TRANSPORT_TYPE_SRTP) { pjmedia_srtp_info *srtp_info = (pjmedia_srtp_info*) tp_info.spc_info[j].buffer; if (srtp_info->active) { result = pj_str("SRTP"); break; } } #if defined(PJMEDIA_HAS_ZRTP) && PJMEDIA_HAS_ZRTP!=0 else if (tp_info.spc_info[j].type == PJMEDIA_TRANSPORT_TYPE_ZRTP) { zrtp_state_info info = jzrtp_getInfoFromTransport(call_med->tp); if(info.secure){ char msg[512]; pj_ansi_snprintf(msg, sizeof(msg), "ZRTP - %s\n%.*s\n%.*s", info.sas_verified ? "Verified": "Not verified", info.sas.slen, info.sas.ptr, info.cipher.slen, info.cipher.ptr); pj_strdup2_with_null(css_var.pool, &result, msg); break; } } #endif } } } } } PJSUA_UNLOCK(); return result; }