summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjmedia/docs/doxygen.cfg6
-rw-r--r--pjmedia/docs/siprtp.jpgbin0 -> 69854 bytes
-rw-r--r--pjmedia/docs/sndtest.jpgbin0 -> 105316 bytes
-rw-r--r--pjmedia/include/pjmedia-codec.h5
-rw-r--r--pjmedia/include/pjmedia-codec/gsm.h2
-rw-r--r--pjmedia/include/pjmedia-codec/l16.h2
-rw-r--r--pjmedia/include/pjmedia-codec/speex.h2
-rw-r--r--pjmedia/include/pjmedia/doxygen.h193
-rw-r--r--pjmedia/include/pjmedia/plc.h2
-rw-r--r--pjmedia/include/pjmedia/rtp.h3
-rw-r--r--pjmedia/include/pjmedia/sdp_neg.h3
-rw-r--r--pjmedia/include/pjmedia/sound.h2
-rw-r--r--pjmedia/include/pjmedia/splitcomb.h24
-rw-r--r--pjmedia/include/pjmedia/wav_port.h92
-rw-r--r--pjmedia/src/pjmedia/conference.c6
-rw-r--r--pjmedia/src/pjmedia/wav_player.c96
-rw-r--r--pjmedia/src/pjmedia/wav_writer.c67
-rw-r--r--pjsip-apps/src/samples/confbench.c19
-rw-r--r--pjsip-apps/src/samples/confsample.c12
-rw-r--r--pjsip-apps/src/samples/level.c13
-rw-r--r--pjsip-apps/src/samples/playfile.c16
-rw-r--r--pjsip-apps/src/samples/playsine.c12
-rw-r--r--pjsip-apps/src/samples/recfile.c13
-rw-r--r--pjsip-apps/src/samples/resampleplay.c12
-rw-r--r--pjsip-apps/src/samples/siprtp.c1
-rw-r--r--pjsip-apps/src/samples/sipstateless.c13
-rw-r--r--pjsip-apps/src/samples/sndtest.c15
-rw-r--r--pjsip-apps/src/samples/streamutil.c12
-rw-r--r--pjsip/docs/doxygen.cfg4
-rw-r--r--pjsip/docs/doxygen.h93
-rw-r--r--pjsip/docs/pjsua.jpgbin0 -> 74682 bytes
31 files changed, 693 insertions, 47 deletions
diff --git a/pjmedia/docs/doxygen.cfg b/pjmedia/docs/doxygen.cfg
index 1a466a43..5397ade6 100644
--- a/pjmedia/docs/doxygen.cfg
+++ b/pjmedia/docs/doxygen.cfg
@@ -345,7 +345,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = include
+INPUT = include ../pjsip-apps/src/samples
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
@@ -354,7 +354,7 @@ INPUT = include
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
# *.h++ *.idl *.odl
-FILE_PATTERNS = *.h
+FILE_PATTERNS = *.h *.c
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
@@ -383,7 +383,7 @@ EXCLUDE_PATTERNS =
# directories that contain example code fragments that are included (see
# the \include command).
-EXAMPLE_PATH =
+EXAMPLE_PATH = ../pjsip-apps/src/samples
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
diff --git a/pjmedia/docs/siprtp.jpg b/pjmedia/docs/siprtp.jpg
new file mode 100644
index 00000000..975f1fad
--- /dev/null
+++ b/pjmedia/docs/siprtp.jpg
Binary files differ
diff --git a/pjmedia/docs/sndtest.jpg b/pjmedia/docs/sndtest.jpg
new file mode 100644
index 00000000..ba1ab980
--- /dev/null
+++ b/pjmedia/docs/sndtest.jpg
Binary files differ
diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h
index f160edef..3658cb10 100644
--- a/pjmedia/include/pjmedia-codec.h
+++ b/pjmedia/include/pjmedia-codec.h
@@ -19,6 +19,11 @@
#ifndef __PJMEDIA_CODEC_PJMEDIA_CODEC_H__
#define __PJMEDIA_CODEC_PJMEDIA_CODEC_H__
+/**
+ * @file pjmedia-codec.h
+ * @brief Include all codecs API in PJMEDIA-CODEC
+ */
+
#include <pjmedia-codec/l16.h>
#include <pjmedia-codec/gsm.h>
#include <pjmedia-codec/speex.h>
diff --git a/pjmedia/include/pjmedia-codec/gsm.h b/pjmedia/include/pjmedia-codec/gsm.h
index 04f526ad..f6d0e99f 100644
--- a/pjmedia/include/pjmedia-codec/gsm.h
+++ b/pjmedia/include/pjmedia-codec/gsm.h
@@ -27,7 +27,7 @@
#include <pjmedia-codec/types.h>
/**
- * @defgroup PJMED_GSM GSM 06.10
+ * @defgroup PJMED_GSM GSM 06.10 Codec
* @ingroup PJMEDIA_CODEC
* @brief Implementation of GSM FR based on GSM 06.10 library
* @{
diff --git a/pjmedia/include/pjmedia-codec/l16.h b/pjmedia/include/pjmedia-codec/l16.h
index 225c0929..59e222e1 100644
--- a/pjmedia/include/pjmedia-codec/l16.h
+++ b/pjmedia/include/pjmedia-codec/l16.h
@@ -23,7 +23,7 @@
/**
- * @defgroup PJMED_L16 L16 Family
+ * @defgroup PJMED_L16 L16 Codec Family
* @ingroup PJMEDIA_CODEC
* @brief 16bit linear codecs (useful for debugging)
* @{
diff --git a/pjmedia/include/pjmedia-codec/speex.h b/pjmedia/include/pjmedia-codec/speex.h
index a773b39e..51b6931c 100644
--- a/pjmedia/include/pjmedia-codec/speex.h
+++ b/pjmedia/include/pjmedia-codec/speex.h
@@ -27,7 +27,7 @@
#include <pjmedia-codec/types.h>
/**
- * @defgroup PJMED_SPEEX Speex
+ * @defgroup PJMED_SPEEX Speex Codec Family
* @ingroup PJMEDIA_CODEC
* @brief Implementation of Speex codecs (narrow/wide/ultrawide-band).
* @{
diff --git a/pjmedia/include/pjmedia/doxygen.h b/pjmedia/include/pjmedia/doxygen.h
index 9ebd27e6..c9bef719 100644
--- a/pjmedia/include/pjmedia/doxygen.h
+++ b/pjmedia/include/pjmedia/doxygen.h
@@ -61,9 +61,63 @@
* PJMEDIA-CODEC.
*
* \n
+ * @section main_page_get_start_sec Getting Started
+ *
+ * For those who likes to just get start coding, the @ref getting_started_pjmedia
+ * may be a good place to start.
+ *
+ * The @ref page_pjmedia_samples page describes some examples that are available
+ * in the source tree.
+ *
+ *
+ * \n
* @section pjmedia_lic Copying and Acknowledgements
*
- * Please see @ref lic_stuffs page for the details.
+ * PJMEDIA and PJMEDIA-CODEC contains various parts obtained from other
+ * places, and each of these would have their own licensing terms.
+ * Please see @ref lic_stuffs page for details.
+ *
+ */
+
+/**
+ * @page pjmed_keywords_page Features Index
+ * @section pjmed_keywords Features Index
+ *
+ * <b>PJMEDIA features</b>, in no particular order (click to go to the relevant
+ * documentation):
+ * @ref lic_stuffs "Open Source media stack",
+ * @ref PJMEDIA_CLOCK,
+ * @ref PJMEDIA_CODEC,
+ * @ref enc_dec_codec,
+ * @ref plc_codec,
+ * @ref PJMEDIA_CONF,
+ * @ref PJMED_G711 "G711/G.711 (PCMA/PCMU) codec with PLC",
+ * @ref PJMED_GSM "GSM codec with PLC",
+ * @ref PJMED_L16 "linear codecs (multiple clockrate, stereo support, etc)",
+ * @ref PJMED_SPEEX "Speex codec (narrowband, wideband, ultra-wideband)",
+ * @ref PJMED_JBUF "portable, adaptive jitter buffer with PLC support",
+ * @ref PJMEDIA_MASTER_PORT,
+ * @ref PJMEDIA_NULL_PORT,
+ * @ref PJMED_PLC,
+ * @ref PJMEDIA_PORT_CONCEPT,
+ * @ref PJMEDIA_PORT_CLOCK,
+ * @ref PJMEDIA_RESAMPLE "high quality resampling/sampling rate conversion",
+ * @ref PJMEDIA_RESAMPLE_PORT,
+ * @ref PJMED_RTCP "small footprint, portable RTCP with media quality statistics",
+ * @ref PJMED_RTP "very small footprint, modular, DSP ready RTP implementation",
+ * @ref PJMEDIA_SDP "modular, small footprint, open source SDP implementation",
+ * @ref PJMEDIA_SDP_NEG "modular SDP negotiation/negotiator abstraction",
+ * @ref PJMED_SES "media session abstraction",
+ * @ref PJMEDIA_SILENCEDET,
+ * @ref PJMED_SND "portable audio/sound hardware/device abstraction for Linux, Unix, Windows, DirectSound, WinCE, Windows Mobile, MacOS X, etc.",
+ * @ref PJMED_SND_PORT,
+ * @ref PJMEDIA_SPLITCOMB,
+ * @ref PJMED_STRM "remote stream",
+ * @ref PJMEDIA_TRANSPORT_H "custom media transport abstraction",
+ * @ref PJMEDIA_TRANSPORT_UDP,
+ * @ref PJMEDIA_FILE_PLAY "WAV/WAVE file playback",
+ * @ref PJMEDIA_FILE_REC "WAV/WAVE file recording/capture",
+ * @ref PJMEDIA_WAVE "portable WAV/WAVE header manipulation"
*/
@@ -325,4 +379,141 @@
*
*/
+
+/**
+ @page getting_started_pjmedia Getting Started with PJMEDIA
+
+ @section getstart_init_setup_build Setting-up the Build System
+
+ @subsection subsec_build_pjmedia Building PJMEDIA and PJMEDIA-CODEC
+
+ The PJMEDIA and PJMEDIA-CODEC libraries are normally bundled in PJPROJECT
+ source tarball, and they are located in <tt><b>pjmedia</b></tt> sub-directory
+ tree.
+
+ Please follow the instructions in <tt><b>INSTALL.txt</b></tt> in the root
+ PJPROJECT directory to build all projects, including PJMEDIA and PJMEDIA-CODEC.
+
+ @subsection subsec_config_build Setting Up the Build Environment
+
+ In your project, you will need to configure the following.
+ - Add <tt><b>$pjproject/pjmedia/include</b></tt> in the search path for
+ include files.
+ - Add <tt><b>$pjproject/pjmedia/lib</b></tt> in the search path for
+ library files.
+ - Add PJMEDIA and PJMEDIA static libraries in the link command.
+
+ @subsection subsec_inc_pjmedia Include PJMEDIA and PJMEDIA-CODEC in Source Files
+
+ To include all features from PJMEDIA and PJMEDIA-CODEC, use the following:
+
+ \code
+ #include <pjlib.h>
+ #include <pjmedia.h>
+ #include <pjmedia-codec.h>
+ \endcode
+
+ Alternatively, you may include only specific parts of the library (for example
+ to speed up compilation by just a fraction), for example:
+
+ \code
+ #include <pjmedia/conference.h>
+ #include <pjmedia/jbuf.h>
+ #include <pjmedia-codec/speex.h>
+ \endcode
+
+ Note that you need to give <b>"pjmedia/"</b> and <b>"pjmedia-codec/"</b>
+ prefix to include specific files.
+
+
+ @section getstart_using Using PJMEDIA
+
+ I wish I could explain more, but for now, please have a look at the
+ @ref page_pjmedia_samples page on some examples.
+ */
+
+/**
+ @page page_pjmedia_samples PJMEDIA and PJMEDIA-CODEC Examples
+
+ @section pjmedia_samples_sec PJMEDIA and PJMEDIA-CODEC Examples
+
+ Please find below some PJMEDIA related examples that may help in giving
+ some more info:
+
+ - @ref page_pjmedia_samples_level_c\n
+ This is a good place to start learning about @ref PJMEDIA_PORT_CONCEPT,
+ as it shows that @ref PJMEDIA_PORT_CONCEPT are only "passive" objects
+ with <tt>get_frame()</tt> and <tt>put_frame()</tt> interface, and
+ someone has to call these to retrieve/store media frames.
+
+ - @ref page_pjmedia_samples_playfile_c\n
+ This example shows that when application connects a media port (in this
+ case a @ref PJMEDIA_FILE_PLAY) to @ref PJMED_SND_PORT, media will flow
+ automatically since the @ref PJMED_SND_PORT provides @ref PJMEDIA_PORT_CLOCK.
+
+ - @ref page_pjmedia_samples_recfile_c\n
+ Demonstrates how to capture audio from microphone to WAV file.
+
+ - @ref page_pjmedia_samples_playsine_c\n
+ Demonstrates how to create a custom @ref PJMEDIA_PORT_CONCEPT (in this
+ case a sine wave generator) and integrate it to PJMEDIA.
+
+ - @ref page_pjmedia_samples_confsample_c\n
+ This demonstrates how to use the @ref PJMEDIA_CONF. The sample program can
+ open multiple WAV files, and instruct the conference bridge to mix the
+ signal before playing it to the sound device.
+
+ - @ref page_pjmedia_samples_confbench_c\n
+ I use this to benchmark/optimize the conference bridge algorithm, but
+ readers may find the source useful.
+
+ - @ref page_pjmedia_samples_resampleplay_c\n
+ Demonstrates how to use @ref PJMEDIA_RESAMPLE_PORT to change the
+ sampling rate of a media port (in this case, a @ref PJMEDIA_FILE_PLAY).
+
+ - @ref page_pjmedia_samples_sndtest_c\n
+ This program performs some tests to the sound device to get some
+ quality parameters (such as sound jitter and clock drifts).\n
+ Screenshots on WinXP: \image html sndtest.jpg "sndtest screenshot on WinXP"
+
+ - @ref page_pjmedia_samples_streamutil_c\n
+ This example mainly demonstrates how to stream media (in this case a
+ @ref PJMEDIA_FILE_PLAY) to remote peer using RTP.
+
+ - @ref page_pjmedia_samples_siprtp_c\n
+ This is a useful program (integrated with PJSIP) to actively measure
+ the network quality/impairment parameters by making one or more SIP
+ calls (or receiving one or more SIP calls) and display the network
+ impairment of each stream direction at the end of the call.
+ The program is able to measure network quality parameters such as
+ jitter, packet lost/reorder/duplicate, round trip time, etc.\n
+ Note that the remote peer MUST support RTCP so that network quality
+ of each direction can be calculated. Using siprtp for both endpoints
+ is recommended.\n
+ Screenshots on WinXP: \image html siprtp.jpg "siprtp screenshot on WinXP"
+
+ */
+
+/**
+ * \page page_pjmedia_samples_siprtp_c Samples: Using SIP and Custom RTP/RTCP to Monitor Quality
+ *
+ * This source is an example to demonstrate using SIP and RTP/RTCP framework
+ * to measure the network quality/impairment from the SIP call. This
+ * program can be used to make calls or to receive calls from other
+ * SIP endpoint (or other siprtp program), and to display the media
+ * quality statistics at the end of the call.
+ *
+ * Note that the remote peer must support RTCP.
+ *
+ * The layout of the program has been designed so that custom reporting
+ * can be generated instead of plain human readable text.
+ *
+ * The source code of the file is pjsip-apps/src/samples/siprtp.c
+ *
+ * Screenshots on WinXP: \image html siprtp.jpg
+ *
+ * \includelineno siprtp.c
+ */
+
#endif /* __PJMEDIA_DOXYGEN_H__ */
+
diff --git a/pjmedia/include/pjmedia/plc.h b/pjmedia/include/pjmedia/plc.h
index 6b80d18a..6a4be4d2 100644
--- a/pjmedia/include/pjmedia/plc.h
+++ b/pjmedia/include/pjmedia/plc.h
@@ -27,7 +27,7 @@
#include <pjmedia/types.h>
/**
- * @defgroup PJMED_PLC Packet Lost Concealment
+ * @defgroup PJMED_PLC Packet Lost Concealment (PLC)
* @ingroup PJMEDIA_FRAME_OP
* @{
* This section describes PJMEDIA's implementation of Packet Lost
diff --git a/pjmedia/include/pjmedia/rtp.h b/pjmedia/include/pjmedia/rtp.h
index edad178a..220a8634 100644
--- a/pjmedia/include/pjmedia/rtp.h
+++ b/pjmedia/include/pjmedia/rtp.h
@@ -37,7 +37,8 @@ PJ_BEGIN_DECL
*
* The RTP module is designed to be dependent only to PJLIB, it does not depend
* on any other parts of PJMEDIA library. The RTP module does not even depend
- * on any transports (sockets), to promote even more use.
+ * on any transports (sockets), to promote even more use, such as in DSP
+ * development (where transport may be handled by different processor).
*
* An RTCP implementation is available, in separate module. Please see
* @ref PJMED_RTCP.
diff --git a/pjmedia/include/pjmedia/sdp_neg.h b/pjmedia/include/pjmedia/sdp_neg.h
index f105c42f..4a07cfe6 100644
--- a/pjmedia/include/pjmedia/sdp_neg.h
+++ b/pjmedia/include/pjmedia/sdp_neg.h
@@ -25,8 +25,9 @@
* @brief SDP negotiator header file.
*/
/**
- * @defgroup PJMEDIA_SDP_NEG SDP Negotiator
+ * @defgroup PJMEDIA_SDP_NEG SDP Negotiation
* @ingroup PJMEDIA_SESSION
+ * @brief Abstraction to perform SDP negotiation
* @{
*
* The header file <b><pjmedia/sdp_neg.h></b> contains the declaration
diff --git a/pjmedia/include/pjmedia/sound.h b/pjmedia/include/pjmedia/sound.h
index b09d69d3..9cd2bbca 100644
--- a/pjmedia/include/pjmedia/sound.h
+++ b/pjmedia/include/pjmedia/sound.h
@@ -30,7 +30,7 @@
PJ_BEGIN_DECL
/**
- * @defgroup PJMED_SND Sound Hardware Abstraction
+ * @defgroup PJMED_SND Portable Sound Hardware Abstraction
* @ingroup PJMED_SND_PORT
* @brief PJMEDIA abstraction for sound device hardware
* @{
diff --git a/pjmedia/include/pjmedia/splitcomb.h b/pjmedia/include/pjmedia/splitcomb.h
index 1bd1bd23..d1104488 100644
--- a/pjmedia/include/pjmedia/splitcomb.h
+++ b/pjmedia/include/pjmedia/splitcomb.h
@@ -21,6 +21,25 @@
/**
+ * @file splitcomb.h
+ * @brief Media channel splitter/combiner port.
+ */
+#include <pjmedia/types.h>
+
+
+/**
+ * @addtogroup PJMEDIA_SPLITCOMB Media channel splitter/combiner
+ * @ingroup PJMEDIA_PORT
+ * @brief Split and combine media channels in media streams
+ * @{
+ * This section describes media port to split and combine media
+ * channels in the stream.
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
* Create a media splitter/combiner with the specified parameters.
* A splitter/combiner splits a single stereo/multichannel audio frame into
* multiple mono audio frames to each channel when put_frame() is called,
@@ -107,6 +126,11 @@ pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool,
+PJ_END_DECL
+
+/**
+ * @}
+ */
#endif /* __PJMEDIA_SPLITCOMB_H__ */
diff --git a/pjmedia/include/pjmedia/wav_port.h b/pjmedia/include/pjmedia/wav_port.h
index 6806f33f..99de617b 100644
--- a/pjmedia/include/pjmedia/wav_port.h
+++ b/pjmedia/include/pjmedia/wav_port.h
@@ -31,7 +31,7 @@ PJ_BEGIN_DECL
/**
- * @defgroup PJMEDIA_FILE_PLAY File Player
+ * @defgroup PJMEDIA_FILE_PLAY WAV File Player
* @ingroup PJMEDIA_PORT
* @brief WAV File Player
* @{
@@ -77,18 +77,52 @@ PJ_DECL(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool,
/**
- * Set the play position of WAV player.
+ * Set the file play position of WAV player.
*
* @param port The file player port.
- * @param samples Sample position (zero as start of file).
+ * @param offset Playback position in bytes, relative to the start of
+ * the payload.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_wav_player_port_set_pos( pjmedia_port *port,
- pj_uint32_t samples );
+ pj_uint32_t offset );
/**
+ * Get the file play position of WAV player.
+ *
+ * @param port The file player port.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port );
+
+
+/**
+ * Register a callback to be called when the file reading has reached the
+ * end of file. If the file is set to play repeatedly, then the callback
+ * will be called multiple times. Note that only one callback can be
+ * registered for each file port.
+ *
+ * @param port The file player port.
+ * @param user_data User data to be specified in the callback. Note that
+ * this overwrites the user data previously set when
+ * the file port is created.
+ * @param cb Callback to be called. If the callback returns non-
+ * PJ_SUCCESS, the playback will stop. Note that if
+ * application destroys the file port in the callback,
+ * it must return non-PJ_SUCCESS here.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_wav_player_set_eof_cb( pjmedia_port *port,
+ void *user_data,
+ pj_status_t (*cb)(pjmedia_port *port,
+ void *usr_data));
+
+/**
* @}
*/
@@ -114,13 +148,13 @@ PJ_DECL(pj_status_t) pjmedia_wav_player_port_set_pos( pjmedia_port *port,
* @param samples_per_frame Number of samples per frame.
* @param bits_per_sample Number of bits per sample (eg 16).
* @param flags Port creation flags (must be 0 at present).
- * @param buff_size Buffer size to be allocated. If the value is zero or
- * negative, the port will use default buffer size (which
- * is about 4KB).
- * @param user_data User data to be associated with the file writer port.
- * @param p_port Pointer to receive the file port instance.
+ * @param buff_size Buffer size to be allocated. If the value is
+ * zero or negative, the port will use default buffer
+ * size (which is about 4KB).
+ * @param user_data User data to be associated with the file port.
+ * @param p_port Pointer to receive the file port instance.
*
- * @return PJ_SUCCESS on success.
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_wav_writer_port_create(pj_pool_t *pool,
const char *filename,
@@ -134,6 +168,44 @@ PJ_DECL(pj_status_t) pjmedia_wav_writer_port_create(pj_pool_t *pool,
pjmedia_port **p_port );
+/**
+ * Get current writing position. Note that this does not necessarily match
+ * the size written to the file, since the WAV writer employs some internal
+ * buffering. Also the value reported here only indicates the payload size
+ * (it does not include the size of the WAV header),
+ *
+ * @param port The file writer port.
+ *
+ * @return Positive value to indicate the position (in bytes),
+ * or negative value containing the error code.
+ */
+PJ_DECL(pj_ssize_t) pjmedia_wav_writer_port_get_pos( pjmedia_port *port );
+
+
+/**
+ * Register the callback to be called when the file writing has reached
+ * certain size. Application can use this callback, for example, to limit
+ * the size of the output file.
+ *
+ * @param port The file writer port.
+ * @param pos The file position on which the callback will be called.
+ * @param user_data User data to be specified in the callback. Note that
+ * this overwrites the user data previously set when
+ * the file port is created.
+ * @param cb Callback to be called. If the callback returns non-
+ * PJ_SUCCESS, the writing will stop. Note that if
+ * application destroys the port in the callback, it must
+ * return non-PJ_SUCCESS here.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_wav_writer_port_set_cb( pjmedia_port *port,
+ pj_size_t pos,
+ void *user_data,
+ pj_status_t (*cb)(pjmedia_port *port,
+ void *usr_data));
+
/**
* @}
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index abf522b1..7c4ed922 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -1055,7 +1055,7 @@ static pj_status_t read_port( pjmedia_conf *conf,
(int)cport->name.slen, cport->name.ptr,
count));
- status = (cport->port->get_frame)(cport->port, &f);
+ status = pjmedia_port_get_frame(cport->port, &f);
*type = f.type;
@@ -1460,6 +1460,10 @@ static pj_status_t get_frame(pjmedia_port *this_port,
*/
continue;
}
+
+ /* Check that the port is not removed when we call get_frame() */
+ if (conf->ports[i] == NULL)
+ continue;
}
/* If we need to adjust the RX level from this port, adjust the level
diff --git a/pjmedia/src/pjmedia/wav_player.c b/pjmedia/src/pjmedia/wav_player.c
index e7c3df40..8261f08a 100644
--- a/pjmedia/src/pjmedia/wav_player.c
+++ b/pjmedia/src/pjmedia/wav_player.c
@@ -30,7 +30,7 @@
#define THIS_FILE "wav_player.c"
-#define SIGNATURE ('F'<<24|'P'<<16|'L'<<8|'Y')
+#define SIGNATURE PJMEDIA_PORT_SIGNATURE('F', 'P', 'l', 'y')
#define BYTES_PER_SAMPLE 2
@@ -65,6 +65,7 @@ struct file_port
pj_off_t fpos;
pj_oshandle_t fd;
+ pj_status_t (*cb)(pjmedia_port*, void*);
};
@@ -116,9 +117,9 @@ static pj_status_t fill_buffer(struct file_port *fport)
pj_ssize_t size;
pj_status_t status;
- if (fport->eof) {
+ /* Can't read file if EOF and loop flag is disabled */
+ if (fport->eof)
return PJ_EEOF;
- }
while (size_left > 0) {
@@ -141,6 +142,27 @@ static pj_status_t fill_buffer(struct file_port *fport)
* encountered EOF. Rewind the file.
*/
if (size < (pj_ssize_t)size_to_read) {
+ /* Call callback, if any. */
+ if (fport->cb) {
+ PJ_LOG(5,(THIS_FILE,
+ "File port %.*s EOF, calling callback",
+ (int)fport->base.info.name.slen,
+ fport->base.info.name.ptr));
+
+ fport->eof = PJ_TRUE;
+ status = (*fport->cb)(&fport->base, fport->base.user_data);
+ if (status != PJ_SUCCESS) {
+ /* This will crash if file port is destroyed in the
+ * callback, that's why we set the eof flag before
+ * calling the callback:
+ fport->eof = PJ_TRUE;
+ */
+ return status;
+ }
+
+ fport->eof = PJ_FALSE;
+ }
+
if (fport->options & PJMEDIA_FILE_NO_LOOP) {
PJ_LOG(5,(THIS_FILE, "File port %.*s EOF, stopping..",
(int)fport->base.info.name.slen,
@@ -335,19 +357,23 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool,
* Set position.
*/
PJ_DEF(pj_status_t) pjmedia_wav_player_port_set_pos(pjmedia_port *port,
- pj_uint32_t samples )
+ pj_uint32_t bytes )
{
struct file_port *fport;
- PJ_ASSERT_RETURN(port, PJ_EINVAL);
+ /* Sanity check */
+ PJ_ASSERT_RETURN(port, -PJ_EINVAL);
+
+ /* Check that this is really a player port */
+ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
fport = (struct file_port*) port;
- PJ_ASSERT_RETURN(samples*BYTES_PER_SAMPLE < fport->fsize -
- sizeof(pjmedia_wave_hdr), PJ_EINVAL);
+ PJ_ASSERT_RETURN(bytes < fport->fsize - sizeof(pjmedia_wave_hdr),
+ PJ_EINVAL);
- fport->fpos = sizeof(struct pjmedia_wave_hdr) +
- samples * BYTES_PER_SAMPLE;
+ fport->fpos = sizeof(struct pjmedia_wave_hdr) + bytes;
pj_file_setpos( fport->fd, fport->fpos, PJ_SEEK_SET);
fport->eof = PJ_FALSE;
@@ -356,6 +382,58 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_set_pos(pjmedia_port *port,
/*
+ * Get the file play position of WAV player.
+ */
+PJ_DEF(pj_ssize_t) pjmedia_wav_player_port_get_pos( pjmedia_port *port )
+{
+ struct file_port *fport;
+ pj_size_t payload_pos;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(port, -PJ_EINVAL);
+
+ /* Check that this is really a player port */
+ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+ fport = (struct file_port*) port;
+
+ payload_pos = (pj_size_t)(fport->fpos - sizeof(pjmedia_wave_hdr));
+ if (payload_pos >= fport->bufsize)
+ return payload_pos - fport->bufsize + (fport->readpos - fport->buf);
+ else
+ return (fport->readpos - fport->buf) % payload_pos;
+}
+
+
+
+/*
+ * Register a callback to be called when the file reading has reached the
+ * end of file.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_wav_player_set_eof_cb( pjmedia_port *port,
+ void *user_data,
+ pj_status_t (*cb)(pjmedia_port *port,
+ void *usr_data))
+{
+ struct file_port *fport;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(port, -PJ_EINVAL);
+
+ /* Check that this is really a player port */
+ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+ fport = (struct file_port*) port;
+
+ fport->base.user_data = user_data;
+ fport->cb = cb;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
* Put frame to file.
*/
static pj_status_t file_put_frame(pjmedia_port *this_port,
diff --git a/pjmedia/src/pjmedia/wav_writer.c b/pjmedia/src/pjmedia/wav_writer.c
index 24816b27..a4b4f74b 100644
--- a/pjmedia/src/pjmedia/wav_writer.c
+++ b/pjmedia/src/pjmedia/wav_writer.c
@@ -28,7 +28,7 @@
#define THIS_FILE "wav_writer.c"
-#define SIGNATURE ('F'<<24|'W'<<16|'R'<<8|'T')
+#define SIGNATURE PJMEDIA_PORT_SIGNATURE('F', 'W', 'R', 'T')
#define BYTES_PER_SAMPLE 2
@@ -38,8 +38,12 @@ struct file_port
pj_size_t bufsize;
char *buf;
char *writepos;
+ pj_size_t total;
pj_oshandle_t fd;
+
+ pj_size_t cb_size;
+ pj_status_t (*cb)(pjmedia_port*, void*);
};
static pj_status_t file_put_frame(pjmedia_port *this_port,
@@ -177,6 +181,54 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool,
}
+
+/*
+ * Get current writing position.
+ */
+PJ_DEF(pj_ssize_t) pjmedia_wav_writer_port_get_pos( pjmedia_port *port )
+{
+ struct file_port *fport;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(port, -PJ_EINVAL);
+
+ /* Check that this is really a writer port */
+ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+ fport = (struct file_port*) port;
+
+ return fport->total;
+}
+
+
+/*
+ * Register callback.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_wav_writer_port_set_cb( pjmedia_port *port,
+ pj_size_t pos,
+ void *user_data,
+ pj_status_t (*cb)(pjmedia_port *port,
+ void *usr_data))
+{
+ struct file_port *fport;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(port && cb, PJ_EINVAL);
+
+ /* Check that this is really a writer port */
+ PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVALIDOP);
+
+ fport = (struct file_port*) port;
+
+ fport->cb_size = pos;
+ fport->base.user_data = user_data;
+ fport->cb = cb;
+
+ return PJ_SUCCESS;
+}
+
+
#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
static void swap_samples(pj_int16_t *samples, unsigned count)
{
@@ -234,6 +286,19 @@ static pj_status_t file_put_frame(pjmedia_port *this_port,
pj_memcpy(fport->writepos, frame->buf, frame->size);
fport->writepos += frame->size;
+ /* Increment total written, and check if we need to call callback */
+ fport->total += frame->size;
+ if (fport->cb && fport->total >= fport->cb_size) {
+ pj_status_t (*cb)(pjmedia_port*, void*);
+ pj_status_t status;
+
+ cb = fport->cb;
+ fport->cb = NULL;
+
+ status = (*cb)(this_port, this_port->user_data);
+ return status;
+ }
+
return PJ_SUCCESS;
}
diff --git a/pjsip-apps/src/samples/confbench.c b/pjsip-apps/src/samples/confbench.c
index 2dae32b7..26a0fac6 100644
--- a/pjsip-apps/src/samples/confbench.c
+++ b/pjsip-apps/src/samples/confbench.c
@@ -17,11 +17,19 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-/*
+
+/**
+ * \page page_pjmedia_samples_confbench_c Samples: Benchmarking Conference Bridge
+ *
* Benchmarking pjmedia (conference bridge+resample). For my use only,
* and it only works in Win32.
+ *
+ * This file is pjsip-apps/src/samples/confbench.c
+ *
+ * \includelineno confbench.c
*/
+
#include <pjmedia.h>
#include <pjlib-util.h> /* pj_getopt */
#include <pjlib.h>
@@ -38,8 +46,8 @@
* HAS_RESAMPLE will activate resampling on about half
* the port.
*/
-#define TEST_SET SMALL_SET
-#define HAS_RESAMPLE 1
+#define TEST_SET LARGE_SET
+#define HAS_RESAMPLE 0
#define SMALL_SET 16
@@ -265,7 +273,10 @@ int main()
return 1;
}
+ printf("Resampling is %s\n", (HAS_RESAMPLE?"active":"disabled"));
+
/* Create Null ports */
+ printf("Creating %d null ports..\n", NULL_COUNT);
for (i=0; i<NULL_COUNT; ++i) {
status = pjmedia_null_port_create(pool, CLOCK_RATE, 1, SAMPLES_PER_FRAME*2, 16, &nulls[i]);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
@@ -275,6 +286,7 @@ int main()
}
/* Create sine ports. */
+ printf("Creating %d sine generator ports..\n", SINE_COUNT);
for (i=0; i<SINE_COUNT; ++i) {
unsigned j, slot;
@@ -304,6 +316,7 @@ int main()
}
/* Create idle ports */
+ printf("Creating %d idle ports..\n", IDLE_COUNT);
for (i=0; i<IDLE_COUNT; ++i) {
pjmedia_port *dummy;
status = pjmedia_null_port_create(pool, CLOCK_RATE, 1, SAMPLES_PER_FRAME, 16, &dummy);
diff --git a/pjsip-apps/src/samples/confsample.c b/pjsip-apps/src/samples/confsample.c
index 7ae80cdc..d87a916a 100644
--- a/pjsip-apps/src/samples/confsample.c
+++ b/pjsip-apps/src/samples/confsample.c
@@ -26,6 +26,18 @@
#include "util.h"
+/**
+ * \page page_pjmedia_samples_confsample_c Samples: Using Conference Bridge
+ *
+ * Sample to mix multiple files in the conference bridge and play the
+ * result to sound device.
+ *
+ * This file is pjsip-apps/src/samples/confsample.c
+ *
+ * \includelineno confsample.c
+ */
+
+
/* For logging purpose. */
#define THIS_FILE "confsample.c"
diff --git a/pjsip-apps/src/samples/level.c b/pjsip-apps/src/samples/level.c
index e2169841..dcf6499d 100644
--- a/pjsip-apps/src/samples/level.c
+++ b/pjsip-apps/src/samples/level.c
@@ -17,6 +17,19 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
+/**
+ * \page page_pjmedia_samples_level_c Samples: Reading from WAV File
+ *
+ * This is a very simple example to use the @ref PJMEDIA_FILE_PLAY, to
+ * directly read the samples from the file.
+ *
+ * This file is pjsip-apps/src/samples/level.c
+ *
+ * \includelineno level.c
+ */
+
+
static const char *desc =
" FILE: \n"
" level.c \n"
diff --git a/pjsip-apps/src/samples/playfile.c b/pjsip-apps/src/samples/playfile.c
index 5b26cd57..754304b3 100644
--- a/pjsip-apps/src/samples/playfile.c
+++ b/pjsip-apps/src/samples/playfile.c
@@ -26,6 +26,22 @@
#include "util.h"
+/**
+ * \page page_pjmedia_samples_playfile_c Samples: Playing WAV File to Sound Device
+ *
+ * This is a very simple example to use the @ref PJMEDIA_FILE_PLAY and
+ * @ref PJMED_SND_PORT. In this example, we open both the file and sound
+ * device, and connect the two of them, and voila! Sound will be playing
+ * the contents of the file.
+ *
+ * @see page_pjmedia_samples_recfile_c
+ *
+ * This file is pjsip-apps/src/samples/playfile.c
+ *
+ * \includelineno playfile.c
+ */
+
+
/*
* playfile.c
*
diff --git a/pjsip-apps/src/samples/playsine.c b/pjsip-apps/src/samples/playsine.c
index 931f9123..ae4bec8c 100644
--- a/pjsip-apps/src/samples/playsine.c
+++ b/pjsip-apps/src/samples/playsine.c
@@ -17,6 +17,18 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
+/**
+ * \page page_pjmedia_samples_playsine_c Samples: Using Custom Ports (Sine Wave Generator)
+ *
+ * This example demonstrate how to create a custom media port (in this case, a
+ * sine wave generator) and connect it to the sound device.
+ *
+ * This file is pjsip-apps/src/samples/playsine.c
+ *
+ * \includelineno playsine.c
+ */
+
/*
* playsine.c
*
diff --git a/pjsip-apps/src/samples/recfile.c b/pjsip-apps/src/samples/recfile.c
index 2236dcad..29aa8324 100644
--- a/pjsip-apps/src/samples/recfile.c
+++ b/pjsip-apps/src/samples/recfile.c
@@ -17,6 +17,18 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+/**
+ * \page page_pjmedia_samples_recfile_c Samples: Capturing Audio to WAV File
+ *
+ * In this example, we capture audio from the sound device and save it to
+ * WAVE file.
+ *
+ * @see page_pjmedia_samples_playfile_c
+ *
+ * This file is pjsip-apps/src/samples/recfile.c
+ *
+ * \includelineno recfile.c
+ */
#include <pjmedia.h>
#include <pjlib.h>
@@ -26,6 +38,7 @@
/* For logging purpose. */
#define THIS_FILE "recfile.c"
+
/* Configs */
#define CLOCK_RATE 44100
#define SAMPLES_PER_FRAME (CLOCK_RATE * 20 / 1000)
diff --git a/pjsip-apps/src/samples/resampleplay.c b/pjsip-apps/src/samples/resampleplay.c
index f8768591..9c7b2f1f 100644
--- a/pjsip-apps/src/samples/resampleplay.c
+++ b/pjsip-apps/src/samples/resampleplay.c
@@ -16,6 +16,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
+/**
+ * \page page_pjmedia_samples_resampleplay_c Samples: Using Resample Port
+ *
+ * This example demonstrates how to use @ref PJMEDIA_RESAMPLE_PORT to
+ * change the sampling rate of the media streams.
+ *
+ * This file is pjsip-apps/src/samples/resampleplay.c
+ *
+ * \includelineno resampleplay.c
+ */
+
#include <pjmedia.h>
#include <pjlib-util.h>
#include <pjlib.h>
diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c
index 42709660..4ffe0123 100644
--- a/pjsip-apps/src/samples/siprtp.c
+++ b/pjsip-apps/src/samples/siprtp.c
@@ -20,6 +20,7 @@
+
/* Usage */
static const char *USAGE =
" PURPOSE: \n"
diff --git a/pjsip-apps/src/samples/sipstateless.c b/pjsip-apps/src/samples/sipstateless.c
index c3cfe071..4c8354c5 100644
--- a/pjsip-apps/src/samples/sipstateless.c
+++ b/pjsip-apps/src/samples/sipstateless.c
@@ -103,20 +103,13 @@ int main()
/* Create global endpoint: */
{
- const pj_str_t *hostname;
- const char *endpt_name;
-
/* Endpoint MUST be assigned a globally unique name.
- * The name will be used as the hostname in Warning header.
+ * Ideally we should put hostname or public IP address, but
+ * we'll just use an arbitrary name here.
*/
- /* For this implementation, we'll use hostname for simplicity */
- hostname = pj_gethostname();
- endpt_name = hostname->ptr;
-
/* Create the endpoint: */
-
- status = pjsip_endpt_create(&cp.factory, endpt_name,
+ status = pjsip_endpt_create(&cp.factory, "sipstateless",
&sip_endpt);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
}
diff --git a/pjsip-apps/src/samples/sndtest.c b/pjsip-apps/src/samples/sndtest.c
index 2c469775..c2b75b99 100644
--- a/pjsip-apps/src/samples/sndtest.c
+++ b/pjsip-apps/src/samples/sndtest.c
@@ -17,6 +17,21 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+/**
+ * \page page_pjmedia_samples_sndtest_c Samples: Sound Card Benchmark
+ *
+ * This example can be used to benchmark the quality of the sound card
+ * installed in the system. At the end of the test, it will report
+ * the jitter and clock drifts of the device.
+ *
+ * This file is pjsip-apps/src/samples/sndtest.c
+ *
+ * Screenshots on WinXP: \image html sndtest.jpg
+ *
+ * \includelineno sndtest.c
+ */
+
+
#include <pjmedia.h>
#include <pjlib.h>
#include <pjlib-util.h>
diff --git a/pjsip-apps/src/samples/streamutil.c b/pjsip-apps/src/samples/streamutil.c
index 29ba520e..4609df0b 100644
--- a/pjsip-apps/src/samples/streamutil.c
+++ b/pjsip-apps/src/samples/streamutil.c
@@ -18,6 +18,18 @@
*/
+/**
+ * \page page_pjmedia_samples_streamutil_c Samples: Remote Streaming
+ *
+ * This example mainly demonstrates how to stream media file to remote
+ * peer using RTP.
+ *
+ * This file is pjsip-apps/src/samples/streamutil.c
+ *
+ * \includelineno streamutil.c
+ */
+
+
static const char *desc =
" streamutil \n"
" \n"
diff --git a/pjsip/docs/doxygen.cfg b/pjsip/docs/doxygen.cfg
index 3edc5bfc..59d6e659 100644
--- a/pjsip/docs/doxygen.cfg
+++ b/pjsip/docs/doxygen.cfg
@@ -384,7 +384,7 @@ EXCLUDE_PATTERNS =
# directories that contain example code fragments that are included (see
# the \include command).
-EXAMPLE_PATH =
+EXAMPLE_PATH = ../pjsip-apps/src/samples ../pjsip-apps/src/pjsua
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
@@ -404,7 +404,7 @@ EXAMPLE_RECURSIVE = NO
# directories that contain image that are included in the documentation (see
# the \image command).
-IMAGE_PATH =
+IMAGE_PATH = docs ../pjmedia/docs
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
diff --git a/pjsip/docs/doxygen.h b/pjsip/docs/doxygen.h
index d13ae200..604284f6 100644
--- a/pjsip/docs/doxygen.h
+++ b/pjsip/docs/doxygen.h
@@ -181,3 +181,96 @@
*/
+/**
+ @page page_pjsip_samples PJSIP Samples
+
+ I wish I could write more samples, but for now here are some samples or
+ working applications that are available from the source tree:
+
+ - @ref page_pjsip_sample_sipstateless_c\n
+ This is about the simplest SIP application with PJSIP, all it does is
+ respond all incoming requests with 501 (Not Implemented) response
+ statelessly.
+
+ - @ref page_pjsip_sample_simple_ua_c\n
+ This is a very simple SIP User Agent application that only use PJSIP
+ (without PJSIP-UA). It's able to make and receive call, and play
+ media to the sound device.
+
+ - @ref page_pjsip_samples_pjsua\n
+ This is the reference implementation for PJSIP and PJMEDIA.
+ PJSUA is a console based application, designed to be simple enough
+ to be readble, but powerful enough to demonstrate all features
+ available in PJSIP and PJMEDIA.\n
+ Screenshot on WinXP: \image html pjsua.jpg "pjsua on WinXP"
+
+ - @ref page_pjmedia_samples_siprtp_c\n
+ This is a useful program (integrated with PJSIP) to actively measure
+ the network quality/impairment parameters by making one or more SIP
+ calls (or receiving one or more SIP calls) and display the network
+ impairment of each stream direction at the end of the call.
+ The program is able to measure network quality parameters such as
+ jitter, packet lost/reorder/duplicate, round trip time, etc.\n
+ Note that the remote peer MUST support RTCP so that network quality
+ of each direction can be calculated. Using siprtp for both endpoints
+ is recommended.\n
+ Screenshots on WinXP: \image html siprtp.jpg "siprtp screenshot on WinXP"
+
+ */
+
+/**
+ * \page page_pjsip_samples_pjsua PJSUA
+ *
+ * This is the reference implementation for PJSIP and PJMEDIA.
+ * PJSUA is a console based application, designed to be simple enough
+ * to be readble, but powerful enough to demonstrate all features
+ * available in PJSIP and PJMEDIA.
+ *
+ * This file is pjsip-apps/src/pjsua/pjsua_app.c
+ *
+ * Screenshot on WinXP: \image html pjsua.jpg "pjsua on WinXP"
+ *
+ * \includelineno pjsua_app.c
+ */
+
+/**
+ * \page page_pjsip_sample_simple_ua_c Samples: Simple UA
+ *
+ * This is a very simple SIP User Agent application that only use PJSIP
+ * (without PJSIP-UA). It's able to make and receive call, and play
+ * media to the sound device.
+ *
+ * \includelineno simpleua.c
+ */
+
+/**
+ * \page page_pjsip_sample_sipstateless_c Samples: Stateless SIP Endpoint
+ *
+ * This is about the simplest SIP application with PJSIP, all it does is
+ * respond all incoming requests with 501 (Not Implemented) response
+ * statelessly.
+ *
+ * \includelineno sipstateless.c
+ */
+
+/**
+ * \page page_pjmedia_samples_siprtp_c Samples: Using SIP and Custom RTP/RTCP to Monitor Quality
+ *
+ * This source is an example to demonstrate using SIP and RTP/RTCP framework
+ * to measure the network quality/impairment from the SIP call. This
+ * program can be used to make calls or to receive calls from other
+ * SIP endpoint (or other siprtp program), and to display the media
+ * quality statistics at the end of the call.
+ *
+ * Note that the remote peer must support RTCP.
+ *
+ * The layout of the program has been designed so that custom reporting
+ * can be generated instead of plain human readable text.
+ *
+ * The source code of the file is pjsip-apps/src/samples/siprtp.c
+ *
+ * Screenshots on WinXP: \image html siprtp.jpg
+ *
+ * \includelineno siprtp.c
+ */
+
diff --git a/pjsip/docs/pjsua.jpg b/pjsip/docs/pjsua.jpg
new file mode 100644
index 00000000..479567bb
--- /dev/null
+++ b/pjsip/docs/pjsua.jpg
Binary files differ