/* API: create stream */ static pj_status_t and_factory_create_stream( pjmedia_vid_dev_factory *ff, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { and_factory *f = (and_factory*)ff; pj_pool_t *pool; and_stream *strm; and_dev_info *adi; const pjmedia_video_format_detail *vfd; const pjmedia_video_format_info *vfi; pjmedia_video_apply_fmt_param vafp; pj_uint32_t and_fmt; unsigned convert_to_i420 = 0; pj_status_t status = PJ_SUCCESS; JNIEnv *jni_env; pj_bool_t with_attach; jobject jcam; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); pj_bzero(&vafp, sizeof(vafp)); adi = &f->dev_info[param->cap_id]; vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); vfi = pjmedia_get_video_format_info(NULL, param->fmt.id); if (param->fmt.id == PJMEDIA_FORMAT_I420 && adi->forced_i420) { /* Not really support I420, need to convert it from YV12/NV21 */ if (adi->has_nv21) { and_fmt = pj_fmt_to_and(PJMEDIA_FORMAT_NV21); convert_to_i420 = 1; } else if (adi->has_yv12) { and_fmt = pj_fmt_to_and(PJMEDIA_FORMAT_YV12); convert_to_i420 = 2; } else pj_assert(!"Bug!"); } else { and_fmt = pj_fmt_to_and(param->fmt.id); } if (!vfi || !and_fmt) return PJMEDIA_EVID_BADFORMAT; vafp.size = vfd->size; if (vfi->apply_fmt(vfi, &vafp) != PJ_SUCCESS) return PJMEDIA_EVID_BADFORMAT; /* Create and Initialize stream descriptor */ pool = pj_pool_create(f->pf, "and-dev", 512, 512, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); strm = PJ_POOL_ZALLOC_T(pool, and_stream); pj_memcpy(&strm->param, param, sizeof(*param)); strm->pool = pool; strm->factory = f; pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); strm->user_data = user_data; pj_memcpy(&strm->vafp, &vafp, sizeof(vafp)); strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1); /* Allocate buffer for YV12 -> I420 conversion */ if (convert_to_i420) { pj_assert(vfi->plane_cnt > 1); strm->convert_to_i420 = convert_to_i420; strm->convert_buf = pj_pool_alloc(pool, vafp.plane_bytes[1]); } /* Native preview */ if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW) { } with_attach = jni_get_env(&jni_env); /* Instantiate PjCamera */ strm->cam_size.w = (vfd->size.w > vfd->size.h? vfd->size.w: vfd->size.h); strm->cam_size.h = (vfd->size.w > vfd->size.h? vfd->size.h: vfd->size.w); jcam = (*jni_env)->NewObject(jni_env, jobjs.cam.cls, jobjs.cam.m_init, adi->dev_idx, /* idx */ strm->cam_size.w, /* w */ strm->cam_size.h, /* h */ and_fmt, /* fmt */ vfd->fps.num*1000/ vfd->fps.denum, /* fps */ (jlong)(intptr_t)strm, /* user data */ NULL /* SurfaceView */ ); if (jcam == NULL) { PJ_LOG(3, (THIS_FILE, "Unable to create PjCamera instance")); status = PJMEDIA_EVID_SYSERR; goto on_return; } strm->jcam = (jobject)(*jni_env)->NewGlobalRef(jni_env, jcam); (*jni_env)->DeleteLocalRef(jni_env, jcam); if (strm->jcam == NULL) { PJ_LOG(3, (THIS_FILE, "Unable to create global ref to PjCamera")); status = PJMEDIA_EVID_SYSERR; goto on_return; } /* Video orientation. * If we send in portrait, we need to set up orientation converter * as well. */ if ((param->flags & PJMEDIA_VID_DEV_CAP_ORIENTATION) || (vfd->size.h > vfd->size.w)) { if (param->orient == PJMEDIA_ORIENT_UNKNOWN) param->orient = PJMEDIA_ORIENT_NATURAL; and_stream_set_cap(&strm->base, PJMEDIA_VID_DEV_CAP_ORIENTATION, ¶m->orient); } on_return: jni_detach_env(with_attach); /* Success */ if (status == PJ_SUCCESS) { strm->base.op = &stream_op; *p_vid_strm = &strm->base; } return status; }
/* * Convert text to IPv4/IPv6 address. */ PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst) { char tempaddr[PJ_INET6_ADDRSTRLEN]; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL); /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be * compatible with pj_inet_aton() */ if (af==PJ_AF_INET) { ((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE; } /* Caution: * this function might be called with cp->slen >= 46 * (i.e. when called with hostname to check if it's an IP addr). */ if (src->slen >= PJ_INET6_ADDRSTRLEN) { return PJ_ENAMETOOLONG; } pj_memcpy(tempaddr, src->ptr, src->slen); tempaddr[src->slen] = '\0'; #if defined(PJ_SOCK_HAS_INET_PTON) && PJ_SOCK_HAS_INET_PTON != 0 /* * Implementation using inet_pton() */ if (inet_pton(af, tempaddr, dst) != 1) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; #elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE) /* * Implementation on Windows, using WSAStringToAddress(). * Should also work on Unicode systems. */ { PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) pj_sockaddr sock_addr; int addr_len = sizeof(sock_addr); int rc; sock_addr.addr.sa_family = (pj_uint16_t)af; rc = WSAStringToAddress( PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), af, NULL, (LPSOCKADDR)&sock_addr, &addr_len); if (rc != 0) { /* If you get rc 130022 Invalid argument (WSAEINVAL) with IPv6, * check that you have IPv6 enabled (install it in the network * adapter). */ pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } if (sock_addr.addr.sa_family == PJ_AF_INET) { pj_memcpy(dst, &sock_addr.ipv4.sin_addr, 4); return PJ_SUCCESS; } else if (sock_addr.addr.sa_family == PJ_AF_INET6) { pj_memcpy(dst, &sock_addr.ipv6.sin6_addr, 16); return PJ_SUCCESS; } else { pj_assert(!"Shouldn't happen"); return PJ_EBUG; } } #elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 /* IPv6 support is disabled, just return error without raising assertion */ return PJ_EIPV6NOTSUP; #else pj_assert(!"Not supported"); return PJ_EIPV6NOTSUP; #endif }
/* * pj_ioqueue_register_sock() */ PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, pj_ioqueue_t *ioqueue, pj_sock_t sock, void *user_data, const pj_ioqueue_callback *cb, pj_ioqueue_key_t **key ) { HANDLE hioq; pj_ioqueue_key_t *rec; u_long value; int rc; PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL); pj_lock_acquire(ioqueue->lock); #if PJ_IOQUEUE_HAS_SAFE_UNREG /* Scan closing list first to release unused keys. * Must do this with lock acquired. */ scan_closing_keys(ioqueue); /* If safe unregistration is used, then get the key record from * the free list. */ if (pj_list_empty(&ioqueue->free_list)) { pj_lock_release(ioqueue->lock); return PJ_ETOOMANY; } rec = ioqueue->free_list.next; pj_list_erase(rec); /* Set initial reference count to 1 */ pj_assert(pj_atomic_get(rec->ref_count) == 0); pj_atomic_inc(rec->ref_count); rec->closing = 0; #else rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); #endif /* Build the key for this socket. */ rec->ioqueue = ioqueue; rec->hnd = (HANDLE)sock; rec->hnd_type = HND_IS_SOCKET; rec->user_data = user_data; pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback)); /* Set concurrency for this handle */ rc = pj_ioqueue_set_concurrency(rec, ioqueue->default_concurrency); if (rc != PJ_SUCCESS) { pj_lock_release(ioqueue->lock); return rc; } #if PJ_HAS_TCP rec->connecting = 0; #endif /* Set socket to nonblocking. */ value = 1; rc = ioctlsocket(sock, FIONBIO, &value); if (rc != 0) { pj_lock_release(ioqueue->lock); return PJ_RETURN_OS_ERROR(WSAGetLastError()); } /* Associate with IOCP */ hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0); if (!hioq) { pj_lock_release(ioqueue->lock); return PJ_RETURN_OS_ERROR(GetLastError()); } *key = rec; #if PJ_IOQUEUE_HAS_SAFE_UNREG pj_list_push_back(&ioqueue->active_list, rec); #endif pj_lock_release(ioqueue->lock); return PJ_SUCCESS; }
/* * WMME capture and playback thread. */ static int PJ_THREAD_FUNC wmme_dev_thread(void *arg) { pjmedia_snd_stream *strm = arg; HANDLE events[3]; unsigned eventCount; unsigned bytes_per_frame; pj_status_t status = PJ_SUCCESS; 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. */ #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0 if (strm->dir & PJMEDIA_DIR_PLAYBACK) CeSetThreadPriority(GetCurrentThread(), 153); else CeSetThreadPriority(GetCurrentThread(), 247); #else SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); #endif /* 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 WMME capture and play buffer. */ while (status == PJ_SUCCESS) { 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 wmme_stream *wmme_strm = &strm->play_strm; MMRESULT mr = MMSYSERR_NOERROR; status = PJ_SUCCESS; /* * Windows Multimedia has requested us to feed some frames to * playback buffer. */ while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE) { void* buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData; PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d", wmme_strm->dwBufIdx)); /* Get frame from application. */ status = (*strm->play_cb)(strm->user_data, wmme_strm->timestamp.u32.lo, buffer, bytes_per_frame); if (status != PJ_SUCCESS) break; /* Write to the device. */ mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]), sizeof(WAVEHDR)); if (mr != MMSYSERR_NOERROR) { status = PJ_STATUS_FROM_OS(mr); break; } /* Increment position. */ if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx) wmme_strm->dwBufIdx = 0; wmme_strm->timestamp.u64 += strm->samples_per_frame / strm->channel_count; } } else { struct wmme_stream *wmme_strm = &strm->rec_strm; MMRESULT mr = MMSYSERR_NOERROR; status = PJ_SUCCESS; /* * Windows Multimedia has indicated that it has some frames ready * in the capture buffer. Get as much frames as possible to * prevent overflows. */ #if 0 { static DWORD tc = 0; DWORD now = GetTickCount(); DWORD i = 0; DWORD bits = 0; if (tc == 0) tc = now; for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i) { bits = bits << 4; bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE; } PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, " "Flags: %6.6x\n", wmme_strm->dwBufIdx, now - tc, bits)); tc = now; } #endif while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE) { char* buffer = (char*) wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData; unsigned cap_len = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded; /* PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len, wmme_strm->dwBufIdx)); */ if (cap_len < bytes_per_frame) pj_bzero(buffer + cap_len, bytes_per_frame - cap_len); /* Copy the audio data out of the wave buffer. */ pj_memcpy(strm->buffer, buffer, bytes_per_frame); /* Re-add the buffer to the device. */ mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]), sizeof(WAVEHDR)); if (mr != MMSYSERR_NOERROR) { status = PJ_STATUS_FROM_OS(mr); break; } /* Call callback */ status = (*strm->rec_cb)(strm->user_data, wmme_strm->timestamp.u32.lo, strm->buffer, bytes_per_frame); if (status != PJ_SUCCESS) break; /* Increment position. */ if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx) wmme_strm->dwBufIdx = 0; wmme_strm->timestamp.u64 += strm->samples_per_frame / strm->channel_count; } } } PJ_LOG(5,(THIS_FILE, "WMME: thread stopping..")); return 0; }
/* * Create server subscription. */ PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_accept_hdr *accept; pjsip_event_hdr *event; content_type_e content_type = CONTENT_TYPE_NONE; pjsip_evsub *sub; pjsip_pres *pres; char obj_name[PJ_MAX_OBJ_NAME]; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is SUBSCRIBE */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_subscribe_method)==0, PJSIP_SIMPLE_ENOTSUBSCRIBE); /* Check that Event header contains "presence" */ event = (pjsip_event_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); if (!event) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); } if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) { return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); } /* Check that request contains compatible Accept header. */ accept = (pjsip_accept_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); if (accept) { unsigned i; for (i=0; i<accept->count; ++i) { if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) { content_type = CONTENT_TYPE_PIDF; break; } else if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) { content_type = CONTENT_TYPE_XPIDF; break; } } if (i==accept->count) { /* Nothing is acceptable */ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); } } else { /* No Accept header. * Treat as "application/pidf+xml" */ content_type = CONTENT_TYPE_PIDF; } /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, 0, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server presence subscription */ pres = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_pres); pres->dlg = dlg; pres->sub = sub; pres->content_type = content_type; if (user_cb) pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "pres%p", dlg->pool); pres->status_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "tmpres%p", dlg->pool); pres->tmp_pool = pj_pool_create(dlg->pool->factory, obj_name, 512, 512, NULL); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; }
pjsip_tx_data *PJUtils::clone_tdata(pjsip_tx_data *tdata) { pjsip_tx_data *cloned_tdata; pj_status_t status; status = pjsip_endpt_create_tdata(stack_data.endpt, &cloned_tdata); if (status != PJ_SUCCESS) { return NULL; } // Always increment ref counter to 1. pjsip_tx_data_add_ref(cloned_tdata); // Clone the message from the supplied tdata. cloned_tdata->msg = pjsip_msg_clone(cloned_tdata->pool, tdata->msg); if (cloned_tdata->msg == NULL) { pjsip_tx_data_dec_ref(cloned_tdata); cloned_tdata = NULL; } // Copy the trail identifier to the cloned message. set_trail(cloned_tdata, get_trail(tdata)); if (tdata->msg->type == PJSIP_REQUEST_MSG) { // Substitute the branch value in the top Via header with a unique // branch identifier. pjsip_via_hdr *via = (pjsip_via_hdr*) pjsip_msg_find_hdr(cloned_tdata->msg, PJSIP_H_VIA, NULL); via->branch_param.ptr = (char*) pj_pool_alloc(cloned_tdata->pool, PJSIP_MAX_BRANCH_LEN); via->branch_param.slen = PJSIP_RFC3261_BRANCH_LEN; pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN); pj_str_t tmp; tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN + 2; // I have absolutely no idea what the following two lines do, but it // doesn't seem to work without them! *(tmp.ptr-2) = (pj_int8_t)(via->branch_param.slen+73); *(tmp.ptr-1) = (pj_int8_t)(via->branch_param.slen+99); pj_generate_unique_string( &tmp ); via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; } // If the original message already had a specified transport set this // on the clone. (Must use pjsip_tx_data_set_transport to ensure // reference counts get updated.) if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) { pjsip_tx_data_set_transport(cloned_tdata, &tdata->tp_sel); } // If the message has any addr in dest_info, copy that if (tdata->dest_info.addr.count != 0) { pj_memcpy(&cloned_tdata->dest_info, &tdata->dest_info, sizeof(cloned_tdata->dest_info)); } return cloned_tdata; }
/* * Callback upon request completion. */ static void on_request_complete(pj_stun_session *stun_sess, pj_status_t status, void *token, pj_stun_tx_data *tdata, const pj_stun_msg *response, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { nat_detect_session *sess; pj_stun_sockaddr_attr *mattr = NULL; pj_stun_changed_addr_attr *ca = NULL; pj_uint32_t *tsx_id; int cmp; unsigned test_id; PJ_UNUSED_ARG(token); PJ_UNUSED_ARG(tdata); PJ_UNUSED_ARG(src_addr); PJ_UNUSED_ARG(src_addr_len); sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess); pj_grp_lock_acquire(sess->grp_lock); /* Find errors in the response */ if (status == PJ_SUCCESS) { /* Check error message */ if (PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) { pj_stun_errcode_attr *eattr; int err_code; eattr = (pj_stun_errcode_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); if (eattr != NULL) err_code = eattr->err_code; else err_code = PJ_STUN_SC_SERVER_ERROR; status = PJ_STATUS_FROM_STUN_CODE(err_code); } else { /* Get MAPPED-ADDRESS or XOR-MAPPED-ADDRESS */ mattr = (pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); if (mattr == NULL) { mattr = (pj_stun_sockaddr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); } if (mattr == NULL) { status = PJNATH_ESTUNNOMAPPEDADDR; } /* Get CHANGED-ADDRESS attribute */ ca = (pj_stun_changed_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0); if (ca == NULL) { status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR); } } } /* Save the result */ tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id; test_id = tsx_id[2]; if (test_id >= ST_MAX) { PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response", test_id)); end_session(sess, PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR), PJ_STUN_NAT_TYPE_ERR_UNKNOWN); goto on_return; } PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d", test_names[test_id], status)); sess->result[test_id].complete = PJ_TRUE; sess->result[test_id].status = status; if (status == PJ_SUCCESS) { pj_memcpy(&sess->result[test_id].ma, &mattr->sockaddr.ipv4, sizeof(pj_sockaddr_in)); pj_memcpy(&sess->result[test_id].ca, &ca->sockaddr.ipv4, sizeof(pj_sockaddr_in)); } /* Send Test 1B only when Test 2 completes. Must not send Test 1B * before Test 2 completes to avoid creating mapping on the NAT. */ if (!sess->result[ST_TEST_1B].executed && sess->result[ST_TEST_2].complete && sess->result[ST_TEST_2].status != PJ_SUCCESS && sess->result[ST_TEST_1].complete && sess->result[ST_TEST_1].status == PJ_SUCCESS) { cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma, sizeof(pj_sockaddr_in)); if (cmp != 0) send_test(sess, ST_TEST_1B, &sess->result[ST_TEST_1].ca, 0); } if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess)) goto on_return; /* Handle the test result according to RFC 3489 page 22: +--------+ | Test | | 1 | +--------+ | | V /\ /\ N / \ Y / \ Y +--------+ UDP <-------/Resp\--------->/ IP \------------->| Test | Blocked \ ? / \Same/ | 2 | \ / \? / +--------+ \/ \/ | | N | | V V /\ +--------+ Sym. N / \ | Test | UDP <---/Resp\ | 2 | Firewall \ ? / +--------+ \ / | \/ V |Y /\ /\ | Symmetric N / \ +--------+ N / \ V NAT <--- / IP \<-----| Test |<--- /Resp\ Open \Same/ | 1B | \ ? / Internet \? / +--------+ \ / \/ \/ | |Y | | | V | Full | Cone V /\ +--------+ / \ Y | Test |------>/Resp\---->Restricted | 3 | \ ? / +--------+ \ / \/ |N | Port +------>Restricted Figure 2: Flow for type discovery process */ switch (sess->result[ST_TEST_1].status) { case PJNATH_ESTUNTIMEDOUT: /* * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED); break; case PJ_SUCCESS: /* * Test 1 is successful. Further tests are needed to detect * NAT type. Compare the MAPPED-ADDRESS with the local address. */ cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma, sizeof(pj_sockaddr_in)); if (cmp==0) { /* * MAPPED-ADDRESS and local address is equal. Need one more * test to determine NAT type. */ switch (sess->result[ST_TEST_2].status) { case PJ_SUCCESS: /* * Test 2 is also successful. We're in the open. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 2 has timed out. We're behind somekind of UDP * firewall. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP); break; default: /* * We've got other error with Test 2. */ end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } else { /* * MAPPED-ADDRESS is different than local address. * We're behind NAT. */ switch (sess->result[ST_TEST_2].status) { case PJ_SUCCESS: /* * Test 2 is successful. We're behind a full-cone NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 2 has timed-out Check result of test 1B.. */ switch (sess->result[ST_TEST_1B].status) { case PJ_SUCCESS: /* * Compare the MAPPED-ADDRESS of test 1B with the * MAPPED-ADDRESS returned in test 1.. */ cmp = pj_memcmp(&sess->result[ST_TEST_1].ma, &sess->result[ST_TEST_1B].ma, sizeof(pj_sockaddr_in)); if (cmp != 0) { /* * MAPPED-ADDRESS is different, we're behind a * symmetric NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC); } else { /* * MAPPED-ADDRESS is equal. We're behind a restricted * or port-restricted NAT, depending on the result of * test 3. */ switch (sess->result[ST_TEST_3].status) { case PJ_SUCCESS: /* * Test 3 is successful, we're behind a restricted * NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; case PJNATH_ESTUNTIMEDOUT: /* * Test 3 failed, we're behind a port restricted * NAT. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_PORT_RESTRICTED); break; default: /* * Got other error with test 3. */ end_session(sess, sess->result[ST_TEST_3].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } break; case PJNATH_ESTUNTIMEDOUT: /* * Strangely test 1B has failed. Maybe connectivity was * lost? Or perhaps port 3489 (the usual port number in * CHANGED-ADDRESS) is blocked? */ switch (sess->result[ST_TEST_3].status) { case PJ_SUCCESS: /* Although test 1B failed, test 3 was successful. * It could be that port 3489 is blocked, while the * NAT itself looks to be a Restricted one. */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_RESTRICTED); break; default: /* Can't distinguish between Symmetric and Port * Restricted, so set the type to Unknown */ end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } break; default: /* * Got other error with test 1B. */ end_session(sess, sess->result[ST_TEST_1B].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } break; default: /* * We've got other error with Test 2. */ end_session(sess, sess->result[ST_TEST_2].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } } break; default: /* * We've got other error with Test 1. */ end_session(sess, sess->result[ST_TEST_1].status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN); break; } on_return: pj_grp_lock_release(sess->grp_lock); }
/* * platform_strerror() * * Platform specific error message. This file is called by pj_strerror() * in errno.c */ int platform_strerror( pj_os_err_type os_errcode, char *buf, pj_size_t bufsize) { pj_size_t len = 0; PJ_DECL_UNICODE_TEMP_BUF(wbuf,128); pj_assert(buf != NULL); pj_assert(bufsize >= 0); /* * MUST NOT check stack here. * This function might be called from PJ_CHECK_STACK() itself! //PJ_CHECK_STACK(); */ if (!len) { #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0) int i; for (i = 0; gaErrorList[i].msg; ++i) { if (gaErrorList[i].code == os_errcode) { len = strlen(gaErrorList[i].msg); if ((pj_size_t)len >= bufsize) { len = bufsize-1; } pj_memcpy(buf, gaErrorList[i].msg, len); buf[len] = '\0'; break; } } #endif /* PJ_HAS_ERROR_STRING */ } if (!len) { #if PJ_NATIVE_STRING_IS_UNICODE len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, os_errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), wbuf, sizeof(wbuf), NULL); if (len) { pj_unicode_to_ansi(wbuf, len, buf, bufsize); } #else len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, os_errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (int)bufsize, NULL); buf[bufsize-1] = '\0'; #endif if (len) { /* Remove trailing newlines. */ while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { buf[len-1] = '\0'; --len; } } } if (!len) { len = pj_ansi_snprintf( buf, bufsize, "Win32 error code %u", (unsigned)os_errcode); if (len < 0 || len >= (int)bufsize) len = bufsize-1; buf[len] = '\0'; } return (int)len; }
static int print_media_desc( pjmedia_sdp_media *m, char *buf, int len) { char *p = buf; char *end = buf+len; unsigned i; int printed; /* check length for the "m=" line. */ if (len < m->desc.media.slen+m->desc.transport.slen+12+24) { return -1; } *p++ = 'm'; /* m= */ *p++ = '='; pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen); p += m->desc.media.slen; *p++ = ' '; printed = pj_utoa(m->desc.port, p); p += printed; if (m->desc.port_count > 1) { *p++ = '/'; printed = pj_utoa(m->desc.port_count, p); p += printed; } *p++ = ' '; pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen); p += m->desc.transport.slen; for (i=0; i<m->desc.fmt_count; ++i) { *p++ = ' '; pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen); p += m->desc.fmt[i].slen; } *p++ = '\r'; *p++ = '\n'; /* print connection info, if present. */ if (m->conn) { printed = print_connection_info(m->conn, p, (int)(end-p)); if (printed < 0) { return -1; } p += printed; } /* print optional bandwidth info. */ for (i=0; i<m->bandw_count; ++i) { printed = (int)print_bandw(m->bandw[i], p, end-p); if (printed < 0) { return -1; } p += printed; } /* print attributes. */ for (i=0; i<m->attr_count; ++i) { printed = (int)print_attr(m->attr[i], p, end-p); if (printed < 0) { return -1; } p += printed; } return (int)(p-buf); }
/* This is a recursive function. */ static int xml_print_node( const pj_xml_node *node, int indent, char *buf, pj_size_t len ) { int i; char *p = buf; pj_xml_attr *attr; pj_xml_node *sub_node; #define SIZE_LEFT() ((int)(len - (p-buf))) PJ_CHECK_STACK(); /* Print name. */ if (SIZE_LEFT() < node->name.slen + indent + 5) return -1; for (i=0; i<indent; ++i) *p++ = ' '; *p++ = '<'; pj_memcpy(p, node->name.ptr, node->name.slen); p += node->name.slen; /* Print attributes. */ attr = node->attr_head.next; while (attr != &node->attr_head) { if (SIZE_LEFT() < attr->name.slen + attr->value.slen + 4) return -1; *p++ = ' '; /* Attribute name. */ pj_memcpy(p, attr->name.ptr, attr->name.slen); p += attr->name.slen; /* Attribute value. */ if (attr->value.slen) { *p++ = '='; *p++ = '"'; pj_memcpy(p, attr->value.ptr, attr->value.slen); p += attr->value.slen; *p++ = '"'; } attr = attr->next; } /* Check for empty node. */ if (node->content.slen==0 && node->node_head.next==(pj_xml_node*)&node->node_head) { *p++ = ' '; *p++ = '/'; *p++ = '>'; return p-buf; } /* Enclosing '>' */ if (SIZE_LEFT() < 1) return -1; *p++ = '>'; /* Print sub nodes. */ sub_node = node->node_head.next; while (sub_node != (pj_xml_node*)&node->node_head) { int printed; if (SIZE_LEFT() < indent + 3) return -1; //*p++ = '\r'; *p++ = '\n'; printed = xml_print_node(sub_node, indent + 1, p, SIZE_LEFT()); if (printed < 0) return -1; p += printed; sub_node = sub_node->next; } /* Content. */ if (node->content.slen) { if (SIZE_LEFT() < node->content.slen) return -1; pj_memcpy(p, node->content.ptr, node->content.slen); p += node->content.slen; } /* Enclosing node. */ if (node->node_head.next != (pj_xml_node*)&node->node_head) { if (SIZE_LEFT() < node->name.slen + 5 + indent) return -1; //*p++ = '\r'; *p++ = '\n'; for (i=0; i<indent; ++i) *p++ = ' '; } else { if (SIZE_LEFT() < node->name.slen + 3) return -1; } *p++ = '<'; *p++ = '/'; pj_memcpy(p, node->name.ptr, node->name.slen); p += node->name.slen; *p++ = '>'; #undef SIZE_LEFT return p - buf; }
static int do_test(const char *title, const struct registrar_cfg *srv_cfg, const struct client *client_cfg, const pj_str_t *registrar_uri, unsigned contact_cnt, const pj_str_t contacts[], unsigned expires, pj_bool_t leave_session, pjsip_regc **p_regc) { pjsip_regc *regc; unsigned i; const pj_str_t aor = pj_str("<sip:[email protected]>"); pjsip_tx_data *tdata; pj_status_t status; PJ_LOG(3,(THIS_FILE, " %s", title)); /* Modify registrar settings */ pj_memcpy(®istrar.cfg, srv_cfg, sizeof(*srv_cfg)); pj_bzero(&client_result, sizeof(client_result)); client_result.destroy_on_cb = client_cfg->destroy_on_cb; status = pjsip_regc_create(endpt, &client_result, &client_cb, ®c); if (status != PJ_SUCCESS) return -100; status = pjsip_regc_init(regc, registrar_uri, &aor, &aor, contact_cnt, contacts, expires ? expires : 60); if (status != PJ_SUCCESS) { pjsip_regc_destroy(regc); return -110; } if (client_cfg->auth) { pjsip_cred_info cred; pj_bzero(&cred, sizeof(cred)); cred.realm = pj_str("*"); cred.scheme = pj_str("digest"); cred.username = pj_str("user"); cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; cred.data = pj_str("password"); status = pjsip_regc_set_credentials(regc, 1, &cred); if (status != PJ_SUCCESS) { pjsip_regc_destroy(regc); return -115; } } /* Register */ status = pjsip_regc_register(regc, PJ_TRUE, &tdata); if (status != PJ_SUCCESS) { pjsip_regc_destroy(regc); return -120; } status = pjsip_regc_send(regc, tdata); /* That's it, wait until the callback is sent */ for (i=0; i<600 && !client_result.done; ++i) { flush_events(100); } if (!client_result.done) { PJ_LOG(3,(THIS_FILE, " error: test has timed out")); pjsip_regc_destroy(regc); return -200; } /* Destroy the regc, we're done with the test, unless we're * instructed to leave the session open. */ if (!leave_session && !client_cfg->destroy_on_cb) pjsip_regc_destroy(regc); /* Compare results with expected results */ if (client_result.error != client_cfg->error) { PJ_LOG(3,(THIS_FILE, " error: expecting err=%d, got err=%d", client_cfg->error, client_result.error)); return -210; } if (client_result.code != client_cfg->code && client_cfg->code != 502 && client_cfg->code != 503 && client_result.code != 502 && client_result.code != 503) { PJ_LOG(3,(THIS_FILE, " error: expecting code=%d, got code=%d", client_cfg->code, client_result.code)); return -220; } if (client_result.expiration != client_cfg->expiration) { PJ_LOG(3,(THIS_FILE, " error: expecting expiration=%d, got expiration=%d", client_cfg->expiration, client_result.expiration)); return -240; } if (client_result.contact_cnt != client_cfg->contact_cnt) { PJ_LOG(3,(THIS_FILE, " error: expecting contact_cnt=%d, got contact_cnt=%d", client_cfg->contact_cnt, client_result.contact_cnt)); return -250; } if (client_result.have_reg != client_cfg->have_reg) { PJ_LOG(3,(THIS_FILE, " error: expecting have_reg=%d, got have_reg=%d", client_cfg->have_reg, client_result.have_reg)); return -260; } if (client_result.have_reg && client_result.interval != client_result.expiration) { PJ_LOG(3,(THIS_FILE, " error: interval (%d) is different than expiration (%d)", client_result.interval, client_result.expiration)); return -270; } if (client_result.interval > 0 && client_result.next_reg < 1) { PJ_LOG(3,(THIS_FILE, " error: next_reg=%d, expecting positive number because interval is %d", client_result.next_reg, client_result.interval)); return -280; } /* Looks like everything is okay. */ if (leave_session) { *p_regc = regc; } return 0; }
/* * Create stream info from SDP media line. */ PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp( pjmedia_vid_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote, unsigned stream_idx) { const pjmedia_sdp_attr *attr; const pjmedia_sdp_media *local_m; const pjmedia_sdp_media *rem_m; const pjmedia_sdp_conn *local_conn; const pjmedia_sdp_conn *rem_conn; int rem_af, local_af; pj_sockaddr local_addr; pj_status_t status; PJ_UNUSED_ARG(endpt); /* Validate arguments: */ PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); /* Keep SDP shortcuts */ local_m = local->media[stream_idx]; rem_m = remote->media[stream_idx]; local_conn = local_m->conn ? local_m->conn : local->conn; if (local_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; rem_conn = rem_m->conn ? rem_m->conn : remote->conn; if (rem_conn == NULL) return PJMEDIA_SDP_EMISSINGCONN; /* Media type must be video */ if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) != 0) return PJMEDIA_EINVALIMEDIATYPE; /* Reset: */ pj_bzero(si, sizeof(*si)); /* Media type: */ si->type = PJMEDIA_TYPE_VIDEO; /* Transport protocol */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, &local_m->desc.transport); if (status != PJ_SUCCESS) return PJMEDIA_SDPNEG_EINVANSTP; if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_AVP; } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; } else { si->proto = PJMEDIA_TP_PROTO_UNKNOWN; return PJ_SUCCESS; } /* Check address family in remote SDP */ rem_af = pj_AF_UNSPEC(); if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { rem_af = pj_AF_INET(); } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { rem_af = pj_AF_INET6(); } } if (rem_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_EAFNOTSUP; } /* Set remote address: */ status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, rem_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Check address family of local info */ local_af = pj_AF_UNSPEC(); if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { local_af = pj_AF_INET(); } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { local_af = pj_AF_INET6(); } } if (local_af==pj_AF_UNSPEC()) { /* Unsupported address family */ return PJ_SUCCESS; } /* Set remote address: */ status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, local_m->desc.port); if (status != PJ_SUCCESS) { /* Invalid IP address. */ return PJMEDIA_EINVALIDIP; } /* Local and remote address family must match */ if (local_af != rem_af) return PJ_EAFNOTSUP; /* Media direction: */ if (local_m->desc.port == 0 || pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { /* Inactive stream. */ si->dir = PJMEDIA_DIR_NONE; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { /* Send only stream. */ si->dir = PJMEDIA_DIR_ENCODING; } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { /* Recv only stream. */ si->dir = PJMEDIA_DIR_DECODING; } else { /* Send and receive stream. */ si->dir = PJMEDIA_DIR_ENCODING_DECODING; } /* No need to do anything else if stream is rejected */ if (local_m->desc.port == 0) { return PJ_SUCCESS; } /* If "rtcp" attribute is present in the SDP, set the RTCP address * from that attribute. Otherwise, calculate from RTP address. */ attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, "rtcp", NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); if (status == PJ_SUCCESS) { if (rtcp.addr.slen) { status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, (pj_uint16_t)rtcp.port); } else { pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, (pj_uint16_t)rtcp.port); pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), pj_sockaddr_get_addr(&si->rem_addr), pj_sockaddr_get_addr_len(&si->rem_addr)); } } } if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { int rtcp_port; pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); } /* Get codec info and param */ status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m); /* Leave SSRC to random. */ si->ssrc = pj_rand(); /* Set default jitter buffer parameter. */ si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; return status; }
/* * Create transferee (receiver of REFER request). * */ PJ_DEF(pj_status_t) pjsip_xfer_create_uas( pjsip_dialog *dlg, const pjsip_evsub_user *user_cb, pjsip_rx_data *rdata, pjsip_evsub **p_evsub ) { pjsip_evsub *sub; pjsip_xfer *xfer; const pj_str_t STR_EVENT = {"Event", 5 }; pjsip_event_hdr *event_hdr; pj_status_t status; /* Check arguments */ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); /* Must be request message */ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG); /* Check that request is REFER */ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_refer_method())==0, PJSIP_ENOTREFER); /* Lock dialog */ pjsip_dlg_inc_lock(dlg); /* The evsub framework expects an Event header in the request, * while a REFER request conveniently doesn't have one (pun intended!). * So create a dummy Event header. */ if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL)==NULL) { event_hdr = pjsip_event_hdr_create(rdata->tp_info.pool); event_hdr->event_type = STR_REFER; pjsip_msg_add_hdr(rdata->msg_info.msg, (pjsip_hdr*)event_hdr); } /* Create server subscription */ status = pjsip_evsub_create_uas( dlg, &xfer_user, rdata, PJSIP_EVSUB_NO_EVENT_ID, &sub); if (status != PJ_SUCCESS) goto on_return; /* Create server xfer subscription */ xfer = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_xfer); xfer->dlg = dlg; xfer->sub = sub; if (user_cb) pj_memcpy(&xfer->user_cb, user_cb, sizeof(pjsip_evsub_user)); /* Attach to evsub */ pjsip_evsub_set_mod_data(sub, mod_xfer.id, xfer); /* Done: */ *p_evsub = sub; on_return: pjsip_dlg_dec_lock(dlg); return status; }
/* API: set capability */ static pj_status_t and_stream_set_cap(pjmedia_vid_dev_stream *s, pjmedia_vid_dev_cap cap, const void *pval) { and_stream *strm = (and_stream*)s; JNIEnv *jni_env; pj_bool_t with_attach; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); switch (cap) { case PJMEDIA_VID_DEV_CAP_SWITCH: { pjmedia_vid_dev_switch_param *p = (pjmedia_vid_dev_switch_param*) pval; and_dev_info *adi; int res; /* Just return if current and target device are the same */ if (strm->param.cap_id == p->target_id) return PJ_SUCCESS; /* Verify target capture ID */ if (p->target_id < 0 || p->target_id >= strm->factory->dev_count) return PJ_EINVAL; /* Ok, let's do the switch */ adi = &strm->factory->dev_info[p->target_id]; PJ_LOG(4, (THIS_FILE, "Switching camera to %s..", adi->info.name)); /* Call PjCamera::Start() method */ with_attach = jni_get_env(&jni_env); res = (*jni_env)->CallIntMethod(jni_env, strm->jcam, jobjs.cam.m_switch, adi->dev_idx); if (res < 0) { PJ_LOG(3, (THIS_FILE, "Failed to switch camera (err=%d)", res)); status = PJMEDIA_EVID_SYSERR; } else { strm->param.cap_id = p->target_id; /* If successful, set the orientation as well */ and_stream_set_cap(s, PJMEDIA_VID_DEV_CAP_ORIENTATION, &strm->param.orient); } jni_detach_env(with_attach); break; } case PJMEDIA_VID_DEV_CAP_ORIENTATION: { pjmedia_orient orient = *(pjmedia_orient *)pval; pjmedia_orient eff_ori; and_dev_info *adi; pj_assert(orient >= PJMEDIA_ORIENT_UNKNOWN && orient <= PJMEDIA_ORIENT_ROTATE_270DEG); if (orient == PJMEDIA_ORIENT_UNKNOWN) return PJ_EINVAL; pj_memcpy(&strm->param.orient, pval, sizeof(strm->param.orient)); if (!strm->conv.conv) { status = pjmedia_vid_dev_conv_create_converter( &strm->conv, strm->pool, &strm->param.fmt, strm->cam_size, strm->param.fmt.det.vid.size, PJ_TRUE, MAINTAIN_ASPECT_RATIO); if (status != PJ_SUCCESS) return status; } eff_ori = strm->param.orient; adi = &strm->factory->dev_info[strm->param.cap_id]; /* Normalize the orientation for back-facing camera */ if (!adi->facing) { if (eff_ori == PJMEDIA_ORIENT_ROTATE_90DEG) eff_ori = PJMEDIA_ORIENT_ROTATE_270DEG; else if (eff_ori == PJMEDIA_ORIENT_ROTATE_270DEG) eff_ori = PJMEDIA_ORIENT_ROTATE_90DEG; } pjmedia_vid_dev_conv_set_rotation(&strm->conv, eff_ori); PJ_LOG(4, (THIS_FILE, "Video capture orientation set to %d", strm->param.orient)); break; } default: status = PJMEDIA_EVID_INVCAP; break; } return status; }
static pj_status_t pj_vpx_codec_encode_more(pjmedia_vid_codec *codec, unsigned out_size, pjmedia_frame *output, pj_bool_t *has_more) { vpx_private *vpx = (vpx_private*) codec->codec_data; const vpx_codec_cx_pkt_t *pkt; /* Default return */ *has_more = PJ_FALSE; output->size = 0; output->type = PJMEDIA_FRAME_TYPE_NONE; if (vpx->enc_frame_len == 0) { /* * For now we assume that we have only one cx data here * Which is probably fine as we do not ask encoder to bufferize */ //PJ_LOG(4, (THIS_FILE, "Encode one frame at %p", vpx->enc_iter)); pkt = vpx_codec_get_cx_data(&vpx->encoder, &vpx->enc_iter); if (pkt == NULL ) { if (!vpx->encoder.err) { PJ_LOG(3, (THIS_FILE, "Encoder packet dropped")); return PJ_SUCCESS; } else { PJ_LOG(1, (THIS_FILE, "Failed to get cx datas : %s", vpx_codec_err_to_string(vpx->encoder.err))); return PJMEDIA_CODEC_EFAILED; } } else if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { pj_memcpy(vpx->enc_buf, pkt->data.frame.buf, pkt->data.frame.sz); vpx->enc_frame_len = pkt->data.frame.sz; vpx->enc_processed = 0; vpx->enc_buf_is_keyframe = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? PJ_TRUE : PJ_FALSE; //PJ_LOG(4, (THIS_FILE, "Encoded with 0 byte : %d", ((pj_uint8_t*)(vpx->enc_buf))[0])); } else { PJ_LOG(6, (THIS_FILE, "Vpx packet kind %d not taken into account", pkt->kind)); return PJ_SUCCESS; } } // TODO we should support if iter not over too if(vpx->enc_frame_len > 0) { //PJ_LOG(4, (THIS_FILE, "We have an enc_frame : %d; max : %d", vpx->enc_frame_len, vpx->param.enc_mtu)); /* Reserve 1 octet for vp8 packetization info */ unsigned max_size = vpx->param.enc_mtu - 1; unsigned remaining_size = vpx->enc_frame_len - vpx->enc_processed; /* TODO : we could equally distributed packets sizes */ unsigned payload_len = PJ_MIN(remaining_size, max_size); pj_uint8_t* p = (pj_uint8_t*) output->buf; pj_uint8_t* s = (pj_uint8_t*) vpx->enc_buf; //PJ_LOG(4, (THIS_FILE, "Payload : %d", payload_len)); output->type = PJMEDIA_FRAME_TYPE_VIDEO; output->bit_info = 0; if (vpx->enc_buf_is_keyframe) { output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; } /* Set vp8 packetization info */ p[0] = 0; if(vpx->enc_processed == 0) p[0] |= 0x10; if(!vpx->enc_buf_is_keyframe) p[0] |= 0x20; pj_memcpy( (p + 1), (s + vpx->enc_processed), payload_len); output->size = payload_len + 1; vpx->enc_processed += payload_len; *has_more = !(vpx->enc_processed == vpx->enc_frame_len); } //PJ_LOG(4, (THIS_FILE, "Encoded size %d", output->size)); return PJ_SUCCESS; }
static int print_session(const pjmedia_sdp_session *ses, char *buf, pj_ssize_t len) { char *p = buf; char *end = buf+len; unsigned i; int printed; /* Check length for v= and o= lines. */ if (len < 5+ 2+ses->origin.user.slen+18+ ses->origin.net_type.slen+ses->origin.addr.slen + 2) { return -1; } /* SDP version (v= line) */ pj_memcpy(p, "v=0\r\n", 5); p += 5; /* Owner (o=) line. */ *p++ = 'o'; *p++ = '='; pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen); p += ses->origin.user.slen; *p++ = ' '; printed = pj_utoa(ses->origin.id, p); p += printed; *p++ = ' '; printed = pj_utoa(ses->origin.version, p); p += printed; *p++ = ' '; pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen); p += ses->origin.net_type.slen; *p++ = ' '; pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen); p += ses->origin.addr_type.slen; *p++ = ' '; pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen); p += ses->origin.addr.slen; *p++ = '\r'; *p++ = '\n'; /* Session name (s=) line. */ if ((end-p) < 8+ses->name.slen) { return -1; } *p++ = 's'; *p++ = '='; pj_memcpy(p, ses->name.ptr, ses->name.slen); p += ses->name.slen; *p++ = '\r'; *p++ = '\n'; /* Connection line (c=) if exist. */ if (ses->conn) { printed = print_connection_info(ses->conn, p, (int)(end-p)); if (printed < 1) { return -1; } p += printed; } /* print optional bandwidth info. */ for (i=0; i<ses->bandw_count; ++i) { printed = (int)print_bandw(ses->bandw[i], p, end-p); if (printed < 1) { return -1; } p += printed; } /* Time */ if ((end-p) < 24) { return -1; } *p++ = 't'; *p++ = '='; printed = pj_utoa(ses->time.start, p); p += printed; *p++ = ' '; printed = pj_utoa(ses->time.stop, p); p += printed; *p++ = '\r'; *p++ = '\n'; /* Print all attribute (a=) lines. */ for (i=0; i<ses->attr_count; ++i) { printed = (int)print_attr(ses->attr[i], p, end-p); if (printed < 0) { return -1; } p += printed; } /* Print media (m=) lines. */ for (i=0; i<ses->media_count; ++i) { printed = print_media_desc(ses->media[i], p, (int)(end-p)); if (printed < 0) { return -1; } p += printed; } return (int)(p-buf); }
static pj_status_t pj_vpx_codec_decode(pjmedia_vid_codec *codec, pj_size_t pkt_count, pjmedia_frame packets[], unsigned out_size, pjmedia_frame *output) { vpx_private *vpx = (vpx_private*) codec->codec_data; vpx_image_t *img; vpx_codec_iter_t iter; int i, res; PJ_ASSERT_RETURN(codec && pkt_count > 0 && packets && output, PJ_EINVAL); vpx->dec_frame_len = 0; /* TODO : packet parsing is absolutely incomplete here !!!! * We should manage extensions, partitions etc * */ for (i = 0; i < pkt_count; ++i) { pj_uint8_t *data; pj_uint8_t extended_bit, s_bit, partition_id; unsigned extension_len = 0; unsigned payload_size = packets[i].size; if(payload_size == 0) { continue; } data = packets[i].buf; extended_bit = (*data) & 0x80; s_bit = (*data) & 0x20; partition_id = (*data) & 0x1F; PJ_UNUSED_ARG(s_bit); PJ_UNUSED_ARG(partition_id); /* First octet is for */ /* |X|R|N|S|PartID | */ if(extended_bit) { pj_uint8_t i_bit, l_bit, t_bit, k_bit; (data)++; extension_len++; i_bit = (*data) & 0x80; l_bit = (*data) & 0x40; t_bit = (*data) & 0x20; k_bit = (*data) & 0x10; if(payload_size <= 1) { PJ_LOG(4, (THIS_FILE, "Error decoding VP8 extended attributes")); continue; } /* We have extension in octet 2 */ /* |I|L|T|K| RSV | */ if (i_bit) { data++; if(payload_size <= 2){ PJ_LOG(4, (THIS_FILE, "Error decoding VP8 extended picture ID attribute")); continue; } // I present check M flag for long picture ID if ((*data) & 0x80) { data++; } } if (l_bit) { data++; } if (t_bit || k_bit) { data++; } } data++; payload_size = packets[i].size - (data - (pj_uint8_t*)packets[i].buf); //PJ_LOG(4, (THIS_FILE, "Unpack RTP %d size %d, start %d", i, packets[i].size, s[0] & 0x10)); if((vpx->dec_frame_len + payload_size) < vpx->dec_buf_size) { pj_memcpy(vpx->dec_buf + vpx->dec_frame_len, data, payload_size); vpx->dec_frame_len += payload_size; } else { PJ_LOG(1, (THIS_FILE, "Buffer is too small")); } } if(vpx->dec_frame_len == 0){ PJ_LOG(1, (THIS_FILE, "No content for these packets")); return PJ_SUCCESS; } res = vpx_codec_decode(&vpx->decoder, vpx->dec_buf, vpx->dec_frame_len, NULL, VPX_DL_REALTIME); switch (res) { case VPX_CODEC_UNSUP_BITSTREAM: case VPX_CODEC_UNSUP_FEATURE: case VPX_CODEC_CORRUPT_FRAME: /* Fatal errors to the stream, request a keyframe to see if we can recover */ PJ_LOG(4, (THIS_FILE, "Fatal error decoding stream: (%d) %s", res, vpx_codec_err_to_string(res))); pjmedia_event event; pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING, NULL, codec); pjmedia_event_publish(NULL, codec, &event, 0); return PJMEDIA_CODEC_EBADBITSTREAM; case VPX_CODEC_OK: break; default: PJ_LOG(4, (THIS_FILE, "Failed to decode packets: (%d) %s", res, vpx_codec_err_to_string(res))); return PJMEDIA_ERROR; } iter = NULL; for (;;) { pj_status_t status; img = vpx_codec_get_frame(&vpx->decoder, &iter); if (img == NULL) break; status = pj_vpx_codec_decode_whole(codec, img, &packets[0].timestamp, out_size, output); if (status != PJ_SUCCESS) { PJ_LOG(4, (THIS_FILE, "Failed to decode frame")); /* XXX stop processing and request keyframe? */ } } return PJ_SUCCESS; }
/* * This is the main function for performing server resolution. */ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip_host_info *target, void *token, pjsip_resolver_callback *cb) { pjsip_server_addresses svr_addr; pj_status_t status = PJ_SUCCESS; int ip_addr_ver; struct query *query; pjsip_transport_type_e type = target->type; /* Is it IP address or hostname? And if it's an IP, which version? */ ip_addr_ver = get_ip_addr_ver(&target->addr.host); /* Set the transport type if not explicitly specified. * RFC 3263 section 4.1 specify rules to set up this. */ if (type == PJSIP_TRANSPORT_UNSPECIFIED) { if (ip_addr_ver || (target->addr.port != 0)) { #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } else { /* No type or explicit port is specified, and the address is * not IP address. * In this case, full NAPTR resolution must be performed. * But we don't support it (yet). */ #if PJ_HAS_TCP if (target->flag & PJSIP_TRANSPORT_SECURE) { type = PJSIP_TRANSPORT_TLS; } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) { type = PJSIP_TRANSPORT_TCP; } else #endif { type = PJSIP_TRANSPORT_UDP; } } } /* Add IPv6 flag for IPv6 address, which may already have been set. */ if (ip_addr_ver == 6) type = (pjsip_transport_type_e)((int)type | PJSIP_TRANSPORT_IPV6); /* If target is an IP address, or if resolver is not configured, * we can just finish the resolution now using pj_gethostbyname() */ if (ip_addr_ver || resolver->res == NULL) { char addr_str[PJ_INET6_ADDRSTRLEN+10]; pj_uint16_t srv_port; if (ip_addr_ver != 0) { /* Target is an IP address, no need to resolve */ if (ip_addr_ver == 4) { pj_sockaddr_init(pj_AF_INET(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_aton(&target->addr.host, &svr_addr.entry[0].addr.ipv4.sin_addr); } else { pj_sockaddr_init(pj_AF_INET6(), &svr_addr.entry[0].addr, NULL, 0); pj_inet_pton(pj_AF_INET6(), &target->addr.host, &svr_addr.entry[0].addr.ipv6.sin6_addr); } } else { pj_addrinfo ai; unsigned count; int af; PJ_LOG(5,(THIS_FILE, "DNS resolver not available, target '%.*s:%d' type=%s " "will be resolved with getaddrinfo()", target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type))); if (type & PJSIP_TRANSPORT_IPV6) { af = pj_AF_INET6(); } else { af = pj_AF_INET(); } /* Resolve */ count = 1; status = pj_getaddrinfo(af, &target->addr.host, &count, &ai); if (status != PJ_SUCCESS) { /* "Normalize" error to PJ_ERESOLVE. This is a special error * because it will be translated to SIP status 502 by * sip_transaction.c */ status = PJ_ERESOLVE; goto on_error; } svr_addr.entry[0].addr.addr.sa_family = (pj_uint16_t)af; pj_memcpy(&svr_addr.entry[0].addr, &ai.ai_addr, sizeof(pj_sockaddr)); } /* Set the port number */ if (target->addr.port == 0) { srv_port = (pj_uint16_t) pjsip_transport_get_default_port_for_type(type); } else { srv_port = (pj_uint16_t)target->addr.port; } pj_sockaddr_set_port(&svr_addr.entry[0].addr, srv_port); /* Call the callback. */ PJ_LOG(5,(THIS_FILE, "Target '%.*s:%d' type=%s resolved to " "'%s' type=%s (%s)", (int)target->addr.host.slen, target->addr.host.ptr, target->addr.port, pjsip_transport_get_type_name(target->type), pj_sockaddr_print(&svr_addr.entry[0].addr, addr_str, sizeof(addr_str), 3), pjsip_transport_get_type_name(type), pjsip_transport_get_type_desc(type))); svr_addr.count = 1; svr_addr.entry[0].priority = 0; svr_addr.entry[0].weight = 0; svr_addr.entry[0].type = type; svr_addr.entry[0].addr_len = pj_sockaddr_get_len(&svr_addr.entry[0].addr); (*cb)(status, token, &svr_addr); /* Done. */ return; } /* Target is not an IP address so we need to resolve it. */ #if PJSIP_HAS_RESOLVER /* Build the query state */ query = PJ_POOL_ZALLOC_T(pool, struct query); query->objname = THIS_FILE; query->token = token; query->cb = cb; query->req.target = *target; pj_strdup(pool, &query->req.target.addr.host, &target->addr.host); /* If port is not specified, start with SRV resolution * (should be with NAPTR, but we'll do that later) */ PJ_TODO(SUPPORT_DNS_NAPTR); /* Build dummy NAPTR entry */ query->naptr_cnt = 1; pj_bzero(&query->naptr[0], sizeof(query->naptr[0])); query->naptr[0].order = 0; query->naptr[0].pref = 0; query->naptr[0].type = type; pj_strdup(pool, &query->naptr[0].name, &target->addr.host); /* Start DNS SRV or A resolution, depending on whether port is specified */ if (target->addr.port == 0) { query->query_type = PJ_DNS_TYPE_SRV; query->req.def_port = 5060; if (type == PJSIP_TRANSPORT_TLS) { query->naptr[0].res_type = pj_str("_sips._tcp."); query->req.def_port = 5061; } else if (type == PJSIP_TRANSPORT_TCP) query->naptr[0].res_type = pj_str("_sip._tcp."); else if (type == PJSIP_TRANSPORT_UDP) query->naptr[0].res_type = pj_str("_sip._udp."); else { pj_assert(!"Unknown transport type"); query->naptr[0].res_type = pj_str("_sip._udp."); } } else { /* Otherwise if port is specified, start with A (or AAAA) host * resolution */ query->query_type = PJ_DNS_TYPE_A; query->naptr[0].res_type.slen = 0; query->req.def_port = target->addr.port; } /* Start the asynchronous query */ PJ_LOG(5, (query->objname, "Starting async DNS %s query: target=%.*s%.*s, transport=%s, " "port=%d", pj_dns_get_type_name(query->query_type), (int)query->naptr[0].res_type.slen, query->naptr[0].res_type.ptr, (int)query->naptr[0].name.slen, query->naptr[0].name.ptr, pjsip_transport_get_type_name(target->type), target->addr.port)); if (query->query_type == PJ_DNS_TYPE_SRV) { status = pj_dns_srv_resolve(&query->naptr[0].name, &query->naptr[0].res_type, query->req.def_port, pool, resolver->res, PJ_TRUE, query, &srv_resolver_cb, NULL); } else if (query->query_type == PJ_DNS_TYPE_A) { status = pj_dns_resolver_start_query(resolver->res, &query->naptr[0].name, PJ_DNS_TYPE_A, 0, &dns_a_callback, query, &query->object); } else { pj_assert(!"Unexpected"); status = PJ_EBUG; } if (status != PJ_SUCCESS) goto on_error; return; #else /* PJSIP_HAS_RESOLVER */ PJ_UNUSED_ARG(pool); PJ_UNUSED_ARG(query); PJ_UNUSED_ARG(srv_name); #endif /* PJSIP_HAS_RESOLVER */ on_error: if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; PJ_LOG(4,(THIS_FILE, "Failed to resolve '%.*s'. Err=%d (%s)", (int)target->addr.host.slen, target->addr.host.ptr, status, pj_strerror(status,errmsg,sizeof(errmsg)).ptr)); (*cb)(status, token, NULL); return; } }
pj_status_t pj_stun_detect_nat_type(const pj_sockaddr_in *server, pj_stun_config *stun_cfg, void *user_data, pj_stun_nat_detect_cb *cb) { pj_pool_t *pool; nat_detect_session *sess; pj_stun_session_cb sess_cb; pj_ioqueue_callback ioqueue_cb; int addr_len; pj_status_t status; PJ_ASSERT_RETURN(server && stun_cfg, PJ_EINVAL); PJ_ASSERT_RETURN(stun_cfg->pf && stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL); /* * Init NAT detection session. */ pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK, PJNATH_POOL_INC_NATCK, NULL); if (!pool) return PJ_ENOMEM; sess = PJ_POOL_ZALLOC_T(pool, nat_detect_session); sess->pool = pool; sess->user_data = user_data; sess->cb = cb; status = pj_grp_lock_create(pool, NULL, &sess->grp_lock); if (status != PJ_SUCCESS) { /* Group lock not created yet, just destroy pool and return */ pj_pool_release(pool); return status; } pj_grp_lock_add_ref(sess->grp_lock); pj_grp_lock_add_handler(sess->grp_lock, pool, sess, &sess_on_destroy); pj_memcpy(&sess->server, server, sizeof(pj_sockaddr_in)); /* * Init timer to self-destroy. */ sess->timer_heap = stun_cfg->timer_heap; sess->timer.cb = &on_sess_timer; sess->timer.user_data = sess; /* * Initialize socket. */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sess->sock); if (status != PJ_SUCCESS) goto on_error; /* * Bind to any. */ pj_bzero(&sess->local_addr, sizeof(pj_sockaddr_in)); sess->local_addr.sin_family = pj_AF_INET(); status = pj_sock_bind(sess->sock, &sess->local_addr, sizeof(pj_sockaddr_in)); if (status != PJ_SUCCESS) goto on_error; /* * Get local/bound address. */ addr_len = sizeof(sess->local_addr); status = pj_sock_getsockname(sess->sock, &sess->local_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; /* * Find out which interface is used to send to the server. */ status = get_local_interface(server, &sess->local_addr.sin_addr); if (status != PJ_SUCCESS) goto on_error; PJ_LOG(5,(sess->pool->obj_name, "Local address is %s:%d", pj_inet_ntoa(sess->local_addr.sin_addr), pj_ntohs(sess->local_addr.sin_port))); PJ_LOG(5,(sess->pool->obj_name, "Server set to %s:%d", pj_inet_ntoa(server->sin_addr), pj_ntohs(server->sin_port))); /* * Register socket to ioqueue to receive asynchronous input * notification. */ pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); ioqueue_cb.on_read_complete = &on_read_complete; status = pj_ioqueue_register_sock2(sess->pool, stun_cfg->ioqueue, sess->sock, sess->grp_lock, sess, &ioqueue_cb, &sess->key); if (status != PJ_SUCCESS) goto on_error; /* * Create STUN session. */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_request_complete = &on_request_complete; sess_cb.on_send_msg = &on_send_msg; status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb, PJ_FALSE, sess->grp_lock, &sess->stun_sess); if (status != PJ_SUCCESS) goto on_error; pj_stun_session_set_user_data(sess->stun_sess, sess); /* * Kick-off ioqueue reading. */ pj_ioqueue_op_key_init(&sess->read_op, sizeof(sess->read_op)); pj_ioqueue_op_key_init(&sess->write_op, sizeof(sess->write_op)); on_read_complete(sess->key, &sess->read_op, 0); /* * Start TEST_1 */ sess->timer.id = TIMER_TEST; on_sess_timer(stun_cfg->timer_heap, &sess->timer); return PJ_SUCCESS; on_error: sess_destroy(sess); return status; }
static void process(struct Times *t) { pj_memcpy(&t->u_kernel_time, &t->kernel_time, sizeof(FILETIME)); pj_memcpy(&t->u_user_time, &t->user_time, sizeof(FILETIME)); t->u_total.QuadPart = t->u_kernel_time.QuadPart + t->u_user_time.QuadPart; }
/* * pj_ioqueue_sendto() * * Start asynchronous write() to the descriptor. */ PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key, pj_ioqueue_op_key_t *op_key, const void *data, pj_ssize_t *length, pj_uint32_t flags, const pj_sockaddr_t *addr, int addrlen) { struct write_operation *write_op; unsigned retry; pj_status_t status; pj_ssize_t sent; PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL); PJ_CHECK_STACK(); /* Check if key is closing. */ if (IS_CLOSING(key)) return PJ_ECANCELLED; /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write */ flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC); /* Fast track: * Try to send data immediately, only if there's no pending write! * Note: * We are speculating that the list is empty here without properly * acquiring ioqueue's mutex first. This is intentional, to maximize * performance via parallelism. * * This should be safe, because: * - by convention, we require caller to make sure that the * key is not unregistered while other threads are invoking * an operation on the same key. * - pj_list_empty() is safe to be invoked by multiple threads, * even when other threads are modifying the list. */ if (pj_list_empty(&key->write_list)) { /* * See if data can be sent immediately. */ sent = *length; status = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen); if (status == PJ_SUCCESS) { /* Success! */ *length = sent; return PJ_SUCCESS; } else { /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report * the error to caller. */ if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) { return status; } status = status; } } /* * Check that address storage can hold the address parameter. */ PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), PJ_EBUG); /* * Schedule asynchronous send. */ write_op = (struct write_operation*)op_key; /* Spin if write_op has pending operation */ for (retry=0; write_op->op != 0 && retry<PENDING_RETRY; ++retry) pj_thread_sleep(0); /* Last chance */ if (write_op->op) { /* Unable to send packet because there is already pending write on the * write_op. We could not put the operation into the write_op * because write_op already contains a pending operation! And * we could not send the packet directly with sendto() either, * because that will break the order of the packet. So we can * only return error here. * * This could happen for example in multithreads program, * where polling is done by one thread, while other threads are doing * the sending only. If the polling thread runs on lower priority * than the sending thread, then it's possible that the pending * write flag is not cleared in-time because clearing is only done * during polling. * * Aplication should specify multiple write operation keys on * situation like this. */ //pj_assert(!"ioqueue: there is pending operation on this key!"); return PJ_EBUSY; } write_op->op = PJ_IOQUEUE_OP_SEND_TO; write_op->buf = (char*)data; write_op->size = *length; write_op->written = 0; write_op->flags = flags; pj_memcpy(&write_op->rmt_addr, addr, addrlen); write_op->rmt_addrlen = addrlen; pj_mutex_lock(key->mutex); pj_list_insert_before(&key->write_list, write_op); ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT); pj_mutex_unlock(key->mutex); return PJ_EPENDING; }
/* * Create stream based on the codec, dir, remote address, etc. */ static pj_status_t create_stream( pj_pool_t *pool, pjmedia_endpt *med_endpt, const pjmedia_vid_codec_info *codec_info, pjmedia_vid_codec_param *codec_param, pjmedia_dir dir, pj_int8_t rx_pt, pj_int8_t tx_pt, pj_uint16_t local_port, const pj_sockaddr_in *rem_addr, #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) pj_bool_t use_srtp, const pj_str_t *crypto_suite, const pj_str_t *srtp_tx_key, const pj_str_t *srtp_rx_key, #endif pjmedia_vid_stream **p_stream ) { pjmedia_vid_stream_info info; pjmedia_transport *transport = NULL; pj_status_t status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) pjmedia_transport *srtp_tp = NULL; #endif /* Reset stream info. */ pj_bzero(&info, sizeof(info)); /* Initialize stream info formats */ info.type = PJMEDIA_TYPE_VIDEO; info.dir = dir; info.codec_info = *codec_info; info.tx_pt = (tx_pt == -1)? codec_info->pt : tx_pt; info.rx_pt = (rx_pt == -1)? codec_info->pt : rx_pt; info.ssrc = pj_rand(); if (codec_param) info.codec_param = codec_param; /* Copy remote address */ pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in)); /* If remote address is not set, set to an arbitrary address * (otherwise stream will assert). */ if (info.rem_addr.addr.sa_family == 0) { const pj_str_t addr = pj_str("127.0.0.1"); pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0); } /* Create media transport */ status = pjmedia_transport_udp_create(med_endpt, NULL, local_port, 0, &transport); if (status != PJ_SUCCESS) return status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* Check if SRTP enabled */ if (use_srtp) { pjmedia_srtp_crypto tx_plc, rx_plc; status = pjmedia_transport_srtp_create(med_endpt, transport, NULL, &srtp_tp); if (status != PJ_SUCCESS) return status; pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto)); pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto)); tx_plc.key = *srtp_tx_key; tx_plc.name = *crypto_suite; rx_plc.key = *srtp_rx_key; rx_plc.name = *crypto_suite; status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc); if (status != PJ_SUCCESS) return status; transport = srtp_tp; } #endif /* Now that the stream info is initialized, we can create the * stream. */ status = pjmedia_vid_stream_create( med_endpt, pool, &info, transport, NULL, p_stream); if (status != PJ_SUCCESS) { app_perror(THIS_FILE, "Error creating stream", status); pjmedia_transport_close(transport); return status; } return PJ_SUCCESS; }
static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, const pjsip_sip_uri *url, char *buf, pj_size_t size) { int printed; char *startbuf = buf; char *endbuf = buf+size; const pj_str_t *scheme; const pjsip_parser_const_t *pc = pjsip_parser_const(); *buf = '\0'; /* Print scheme ("sip:" or "sips:") */ scheme = pjsip_uri_get_scheme(url); copy_advance_check(buf, *scheme); *buf++ = ':'; /* Print "user:password@", if any. */ if (url->user.slen) { const pj_cis_t *spec = pjsip_cfg()->endpt.allow_tx_hash_in_uri ? &pc->pjsip_USER_SPEC_LENIENT : &pc->pjsip_USER_SPEC; copy_advance_escape(buf, url->user, *spec); if (url->passwd.slen) { *buf++ = ':'; copy_advance_escape(buf, url->passwd, pc->pjsip_PASSWD_SPEC); } *buf++ = '@'; } /* Print host. */ pj_assert(url->host.slen != 0); /* Detect IPv6 IP address */ if (pj_memchr(url->host.ptr, ':', url->host.slen)) { copy_advance_pair_quote_cond(buf, "", 0, url->host, '[', ']'); } else { copy_advance_check(buf, url->host); } /* Only print port if it is explicitly specified. * Port is not allowed in To and From header, see Table 1 in * RFC 3261 Section 19.1.1 */ /* Note: ticket #1141 adds run-time setting to allow port number to * appear in From/To header. Default is still false. */ if (url->port && (context != PJSIP_URI_IN_FROMTO_HDR || pjsip_cfg()->endpt.allow_port_in_fromto_hdr)) { if (endbuf - buf < 10) return -1; *buf++ = ':'; printed = pj_utoa(url->port, buf); buf += printed; } /* User param is allowed in all contexes */ copy_advance_pair_check(buf, ";user="******";method=", 8, url->method_param, pc->pjsip_PARAM_CHAR_SPEC); } /* Transport is not allowed in From/To header. */ if (context != PJSIP_URI_IN_FROMTO_HDR) { copy_advance_pair_escape(buf, ";transport=", 11, url->transport_param, pc->pjsip_PARAM_CHAR_SPEC); } /* TTL param is not allowed in From, To, Route, and Record-Route header. */ if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR && context != PJSIP_URI_IN_ROUTING_HDR) { if (endbuf - buf < 15) return -1; pj_memcpy(buf, ";ttl=", 5); printed = pj_utoa(url->ttl_param, buf+5); buf += printed + 5; } /* maddr param is not allowed in From and To header. */ if (context != PJSIP_URI_IN_FROMTO_HDR && url->maddr_param.slen) { /* Detect IPv6 IP address */ if (pj_memchr(url->maddr_param.ptr, ':', url->maddr_param.slen)) { copy_advance_pair_quote_cond(buf, ";maddr=", 7, url->maddr_param, '[', ']'); } else { copy_advance_pair_escape(buf, ";maddr=", 7, url->maddr_param, pc->pjsip_PARAM_CHAR_SPEC); } } /* lr param is not allowed in From, To, and Contact header. */ if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR && context != PJSIP_URI_IN_CONTACT_HDR) { pj_str_t lr = { ";lr", 3 }; if (endbuf - buf < 3) return -1; copy_advance_check(buf, lr); } /* Other param. */ printed = (int)pjsip_param_print_on(&url->other_param, buf, endbuf-buf, &pc->pjsip_PARAM_CHAR_SPEC, &pc->pjsip_PARAM_CHAR_SPEC, ';'); if (printed < 0) return -1; buf += printed; /* Header param. * Header param is only allowed in these contexts: * - PJSIP_URI_IN_CONTACT_HDR * - PJSIP_URI_IN_OTHER */ if (context == PJSIP_URI_IN_CONTACT_HDR || context == PJSIP_URI_IN_OTHER) { printed = (int)pjsip_param_print_on(&url->header_param, buf, endbuf-buf, &pc->pjsip_HDR_CHAR_SPEC, &pc->pjsip_HDR_CHAR_SPEC, '?'); if (printed < 0) return -1; buf += printed; } *buf = '\0'; return buf-startbuf; }
/* Scan V4L2 devices */ static pj_status_t v4l2_scan_devs(vid4lin_factory *f) { vid4lin_dev_info vdi[V4L2_MAX_DEVS]; char dev_name[32]; unsigned i, old_count; pj_status_t status; if (f->dev_pool) { pj_pool_release(f->dev_pool); f->dev_pool = NULL; } pj_bzero(vdi, sizeof(vdi)); old_count = f->dev_count; f->dev_count = 0; f->dev_pool = pj_pool_create(f->pf, DRIVER_NAME, 500, 500, NULL); for (i=0; i<V4L2_MAX_DEVS && f->dev_count < V4L2_MAX_DEVS; ++i) { int fd; vid4lin_dev_info *pdi; pj_uint32_t fmt_cap[8]; int j, fmt_cnt=0; pdi = &vdi[f->dev_count]; snprintf(dev_name, sizeof(dev_name), "/dev/video%d", i); if (!pj_file_exists(dev_name)) continue; fd = v4l2_open(dev_name, O_RDWR, 0); if (fd == -1) continue; status = xioctl(fd, VIDIOC_QUERYCAP, &pdi->v4l2_cap); if (status != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, status, "Error querying %s", dev_name)); v4l2_close(fd); continue; } if ((pdi->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) { v4l2_close(fd); continue; } PJ_LOG(5,(THIS_FILE, "Found capture device %s", pdi->v4l2_cap.card)); PJ_LOG(5,(THIS_FILE, " Enumerating formats:")); for (j=0; fmt_cnt<PJ_ARRAY_SIZE(fmt_cap); ++j) { struct v4l2_fmtdesc fdesc; unsigned k; pj_bzero(&fdesc, sizeof(fdesc)); fdesc.index = j; fdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; status = xioctl(fd, VIDIOC_ENUM_FMT, &fdesc); if (status != PJ_SUCCESS) break; for (k=0; k<PJ_ARRAY_SIZE(v4l2_fmt_maps); ++k) { if (v4l2_fmt_maps[k].v4l2_fmt_id == fdesc.pixelformat) { fmt_cap[fmt_cnt++] = v4l2_fmt_maps[k].pjmedia_fmt_id; PJ_LOG(5,(THIS_FILE, " Supported: %s", fdesc.description)); break; } } if (k==PJ_ARRAY_SIZE(v4l2_fmt_maps)) { PJ_LOG(5,(THIS_FILE, " Unsupported: %s", fdesc.description)); } } v4l2_close(fd); if (fmt_cnt==0) { PJ_LOG(5,(THIS_FILE, " Found no common format")); continue; } strncpy(pdi->dev_name, dev_name, sizeof(pdi->dev_name)); pdi->dev_name[sizeof(pdi->dev_name)-1] = '\0'; strncpy(pdi->info.name, (char*)pdi->v4l2_cap.card, sizeof(pdi->info.name)); pdi->info.name[sizeof(pdi->info.name)-1] = '\0'; strncpy(pdi->info.driver, DRIVER_NAME, sizeof(pdi->info.driver)); pdi->info.driver[sizeof(pdi->info.driver)-1] = '\0'; pdi->info.dir = PJMEDIA_DIR_CAPTURE; pdi->info.has_callback = PJ_FALSE; pdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; pdi->info.fmt_cnt = fmt_cnt; for (j=0; j<fmt_cnt; ++j) { pjmedia_format_init_video(&pdi->info.fmt[j], fmt_cap[j], DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FPS, 1); } if (j < fmt_cnt) continue; f->dev_count++; } if (f->dev_count == 0) return PJ_SUCCESS; if (f->dev_count > old_count || f->dev_info == NULL) { f->dev_info = (vid4lin_dev_info*) pj_pool_calloc(f->dev_pool, f->dev_count, sizeof(vid4lin_dev_info)); } pj_memcpy(f->dev_info, vdi, f->dev_count * sizeof(vid4lin_dev_info)); return PJ_SUCCESS; }
/* * Create. */ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, int af, pj_turn_tp_type conn_type, const pj_turn_sock_cb *cb, const pj_turn_sock_cfg *setting, void *user_data, pj_turn_sock **p_turn_sock) { pj_turn_sock *turn_sock; pj_turn_session_cb sess_cb; pj_turn_sock_cfg default_setting; pj_pool_t *pool; const char *name_tmpl; pj_status_t status; PJ_ASSERT_RETURN(cfg && p_turn_sock, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET() || af==pj_AF_INET6(), PJ_EINVAL); PJ_ASSERT_RETURN(conn_type!=PJ_TURN_TP_TCP || PJ_HAS_TCP, PJ_EINVAL); if (!setting) { pj_turn_sock_cfg_default(&default_setting); setting = &default_setting; } switch (conn_type) { case PJ_TURN_TP_UDP: name_tmpl = "udprel%p"; break; case PJ_TURN_TP_TCP: name_tmpl = "tcprel%p"; break; default: PJ_ASSERT_RETURN(!"Invalid TURN conn_type", PJ_EINVAL); name_tmpl = "tcprel%p"; break; } /* Create and init basic data structure */ pool = pj_pool_create(cfg->pf, name_tmpl, PJNATH_POOL_LEN_TURN_SOCK, PJNATH_POOL_INC_TURN_SOCK, NULL); turn_sock = PJ_POOL_ZALLOC_T(pool, pj_turn_sock); turn_sock->pool = pool; turn_sock->obj_name = pool->obj_name; turn_sock->user_data = user_data; turn_sock->af = af; turn_sock->conn_type = conn_type; /* Copy STUN config (this contains ioqueue, timer heap, etc.) */ pj_memcpy(&turn_sock->cfg, cfg, sizeof(*cfg)); /* Copy setting (QoS parameters etc */ pj_memcpy(&turn_sock->setting, setting, sizeof(*setting)); /* Set callback */ if (cb) { pj_memcpy(&turn_sock->cb, cb, sizeof(*cb)); } /* Create lock */ status = pj_lock_create_recursive_mutex(pool, turn_sock->obj_name, &turn_sock->lock); if (status != PJ_SUCCESS) { destroy(turn_sock); return status; } /* Init timer */ pj_timer_entry_init(&turn_sock->timer, TIMER_NONE, turn_sock, &timer_cb); /* Init TURN session */ pj_bzero(&sess_cb, sizeof(sess_cb)); sess_cb.on_send_pkt = &turn_on_send_pkt; sess_cb.on_channel_bound = &turn_on_channel_bound; sess_cb.on_rx_data = &turn_on_rx_data; sess_cb.on_state = &turn_on_state; status = pj_turn_session_create(cfg, pool->obj_name, af, conn_type, &sess_cb, 0, turn_sock, &turn_sock->sess); if (status != PJ_SUCCESS) { destroy(turn_sock); return status; } /* Note: socket and ioqueue will be created later once the TURN server * has been resolved. */ *p_turn_sock = turn_sock; return PJ_SUCCESS; }
/* API: create stream */ static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f, pjmedia_vid_dev_param *param, const pjmedia_vid_dev_cb *cb, void *user_data, pjmedia_vid_dev_stream **p_vid_strm) { vid4lin_factory *cf = (vid4lin_factory*)f; pj_pool_t *pool; vid4lin_stream *stream; vid4lin_dev_info *vdi; const vid4lin_fmt_map *fmt_map; const pjmedia_video_format_info *fmt_info; pjmedia_video_format_detail *vfd; pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL); PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO && param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO && param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); PJ_ASSERT_RETURN(param->cap_id >= 0 && param->cap_id < cf->dev_count, PJMEDIA_EVID_INVDEV); fmt_info = pjmedia_get_video_format_info(NULL, param->fmt.id); if (!fmt_info || (fmt_map=get_v4l2_format_info(param->fmt.id))==NULL) return PJMEDIA_EVID_BADFORMAT; vdi = &cf->dev_info[param->cap_id]; vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); /* Create and Initialize stream descriptor */ pool = pj_pool_create(cf->pf, vdi->info.name, 512, 512, NULL); PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); stream = PJ_POOL_ZALLOC_T(pool, vid4lin_stream); pj_memcpy(&stream->param, param, sizeof(*param)); stream->pool = pool; pj_memcpy(&stream->vid_cb, cb, sizeof(*cb)); strncpy(stream->name, vdi->info.name, sizeof(stream->name)); stream->name[sizeof(stream->name)-1] = '\0'; stream->user_data = user_data; stream->fd = INVALID_FD; stream->fd = v4l2_open(vdi->dev_name, O_RDWR, 0); if (stream->fd < 0) goto on_error; status = vid4lin_stream_init_fmt(stream, param, fmt_map->v4l2_fmt_id); if (status != PJ_SUCCESS) goto on_error; if (vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING) status = vid4lin_stream_init_streaming(stream); if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING) status = vid4lin_stream_init_streaming_user(stream); if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_READWRITE) status = vid4lin_stream_init_read_write(stream); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Error: unable to initiate I/O on %s", stream->name)); goto on_error; } /* Done */ stream->base.op = &stream_op; *p_vid_strm = &stream->base; return PJ_SUCCESS; on_error: if (status == PJ_SUCCESS) status = PJ_RETURN_OS_ERROR(errno); vid4lin_stream_destroy(&stream->base); return status; }
/* * Convert IPv4/IPv6 address to text. */ PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src, char *dst, int size) { PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL); *dst = '\0'; PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP); #if defined(PJ_SOCK_HAS_INET_NTOP) && PJ_SOCK_HAS_INET_NTOP != 0 /* * Implementation using inet_ntop() */ if (inet_ntop(af, src, dst, size) == NULL) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; #elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE) /* * Implementation on Windows, using WSAAddressToString(). * Should also work on Unicode systems. */ { PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN) pj_sockaddr sock_addr; DWORD addr_len, addr_str_len; int rc; pj_bzero(&sock_addr, sizeof(sock_addr)); sock_addr.addr.sa_family = (pj_uint16_t)af; if (af == PJ_AF_INET) { if (size < PJ_INET_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&sock_addr.ipv4.sin_addr, src, 4); addr_len = sizeof(pj_sockaddr_in); addr_str_len = PJ_INET_ADDRSTRLEN; } else if (af == PJ_AF_INET6) { if (size < PJ_INET6_ADDRSTRLEN) return PJ_ETOOSMALL; pj_memcpy(&sock_addr.ipv6.sin6_addr, src, 16); addr_len = sizeof(pj_sockaddr_in6); addr_str_len = PJ_INET6_ADDRSTRLEN; } else { pj_assert(!"Unsupported address family"); return PJ_EAFNOTSUP; } #if PJ_NATIVE_STRING_IS_UNICODE rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, NULL, wtempaddr, &addr_str_len); if (rc == 0) { pj_unicode_to_ansi(wtempaddr, wcslen(wtempaddr), dst, size); } #else rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len, NULL, dst, &addr_str_len); #endif if (rc != 0) { pj_status_t status = pj_get_netos_error(); if (status == PJ_SUCCESS) status = PJ_EUNKNOWN; return status; } return PJ_SUCCESS; } #elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0 /* IPv6 support is disabled, just return error without raising assertion */ return PJ_EIPV6NOTSUP; #else pj_assert(!"Not supported"); return PJ_EIPV6NOTSUP; #endif }
/* * Open codec. */ static pj_status_t pj_vpx_codec_open(pjmedia_vid_codec *codec, pjmedia_vid_codec_param *attr) { vpx_private *vpx; pj_status_t status; PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL); vpx = (vpx_private*) codec->codec_data; pj_memcpy(&vpx->param, attr, sizeof(*attr)); /* Normalize encoding MTU in codec param */ if (attr->enc_mtu > PJMEDIA_MAX_VID_PAYLOAD_SIZE) { attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE; } /* Init format info and apply-param of decoder */ vpx->dec_vfi = pjmedia_get_video_format_info(NULL, vpx->param.dec_fmt.id); if (!vpx->dec_vfi) { status = PJ_EINVAL; goto on_error; } pj_bzero(&vpx->dec_vafp, sizeof(vpx->dec_vafp)); vpx->dec_vafp.size = vpx->param.dec_fmt.det.vid.size; vpx->dec_vafp.buffer = NULL; status = (*vpx->dec_vfi->apply_fmt)(vpx->dec_vfi, &vpx->dec_vafp); if (status != PJ_SUCCESS) { goto on_error; } /* Init format info and apply-param of encoder */ vpx->enc_vfi = pjmedia_get_video_format_info(NULL, vpx->param.dec_fmt.id); if (!vpx->enc_vfi) { status = PJ_EINVAL; goto on_error; } pj_bzero(&vpx->enc_vafp, sizeof(vpx->enc_vafp)); vpx->enc_vafp.size = vpx->param.enc_fmt.det.vid.size; vpx->enc_vafp.buffer = NULL; status = (*vpx->enc_vfi->apply_fmt)(vpx->enc_vfi, &vpx->enc_vafp); if (status != PJ_SUCCESS) { goto on_error; } /* Open the encoder */ TRACE_((THIS_FILE, "Open vpx version : %s build : %s", vpx_codec_version_str(), vpx_codec_build_config())); if (vpx->param.dir & PJMEDIA_DIR_ENCODING) { status = pj_vpx_encoder_open(vpx); if (status != PJ_SUCCESS) { goto on_error; } } if (vpx->param.dir & PJMEDIA_DIR_DECODING) { status = pj_vpx_decoder_open(vpx); if (status != PJ_SUCCESS) { goto on_error; } } /* Update codec attributes, e.g: encoding format may be changed by * SDP fmtp negotiation. */ pj_memcpy(attr, &vpx->param, sizeof(*attr)); return PJ_SUCCESS; on_error: pj_vpx_codec_close(codec); return status; }
/* * Copy socket address. */ PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src) { pj_memcpy(dst, src, pj_sockaddr_get_len(src)); }
/* API: refresh the list of devices */ static pj_status_t and_factory_refresh(pjmedia_vid_dev_factory *ff) { and_factory *f = (and_factory*)ff; pj_status_t status = PJ_SUCCESS; JNIEnv *jni_env; pj_bool_t with_attach, found_front = PJ_FALSE; int i, dev_count = 0; /* Clean up device info and pool */ f->dev_count = 0; pj_pool_reset(f->dev_pool); with_attach = jni_get_env(&jni_env); /* dev_count = PjCameraInfo::GetCameraCount() */ dev_count = (*jni_env)->CallStaticIntMethod(jni_env, jobjs.cam_info.cls, jobjs.cam_info.m_get_cnt); if (dev_count < 0) { PJ_LOG(3, (THIS_FILE, "Failed to get camera count")); status = PJMEDIA_EVID_SYSERR; goto on_return; } /* Start querying device info */ f->dev_info = (and_dev_info*) pj_pool_calloc(f->dev_pool, dev_count, sizeof(and_dev_info)); for (i = 0; i < dev_count; i++) { and_dev_info *adi = &f->dev_info[f->dev_count]; pjmedia_vid_dev_info *vdi = &adi->info; jobject jdev_info; jobject jtmp; int facing, max_fmt_cnt = PJMEDIA_VID_DEV_INFO_FMT_CNT; /* jdev_info = PjCameraInfo::GetCameraInfo(i) */ jdev_info = (*jni_env)->CallStaticObjectMethod( jni_env, jobjs.cam_info.cls, jobjs.cam_info.m_get_info, i); if (jdev_info == NULL) continue; /* Get camera facing: 0=back 1=front */ facing = (*jni_env)->GetIntField(jni_env, jdev_info, jobjs.cam_info.f_facing); if (facing < 0) goto on_skip_dev; /* Set device ID, direction, and has_callback info */ adi->dev_idx = i; vdi->id = f->dev_count; vdi->dir = PJMEDIA_DIR_CAPTURE; vdi->has_callback = PJ_TRUE; vdi->caps = PJMEDIA_VID_DEV_CAP_SWITCH | PJMEDIA_VID_DEV_CAP_ORIENTATION; /* Set driver & name info */ pj_ansi_strncpy(vdi->driver, "Android", sizeof(vdi->driver)); adi->facing = facing; if (facing == 0) { pj_ansi_strncpy(vdi->name, "Back camera", sizeof(vdi->name)); } else { pj_ansi_strncpy(vdi->name, "Front camera", sizeof(vdi->name)); } /* Get supported sizes */ jtmp = (*jni_env)->GetObjectField(jni_env, jdev_info, jobjs.cam_info.f_sup_size); if (jtmp) { jintArray jiarray = (jintArray*)jtmp; jint *sizes; jsize cnt, j; cnt = (*jni_env)->GetArrayLength(jni_env, jiarray); sizes = (*jni_env)->GetIntArrayElements(jni_env, jiarray, 0); adi->sup_size_cnt = cnt/2; adi->sup_size = pj_pool_calloc(f->dev_pool, adi->sup_size_cnt, sizeof(adi->sup_size[0])); for (j = 0; j < adi->sup_size_cnt; j++) { adi->sup_size[j].w = sizes[j*2]; adi->sup_size[j].h = sizes[j*2+1]; } (*jni_env)->ReleaseIntArrayElements(jni_env, jiarray, sizes, 0); (*jni_env)->DeleteLocalRef(jni_env, jtmp); } else { goto on_skip_dev; } /* Get supported formats */ jtmp = (*jni_env)->GetObjectField(jni_env, jdev_info, jobjs.cam_info.f_sup_fmt); if (jtmp) { jintArray jiarray = (jintArray*)jtmp; jint *fmts; jsize cnt, j; pj_bool_t has_i420 = PJ_FALSE; cnt = (*jni_env)->GetArrayLength(jni_env, jiarray); fmts = (*jni_env)->GetIntArrayElements(jni_env, jiarray, 0); for (j = 0; j < cnt; j++) { int k; pjmedia_format_id fmt = and_fmt_to_pj((pj_uint32_t)fmts[j]); /* Check for any duplicate */ for (k = 0; k < vdi->fmt_cnt; k++) { if (fmt == 0 || fmt == vdi->fmt[k].id) { fmt = 0; break; } } /* Make sure we recognize this format */ if (fmt == 0) continue; /* Check formats for I420 conversion */ if (fmt == PJMEDIA_FORMAT_I420) has_i420 = PJ_TRUE; else if (fmt == PJMEDIA_FORMAT_YV12) adi->has_yv12 = PJ_TRUE; else if (fmt == PJMEDIA_FORMAT_NV21) adi->has_nv21 = PJ_TRUE; for (k = 0; k < adi->sup_size_cnt && vdi->fmt_cnt < max_fmt_cnt-1; k++) { /* Landscape video */ pjmedia_format_init_video(&vdi->fmt[vdi->fmt_cnt++], fmt, adi->sup_size[k].w, adi->sup_size[k].h, DEFAULT_FPS, 1); /* Portrait video */ pjmedia_format_init_video(&vdi->fmt[vdi->fmt_cnt++], fmt, adi->sup_size[k].h, adi->sup_size[k].w, DEFAULT_FPS, 1); } } (*jni_env)->ReleaseIntArrayElements(jni_env, jiarray, fmts, JNI_ABORT); (*jni_env)->DeleteLocalRef(jni_env, jtmp); /* Pretend to support I420/IYUV, only if we support YV12/NV21 */ if (!has_i420 && (adi->has_yv12 || adi->has_nv21) && vdi->fmt_cnt < PJ_ARRAY_SIZE(vdi->fmt)) { int k; adi->forced_i420 = PJ_TRUE; for (k = 0; k < adi->sup_size_cnt && vdi->fmt_cnt < max_fmt_cnt-1; k++) { pjmedia_format_init_video(&vdi->fmt[vdi->fmt_cnt++], PJMEDIA_FORMAT_I420, adi->sup_size[k].w, adi->sup_size[k].h, DEFAULT_FPS, 1); pjmedia_format_init_video(&vdi->fmt[vdi->fmt_cnt++], PJMEDIA_FORMAT_I420, adi->sup_size[k].h, adi->sup_size[k].w, DEFAULT_FPS, 1); } } } else { goto on_skip_dev; } /* If this is front camera, set it as first/default (if not yet) */ if (facing == 1) { if (!found_front && f->dev_count > 0) { /* Swap this front cam info with one whose idx==0 */ and_dev_info tmp_adi; pj_memcpy(&tmp_adi, &f->dev_info[0], sizeof(tmp_adi)); pj_memcpy(&f->dev_info[0], adi, sizeof(tmp_adi)); pj_memcpy(adi, &tmp_adi, sizeof(tmp_adi)); f->dev_info[0].info.id = 0; f->dev_info[f->dev_count].info.id = f->dev_count; } found_front = PJ_TRUE; } f->dev_count++; on_skip_dev: (*jni_env)->DeleteLocalRef(jni_env, jdev_info); } PJ_LOG(4, (THIS_FILE, "Android video capture initialized with %d device(s):", f->dev_count)); for (i = 0; i < f->dev_count; i++) { and_dev_info *adi = &f->dev_info[i]; char tmp_str[2048], *p; int j, plen, slen; PJ_LOG(4, (THIS_FILE, "%2d: %s", i, f->dev_info[i].info.name)); /* Print supported formats */ p = tmp_str; plen = sizeof(tmp_str); for (j = 0; j < adi->info.fmt_cnt; j++) { char tmp_str2[5]; const pjmedia_video_format_detail *vfd = pjmedia_format_get_video_format_detail(&adi->info.fmt[j], 0); pjmedia_fourcc_name(adi->info.fmt[j].id, tmp_str2); slen = pj_ansi_snprintf(p, plen, "%s/%dx%d ", tmp_str2, vfd->size.w, vfd->size.h); if (slen < 0 || slen >= plen) break; plen -= slen; p += slen; } PJ_LOG(4, (THIS_FILE, " supported format = %s", tmp_str)); } on_return: jni_detach_env(with_attach); return status; }