summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorLiong Sauw Ming <ming@teluu.com>2011-10-24 09:28:13 +0000
committerLiong Sauw Ming <ming@teluu.com>2011-10-24 09:28:13 +0000
commit2068f13bc42cf3a47374aa2765f82724a5782028 (patch)
tree29fbeaa152ab51e59b650c0d7cd83a38111e1ecc /pjmedia
parent1c72a42676e7aa0c2ae0734549050f738f3bdf02 (diff)
Re #1395: Backport of PJSIP 1.x branch into PJSIP 2.0 trunk
* Backport of r3557:r3832 TODO: ticket #1268 (Option for automatic/manual sending of RTCP SDES/BYE for the stream) for video stream. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3841 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/build/Jbtest.dat39
-rw-r--r--pjmedia/build/os-auto.mak.in7
-rw-r--r--pjmedia/build/pjmedia_codec.vcproj8
-rw-r--r--pjmedia/include/pjmedia-audiodev/config.h15
-rw-r--r--pjmedia/include/pjmedia-codec.h1
-rw-r--r--pjmedia/include/pjmedia-codec/amr_helper.h17
-rw-r--r--pjmedia/include/pjmedia-codec/config.h33
-rw-r--r--pjmedia/include/pjmedia-codec/config_auto.h.in5
-rw-r--r--pjmedia/include/pjmedia-codec/opencore_amrnb.h89
-rw-r--r--pjmedia/include/pjmedia/config.h71
-rw-r--r--pjmedia/include/pjmedia/delaybuf.h20
-rw-r--r--pjmedia/include/pjmedia/echo.h10
-rw-r--r--pjmedia/include/pjmedia/jbuf.h52
-rw-r--r--pjmedia/include/pjmedia/session.h20
-rw-r--r--pjmedia/include/pjmedia/stream.h23
-rw-r--r--pjmedia/include/pjmedia/transport_adapter_sample.h9
-rw-r--r--pjmedia/src/pjmedia-audiodev/audiodev.c10
-rw-r--r--pjmedia/src/pjmedia-audiodev/coreaudio_dev.c12
-rw-r--r--pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp4
-rw-r--r--pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp84
-rw-r--r--pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp25
-rw-r--r--pjmedia/src/pjmedia-codec/audio_codecs.c7
-rw-r--r--pjmedia/src/pjmedia-codec/ipp_codecs.c13
-rw-r--r--pjmedia/src/pjmedia-codec/opencore_amrnb.c820
-rw-r--r--pjmedia/src/pjmedia-codec/passthrough.c2
-rw-r--r--pjmedia/src/pjmedia/alaw_ulaw.c6
-rw-r--r--pjmedia/src/pjmedia/delaybuf.c97
-rw-r--r--pjmedia/src/pjmedia/echo_common.c5
-rw-r--r--pjmedia/src/pjmedia/jbuf.c369
-rw-r--r--pjmedia/src/pjmedia/sdp_neg.c5
-rw-r--r--pjmedia/src/pjmedia/session.c34
-rw-r--r--pjmedia/src/pjmedia/stream.c96
-rw-r--r--pjmedia/src/pjmedia/transport_adapter_sample.c7
-rw-r--r--pjmedia/src/pjmedia/transport_ice.c66
-rw-r--r--pjmedia/src/pjmedia/transport_udp.c53
-rw-r--r--pjmedia/src/test/jbuf_test.c11
-rw-r--r--pjmedia/src/test/mips_test.c40
-rw-r--r--pjmedia/src/test/test.c11
38 files changed, 1869 insertions, 327 deletions
diff --git a/pjmedia/build/Jbtest.dat b/pjmedia/build/Jbtest.dat
index b2f4999d..4f5a67fd 100644
--- a/pjmedia/build/Jbtest.dat
+++ b/pjmedia/build/Jbtest.dat
@@ -19,11 +19,11 @@
#
# 3. Success conditions, started with '!', followed by condition name
# and its maximum tolerable value, in frames unit. Recognized condition
-# names are: burst, discard, lost, empty, delay. These conditions will
-# be verified with jitter buffer statistics after all session test data
-# are executed.
+# names are: burst, discard, lost, empty, delay, delay_min. These
+# conditions will be verified with jitter buffer statistics after all
+# session test data are executed.
# Example:
-# !delay 10 <- maximum average delay of jbuf is 10 frames
+# !delay 10 <- average delay of jbuf is 10 frames
#
# 4. Session test data, containing sequence of jitter buffer events,
# an event is represented by a character as follow:
@@ -295,7 +295,7 @@ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
!discard 50 <- frames discarded for delay adaptation
!lost 50 <- ticket #1188, normal frame after discarded frame is flagged 'lost' to align signal
!empty 0
-!delay 25 <- average delay, JB is able to adapt the delay
+!delay_min 2 <- minimum delay, JB is able to adapt the delay
PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
@@ -311,13 +311,30 @@ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
.
= Fixed mode prefetch 5, with two empty events
%fixed 5
!burst 1
-!discard 0
-!lost 0
+!discard 4 <- the burst level is about 1, but prefetching will cause delay by 5 frames prefetching, delay adjustment may take place later on
+!lost 4 <- progressive discard drops frames as if they were lost
!empty 10
!delay 5
G
@@ -329,8 +346,8 @@ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
= Fixed mode prefetch 5, with random burst
%fixed 5
!burst 3
-!discard 0
-!lost 0
+!discard 4 <- (see above test scenario)
+!lost 4 <- (see above test scenario)
!empty 5
!delay 5
PGPGPPGGPPPPGGPGGGPG PGGGGPPPGPPGPPPGGPGG PGPGPPGGPPPPGGPGGGPG
@@ -349,7 +366,7 @@ PGGGGPPPGPPGPPPGGPGG PGPGPPGGPPGGPPPGGGPG PGGGGPPPGPPGPPPGGPGG
!discard 50 <- frames discarded for delay adaptation
!lost 50 <- ticket #1188, normal frame after discarded frame is flagged 'lost' to align signal
!empty 0
-!delay 20 <- average delay, twice of minimal prefetch
+!delay_min 20 <- minimum delay, twice of minimal prefetch
PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
@@ -369,7 +386,7 @@ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
= Large PUT burst at beginning, then normal with burst level 10 and periodic burst spikes
%adaptive 0 0 40
-!burst 10
+!burst 12
!discard 300 <- not so relevant for long period session with many delay adjustments needed (i.e: for first burst and periodic spikes)
!lost 300 <- ticket #1188, normal frame after discarded frame is flagged 'lost' to align signal
!empty 60 <- delay adjustment effect, as there is actually no drift
diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in
index 600b0345..98ecf288 100644
--- a/pjmedia/build/os-auto.mak.in
+++ b/pjmedia/build/os-auto.mak.in
@@ -57,6 +57,7 @@ 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@
export CODEC_OBJS=
@@ -109,6 +110,12 @@ export CODEC_OBJS += g7221.o
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_amrnb.o
+endif
+
#
# PortAudio
diff --git a/pjmedia/build/pjmedia_codec.vcproj b/pjmedia/build/pjmedia_codec.vcproj
index 6f09b2ae..516c011e 100644
--- a/pjmedia/build/pjmedia_codec.vcproj
+++ b/pjmedia/build/pjmedia_codec.vcproj
@@ -2967,6 +2967,10 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia-codec\opencore_amrnb.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia-codec\passthrough.c"
>
</File>
@@ -3102,6 +3106,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia-codec\opencore_amrnb.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia-codec\passthrough.h"
>
</File>
diff --git a/pjmedia/include/pjmedia-audiodev/config.h b/pjmedia/include/pjmedia-audiodev/config.h
index 2ef9e9f6..d0df6756 100644
--- a/pjmedia/include/pjmedia-audiodev/config.h
+++ b/pjmedia/include/pjmedia-audiodev/config.h
@@ -145,6 +145,21 @@ PJ_BEGIN_DECL
/**
+ * 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).
diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h
index 7e77b1d7..9eba4c11 100644
--- a/pjmedia/include/pjmedia-codec.h
+++ b/pjmedia/include/pjmedia-codec.h
@@ -35,6 +35,7 @@
#include <pjmedia-codec/g7221.h>
#include <pjmedia-codec/ipp_codecs.h>
#include <pjmedia-codec/passthrough.h>
+#include <pjmedia-codec/opencore_amrnb.h>
#endif /* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */
diff --git a/pjmedia/include/pjmedia-codec/amr_helper.h b/pjmedia/include/pjmedia-codec/amr_helper.h
index 6bca5ac1..fc63eb47 100644
--- a/pjmedia/include/pjmedia-codec/amr_helper.h
+++ b/pjmedia/include/pjmedia-codec/amr_helper.h
@@ -567,7 +567,7 @@ const pj_int16_t* const pjmedia_codec_amrwb_ordermaps[9] =
* Constant of AMR-NB frame lengths in bytes.
*/
const pj_uint8_t pjmedia_codec_amrnb_framelen[16] =
- {12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 5};
+ {12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
/**
* Constant of AMR-NB frame lengths in bits.
*/
@@ -583,7 +583,7 @@ const pj_uint16_t pjmedia_codec_amrnb_bitrates[8] =
* Constant of AMR-WB frame lengths in bytes.
*/
const pj_uint8_t pjmedia_codec_amrwb_framelen[16] =
- {17, 23, 32, 37, 40, 46, 50, 58, 60, 5, 0, 0, 0, 0, 0, 5};
+ {17, 23, 32, 37, 40, 46, 50, 58, 60, 5, 0, 0, 0, 0, 0, 0};
/**
* Constant of AMR-WB frame lengths in bits.
*/
@@ -606,6 +606,7 @@ typedef struct pjmedia_codec_amr_bit_info {
pj_int8_t mode; /**< AMR mode. */
pj_uint8_t start_bit; /**< Frame start bit. */
pj_uint8_t good_quality:1; /**< Flag if frame is good/degraded. */
+ pj_uint8_t STI:1; /**< STI mode (first/update). */
} pjmedia_codec_amr_bit_info;
#pragma pack()
@@ -1020,9 +1021,7 @@ PJ_INLINE (pj_status_t) pjmedia_codec_amr_pack(
} else if (info->frame_type == SID_FT) {
/* SID */
- pj_uint8_t STI = 0;
-
- amr_bits[35] = (pj_uint8_t)(STI & 1);
+ amr_bits[35] |= info->STI;
if (setting->amr_nb) {
amr_bits[36] = (pj_uint8_t)((info->mode >> 2) & 1);
@@ -1163,6 +1162,7 @@ PJ_INLINE(pj_status_t) pjmedia_codec_amr_parse(
info->mode = (pj_int8_t)((FT < SID_FT)? FT : -1);
info->good_quality = (pj_uint8_t)(Q == 1);
info->start_bit = 0;
+ info->STI = 0;
frames[cnt].timestamp = ts_;
frames[cnt].type = PJMEDIA_FRAME_TYPE_AUDIO;
@@ -1186,6 +1186,13 @@ PJ_INLINE(pj_status_t) pjmedia_codec_amr_parse(
frames[cnt].buf = r;
info->start_bit = r_bitptr;
+ if (FT == SID_FT) {
+ unsigned sti_bitptr;
+ sti_bitptr = r_bitptr + 35;
+ info->STI = (pj_uint8_t)
+ (r[sti_bitptr >> 3] >> (7 - (sti_bitptr % 8))) & 1;
+ }
+
if (setting->octet_aligned) {
r += framelen_tbl[FT];
frames[cnt].size = framelen_tbl[FT];
diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h
index 6b55baa2..0f94a98c 100644
--- a/pjmedia/include/pjmedia-codec/config.h
+++ b/pjmedia/include/pjmedia-codec/config.h
@@ -313,6 +313,39 @@
#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
+
+/**
+ * Link with libopencore-amrXX via pragma comment on Visual Studio.
+ * This option only makes sense if PJMEDIA_HAS_OPENCORE_AMRNB_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_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
diff --git a/pjmedia/include/pjmedia-codec/config_auto.h.in b/pjmedia/include/pjmedia-codec/config_auto.h.in
index 9c2a9af2..c469d795 100644
--- a/pjmedia/include/pjmedia-codec/config_auto.h.in
+++ b/pjmedia/include/pjmedia-codec/config_auto.h.in
@@ -69,6 +69,11 @@
#undef PJMEDIA_HAS_G7221_CODEC
#endif
+/* OpenCORE AMR-NB codec */
+#ifndef PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+#undef PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+#endif
+
#endif /* __PJMEDIA_CODEC_CONFIG_AUTO_H_ */
diff --git a/pjmedia/include/pjmedia-codec/opencore_amrnb.h b/pjmedia/include/pjmedia-codec/opencore_amrnb.h
new file mode 100644
index 00000000..3e618e46
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/opencore_amrnb.h
@@ -0,0 +1,89 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se>
+ *
+ * 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
+ */
+#ifndef __PJMEDIA_CODEC_OPENCORE_AMRNB_H__
+#define __PJMEDIA_CODEC_OPENCORE_AMRNB_H__
+
+#include <pjmedia-codec/types.h>
+
+/**
+ * @defgroup PJMED_OC_AMRNB OpenCORE AMR-NB Codec
+ * @ingroup PJMEDIA_CODEC_CODECS
+ * @brief AMRCodec wrapper for OpenCORE AMR-NB codec
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * Settings. Use #pjmedia_codec_opencore_amrnb_set_config() to
+ * activate.
+ */
+typedef struct pjmedia_codec_amrnb_config
+{
+ /**
+ * Control whether to use octent align.
+ */
+ pj_bool_t octet_align;
+
+ /**
+ * Set the bitrate.
+ */
+ unsigned bitrate;
+
+} pjmedia_codec_amrnb_config;
+
+
+/**
+ * Initialize and register AMR-NB codec factory to pjmedia endpoint.
+ *
+ * @param endpt The pjmedia endpoint.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_init(pjmedia_endpt* endpt);
+
+/**
+ * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize
+ * the OpenCORE codec library.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void);
+
+
+/**
+ * Set AMR-NB parameters.
+ *
+ * @param cfg The settings;
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
+ const pjmedia_codec_amrnb_config* cfg);
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_CODEC_OPENCORE_AMRNB_H__ */
+
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index e7d2e149..60930818 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -387,7 +387,7 @@
/**
- * Number of packets received from different source IP address from the
+ * Number of RTP packets received from different source IP address from the
* remote address required to make the stream switch transmission
* to the source address.
*/
@@ -397,6 +397,16 @@
/**
+ * Number of RTCP packets received from different source IP address from the
+ * remote address required to make the stream switch RTCP transmission
+ * to the source address.
+ */
+#ifndef PJMEDIA_RTCP_NAT_PROBATION_CNT
+# define PJMEDIA_RTCP_NAT_PROBATION_CNT 3
+#endif
+
+
+/**
* Specify whether RTCP should be advertised in SDP. This setting would
* affect whether RTCP candidate will be added in SDP when ICE is used.
* Application might want to disable RTCP advertisement in SDP to
@@ -916,7 +926,7 @@
* Default: 5 seconds
*/
#ifndef PJMEDIA_STREAM_KA_INTERVAL
-# define PJMEDIA_STREAM_KA_INTERVAL 5
+# define PJMEDIA_STREAM_KA_INTERVAL 5
#endif
@@ -1057,6 +1067,63 @@
/**
+ * Minimum gap between two consecutive discards in jitter buffer,
+ * in milliseconds.
+ *
+ * Default: 200 ms
+ */
+#ifndef PJMEDIA_JBUF_DISC_MIN_GAP
+# define PJMEDIA_JBUF_DISC_MIN_GAP 200
+#endif
+
+
+/**
+ * Minimum burst level reference used for calculating discard duration
+ * in jitter buffer progressive discard algorithm, in frames.
+ *
+ * Default: 1 frame
+ */
+#ifndef PJMEDIA_JBUF_PRO_DISC_MIN_BURST
+# define PJMEDIA_JBUF_PRO_DISC_MIN_BURST 1
+#endif
+
+
+/**
+ * Maximum burst level reference used for calculating discard duration
+ * in jitter buffer progressive discard algorithm, in frames.
+ *
+ * Default: 200 frames
+ */
+#ifndef PJMEDIA_JBUF_PRO_DISC_MAX_BURST
+# define PJMEDIA_JBUF_PRO_DISC_MAX_BURST 100
+#endif
+
+
+/**
+ * Duration for progressive discard algotithm in jitter buffer to discard
+ * an excessive frame when burst is equal to or lower than
+ * PJMEDIA_JBUF_PRO_DISC_MIN_BURST, in milliseconds.
+ *
+ * Default: 2000 ms
+ */
+#ifndef PJMEDIA_JBUF_PRO_DISC_T1
+# define PJMEDIA_JBUF_PRO_DISC_T1 2000
+#endif
+
+
+/**
+ * Duration for progressive discard algotithm in jitter buffer to discard
+ * an excessive frame when burst is equal to or lower than
+ * PJMEDIA_JBUF_PRO_DISC_MAX_BURST, in milliseconds.
+ *
+ * Default: 10000 ms
+ */
+#ifndef PJMEDIA_JBUF_PRO_DISC_T2
+# define PJMEDIA_JBUF_PRO_DISC_T2 10000
+#endif
+
+
+/**
* Video stream will discard old picture from the jitter buffer as soon as
* new picture is received, to reduce latency.
*
diff --git a/pjmedia/include/pjmedia/delaybuf.h b/pjmedia/include/pjmedia/delaybuf.h
index 9dcad5ee..09b01bbb 100644
--- a/pjmedia/include/pjmedia/delaybuf.h
+++ b/pjmedia/include/pjmedia/delaybuf.h
@@ -64,6 +64,19 @@ PJ_BEGIN_DECL
typedef struct pjmedia_delay_buf pjmedia_delay_buf;
/**
+ * Delay buffer options.
+ */
+typedef enum pjmedia_delay_buf_flag
+{
+ /**
+ * Use simple FIFO mechanism for the delay buffer, i.e.
+ * without WSOLA for expanding and shrinking audio samples.
+ */
+ PJMEDIA_DELAY_BUF_SIMPLE_FIFO = 1
+
+} pjmedia_delay_buf_flag;
+
+/**
* Create the delay buffer. Once the delay buffer is created, it will
* enter learning state unless the delay argument is specified, which
* in this case it will directly enter the running state.
@@ -79,7 +92,12 @@ typedef struct pjmedia_delay_buf pjmedia_delay_buf;
* in ms, if this value is negative or less than
* one frame time, default maximum delay used is
* 400 ms.
- * @param options Option flags, must be zero for now.
+ * @param options Options. If PJMEDIA_DELAY_BUF_SIMPLE_FIFO is
+ * specified, then a simple FIFO mechanism
+ * will be used instead of the adaptive
+ * implementation (which uses WSOLA to expand
+ * or shrink audio samples).
+ * See #pjmedia_delay_buf_flag for other options.
* @param p_b Pointer to receive the delay buffer instance.
*
* @return PJ_SUCCESS if the delay buffer has been
diff --git a/pjmedia/include/pjmedia/echo.h b/pjmedia/include/pjmedia/echo.h
index f2c2210c..ba7f8897 100644
--- a/pjmedia/include/pjmedia/echo.h
+++ b/pjmedia/include/pjmedia/echo.h
@@ -88,7 +88,15 @@ typedef enum pjmedia_echo_flag
* for the echo canceller, but application will guarantee that echo
* canceller will not be called by different threads at the same time.
*/
- PJMEDIA_ECHO_NO_LOCK = 16
+ PJMEDIA_ECHO_NO_LOCK = 16,
+
+ /**
+ * If PJMEDIA_ECHO_USE_SIMPLE_FIFO flag is specified, the delay buffer
+ * created for the echo canceller will use simple FIFO mechanism, i.e.
+ * without using WSOLA to expand and shrink audio samples.
+ */
+ PJMEDIA_ECHO_USE_SIMPLE_FIFO = 32
+
} pjmedia_echo_flag;
diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h
index 2ae4ed6c..2cda3dd9 100644
--- a/pjmedia/include/pjmedia/jbuf.h
+++ b/pjmedia/include/pjmedia/jbuf.h
@@ -48,7 +48,7 @@ PJ_BEGIN_DECL
/**
* Types of frame returned by the jitter buffer.
*/
-enum pjmedia_jb_frame_type
+typedef enum pjmedia_jb_frame_type
{
PJMEDIA_JB_MISSING_FRAME = 0, /**< No frame because it's missing */
PJMEDIA_JB_NORMAL_FRAME = 1, /**< Normal frame is being returned */
@@ -56,13 +56,41 @@ enum pjmedia_jb_frame_type
because JB is bufferring. */
PJMEDIA_JB_ZERO_EMPTY_FRAME = 3 /**< Zero frame is being returned
because JB is empty. */
-};
+} pjmedia_jb_frame_type;
/**
- * @see pjmedia_jb_frame_type.
+ * Enumeration of jitter buffer discard algorithm. The jitter buffer
+ * continuously calculates the jitter level to get the optimum latency at
+ * any time and in order to adjust the latency, the jitter buffer may need
+ * to discard some frames.
*/
-typedef enum pjmedia_jb_frame_type pjmedia_jb_frame_type;
+typedef enum pjmedia_jb_discard_algo
+{
+ /**
+ * Jitter buffer should not discard any frame, except when the jitter
+ * buffer is full and a new frame arrives, one frame will be discarded
+ * to make space for the new frame.
+ */
+ PJMEDIA_JB_DISCARD_NONE = 0,
+
+ /**
+ * Only discard one frame in at least 200ms when the latency is considered
+ * much higher than it should be. When the jitter buffer is full and a new
+ * frame arrives, one frame will be discarded to make space for the new
+ * frame.
+ */
+ PJMEDIA_JB_DISCARD_STATIC,
+
+ /**
+ * The discard rate is dynamically calculated based on actual parameters
+ * such as jitter level and latency. When the jitter buffer is full and
+ * a new frame arrives, one frame will be discarded to make space for the
+ * new frame.
+ */
+ PJMEDIA_JB_DISCARD_PROGRESSIVE
+
+} pjmedia_jb_discard_algo;
/**
@@ -107,7 +135,9 @@ typedef struct pjmedia_jbuf pjmedia_jbuf;
/**
* Create an adaptive jitter buffer according to the specification. If
* application wants to have a fixed jitter buffer, it may call
- * #pjmedia_jbuf_set_fixed() after the jitter buffer is created.
+ * #pjmedia_jbuf_set_fixed() after the jitter buffer is created. Also
+ * if application wants to alter the discard algorithm, which the default
+ * PJMEDIA_JB_DISCARD_PROGRESSIVE, it may call #pjmedia_jbuf_set_discard().
*
* This function may allocate large chunk of memory to keep the frames in
* the buffer.
@@ -174,16 +204,16 @@ PJ_DECL(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb,
/**
- * Enable/disable the jitter buffer drift detection and handling mechanism.
- * The default behavior is enabled.
+ * Set the jitter buffer discard algorithm. The default discard algorithm,
+ * set in jitter buffer creation, is PJMEDIA_JB_DISCARD_PROGRESSIVE.
*
- * @param jb The jitter buffer
- * @param enable Set to PJ_TRUE to enable or PJ_FALSE to disable.
+ * @param jb The jitter buffer.
+ * @param algo The discard algorithm to be used.
*
* @return PJ_SUCCESS on success.
*/
-PJ_DECL(pj_status_t) pjmedia_jbuf_enable_discard(pjmedia_jbuf *jb,
- pj_bool_t enable);
+PJ_DECL(pj_status_t) pjmedia_jbuf_set_discard(pjmedia_jbuf *jb,
+ pjmedia_jb_discard_algo algo);
/**
diff --git a/pjmedia/include/pjmedia/session.h b/pjmedia/include/pjmedia/session.h
index efab853a..44238c56 100644
--- a/pjmedia/include/pjmedia/session.h
+++ b/pjmedia/include/pjmedia/session.h
@@ -232,6 +232,26 @@ PJ_DECL(pj_status_t) pjmedia_session_resume_stream(pjmedia_session *session,
pjmedia_dir dir);
/**
+ * Send RTCP SDES for the session.
+ *
+ * @param session The media session.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_session_send_rtcp_sdes( const pjmedia_session *session );
+
+/**
+ * Send RTCP BYE for the session.
+ *
+ * @param session The media session.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_session_send_rtcp_bye( const pjmedia_session *session );
+
+/**
* Enumerate media streams in the session.
*
* @param session The media session.
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index ea3a1b64..88e5a882 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -135,6 +135,9 @@ typedef struct pjmedia_stream_info
(see #PJMEDIA_STREAM_ENABLE_KA)
is enabled? */
#endif
+ pj_bool_t rtcp_sdes_bye_disabled;
+ /**< Disable automatic sending of RTCP
+ SDES and BYE. */
} pjmedia_stream_info;
@@ -404,6 +407,26 @@ pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream,
/**
+ * Send RTCP SDES for the media stream.
+ *
+ * @param stream The media stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_stream_send_rtcp_sdes( pjmedia_stream *stream );
+
+/**
+ * Send RTCP BYE for the media stream.
+ *
+ * @param stream The media stream.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_stream_send_rtcp_bye( pjmedia_stream *stream );
+
+/**
* @}
*/
diff --git a/pjmedia/include/pjmedia/transport_adapter_sample.h b/pjmedia/include/pjmedia/transport_adapter_sample.h
index 6c41a69d..62eb1c0f 100644
--- a/pjmedia/include/pjmedia/transport_adapter_sample.h
+++ b/pjmedia/include/pjmedia/transport_adapter_sample.h
@@ -49,15 +49,18 @@ PJ_BEGIN_DECL
* @param endpt The media endpoint.
* @param name Optional name to identify this media transport
* for logging purposes.
- * @param transport The underlying media transport to send and receive
- * RTP/RTCP packets.
+ * @param base_tp The base/underlying media transport to send and
+ * receive RTP/RTCP packets.
+ * @param del_base Specify whether the base transport should also be
+ * destroyed when destroy() is called upon us.
* @param p_tp Pointer to receive the media transport instance.
*
* @return PJ_SUCCESS on success, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt,
const char *name,
- pjmedia_transport *transport,
+ pjmedia_transport *base_tp,
+ pj_bool_t del_base,
pjmedia_transport **p_tp);
PJ_END_DECL
diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c
index 437ee584..067fc78f 100644
--- a/pjmedia/src/pjmedia-audiodev/audiodev.c
+++ b/pjmedia/src/pjmedia-audiodev/audiodev.c
@@ -490,11 +490,13 @@ PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
}
--aud_subsys.init_count;
- for (i=0; i<aud_subsys.drv_cnt; ++i) {
- deinit_driver(i);
- }
+ if (aud_subsys.init_count == 0) {
+ for (i=0; i<aud_subsys.drv_cnt; ++i) {
+ deinit_driver(i);
+ }
- aud_subsys.pf = NULL;
+ aud_subsys.pf = NULL;
+ }
return PJ_SUCCESS;
}
diff --git a/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c b/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c
index fd77c993..c5a3e621 100644
--- a/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c
@@ -1111,7 +1111,17 @@ static void interruptionListener(void *inClientData, UInt32 inInterruption)
{
struct stream_list *it, *itBegin;
pj_status_t status;
-
+ pj_thread_desc thread_desc;
+ pj_thread_t *thread;
+
+ /* Register the thread with PJLIB, this is must for any external threads
+ * which need to use the PJLIB framework.
+ */
+ if (!pj_thread_is_registered()) {
+ pj_bzero(thread_desc, sizeof(pj_thread_desc));
+ status = pj_thread_register("intListener", thread_desc, &thread);
+ }
+
PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---",
inInterruption == kAudioSessionBeginInterruption ?
"Begin Interruption" : "End Interruption"));
diff --git a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
index a2b67380..309aaf59 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
@@ -1086,7 +1086,7 @@ static void PlayCb(TAPSCommBuffer &buf, void *user_data)
buf.iBuffer.Append((TUint8*)sf->data, len);
} else {
enum {NO_DATA_FT = 15 };
- pj_uint8_t amr_header = 4 || (NO_DATA_FT << 3);
+ pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
buf.iBuffer.Append(amr_header);
}
@@ -1095,7 +1095,7 @@ static void PlayCb(TAPSCommBuffer &buf, void *user_data)
} else { /* PJMEDIA_FRAME_TYPE_NONE */
enum {NO_DATA_FT = 15 };
- pj_uint8_t amr_header = 4 || (NO_DATA_FT << 3);
+ pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
buf.iBuffer.Append(amr_header);
diff --git a/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
index d0e66bdc..645ed394 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
@@ -239,6 +239,7 @@ private:
TPtr8 iFramePtr_;
TInt lastError_;
pj_uint32_t timeStamp_;
+ CActiveSchedulerWait startAsw_;
// cache variable
// to avoid calculating frame length repeatedly
@@ -363,6 +364,13 @@ pj_status_t CPjAudioInputEngine::StartRecord()
lastError_ = KRequestPending;
iInputStream_->Open(&iStreamSettings);
+#if defined(PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START) && \
+ PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START != 0
+
+ startAsw_.Start();
+
+#endif
+
// Success
PJ_LOG(4,(THIS_FILE, "Sound capture started."));
return PJ_SUCCESS;
@@ -386,6 +394,10 @@ void CPjAudioInputEngine::Stop()
iInputStream_ = NULL;
}
+ if (startAsw_.IsStarted()) {
+ startAsw_.AsyncStop();
+ }
+
state_ = STATE_INACTIVE;
}
@@ -399,12 +411,25 @@ TPtr8 & CPjAudioInputEngine::GetFrame()
void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
{
+ if (startAsw_.IsStarted()) {
+ startAsw_.AsyncStop();
+ }
+
lastError_ = aError;
if (aError != KErrNone) {
snd_perror("Error in MaiscOpenComplete()", aError);
return;
}
+ /* Apply input volume setting if specified */
+ if (parentStrm_->param.flags &
+ PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING)
+ {
+ stream_set_cap(&parentStrm_->base,
+ PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+ &parentStrm_->param.input_vol);
+ }
+
// set stream priority to normal and time sensitive
iInputStream_->SetPriority(EPriorityNormal,
EMdaPriorityPreferenceTime);
@@ -414,7 +439,12 @@ void CPjAudioInputEngine::MaiscOpenComplete(TInt aError)
TRAPD(err2, iInputStream_->ReadL(frm));
if (err2) {
PJ_LOG(4,(THIS_FILE, "Exception in iInputStream_->ReadL()"));
+ lastError_ = err2;
+ return;
}
+
+ // input stream opened succesfully, set status to Active
+ state_ = STATE_ACTIVE;
}
void CPjAudioInputEngine::MaiscBufferCopied(TInt aError,
@@ -547,6 +577,7 @@ private:
TPtrC8 frame_;
TInt lastError_;
unsigned timestamp_;
+ CActiveSchedulerWait startAsw_;
CPjAudioOutputEngine(struct mda_stream *parent_strm,
pjmedia_aud_play_cb play_cb,
@@ -638,6 +669,13 @@ pj_status_t CPjAudioOutputEngine::StartPlay()
// Open stream.
lastError_ = KRequestPending;
iOutputStream_->Open(&iStreamSettings);
+
+#if defined(PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START) && \
+ PJMEDIA_AUDIO_DEV_MDA_USE_SYNC_START != 0
+
+ startAsw_.Start();
+
+#endif
// Success
PJ_LOG(4,(THIS_FILE, "Sound playback started"));
@@ -662,17 +700,22 @@ void CPjAudioOutputEngine::Stop()
iOutputStream_ = NULL;
}
+ if (startAsw_.IsStarted()) {
+ startAsw_.AsyncStop();
+ }
+
state_ = STATE_INACTIVE;
}
void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
{
+ if (startAsw_.IsStarted()) {
+ startAsw_.AsyncStop();
+ }
+
lastError_ = aError;
if (aError==KErrNone) {
- // output stream opened succesfully, set status to Active
- state_ = STATE_ACTIVE;
-
// set stream properties, 16bit 8KHz mono
TMdaAudioDataSettings iSettings;
iSettings.iChannels =
@@ -683,8 +726,17 @@ void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
iOutputStream_->SetAudioPropertiesL(iSettings.iSampleRate,
iSettings.iChannels);
- // set volume to 1/2th of stream max volume
- iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/2);
+ /* Apply output volume setting if specified */
+ if (parentStrm_->param.flags &
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING)
+ {
+ stream_set_cap(&parentStrm_->base,
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ &parentStrm_->param.output_vol);
+ } else {
+ // set volume to 1/2th of stream max volume
+ iOutputStream_->SetVolume(iOutputStream_->MaxVolume()/2);
+ }
// set stream priority to normal and time sensitive
iOutputStream_->SetPriority(EPriorityNormal,
@@ -718,6 +770,9 @@ void CPjAudioOutputEngine::MaoscOpenComplete(TInt aError)
// until whole data buffer is written.
frame_.Set(frameBuf_, frameBufSize_);
iOutputStream_->WriteL(frame_);
+
+ // output stream opened succesfully, set status to Active
+ state_ = STATE_ACTIVE;
} else {
snd_perror("Error in MaoscOpenComplete()", aError);
}
@@ -881,7 +936,8 @@ static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
param->channel_count = 1;
param->samples_per_frame = af->dev_info.default_samples_per_sec * 20 / 1000;
param->bits_per_sample = BITS_PER_SAMPLE;
- param->flags = af->dev_info.caps;
+ // Don't set the flags without specifying the flags value.
+ //param->flags = af->dev_info.caps;
return PJ_SUCCESS;
}
@@ -956,6 +1012,20 @@ static pj_status_t stream_get_param(pjmedia_aud_stream *s,
pj_memcpy(pi, &strm->param, sizeof(*pi));
+ /* Update the output volume setting */
+ if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ &pi->output_vol) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
+ }
+
+ /* Update the input volume setting */
+ if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+ &pi->input_vol) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING;
+ }
+
return PJ_SUCCESS;
}
@@ -1034,7 +1104,7 @@ static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
}
break;
case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
- if (strm->param.dir & PJMEDIA_DIR_CAPTURE) {
+ if (strm->param.dir & PJMEDIA_DIR_PLAYBACK) {
PJ_ASSERT_RETURN(strm->out_engine, PJ_EINVAL);
TInt max_vol = strm->out_engine->GetMaxVolume();
diff --git a/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp
index fd4d0806..bcd9d75a 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_vas_dev.cpp
@@ -1130,7 +1130,7 @@ static void PlayCb(CVoIPDataBuffer *buf, void *user_data)
buffer.Append((TUint8*)sf->data, len);
} else {
enum {NO_DATA_FT = 15 };
- pj_uint8_t amr_header = 4 || (NO_DATA_FT << 3);
+ pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
buffer.Append(amr_header);
}
@@ -1139,7 +1139,7 @@ static void PlayCb(CVoIPDataBuffer *buf, void *user_data)
} else { /* PJMEDIA_FRAME_TYPE_NONE */
enum {NO_DATA_FT = 15 };
- pj_uint8_t amr_header = 4 || (NO_DATA_FT << 3);
+ pj_uint8_t amr_header = 4 | (NO_DATA_FT << 3);
buffer.Append(amr_header);
@@ -1746,12 +1746,6 @@ static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
return PJ_RETURN_OS_ERROR(err);
}
- /* Apply output volume setting if specified */
- if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
- stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
- &param->output_vol);
- }
-
/* Done */
strm->base.op = &stream_op;
*p_aud_strm = &strm->base;
@@ -1945,10 +1939,21 @@ static pj_status_t stream_start(pjmedia_aud_stream *strm)
} while (!stream->engine->IsStarted() &&
(now.MicroSecondsFrom(start) < VAS_WAIT_START * 1000));
- if (stream->engine->IsStarted())
+ if (stream->engine->IsStarted()) {
+
+ /* Apply output volume setting if specified */
+ if (stream->param.flags &
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING)
+ {
+ stream_set_cap(strm,
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ &stream->param.output_vol);
+ }
+
return PJ_SUCCESS;
- else
+ } else {
return PJ_ETIMEDOUT;
+ }
}
return PJ_EINVALIDOP;
diff --git a/pjmedia/src/pjmedia-codec/audio_codecs.c b/pjmedia/src/pjmedia-codec/audio_codecs.c
index a9e0700a..eea9ba74 100644
--- a/pjmedia/src/pjmedia-codec/audio_codecs.c
+++ b/pjmedia/src/pjmedia-codec/audio_codecs.c
@@ -107,6 +107,13 @@ pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt,
return status;
#endif /* PJMEDIA_HAS_L16_CODEC */
+#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+ /* Register OpenCORE AMR-NB */
+ status = pjmedia_codec_opencore_amrnb_init(endpt);
+ if (status != PJ_SUCCESS)
+ return status;
+#endif
+
return PJ_SUCCESS;
}
diff --git a/pjmedia/src/pjmedia-codec/ipp_codecs.c b/pjmedia/src/pjmedia-codec/ipp_codecs.c
index f5690eee..a2ead05b 100644
--- a/pjmedia/src/pjmedia-codec/ipp_codecs.c
+++ b/pjmedia/src/pjmedia-codec/ipp_codecs.c
@@ -42,6 +42,7 @@
#define THIS_FILE "ipp_codecs.c"
+
/* Prototypes for IPP codecs factory */
static pj_status_t ipp_test_alloc( pjmedia_codec_factory *factory,
const pjmedia_codec_info *id );
@@ -238,9 +239,8 @@ static struct ipp_codec {
ipp_codec[] =
{
# if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR
- /* AMR-NB SID seems to produce noise, so let's just disable its VAD. */
{1, "AMR", PJMEDIA_RTP_PT_AMR, &USC_GSMAMR_Fxns, 8000, 1, 160,
- 7400, 12200, 2, 0, 1,
+ 7400, 12200, 2, 1, 1,
&predecode_amr, &parse_amr, &pack_amr,
{1, {{{"octet-align", 11}, {"1", 1}}} }
},
@@ -488,9 +488,7 @@ static void predecode_amr( ipp_private_t *codec_data,
} else if (frame.size == 5) {
/* SID */
if (info->good_quality) {
- pj_bool_t STI;
- STI = (((pj_uint8_t*)frame.buf)[35 >> 3] & 0x10) != 0;
- usc_frame->frametype = STI? 2 : 1;
+ usc_frame->frametype = info->STI? 2 : 1;
} else {
usc_frame->frametype = setting->amr_nb ? 6 : 7;
}
@@ -534,6 +532,7 @@ static pj_status_t pack_amr(ipp_private_t *codec_data, void *pkt,
info->frame_type = (pj_uint8_t)(info_ & 0x0F);
info->good_quality = (pj_uint8_t)((info_ & 0x80) == 0);
info->mode = (pj_int8_t) ((info_ >> 8) & 0x0F);
+ info->STI = (pj_uint8_t)((info_ >> 5) & 1);
frames[nframes].buf = r + 2;
frames[nframes].size = info->frame_type <= SID_FT ?
@@ -1419,6 +1418,7 @@ static pj_status_t ipp_codec_encode( pjmedia_codec *codec,
/* Two octets for AMR frame info, 0=LSB:
* bit 0-3 : frame type
+ * bit 5 : STI flag
* bit 6 : last frame flag
* bit 7 : quality flag
* bit 8-11 : mode
@@ -1442,6 +1442,9 @@ static pj_status_t ipp_codec_encode( pjmedia_codec *codec,
/* Quality */
if (out.frametype == 6 || out.frametype == 7)
*info |= 0x80;
+ /* STI */
+ if (out.frametype != 1)
+ *info |= 0x20;
} else {
/* Untransmited */
*info = 15;
diff --git a/pjmedia/src/pjmedia-codec/opencore_amrnb.c b/pjmedia/src/pjmedia-codec/opencore_amrnb.c
new file mode 100644
index 00000000..eba53215
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/opencore_amrnb.c
@@ -0,0 +1,820 @@
+/* $Id */
+/*
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2011 Dan Arrhenius <dan@keystream.se>
+ *
+ * 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
+ */
+
+/*
+ * AMR-NB codec implementation with OpenCORE AMRNB library
+ */
+#include <pjmedia-codec/g722.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/endpoint.h>
+#include <pjmedia/plc.h>
+#include <pjmedia/port.h>
+#include <pjmedia/silencedet.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/math.h>
+
+#if defined(PJMEDIA_HAS_OPENCORE_AMRNB_CODEC) && \
+ (PJMEDIA_HAS_OPENCORE_AMRNB_CODEC != 0)
+
+#include <opencore-amrnb/interf_enc.h>
+#include <opencore-amrnb/interf_dec.h>
+#include <pjmedia-codec/amr_helper.h>
+#include <pjmedia-codec/opencore_amrnb.h>
+
+#define THIS_FILE "opencore_amrnb.c"
+
+/* Tracing */
+#define PJ_TRACE 0
+
+#if PJ_TRACE
+# define TRACE_(expr) PJ_LOG(4,expr)
+#else
+# define TRACE_(expr)
+#endif
+
+/* Use PJMEDIA PLC */
+#define USE_PJMEDIA_PLC 1
+
+
+
+/* Prototypes for AMR-NB factory */
+static pj_status_t amr_test_alloc(pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id );
+static pj_status_t amr_default_attr(pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id,
+ pjmedia_codec_param *attr );
+static pj_status_t amr_enum_codecs(pjmedia_codec_factory *factory,
+ unsigned *count,
+ pjmedia_codec_info codecs[]);
+static pj_status_t amr_alloc_codec(pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id,
+ pjmedia_codec **p_codec);
+static pj_status_t amr_dealloc_codec(pjmedia_codec_factory *factory,
+ pjmedia_codec *codec );
+
+/* Prototypes for AMR-NB implementation. */
+static pj_status_t amr_codec_init(pjmedia_codec *codec,
+ pj_pool_t *pool );
+static pj_status_t amr_codec_open(pjmedia_codec *codec,
+ pjmedia_codec_param *attr );
+static pj_status_t amr_codec_close(pjmedia_codec *codec );
+static pj_status_t amr_codec_modify(pjmedia_codec *codec,
+ const pjmedia_codec_param *attr );
+static pj_status_t amr_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 amr_codec_encode(pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output);
+static pj_status_t amr_codec_decode(pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output);
+static pj_status_t amr_codec_recover(pjmedia_codec *codec,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output);
+
+
+
+/* Definition for AMR-NB codec operations. */
+static pjmedia_codec_op amr_op =
+{
+ &amr_codec_init,
+ &amr_codec_open,
+ &amr_codec_close,
+ &amr_codec_modify,
+ &amr_codec_parse,
+ &amr_codec_encode,
+ &amr_codec_decode,
+ &amr_codec_recover
+};
+
+/* Definition for AMR-NB codec factory operations. */
+static pjmedia_codec_factory_op amr_factory_op =
+{
+ &amr_test_alloc,
+ &amr_default_attr,
+ &amr_enum_codecs,
+ &amr_alloc_codec,
+ &amr_dealloc_codec
+};
+
+
+/* AMR-NB factory */
+static struct amr_codec_factory
+{
+ pjmedia_codec_factory base;
+ pjmedia_endpt *endpt;
+ pj_pool_t *pool;
+} amr_codec_factory;
+
+
+/* AMR-NB codec private data. */
+struct amr_data
+{
+ pj_pool_t *pool;
+ void *encoder;
+ void *decoder;
+ pj_bool_t plc_enabled;
+ pj_bool_t vad_enabled;
+ int enc_mode;
+ pjmedia_codec_amr_pack_setting enc_setting;
+ pjmedia_codec_amr_pack_setting dec_setting;
+#if USE_PJMEDIA_PLC
+ pjmedia_plc *plc;
+#endif
+ pj_timestamp last_tx;
+};
+
+static pjmedia_codec_amrnb_config def_config =
+{
+ PJ_FALSE, /* octet align */
+ 5900 /* bitrate */
+};
+
+
+
+/*
+ * Initialize and register AMR-NB codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_init( pjmedia_endpt *endpt )
+{
+ pjmedia_codec_mgr *codec_mgr;
+ pj_status_t status;
+
+ if (amr_codec_factory.pool != NULL)
+ return PJ_SUCCESS;
+
+ /* Create AMR-NB codec factory. */
+ amr_codec_factory.base.op = &amr_factory_op;
+ amr_codec_factory.base.factory_data = NULL;
+ amr_codec_factory.endpt = endpt;
+
+ amr_codec_factory.pool = pjmedia_endpt_create_pool(endpt, "amrnb", 1000,
+ 1000);
+ if (!amr_codec_factory.pool)
+ return PJ_ENOMEM;
+
+ /* Get the codec manager. */
+ codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
+ if (!codec_mgr) {
+ status = PJ_EINVALIDOP;
+ goto on_error;
+ }
+
+ /* Register codec factory to endpoint. */
+ status = pjmedia_codec_mgr_register_factory(codec_mgr,
+ &amr_codec_factory.base);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Done. */
+ return PJ_SUCCESS;
+
+on_error:
+ pj_pool_release(amr_codec_factory.pool);
+ amr_codec_factory.pool = NULL;
+ return status;
+}
+
+
+/*
+ * Unregister AMR-NB codec factory from pjmedia endpoint and deinitialize
+ * the AMR-NB codec library.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_deinit(void)
+{
+ pjmedia_codec_mgr *codec_mgr;
+ pj_status_t status;
+
+ if (amr_codec_factory.pool == NULL)
+ return PJ_SUCCESS;
+
+ /* Get the codec manager. */
+ codec_mgr = pjmedia_endpt_get_codec_mgr(amr_codec_factory.endpt);
+ if (!codec_mgr) {
+ pj_pool_release(amr_codec_factory.pool);
+ amr_codec_factory.pool = NULL;
+ return PJ_EINVALIDOP;
+ }
+
+ /* Unregister AMR-NB codec factory. */
+ status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
+ &amr_codec_factory.base);
+
+ /* Destroy pool. */
+ pj_pool_release(amr_codec_factory.pool);
+ amr_codec_factory.pool = NULL;
+
+ return status;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_opencore_amrnb_set_config(
+ const pjmedia_codec_amrnb_config *config)
+{
+ unsigned nbitrates;
+
+
+ def_config = *config;
+
+ /* Normalize bitrate. */
+ nbitrates = PJ_ARRAY_SIZE(pjmedia_codec_amrnb_bitrates);
+ if (def_config.bitrate < pjmedia_codec_amrnb_bitrates[0])
+ def_config.bitrate = pjmedia_codec_amrnb_bitrates[0];
+ else if (def_config.bitrate > pjmedia_codec_amrnb_bitrates[nbitrates-1])
+ def_config.bitrate = pjmedia_codec_amrnb_bitrates[nbitrates-1];
+ else
+ {
+ unsigned i;
+
+ for (i = 0; i < nbitrates; ++i) {
+ if (def_config.bitrate <= pjmedia_codec_amrnb_bitrates[i])
+ break;
+ }
+ def_config.bitrate = pjmedia_codec_amrnb_bitrates[i];
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Check if factory can allocate the specified codec.
+ */
+static pj_status_t amr_test_alloc( pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *info )
+{
+ PJ_UNUSED_ARG(factory);
+
+ /* Check payload type. */
+ if (info->pt != PJMEDIA_RTP_PT_AMR)
+ return PJMEDIA_CODEC_EUNSUP;
+
+ /* Ignore the rest, since it's static payload type. */
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t amr_default_attr( pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id,
+ pjmedia_codec_param *attr )
+{
+ PJ_UNUSED_ARG(factory);
+ PJ_UNUSED_ARG(id);
+
+ pj_bzero(attr, sizeof(pjmedia_codec_param));
+ attr->info.clock_rate = 8000;
+ attr->info.channel_cnt = 1;
+ attr->info.avg_bps = def_config.bitrate;
+ attr->info.max_bps = pjmedia_codec_amrnb_bitrates[7];
+ attr->info.pcm_bits_per_sample = 16;
+ attr->info.frm_ptime = 20;
+ attr->info.pt = PJMEDIA_RTP_PT_AMR;
+
+ attr->setting.frm_per_pkt = 2;
+ attr->setting.vad = 1;
+ attr->setting.plc = 1;
+
+ if (def_config.octet_align) {
+ attr->setting.dec_fmtp.cnt = 1;
+ attr->setting.dec_fmtp.param[0].name = pj_str("octet-align");
+ attr->setting.dec_fmtp.param[0].val = pj_str("1");
+ }
+
+ /* Default all other flag bits disabled. */
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum codecs supported by this factory (i.e. only AMR-NB!).
+ */
+static pj_status_t amr_enum_codecs( pjmedia_codec_factory *factory,
+ unsigned *count,
+ pjmedia_codec_info codecs[])
+{
+ PJ_UNUSED_ARG(factory);
+ PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
+
+ pj_bzero(&codecs[0], sizeof(pjmedia_codec_info));
+ codecs[0].encoding_name = pj_str("AMR");
+ codecs[0].pt = PJMEDIA_RTP_PT_AMR;
+ codecs[0].type = PJMEDIA_TYPE_AUDIO;
+ codecs[0].clock_rate = 8000;
+ codecs[0].channel_cnt = 1;
+
+ *count = 1;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate a new AMR-NB codec instance.
+ */
+static pj_status_t amr_alloc_codec( pjmedia_codec_factory *factory,
+ const pjmedia_codec_info *id,
+ pjmedia_codec **p_codec)
+{
+ pj_pool_t *pool;
+ pjmedia_codec *codec;
+ struct amr_data *amr_data;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(factory && id && p_codec, PJ_EINVAL);
+ PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
+
+ pool = pjmedia_endpt_create_pool(amr_codec_factory.endpt, "amrnb-inst",
+ 512, 512);
+
+ codec = PJ_POOL_ZALLOC_T(pool, pjmedia_codec);
+ PJ_ASSERT_RETURN(codec != NULL, PJ_ENOMEM);
+ codec->op = &amr_op;
+ codec->factory = factory;
+
+ amr_data = PJ_POOL_ZALLOC_T(pool, struct amr_data);
+ codec->codec_data = amr_data;
+ amr_data->pool = pool;
+
+#if USE_PJMEDIA_PLC
+ /* Create PLC */
+ status = pjmedia_plc_create(pool, 8000, 160, 0, &amr_data->plc);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+#else
+ PJ_UNUSED_ARG(status);
+#endif
+ *p_codec = codec;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Free codec.
+ */
+static pj_status_t amr_dealloc_codec( pjmedia_codec_factory *factory,
+ pjmedia_codec *codec )
+{
+ struct amr_data *amr_data;
+
+ PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+ PJ_ASSERT_RETURN(factory == &amr_codec_factory.base, PJ_EINVAL);
+
+ amr_data = (struct amr_data*) codec->codec_data;
+
+ /* Close codec, if it's not closed. */
+ amr_codec_close(codec);
+
+ pj_pool_release(amr_data->pool);
+ amr_data = NULL;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t amr_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 amr_codec_open( pjmedia_codec *codec,
+ pjmedia_codec_param *attr )
+{
+ struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+ pjmedia_codec_amr_pack_setting *setting;
+ unsigned i;
+ pj_uint8_t octet_align = 0;
+ pj_int8_t enc_mode;
+ const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
+
+ PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
+ PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
+
+ enc_mode = pjmedia_codec_amr_get_mode(attr->info.avg_bps);
+ pj_assert(enc_mode >= 0 && enc_mode <= 7);
+
+ /* Check octet-align */
+ for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
+ if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name,
+ &STR_FMTP_OCTET_ALIGN) == 0)
+ {
+ octet_align = (pj_uint8_t)
+ (pj_strtoul(&attr->setting.dec_fmtp.param[i].val));
+ break;
+ }
+ }
+
+ /* Check mode-set */
+ for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+ const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8};
+
+ if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name,
+ &STR_FMTP_MODE_SET) == 0)
+ {
+ const char *p;
+ pj_size_t l;
+ pj_int8_t diff = 99;
+
+ /* Encoding mode is chosen based on local default mode setting:
+ * - if local default mode is included in the mode-set, use it
+ * - otherwise, find the closest mode to local default mode;
+ * if there are two closest modes, prefer to use the higher
+ * one, e.g: local default mode is 4, the mode-set param
+ * contains '2,3,5,6', then 5 will be chosen.
+ */
+ p = pj_strbuf(&attr->setting.enc_fmtp.param[i].val);
+ l = pj_strlen(&attr->setting.enc_fmtp.param[i].val);
+ while (l--) {
+ if (*p>='0' && *p<='7') {
+ pj_int8_t tmp = *p - '0' - enc_mode;
+
+ if (PJ_ABS(diff) > PJ_ABS(tmp) ||
+ (PJ_ABS(diff) == PJ_ABS(tmp) && tmp > diff))
+ {
+ diff = tmp;
+ if (diff == 0) break;
+ }
+ }
+ ++p;
+ }
+ PJ_ASSERT_RETURN(diff != 99, PJMEDIA_CODEC_EFAILED);
+
+ enc_mode = enc_mode + diff;
+
+ break;
+ }
+ }
+
+ amr_data->vad_enabled = (attr->setting.vad != 0);
+ amr_data->plc_enabled = (attr->setting.plc != 0);
+ amr_data->enc_mode = enc_mode;
+
+ amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+ if (amr_data->encoder == NULL) {
+ TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
+ amr_codec_close(codec);
+ return PJMEDIA_CODEC_EFAILED;
+ }
+ setting = &amr_data->enc_setting;
+ pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
+ setting->amr_nb = 1;
+ setting->reorder = 0;
+ setting->octet_aligned = octet_align;
+ setting->cmr = 15;
+
+ amr_data->decoder = Decoder_Interface_init();
+ if (amr_data->decoder == NULL) {
+ TRACE_((THIS_FILE, "Decoder_Interface_init() failed"));
+ amr_codec_close(codec);
+ return PJMEDIA_CODEC_EFAILED;
+ }
+ setting = &amr_data->dec_setting;
+ pj_bzero(setting, sizeof(pjmedia_codec_amr_pack_setting));
+ setting->amr_nb = 1;
+ setting->reorder = 0;
+ setting->octet_aligned = octet_align;
+
+ TRACE_((THIS_FILE, "AMR-NB codec allocated: vad=%d, plc=%d, bitrate=%d",
+ amr_data->vad_enabled, amr_data->plc_enabled,
+ pjmedia_codec_amrnb_bitrates[amr_data->enc_mode]));
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Close codec.
+ */
+static pj_status_t amr_codec_close( pjmedia_codec *codec )
+{
+ struct amr_data *amr_data;
+
+ PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+ amr_data = (struct amr_data*) codec->codec_data;
+ PJ_ASSERT_RETURN(amr_data != NULL, PJ_EINVALIDOP);
+
+ if (amr_data->encoder) {
+ Encoder_Interface_exit(amr_data->encoder);
+ amr_data->encoder = NULL;
+ }
+
+ if (amr_data->decoder) {
+ Decoder_Interface_exit(amr_data->decoder);
+ amr_data->decoder = NULL;
+ }
+
+ TRACE_((THIS_FILE, "AMR-NB codec closed"));
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t amr_codec_modify( pjmedia_codec *codec,
+ const pjmedia_codec_param *attr )
+{
+ struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+ pj_bool_t prev_vad_state;
+
+ pj_assert(amr_data != NULL);
+ pj_assert(amr_data->encoder != NULL && amr_data->decoder != NULL);
+
+ prev_vad_state = amr_data->vad_enabled;
+ amr_data->vad_enabled = (attr->setting.vad != 0);
+ amr_data->plc_enabled = (attr->setting.plc != 0);
+
+ if (prev_vad_state != amr_data->vad_enabled) {
+ /* Reinit AMR encoder to update VAD setting */
+ TRACE_((THIS_FILE, "Reiniting AMR encoder to update VAD setting."));
+ Encoder_Interface_exit(amr_data->encoder);
+ amr_data->encoder = Encoder_Interface_init(amr_data->vad_enabled);
+ if (amr_data->encoder == NULL) {
+ TRACE_((THIS_FILE, "Encoder_Interface_init() failed"));
+ amr_codec_close(codec);
+ return PJMEDIA_CODEC_EFAILED;
+ }
+ }
+
+ TRACE_((THIS_FILE, "AMR-NB codec modified: vad=%d, plc=%d",
+ amr_data->vad_enabled, amr_data->plc_enabled));
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frames in the packet.
+ */
+static pj_status_t amr_codec_parse( pjmedia_codec *codec,
+ void *pkt,
+ pj_size_t pkt_size,
+ const pj_timestamp *ts,
+ unsigned *frame_cnt,
+ pjmedia_frame frames[])
+{
+ struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+ pj_uint8_t cmr;
+ pj_status_t status;
+
+ status = pjmedia_codec_amr_parse(pkt, pkt_size, ts, &amr_data->dec_setting,
+ frames, frame_cnt, &cmr);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Check for Change Mode Request. */
+ if (cmr <= 7 && amr_data->enc_mode != cmr) {
+ amr_data->enc_mode = cmr;
+ TRACE_((THIS_FILE, "AMR-NB encoder switched mode to %d (%dbps)",
+ amr_data->enc_mode,
+ pjmedia_codec_amrnb_bitrates[amr_data->enc_mode]));
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Encode frame.
+ */
+static pj_status_t amr_codec_encode( pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output)
+{
+ struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+ unsigned char *bitstream;
+ pj_int16_t *speech;
+ unsigned nsamples, samples_per_frame;
+ enum {MAX_FRAMES_PER_PACKET = 16};
+ pjmedia_frame frames[MAX_FRAMES_PER_PACKET];
+ pj_uint8_t *p;
+ unsigned i, out_size = 0, nframes = 0;
+ pj_size_t payload_len;
+ unsigned dtx_cnt, sid_cnt;
+ pj_status_t status;
+ int size;
+
+ pj_assert(amr_data != NULL);
+ PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+ nsamples = input->size >> 1;
+ samples_per_frame = 160;
+ PJ_ASSERT_RETURN(nsamples % samples_per_frame == 0,
+ PJMEDIA_CODEC_EPCMFRMINLEN);
+
+ nframes = nsamples / samples_per_frame;
+ PJ_ASSERT_RETURN(nframes <= MAX_FRAMES_PER_PACKET,
+ PJMEDIA_CODEC_EFRMTOOSHORT);
+
+ /* Encode the frames */
+ speech = (pj_int16_t*)input->buf;
+ bitstream = (unsigned char*)output->buf;
+ while (nsamples >= samples_per_frame) {
+ size = Encoder_Interface_Encode (amr_data->encoder, amr_data->enc_mode,
+ speech, bitstream, 0);
+ if (size == 0) {
+ output->size = 0;
+ output->buf = NULL;
+ output->type = PJMEDIA_FRAME_TYPE_NONE;
+ TRACE_((THIS_FILE, "AMR-NB encode() failed"));
+ return PJMEDIA_CODEC_EFAILED;
+ }
+ nsamples -= 160;
+ speech += samples_per_frame;
+ bitstream += size;
+ out_size += size;
+ TRACE_((THIS_FILE, "AMR-NB encode(): mode=%d, size=%d",
+ amr_data->enc_mode, out_size));
+ }
+
+ /* Pack payload */
+ p = (pj_uint8_t*)output->buf + output_buf_len - out_size;
+ pj_memmove(p, output->buf, out_size);
+ dtx_cnt = sid_cnt = 0;
+ for (i = 0; i < nframes; ++i) {
+ pjmedia_codec_amr_bit_info *info = (pjmedia_codec_amr_bit_info*)
+ &frames[i].bit_info;
+ info->frame_type = (pj_uint8_t)((*p >> 3) & 0x0F);
+ info->good_quality = (pj_uint8_t)((*p >> 2) & 0x01);
+ info->mode = (pj_int8_t)amr_data->enc_mode;
+ info->start_bit = 0;
+ frames[i].buf = p + 1;
+ frames[i].size = (info->frame_type <= 8)?
+ pjmedia_codec_amrnb_framelen[info->frame_type] : 0;
+ p += frames[i].size + 1;
+
+ /* Count the number of SID and DTX frames */
+ if (info->frame_type == 15) /* DTX*/
+ ++dtx_cnt;
+ else if (info->frame_type == 8) /* SID */
+ ++sid_cnt;
+ }
+
+ /* VA generates DTX frames as DTX+SID frames switching quickly and it
+ * seems that the SID frames occur too often (assuming the purpose is
+ * only for keeping NAT alive?). So let's modify the behavior a bit.
+ * Only an SID frame will be sent every PJMEDIA_CODEC_MAX_SILENCE_PERIOD
+ * milliseconds.
+ */
+ if (sid_cnt + dtx_cnt == nframes) {
+ pj_int32_t dtx_duration;
+
+ dtx_duration = pj_timestamp_diff32(&amr_data->last_tx,
+ &input->timestamp);
+ if (PJMEDIA_CODEC_MAX_SILENCE_PERIOD == -1 ||
+ dtx_duration < PJMEDIA_CODEC_MAX_SILENCE_PERIOD*8000/1000)
+ {
+ output->size = 0;
+ output->type = PJMEDIA_FRAME_TYPE_NONE;
+ output->timestamp = input->timestamp;
+ return PJ_SUCCESS;
+ }
+ }
+
+ payload_len = output_buf_len;
+
+ status = pjmedia_codec_amr_pack(frames, nframes, &amr_data->enc_setting,
+ output->buf, &payload_len);
+ if (status != PJ_SUCCESS) {
+ output->size = 0;
+ output->buf = NULL;
+ output->type = PJMEDIA_FRAME_TYPE_NONE;
+ TRACE_((THIS_FILE, "Failed to pack AMR payload, status=%d", status));
+ return status;
+ }
+
+ output->size = payload_len;
+ output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ output->timestamp = input->timestamp;
+
+ amr_data->last_tx = input->timestamp;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Decode frame.
+ */
+static pj_status_t amr_codec_decode( pjmedia_codec *codec,
+ const struct pjmedia_frame *input,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output)
+{
+ struct amr_data *amr_data = (struct amr_data*) codec->codec_data;
+ pjmedia_frame input_;
+ pjmedia_codec_amr_bit_info *info;
+ /* VA AMR-NB decoding buffer: AMR-NB max frame size + 1 byte header. */
+ unsigned char bitstream[32];
+
+ pj_assert(amr_data != NULL);
+ PJ_ASSERT_RETURN(input && output, PJ_EINVAL);
+
+ if (output_buf_len < 320)
+ return PJMEDIA_CODEC_EPCMTOOSHORT;
+
+ input_.buf = &bitstream[1];
+ input_.size = 31; /* AMR-NB max frame size */
+ pjmedia_codec_amr_predecode(input, &amr_data->dec_setting, &input_);
+ info = (pjmedia_codec_amr_bit_info*)&input_.bit_info;
+
+ /* VA AMRNB decoder requires frame info in the first byte. */
+ bitstream[0] = (info->frame_type << 3) | (info->good_quality << 2);
+
+ TRACE_((THIS_FILE, "AMR-NB decode(): mode=%d, ft=%d, size=%d",
+ info->mode, info->frame_type, input_.size));
+
+ /* Decode */
+ Decoder_Interface_Decode(amr_data->decoder, bitstream,
+ (pj_int16_t*)output->buf, 0);
+
+ output->size = 320;
+ output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+ output->timestamp = input->timestamp;
+
+#if USE_PJMEDIA_PLC
+ if (amr_data->plc_enabled)
+ pjmedia_plc_save(amr_data->plc, (pj_int16_t*)output->buf);
+#endif
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Recover lost frame.
+ */
+#if USE_PJMEDIA_PLC
+/*
+ * Recover lost frame.
+ */
+static pj_status_t amr_codec_recover( pjmedia_codec *codec,
+ unsigned output_buf_len,
+ struct pjmedia_frame *output)
+{
+ struct amr_data *amr_data = codec->codec_data;
+
+ TRACE_((THIS_FILE, "amr_codec_recover"));
+
+ PJ_ASSERT_RETURN(amr_data->plc_enabled, PJ_EINVALIDOP);
+
+ PJ_ASSERT_RETURN(output_buf_len >= 320, PJMEDIA_CODEC_EPCMTOOSHORT);
+
+ pjmedia_plc_generate(amr_data->plc, (pj_int16_t*)output->buf);
+
+ output->size = 320;
+ output->type = PJMEDIA_FRAME_TYPE_AUDIO;
+
+ return PJ_SUCCESS;
+}
+#endif
+
+#if defined(_MSC_VER) && PJMEDIA_AUTO_LINK_OPENCORE_AMR_LIBS
+# if PJMEDIA_OPENCORE_AMR_BUILT_WITH_GCC
+# pragma comment( lib, "libopencore-amrnb.a")
+# else
+# error Unsupported OpenCORE AMR library, fix here
+# endif
+#endif
+
+#endif
diff --git a/pjmedia/src/pjmedia-codec/passthrough.c b/pjmedia/src/pjmedia-codec/passthrough.c
index 8ded8e2a..8fdd8271 100644
--- a/pjmedia/src/pjmedia-codec/passthrough.c
+++ b/pjmedia/src/pjmedia-codec/passthrough.c
@@ -267,6 +267,8 @@ static pj_status_t pack_amr ( codec_private_t *codec_data,
}
info->good_quality = 1;
info->mode = setting->enc_mode;
+ if (info->frame_type == SID_FT)
+ info->STI = (sf->data[4] >> 4) & 1;
frames[i].buf = sf->data;
frames[i].size = len;
diff --git a/pjmedia/src/pjmedia/alaw_ulaw.c b/pjmedia/src/pjmedia/alaw_ulaw.c
index 7bb5c77f..0a14de87 100644
--- a/pjmedia/src/pjmedia/alaw_ulaw.c
+++ b/pjmedia/src/pjmedia/alaw_ulaw.c
@@ -128,6 +128,12 @@ PJ_DEF(pj_uint8_t) pjmedia_linear2alaw(
} else {
mask = 0x55; /* sign bit = 0 */
pcm_val = -pcm_val - 8;
+
+ /* https://trac.pjsip.org/repos/ticket/1301
+ * Thank you K Johnson - Zetron - 27 May 2011
+ */
+ if (pcm_val < 0)
+ pcm_val = 0;
}
/* Convert the scaled magnitude to segment number. */
diff --git a/pjmedia/src/pjmedia/delaybuf.c b/pjmedia/src/pjmedia/delaybuf.c
index a54ec5de..9042ef99 100644
--- a/pjmedia/src/pjmedia/delaybuf.c
+++ b/pjmedia/src/pjmedia/delaybuf.c
@@ -100,9 +100,6 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
PJ_ASSERT_RETURN(pool && samples_per_frame && clock_rate && channel_count &&
p_b, PJ_EINVAL);
- PJ_ASSERT_RETURN(options==0, PJ_EINVAL);
-
- PJ_UNUSED_ARG(options);
if (!name) {
name = "delaybuf";
@@ -127,11 +124,16 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
if (status != PJ_SUCCESS)
return status;
- /* Create WSOLA */
- status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1,
- PJMEDIA_WSOLA_NO_FADING, &b->wsola);
- if (status != PJ_SUCCESS)
- return status;
+ if (!(options & PJMEDIA_DELAY_BUF_SIMPLE_FIFO)) {
+ /* Create WSOLA */
+ status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1,
+ PJMEDIA_WSOLA_NO_FADING, &b->wsola);
+ if (status != PJ_SUCCESS)
+ return status;
+ PJ_LOG(5, (b->obj_name, "Using delay buffer with WSOLA."));
+ } else {
+ PJ_LOG(5, (b->obj_name, "Using simple FIFO delay buffer."));
+ }
/* Finally, create mutex */
status = pj_lock_create_recursive_mutex(pool, b->obj_name,
@@ -148,15 +150,17 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
PJ_DEF(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b)
{
- pj_status_t status;
+ pj_status_t status = PJ_SUCCESS;
PJ_ASSERT_RETURN(b, PJ_EINVAL);
pj_lock_acquire(b->lock);
- status = pjmedia_wsola_destroy(b->wsola);
- if (status == PJ_SUCCESS)
- b->wsola = NULL;
+ if (b->wsola) {
+ status = pjmedia_wsola_destroy(b->wsola);
+ if (status == PJ_SUCCESS)
+ b->wsola = NULL;
+ }
pj_lock_release(b->lock);
@@ -265,12 +269,14 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
pj_lock_acquire(b->lock);
- update(b, OP_PUT);
+ if (b->wsola) {
+ update(b, OP_PUT);
- status = pjmedia_wsola_save(b->wsola, frame, PJ_FALSE);
- if (status != PJ_SUCCESS) {
- pj_lock_release(b->lock);
- return status;
+ status = pjmedia_wsola_save(b->wsola, frame, PJ_FALSE);
+ if (status != PJ_SUCCESS) {
+ pj_lock_release(b->lock);
+ return status;
+ }
}
/* Overflow checking */
@@ -278,13 +284,15 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
b->max_cnt)
{
unsigned erase_cnt;
-
- /* shrink one frame or just the diff? */
- //erase_cnt = b->samples_per_frame;
- erase_cnt = pjmedia_circ_buf_get_len(b->circ_buf) +
- b->samples_per_frame - b->max_cnt;
- shrink_buffer(b, erase_cnt);
+ if (b->wsola) {
+ /* shrink one frame or just the diff? */
+ //erase_cnt = b->samples_per_frame;
+ erase_cnt = pjmedia_circ_buf_get_len(b->circ_buf) +
+ b->samples_per_frame - b->max_cnt;
+
+ shrink_buffer(b, erase_cnt);
+ }
/* Check if shrinking failed or erased count is less than requested,
* delaybuf needs to drop eldest samples, this is bad since the voice
@@ -298,9 +306,9 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
pjmedia_circ_buf_adv_read_ptr(b->circ_buf, erase_cnt);
- PJ_LOG(4,(b->obj_name,"Shrinking failed or insufficient, dropping"
- " %d eldest samples, buf_cnt=%d", erase_cnt,
- pjmedia_circ_buf_get_len(b->circ_buf)));
+ PJ_LOG(4,(b->obj_name,"%sDropping %d eldest samples, buf_cnt=%d",
+ (b->wsola? "Shrinking failed or insufficient. ": ""),
+ erase_cnt, pjmedia_circ_buf_get_len(b->circ_buf)));
}
}
@@ -313,13 +321,14 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
pj_int16_t frame[])
{
- pj_status_t status;
+ pj_status_t status = PJ_SUCCESS;
PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
pj_lock_acquire(b->lock);
- update(b, OP_GET);
+ if (b->wsola)
+ update(b, OP_GET);
/* Starvation checking */
if (pjmedia_circ_buf_get_len(b->circ_buf) < b->samples_per_frame) {
@@ -327,24 +336,29 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
PJ_LOG(4,(b->obj_name,"Underflow, buf_cnt=%d, will generate 1 frame",
pjmedia_circ_buf_get_len(b->circ_buf)));
- status = pjmedia_wsola_generate(b->wsola, frame);
+ if (b->wsola) {
+ status = pjmedia_wsola_generate(b->wsola, frame);
- if (status == PJ_SUCCESS) {
- TRACE__((b->obj_name,"Successfully generate 1 frame"));
- if (pjmedia_circ_buf_get_len(b->circ_buf) == 0) {
- pj_lock_release(b->lock);
- return PJ_SUCCESS;
- }
+ if (status == PJ_SUCCESS) {
+ TRACE__((b->obj_name,"Successfully generate 1 frame"));
+ if (pjmedia_circ_buf_get_len(b->circ_buf) == 0) {
+ pj_lock_release(b->lock);
+ return PJ_SUCCESS;
+ }
- /* Put generated frame into buffer */
- pjmedia_circ_buf_write(b->circ_buf, frame, b->samples_per_frame);
+ /* Put generated frame into buffer */
+ pjmedia_circ_buf_write(b->circ_buf, frame,
+ b->samples_per_frame);
+ }
+ }
- } else {
+ if (!b->wsola || status != PJ_SUCCESS) {
unsigned buf_len = pjmedia_circ_buf_get_len(b->circ_buf);
/* Give all what delay buffer has, then pad with zeroes */
- PJ_LOG(4,(b->obj_name,"Error generating frame, status=%d",
- status));
+ if (b->wsola)
+ PJ_LOG(4,(b->obj_name,"Error generating frame, status=%d",
+ status));
pjmedia_circ_buf_read(b->circ_buf, frame, buf_len);
pjmedia_zero_samples(&frame[buf_len],
@@ -379,7 +393,8 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
pjmedia_circ_buf_reset(b->circ_buf);
/* Reset WSOLA */
- pjmedia_wsola_reset(b->wsola, 0);
+ if (b->wsola)
+ pjmedia_wsola_reset(b->wsola, 0);
pj_lock_release(b->lock);
diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c
index 7316dfe8..191a80f1 100644
--- a/pjmedia/src/pjmedia/echo_common.c
+++ b/pjmedia/src/pjmedia/echo_common.c
@@ -145,6 +145,7 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
pjmedia_echo_state **p_echo )
{
unsigned ptime, lat_cnt;
+ unsigned delay_buf_opt = 0;
pjmedia_echo_state *ec;
pj_status_t status;
@@ -212,10 +213,12 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
}
/* Create delay buffer to compensate drifts */
+ if (options & PJMEDIA_ECHO_USE_SIMPLE_FIFO)
+ delay_buf_opt |= PJMEDIA_DELAY_BUF_SIMPLE_FIFO;
status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate,
samples_per_frame, channel_count,
(PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime,
- 0, &ec->delay_buf);
+ delay_buf_opt, &ec->delay_buf);
if (status != PJ_SUCCESS) {
pj_pool_release(pool);
return status;
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index 52a3b0eb..94a88c15 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -32,14 +32,6 @@
#define THIS_FILE "jbuf.c"
-/* Minimal difference between JB size and 2*burst-level to perform
- * JB shrinking.
- */
-#define SAFE_SHRINKING_DIFF 1
-
-/* Minimal gap (in ms) between JB shrinking */
-#define MIN_SHRINK_GAP_MSEC 200
-
/* Invalid sequence number, used as the initial value. */
#define INVALID_OFFSET -9999
@@ -54,6 +46,12 @@
#define INIT_CYCLE 10
+/* Minimal difference between JB size and 2*burst-level to perform
+ * JB shrinking in static discard algorithm.
+ */
+#define STA_DISC_SAFE_SHRINKING_DIFF 1
+
+
/* Struct of JB internal buffer, represented in a circular buffer containing
* frame content, frame type, frame length, and frame bit info.
*/
@@ -82,6 +80,11 @@ typedef struct jb_framelist_t
} jb_framelist_t;
+typedef void (*discard_algo)(pjmedia_jbuf *jb);
+static void jbuf_discard_static(pjmedia_jbuf *jb);
+static void jbuf_discard_progressive(pjmedia_jbuf *jb);
+
+
struct pjmedia_jbuf
{
/* Settings (consts) */
@@ -98,6 +101,7 @@ struct pjmedia_jbuf
won't be included in level
calculation */
int jb_min_shrink_gap; /**< How often can we shrink */
+ discard_algo jb_discard_algo; /**< Discard algorithm */
/* Buffer */
jb_framelist_t jb_framelist; /**< the buffer */
@@ -120,13 +124,16 @@ struct pjmedia_jbuf
operation), the value may be
continuously updated based on
current frame burst level. */
+ pj_bool_t jb_prefetching; /**< flag if jbuf is prefetching. */
int jb_status; /**< status is 'init' until the first
'put' operation */
int jb_init_cycle_cnt; /**< status is 'init' until the first
'put' operation */
- int jb_last_del_seq; /**< Seq # of last frame deleted */
- int jb_last_discard_seq;/**< Seq # of last frame discarded */
+ int jb_discard_ref; /**< Seq # of last frame deleted or
+ discarded */
+ unsigned jb_discard_dist; /**< Distance from jb_discard_ref
+ to perform discard (in frm) */
/* Statistics */
pj_math_stat jb_delay; /**< Delay statistics of jitter buffer
@@ -141,7 +148,6 @@ struct pjmedia_jbuf
#define JB_STATUS_INITIALIZING 0
#define JB_STATUS_PROCESSING 1
-#define JB_STATUS_PREFETCHING 2
@@ -451,7 +457,7 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
enum { MAX_MISORDER = 100 };
enum { MAX_DROPOUT = 3000 };
- assert(frame_size <= framelist->frame_size);
+ PJ_ASSERT_RETURN(frame_size <= framelist->frame_size, PJ_EINVAL);
/* too late or sequence restart */
if (index < framelist->origin) {
@@ -507,15 +513,32 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
/* copy frame content */
pj_memcpy(framelist->content + pos * framelist->frame_size,
frame, frame_size);
- return PJ_SUCCESS;
- } else {
- /* frame is being discarded */
- framelist->discarded_num++;
- return PJ_EIGNORED;
}
+
+ return PJ_SUCCESS;
}
+static pj_status_t jb_framelist_discard(jb_framelist_t *framelist,
+ int index)
+{
+ unsigned pos;
+
+ PJ_ASSERT_RETURN(index >= framelist->origin &&
+ index < framelist->origin + (int)framelist->size,
+ PJ_EINVAL);
+
+ /* Get the slot position */
+ pos = (framelist->head + (index - framelist->origin)) %
+ framelist->max_count;
+
+ /* Discard the frame */
+ framelist->frame_type[pos] = PJMEDIA_JB_DISCARDED_FRAME;
+ framelist->discarded_num++;
+
+ return PJ_SUCCESS;
+}
+
enum pjmedia_jb_op
{
@@ -548,13 +571,13 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool,
jb->jb_min_prefetch = 0;
jb->jb_max_prefetch = max_count*4/5;
jb->jb_max_count = max_count;
- jb->jb_min_shrink_gap= MIN_SHRINK_GAP_MSEC / ptime;
+ jb->jb_min_shrink_gap= PJMEDIA_JBUF_DISC_MIN_GAP / ptime;
jb->jb_max_burst = PJ_MAX(MAX_BURST_MSEC / ptime, max_count*3/4);
- jb->jb_last_discard_seq = 0;
pj_math_stat_init(&jb->jb_delay);
pj_math_stat_init(&jb->jb_burst);
+ pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_PROGRESSIVE);
pjmedia_jbuf_reset(jb);
*p_jb = jb;
@@ -589,7 +612,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb,
unsigned max_prefetch)
{
PJ_ASSERT_RETURN(jb, PJ_EINVAL);
- PJ_ASSERT_RETURN(min_prefetch < max_prefetch &&
+ PJ_ASSERT_RETURN(min_prefetch <= max_prefetch &&
prefetch <= max_prefetch &&
max_prefetch <= jb->jb_max_count,
PJ_EINVAL);
@@ -602,6 +625,30 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb,
}
+PJ_DEF(pj_status_t) pjmedia_jbuf_set_discard( pjmedia_jbuf *jb,
+ pjmedia_jb_discard_algo algo)
+{
+ PJ_ASSERT_RETURN(jb, PJ_EINVAL);
+ PJ_ASSERT_RETURN(algo >= PJMEDIA_JB_DISCARD_NONE &&
+ algo <= PJMEDIA_JB_DISCARD_PROGRESSIVE,
+ PJ_EINVAL);
+
+ switch(algo) {
+ case PJMEDIA_JB_DISCARD_PROGRESSIVE:
+ jb->jb_discard_algo = &jbuf_discard_progressive;
+ break;
+ case PJMEDIA_JB_DISCARD_STATIC:
+ jb->jb_discard_algo = &jbuf_discard_static;
+ break;
+ default:
+ jb->jb_discard_algo = NULL;
+ break;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb)
{
jb->jb_level = 0;
@@ -610,6 +657,8 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb)
jb->jb_status = JB_STATUS_INITIALIZING;
jb->jb_init_cycle_cnt= 0;
jb->jb_max_hist_level= 0;
+ jb->jb_prefetching = (jb->jb_prefetch != 0);
+ jb->jb_discard_dist = 0;
jb_framelist_reset(&jb->jb_framelist);
@@ -621,11 +670,13 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb)
{
PJ_LOG(5, (jb->jb_name.ptr, ""
"JB summary:\n"
- " size=%d prefetch=%d level=%d\n"
+ " size=%d/eff=%d prefetch=%d level=%d\n"
" delay (min/max/avg/dev)=%d/%d/%d/%d ms\n"
" burst (min/max/avg/dev)=%d/%d/%d/%d frames\n"
" lost=%d discard=%d empty=%d",
- jb->jb_framelist.size, jb->jb_prefetch, jb->jb_eff_level,
+ jb_framelist_size(&jb->jb_framelist),
+ jb_framelist_eff_size(&jb->jb_framelist),
+ jb->jb_prefetch, jb->jb_eff_level,
jb->jb_delay.min, jb->jb_delay.max, jb->jb_delay.mean,
pj_math_stat_get_stddev(&jb->jb_delay),
jb->jb_burst.min, jb->jb_burst.max, jb->jb_burst.mean,
@@ -712,6 +763,136 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb)
}
}
+
+static void jbuf_discard_static(pjmedia_jbuf *jb)
+{
+ /* These code is used for shortening the delay in the jitter buffer.
+ * It needs shrink only when there is possibility of drift. Drift
+ * detection is performed by inspecting the jitter buffer size, if
+ * its size is twice of current burst level, there can be drift.
+ *
+ * Moreover, normally drift level is quite low, so JB shouldn't need
+ * to shrink aggresively, it will shrink maximum one frame per
+ * PJMEDIA_JBUF_DISC_MIN_GAP ms. Theoritically, JB may handle drift level
+ * as much as = FRAME_PTIME/PJMEDIA_JBUF_DISC_MIN_GAP * 100%
+ *
+ * Whenever there is drift, where PUT > GET, this method will keep
+ * the latency (JB size) as much as twice of burst level.
+ */
+
+ /* Shrinking due of drift will be implicitly done by progressive discard,
+ * so just disable it when progressive discard is active.
+ */
+ int diff, burst_level;
+
+ burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
+ diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2;
+
+ if (diff >= STA_DISC_SAFE_SHRINKING_DIFF) {
+ int seq_origin;
+
+ /* Check and adjust jb_discard_ref, in case there was
+ * seq restart
+ */
+ seq_origin = jb_framelist_origin(&jb->jb_framelist);
+ if (seq_origin < jb->jb_discard_ref)
+ jb->jb_discard_ref = seq_origin;
+
+ if (seq_origin - jb->jb_discard_ref >= jb->jb_min_shrink_gap)
+ {
+ /* Shrink slowly, one frame per cycle */
+ diff = 1;
+
+ /* Drop frame(s)! */
+ diff = jb_framelist_remove_head(&jb->jb_framelist, diff);
+ jb->jb_discard_ref = jb_framelist_origin(&jb->jb_framelist);
+ jb->jb_discard += diff;
+
+ TRACE__((jb->jb_name.ptr,
+ "JB shrinking %d frame(s), cur size=%d", diff,
+ jb_framelist_eff_size(&jb->jb_framelist)));
+ }
+ }
+}
+
+
+static void jbuf_discard_progressive(pjmedia_jbuf *jb)
+{
+ unsigned cur_size, burst_level, overflow, T, discard_dist;
+ int last_seq;
+
+ /* Should be done in PUT operation */
+ if (jb->jb_last_op != JB_OP_PUT)
+ return;
+
+ /* Check if latency is longer than burst */
+ cur_size = jb_framelist_eff_size(&jb->jb_framelist);
+ burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
+ if (cur_size <= burst_level) {
+ /* Reset any scheduled discard */
+ jb->jb_discard_dist = 0;
+ return;
+ }
+
+ /* Estimate discard duration needed for adjusting latency */
+ if (burst_level <= PJMEDIA_JBUF_PRO_DISC_MIN_BURST)
+ T = PJMEDIA_JBUF_PRO_DISC_T1;
+ else if (burst_level >= PJMEDIA_JBUF_PRO_DISC_MAX_BURST)
+ T = PJMEDIA_JBUF_PRO_DISC_T2;
+ else
+ T = PJMEDIA_JBUF_PRO_DISC_T1 +
+ (PJMEDIA_JBUF_PRO_DISC_T2 - PJMEDIA_JBUF_PRO_DISC_T1) *
+ (burst_level - PJMEDIA_JBUF_PRO_DISC_MIN_BURST) /
+ (PJMEDIA_JBUF_PRO_DISC_MAX_BURST-PJMEDIA_JBUF_PRO_DISC_MIN_BURST);
+
+ /* Calculate current discard distance */
+ overflow = cur_size - burst_level;
+ discard_dist = T / overflow / jb->jb_frame_ptime;
+
+ /* Get last seq number in the JB */
+ last_seq = jb_framelist_origin(&jb->jb_framelist) +
+ jb_framelist_size(&jb->jb_framelist) - 1;
+
+ /* Setup new discard schedule if none, otherwise, update the existing
+ * discard schedule (can be delayed or accelerated).
+ */
+ if (jb->jb_discard_dist == 0) {
+ /* Setup new discard schedule */
+ jb->jb_discard_ref = last_seq;
+ } else if (last_seq < jb->jb_discard_ref) {
+ /* Seq restarted, update discard reference */
+ jb->jb_discard_ref = last_seq;
+ }
+ jb->jb_discard_dist = PJ_MAX(jb->jb_min_shrink_gap, (int)discard_dist);
+
+ /* Check if we need to discard now */
+ if (last_seq >= (jb->jb_discard_ref + (int)jb->jb_discard_dist)) {
+ int discard_seq;
+
+ discard_seq = jb->jb_discard_ref + jb->jb_discard_dist;
+ if (discard_seq < jb_framelist_origin(&jb->jb_framelist))
+ discard_seq = jb_framelist_origin(&jb->jb_framelist);
+
+ jb_framelist_discard(&jb->jb_framelist, discard_seq);
+
+ TRACE__((jb->jb_name.ptr,
+ "Discard #%d: ref=#%d dist=%d orig=%d size=%d/%d "
+ "burst=%d/%d",
+ discard_seq,
+ jb->jb_discard_ref,
+ jb->jb_discard_dist,
+ jb_framelist_origin(&jb->jb_framelist),
+ cur_size,
+ jb_framelist_size(&jb->jb_framelist),
+ jb->jb_eff_level,
+ burst_level));
+
+ /* Update discard reference */
+ jb->jb_discard_ref = discard_seq;
+ }
+}
+
+
PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper)
{
if(jb->jb_last_op != oper) {
@@ -747,63 +928,10 @@ PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper)
jb->jb_level = 0;
}
- /* These code is used for shortening the delay in the jitter buffer.
- * It needs shrink only when there is possibility of drift. Drift
- * detection is performed by inspecting the jitter buffer size, if
- * its size is twice of current burst level, there can be drift.
- *
- * Moreover, normally drift level is quite low, so JB shouldn't need
- * to shrink aggresively, it will shrink maximum one frame per
- * MIN_SHRINK_GAP_MSEC ms. Theoritically, JB may handle drift level
- * as much as = FRAME_PTIME/MIN_SHRINK_GAP_MSEC * 100%
- *
- * Whenever there is drift, where PUT > GET, this method will keep
- * the latency (JB size) as much as twice of burst level.
- */
-
- /* Shrinking due of drift will be implicitly done by progressive discard,
- * so just disable it when progressive discard is active.
- */
-#if !PROGRESSIVE_DISCARD
-
- if (jb->jb_status != JB_STATUS_PROCESSING)
- return;
-
- {
- int diff, burst_level;
-
- burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
- diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2;
-
- if (diff >= SAFE_SHRINKING_DIFF) {
- int seq_origin;
-
- /* Check and adjust jb_last_del_seq, in case there was
- * seq restart
- */
- seq_origin = jb_framelist_origin(&jb->jb_framelist);
- if (seq_origin < jb->jb_last_del_seq)
- jb->jb_last_del_seq = seq_origin;
-
- if (seq_origin - jb->jb_last_del_seq >= jb->jb_min_shrink_gap)
- {
- /* Shrink slowly, one frame per cycle */
- diff = 1;
-
- /* Drop frame(s)! */
- diff = jb_framelist_remove_head(&jb->jb_framelist, diff);
- jb->jb_last_del_seq = jb_framelist_origin(&jb->jb_framelist);
- jb->jb_discard += diff;
-
- TRACE__((jb->jb_name.ptr,
- "JB shrinking %d frame(s), cur size=%d", diff,
- jb_framelist_eff_size(&jb->jb_framelist)));
- }
- }
+ /* Call discard algorithm */
+ if (jb->jb_status == JB_STATUS_PROCESSING && jb->jb_discard_algo) {
+ (*jb->jb_discard_algo)(jb);
}
-
-#endif /* !PROGRESSIVE_DISCARD */
-
}
PJ_DEF(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb,
@@ -834,96 +962,35 @@ PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb,
pj_bool_t *discarded)
{
pj_size_t min_frame_size;
- int new_size, cur_size, frame_type = PJMEDIA_JB_NORMAL_FRAME;
+ int new_size, cur_size;
pj_status_t status;
cur_size = jb_framelist_eff_size(&jb->jb_framelist);
-#if PROGRESSIVE_DISCARD
- {
- unsigned interval, seq_delta;
- unsigned burst_level, burst_factor;
-
- /* Calculating discard interval (aggressiveness) based on
- * (current size / burst level).
- */
- if (jb->jb_status == JB_STATUS_PROCESSING) {
- burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
- burst_factor = cur_size / burst_level;
- /* Tolerate small spikes */
- if ((burst_level <= 5) && (burst_factor < 3))
- burst_factor = 0;
- } else {
- burst_factor = 0;
- }
-
- switch (burst_factor) {
- case 0:
- interval = 0;
- break;
- case 1:
- interval = 7;
- break;
- case 2:
- interval = 5;
- break;
- default:
- interval = 4;
- break;
- }
-
- /* Do the math now to see if we should discard this packet.
- * Calculate the distance from the last sequence
- * discarded. If negative, then this is an out of
- * order frame so just proceed with discard. Else
- * see if the delta is at least the intervals worth away
- * from the last frame discarded.
- */
- seq_delta = (pj_uint16_t)(frame_seq - jb->jb_last_discard_seq);
- if ((0 != interval) && (seq_delta >= interval)) {
- frame_type = PJMEDIA_JB_DISCARDED_FRAME;
- jb->jb_last_discard_seq = frame_seq;
-
- TRACE__((jb->jb_name.ptr,
- "Discarding frame #%d: eff=%d disc=%d orig:%d"
- " seq_delta:%d",
- frame_seq,
- cur_size,
- jb_framelist_size(&jb->jb_framelist) - cur_size,
- jb_framelist_origin(&jb->jb_framelist),
- (int)seq_delta));
- }
- }
-#endif /* PROGRESSIVE_DISCARD */
-
-
/* Attempt to store the frame */
min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
- min_frame_size, bit_info, ts, frame_type);
+ min_frame_size, bit_info, ts,
+ PJMEDIA_JB_NORMAL_FRAME);
/* Jitter buffer is full, remove some older frames */
while (status == PJ_ETOOMANY) {
int distance;
unsigned removed;
- /* When progressive discard activated, just remove as few as possible
- * just to make this frame in.
- */
-#if PROGRESSIVE_DISCARD
- /* The cases of seq-jump, out-of-order, and seq restart should have
+ /* Remove as few as possible just to make this frame in. Note that
+ * the cases of seq-jump, out-of-order, and seq restart should have
* been handled/normalized by previous call of jb_framelist_put_at().
* So we're confident about 'distance' value here.
*/
distance = (frame_seq - jb_framelist_origin(&jb->jb_framelist)) -
jb->jb_max_count + 1;
pj_assert(distance > 0);
-#else
- distance = PJ_MAX(jb->jb_max_count/4, 1);
-#endif
+
removed = jb_framelist_remove_head(&jb->jb_framelist, distance);
status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
- min_frame_size, bit_info, ts, frame_type);
+ min_frame_size, bit_info, ts,
+ PJMEDIA_JB_NORMAL_FRAME);
jb->jb_discard += removed;
}
@@ -936,11 +1003,11 @@ PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb,
*discarded = (status != PJ_SUCCESS);
if (status == PJ_SUCCESS) {
- if (jb->jb_status == JB_STATUS_PREFETCHING) {
+ if (jb->jb_prefetching) {
TRACE__((jb->jb_name.ptr, "PUT prefetch_cnt=%d/%d",
new_size, jb->jb_prefetch));
if (new_size >= jb->jb_prefetch)
- jb->jb_status = JB_STATUS_PROCESSING;
+ jb->jb_prefetching = PJ_FALSE;
}
jb->jb_level += (new_size > cur_size ? new_size-cur_size : 1);
jbuf_update(jb, JB_OP_PUT);
@@ -983,7 +1050,7 @@ PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb,
pj_uint32_t *ts,
int *seq)
{
- if (jb->jb_status == JB_STATUS_PREFETCHING) {
+ if (jb->jb_prefetching) {
/* Can't return frame because jitter buffer is filling up
* minimum prefetch.
@@ -1030,7 +1097,7 @@ PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb,
} else {
/* Jitter buffer is empty */
if (jb->jb_prefetch)
- jb->jb_status = JB_STATUS_PREFETCHING;
+ jb->jb_prefetching = PJ_TRUE;
//pj_bzero(frame, jb->jb_frame_size);
*p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c
index 874f1d2d..d484a91a 100644
--- a/pjmedia/src/pjmedia/sdp_neg.c
+++ b/pjmedia/src/pjmedia/sdp_neg.c
@@ -1330,7 +1330,10 @@ static pj_status_t match_offer(pj_pool_t *pool,
if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
or_.clock_rate == lr.clock_rate &&
(pj_stricmp(&or_.param, &lr.param)==0 ||
- (or_.param.slen==1 && *or_.param.ptr=='1')))
+ (lr.param.slen==0 && or_.param.slen==1 &&
+ *or_.param.ptr=='1') ||
+ (or_.param.slen==0 && lr.param.slen==1 &&
+ *lr.param.ptr=='1')))
{
/* Match! */
if (is_codec) {
diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c
index bc1d1754..43767790 100644
--- a/pjmedia/src/pjmedia/session.c
+++ b/pjmedia/src/pjmedia/session.c
@@ -273,6 +273,40 @@ PJ_DEF(pj_status_t) pjmedia_session_resume_stream( pjmedia_session *session,
}
/**
+ * Send RTCP SDES for the session.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_session_send_rtcp_sdes( const pjmedia_session *session )
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(session, PJ_EINVAL);
+
+ for (i=0; i<session->stream_cnt; ++i) {
+ pjmedia_stream_send_rtcp_sdes(session->stream[i]);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/**
+ * Send RTCP BYE for the session.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_session_send_rtcp_bye( const pjmedia_session *session )
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(session, PJ_EINVAL);
+
+ for (i=0; i<session->stream_cnt; ++i) {
+ pjmedia_stream_send_rtcp_bye(session->stream[i]);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/**
* Enumerate media stream in the session.
*/
PJ_DEF(pj_status_t) pjmedia_session_enum_streams(const pjmedia_session *session,
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 8534caa8..4022c36a 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -154,6 +154,7 @@ struct pjmedia_stream
pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */
pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */
pj_bool_t initial_rr; /**< Initial RTCP RR sent */
+ pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/
/* RFC 2833 DTMF transmission queue: */
int tx_event_pt; /**< Outgoing pt for dtmf. */
@@ -1824,19 +1825,24 @@ on_return:
/* Build RR or SR */
pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
- pkt = (pj_uint8_t*) stream->enc->out_pkt;
- pj_memcpy(pkt, sr_rr_pkt, len);
- pkt += len;
-
- /* Append SDES */
- len = create_rtcp_sdes(stream, (pj_uint8_t*)pkt,
- stream->enc->out_pkt_size - len);
- if (len > 0) {
- pkt += len;
- len = ((pj_uint8_t*)pkt) - ((pj_uint8_t*)stream->enc->out_pkt);
- pjmedia_transport_send_rtcp(stream->transport,
- stream->enc->out_pkt, len);
- }
+
+ if (!stream->rtcp_sdes_bye_disabled) {
+ pkt = (pj_uint8_t*) stream->enc->out_pkt;
+ pj_memcpy(pkt, sr_rr_pkt, len);
+ pkt += len;
+
+ /* Append SDES */
+ len = create_rtcp_sdes(stream, (pj_uint8_t*)pkt,
+ stream->enc->out_pkt_size - len);
+ if (len > 0) {
+ pkt += len;
+ len = ((pj_uint8_t*)pkt) - ((pj_uint8_t*)stream->enc->out_pkt);
+ pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->out_pkt, len);
+ }
+ } else {
+ pjmedia_transport_send_rtcp(stream->transport, sr_rr_pkt, len);
+ }
stream->initial_rr = PJ_TRUE;
}
@@ -1959,7 +1965,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
enum { M = 32 };
pjmedia_stream *stream;
pj_str_t name;
- unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+ unsigned jb_init, jb_max, jb_min_pre, jb_max_pre;
pjmedia_audio_format_detail *afd;
pj_pool_t *own_pool = NULL;
char *p;
@@ -2010,6 +2016,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
stream->user_data = user_data;
stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) *
info->fmt.clock_rate / 1000;
+ stream->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled;
stream->tx_event_pt = info->tx_event_pt ? info->tx_event_pt : -1;
stream->rx_event_pt = info->rx_event_pt ? info->rx_event_pt : -1;
@@ -2322,11 +2329,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
#endif
/* Send RTCP SDES */
- len = create_rtcp_sdes(stream, (pj_uint8_t*)stream->enc->out_pkt,
- stream->enc->out_pkt_size);
- if (len != 0) {
- pjmedia_transport_send_rtcp(stream->transport,
- stream->enc->out_pkt, len);
+ if (!stream->rtcp_sdes_bye_disabled) {
+ pjmedia_stream_send_rtcp_sdes(stream);
}
#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
@@ -2343,7 +2347,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
pj_ansi_snprintf(trace_name, sizeof(trace_name),
TRACE_JB_PATH_PREFIX "%s.csv",
stream->port.info.name.ptr);
- status = pj_file_open(pool, trace_name, PJ_O_RDWR, &stream->trace_jb_fd);
+ status = pj_file_open(pool, trace_name, PJ_O_WRONLY, &stream->trace_jb_fd);
if (status != PJ_SUCCESS) {
stream->trace_jb_fd = TRACE_JB_INVALID_FD;
PJ_LOG(3,(THIS_FILE, "Failed creating RTP trace file '%s'",
@@ -2381,7 +2385,6 @@ err_cleanup:
*/
PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
{
- unsigned len;
PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
@@ -2423,13 +2426,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
#endif
/* Send RTCP BYE */
- if (stream->enc && stream->transport) {
- len = create_rtcp_bye(stream, (pj_uint8_t*)stream->enc->out_pkt,
- stream->enc->out_pkt_size);
- if (len != 0) {
- pjmedia_transport_send_rtcp(stream->transport,
- stream->enc->out_pkt, len);
- }
+ if (!stream->rtcp_sdes_bye_disabled) {
+ pjmedia_stream_send_rtcp_bye(stream);
}
/* Detach from transport
@@ -3298,3 +3296,45 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
return status;
}
+
+/*
+ * Send RTCP SDES.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_stream_send_rtcp_sdes( pjmedia_stream *stream )
+{
+ unsigned len;
+
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ len = create_rtcp_sdes(stream, (pj_uint8_t*)stream->enc->out_pkt,
+ stream->enc->out_pkt_size);
+ if (len != 0) {
+ return pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->out_pkt, len);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Send RTCP BYE.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_stream_send_rtcp_bye( pjmedia_stream *stream )
+{
+ PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+ if (stream->enc && stream->transport) {
+ unsigned len;
+
+ len = create_rtcp_bye(stream, (pj_uint8_t*)stream->enc->out_pkt,
+ stream->enc->out_pkt_size);
+ if (len != 0) {
+ return pjmedia_transport_send_rtcp(stream->transport,
+ stream->enc->out_pkt, len);
+ }
+ }
+
+ return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/transport_adapter_sample.c b/pjmedia/src/pjmedia/transport_adapter_sample.c
index c118c108..a076bdab 100644
--- a/pjmedia/src/pjmedia/transport_adapter_sample.c
+++ b/pjmedia/src/pjmedia/transport_adapter_sample.c
@@ -94,6 +94,7 @@ static struct pjmedia_transport_op tp_adapter_op =
struct tp_adapter
{
pjmedia_transport base;
+ pj_bool_t del_base;
pj_pool_t *pool;
@@ -118,6 +119,7 @@ struct tp_adapter
PJ_DEF(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt,
const char *name,
pjmedia_transport *transport,
+ pj_bool_t del_base,
pjmedia_transport **p_tp)
{
pj_pool_t *pool;
@@ -138,6 +140,7 @@ PJ_DEF(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt,
/* Save the transport as the slave transport */
adapter->slave_tp = transport;
+ adapter->del_base = del_base;
/* Done */
*p_tp = &adapter->base;
@@ -421,7 +424,9 @@ static pj_status_t transport_destroy (pjmedia_transport *tp)
struct tp_adapter *adapter = (struct tp_adapter*)tp;
/* Close the slave transport */
- pjmedia_transport_close(adapter->slave_tp);
+ if (adapter->del_base) {
+ pjmedia_transport_close(adapter->slave_tp);
+ }
/* Self destruct.. */
pj_pool_release(adapter->pool);
diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c
index 54ae0324..38bd70d4 100644
--- a/pjmedia/src/pjmedia/transport_ice.c
+++ b/pjmedia/src/pjmedia/transport_ice.c
@@ -72,6 +72,7 @@ struct transport_ice
pj_sockaddr rtp_src_addr; /**< Actual source RTP address. */
pj_sockaddr rtcp_src_addr; /**< Actual source RTCP address. */
unsigned rtp_src_cnt; /**< How many pkt from this addr. */
+ unsigned rtcp_src_cnt; /**< How many pkt from this addr. */
unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */
unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */
@@ -1516,6 +1517,8 @@ static pj_status_t transport_get_info(pjmedia_transport *tp,
*/
if (tp_ice->use_ice || tp_ice->rtp_src_cnt) {
info->src_rtp_name = tp_ice->rtp_src_addr;
+ }
+ if (tp_ice->use_ice || tp_ice->rtcp_src_cnt) {
info->src_rtcp_name = tp_ice->rtcp_src_addr;
}
@@ -1581,6 +1584,7 @@ static pj_status_t transport_attach (pjmedia_transport *tp,
tp_ice->rtp_src_addr = tp_ice->remote_rtp;
tp_ice->rtcp_src_addr = tp_ice->remote_rtcp;
tp_ice->rtp_src_cnt = 0;
+ tp_ice->rtcp_src_cnt = 0;
return PJ_SUCCESS;
}
@@ -1656,6 +1660,7 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
unsigned src_addr_len)
{
struct transport_ice *tp_ice;
+ pj_bool_t discard = PJ_FALSE;
tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st);
@@ -1671,20 +1676,23 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
}
}
- (*tp_ice->rtp_cb)(tp_ice->stream, pkt, size);
-
/* See if source address of RTP packet is different than the
* configured address, and switch RTP remote address to
* source packet address after several consecutive packets
* have been received.
*/
if (!tp_ice->use_ice) {
+ pj_bool_t enable_switch =
+ ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0);
- /* Increment counter and avoid zero */
- if (++tp_ice->rtp_src_cnt == 0)
- tp_ice->rtp_src_cnt = 1;
+ if (!enable_switch ||
+ pj_sockaddr_cmp(&tp_ice->remote_rtp, src_addr) == 0)
+ {
+ /* Don't switch while we're receiving from remote_rtp */
+ tp_ice->rtp_src_cnt = 0;
+ } else {
- if (pj_sockaddr_cmp(&tp_ice->remote_rtp, src_addr) != 0) {
+ ++tp_ice->rtp_src_cnt;
/* Check if the source address is recognized. */
if (pj_sockaddr_cmp(src_addr, &tp_ice->rtp_src_addr) != 0) {
@@ -1692,11 +1700,12 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
pj_sockaddr_cp(&tp_ice->rtp_src_addr, src_addr);
/* Reset counter */
tp_ice->rtp_src_cnt = 0;
+ discard = PJ_TRUE;
}
- if ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0 &&
- tp_ice->rtp_src_cnt >= PJMEDIA_RTP_NAT_PROBATION_CNT)
- {
+ if (tp_ice->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) {
+ discard = PJ_TRUE;
+ } else {
char addr_text[80];
/* Set remote RTP address to source address */
@@ -1725,7 +1734,8 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
pj_sockaddr_set_port(&tp_ice->remote_rtcp, port);
PJ_LOG(4,(tp_ice->base.name,
- "Remote RTCP address switched to %s",
+ "Remote RTCP address switched to predicted "
+ "address %s",
pj_sockaddr_print(&tp_ice->remote_rtcp,
addr_text,
sizeof(addr_text), 3)));
@@ -1733,31 +1743,45 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
}
}
}
+
+ if (!discard)
+ (*tp_ice->rtp_cb)(tp_ice->stream, pkt, size);
+
} else if (comp_id==2 && tp_ice->rtcp_cb) {
- (*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size);
/* Check if RTCP source address is the same as the configured
* remote address, and switch the address when they are
* different.
*/
if (!tp_ice->use_ice &&
- pj_sockaddr_cmp(&tp_ice->remote_rtcp, src_addr) != 0)
+ (tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0)
{
- pj_sockaddr_cp(&tp_ice->rtcp_src_addr, src_addr);
-
- if ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0) {
+ if (pj_sockaddr_cmp(&tp_ice->remote_rtcp, src_addr) == 0) {
+ tp_ice->rtcp_src_cnt = 0;
+ } else {
char addr_text[80];
- pj_sockaddr_cp(&tp_ice->remote_rtcp, src_addr);
+ ++tp_ice->rtcp_src_cnt;
+ if (tp_ice->rtcp_src_cnt < PJMEDIA_RTCP_NAT_PROBATION_CNT) {
+ discard = PJ_TRUE;
+ } else {
+ tp_ice->rtcp_src_cnt = 0;
+ pj_sockaddr_cp(&tp_ice->rtcp_src_addr, src_addr);
+ pj_sockaddr_cp(&tp_ice->remote_rtcp, src_addr);
- pj_assert(tp_ice->addr_len == pj_sockaddr_get_len(src_addr));
+ pj_assert(tp_ice->addr_len==pj_sockaddr_get_len(src_addr));
- PJ_LOG(4,(tp_ice->base.name,
- "Remote RTCP address switched to %s",
- pj_sockaddr_print(&tp_ice->remote_rtcp, addr_text,
- sizeof(addr_text), 3)));
+ PJ_LOG(4,(tp_ice->base.name,
+ "Remote RTCP address switched to %s",
+ pj_sockaddr_print(&tp_ice->remote_rtcp,
+ addr_text, sizeof(addr_text),
+ 3)));
+ }
}
}
+
+ if (!discard)
+ (*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size);
}
PJ_UNUSED_ARG(src_addr_len);
diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c
index 59d2dc70..63178599 100644
--- a/pjmedia/src/pjmedia/transport_udp.c
+++ b/pjmedia/src/pjmedia/transport_udp.c
@@ -83,6 +83,7 @@ struct transport_udp
pj_sock_t rtcp_sock; /**< RTCP socket */
pj_sockaddr rtcp_addr_name; /**< Published RTCP address. */
pj_sockaddr rtcp_src_addr; /**< Actual source RTCP address. */
+ unsigned rtcp_src_cnt; /**< How many pkt from this addr. */
int rtcp_addr_len; /**< Length of RTCP src address. */
pj_ioqueue_key_t *rtcp_key; /**< RTCP socket key in ioqueue */
pj_ioqueue_op_key_t rtcp_read_op; /**< Pending read operation */
@@ -452,6 +453,7 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
do {
void (*cb)(void*,void*,pj_ssize_t);
void *user_data;
+ pj_bool_t discard = PJ_FALSE;
cb = udp->rtp_cb;
user_data = udp->user_data;
@@ -462,14 +464,10 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
PJ_LOG(5,(udp->base.name,
"RX RTP packet dropped because of pkt lost "
"simulation"));
- goto read_next_packet;
+ discard = PJ_TRUE;
}
}
-
- if (udp->attached && cb)
- (*cb)(user_data, udp->rtp_pkt, bytes_read);
-
/* See if source address of RTP packet is different than the
* configured address, and switch RTP remote address to
* source packet address after several consecutive packets
@@ -478,11 +476,15 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
if (bytes_read>0 &&
(udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0)
{
- if (pj_sockaddr_cmp(&udp->rem_rtp_addr, &udp->rtp_src_addr) != 0) {
-
+ if (pj_sockaddr_cmp(&udp->rem_rtp_addr, &udp->rtp_src_addr) == 0) {
+ /* We're still receiving from rem_rtp_addr. Don't switch. */
+ udp->rtp_src_cnt = 0;
+ } else {
udp->rtp_src_cnt++;
- if (udp->rtp_src_cnt >= PJMEDIA_RTP_NAT_PROBATION_CNT) {
+ if (udp->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) {
+ discard = PJ_TRUE;
+ } else {
char addr_text[80];
@@ -516,7 +518,8 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
sizeof(pj_sockaddr));
PJ_LOG(4,(udp->base.name,
- "Remote RTCP address switched to %s",
+ "Remote RTCP address switched to predicted"
+ " address %s",
pj_sockaddr_print(&udp->rtcp_src_addr,
addr_text,
sizeof(addr_text), 3)));
@@ -526,7 +529,9 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
}
}
-read_next_packet:
+ if (!discard && udp->attached && cb)
+ (*cb)(user_data, udp->rtp_pkt, bytes_read);
+
bytes_read = sizeof(udp->rtp_pkt);
udp->rtp_addrlen = sizeof(udp->rtp_src_addr);
status = pj_ioqueue_recvfrom(udp->rtp_key, &udp->rtp_read_op,
@@ -568,18 +573,27 @@ static void on_rx_rtcp(pj_ioqueue_key_t *key,
* different.
*/
if (bytes_read>0 &&
- (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0 &&
- pj_sockaddr_cmp(&udp->rem_rtcp_addr, &udp->rtcp_src_addr) != 0)
+ (udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0)
{
- char addr_text[80];
+ if (pj_sockaddr_cmp(&udp->rem_rtcp_addr, &udp->rtcp_src_addr) == 0) {
+ /* Still receiving from rem_rtcp_addr, don't switch */
+ udp->rtcp_src_cnt = 0;
+ } else {
+ ++udp->rtcp_src_cnt;
- pj_memcpy(&udp->rem_rtcp_addr, &udp->rtcp_src_addr,
- sizeof(pj_sockaddr));
+ if (udp->rtcp_src_cnt >= PJMEDIA_RTCP_NAT_PROBATION_CNT ) {
+ char addr_text[80];
+
+ udp->rtcp_src_cnt = 0;
+ pj_memcpy(&udp->rem_rtcp_addr, &udp->rtcp_src_addr,
+ sizeof(pj_sockaddr));
- PJ_LOG(4,(udp->base.name,
- "Remote RTCP address switched to %s",
- pj_sockaddr_print(&udp->rtcp_src_addr, addr_text,
- sizeof(addr_text), 3)));
+ PJ_LOG(4,(udp->base.name,
+ "Remote RTCP address switched to %s",
+ pj_sockaddr_print(&udp->rtcp_src_addr, addr_text,
+ sizeof(addr_text), 3)));
+ }
+ }
}
bytes_read = sizeof(udp->rtcp_pkt);
@@ -677,6 +691,7 @@ static pj_status_t transport_attach( pjmedia_transport *tp,
pj_bzero(&udp->rtp_src_addr, sizeof(udp->rtp_src_addr));
pj_bzero(&udp->rtcp_src_addr, sizeof(udp->rtcp_src_addr));
udp->rtp_src_cnt = 0;
+ udp->rtcp_src_cnt = 0;
/* Unlock keys */
pj_ioqueue_unlock_key(udp->rtcp_key);
diff --git a/pjmedia/src/test/jbuf_test.c b/pjmedia/src/test/jbuf_test.c
index 9cffa921..309459b0 100644
--- a/pjmedia/src/test/jbuf_test.c
+++ b/pjmedia/src/test/jbuf_test.c
@@ -43,7 +43,8 @@ typedef struct test_cond_t {
int discard;
int lost;
int empty;
- int delay; /**< Maximum delay, in frames. */
+ int delay; /**< Average delay, in frames. */
+ int delay_min; /**< Minimum delay, in frames. */
} test_cond_t;
static pj_bool_t parse_test_headers(char *line, test_param_t *param,
@@ -69,6 +70,8 @@ static pj_bool_t parse_test_headers(char *line, test_param_t *param,
cond->burst = cond_val;
else if (pj_ansi_stricmp(cond_st, "delay") == 0)
cond->delay = cond_val;
+ else if (pj_ansi_stricmp(cond_st, "delay_min") == 0)
+ cond->delay_min = cond_val;
else if (pj_ansi_stricmp(cond_st, "discard") == 0)
cond->discard = cond_val;
else if (pj_ansi_stricmp(cond_st, "empty") == 0)
@@ -217,6 +220,7 @@ int jbuf_main(void)
cond.burst = -1;
cond.delay = -1;
+ cond.delay_min = -1;
cond.discard = -1;
cond.empty = -1;
cond.lost = -1;
@@ -313,6 +317,11 @@ int jbuf_main(void)
cond.delay, state.avg_delay/JB_PTIME);
rc |= 2;
}
+ if (cond.delay_min >= 0 && (int)state.min_delay/JB_PTIME > cond.delay_min) {
+ printf("! 'Minimum delay' should be %d, it is %d\n",
+ cond.delay_min, state.min_delay/JB_PTIME);
+ rc |= 32;
+ }
if (cond.discard >= 0 && (int)state.discard > cond.discard) {
printf("! 'Discard' should be %d, it is %d\n",
cond.discard, state.discard);
diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c
index 91551196..977fe9a0 100644
--- a/pjmedia/src/test/mips_test.c
+++ b/pjmedia/src/test/mips_test.c
@@ -916,6 +916,23 @@ static pjmedia_port* g7221c_encode_decode(pj_pool_t *pool,
}
#endif /* PJMEDIA_HAS_G7221_CODEC */
+#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+/* AMR-NB benchmark benchmark */
+static pjmedia_port* amr_encode_decode(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ return codec_encode_decode(pool, "AMR/8000",
+ &pjmedia_codec_opencore_amrnb_init,
+ &pjmedia_codec_opencore_amrnb_deinit,
+ clock_rate, channel_count,
+ samples_per_frame, flags, te);
+}
+#endif /* PJMEDIA_HAS_OPENCORE_AMRNB_CODEC */
+
#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0
static pj_status_t init_l16_default(pjmedia_endpt *endpt)
{
@@ -1976,6 +1993,23 @@ static pjmedia_port* create_stream_g7221c( pj_pool_t *pool,
}
#endif /* PJMEDIA_HAS_G7221_CODEC */
+/* AMR-NB stream */
+#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+static pjmedia_port* create_stream_amr( pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ return create_stream(pool, "AMR/8000", &pjmedia_codec_opencore_amrnb_init,
+ &pjmedia_codec_opencore_amrnb_deinit,
+ PJ_FALSE, PJ_FALSE, PJ_FALSE,
+ clock_rate, channel_count,
+ samples_per_frame, flags, te);
+}
+#endif /* PJMEDIA_HAS_OPENCORE_AMRNB_CODEC */
+
/***************************************************************************/
/* Delay buffer */
enum {DELAY_BUF_MAX_DELAY = 80};
@@ -2366,6 +2400,9 @@ int mips_test(void)
{ "codec encode/decode - G.722.1", OP_PUT, K16, &g7221_encode_decode},
{ "codec encode/decode - G.722.1c", OP_PUT, K32, &g7221c_encode_decode},
#endif
+#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+ { "codec encode/decode - AMR-NB", OP_PUT, K8, &amr_encode_decode},
+#endif
#if PJMEDIA_HAS_L16_CODEC
{ "codec encode/decode - L16/8000/1", OP_PUT, K8, &l16_8_encode_decode},
{ "codec encode/decode - L16/16000/1", OP_PUT, K16, &l16_16_encode_decode},
@@ -2391,6 +2428,9 @@ int mips_test(void)
{ "stream TX/RX - G.722.1", OP_PUT_GET, K16, &create_stream_g7221},
{ "stream TX/RX - G.722.1c", OP_PUT_GET, K32, &create_stream_g7221c},
#endif
+#if PJMEDIA_HAS_OPENCORE_AMRNB_CODEC
+ { "stream TX/RX - AMR-NB", OP_PUT_GET, K8, &create_stream_amr},
+#endif
};
unsigned i, c, k[3] = {K8, K16, K32}, clock_rates[3] = {8000, 16000, 32000};
diff --git a/pjmedia/src/test/test.c b/pjmedia/src/test/test.c
index 82b20e17..2b7c9ca5 100644
--- a/pjmedia/src/test/test.c
+++ b/pjmedia/src/test/test.c
@@ -43,6 +43,17 @@ void app_perror(pj_status_t status, const char *msg)
PJ_LOG(3,(THIS_FILE, "%s: %s", msg, errbuf));
}
+/* Force linking PLC stuff if G.711 is disabled. See:
+ * https://trac.pjsip.org/repos/ticket/1337
+ */
+#if PJMEDIA_HAS_G711_CODEC==0
+int dummy()
+{
+ // Dummy
+ return (int) &pjmedia_plc_save;
+}
+#endif
+
int test_main(void)
{
int rc = 0;