/* * Create the adapter. */ PJ_DEF(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt, const char *name, pjmedia_transport *transport, pj_bool_t del_base, pjmedia_transport **p_tp) { pj_pool_t *pool; struct tp_adapter *adapter; if (name == NULL) name = "tpad%p"; /* Create the pool and initialize the adapter structure */ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); adapter = PJ_POOL_ZALLOC_T(pool, struct tp_adapter); adapter->pool = pool; pj_ansi_strncpy(adapter->base.name, pool->obj_name, sizeof(adapter->base.name)); adapter->base.type = (pjmedia_transport_type) (PJMEDIA_TRANSPORT_TYPE_USER + 1); adapter->base.op = &tp_adapter_op; /* Save the transport as the slave transport */ adapter->slave_tp = transport; adapter->del_base = del_base; /* Done */ *p_tp = &adapter->base; return PJ_SUCCESS; }
/* * pj_sem_create() */ PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_sem_t **sem_ptr) { pj_sem_t *sem; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL); sem = pj_pool_alloc(pool, sizeof(*sem)); sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL); if (!sem->hSemaphore) return PJ_RETURN_OS_ERROR(GetLastError()); /* Set name. */ if (!name) { name = "sem%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); } else { pj_ansi_strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } LOG_MUTEX((sem->obj_name, "Semaphore created")); *sem_ptr = sem; return PJ_SUCCESS; }
static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name) { PJ_CHECK_STACK(); #if PJ_WIN32_WINNT >= 0x0400 InitializeCriticalSection(&mutex->crit); #else mutex->hMutex = CreateMutex(NULL, FALSE, NULL); if (!mutex->hMutex) { return PJ_RETURN_OS_ERROR(GetLastError()); } #endif #if PJ_DEBUG /* Set owner. */ mutex->nesting_level = 0; mutex->owner = NULL; #endif /* Set name. */ if (!name) { name = "mtx%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); } else { pj_ansi_strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (mutex->obj_name, "Mutex created")); return PJ_SUCCESS; }
/* Create pool */ PJ_DEF(pj_pool_t*) pj_pool_create_imp( const char *file, int line, void *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback) { pj_pool_t *pool; PJ_UNUSED_ARG(file); PJ_UNUSED_ARG(line); PJ_UNUSED_ARG(factory); PJ_UNUSED_ARG(initial_size); PJ_UNUSED_ARG(increment_size); pool = malloc(sizeof(struct pj_pool_t)); if (!pool) return NULL; if (name) { pj_ansi_strncpy(pool->obj_name, name, sizeof(pool->obj_name)); pool->obj_name[sizeof(pool->obj_name)-1] = '\0'; } else { strcpy(pool->obj_name, "altpool"); } pool->factory = NULL; pool->first_mem = NULL; pool->used_size = 0; pool->cb = callback; return pool; }
PJ_DEF(pj_status_t) pjmedia_silence_det_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, pjmedia_silence_det **p_sd) { pjmedia_silence_det *sd; PJ_ASSERT_RETURN(pool && p_sd, PJ_EINVAL); sd = pj_pool_zalloc(pool, sizeof(struct pjmedia_silence_det)); pj_ansi_strncpy(sd->objname, THIS_FILE, PJ_MAX_OBJ_NAME); sd->objname[PJ_MAX_OBJ_NAME-1] = '\0'; sd->ptime = samples_per_frame * 1000 / clock_rate; sd->signal_cnt = 0; sd->silence_cnt = 0; sd->weakest_signal = 0xFFFFFFFFUL; sd->loudest_silence = 0; /* Default settings */ pjmedia_silence_det_set_params(sd, -1, -1, -1); /* Restart in fixed, silent mode */ sd->in_talk = PJ_FALSE; pjmedia_silence_det_set_adaptive( sd, -1 ); *p_sd = sd; return PJ_SUCCESS; }
void MainWin::call() { if (callButton_->text() == "Answer") { pjsua_call_setting call_setting; pj_assert(currentCall_ != -1); pjsua_call_setting_default(&call_setting); call_setting.vid_cnt = (vidEnabled_->checkState()==Qt::Checked); pjsua_call_answer2(currentCall_, &call_setting, 200, NULL, NULL); callButton_->setEnabled(false); } else { pj_status_t status; QString dst = url_->text(); char uri[256]; pjsua_call_setting call_setting; pj_ansi_strncpy(uri, dst.toAscii().data(), sizeof(uri)); pj_str_t uri2 = pj_str((char*)uri); pj_assert(currentCall_ == -1); pjsua_call_setting_default(&call_setting); call_setting.vid_cnt = (vidEnabled_->checkState()==Qt::Checked); status = pjsua_call_make_call(accountId_, &uri2, &call_setting, NULL, NULL, ¤tCall_); if (status != PJ_SUCCESS) { showError("make call", status); return; } } }
/* * Create the ZRTP transport. */ PJ_DEF(pj_status_t) pjmedia_transport_zrtp_create(pjmedia_endpt *endpt, const char *name, pjmedia_transport *transport, pjmedia_transport **p_tp, pj_bool_t close_slave) { pj_pool_t *pool; struct tp_zrtp *zrtp; pj_status_t rc; if (name == NULL) name = "tzrtp%p"; /* Create the pool and initialize the adapter structure */ pool = pjmedia_endpt_create_pool(endpt, name, 5*1024, 512); zrtp = PJ_POOL_ZALLOC_T(pool, struct tp_zrtp); zrtp->pool = pool; pj_ansi_strncpy(zrtp->base.name, pool->obj_name, sizeof(zrtp->base.name)); zrtp->base.type = (pjmedia_transport_type) (PJMEDIA_TRANSPORT_TYPE_USER + 2); zrtp->base.op = &tp_zrtp_op; #ifndef DYNAMIC_TIMER if (timer_pool == NULL) { timer_pool = pjmedia_endpt_create_pool(endpt, "zrtp_timer", 256, 256); rc = timer_initialize(); if (rc != PJ_SUCCESS) { pj_pool_release(timer_pool); pj_pool_release(zrtp->pool); return rc; } } #else zrtp->timer_heap = pjsip_endpt_get_timer_heap(pjsua_var.endpt); #endif /* Create the empty wrapper */ zrtp->zrtpCtx = zrtp_CreateWrapper(); /* Initialize standard values */ zrtp->clientIdString = clientId; /* Set standard name */ zrtp->zrtpSeq = 1; /* TODO: randomize */ rc = pj_mutex_create_simple(zrtp->pool, "zrtp", &zrtp->zrtpMutex); zrtp->zrtpBuffer = pj_pool_zalloc(pool, MAX_ZRTP_SIZE); zrtp->sendBuffer = pj_pool_zalloc(pool, MAX_RTP_BUFFER_LEN); zrtp->sendBufferCtrl = pj_pool_zalloc(pool, MAX_RTCP_BUFFER_LEN); zrtp->slave_tp = transport; zrtp->close_slave = close_slave; zrtp->mitmMode = PJ_FALSE; /* Done */ zrtp->refcount++; *p_tp = &zrtp->base; return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool, const char *name, unsigned clock_rate, unsigned samples_per_frame, unsigned channel_count, unsigned max_delay, unsigned options, pjmedia_delay_buf **p_b) { pjmedia_delay_buf *b; pj_status_t status; PJ_ASSERT_RETURN(pool && samples_per_frame && clock_rate && channel_count && p_b, PJ_EINVAL); PJ_ASSERT_RETURN(options==0, PJ_EINVAL); PJ_UNUSED_ARG(options); if (!name) { name = "delaybuf"; } b = PJ_POOL_ZALLOC_T(pool, pjmedia_delay_buf); pj_ansi_strncpy(b->obj_name, name, PJ_MAX_OBJ_NAME-1); b->samples_per_frame = samples_per_frame; b->channel_count = channel_count; b->ptime = samples_per_frame * 1000 / clock_rate / channel_count; if (max_delay < b->ptime) max_delay = PJ_MAX(DEFAULT_MAX_DELAY, b->ptime); b->max_cnt = samples_per_frame * max_delay / b->ptime; b->eff_cnt = b->max_cnt >> 1; b->recalc_timer = RECALC_TIME; /* Create circular buffer */ status = pjmedia_circ_buf_create(pool, b->max_cnt, &b->circ_buf); if (status != PJ_SUCCESS) return status; /* Create WSOLA */ status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1, PJMEDIA_WSOLA_NO_FADING, &b->wsola); if (status != PJ_SUCCESS) return status; /* Finally, create mutex */ status = pj_lock_create_recursive_mutex(pool, b->obj_name, &b->lock); if (status != PJ_SUCCESS) return status; *p_b = b; TRACE__((b->obj_name,"Delay buffer created")); return PJ_SUCCESS; }
/* API: refresh the list of devices */ static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f) { ffmpeg_factory *ff = (ffmpeg_factory*)f; AVInputFormat *p; ffmpeg_dev_info *info; av_log_set_callback(&print_ffmpeg_log); av_log_set_level(AV_LOG_DEBUG); if (ff->dev_pool) { pj_pool_release(ff->dev_pool); ff->dev_pool = NULL; } /* TODO: this should enumerate devices, now it enumerates host APIs */ ff->dev_count = 0; ff->dev_pool = pj_pool_create(ff->pf, "ffmpeg_cap_dev", 500, 500, NULL); p = av_iformat_next(NULL); while (p) { if (p->flags & AVFMT_NOFILE) { unsigned i; info = &ff->dev_info[ff->dev_count++]; pj_bzero(info, sizeof(*info)); pj_ansi_strncpy(info->base.name, "default", sizeof(info->base.name)); pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver), "%s (ffmpeg)", p->name); info->base.dir = PJMEDIA_DIR_CAPTURE; info->base.has_callback = PJ_FALSE; info->host_api = p; #if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ (defined(PJ_WIN64) && PJ_WIN64!=0) info->def_devname = "0"; #elif defined(PJ_LINUX) && PJ_LINUX!=0 info->def_devname = "/dev/video0"; #endif /* Set supported formats, currently hardcoded to RGB24 only */ info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT; info->base.fmt_cnt = 1; for (i = 0; i < info->base.fmt_cnt; ++i) { pjmedia_format *fmt = &info->base.fmt[i]; fmt->id = PJMEDIA_FORMAT_RGB24; fmt->type = PJMEDIA_TYPE_VIDEO; fmt->detail_type = PJMEDIA_FORMAT_DETAIL_NONE; } } p = av_iformat_next(p); } return PJ_SUCCESS; }
/* * pj_thread_create(...) */ PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **thread_ptr) { DWORD dwflags = 0; pj_thread_t *rec; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL); /* Set flags */ if (flags & PJ_THREAD_SUSPENDED) dwflags |= CREATE_SUSPENDED; /* Create thread record and assign name for the thread */ rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t)); if (!rec) return PJ_ENOMEM; /* Set name. */ if (!thread_name) thread_name = "thr%p"; if (strchr(thread_name, '%')) { pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); } else { pj_ansi_strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (rec->obj_name, "Thread created")); #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 rec->stk_size = stack_size ? (pj_uint32_t)stack_size : 0xFFFFFFFFUL; rec->stk_max_usage = 0; #endif /* Create the thread. */ rec->proc = proc; rec->arg = arg; rec->hthread = CreateThread(NULL, stack_size, thread_main, rec, dwflags, &rec->idthread); if (rec->hthread == NULL) return PJ_RETURN_OS_ERROR(GetLastError()); /* Success! */ *thread_ptr = rec; return PJ_SUCCESS; }
/* reset dev info */ static void reset_dev_info(struct avi_dev_info *adi) { /* Close avi streams */ if (adi->avi) { unsigned i, cnt; cnt = pjmedia_avi_streams_get_num_streams(adi->avi); for (i=0; i<cnt; ++i) { pjmedia_avi_stream *as; as = pjmedia_avi_streams_get_stream(adi->avi, i); if (as) { pjmedia_port *port; port = pjmedia_avi_stream_get_port(as); pjmedia_port_destroy(port); } } adi->avi = NULL; } if (adi->codec) { pjmedia_vid_codec_close(adi->codec); adi->codec = NULL; } if (adi->pool) pj_pool_release(adi->pool); pj_bzero(adi, sizeof(*adi)); /* Fill up with *dummy" device info */ pj_ansi_strncpy(adi->info.name, "AVI Player", sizeof(adi->info.name)-1); pj_ansi_strncpy(adi->info.driver, DRIVER_NAME, sizeof(adi->info.driver)-1); adi->info.dir = PJMEDIA_DIR_CAPTURE; adi->info.has_callback = PJ_FALSE; }
/* * pj_mutex_lock() */ PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) { #if PJ_HAS_THREADS pj_status_t status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); #if PJ_DEBUG PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting (mutex owner=%s)", pj_thread_this(mutex->inst_id)->obj_name, mutex->owner_name)); #else PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", pj_thread_this(mutex->inst_id)->obj_name)); #endif status = pthread_mutex_lock( &mutex->mutex ); #if PJ_DEBUG if (status == PJ_SUCCESS) { mutex->owner = pj_thread_this(mutex->inst_id); pj_ansi_strncpy(mutex->owner_name, mutex->owner->obj_name, PJ_MAX_OBJ_NAME); ++mutex->nesting_level; } PJ_LOG(6,(mutex->obj_name, (status==0 ? "Mutex acquired by thread %s (level=%d)" : "Mutex acquisition FAILED by %s (level=%d)"), pj_thread_this(mutex->inst_id)->obj_name, mutex->nesting_level)); #else PJ_LOG(6,(mutex->obj_name, (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"), pj_thread_this(mutex->inst_id)->obj_name)); #endif if (status == 0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(status); #else /* PJ_HAS_THREADS */ pj_assert( mutex == (pj_mutex_t*)1 ); return PJ_SUCCESS; #endif }
PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index) { pjmedia_snd_dev_info *oi = &g_sys.info[g_sys.info_counter]; pjmedia_aud_dev_info di; g_sys.info_counter = (g_sys.info_counter+1) % PJ_ARRAY_SIZE(g_sys.info); if (pjmedia_aud_dev_get_info(index, &di) != PJ_SUCCESS) return NULL; pj_bzero(oi, sizeof(*oi)); pj_ansi_strncpy(oi->name, di.name, sizeof(oi->name)); oi->name[sizeof(oi->name)-1] = '\0'; oi->input_count = di.input_count; oi->output_count = di.output_count; oi->default_samples_per_sec = di.default_samples_per_sec; return oi; }
/* * Internal function to initialize pool. */ PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, const char *name, pj_size_t increment_size, pj_pool_callback *callback) { PJ_CHECK_STACK(); pool->increment_size = increment_size; pool->callback = callback; if (name) { if (strchr(name, '%') != NULL) { pj_ansi_snprintf(pool->obj_name, sizeof(pool->obj_name), name, pool); } else { pj_ansi_strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); } } else { pool->obj_name[0] = '\0'; } }
/* * pj_mutex_trylock() */ PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) { #if PJ_HAS_THREADS int status; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(mutex, PJ_EINVAL); PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is trying", pj_thread_this(mutex->inst_id)->obj_name)); status = pthread_mutex_trylock( &mutex->mutex ); if (status==0) { #if PJ_DEBUG mutex->owner = pj_thread_this(mutex->inst_id); pj_ansi_strncpy(mutex->owner_name, mutex->owner->obj_name, PJ_MAX_OBJ_NAME); ++mutex->nesting_level; PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s (level=%d)", pj_thread_this(mutex->inst_id)->obj_name, mutex->nesting_level)); #else PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", pj_thread_this(mutex->inst_id)->obj_name)); #endif } else { PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s's trylock() failed", pj_thread_this(mutex->inst_id)->obj_name)); } if (status==0) return PJ_SUCCESS; else return PJ_RETURN_OS_ERROR(status); #else /* PJ_HAS_THREADS */ pj_assert( mutex == (pj_mutex_t*)1); return PJ_SUCCESS; #endif }
/* * pj_event_create() */ PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, const char *name, pj_bool_t manual_reset, pj_bool_t initial, pj_event_t **event_ptr) { pj_event_t *event; PJ_CHECK_STACK(); PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL); event = pj_pool_alloc(pool, sizeof(*event)); if (!event) return PJ_ENOMEM; event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, initial?TRUE:FALSE, NULL); if (!event->hEvent) return PJ_RETURN_OS_ERROR(GetLastError()); /* Set name. */ if (!name) { name = "evt%p"; } if (strchr(name, '%')) { pj_ansi_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event); } else { pj_ansi_strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME); event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; } PJ_LOG(6, (event->obj_name, "Event created")); *event_ptr = event; return PJ_SUCCESS; }
static void get_mixer_name(struct alsa_factory *af) { snd_mixer_t *handle; snd_mixer_elem_t *elem; if (snd_mixer_open(&handle, 0) < 0) return; if (snd_mixer_attach(handle, "default") < 0) { snd_mixer_close(handle); return; } if (snd_mixer_selem_register(handle, NULL, NULL) < 0) { snd_mixer_close(handle); return; } if (snd_mixer_load(handle) < 0) { snd_mixer_close(handle); return; } for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) { if (snd_mixer_selem_is_active(elem) && snd_mixer_selem_has_playback_volume(elem)) { pj_ansi_strncpy(af->pb_mixer_name, snd_mixer_selem_get_name(elem), sizeof(af->pb_mixer_name)); TRACE_((THIS_FILE, "Playback mixer name: %s", af->pb_mixer_name)); break; } } snd_mixer_close(handle); }
/** * Create loopback transport. */ PJ_DEF(pj_status_t) pjmedia_transport_loop_create(pjmedia_endpt *endpt, pjmedia_transport **p_tp) { struct transport_loop *tp; pj_pool_t *pool; /* Sanity check */ PJ_ASSERT_RETURN(endpt && p_tp, PJ_EINVAL); /* Create transport structure */ pool = pjmedia_endpt_create_pool(endpt, "tploop", 512, 512); if (!pool) return PJ_ENOMEM; tp = PJ_POOL_ZALLOC_T(pool, struct transport_loop); tp->pool = pool; pj_ansi_strncpy(tp->base.name, tp->pool->obj_name, PJ_MAX_OBJ_NAME-1); tp->base.op = &transport_udp_op; tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; /* Done */ *p_tp = &tp->base; return PJ_SUCCESS; }
/* 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; }
/* Timer callback. When the timer is fired, it can be time to refresh * the session if UA is the refresher, otherwise it is time to end * the session. */ static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) { pjsip_inv_session *inv = (pjsip_inv_session*) entry->user_data; pjsip_tx_data *tdata = NULL; pj_status_t status; pj_bool_t as_refresher; int entry_id; char obj_name[PJ_MAX_OBJ_NAME]; pj_assert(inv); PJ_UNUSED_ARG(timer_heap); /* Lock dialog. */ pjsip_dlg_inc_lock(inv->dlg); /* Check our role */ as_refresher = (inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) || (inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS); entry_id = entry->id; pj_ansi_strncpy(obj_name, inv->pool->obj_name, PJ_MAX_OBJ_NAME); /* Do action based on role(refresher or refreshee). * As refresher: * - send refresh, or * - end session if there is no response to the refresh request. * As refreshee: * - end session if there is no refresh request received. */ if (as_refresher && (entry_id != REFRESHER_EXPIRE_TIMER_ID)) { pj_time_val now; /* As refresher, reshedule the refresh request on the following: * - must not send re-INVITE if another INVITE or SDP negotiation * is in progress. * - must not send UPDATE with SDP if SDP negotiation is in progress */ pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg); inv->timer->timer.id = 0; if ( (!inv->timer->use_update && ( inv->invite_tsx != NULL || neg_state != PJMEDIA_SDP_NEG_STATE_DONE) ) || (inv->timer->use_update && inv->timer->with_sdp && neg_state != PJMEDIA_SDP_NEG_STATE_DONE ) ) { pj_time_val delay = {1, 0}; inv->timer->timer.id = 1; pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer, &delay); pjsip_dlg_dec_lock(inv->dlg); return; } /* Refresher, refresh the session */ if (inv->timer->use_update) { const pjmedia_sdp_session *offer = NULL; if (inv->timer->with_sdp) { pjmedia_sdp_neg_get_active_local(inv->neg, &offer); } status = pjsip_inv_update(inv, NULL, offer, &tdata); } else { /* Create re-INVITE without modifying session */ pjsip_msg_body *body; const pjmedia_sdp_session *offer = NULL; pj_assert(pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_DONE); status = pjsip_inv_invite(inv, &tdata); if (status == PJ_SUCCESS) status = pjmedia_sdp_neg_send_local_offer(inv->pool_prov, inv->neg, &offer); if (status == PJ_SUCCESS) status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer); if (status == PJ_SUCCESS) { status = pjsip_create_sdp_body(tdata->pool, (pjmedia_sdp_session*)offer, &body); tdata->msg->body = body; } } pj_gettimeofday(&now); PJ_LOG(4, (obj_name, "Refreshing session after %ds (expiration period=%ds)", (now.sec-inv->timer->last_refresh.sec), inv->timer->setting.sess_expires)); } else { pj_time_val now; if (as_refresher) inv->timer->expire_timer.id = 0; else inv->timer->timer.id = 0; /* Terminate the session */ status = pjsip_inv_end_session(inv, PJSIP_SC_REQUEST_TIMEOUT, NULL, &tdata); pj_gettimeofday(&now); PJ_LOG(3, (obj_name, "No session %s received after %ds " "(expiration period=%ds), stopping session now!", (as_refresher?"refresh response":"refresh"), (now.sec-inv->timer->last_refresh.sec), inv->timer->setting.sess_expires)); } /* Unlock dialog. */ pjsip_dlg_dec_lock(inv->dlg); /* Send message, if any */ if (tdata && status == PJ_SUCCESS) { inv->timer->refresh_tdata = tdata; status = pjsip_inv_send_msg(inv, tdata); } /* * At this point, dialog might have already been destroyed, * including its pool used by the invite session. */ /* Print error message, if any */ if (status != PJ_SUCCESS) { PJ_PERROR(2, (obj_name, status, "Error in %s session timer", ((as_refresher && entry_id != REFRESHER_EXPIRE_TIMER_ID)? "refreshing" : "terminating"))); } }
/* Resolve IPv4/IPv6 address */ PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename, unsigned *count, pj_addrinfo ai[]) { #if defined(PJ_SOCK_HAS_GETADDRINFO) && PJ_SOCK_HAS_GETADDRINFO!=0 char nodecopy[PJ_MAX_HOSTNAME]; struct addrinfo hint, *res, *orig_res; unsigned i; int rc; PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL); PJ_ASSERT_RETURN(nodename->ptr && nodename->slen, PJ_EINVAL); PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if (af == PJ_AF_UNSPEC) { if (pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; } else if (pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; } if (af != PJ_AF_UNSPEC) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } } /* Copy node name to null terminated string. */ if (nodename->slen >= PJ_MAX_HOSTNAME) return PJ_ENAMETOOLONG; pj_memcpy(nodecopy, nodename->ptr, nodename->slen); nodecopy[nodename->slen] = '\0'; /* Call getaddrinfo() */ pj_bzero(&hint, sizeof(hint)); hint.ai_family = af; rc = getaddrinfo(nodecopy, NULL, &hint, &res); if (rc != 0) return PJ_ERESOLVE; orig_res = res; /* Enumerate each item in the result */ for (i=0; i<*count && res; res=res->ai_next) { /* Ignore unwanted address families */ if (af!=PJ_AF_UNSPEC && res->ai_family != af) continue; /* Store canonical name (possibly truncating the name) */ if (res->ai_canonname) { pj_ansi_strncpy(ai[i].ai_canonname, res->ai_canonname, sizeof(ai[i].ai_canonname)); ai[i].ai_canonname[sizeof(ai[i].ai_canonname)-1] = '\0'; } else { pj_ansi_strcpy(ai[i].ai_canonname, nodecopy); } /* Store address */ PJ_ASSERT_ON_FAIL(res->ai_addrlen <= sizeof(pj_sockaddr), continue); pj_memcpy(&ai[i].ai_addr, res->ai_addr, res->ai_addrlen); PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr); /* Next slot */ ++i; } *count = i; freeaddrinfo(orig_res); /* Done */ return PJ_SUCCESS; #else /* PJ_SOCK_HAS_GETADDRINFO */ PJ_ASSERT_RETURN(count && *count, PJ_EINVAL); /* Check if nodename is IP address */ pj_bzero(&ai[0], sizeof(ai[0])); if (af == PJ_AF_UNSPEC) { if (pj_inet_pton(PJ_AF_INET, nodename, &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS) { af = PJ_AF_INET; } else if (pj_inet_pton(PJ_AF_INET6, nodename, &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS) { af = PJ_AF_INET6; } if (af != PJ_AF_UNSPEC) { pj_str_t tmp; tmp.ptr = ai[0].ai_canonname; pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME); ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af; *count = 1; return PJ_SUCCESS; } } if (af == PJ_AF_INET || af == PJ_AF_UNSPEC) { pj_hostent he; unsigned i, max_count; pj_status_t status; /* VC6 complains that "he" is uninitialized */ #ifdef _MSC_VER pj_bzero(&he, sizeof(he)); #endif status = pj_gethostbyname(nodename, &he); if (status != PJ_SUCCESS) return status; max_count = *count; *count = 0; pj_bzero(ai, max_count * sizeof(pj_addrinfo)); for (i=0; he.h_addr_list[i] && *count<max_count; ++i) { pj_ansi_strncpy(ai[*count].ai_canonname, he.h_name, sizeof(ai[*count].ai_canonname)); ai[*count].ai_canonname[sizeof(ai[*count].ai_canonname)-1] = '\0'; ai[*count].ai_addr.ipv4.sin_family = PJ_AF_INET; pj_memcpy(&ai[*count].ai_addr.ipv4.sin_addr, he.h_addr_list[i], he.h_length); PJ_SOCKADDR_RESET_LEN(&ai[*count].ai_addr); (*count)++; } return PJ_SUCCESS; } else { /* IPv6 is not supported */ *count = 0; return PJ_EIPV6NOTSUP; } #endif /* PJ_SOCK_HAS_GETADDRINFO */ }
/* Internal: init driver */ static pj_status_t init_driver(unsigned drv_idx, pj_bool_t refresh) { struct driver *drv = &aud_subsys.drv[drv_idx]; pjmedia_aud_dev_factory *f; unsigned i, dev_cnt; pj_status_t status; if (!refresh && drv->create) { /* Create the factory */ f = (*drv->create)(aud_subsys.pf); if (!f) return PJ_EUNKNOWN; /* Call factory->init() */ status = f->op->init(f); if (status != PJ_SUCCESS) { f->op->destroy(f); return status; } } else { f = drv->f; } /* Register device change observer */ if (!refresh) { f->op->set_dev_change_cb(f, &process_aud_dev_change_event); } if (!f) return PJ_EUNKNOWN; /* Get number of devices */ dev_cnt = f->op->get_dev_count(f); if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) { PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because" " there are too many devices", aud_subsys.dev_cnt + dev_cnt - MAX_DEVS)); dev_cnt = MAX_DEVS - aud_subsys.dev_cnt; } /* enabling this will cause pjsua-lib initialization to fail when there * is no sound device installed in the system, even when pjsua has been * run with --null-audio * if (dev_cnt == 0) { f->op->destroy(f); return PJMEDIA_EAUD_NODEV; } */ /* Fill in default devices */ drv->rec_dev_idx = f->op->get_default_rec_dev(f); drv->play_dev_idx = f->op->get_default_play_dev(f); for (i=0; i<dev_cnt; ++i) { pjmedia_aud_dev_info info; status = f->op->get_dev_info(f, i, &info); if (status != PJ_SUCCESS) { f->op->destroy(f); return status; } if (drv->name[0]=='\0') { /* Set driver name */ pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name)); drv->name[sizeof(drv->name)-1] = '\0'; } if (drv->play_dev_idx < 0 && info.output_count) { /* Set default playback device */ drv->play_dev_idx = i; } if (drv->rec_dev_idx < 0 && info.input_count) { /* Set default capture device */ drv->rec_dev_idx = i; } if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0) { /* Done. */ break; } } /* Register the factory */ drv->f = f; drv->f->sys.drv_idx = drv_idx; drv->start_idx = aud_subsys.dev_cnt; drv->dev_cnt = dev_cnt; /* Register devices to global list */ for (i=0; i<dev_cnt; ++i) { aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i); } return PJ_SUCCESS; }
/* * Parse fmtp for specified format/payload type. */ static void parse_fmtp( pj_pool_t *pool, const pjmedia_sdp_media *m, unsigned pt, pjmedia_codec_fmtp *fmtp) { const pjmedia_sdp_attr *attr; pjmedia_sdp_fmtp sdp_fmtp; char *p, *p_end, fmt_buf[8]; pj_str_t fmt; pj_assert(m && fmtp); pj_bzero(fmtp, sizeof(pjmedia_codec_fmtp)); /* Get "fmtp" attribute for the format */ pj_ansi_sprintf(fmt_buf, "%d", pt); fmt = pj_str(fmt_buf); attr = pjmedia_sdp_media_find_attr2(m, "fmtp", &fmt); if (attr == NULL) return; /* Parse "fmtp" attribute */ if (pjmedia_sdp_attr_get_fmtp(attr, &sdp_fmtp) != PJ_SUCCESS) return; /* Prepare parsing */ p = sdp_fmtp.fmt_param.ptr; p_end = p + sdp_fmtp.fmt_param.slen; /* Parse */ while (p < p_end) { char *token, *start, *end; /* Skip whitespaces */ while (p < p_end && (*p == ' ' || *p == '\t')) ++p; if (p == p_end) break; /* Get token */ start = p; while (p < p_end && *p != ';' && *p != '=') ++p; end = p - 1; /* Right trim */ while (end >= start && (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n' )) --end; /* Forward a char after trimming */ ++end; /* Store token */ if (end > start) { token = (char*)pj_pool_alloc(pool, end - start); pj_ansi_strncpy(token, start, end - start); if (*p == '=') /* Got param name */ pj_strset(&fmtp->param[fmtp->cnt].name, token, end - start); else /* Got param value */ pj_strset(&fmtp->param[fmtp->cnt++].val, token, end - start); } else if (*p != '=') { ++fmtp->cnt; } /* Next */ ++p; } }
/* API: init driver */ PJ_DEF(pj_status_t) pjmedia_vid_driver_init(unsigned drv_idx, pj_bool_t refresh) { pjmedia_vid_driver *drv = &vid_subsys.drv[drv_idx]; pjmedia_vid_dev_factory *f; unsigned i, dev_cnt; pj_status_t status; if (!refresh) { /* Create the factory */ f = (*drv->create)(vid_subsys.pf); if (!f) return PJ_EUNKNOWN; /* Call factory->init() */ status = f->op->init(f); if (status != PJ_SUCCESS) { f->op->destroy(f); return status; } } else { f = drv->f; } /* Get number of devices */ dev_cnt = f->op->get_dev_count(f); if (dev_cnt + vid_subsys.dev_cnt > MAX_DEVS) { PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because" " there are too many devices", vid_subsys.dev_cnt + dev_cnt - MAX_DEVS)); dev_cnt = MAX_DEVS - vid_subsys.dev_cnt; } /* enabling this will cause pjsua-lib initialization to fail when there * is no video device installed in the system, even when pjsua has been * run with --null-video * if (dev_cnt == 0) { f->op->destroy(f); return PJMEDIA_EVID_NODEV; } */ /* Fill in default devices */ drv->rend_dev_idx = drv->cap_dev_idx = -1; for (i=0; i<dev_cnt; ++i) { pjmedia_vid_dev_info info; status = f->op->get_dev_info(f, i, &info); if (status != PJ_SUCCESS) { f->op->destroy(f); return status; } if (drv->name[0]=='\0') { /* Set driver name */ pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name)); drv->name[sizeof(drv->name)-1] = '\0'; } if (drv->rend_dev_idx < 0 && (info.dir & PJMEDIA_DIR_RENDER)) { /* Set default render device */ drv->rend_dev_idx = i; } if (drv->cap_dev_idx < 0 && (info.dir & PJMEDIA_DIR_CAPTURE)) { /* Set default capture device */ drv->cap_dev_idx = i; } if (drv->rend_dev_idx >= 0 && drv->cap_dev_idx >= 0) { /* Done. */ break; } } /* Register the factory */ drv->f = f; drv->f->sys.drv_idx = drv_idx; drv->start_idx = vid_subsys.dev_cnt; drv->dev_cnt = dev_cnt; /* Register devices to global list */ for (i=0; i<dev_cnt; ++i) { vid_subsys.dev_list[vid_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i); } return PJ_SUCCESS; }
/* API: configure the AVI */ PJ_DEF(pj_status_t) pjmedia_avi_dev_alloc( pjmedia_vid_dev_factory *f, pjmedia_avi_dev_param *p, pjmedia_vid_dev_index *p_id) { pjmedia_vid_dev_index id; struct avi_factory *cf = (struct avi_factory*)f; unsigned local_idx; struct avi_dev_info *adi = NULL; pjmedia_format avi_fmt; const pjmedia_video_format_info *vfi; pj_status_t status; PJ_ASSERT_RETURN(f && p && p_id, PJ_EINVAL); if (p_id) *p_id = PJMEDIA_VID_INVALID_DEV; /* Get a free dev */ for (local_idx=0; local_idx<cf->dev_count; ++local_idx) { if (cf->dev_info[local_idx].avi == NULL) { adi = &cf->dev_info[local_idx]; break; } } if (!adi) return PJ_ETOOMANY; /* Convert local ID to global id */ status = pjmedia_vid_dev_get_global_index(&cf->base, local_idx, &id); if (status != PJ_SUCCESS) return status; /* Reset */ if (adi->pool) { pj_pool_release(adi->pool); } pj_bzero(adi, sizeof(*adi)); /* Reinit */ PJ_ASSERT_RETURN(p->path.slen, PJ_EINVAL); adi->pool = pj_pool_create(cf->pf, "avidi%p", 512, 512, NULL); /* Open the AVI */ pj_strdup_with_null(adi->pool, &adi->fpath, &p->path); status = pjmedia_avi_player_create_streams(adi->pool, adi->fpath.ptr, 0, &adi->avi); if (status != PJ_SUCCESS) { goto on_error; } adi->vid = pjmedia_avi_streams_get_stream_by_media(adi->avi, 0, PJMEDIA_TYPE_VIDEO); if (!adi->vid) { status = PJMEDIA_EVID_BADFORMAT; PJ_LOG(4,(THIS_FILE, "Error: cannot find video in AVI %s", adi->fpath.ptr)); goto on_error; } pjmedia_format_copy(&avi_fmt, &adi->vid->info.fmt); vfi = pjmedia_get_video_format_info(NULL, avi_fmt.id); /* Check whether the frame is encoded. */ if (!vfi || vfi->bpp == 0) { /* Yes, prepare codec */ const pjmedia_vid_codec_info *codec_info; pjmedia_vid_codec_param codec_param; pjmedia_video_apply_fmt_param vafp; /* Lookup codec */ status = pjmedia_vid_codec_mgr_get_codec_info2(NULL, avi_fmt.id, &codec_info); if (status != PJ_SUCCESS || !codec_info) goto on_error; status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info, &codec_param); if (status != PJ_SUCCESS) goto on_error; /* Open codec */ status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info, &adi->codec); if (status != PJ_SUCCESS) goto on_error; status = pjmedia_vid_codec_init(adi->codec, adi->pool); if (status != PJ_SUCCESS) goto on_error; codec_param.dir = PJMEDIA_DIR_DECODING; codec_param.packing = PJMEDIA_VID_PACKING_WHOLE; status = pjmedia_vid_codec_open(adi->codec, &codec_param); if (status != PJ_SUCCESS) goto on_error; /* Allocate buffer */ avi_fmt.id = codec_info->dec_fmt_id[0]; vfi = pjmedia_get_video_format_info(NULL, avi_fmt.id); pj_bzero(&vafp, sizeof(vafp)); vafp.size = avi_fmt.det.vid.size; status = vfi->apply_fmt(vfi, &vafp); if (status != PJ_SUCCESS) goto on_error; adi->enc_buf = pj_pool_alloc(adi->pool, vafp.framebytes); adi->enc_buf_size = vafp.framebytes; } /* Calculate title */ if (p->title.slen) { pj_strdup_with_null(adi->pool, &adi->title, &p->title); } else { char *start = p->path.ptr + p->path.slen; pj_str_t tmp; while (start >= p->path.ptr) { if (*start == '/' || *start == '\\') break; --start; } tmp.ptr = start + 1; tmp.slen = p->path.ptr + p->path.slen - tmp.ptr; pj_strdup_with_null(adi->pool, &adi->title, &tmp); } /* Init device info */ pj_ansi_strncpy(adi->info.name, adi->title.ptr, sizeof(adi->info.name)-1); pj_ansi_strncpy(adi->info.driver, DRIVER_NAME, sizeof(adi->info.driver)-1); adi->info.dir = PJMEDIA_DIR_CAPTURE; adi->info.has_callback = PJ_FALSE; adi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; adi->info.fmt_cnt = 1; pjmedia_format_copy(&adi->info.fmt[0], &avi_fmt); /* Set out vars */ if (p_id) *p_id = id; p->avi_streams = adi->avi; if (p->title.slen == 0) p->title = adi->title; return PJ_SUCCESS; on_error: if (adi->codec) { pjmedia_vid_codec_close(adi->codec); adi->codec = NULL; } if (adi->pool) { pj_pool_release(adi->pool); adi->pool = NULL; } pjmedia_avi_dev_free(id); return status; }
static void systest_aec_test(void) { const char *ref_wav_paths[] = { add_path(res_path, WAV_PLAYBACK_PATH), ALT_PATH1 WAV_PLAYBACK_PATH }; pjsua_player_id player_id = PJSUA_INVALID_ID; pjsua_recorder_id writer_id = PJSUA_INVALID_ID; enum gui_key key; test_item_t *ti; const char *title = "AEC/AES Test"; unsigned last_ec_tail = 0; pj_status_t status; pj_str_t tmp; ti = systest_alloc_test_item(title); if (!ti) return; key = gui_msgbox(title, "This test will try to find whether the AEC/AES " "works good on this system. Test will play a file " "while recording from mic. The recording will be " "played back later so you can check if echo is there. " "Press OK to start.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } /* Save current EC tail */ status = pjsua_get_ec_tail(&last_ec_tail); if (status != PJ_SUCCESS) goto on_return; /* Set EC tail setting to default */ status = pjsua_set_ec(PJSUA_DEFAULT_EC_TAIL_LEN, 0); if (status != PJ_SUCCESS) goto on_return; /* * Create player and recorder */ status = create_player(PJ_ARRAY_SIZE(ref_wav_paths), ref_wav_paths, &player_id); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "Error opening WAV file %s", WAV_PLAYBACK_PATH)); goto on_return; } status = pjsua_recorder_create( pj_cstr(&tmp, add_path(doc_path, AEC_REC_PATH)), 0, 0, -1, 0, &writer_id); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "Error writing WAV file %s", AEC_REC_PATH)); goto on_return; } /* * Start playback and recording. */ pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0); pj_thread_sleep(100); pjsua_conf_connect(0, pjsua_recorder_get_conf_port(writer_id)); /* Wait user signal */ gui_msgbox(title, "AEC/AES test is running. Press OK to stop this test.", WITH_OK); /* * Stop and close playback and recorder */ pjsua_conf_disconnect(0, pjsua_recorder_get_conf_port(writer_id)); pjsua_conf_disconnect(pjsua_player_get_conf_port(player_id), 0); pjsua_recorder_destroy(writer_id); pjsua_player_destroy(player_id); player_id = PJSUA_INVALID_ID; writer_id = PJSUA_INVALID_ID; /* * Play the result. */ status = pjsua_player_create( pj_cstr(&tmp, add_path(doc_path, AEC_REC_PATH)), 0, &player_id); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "Error opening WAV file %s", AEC_REC_PATH)); goto on_return; } pjsua_conf_connect(pjsua_player_get_conf_port(player_id), 0); /* Wait user signal */ gui_msgbox(title, "We are now playing the captured audio from the mic. " "Check if echo (of the audio played back previously) is " "present in the audio. The recording is stored in " AEC_REC_PATH " for offline analysis. " "Press OK to stop.", WITH_OK); pjsua_conf_disconnect(pjsua_player_get_conf_port(player_id), 0); key = gui_msgbox(title, "Did you notice any echo in the recording?", WITH_YESNO); on_return: if (player_id != PJSUA_INVALID_ID) pjsua_player_destroy(player_id); if (writer_id != PJSUA_INVALID_ID) pjsua_recorder_destroy(writer_id); /* Wait until sound device closed before restoring back EC tail setting */ while (pjsua_snd_is_active()) pj_thread_sleep(10); pjsua_set_ec(last_ec_tail, 0); if (status != PJ_SUCCESS) { systest_perror("Sorry we encountered an error: ", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); } else if (key == KEY_YES) { ti->success = PJ_FALSE; if (!ti->success) { pj_ansi_strcpy(ti->reason, USER_ERROR); } } else { char msg[200]; pj_ansi_snprintf(msg, sizeof(msg), "Test succeeded.\r\n"); ti->success = PJ_TRUE; pj_ansi_strncpy(ti->reason, msg, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; } }
static void systest_latency_test(void) { const char *ref_wav_paths[] = { add_path(res_path, WAV_TOCK8_PATH), ALT_PATH1 WAV_TOCK8_PATH }; pj_str_t rec_wav_file; pjsua_player_id play_id = PJSUA_INVALID_ID; pjsua_conf_port_id play_slot = PJSUA_INVALID_ID; pjsua_recorder_id rec_id = PJSUA_INVALID_ID; pjsua_conf_port_id rec_slot = PJSUA_INVALID_ID; pj_pool_t *pool = NULL; pjmedia_port *wav_port = NULL; unsigned lat_sum=0, lat_cnt=0, lat_min=0, lat_max=0; enum gui_key key; test_item_t *ti; const char *title = "Audio Latency Test"; pj_status_t status; ti = systest_alloc_test_item(title); if (!ti) return; key = gui_msgbox(title, "This test will try to find the audio device's " "latency. We will play a special WAV file to the " "speaker for ten seconds, then at the end " "calculate the latency. Please don't do anything " "until the test is done.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } key = gui_msgbox(title, "For this test to work, we must be able to capture " "the audio played in the speaker (the echo), and only" " that audio (i.e. you must be in relatively quiet " "place to run this test). " "Press OK to start, or CANCEL to skip.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } PJ_LOG(3,(THIS_FILE, "Running %s", title)); status = create_player(PJ_ARRAY_SIZE(ref_wav_paths), ref_wav_paths, &play_id); if (status != PJ_SUCCESS) goto on_return; play_slot = pjsua_player_get_conf_port(play_id); rec_wav_file = pj_str(add_path(doc_path, WAV_LATENCY_OUT_PATH)); status = pjsua_recorder_create(&rec_wav_file, 0, NULL, -1, 0, &rec_id); if (status != PJ_SUCCESS) goto on_return; rec_slot = pjsua_recorder_get_conf_port(rec_id); /* Setup the test */ //status = pjsua_conf_connect(0, 0); status = pjsua_conf_connect(play_slot, 0); status = pjsua_conf_connect(0, rec_slot); status = pjsua_conf_connect(play_slot, rec_slot); /* We're running */ PJ_LOG(3,(THIS_FILE, "Please wait while test is running (~10 sec)")); gui_sleep(10); /* Done with the test */ //status = pjsua_conf_disconnect(0, 0); status = pjsua_conf_disconnect(play_slot, rec_slot); status = pjsua_conf_disconnect(0, rec_slot); status = pjsua_conf_disconnect(play_slot, 0); pjsua_recorder_destroy(rec_id); rec_id = PJSUA_INVALID_ID; pjsua_player_destroy(play_id); play_id = PJSUA_INVALID_ID; /* Confirm that echo is heard */ gui_msgbox(title, "Test is done. Now we need to confirm that we indeed " "captured the echo. We will play the captured audio " "and please confirm that you can hear the 'tock' echo.", WITH_OK); status = pjsua_player_create(&rec_wav_file, 0, &play_id); if (status != PJ_SUCCESS) goto on_return; play_slot = pjsua_player_get_conf_port(play_id); status = pjsua_conf_connect(play_slot, 0); if (status != PJ_SUCCESS) goto on_return; key = gui_msgbox(title, "The captured audio is being played back now. " "Can you hear the 'tock' echo?", WITH_YESNO); pjsua_player_destroy(play_id); play_id = PJSUA_INVALID_ID; if (key != KEY_YES) goto on_return; /* Now analyze the latency */ pool = pjsua_pool_create("latency", 512, 512); status = pjmedia_wav_player_port_create(pool, rec_wav_file.ptr, 0, 0, 0, &wav_port); if (status != PJ_SUCCESS) goto on_return; status = calculate_latency(pool, wav_port, &lat_sum, &lat_cnt, &lat_min, &lat_max); if (status != PJ_SUCCESS) goto on_return; on_return: if (wav_port) pjmedia_port_destroy(wav_port); if (pool) pj_pool_release(pool); if (play_id != PJSUA_INVALID_ID) pjsua_player_destroy(play_id); if (rec_id != PJSUA_INVALID_ID) pjsua_recorder_destroy(rec_id); if (status != PJ_SUCCESS) { systest_perror("Sorry we encountered an error: ", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); } else if (key != KEY_YES) { ti->success = PJ_FALSE; if (!ti->success) { pj_ansi_strcpy(ti->reason, USER_ERROR); } } else { char msg[200]; pj_size_t msglen; pj_ansi_snprintf(msg, sizeof(msg), "The sound device latency:\r\n" " Min=%u, Max=%u, Avg=%u\r\n", lat_min, lat_max, lat_sum/lat_cnt); msglen = strlen(msg); if (lat_sum/lat_cnt > 500) { pj_ansi_snprintf(msg+msglen, sizeof(msg)-msglen, "The latency is huge!\r\n"); msglen = strlen(msg); } else if (lat_sum/lat_cnt > 200) { pj_ansi_snprintf(msg+msglen, sizeof(msg)-msglen, "The latency is quite high\r\n"); msglen = strlen(msg); } key = gui_msgbox(title, msg, WITH_OK); ti->success = PJ_TRUE; pj_ansi_strncpy(ti->reason, msg, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; } }
/**************************************************************************** * test: audio system test */ static void systest_audio_test(void) { enum { GOOD_MAX_INTERVAL = 5, }; const pjmedia_dir dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; pjmedia_aud_param param; pjmedia_aud_test_results result; pj_size_t textbufpos; enum gui_key key; unsigned problem_count = 0; const char *problems[16]; char drifttext[120]; test_item_t *ti; const char *title = "Audio Device Test"; pj_status_t status; ti = systest_alloc_test_item(title); if (!ti) return; key = gui_msgbox(title, "This will run an automated test for about " "ten seconds or so, and display some " "statistics about your sound device. " "Please don't do anything until the test completes. " "Press OK to start, or CANCEL to skip this test.", WITH_OKCANCEL); if (key != KEY_OK) { ti->skipped = PJ_TRUE; return; } PJ_LOG(3,(THIS_FILE, "Running %s", title)); /* Disable sound device in pjsua first */ pjsua_set_no_snd_dev(); /* Setup parameters */ status = pjmedia_aud_dev_default_param(systest.play_id, ¶m); if (status != PJ_SUCCESS) { systest_perror("Sorry we had error in pjmedia_aud_dev_default_param()", status); pjsua_set_snd_dev(systest.rec_id, systest.play_id); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; return; } param.dir = dir; param.rec_id = systest.rec_id; param.play_id = systest.play_id; param.clock_rate = systest.media_cfg.snd_clock_rate; param.channel_count = systest.media_cfg.channel_count; param.samples_per_frame = param.clock_rate * param.channel_count * systest.media_cfg.audio_frame_ptime / 1000; /* Latency settings */ param.flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY); param.input_latency_ms = systest.media_cfg.snd_rec_latency; param.output_latency_ms = systest.media_cfg.snd_play_latency; /* Run the test */ status = pjmedia_aud_test(¶m, &result); if (status != PJ_SUCCESS) { systest_perror("Sorry we encountered error with the test", status); pjsua_set_snd_dev(systest.rec_id, systest.play_id); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; return; } /* Restore pjsua sound device */ pjsua_set_snd_dev(systest.rec_id, systest.play_id); /* Analyze the result! */ strcpy(textbuf, "Here are the audio statistics:\r\n"); textbufpos = strlen(textbuf); if (result.rec.frame_cnt==0) { problems[problem_count++] = "No audio frames were captured from the microphone. " "This means the audio device is not working properly."; } else { pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, "Rec : interval (min/max/avg/dev)=\r\n" " %u/%u/%u/%u (ms)\r\n" " max burst=%u\r\n", result.rec.min_interval, result.rec.max_interval, result.rec.avg_interval, result.rec.dev_interval, result.rec.max_burst); textbufpos = strlen(textbuf); if (result.rec.max_burst > GOOD_MAX_INTERVAL) { problems[problem_count++] = "Recording max burst is quite high"; } } if (result.play.frame_cnt==0) { problems[problem_count++] = "No audio frames were played to the speaker. " "This means the audio device is not working properly."; } else { pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, "Play: interval (min/max/avg/dev)=\r\n" " %u/%u/%u/%u (ms)\r\n" " burst=%u\r\n", result.play.min_interval, result.play.max_interval, result.play.avg_interval, result.play.dev_interval, result.play.max_burst); textbufpos = strlen(textbuf); if (result.play.max_burst > GOOD_MAX_INTERVAL) { problems[problem_count++] = "Playback max burst is quite high"; } } if (result.rec_drift_per_sec) { const char *which = result.rec_drift_per_sec>=0 ? "faster" : "slower"; unsigned drift = result.rec_drift_per_sec>=0 ? result.rec_drift_per_sec : -result.rec_drift_per_sec; pj_ansi_snprintf(drifttext, sizeof(drifttext), "Clock drifts detected. Capture " "is %d samples/sec %s " "than the playback device", drift, which); problems[problem_count++] = drifttext; } if (problem_count == 0) { pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, "\r\nThe sound device seems to be okay!"); textbufpos = strlen(textbuf); key = gui_msgbox("Audio Device Test", textbuf, WITH_OK); } else { unsigned i; pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, "There could be %d problem(s) with the " "sound device:\r\n", problem_count); textbufpos = strlen(textbuf); for (i=0; i<problem_count; ++i) { pj_ansi_snprintf(textbuf+textbufpos, sizeof(textbuf)-textbufpos, " %d: %s\r\n", i+1, problems[i]); textbufpos = strlen(textbuf); } key = gui_msgbox(title, textbuf, WITH_OK); } ti->success = PJ_TRUE; pj_ansi_strncpy(ti->reason, textbuf, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; }
static void systest_display_settings(void) { pjmedia_aud_dev_info di; pj_size_t len = 0; enum gui_key key; test_item_t *ti; const char *title = "Audio Settings"; pj_status_t status; ti = systest_alloc_test_item(title); if (!ti) return; PJ_LOG(3,(THIS_FILE, "Running %s", title)); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Version: %s\r\n", pj_get_version()); len = strlen(textbuf); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Test clock rate: %d\r\n", systest.media_cfg.clock_rate); len = strlen(textbuf); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Device clock rate: %d\r\n", systest.media_cfg.snd_clock_rate); len = strlen(textbuf); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Aud frame ptime: %d\r\n", systest.media_cfg.audio_frame_ptime); len = strlen(textbuf); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Channel count: %d\r\n", systest.media_cfg.channel_count); len = strlen(textbuf); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Audio switching: %s\r\n", (PJMEDIA_CONF_USE_SWITCH_BOARD ? "Switchboard" : "Conf bridge")); len = strlen(textbuf); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Snd buff count: %d\r\n", PJMEDIA_SOUND_BUFFER_COUNT); len = strlen(textbuf); /* Capture device */ status = pjmedia_aud_dev_get_info(systest.rec_id, &di); if (status != PJ_SUCCESS) { systest_perror("Error querying device info", status); ti->success = PJ_FALSE; pj_strerror(status, ti->reason, sizeof(ti->reason)); return; } pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Rec dev : %d (%s) [%s]\r\n", systest.rec_id, di.name, di.driver); len = strlen(textbuf); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Rec buf : %d msec\r\n", systest.media_cfg.snd_rec_latency); len = strlen(textbuf); /* Playback device */ status = pjmedia_aud_dev_get_info(systest.play_id, &di); if (status != PJ_SUCCESS) { systest_perror("Error querying device info", status); return; } pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Play dev: %d (%s) [%s]\r\n", systest.play_id, di.name, di.driver); len = strlen(textbuf); pj_ansi_snprintf(textbuf+len, sizeof(textbuf)-len, "Play buf: %d msec\r\n", systest.media_cfg.snd_play_latency); len = strlen(textbuf); ti->success = PJ_TRUE; pj_ansi_strncpy(ti->reason, textbuf, sizeof(ti->reason)); ti->reason[sizeof(ti->reason)-1] = '\0'; key = gui_msgbox(title, textbuf, WITH_OK); PJ_UNUSED_ARG(key); /* Warning about unused var */ }