From 66f9158fa3c12ebd3b2d317cf42e461e0b86a6aa Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Wed, 8 Feb 2006 22:43:39 +0000 Subject: Finished new pjmedia rewrite git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@159 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/build/pjmedia.dsp | 15 +- pjmedia/include/pjmedia/codec.h | 504 ++++++++++++--------- pjmedia/include/pjmedia/endpoint.h | 131 ++++++ pjmedia/include/pjmedia/errno.h | 36 ++ pjmedia/include/pjmedia/mediamgr.h | 100 ----- pjmedia/include/pjmedia/rtp.h | 16 +- pjmedia/include/pjmedia/session.h | 180 ++++---- pjmedia/include/pjmedia/stream.h | 147 ++++-- pjmedia/include/pjmedia/types.h | 142 ++++++ pjmedia/src/pjmedia/codec.c | 146 ++++-- pjmedia/src/pjmedia/endpoint.c | 239 ++++++++++ pjmedia/src/pjmedia/errno.c | 9 + pjmedia/src/pjmedia/g711.c | 126 +++--- pjmedia/src/pjmedia/mediamgr.c | 112 ----- pjmedia/src/pjmedia/rtcp.c | 2 +- pjmedia/src/pjmedia/rtp.c | 22 +- pjmedia/src/pjmedia/session.c | 890 ++++++++++--------------------------- pjmedia/src/pjmedia/stream.c | 805 +++++++++++++++++---------------- pjmedia/src/test/audio_tool.c | 36 +- pjmedia/src/test/session_test.c | 4 +- 20 files changed, 1931 insertions(+), 1731 deletions(-) create mode 100644 pjmedia/include/pjmedia/endpoint.h delete mode 100644 pjmedia/include/pjmedia/mediamgr.h create mode 100644 pjmedia/src/pjmedia/endpoint.c delete mode 100644 pjmedia/src/pjmedia/mediamgr.c (limited to 'pjmedia') diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp index d8feb969..a7f86a47 100644 --- a/pjmedia/build/pjmedia.dsp +++ b/pjmedia/build/pjmedia.dsp @@ -96,19 +96,19 @@ SOURCE=..\src\pjmedia\dsound.c # End Source File # Begin Source File -SOURCE=..\src\pjmedia\errno.c +SOURCE=..\src\pjmedia\endpoint.c # End Source File # Begin Source File -SOURCE=..\src\pjmedia\g711.c +SOURCE=..\src\pjmedia\errno.c # End Source File # Begin Source File -SOURCE=..\src\pjmedia\jbuf.c +SOURCE=..\src\pjmedia\g711.c # End Source File # Begin Source File -SOURCE=..\src\pjmedia\mediamgr.c +SOURCE=..\src\pjmedia\jbuf.c # End Source File # Begin Source File @@ -142,7 +142,6 @@ SOURCE=..\src\pjmedia\sdp_neg.c # Begin Source File SOURCE=..\src\pjmedia\session.c -# PROP Exclude_From_Build 1 # End Source File # Begin Source File @@ -162,15 +161,15 @@ SOURCE=..\include\pjmedia\config.h # End Source File # Begin Source File -SOURCE=..\include\pjmedia\errno.h +SOURCE=..\include\pjmedia\endpoint.h # End Source File # Begin Source File -SOURCE=..\include\pjmedia\jbuf.h +SOURCE=..\include\pjmedia\errno.h # End Source File # Begin Source File -SOURCE=..\include\pjmedia\mediamgr.h +SOURCE=..\include\pjmedia\jbuf.h # End Source File # Begin Source File diff --git a/pjmedia/include/pjmedia/codec.h b/pjmedia/include/pjmedia/codec.h index 3069f31c..75135d62 100644 --- a/pjmedia/include/pjmedia/codec.h +++ b/pjmedia/include/pjmedia/codec.h @@ -25,6 +25,7 @@ * @brief Codec framework. */ +#include #include PJ_BEGIN_DECL @@ -36,312 +37,393 @@ PJ_BEGIN_DECL * @{ */ -/** Top most media type. */ -typedef enum pj_media_type -{ - /** No type. */ - PJ_MEDIA_TYPE_NONE = 0, - - /** The media is audio */ - PJ_MEDIA_TYPE_AUDIO = 1, - - /** The media is video. */ - PJ_MEDIA_TYPE_VIDEO = 2, - - /** Unknown media type, in this case the name will be specified in - * encoding_name. - */ - PJ_MEDIA_TYPE_UNKNOWN = 3, - -} pj_media_type; - - -/** Media direction. */ -typedef enum pj_media_dir_t -{ - /** None */ - PJ_MEDIA_DIR_NONE = 0, - - /** Encoding (outgoing to network) stream */ - PJ_MEDIA_DIR_ENCODING = 1, - - /** Decoding (incoming from network) stream. */ - PJ_MEDIA_DIR_DECODING = 2, - - /** Incoming and outgoing stream. */ - PJ_MEDIA_DIR_ENCODING_DECODING = 3, -} pj_media_dir_t; - - -/** Standard RTP paylist types. */ -typedef enum pj_rtp_pt -{ - PJ_RTP_PT_PCMU = 0, /* audio PCMU */ - PJ_RTP_PT_GSM = 3, /* audio GSM */ - PJ_RTP_PT_G723 = 4, /* audio G723 */ - PJ_RTP_PT_DVI4_8K = 5, /* audio DVI4 8KHz */ - PJ_RTP_PT_DVI4_16K = 6, /* audio DVI4 16Khz */ - PJ_RTP_PT_LPC = 7, /* audio LPC */ - PJ_RTP_PT_PCMA = 8, /* audio PCMA */ - PJ_RTP_PT_G722 = 9, /* audio G722 */ - PJ_RTP_PT_L16_2 = 10, /* audio 16bit linear 44.1KHz stereo */ - PJ_RTP_PT_L16_1 = 11, /* audio 16bit linear 44.1KHz mono */ - PJ_RTP_PT_QCELP = 12, /* audio QCELP */ - PJ_RTP_PT_CN = 13, /* audio Comfort Noise */ - PJ_RTP_PT_MPA = 14, /* audio MPEG1 or MPEG2 as elementary streams */ - PJ_RTP_PT_G728 = 15, /* audio G728 */ - PJ_RTP_PT_DVI4_11K = 16, /* audio DVI4 11.025KHz mono */ - PJ_RTP_PT_DVI4_22K = 17, /* audio DVI4 22.050KHz mono */ - PJ_RTP_PT_G729 = 18, /* audio G729 */ - PJ_RTP_PT_CELB = 25, /* video/comb Cell-B by Sun Microsystems (RFC 2029) */ - PJ_RTP_PT_JPEG = 26, /* video JPEG */ - PJ_RTP_PT_NV = 28, /* video NV implemented by nv program by Xerox */ - PJ_RTP_PT_H261 = 31, /* video H261 */ - PJ_RTP_PT_MPV = 32, /* video MPEG1 or MPEG2 elementary streams */ - PJ_RTP_PT_MP2T = 33, /* video MPEG2 transport */ - PJ_RTP_PT_H263 = 34, /* video H263 */ - - PJ_RTP_PT_DYNAMIC = 96, /* start of dynamic RTP payload */ -} pj_rtp_pt; - - -/** Identification used to search for codec factory that supports specific - * codec specification. +/** + * Standard RTP static payload types, as defined by RFC 3551. */ -typedef struct pj_codec_id +enum pjmedia_rtp_pt { - /** Media type. */ - pj_media_type type; + PJMEDIA_RTP_PT_PCMU = 0, /* audio PCMU */ + PJMEDIA_RTP_PT_GSM = 3, /* audio GSM */ + PJMEDIA_RTP_PT_G723 = 4, /* audio G723 */ + PJMEDIA_RTP_PT_DVI4_8K = 5, /* audio DVI4 8KHz */ + PJMEDIA_RTP_PT_DVI4_16K = 6, /* audio DVI4 16Khz */ + PJMEDIA_RTP_PT_LPC = 7, /* audio LPC */ + PJMEDIA_RTP_PT_PCMA = 8, /* audio PCMA */ + PJMEDIA_RTP_PT_G722 = 9, /* audio G722 */ + PJMEDIA_RTP_PT_L16_2 = 10, /* audio 16bit linear 44.1KHz stereo */ + PJMEDIA_RTP_PT_L16_1 = 11, /* audio 16bit linear 44.1KHz mono */ + PJMEDIA_RTP_PT_QCELP = 12, /* audio QCELP */ + PJMEDIA_RTP_PT_CN = 13, /* audio Comfort Noise */ + PJMEDIA_RTP_PT_MPA = 14, /* audio MPEG1/MPEG2 elementary streams */ + PJMEDIA_RTP_PT_G728 = 15, /* audio G728 */ + PJMEDIA_RTP_PT_DVI4_11K = 16, /* audio DVI4 11.025KHz mono */ + PJMEDIA_RTP_PT_DVI4_22K = 17, /* audio DVI4 22.050KHz mono */ + PJMEDIA_RTP_PT_G729 = 18, /* audio G729 */ + + PJMEDIA_RTP_PT_CELB = 25, /* video/comb Cell-B by Sun (RFC 2029) */ + PJMEDIA_RTP_PT_JPEG = 26, /* video JPEG */ + PJMEDIA_RTP_PT_NV = 28, /* video NV by nv program by Xerox */ + PJMEDIA_RTP_PT_H261 = 31, /* video H261 */ + PJMEDIA_RTP_PT_MPV = 32, /* video MPEG1 or MPEG2 elementary */ + PJMEDIA_RTP_PT_MP2T = 33, /* video MPEG2 transport */ + PJMEDIA_RTP_PT_H263 = 34, /* video H263 */ + + PJMEDIA_RTP_PT_DYNAMIC = 96, /* start of dynamic RTP payload */ - /** Payload type (can be dynamic). */ - unsigned pt; - - /** Encoding name, must be present if the payload type is dynamic. */ - pj_str_t encoding_name; - - /** Sampling rate. */ - unsigned sample_rate; -} pj_codec_id; +}; -/** Detailed codec attributes used both to configure a codec and to query - * the capability of codec factories. +/** + * Identification used to search for codec factory that supports specific + * codec specification. */ -typedef struct pj_codec_attr +struct pjmedia_codec_info { - pj_uint32_t sample_rate; /* Sampling rate in Hz */ - pj_uint32_t avg_bps; /* Average bandwidth in bits per second */ - - pj_uint8_t pcm_bits_per_sample;/* Bits per sample in the PCM side */ - pj_uint16_t ptime; /* Packet time in miliseconds */ - - unsigned pt:8; /* Payload type. */ - unsigned vad_enabled:1; /* Voice Activity Detector. */ - unsigned cng_enabled:1; /* Comfort Noise Generator. */ - unsigned lpf_enabled:1; /* Low pass filter */ - unsigned hpf_enabled:1; /* High pass filter */ - unsigned penh_enabled:1; /* Perceptual Enhancement */ - unsigned concl_enabled:1; /* Packet loss concealment */ - unsigned reserved_bit:1; + pjmedia_type type; /**< Media type. */ + unsigned pt; /**< Payload type (can be dynamic). */ + pj_str_t encoding_name; /**< Encoding name. */ + unsigned sample_rate; /**< Sampling rate. */ +}; -} pj_codec_attr; -/** Types of audio frame. */ -typedef enum pj_audio_frame_type +/** + * Detailed codec attributes used both to configure a codec and to query + * the capability of codec factories. + */ +struct pjmedia_codec_param { - /** The frame is a silence audio frame. */ - PJ_AUDIO_FRAME_SILENCE, + pj_uint32_t sample_rate; /**< Sampling rate in Hz */ + pj_uint32_t avg_bps; /**< Average bandwidth in bits/sec */ - /** The frame is a non-silence audio frame. */ - PJ_AUDIO_FRAME_AUDIO, + pj_uint8_t pcm_bits_per_sample;/**< Bits/sample in the PCM side */ + pj_uint16_t ptime; /**< Packet time in miliseconds */ -} pj_audio_frame_type; + unsigned pt:8; /**< Payload type. */ + unsigned vad_enabled:1; /**< Voice Activity Detector. */ + unsigned cng_enabled:1; /**< Comfort Noise Generator. */ + unsigned lpf_enabled:1; /**< Low pass filter */ + unsigned hpf_enabled:1; /**< High pass filter */ + unsigned penh_enabled:1; /**< Perceptual Enhancement */ + unsigned concl_enabled:1; /**< Packet loss concealment */ + unsigned reserved_bit:1; /**< Reserved, must be NULL. */ -typedef struct pj_codec pj_codec; -typedef struct pj_codec_factory pj_codec_factory; +}; -/** This structure describes an audio frame. */ -struct pj_audio_frame +/** + * Types of media frame. + */ +enum pjmedia_frame_type { - /** Type: silence or non-silence. */ - pj_audio_frame_type type; + PJMEDIA_FRAME_TYPE_SILENCE_AUDIO, /**< Silence audio frame. */ + PJMEDIA_FRAME_TYPE_AUDIO, /**< Normal audio frame. */ - /** Pointer to buffer. */ - void *buf; +}; - /** Frame size in bytes. */ - unsigned size; +/** + * This structure describes a media frame. + */ +struct pjmedia_frame +{ + pjmedia_frame_type type; /**< Frame type. */ + void *buf; /**< Pointer to buffer. */ + pj_size_t size; /**< Frame size in bytes. */ }; /** - * Operations that must be supported by the codec. + * This structure describes codec operations. Each codec MUST implement + * all of these functions. */ -typedef struct pj_codec_op +struct pjmedia_codec_op { - /** Get default attributes. */ - pj_status_t (*default_attr) (pj_codec *codec, pj_codec_attr *attr); - - /** Open and initialize codec using the specified attribute. - * @return zero on success. + /** + * Get default attributes for this codec. + * + * @param codec The codec instance. + * @param attr Pointer to receive default codec attributes. + * + * @return PJ_SUCCESS on success. */ - pj_status_t (*init)( pj_codec *codec, pj_pool_t *pool ); - - /** Close and shutdown codec. + pj_status_t (*default_attr)(pjmedia_codec *codec, + pjmedia_codec_param *attr); + + /** + * Initialize codec using the specified attribute. + * + * @param codec The codec instance. + * @param pool Pool to use when the codec needs to allocate + * some memory. + * + * @return PJ_SUCCESS on success. */ - pj_status_t (*open)( pj_codec *codec, pj_codec_attr *attr ); - - /** Close and shutdown codec. + pj_status_t (*init)(pjmedia_codec *codec, + pj_pool_t *pool ); + + /** + * Open the codec and initialize with the specified parameter.. + * + * @param codec The codec instance. + * @param param Codec initialization parameter. + * + * @return PJ_SUCCESS on success. */ - pj_status_t (*close)( pj_codec *codec ); - - /** Encode frame. + pj_status_t (*open)(pjmedia_codec *codec, + pjmedia_codec_param *param ); + + /** + * Close and shutdown codec, releasing all resources allocated by + * this codec, if any. + * + * @param codec The codec instance. + * + * @return PJ_SUCCESS on success. */ - pj_status_t (*encode)( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output); - - /** Decode frame. + pj_status_t (*close)(pjmedia_codec *codec); + + + /** + * Instruct the codec to encode the specified input frame. + * + * @param codec The codec instance. + * @param input The input frame. + * @param out_size The length of buffer in the output frame. + * @param output The output frame. + * + * @return PJ_SUCCESS on success; */ - pj_status_t (*decode)( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output); + pj_status_t (*encode)(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned out_size, + struct pjmedia_frame *output); + + /** + * Instruct the codec to decode the specified input frame. + * + * @param codec The codec instance. + * @param input The input frame. + * @param out_size The length of buffer in the output frame. + * @param output The output frame. + * + * @return PJ_SUCCESS on success; + */ + pj_status_t (*decode)(pjmedia_codec *codec, + const struct pjmedia_frame *input, + unsigned out_size, + struct pjmedia_frame *output); + +}; -} pj_codec_op; /** - * A codec describes an instance to encode or decode media frames. + * This structure describes a codec instance. */ -struct pj_codec +struct pjmedia_codec { /** Entries to put this codec instance in codec factory's list. */ - PJ_DECL_LIST_MEMBER(struct pj_codec); + PJ_DECL_LIST_MEMBER(struct pjmedia_codec); /** Codec's private data. */ void *codec_data; /** Codec factory where this codec was allocated. */ - pj_codec_factory *factory; + pjmedia_codec_factory *factory; /** Operations to codec. */ - pj_codec_op *op; + pjmedia_codec_op *op; }; + /** - * This structure describes operations that must be supported by codec factories. + * This structure describes operations that must be supported by codec + * factories. */ -typedef struct pj_codec_factory_op +struct pjmedia_codec_factory_op { - /** Check whether the factory can create codec with the specified ID. - * @param factory The codec factory. - * @param id The codec ID. - * @return zero it matches. + /** + * Check whether the factory can create codec with the specified + * codec info. + * + * @param factory The codec factory. + * @param info The codec info. + * + * @return PJ_SUCCESS if this factory is able to create an + * instance of codec with the specified info. */ - pj_status_t (*match_id)( pj_codec_factory *factory, const pj_codec_id *id ); - - /** Create default attributes for the specified codec ID. This function can - * be called by application to get the capability of the codec. - * @param factory The codec factory. - * @param id The codec ID. - * @param attr The attribute to be initialized. - * @return zero if success. + pj_status_t (*test_alloc)(pjmedia_codec_factory *factory, + const pjmedia_codec_info *info ); + + /** + * Create default attributes for the specified codec ID. This function + * can be called by application to get the capability of the codec. + * + * @param factory The codec factory. + * @param info The codec info. + * @param attr The attribute to be initialized. + * + * @return PJ_SUCCESS if success. */ - pj_status_t (*default_attr)( pj_codec_factory *factory, const pj_codec_id *id, - pj_codec_attr *attr ); - - /** Enumerate supported codecs. - * @param factory The codec factory. - * @param count Number of entries in the array. - * @param codecs The codec array. - * @return the total number of supported codecs, which can be less or - * greater than requested. + pj_status_t (*default_attr)(pjmedia_codec_factory *factory, + const pjmedia_codec_info *info, + pjmedia_codec_param *attr ); + + /** + * Enumerate supported codecs that can be created using this factory. + * + * @param factory The codec factory. + * @param count On input, specifies the number of elements in + * the array. On output, the value will be set to + * the number of elements that have been initialized + * by this function. + * @param info The codec info array, which contents will be + * initialized upon return. + * + * @return PJ_SUCCESS on success. */ - unsigned (*enum_codecs) (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]); - - /** This function is called by codec manager to instantiate one codec - * instance. - * @param factory The codec factory. - * @param id The codec ID. - * @return the instance of the codec, or NULL if codec can not be created. + pj_status_t (*enum_info)(pjmedia_codec_factory *factory, + unsigned *count, + pjmedia_codec_info codecs[]); + + /** + * Create one instance of the codec with the specified codec info. + * + * @param factory The codec factory. + * @param info The codec info. + * @param p_codec Pointer to receive the codec instance. + * + * @return PJ_SUCCESS on success. */ - pj_codec* (*alloc_codec)( pj_codec_factory *factory, const pj_codec_id *id); - - /** This function is called by codec manager to return a particular instance - * of codec back to the codec factory. - * @param factory The codec factory. - * @param codec The codec instance to be returned. + pj_status_t (*alloc_codec)(pjmedia_codec_factory *factory, + const pjmedia_codec_info *info, + pjmedia_codec **p_codec); + + /** + * This function is called by codec manager to return a particular + * instance of codec back to the codec factory. + * + * @param factory The codec factory. + * @param codec The codec instance to be returned. + * + * @return PJ_SUCCESS on success. */ - void (*dealloc_codec)( pj_codec_factory *factory, pj_codec *codec ); + pj_status_t (*dealloc_codec)(pjmedia_codec_factory *factory, + pjmedia_codec *codec ); + +}; -} pj_codec_factory_op; /** * Codec factory describes a module that is able to create codec with specific * capabilities. These capabilities can be queried by codec manager to create * instances of codec. */ -struct pj_codec_factory +struct pjmedia_codec_factory { /** Entries to put this structure in the codec manager list. */ - PJ_DECL_LIST_MEMBER(struct pj_codec_factory); + PJ_DECL_LIST_MEMBER(struct pjmedia_codec_factory); /** The factory's private data. */ - void *factory_data; + void *factory_data; /** Operations to the factory. */ - pj_codec_factory_op *op; + pjmedia_codec_factory_op *op; }; /** * Declare maximum codecs */ -#define PJ_CODEC_MGR_MAX_CODECS 32 +#define PJMEDIA_CODEC_MGR_MAX_CODECS 32 /** * Codec manager maintains codec factory etc. */ -typedef struct pj_codec_mgr +typedef struct pjmedia_codec_mgr { - pj_codec_factory factory_list; - unsigned codec_cnt; - pj_codec_id codecs[PJ_CODEC_MGR_MAX_CODECS]; -} pj_codec_mgr; + pjmedia_codec_factory factory_list; + unsigned codec_cnt; + pjmedia_codec_info codecs[PJMEDIA_CODEC_MGR_MAX_CODECS]; +} pjmedia_codec_mgr; + + /** - * Init codec manager. + * Initialize codec manager. + * + * @param mgr Codec manager instance. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(pj_status_t) -pj_codec_mgr_init (pj_codec_mgr *mgr); +PJ_DECL(pj_status_t) pjmedia_codec_mgr_init(pjmedia_codec_mgr *mgr); + /** - * Register codec to codec manager. + * Register codec factory to codec manager. + * + * @param mgr The codec manager. + * @param factory The codec factory to be registered. + * + * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) -pj_codec_mgr_register_factory (pj_codec_mgr *mgr, pj_codec_factory *factory); +pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr, + pjmedia_codec_factory *factory); /** - * Unregister codec. + * Unregister codec factory from the codec manager. + * + * @param mgr The codec manager. + * @param factory The codec factory to be unregistered. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(void) -pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory); +PJ_DECL(pj_status_t) +pjmedia_codec_mgr_unregister_factory( pjmedia_codec_mgr *mgr, + pjmedia_codec_factory *factory); /** - * Enumerate codecs. + * Enumerate all supported codec. + * + * @param mgr The codec manager. + * @param count On input, specifies the number of elements in + * the array. On output, the value will be set to + * the number of elements that have been initialized + * by this function. + * @param info The codec info array, which contents will be + * initialized upon return. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(unsigned) -pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]); +PJ_DECL(pj_status_t) pjmedia_codec_mgr_enum_codecs( pjmedia_codec_mgr *mgr, + unsigned *count, + pjmedia_codec_info info[]); /** - * Open codec. + * Request the codec manager to allocate one instance of codec with the + * specified codec info. The codec will enumerate all codec factories + * until it finds factory that is able to create the specified codec. + * + * @param mgr The codec manager. + * @param info The information about the codec to be created. + * @param p_codec Pointer to receive the codec instance. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(pj_codec*) -pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id); +PJ_DECL(pj_status_t) pjmedia_codec_mgr_alloc_codec(pjmedia_codec_mgr *mgr, + const pjmedia_codec_info *info, + pjmedia_codec **p_codec); /** - * Close codec. + * Deallocate the specified codec instance. The codec manager will return + * the instance of the codec back to its factory. + * + * @param mgr The codec manager. + * @param codec The codec instance. + * + * @return PJ_SUCESS on success. */ -PJ_DECL(void) -pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec); +PJ_DECL(pj_status_t) pjmedia_codec_mgr_dealloc_codec(pjmedia_codec_mgr *mgr, + pjmedia_codec *codec); /** * @} diff --git a/pjmedia/include/pjmedia/endpoint.h b/pjmedia/include/pjmedia/endpoint.h new file mode 100644 index 00000000..27e8f4bf --- /dev/null +++ b/pjmedia/include/pjmedia/endpoint.h @@ -0,0 +1,131 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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_MEDIAMGR_H__ +#define __PJMEDIA_MEDIAMGR_H__ + + +/** + * @file mediamgr.h + * @brief Media Manager. + */ +/** + * @defgroup PJMED_ENDPT Media Endpoint + * @ingroup PJMEDIA + * @{ + * + * The media endpoint acts as placeholder for endpoint capabilities. Each + * media endpoint will have a codec manager to manage list of codecs installed + * in the endpoint and a sound device factory. + * + * A reference to media endpoint instance is required when application wants + * to create a media session (#pj_media_session_create or + * #pj_media_session_create_from_sdp). + */ + +#include +#include + + +PJ_BEGIN_DECL + + + +/** + * Create an instance of media endpoint. + * + * @param pf Pool factory, which will be used by the media endpoint + * throughout its lifetime. + * @param p_endpt Pointer to receive the endpoint instance. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_endpt_create( pj_pool_factory *pf, + pjmedia_endpt **p_endpt); + +/** + * Destroy media endpoint instance. + * + * @param endpt Media endpoint instance. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_endpt_destroy(pjmedia_endpt *endpt); + + +/** + * Request the media endpoint to create pool. + * + * @param endpt The media endpoint instance. + * @param name Name to be assigned to the pool. + * @param initial Initial pool size, in bytes. + * @param increment Increment size, in bytes. + * + * @return Memory pool. + */ +PJ_DECL(pj_pool_t*) pjmedia_endpt_create_pool( pjmedia_endpt *endpt, + const char *name, + pj_size_t initial, + pj_size_t increment); + +/** + * Get the codec manager instance of the media endpoint. + * + * @param endpt The media endpoint instance. + * + * @return The instance of codec manager belonging to + * this media endpoint. + */ +PJ_DECL(pjmedia_codec_mgr*) pjmedia_endpt_get_codec_mgr(pjmedia_endpt *mgr); + + +/** + * Create a SDP session description that describes the endpoint + * capability. + * + * @param endpt The media endpoint. + * @param pool Pool to use to create the SDP descriptor. + * @param stream_cnt Number of elements in the sock_info array. This + * also denotes the maximum number of streams (i.e. + * the "m=" lines) that will be created in the SDP. + * @param sock_info Array of socket transport information. One + * transport is needed for each media stream, and + * each transport consists of an RTP and RTCP socket + * pair. + * @param p_sdp Pointer to receive SDP session descriptor. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt, + pj_pool_t *pool, + unsigned stream_cnt, + const pjmedia_sock_info sock_info[], + pjmedia_sdp_session **p_sdp ); + + + +PJ_END_DECL + + +/** + * @} + */ + + + +#endif /* __PJMEDIA_MEDIAMGR_H__ */ diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h index 14da37de..8d1f6e14 100644 --- a/pjmedia/include/pjmedia/errno.h +++ b/pjmedia/include/pjmedia/errno.h @@ -259,6 +259,42 @@ PJ_DECL(pj_str_t) pjmedia_strerror( pj_status_t status, char *buffer, #define PJMEDIA_SDP_ETIMENOTEQUAL (PJMEDIA_ERRNO_START+72) /* 220072 */ +/************************************************************ + * CODEC + ***********************************************************/ +/** + * @hideinitializer + * Unsupported codec. + */ +#define PJMEDIA_CODEC_EUNSUP (PJMEDIA_ERRNO_START+80) /* 220080 */ + + +/************************************************************ + * MEDIA + ***********************************************************/ +/** + * @hideinitializer + * Invalid remote IP address (in SDP). + */ +#define PJMEDIA_EINVALIDIP (PJMEDIA_ERRNO_START+100) /* 220100 */ +/** + * @hideinitializer + * Asymetric codec is not supported. + */ +#define PJMEDIA_EASYMCODEC (PJMEDIA_ERRNO_START+101) /* 220101 */ +/** + * @hideinitializer + * Invalid payload type. + */ +#define PJMEDIA_EINVALIDPT (PJMEDIA_ERRNO_START+102) /* 220102 */ +/** + * @hideinitializer + * Missing rtpmap. + */ +#define PJMEDIA_EMISSINGRTPMAP (PJMEDIA_ERRNO_START+103) /* 220103 */ + + + PJ_END_DECL #endif /* __PJMEDIA_ERRNO_H__ */ diff --git a/pjmedia/include/pjmedia/mediamgr.h b/pjmedia/include/pjmedia/mediamgr.h deleted file mode 100644 index 148038ec..00000000 --- a/pjmedia/include/pjmedia/mediamgr.h +++ /dev/null @@ -1,100 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * 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_MEDIAMGR_H__ -#define __PJMEDIA_MEDIAMGR_H__ - - -/** - * @file mediamgr.h - * @brief Media Manager. - */ -/** - * @defgroup PJMED_MGR Media Manager - * @ingroup PJMEDIA - * @{ - * - * The media manager acts as placeholder for endpoint capabilities. Each - * media manager will have a codec manager to manage list of codecs installed - * in the endpoint and a sound device factory. - * - * A reference to media manager instance is required when application wants - * to create a media session (#pj_media_session_create or - * #pj_media_session_create_from_sdp). - */ - -#include -#include - - -PJ_BEGIN_DECL - - -/** Opague declaration of media manager. */ -typedef struct pj_med_mgr_t pj_med_mgr_t; - -/** - * Create an instance of media manager. - * - * @param pf Pool factory. - * @param conn_addr Connection address to be used by this media manager. - * - * @return A new instance of media manager, or NULL if failed. - */ -PJ_DECL(pj_med_mgr_t*) pj_med_mgr_create (pj_pool_factory *pf); - -/** - * Destroy media manager instance. - * - * @param mgr Media manager instance. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr); - -/** - * Get pool factory of the media manager as specified when the media - * manager was created. - * - * @param mgr The media manager instance. - * - * @return Pool factory instance of the media manager. - */ -PJ_DECL(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr); - -/** - * Get the codec manager instance. - * - * @param mgr The media manager instance. - * - * @return The instance of codec manager. - */ -PJ_DECL(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr); - - - -PJ_END_DECL - - -/** - * @} - */ - - - -#endif /* __PJMEDIA_MEDIAMGR_H__ */ diff --git a/pjmedia/include/pjmedia/rtp.h b/pjmedia/include/pjmedia/rtp.h index 54c1e32e..bbb38f53 100644 --- a/pjmedia/include/pjmedia/rtp.h +++ b/pjmedia/include/pjmedia/rtp.h @@ -79,14 +79,14 @@ PJ_BEGIN_DECL */ enum pj_rtp_error_t { - PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */ - PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */ - PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */ - PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */ - PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */ - PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */ - PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */ - PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */ + PJMEDIA_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */ + PJMEDIA_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */ + PJMEDIA_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */ + PJMEDIA_RTP_ERR_INVALID_PT, /**< Invalid payload type. */ + PJMEDIA_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */ + PJMEDIA_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */ + PJMEDIA_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */ + PJMEDIA_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */ }; #pragma pack(1) diff --git a/pjmedia/include/pjmedia/session.h b/pjmedia/include/pjmedia/session.h index d9efeb81..f6a9fc11 100644 --- a/pjmedia/include/pjmedia/session.h +++ b/pjmedia/include/pjmedia/session.h @@ -25,8 +25,7 @@ * @brief Media Session. */ -#include -#include +#include #include #include @@ -38,109 +37,140 @@ PJ_BEGIN_DECL * @{ */ -/** Opaque declaration of media session. */ -typedef struct pj_media_session_t pj_media_session_t; - -/** Media socket info. */ -typedef struct pj_media_sock_info -{ - pj_sock_t rtp_sock, rtcp_sock; - pj_sockaddr_in rtp_addr_name; -} pj_media_sock_info; - -/** Stream info. */ -typedef struct pj_media_stream_info -{ - pj_str_t type; - pj_media_dir_t dir; - pj_str_t transport; - pj_media_sock_info sock_info; - pj_str_t rem_addr; - unsigned short rem_port; - unsigned fmt_cnt; - pj_codec_id fmt[PJSDP_MAX_FMT]; - -} pj_media_stream_info; - -/** Flag for modifying stream. */ -enum -{ - PJ_MEDIA_STREAM_MODIFY_DIR = 1, -}; /** - * Create new session offering. + * Create new session offering based on the local and remote SDP. + * The session initially will be inactive. + * + * @param endpt The PJMEDIA endpoint instance. + * @param stream_cnt Maximum number of streams to be created. This + * also denotes the number of elements in the + * socket information. + * @param skinfo Array of socket informations. The argument stream_cnt + * specifies the number of elements in this array. One + * element is needed for each media stream to be + * created in the session. + * @param local_sdp The SDP describing local capability. + * @param rem_sdp The SDP describing remote capability. + * @param p_session Pointer to receive the media session. + * + * @return PJ_SUCCESS if media session can be created + * successfully. */ -PJ_DECL(pj_media_session_t*) -pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo ); +PJ_DECL(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, + unsigned stream_cnt, + const pjmedia_sock_info skinfo[], + const pjmedia_sdp_session *local_sdp, + const pjmedia_sdp_session *rem_sdp, + pjmedia_session **p_session ); -/** - * Create new session based on peer's offering. - */ -PJ_DECL(pj_media_session_t*) -pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, - const pj_media_sock_info *skinfo); /** - * Duplicate session. The new session is inactive. + * Activate all streams in media session for the specified direction. + * + * @param session The media session. + * @param dir The direction to activate. + * + * @return PJ_SUCCESS if success. */ -PJ_DECL(pj_media_session_t*) -pj_media_session_clone (const pj_media_session_t *session); +PJ_DECL(pj_status_t) pjmedia_session_resume(pjmedia_session *session, + pjmedia_dir dir); -/** - * Create SDP description from the session. - */ -PJ_DECL(pjsdp_session_desc*) -pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool, - pj_bool_t only_first_fmt); /** - * Update session with SDP answer from peer. The session must NOT active. + * Suspend receipt and transmission of all streams in media session + * for the specified direction. + * + * @param session The media session. + * @param dir The media direction to suspend. + * + * @return PJ_SUCCESS if success. */ -PJ_DECL(pj_status_t) -pj_media_session_update ( pj_media_session_t *session, - const pjsdp_session_desc *sdp); +PJ_DECL(pj_status_t) pjmedia_session_pause(pjmedia_session *session, + pjmedia_dir dir); /** - * Enumerate media streams in the session. - * @return the actual number of streams. + * Suspend receipt and transmission of individual stream in media session + * for the specified direction. + * + * @param session The media session. + * @param index The stream index. + * @param dir The media direction to pause. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(unsigned) -pj_media_session_enum_streams (const pj_media_session_t *session, - unsigned count, const pj_media_stream_info *info[]); +PJ_DECL(pj_status_t) pjmedia_session_pause_stream( pjmedia_session *session, + unsigned index, + pjmedia_dir dir); /** - * Get stream statistics. + * Activate individual stream in media session for the specified direction. + * + * @param session The media session. + * @param index The stream index. + * @param dir The media direction to activate. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(pj_status_t) -pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, - pj_media_stream_stat *tx_stat, - pj_media_stream_stat *rx_stat); +PJ_DECL(pj_status_t) pjmedia_session_resume_stream(pjmedia_session *session, + unsigned index, + pjmedia_dir dir); /** - * Modify stream, only when stream is inactive. + * Enumerate media streams in the session. + * + * @param session The media session. + * @param count On input, specifies the number of elements in + * the array. On output, the number will be filled + * with number of streams in the session. + * @param strm_info Array of stream info. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(pj_status_t) -pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, - unsigned modify_flag, const pj_media_stream_info *info); +PJ_DECL(pj_status_t) pjmedia_session_enum_streams(const pjmedia_session *session, + unsigned *count, + pjmedia_stream_info strm_info[]); + /** - * Activate all streams in media session. + * Get session statistics. The stream statistic shows various + * indicators such as packet count, packet lost, jitter, delay, etc. + * + * @param session The media session. + * @param count On input, specifies the number of elements in + * the array. On output, the number will be filled + * with number of streams in the session. + * @param stat Array of stream statistics. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(pj_status_t) -pj_media_session_activate (pj_media_session_t *session); +PJ_DECL(pj_status_t) pjmedia_session_get_stat(const pjmedia_session *session, + unsigned *count, + pjmedia_stream_stat stat[]); /** - * Activate individual stream in media session. + * Get individual stream statistics. The stream statistic shows various + * indicators such as packet count, packet lost, jitter, delay, etc. + * + * @param s The media session. + * @param index The stream index. + * @param stat Stream statistics. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(pj_status_t) -pj_media_session_activate_stream (pj_media_session_t *session, unsigned index); +PJ_DECL(pj_status_t) pjmedia_session_get_stream_stat(const pjmedia_session *s, + unsigned index, + pjmedia_stream_stat *stat); /** * Destroy media session. + * + * @param session The media session. + * + * @return PJ_SUCCESS if success. */ -PJ_DECL(pj_status_t) -pj_media_session_destroy (pj_media_session_t *session); +PJ_DECL(pj_status_t) pjmedia_session_destroy(pjmedia_session *session); + /** diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h index 09f6acf1..83f7e14e 100644 --- a/pjmedia/include/pjmedia/stream.h +++ b/pjmedia/include/pjmedia/stream.h @@ -27,7 +27,7 @@ #include #include -#include +#include #include PJ_BEGIN_DECL @@ -39,55 +39,124 @@ PJ_BEGIN_DECL * @{ */ -typedef struct pj_media_stream_t pj_media_stream_t; +/** + * Opaque declaration for media channel. + * Media channel is unidirectional flow of media from sender to + * receiver. + */ +typedef struct pjmedia_channel pjmedia_channel; -/** Parameter for creating channel. */ -typedef struct pj_media_stream_create_param +/** + * This structure describes media stream information. Each media stream + * corresponds to one "m=" line in SDP session descriptor, and it has + * its own RTP/RTCP socket pair. + */ +struct pjmedia_stream_info { - /** Codec ID, must NOT be NULL. */ - pj_codec_id *codec_id; + pjmedia_type type; /**< Media type (audio, video) */ + pjmedia_dir dir; /**< Media direction. */ + pjmedia_sock_info sock_info; /**< Media transport (RTP/RTCP sockets) */ + pj_sockaddr_in rem_addr; /**< Remote RTP address */ + pjmedia_codec_info fmt; /**< Codec format info. */ + pj_uint32_t ssrc; /**< RTP SSRC. */ + int jb_min; /**< Jitter buffer min delay. */ + int jb_max; /**< Jitter buffer max delay. */ + int jb_maxcnt; /**< Jitter buffer max delay. */ +}; - /** Media manager, must NOT be NULL. */ - pj_med_mgr_t *mediamgr; - /** Direction: IN_OUT, or IN only, or OUT only. */ - pj_media_dir_t dir; +/** + * Individual channel statistic. + */ +struct pjmedia_channel_stat +{ + pj_uint32_t pkt; /**< Total number of packets. */ + pj_uint32_t bytes; /**< Total number of bytes, including RTP hdr. */ + pj_uint32_t lost; /**< Total number of packet lost */ +}; - /** RTP socket. */ - pj_sock_t rtp_sock; +/** + * Stream statistic. + */ +struct pjmedia_stream_stat +{ + pjmedia_channel_stat enc; /**< Encoder statistics. */ + pjmedia_channel_stat dec; /**< Decoder statistics. */ +}; - /** RTCP socket. */ - pj_sock_t rtcp_sock; - /** Address of remote */ - pj_sockaddr_in *remote_addr; +/** + * Create a media stream based on the specified stream parameter. + * All channels in the stream initially will be inactive. + * + * @param endpt Media endpoint. + * @param pool Pool to allocate memory for the stream. A large + * number of memory may be needed because jitter + * buffer needs to preallocate some storage. + * @param info Stream information. + * @param p_stream Pointer to receive the media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_stream_create(pjmedia_endpt *endpt, + pj_pool_t *pool, + const pjmedia_stream_info *info, + pjmedia_stream **p_stream); - /** RTP SSRC */ - pj_uint32_t ssrc; +/** + * Destroy the media stream. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_stream_destroy(pjmedia_stream *stream); - /** Jitter buffer parameters. */ - int jb_min, jb_max, jb_maxcnt; +/** + * Start the media stream. This will start the appropriate channels + * in the media stream, depending on the media direction that was set + * when the stream was created. + * + * @param stream The media stream. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream); -} pj_media_stream_create_param; -typedef struct pj_media_stream_stat -{ - pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */ - pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */ - pj_uint32_t jitter; /* receive jitter in ms */ - pj_uint32_t pkt_lost; /* total packet lost count */ -} pj_media_stream_stat; - -PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool, - pj_media_stream_t **enc_stream, - pj_media_stream_t **dec_stream, - pj_media_stream_create_param *param); -PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream); -PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream, - pj_media_stream_stat *stat); -PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream); -PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream); -PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream); +/** + * Get the stream statistics. + * + * @param stream The media stream. + * @param stat Media stream statistics. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream, + pjmedia_stream_stat *stat); + +/** + * Pause the individual channel in the stream. + * + * @param channel The media channel. + * @param dir Which direction to pause. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, + pjmedia_dir dir); + +/** + * Resume the individual channel in the stream. + * + * @param channel The media channel. + * @param dir Which direction to resume. + * + * @return PJ_SUCCESS on success; + */ +PJ_DECL(pj_status_t) pjmedia_stream_resume(pjmedia_stream *stream, + pjmedia_dir dir); + /** * @} diff --git a/pjmedia/include/pjmedia/types.h b/pjmedia/include/pjmedia/types.h index 217a001b..6dd44bf7 100644 --- a/pjmedia/include/pjmedia/types.h +++ b/pjmedia/include/pjmedia/types.h @@ -20,8 +20,150 @@ #define __PJMEDIA_TYPES_H__ #include +#include +/** + * Top most media type. + */ +enum pjmedia_type +{ + /** No type. */ + PJMEDIA_TYPE_NONE = 0, + + /** The media is audio */ + PJMEDIA_TYPE_AUDIO = 1, + + /** The media is video. */ + PJMEDIA_TYPE_VIDEO = 2, + + /** Unknown media type, in this case the name will be specified in + * encoding_name. + */ + PJMEDIA_TYPE_UNKNOWN = 3, + +}; + + +/** + * Media direction. + */ +enum pjmedia_dir +{ + /** None */ + PJMEDIA_DIR_NONE = 0, + + /** Encoding (outgoing to network) stream */ + PJMEDIA_DIR_ENCODING = 1, + + /** Decoding (incoming from network) stream. */ + PJMEDIA_DIR_DECODING = 2, + + /** Incoming and outgoing stream. */ + PJMEDIA_DIR_ENCODING_DECODING = 3, + +}; + + +/** + * Top level media type. + */ +typedef enum pjmedia_type pjmedia_type; + +/** + * Media direction. + */ +typedef enum pjmedia_dir pjmedia_dir; + +/** + * Codec info. + */ +typedef struct pjmedia_codec_info pjmedia_codec_info; + +/** + * Codec initialization parameter. + */ +typedef struct pjmedia_codec_param pjmedia_codec_param; + +/** + * Types of media frames. + */ +typedef enum pjmedia_frame_type pjmedia_frame_type; + +/** + * This structure describes a media frame. + */ +typedef struct pjmedia_frame pjmedia_frame; + +/** + * Codec instance. + */ +typedef struct pjmedia_codec pjmedia_codec; + +/** + * Codec factory. + */ +typedef struct pjmedia_codec_factory pjmedia_codec_factory; + +/** + * Codec operation. + */ +typedef struct pjmedia_codec_op pjmedia_codec_op; + +/** + * Codec factory operation. + */ +typedef struct pjmedia_codec_factory_op pjmedia_codec_factory_op; + +/** + * Codec manager. + */ +typedef struct pjmedia_codec_mgr pjmedia_codec_mgr; + +/** + * Opague declaration of media endpoint. + */ +typedef struct pjmedia_endpt pjmedia_endpt; + + +/** + * Media socket info. + */ +typedef struct pjmedia_sock_info +{ + + pj_sock_t rtp_sock; + pj_sock_t rtcp_sock; + pj_sockaddr_in rtp_addr_name; + +} pjmedia_sock_info; + + +/** + * Typedef for media stream information. + */ +typedef struct pjmedia_stream_info pjmedia_stream_info; + +/** + * Typedef for media stream statistic. + */ +typedef struct pjmedia_stream_stat pjmedia_stream_stat; + +/** + * Typedef for media stream. + */ +typedef struct pjmedia_stream pjmedia_stream; + +/** + * Individual channel statistic. + */ +typedef struct pjmedia_channel_stat pjmedia_channel_stat; + +/** + * Opaque declaration of media session. + */ +typedef struct pjmedia_session pjmedia_session; + /** * Forward declaration for SDP attribute (sdp.h) */ diff --git a/pjmedia/src/pjmedia/codec.c b/pjmedia/src/pjmedia/codec.c index a87c301c..2d60d287 100644 --- a/pjmedia/src/pjmedia/codec.c +++ b/pjmedia/src/pjmedia/codec.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #include #include @@ -24,84 +25,137 @@ #define THIS_FILE "codec.c" -static void enum_all_codecs (pj_codec_mgr *cm) +/* + * Reinitialize array of supported codecs. + */ +static void enum_all_codecs (pjmedia_codec_mgr *mgr) { - pj_codec_factory *cf; - - cf = cm->factory_list.next; - cm->codec_cnt = 0; - while (cf != &cm->factory_list) { - pj_codec_id temp[PJ_CODEC_MGR_MAX_CODECS]; - int i, cnt; - - cnt = cf->op->enum_codecs (cf, PJ_CODEC_MGR_MAX_CODECS, temp); - if (cnt > PJ_CODEC_MGR_MAX_CODECS) { - pj_assert(0); - PJ_LOG(4, (THIS_FILE, "Too many codecs reported by factory")); - cnt = PJ_CODEC_MGR_MAX_CODECS; - } + pjmedia_codec_factory *factory; - for (i=0; icodec_cnt < PJ_CODEC_MGR_MAX_CODECS; ++i) { - cm->codecs[cm->codec_cnt++] = temp[i]; - } + mgr->codec_cnt = 0; - cf = cf->next; + factory = mgr->factory_list.next; + while (factory != &mgr->factory_list) { + unsigned count; + pj_status_t status; + + count = PJ_ARRAY_SIZE(mgr->codecs) - mgr->codec_cnt; + status = factory->op->enum_info(factory, &count, + mgr->codecs+mgr->codec_cnt); + if (status == PJ_SUCCESS) + mgr->codec_cnt += count; + + factory = factory->next; } } -PJ_DEF(pj_status_t) pj_codec_mgr_init (pj_codec_mgr *mgr) +/* + * Initialize codec manager. + */ +PJ_DEF(pj_status_t) pjmedia_codec_mgr_init (pjmedia_codec_mgr *mgr) { + PJ_ASSERT_RETURN(mgr, PJ_EINVAL); + pj_list_init (&mgr->factory_list); mgr->codec_cnt = 0; - return 0; + + return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pj_codec_mgr_register_factory (pj_codec_mgr *mgr, - pj_codec_factory *factory) +/* + * Register a codec factory. + */ +PJ_DEF(pj_status_t) +pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr, + pjmedia_codec_factory *factory) { - pj_list_insert_before (&mgr->factory_list, factory); + PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL); + + pj_list_push_back(&mgr->factory_list, factory); enum_all_codecs (mgr); - return 0; + + return PJ_SUCCESS; } -PJ_DEF(void) pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory) +/* + * Unregister a codec factory. + */ +PJ_DEF(pj_status_t) +pjmedia_codec_mgr_unregister_factory(pjmedia_codec_mgr *mgr, + pjmedia_codec_factory *factory) { - PJ_UNUSED_ARG(mgr); + + PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL); + + /* Factory must be registered. */ + PJ_ASSERT_RETURN(pj_list_find_node(&mgr->factory_list, factory)==factory, + PJ_ENOTFOUND); + + pj_list_erase(factory); enum_all_codecs (mgr); + + return PJ_SUCCESS; } -PJ_DEF(unsigned) -pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]) +/* + * Enum all codecs. + */ +PJ_DEF(pj_status_t) +pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr *mgr, + unsigned *count, + pjmedia_codec_info codecs[]) { - unsigned i; - - if (count > mgr->codec_cnt) - count = mgr->codec_cnt; + PJ_ASSERT_RETURN(mgr && count && codecs, PJ_EINVAL); - for (i=0; icodecs[i]; + if (*count > mgr->codec_cnt) + *count = mgr->codec_cnt; + + pj_memcpy(codecs, mgr->codecs, *count * sizeof(pjmedia_codec_info)); - return mgr->codec_cnt; + return PJ_SUCCESS; } -PJ_DEF(pj_codec*) pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id) +/* + * Allocate one codec. + */ +PJ_DEF(pj_status_t) pjmedia_codec_mgr_alloc_codec(pjmedia_codec_mgr *mgr, + const pjmedia_codec_info *info, + pjmedia_codec **p_codec) { - pj_codec_factory *factory = mgr->factory_list.next; + pjmedia_codec_factory *factory; + pj_status_t status; + + PJ_ASSERT_RETURN(mgr && info && p_codec, PJ_EINVAL); + + *p_codec = NULL; + + factory = mgr->factory_list.next; while (factory != &mgr->factory_list) { - if ( (*factory->op->match_id)(factory, id) == 0 ) { - pj_codec *codec = (*factory->op->alloc_codec)(factory, id); - if (codec != NULL) - return codec; + + if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) { + + status = (*factory->op->alloc_codec)(factory, info, p_codec); + if (status == PJ_SUCCESS) + return PJ_SUCCESS; + } + factory = factory->next; } - return NULL; + + + return PJMEDIA_CODEC_EUNSUP; } -PJ_DEF(void) pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec) +/* + * Dealloc codec. + */ +PJ_DEF(pj_status_t) pjmedia_codec_mgr_dealloc_codec(pjmedia_codec_mgr *mgr, + pjmedia_codec *codec) { - PJ_UNUSED_ARG(mgr); - (*codec->factory->op->dealloc_codec)(codec->factory, codec); + PJ_ASSERT_RETURN(mgr && codec, PJ_EINVAL); + + return (*codec->factory->op->dealloc_codec)(codec->factory, codec); } diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c new file mode 100644 index 00000000..0100bd3f --- /dev/null +++ b/pjmedia/src/pjmedia/endpoint.c @@ -0,0 +1,239 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +#define THIS_FILE "endpoint.c" + +static const pj_str_t STR_AUDIO = { "audio", 5}; +static const pj_str_t STR_VIDEO = { "video", 5}; +static const pj_str_t STR_IN = { "IN", 2 }; +static const pj_str_t STR_IP4 = { "IP4", 3}; +static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 }; +static const pj_str_t STR_SDP_NAME = { "pjmedia", 7 }; +static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; + + +PJ_DECL(pj_status_t) g711_init_factory (pjmedia_codec_factory *factory, pj_pool_t *pool); +PJ_DECL(pj_status_t) g711_deinit_factory (pjmedia_codec_factory *factory); + + +/** Concrete declaration of media endpoint. */ +struct pjmedia_endpt +{ + /** Pool. */ + pj_pool_t *pool; + + /** Pool factory. */ + pj_pool_factory *pf; + + /** Codec manager. */ + pjmedia_codec_mgr codec_mgr; +}; + +/** + * Initialize and get the instance of media endpoint. + */ +PJ_DEF(pj_status_t) pjmedia_endpt_create(pj_pool_factory *pf, + pjmedia_endpt **p_endpt) +{ + pj_pool_t *pool; + pjmedia_endpt *endpt; + pjmedia_codec_factory *factory; + pj_status_t status; + + PJ_ASSERT_RETURN(pf && p_endpt, PJ_EINVAL); + + pool = pj_pool_create(pf, "med-ept", 512, 512, NULL); + if (!pool) + return PJ_ENOMEM; + + endpt = pj_pool_zalloc(pool, sizeof(struct pjmedia_endpt)); + endpt->pool = pool; + endpt->pf = pf; + + /* Sound */ + pj_snd_init(pf); + + /* Init codec manager. */ + status = pjmedia_codec_mgr_init(&endpt->codec_mgr); + if (status != PJ_SUCCESS) { + pj_snd_deinit(); + goto on_error; + } + + /* Init and register G.711 codec. */ + factory = pj_pool_alloc (endpt->pool, sizeof(pjmedia_codec_factory)); + + status = g711_init_factory (factory, endpt->pool); + if (status != PJ_SUCCESS) { + pj_snd_deinit(); + goto on_error; + } + + status = pjmedia_codec_mgr_register_factory (&endpt->codec_mgr, factory); + if (status != PJ_SUCCESS) { + pj_snd_deinit(); + goto on_error; + } + + *p_endpt = endpt; + return PJ_SUCCESS; + +on_error: + pj_pool_release(pool); + return status; +} + +/** + * Get the codec manager instance. + */ +PJ_DEF(pjmedia_codec_mgr*) pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt) +{ + return &endpt->codec_mgr; +} + +/** + * Deinitialize media endpoint. + */ +PJ_DEF(pj_status_t) pjmedia_endpt_destroy (pjmedia_endpt *endpt) +{ + PJ_ASSERT_RETURN(endpt, PJ_EINVAL); + + endpt->pf = NULL; + + pj_snd_deinit(); + pj_pool_release (endpt->pool); + + return PJ_SUCCESS; +} + +/** + * Create pool. + */ +PJ_DEF(pj_pool_t*) pjmedia_endpt_create_pool( pjmedia_endpt *endpt, + const char *name, + pj_size_t initial, + pj_size_t increment) +{ + pj_assert(endpt != NULL); + + return pj_pool_create(endpt->pf, name, initial, increment, NULL); +} + +/** + * Create a SDP session description that describes the endpoint + * capability. + */ +PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt, + pj_pool_t *pool, + unsigned stream_cnt, + const pjmedia_sock_info sock_info[], + pjmedia_sdp_session **p_sdp ) +{ + pj_time_val tv; + unsigned i; + pjmedia_sdp_session *sdp; + pjmedia_sdp_media *m; + pjmedia_sdp_attr *attr; + + PJ_ASSERT_RETURN(endpt && pool && p_sdp && stream_cnt, PJ_EINVAL); + + + /* Create and initialize basic SDP session */ + sdp = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_session)); + + pj_gettimeofday(&tv); + sdp->origin.user = pj_str("-"); + sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL; + sdp->origin.net_type = STR_IN; + sdp->origin.addr_type = STR_IP4; + sdp->origin.addr = *pj_gethostname(); + sdp->name = STR_SDP_NAME; + + /* Since we only support one media stream at present, put the + * SDP connection line in the session level. + */ + sdp->conn = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_conn)); + sdp->conn->net_type = STR_IN; + sdp->conn->addr_type = STR_IP4; + pj_strdup2(pool, &sdp->conn->addr, + pj_inet_ntoa(sock_info[0].rtp_addr_name.sin_addr)); + + + /* SDP time and attributes. */ + sdp->time.start = sdp->time.stop = 0; + sdp->attr_count = 0; + + /* Create media stream 0: */ + + sdp->media_count = 1; + m = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_media)); + sdp->media[0] = m; + + /* Standard media info: */ + pj_strdup(pool, &m->desc.media, &STR_AUDIO); + m->desc.port = pj_ntohs(sock_info[0].rtp_addr_name.sin_port); + m->desc.port_count = 1; + pj_strdup (pool, &m->desc.transport, &STR_RTP_AVP); + + /* Add format and rtpmap for each codec. */ + m->desc.fmt_count = 0; + m->attr_count = 0; + + for (i=0; icodec_mgr.codec_cnt; ++i) { + + pjmedia_codec_info *codec_info = &endpt->codec_mgr.codecs[i]; + pjmedia_sdp_rtpmap rtpmap; + pjmedia_sdp_attr *attr; + pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++]; + + fmt->ptr = pj_pool_alloc(pool, 8); + fmt->slen = pj_utoa(codec_info->pt, fmt->ptr); + + rtpmap.pt = *fmt; + rtpmap.clock_rate = codec_info->sample_rate; + rtpmap.enc_name = codec_info->encoding_name; + rtpmap.param.slen = 0; + + pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); + m->attr[m->attr_count++] = attr; + + } + + /* Add sendrect attribute. */ + attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr)); + attr->name = STR_SENDRECV; + m->attr[m->attr_count++] = attr; + + + /* Done */ + *p_sdp = sdp; + + return PJ_SUCCESS; + +} + diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c index e42b5444..aca50aef 100644 --- a/pjmedia/src/pjmedia/errno.c +++ b/pjmedia/src/pjmedia/errno.c @@ -76,6 +76,15 @@ static const struct { PJMEDIA_SDP_EORIGINNOTEQUAL, "SDP origin line not equal" }, { PJMEDIA_SDP_ENAMENOTEQUAL, "SDP name/subject line not equal" }, { PJMEDIA_SDP_ETIMENOTEQUAL, "SDP time line not equal" }, + + /* Codec errors. */ + { PJMEDIA_CODEC_EUNSUP, "Unsupported media codec" }, + + /* Media errors. */ + { PJMEDIA_EINVALIDIP, "Invalid remote media (IP) address" }, + { PJMEDIA_EASYMCODEC, "Asymetric media codec is not supported" }, + { PJMEDIA_EINVALIDPT, "Invalid media payload type" }, + { PJMEDIA_EMISSINGRTPMAP, "Missing rtpmap in media description" }, }; diff --git a/pjmedia/src/pjmedia/g711.c b/pjmedia/src/pjmedia/g711.c index 9d79bccf..5844208f 100644 --- a/pjmedia/src/pjmedia/g711.c +++ b/pjmedia/src/pjmedia/g711.c @@ -20,6 +20,7 @@ * notice in the second half of this file. */ #include +#include #include #include #include @@ -29,8 +30,8 @@ #define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */ /* These are the only public functions exported to applications */ -PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool); -PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory); +PJ_DECL(pj_status_t) g711_init_factory (pjmedia_codec_factory *factory, pj_pool_t *pool); +PJ_DECL(pj_status_t) g711_deinit_factory (pjmedia_codec_factory *factory); /* Algorithm prototypes. */ static unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */ @@ -39,24 +40,24 @@ static unsigned char linear2ulaw(int pcm_val); static int ulaw2linear(unsigned char u_val); /* Prototypes for G711 factory */ -static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id ); -static pj_status_t g711_default_attr( pj_codec_factory *factory, const pj_codec_id *id, pj_codec_attr *attr ); -static unsigned g711_enum_codecs (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]); -static pj_codec* g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id); -static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec ); +static pj_status_t g711_match_id( pjmedia_codec_factory *factory, const pjmedia_codec_info *id ); +static pj_status_t g711_default_attr( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec_param *attr ); +static pj_status_t g711_enum_codecs (pjmedia_codec_factory *factory, unsigned *count, pjmedia_codec_info codecs[]); +static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory, const pjmedia_codec_info *id, pjmedia_codec **p_codec); +static pj_status_t g711_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ); /* Prototypes for G711 implementation. */ -static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr); -static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool ); -static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr ); -static pj_status_t g711_close( pj_codec *codec ); -static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output); -static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output); +static pj_status_t g711_codec_default_attr (pjmedia_codec *codec, pjmedia_codec_param *attr); +static pj_status_t g711_init( pjmedia_codec *codec, pj_pool_t *pool ); +static pj_status_t g711_open( pjmedia_codec *codec, pjmedia_codec_param *attr ); +static pj_status_t g711_close( pjmedia_codec *codec ); +static pj_status_t g711_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, + unsigned output_buf_len, struct pjmedia_frame *output); +static pj_status_t g711_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, + unsigned output_buf_len, struct pjmedia_frame *output); /* Definition for G711 codec operations. */ -static pj_codec_op g711_op = +static pjmedia_codec_op g711_op = { &g711_codec_default_attr , &g711_init, @@ -67,7 +68,7 @@ static pj_codec_op g711_op = }; /* Definition for G711 codec factory operations. */ -static pj_codec_factory_op g711_factory_op = +static pjmedia_codec_factory_op g711_factory_op = { &g711_match_id, &g711_default_attr, @@ -80,7 +81,7 @@ static pj_codec_factory_op g711_factory_op = struct g711_factory_private { pj_pool_t *pool; - pj_codec codec_list; + pjmedia_codec codec_list; }; /* G711 codec private data. */ @@ -90,10 +91,10 @@ struct g711_private }; -PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool) +PJ_DEF(pj_status_t) g711_init_factory (pjmedia_codec_factory *factory, pj_pool_t *pool) { struct g711_factory_private *priv; - //enum { CODEC_MEM_SIZE = sizeof(pj_codec) + sizeof(struct g711_private) + 4 }; + //enum { CODEC_MEM_SIZE = sizeof(pjmedia_codec) + sizeof(struct g711_private) + 4 }; /* Create pool. */ /* @@ -117,7 +118,7 @@ PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *poo return 0; } -PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory) +PJ_DEF(pj_status_t) g711_deinit_factory (pjmedia_codec_factory *factory) { struct g711_factory_private *priv = factory->factory_data; @@ -127,21 +128,21 @@ PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory) return 0; } -static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id ) +static pj_status_t g711_match_id( pjmedia_codec_factory *factory, const pjmedia_codec_info *id ) { PJ_UNUSED_ARG(factory); /* It's sufficient to check payload type only. */ - return (id->pt==PJ_RTP_PT_PCMU || id->pt==PJ_RTP_PT_PCMA) ? 0 : -1; + return (id->pt==PJMEDIA_RTP_PT_PCMU || id->pt==PJMEDIA_RTP_PT_PCMA) ? 0 : -1; } -static pj_status_t g711_default_attr (pj_codec_factory *factory, - const pj_codec_id *id, - pj_codec_attr *attr ) +static pj_status_t g711_default_attr (pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec_param *attr ) { PJ_UNUSED_ARG(factory); - memset(attr, 0, sizeof(pj_codec_attr)); + memset(attr, 0, sizeof(pjmedia_codec_param)); attr->sample_rate = 8000; attr->avg_bps = G711_BPS; attr->pcm_bits_per_sample = 16; @@ -153,40 +154,46 @@ static pj_status_t g711_default_attr (pj_codec_factory *factory, return PJ_SUCCESS; } -static unsigned g711_enum_codecs (pj_codec_factory *factory, - unsigned count, pj_codec_id codecs[]) +static pj_status_t g711_enum_codecs(pjmedia_codec_factory *factory, + unsigned *count, + pjmedia_codec_info codecs[]) { PJ_UNUSED_ARG(factory); - if (count > 0) { - codecs[0].type = PJ_MEDIA_TYPE_AUDIO; - codecs[0].pt = PJ_RTP_PT_PCMU; + if (*count > 0) { + codecs[0].type = PJMEDIA_TYPE_AUDIO; + codecs[0].pt = PJMEDIA_RTP_PT_PCMU; codecs[0].encoding_name = pj_str("PCMU"); codecs[0].sample_rate = 8000; } - if (count > 1) { - codecs[1].type = PJ_MEDIA_TYPE_AUDIO; - codecs[1].pt = PJ_RTP_PT_PCMA; + if (*count > 1) { + codecs[1].type = PJMEDIA_TYPE_AUDIO; + codecs[1].pt = PJMEDIA_RTP_PT_PCMA; codecs[1].encoding_name = pj_str("PCMA"); codecs[1].sample_rate = 8000; } - return 2; + if (*count > 0) *count=1; + if (*count > 1) *count=2; + + return PJ_SUCCESS; } -static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id) +static pj_status_t g711_alloc_codec( pjmedia_codec_factory *factory, + const pjmedia_codec_info *id, + pjmedia_codec **p_codec) { struct g711_factory_private *priv = factory->factory_data; - pj_codec *codec = NULL; + pjmedia_codec *codec = NULL; /* Allocate new codec if no more is available */ if (pj_list_empty(&priv->codec_list)) { struct g711_private *codec_priv; - codec = pj_pool_alloc(priv->pool, sizeof(pj_codec)); + codec = pj_pool_alloc(priv->pool, sizeof(pjmedia_codec)); codec_priv = pj_pool_alloc(priv->pool, sizeof(struct g711_private)); if (!codec || !codec_priv) - return NULL; + return PJ_ENOMEM; codec_priv->pt = id->pt; @@ -201,33 +208,36 @@ static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id /* Zero the list, for error detection in g711_dealloc_codec */ codec->next = codec->prev = NULL; - return codec; + *p_codec = codec; + return PJ_SUCCESS; } -static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec ) +static pj_status_t g711_dealloc_codec( pjmedia_codec_factory *factory, pjmedia_codec *codec ) { struct g711_factory_private *priv = factory->factory_data; /* Check that this node has not been deallocated before */ pj_assert (codec->next==NULL && codec->prev==NULL); if (codec->next!=NULL || codec->prev!=NULL) { - return; + return PJ_EINVALIDOP; } /* Insert at the back of the list */ pj_list_insert_before(&priv->codec_list, codec); + + return PJ_SUCCESS; } -static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr) +static pj_status_t g711_codec_default_attr (pjmedia_codec *codec, pjmedia_codec_param *attr) { struct g711_private *priv = codec->codec_data; - pj_codec_id id; + pjmedia_codec_info id; id.pt = priv->pt; return g711_default_attr (NULL, &id, attr); } -static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool ) +static pj_status_t g711_init( pjmedia_codec *codec, pj_pool_t *pool ) { /* There's nothing to do here really */ PJ_UNUSED_ARG(codec); @@ -236,22 +246,22 @@ static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool ) return PJ_SUCCESS; } -static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr ) +static pj_status_t g711_open( pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct g711_private *priv = codec->codec_data; priv->pt = attr->pt; return PJ_SUCCESS; } -static pj_status_t g711_close( pj_codec *codec ) +static pj_status_t g711_close( pjmedia_codec *codec ) { PJ_UNUSED_ARG(codec); /* Nothing to do */ return PJ_SUCCESS; } -static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output) +static pj_status_t g711_encode( pjmedia_codec *codec, const struct pjmedia_frame *input, + unsigned output_buf_len, struct pjmedia_frame *output) { pj_int16_t *samples = (pj_int16_t*) input->buf; struct g711_private *priv = codec->codec_data; @@ -261,14 +271,14 @@ static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *i return -1; /* Encode */ - if (priv->pt == PJ_RTP_PT_PCMA) { + if (priv->pt == PJMEDIA_RTP_PT_PCMA) { unsigned i; pj_uint8_t *dst = output->buf; for (i=0; i!=input->size/2; ++i, ++dst) { *dst = linear2alaw(samples[i]); } - } else if (priv->pt == PJ_RTP_PT_PCMU) { + } else if (priv->pt == PJMEDIA_RTP_PT_PCMU) { unsigned i; pj_uint8_t *dst = output->buf; @@ -280,14 +290,14 @@ static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *i return -1; } - output->type = PJ_AUDIO_FRAME_AUDIO; + output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = input->size / 2; return 0; } -static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output) +static pj_status_t g711_decode( pjmedia_codec *codec, const struct pjmedia_frame *input, + unsigned output_buf_len, struct pjmedia_frame *output) { struct g711_private *priv = codec->codec_data; @@ -296,7 +306,7 @@ static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *i return -1; /* Decode */ - if (priv->pt == PJ_RTP_PT_PCMA) { + if (priv->pt == PJMEDIA_RTP_PT_PCMA) { unsigned i; pj_uint8_t *src = input->buf; pj_uint16_t *dst = output->buf; @@ -304,7 +314,7 @@ static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *i for (i=0; i!=input->size; ++i) { *dst++ = (pj_uint16_t) alaw2linear(*src++); } - } else if (priv->pt == PJ_RTP_PT_PCMU) { + } else if (priv->pt == PJMEDIA_RTP_PT_PCMU) { unsigned i; pj_uint8_t *src = input->buf; pj_uint16_t *dst = output->buf; @@ -317,7 +327,7 @@ static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *i return -1; } - output->type = PJ_AUDIO_FRAME_AUDIO; + output->type = PJMEDIA_FRAME_TYPE_AUDIO; output->size = input->size * 2; return 0; diff --git a/pjmedia/src/pjmedia/mediamgr.c b/pjmedia/src/pjmedia/mediamgr.c deleted file mode 100644 index e43e742d..00000000 --- a/pjmedia/src/pjmedia/mediamgr.c +++ /dev/null @@ -1,112 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * 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 - */ -#include -#include -#include -#include - -PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool); -PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory); - -/** Concrete declaration of media manager. */ -struct pj_med_mgr_t -{ - /** Pool. */ - pj_pool_t *pool; - - /** Pool factory. */ - pj_pool_factory *pf; - - /** Codec manager. */ - pj_codec_mgr codec_mgr; -}; - -/** - * Initialize and get the instance of media manager. - */ -PJ_DEF(pj_med_mgr_t*) pj_med_mgr_create ( pj_pool_factory *pf) -{ - pj_pool_t *pool; - pj_med_mgr_t *mm; - pj_codec_factory *cf; - pj_status_t status; - - pool = pj_pool_create(pf, "mediamgr", 512, 512, NULL); - if (!pool) - return NULL; - - mm = pj_pool_calloc(pool, 1, sizeof(struct pj_med_mgr_t)); - mm->pool = pool; - mm->pf = pf; - - /* Sound */ - pj_snd_init(pf); - - /* Init codec manager. */ - status = pj_codec_mgr_init(&mm->codec_mgr); - if (status != 0) { - pj_snd_deinit(); - goto on_error; - } - - /* Init and register G.711 codec. */ - cf = pj_pool_alloc (mm->pool, sizeof(pj_codec_factory)); - - status = g711_init_factory (cf, mm->pool); - if (status != 0) { - pj_snd_deinit(); - return NULL; - } - - status = pj_codec_mgr_register_factory (&mm->codec_mgr, cf); - if (status != 0) - return NULL; - - return mm; - -on_error: - pj_pool_release(pool); - return NULL; -} - -/** - * Get the codec manager instance. - */ -PJ_DEF(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr) -{ - return &mgr->codec_mgr; -} - -/** - * Deinitialize media manager. - */ -PJ_DEF(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr) -{ - pj_snd_deinit(); - pj_pool_release (mgr->pool); - return 0; -} - -/** - * Get pool factory. - */ -PJ_DEF(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr) -{ - return mgr->pf; -} diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c index 5f45775a..14cc065b 100644 --- a/pjmedia/src/pjmedia/rtcp.c +++ b/pjmedia/src/pjmedia/rtcp.c @@ -92,7 +92,7 @@ PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp /* Update sequence numbers (received, lost, etc). */ status = pj_rtp_seq_update(&s->seq_ctrl, seq); - if (status == PJ_RTP_ERR_SESSION_RESTARTED) { + if (status == PJMEDIA_RTP_ERR_SESSION_RESTARTED) { rtcp_init_seq(s, seq); status = 0; } diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c index 5a337937..8d653698 100644 --- a/pjmedia/src/pjmedia/rtp.c +++ b/pjmedia/src/pjmedia/rtp.c @@ -43,7 +43,7 @@ PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses, /* Check RTP header packing. */ if (sizeof(struct pj_rtp_hdr) != 12) { pj_assert(!"Wrong RTP header packing!"); - return PJ_RTP_ERR_RTP_PACKING; + return PJMEDIA_RTP_ERR_RTP_PACKING; } /* If sender_ssrc is not specified, create from time value. */ @@ -128,7 +128,7 @@ PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, /* Check RTP header sanity. */ if ((*hdr)->v != RTP_VERSION) { PJ_LOG(4, (THIS_FILE, " invalid RTP version!")); - return PJ_RTP_ERR_INVALID_VERSION; + return PJMEDIA_RTP_ERR_INVALID_VERSION; } /* Payload is located right after header plus CSRC */ @@ -142,7 +142,7 @@ PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, /* Check that offset is less than packet size */ if (offset >= pkt_len) - return PJ_RTP_ERR_INVALID_PACKET; + return PJMEDIA_RTP_ERR_INVALID_PACKET; /* Find and set payload. */ *payload = ((pj_uint8_t*)pkt) + offset; @@ -162,7 +162,7 @@ PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) { PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)", ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc)); - return PJ_RTP_ERR_INVALID_SSRC; + return PJMEDIA_RTP_ERR_INVALID_SSRC; } */ @@ -170,7 +170,7 @@ PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr if (hdr->pt != ses->out_pt) { PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)", ses, hdr->pt, ses->out_pt)); - return PJ_RTP_ERR_INVALID_PT; + return PJMEDIA_RTP_ERR_INVALID_PT; } /* Initialize sequence number on first packet received. */ @@ -179,10 +179,10 @@ PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr /* Check sequence number to see if remote session has been restarted. */ status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq)); - if (status == PJ_RTP_ERR_SESSION_RESTARTED) { + if (status == PJMEDIA_RTP_ERR_SESSION_RESTARTED) { pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq)); ++ses->received; - } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) { + } else if (status == 0 || status == PJMEDIA_RTP_ERR_SESSION_PROBATION) { ++ses->received; } @@ -223,13 +223,13 @@ int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq) sctrl->probation--; sctrl->max_seq = seq; if (sctrl->probation == 0) { - return PJ_RTP_ERR_SESSION_RESTARTED; + return PJMEDIA_RTP_ERR_SESSION_RESTARTED; } } else { sctrl->probation = MIN_SEQUENTIAL - 1; sctrl->max_seq = seq; } - return PJ_RTP_ERR_SESSION_PROBATION; + return PJMEDIA_RTP_ERR_SESSION_PROBATION; } else if (udelta < MAX_DROPOUT) { /* in order, with permissible gap */ @@ -247,11 +247,11 @@ int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq) * restarted without telling us so just re-sync * (i.e., pretend this was the first packet). */ - return PJ_RTP_ERR_SESSION_RESTARTED; + return PJMEDIA_RTP_ERR_SESSION_RESTARTED; } else { sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); - return PJ_RTP_ERR_BAD_SEQUENCE; + return PJMEDIA_RTP_ERR_BAD_SEQUENCE; } } else { /* duplicate or reordered packet */ diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c index 996ed474..1ddb13b4 100644 --- a/pjmedia/src/pjmedia/session.c +++ b/pjmedia/src/pjmedia/session.c @@ -17,31 +17,28 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #include #include #include #include #include +#include -typedef struct pj_media_stream_desc -{ - pj_media_stream_info info; - pj_media_stream_t *enc_stream, *dec_stream; -} pj_media_stream_desc; - -struct pj_media_session_t +struct pjmedia_session { pj_pool_t *pool; - pj_med_mgr_t *mediamgr; + pjmedia_endpt *endpt; unsigned stream_cnt; - pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA]; + pjmedia_stream_info stream_info[PJSDP_MAX_MEDIA]; + pjmedia_stream *stream[PJSDP_MAX_MEDIA]; }; #define THIS_FILE "session.c" -#define PJ_MEDIA_SESSION_SIZE (48*1024) -#define PJ_MEDIA_SESSION_INC 1024 +#define PJMEDIA_SESSION_SIZE (48*1024) +#define PJMEDIA_SESSION_INC 1024 static const pj_str_t ID_AUDIO = { "audio", 5}; static const pj_str_t ID_VIDEO = { "video", 5}; @@ -49,787 +46,350 @@ static const pj_str_t ID_IN = { "IN", 2 }; static const pj_str_t ID_IP4 = { "IP4", 3}; static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; +static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; -static void session_init (pj_media_session_t *ses) -{ - pj_memset (ses, 0, sizeof(pj_media_session_t)); -} +static const pj_str_t STR_INACTIVE = { "inactive", 8 }; +static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; +static const pj_str_t STR_SENDONLY = { "sendonly", 8 }; +static const pj_str_t STR_RECVONLY = { "recvonly", 8 }; -/** - * Create new session offering. +/* + * Create stream info from SDP media line. */ -PJ_DEF(pj_media_session_t*) -pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info) +static pj_status_t create_stream_info_from_sdp(pj_pool_t *pool, + pjmedia_stream_info *si, + const pjmedia_sdp_conn *local_conn, + const pjmedia_sdp_conn *rem_conn, + const pjmedia_sdp_media *local_m, + const pjmedia_sdp_media *rem_m) { - pj_pool_factory *pf; - pj_pool_t *pool; - pj_media_session_t *session; - pj_media_stream_desc *sd; - unsigned i, codec_cnt; - pj_codec_mgr *cm; - const pj_codec_id *codecs[PJSDP_MAX_FMT]; - - pf = pj_med_mgr_get_pool_factory(mgr); + const pjmedia_sdp_attr *attr; + pjmedia_sdp_rtpmap *rtpmap; + pj_status_t status; - pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); - if (!pool) - return NULL; - session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); - if (!session) - return NULL; + /* Validate arguments: */ - session_init (session); + PJ_ASSERT_RETURN(pool && si && local_conn && rem_conn && + local_m && rem_m, PJ_EINVAL); - session->pool = pool; - session->mediamgr = mgr; - - /* Create first stream */ - sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); - if (!sd) - return NULL; - - sd->info.type = ID_AUDIO; - sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; - sd->info.transport = ID_RTP_AVP; - pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); - - /* Enum audio codecs. */ - sd->info.fmt_cnt = 0; - cm = pj_med_mgr_get_codec_mgr (mgr); - codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs); - if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; - for (i=0; itype != PJ_MEDIA_TYPE_AUDIO) - continue; - - sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt; - sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate; - pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name); - ++sd->info.fmt_cnt; - } + /* Reset: */ - session->stream_desc[session->stream_cnt++] = sd; + pj_memset(si, 0, sizeof(*si)); - return session; -} + /* Media type: */ -static int sdp_check (const pjsdp_session_desc *sdp) -{ - int has_conn = 0; - unsigned i; + if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) { - if (sdp->conn) - has_conn = 1; + si->type = PJMEDIA_TYPE_AUDIO; - if (sdp->media_count == 0) { - PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition")); - return -1; - } + } else if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0) { - for (i=0; imedia_count; ++i) { - pjsdp_media_desc *m = sdp->media[i]; + si->type = PJMEDIA_TYPE_VIDEO; - if (!m) { - pj_assert(0); - return -1; - } + } else { - if (m->desc.fmt_count == 0) { - PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream")); - return -1; - } + si->type = PJMEDIA_TYPE_UNKNOWN; - if (!has_conn && m->conn == NULL) { - PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media")); - return -1; - } } - return 0; -} - -/* - * Create local stream definition that matches SDP received from peer. - */ -static pj_media_stream_desc* -create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn, - const pjsdp_media_desc *m, const pj_media_sock_info *sock_info) -{ - pj_media_stream_desc *sd; - - sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); - if (!sd) { - PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor")); - return NULL; - } + /* Media direction: */ - if (pj_stricmp(&conn->net_type, &ID_IN)==0 && - pj_stricmp(&conn->addr_type, &ID_IP4)==0 && - pj_stricmp(&m->desc.media, &ID_AUDIO)==0 && - pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0) + if (local_m->desc.port == 0 || + pj_inet_addr(&local_conn->addr).s_addr==0 || + pj_inet_addr(&rem_conn->addr).s_addr==0 || + pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) { - /* - * Got audio stream. - */ - unsigned i, codec_cnt; - pj_codec_mgr *cm; - const pj_codec_id *codecs[PJSDP_MAX_FMT]; - - sd->info.type = ID_AUDIO; - sd->info.transport = ID_RTP_AVP; - pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); - sd->info.rem_port = m->desc.port; - pj_strdup (pool, &sd->info.rem_addr, &conn->addr); - - /* Enum audio codecs. */ - sd->info.fmt_cnt = 0; - cm = pj_med_mgr_get_codec_mgr (mgr); - codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs); - if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; - - /* Find just one codec which we can support. */ - for (i=0; idesc.fmt_count && sd->info.fmt_cnt == 0; ++i) { - unsigned j, fmt_i; - - /* For static payload, just match payload type. */ - /* Else match clock rate and encoding name. */ - fmt_i = pj_strtoul(&m->desc.fmt[i]); - if (fmt_i < PJ_RTP_PT_DYNAMIC) { - for (j=0; jpt == fmt_i) { - sd->info.fmt_cnt = 1; - sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; - sd->info.fmt[0].pt = codecs[j]->pt; - sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; - pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name); - break; - } - } - } else { - - /* Find the rtpmap for the payload type. */ - const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i); - - /* Don't accept the media if no rtpmap for dynamic PT. */ - if (rtpmap == NULL) { - PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i])); - continue; - } - - /* Check whether we can take this codec. */ - for (j=0; jclock_rate == codecs[j]->sample_rate && - pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0) - { - sd->info.fmt_cnt = 1; - sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; - sd->info.fmt[0].pt = codecs[j]->pt; - sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; - sd->info.fmt[0].encoding_name = codecs[j]->encoding_name; - break; - } - } - } - } + /* Inactive stream. */ - /* Match codec and direction. */ - if (sd->info.fmt_cnt == 0 || m->desc.port == 0 || - pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) - { - sd->info.dir = PJ_MEDIA_DIR_NONE; - } - else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { - sd->info.dir = PJ_MEDIA_DIR_ENCODING; - } - else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { - sd->info.dir = PJ_MEDIA_DIR_DECODING; - } - else { - sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; - } + si->dir = PJMEDIA_DIR_NONE; - } else { - /* Unsupported media stream. */ - unsigned fmt_num; - const pjsdp_rtpmap_attr *rtpmap = NULL; - - pj_strdup(pool, &sd->info.type, &m->desc.media); - pj_strdup(pool, &sd->info.transport, &m->desc.transport); - pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info)); - pj_strdup (pool, &sd->info.rem_addr, &conn->addr); - sd->info.rem_port = m->desc.port; - - /* Just put one format and rtpmap, so that we don't have to make - * special exception when we convert this stream to SDP. - */ - - /* Find the rtpmap for the payload type. */ - fmt_num = pj_strtoul(&m->desc.fmt[0]); - rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num); - - sd->info.fmt_cnt = 1; - if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) { - sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO; - sd->info.fmt[0].pt = fmt_num; - if (rtpmap) { - pj_strdup (pool, &sd->info.fmt[0].encoding_name, - &rtpmap->encoding_name); - sd->info.fmt[0].sample_rate = rtpmap->clock_rate; - } - } else { - sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN; - pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]); - } - - sd->info.dir = PJ_MEDIA_DIR_NONE; - } + } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { - return sd; -} + /* Send only stream. */ -/** - * Create new session based on peer's offering. - */ -PJ_DEF(pj_media_session_t*) -pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, - const pj_media_sock_info *sock_info) -{ - pj_pool_factory *pf; - pj_pool_t *pool; - pj_media_session_t *session; - unsigned i; + si->dir = PJMEDIA_DIR_ENCODING; - if (sdp_check(sdp) != 0) - return NULL; + } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { - pf = pj_med_mgr_get_pool_factory(mgr); - pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); - if (!pool) - return NULL; + /* Recv only stream. */ - session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); - if (!session) { - PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); - pj_pool_release (pool); - return NULL; - } + si->dir = PJMEDIA_DIR_DECODING; + + } else { - session_init (session); + /* Send and receive stream. */ - session->pool = pool; - session->mediamgr = mgr; + si->dir = PJMEDIA_DIR_ENCODING_DECODING; - /* Enumerate each media stream and create our peer. */ - for (i=0; imedia_count; ++i) { - const pjsdp_conn_info *conn; - const pjsdp_media_desc *m; - pj_media_stream_desc *sd; + } - m = sdp->media[i]; - conn = m->conn ? m->conn : sdp->conn; - /* - * Bug: - * the sock_info below is used by more than one 'm' lines - */ - PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES) + /* Set remote address: */ - sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info); - pj_assert (sd); + si->rem_addr.sin_family = PJ_AF_INET; + si->rem_addr.sin_port = pj_htons(rem_m->desc.port); + if (pj_inet_aton(&rem_conn->addr, &si->rem_addr.sin_addr) == 0) { - session->stream_desc[session->stream_cnt++] = sd; + /* Invalid IP address. */ + return PJMEDIA_EINVALIDIP; } - return session; -} + /* For this version of PJMEDIA, send and receive media must use + * the same codec. + */ + if (pj_strcmp(&local_m->desc.fmt[0], &rem_m->desc.fmt[0]) != 0) + return PJMEDIA_EASYMCODEC; -/** - * Duplicate session. The new session is inactive. - */ -PJ_DEF(pj_media_session_t*) -pj_media_session_clone (const pj_media_session_t *rhs) -{ - pj_pool_factory *pf; - pj_pool_t *pool; - pj_media_session_t *session; - unsigned i; - pf = pj_med_mgr_get_pool_factory(rhs->mediamgr); - pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); - if (!pool) { - return NULL; - } + /* And codec must be numeric! */ + if (!pj_isdigit(*local_m->desc.fmt[0].ptr)) + return PJMEDIA_EINVALIDPT; - session = pj_pool_alloc (pool, sizeof(*session)); - if (!session) { - PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); - pj_pool_release (pool); - return NULL; - } + /* Find rtpmap for the first codec. + * For this version of PJMEDIA, we do not support static payload + * type without rtpmap. + */ + attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, NULL); + if (attr == NULL) + return PJMEDIA_EMISSINGRTPMAP; - session->pool = pool; - session->mediamgr = rhs->mediamgr; - session->stream_cnt = rhs->stream_cnt; + status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); + if (status != PJ_SUCCESS) + return status; - for (i=0; istream_cnt; ++i) { - pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc)); - const pj_media_stream_desc *sd2 = rhs->stream_desc[i]; + /* Build codec format info: */ - if (!sd1) { - PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor")); - pj_pool_release (pool); - return NULL; - } + si->fmt.type = si->type; + si->fmt.pt = pj_strtoul(&local_m->desc.fmt[0]); + pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); + si->fmt.sample_rate = rtpmap->clock_rate; - session->stream_desc[i] = sd1; - sd1->enc_stream = sd1->dec_stream = NULL; - pj_strdup (pool, &sd1->info.type, &sd2->info.type); - sd1->info.dir = sd2->info.dir; - pj_strdup (pool, &sd1->info.transport, &sd2->info.transport); - pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info)); - pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr); - sd1->info.rem_port = sd2->info.rem_port; - sd1->info.fmt_cnt = sd2->info.fmt_cnt; - pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt)); - } + /* Leave SSRC to zero. */ - return session; + /* Leave jitter buffer parameter. */ + + return PJ_SUCCESS; } + /** - * Create SDP description from the session. + * Create new session. */ -PJ_DEF(pjsdp_session_desc*) -pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool, - pj_bool_t only_first_fmt) +PJ_DEF(pj_status_t) pjmedia_session_create( pjmedia_endpt *endpt, + unsigned stream_cnt, + const pjmedia_sock_info skinfo[], + const pjmedia_sdp_session *local_sdp, + const pjmedia_sdp_session *rem_sdp, + pjmedia_session **p_session ) { - pjsdp_session_desc *sdp; - pj_time_val tv; - unsigned i; - pj_media_sock_info *c_addr = NULL; + pj_pool_t *pool; + pjmedia_session *session; + int i; /* Must be signed */ + pj_status_t status; - if (session->stream_cnt == 0) { - pj_assert(0); - return NULL; - } + /* Verify arguments. */ + PJ_ASSERT_RETURN(endpt && stream_cnt && skinfo && + local_sdp && rem_sdp && p_session, PJ_EINVAL); - sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc)); - if (!sdp) { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor")); - return NULL; - } + /* Create pool for the session. */ + pool = pjmedia_endpt_create_pool( endpt, "session", + PJMEDIA_SESSION_SIZE, + PJMEDIA_SESSION_INC); + PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); - pj_gettimeofday(&tv); + session = pj_pool_zalloc(pool, sizeof(pjmedia_session)); + session->pool = pool; + session->endpt = endpt; + session->stream_cnt = stream_cnt; + + /* Stream count is the lower number of stream_cnt or SDP m= lines count */ + if (stream_cnt < local_sdp->media_count) + stream_cnt = local_sdp->media_count; - sdp->origin.user = pj_str("-"); - sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL; - sdp->origin.net_type = ID_IN; - sdp->origin.addr_type = ID_IP4; - sdp->origin.addr = *pj_gethostname(); + /* + * Create streams: + */ + for (i=0; i<(int)stream_cnt; ++i) { - sdp->name = ID_SDP_NAME; + pjmedia_stream_info *si = &session->stream_info[i]; + const pjmedia_sdp_media *local_m = local_sdp->media[i]; + const pjmedia_sdp_media *rem_m = rem_sdp->media[i]; + pjmedia_sdp_conn *local_conn, *rem_conn; - /* If all media addresses are the same, then put the connection - * info in the session level, otherwise put it in media stream - * level. - */ - for (i=0; istream_cnt; ++i) { - if (c_addr == NULL) { - c_addr = &session->stream_desc[i]->info.sock_info; - } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr) - { - c_addr = NULL; - break; - } - } + /* Build stream info based on media line in local SDP */ + local_conn = local_m->conn ? local_m->conn : local_sdp->conn; + rem_conn = rem_m->conn ? rem_m->conn : rem_sdp->conn; - if (c_addr) { - /* All addresses are the same, put connection info in session level. */ - sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); - if (!sdp->conn) { - PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info")); - return NULL; - } + status = create_stream_info_from_sdp(session->pool, si, + local_conn, rem_conn, + local_m, rem_m); + if (status != PJ_SUCCESS) + return status; - sdp->conn->net_type = ID_IN; - sdp->conn->addr_type = ID_IP4; - pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr)); + /* Assign sockinfo */ + si->sock_info = skinfo[i]; } - sdp->time.start = sdp->time.stop = 0; - sdp->attr_count = 0; - - /* Create each media. */ - sdp->media_count = 0; - for (i=0; istream_cnt; ++i) { - const pj_media_stream_desc *sd = session->stream_desc[i]; - pjsdp_media_desc *m; - unsigned j; - unsigned fmt_cnt; - pjsdp_attr *attr; - - m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc)); - if (!m) { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor")); - return NULL; - } + /* + * Now create the stream! + */ + for (i=0; i<(int)stream_cnt; ++i) { - sdp->media[sdp->media_count++] = m; - - pj_strdup (pool, &m->desc.media, &sd->info.type); - m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port); - m->desc.port_count = 1; - pj_strdup (pool, &m->desc.transport, &sd->info.transport); - - /* Add format and rtpmap for each codec. */ - m->desc.fmt_count = 0; - m->attr_count = 0; - fmt_cnt = sd->info.fmt_cnt; - if (fmt_cnt > 0 && only_first_fmt) - fmt_cnt = 1; - for (j=0; jdesc.fmt[m->desc.fmt_count++]; - - if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) { - pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name); - } else { - fmt->ptr = pj_pool_alloc(pool, 8); - fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr); - - rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr)); - if (rtpmap) { - m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap; - rtpmap->type = PJSDP_ATTR_RTPMAP; - rtpmap->payload_type = sd->info.fmt[j].pt; - rtpmap->clock_rate = sd->info.fmt[j].sample_rate; - pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name); - } else { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor")); - } - } - } + status = pjmedia_stream_create(endpt, session->pool, + &session->stream_info[i], + &session->stream[i]); + if (status != PJ_SUCCESS) { - /* If we don't have connection info in session level, create one. */ - if (sdp->conn == NULL) { - m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); - if (m->conn) { - m->conn->net_type = ID_IN; - m->conn->addr_type = ID_IP4; - pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr)); - } else { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info")); - return NULL; + for ( --i; i>=0; ++i) { + pjmedia_stream_destroy(session->stream[i]); } - } - /* Add additional attribute to the media stream. */ - attr = pj_pool_alloc(pool, sizeof(pjsdp_attr)); - if (!attr) { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute")); - return NULL; - } - m->attr[m->attr_count++] = attr; - - switch (sd->info.dir) { - case PJ_MEDIA_DIR_NONE: - attr->type = PJSDP_ATTR_INACTIVE; - break; - case PJ_MEDIA_DIR_ENCODING: - attr->type = PJSDP_ATTR_SEND_ONLY; - break; - case PJ_MEDIA_DIR_DECODING: - attr->type = PJSDP_ATTR_RECV_ONLY; - break; - case PJ_MEDIA_DIR_ENCODING_DECODING: - attr->type = PJSDP_ATTR_SEND_RECV; - break; + pj_pool_release(session->pool); + return status; } } - return sdp; + + /* Done. */ + + *p_session = session; + return PJ_SUCCESS; } + /** - * Update session with SDP answer from peer. + * Destroy media session. */ -PJ_DEF(pj_status_t) -pj_media_session_update (pj_media_session_t *session, - const pjsdp_session_desc *sdp) +PJ_DEF(pj_status_t) pjmedia_session_destroy (pjmedia_session *session) { unsigned i; - unsigned count; - - /* Check SDP */ - if (sdp_check (sdp) != 0) { - return -1; - } - - /* If the media stream count doesn't match, only update one. */ - if (session->stream_cnt != sdp->media_count) { - PJ_LOG(3,(THIS_FILE, "pj_media_session_update : " - "SDP media count mismatch! (rmt=%d, lcl=%d)", - sdp->media_count, session->stream_cnt)); - count = (session->stream_cnt < sdp->media_count) ? - session->stream_cnt : sdp->media_count; - } else { - count = session->stream_cnt; - } - - for (i=0; istream_desc[i]; - const pjsdp_media_desc *m = sdp->media[i]; - const pjsdp_conn_info *conn; - unsigned j; - - /* Check that the session is not active. */ - pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL); - - conn = m->conn ? m->conn : sdp->conn; - pj_assert(conn); - - /* Update remote address. */ - sd->info.rem_port = m->desc.port; - pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr); - - /* Select one active codec according to what peer wants. */ - for (j=0; jinfo.fmt_cnt; ++j) { - unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]); - if (sd->info.fmt[j].pt == fmt_0) { - pj_codec_id temp; - - /* Put active format to the front. */ - if (j == 0) - break; - pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp)); - pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp)); - pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp)); - break; - } - } + PJ_ASSERT_RETURN(session, PJ_EINVAL); - if (j == sd->info.fmt_cnt) { - /* Peer has answered SDP with new codec, which doesn't exist - * in the offer! - * Mute this media. - */ - PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!")); - sd->info.dir = PJ_MEDIA_DIR_NONE; - continue; - } + for (i=0; istream_cnt; ++i) { + + pjmedia_stream_destroy(session->stream[i]); - /* Check direction. */ - if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) { - sd->info.dir = PJ_MEDIA_DIR_NONE; - } - else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { - sd->info.dir = PJ_MEDIA_DIR_ENCODING; - } - else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { - sd->info.dir = PJ_MEDIA_DIR_DECODING; - } - else { - sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; - } } - return 0; + pj_pool_release (session->pool); + + return PJ_SUCCESS; } + /** - * Enumerate media streams in the session. + * Activate all stream in media session. + * */ -PJ_DEF(unsigned) -pj_media_session_enum_streams (const pj_media_session_t *session, - unsigned count, const pj_media_stream_info *info[]) +PJ_DEF(pj_status_t) pjmedia_session_resume(pjmedia_session *session, + pjmedia_dir dir) { unsigned i; - if (count > session->stream_cnt) - count = session->stream_cnt; + PJ_ASSERT_RETURN(session, PJ_EINVAL); - for (i=0; istream_desc[i]->info; + for (i=0; istream_cnt; ++i) { + pjmedia_session_resume_stream(session, i, dir); } - return session->stream_cnt; + return PJ_SUCCESS; } + /** - * Get statistics + * Suspend receipt and transmission of all stream in media session. + * */ -PJ_DEF(pj_status_t) -pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, - pj_media_stream_stat *tx_stat, - pj_media_stream_stat *rx_stat) +PJ_DEF(pj_status_t) pjmedia_session_pause(pjmedia_session *session, + pjmedia_dir dir) { - pj_media_stream_desc *sd; - int stat_cnt = 0; - - if (index >= session->stream_cnt) { - pj_assert(0); - return -1; - } - - sd = session->stream_desc[index]; + unsigned i; - if (sd->enc_stream && tx_stat) { - pj_media_stream_get_stat (sd->enc_stream, tx_stat); - ++stat_cnt; - } else if (tx_stat) { - pj_memset (tx_stat, 0, sizeof(*tx_stat)); - } + PJ_ASSERT_RETURN(session, PJ_EINVAL); - if (sd->dec_stream && rx_stat) { - pj_media_stream_get_stat (sd->dec_stream, rx_stat); - ++stat_cnt; - } else if (rx_stat) { - pj_memset (rx_stat, 0, sizeof(*rx_stat)); + for (i=0; istream_cnt; ++i) { + pjmedia_session_pause_stream(session, i, dir); } - return stat_cnt ? 0 : -1; + return PJ_SUCCESS; } + /** - * Modify stream, only when stream is inactive. + * Suspend receipt and transmission of individual stream in media session. */ -PJ_DEF(pj_status_t) -pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, - unsigned modify_flag, const pj_media_stream_info *info) +PJ_DEF(pj_status_t) pjmedia_session_pause_stream( pjmedia_session *session, + unsigned index, + pjmedia_dir dir) { - pj_media_stream_desc *sd; - - if (index >= session->stream_cnt) { - pj_assert(0); - return -1; - } - - sd = session->stream_desc[index]; - - if (sd->enc_stream || sd->dec_stream) { - pj_assert(0); - return -1; - } - - if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) { - sd->info.dir = info->dir; - } + PJ_ASSERT_RETURN(session && index < session->stream_cnt, PJ_EINVAL); - return 0; + return pjmedia_stream_pause(session->stream[index], dir); } + /** - * Activate media session. + * Activate individual stream in media session. + * */ -PJ_DEF(pj_status_t) -pj_media_session_activate (pj_media_session_t *session) +PJ_DEF(pj_status_t) pjmedia_session_resume_stream( pjmedia_session *session, + unsigned index, + pjmedia_dir dir) { - unsigned i; - pj_status_t status = 0; + PJ_ASSERT_RETURN(session && index < session->stream_cnt, PJ_EINVAL); - for (i=0; istream_cnt; ++i) { - pj_status_t rc; - rc = pj_media_session_activate_stream (session, i); - if (status == 0) - status = rc; - } - return status; + return pjmedia_stream_resume(session->stream[index], dir); } /** - * Activate individual stream in media session. + * Enumerate media stream in the session. */ -PJ_DEF(pj_status_t) -pj_media_session_activate_stream (pj_media_session_t *session, unsigned index) +PJ_DEF(pj_status_t) pjmedia_session_enum_streams(const pjmedia_session *session, + unsigned *count, + pjmedia_stream_info info[]) { - pj_media_stream_desc *sd; - pj_media_stream_create_param scp; - pj_status_t status; - pj_time_val tv; - - if (index < 0 || index >= session->stream_cnt) { - pj_assert(0); - return -1; - } + unsigned i; - sd = session->stream_desc[index]; + PJ_ASSERT_RETURN(session && count && *count && info, PJ_EINVAL); - if (sd->enc_stream || sd->dec_stream) { - /* Stream already active. */ - pj_assert(0); - return 0; - } + if (*count > session->stream_cnt) + *count = session->stream_cnt; - pj_gettimeofday(&tv); - - /* Initialize parameter to create stream. */ - pj_memset (&scp, 0, sizeof(scp)); - scp.codec_id = &sd->info.fmt[0]; - scp.mediamgr = session->mediamgr; - scp.dir = sd->info.dir; - scp.rtp_sock = sd->info.sock_info.rtp_sock; - scp.rtcp_sock = sd->info.sock_info.rtcp_sock; - scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in)); - pj_sockaddr_in_init(scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port); - scp.ssrc = tv.sec; - scp.jb_min = 1; - scp.jb_max = 15; - scp.jb_maxcnt = 16; - - /* Build the stream! */ - status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp); - - if (status==0 && sd->enc_stream) { - status = pj_media_stream_start (sd->enc_stream); - if (status != 0) - goto on_error; + for (i=0; i<*count; ++i) { + pj_memcpy(&info[i], &session->stream[i], sizeof(pjmedia_stream_info)); } - if (status==0 && sd->dec_stream) { - status = pj_media_stream_start (sd->dec_stream); - if (status != 0) - goto on_error; - } - return status; -on_error: - if (sd->enc_stream) { - pj_media_stream_destroy (sd->enc_stream); - sd->enc_stream = NULL; - } - if (sd->dec_stream) { - pj_media_stream_destroy (sd->dec_stream); - sd->dec_stream = NULL; - } - return status; + return PJ_SUCCESS; } /** - * Destroy media session. + * Get statistics */ -PJ_DEF(pj_status_t) -pj_media_session_destroy (pj_media_session_t *session) +PJ_DEF(pj_status_t) pjmedia_session_get_stat(const pjmedia_session *session, + unsigned *count, + pjmedia_stream_stat stat[]) { - unsigned i; + PJ_ASSERT_RETURN(session && count && *count && stat, PJ_EINVAL); - if (!session) - return -1; + *count = 0; + pj_memset(stat, 0, *count * sizeof(pjmedia_stream_stat)); + return PJ_EINVALIDOP; +} - for (i=0; istream_cnt; ++i) { - pj_media_stream_desc *sd = session->stream_desc[i]; - if (sd->enc_stream) { - pj_media_stream_destroy (sd->enc_stream); - sd->enc_stream = NULL; - } - if (sd->dec_stream) { - pj_media_stream_destroy (sd->dec_stream); - sd->dec_stream = NULL; - } - } - pj_pool_release (session->pool); - return 0; +/** + * Get individual stream statistic. + */ +PJ_DEF(pj_status_t) pjmedia_session_get_stream_stat( const pjmedia_session *s, + unsigned index, + pjmedia_stream_stat *stat) +{ + PJ_ASSERT_RETURN(s && index < s->stream_cnt && stat, PJ_EINVAL); + pj_memset(stat, 0, sizeof(pjmedia_stream_stat)); + return PJ_EINVALIDOP; } + diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index 323e0415..5846e27e 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -31,12 +31,13 @@ #include -#define THISFILE "stream.c" -#define ERRLEVEL 1 +#define THIS_FILE "stream.c" +#define ERRLEVEL 1 +#define TRACE_(expr) PJ_LOG(3,expr) -#define PJ_MAX_FRAME_DURATION_MS 200 -#define PJ_MAX_BUFFER_SIZE_MS 2000 -#define PJ_MAX_MTU 1500 +#define PJMEDIA_MAX_FRAME_DURATION_MS 200 +#define PJMEDIA_MAX_BUFFER_SIZE_MS 2000 +#define PJMEDIA_MAX_MTU 1500 struct jb_frame { @@ -48,196 +49,218 @@ struct jb_frame #define pj_fifobuf_unalloc(fifo,buf) free(buf) #define pj_fifobuf_free(fifo, buf) free(buf) -enum stream_state + +/** + * Media channel. + */ +struct pjmedia_channel { - STREAM_STOPPED, - STREAM_STARTED, + pjmedia_stream *stream; /**< Parent stream. */ + pjmedia_dir dir; /**< Channel direction. */ + unsigned pt; /**< Payload type. */ + pj_bool_t paused; /**< Paused?. */ + pj_snd_stream_info snd_info; /**< Sound stream param. */ + pj_snd_stream *snd_stream; /**< Sound stream. */ + unsigned in_pkt_size; /**< Size of input buffer. */ + void *in_pkt; /**< Input buffer. */ + unsigned out_pkt_size; /**< Size of output buffer. */ + void *out_pkt; /**< Output buffer. */ + unsigned pcm_buf_size; /**< Size of PCM buffer. */ + void *pcm_buf; /**< PCM buffer. */ + pj_rtp_session rtp; /**< RTP session. */ }; -struct pj_media_stream_t + +/** + * This structure describes media stream. + * A media stream is bidirectional media transmission between two endpoints. + * It consists of two channels, i.e. encoding and decoding channels. + * A media stream corresponds to a single "m=" line in a SDP session + * description. + */ +struct pjmedia_stream { - pj_media_dir_t dir; - int pt; - int state; - pj_media_stream_stat stat; - pj_media_stream_t *peer; - pj_snd_stream_info snd_info; - pj_snd_stream *snd_stream; - pj_mutex_t *mutex; - unsigned in_pkt_size; - void *in_pkt; - unsigned out_pkt_size; - void *out_pkt; - unsigned pcm_buf_size; - void *pcm_buf; - //pj_fifobuf_t fifobuf; - pj_codec_mgr *codec_mgr; - pj_codec *codec; - pj_rtp_session rtp; - pj_rtcp_session *rtcp; - pj_jitter_buffer *jb; - pj_sock_t rtp_sock; - pj_sock_t rtcp_sock; - pj_sockaddr_in dst_addr; - pj_thread_t *transport_thread; - int thread_quit_flag; + pjmedia_channel *enc; /**< Encoding channel. */ + pjmedia_channel *dec; /**< Decoding channel. */ + + pjmedia_dir dir; /**< Stream direction. */ + pjmedia_stream_stat stat; /**< Stream statistics. */ + + pjmedia_codec_mgr *codec_mgr; /**< Codec manager instance. */ + pjmedia_codec *codec; /**< Codec instance being used. */ + + pj_mutex_t *jb_mutex; + pj_jitter_buffer jb; /**< Jitter buffer. */ + + pj_sock_t rtp_sock; /**< RTP socket. */ + pj_sock_t rtcp_sock; /**< RTCP socket. */ + pj_sockaddr_in dst_addr; /**< Destination RTP address. */ + + pj_rtcp_session rtcp; /**< RTCP for incoming RTP. */ + + pj_bool_t quit_flag; /**< To signal thread exit. */ + pj_thread_t *thread; /**< Jitter buffer's thread. */ }; + +/* + * play_callback() + * + * This callback is called by sound device's player thread when it + * needs to feed the player with some frames. + */ static pj_status_t play_callback(/* in */ void *user_data, /* in */ pj_uint32_t timestamp, /* out */ void *frame, /*inout*/ unsigned size) { - pj_media_stream_t *channel = user_data; + pjmedia_channel *channel = user_data; + pjmedia_stream *stream = channel->stream; struct jb_frame *jb_frame; void *p; pj_uint32_t extseq; pj_status_t status; - struct pj_audio_frame frame_in, frame_out; + struct pjmedia_frame frame_in, frame_out; PJ_UNUSED_ARG(timestamp); - /* Lock mutex */ - pj_mutex_lock (channel->mutex); - - if (!channel->codec) { - pj_mutex_unlock (channel->mutex); + /* Do nothing if we're quitting. */ + if (stream->quit_flag) return -1; - } + + /* Lock jitter buffer mutex */ + pj_mutex_lock( stream->jb_mutex ); /* Get frame from jitter buffer. */ - status = pj_jb_get (channel->jb, &extseq, &p); + status = pj_jb_get(&stream->jb, &extseq, &p); + + /* Unlock jitter buffer mutex. */ + pj_mutex_unlock( stream->jb_mutex ); + jb_frame = p; - if (status != 0 || jb_frame == NULL) { + if (status != PJ_SUCCESS || jb_frame == NULL) { pj_memset(frame, 0, size); - pj_mutex_unlock(channel->mutex); return 0; } /* Decode */ frame_in.buf = jb_frame->buf; frame_in.size = jb_frame->size; - frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */ + frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO; /* ignored */ frame_out.buf = channel->pcm_buf; - status = channel->codec->op->decode (channel->codec, &frame_in, - channel->pcm_buf_size, &frame_out); + status = stream->codec->op->decode( stream->codec, &frame_in, + channel->pcm_buf_size, &frame_out); if (status != 0) { - PJ_LOG(3, (THISFILE, "decode() has return error status %d", - status)); + TRACE_((THIS_FILE, "decode() has return error status %d", status)); pj_memset(frame, 0, size); pj_fifobuf_free (&channel->fifobuf, jb_frame); - pj_mutex_unlock(channel->mutex); return 0; } /* Put in sound buffer. */ if (frame_out.size > size) { - PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes", - frame_out.size - size)); + TRACE_((THIS_FILE, "Sound playout buffer truncated %d bytes", + frame_out.size - size)); frame_out.size = size; } pj_memcpy(frame, frame_out.buf, size); - pj_fifobuf_free (&channel->fifobuf, jb_frame); - pj_mutex_unlock(channel->mutex); + return 0; } + +/** + * rec_callback() + * + * This callback is called when the mic device has gathered + * enough audio samples. We will encode the audio samples and + * send it to remote. + */ static pj_status_t rec_callback( /* in */ void *user_data, /* in */ pj_uint32_t timestamp, /* in */ const void *frame, /* in */ unsigned size) { - pj_media_stream_t *channel = user_data; + pjmedia_channel *channel = user_data; + pjmedia_stream *stream = channel->stream; pj_status_t status = 0; - struct pj_audio_frame frame_in, frame_out; + struct pjmedia_frame frame_in, frame_out; int ts_len; void *rtphdr; int rtphdrlen; pj_ssize_t sent; -#if 0 - static FILE *fhnd = NULL; -#endif - PJ_UNUSED_ARG(timestamp); - /* Start locking channel mutex */ - pj_mutex_lock (channel->mutex); + PJ_UNUSED_ARG(timestamp); - if (!channel->codec) { - status = -1; - goto on_return; - } + /* Check if stream is quitting. */ + if (stream->quit_flag) + return -1; /* Encode. */ - frame_in.type = PJ_MEDIA_TYPE_AUDIO; + frame_in.type = PJMEDIA_TYPE_AUDIO; frame_in.buf = (void*)frame; frame_in.size = size; frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr); - status = channel->codec->op->encode (channel->codec, &frame_in, - channel->out_pkt_size - sizeof(pj_rtp_hdr), - &frame_out); + status = stream->codec->op->encode( stream->codec, &frame_in, + channel->out_pkt_size - sizeof(pj_rtp_hdr), + &frame_out); if (status != 0) { - PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d", - status)); - goto on_return; + TRACE_((THIS_FILE, "Codec encode() has returned error status %d", + status)); + return status; } /* Encapsulate. */ ts_len = size / (channel->snd_info.bits_per_sample / 8); - status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0, + status = pj_rtp_encode_rtp( &channel->rtp, + channel->pt, 0, frame_out.size, ts_len, (const void**)&rtphdr, &rtphdrlen); if (status != 0) { - PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d", - status)); - goto on_return; + TRACE_((THIS_FILE, "RTP encode_rtp() has returned error status %d", + status)); + return status; } if (rtphdrlen != sizeof(pj_rtp_hdr)) { /* We don't support RTP with extended header yet. */ PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER); - PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission")); - goto on_return; + TRACE_((THIS_FILE, "Unsupported extended RTP header for transmission")); + return 0; } pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr)); /* Send. */ sent = frame_out.size+sizeof(pj_rtp_hdr); - status = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, &sent, 0, - &channel->dst_addr, sizeof(channel->dst_addr)); + status = pj_sock_sendto(stream->rtp_sock, channel->out_pkt, &sent, 0, + &stream->dst_addr, sizeof(stream->dst_addr)); if (status != PJ_SUCCESS) - goto on_return; + return status; /* Update stat */ - channel->stat.pkt_tx++; - channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr); - -#if 0 - if (fhnd == NULL) { - fhnd = fopen("RTP.DAT", "wb"); - if (fhnd) { - fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd); - fclose(fhnd); - } - } -#endif + stream->stat.enc.pkt++; + stream->stat.enc.bytes += frame_out.size+sizeof(pj_rtp_hdr); -on_return: - pj_mutex_unlock (channel->mutex); - return status; + return 0; } -static int PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg) +/* + * This thread will poll the socket for incoming packets, and put + * the packets to jitter buffer. + */ +static int PJ_THREAD_FUNC jitter_buffer_thread (void*arg) { - pj_media_stream_t *channel = arg; + pjmedia_stream *stream = arg; + pjmedia_channel *channel = stream->dec; - while (!channel->thread_quit_flag) { + while (!stream->quit_flag) { pj_ssize_t len, size; const pj_rtp_hdr *hdr; const void *payload; @@ -250,64 +273,64 @@ static int PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg) pj_time_val timeout; PJ_FD_ZERO (&fds); - PJ_FD_SET (channel->rtp_sock, &fds); + PJ_FD_SET (stream->rtp_sock, &fds); timeout.sec = 0; - timeout.msec = 100; + timeout.msec = 1; /* Wait with timeout. */ - status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout); + status = pj_sock_select(stream->rtp_sock, &fds, NULL, NULL, &timeout); if (status != 1) continue; /* Get packet from socket. */ len = channel->in_pkt_size; - status = pj_sock_recv (channel->rtp_sock, channel->in_pkt, &len, 0); + status = pj_sock_recv(stream->rtp_sock, channel->in_pkt, &len, 0); if (len < 1 || status != PJ_SUCCESS) { if (pj_get_netos_error() == PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) { - /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET - when the sending side receives ICMP port unreachable. + /* On Win2K SP2 (or above) and WinXP, recv() will get + * WSAECONNRESET when the sending side receives ICMP port + * unreachable. */ continue; } - //pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len); pj_thread_sleep(1); continue; } - if (channel->state != STREAM_STARTED) + if (channel->paused) continue; - if (channel->thread_quit_flag) - break; - - /* Start locking the channel. */ - pj_mutex_lock (channel->mutex); - /* Update RTP and RTCP session. */ - status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen); - if (status != 0) { - pj_mutex_unlock (channel->mutex); - PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status)); + status = pj_rtp_decode_rtp(&channel->rtp, channel->in_pkt, len, + &hdr, &payload, &payloadlen); + if (status != PJ_SUCCESS) { + TRACE_((THIS_FILE, "RTP decode_rtp() has returned error status %d", + status)); continue; } - status = pj_rtp_session_update (&channel->rtp, hdr); - if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) { - pj_mutex_unlock (channel->mutex); - PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status)); + + status = pj_rtp_session_update(&channel->rtp, hdr); + if (status != 0 && + status != PJMEDIA_RTP_ERR_SESSION_PROBATION && + status != PJMEDIA_RTP_ERR_SESSION_RESTARTED) + { + TRACE_((THIS_FILE, + "RTP session_update() has returned error status %d", + status)); continue; } - pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts)); + pj_rtcp_rx_rtp(&stream->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts)); /* Update stat */ - channel->stat.pkt_rx++; - channel->stat.oct_rx += len; + stream->stat.dec.pkt++; + stream->stat.dec.bytes += len; /* Copy to FIFO buffer. */ size = payloadlen+sizeof(struct jb_frame); jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size); if (jb_frame == NULL) { - pj_mutex_unlock (channel->mutex); - PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size)); + TRACE_((THIS_FILE, "Unable to allocate %d bytes FIFO buffer", + size)); continue; } @@ -317,141 +340,109 @@ static int PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg) pj_memcpy (jb_frame->buf, payload, payloadlen); /* Put to jitter buffer. */ - status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame); + pj_mutex_lock( stream->jb_mutex ); + status = pj_jb_put(&stream->jb, pj_ntohs(hdr->seq), jb_frame); + pj_mutex_unlock( stream->jb_mutex ); + if (status != 0) { pj_fifobuf_unalloc (&channel->fifobuf, jb_frame); - pj_mutex_unlock (channel->mutex); - PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status)); + + TRACE_((THIS_FILE, + "Jitter buffer put() has returned error status %d", + status)); continue; } - - pj_mutex_unlock (channel->mutex); } return 0; } -static void init_snd_param_from_codec_attr (pj_snd_stream_info *param, - const pj_codec_attr *attr) + +/* + * Create sound stream parameter from codec attributes. + */ +static void init_snd_param( pj_snd_stream_info *snd_param, + const pjmedia_codec_param *codec_param) { - param->bits_per_sample = attr->pcm_bits_per_sample; - param->bytes_per_frame = 2; - param->frames_per_packet = attr->sample_rate * attr->ptime / 1000; - param->samples_per_frame = 1; - param->samples_per_sec = attr->sample_rate; + pj_memset(snd_param, 0, sizeof(*snd_param)); + + snd_param->bits_per_sample = codec_param->pcm_bits_per_sample; + snd_param->bytes_per_frame = 2; + snd_param->frames_per_packet = codec_param->sample_rate * + codec_param->ptime / + 1000; + snd_param->samples_per_frame = 1; + snd_param->samples_per_sec = codec_param->sample_rate; } -static pj_media_stream_t *create_channel ( pj_pool_t *pool, - pj_media_dir_t dir, - pj_media_stream_t *peer, - pj_codec_id *codec_id, - pj_media_stream_create_param *param) + +/* + * Create media channel. + */ +static pj_status_t create_channel( pj_pool_t *pool, + pjmedia_stream *stream, + pjmedia_dir dir, + const pjmedia_stream_info *param, + const pjmedia_codec_param *codec_param, + pjmedia_channel **p_channel) { - pj_media_stream_t *channel; - pj_codec_attr codec_attr; - void *ptr; - unsigned size; + pjmedia_channel *channel; pj_status_t status; /* Allocate memory for channel descriptor */ - size = sizeof(pj_media_stream_t); - channel = pj_pool_calloc(pool, 1, size); - if (!channel) { - PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor", - size)); - return NULL; - } - - channel->dir = dir; - channel->pt = codec_id->pt; - channel->peer = peer; - channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr); - channel->rtp_sock = param->rtp_sock; - channel->rtcp_sock = param->rtcp_sock; - channel->dst_addr = *param->remote_addr; - channel->state = STREAM_STOPPED; - - /* Create mutex for the channel. */ - status = pj_mutex_create_simple(pool, NULL, &channel->mutex); - if (status != PJ_SUCCESS) - goto err_cleanup; - /* Create and initialize codec, only if peer is not present. - We only use one codec instance for both encoder and decoder. - */ - if (peer && peer->codec) { - channel->codec = peer->codec; - status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, - &codec_attr); - if (status != 0) { - goto err_cleanup; - } + channel = pj_pool_zalloc(pool, sizeof(pjmedia_channel)); + PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM); - } else { - channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id); - if (channel->codec == NULL) { - goto err_cleanup; - } - - status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, - &codec_attr); - if (status != 0) { - goto err_cleanup; - } + /* Init channel info. */ - codec_attr.pt = codec_id->pt; - status = channel->codec->op->open(channel->codec, &codec_attr); - if (status != 0) { - goto err_cleanup; - } - } + channel->stream = stream; + channel->dir = dir; + channel->paused = 1; + channel->pt = param->fmt.pt; /* Allocate buffer for incoming packet. */ - channel->in_pkt_size = PJ_MAX_MTU; - channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size); - if (!channel->in_pkt) { - PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer", - channel->in_pkt_size)); - goto err_cleanup; - } + channel->in_pkt_size = PJMEDIA_MAX_MTU; + channel->in_pkt = pj_pool_alloc( pool, channel->in_pkt_size ); + PJ_ASSERT_RETURN(channel->in_pkt != NULL, PJ_ENOMEM); + + /* Allocate buffer for outgoing packet. */ + channel->out_pkt_size = sizeof(pj_rtp_hdr) + - codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000; - if (channel->out_pkt_size > PJ_MAX_MTU) - channel->out_pkt_size = PJ_MAX_MTU; + codec_param->avg_bps/8 * + PJMEDIA_MAX_FRAME_DURATION_MS / + 1000; + + if (channel->out_pkt_size > PJMEDIA_MAX_MTU) + channel->out_pkt_size = PJMEDIA_MAX_MTU; + channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size); - if (!channel->out_pkt) { - PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer", - channel->out_pkt_size)); - goto err_cleanup; - } + PJ_ASSERT_RETURN(channel->out_pkt != NULL, PJ_ENOMEM); + + + /* Allocate buffer for decoding to PCM: */ - /* Allocate buffer for decoding to PCM */ - channel->pcm_buf_size = codec_attr.sample_rate * - codec_attr.pcm_bits_per_sample / 8 * - PJ_MAX_FRAME_DURATION_MS / 1000; + channel->pcm_buf_size = codec_param->sample_rate * + codec_param->pcm_bits_per_sample / 8 * + PJMEDIA_MAX_FRAME_DURATION_MS / 1000; channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size); - if (!channel->pcm_buf) { - PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer", - channel->pcm_buf_size)); - goto err_cleanup; - } + PJ_ASSERT_RETURN(channel->pcm_buf != NULL, PJ_ENOMEM); - /* Allocate buffer for frames put in jitter buffer. */ - size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000; - ptr = pj_pool_alloc(pool, size); - if (!ptr) { - PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer", - channel->pcm_buf_size)); - goto err_cleanup; - } - //pj_fifobuf_init (&channel->fifobuf, ptr, size); + + /* Create RTP and RTCP sessions: */ + + status = pj_rtp_session_init(&channel->rtp, param->fmt.pt, + param->ssrc); + if (status != PJ_SUCCESS) + return status; /* Create and initialize sound device */ - init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr); - if (dir == PJ_MEDIA_DIR_ENCODING) + init_snd_param(&channel->snd_info, codec_param); + + if (dir == PJMEDIA_DIR_ENCODING) channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info, &rec_callback, channel); else @@ -459,189 +450,249 @@ static pj_media_stream_t *create_channel ( pj_pool_t *pool, &play_callback, channel); if (!channel->snd_stream) + return -1; + + + /* Done. */ + *p_channel = channel; + return PJ_SUCCESS; +} + + +/* + * Create media stream. + */ +PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, + pj_pool_t *pool, + const pjmedia_stream_info *info, + pjmedia_stream **p_stream) + +{ + pjmedia_stream *stream; + pjmedia_codec_param codec_param; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && info && p_stream, PJ_EINVAL); + + + /* Allocate the media stream: */ + + stream = pj_pool_zalloc(pool, sizeof(pjmedia_stream)); + PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM); + + + /* Init stream: */ + + stream->dir = info->dir; + stream->codec_mgr = pjmedia_endpt_get_codec_mgr(endpt); + + /* Create mutex to protect jitter buffer: */ + + status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex); + if (status != PJ_SUCCESS) goto err_cleanup; - /* Create RTP and RTCP sessions. */ - if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) { - PJ_LOG(1, (THISFILE, "RTP session initialization error")); + + /* Create and initialize codec: */ + + status = pjmedia_codec_mgr_alloc_codec( stream->codec_mgr, + &info->fmt, &stream->codec); + if (status != PJ_SUCCESS) goto err_cleanup; - } - /* For decoder, create RTCP session, jitter buffer, and transport thread. */ - if (dir == PJ_MEDIA_DIR_DECODING) { - channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session)); - if (!channel->rtcp) { - PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session")); - goto err_cleanup; - } - pj_rtcp_init(channel->rtcp, param->ssrc); + /* Get default codec param: */ - channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer)); - if (!channel->jb) { - PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor")); - goto err_cleanup; - } - if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) { - PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer")); - goto err_cleanup; - } + status = stream->codec->op->default_attr(stream->codec, &codec_param); + if (status != PJ_SUCCESS) + goto err_cleanup; - status = pj_thread_create(pool, "decode", - &stream_decoder_transport_thread, channel, - 0, 0, &channel->transport_thread); - if (status != PJ_SUCCESS) { - //pj_perror(THISFILE, "Unable to create transport thread"); - goto err_cleanup; - } - } - /* Done. */ - return channel; + /* Open the codec: */ + + status = stream->codec->op->open(stream->codec, &codec_param); + if (status != PJ_SUCCESS) + goto err_cleanup; + + + /* Init RTCP session: */ + + pj_rtcp_init(&stream->rtcp, info->ssrc); + + + /* Init jitter buffer: */ + + status = pj_jb_init(&stream->jb, pool, + info->jb_min, info->jb_max, info->jb_maxcnt); + if (status != PJ_SUCCESS) + goto err_cleanup; + + + /* Create jitter buffer thread: */ + + status = pj_thread_create(pool, "decode", + &jitter_buffer_thread, stream, + 0, 0, &stream->thread); + if (status != PJ_SUCCESS) + goto err_cleanup; + + + /* Create decoder channel: */ + + status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, info, + &codec_param, &stream->dec); + if (status != PJ_SUCCESS) + goto err_cleanup; + + + /* Create encoder channel: */ + + status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, info, + &codec_param, &stream->enc); + if (status != PJ_SUCCESS) + goto err_cleanup; + + + /* Success! */ + *p_stream = stream; + return PJ_SUCCESS; + err_cleanup: - pj_media_stream_destroy(channel); - return NULL; + pjmedia_stream_destroy(stream); + return status; } -PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool, - pj_media_stream_t **enc_stream, - pj_media_stream_t **dec_stream, - pj_media_stream_create_param *param) +/* + * Destroy stream. + */ +PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) { - *dec_stream = *enc_stream = NULL; - if (param->dir & PJ_MEDIA_DIR_DECODING) { - *dec_stream = - create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param); - if (!*dec_stream) - return -1; + PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); + + /* Signal threads to quit. */ + + stream->quit_flag = 1; + + + /* Close encoding sound stream. */ + + if (stream->enc && stream->enc->snd_stream) { + + pj_snd_stream_stop(stream->enc->snd_stream); + pj_snd_stream_close(stream->enc->snd_stream); + stream->enc->snd_stream = NULL; + } - if (param->dir & PJ_MEDIA_DIR_ENCODING) { - *enc_stream = - create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param); - if (!*enc_stream) { - if (*dec_stream) { - pj_media_stream_destroy(*dec_stream); - *dec_stream = NULL; - } - return -1; - } + /* Close decoding sound stream. */ + + if (stream->dec && stream->dec->snd_stream) { + + pj_snd_stream_stop(stream->dec->snd_stream); + pj_snd_stream_close(stream->dec->snd_stream); + stream->dec->snd_stream = NULL; - if (*dec_stream) { - (*dec_stream)->peer = *enc_stream; - } } - return 0; -} + /* Wait for jitter buffer thread to quit: */ -PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel) -{ - pj_status_t status; + if (stream->thread) { + pj_thread_join(stream->thread); + pj_thread_destroy(stream->thread); + stream->thread = NULL; + } - status = pj_snd_stream_start(channel->snd_stream); + /* Free codec. */ - if (status == 0) - channel->state = STREAM_STARTED; - return status; -} + if (stream->codec) { + stream->codec->op->close(stream->codec); + pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); + stream->codec = NULL; + } -PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream, - pj_media_stream_stat *stat) -{ - if (stream->dir == PJ_MEDIA_DIR_ENCODING) { - pj_memcpy (stat, &stream->stat, sizeof(*stat)); - } else { - pj_rtcp_pkt *rtcp_pkt; - int len; - - pj_memset (stat, 0, sizeof(*stat)); - pj_assert (stream->rtcp != 0); - pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len); - - stat->pkt_rx = stream->stat.pkt_rx; - stat->oct_rx = stream->stat.oct_rx; - - PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE) - stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8; - stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) + - (rtcp_pkt->rr.total_lost_1 << 8) + - rtcp_pkt->rr.total_lost_0; + /* Free mutex */ + + if (stream->jb_mutex) { + pj_mutex_destroy(stream->jb_mutex); + stream->jb_mutex = NULL; } - return 0; + + return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel) + + +/* + * Start stream. + */ +PJ_DEF(pj_status_t) pjmedia_stream_start(pjmedia_stream *stream) { - PJ_UNUSED_ARG(channel); - return -1; + + PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP); + + if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) { + stream->enc->paused = 0; + pj_snd_stream_start(stream->enc->snd_stream); + } + + if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) { + stream->dec->paused = 0; + pj_snd_stream_start(stream->dec->snd_stream); + } + + return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel) + +/* + * Get stream statistics. + */ +PJ_DEF(pj_status_t) pjmedia_stream_get_stat( const pjmedia_stream *stream, + pjmedia_stream_stat *stat) { - PJ_UNUSED_ARG(channel); - return -1; + PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL); + + pj_memcpy(stat, &stream->stat, sizeof(pjmedia_stream_stat)); + + return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel) + +/* + * Pause stream. + */ +PJ_DEF(pj_status_t) pjmedia_stream_pause( pjmedia_stream *stream, + pjmedia_dir dir) { - channel->thread_quit_flag = 1; + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - pj_mutex_lock (channel->mutex); - if (channel->peer) - pj_mutex_lock (channel->peer->mutex); + if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) + stream->enc->paused = 1; - if (channel->jb) { - /* No need to deinitialize jitter buffer. */ - } - if (channel->transport_thread) { - pj_thread_join(channel->transport_thread); - pj_thread_destroy(channel->transport_thread); - channel->transport_thread = NULL; - } - if (channel->snd_stream != NULL) { - pj_mutex_unlock (channel->mutex); - pj_snd_stream_stop(channel->snd_stream); - pj_mutex_lock (channel->mutex); - pj_snd_stream_close(channel->snd_stream); - channel->snd_stream = NULL; - } - if (channel->codec) { - channel->codec->op->close(channel->codec); - pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec); - channel->codec = NULL; - } - if (channel->peer) { - pj_media_stream_t *peer = channel->peer; - peer->peer = NULL; - peer->codec = NULL; - peer->thread_quit_flag = 1; - if (peer->transport_thread) { - pj_mutex_unlock (peer->mutex); - pj_thread_join(peer->transport_thread); - pj_mutex_lock (peer->mutex); - pj_thread_destroy(peer->transport_thread); - peer->transport_thread = NULL; - } - if (peer->snd_stream) { - pj_mutex_unlock (peer->mutex); - pj_snd_stream_stop(peer->snd_stream); - pj_mutex_lock (peer->mutex); - pj_snd_stream_close(peer->snd_stream); - peer->snd_stream = NULL; - } - } + if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) + stream->dec->paused = 1; + + return PJ_SUCCESS; +} + + +/* + * Resume stream + */ +PJ_DEF(pj_status_t) pjmedia_stream_resume( pjmedia_stream *stream, + pjmedia_dir dir) +{ + PJ_ASSERT_RETURN(stream, PJ_EINVAL); - channel->state = STREAM_STOPPED; + if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) + stream->enc->paused = 1; - if (channel->peer) - pj_mutex_unlock (channel->peer->mutex); - pj_mutex_unlock(channel->mutex); - pj_mutex_destroy(channel->mutex); + if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) + stream->dec->paused = 1; - return 0; + return PJ_SUCCESS; } diff --git a/pjmedia/src/test/audio_tool.c b/pjmedia/src/test/audio_tool.c index 074802be..d4b8ad2f 100644 --- a/pjmedia/src/test/audio_tool.c +++ b/pjmedia/src/test/audio_tool.c @@ -26,8 +26,8 @@ static pj_caching_pool caching_pool; static pj_pool_factory *pf; static FILE *fhnd; static pj_med_mgr_t *mm; -static pj_codec *codec; -static pj_codec_attr cattr; +static pjmedia_codec *codec; +static pjmedia_codec_param cattr; #define WRITE_ORIGINAL_PCM 0 @@ -60,14 +60,14 @@ static pj_status_t play_callback(/* in */ void *user_data, /* out */ unsigned size) { char pkt[160]; - struct pj_audio_frame in, out; + struct pjmedia_frame in, out; int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000; if (fread(pkt, frmsz, 1, fhnd) != 1) { puts("EOF"); return -1; } else { - in.type = PJ_AUDIO_FRAME_AUDIO; + in.type = PJMEDIA_FRAME_TYPE_AUDIO; in.buf = pkt; in.size = frmsz; out.buf = frame; @@ -85,14 +85,14 @@ static pj_status_t rec_callback( /* in */ void *user_data, /* in*/ unsigned size) { char pkt[160]; - struct pj_audio_frame in, out; + struct pjmedia_frame in, out; //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000; #if WRITE_ORIGINAL_PCM fwrite(frame, size, 1, fhnd_pcm); #endif - in.type = PJ_AUDIO_FRAME_AUDIO; + in.type = PJMEDIA_FRAME_TYPE_AUDIO; in.buf = (void*)frame; in.size = size; out.buf = pkt; @@ -107,8 +107,8 @@ static pj_status_t rec_callback( /* in */ void *user_data, static pj_status_t init() { - pj_codec_mgr *cm; - pj_codec_id id; + pjmedia_codec_mgr *cm; + pjmedia_codec_info id; int i; pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); @@ -129,12 +129,12 @@ static pj_status_t init() mm = pj_med_mgr_create (&caching_pool.factory); cm = pj_med_mgr_get_codec_mgr (mm); - id.type = PJ_MEDIA_TYPE_AUDIO; + id.type = PJMEDIA_TYPE_AUDIO; id.pt = 0; id.encoding_name = pj_str("PCMU"); id.sample_rate = 8000; - codec = pj_codec_mgr_alloc_codec (cm, &id); + codec = pjmedia_codec_mgr_alloc_codec (cm, &id); codec->op->default_attr(codec, &cattr); codec->op->open(codec, &cattr); return 0; @@ -142,10 +142,10 @@ static pj_status_t init() static pj_status_t deinit() { - pj_codec_mgr *cm; + pjmedia_codec_mgr *cm; cm = pj_med_mgr_get_codec_mgr (mm); codec->op->close(codec); - pj_codec_mgr_dealloc_codec (cm, codec); + pjmedia_codec_mgr_dealloc_codec (cm, codec); pj_med_mgr_destroy (mm); pj_caching_pool_destroy(&caching_pool); return 0; @@ -300,13 +300,13 @@ static int create_ses_by_remote_sdp(int local_port, char *sdp) char *local_ip; switch (info[i]->dir) { - case PJ_MEDIA_DIR_NONE: + case PJMEDIA_DIR_NONE: dir = "- NONE -"; break; - case PJ_MEDIA_DIR_ENCODING: + case PJMEDIA_DIR_ENCODING: dir = "SENDONLY"; break; - case PJ_MEDIA_DIR_DECODING: + case PJMEDIA_DIR_DECODING: dir = "RECVONLY"; break; - case PJ_MEDIA_DIR_ENCODING_DECODING: + case PJMEDIA_DIR_ENCODING_DECODING: dir = "SENDRECV"; break; default: dir = "?UNKNOWN"; break; @@ -345,7 +345,7 @@ static pj_status_t convert(const char *src, const char *dst) { char pcm[320]; char frame[160]; - struct pj_audio_frame in, out; + struct pjmedia_frame in, out; fhnd_pcm = fopen(src, "rb"); if (!fhnd_pcm) @@ -356,7 +356,7 @@ static pj_status_t convert(const char *src, const char *dst) while (fread(pcm, 320, 1, fhnd_pcm) == 1) { - in.type = PJ_AUDIO_FRAME_AUDIO; + in.type = PJMEDIA_FRAME_TYPE_AUDIO; in.buf = pcm; in.size = 320; out.buf = frame; diff --git a/pjmedia/src/test/session_test.c b/pjmedia/src/test/session_test.c index c106f04d..f1d57282 100644 --- a/pjmedia/src/test/session_test.c +++ b/pjmedia/src/test/session_test.c @@ -44,8 +44,8 @@ pj_status_t session_test (pj_pool_factory *pf) s1 = pj_media_session_create (mm, NULL); // Set caller's media to send-only. - sd_info.dir = PJ_MEDIA_DIR_ENCODING; - pj_media_session_modify_stream (s1, 0, PJ_MEDIA_STREAM_MODIFY_DIR, &sd_info); + sd_info.dir = PJMEDIA_DIR_ENCODING; + pj_media_session_modify_stream (s1, 0, PJMEDIA_STREAM_MODIFY_DIR, &sd_info); // Create caller SDP. sdp = pj_media_session_create_sdp (s1, pool, 0); -- cgit v1.2.3