diff --git a/deps/5249-pjlib.diff b/deps/5249-pjlib.diff new file mode 100644 index 00000000..d62e0ba1 --- /dev/null +++ b/deps/5249-pjlib.diff @@ -0,0 +1,452 @@ +Index: pjlib/src/pj/os_core_unix.c +=================================================================== +--- pjlib/src/pj/os_core_unix.c (revision 5249) ++++ pjlib/src/pj/os_core_unix.c (working copy) +@@ -37,6 +37,11 @@ + + #if defined(PJ_HAS_SEMAPHORE_H) && PJ_HAS_SEMAPHORE_H != 0 + # include ++# if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 ++# include ++# include ++# include ++# endif + #endif + + #include // getpid() +@@ -107,7 +112,11 @@ + #if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + struct pj_sem_t + { ++#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 ++ semaphore_t *sem; ++#else + sem_t *sem; ++#endif + char obj_name[PJ_MAX_OBJ_NAME]; + }; + #endif /* PJ_HAS_SEMAPHORE */ +@@ -1553,35 +1562,16 @@ + PJ_ASSERT_RETURN(sem, PJ_ENOMEM); + + #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 +- /* MacOS X doesn't support anonymous semaphore */ + { +- char sem_name[PJ_GUID_MAX_LENGTH+1]; +- pj_str_t nam; +- +- /* We should use SEM_NAME_LEN, but this doesn't seem to be +- * declared anywhere? The value here is just from trial and error +- * to get the longest name supported. +- */ +-# define MAX_SEM_NAME_LEN 23 +- +- /* Create a unique name for the semaphore. */ +- if (PJ_GUID_STRING_LENGTH <= MAX_SEM_NAME_LEN) { +- nam.ptr = sem_name; +- pj_generate_unique_string(&nam); +- sem_name[nam.slen] = '\0'; +- } else { +- pj_create_random_string(sem_name, MAX_SEM_NAME_LEN); +- sem_name[MAX_SEM_NAME_LEN] = '\0'; +- } +- +- /* Create semaphore */ +- sem->sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, +- initial); +- if (sem->sem == SEM_FAILED) +- return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +- +- /* And immediately release the name as we don't need it */ +- sem_unlink(sem_name); ++ kern_return_t err; ++ sem->sem = PJ_POOL_ALLOC_T(pool, semaphore_t); ++ err = semaphore_create(mach_task_self(), sem->sem, SYNC_POLICY_FIFO, initial); ++ if (err != KERN_SUCCESS) { ++ if (err == KERN_RESOURCE_SHORTAGE) ++ return PJ_RETURN_OS_ERROR(ENOMEM); ++ else ++ return PJ_RETURN_OS_ERROR(EINVAL); ++ } + } + #else + sem->sem = PJ_POOL_ALLOC_T(pool, sem_t); +@@ -1617,6 +1607,7 @@ + { + #if PJ_HAS_THREADS + int result; ++ int error; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); +@@ -1624,6 +1615,20 @@ + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", + pj_thread_this()->obj_name)); + ++#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 ++ { ++ do ++ result = semaphore_wait(*(sem->sem)); ++ while (result == KERN_ABORTED); ++ ++ if (result == KERN_SUCCESS) { ++ result = error = 0; ++ } else { ++ result = -1; ++ error = EINVAL; ++ } ++ } ++#else + result = sem_wait( sem->sem ); + + if (result == 0) { +@@ -1632,12 +1637,14 @@ + } else { + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); ++ error = pj_get_native_os_error(); + } ++#endif + + if (result == 0) + return PJ_SUCCESS; + else +- return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); ++ return PJ_RETURN_OS_ERROR(error); + #else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +@@ -1651,20 +1658,45 @@ + { + #if PJ_HAS_THREADS + int result; ++ int error; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + ++#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 ++ { ++ mach_timespec_t interval; ++ kern_return_t err; ++ ++ interval.tv_sec = 0; ++ interval.tv_nsec = 0; ++ ++ err = semaphore_timedwait(*(sem->sem), interval); ++ if (err == KERN_SUCCESS) { ++ result = error = 0; ++ } else if (err == KERN_OPERATION_TIMED_OUT) { ++ result = -1; ++ error = EAGAIN; ++ } else { ++ result = -1; ++ error = EINVAL; ++ } ++ } ++#else + result = sem_trywait( sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); ++ } else { ++ error = pj_get_native_os_error(); + } ++#endif ++ + if (result == 0) + return PJ_SUCCESS; + else +- return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); ++ return PJ_RETURN_OS_ERROR(error); + #else + pj_assert( sem == (pj_sem_t*)1 ); + return PJ_SUCCESS; +@@ -1678,14 +1710,30 @@ + { + #if PJ_HAS_THREADS + int result; ++ int error; + PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", + pj_thread_this()->obj_name)); ++#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 ++ { ++ kern_return_t err; ++ err = semaphore_signal(*(sem->sem)); ++ if (err == KERN_SUCCESS) { ++ result = error = 0; ++ } else { ++ result = -1; ++ error = EINVAL; ++ } ++ } ++#else + result = sem_post( sem->sem ); ++ if (result != 0) ++ error = pj_get_native_os_error(); ++#endif + + if (result == 0) + return PJ_SUCCESS; + else +- return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); ++ return PJ_RETURN_OS_ERROR(error); + #else + pj_assert( sem == (pj_sem_t*) 1); + return PJ_SUCCESS; +@@ -1699,6 +1747,7 @@ + { + #if PJ_HAS_THREADS + int result; ++ int error; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); +@@ -1706,15 +1755,26 @@ + PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", + pj_thread_this()->obj_name)); + #if defined(PJ_DARWINOS) && PJ_DARWINOS!=0 +- result = sem_close( sem->sem ); ++ { ++ kern_return_t err; ++ err = semaphore_destroy(mach_task_self(), *(sem->sem)); ++ if (err == KERN_SUCCESS) { ++ result = error = -1; ++ } else { ++ result = -1; ++ error = EINVAL; ++ } ++ } + #else + result = sem_destroy( sem->sem ); ++ if (result != 0) ++ error = pj_get_native_os_error(); + #endif + + if (result == 0) + return PJ_SUCCESS; + else +- return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); ++ return PJ_RETURN_OS_ERROR(error); + #else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +Index: pjlib/src/pj/ssl_sock_ossl.c +=================================================================== +--- pjlib/src/pj/ssl_sock_ossl.c (revision 5249) ++++ pjlib/src/pj/ssl_sock_ossl.c (working copy) +@@ -43,6 +43,7 @@ + /* + * Include OpenSSL headers + */ ++#include + #include + #include + #include +@@ -49,9 +50,24 @@ + #include + + ++#if !USING_LIBRESSL && OPENSSL_VERSION_NUMBER >= 0x10100000L ++# define OPENSSL_NO_SSL2 /* seems to be removed in 1.1.0 */ ++# define M_ASN1_STRING_data(x) ASN1_STRING_get0_data(x) ++# define M_ASN1_STRING_length(x) ASN1_STRING_length(x) ++# if defined(OPENSSL_API_COMPAT) && OPENSSL_API_COMPAT >= 0x10100000L ++# define X509_get_notBefore(x) X509_get0_notBefore(x) ++# define X509_get_notAfter(x) X509_get0_notAfter(x) ++# endif ++#else ++# define SSL_CIPHER_get_id(c) (c)->id ++# define SSL_set_session(ssl, s) (ssl)->session = (s) ++#endif ++ ++ + #ifdef _MSC_VER + # pragma comment( lib, "libeay32") + # pragma comment( lib, "ssleay32") ++# pragma comment( lib, "crypt32") + #endif + + +@@ -174,6 +190,7 @@ + send_buf_t send_buf; + write_data_t send_pending; /* list of pending write to network */ + pj_lock_t *write_mutex; /* protect write BIO and send_buf */ ++ pj_lock_t *state_mutex; /* protect the socket state (sending on one thread, destroying it in another */ + + SSL_CTX *ossl_ctx; + SSL *ossl_ssl; +@@ -318,8 +335,12 @@ + pj_assert(status == PJ_SUCCESS); + + /* Init OpenSSL lib */ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_library_init(); + SSL_load_error_strings(); ++#else ++ OPENSSL_init_ssl(0, NULL); ++#endif + #if OPENSSL_VERSION_NUMBER < 0x009080ffL + /* This is now synonym of SSL_library_init() */ + OpenSSL_add_all_algorithms(); +@@ -333,6 +354,7 @@ + STACK_OF(SSL_CIPHER) *sk_cipher; + unsigned i, n; + ++#if OPENSSL_VERSION_NUMBER < 0x10100000L + meth = (SSL_METHOD*)SSLv23_server_method(); + if (!meth) + meth = (SSL_METHOD*)TLSv1_server_method(); +@@ -344,6 +366,12 @@ + if (!meth) + meth = (SSL_METHOD*)SSLv2_server_method(); + #endif ++ ++#else ++ /* Specific version methods are deprecated in 1.1.0 */ ++ meth = (SSL_METHOD*)TLS_method(); ++#endif ++ + pj_assert(meth); + + ctx=SSL_CTX_new(meth); +@@ -360,7 +388,7 @@ + const SSL_CIPHER *c; + c = sk_SSL_CIPHER_value(sk_cipher,i); + openssl_ciphers[i].id = (pj_ssl_cipher) +- (pj_uint32_t)c->id & 0x00FFFFFF; ++ (pj_uint32_t)SSL_CIPHER_get_id(c) & 0x00FFFFFF; + openssl_ciphers[i].name = SSL_CIPHER_get_name(c); + } + +@@ -525,6 +553,7 @@ + ssock->param.proto = PJ_SSL_SOCK_PROTO_SSL23; + + /* Determine SSL method to use */ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L + switch (ssock->param.proto) { + case PJ_SSL_SOCK_PROTO_TLS1: + ssl_method = (SSL_METHOD*)TLSv1_method(); +@@ -540,6 +569,10 @@ + #endif + break; + } ++#else ++ /* Specific version methods are deprecated in 1.1.0 */ ++ ssl_method = (SSL_METHOD*)TLS_method(); ++#endif + + if (!ssl_method) { + ssl_method = (SSL_METHOD*)SSLv23_method(); +@@ -810,6 +843,8 @@ + /* Reset SSL socket state */ + static void reset_ssl_sock_state(pj_ssl_sock_t *ssock) + { ++ pj_lock_acquire(ssock->state_mutex); ++ + ssock->ssl_state = SSL_STATE_NULL; + + destroy_ssl(ssock); +@@ -831,6 +866,8 @@ + * For now, just clear thread error queue here. + */ + ERR_clear_error(); ++ ++ pj_lock_release(ssock->state_mutex); + } + + +@@ -864,7 +901,8 @@ + const SSL_CIPHER *c; + c = sk_SSL_CIPHER_value(sk_cipher, j); + if (ssock->param.ciphers[i] == (pj_ssl_cipher) +- ((pj_uint32_t)c->id & 0x00FFFFFF)) ++ ((pj_uint32_t)SSL_CIPHER_get_id(c) & ++ 0x00FFFFFF)) + { + const char *c_name; + +@@ -989,7 +1027,7 @@ + pj_bool_t update_needed; + char buf[512]; + pj_uint8_t serial_no[64] = {0}; /* should be >= sizeof(ci->serial_no) */ +- pj_uint8_t *q; ++ const pj_uint8_t *q; + unsigned len; + GENERAL_NAMES *names = NULL; + +@@ -999,7 +1037,7 @@ + X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof(buf)); + + /* Get serial no */ +- q = (pj_uint8_t*) M_ASN1_STRING_data(X509_get_serialNumber(x)); ++ q = (const pj_uint8_t*) M_ASN1_STRING_data(X509_get_serialNumber(x)); + len = M_ASN1_STRING_length(X509_get_serialNumber(x)); + if (len > sizeof(ci->serial_no)) + len = sizeof(ci->serial_no); +@@ -1070,8 +1108,8 @@ + type = PJ_SSL_CERT_NAME_URI; + break; + case GEN_IPADD: +- p = ASN1_STRING_data(name->d.ip); +- len = ASN1_STRING_length(name->d.ip); ++ p = (unsigned char*)M_ASN1_STRING_data(name->d.ip); ++ len = M_ASN1_STRING_length(name->d.ip); + type = PJ_SSL_CERT_NAME_IP; + break; + default: +@@ -2200,6 +2238,12 @@ + if (status != PJ_SUCCESS) + return status; + ++ /* Create socket state mutex */ ++ status = pj_lock_create_recursive_mutex(pool, pool->obj_name, ++ &ssock->state_mutex); ++ if (status != PJ_SUCCESS) ++ return status; ++ + /* Init secure socket param */ + pj_ssl_sock_param_copy(pool, &ssock->param, param); + ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3; +@@ -2231,6 +2275,7 @@ + + reset_ssl_sock_state(ssock); + pj_lock_destroy(ssock->write_mutex); ++ pj_lock_destroy(ssock->state_mutex); + + pool = ssock->pool; + ssock->pool = NULL; +@@ -2288,7 +2333,7 @@ + + /* Current cipher */ + cipher = SSL_get_current_cipher(ssock->ossl_ssl); +- info->cipher = (cipher->id & 0x00FFFFFF); ++ info->cipher = (SSL_CIPHER_get_id(cipher) & 0x00FFFFFF); + + /* Remote address */ + pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr); +@@ -2561,9 +2606,13 @@ + + PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL); + +- if (ssock->ssl_state != SSL_STATE_ESTABLISHED) +- return PJ_EINVALIDOP; ++ pj_lock_acquire(ssock->state_mutex); + ++ if (ssock->ssl_state != SSL_STATE_ESTABLISHED) { ++ status = PJ_EINVALIDOP; ++ goto on_return; ++ } ++ + // Ticket #1573: Don't hold mutex while calling PJLIB socket send(). + //pj_lock_acquire(ssock->write_mutex); + +@@ -2588,6 +2637,7 @@ + + on_return: + //pj_lock_release(ssock->write_mutex); ++ pj_lock_release(ssock->state_mutex); + return status; + } + diff --git a/deps/5249-pjmedia.diff b/deps/5249-pjmedia.diff new file mode 100644 index 00000000..64d45efa --- /dev/null +++ b/deps/5249-pjmedia.diff @@ -0,0 +1,6667 @@ +Index: pjmedia/build/Makefile +=================================================================== +--- pjmedia/build/Makefile (revision 5249) ++++ pjmedia/build/Makefile (working copy) +@@ -3,11 +3,12 @@ + + THIRD_PARTY:=$(PJDIR)/third_party + ++SRTP_INC=$(CC_INC)$(THIRD_PARTY)/build/srtp \ ++ $(CC_INC)$(THIRD_PARTY)/srtp/crypto/include \ ++ $(CC_INC)$(THIRD_PARTY)/srtp/include ++ + include $(PJDIR)/build/common.mak + +-export LIBDIR := ../lib +-export BINDIR := ../bin +- + RULES_MAK := $(PJDIR)/build/rules.mak + + PJLIB_LIB:=$(PJDIR)/pjlib/lib/libpj-$(TARGET_NAME)$(LIBEXT) +@@ -14,27 +15,13 @@ + PJLIB_UTIL_LIB:=$(PJDIR)/pjlib-util/lib/libpjlib-util-$(TARGET_NAME)$(LIBEXT) + PJNATH_LIB:=$(PJDIR)/pjnath/lib/libpjnath-$(TARGET_NAME)$(LIBEXT) + +-export PJMEDIA_LIB:=libpjmedia-$(TARGET_NAME)$(LIBEXT) +-export PJMEDIA_CODEC_LIB:=libpjmedia-codec-$(TARGET_NAME)$(LIBEXT) +-export PJSDP_LIB:=libpjsdp-$(TARGET_NAME)$(LIBEXT) +-export PJMEDIA_AUDIODEV_LIB:=libpjmedia-audiodev-$(TARGET_NAME)$(LIBEXT) +-export PJMEDIA_VIDEODEV_LIB:=libpjmedia-videodev-$(TARGET_NAME)$(LIBEXT) ++export PJMEDIA_LIB:=../lib/libpjmedia-$(TARGET_NAME)$(LIBEXT) ++export PJMEDIA_CODEC_LIB:=../lib/libpjmedia-codec-$(TARGET_NAME)$(LIBEXT) ++export PJSDP_LIB:=../lib/libpjsdp-$(TARGET_NAME)$(LIBEXT) ++export PJMEDIA_AUDIODEV_LIB:=../lib/libpjmedia-audiodev-$(TARGET_NAME)$(LIBEXT) ++export PJMEDIA_VIDEODEV_LIB:=../lib/libpjmedia-videodev-$(TARGET_NAME)$(LIBEXT) + +-ifeq ($(PJ_SHARED_LIBRARIES),) +-else +-export PJMEDIA_SONAME:=libpjmedia.$(SHLIB_SUFFIX) +-export PJMEDIA_SHLIB:=$(PJMEDIA_SONAME).$(PJ_VERSION_MAJOR) +-export PJMEDIA_CODEC_SONAME:=libpjmedia-codec.$(SHLIB_SUFFIX) +-export PJMEDIA_CODEC_SHLIB:=$(PJMEDIA_CODEC_SONAME).$(PJ_VERSION_MAJOR) +-export PJSDP_SONAME:=libpjsdp.$(SHLIB_SUFFIX) +-export PJSDP_SHLIB:=$(PJSDP_SONAME).$(PJ_VERSION_MAJOR) +-export PJMEDIA_AUDIODEV_SONAME:=libpjmedia-audiodev.$(SHLIB_SUFFIX) +-export PJMEDIA_AUDIODEV_SHLIB:=$(PJMEDIA_AUDIODEV_SONAME).$(PJ_VERSION_MAJOR) +-export PJMEDIA_VIDEODEV_SONAME:=libpjmedia-videodev.$(SHLIB_SUFFIX) +-export PJMEDIA_VIDEODEV_SHLIB:=$(PJMEDIA_VIDEODEV_SONAME).$(PJ_VERSION_MAJOR) +-endif + +- + ############################################################################### + # Gather all flags. + # +@@ -44,14 +31,22 @@ + $(CC_INC)../../pjlib-util/include \ + $(CC_INC)../../pjmedia/include \ + $(CC_INC)../../pjnath/include \ +- $(CC_INC)../.. ++ $(CC_INC)../.. \ ++ $(SRTP_INC) + export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ + $(HOST_CXXFLAGS) $(CXXFLAGS) +- +-export _LDFLAGS := $(APP_THIRD_PARTY_LIBS) \ ++export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJMEDIA_VIDEODEV_LIB)) \ ++ $(subst /,$(HOST_PSEP),$(PJMEDIA_CODEC_LIB)) \ ++ $(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \ ++ $(subst /,$(HOST_PSEP),$(PJMEDIA_AUDIODEV_LIB)) \ ++ $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \ ++ $(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \ ++ $(subst /,$(HOST_PSEP),$(PJNATH_LIB)) \ ++ -L$(PJDIR)/third_party/lib \ ++ $(APP_THIRD_PARTY_LIBS) \ + $(APP_THIRD_PARTY_EXT) \ + $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ +- $(APP_LDFLAGS) $(LDFLAGS) ++ $(LDFLAGS) + + ############################################################################### + # Defines for building PJMEDIA library +@@ -62,16 +57,16 @@ + bidirectional.o clock_thread.o codec.o conference.o \ + conf_switch.o converter.o converter_libswscale.o converter_libyuv.o \ + delaybuf.o echo_common.o \ +- echo_port.o echo_suppress.o echo_webrtc.o endpoint.o errno.o \ ++ echo_port.o echo_suppress.o endpoint.o errno.o \ + event.o format.o ffmpeg_util.o \ + g711.o jbuf.o master_port.o mem_capture.o mem_player.o \ +- null_port.o plc_common.o port.o splitcomb.o \ +- resample_resample.o resample_libsamplerate.o resample_speex.o \ ++ mixer_port.o null_port.o plc_common.o port.o splitcomb.o \ ++ resample_resample.o resample_libsamplerate.o \ + resample_port.o rtcp.o rtcp_xr.o rtp.o \ + sdp.o sdp_cmp.o sdp_neg.o session.o silencedet.o \ + sound_legacy.o sound_port.o stereo_port.o stream_common.o \ + stream.o stream_info.o tonegen.o transport_adapter_sample.o \ +- transport_ice.o transport_loop.o transport_srtp.o transport_udp.o \ ++ transport_ice.o transport_loop.o transport_srtp.o transport_zrtp.o transport_udp.o \ + types.o vid_codec.o vid_codec_util.o \ + vid_port.o vid_stream.o vid_stream_info.o vid_tee.o \ + wav_player.o wav_playlist.o wav_writer.o wave.o \ +@@ -78,13 +73,6 @@ + wsola.o + + export PJMEDIA_CFLAGS += $(_CFLAGS) +-export PJMEDIA_CXXFLAGS += $(_CXXFLAGS) +-export PJMEDIA_LDFLAGS += $(PJMEDIA_VIDEODEV_LDLIB) \ +- $(PJMEDIA_AUDIODEV_LDLIB) \ +- $(PJLIB_LDLIB) \ +- $(PJLIB_UTIL_LDLIB) \ +- $(PJNATH_LDLIB) \ +- $(_LDFLAGS) + + + ############################################################################### +@@ -91,14 +79,8 @@ + # Defines for building PJMEDIA-AUDIODEV library + # + export PJMEDIA_AUDIODEV_SRCDIR = ../src/pjmedia-audiodev +-export PJMEDIA_AUDIODEV_OBJS += audiodev.o audiotest.o errno.o \ +- legacy_dev.o null_dev.o pa_dev.o wmme_dev.o \ +- alsa_dev.o bb10_dev.o bdimad_dev.o \ +- android_jni_dev.o opensl_dev.o ++export PJMEDIA_AUDIODEV_OBJS += audiodev.o errno.o null_dev.o wmme_dev.o alsa_dev.o + export PJMEDIA_AUDIODEV_CFLAGS += $(_CFLAGS) +-export PJMEDIA_AUDIODEV_CXXFLAGS += $(_CXXFLAGS) +-export PJMEDIA_AUDIODEV_LDFLAGS += $(PJLIB_LDLIB) \ +- $(_LDFLAGS) + + + ############################################################################### +@@ -105,13 +87,9 @@ + # Defines for building PJMEDIA-VIDEODEV library + # + export PJMEDIA_VIDEODEV_SRCDIR = ../src/pjmedia-videodev +-export PJMEDIA_VIDEODEV_OBJS += errno.o videodev.o avi_dev.o ffmpeg_dev.o \ +- colorbar_dev.o v4l2_dev.o opengl_dev.o \ +- util.o ++export PJMEDIA_VIDEODEV_OBJS += errno.o videodev.o avi_dev.o colorbar_dev.o v4l2_dev.o fb_dev.o null_dev.o util.o + export PJMEDIA_VIDEODEV_CFLAGS += $(_CFLAGS) + export PJMEDIA_VIDEODEV_CXXFLAGS += $(_CXXFLAGS) +-export PJMEDIA_VIDEODEV_LDFLAGS += $(PJLIB_LDLIB) \ +- $(_LDFLAGS) + + + ############################################################################### +@@ -124,11 +102,6 @@ + export PJSDP_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ + errno.o sdp.o sdp_cmp.o sdp_neg.o + export PJSDP_CFLAGS += $(_CFLAGS) +-export PJSDP_CXXFLAGS += $(_CXXFLAGS) +-export PJSDP_LDFLAGS += $(PJMEDIA_LDLIB) \ +- $(PJLIB_LDLIB) \ +- $(PJLIB_UTIL_LDLIB) \ +- $(_LDFLAGS) + + + ############################################################################### +@@ -136,18 +109,16 @@ + # + export PJMEDIA_CODEC_SRCDIR = ../src/pjmedia-codec + export PJMEDIA_CODEC_OBJS += audio_codecs.o ffmpeg_vid_codecs.o openh264.o \ +- h263_packetizer.o h264_packetizer.o \ ++ h263_packetizer.o h264_packetizer.o vpx.o \ + $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ +- ipp_codecs.o silk.o opus.o $(CODEC_OBJS) \ +- g7221_sdp_match.o amr_sdp_match.o ++ $(CODEC_OBJS) \ ++ g7221_sdp_match.o + export PJMEDIA_CODEC_CFLAGS += $(_CFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \ + $(ILBC_CFLAGS) $(IPP_CFLAGS) $(G7221_CFLAGS) + export PJMEDIA_CODEC_CXXFLAGS += $(_CXXFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \ + $(ILBC_CFLAGS) $(IPP_CFLAGS) $(G7221_CFLAGS) +-export PJMEDIA_CODEC_LDFLAGS += $(PJMEDIA_LDLIB) \ +- $(PJLIB_LDLIB) \ +- $(_LDFLAGS) + ++ + ############################################################################### + # Defines for building test application + # +@@ -157,16 +128,8 @@ + rtp_test.o test.o + export PJMEDIA_TEST_OBJS += sdp_neg_test.o + export PJMEDIA_TEST_CFLAGS += $(_CFLAGS) +-export PJMEDIA_TEST_CXXFLAGS += $(_CXXFLAGS) +-export PJMEDIA_TEST_LDFLAGS += $(PJMEDIA_CODEC_LDLIB) \ +- $(PJMEDIA_VIDEODEV_LDLIB) \ +- $(PJMEDIA_LDLIB) \ +- $(PJMEDIA_AUDIODEV_LDLIB) \ +- $(PJLIB_LDLIB) \ +- $(PJLIB_UTIL_LDLIB) \ +- $(PJNATH_LDLIB) \ +- $(_LDFLAGS) +-export PJMEDIA_TEST_EXE:=pjmedia-test-$(TARGET_NAME)$(HOST_EXE) ++export PJMEDIA_TEST_LDFLAGS += $(_LDFLAGS) ++export PJMEDIA_TEST_EXE:=../bin/pjmedia-test-$(TARGET_NAME)$(HOST_EXE) + + + export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT +@@ -175,17 +138,10 @@ + # + # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. + # +-TARGETS := $(PJMEDIA_LIB) $(PJMEDIA_SONAME) \ +- $(PJMEDIA_CODEC_LIB) $(PJMEDIA_CODEC_SONAME)\ +- $(PJMEDIA_VIDEODEV_LIB) $(PJMEDIA_VIDEODEV_SONAME) \ +- $(PJMEDIA_AUDIODEV_LIB) $(PJMEDIA_AUDIODEV_SONAME) \ +- $(PJSDP_LIB) $(PJSDP_SONAME) +-TARGETS_EXE := $(PJMEDIA_TEST_EXE) ++TARGETS := pjmedia pjmedia-videodev pjmedia-audiodev pjmedia-codec pjsdp + +-all: $(TARGETS) $(TARGETS_EXE) ++all: $(TARGETS) + +-lib: $(TARGETS) +- + doc: + cd .. && rm -rf docs/$(PJ_VERSION) && doxygen docs/doxygen.cfg + @if [ -n "$(WWWDIR)" ] && ! [ -d "$(WWWDIR)/docs/$(PJ_VERSION)/pjmedia/docs/html" ] ; then \ +@@ -200,61 +156,41 @@ + dep: depend + distclean: realclean + +-.PHONY: all dep depend clean realclean distclean +-.PHONY: $(TARGETS) +-.PHONY: $(PJMEDIA_LIB) $(PJMEDIA_SONAME) +-.PHONY: $(PJMEDIA_CODEC_LIB) $(PJMEDIA_CODEC_SONAME) +-.PHONY: $(PJMEDIA_VIDEODEV_LIB) $(PJMEDIA_VIDEODEV_SONAME) +-.PHONY: $(PJMEDIA_AUDIODEV_LIB) $(PJMEDIA_AUDIODEV_SONAME) +-.PHONY: $(PJSDP_LIB) $(PJSDP_SONAME) +-.PHONY: $(PJMEDIA_TEST_EXE) ++.PHONY: dep depend pjmedia pjmedia-codec pjmedia-videodev pjmedia-audiodev pjmedia-test clean realclean distclean + +-pjmedia: $(PJMEDIA_LIB) +-$(PJMEDIA_SONAME): $(PJMEDIA_LIB) +-$(PJMEDIA_LIB) $(PJMEDIA_SONAME): $(PJMEDIA_AUDIODEV_LIB) $(PJMEDIA_AUDIODEV_SONAME) $(PJMEDIA_VIDEODEV_LIB) $(PJMEDIA_VIDEODEV_SONAME) +- $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $(subst /,$(HOST_PSEP),$(LIBDIR)/$@) ++pjmedia: ++ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $(PJMEDIA_LIB) + +-pjmedia-codec: $(PJMEDIA_CODEC_LIB) +-$(PJMEDIA_CODEC_SONAME): $(PJMEDIA_CODEC_LIB) +-$(PJMEDIA_CODEC_LIB) $(PJMEDIA_CODEC_SONAME): $(PJMEDIA_LIB) $(PJMEDIA_SONAME) +- $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $(subst /,$(HOST_PSEP),$(LIBDIR)/$@) ++pjmedia-codec: ++ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $(PJMEDIA_CODEC_LIB) + +-pjmedia-videodev: $(PJMEDIA_VIDEODEV_LIB) +-$(PJMEDIA_VIDEODEV_SONAME): $(PJMEDIA_VIDEODEV_LIB) +-$(PJMEDIA_VIDEODEV_LIB) $(PJMEDIA_VIDEODEV_SONAME): +- $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $(subst /,$(HOST_PSEP),$(LIBDIR)/$@) ++pjmedia-videodev: ++ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $(PJMEDIA_VIDEODEV_LIB) + +-pjmedia-audiodev: $(PJMEDIA_AUDIODEV_LIB) +-$(PJMEDIA_AUDIODEV_SONAME): $(PJMEDIA_AUDIODEV_LIB) +-$(PJMEDIA_AUDIODEV_LIB) $(PJMEDIA_AUDIODEV_SONAME): +- $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $(subst /,$(HOST_PSEP),$(LIBDIR)/$@) ++pjmedia-audiodev: ++ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $(PJMEDIA_AUDIODEV_LIB) + +-pjsdp: $(PJSDP_LIB) +-$(PJSDP_SONAME): $(PJSDP_LIB) +-$(PJSDP_LIB) $(PJSDP_SONAME): $(PJMEDIA_LIB) $(PJMEDIA_SONAME) +- $(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $(LIBDIR)/$@ ++pjsdp: ++ $(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $(PJSDP_LIB) + +-pjmedia-test: $(PJMEDIA_TEST_EXE) +-$(PJMEDIA_TEST_EXE): $(PJMEDIA_LIB) $(PJMEDIA_SONAME) +-$(PJMEDIA_TEST_EXE): $(PJMEDIA_AUDIODEV_LIB) $(PJMEDIA_AUDIODEV_SONAME) +-$(PJMEDIA_TEST_EXE): $(PJMEDIA_VIDEODEV_LIB) $(PJMEDIA_VIDEODEV_SONAME) +-$(PJMEDIA_TEST_EXE): $(PJMEDIA_CODEC_LIB) $(PJMEDIA_CODEC_SONAME) +-$(PJMEDIA_TEST_EXE): +- $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $(BINDIR)/$@ ++$(PJMEDIA_LIB): pjmedia + +-.PHONY: pjmedia.ko +-pjmedia.ko: ++pjmedia-test: $(PJMEDIA_LIB) pjmedia ++ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $(PJMEDIA_TEST_EXE) ++ ++.PHONY: ../lib/pjmedia.ko ++../lib/pjmedia.ko: + echo Making $@ +- $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $(LIBDIR)/$@ ++ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@ + +-.PHONY: pjmedia-codec.ko +-pjmedia-codec.ko: ++.PHONY: ../lib/pjmedia-codec.ko ++../lib/pjmedia-codec.ko: + echo Making $@ +- $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $(LIBDIR)/$@ ++ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@ + +-.PHONY: pjmedia-test.ko +-pjmedia-test.ko: +- $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $(LIBDIR)/$@ ++.PHONY: ../lib/pjmedia-test.ko ++../lib/pjmedia-test.ko: ++ $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@ + + clean: + $(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@ +@@ -286,6 +222,6 @@ + $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@ + $(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@ + $(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@ +- echo '$(BINDIR)/$(PJMEDIA_TEST_EXE): $(LIBDIR)/$(PJMEDIA_LIB) $(LIBDIR)/$(PJMEDIA_CODEC_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjmedia-test-$(TARGET_NAME).depend ++ echo '$(PJMEDIA_TEST_EXE): $(PJMEDIA_LIB) $(PJMEDIA_CODEC_LIB) $(PJNATH_LIB) $(PJLIB_UTIL_LIB) $(PJLIB_LIB)' >> .pjmedia-test-$(TARGET_NAME).depend + + +Index: pjmedia/build/os-auto.mak.in +=================================================================== +--- pjmedia/build/os-auto.mak.in (revision 5249) ++++ pjmedia/build/os-auto.mak.in (working copy) +@@ -7,27 +7,29 @@ + # - android_os + AC_PJMEDIA_VIDEO = @ac_pjmedia_video@ + +-# SDL flags +-SDL_CFLAGS = @ac_sdl_cflags@ +-SDL_LDFLAGS = @ac_sdl_ldflags@ +- + # FFMPEG dlags + FFMPEG_CFLAGS = @ac_ffmpeg_cflags@ + FFMPEG_LDFLAGS = @ac_ffmpeg_ldflags@ + ++# VPX flags ++VPX_CFLAGS = @ac_vpx_cflags@ ++VPX_LDFLAGS = @ac_vpx_ldflags@ ++ + # Video4Linux2 + V4L2_CFLAGS = @ac_v4l2_cflags@ + V4L2_LDFLAGS = @ac_v4l2_ldflags@ + +-# QT +-AC_PJMEDIA_VIDEO_HAS_QT = @ac_pjmedia_video_has_qt@ +-QT_CFLAGS = @ac_qt_cflags@ ++# AVF ++AC_PJMEDIA_VIDEO_HAS_AVF = @ac_pjmedia_video_has_avf@ ++AVF_CFLAGS = @ac_avf_cflags@ + + # iOS + IOS_CFLAGS = @ac_ios_cflags@ + +-# Android +-ANDROID_CFLAGS = @ac_android_cflags@ ++# Dshow ++AC_PJMEDIA_VIDEO_HAS_DSHOW = @ac_pjmedia_video_has_dshow@ ++DSHOW_CFLAGS = @ac_dshow_cflags@ ++DSHOW_LDFLAGS = @ac_dshow_ldflags@ + + # libyuv + LIBYUV_CFLAGS = @ac_libyuv_cflags@ +@@ -37,75 +39,26 @@ + OPENH264_CFLAGS = @ac_openh264_cflags@ + OPENH264_LDFLAGS = @ac_openh264_ldflags@ + +-# WebRtc +-WEBRTC_CFLAGS = @ac_webrtc_cflags@ +-WEBRTC_LDFLAGS = @ac_webrtc_ldflags@ +- +- + # PJMEDIA features exclusion + export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_speex_aec@ \ +- $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \ +- $(IOS_CFLAGS) $(ANDROID_CFLAGS) $(LIBYUV_CFLAGS) \ +- $(OPENH264_CFLAGS) $(WEBRTC_CFLAGS) +-export LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) \ +- $(LIBYUV_LDFLAGS) $(OPENH264_LDFLAGS) $(WEBRTC_LDFLAGS) ++ $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(AVF_CFLAGS) \ ++ $(IOS_CFLAGS) $(DSHOW_CFLAGS) $(LIBYUV_CFLAGS) $(OPENH264_CFLAGS) \ ++ $(VPX_CFLAGS) ++export LDFLAGS += $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) $(DSHOW_LDFLAGS) \ ++ $(LIBYUV_LDFLAGS) $(OPENH264_LDFLAGS) $(VPX_LDFLAGS) + +-# Define the desired sound device backend +-# Valid values are: +-# - pa_unix: PortAudio on Unix (OSS or ALSA) +-# - pa_darwinos: PortAudio on MacOSX (CoreAudio) +-# - pa_old_darwinos: PortAudio on MacOSX (old CoreAudio, for OSX 10.2) +-# - pa_win32: PortAudio on Win32 (WMME) +-# - win32: Win32 MME (wmme_dev.c) +-# - coreaudio: MaxOSX CoreAudio (coreaudio_dev.m) +-# - alsa: Unix ALSA (alsa_dev.c) +-# - null: Null sound device (nullsound.c) +-# - external: Link with no sounddev (app will provide) +-AC_PJMEDIA_SND=@ac_pjmedia_snd@ + +-# For Unix, specify if ALSA should be supported +-AC_PA_USE_ALSA=@ac_pa_use_alsa@ +- +-# Additional PortAudio CFLAGS are in @ac_pa_cflags@ +- + # + # Codecs + # +-AC_NO_G711_CODEC=@ac_no_g711_codec@ +-AC_NO_L16_CODEC=@ac_no_l16_codec@ +-AC_NO_GSM_CODEC=@ac_no_gsm_codec@ +-AC_NO_SPEEX_CODEC=@ac_no_speex_codec@ +-AC_NO_ILBC_CODEC=@ac_no_ilbc_codec@ +-AC_NO_G722_CODEC=@ac_no_g722_codec@ + AC_NO_G7221_CODEC=@ac_no_g7221_codec@ +-AC_NO_OPENCORE_AMRNB=@ac_no_opencore_amrnb@ +-AC_NO_OPENCORE_AMRWB=@ac_no_opencore_amrwb@ + + export CODEC_OBJS= + +-export PJMEDIA_AUDIODEV_OBJS += @ac_pjmedia_audiodev_objs@ ++export PJMEDIA_AUDIODEV_OBJS += @ac_pjmedia_audiodev_objs@ + +-ifeq ($(AC_NO_G711_CODEC),1) +-export CFLAGS += -DPJMEDIA_HAS_G711_CODEC=0 +-else +-export CODEC_OBJS += +-endif +- +-ifeq ($(AC_NO_L16_CODEC),1) +-export CFLAGS += -DPJMEDIA_HAS_L16_CODEC=0 +-else +-export CODEC_OBJS += l16.o +-endif +- +-ifeq ($(AC_NO_GSM_CODEC),1) +-export CFLAGS += -DPJMEDIA_HAS_GSM_CODEC=0 +-else + export CODEC_OBJS += gsm.o +-endif + +-ifeq ($(AC_NO_SPEEX_CODEC),1) +-export CFLAGS += -DPJMEDIA_HAS_SPEEX_CODEC=0 +-else + export CFLAGS += -I$(THIRD_PARTY)/build/speex -I$(THIRD_PARTY)/speex/include + export CODEC_OBJS += speex_codec.o + +@@ -113,19 +66,12 @@ + export PJMEDIA_OBJS += echo_speex.o + endif + +-endif ++export CFLAGS += -I$(THIRD_PARTY)/webrtc/src ++export PJMEDIA_OBJS += echo_webrtc_aec.o + +-ifeq ($(AC_NO_ILBC_CODEC),1) +-export CFLAGS += -DPJMEDIA_HAS_ILBC_CODEC=0 +-else + export CODEC_OBJS += ilbc.o +-endif + +-ifeq ($(AC_NO_G722_CODEC),1) +-export CFLAGS += -DPJMEDIA_HAS_G722_CODEC=0 +-else + export CODEC_OBJS += g722.o g722/g722_enc.o g722/g722_dec.o +-endif + + ifeq ($(AC_NO_G7221_CODEC),1) + export CFLAGS += -DPJMEDIA_HAS_G7221_CODEC=0 +@@ -134,148 +80,19 @@ + export G7221_CFLAGS += -I$(THIRD_PARTY) + endif + +-ifeq ($(AC_NO_OPENCORE_AMRNB),1) +-export CFLAGS += -DPJMEDIA_HAS_OPENCORE_AMRNB_CODEC=0 +-else +-export CODEC_OBJS += opencore_amr.o +-endif ++export CODEC_OBJS += opus.o + +-ifeq ($(AC_NO_OPENCORE_AMRWB),1) +-export CFLAGS += -DPJMEDIA_HAS_OPENCORE_AMRWB_CODEC=0 +-else +-ifeq ($(AC_NO_OPENCORE_AMRNB),1) +-export CODEC_OBJS += opencore_amr.o +-endif +-endif +- +- + # +-# SRTP ++# Dshow video device + # +-ifeq (@ac_external_srtp@,1) +-# External SRTP +-export CFLAGS += -DPJMEDIA_EXTERNAL_SRTP=1 +-# SRTP srtp_deinit()/srtp_shutdown() API availability settings +-export CFLAGS += -DPJMEDIA_SRTP_HAS_DEINIT=@ac_srtp_deinit_present@ \ +- -DPJMEDIA_SRTP_HAS_SHUTDOWN=@ac_srtp_shutdown_present@ +-else +-# Our SRTP in third_party +-export CFLAGS += -I$(THIRD_PARTY)/build/srtp \ +- -I$(THIRD_PARTY)/srtp/crypto/include \ +- -I$(THIRD_PARTY)/srtp/include +- ++ifeq ($(AC_PJMEDIA_VIDEO_HAS_DSHOW),yes) ++export PJMEDIA_VIDEODEV_OBJS += dshow_dev.o dshow_filter.o + endif + + # +-# Resample ++# AVF video device + # +-AC_PJMEDIA_RESAMPLE=@ac_pjmedia_resample@ +- +-ifeq ($(AC_PJMEDIA_RESAMPLE),none) +-# No resample support +-export CFLAGS += -DPJMEDIA_RESAMPLE_IMP=PJMEDIA_RESAMPLE_NONE ++ifeq ($(AC_PJMEDIA_VIDEO_HAS_AVF),yes) ++export PJMEDIA_VIDEODEV_OBJS += avf_dev.o + endif + +-ifeq ($(AC_PJMEDIA_RESAMPLE),libresample) +-export CFLAGS += -DPJMEDIA_RESAMPLE_IMP=PJMEDIA_RESAMPLE_LIBRESAMPLE +-endif +- +-ifeq ($(AC_PJMEDIA_RESAMPLE),libsamplerate) +-export CFLAGS += -DPJMEDIA_RESAMPLE_IMP=PJMEDIA_RESAMPLE_LIBSAMPLERATE +-endif +- +-ifeq ($(AC_PJMEDIA_RESAMPLE),speex) +-export CFLAGS += -DPJMEDIA_RESAMPLE_IMP=PJMEDIA_RESAMPLE_SPEEX +-endif +- +-# +-# PortAudio +-# +-ifneq ($(findstring pa,$(AC_PJMEDIA_SND)),) +-ifeq (@ac_external_pa@,1) +-# External PA +-export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=1 +-else +-# Our PA in third_party +-export CFLAGS += -I$(THIRD_PARTY)/build/portaudio \ +- -I$(THIRD_PARTY)/portaudio/include \ +- -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=1 +-endif +-endif +- +-# +-# MacOSX specific +-# +-ifneq ($(findstring coreaudio,$(AC_PJMEDIA_SND)),) +-export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_COREAUDIO=1 \ +- -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=0 \ +- -DPJMEDIA_AUDIO_DEV_HAS_WMME=0 +-endif +- +-# +-# Unix specific +-# +-ifneq ($(findstring alsa,$(AC_PJMEDIA_SND)),) +-export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_ALSA=1 \ +- -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=0 \ +- -DPJMEDIA_AUDIO_DEV_HAS_WMME=0 +-endif +- +-# +-# Windows specific +-# +-ifneq ($(findstring win32,$(AC_PJMEDIA_SND)),) +-export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_WMME=1 \ +- -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=0 +-else +-export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_WMME=0 +-endif +- +-# +-# Null sound device +-# +-ifeq ($(AC_PJMEDIA_SND),null) +-export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=0 \ +- -DPJMEDIA_AUDIO_DEV_HAS_WMME=0 +-endif +- +-# +-# External sound device +-# +-ifeq ($(AC_PJMEDIA_SND),external) +-export CFLAGS += -DPJMEDIA_AUDIO_DEV_HAS_PORTAUDIO=0 \ +- -DPJMEDIA_AUDIO_DEV_HAS_WMME=0 +-endif +- +-# +-# QT video device +-# +-ifeq ($(AC_PJMEDIA_VIDEO_HAS_QT),yes) +-export PJMEDIA_VIDEODEV_OBJS += qt_dev.o +-endif +- +-# +-# iOS video device +-# +-ifeq ($(AC_PJMEDIA_VIDEO),iphone_os) +-export PJMEDIA_VIDEODEV_OBJS += ios_dev.o ios_opengl_dev.o +-endif +- +-# +-# Android video device +-# +-ifeq ($(AC_PJMEDIA_VIDEO),android_os) +-export PJMEDIA_VIDEODEV_OBJS += android_dev.o android_opengl.o +-endif +- +-# +-# Determine whether we should compile the obj-c version of a particular +-# source code +-# +-ifneq (,$(filter $(AC_PJMEDIA_VIDEO),mac_os iphone_os)) +-# Mac and iPhone OS specific, use obj-c +-export PJMEDIA_VIDEODEV_OBJS += sdl_dev_m.o +-else +-# Other platforms, compile .c +-export PJMEDIA_VIDEODEV_OBJS += sdl_dev.o +-endif +Index: pjmedia/include/pjmedia/config.h +=================================================================== +--- pjmedia/include/pjmedia/config.h (revision 5249) ++++ pjmedia/include/pjmedia/config.h (working copy) +@@ -657,24 +657,6 @@ + + + /** +- * WebRtc Accoustic Echo Cancellation (AEC). +- * By default is disabled. +- */ +-#ifndef PJMEDIA_HAS_WEBRTC_AEC +-# define PJMEDIA_HAS_WEBRTC_AEC 0 +-#endif +- +-/** +- * Specify whether WebRtc EC should use its mobile version AEC. +- * +- * Default: 0 (no) +- */ +-#ifndef PJMEDIA_WEBRTC_AEC_USE_MOBILE +-# define PJMEDIA_WEBRTC_AEC_USE_MOBILE 0 +-#endif +- +- +-/** + * Maximum number of parameters in SDP fmtp attribute. + * + * Default: 16 +@@ -972,7 +954,6 @@ + * See: + * - G.722 : RFC 3551 4.5.2 + * - MPEG audio : RFC 3551 4.5.13 & RFC 3119 +- * - OPUS : RFC 7587 + * + * Also when this feature is enabled, some handling will be performed + * to deal with clock rate incompatibilities of some phones. +@@ -1078,10 +1059,10 @@ + /** + * Top level option to enable/disable video features. + * +- * Default: 0 (disabled) ++ * Default: 1 (enabled) + */ + #ifndef PJMEDIA_HAS_VIDEO +-# define PJMEDIA_HAS_VIDEO 0 ++# define PJMEDIA_HAS_VIDEO 1 + #endif + + +@@ -1132,21 +1113,21 @@ + #endif + + /** +- * Specify if FFMPEG libavdevice is available. ++ * Specify if FFMPEG libavcore is available. + * + * Default: PJMEDIA_HAS_FFMPEG (or detected by configure) + */ +-#ifndef PJMEDIA_HAS_LIBAVDEVICE +-# define PJMEDIA_HAS_LIBAVDEVICE PJMEDIA_HAS_FFMPEG ++#ifndef PJMEDIA_HAS_LIBAVCORE ++# define PJMEDIA_HAS_LIBAVCORE PJMEDIA_HAS_FFMPEG + #endif + + /** +- * Specify if FFMPEG libavcore is available. ++ * Specify if libvpx is available. + * +- * Default: PJMEDIA_HAS_FFMPEG (or detected by configure) ++ * Default: 0 (or detected by configure) + */ +-#ifndef PJMEDIA_HAS_LIBAVCORE +-# define PJMEDIA_HAS_LIBAVCORE PJMEDIA_HAS_FFMPEG ++#ifndef PJMEDIA_HAS_LIBVPX ++# define PJMEDIA_HAS_LIBVPX 0 + #endif + + /** +@@ -1321,7 +1302,6 @@ + # endif + #endif + +- + /** + * Specify if libyuv is available. + * +Index: pjmedia/include/pjmedia/echo.h +=================================================================== +--- pjmedia/include/pjmedia/echo.h (revision 5249) ++++ pjmedia/include/pjmedia/echo.h (working copy) +@@ -66,8 +66,7 @@ + + /** + * Force to use Speex AEC as the backend echo canceller algorithm. +- * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE and +- * PJMEDIA_ECHO_WEBRTC. ++ * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE. + */ + PJMEDIA_ECHO_SPEEX = 1, + +@@ -75,14 +74,13 @@ + * If PJMEDIA_ECHO_SIMPLE flag is specified during echo canceller + * creation, then a simple echo suppressor will be used instead of + * an accoustic echo cancellation. This setting is mutually exclusive +- * with PJMEDIA_ECHO_SPEEX and PJMEDIA_ECHO_WEBRTC. ++ * with PJMEDIA_ECHO_SPEEX. + */ + PJMEDIA_ECHO_SIMPLE = 2, + + /** + * Force to use WebRTC AEC as the backend echo canceller algorithm. +- * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE and +- * PJMEDIA_ECHO_SPEEX. ++ * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE & PJMEDIA_ECHO_SPEEX. + */ + PJMEDIA_ECHO_WEBRTC = 3, + +@@ -109,46 +107,7 @@ + * If PJMEDIA_ECHO_USE_SW_ECHO flag is specified, software echo canceller + * will be used instead of device EC. + */ +- PJMEDIA_ECHO_USE_SW_ECHO = 64, +- +- /** +- * If PJMEDIA_ECHO_USE_NOISE_SUPPRESSOR flag is specified, the echo +- * canceller will also apply noise suppressor method to reduce noise. +- */ +- PJMEDIA_ECHO_USE_NOISE_SUPPRESSOR = 128, +- +- /** +- * Use default aggressiveness setting for the echo canceller algorithm. +- * This setting is mutually exclusive with the other aggressiveness +- * settings. +- */ +- PJMEDIA_ECHO_AGGRESSIVENESS_DEFAULT = 0, +- +- /** +- * Use conservative aggressiveness setting for the echo canceller +- * algorithm. This setting is mutually exclusive with the other +- * aggressiveness settings. +- */ +- PJMEDIA_ECHO_AGGRESSIVENESS_CONSERVATIVE = 0x100, +- +- /** +- * Use moderate aggressiveness setting for the echo canceller algorithm. +- * This setting is mutually exclusive with the other aggressiveness +- * settings. +- */ +- PJMEDIA_ECHO_AGGRESSIVENESS_MODERATE = 0x200, +- +- /** +- * Use aggressive aggressiveness setting for the echo canceller +- * algorithm. This setting is mutually exclusive with the other +- * aggressiveness settings. +- */ +- PJMEDIA_ECHO_AGGRESSIVENESS_AGGRESSIVE = 0x300, +- +- /** +- * For internal use. +- */ +- PJMEDIA_ECHO_AGGRESSIVENESS_MASK = 0xF00 ++ PJMEDIA_ECHO_USE_SW_ECHO = 64 + + } pjmedia_echo_flag; + +Index: pjmedia/include/pjmedia/errno.h +=================================================================== +--- pjmedia/include/pjmedia/errno.h (revision 5249) ++++ pjmedia/include/pjmedia/errno.h (working copy) +@@ -46,18 +46,6 @@ + #define PJMEDIA_ERRNO_END (PJMEDIA_ERRNO_START + PJ_ERRNO_SPACE_SIZE - 1) + + +-/** +- * Mapping from PortAudio error codes to pjmedia error space. +- */ +-#define PJMEDIA_PORTAUDIO_ERRNO_START (PJMEDIA_ERRNO_END-10000) +-#define PJMEDIA_PORTAUDIO_ERRNO_END (PJMEDIA_PORTAUDIO_ERRNO_START + 10000 -1) +-/** +- * Convert PortAudio error code to PJMEDIA error code. +- * PortAudio error code range: 0 >= err >= -10000 +- */ +-#define PJMEDIA_ERRNO_FROM_PORTAUDIO(err) ((int)PJMEDIA_PORTAUDIO_ERRNO_START-err) +- +- + #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + + /** +Index: pjmedia/include/pjmedia/event.h +=================================================================== +--- pjmedia/include/pjmedia/event.h (revision 5249) ++++ pjmedia/include/pjmedia/event.h (working copy) +@@ -80,6 +80,11 @@ + PJMEDIA_EVENT_KEYFRAME_MISSING = PJMEDIA_FOURCC('I', 'F', 'R', 'M'), + + /** ++ * Remote video decoder asked for a keyframe. ++ */ ++ PJMEDIA_EVENT_KEYFRAME_REQUESTED = PJMEDIA_FOURCC('I', 'F', 'R', 'R'), ++ ++ /** + * Video orientation has been changed event. + */ + PJMEDIA_EVENT_ORIENT_CHANGED = PJMEDIA_FOURCC('O', 'R', 'N', 'T') +Index: pjmedia/include/pjmedia/format.h +=================================================================== +--- pjmedia/include/pjmedia/format.h (revision 5249) ++++ pjmedia/include/pjmedia/format.h (working copy) +@@ -97,6 +97,7 @@ + /** + * 32bit RGB with alpha channel + */ ++ PJMEDIA_FORMAT_ARGB = PJMEDIA_FORMAT_PACK('A', 'R', 'G', 'B'), + PJMEDIA_FORMAT_RGBA = PJMEDIA_FORMAT_PACK('R', 'G', 'B', 'A'), + PJMEDIA_FORMAT_BGRA = PJMEDIA_FORMAT_PACK('B', 'G', 'R', 'A'), + +@@ -216,6 +217,7 @@ + PJMEDIA_FORMAT_MPEG2VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '2', 'V'), + PJMEDIA_FORMAT_MPEG4 = PJMEDIA_FORMAT_PACK('M', 'P', 'G', '4'), + ++ PJMEDIA_FORMAT_VP8 = PJMEDIA_FORMAT_PACK('L', 'V', 'P', '8'), + } pjmedia_format_id; + + /** +Index: pjmedia/include/pjmedia/rtcp.h +=================================================================== +--- pjmedia/include/pjmedia/rtcp.h (revision 5249) ++++ pjmedia/include/pjmedia/rtcp.h (working copy) +@@ -256,6 +256,8 @@ + + pjmedia_rtcp_stat stat; /**< Bidirectional stream stat. */ + ++ pj_bool_t keyframe_requested; /** Set to true when RTCP PLI is received */ ++ + #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) + /** + * Specify whether RTCP XR processing is enabled on this session. +@@ -462,6 +464,22 @@ + pj_size_t *length, + const pj_str_t *reason); + ++/** ++ * Build an RTCP PLI packet. This packet can be appended to other RTCP ++ * packets, e.g: RTCP RR/SR, to compose a compound RTCP packet. ++ * ++ * @param session The RTCP session. ++ * @param buf The buffer to receive RTCP PLI packet. ++ * @param length On input, it will contain the buffer length. ++ * On output, it will contain the generated RTCP PLI ++ * packet length. ++ * ++ * @return PJ_SUCCESS on success. ++ */ ++PJ_DECL(pj_status_t) pjmedia_rtcp_build_rtcp_pli( ++ pjmedia_rtcp_session *session, ++ void *buf, ++ pj_size_t *length); + + /** + * Call this function if RTCP XR needs to be enabled/disabled in the +Index: pjmedia/include/pjmedia/signatures.h +=================================================================== +--- pjmedia/include/pjmedia/signatures.h (revision 5249) ++++ pjmedia/include/pjmedia/signatures.h (working copy) +@@ -153,6 +153,7 @@ + #define PJMEDIA_SIG_PORT_ECHO PJMEDIA_SIG_CLASS_PORT_AUD('E','C') + #define PJMEDIA_SIG_PORT_MEM_CAPTURE PJMEDIA_SIG_CLASS_PORT_AUD('M','C') + #define PJMEDIA_SIG_PORT_MEM_PLAYER PJMEDIA_SIG_CLASS_PORT_AUD('M','P') ++#define PJMEDIA_SIG_PORT_MIXER PJMEDIA_SIG_CLASS_PORT_AUD('M','X') + #define PJMEDIA_SIG_PORT_NULL PJMEDIA_SIG_CLASS_PORT_AUD('N','U') + #define PJMEDIA_SIG_PORT_RESAMPLE PJMEDIA_SIG_CLASS_PORT_AUD('R','E') + #define PJMEDIA_SIG_PORT_SPLIT_COMB PJMEDIA_SIG_CLASS_PORT_AUD('S','C') +Index: pjmedia/include/pjmedia/sound_port.h +=================================================================== +--- pjmedia/include/pjmedia/sound_port.h (revision 5249) ++++ pjmedia/include/pjmedia/sound_port.h (working copy) +@@ -330,6 +330,16 @@ + + + /** ++ * Reset the EC state in the sound port. ++ * ++ * @param snd_port The sound device port. ++ * ++ * @return PJ_SUCCESS on success. ++ */ ++PJ_DECL(pj_status_t) pjmedia_snd_port_reset_ec_state(pjmedia_snd_port *snd_port); ++ ++ ++/** + * Connect a port to the sound device port. If the sound device port has a + * sound recorder device, then this will start periodic function call to + * the port's put_frame() function. If the sound device has a sound player +Index: pjmedia/include/pjmedia/transport_ice.h +=================================================================== +--- pjmedia/include/pjmedia/transport_ice.h (revision 5249) ++++ pjmedia/include/pjmedia/transport_ice.h (working copy) +@@ -59,6 +59,28 @@ + pj_ice_strans_op op, + pj_status_t status); + ++ /** ++ * This callback will be called when ICE state changes. ++ * ++ * @param tp PJMEDIA ICE transport. ++ * @param prev Previous state. ++ * @param curr Current state. ++ */ ++ void (*on_ice_state)(pjmedia_transport *tp, ++ pj_ice_strans_state prev, ++ pj_ice_strans_state curr); ++ ++ /** ++ * This callback will be called when ICE is stopped. ++ * ++ * @param tp PJMEDIA ICE transport. ++ * @param reason Reason for stopping ICE. ++ * @param err Error code ++ */ ++ void (*on_ice_stop)(pjmedia_transport *tp, ++ char *reason, ++ pj_status_t err); ++ + } pjmedia_ice_cb; + + +@@ -222,6 +244,16 @@ + pjmedia_transport **p_tp); + + /** ++ * Return the ICE stream transport associated with this PJMEDIA transport ++ * ++ * @param tp Media transport instance. ++ * ++ * @return Pointer to the pj_ice_strans instance associated with this ++ * media transport. ++ */ ++PJ_DECL(pj_ice_strans*) pjmedia_ice_get_strans(pjmedia_transport *tp); ++ ++/** + * Get the group lock for the ICE media transport. + * + * @param tp The ICE media transport. +Index: pjmedia/include/pjmedia/vid_stream.h +=================================================================== +--- pjmedia/include/pjmedia/vid_stream.h (revision 5249) ++++ pjmedia/include/pjmedia/vid_stream.h (working copy) +@@ -414,6 +414,17 @@ + + + /** ++ * Send RTCP PLI for the media stream. ++ * ++ * @param stream The media stream. ++ * ++ * @return PJ_SUCCESS on success. ++ */ ++PJ_DECL(pj_status_t) pjmedia_vid_stream_send_rtcp_pli( ++ pjmedia_vid_stream *stream); ++ ++ ++/** + * @} + */ + +Index: pjmedia/include/pjmedia-audiodev/audiodev.h +=================================================================== +--- pjmedia/include/pjmedia-audiodev/audiodev.h (revision 5249) ++++ pjmedia/include/pjmedia-audiodev/audiodev.h (working copy) +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + + +@@ -437,6 +438,28 @@ + } pjmedia_aud_param; + + ++typedef enum pjmedia_aud_dev_event { ++ PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED, ++ PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED, ++ PJMEDIA_AUD_DEV_LIST_WILL_REFRESH, ++ PJMEDIA_AUD_DEV_LIST_DID_REFRESH ++} pjmedia_aud_dev_event; ++ ++ ++typedef void (*pjmedia_aud_dev_observer_callback)(pjmedia_aud_dev_event event); ++ ++/** ++ * This structure specifies the parameters to set an audio device observer ++ */ ++typedef struct pjmedia_aud_dev_observer { ++ pjmedia_aud_dev_observer_callback cb; ++ pj_pool_t *pool; ++ pj_mutex_t *lock; ++ pj_thread_t *thread; ++ pj_thread_desc thread_desc; ++} pjmedia_aud_dev_observer; ++ ++ + /** Forward declaration for pjmedia_aud_stream */ + typedef struct pjmedia_aud_stream pjmedia_aud_stream; + +@@ -717,7 +740,19 @@ + */ + PJ_DECL(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm); + ++/** ++ * Set an audio device observer callback. ++ * ++ * @param cb The callback that needs to be registred, or NULL in ++ * in case it needs to be unregistered. Only one callback ++ * can be registered. ++ * ++ * @return PJ_SUCCESS on successful operation or the appropriate ++ * error code. ++ */ ++PJ_DECL(pj_status_t) pjmedia_aud_dev_set_observer_cb(pjmedia_aud_dev_observer_callback cb); + ++ + /** + * @} + */ +Index: pjmedia/include/pjmedia-audiodev/audiodev_imp.h +=================================================================== +--- pjmedia/include/pjmedia-audiodev/audiodev_imp.h (revision 5249) ++++ pjmedia/include/pjmedia-audiodev/audiodev_imp.h (working copy) +@@ -29,6 +29,14 @@ + * @{ + */ + ++typedef enum pjmedia_aud_dev_change_event { ++ DEFAULT_INPUT_CHANGED = 1, ++ DEFAULT_OUTPUT_CHANGED, ++ DEVICE_LIST_CHANGED ++} pjmedia_aud_dev_change_event; ++ ++typedef void (*pjmedia_aud_dev_change_callback)(pjmedia_aud_dev_change_event event); ++ + /** + * Sound device factory operations. + */ +@@ -99,6 +107,29 @@ + */ + pj_status_t (*refresh)(pjmedia_aud_dev_factory *f); + ++ /** ++ * Set audio device change callback ++ * ++ * @param f The audio device factory. ++ * @param cb The audio device change callback. ++ */ ++ void (*set_dev_change_cb)(pjmedia_aud_dev_factory *f, ++ pjmedia_aud_dev_change_callback cb); ++ ++ /** ++ * Get default recording device index ++ * ++ * @param f The audio device factory. ++ */ ++ int (*get_default_rec_dev)(pjmedia_aud_dev_factory *f); ++ ++ /** ++ * Get default playback device index ++ * ++ * @param f The audio device factory. ++ */ ++ int (*get_default_play_dev)(pjmedia_aud_dev_factory *f); ++ + } pjmedia_aud_dev_factory_op; + + +Index: pjmedia/include/pjmedia-audiodev/config.h +=================================================================== +--- pjmedia/include/pjmedia-audiodev/config.h (revision 5249) ++++ pjmedia/include/pjmedia-audiodev/config.h (working copy) +@@ -43,60 +43,6 @@ + */ + + /** +- * This setting controls the buffer length of audio device name. +- * +- * Default: 128 for Windows platforms, 64 for others +- */ +-#ifndef PJMEDIA_AUD_DEV_INFO_NAME_LEN +-# if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ +- (defined(PJ_WIN64) && PJ_WIN64!=0) +-# define PJMEDIA_AUD_DEV_INFO_NAME_LEN 128 +-# else +-# define PJMEDIA_AUD_DEV_INFO_NAME_LEN 64 +-# endif +-#endif +- +-/** +- * This setting controls whether PortAudio support should be included. +- * +- * By default it is enabled except on Windows platforms (including +- * Windows Mobile) and Symbian. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO +-# if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ +- (defined(PJ_WIN64) && PJ_WIN64!=0) || \ +- (defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0) +-# define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 0 +-# else +-# define PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO 1 +-# endif +-#endif +- +-/** +- * This setting controls whether Android OpenSL audio support should be +- * included. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_OPENSL +-# define PJMEDIA_AUDIO_DEV_HAS_OPENSL 0 +-#endif +- +-/** +- * This setting controls whether Android JNI audio support should be +- * included. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI +-# define PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI 0 +-#endif +- +-/** +- * This setting controls whether BlackBerry 10 (BB10) audio support +- * should be included. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_BB10 +-# define PJMEDIA_AUDIO_DEV_HAS_BB10 0 +-#endif +- +-/** + * This setting controls whether native ALSA support should be included. + */ + #ifndef PJMEDIA_AUDIO_DEV_HAS_ALSA +@@ -124,107 +70,26 @@ + * This setting controls whether WMME support should be included. + */ + #ifndef PJMEDIA_AUDIO_DEV_HAS_WMME +-# define PJMEDIA_AUDIO_DEV_HAS_WMME 1 ++# define PJMEDIA_AUDIO_DEV_HAS_WMME 0 + #endif + +- +-/** +- * This setting controls whether BDIMAD support should be included. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_BDIMAD +-# define PJMEDIA_AUDIO_DEV_HAS_BDIMAD 0 +-#endif + +- + /** +- * This setting controls whether Symbian APS support should be included. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_SYMB_APS +-# define PJMEDIA_AUDIO_DEV_HAS_SYMB_APS 0 +-#endif +- +- +-/** +- * This setting controls whether Symbian APS should perform codec +- * detection in its factory initalization. Note that codec detection +- * may take few seconds and detecting more codecs will take more time. +- * Possible values are: +- * - 0: no codec detection, all APS codec (AMR-NB, G.711, G.729, and +- * iLBC) will be assumed as supported. +- * - 1: minimal codec detection, i.e: only detect for AMR-NB and G.711, +- * (G.729 and iLBC are considered to be supported/unsupported when +- * G.711 is supported/unsupported). +- * - 2: full codec detection, i.e: detect AMR-NB, G.711, G.729, and iLBC. +- * +- * Default: 1 (minimal codec detection) +- */ +-#ifndef PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC +-# define PJMEDIA_AUDIO_DEV_SYMB_APS_DETECTS_CODEC 1 +-#endif +- +- +-/** +- * This setting controls whether Symbian VAS support should be included. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS +-# define PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS 0 +-#endif +- +-/** +- * This setting controls Symbian VAS version to be used. Currently, valid +- * values are only 1 (for VAS 1.0) and 2 (for VAS 2.0). ++ * This setting controls the buffer length of audio device name. + * +- * Default: 1 (VAS version 1.0) ++ * Default: 128 for Windows platforms, 64 for others + */ +-#ifndef PJMEDIA_AUDIO_DEV_SYMB_VAS_VERSION +-# define PJMEDIA_AUDIO_DEV_SYMB_VAS_VERSION 1 ++#ifndef PJMEDIA_AUD_DEV_INFO_NAME_LEN ++# if (defined(PJ_WIN32) && PJ_WIN32!=0) || \ ++ (defined(PJ_WIN64) && PJ_WIN64!=0) ++# define PJMEDIA_AUD_DEV_INFO_NAME_LEN 128 ++# else ++# define PJMEDIA_AUD_DEV_INFO_NAME_LEN 64 ++# endif + #endif + + + /** +- * This setting controls whether Symbian audio (using built-in multimedia +- * framework) support should be included. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA +-# define PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA PJ_SYMBIAN +-#endif +- +- +-/** +- * This setting controls whether the Symbian audio with built-in multimedia +- * framework backend should be started synchronously. Note that synchronous +- * start will block the application/UI, e.g: about 40ms for each direction +- * on N95. While asynchronous start may cause invalid value (always zero) +- * returned in input/output volume query, if the query is performed when +- * the internal start procedure is not completely finished. +- * +- * Default: 1 (yes) +- */ +-#ifndef PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START +-# define PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START 1 +-#endif +- +- +-/** +- * This setting controls whether the Audio Device API should support +- * device implementation that is based on the old sound device API +- * (sound.h). +- * +- * Enable this API if: +- * - you have implemented your own sound device using the old sound +- * device API (sound.h), and +- * - you wish to be able to use your sound device implementation +- * using the new Audio Device API. +- * +- * Please see http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more +- * info. +- */ +-#ifndef PJMEDIA_AUDIO_DEV_HAS_LEGACY_DEVICE +-# define PJMEDIA_AUDIO_DEV_HAS_LEGACY_DEVICE 0 +-#endif +- +- +-/** + * @} + */ + +Index: pjmedia/include/pjmedia-audiodev/errno.h +=================================================================== +--- pjmedia/include/pjmedia-audiodev/errno.h (revision 5249) ++++ pjmedia/include/pjmedia-audiodev/errno.h (working copy) +@@ -49,20 +49,6 @@ + + + /** +- * Mapping from PortAudio error codes to pjmedia error space. +- */ +-#define PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_START \ +- (PJMEDIA_AUDIODEV_ERRNO_END-10000) +-#define PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_END \ +- (PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_START + 10000 -1) +-/** +- * Convert PortAudio error code to PJLIB error code. +- * PortAudio error code range: 0 >= err >= -10000 +- */ +-#define PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) \ +- ((int)PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_START-err) +- +-/** + * Mapping from Windows multimedia WaveIn error codes. + */ + #define PJMEDIA_AUDIODEV_WMME_IN_ERROR_START \ +Index: pjmedia/include/pjmedia-codec/config.h +=================================================================== +--- pjmedia/include/pjmedia-codec/config.h (revision 5249) ++++ pjmedia/include/pjmedia-codec/config.h (working copy) +@@ -132,116 +132,14 @@ + + + /** +- * Enable the features provided by Intel IPP libraries, for example +- * codecs such as G.729, G.723.1, G.726, G.728, G.722.1, and AMR. +- * +- * By default this is disabled. Please follow the instructions in +- * http://trac.pjsip.org/repos/wiki/Intel_IPP_Codecs on how to setup +- * Intel IPP with PJMEDIA. ++ * Unless specified otherwise, opus codec is included by default. + */ +-#ifndef PJMEDIA_HAS_INTEL_IPP +-# define PJMEDIA_HAS_INTEL_IPP 0 ++#ifndef PJMEDIA_HAS_OPUS_CODEC ++# define PJMEDIA_HAS_OPUS_CODEC 1 + #endif + + + /** +- * Visual Studio only: when this option is set, the Intel IPP libraries +- * will be automatically linked to application using pragma(comment) +- * constructs. This is convenient, however it will only link with +- * the stub libraries and the Intel IPP DLL's will be required when +- * distributing the application. +- * +- * If application wants to link with the different types of the Intel IPP +- * libraries (for example, the static libraries), it must set this option +- * to zero and specify the Intel IPP libraries in the application's input +- * library specification manually. +- * +- * Default 1. +- */ +-#ifndef PJMEDIA_AUTO_LINK_IPP_LIBS +-# define PJMEDIA_AUTO_LINK_IPP_LIBS 1 +-#endif +- +- +-/** +- * Enable Intel IPP AMR codec. This also needs to be enabled when AMR WB +- * codec is enabled. This option is only used when PJMEDIA_HAS_INTEL_IPP +- * is enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_INTEL_IPP_CODEC_AMR +-# define PJMEDIA_HAS_INTEL_IPP_CODEC_AMR 1 +-#endif +- +- +-/** +- * Enable Intel IPP AMR wideband codec. The PJMEDIA_HAS_INTEL_IPP_CODEC_AMR +- * option must also be enabled to use this codec. This option is only used +- * when PJMEDIA_HAS_INTEL_IPP is enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_INTEL_IPP_CODEC_AMRWB +-# define PJMEDIA_HAS_INTEL_IPP_CODEC_AMRWB 1 +-#endif +- +- +-/** +- * Enable Intel IPP G.729 codec. This option is only used when +- * PJMEDIA_HAS_INTEL_IPP is enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_INTEL_IPP_CODEC_G729 +-# define PJMEDIA_HAS_INTEL_IPP_CODEC_G729 1 +-#endif +- +- +-/** +- * Enable Intel IPP G.723.1 codec. This option is only used when +- * PJMEDIA_HAS_INTEL_IPP is enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_INTEL_IPP_CODEC_G723_1 +-# define PJMEDIA_HAS_INTEL_IPP_CODEC_G723_1 1 +-#endif +- +- +-/** +- * Enable Intel IPP G.726 codec. This option is only used when +- * PJMEDIA_HAS_INTEL_IPP is enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_INTEL_IPP_CODEC_G726 +-# define PJMEDIA_HAS_INTEL_IPP_CODEC_G726 1 +-#endif +- +- +-/** +- * Enable Intel IPP G.728 codec. This option is only used when +- * PJMEDIA_HAS_INTEL_IPP is enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_INTEL_IPP_CODEC_G728 +-# define PJMEDIA_HAS_INTEL_IPP_CODEC_G728 1 +-#endif +- +- +-/** +- * Enable Intel IPP G.722.1 codec. This option is only used when +- * PJMEDIA_HAS_INTEL_IPP is enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1 +-# define PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1 1 +-#endif +- +-/** + * Enable Passthrough codecs. + * + * Default: 0 +@@ -251,61 +149,6 @@ + #endif + + /** +- * Enable AMR passthrough codec. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR +-# define PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR 1 +-#endif +- +-/** +- * Enable G.729 passthrough codec. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 +-# define PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 1 +-#endif +- +-/** +- * Enable iLBC passthrough codec. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC +-# define PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC 1 +-#endif +- +-/** +- * Enable PCMU passthrough codec. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU +-# define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU 1 +-#endif +- +-/** +- * Enable PCMA passthrough codec. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA +-# define PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA 1 +-#endif +- +-/* If passthrough and PCMU/PCMA are enabled, disable the software +- * G.711 codec +- */ +-#if PJMEDIA_HAS_PASSTHROUGH_CODECS && \ +- (PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMU || PJMEDIA_HAS_PASSTHROUGH_CODEC_PCMA) +-# undef PJMEDIA_HAS_G711_CODEC +-# define PJMEDIA_HAS_G711_CODEC 0 +-#endif +- +- +-/** + * G.722.1 codec is disabled by default. + */ + #ifndef PJMEDIA_HAS_G7221_CODEC +@@ -313,49 +156,6 @@ + #endif + + /** +- * Enable OpenCORE AMR-NB codec. +- * See https://trac.pjsip.org/repos/ticket/1388 for some info. +- * +- * Default: 0 +- */ +-#ifndef PJMEDIA_HAS_OPENCORE_AMRNB_CODEC +-# define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 0 +-#endif +- +-/** +- * Enable OpenCORE AMR-WB codec. +- * See https://trac.pjsip.org/repos/ticket/1608 for some info. +- * +- * Default: 0 +- */ +-#ifndef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +-# define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0 +-#endif +- +-/** +- * Link with libopencore-amrXX via pragma comment on Visual Studio. +- * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB/WB_CODEC +- * is enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS +-# define PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS 1 +-#endif +- +-/** +- * Link with libopencore-amrXX.a that has been produced with gcc. +- * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB/WB_CODEC +- * and PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS are enabled. +- * +- * Default: 1 +- */ +-#ifndef PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC +-# define PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC 1 +-#endif +- +- +-/** + * Default G.722.1 codec encoder and decoder level adjustment. + * If the value is non-zero, then PCM input samples to the encoder will + * be shifted right by this value, and similarly PCM output samples from +@@ -388,88 +188,6 @@ + + + /** +- * Enable SILK codec. +- * +- * Default: 0 +- */ +-#ifndef PJMEDIA_HAS_SILK_CODEC +-# define PJMEDIA_HAS_SILK_CODEC 0 +-#endif +- +- +-/** +- * SILK codec default complexity setting, valid values are 0 (lowest), 1, +- * and 2. +- * +- * Default: 2 +- */ +-#ifndef PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY +-# define PJMEDIA_CODEC_SILK_DEFAULT_COMPLEXITY 2 +-#endif +- +-/** +- * SILK codec default quality setting, valid values are ranging from +- * 0 (lowest) to 10. Please note that pjsua-lib may override this setting +- * via its codec quality setting (i.e PJSUA_DEFAULT_CODEC_QUALITY). +- * +- * Default: 10 +- */ +-#ifndef PJMEDIA_CODEC_SILK_DEFAULT_QUALITY +-# define PJMEDIA_CODEC_SILK_DEFAULT_QUALITY 10 +-#endif +- +- +-/** +- * Enable OPUS codec. +- * +- * Default: 0 +- */ +-#ifndef PJMEDIA_HAS_OPUS_CODEC +-# define PJMEDIA_HAS_OPUS_CODEC 0 +-#endif +- +-/** +- * OPUS codec sample rate. +- * +- * Default: 48000 +- */ +-#ifndef PJMEDIA_CODEC_OPUS_DEFAULT_SAMPLE_RATE +-# define PJMEDIA_CODEC_OPUS_DEFAULT_SAMPLE_RATE 48000 +-#endif +- +-/** +- * OPUS codec default maximum average bit rate. +- * +- * Default: 0 (leave it to default value specified by Opus, which will +- * take into account factors such as media content (speech/music), sample +- * rate, channel count, etc). +- */ +-#ifndef PJMEDIA_CODEC_OPUS_DEFAULT_BIT_RATE +-# define PJMEDIA_CODEC_OPUS_DEFAULT_BIT_RATE 0 +-#endif +- +- +-/** +- * OPUS default encoding complexity, which is an integer from +- * 0 to 10, where 0 is the lowest complexity and 10 is the highest. +- * +- * Default: 5 +- */ +-#ifndef PJMEDIA_CODEC_OPUS_DEFAULT_COMPLEXITY +-# define PJMEDIA_CODEC_OPUS_DEFAULT_COMPLEXITY 5 +-#endif +- +- +-/** +- * OPUS default CBR (constant bit rate) setting +- * +- * Default: PJ_FALSE (which means Opus will use VBR (variable bit rate)) +- */ +-#ifndef PJMEDIA_CODEC_OPUS_DEFAULT_CBR +-# define PJMEDIA_CODEC_OPUS_DEFAULT_CBR PJ_FALSE +-#endif +- +-/** + * Specify if FFMPEG codecs are available. + * + * Default: PJMEDIA_HAS_LIBAVCODEC +@@ -507,6 +225,14 @@ + #endif + + /** ++ * Compile VPX support, unless explicitly disabled ++ */ ++#ifndef PJMEDIA_HAS_VPX_CODEC ++# define PJMEDIA_HAS_VPX_CODEC PJMEDIA_HAS_LIBVPX ++#endif ++ ++ ++/** + * @} + */ + +Index: pjmedia/include/pjmedia-codec/config_auto.h.in +=================================================================== +--- pjmedia/include/pjmedia-codec/config_auto.h.in (revision 5249) ++++ pjmedia/include/pjmedia-codec/config_auto.h.in (working copy) +@@ -31,28 +31,17 @@ + * including the setting as detected by autoconf. + */ + +-/* L16 codec */ +-#ifndef PJMEDIA_HAS_L16_CODEC +-#undef PJMEDIA_HAS_L16_CODEC +-#endif + +- + /* GSM codec */ + #ifndef PJMEDIA_HAS_GSM_CODEC + #undef PJMEDIA_HAS_GSM_CODEC + #endif + +-#undef PJMEDIA_EXTERNAL_GSM_CODEC +-#undef PJMEDIA_EXTERNAL_GSM_GSM_H +-#undef PJMEDIA_EXTERNAL_GSM_H +- + /* Speex codec */ + #ifndef PJMEDIA_HAS_SPEEX_CODEC + #undef PJMEDIA_HAS_SPEEX_CODEC + #endif + +-#undef PJMEDIA_EXTERNAL_SPEEX_CODEC +- + /* iLBC codec */ + #ifndef PJMEDIA_HAS_ILBC_CODEC + #undef PJMEDIA_HAS_ILBC_CODEC +@@ -69,26 +58,6 @@ + #undef PJMEDIA_HAS_G7221_CODEC + #endif + +-/* OpenCORE AMR-NB codec */ +-#ifndef PJMEDIA_HAS_OPENCORE_AMRNB_CODEC +-#undef PJMEDIA_HAS_OPENCORE_AMRNB_CODEC +-#endif +- +-/* OpenCORE AMR-WB codec */ +-#ifndef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +-#undef PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +-#endif +- +-/* SILK codec */ +-#ifndef PJMEDIA_HAS_SILK_CODEC +-#undef PJMEDIA_HAS_SILK_CODEC +-#endif +- +-/* OPUS codec */ +-#ifndef PJMEDIA_HAS_OPUS_CODEC +-#undef PJMEDIA_HAS_OPUS_CODEC +-#endif +- + #endif /* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */ + + +Index: pjmedia/include/pjmedia-codec/opus.h +=================================================================== +--- pjmedia/include/pjmedia-codec/opus.h (revision 5249) ++++ pjmedia/include/pjmedia-codec/opus.h (working copy) +@@ -1,155 +1,25 @@ +-/* $Id$ */ +-/* +- * Copyright (C) 2015-2016 Teluu Inc. (http://www.teluu.com) +- * Copyright (C) 2012-2015 Zaark Technology AB +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +-/* This file is the header of Opus codec wrapper and was contributed by +- * Zaark Technology AB +- */ + +-#ifndef __PJMEDIA_CODEC_OPUS_H__ +-#define __PJMEDIA_CODEC_OPUS_H__ ++#ifndef __PJMEDIA_CODEC_OPUS_CODEC_H__ ++#define __PJMEDIA_CODEC_OPUS_CODEC_H__ + + /** +- * @file opus.h +- * @brief Opus codec. ++ * @file pj_opus.h ++ * @brief OPUS codec. + */ + + #include + ++ + PJ_BEGIN_DECL + +-/** +- * @defgroup PJMED_OPUS Opus Codec Family +- * @ingroup PJMEDIA_CODEC_CODECS +- * @brief Opus codec wrapper +- * @{ +- * +- * This section describes functions to initialize and register Opus codec +- * factory to the codec manager. After the codec factory has been registered, +- * application can use @ref PJMEDIA_CODEC API to manipulate the codec. +- * +- * Opus codec uses multiple bit rates, and supports fullband (48 kHz +- * sampling rate), super wideband (24 kHz sampling rate), wideband (16 kHz +- * sampling rate), medium band (12kHz sampling rate), and narrowband +- * (8 kHz sampling rate). +- * +- * +- * \section codec_setting Codec Settings +- * +- * General codec settings for this codec such as VAD and PLC can be +- * manipulated through the setting field in #pjmedia_codec_param +- * (see the documentation of #pjmedia_codec_param for more info). +- * +- * For Opus codec specific settings, such as sample rate, +- * channel count, bit rate, complexity, and CBR, can be configured +- * in #pjmedia_codec_opus_config. +- * The default setting of sample rate is specified in +- * #PJMEDIA_CODEC_OPUS_DEFAULT_SAMPLE_RATE. The default setting of +- * bitrate is specified in #PJMEDIA_CODEC_OPUS_DEFAULT_BIT_RATE. +- * And the default setting of complexity is specified in +- * #PJMEDIA_CODEC_OPUS_DEFAULT_COMPLEXITY. +- * +- * After modifying any of these settings, application needs to call +- * #pjmedia_codec_opus_set_default_param(), which will generate the +- * appropriate decoding fmtp attributes. +- * +- * Here is an example of modifying the codec settings: +- \code +- pjmedia_codec_param param; +- pjmedia_codec_opus_config opus_cfg; ++PJ_DECL(pj_status_t) pjmedia_codec_opus_init( pjmedia_endpt *endpt); ++PJ_DECL(pj_status_t) pjmedia_codec_opus_deinit(void); + +- pjmedia_codec_mgr_get_default_param(.., ¶m); +- pjmedia_codec_opus_get_config(&opus_cfg); +- ... +- // Set VAD +- param.setting.vad = 1; +- // Set PLC +- param.setting.vad = 1; +- // Set sample rate +- opus_cfg.sample_rate = 16000; +- // Set channel count +- opus_cfg.channel_cnt = 2; +- // Set bit rate +- opus_cfg.bit_rate = 20000; +- ... +- pjmedia_codec_opus_set_default_param(&opus_cfg, ¶m); +- \endcode +- * +- */ ++PJ_END_DECL + +-/** +- * Opus codec configuration. +- */ +-typedef struct pjmedia_codec_opus_config +-{ +- unsigned sample_rate; /**< Sample rate in Hz. */ +- unsigned channel_cnt; /**< Number of channels. */ +- unsigned bit_rate; /**< Encoder bit rate in bps. */ +- unsigned packet_loss; /**< Encoder's expected packet loss pct. */ +- unsigned complexity; /**< Encoder complexity, 0-10(10 is highest)*/ +- pj_bool_t cbr; /**< Constant bit rate? */ +-} pjmedia_codec_opus_config; + +- + /** +- * Initialize and register Opus codec factory to pjmedia endpoint. +- * +- * @param endpt The pjmedia endpoint. +- * +- * @return PJ_SUCCESS on success. +- */ +-PJ_DECL(pj_status_t) pjmedia_codec_opus_init( pjmedia_endpt *endpt ); +- +-/** +- * Unregister Opus codec factory from pjmedia endpoint and deinitialize +- * the Opus codec library. +- * +- * @return PJ_SUCCESS on success. +- */ +-PJ_DECL(pj_status_t) pjmedia_codec_opus_deinit( void ); +- +-/** +- * Get the default Opus configuration. +- * +- * @param cfg Opus codec configuration. +- * +- * @return PJ_SUCCESS on success. +- */ +-PJ_DECL(pj_status_t) +-pjmedia_codec_opus_get_config( pjmedia_codec_opus_config *cfg ); +- +-/** +- * Set the default Opus configuration and set the default Opus codec param. +- * Note that the function will call #pjmedia_codec_mgr_set_default_param(). +- * +- * @param cfg Opus codec configuration. +- * @param param The new default Opus codec parameter. +- * +- * @return PJ_SUCCESS on success. +- */ +-PJ_DECL(pj_status_t) +-pjmedia_codec_opus_set_default_param(const pjmedia_codec_opus_config *cfg, +- pjmedia_codec_param *param ); +- +-PJ_END_DECL +- +-/** + * @} + */ + +-#endif /* __PJMEDIA_CODEC_OPUS_H__ */ ++#endif /* __PJMEDIA_CODEC_OPUS_CODEC_H__ */ +Index: pjmedia/include/pjmedia-codec/types.h +=================================================================== +--- pjmedia/include/pjmedia-codec/types.h (revision 5249) ++++ pjmedia/include/pjmedia-codec/types.h (working copy) +@@ -71,6 +71,7 @@ + PJMEDIA_RTP_PT_AMR, /**< AMR (4.75 - 12.2Kbps) */ + PJMEDIA_RTP_PT_AMRWB, /**< AMRWB (6.6 - 23.85Kbps)*/ + PJMEDIA_RTP_PT_AMRWBE, /**< AMRWBE */ ++ PJMEDIA_RTP_PT_OPUS, /**< OPUS */ + PJMEDIA_RTP_PT_G726_16, /**< G726 @ 16Kbps */ + PJMEDIA_RTP_PT_G726_24, /**< G726 @ 24Kbps */ + PJMEDIA_RTP_PT_G726_32, /**< G726 @ 32Kbps */ +@@ -83,7 +84,6 @@ + PJMEDIA_RTP_PT_G7221C_48, /**< G722.1 Annex C (48Kbps)*/ + PJMEDIA_RTP_PT_G7221_RSV1, /**< G722.1 reserve */ + PJMEDIA_RTP_PT_G7221_RSV2, /**< G722.1 reserve */ +- PJMEDIA_RTP_PT_OPUS, /**< OPUS */ + PJMEDIA_RTP_PT_L16_8KHZ_MONO, /**< L16 @ 8KHz, mono */ + PJMEDIA_RTP_PT_L16_8KHZ_STEREO, /**< L16 @ 8KHz, stereo */ + //PJMEDIA_RTP_PT_L16_11KHZ_MONO, /**< L16 @ 11KHz, mono */ +@@ -116,6 +116,7 @@ + PJMEDIA_RTP_PT_H264_RSV2, + PJMEDIA_RTP_PT_H264_RSV3, + PJMEDIA_RTP_PT_H264_RSV4, ++ PJMEDIA_RTP_PT_VP8, + + /* Caution! + * Ensure the value of the last pt above is <= 127. +Index: pjmedia/include/pjmedia-codec.h +=================================================================== +--- pjmedia/include/pjmedia-codec.h (revision 5249) ++++ pjmedia/include/pjmedia-codec.h (working copy) +@@ -26,18 +26,15 @@ + */ + + #include +-#include + #include ++#include + #include + #include + #include + #include + #include +-#include +-#include + #include + #include +-#include + #include + + +Index: pjmedia/include/pjmedia-videodev/config.h +=================================================================== +--- pjmedia/include/pjmedia-videodev/config.h (revision 5249) ++++ pjmedia/include/pjmedia-videodev/config.h (working copy) +@@ -74,95 +74,16 @@ + + + /** +- * This setting controls whether OpenGL for iOS should be included. ++ * This setting controls whether AVFoundation support should be included. + * + * Default: 0 (or detected by configure) + */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL +-# define PJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL 0 +-#else +-# if defined(PJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL) && \ +- PJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL != 0 +-# undef PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES +-# define PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES 1 +-# endif ++#ifndef PJMEDIA_VIDEO_DEV_HAS_AVF ++# define PJMEDIA_VIDEO_DEV_HAS_AVF 0 + #endif + + + /** +- * This setting controls whether OpenGL for Android should be included. +- * +- * Default: 0 (or detected by configure) +- */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_ANDROID_OPENGL +-# define PJMEDIA_VIDEO_DEV_HAS_ANDROID_OPENGL 0 +-#else +-# if defined(PJMEDIA_VIDEO_DEV_HAS_ANDROID_OPENGL) && \ +- PJMEDIA_VIDEO_DEV_HAS_ANDROID_OPENGL != 0 +-# undef PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES +-# define PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES 1 +-# endif +-#endif +- +- +-/** +- * This setting controls whether OpenGL ES support should be included. +- * +- * Default: 0 (or detected by configure) +- */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES +-# define PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES 0 +-#else +-# if defined(PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES) && \ +- PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES != 0 +-# undef PJMEDIA_VIDEO_DEV_HAS_OPENGL +-# define PJMEDIA_VIDEO_DEV_HAS_OPENGL 1 +-# endif +-#endif +- +- +-/** +- * This setting controls whether OpenGL support should be included. +- * +- * Default: 0 (or detected by configure) +- */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_OPENGL +-# define PJMEDIA_VIDEO_DEV_HAS_OPENGL 0 +-#endif +- +- +-/** +- * This setting controls whether SDL support should be included. +- * +- * Default: 0 (or detected by configure) +- */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_SDL +-# define PJMEDIA_VIDEO_DEV_HAS_SDL 0 +-# define PJMEDIA_VIDEO_DEV_SDL_HAS_OPENGL 0 +-#endif +- +- +-/** +- * This setting controls whether QT support should be included. +- * +- * Default: 0 (or detected by configure) +- */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_QT +-# define PJMEDIA_VIDEO_DEV_HAS_QT 0 +-#endif +- +- +-/** +- * This setting controls whether IOS support should be included. +- * +- * Default: 0 (or detected by configure) +- */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_IOS +-# define PJMEDIA_VIDEO_DEV_HAS_IOS 0 +-#endif +- +- +-/** + * This setting controls whether Direct Show support should be included. + * + * Default: 0 (unfinished) +@@ -183,16 +104,6 @@ + + + /** +- * This setting controls whether ffmpeg support should be included. +- * +- * Default: 0 (unfinished) +- */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_FFMPEG +-# define PJMEDIA_VIDEO_DEV_HAS_FFMPEG 0 +-#endif +- +- +-/** + * Video4Linux2 + * + * Default: 0 (or detected by configure) +@@ -211,29 +122,16 @@ + # define PJMEDIA_VIDEO_DEV_HAS_AVI 1 + #endif + +- + /** +- * This setting controls whether Android support should be included. ++ * Enable support for frame buffer render device. + * +- * Default: 0 (or detected by configure) ++ * Default: 1 + */ +-#ifndef PJMEDIA_VIDEO_DEV_HAS_ANDROID +-# define PJMEDIA_VIDEO_DEV_HAS_ANDROID 0 ++#ifndef PJMEDIA_VIDEO_DEV_HAS_FB ++# define PJMEDIA_VIDEO_DEV_HAS_FB 1 + #endif + + +-/** +- * Specify the SDL library name to be linked with Visual Studio project. +- * By default, the name is autodetected based on SDL version ("sdl.lib" or +- * "sdl2.lib"), but application may explicitly specify the library name if this +- * autodetection fails. Common names are: "sdl2.lib" or "sdl.lib". +- * +- * Default: undeclared. +- */ +-#ifndef PJMEDIA_SDL_LIB +-# undef PJMEDIA_SDL_LIB +-#endif +- + #endif /* defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) */ + + /** +Index: pjmedia/include/pjmedia.h +=================================================================== +--- pjmedia/include/pjmedia.h (revision 5249) ++++ pjmedia/include/pjmedia.h (working copy) +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -67,6 +68,7 @@ + #include + #include + #include ++#include + #include + #include + #include +Index: pjmedia/include/pjmedia_audiodev.h +=================================================================== +--- pjmedia/include/pjmedia_audiodev.h (revision 5249) ++++ pjmedia/include/pjmedia_audiodev.h (working copy) +@@ -27,7 +27,6 @@ + + #include + #include +-#include + + #endif /* __PJMEDIA_AUDIODEV_H__ */ + +Index: pjmedia/include/pjmedia_videodev.h +=================================================================== +--- pjmedia/include/pjmedia_videodev.h (revision 5249) ++++ pjmedia/include/pjmedia_videodev.h (working copy) +@@ -27,5 +27,6 @@ + #include + #include + #include ++#include + + #endif /* __PJMEDIA_VIDEODEV_H__ */ +Index: pjmedia/src/pjmedia/converter.c +=================================================================== +--- pjmedia/src/pjmedia/converter.c (revision 5249) ++++ pjmedia/src/pjmedia/converter.c (working copy) +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + #define THIS_FILE "converter.c" + +@@ -119,7 +120,7 @@ + + pf = mgr->factory_list.next; + while (pf != &mgr->factory_list) { +- if (pf->priority > factory->priority) ++ if (pf->priority < factory->priority) + break; + pf = pf->next; + } +@@ -170,8 +171,25 @@ + if (status != PJ_SUCCESS) + return status; + ++ if (param->src.type == PJMEDIA_TYPE_VIDEO) { ++ char src_fourcc_name[5]; ++ char dst_fourcc_name[5]; ++ PJ_LOG(4, (THIS_FILE, "Converter %p (%s) created for video: %dx%d %s -> %dx%d %s", ++ cv, ++ f->name, ++ param->src.det.vid.size.w, ++ param->src.det.vid.size.h, ++ pjmedia_fourcc_name(param->src.id, src_fourcc_name), ++ param->dst.det.vid.size.w, ++ param->dst.det.vid.size.h, ++ pjmedia_fourcc_name(param->dst.id, dst_fourcc_name))); ++ } else if (param->src.type == PJMEDIA_TYPE_AUDIO) { ++ PJ_LOG(4, (THIS_FILE, "Converter %p created for audio", cv)); ++ } else { ++ PJ_LOG(4, (THIS_FILE, "Converter %p created for unknown", cv)); ++ } ++ + *p_cv = cv; +- + return PJ_SUCCESS; + } + +@@ -184,6 +202,7 @@ + + PJ_DEF(void) pjmedia_converter_destroy(pjmedia_converter *cv) + { ++ PJ_LOG(4, (THIS_FILE, "Converter %p destroyed", cv)); + (*cv->op->destroy)(cv); + } + +Index: pjmedia/src/pjmedia/echo_common.c +=================================================================== +--- pjmedia/src/pjmedia/echo_common.c (revision 5249) ++++ pjmedia/src/pjmedia/echo_common.c (working copy) +@@ -124,13 +124,14 @@ + }; + #endif + ++ + /* +- * WebRTC AEC prototypes ++ * WebRTC prototypes + */ + #if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0 + static struct ec_operations webrtc_aec_op = + { +- "WebRTC AEC", ++ "WEBRTC AEC", + &webrtc_aec_create, + &webrtc_aec_destroy, + &webrtc_aec_reset, +@@ -138,6 +139,7 @@ + }; + #endif + ++ + /* + * Create the echo canceller. + */ +@@ -191,6 +193,13 @@ + ec->op = &speex_aec_op; + #endif + ++#if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0 ++ } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_WEBRTC || ++ (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) ++ { ++ ec->op = &webrtc_aec_op; ++#endif ++ + #if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 + } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_IPP || + (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) +@@ -199,13 +208,6 @@ + + #endif + +-#if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0 +- } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_WEBRTC || +- (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) +- { +- ec->op = &webrtc_aec_op; +-#endif +- + } else { + ec->op = &echo_supp_op; + } +Index: pjmedia/src/pjmedia/echo_internal.h +=================================================================== +--- pjmedia/src/pjmedia/echo_internal.h (revision 5249) ++++ pjmedia/src/pjmedia/echo_internal.h (working copy) +@@ -78,21 +78,20 @@ + void *reserved ); + + PJ_DECL(pj_status_t) webrtc_aec_create(pj_pool_t *pool, +- unsigned clock_rate, +- unsigned channel_count, +- unsigned samples_per_frame, +- unsigned tail_ms, +- unsigned options, +- void **p_echo ); ++ unsigned clock_rate, ++ unsigned channel_count, ++ unsigned samples_per_frame, ++ unsigned tail_ms, ++ unsigned options, ++ void **p_echo ); + PJ_DECL(pj_status_t) webrtc_aec_destroy(void *state ); + PJ_DECL(void) webrtc_aec_reset(void *state ); + PJ_DECL(pj_status_t) webrtc_aec_cancel_echo(void *state, +- pj_int16_t *rec_frm, +- const pj_int16_t *play_frm, +- unsigned options, +- void *reserved ); ++ pj_int16_t *rec_frm, ++ const pj_int16_t *play_frm, ++ unsigned options, ++ void *reserved ); + +- + PJ_END_DECL + + #endif +Index: pjmedia/src/pjmedia/endpoint.c +=================================================================== +--- pjmedia/src/pjmedia/endpoint.c (revision 5249) ++++ pjmedia/src/pjmedia/endpoint.c (working copy) +@@ -568,6 +568,7 @@ + /* Put bandwidth info in media level using bandwidth modifier "TIAS" + * (RFC3890). + */ ++#if 0 + if (max_bitrate && pjmedia_add_bandwidth_tias_in_sdp) { + const pj_str_t STR_BANDW_MODIFIER = { "TIAS", 4 }; + pjmedia_sdp_bandw *b; +@@ -577,6 +578,7 @@ + b->value = max_bitrate; + m->bandw[m->bandw_count++] = b; + } ++#endif + + *p_m = m; + return PJ_SUCCESS; +Index: pjmedia/src/pjmedia/errno.c +=================================================================== +--- pjmedia/src/pjmedia/errno.c (revision 5249) ++++ pjmedia/src/pjmedia/errno.c (working copy) +@@ -20,10 +20,6 @@ + #include + #include + #include +-#if defined(PJMEDIA_SOUND_IMPLEMENTATION) && \ +- PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_PORTAUDIO_SOUND +-# include +-#endif + + #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + PJ_BEGIN_DECL +@@ -183,27 +179,6 @@ + + #if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0) + +- /* See if the error comes from PortAudio. */ +-#if defined(PJMEDIA_SOUND_IMPLEMENTATION) && \ +- PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_PORTAUDIO_SOUND +- if (statcode >= PJMEDIA_PORTAUDIO_ERRNO_START && +- statcode <= PJMEDIA_PORTAUDIO_ERRNO_END) +- { +- +- //int pa_err = statcode - PJMEDIA_ERRNO_FROM_PORTAUDIO(0); +- int pa_err = PJMEDIA_PORTAUDIO_ERRNO_START - statcode; +- pj_str_t msg; +- +- msg.ptr = (char*)Pa_GetErrorText(pa_err); +- msg.slen = pj_ansi_strlen(msg.ptr); +- +- errstr.ptr = buf; +- pj_strncpy_with_null(&errstr, &msg, bufsize); +- return errstr; +- +- } else +-#endif /* PJMEDIA_SOUND_IMPLEMENTATION */ +- + #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + /* LIBSRTP error */ + if (statcode >= PJMEDIA_LIBSRTP_ERRNO_START && +Index: pjmedia/src/pjmedia/ffmpeg_util.c +=================================================================== +--- pjmedia/src/pjmedia/ffmpeg_util.c (revision 5249) ++++ pjmedia/src/pjmedia/ffmpeg_util.c (working copy) +@@ -26,6 +26,7 @@ + #include "ffmpeg_util.h" + #include + ++ + /* Conversion table between pjmedia_format_id and AVPixelFormat */ + static const struct ffmpeg_fmt_table_t + { +@@ -33,10 +34,11 @@ + enum AVPixelFormat pf; + } ffmpeg_fmt_table[] = + { ++ { PJMEDIA_FORMAT_ARGB, AV_PIX_FMT_ARGB}, + { PJMEDIA_FORMAT_RGBA, AV_PIX_FMT_RGBA}, + { PJMEDIA_FORMAT_RGB24,AV_PIX_FMT_BGR24}, + { PJMEDIA_FORMAT_BGRA, AV_PIX_FMT_BGRA}, +- { PJMEDIA_FORMAT_GBRP, AV_PIX_FMT_GBRP}, ++ { PJMEDIA_FORMAT_GBRP, AV_PIX_FMT_GBR24P}, + + { PJMEDIA_FORMAT_AYUV, AV_PIX_FMT_NONE}, + { PJMEDIA_FORMAT_YUY2, AV_PIX_FMT_YUYV422}, +Index: pjmedia/src/pjmedia/format.c +=================================================================== +--- pjmedia/src/pjmedia/format.c (revision 5249) ++++ pjmedia/src/pjmedia/format.c (working copy) +@@ -74,6 +74,7 @@ + static pjmedia_video_format_info built_in_vid_fmt_info[] = + { + {PJMEDIA_FORMAT_RGB24, "RGB24", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt}, ++ {PJMEDIA_FORMAT_ARGB, "ARGB", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt}, + {PJMEDIA_FORMAT_RGBA, "RGBA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt}, + {PJMEDIA_FORMAT_BGRA, "BGRA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt}, + {PJMEDIA_FORMAT_DIB , "DIB ", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt}, +Index: pjmedia/src/pjmedia/rtcp.c +=================================================================== +--- pjmedia/src/pjmedia/rtcp.c (revision 5249) ++++ pjmedia/src/pjmedia/rtcp.c (working copy) +@@ -31,6 +31,7 @@ + #define RTCP_RR 201 + #define RTCP_SDES 202 + #define RTCP_BYE 203 ++#define RTCP_PSFB 206 /* Payload-specific FB message (RFC 4585) */ + #define RTCP_XR 207 + + enum { +@@ -764,6 +765,21 @@ + } + + ++static void parse_rtcp_psfb(pjmedia_rtcp_session *sess, ++ const void *pkt, ++ pj_size_t size) ++{ ++ pjmedia_rtcp_common *common = (pjmedia_rtcp_common*)pkt; ++ pj_assert(common->pt == RTCP_PSFB); ++ ++ if (common->count == 1) { ++ /* It's a PLI */ ++ PJ_LOG(5, (sess->name, "Received RTCP PLI")); ++ sess->keyframe_requested = PJ_TRUE; ++ } ++} ++ ++ + PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess, + const void *pkt, + pj_size_t size) +@@ -770,6 +786,8 @@ + { + pj_uint8_t *p, *p_end; + ++ sess->keyframe_requested = PJ_FALSE; ++ + p = (pj_uint8_t*)pkt; + p_end = p + size; + while (p < p_end) { +@@ -789,6 +807,9 @@ + case RTCP_BYE: + parse_rtcp_bye(sess, p, len); + break; ++ case RTCP_PSFB: ++ parse_rtcp_psfb(sess, p, len); ++ break; + default: + /* Ignore unknown RTCP */ + TRACE_((sess->name, "Received unknown RTCP packet type=%d", +@@ -1072,6 +1093,34 @@ + } + + ++PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_pli(pjmedia_rtcp_session *session, ++ void *buf, ++ pj_size_t *length) ++{ ++ pjmedia_rtcp_common *hdr; ++ pj_uint8_t *p; ++ pj_size_t len = 12; /* pjmedia_rtcp_common + media SSRC (uint32_t) */ ++ ++ PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL); ++ ++ /* Verify buffer length */ ++ if (len > *length) ++ return PJ_ETOOSMALL; ++ ++ /* Build RTCP PLI */ ++ hdr = (pjmedia_rtcp_common*)buf; ++ pj_memcpy(hdr, &session->rtcp_sr_pkt.common, sizeof(*hdr)); ++ hdr->pt = RTCP_PSFB; ++ hdr->count = 1; /* FMT: 1 == Picture Loss Indication (PLI) */ ++ hdr->length = pj_htons((pj_uint16_t)(len/4 - 1)); ++ ++ p = (pj_uint8_t*)hdr + sizeof(*hdr); ++ pj_memset(p, 0, (pj_uint8_t*)hdr + len - p); ++ *length = len; ++ return PJ_SUCCESS; ++} ++ ++ + PJ_DEF(pj_status_t) pjmedia_rtcp_enable_xr( pjmedia_rtcp_session *sess, + pj_bool_t enable) + { +Index: pjmedia/src/pjmedia/sdp_neg.c +=================================================================== +--- pjmedia/src/pjmedia/sdp_neg.c (revision 5249) ++++ pjmedia/src/pjmedia/sdp_neg.c (working copy) +@@ -701,9 +701,8 @@ + return PJ_SUCCESS; + } + +- /* Process direction attributes */ +- update_media_direction(pool, answer, offer); +- ++ /* No need to update the direction when processing an answer */ ++ + /* If asymetric media is allowed, then just check that remote answer has + * codecs that are within the offer. + * +Index: pjmedia/src/pjmedia/sound_legacy.c +=================================================================== +--- pjmedia/src/pjmedia/sound_legacy.c (revision 5249) ++++ pjmedia/src/pjmedia/sound_legacy.c (working copy) +@@ -124,12 +124,6 @@ + pjmedia_aud_param param; + pj_status_t status; + +- /* Normalize rec_id & play_id */ +- if (dir & PJMEDIA_DIR_CAPTURE && rec_id < 0) +- rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV; +- if (dir & PJMEDIA_DIR_PLAYBACK && play_id < 0) +- play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; +- + /* Initialize parameters */ + if (dir & PJMEDIA_DIR_CAPTURE) { + status = pjmedia_aud_dev_default_param(rec_id, ¶m); +Index: pjmedia/src/pjmedia/sound_port.c +=================================================================== +--- pjmedia/src/pjmedia/sound_port.c (revision 5249) ++++ pjmedia/src/pjmedia/sound_port.c (working copy) +@@ -98,7 +98,7 @@ + if (snd_port->ec_state) { + if (snd_port->ec_suspended) { + snd_port->ec_suspended = PJ_FALSE; +- //pjmedia_echo_state_reset(snd_port->ec_state); ++ pjmedia_echo_reset(snd_port->ec_state); + PJ_LOG(4,(THIS_FILE, "EC activated")); + } + snd_port->ec_suspend_count = 0; +@@ -311,15 +311,10 @@ + PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms", + snd_port->aud_param.ec_tail_ms)); + } +- +- status = pjmedia_snd_port_set_ec(snd_port, pool, +- snd_port->aud_param.ec_tail_ms, +- snd_port->prm_ec_options); +- if (status != PJ_SUCCESS) { +- pjmedia_aud_stream_destroy(snd_port->aud_stream); +- snd_port->aud_stream = NULL; +- return status; +- } ++ ++ pjmedia_snd_port_set_ec(snd_port, pool, ++ snd_port->aud_param.ec_tail_ms, ++ snd_port->prm_ec_options); + } + + /* Start sound stream. */ +@@ -550,6 +545,18 @@ + } + + ++/* Reset EC state */ ++PJ_DEF(pj_status_t) pjmedia_snd_port_reset_ec_state( pjmedia_snd_port *snd_port ) ++{ ++ PJ_ASSERT_RETURN(snd_port, PJ_EINVAL); ++ if (snd_port->ec_state) { ++ pjmedia_echo_reset(snd_port->ec_state); ++ PJ_LOG(4,(THIS_FILE, "EC reset")); ++ } ++ return PJ_SUCCESS; ++} ++ ++ + /* + * Change EC settings. + */ +Index: pjmedia/src/pjmedia/stream_info.c +=================================================================== +--- pjmedia/src/pjmedia/stream_info.c (revision 5249) ++++ pjmedia/src/pjmedia/stream_info.c (working copy) +@@ -33,47 +33,6 @@ + static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; + static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 }; + +-static void get_opus_channels_and_clock_rate(const pjmedia_codec_fmtp *enc_fmtp, +- const pjmedia_codec_fmtp *dec_fmtp, +- unsigned *channel_cnt, +- unsigned *clock_rate) +-{ +- unsigned i; +- unsigned enc_channel_cnt = 0, local_channel_cnt = 0; +- unsigned enc_clock_rate = 0, local_clock_rate = 0; +- +- for (i = 0; i < dec_fmtp->cnt; ++i) { +- if (!pj_stricmp2(&dec_fmtp->param[i].name, "sprop-maxcapturerate")) { +- local_clock_rate = (unsigned)pj_strtoul(&dec_fmtp->param[i].val); +- } else if (!pj_stricmp2(&dec_fmtp->param[i].name, "sprop-stereo")) { +- local_channel_cnt = (unsigned)pj_strtoul(&dec_fmtp->param[i].val); +- local_channel_cnt = (local_channel_cnt > 0) ? 2 : 1; +- } +- } +- if (!local_clock_rate) local_clock_rate = *clock_rate; +- if (!local_channel_cnt) local_channel_cnt = *channel_cnt; +- +- for (i = 0; i < enc_fmtp->cnt; ++i) { +- if (!pj_stricmp2(&enc_fmtp->param[i].name, "maxplaybackrate")) { +- enc_clock_rate = (unsigned)pj_strtoul(&enc_fmtp->param[i].val); +- } else if (!pj_stricmp2(&enc_fmtp->param[i].name, "stereo")) { +- enc_channel_cnt = (unsigned)pj_strtoul(&enc_fmtp->param[i].val); +- enc_channel_cnt = (enc_channel_cnt > 0) ? 2 : 1; +- } +- } +- /* The default is a standard mono session with 48000 Hz clock rate +- * (RFC 7587, section 7) +- */ +- if (!enc_clock_rate) enc_clock_rate = 48000; +- if (!enc_channel_cnt) enc_channel_cnt = 1; +- +- *clock_rate = (enc_clock_rate < local_clock_rate) ? enc_clock_rate : +- local_clock_rate; +- +- *channel_cnt = (enc_channel_cnt < local_channel_cnt) ? enc_channel_cnt : +- local_channel_cnt; +-} +- + /* + * Internal function for collecting codec info and param from the SDP media. + */ +@@ -259,14 +218,6 @@ + pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, + &si->param->setting.dec_fmtp); + +- if (!pj_stricmp2(&si->fmt.encoding_name, "opus")) { +- get_opus_channels_and_clock_rate(&si->param->setting.enc_fmtp, +- &si->param->setting.dec_fmtp, +- &si->fmt.channel_cnt, +- &si->fmt.clock_rate); +- } +- +- + /* Get the remote ptime for our encoder. */ + attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, + "ptime", NULL); +Index: pjmedia/src/pjmedia/transport_ice.c +=================================================================== +--- pjmedia/src/pjmedia/transport_ice.c (revision 5249) ++++ pjmedia/src/pjmedia/transport_ice.c (working copy) +@@ -147,6 +147,9 @@ + static void ice_on_ice_complete(pj_ice_strans *ice_st, + pj_ice_strans_op op, + pj_status_t status); ++static void ice_on_ice_state(pj_ice_strans *ice_st, ++ pj_ice_strans_state prev, ++ pj_ice_strans_state curr); + + + static pjmedia_transport_op transport_ice_op = +@@ -258,6 +261,7 @@ + /* Configure ICE callbacks */ + pj_bzero(&ice_st_cb, sizeof(ice_st_cb)); + ice_st_cb.on_ice_complete = &ice_on_ice_complete; ++ ice_st_cb.on_ice_state = &ice_on_ice_state; + ice_st_cb.on_rx_data = &ice_on_rx_data; + + /* Configure RTP socket buffer settings, if not set */ +@@ -283,6 +287,17 @@ + return PJ_SUCCESS; + } + ++/* ++ * Get the ICE stream transport associated with this media transport. ++ */ ++PJ_DEF(pj_ice_strans*) pjmedia_ice_get_strans(pjmedia_transport *tp) ++{ ++ struct transport_ice *tp_ice; ++ ++ tp_ice = (struct transport_ice*) tp; ++ return tp_ice->ice_st; ++} ++ + PJ_DEF(pj_grp_lock_t *) pjmedia_ice_get_grp_lock(pjmedia_transport *tp) + { + PJ_ASSERT_RETURN(tp, NULL); +@@ -303,6 +318,10 @@ + "Stopping ICE, reason=%s", reason)); + } + ++ /* Notify application about ICE stop */ ++ if (tp_ice->cb.on_ice_stop) ++ (*tp_ice->cb.on_ice_stop)(&tp_ice->base, (char *)reason, err); ++ + if (tp_ice->ice_st) { + pj_ice_strans_stop_ice(tp_ice->ice_st); + } +@@ -1526,7 +1545,7 @@ + if (status != PJ_SUCCESS) + return status; + +- pj_sockaddr_cp(&info->sock_info.rtp_addr_name, &cand.addr); ++ pj_sockaddr_cp(&info->sock_info.rtp_addr_name, &cand.base_addr); + + /* Get RTCP default address */ + if (tp_ice->comp_cnt > 1) { +@@ -1534,7 +1553,7 @@ + if (status != PJ_SUCCESS) + return status; + +- pj_sockaddr_cp(&info->sock_info.rtcp_addr_name, &cand.addr); ++ pj_sockaddr_cp(&info->sock_info.rtcp_addr_name, &cand.base_addr); + } + + /* Set remote address originating RTP & RTCP if this transport has +@@ -1833,6 +1852,20 @@ + } + + ++static void ice_on_ice_state(pj_ice_strans *ice_st, ++ pj_ice_strans_state prev, ++ pj_ice_strans_state curr) ++{ ++ struct transport_ice *tp_ice; ++ ++ tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st); ++ ++ /* Notify application */ ++ if (tp_ice->cb.on_ice_state) ++ (*tp_ice->cb.on_ice_state)(&tp_ice->base, prev, curr); ++} ++ ++ + /* Simulate lost */ + static pj_status_t transport_simulate_lost(pjmedia_transport *tp, + pjmedia_dir dir, +Index: pjmedia/src/pjmedia/transport_srtp.c +=================================================================== +--- pjmedia/src/pjmedia/transport_srtp.c (revision 5249) ++++ pjmedia/src/pjmedia/transport_srtp.c (working copy) +@@ -1337,6 +1337,10 @@ + pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0) + goto BYPASS_SRTP; + ++ /* Do nothing if we are in bypass */ ++ if (srtp->bypass_srtp) ++ goto BYPASS_SRTP; ++ + /* If the media is inactive, do nothing. */ + /* No, we still need to process SRTP offer/answer even if the media is + * marked as inactive, because the transport is still alive in this +Index: pjmedia/src/pjmedia/vid_codec_util.c +=================================================================== +--- pjmedia/src/pjmedia/vid_codec_util.c (revision 5249) ++++ pjmedia/src/pjmedia/vid_codec_util.c (working copy) +@@ -515,15 +515,13 @@ + if (option & PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER) { + unsigned i; + +- /* Flexible negotiation, if the answer has higher capability than +- * the offer, adjust the answer capability to be match to the offer. ++ /* Flexible negotiation, adjust our answer to the offer. ++ * Apply Postel's Principle (TM) in it's full glory. + */ +- if (a_fmtp.profile_idc >= o_fmtp.profile_idc) ++ if (a_fmtp.profile_idc != o_fmtp.profile_idc) + a_fmtp.profile_idc = o_fmtp.profile_idc; + if (a_fmtp.profile_iop != o_fmtp.profile_iop) + a_fmtp.profile_iop = o_fmtp.profile_iop; +- if (a_fmtp.level >= o_fmtp.level) +- a_fmtp.level = o_fmtp.level; + if (a_fmtp.packetization_mode >= o_fmtp.packetization_mode) + a_fmtp.packetization_mode = o_fmtp.packetization_mode; + +@@ -531,7 +529,6 @@ + #if H264_STRICT_SDP_NEGO + if (a_fmtp.profile_idc != o_fmtp.profile_idc || + a_fmtp.profile_iop != o_fmtp.profile_iop || +- a_fmtp.level != o_fmtp.level || + a_fmtp.packetization_mode != o_fmtp.packetization_mode) + { + return PJMEDIA_SDP_EFORMATNOTEQUAL; +@@ -551,8 +548,6 @@ + pj_val_to_hex_digit(a_fmtp.profile_idc, p); + p += 2; + pj_val_to_hex_digit(a_fmtp.profile_iop, p); +- p += 2; +- pj_val_to_hex_digit(a_fmtp.level, p); + } + else if (pj_stricmp(&a_fmtp_raw.param[i].name, &PACKETIZATION_MODE) == 0) + { +@@ -565,7 +560,6 @@ + /* Strict negotiation */ + if (a_fmtp.profile_idc != o_fmtp.profile_idc || + a_fmtp.profile_iop != o_fmtp.profile_iop || +- a_fmtp.level != o_fmtp.level || + a_fmtp.packetization_mode != o_fmtp.packetization_mode) + { + return PJMEDIA_SDP_EFORMATNOTEQUAL; +Index: pjmedia/src/pjmedia/vid_port.c +=================================================================== +--- pjmedia/src/pjmedia/vid_port.c (revision 5249) ++++ pjmedia/src/pjmedia/vid_port.c (working copy) +@@ -964,6 +964,7 @@ + PJ_LOG(3, (THIS_FILE, "reverting to its original format: %s", + status != PJMEDIA_EVID_ERR ? "success" : + "failure")); ++ pjmedia_vid_port_start(vp); + return status; + } + } +Index: pjmedia/src/pjmedia/vid_stream.c +=================================================================== +--- pjmedia/src/pjmedia/vid_stream.c (revision 5249) ++++ pjmedia/src/pjmedia/vid_stream.c (working copy) +@@ -142,10 +142,18 @@ + pjmedia_frame dec_frame; /**< Current decoded frame. */ + pjmedia_event fmt_event; /**< Buffered fmt_changed event + to avoid deadlock */ ++ pjmedia_event found_keyframe_event; ++ /**< Buffered found keyframe ++ event for delayed republish*/ ++ + pjmedia_event miss_keyframe_event; + /**< Buffered missing keyframe + event for delayed republish*/ + ++ pjmedia_event keyframe_req_event; ++ /**< Buffered keyframe request ++ event for delayed republish*/ ++ + unsigned frame_size; /**< Size of encoded base frame.*/ + unsigned frame_ts_len; /**< Frame length in timestamp. */ + +@@ -362,7 +370,7 @@ + const pjmedia_port_info *pi = &chan->port.info; + char fourcc_name[5]; + +- PJ_LOG(5, (pi->name.ptr, ++ PJ_LOG(4, (pi->name.ptr, + " %s format %s: %dx%d %s%s %d/%d(~%d)fps", + (chan->dir==PJMEDIA_DIR_DECODING? "Decoding":"Encoding"), + event_name, +@@ -392,6 +400,11 @@ + pj_memcpy(&stream->fmt_event, event, sizeof(*event)); + return PJ_SUCCESS; + ++ case PJMEDIA_EVENT_KEYFRAME_FOUND: ++ /* Republish this event later from get_frame(). */ ++ pj_memcpy(&stream->found_keyframe_event, event, sizeof(*event)); ++ return PJ_SUCCESS; ++ + case PJMEDIA_EVENT_KEYFRAME_MISSING: + /* Republish this event later from get_frame(). */ + pj_memcpy(&stream->miss_keyframe_event, event, sizeof(*event)); +@@ -797,6 +810,13 @@ + } + + pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read); ++ ++ /* XXX: posting some event from the RTCP session might be a better option */ ++ if (stream->rtcp.keyframe_requested) { ++ pjmedia_event event; ++ pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_REQUESTED, NULL, stream); ++ pj_memcpy(&stream->keyframe_req_event, &event, sizeof(event)); ++ } + } + + static pj_status_t put_frame(pjmedia_port *port, +@@ -1137,12 +1157,13 @@ + stream->info.codec_param->dec_fmt.det.vid.fps = vfd->fps; + + /* Publish PJMEDIA_EVENT_FMT_CHANGED event if frame rate +- * increased and not exceeding 100fps. ++ * increased and not exceeding 60fps. + */ +- if (vfd->fps.num/vfd->fps.denum <= 100 && ++ if (vfd->fps.num/vfd->fps.denum <= 60.0 && + vfd->fps.num * stream->dec_max_fps.denum > + stream->dec_max_fps.num * vfd->fps.denum) + { ++ /*printf("FPS CHANGED: %d/%d -> %d/%d\n", stream->dec_max_fps.num, stream->dec_max_fps.denum, vfd->fps.num, vfd->fps.denum);*/ + pjmedia_event *event = &stream->fmt_event; + + /* Update max fps of decoding dir */ +@@ -1208,8 +1229,10 @@ + /* Override the framerate to be 1.5x higher in the event + * for the renderer. + */ ++#if 0 + fmt_chg_data->new_fmt.det.vid.fps.num *= 3; + fmt_chg_data->new_fmt.det.vid.fps.num /= 2; ++#endif + } else { + pjmedia_format_copy(&stream->info.codec_param->enc_fmt, + &fmt_chg_data->new_fmt); +@@ -1226,6 +1249,12 @@ + stream->fmt_event.type = PJMEDIA_EVENT_NONE; + } + ++ if (stream->found_keyframe_event.type != PJMEDIA_EVENT_NONE) { ++ pjmedia_event_publish(NULL, port, &stream->found_keyframe_event, ++ PJMEDIA_EVENT_PUBLISH_POST_EVENT); ++ stream->found_keyframe_event.type = PJMEDIA_EVENT_NONE; ++ } ++ + if (stream->miss_keyframe_event.type != PJMEDIA_EVENT_NONE) { + pjmedia_event_publish(NULL, port, &stream->miss_keyframe_event, + PJMEDIA_EVENT_PUBLISH_POST_EVENT); +@@ -1232,6 +1261,12 @@ + stream->miss_keyframe_event.type = PJMEDIA_EVENT_NONE; + } + ++ if (stream->keyframe_req_event.type != PJMEDIA_EVENT_NONE) { ++ pjmedia_event_publish(NULL, port, &stream->keyframe_req_event, ++ PJMEDIA_EVENT_PUBLISH_POST_EVENT); ++ stream->keyframe_req_event.type = PJMEDIA_EVENT_NONE; ++ } ++ + pj_mutex_lock( stream->jb_mutex ); + + if (stream->dec_frame.size == 0) { +@@ -1535,7 +1570,9 @@ + * local renderer clock) or video setup lag. Note that the actual framerate + * will be continuously calculated based on the incoming RTP timestamps. + */ ++#if 0 + vfd_dec->fps.num = vfd_dec->fps.num * 3 / 2; ++#endif + stream->dec_max_fps = vfd_dec->fps; + + /* Create decoder channel */ +@@ -1999,6 +2036,47 @@ + + + /* ++ * Send RTCP PLI. ++ */ ++PJ_DEF(pj_status_t) pjmedia_vid_stream_send_rtcp_pli( ++ pjmedia_vid_stream *stream) ++{ ++ PJ_ASSERT_RETURN(stream, PJ_EINVAL); ++ ++ if (stream->enc && stream->transport) { ++ void *sr_rr_pkt; ++ pj_uint8_t *pkt; ++ int len, max_len; ++ pj_status_t status; ++ pj_size_t pli_len; ++ ++ /* Build RTCP RR/SR packet */ ++ pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len); ++ ++ pkt = (pj_uint8_t*) stream->out_rtcp_pkt; ++ pj_memcpy(pkt, sr_rr_pkt, len); ++ max_len = stream->out_rtcp_pkt_size; ++ ++ /* Build RTCP PLI packet */ ++ pli_len = max_len - len; ++ status = pjmedia_rtcp_build_rtcp_pli(&stream->rtcp, pkt+len, &pli_len); ++ if (status != PJ_SUCCESS) { ++ PJ_PERROR(4,(stream->name.ptr, status, "Error generating RTCP PLI")); ++ } else { ++ len += (int)pli_len; ++ } ++ ++ /* Send! */ ++ status = pjmedia_transport_send_rtcp(stream->transport, pkt, len); ++ ++ return status; ++ } ++ ++ return PJ_SUCCESS; ++} ++ ++ ++/* + * Initialize the video stream rate control with default settings. + */ + PJ_DEF(void) +Index: pjmedia/src/pjmedia/vid_tee.c +=================================================================== +--- pjmedia/src/pjmedia/vid_tee.c (revision 5249) ++++ pjmedia/src/pjmedia/vid_tee.c (working copy) +@@ -52,6 +52,7 @@ + unsigned dst_port_cnt; + vid_tee_dst_port *dst_ports; + pj_uint8_t *put_frm_flag; ++ pj_mutex_t *lock; + + struct vid_tee_conv_t { + pjmedia_converter *conv; +@@ -86,6 +87,11 @@ + tee->pf = pool->factory; + tee->pool = pj_pool_create(tee->pf, "video tee", 500, 500, NULL); + ++ /* Create lock */ ++ status = pj_mutex_create_simple(pool, "vid-tee-mutex", &tee->lock); ++ if (status != PJ_SUCCESS) ++ return status; ++ + /* Initialize video tee structure */ + tee->dst_port_maxcnt = max_dst_cnt; + tee->dst_ports = (vid_tee_dst_port*) +@@ -100,14 +106,16 @@ + + /* Initialize video tee buffer, its size is one frame */ + vfi = pjmedia_get_video_format_info(NULL, fmt->id); +- if (vfi == NULL) +- return PJMEDIA_EBADFMT; ++ if (vfi == NULL) { ++ status = PJMEDIA_EBADFMT; ++ goto on_error; ++ } + + pj_bzero(&vafp, sizeof(vafp)); + vafp.size = fmt->det.vid.size; + status = vfi->apply_fmt(vfi, &vafp); + if (status != PJ_SUCCESS) +- return status; ++ goto on_error; + + tee->buf_size = vafp.framebytes; + +@@ -118,7 +126,7 @@ + PJMEDIA_DIR_ENCODING, + fmt); + if (status != PJ_SUCCESS) +- return status; ++ goto on_error; + + tee->base.get_frame = &tee_get_frame; + tee->base.put_frame = &tee_put_frame; +@@ -128,6 +136,11 @@ + *p_vid_tee = &tee->base; + + return PJ_SUCCESS; ++ ++on_error: ++ pj_mutex_destroy(tee->lock); ++ tee->lock = NULL; ++ return status; + } + + static void realloc_buf(vid_tee_port *vid_tee, +@@ -169,23 +182,31 @@ + { + vid_tee_port *tee = (vid_tee_port*)vid_tee; + pjmedia_video_format_detail *vfd; ++ pj_status_t status; + + PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN, + PJ_EINVAL); + +- if (tee->dst_port_cnt >= tee->dst_port_maxcnt) +- return PJ_ETOOMANY; +- +- if (vid_tee->info.fmt.id != port->info.fmt.id) +- return PJMEDIA_EBADFMT; ++ pj_mutex_lock(tee->lock); + ++ if (tee->dst_port_cnt >= tee->dst_port_maxcnt) { ++ status = PJ_ETOOMANY; ++ goto end; ++ } ++ ++ if (vid_tee->info.fmt.id != port->info.fmt.id) { ++ status = PJMEDIA_EBADFMT; ++ goto end; ++ } ++ + vfd = pjmedia_format_get_video_format_detail(&port->info.fmt, PJ_TRUE); + if (vfd->size.w != vid_tee->info.fmt.det.vid.size.w || + vfd->size.h != vid_tee->info.fmt.det.vid.size.h) + { +- return PJMEDIA_EBADFMT; ++ status = PJMEDIA_EBADFMT; ++ goto end; + } +- ++ + realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)? + 1: 0, tee->buf_size); + +@@ -194,7 +215,11 @@ + tee->dst_ports[tee->dst_port_cnt].option = option; + ++tee->dst_port_cnt; + +- return PJ_SUCCESS; ++ status = PJ_SUCCESS; ++ ++end: ++ pj_mutex_unlock(tee->lock); ++ return status; + } + + +@@ -208,12 +233,17 @@ + { + vid_tee_port *tee = (vid_tee_port*)vid_tee; + pjmedia_video_format_detail *vfd; ++ pj_status_t status; + + PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN, + PJ_EINVAL); + +- if (tee->dst_port_cnt >= tee->dst_port_maxcnt) +- return PJ_ETOOMANY; ++ pj_mutex_lock(tee->lock); ++ ++ if (tee->dst_port_cnt >= tee->dst_port_maxcnt) { ++ status = PJ_ETOOMANY; ++ goto end; ++ } + + pj_bzero(&tee->tee_conv[tee->dst_port_cnt], sizeof(tee->tee_conv[0])); + +@@ -226,17 +256,18 @@ + const pjmedia_video_format_info *vfi; + pjmedia_video_apply_fmt_param vafp; + pjmedia_conversion_param conv_param; +- pj_status_t status; + + vfi = pjmedia_get_video_format_info(NULL, port->info.fmt.id); +- if (vfi == NULL) +- return PJMEDIA_EBADFMT; ++ if (vfi == NULL) { ++ status = PJMEDIA_EBADFMT; ++ goto end; ++ } + + pj_bzero(&vafp, sizeof(vafp)); + vafp.size = port->info.fmt.det.vid.size; + status = vfi->apply_fmt(vfi, &vafp); + if (status != PJ_SUCCESS) +- return status; ++ goto end; + + realloc_buf(tee, (option & PJMEDIA_VID_TEE_DST_DO_IN_PLACE_PROC)? + 2: 1, vafp.framebytes); +@@ -248,7 +279,7 @@ + NULL, tee->pool, &conv_param, + &tee->tee_conv[tee->dst_port_cnt].conv); + if (status != PJ_SUCCESS) +- return status; ++ goto end; + + tee->tee_conv[tee->dst_port_cnt].conv_buf_size = vafp.framebytes; + } else { +@@ -259,8 +290,12 @@ + tee->dst_ports[tee->dst_port_cnt].dst = port; + tee->dst_ports[tee->dst_port_cnt].option = option; + ++tee->dst_port_cnt; +- +- return PJ_SUCCESS; ++ ++ status = PJ_SUCCESS; ++ ++end: ++ pj_mutex_unlock(tee->lock); ++ return status; + } + + +@@ -276,6 +311,8 @@ + PJ_ASSERT_RETURN(vid_tee && vid_tee->info.signature==TEE_PORT_SIGN, + PJ_EINVAL); + ++ pj_mutex_lock(tee->lock); ++ + for (i = 0; i < tee->dst_port_cnt; ++i) { + if (tee->dst_ports[i].dst == port) { + if (tee->tee_conv[i].conv) +@@ -286,10 +323,13 @@ + pj_array_erase(tee->tee_conv, sizeof(tee->tee_conv[0]), + tee->dst_port_cnt, i); + --tee->dst_port_cnt; ++ ++ pj_mutex_unlock(tee->lock); + return PJ_SUCCESS; + } + } + ++ pj_mutex_unlock(tee->lock); + return PJ_ENOTFOUND; + } + +@@ -300,6 +340,11 @@ + unsigned i, j; + const pj_uint8_t PUT_FRM_DONE = 1; + ++ if (pj_mutex_trylock(tee->lock) != PJ_SUCCESS) { ++ /* we are busy adding / removing consumers */ ++ return PJ_SUCCESS; ++ } ++ + pj_bzero(tee->put_frm_flag, tee->dst_port_cnt * + sizeof(tee->put_frm_flag[0])); + +@@ -364,6 +409,7 @@ + } + } + ++ pj_mutex_unlock(tee->lock); + return PJ_SUCCESS; + } + +@@ -383,6 +429,11 @@ + + PJ_ASSERT_RETURN(port && port->info.signature==TEE_PORT_SIGN, PJ_EINVAL); + ++ if (tee->lock) { ++ pj_mutex_destroy(tee->lock); ++ tee->lock = NULL; ++ } ++ + pj_pool_release(tee->pool); + if (tee->buf_pool) + pj_pool_release(tee->buf_pool); +Index: pjmedia/src/pjmedia-audiodev/alsa_dev.c +=================================================================== +--- pjmedia/src/pjmedia-audiodev/alsa_dev.c (revision 5249) ++++ pjmedia/src/pjmedia-audiodev/alsa_dev.c (working copy) +@@ -38,12 +38,7 @@ + + + #define THIS_FILE "alsa_dev.c" +-#define ALSA_DEVICE_NAME "plughw:%d,%d" +-#define ALSASOUND_PLAYBACK 1 +-#define ALSASOUND_CAPTURE 2 +-#define MAX_SOUND_CARDS 5 +-#define MAX_SOUND_DEVICES_PER_CARD 5 +-#define MAX_DEVICES 32 ++#define MAX_DEVICES 128 + + /* Set to 1 to enable tracing */ + #if 0 +@@ -71,7 +66,12 @@ + pjmedia_aud_play_cb play_cb, + void *user_data, + pjmedia_aud_stream **p_strm); ++static void alsa_factory_set_observer(pjmedia_aud_dev_factory *f, ++ pjmedia_aud_dev_change_callback cb); ++static int alsa_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f); ++static int alsa_factory_get_default_play_dev(pjmedia_aud_dev_factory *f); + ++ + /* + * Stream prototypes + */ +@@ -88,6 +88,13 @@ + static pj_status_t alsa_stream_destroy(pjmedia_aud_stream *strm); + + ++/* alsa device info */ ++struct alsa_dev_info ++{ ++ pjmedia_aud_dev_info info; ++ char alsa_name[64]; ++}; ++ + struct alsa_factory + { + pjmedia_aud_dev_factory base; +@@ -96,7 +103,7 @@ + pj_pool_t *base_pool; + + unsigned dev_cnt; +- pjmedia_aud_dev_info devs[MAX_DEVICES]; ++ struct alsa_dev_info devs[MAX_DEVICES]; + }; + + struct alsa_stream +@@ -136,7 +143,10 @@ + &alsa_factory_get_dev_info, + &alsa_factory_default_param, + &alsa_factory_create_stream, +- &alsa_factory_refresh ++ &alsa_factory_refresh, ++ &alsa_factory_set_observer, ++ &alsa_factory_get_default_rec_dev, ++ &alsa_factory_get_default_play_dev + }; + + static pjmedia_aud_stream_op alsa_stream_op = +@@ -208,9 +218,9 @@ + } + + +-static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name) ++static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name, const char *dev_desc) + { +- pjmedia_aud_dev_info *adi; ++ struct alsa_dev_info *adi; + snd_pcm_t* pcm; + int pb_result, ca_result; + +@@ -249,23 +259,63 @@ + pj_bzero(adi, sizeof(*adi)); + + /* Set device name */ +- strncpy(adi->name, dev_name, sizeof(adi->name)); ++ strncpy(adi->alsa_name, dev_name, sizeof(adi->alsa_name)); + ++ /* Set comprehensive device name */ ++ int name_size = sizeof(adi->info.name); ++ if (dev_desc) { ++ pj_bool_t name_set = PJ_FALSE; ++ if (strncmp("sysdefault", dev_name, 10) == 0) { ++ /* Only use first line for default device*/ ++ char *ptr = strstr(dev_desc, "\n"); ++ if (ptr) { ++ int len = ptr - dev_desc; ++ strncpy(adi->info.name, dev_desc, (len >= name_size-1)?name_size:len); ++ name_set = PJ_TRUE; ++ } ++ } else if (strncmp("iec958", dev_name, 6) == 0) { ++ /* Mangle name for SPDIF devices*/ ++ char *ptr = strstr(dev_desc, ","); ++ if (ptr) { ++ int len = ptr - dev_desc; ++ if (len + 18 < name_size) { ++ strncpy(adi->info.name, dev_desc, len); ++ strncpy(adi->info.name+len, ", Digital (S/PDIF)", 18); ++ name_set = PJ_TRUE; ++ } ++ } ++ } ++ ++ if (!name_set) { ++ /* Use the entire description for other device names */ ++ int i = 0; ++ while (i < name_size-1 && dev_desc[i] != '\0') { ++ if (dev_desc[i] == '\n' || dev_desc[i] == '\r') ++ adi->info.name[i] = ' '; ++ else ++ adi->info.name[i] = dev_desc[i]; ++ i++; ++ } ++ } ++ } else { ++ strncpy(adi->info.name, dev_name, name_size); ++ } ++ + /* Check the number of playback channels */ +- adi->output_count = (pb_result>=0) ? 1 : 0; ++ adi->info.output_count = (pb_result>=0) ? 1 : 0; + + /* Check the number of capture channels */ +- adi->input_count = (ca_result>=0) ? 1 : 0; ++ adi->info.input_count = (ca_result>=0) ? 1 : 0; + + /* Set the default sample rate */ +- adi->default_samples_per_sec = 8000; ++ adi->info.default_samples_per_sec = 8000; + + /* Driver name */ +- strcpy(adi->driver, "ALSA"); ++ strcpy(adi->info.driver, "ALSA"); + + ++af->dev_cnt; + +- PJ_LOG (5,(THIS_FILE, "Added sound device %s", adi->name)); ++ PJ_LOG (5,(THIS_FILE, "Added sound device %s", adi->alsa_name)); + + return PJ_SUCCESS; + } +@@ -348,9 +398,26 @@ + n = hints; + while (*n != NULL) { + char *name = snd_device_name_get_hint(*n, "NAME"); +- if (name != NULL && 0 != strcmp("null", name)) { +- add_dev(af, name); ++ char *desc = snd_device_name_get_hint(*n, "DESC"); ++ if (name != NULL) { ++ if (strncmp("null", name, 4) == 0 || ++ strncmp("front", name, 5) == 0 || ++ strncmp("rear", name, 4) == 0 || ++ strncmp("side", name, 4) == 0 || ++ strncmp("dmix", name, 4) == 0 || ++ strncmp("dsnoop", name, 6) == 0 || ++ strncmp("hw", name, 2) == 0 || ++ strncmp("plughw", name, 6) == 0 || ++ strncmp("center_lfe", name, 10) == 0 || ++ strncmp("surround", name, 8) == 0 || ++ (strncmp("default", name, 7) == 0 && strstr(name, ":CARD=") != NULL)) { ++ /* skip these devices, 'sysdefault' always contains the relevant information */ ++ ; ++ } else { ++ add_dev(af, name, desc); ++ } + free(name); ++ free(desc); + } + n++; + } +@@ -385,7 +452,7 @@ + + PJ_ASSERT_RETURN(index>=0 && indexdev_cnt, PJ_EINVAL); + +- pj_memcpy(info, &af->devs[index], sizeof(*info)); ++ pj_memcpy(info, &af->devs[index].info, sizeof(*info)); + info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | + PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; + return PJ_SUCCESS; +@@ -397,7 +464,7 @@ + pjmedia_aud_param *param) + { + struct alsa_factory *af = (struct alsa_factory*)f; +- pjmedia_aud_dev_info *adi; ++ struct alsa_dev_info *adi; + + PJ_ASSERT_RETURN(index>=0 && indexdev_cnt, PJ_EINVAL); + +@@ -404,15 +471,15 @@ + adi = &af->devs[index]; + + pj_bzero(param, sizeof(*param)); +- if (adi->input_count && adi->output_count) { ++ if (adi->info.input_count && adi->info.output_count) { + param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; + param->rec_id = index; + param->play_id = index; +- } else if (adi->input_count) { ++ } else if (adi->info.input_count) { + param->dir = PJMEDIA_DIR_CAPTURE; + param->rec_id = index; + param->play_id = PJMEDIA_AUD_INVALID_DEV; +- } else if (adi->output_count) { ++ } else if (adi->info.output_count) { + param->dir = PJMEDIA_DIR_PLAYBACK; + param->play_id = index; + param->rec_id = PJMEDIA_AUD_INVALID_DEV; +@@ -420,11 +487,11 @@ + return PJMEDIA_EAUD_INVDEV; + } + +- param->clock_rate = adi->default_samples_per_sec; ++ param->clock_rate = adi->info.default_samples_per_sec; + param->channel_count = 1; +- param->samples_per_frame = adi->default_samples_per_sec * 20 / 1000; ++ param->samples_per_frame = adi->info.default_samples_per_sec * 20 / 1000; + param->bits_per_sample = 16; +- param->flags = adi->caps; ++ param->flags = adi->info.caps; + param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY; + param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; + +@@ -549,7 +616,7 @@ + + tstamp.u64 += nframes; + } +- snd_pcm_drain (pcm); ++ snd_pcm_drop (pcm); + TRACE_((THIS_FILE, "ca_thread_func: Stopped")); + + return PJ_SUCCESS; +@@ -571,9 +638,9 @@ + + /* Open PCM for playback */ + PJ_LOG (5,(THIS_FILE, "open_playback: Open playback device '%s'", +- stream->af->devs[param->play_id].name)); ++ stream->af->devs[param->play_id].alsa_name)); + result = snd_pcm_open (&stream->pb_pcm, +- stream->af->devs[param->play_id].name, ++ stream->af->devs[param->play_id].alsa_name, + SND_PCM_STREAM_PLAYBACK, + 0); + if (result < 0) +@@ -665,7 +732,7 @@ + + PJ_LOG (5,(THIS_FILE, "Opened device alsa(%s) for playing, sample rate=%d" + ", ch=%d, bits=%d, period size=%d frames, latency=%d ms", +- stream->af->devs[param->play_id].name, ++ stream->af->devs[param->play_id].alsa_name, + rate, param->channel_count, + param->bits_per_sample, stream->pb_frames, + (int)stream->param.output_latency_ms)); +@@ -689,9 +756,9 @@ + + /* Open PCM for capture */ + PJ_LOG (5,(THIS_FILE, "open_capture: Open capture device '%s'", +- stream->af->devs[param->rec_id].name)); ++ stream->af->devs[param->rec_id].alsa_name)); + result = snd_pcm_open (&stream->ca_pcm, +- stream->af->devs[param->rec_id].name, ++ stream->af->devs[param->rec_id].alsa_name, + SND_PCM_STREAM_CAPTURE, + 0); + if (result < 0) +@@ -783,7 +850,7 @@ + + PJ_LOG (5,(THIS_FILE, "Opened device alsa(%s) for capture, sample rate=%d" + ", ch=%d, bits=%d, period size=%d frames, latency=%d ms", +- stream->af->devs[param->rec_id].name, ++ stream->af->devs[param->rec_id].alsa_name, + rate, param->channel_count, + param->bits_per_sample, stream->ca_frames, + (int)stream->param.input_latency_ms)); +@@ -845,6 +912,28 @@ + } + + ++/* API: set audio device change observer */ ++static void alsa_factory_set_observer(pjmedia_aud_dev_factory *f, ++ pjmedia_aud_dev_change_callback cb) ++{ ++ PJ_UNUSED_ARG(f); ++ PJ_UNUSED_ARG(cb); ++} ++ ++/* API: get default recording device */ ++static int alsa_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f) ++{ ++ PJ_UNUSED_ARG(f); ++ return -1; ++} ++ ++/* API: get default playback device */ ++static int alsa_factory_get_default_play_dev(pjmedia_aud_dev_factory *f) ++{ ++ PJ_UNUSED_ARG(f); ++ return -1; ++} ++ + /* API: get running parameter */ + static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *s, + pjmedia_aud_param *pi) +Index: pjmedia/src/pjmedia-audiodev/audiodev.c +=================================================================== +--- pjmedia/src/pjmedia-audiodev/audiodev.c (revision 5249) ++++ pjmedia/src/pjmedia-audiodev/audiodev.c (working copy) +@@ -65,11 +65,6 @@ + #define DEFAULT_DEV_ID 0 + + +-/* extern functions to create factories */ +-#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO +-pjmedia_aud_dev_factory* pjmedia_pa_factory(pj_pool_factory *pf); +-#endif +- + #if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO + pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf); + #endif +@@ -130,7 +125,6 @@ + unsigned start_idx; /* Start index in global list */ + int rec_dev_idx;/* Default capture device. */ + int play_dev_idx;/* Default playback device */ +- int dev_idx; /* Default device. */ + }; + + /* The audio subsystem */ +@@ -145,8 +139,64 @@ + unsigned dev_cnt; /* Total number of devices. */ + pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */ + ++ pjmedia_aud_dev_observer dev_observer; ++ + } aud_subsys; + ++/* callback for device change operations */ ++static void process_aud_dev_change_event(pjmedia_aud_dev_change_event event) ++{ ++ pj_status_t status; ++ ++ if (!pj_thread_is_registered()) { ++ status = pj_thread_register("aud_dev_observer", aud_subsys.dev_observer.thread_desc, &aud_subsys.dev_observer.thread); ++ if (status != PJ_SUCCESS) { ++ return; ++ } ++ PJ_LOG(5, (THIS_FILE, "Audio device change thread registered")); ++ } ++ ++ status = pj_mutex_lock(aud_subsys.dev_observer.lock); ++ if (status != PJ_SUCCESS) { ++ PJ_LOG(5, (THIS_FILE, "Could not acquire audio device change lock")); ++ return; ++ } ++ ++ if (!aud_subsys.dev_observer.cb) { ++ /* there is no registered callback to call */ ++ goto end; ++ } ++ ++ switch(event) { ++ case DEFAULT_INPUT_CHANGED: ++ PJ_LOG(5, (THIS_FILE, "Default input device changed")); ++ pjmedia_aud_dev_refresh(); ++ (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED); ++ break; ++ case DEFAULT_OUTPUT_CHANGED: ++ PJ_LOG(5, (THIS_FILE, "Default output device changed")); ++ pjmedia_aud_dev_refresh(); ++ (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED); ++ break; ++ case DEVICE_LIST_CHANGED: ++ PJ_LOG(5, (THIS_FILE, "Device list changed")); ++ (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_LIST_WILL_REFRESH); ++ pjmedia_aud_dev_refresh(); ++ (*aud_subsys.dev_observer.cb)(PJMEDIA_AUD_DEV_LIST_DID_REFRESH); ++ break; ++ default: ++ PJ_LOG(5, (THIS_FILE, "Unknown event: %d", event)); ++ break; ++ } ++ ++end: ++ status = pj_mutex_unlock(aud_subsys.dev_observer.lock); ++ if (status != PJ_SUCCESS) { ++ PJ_LOG(5, (THIS_FILE, "Could not release audio device change lock")); ++ } ++ ++} ++ + /* API: get capability name/info */ + PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap, + const char **p_desc) +@@ -291,6 +341,11 @@ + 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; + +@@ -314,8 +369,8 @@ + */ + + /* Fill in default devices */ +- drv->play_dev_idx = drv->rec_dev_idx = +- drv->dev_idx = PJMEDIA_AUD_INVALID_DEV; ++ 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; irec_dev_idx = i; + } +- if (drv->dev_idx < 0 && info.input_count && +- info.output_count) +- { +- /* Set default capture and playback device */ +- drv->dev_idx = i; +- } + +- if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 && +- drv->dev_idx >= 0) ++ if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0) + { + /* Done. */ + break; +@@ -374,13 +422,13 @@ + struct driver *drv = &aud_subsys.drv[drv_idx]; + + if (drv->f) { ++ drv->f->op->set_dev_change_cb(drv->f, NULL); + drv->f->op->destroy(drv->f); + drv->f = NULL; + } + + pj_bzero(drv, sizeof(*drv)); +- drv->play_dev_idx = drv->rec_dev_idx = +- drv->dev_idx = PJMEDIA_AUD_INVALID_DEV; ++ drv->play_dev_idx = drv->rec_dev_idx = PJMEDIA_AUD_INVALID_DEV; + } + + /* API: Initialize the audio subsystem. */ +@@ -423,9 +471,6 @@ + #if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO + aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_coreaudio_factory; + #endif +-#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO +- aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory; +-#endif + #if PJMEDIA_AUDIO_DEV_HAS_WMME + aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory; + #endif +@@ -445,6 +490,18 @@ + aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_null_audio_factory; + #endif + ++ /* Initialize audio device observer objects */ ++ pj_status_t st; ++ aud_subsys.dev_observer.pool = pj_pool_create(pf, "aud_dev_observer_pool", 512, 512, NULL); ++ if (!aud_subsys.dev_observer.pool) { ++ return PJ_ENOMEM; ++ } ++ st = pj_mutex_create_simple(aud_subsys.dev_observer.pool, "aud_dev_observer_lock", &aud_subsys.dev_observer.lock); ++ if (st != PJ_SUCCESS) { ++ return st; ++ } ++ aud_subsys.dev_observer.cb = NULL; ++ + /* Initialize each factory and build the device ID list */ + for (i=0; idev_idx >= 0) { +- id = drv->dev_idx; +- make_global_index(i, &id); +- break; +- } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV && ++ if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV && + drv->rec_dev_idx >= 0) + { + id = drv->rec_dev_idx; +@@ -609,7 +665,7 @@ + id = drv->play_dev_idx; + make_global_index(i, &id); + break; +- } ++ } + } + + if (id < 0) { +@@ -844,4 +900,24 @@ + return strm->op->destroy(strm); + } + ++/* API: Register device change observer. */ ++PJ_DEF(pj_status_t) pjmedia_aud_dev_set_observer_cb(pjmedia_aud_dev_observer_callback cb) ++{ ++ pj_status_t status; + ++ status = pj_mutex_lock(aud_subsys.dev_observer.lock); ++ if (status != PJ_SUCCESS) { ++ PJ_LOG(5, (THIS_FILE, "Could not acquire audio device change lock")); ++ return status; ++ } ++ ++ aud_subsys.dev_observer.cb = cb; ++ ++ status = pj_mutex_unlock(aud_subsys.dev_observer.lock); ++ if (status != PJ_SUCCESS) { ++ PJ_LOG(5, (THIS_FILE, "Could not release audio device change lock")); ++ } ++ ++ return status; ++} ++ +Index: pjmedia/src/pjmedia-audiodev/coreaudio_dev.m +=================================================================== +--- pjmedia/src/pjmedia-audiodev/coreaudio_dev.m (revision 5249) ++++ pjmedia/src/pjmedia-audiodev/coreaudio_dev.m (working copy) +@@ -166,6 +166,11 @@ + void *user_data, + pjmedia_aud_stream **p_aud_strm); + ++static void ca_factory_set_observer(pjmedia_aud_dev_factory *f, ++ pjmedia_aud_dev_change_callback cb); ++static int ca_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f); ++static int ca_factory_get_default_play_dev(pjmedia_aud_dev_factory *f); ++ + static pj_status_t ca_stream_get_param(pjmedia_aud_stream *strm, + pjmedia_aud_param *param); + static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *strm, +@@ -199,7 +204,10 @@ + &ca_factory_get_dev_info, + &ca_factory_default_param, + &ca_factory_create_stream, +- &ca_factory_refresh ++ &ca_factory_refresh, ++ &ca_factory_set_observer, ++ &ca_factory_get_default_rec_dev, ++ &ca_factory_get_default_play_dev + }; + + static pjmedia_aud_stream_op stream_op = +@@ -701,6 +709,169 @@ + return PJ_SUCCESS; + } + ++static OSStatus property_listener_proc(AudioObjectID objectID, ++ UInt32 numberAddresses, ++ const AudioObjectPropertyAddress inAddresses[], ++ void *clientData) ++{ ++ pjmedia_aud_dev_change_callback cb = (pjmedia_aud_dev_change_callback)clientData; ++ pjmedia_aud_dev_change_event event; ++ UInt32 i; ++ ++ for(i = 0; i < numberAddresses; i++) { ++ event = 0; ++ switch (inAddresses[i].mSelector) { ++ case kAudioHardwarePropertyDefaultInputDevice: ++ event = DEFAULT_INPUT_CHANGED; ++ break; ++ case kAudioHardwarePropertyDefaultOutputDevice: ++ event = DEFAULT_OUTPUT_CHANGED; ++ break; ++ case kAudioHardwarePropertyDevices: ++ event = DEVICE_LIST_CHANGED; ++ break; ++ default: ++ break; ++ } ++ if (event > 0) { ++ (cb)(event); ++ } ++ } ++ ++ return noErr; ++} ++ ++/* API: set audio device change observer */ ++static void ca_factory_set_observer(pjmedia_aud_dev_factory *f, ++ pjmedia_aud_dev_change_callback cb) ++{ ++ AudioObjectPropertyAddress addr; ++ OSStatus ostatus; ++ ++ /* observer for devices list */ ++ addr.mSelector = kAudioHardwarePropertyDevices; ++ addr.mScope = kAudioObjectPropertyScopeGlobal; ++ addr.mElement = kAudioObjectPropertyElementMaster; ++ ++ if (cb) { ++ ostatus = AudioObjectAddPropertyListener(kAudioObjectSystemObject, ++ &addr, ++ property_listener_proc, ++ cb); ++ } else { ++ ostatus = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, ++ &addr, ++ property_listener_proc, ++ cb); ++ } ++ if (ostatus != noErr) { ++ PJ_LOG(5,(THIS_FILE, "Error %sregistering devices list observer", cb==NULL ? "un-" : "")); ++ } ++ ++ /* observer for default input device */ ++ addr.mSelector = kAudioHardwarePropertyDefaultInputDevice; ++ ++ if (cb) { ++ ostatus = AudioObjectAddPropertyListener(kAudioObjectSystemObject, ++ &addr, ++ property_listener_proc, ++ cb); ++ } else { ++ ostatus = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, ++ &addr, ++ property_listener_proc, ++ cb); ++ } ++ if (ostatus != noErr) { ++ PJ_LOG(5,(THIS_FILE, "Error %sregistering default input device observer", cb==NULL ? "un-" : "")); ++ } ++ ++ /* observer for default output device */ ++ addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; ++ ++ if (cb) { ++ ostatus = AudioObjectAddPropertyListener(kAudioObjectSystemObject, ++ &addr, ++ property_listener_proc, ++ cb); ++ } else { ++ ostatus = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, ++ &addr, ++ property_listener_proc, ++ cb); ++ } ++ if (ostatus != noErr) { ++ PJ_LOG(5,(THIS_FILE, "Error %sregistering default output device observer", cb==NULL ? "un-" : "")); ++ } ++ ++} ++ ++/* API: get default recording device */ ++static int ca_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f) ++{ ++ AudioDeviceID dev_id = kAudioObjectUnknown; ++ AudioObjectPropertyAddress addr; ++ UInt32 size; ++ OSStatus ostatus; ++ int i; ++ int idx = -1; ++ struct coreaudio_factory *cf = (struct coreaudio_factory*)f; ++ ++ /* Find default audio input device */ ++ addr.mSelector = kAudioHardwarePropertyDefaultInputDevice; ++ addr.mScope = kAudioObjectPropertyScopeGlobal; ++ addr.mElement = kAudioObjectPropertyElementMaster; ++ size = sizeof(dev_id); ++ ++ ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, ++ &addr, 0, NULL, ++ &size, (void *)&dev_id); ++ if (ostatus == noErr) { ++ for (i = 0; i < cf->dev_count; i++) { ++ struct coreaudio_dev_info *cdi; ++ cdi = &cf->dev_info[i]; ++ if (cdi->dev_id == dev_id) { ++ idx = i; ++ break; ++ } ++ } ++ } ++ return idx; ++} ++ ++/* API: get default playback device */ ++static int ca_factory_get_default_play_dev(pjmedia_aud_dev_factory *f) ++{ ++ AudioDeviceID dev_id = kAudioObjectUnknown; ++ AudioObjectPropertyAddress addr; ++ UInt32 size; ++ OSStatus ostatus; ++ int i; ++ int idx = -1; ++ struct coreaudio_factory *cf = (struct coreaudio_factory*)f; ++ ++ /* Find default audio output device */ ++ addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; ++ addr.mScope = kAudioObjectPropertyScopeGlobal; ++ addr.mElement = kAudioObjectPropertyElementMaster; ++ size = sizeof(dev_id); ++ ++ ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, ++ &addr, 0, NULL, ++ &size, (void *)&dev_id); ++ if (ostatus == noErr) { ++ for (i = 0; i < cf->dev_count; i++) { ++ struct coreaudio_dev_info *cdi; ++ cdi = &cf->dev_info[i]; ++ if (cdi->dev_id == dev_id) { ++ idx = i; ++ break; ++ } ++ } ++ } ++ return idx; ++} ++ + OSStatus resampleProc(AudioConverterRef inAudioConverter, + UInt32 *ioNumberDataPackets, + AudioBufferList *ioData, +@@ -1727,7 +1898,6 @@ + { + strm->param.input_latency_ms = (latency + latency2) * 1000 / + strm->param.clock_rate; +- strm->param.input_latency_ms++; + } + } + #else +@@ -1735,7 +1905,6 @@ + strm->param.input_latency_ms = + (unsigned)(([strm->sess inputLatency] + + [strm->sess IOBufferDuration]) * 1000); +- strm->param.input_latency_ms++; + } else + return PJMEDIA_EAUD_INVCAP; + #endif +@@ -1768,7 +1937,6 @@ + { + strm->param.output_latency_ms = (latency + latency2) * 1000 / + strm->param.clock_rate; +- strm->param.output_latency_ms++; + } + } + #else +@@ -1776,11 +1944,10 @@ + strm->param.output_latency_ms = + (unsigned)(([strm->sess outputLatency] + + [strm->sess IOBufferDuration]) * 1000); +- strm->param.output_latency_ms++; + } else + return PJMEDIA_EAUD_INVCAP; + #endif +- *(unsigned*)pval = (++strm->param.output_latency_ms * 2); ++ *(unsigned*)pval = strm->param.output_latency_ms; + return PJ_SUCCESS; + } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING && + (strm->param.dir & PJMEDIA_DIR_PLAYBACK)) +Index: pjmedia/src/pjmedia-audiodev/errno.c +=================================================================== +--- pjmedia/src/pjmedia-audiodev/errno.c (revision 5249) ++++ pjmedia/src/pjmedia-audiodev/errno.c (working copy) +@@ -19,9 +19,6 @@ + #include + #include + #include +-#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO +-# include +-#endif + #if PJMEDIA_AUDIO_DEV_HAS_WMME + # ifdef _MSC_VER + # pragma warning(push, 3) +@@ -91,26 +88,6 @@ + } else + #endif + +- /* See if the error comes from PortAudio. */ +-#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO +- if (statcode >= PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_START && +- statcode <= PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_END) +- { +- +- //int pa_err = statcode - PJMEDIA_ERRNO_FROM_PORTAUDIO(0); +- int pa_err = PJMEDIA_AUDIODEV_PORTAUDIO_ERRNO_START - statcode; +- pj_str_t msg; +- +- msg.ptr = (char*)Pa_GetErrorText(pa_err); +- msg.slen = pj_ansi_strlen(msg.ptr); +- +- errstr.ptr = buf; +- pj_strncpy_with_null(&errstr, &msg, bufsize); +- return errstr; +- +- } else +-#endif /* PJMEDIA_SOUND_IMPLEMENTATION */ +- + /* See if the error comes from WMME */ + #if PJMEDIA_AUDIO_DEV_HAS_WMME + if ((statcode >= PJMEDIA_AUDIODEV_WMME_IN_ERROR_START && +Index: pjmedia/src/pjmedia-audiodev/wmme_dev.c +=================================================================== +--- pjmedia/src/pjmedia-audiodev/wmme_dev.c (revision 5249) ++++ pjmedia/src/pjmedia-audiodev/wmme_dev.c (working copy) +@@ -31,6 +31,7 @@ + #endif + + #include ++#include + #include + #include + +@@ -68,6 +69,15 @@ + + #define THIS_FILE "wmme_dev.c" + ++/* WMME device change observer */ ++struct wmme_dev_observer ++{ ++ pj_thread_t *thread; ++ pj_pool_t *pool; ++ pjmedia_aud_dev_change_callback cb; ++ HWND hWnd; ++}; ++ + /* WMME device info */ + struct wmme_dev_info + { +@@ -86,6 +96,8 @@ + + unsigned dev_count; + struct wmme_dev_info *dev_info; ++ ++ struct wmme_dev_observer dev_observer; + }; + + +@@ -150,7 +162,12 @@ + pjmedia_aud_play_cb play_cb, + void *user_data, + pjmedia_aud_stream **p_aud_strm); ++static void factory_set_observer(pjmedia_aud_dev_factory *f, ++ pjmedia_aud_dev_change_callback cb); ++static int factory_get_default_rec_dev(pjmedia_aud_dev_factory *f); ++static int factory_get_default_play_dev(pjmedia_aud_dev_factory *f); + ++ + static pj_status_t stream_get_param(pjmedia_aud_stream *strm, + pjmedia_aud_param *param); + static pj_status_t stream_get_cap(pjmedia_aud_stream *strm, +@@ -173,7 +190,10 @@ + &factory_get_dev_info, + &factory_default_param, + &factory_create_stream, +- &factory_refresh ++ &factory_refresh, ++ &factory_set_observer, ++ &factory_get_default_rec_dev, ++ &factory_get_default_play_dev + }; + + static pjmedia_aud_stream_op stream_op = +@@ -1306,6 +1326,201 @@ + return PJ_SUCCESS; + } + ++/* Processes OS messages arriving at the hWnd window */ ++INT_PTR WINAPI ProcessOSMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ++{ ++ /* wf is used in order to query the number of audio devices currently handled */ ++ static struct wmme_factory *wf = NULL; ++ ++ switch( message ) ++ { ++ case WM_CREATE: ++ /* Initialize wf pointer on the first run */ ++ if (wf == NULL) ++ { ++ CREATESTRUCT *CrtStrPtr = (CREATESTRUCT *) lParam; ++ wf = (struct wmme_factory *)(CrtStrPtr->lpCreateParams); ++ } ++ break; ++ case WM_DEVICECHANGE: ++ /* Possible insertion or removal of device. There's some issues: ++ ++ - Some devices/drivers does not trigger arrival nor ++ removecomplete events, but only devnodes_changed events. ++ Therefore, we process all of those type of events. ++ ++ - Some hardware can send many devnodes_changed events at the ++ same time (up to ~15 of such events). These batches are ++ detected using temporal locality, using constMaxBatchPeriod_. ++ Once the device is detected, the rest of redundant events ++ are discarded. In order to know if there's a new device or not, ++ actual audio devices count is compared to stored audio devices ++ count (via wf->dev_count). ++ ++ - Hardware takes some time to settle and be recognized by ++ drivers. A small window of time is given in order to account ++ for this (constMaxSettleTime_); ++ ++ Settle time should be slightly lower than batch period. ++ */ ++ if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE || wParam == DBT_DEVNODES_CHANGED) { ++ const int constMaxBatchPeriod_ = 3; /* seconds */ ++ const int constMaxSettleTime_ = (constMaxBatchPeriod_ * 1000) - 500; /* milliseconds */ ++ ++ /* Loop that allows hardware to settle */ ++ int settleTimeLeft = constMaxSettleTime_; ++ while (settleTimeLeft > 0) { ++ /* Check if actual devices lists (I/O) sizes have actually ++ changed before notifying upper levels. Consider input ++ devices, output devices and a WAVE MAPPER device for each. ++ */ ++ if(waveInGetNumDevs() + waveOutGetNumDevs() + 2 != wf->dev_count) { ++ /* Hardware changed */ ++ if (wf->dev_observer.cb) { ++ wf->dev_observer.cb(DEVICE_LIST_CHANGED); ++ } ++ break; ++ } else { ++ /* Hardware is settling... */ ++ Sleep(250); ++ settleTimeLeft -= 250; ++ } ++ } ++ } ++ break; ++ case WM_CLOSE: ++ if (!DestroyWindow(hWnd)) { ++ PJ_LOG(4,(THIS_FILE, "Couldn't destroy message window")); ++ } ++ break; ++ case WM_DESTROY: ++ PostQuitMessage(0); ++ break; ++ default: ++ break; ++ } ++ ++ return 1; ++} ++ ++static pj_status_t create_os_messages_window(struct wmme_factory *wf) ++{ ++ pj_status_t status = PJ_EBUG; ++ WNDCLASSEX wndClass; ++ HWND hWnd; ++ ++ /* Set up and register window class */ ++ ZeroMemory(&wndClass, sizeof(WNDCLASSEX)); ++ wndClass.cbSize = sizeof(WNDCLASSEX); ++ wndClass.style = CS_OWNDC; ++ wndClass.lpfnWndProc = (WNDPROC)(ProcessOSMessage); ++ wndClass.hInstance = (HINSTANCE)(GetModuleHandle(0)); ++ wndClass.lpszClassName = "DeviceChangeMessageWindow"; ++ ++ if (RegisterClassEx(&wndClass)) { ++ /* Create the window that will receive OS messages */ ++ hWnd = CreateWindowEx( 0, "DeviceChangeMessageWindow", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, (LPVOID)(wf)); ++ if (hWnd != NULL) { ++ wf->dev_observer.hWnd = hWnd; ++ if (UpdateWindow(hWnd) != 0) { ++ status = PJ_SUCCESS; ++ } ++ } else { ++ PJ_LOG(4,(THIS_FILE, "Error creating window to receive device change events")); ++ } ++ } ++ ++ return status; ++ ++} ++ ++static pj_status_t dispatch_os_messages(void) ++{ ++ pj_status_t status = PJ_SUCCESS; ++ MSG msg; ++ int ret; ++ ++ /* Process OS messages with low cpu-usage wait loop */ ++ while((ret = GetMessage(&msg, NULL, 0, 0)) != 0) { ++ if (ret == -1) { ++ PJ_LOG(4,(THIS_FILE, "Couldn't process OS message")); ++ status = PJ_EBUG; ++ break; ++ } else { ++ TranslateMessage(&msg); ++ DispatchMessage(&msg); ++ } ++ } ++ ++ return status; ++ ++} ++ ++/* WMME device observer thread thread. */ ++static int PJ_THREAD_FUNC wmme_dev_observer_thread(void *arg) ++{ ++ struct wmme_factory *wf = (struct wmme_factory*)arg; ++ pj_status_t status; ++ ++ status = create_os_messages_window(wf); ++ if (status == PJ_SUCCESS) { ++ status = dispatch_os_messages(); ++ if (status != PJ_SUCCESS) { ++ PJ_LOG(4,(THIS_FILE, "Error dispatching device detection window events")); ++ } ++ } else { ++ PJ_LOG(4,(THIS_FILE, "Failed to create window for receiving device detection events")); ++ } ++ ++ return status; ++} ++ ++/* API: set audio device change observer */ ++static void factory_set_observer(pjmedia_aud_dev_factory *f, ++ pjmedia_aud_dev_change_callback cb) ++{ ++ struct wmme_factory *wf = (struct wmme_factory*)f; ++ pj_pool_t *pool; ++ pj_status_t status; ++ ++ if (cb) { ++ pool = pj_pool_create(wf->pf, "wmme-dev-observer", 1000, 1000, NULL); ++ PJ_ASSERT_ON_FAIL(pool != NULL, {return;}); ++ status = pj_thread_create(pool, "wmme_observer", &wmme_dev_observer_thread, wf, 0, 0, &wf->dev_observer.thread); ++ if (status != PJ_SUCCESS) { ++ PJ_LOG(4,(THIS_FILE, "Failed to create WMME device detection thread")); ++ wf->dev_observer.thread = NULL; ++ return; ++ } ++ wf->dev_observer.cb = cb; ++ } else { ++ wf->dev_observer.cb = NULL; ++ if (wf->dev_observer.hWnd) { ++ CloseWindow(wf->dev_observer.hWnd); ++ wf->dev_observer.hWnd = NULL; ++ } ++ pj_thread_join(wf->dev_observer.thread); ++ pj_thread_destroy(wf->dev_observer.thread); ++ wf->dev_observer.thread = NULL; ++ } ++} ++ ++/* API: get default recording device */ ++static int factory_get_default_rec_dev(pjmedia_aud_dev_factory *f) ++{ ++ PJ_UNUSED_ARG(f); ++ /* Let PJMEDIA pick the first one available */ ++ return -1; ++} ++ ++/* API: get default playback device */ ++static int factory_get_default_play_dev(pjmedia_aud_dev_factory *f) ++{ ++ PJ_UNUSED_ARG(f); ++ /* Let PJMEDIA pick the first one available */ ++ return -1; ++} ++ + /* API: Get stream info. */ + static pj_status_t stream_get_param(pjmedia_aud_stream *s, + pjmedia_aud_param *pi) +Index: pjmedia/src/pjmedia-codec/audio_codecs.c +=================================================================== +--- pjmedia/src/pjmedia-codec/audio_codecs.c (revision 5249) ++++ pjmedia/src/pjmedia-codec/audio_codecs.c (working copy) +@@ -86,13 +86,6 @@ + return status; + #endif /* PJMEDIA_HAS_G722_CODEC */ + +-#if PJMEDIA_HAS_INTEL_IPP +- /* Register IPP codecs */ +- status = pjmedia_codec_ipp_init(endpt); +- if (status != PJ_SUCCESS) +- return status; +-#endif /* PJMEDIA_HAS_INTEL_IPP */ +- + #if PJMEDIA_HAS_G7221_CODEC + /* Register G722.1 codecs */ + status = pjmedia_codec_g7221_init(endpt); +@@ -100,33 +93,12 @@ + return status; + #endif /* PJMEDIA_HAS_G7221_CODEC */ + +-#if PJMEDIA_HAS_L16_CODEC +- /* Register L16 family codecs */ +- status = pjmedia_codec_l16_init(endpt, 0); +- if (status != PJ_SUCCESS) +- return status; +-#endif /* PJMEDIA_HAS_L16_CODEC */ +- +-#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC || PJMEDIA_HAS_OPENCORE_AMRWB_CODEC +- /* Register OpenCORE AMR */ +- status = pjmedia_codec_opencore_amr_init(endpt, 0); +- if (status != PJ_SUCCESS) +- return status; +-#endif +- +-#if PJMEDIA_HAS_SILK_CODEC +- /* Register SILK */ +- status = pjmedia_codec_silk_init(endpt); +- if (status != PJ_SUCCESS) +- return status; +-#endif +- + #if PJMEDIA_HAS_OPUS_CODEC +- /* Register OPUS */ ++ /* Register opus codecs */ + status = pjmedia_codec_opus_init(endpt); + if (status != PJ_SUCCESS) + return status; +-#endif ++#endif /* PJMEDIA_HAS_OPUS_CODEC */ + + return PJ_SUCCESS; + } +Index: pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c +=================================================================== +--- pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c (revision 5249) ++++ pjmedia/src/pjmedia-codec/ffmpeg_vid_codecs.c (working copy) +@@ -62,16 +62,7 @@ + #endif + + #if LIBAVCODEC_VER_AT_LEAST(53,61) +-# if LIBAVCODEC_VER_AT_LEAST(54,59) +- /* Not sure when AVCodec::encode is obsoleted/removed. */ +-# define AVCODEC_HAS_ENCODE(c) (c->encode2) +-# else +- /* Not sure when AVCodec::encode2 is introduced. It appears in +- * libavcodec 53.61 where some codecs actually still use AVCodec::encode +- * (e.g: H263, H264). +- */ +-# define AVCODEC_HAS_ENCODE(c) (c->encode || c->encode2) +-# endif ++# define AVCODEC_HAS_ENCODE(c) (c->encode2) + # define AV_OPT_SET(obj,name,val,opt) (av_opt_set(obj,name,val,opt)==0) + # define AV_OPT_SET_INT(obj,name,val) (av_opt_set_int(obj,name,val,0)==0) + #else +@@ -266,6 +257,7 @@ + /* H264 constants */ + #define PROFILE_H264_BASELINE 66 + #define PROFILE_H264_MAIN 77 ++#define PROFILE_H264_HIGH 100 + + /* Codec specific functions */ + #if PJMEDIA_HAS_FFMPEG_CODEC_H264 +@@ -403,6 +395,9 @@ + case PROFILE_H264_MAIN: + profile = "main"; + break; ++ case PROFILE_H264_HIGH: ++ profile = "high"; ++ break; + default: + break; + } +@@ -439,11 +434,11 @@ + /* Misc x264 settings (performance, quality, latency, etc). + * Let's just use the x264 predefined preset & tune. + */ +- if (!AV_OPT_SET(ctx->priv_data, "preset", "veryfast", 0)) { ++ if (!AV_OPT_SET(ctx->priv_data, "preset", "ultrafast", 0)) { + PJ_LOG(3, (THIS_FILE, "Failed to set x264 preset 'veryfast'")); + } +- if (!AV_OPT_SET(ctx->priv_data, "tune", "animation+zerolatency", 0)) { +- PJ_LOG(3, (THIS_FILE, "Failed to set x264 tune 'zerolatency'")); ++ if (!AV_OPT_SET(ctx->priv_data, "tune", "fastdecode+zerolatency", 0)) { ++ PJ_LOG(3, (THIS_FILE, "Failed to set x264 tune 'fastdecode+zerolatency'")); + } + } + +@@ -1474,7 +1469,7 @@ + } else { + output->size = err; + output->bit_info = 0; +- if (ff->enc_ctx->coded_frame->key_frame) ++ if (avpacket.flags & AV_PKT_FLAG_KEY) + output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; + } + +@@ -1689,12 +1684,12 @@ + avpacket.size = (int)input->size; + + /* ffmpeg warns: +- * - input buffer padding, at least FF_INPUT_BUFFER_PADDING_SIZE ++ * - input buffer padding, at least AV_INPUT_BUFFER_PADDING_SIZE + * - null terminated + * Normally, encoded buffer is allocated more than needed, so lets just + * bzero the input buffer end/pad, hope it will be just fine. + */ +- pj_bzero(avpacket.data+avpacket.size, FF_INPUT_BUFFER_PADDING_SIZE); ++ pj_bzero(avpacket.data+avpacket.size, AV_INPUT_BUFFER_PADDING_SIZE); + + output->bit_info = 0; + output->timestamp = input->timestamp; +Index: pjmedia/src/pjmedia-codec/openh264.cpp +=================================================================== +--- pjmedia/src/pjmedia-codec/openh264.cpp (revision 5249) ++++ pjmedia/src/pjmedia-codec/openh264.cpp (working copy) +@@ -163,8 +163,8 @@ + + struct SLayerPEncCtx + { +- pj_int32_t iDLayerQp; +- SSliceConfig sSliceCfg; ++ pj_int32_t iDLayerQp; ++ SSliceArgument sSliceArgument; + }; + + PJ_DEF(pj_status_t) pjmedia_codec_openh264_vid_init(pjmedia_vid_codec_mgr *mgr, +@@ -470,18 +470,19 @@ + */ + + /* Init encoder parameters */ +- pj_bzero(&eprm, sizeof(eprm)); +- eprm.iInputCsp = videoFormatI420; ++ oh264_data->enc->GetDefaultParams (&eprm); ++ eprm.iComplexityMode = MEDIUM_COMPLEXITY; + eprm.sSpatialLayers[0].uiProfileIdc = PRO_BASELINE; + eprm.iPicWidth = param->enc_fmt.det.vid.size.w; ++ eprm.iUsageType = CAMERA_VIDEO_REAL_TIME; + eprm.iPicHeight = param->enc_fmt.det.vid.size.h; + eprm.fMaxFrameRate = (param->enc_fmt.det.vid.fps.num * + 1.0f / + param->enc_fmt.det.vid.fps.denum); +- eprm.uiFrameToBeCoded = (unsigned int) -1; + eprm.iTemporalLayerNum = 1; + eprm.uiIntraPeriod = 0; /* I-Frame interval in frames */ +- eprm.bEnableSpsPpsIdAddition = (oh264_data->whole? false : true); ++ eprm.eSpsPpsIdStrategy = (oh264_data->whole ? CONSTANT_ID : ++ INCREASING_ID); + eprm.bEnableFrameCroppingFlag = true; + eprm.iLoopFilterDisableIdc = 0; + eprm.iLoopFilterAlphaC0Offset = 0; +@@ -504,18 +505,19 @@ + + pj_bzero(&elayer_ctx, sizeof (SLayerPEncCtx)); + elayer_ctx.iDLayerQp = 24; +- elayer_ctx.sSliceCfg.uiSliceMode = (oh264_data->whole ? +- SM_SINGLE_SLICE : SM_DYN_SLICE); +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceSizeConstraint = param->enc_mtu; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceNum = 1; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[0] = 960; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[1] = 0; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[2] = 0; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[3] = 0; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[4] = 0; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[5] = 0; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[6] = 0; +- elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[7] = 0; ++ elayer_ctx.sSliceArgument.uiSliceMode = (oh264_data->whole ? ++ SM_SINGLE_SLICE : ++ SM_SIZELIMITED_SLICE); ++ elayer_ctx.sSliceArgument.uiSliceSizeConstraint = param->enc_mtu; ++ elayer_ctx.sSliceArgument.uiSliceNum = 1; ++ elayer_ctx.sSliceArgument.uiSliceMbNum[0] = 960; ++ elayer_ctx.sSliceArgument.uiSliceMbNum[1] = 0; ++ elayer_ctx.sSliceArgument.uiSliceMbNum[2] = 0; ++ elayer_ctx.sSliceArgument.uiSliceMbNum[3] = 0; ++ elayer_ctx.sSliceArgument.uiSliceMbNum[4] = 0; ++ elayer_ctx.sSliceArgument.uiSliceMbNum[5] = 0; ++ elayer_ctx.sSliceArgument.uiSliceMbNum[6] = 0; ++ elayer_ctx.sSliceArgument.uiSliceMbNum[7] = 0; + + elayer->iVideoWidth = eprm.iPicWidth; + elayer->iVideoHeight = eprm.iPicHeight; +@@ -523,14 +525,14 @@ + elayer->uiProfileIdc = eprm.sSpatialLayers[0].uiProfileIdc; + elayer->iSpatialBitrate = eprm.iTargetBitrate; + elayer->iDLayerQp = elayer_ctx.iDLayerQp; +- elayer->sSliceCfg.uiSliceMode = elayer_ctx.sSliceCfg.uiSliceMode; ++ elayer->sSliceArgument.uiSliceMode = elayer_ctx.sSliceArgument.uiSliceMode; + +- memcpy( &elayer->sSliceCfg, +- &elayer_ctx.sSliceCfg, +- sizeof (SSliceConfig)); +- memcpy( &elayer->sSliceCfg.sSliceArgument.uiSliceMbNum[0], +- &elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum[0], +- sizeof (elayer_ctx.sSliceCfg.sSliceArgument.uiSliceMbNum)); ++ memcpy ( &elayer->sSliceArgument, ++ &elayer_ctx.sSliceArgument, ++ sizeof (SSliceArgument)); ++ memcpy ( &elayer->sSliceArgument.uiSliceMbNum[0], ++ &elayer_ctx.sSliceArgument.uiSliceMbNum[0], ++ sizeof (elayer_ctx.sSliceArgument.uiSliceMbNum)); + + /* Init input picture */ + oh264_data->esrc_pic->iColorFormat = videoFormatI420; +@@ -552,13 +554,20 @@ + return PJMEDIA_CODEC_EFAILED; + } + ++ int videoFormat = videoFormatI420; ++ rc = oh264_data->enc->SetOption (ENCODER_OPTION_DATAFORMAT, &videoFormat); ++ if (rc != cmResultSuccess) { ++ PJ_LOG(4,(THIS_FILE, "SVC encoder SetOption videoFormatI420 failed, " ++ "rc=%d", rc)); ++ return PJMEDIA_CODEC_EFAILED; ++ } ++ + /* + * Decoder + */ + sDecParam.sVideoProperty.size = sizeof (sDecParam.sVideoProperty); +- sDecParam.iOutputColorFormat = videoFormatI420; + sDecParam.uiTargetDqLayer = (pj_uint8_t) - 1; +- sDecParam.uiEcActiveFlag = 1; ++ sDecParam.eEcActiveIdc = ERROR_CON_SLICE_COPY; + sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT; + + //TODO: +@@ -576,14 +585,6 @@ + return PJMEDIA_CODEC_EFAILED; + } + +- pj_int32_t color_fmt = videoFormatI420; +- rc = oh264_data->dec->SetOption (DECODER_OPTION_DATAFORMAT, &color_fmt); +- if (rc) { +- PJ_LOG(4,(THIS_FILE, +- "Warning: SetOption(DECODER_OPTION_DATAFORMAT) failed, rc=%d", +- rc)); +- } +- + oh264_data->dec_buf_size = (MAX_RX_WIDTH * MAX_RX_HEIGHT * 3 >> 1) + + (MAX_RX_WIDTH); + oh264_data->dec_buf = (pj_uint8_t*)pj_pool_alloc(oh264_data->pool, +@@ -661,7 +662,7 @@ + return PJMEDIA_CODEC_EFAILED; + } + +- if (oh264_data->bsi.eOutputFrameType == videoFrameTypeSkip) { ++ if (oh264_data->bsi.eFrameType == videoFrameTypeSkip) { + output->size = 0; + output->type = PJMEDIA_FRAME_TYPE_NONE; + output->timestamp = input->timestamp; +@@ -681,13 +682,13 @@ + + /* Find which layer with biggest payload */ + oh264_data->ilayer = 0; +- payload_size = oh264_data->bsi.sLayerInfo[0].iNalLengthInByte[0]; ++ payload_size = oh264_data->bsi.sLayerInfo[0].pNalLengthInByte[0]; + for (i=0; i < (unsigned)oh264_data->bsi.iLayerNum; ++i) { + unsigned j; + pLayerBsInfo = &oh264_data->bsi.sLayerInfo[i]; + for (j=0; j < (unsigned)pLayerBsInfo->iNalCount; ++j) { +- if (pLayerBsInfo->iNalLengthInByte[j] > (int)payload_size) { +- payload_size = pLayerBsInfo->iNalLengthInByte[j]; ++ if (pLayerBsInfo->pNalLengthInByte[j] > (int)payload_size) { ++ payload_size = pLayerBsInfo->pNalLengthInByte[j]; + oh264_data->ilayer = i; + } + } +@@ -703,7 +704,7 @@ + payload = pLayerBsInfo->pBsBuf; + payload_size = 0; + for (int inal = pLayerBsInfo->iNalCount - 1; inal >= 0; --inal) { +- payload_size += pLayerBsInfo->iNalLengthInByte[inal]; ++ payload_size += pLayerBsInfo->pNalLengthInByte[inal]; + } + + if (payload_size > out_size) +@@ -760,7 +761,7 @@ + pj_memcpy(output->buf, payload, payload_len); + output->size = payload_len; + +- if (oh264_data->bsi.eOutputFrameType == videoFrameTypeIDR) { ++ if (oh264_data->bsi.eFrameType == videoFrameTypeIDR) { + output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; + } + +@@ -782,7 +783,7 @@ + + oh264_data->enc_frame_size = 0; + for (int inal = pLayerBsInfo->iNalCount - 1; inal >= 0; --inal) { +- oh264_data->enc_frame_size += pLayerBsInfo->iNalLengthInByte[inal]; ++ oh264_data->enc_frame_size += pLayerBsInfo->pNalLengthInByte[inal]; + } + + oh264_data->enc_frame_whole = pLayerBsInfo->pBsBuf; +@@ -809,7 +810,7 @@ + pj_memcpy(output->buf, payload, payload_len); + output->size = payload_len; + +- if (oh264_data->bsi.eOutputFrameType == videoFrameTypeIDR) { ++ if (oh264_data->bsi.eFrameType == videoFrameTypeIDR) { + output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME; + } + +Index: pjmedia/src/pjmedia-codec/opus.c +=================================================================== +--- pjmedia/src/pjmedia-codec/opus.c (revision 5249) ++++ pjmedia/src/pjmedia-codec/opus.c (working copy) +@@ -1,1070 +1,762 @@ +-/* $Id$ */ +-/* +- * Copyright (C) 2015-2016 Teluu Inc. (http://www.teluu.com) +- * Copyright (C) 2012-2015 Zaark Technology AB ++/** ++ * Copyright (C) 2010 Regis Montoya (aka r3gis - www.r3gis.fr) ++ * This file is part of pjsip_android. + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at + * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. ++ * http://www.apache.org/licenses/LICENSE-2.0 + * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. + */ +-/* This file is the implementation of Opus codec wrapper and was contributed by +- * Zaark Technology AB +- */ + + #include ++#include ++#include ++#include + #include +-#include ++#include ++#include ++#include ++#include + #include + + #if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0) + +-#include ++#include "../../third_party/opus/include/opus.h" + +-#define THIS_FILE "opus.c" ++/* Opus can encode frames of 2.5, 5, 10, 20, 40, or 60 ms. */ ++#define FRAME_LENGTH_MS 20 + +-/* Default packet loss concealment setting. */ +-#define OPUS_DEFAULT_PLC 1 +-/* Default Voice Activity Detector setting. */ +-#define OPUS_DEFAULT_VAD 0 ++/* It can also combine multiple frames into packets of up to 120 ms */ ++/* So at maximum 2.5ms * 48frames = 120ms*/ ++#define OPUS_MAX_FRAMES_PER_PACKET 48 + +-/* Maximum size of an encoded packet. +- * If the the actual size is bigger, the encode/parse will fail. +- */ +-#define MAX_ENCODED_PACKET_SIZE 1280 ++#define OPUS_CLOCK_RATE 48000 + +-/* Tracing */ +-#if 0 +-# define TRACE_(expr) PJ_LOG(4,expr) +-#else +-# define TRACE_(expr) +-#endif ++#define _TRACE_OPUS 0 + ++#define THIS_FILE "opus.c" + +-/* Prototypes for Opus factory */ +-static pj_status_t factory_test_alloc( pjmedia_codec_factory *factory, +- const pjmedia_codec_info *ci ); +-static pj_status_t factory_default_attr( pjmedia_codec_factory *factory, +- const pjmedia_codec_info *ci, +- pjmedia_codec_param *attr ); +-static pj_status_t factory_enum_codecs( pjmedia_codec_factory *factory, +- unsigned *count, +- pjmedia_codec_info codecs[]); +-static pj_status_t factory_alloc_codec( pjmedia_codec_factory *factory, +- const pjmedia_codec_info *ci, +- pjmedia_codec **p_codec); +-static pj_status_t factory_dealloc_codec( pjmedia_codec_factory *factory, +- pjmedia_codec *codec ); ++/* Prototypes for OPUS factory */ ++static pj_status_t opus_test_alloc(pjmedia_codec_factory *factory, ++ const pjmedia_codec_info *id); ++static pj_status_t opus_default_attr(pjmedia_codec_factory *factory, ++ const pjmedia_codec_info *id, ++ pjmedia_codec_param *attr); ++static pj_status_t opus_enum_codecs(pjmedia_codec_factory *factory, ++ unsigned *count, ++ pjmedia_codec_info codecs[]); ++static pj_status_t opus_alloc_codec(pjmedia_codec_factory *factory, ++ const pjmedia_codec_info *id, ++ pjmedia_codec **p_codec); ++static pj_status_t opus_dealloc_codec(pjmedia_codec_factory *factory, ++ pjmedia_codec *codec); + ++/* Prototypes for OPUS implementation. */ ++static pj_status_t opus_codec_init(pjmedia_codec *codec, ++ pj_pool_t *pool); ++static pj_status_t opus_codec_open(pjmedia_codec *codec, ++ pjmedia_codec_param *attr); ++static pj_status_t opus_codec_close(pjmedia_codec *codec); ++static pj_status_t opus_codec_modify(pjmedia_codec *codec, ++ const pjmedia_codec_param *attr); ++static pj_status_t opus_codec_parse(pjmedia_codec *codec, ++ void *pkt, ++ pj_size_t pkt_size, ++ const pj_timestamp *timestamp, ++ unsigned *frame_cnt, ++ pjmedia_frame frames[]); ++static pj_status_t opus_codec_encode(pjmedia_codec *codec, ++ const struct pjmedia_frame *input, ++ unsigned output_buf_len, ++ struct pjmedia_frame *output); ++static pj_status_t opus_codec_decode(pjmedia_codec *codec, ++ const struct pjmedia_frame *input, ++ unsigned output_buf_len, ++ struct pjmedia_frame *output); ++static pj_status_t opus_codec_recover(pjmedia_codec *codec, ++ unsigned output_buf_len, ++ struct pjmedia_frame *output); + +-/* Prototypes for Opus implementation. */ +-static pj_status_t codec_init( pjmedia_codec *codec, +- pj_pool_t *pool ); +-static pj_status_t codec_open( pjmedia_codec *codec, +- pjmedia_codec_param *attr ); +-static pj_status_t codec_close( pjmedia_codec *codec ); +-static pj_status_t codec_modify( pjmedia_codec *codec, +- const pjmedia_codec_param *attr ); +-static pj_status_t codec_parse( pjmedia_codec *codec, +- void *pkt, +- pj_size_t pkt_size, +- const pj_timestamp *ts, +- unsigned *frame_cnt, +- pjmedia_frame frames[]); +-static pj_status_t codec_encode( pjmedia_codec *codec, +- const struct pjmedia_frame *input, +- unsigned output_buf_len, +- struct pjmedia_frame *output); +-static pj_status_t codec_decode( pjmedia_codec *codec, +- const struct pjmedia_frame *input, +- unsigned output_buf_len, +- struct pjmedia_frame *output); +-static pj_status_t codec_recover( pjmedia_codec *codec, +- unsigned output_buf_len, +- struct pjmedia_frame *output); +- +-/* Definition for Opus operations. */ +-static pjmedia_codec_op opus_op = +-{ +- &codec_init, +- &codec_open, +- &codec_close, +- &codec_modify, +- &codec_parse, +- &codec_encode, +- &codec_decode, +- &codec_recover ++/* Definition for OPUS codec operations. */ ++static pjmedia_codec_op opus_op = { ++ &opus_codec_init, ++ &opus_codec_open, ++ &opus_codec_close, ++ &opus_codec_modify, ++ &opus_codec_parse, ++ &opus_codec_encode, ++ &opus_codec_decode, ++ &opus_codec_recover + }; + +-/* Definition for Opus factory operations. */ +-static pjmedia_codec_factory_op opus_factory_op = +-{ +- &factory_test_alloc, +- &factory_default_attr, +- &factory_enum_codecs, +- &factory_alloc_codec, +- &factory_dealloc_codec, ++/* Definition for OPUS codec factory operations. */ ++static pjmedia_codec_factory_op opus_factory_op = { ++ &opus_test_alloc, ++ &opus_default_attr, ++ &opus_enum_codecs, ++ &opus_alloc_codec, ++ &opus_dealloc_codec, + &pjmedia_codec_opus_deinit + }; + ++/* OPUS factory private data */ ++static struct opus_factory { ++ pjmedia_codec_factory base; ++ pjmedia_endpt *endpt; ++ pj_pool_t *pool; ++ pj_mutex_t *mutex; ++ pjmedia_codec codec_list; ++} opus_factory; + +-/* Opus factory */ +-struct opus_codec_factory +-{ +- pjmedia_codec_factory base; +- pjmedia_endpt *endpt; +- pj_pool_t *pool; +-}; ++/* OPUS codec private data. */ ++struct opus_private { ++ pj_pool_t *pool; /* Pool for each instance. */ ++ pj_uint8_t pcm_bytes_per_sample; + +-/* Opus codec private data. */ +-struct opus_data +-{ +- pj_pool_t *pool; +- pj_mutex_t *mutex; +- OpusEncoder *enc; +- OpusDecoder *dec; +- OpusRepacketizer *enc_packer; +- OpusRepacketizer *dec_packer; +- pjmedia_codec_opus_config cfg; +- unsigned ptime; +- pjmedia_frame dec_frame[2]; +- int dec_frame_index; +-}; ++ int externalFs; /* Clock rate we would like to limit from outside */ + +-/* Codec factory instance */ +-static struct opus_codec_factory opus_codec_factory; ++ pj_bool_t enc_ready; ++ OpusEncoder* psEnc; + +-/* Opus default configuration */ +-static pjmedia_codec_opus_config opus_cfg = +-{ +- PJMEDIA_CODEC_OPUS_DEFAULT_SAMPLE_RATE, /* Sample rate */ +- 1, /* Channel count */ +- PJMEDIA_CODEC_OPUS_DEFAULT_BIT_RATE, /* Bit rate */ +- 5, /* Expected packet loss */ +- PJMEDIA_CODEC_OPUS_DEFAULT_COMPLEXITY, /* Complexity */ +- PJMEDIA_CODEC_OPUS_DEFAULT_CBR, /* Constant bit rate */ ++ pj_bool_t dec_ready; ++ OpusDecoder* psDec; ++ ++ /* Buffer of 120ms to hold decoded frames. */ ++ void *dec_buf; ++ pj_size_t dec_buf_size; ++ pj_size_t dec_buf_max_size; ++ int dec_buf_sample_per_frame; ++ pj_uint32_t pkt_info; /* Packet info for buffered frames. */ + }; + +- +-static int get_opus_bw_constant (unsigned sample_rate) +-{ +- if (sample_rate <= 8000) +- return OPUS_BANDWIDTH_NARROWBAND; +- else if (sample_rate <= 12000) +- return OPUS_BANDWIDTH_MEDIUMBAND; +- else if (sample_rate <= 16000) +- return OPUS_BANDWIDTH_WIDEBAND; +- else if (sample_rate <= 24000) +- return OPUS_BANDWIDTH_SUPERWIDEBAND; +- else +- return OPUS_BANDWIDTH_FULLBAND; ++int opus_to_pjsip_error_code(int opus_error) { ++ switch (opus_error) { ++ case OPUS_BAD_ARG: ++ /* One or more invalid/out of range arguments */ ++ return PJ_EINVAL; ++ case OPUS_BUFFER_TOO_SMALL: ++ /* The mode struct passed is invalid */ ++ return PJMEDIA_CODEC_EPCMTOOSHORT; ++ case OPUS_INTERNAL_ERROR: ++ /* An internal error was detected */ ++ return PJMEDIA_CODEC_EFAILED; ++ case OPUS_INVALID_PACKET: ++ /* The compressed data passed is corrupted */ ++ return PJMEDIA_CODEC_EBADBITSTREAM; ++ case OPUS_UNIMPLEMENTED: ++ /* Invalid/unsupported request number */ ++ return PJ_ENOTSUP; ++ case OPUS_INVALID_STATE: ++ /* An encoder or decoder structure is invalid or already freed */ ++ return PJ_EINVALIDOP; ++ case OPUS_ALLOC_FAIL: ++ /* Memory allocation has failed */ ++ return PJMEDIA_CODEC_EFAILED; ++ } ++ return PJMEDIA_ERROR; + } + +- + /* +- * Initialize and register Opus codec factory to pjmedia endpoint. ++ * Apply opus settings to dec_fmtp parameters + */ +-PJ_DEF(pj_status_t) pjmedia_codec_opus_init( pjmedia_endpt *endpt ) +-{ +- pj_status_t status; +- pjmedia_codec_mgr *codec_mgr; ++void apply_opus_codec_params(pj_pool_t* pool, pjmedia_codec_param *attr) { ++ attr->setting.dec_fmtp.cnt = 0; ++ attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].name = pj_str("useinbandfec"); ++ if (attr->setting.plc == 0) { ++ attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val = pj_str("0"); ++ } else { ++ attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val = pj_str("1"); ++ } ++ attr->setting.dec_fmtp.cnt++; ++ if (attr->setting.vad == 1) { ++ attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].name = pj_str("usedtx"); ++ attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val = pj_str("1"); ++ attr->setting.dec_fmtp.cnt++; ++ } ++ if (attr->info.channel_cnt == 2) { ++ attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].name = pj_str("stereo"); ++ attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val = pj_str("1"); ++ attr->setting.dec_fmtp.cnt++; ++ } ++ if (attr->info.clock_rate < 48000) { ++ attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].name = pj_str("maxcodedaudiobandwidth"); ++ char clock_rate_char[8]; ++ pj_utoa(attr->info.clock_rate, clock_rate_char); ++ pj_strdup2(pool, &attr->setting.dec_fmtp.param[attr->setting.dec_fmtp.cnt].val, clock_rate_char); ++ attr->setting.dec_fmtp.cnt++; ++ } ++} + +- PJ_ASSERT_RETURN(endpt, PJ_EINVAL); ++PJ_DEF(pj_status_t) pjmedia_codec_opus_init(pjmedia_endpt *endpt) { ++ pjmedia_codec_mgr *codec_mgr; ++ pj_status_t status; + +- if (opus_codec_factory.pool != NULL) +- return PJ_SUCCESS; ++ if (opus_factory.endpt != NULL) { ++ /* Already initialized. */ ++ return PJ_SUCCESS; ++ } + +- /* Create the Opus codec factory */ +- opus_codec_factory.base.op = &opus_factory_op; +- opus_codec_factory.base.factory_data = &opus_codec_factory; +- opus_codec_factory.endpt = endpt; ++ /* Init factory */ ++ opus_factory.base.op = &opus_factory_op; ++ opus_factory.base.factory_data = NULL; ++ opus_factory.endpt = endpt; + +- opus_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "opus-factory", +- 1024, 1024); +- if (!opus_codec_factory.pool) { +- PJ_LOG(2, (THIS_FILE, "Unable to create memory pool for Opus codec")); +- return PJ_ENOMEM; +- } ++ /* Create pool */ ++ opus_factory.pool = pjmedia_endpt_create_pool(endpt, "opus codecs", 4000, 4000); ++ if (!opus_factory.pool) ++ return PJ_ENOMEM; + +- /* Get the codec manager */ +- codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); +- if (!codec_mgr) { +- PJ_LOG(2, (THIS_FILE, "Unable to get the codec manager")); +- status = PJ_EINVALIDOP; +- goto on_codec_factory_error; +- } ++ /* Init list */ ++ pj_list_init(&opus_factory.codec_list); + +- /* Register the codec factory */ +- status = pjmedia_codec_mgr_register_factory (codec_mgr, +- &opus_codec_factory.base); +- if (status != PJ_SUCCESS) { +- PJ_LOG(2, (THIS_FILE, "Unable to register the codec factory")); +- goto on_codec_factory_error; +- } ++ /* Create mutex. */ ++ status = pj_mutex_create_simple(opus_factory.pool, "opus codecs", &opus_factory.mutex); ++ if (status != PJ_SUCCESS) ++ goto on_error; + +- return PJ_SUCCESS; ++ PJ_LOG(5, (THIS_FILE, "Init opus")); + +-on_codec_factory_error: +- pj_pool_release(opus_codec_factory.pool); +- opus_codec_factory.pool = NULL; +- return status; +-} ++ /* Get the codec manager. */ ++ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); ++ if (!codec_mgr) ++ return PJ_EINVALIDOP; + + +-/* +- * Unregister Opus codec factory from pjmedia endpoint and +- * deinitialize the codec. +- */ +-PJ_DEF(pj_status_t) pjmedia_codec_opus_deinit( void ) +-{ +- pj_status_t status; +- pjmedia_codec_mgr *codec_mgr; ++ PJ_LOG(5, (THIS_FILE, "Init opus > DONE")); + +- if (opus_codec_factory.pool == NULL) ++ /* Register codec factory to endpoint. */ ++ status = pjmedia_codec_mgr_register_factory(codec_mgr, &opus_factory.base); ++ if (status != PJ_SUCCESS) ++ return status; ++ + return PJ_SUCCESS; + +- /* Get the codec manager */ +- codec_mgr = pjmedia_endpt_get_codec_mgr(opus_codec_factory.endpt); +- if (!codec_mgr) { +- PJ_LOG(2, (THIS_FILE, "Unable to get the codec manager")); +- pj_pool_release(opus_codec_factory.pool); +- opus_codec_factory.pool = NULL; +- return PJ_EINVALIDOP; +- } ++on_error: ++ if (opus_factory.mutex) { ++ pj_mutex_destroy(opus_factory.mutex); ++ opus_factory.mutex = NULL; ++ } ++ if (opus_factory.pool) { ++ pj_pool_release(opus_factory.pool); ++ opus_factory.pool = NULL; ++ } + +- /* Unregister the codec factory */ +- status = pjmedia_codec_mgr_unregister_factory(codec_mgr, +- &opus_codec_factory.base); +- if (status != PJ_SUCCESS) +- PJ_LOG(2, (THIS_FILE, "Unable to unregister the codec factory")); +- +- /* Release the memory pool */ +- pj_pool_release(opus_codec_factory.pool); +- opus_codec_factory.pool = NULL; +- +- return status; ++ return status; + } + +- +-/** +- * Get the opus configuration for a specific sample rate. ++/* ++ * Unregister OPUS codec factory from pjmedia endpoint and deinitialize ++ * the OPUS codec library. + */ +-PJ_DEF(pj_status_t) +-pjmedia_codec_opus_get_config( pjmedia_codec_opus_config *cfg ) +-{ +- PJ_ASSERT_RETURN(cfg, PJ_EINVAL); ++PJ_DEF(pj_status_t) pjmedia_codec_opus_deinit(void) { ++ pjmedia_codec_mgr *codec_mgr; ++ pj_status_t status; + +- pj_memcpy(cfg, &opus_cfg, sizeof(pjmedia_codec_opus_config)); +- return PJ_SUCCESS; +-} ++ if (opus_factory.endpt == NULL) { ++ /* Not registered. */ ++ return PJ_SUCCESS; ++ } + ++ /* Lock mutex. */ ++ pj_mutex_lock(opus_factory.mutex); + +-static pj_str_t STR_MAX_PLAYBACK = {"maxplaybackrate", 15}; +-static pj_str_t STR_MAX_CAPTURE = {"sprop-maxcapturerate", 20}; +-static pj_str_t STR_STEREO = {"stereo", 6}; +-static pj_str_t STR_SPROP_STEREO = {"sprop-stereo", 12}; +-static pj_str_t STR_MAX_BIT_RATE = {"maxaveragebitrate", 17}; +-static pj_str_t STR_INBAND_FEC = {"useinbandfec", 12}; +-static pj_str_t STR_DTX = {"usedtx", 6}; +-static pj_str_t STR_CBR = {"cbr", 3}; +- +-static int find_fmtp(pjmedia_codec_fmtp *fmtp, pj_str_t *name, pj_bool_t add) +-{ +- int i; +- for (i = 0; i < fmtp->cnt; i++) { +- if (pj_stricmp(&fmtp->param[i].name, name) == 0) +- return i; +- } +- +- if (add && (i < PJMEDIA_CODEC_MAX_FMTP_CNT)) { +- fmtp->param[i].name = *name; +- fmtp->cnt++; +- return i; +- } else +- return -1; +-} +- +-static pj_status_t generate_fmtp(pjmedia_codec_param *attr) +-{ +- int idx; +- static char bitrate_str[12]; +- static char clockrate_str[12]; +- +- if (attr->info.clock_rate != 48000) { +- pj_ansi_snprintf(clockrate_str, sizeof(clockrate_str), "%u", +- attr->info.clock_rate); +- +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_PLAYBACK, PJ_TRUE); +- if (idx >= 0) +- attr->setting.dec_fmtp.param[idx].val = pj_str(clockrate_str); +- +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_CAPTURE, PJ_TRUE); +- if (idx >= 0) +- attr->setting.dec_fmtp.param[idx].val = pj_str(clockrate_str); +- } +- +- /* Check if we need to set parameter 'maxaveragebitrate' */ +- if (opus_cfg.bit_rate > 0) { +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_BIT_RATE, PJ_TRUE); +- if (idx >= 0) { +- pj_ansi_snprintf(bitrate_str, sizeof(bitrate_str), "%u", +- attr->info.avg_bps); +- attr->setting.dec_fmtp.param[idx].val = pj_str(bitrate_str); ++ /* Get the codec manager. */ ++ codec_mgr = pjmedia_endpt_get_codec_mgr(opus_factory.endpt); ++ if (!codec_mgr) { ++ opus_factory.endpt = NULL; ++ pj_mutex_unlock(opus_factory.mutex); ++ return PJ_EINVALIDOP; + } +- } + +- if (attr->info.channel_cnt > 1) { +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_STEREO, PJ_TRUE); +- if (idx >= 0) +- attr->setting.dec_fmtp.param[idx].val = pj_str("1"); ++ /* Unregister opus codec factory. */ ++ status = pjmedia_codec_mgr_unregister_factory(codec_mgr, &opus_factory.base); ++ opus_factory.endpt = NULL; + +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_SPROP_STEREO, PJ_TRUE); +- if (idx >= 0) +- attr->setting.dec_fmtp.param[idx].val = pj_str("1"); +- } ++ /* Destroy mutex. */ ++ pj_mutex_unlock(opus_factory.mutex); ++ pj_mutex_destroy(opus_factory.mutex); ++ opus_factory.mutex = NULL; + +- if (opus_cfg.cbr) { +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_CBR, PJ_TRUE); +- if (idx >= 0) +- attr->setting.dec_fmtp.param[idx].val = pj_str("1"); +- } ++ /* Release pool. */ ++ pj_pool_release(opus_factory.pool); ++ opus_factory.pool = NULL; + +- if (attr->setting.plc) { +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_INBAND_FEC, PJ_TRUE); +- if (idx >= 0) +- attr->setting.dec_fmtp.param[idx].val = pj_str("1"); +- } +- +- if (attr->setting.vad) { +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_DTX, PJ_TRUE); +- if (idx >= 0) +- attr->setting.dec_fmtp.param[idx].val = pj_str("1"); +- } +- +- return PJ_SUCCESS; ++ return status; + } + +-/** +- * Set the opus configuration and default param. ++/* ++ * Check if factory can allocate the specified codec. + */ +-PJ_DEF(pj_status_t) +-pjmedia_codec_opus_set_default_param(const pjmedia_codec_opus_config *cfg, +- pjmedia_codec_param *param ) +-{ +- const pj_str_t opus_str = {"opus", 4}; +- const pjmedia_codec_info *info[1]; +- pjmedia_codec_mgr *codec_mgr; +- unsigned count = 1; +- pj_status_t status; ++static pj_status_t opus_test_alloc(pjmedia_codec_factory *factory, const pjmedia_codec_info *info) { ++ const pj_str_t opus_tag = { "opus", 4 }; + +- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); +- PJ_ASSERT_RETURN(cfg, PJ_EINVAL); ++ PJ_UNUSED_ARG(factory); ++ PJ_ASSERT_RETURN(factory==&opus_factory.base, PJ_EINVAL); + +- codec_mgr = pjmedia_endpt_get_codec_mgr(opus_codec_factory.endpt); ++ /* Type MUST be audio. */ ++ if (info->type != PJMEDIA_TYPE_AUDIO) ++ return PJMEDIA_CODEC_EUNSUP; + +- status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, &opus_str, +- &count, info, NULL); +- if (status != PJ_SUCCESS) +- return status; ++ /* Check encoding name. */ ++ if (pj_stricmp(&info->encoding_name, &opus_tag) != 0) ++ return PJMEDIA_CODEC_EUNSUP; + +- /* Set sample rate */ +- if (cfg->sample_rate != 8000 && cfg->sample_rate != 12000 && +- cfg->sample_rate != 16000 && cfg->sample_rate != 24000 && +- cfg->sample_rate != 48000) +- { +- return PJ_EINVAL; +- } +- param->info.clock_rate = opus_cfg.sample_rate = cfg->sample_rate; ++ /* Check clock-rate */ ++ if (info->clock_rate == 8000 || info->clock_rate == 12000 || ++ info->clock_rate == 16000 || info->clock_rate == 24000 || info->clock_rate == 48000) { ++ return PJ_SUCCESS; ++ } + +- /* Set channel count */ +- if (cfg->channel_cnt != 1 && cfg->channel_cnt != 2) +- return PJ_EINVAL; +- param->info.channel_cnt = opus_cfg.channel_cnt = cfg->channel_cnt; +- +- /* Set bit_rate */ +- if (cfg->bit_rate < 6000 || cfg->bit_rate > 510000) { +- return PJ_EINVAL; +- } +- opus_cfg.bit_rate = cfg->bit_rate; +- +- /* Set expected packet loss */ +- if (cfg->packet_loss >= 100) +- return PJ_EINVAL; +- opus_cfg.packet_loss = cfg->packet_loss; +- +- /* Set complexity */ +- if (cfg->complexity > 10) +- return PJ_EINVAL; +- opus_cfg.complexity = cfg->complexity; +- +- opus_cfg.cbr = cfg->cbr; +- +- generate_fmtp(param); +- +- status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param); +- return status; ++ /* Clock rate not supported */ ++ return PJMEDIA_CODEC_EUNSUP; + } + +- + /* +- * Check if factory can allocate the specified codec. ++ * Generate default attribute. + */ +-static pj_status_t factory_test_alloc( pjmedia_codec_factory *factory, +- const pjmedia_codec_info *ci ) +-{ +- const pj_str_t opus_tag = {"OPUS", 4}; ++static pj_status_t opus_default_attr(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr) { ++ PJ_ASSERT_RETURN(factory == &opus_factory.base, PJ_EINVAL); ++ pj_bzero(attr, sizeof(pjmedia_codec_param)); + +- PJ_UNUSED_ARG(factory); +- PJ_ASSERT_RETURN(factory==&opus_codec_factory.base, PJ_EINVAL); ++ /* Table from opus rfc ++ +-------+---------+-----------+ ++ | Mode | fs (Hz) | BR (kbps) | ++ +-------+---------+-----------+ ++ | voice | 8000 | 6 - 20 | ++ | voice | 12000 | 7 - 25 | ++ | voice | 16000 | 8 - 30 | ++ | voice | 24000 | 18 - 28 | ++ | voice | 48000 | 24 - 32 | ++ +-------+---------+-----------+ ++ */ + +- /* Type MUST be audio. */ +- if (ci->type != PJMEDIA_TYPE_AUDIO) +- return PJMEDIA_CODEC_EUNSUP; ++ attr->info.channel_cnt = 1; ++ /* SAGH: set to 2? */ ++ /* ++ * TODO : would like to use 16kHz as internal clock rate in our case ++ * pjmedia seems to have no support of different clock rate for RTP ++ * and for associated port. Keeping 48kHz for RTP is needed (we just have ++ * to transform timestamps) but to feed codec with 16kHz frames seems requires ++ * some extra work in pjmedia. ++ * For now we are obliged to use pjmedia resampler while would be ++ * more efficient to use the Opus feature instead. ++ * Using g722 hack was tried but seems useless. ++ */ ++ attr->info.clock_rate = 48000; ++ attr->info.avg_bps = 20000; ++ attr->info.max_bps = 32000; ++ attr->info.frm_ptime = FRAME_LENGTH_MS; ++ attr->info.pcm_bits_per_sample = 16; ++ attr->info.pt = (pj_uint8_t) id->pt; + +- /* Check encoding name. */ +- if (pj_stricmp(&ci->encoding_name, &opus_tag) != 0) +- return PJMEDIA_CODEC_EUNSUP; ++ /* Inform the stream to prepare a larger buffer since we cannot parse ++ * OPUS packets and split it into individual frames. ++ * Max packet size of opus is 120ms audio ++ */ ++ attr->info.max_rx_frame_size = attr->info.max_bps * 120 / 8 / 1000; ++ if ((attr->info.max_bps * attr->info.frm_ptime) % 8000 != 0) ++ ++attr->info.max_rx_frame_size; + +- /* Check clock rate */ +- if (ci->clock_rate != 8000 && ci->clock_rate != 12000 && +- ci->clock_rate != 16000 && ci->clock_rate != 24000 && +- ci->clock_rate != 48000) +- { +- return PJMEDIA_CODEC_EUNSUP; +- } + +- return PJ_SUCCESS; +-} ++ attr->setting.frm_per_pkt = 1; ++ /* Default usedtx is 0 in opus */ ++ attr->setting.vad = 0; ++ /* Default useinbandfec is 1 in opus */ ++ attr->setting.plc = 1; + ++ /* Apply these settings to relevant fmtp parameters */ ++ apply_opus_codec_params(opus_factory.pool, attr); + +-/* +- * Generate default attribute. +- */ +-static pj_status_t factory_default_attr( pjmedia_codec_factory *factory, +- const pjmedia_codec_info *ci, +- pjmedia_codec_param *attr ) +-{ +- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); +- +- pj_bzero(attr, sizeof(pjmedia_codec_param)); +- attr->info.pt = (pj_uint8_t)ci->pt; +- attr->info.clock_rate = opus_cfg.sample_rate; +- attr->info.channel_cnt = opus_cfg.channel_cnt; +- attr->info.avg_bps = (opus_cfg.bit_rate > 0) ? +- opus_cfg.bit_rate : +- opus_cfg.sample_rate; // Estimate +- attr->info.max_bps = opus_cfg.bit_rate * 2; +- attr->info.frm_ptime = 20; +- attr->setting.frm_per_pkt = 1; +- attr->info.pcm_bits_per_sample = 16; +- attr->setting.vad = OPUS_DEFAULT_VAD; +- attr->setting.plc = OPUS_DEFAULT_PLC; +- +- generate_fmtp(attr); +- +- return PJ_SUCCESS; ++ return PJ_SUCCESS; + } + +- + /* + * Enum codecs supported by this factory. + */ +-static pj_status_t factory_enum_codecs( pjmedia_codec_factory *factory, +- unsigned *count, +- pjmedia_codec_info codecs[] ) +-{ +- PJ_UNUSED_ARG(factory); +- PJ_ASSERT_RETURN(codecs, PJ_EINVAL); ++static pj_status_t opus_enum_codecs(pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]) { ++ PJ_UNUSED_ARG(factory); ++ PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL); + +- if (*count > 0) { + pj_bzero(&codecs[0], sizeof(pjmedia_codec_info)); +- codecs[0].type = PJMEDIA_TYPE_AUDIO; +- codecs[0].pt = PJMEDIA_RTP_PT_OPUS; +- /* +- * RFC 7587, Section 7: +- * The media subtype ("opus") goes in SDP "a=rtpmap" as the encoding +- * name. The RTP clock rate in "a=rtpmap" MUST be 48000 and the +- * number of channels MUST be 2. +- */ + codecs[0].encoding_name = pj_str("opus"); +- codecs[0].clock_rate = 48000; +- codecs[0].channel_cnt = 2; ++ codecs[0].pt = PJMEDIA_RTP_PT_OPUS; ++ codecs[0].type = PJMEDIA_TYPE_AUDIO; ++ codecs[0].clock_rate = 48000; ++ codecs[0].channel_cnt = 1; /* SAGHUL: set to 2? */ ++ + *count = 1; +- } + +- return PJ_SUCCESS; ++ return PJ_SUCCESS; ++ + } + +- + /* +- * Allocate a new Opus codec instance. ++ * Allocate a new OPUS codec instance. + */ +-static pj_status_t factory_alloc_codec( pjmedia_codec_factory *factory, +- const pjmedia_codec_info *ci, +- pjmedia_codec **p_codec ) +-{ +- pjmedia_codec *codec; +- pj_pool_t *pool; +- pj_status_t status; +- struct opus_data *opus_data; +- struct opus_codec_factory *f = (struct opus_codec_factory*) factory; ++static pj_status_t opus_alloc_codec(pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec) { ++ pjmedia_codec *codec; ++ struct opus_private *opus; + +- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); ++ PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL); ++ PJ_ASSERT_RETURN(factory == &opus_factory.base, PJ_EINVAL); + +- pool = pjmedia_endpt_create_pool(f->endpt, "opus", 512, 512); +- if (!pool) return PJ_ENOMEM; +- +- opus_data = PJ_POOL_ZALLOC_T(pool, struct opus_data); +- codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec); ++ pj_mutex_lock(opus_factory.mutex); + +- status = pj_mutex_create_simple (pool, "opus_mutex", &opus_data->mutex); +- if (status != PJ_SUCCESS) { +- pj_pool_release(pool); +- return status; +- } ++ /* Get free nodes, if any. */ ++ if (!pj_list_empty(&opus_factory.codec_list)) { ++ codec = opus_factory.codec_list.next; ++ pj_list_erase(codec); ++ } else { ++ codec = PJ_POOL_ZALLOC_T(opus_factory.pool, pjmedia_codec); ++ PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM); ++ codec->op = &opus_op; ++ codec->factory = factory; ++ codec->codec_data = pj_pool_alloc(opus_factory.pool, sizeof(struct opus_private)); ++ } + +- pj_memcpy(&opus_data->cfg, &opus_cfg, sizeof(pjmedia_codec_opus_config)); +- opus_data->pool = pool; +- codec->op = &opus_op; +- codec->factory = factory; +- codec->codec_data = opus_data; ++ pj_mutex_unlock(opus_factory.mutex); + +- *p_codec = codec; +- return PJ_SUCCESS; ++ opus = (struct opus_private*) codec->codec_data; ++ opus->enc_ready = PJ_FALSE; ++ opus->dec_ready = PJ_FALSE; ++ ++ /* Create pool for codec instance */ ++ opus->pool = pjmedia_endpt_create_pool(opus_factory.endpt, "opuscodec", 512, 512); ++ ++ *p_codec = codec; ++ return PJ_SUCCESS; + } + +- + /* + * Free codec. + */ +-static pj_status_t factory_dealloc_codec( pjmedia_codec_factory *factory, +- pjmedia_codec *codec ) +-{ +- struct opus_data *opus_data; ++static pj_status_t opus_dealloc_codec(pjmedia_codec_factory *factory, pjmedia_codec *codec) { ++ struct opus_private *opus; + +- PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); +- PJ_ASSERT_RETURN(factory == &opus_codec_factory.base, PJ_EINVAL); ++ PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL); ++ PJ_UNUSED_ARG(factory); ++ PJ_ASSERT_RETURN(factory == &opus_factory.base, PJ_EINVAL); + +- opus_data = (struct opus_data *)codec->codec_data; +- if (opus_data) { +- pj_mutex_destroy(opus_data->mutex); +- opus_data->mutex = NULL; +- pj_pool_release(opus_data->pool); +- } ++ opus = (struct opus_private*) codec->codec_data; + +- return PJ_SUCCESS; ++ /* Close codec, if it's not closed. */ ++ if (opus->enc_ready || opus->dec_ready) ++ opus_codec_close(codec); ++ ++ /* Put in the free list. */ ++ pj_mutex_lock(opus_factory.mutex); ++ pj_list_push_front(&opus_factory.codec_list, codec); ++ pj_mutex_unlock(opus_factory.mutex); ++ ++ pj_pool_release(opus->pool); ++ opus->pool = NULL; ++ ++ return PJ_SUCCESS; + } + +- + /* + * Init codec. + */ +-static pj_status_t codec_init( pjmedia_codec *codec, +- pj_pool_t *pool ) +-{ +- PJ_UNUSED_ARG(codec); +- PJ_UNUSED_ARG(pool); +- return PJ_SUCCESS; ++static pj_status_t opus_codec_init(pjmedia_codec *codec, pj_pool_t *pool) { ++ PJ_UNUSED_ARG(codec); ++ PJ_UNUSED_ARG(pool); ++ return PJ_SUCCESS; + } + +- + /* + * Open codec. + */ +-static pj_status_t codec_open( pjmedia_codec *codec, +- pjmedia_codec_param *attr ) +-{ +- struct opus_data *opus_data = (struct opus_data *)codec->codec_data; +- int idx, err; ++static pj_status_t opus_codec_open(pjmedia_codec *codec, pjmedia_codec_param *attr) { ++ const pj_str_t STR_FMTP_USE_INBAND_FEC = { "useinbandfec", 12 }; ++ const pj_str_t STR_FMTP_MAX_AVERAGE_BITRATE = { "maxaveragebitrate", 17 }; ++ const pj_str_t STR_FMTP_MAX_CODED_AUDIO_BANDWIDTH = { "maxcodedaudiobandwidth", 22 }; ++ const pj_str_t STR_FMTP_USE_DTX = { "usedtx", 6 }; + +- PJ_ASSERT_RETURN(codec && attr && opus_data, PJ_EINVAL); ++ struct opus_private *opus; ++ int ret, tmpFmtpVal; ++ unsigned i, structSizeBytes, max_nsamples; + +- pj_mutex_lock (opus_data->mutex); ++ opus = (struct opus_private*) codec->codec_data; + +- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); ++ PJ_ASSERT_RETURN(opus && !opus->enc_ready && !opus->dec_ready, PJ_EINVAL); + +- opus_data->cfg.sample_rate = attr->info.clock_rate; +- opus_data->cfg.channel_cnt = attr->info.channel_cnt; +- opus_data->ptime = attr->info.frm_ptime; ++ PJ_LOG(4, (THIS_FILE, "Clock rate is %d ", attr->info.clock_rate)); ++ opus->externalFs = attr->info.clock_rate; + +- /* Allocate memory used by the codec */ +- if (!opus_data->enc) { +- /* Allocate memory for max 2 channels */ +- opus_data->enc = pj_pool_zalloc(opus_data->pool, +- opus_encoder_get_size(2)); +- } +- if (!opus_data->dec) { +- /* Allocate memory for max 2 channels */ +- opus_data->dec = pj_pool_zalloc(opus_data->pool, +- opus_decoder_get_size(2)); +- } +- if (!opus_data->enc_packer) { +- opus_data->enc_packer = pj_pool_zalloc(opus_data->pool, +- opus_repacketizer_get_size()); +- } +- if (!opus_data->dec_packer) { +- opus_data->dec_packer = pj_pool_zalloc(opus_data->pool, +- opus_repacketizer_get_size()); +- } +- if (!opus_data->enc || !opus_data->dec || +- !opus_data->enc_packer || !opus_data->dec_packer) +- { +- PJ_LOG(2, (THIS_FILE, "Unable to allocate memory for the codec")); +- pj_mutex_unlock (opus_data->mutex); +- return PJ_ENOMEM; +- } ++ /* Create Encoder */ ++ structSizeBytes = opus_encoder_get_size(attr->info.channel_cnt); ++ opus->psEnc = pj_pool_zalloc(opus->pool, structSizeBytes); ++ ret = opus_encoder_init(opus->psEnc, opus->externalFs, attr->info.channel_cnt, OPUS_APPLICATION_AUDIO); ++ if (ret) { ++ PJ_LOG(1, (THIS_FILE, "Unable to init encoder : %d", ret)); ++ return PJ_EINVAL; ++ } + +- /* Check max average bit rate */ +- idx = find_fmtp(&attr->setting.enc_fmtp, &STR_MAX_BIT_RATE, PJ_FALSE); +- if (idx >= 0) { +- unsigned rate; +- rate = (unsigned)pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); +- if (rate < attr->info.avg_bps) +- attr->info.avg_bps = rate; +- } ++ /* ++ * Set Encoder parameters ++ * TODO : have it configurable ++ */ ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_COMPLEXITY(10)); ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_INBAND_FEC(1)); /* on by default */ ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_PACKET_LOSS_PERC(5)); ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_SIGNAL(OPUS_AUTO)); + +- /* Check plc */ +- idx = find_fmtp(&attr->setting.enc_fmtp, &STR_INBAND_FEC, PJ_FALSE); +- if (idx >= 0) { +- unsigned plc; +- plc = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); +- attr->setting.plc = plc > 0? PJ_TRUE: PJ_FALSE; +- } ++ /* Apply fmtp params to Encoder */ ++ for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { ++ if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_USE_INBAND_FEC) == 0) { ++ tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_INBAND_FEC(tmpFmtpVal)); ++ break; ++ } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_MAX_AVERAGE_BITRATE) == 0) { ++ tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); ++ if (tmpFmtpVal >= 6000 && tmpFmtpVal <= 510000) { ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_BITRATE(tmpFmtpVal)); ++ } ++ } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_MAX_CODED_AUDIO_BANDWIDTH) == 0) { ++ tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); ++ if (tmpFmtpVal <= 8000) { ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); ++ } else if (tmpFmtpVal <= 12000) { ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND)); ++ } else if (tmpFmtpVal <= 16000) { ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); ++ } else if (tmpFmtpVal <= 24000) { ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); ++ } else if (tmpFmtpVal <= 48000) { ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); ++ } ++ } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_USE_DTX) == 0) { ++ tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); ++ opus_encoder_ctl(opus->psEnc, OPUS_SET_DTX(tmpFmtpVal)); ++ } ++ } + +- /* Check vad */ +- idx = find_fmtp(&attr->setting.enc_fmtp, &STR_DTX, PJ_FALSE); +- if (idx >= 0) { +- unsigned vad; +- vad = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); +- attr->setting.vad = vad > 0? PJ_TRUE: PJ_FALSE; +- } ++ opus->enc_ready = PJ_TRUE; + +- /* Check cbr */ +- idx = find_fmtp(&attr->setting.enc_fmtp, &STR_CBR, PJ_FALSE); +- if (idx >= 0) { +- unsigned cbr; +- cbr = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); +- opus_data->cfg.cbr = cbr > 0? PJ_TRUE: PJ_FALSE; +- } +- +- /* Check max average bit rate */ +- idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_BIT_RATE, PJ_FALSE); +- if (idx >= 0) { +- unsigned rate; +- rate = (unsigned) pj_strtoul(&attr->setting.dec_fmtp.param[idx].val); +- if (rate < attr->info.avg_bps) +- attr->info.avg_bps = rate; +- } ++ /* Decoder buffer */ ++ opus->pcm_bytes_per_sample = attr->info.pcm_bits_per_sample / 8; ++ max_nsamples = 120 * OPUS_CLOCK_RATE / 1000; /* 120ms is max frame time */ ++ opus->dec_buf_max_size = max_nsamples * opus->pcm_bytes_per_sample; ++ opus->dec_buf = pj_pool_alloc(opus->pool, opus->dec_buf_max_size); + +- TRACE_((THIS_FILE, "%s:%d: sample_rate: %u", +- __FUNCTION__, __LINE__, opus_data->cfg.sample_rate)); ++ /* Create decoder */ ++ structSizeBytes = opus_decoder_get_size(attr->info.channel_cnt); ++ opus->psDec = pj_pool_zalloc(opus->pool, structSizeBytes); ++ ret = opus_decoder_init(opus->psDec, opus->externalFs, attr->info.channel_cnt); ++ if (ret) { ++ PJ_LOG(1, (THIS_FILE, "Unable to init decoder : %d", ret)); ++ return PJ_EINVAL; ++ } + +- /* Initialize encoder */ +- err = opus_encoder_init(opus_data->enc, +- opus_data->cfg.sample_rate, +- attr->info.channel_cnt, +- OPUS_APPLICATION_VOIP); +- if (err != OPUS_OK) { +- PJ_LOG(2, (THIS_FILE, "Unable to create encoder")); +- return PJMEDIA_CODEC_EFAILED; +- } +- +- /* Set signal type */ +- opus_encoder_ctl(opus_data->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); +- /* Set bitrate */ +- opus_encoder_ctl(opus_data->enc, OPUS_SET_BITRATE(attr->info.avg_bps)); +- /* Set VAD */ +- opus_encoder_ctl(opus_data->enc, OPUS_SET_DTX(attr->setting.vad ? 1 : 0)); +- /* Set PLC */ +- opus_encoder_ctl(opus_data->enc, +- OPUS_SET_INBAND_FEC(attr->setting.plc ? 1 : 0)); +- /* Set bandwidth */ +- opus_encoder_ctl(opus_data->enc, +- OPUS_SET_MAX_BANDWIDTH(get_opus_bw_constant( +- opus_data->cfg.sample_rate))); +- /* Set expected packet loss */ +- opus_encoder_ctl(opus_data->enc, +- OPUS_SET_PACKET_LOSS_PERC(opus_data->cfg.packet_loss)); +- /* Set complexity */ +- opus_encoder_ctl(opus_data->enc, +- OPUS_SET_COMPLEXITY(opus_data->cfg.complexity)); +- /* Set constant bit rate */ +- opus_encoder_ctl(opus_data->enc, +- OPUS_SET_VBR(opus_data->cfg.cbr ? 0 : 1)); ++ opus->dec_ready = PJ_TRUE; + +- PJ_LOG(5, (THIS_FILE, "Initialize Opus encoder, sample rate: %d, " +- "avg bitrate: %d, vad: %d, plc: %d, pkt loss: %d, " +- "complexity: %d, constant bit rate: %d", +- opus_data->cfg.sample_rate, +- attr->info.avg_bps, attr->setting.vad?1:0, +- attr->setting.plc?1:0, +- opus_data->cfg.packet_loss, +- opus_data->cfg.complexity, +- opus_data->cfg.cbr?1:0)); +- +- /* Initialize decoder */ +- err = opus_decoder_init (opus_data->dec, +- opus_data->cfg.sample_rate, +- attr->info.channel_cnt); +- if (err != OPUS_OK) { +- PJ_LOG(2, (THIS_FILE, "Unable to initialize decoder")); +- return PJMEDIA_CODEC_EFAILED; +- } +- +- /* Initialize temporary decode frames used for FEC */ +- opus_data->dec_frame[0].type = PJMEDIA_FRAME_TYPE_NONE; +- opus_data->dec_frame[0].buf = pj_pool_zalloc(opus_data->pool, +- (opus_data->cfg.sample_rate / 1000) +- * 60 * attr->info.channel_cnt * 2 /* bytes per sample */); +- opus_data->dec_frame[1].type = PJMEDIA_FRAME_TYPE_NONE; +- opus_data->dec_frame[1].buf = pj_pool_zalloc(opus_data->pool, +- (opus_data->cfg.sample_rate / 1000) +- * 60 * attr->info.channel_cnt * 2 /* bytes per sample */); +- opus_data->dec_frame_index = -1; +- +- /* Initialize the repacketizers */ +- opus_repacketizer_init(opus_data->enc_packer); +- opus_repacketizer_init(opus_data->dec_packer); +- +- pj_mutex_unlock (opus_data->mutex); +- return PJ_SUCCESS; ++ return PJ_SUCCESS; + } + +- + /* + * Close codec. + */ +-static pj_status_t codec_close( pjmedia_codec *codec ) +-{ +- PJ_UNUSED_ARG(codec); +- return PJ_SUCCESS; ++static pj_status_t opus_codec_close(pjmedia_codec *codec) { ++ struct opus_private *opus; ++ opus = (struct opus_private*) codec->codec_data; ++ ++ opus->enc_ready = PJ_FALSE; ++ opus->dec_ready = PJ_FALSE; ++ ++ PJ_LOG(5, (THIS_FILE, "OPUS codec closed")); ++ return PJ_SUCCESS; + } + +- + /* + * Modify codec settings. + */ +-static pj_status_t codec_modify( pjmedia_codec *codec, +- const pjmedia_codec_param *attr ) +-{ +- struct opus_data *opus_data = (struct opus_data *)codec->codec_data; ++static pj_status_t opus_codec_modify(pjmedia_codec *codec, const pjmedia_codec_param *attr) { ++ PJ_TODO(implement_opus_codec_modify); + +- pj_mutex_lock (opus_data->mutex); ++ PJ_UNUSED_ARG(codec); ++ PJ_UNUSED_ARG(attr); + +- TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); +- +- /* Set bitrate */ +- opus_data->cfg.bit_rate = attr->info.avg_bps; +- opus_encoder_ctl(opus_data->enc, OPUS_SET_BITRATE(attr->info.avg_bps)); +- /* Set VAD */ +- opus_encoder_ctl(opus_data->enc, OPUS_SET_DTX(attr->setting.vad ? 1 : 0)); +- /* Set PLC */ +- opus_encoder_ctl(opus_data->enc, +- OPUS_SET_INBAND_FEC(attr->setting.plc ? 1 : 0)); +- +- pj_mutex_unlock (opus_data->mutex); + return PJ_SUCCESS; + } + +- + /* +- * Get frames in the packet. ++ * Encode frame. + */ +-static pj_status_t codec_parse( pjmedia_codec *codec, +- void *pkt, +- pj_size_t pkt_size, +- const pj_timestamp *ts, +- unsigned *frame_cnt, +- pjmedia_frame frames[] ) +-{ +- struct opus_data *opus_data = (struct opus_data *)codec->codec_data; +- unsigned char tmp_buf[MAX_ENCODED_PACKET_SIZE]; +- int i, num_frames; +- int size, out_pos; +- unsigned samples_per_frame; +-#if (USE_INCOMING_WORSE_SETTINGS) +- int bw; +-#endif ++static pj_status_t opus_codec_encode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { ++ struct opus_private *opus; ++ opus_int32 ret; ++ unsigned nsamples; + +- pj_mutex_lock (opus_data->mutex); ++ PJ_ASSERT_RETURN(codec && input && output, PJ_EINVAL); + +- if (pkt_size > sizeof(tmp_buf)) { +- PJ_LOG(5, (THIS_FILE, "Encoded size bigger than buffer")); +- pj_mutex_unlock (opus_data->mutex); +- return PJMEDIA_CODEC_EFRMTOOSHORT; +- } ++ opus = (struct opus_private*) codec->codec_data; + +- samples_per_frame = (opus_data->cfg.sample_rate * +- opus_data->ptime) / 1000; ++ /* Check frame in size */ ++ nsamples = input->size / opus->pcm_bytes_per_sample; ++ /* TODO: validate? */ + +- pj_memcpy(tmp_buf, pkt, pkt_size); ++ /* Encode */ ++ output->size = 0; + +- opus_repacketizer_init(opus_data->dec_packer); +- opus_repacketizer_cat(opus_data->dec_packer, tmp_buf, pkt_size); +- +- num_frames = opus_repacketizer_get_nb_frames(opus_data->dec_packer); +- out_pos = 0; +- for (i = 0; i < num_frames; ++i) { +- size = opus_repacketizer_out_range(opus_data->dec_packer, i, i+1, +- ((unsigned char*)pkt) + out_pos, +- sizeof(tmp_buf)); +- if (size < 0) { +- PJ_LOG(5, (THIS_FILE, "Parse failed! (%d)", pkt_size)); +- pj_mutex_unlock (opus_data->mutex); +- return PJMEDIA_CODEC_EFAILED; ++ ret = opus_encode(opus->psEnc, (opus_int16*) input->buf, nsamples, (unsigned char *) output->buf, output_buf_len); ++ if (ret < 0) { ++ PJ_LOG(1, (THIS_FILE, "Impossible to encode packet %d", ret)); ++ return opus_to_pjsip_error_code(ret); ++ } else { ++ output->size = (pj_size_t) ret; + } +- frames[i].type = PJMEDIA_FRAME_TYPE_AUDIO; +- frames[i].buf = ((char*)pkt) + out_pos; +- frames[i].size = size; +- frames[i].timestamp.u64 = ts->u64 + i * samples_per_frame; +- out_pos += size; +- } +- *frame_cnt = num_frames; +- +- pj_mutex_unlock (opus_data->mutex); +- return PJ_SUCCESS; ++ output->type = PJMEDIA_FRAME_TYPE_AUDIO; ++ output->timestamp = input->timestamp; ++#if _TRACE_OPUS ++ PJ_LOG(4, (THIS_FILE, "Encoder packet size %d for input %d ouput max len %d @ %d", output->size, input->size, output_buf_len, (unsigned) output->timestamp.u64)); ++#endif ++ return PJ_SUCCESS; + } + +- + /* +- * Encode frame. ++ * Get frames in the packet. + */ +-static pj_status_t codec_encode( pjmedia_codec *codec, +- const struct pjmedia_frame *input, +- unsigned output_buf_len, +- struct pjmedia_frame *output ) +-{ +- struct opus_data *opus_data = (struct opus_data *)codec->codec_data; +- opus_int32 size = 0; +- unsigned in_pos = 0; +- unsigned out_pos = 0; +- unsigned frame_size; +- unsigned samples_per_frame; +- unsigned char tmp_buf[MAX_ENCODED_PACKET_SIZE]; +- unsigned tmp_bytes_left = sizeof(tmp_buf); + +- pj_mutex_lock (opus_data->mutex); ++static pj_status_t opus_codec_parse(pjmedia_codec *codec, void *pkt, pj_size_t pkt_size, const pj_timestamp *ts, unsigned *frame_cnt, pjmedia_frame frames[]) { ++ struct opus_private *opus; ++ unsigned char toc; ++ const unsigned char *raw_frames[48]; ++ short size[48]; ++ int err, payload_offset, samples_per_frame; ++ unsigned i; + +- samples_per_frame = (opus_data->cfg.sample_rate * +- opus_data->ptime) / 1000; +- frame_size = samples_per_frame * opus_data->cfg.channel_cnt * +- sizeof(opus_int16); ++ PJ_ASSERT_RETURN(frame_cnt, PJ_EINVAL); + +- opus_repacketizer_init(opus_data->enc_packer); +- while (input->size - in_pos >= frame_size) { +- size = opus_encode(opus_data->enc, +- (const opus_int16*)(((char*)input->buf) + in_pos), +- samples_per_frame, +- tmp_buf + out_pos, +- (tmp_bytes_left < frame_size ? +- tmp_bytes_left : frame_size)); +- if (size < 0) { +- PJ_LOG(4, (THIS_FILE, "Encode failed! (%d)", size)); +- pj_mutex_unlock (opus_data->mutex); +- return PJMEDIA_CODEC_EFAILED; +- } else if (size > 0) { +- /* Only add packets containing more than the TOC */ +- opus_repacketizer_cat(opus_data->enc_packer, +- tmp_buf + out_pos, +- size); +- out_pos += size; +- tmp_bytes_left -= size; +- } +- in_pos += frame_size; +- } ++ opus = (struct opus_private*) codec->codec_data; + +- if (!opus_repacketizer_get_nb_frames(opus_data->enc_packer)) { +- /* Empty packet */ +- output->size = 0; +- output->type = PJMEDIA_FRAME_TYPE_NONE; +- output->timestamp = input->timestamp; +- } ++ err = opus_packet_parse(pkt, pkt_size, &toc, raw_frames, size, &payload_offset); ++ if (err <= 0) { ++ PJ_LOG(4, (THIS_FILE, "Error parsing Opus packet: %s", opus_strerror(err))); ++ *frame_cnt = 0; ++ return opus_to_pjsip_error_code(err); ++ } + +- if (size) { +- size = opus_repacketizer_out(opus_data->enc_packer, +- output->buf, +- output_buf_len); +- if (size < 0) { +- PJ_LOG(4, (THIS_FILE, "Encode failed! (%d), out_size: %u", +- size, output_buf_len)); +- pj_mutex_unlock (opus_data->mutex); +- return PJMEDIA_CODEC_EFAILED; +- } ++ *frame_cnt = (unsigned)err; ++ samples_per_frame = opus_packet_get_samples_per_frame(pkt, opus->externalFs); ++ ++#if _TRACE_OPUS ++ PJ_LOG(4, (THIS_FILE, "Pkt info : bw -> %d , spf -> %d", opus_packet_get_bandwidth(pkt), samples_per_frame)); ++#endif ++ ++ for (i = 0; i < *frame_cnt; i++) { ++ frames[i].type = PJMEDIA_FRAME_TYPE_AUDIO; ++ frames[i].bit_info = (((unsigned)ts->u64 & 0xFFFF) << 16) | (((unsigned)pkt & 0xFF) << 8) | i; ++ frames[i].buf = pkt; ++ frames[i].size = pkt_size; ++ frames[i].timestamp.u64 = ts->u64 + i * samples_per_frame; ++#if _TRACE_OPUS ++ PJ_LOG(4, (THIS_FILE, "parsed %d of %d",frames[i].size, *frame_cnt)); ++#endif + } + +- output->size = (unsigned)size; +- output->type = PJMEDIA_FRAME_TYPE_AUDIO; +- output->timestamp = input->timestamp; +- +- pj_mutex_unlock (opus_data->mutex); + return PJ_SUCCESS; + } + ++static pj_status_t opus_codec_decode(pjmedia_codec *codec, const struct pjmedia_frame *input, unsigned output_buf_len, struct pjmedia_frame *output) { ++ struct opus_private *opus; ++ unsigned pkt_info, frm_info, frm_size; + +-/* +- * Decode frame. +- */ +-static pj_status_t codec_decode( pjmedia_codec *codec, +- const struct pjmedia_frame *input, +- unsigned output_buf_len, +- struct pjmedia_frame *output ) +-{ +- struct opus_data *opus_data = (struct opus_data *)codec->codec_data; +- int decoded_samples; +- pjmedia_frame *inframe; +- int fec = 0; ++ PJ_ASSERT_RETURN(codec && input && output_buf_len && output, PJ_EINVAL); + +- pj_mutex_lock (opus_data->mutex); ++ opus = (struct opus_private*) codec->codec_data; + +- if (opus_data->dec_frame_index == -1) { +- /* First packet, buffer it. */ +- opus_data->dec_frame[0].type = input->type; +- opus_data->dec_frame[0].size = input->size; +- opus_data->dec_frame[0].timestamp = input->timestamp; +- pj_memcpy(opus_data->dec_frame[0].buf, input->buf, input->size); +- opus_data->dec_frame_index = 0; +- pj_mutex_unlock (opus_data->mutex); +- +- /* Return zero decoded bytes */ +- output->size = 0; +- output->type = PJMEDIA_FRAME_TYPE_NONE; +- output->timestamp = input->timestamp; +- +- return PJ_SUCCESS; ++ pkt_info = input->bit_info & 0xFFFFFF00; ++ frm_info = input->bit_info & 0xF; ++ if (opus->pkt_info != pkt_info || input->bit_info == 0) { ++ opus->pkt_info = pkt_info; ++ opus->dec_buf_sample_per_frame = opus_packet_get_samples_per_frame(input->buf, opus->externalFs); ++ /* We need to decode all the frames in the packet. */ ++ opus->dec_buf_size = opus_decode(opus->psDec, ++ (const unsigned char *) input->buf, ++ (opus_int32) input->size, ++ opus->dec_buf, ++ opus->dec_buf_max_size, ++ 0 /* decode FEC */); ++ if(opus->dec_buf_size <= 0){ ++ PJ_LOG(2, (THIS_FILE, "Failed to decode frame (err=%d)", opus->dec_buf_size)); ++ opus->dec_buf_size = 0; ++ } else { ++ opus->dec_buf_size = opus->dec_buf_size * opus->pcm_bytes_per_sample; ++ } + } + +- inframe = &opus_data->dec_frame[opus_data->dec_frame_index]; ++ /* We have this packet decoded now (either was previously in the buffer or was just added to buffer). */ ++ if (opus->dec_buf_size == 0) { ++ /* The decoding was a failure. */ ++ output->size = 0; ++ } else { ++ frm_size = opus->dec_buf_sample_per_frame * opus->pcm_bytes_per_sample; ++#if _TRACE_OPUS ++ PJ_LOG(4, (THIS_FILE, "Decode : copy from big buffer %d to %d", output_buf_len, frm_size)); ++#endif ++ if(output_buf_len < frm_size){ ++ return PJ_ETOOSMALL; ++ } ++ /* Copy the decoded frame from the buffer. */ ++ pj_memcpy(output->buf, ((opus_int16*)opus->dec_buf) + (frm_info * frm_size), frm_size); ++ output->size = frm_size; ++ } + +- if (inframe->type != PJMEDIA_FRAME_TYPE_AUDIO) { +- /* Update current frame index */ +- opus_data->dec_frame_index++; +- if (opus_data->dec_frame_index > 1) +- opus_data->dec_frame_index = 0; +- /* Copy original input buffer to current indexed frame */ +- inframe = &opus_data->dec_frame[opus_data->dec_frame_index]; +- inframe->type = input->type; +- inframe->size = input->size; +- inframe->timestamp = input->timestamp; +- pj_memcpy(inframe->buf, input->buf, input->size); +- fec = 1; +- } ++ if (output->size == 0) { ++ output->type = PJMEDIA_FRAME_TYPE_NONE; ++ output->buf = NULL; ++ return PJMEDIA_CODEC_EFAILED; ++ } + +- decoded_samples = opus_decode(opus_data->dec, +- inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ? +- inframe->buf : NULL, +- inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ? +- inframe->size : 0, +- (opus_int16*)output->buf, +- output->size / (sizeof(opus_int16) * +- opus_data->cfg.channel_cnt), +- fec); +- output->timestamp = inframe->timestamp; +- +- if (inframe->type == PJMEDIA_FRAME_TYPE_AUDIO) { +- /* Mark current indexed frame as invalid */ +- inframe->type = PJMEDIA_FRAME_TYPE_NONE; +- /* Update current frame index */ +- opus_data->dec_frame_index++; +- if (opus_data->dec_frame_index > 1) +- opus_data->dec_frame_index = 0; +- /* Copy original input buffer to current indexed frame */ +- inframe = &opus_data->dec_frame[opus_data->dec_frame_index]; +- inframe->type = input->type; +- inframe->size = input->size; +- inframe->timestamp = input->timestamp; +- pj_memcpy(inframe->buf, input->buf, input->size); +- } ++ output->type = PJMEDIA_FRAME_TYPE_AUDIO; ++ output->timestamp = input->timestamp; + +- if (decoded_samples < 0) { +- PJ_LOG(4, (THIS_FILE, "Decode failed!")); +- pj_mutex_unlock (opus_data->mutex); +- return PJMEDIA_CODEC_EFAILED; +- } +- +- output->size = decoded_samples * sizeof(opus_int16) * +- opus_data->cfg.channel_cnt; +- output->type = PJMEDIA_FRAME_TYPE_AUDIO; +- +- pj_mutex_unlock (opus_data->mutex); +- return PJ_SUCCESS; ++#if _TRACE_OPUS ++ PJ_LOG(4, (THIS_FILE, "Decoded %d to %d with max %d", input->size, output->size, output_buf_len)); ++#endif ++ return PJ_SUCCESS; + } + +- + /* + * Recover lost frame. + */ +-static pj_status_t codec_recover( pjmedia_codec *codec, +- unsigned output_buf_len, +- struct pjmedia_frame *output ) +-{ +- struct opus_data *opus_data = (struct opus_data *)codec->codec_data; +- int decoded_samples; +- pjmedia_frame *inframe; ++static pj_status_t opus_codec_recover(pjmedia_codec *codec, unsigned output_buf_len, struct pjmedia_frame *output) { ++ struct opus_private *opus; ++ int ret = 0; ++ int frame_size; + +- pj_mutex_lock (opus_data->mutex); ++ PJ_ASSERT_RETURN(output, PJ_EINVAL); ++ opus = (struct opus_private*) codec->codec_data; + +- if (opus_data->dec_frame_index == -1) { +- /* Recover the first packet? Don't think so, fill it with zeroes. */ +- pj_uint16_t samples_per_frame; +- samples_per_frame = (opus_data->cfg.sample_rate * +- opus_data->ptime) / 1000; +- output->type = PJMEDIA_FRAME_TYPE_AUDIO; +- output->size = samples_per_frame << 1; +- pjmedia_zero_samples((pj_int16_t*)output->buf, samples_per_frame); +- pj_mutex_unlock (opus_data->mutex); ++ frame_size = output_buf_len / opus->pcm_bytes_per_sample; ++ /* Decode */ ++ ret = opus_decode(opus->psDec, (const unsigned char *) NULL, 0, output->buf, frame_size, 0); ++ if (ret < 0) { ++ PJ_LOG(1, (THIS_FILE, "Failed to recover opus frame %d", ret)); ++ return PJ_EINVAL; ++ } else if (ret == 0) { ++#if _TRACE_OPUS ++ PJ_LOG(4, (THIS_FILE, "Empty frame recovered %d", ret)); ++#endif ++ output->type = PJMEDIA_FRAME_TYPE_NONE; ++ output->buf = NULL; ++ output->size = 0; ++ } else { ++#if _TRACE_OPUS ++ PJ_LOG(4, (THIS_FILE, "Frame recovered %d", ret)); ++#endif ++ output->size = ret * opus->pcm_bytes_per_sample; ++ output->type = PJMEDIA_FRAME_TYPE_AUDIO; ++ } + +- return PJ_SUCCESS; +- } +- +- inframe = &opus_data->dec_frame[opus_data->dec_frame_index]; +- decoded_samples = opus_decode(opus_data->dec, +- inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ? +- inframe->buf : NULL, +- inframe->type==PJMEDIA_FRAME_TYPE_AUDIO ? +- inframe->size : 0, +- (opus_int16*)output->buf, +- output->size / (sizeof(opus_int16) * +- opus_data->cfg.channel_cnt), +- 0); +- +- /* Mark current indexed frame as invalid */ +- inframe->type = PJMEDIA_FRAME_TYPE_NONE; +- +- /* Update current frame index */ +- opus_data->dec_frame_index++; +- if (opus_data->dec_frame_index > 1) +- opus_data->dec_frame_index = 0; +- /* Mark current indexed frame as invalid */ +- inframe = &opus_data->dec_frame[opus_data->dec_frame_index]; +- inframe->type = PJMEDIA_FRAME_TYPE_NONE; +- +- if (decoded_samples < 0) { +- PJ_LOG(4, (THIS_FILE, "Recover failed!")); +- pj_mutex_unlock (opus_data->mutex); +- return PJMEDIA_CODEC_EFAILED; +- } +- +- output->size = decoded_samples * sizeof(opus_int16) * +- opus_data->cfg.channel_cnt; +- output->type = PJMEDIA_FRAME_TYPE_AUDIO; +- output->timestamp = inframe->timestamp; +- +- pj_mutex_unlock (opus_data->mutex); +- return PJ_SUCCESS; ++ return PJ_SUCCESS; + } + +-#if defined(_MSC_VER) +-# pragma comment(lib, "libopus.a") + #endif +- +- +-#endif /* PJMEDIA_HAS_OPUS_CODEC */ +Index: pjmedia/src/pjmedia-videodev/dshow_dev.c +=================================================================== +--- pjmedia/src/pjmedia-videodev/dshow_dev.c (revision 5249) ++++ pjmedia/src/pjmedia-videodev/dshow_dev.c (working copy) +@@ -20,30 +20,24 @@ + #include + #include + #include +-#include + + + #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \ + defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0 + +- +-#ifdef _MSC_VER +-# pragma warning(push, 3) +-#endif +- + #include + #define COBJMACROS + #include + #include ++#include + +-#ifdef _MSC_VER +-# pragma warning(pop) ++#ifndef DIBSIZE ++# define WIDTHBYTES(BTIS) ((DWORD)(((BTIS)+31) & (~31)) / 8) ++# define DIBWIDTHBYTES(BI) (DWORD)(BI).biBitCount) * (DWORD)WIDTHBYTES((DWORD)(BI).biWidth ++# define _DIBSIZE(BI) (DIBWIDTHBYTES(BI) * (DWORD)(BI).biHeight) ++# define DIBSIZE(BI) ((BI).biHeight < 0 ? (-1)*(_DIBSIZE(BI)) : _DIBSIZE(BI)) + #endif + +-#pragma comment(lib, "Strmiids.lib") +-#pragma comment(lib, "Rpcrt4.lib") +-#pragma comment(lib, "Quartz.lib") +- + #define THIS_FILE "dshow_dev.c" + #define DEFAULT_CLOCK_RATE 90000 + #define DEFAULT_WIDTH 640 +@@ -57,10 +51,6 @@ + typedef struct NullRenderer NullRenderer; + IBaseFilter* NullRenderer_Create(input_callback input_cb, + void *user_data); +-typedef struct SourceFilter SourceFilter; +-IBaseFilter* SourceFilter_Create(SourceFilter **pSrc); +-HRESULT SourceFilter_Deliver(SourceFilter *src, void *buf, long size); +-void SourceFilter_SetMediaType(SourceFilter *src, AM_MEDIA_TYPE *pmt); + + typedef struct dshow_fmt_info + { +@@ -121,7 +111,6 @@ + { + IFilterGraph *filter_graph; + IMediaFilter *media_filter; +- SourceFilter *csource_filter; + IBaseFilter *source_filter; + IBaseFilter *rend_filter; + AM_MEDIA_TYPE *mediatype; +@@ -160,8 +149,6 @@ + pjmedia_vid_dev_cap cap, + const void *value); + static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm); +-static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm, +- const pjmedia_frame *frame); + static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm); + static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm); + +@@ -184,7 +171,7 @@ + &dshow_stream_set_cap, + &dshow_stream_start, + NULL, +- &dshow_stream_put_frame, ++ NULL, + &dshow_stream_stop, + &dshow_stream_destroy + }; +@@ -213,19 +200,12 @@ + /* API: init factory */ + static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f) + { +- HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); +- if (hr == RPC_E_CHANGED_MODE) { +- /* When using apartment mode, Dshow object would not be accessible from +- * other thread. Take this into consideration when implementing native +- * renderer using Dshow. +- */ +- hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); +- if (FAILED(hr)) { +- PJ_LOG(4,(THIS_FILE, "Failed initializing DShow: " +- "COM library already initialized with " +- "incompatible concurrency model")); +- return PJMEDIA_EVID_INIT; +- } ++ HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); ++ if (hr == RPC_E_CHANGED_MODE) { ++ PJ_LOG(4,(THIS_FILE, "Failed initializing DShow: " ++ "COM library already initialized with " ++ "incompatible concurrency model")); ++ return PJMEDIA_EVID_INIT; + } + + return dshow_factory_refresh(f); +@@ -459,14 +439,22 @@ + if (SUCCEEDED(hr) && var_name.bstrVal) { + WCHAR *wszDisplayName = NULL; + IBaseFilter *filter; ++ pj_ssize_t len; + + ddi = &df->dev_info[df->dev_count++]; + pj_bzero(ddi, sizeof(*ddi)); +- pj_unicode_to_ansi(var_name.bstrVal, +- wcslen(var_name.bstrVal), +- ddi->info.name, +- sizeof(ddi->info.name)); + ++ len = wcslen(var_name.bstrVal), ++ len = WideCharToMultiByte(CP_ACP, ++ 0, ++ var_name.bstrVal, ++ (int)len, ++ ddi->info.name, ++ sizeof(ddi->info.name), ++ NULL, ++ NULL); ++ ddi->info.name[len] = '\0'; ++ + hr = IMoniker_GetDisplayName(moniker, NULL, NULL, + &wszDisplayName); + if (hr == S_OK && wszDisplayName) { +@@ -502,26 +490,6 @@ + ICreateDevEnum_Release(dev_enum); + } + +-#if HAS_VMR +- ddi = &df->dev_info[df->dev_count++]; +- pj_bzero(ddi, sizeof(*ddi)); +- pj_ansi_strncpy(ddi->info.name, "Video Mixing Renderer", +- sizeof(ddi->info.name)); +- ddi->info.name[sizeof(ddi->info.name)-1] = '\0'; +- pj_ansi_strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver)); +- ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0'; +- ddi->info.dir = PJMEDIA_DIR_RENDER; +- ddi->info.has_callback = PJ_FALSE; +- ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT; +-// TODO: +-// ddi->info.caps |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW; +- +- ddi->info.fmt_cnt = 1; +- pjmedia_format_init_video(&ddi->info.fmt[0], dshow_fmts[0].pjmedia_format, +- DEFAULT_WIDTH, DEFAULT_HEIGHT, +- DEFAULT_FPS, 1); +-#endif +- + PJ_LOG(4, (THIS_FILE, "DShow has %d devices:", + df->dev_count)); + for (c = 0; c < df->dev_count; ++c) { +@@ -574,10 +542,6 @@ + param->dir = PJMEDIA_DIR_CAPTURE; + param->cap_id = index; + param->rend_id = PJMEDIA_VID_INVALID_DEV; +- } else if (di->info.dir & PJMEDIA_DIR_RENDER) { +- param->dir = PJMEDIA_DIR_RENDER; +- param->rend_id = index; +- param->cap_id = PJMEDIA_VID_INVALID_DEV; + } else { + return PJMEDIA_EVID_INVDEV; + } +@@ -645,26 +609,6 @@ + (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame); + } + +-/* API: Put frame from stream */ +-static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm, +- const pjmedia_frame *frame) +-{ +- struct dshow_stream *stream = (struct dshow_stream*)strm; +- HRESULT hr; +- +- if (stream->quit_flag) { +- stream->rend_thread_exited = PJ_TRUE; +- return PJ_SUCCESS; +- } +- +- hr = SourceFilter_Deliver(stream->dgraph.csource_filter, +- frame->buf, (long)frame->size); +- if (FAILED(hr)) +- return hr; +- +- return PJ_SUCCESS; +-} +- + static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id) + { + unsigned i; +@@ -689,16 +633,24 @@ + IEnumPins *pEnum; + IPin *srcpin = NULL; + IPin *sinkpin = NULL; +- AM_MEDIA_TYPE *mediatype= NULL, mtype; ++ AM_MEDIA_TYPE *mediatype = NULL; + VIDEOINFOHEADER *video_info, *vi = NULL; + pjmedia_video_format_detail *vfd; + const pjmedia_video_format_info *vfi; + ++ PJ_ASSERT_RETURN(dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); ++ + vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(), + strm->param.fmt.id); + if (!vfi) + return PJMEDIA_EVID_BADFORMAT; + ++ hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); ++ if (FAILED(hr)) { ++ PJ_LOG(4,(THIS_FILE, "Error: CoInitializeEx")); ++ goto on_error; ++ } ++ + hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC, + &IID_IFilterGraph, (LPVOID *)&graph->filter_graph); + if (FAILED(hr)) { +@@ -711,14 +663,10 @@ + goto on_error; + } + +- if (dir == PJMEDIA_DIR_CAPTURE) { + hr = get_cap_device(df, id, &graph->source_filter); + if (FAILED(hr)) { + goto on_error; + } +- } else { +- graph->source_filter = SourceFilter_Create(&graph->csource_filter); +- } + + hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter, + L"capture"); +@@ -726,16 +674,7 @@ + goto on_error; + } + +- if (dir == PJMEDIA_DIR_CAPTURE) { +- graph->rend_filter = NullRenderer_Create(input_cb, strm); +- } else { +- hr = CoCreateInstance(&CLSID_VideoMixingRenderer, NULL, +- CLSCTX_INPROC, &IID_IBaseFilter, +- (LPVOID *)&graph->rend_filter); +- if (FAILED (hr)) { +- goto on_error; +- } +- } ++ graph->rend_filter = NullRenderer_Create(input_cb, strm); + + IBaseFilter_EnumPins(graph->rend_filter, &pEnum); + if (SUCCEEDED(hr)) { +@@ -763,34 +702,6 @@ + (use_def_size? 0: vfd->size.h), &srcpin, NULL); + graph->mediatype = mediatype; + +- if (srcpin && dir == PJMEDIA_DIR_RENDER) { +- mediatype = graph->mediatype = &mtype; +- +- memset (mediatype, 0, sizeof(AM_MEDIA_TYPE)); +- mediatype->majortype = MEDIATYPE_Video; +- mediatype->subtype = *(get_dshow_format_info(strm->param.fmt.id)-> +- dshow_format); +- mediatype->bFixedSizeSamples = TRUE; +- mediatype->bTemporalCompression = FALSE; +- +- vi = (VIDEOINFOHEADER *) +- CoTaskMemAlloc(sizeof(VIDEOINFOHEADER)); +- memset (vi, 0, sizeof(VIDEOINFOHEADER)); +- mediatype->formattype = FORMAT_VideoInfo; +- mediatype->cbFormat = sizeof(VIDEOINFOHEADER); +- mediatype->pbFormat = (BYTE *)vi; +- +- vi->rcSource.bottom = vfd->size.h; +- vi->rcSource.right = vfd->size.w; +- vi->rcTarget.bottom = vfd->size.h; +- vi->rcTarget.right = vfd->size.w; +- +- vi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); +- vi->bmiHeader.biPlanes = 1; +- vi->bmiHeader.biBitCount = vfi->bpp; +- vi->bmiHeader.biCompression = strm->param.fmt.id; +- } +- + if (!srcpin || !sinkpin || !mediatype) { + hr = VFW_E_TYPE_NOT_ACCEPTED; + goto on_error; +@@ -809,9 +720,6 @@ + } + video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader); + mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader); +- if (graph->csource_filter) +- SourceFilter_SetMediaType(graph->csource_filter, +- mediatype); + + hr = IFilterGraph_AddFilter(graph->filter_graph, + (IBaseFilter *)graph->rend_filter, +@@ -893,9 +801,9 @@ + pj_pool_t *pool; + struct dshow_stream *strm; + pj_status_t status; ++ const pjmedia_video_format_detail *vfd; + +- PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE || +- param->dir == PJMEDIA_DIR_RENDER, PJ_EINVAL); ++ PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL); + + if (!get_dshow_format_info(param->fmt.id)) + return PJMEDIA_EVID_BADFORMAT; +@@ -910,9 +818,6 @@ + pj_memcpy(&strm->vid_cb, cb, sizeof(*cb)); + strm->user_data = user_data; + +- if (param->dir & PJMEDIA_DIR_CAPTURE) { +- const pjmedia_video_format_detail *vfd; +- + /* Create capture stream here */ + status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id, + PJ_FALSE, PJ_FALSE, df, strm, +@@ -944,22 +849,7 @@ + + vfd = pjmedia_format_get_video_format_detail(¶m->fmt, PJ_TRUE); + strm->cap_ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1); +- } else if (param->dir & PJMEDIA_DIR_RENDER) { +- /* Create render stream here */ +- status = create_filter_graph(PJMEDIA_DIR_RENDER, param->rend_id, +- PJ_FALSE, PJ_FALSE, df, strm, +- &strm->dgraph); +- if (status != PJ_SUCCESS) +- goto on_error; +- } + +- /* Apply the remaining settings */ +- if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) { +- dshow_stream_set_cap(&strm->base, +- PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW, +- ¶m->window); +- } +- + /* Done */ + strm->base.op = &stream_op; + *p_vid_strm = &strm->base; +Index: pjmedia/src/pjmedia-videodev/videodev.c +=================================================================== +--- pjmedia/src/pjmedia-videodev/videodev.c (revision 5249) ++++ pjmedia/src/pjmedia-videodev/videodev.c (working copy) +@@ -77,20 +77,12 @@ + pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf); + #endif + +-#if PJMEDIA_VIDEO_DEV_HAS_SDL +-pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf); +-#endif +- +-#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG +-pjmedia_vid_dev_factory* pjmedia_ffmpeg_factory(pj_pool_factory *pf); +-#endif +- + #if PJMEDIA_VIDEO_DEV_HAS_V4L2 + pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf); + #endif + +-#if PJMEDIA_VIDEO_DEV_HAS_QT +-pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf); ++#if PJMEDIA_VIDEO_DEV_HAS_AVF ++pjmedia_vid_dev_factory* pjmedia_avf_factory(pj_pool_factory *pf); + #endif + + #if PJMEDIA_VIDEO_DEV_HAS_IOS +@@ -101,6 +93,14 @@ + pjmedia_vid_dev_factory* pjmedia_opengl_factory(pj_pool_factory *pf); + #endif + ++#if PJMEDIA_VIDEO_DEV_HAS_FB ++pjmedia_vid_dev_factory* pjmedia_fb_factory(pj_pool_factory *pf); ++#endif ++ ++#if PJMEDIA_VIDEO_DEV_HAS_NULL ++pjmedia_vid_dev_factory* pjmedia_null_factory(pj_pool_factory *pf); ++#endif ++ + #if PJMEDIA_VIDEO_DEV_HAS_ANDROID + pjmedia_vid_dev_factory* pjmedia_and_factory(pj_pool_factory *pf); + #endif +@@ -382,8 +382,8 @@ + #if PJMEDIA_VIDEO_DEV_HAS_V4L2 + vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_v4l2_factory; + #endif +-#if PJMEDIA_VIDEO_DEV_HAS_QT +- vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_qt_factory; ++#if PJMEDIA_VIDEO_DEV_HAS_AVF ++ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_avf_factory; + #endif + #if PJMEDIA_VIDEO_DEV_HAS_OPENGL + vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_opengl_factory; +@@ -394,21 +394,15 @@ + #if PJMEDIA_VIDEO_DEV_HAS_DSHOW + vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_dshow_factory; + #endif +-#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG +- vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ffmpeg_factory; ++#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC ++ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_cbar_factory; + #endif +-#if PJMEDIA_VIDEO_DEV_HAS_SDL +- vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_sdl_factory; ++#if PJMEDIA_VIDEO_DEV_HAS_FB ++ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_fb_factory; + #endif +-#if PJMEDIA_VIDEO_DEV_HAS_ANDROID +- vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_and_factory; ++#if PJMEDIA_VIDEO_DEV_HAS_NULL ++ vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_null_factory; + #endif +-#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC +- /* Better put colorbar at the last, so the default capturer will be +- * a real capturer, if any. +- */ +- vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_cbar_factory; +-#endif + + /* Initialize each factory and build the device ID list */ + for (i=0; i