Re #1182:
 - Added video stream interface in vid_stream.h, the video stream will be able to handle different video formats in encoding and decoding direction.
 - Renamed video device stream class identifiers from 'pjmedia_vid_stream*' to 'pjmedia_vid_dev_stream*' as 'pjmedia_vid_stream' is used by video stream interface.
 - Added ffmpeg video capability to be able to parse SDP format param for H263 and also decide video format for encoding direction based on remote preference and local format-capability setting.
 - Added some new APIs in jitter buffer for handling video stream: pjmedia_jbuf_put_frame3(), pjmedia_jbuf_get_frame3(), pjmedia_jbuf_peek_frame(), and pjmedia_jbuf_remove_frame().
 - Moved pjmedia_stream_info_from_sdp() from session to stream




git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/2.0-dev@3420 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj
index 2b5a89e..72b0532 100644
--- a/pjmedia/build/pjmedia.vcproj
+++ b/pjmedia/build/pjmedia.vcproj
@@ -4312,64 +4312,6 @@
 				</FileConfiguration>

 			</File>

 			<File

-				RelativePath="..\src\pjmedia\session.c"

-				>

-				<FileConfiguration

-					Name="Release|Win32"

-					>

-					<Tool

-						Name="VCCLCompilerTool"

-						AdditionalIncludeDirectories=""

-						PreprocessorDefinitions=""

-					/>

-				</FileConfiguration>

-				<FileConfiguration

-					Name="Debug|Win32"

-					>

-					<Tool

-						Name="VCCLCompilerTool"

-						AdditionalIncludeDirectories=""

-						PreprocessorDefinitions=""

-					/>

-				</FileConfiguration>

-				<FileConfiguration

-					Name="Debug-Static|Win32"

-					>

-					<Tool

-						Name="VCCLCompilerTool"

-						AdditionalIncludeDirectories=""

-						PreprocessorDefinitions=""

-					/>

-				</FileConfiguration>

-				<FileConfiguration

-					Name="Release-Dynamic|Win32"

-					>

-					<Tool

-						Name="VCCLCompilerTool"

-						AdditionalIncludeDirectories=""

-						PreprocessorDefinitions=""

-					/>

-				</FileConfiguration>

-				<FileConfiguration

-					Name="Debug-Dynamic|Win32"

-					>

-					<Tool

-						Name="VCCLCompilerTool"

-						AdditionalIncludeDirectories=""

-						PreprocessorDefinitions=""

-					/>

-				</FileConfiguration>

-				<FileConfiguration

-					Name="Release-Static|Win32"

-					>

-					<Tool

-						Name="VCCLCompilerTool"

-						AdditionalIncludeDirectories=""

-						PreprocessorDefinitions=""

-					/>

-				</FileConfiguration>

-			</File>

-			<File

 				RelativePath="..\src\pjmedia\silencedet.c"

 				>

 				<FileConfiguration

@@ -4610,6 +4552,10 @@
 				</FileConfiguration>

 			</File>

 			<File

+				RelativePath="..\src\pjmedia\stream_common.c"

+				>

+			</File>

+			<File

 				RelativePath="..\src\pjmedia\tonegen.c"

 				>

 				<FileConfiguration

@@ -4746,6 +4692,10 @@
 				>

 			</File>

 			<File

+				RelativePath="..\src\pjmedia\vid_stream.c"

+				>

+			</File>

+			<File

 				RelativePath="..\src\pjmedia\videoport.c"

 				>

 			</File>

@@ -5119,10 +5069,6 @@
 				>

 			</File>

 			<File

-				RelativePath="..\include\pjmedia\session.h"

-				>

-			</File>

-			<File

 				RelativePath="..\include\pjmedia\silencedet.h"

 				>

 			</File>

@@ -5147,6 +5093,10 @@
 				>

 			</File>

 			<File

+				RelativePath="..\include\pjmedia\stream_common.h"

+				>

+			</File>

+			<File

 				RelativePath="..\include\pjmedia\tonegen.h"

 				>

 			</File>

@@ -5183,6 +5133,10 @@
 				>

 			</File>

 			<File

+				RelativePath="..\include\pjmedia\vid_stream.h"

+				>

+			</File>

+			<File

 				RelativePath="..\include\pjmedia\videoport.h"

 				>

 			</File>

diff --git a/pjmedia/include/pjmedia-videodev/videodev.h b/pjmedia/include/pjmedia-videodev/videodev.h
index f9766f6..2a17df8 100644
--- a/pjmedia/include/pjmedia-videodev/videodev.h
+++ b/pjmedia/include/pjmedia-videodev/videodev.h
@@ -78,8 +78,8 @@
  * structure.
  *
  * Once video stream is running, application can also retrieve or set some
- * specific video capability, by using #pjmedia_vid_stream_get_cap() and
- * #pjmedia_vid_stream_set_cap() and specifying the desired capability. The
+ * specific video capability, by using #pjmedia_vid_dev_stream_get_cap() and
+ * #pjmedia_vid_dev_stream_set_cap() and specifying the desired capability. The
  * value of the capability is specified as pointer, and application needs to
  * supply the pointer with the correct value, according to the documentation
  * of each of the capability.
@@ -154,8 +154,8 @@
 } pjmedia_vid_dev_info;
 
 
-/** Forward declaration for pjmedia_vid_stream */
-typedef struct pjmedia_vid_stream pjmedia_vid_stream;
+/** Forward declaration for pjmedia_vid_dev_stream */
+typedef struct pjmedia_vid_dev_stream pjmedia_vid_dev_stream;
 
 typedef enum pjmedia_event_type
 {
@@ -187,7 +187,7 @@
     * @return              Returning non-PJ_SUCCESS will cause the video
     *                      stream to stop
     */
-    pj_status_t (*capture_cb)(pjmedia_vid_stream *stream,
+    pj_status_t (*capture_cb)(pjmedia_vid_dev_stream *stream,
 		              void *user_data,
                               pjmedia_frame *frame);
 
@@ -210,7 +210,7 @@
     * @return              Returning non-PJ_SUCCESS will cause the video 
     *                      stream to stop
     */
-    pj_status_t (*render_cb)(pjmedia_vid_stream *stream,
+    pj_status_t (*render_cb)(pjmedia_vid_dev_stream *stream,
 			     void *user_data,
                              pjmedia_frame *frame);
 
@@ -226,7 +226,7 @@
     *                      default event-handler (if any), otherwise the
     *                      video stream will ignore the particular event.
     */
-    pj_status_t (*on_event_cb)(pjmedia_vid_stream *stream,
+    pj_status_t (*on_event_cb)(pjmedia_vid_dev_stream *stream,
 			       void *user_data,
                                pjmedia_vid_event *event);
 
@@ -476,10 +476,11 @@
  * @return              PJ_SUCCESS on successful operation or the appropriate
  *                      error code.
  */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_create(const pjmedia_vid_param *param,
-                                               const pjmedia_vid_cb *cb,
-                                               void *user_data,
-                                               pjmedia_vid_stream **p_strm);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_create(
+					    const pjmedia_vid_param *param,
+					    const pjmedia_vid_cb *cb,
+					    void *user_data,
+					    pjmedia_vid_dev_stream **p_strm);
 
 /**
  * Get the running parameters for the specified video stream.
@@ -491,8 +492,9 @@
  * @return          PJ_SUCCESS on successful operation or the appropriate
  *                  error code.
  */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_get_param(pjmedia_vid_stream *strm,
-                                                  pjmedia_vid_param *param);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_param(
+					    pjmedia_vid_dev_stream *strm,
+                                            pjmedia_vid_param *param);
 
 /**
  * Get the value of a specific capability of the video stream.
@@ -507,9 +509,10 @@
  * @return          PJ_SUCCESS on successful operation or the appropriate
  *                  error code.
  */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_get_cap(pjmedia_vid_stream *strm,
-                                                pjmedia_vid_dev_cap cap,
-                                                void *value);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_cap(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_vid_dev_cap cap,
+                                            void *value);
 
 /**
  * Set the value of a specific capability of the video stream.
@@ -522,9 +525,10 @@
  * @return          PJ_SUCCESS on successful operation or the appropriate
  *                  error code.
  */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_set_cap(pjmedia_vid_stream *strm,
-                                                pjmedia_vid_dev_cap cap,
-                                                const void *value);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_set_cap(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_vid_dev_cap cap,
+					    const void *value);
 
 /**
  * Start the stream.
@@ -534,14 +538,17 @@
  * @return          PJ_SUCCESS on successful operation or the appropriate
  *                  error code.
  */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *strm);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_start(
+					    pjmedia_vid_dev_stream *strm);
 
 /* Get/put frame API for passive stream */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_get_frame(pjmedia_vid_stream *strm,
-                                                  pjmedia_frame *frame);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_frame(
+					    pjmedia_vid_dev_stream *strm,
+                                            pjmedia_frame *frame);
 
-PJ_DECL(pj_status_t) pjmedia_vid_stream_put_frame(pjmedia_vid_stream *strm,
-                                                  const pjmedia_frame *frame);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_put_frame(
+					    pjmedia_vid_dev_stream *strm,
+                                            const pjmedia_frame *frame);
 
 /**
  * Stop the stream.
@@ -551,7 +558,8 @@
  * @return          PJ_SUCCESS on successful operation or the appropriate
  *                  error code.
  */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_stop(pjmedia_vid_stream *strm);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_stop(
+					    pjmedia_vid_dev_stream *strm);
 
 /**
  * Destroy the stream.
@@ -561,7 +569,8 @@
  * @return          PJ_SUCCESS on successful operation or the appropriate
  *                  error code.
  */
-PJ_DECL(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *strm);
+PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_destroy(
+					    pjmedia_vid_dev_stream *strm);
 
 
 /**
diff --git a/pjmedia/include/pjmedia-videodev/videodev_imp.h b/pjmedia/include/pjmedia-videodev/videodev_imp.h
index e16b90a..53d6927 100644
--- a/pjmedia/include/pjmedia-videodev/videodev_imp.h
+++ b/pjmedia/include/pjmedia-videodev/videodev_imp.h
@@ -83,13 +83,13 @@
 
     /**
      * Open the video device and create video stream. See
-     * #pjmedia_vid_stream_create()
+     * #pjmedia_vid_dev_stream_create()
      */
     pj_status_t (*create_stream)(pjmedia_vid_dev_factory *f,
 				 const pjmedia_vid_param *param,
 				 const pjmedia_vid_cb *cb,
 				 void *user_data,
-				 pjmedia_vid_stream **p_vid_strm);
+				 pjmedia_vid_dev_stream **p_vid_strm);
 
 } pjmedia_vid_dev_factory_op;
 
@@ -113,62 +113,62 @@
 /**
  * Video stream operations.
  */
-typedef struct pjmedia_vid_stream_op
+typedef struct pjmedia_vid_dev_stream_op
 {
     /**
-     * See #pjmedia_vid_stream_get_param()
+     * See #pjmedia_vid_dev_stream_get_param()
      */
-    pj_status_t (*get_param)(pjmedia_vid_stream *strm,
+    pj_status_t (*get_param)(pjmedia_vid_dev_stream *strm,
 			     pjmedia_vid_param *param);
 
     /**
-     * See #pjmedia_vid_stream_get_cap()
+     * See #pjmedia_vid_dev_stream_get_cap()
      */
-    pj_status_t (*get_cap)(pjmedia_vid_stream *strm,
+    pj_status_t (*get_cap)(pjmedia_vid_dev_stream *strm,
 			   pjmedia_vid_dev_cap cap,
 			   void *value);
 
     /**
-     * See #pjmedia_vid_stream_set_cap()
+     * See #pjmedia_vid_dev_stream_set_cap()
      */
-    pj_status_t (*set_cap)(pjmedia_vid_stream *strm,
+    pj_status_t (*set_cap)(pjmedia_vid_dev_stream *strm,
 			   pjmedia_vid_dev_cap cap,
 			   const void *value);
 
     /**
-     * See #pjmedia_vid_stream_start()
+     * See #pjmedia_vid_dev_stream_start()
      */
-    pj_status_t (*start)(pjmedia_vid_stream *strm);
+    pj_status_t (*start)(pjmedia_vid_dev_stream *strm);
 
     /**
-     * See #pjmedia_vid_stream_get_frame()
+     * See #pjmedia_vid_dev_stream_get_frame()
      */
-    pj_status_t (*get_frame)(pjmedia_vid_stream *strm,
+    pj_status_t (*get_frame)(pjmedia_vid_dev_stream *strm,
                              pjmedia_frame *frame);
 
     /**
-     * See #pjmedia_vid_stream_put_frame()
+     * See #pjmedia_vid_dev_stream_put_frame()
      */
-    pj_status_t (*put_frame)(pjmedia_vid_stream *strm,
+    pj_status_t (*put_frame)(pjmedia_vid_dev_stream *strm,
                              const pjmedia_frame *frame);
 
     /**
-     * See #pjmedia_vid_stream_stop().
+     * See #pjmedia_vid_dev_stream_stop().
      */
-    pj_status_t (*stop)(pjmedia_vid_stream *strm);
+    pj_status_t (*stop)(pjmedia_vid_dev_stream *strm);
 
     /**
-     * See #pjmedia_vid_stream_destroy().
+     * See #pjmedia_vid_dev_stream_destroy().
      */
-    pj_status_t (*destroy)(pjmedia_vid_stream *strm);
+    pj_status_t (*destroy)(pjmedia_vid_dev_stream *strm);
 
-} pjmedia_vid_stream_op;
+} pjmedia_vid_dev_stream_op;
 
 
 /**
  * This structure describes the video device stream.
  */
-struct pjmedia_vid_stream
+struct pjmedia_vid_dev_stream
 {
     /** Internal data to be initialized by video subsystem */
     struct {
@@ -177,7 +177,7 @@
     } sys;
 
     /** Operations */
-    pjmedia_vid_stream_op *op;
+    pjmedia_vid_dev_stream_op *op;
 };
 
 
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index 8187b4c..6b9e507 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -52,13 +52,14 @@
 #include <pjmedia/rtp.h>
 #include <pjmedia/sdp.h>
 #include <pjmedia/sdp_neg.h>
-#include <pjmedia/session.h>
+//#include <pjmedia/session.h>
 #include <pjmedia/silencedet.h>
 #include <pjmedia/sound.h>
 #include <pjmedia/sound_port.h>
 #include <pjmedia/splitcomb.h>
 #include <pjmedia/stereo.h>
 #include <pjmedia/stream.h>
+#include <pjmedia/stream_common.h>
 #include <pjmedia/tonegen.h>
 #include <pjmedia/transport.h>
 #include <pjmedia/transport_adapter_sample.h>
@@ -68,6 +69,7 @@
 #include <pjmedia/transport_udp.h>
 #include <pjmedia/videoport.h>
 #include <pjmedia/vid_codec.h>
+#include <pjmedia/vid_stream.h>
 #include <pjmedia/wav_playlist.h>
 #include <pjmedia/wav_port.h>
 #include <pjmedia/wave.h>
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index de5c443..2c61577 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -979,6 +979,15 @@
 #endif
 
 /**
+ * Maximum video frame size.
+ * Default: 16kB
+ */
+#ifndef PJMEDIA_MAX_VIDEO_FRAME_SIZE			
+#  define PJMEDIA_MAX_VIDEO_FRAME_SIZE			(1<<14)
+#endif
+
+
+/**
  * Specify the maximum duration (in ms) for resynchronization. When a media
  * is late to another media it is supposed to be synchronized to, it is
  * guaranteed to be synchronized again after this duration. While if the
diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h
index 4f5a12b..04892cb 100644
--- a/pjmedia/include/pjmedia/jbuf.h
+++ b/pjmedia/include/pjmedia/jbuf.h
@@ -68,7 +68,7 @@
 /**
  * This structure describes jitter buffer state.
  */
-struct pjmedia_jb_state
+typedef struct pjmedia_jb_state
 {
     /* Setting */
     unsigned	frame_size;	    /**< Individual frame size, in bytes.   */
@@ -89,13 +89,7 @@
     unsigned	lost;		    /**< Number of lost frames.		    */
     unsigned	discard;	    /**< Number of discarded frames.	    */
     unsigned	empty;		    /**< Number of empty on GET events.	    */
-};
-
-
-/**
- * @see pjmedia_jb_state
- */
-typedef struct pjmedia_jb_state pjmedia_jb_state;
+} pjmedia_jb_state;
 
 
 /**
@@ -180,6 +174,19 @@
 
 
 /**
+ * Enable/disable the jitter buffer drift detection and handling mechanism.
+ * The default behavior is enabled.
+ *
+ * @param jb		The jitter buffer
+ * @param enable	Set to PJ_TRUE to enable or PJ_FALSE to disable.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_jbuf_enable_discard(pjmedia_jbuf *jb,
+						 pj_bool_t enable);
+
+
+/**
  * Destroy jitter buffer instance.
  *
  * @param jb		The jitter buffer.
@@ -244,6 +251,32 @@
 				       pj_bool_t *discarded);
 
 /**
+ * Put a frame to the jitter buffer. If the frame can be accepted (based
+ * on the sequence number), the jitter buffer will copy the frame and put
+ * it in the appropriate position in the buffer.
+ *
+ * Application MUST manage it's own synchronization when multiple threads
+ * are accessing the jitter buffer at the same time.
+ *
+ * @param jb		The jitter buffer.
+ * @param frame		Pointer to frame buffer to be stored in the jitter
+ *			buffer.
+ * @param size		The frame size.
+ * @param bit_info	Bit precise info of the frame, e.g: a frame may not 
+ *			exactly start and end at the octet boundary, so this
+ *			field may be used for specifying start & end bit offset.
+ * @param frame_seq	The frame sequence number.
+ * @param frame_ts	The frame timestamp.
+ * @param discarded	Flag whether the frame is discarded by jitter buffer.
+ */
+PJ_DECL(void) pjmedia_jbuf_put_frame3( pjmedia_jbuf *jb, 
+				       const void *frame, 
+				       pj_size_t size, 
+				       pj_uint32_t bit_info,
+				       int frame_seq,
+				       pj_uint32_t frame_ts,
+				       pj_bool_t *discarded);
+/**
  * Get a frame from the jitter buffer. The jitter buffer will return the
  * oldest frame from it's buffer, when it is available.
  *
@@ -293,6 +326,25 @@
 				      pj_uint32_t *bit_info);
 
 
+PJ_DECL(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb, 
+				      void *frame, 
+				      pj_size_t *size, 
+				      char *p_frm_type,
+				      pj_uint32_t *bit_info,
+				      pj_uint32_t *ts);
+
+PJ_DECL(void) pjmedia_jbuf_peek_frame(pjmedia_jbuf *jb,
+				      unsigned idx,
+				      const void **frame, 
+				      pj_size_t *size, 
+				      char *p_frm_type,
+				      pj_uint32_t *bit_info,
+				      pj_uint32_t *ts);
+
+PJ_DECL(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb, 
+					    unsigned frame_cnt);
+
+
 /**
  * Get jitter buffer current state/settings.
  *
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index 8bf4433..8094c30 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -143,6 +143,30 @@
 typedef struct pjmedia_stream_info pjmedia_stream_info;
 
 
+/**
+ * This function will initialize the stream info based on information
+ * in both SDP session descriptors for the specified stream index. 
+ * The remaining information will be taken from default codec parameters. 
+ * If socket info array is specified, the socket will be copied to the 
+ * session info as well.
+ *
+ * @param si		Stream info structure to be initialized.
+ * @param pool		Pool to allocate memory.
+ * @param endpt		PJMEDIA endpoint instance.
+ * @param local		Local SDP session descriptor.
+ * @param remote	Remote SDP session descriptor.
+ * @param stream_idx	Media stream index in the session descriptor.
+ *
+ * @return		PJ_SUCCESS if stream info is successfully initialized.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_stream_info_from_sdp( pjmedia_stream_info *si,
+			      pj_pool_t *pool,
+			      pjmedia_endpt *endpt,
+			      const pjmedia_sdp_session *local,
+			      const pjmedia_sdp_session *remote,
+			      unsigned stream_idx);
+
 
 /**
  * Create a media stream based on the specified parameter. After the stream
diff --git a/pjmedia/include/pjmedia/vid_codec.h b/pjmedia/include/pjmedia/vid_codec.h
index d5321b5..24eaa88 100644
--- a/pjmedia/include/pjmedia/vid_codec.h
+++ b/pjmedia/include/pjmedia/vid_codec.h
@@ -22,8 +22,8 @@
 
 
 /**
- * @file codec.h
- * @brief Codec framework.
+ * @file vid_codec.h
+ * @brief Video codec framework.
  */
 
 #include <pjmedia/codec.h>
@@ -45,20 +45,22 @@
 {
     pjmedia_format_id   fmt_id;         /**< Encoded format ID              */
     pj_str_t	        encoding_name;  /**< Encoding name                  */
-    pjmedia_dir         dir;            /**< Direction                      */
-    unsigned            pt;             /**< Payload type (may be 0 for
+    unsigned            pt;             /**< Payload type (may be 255 for
                                              dynamic payload type)          */
-    unsigned            clock_rate;     /**< (?) Clock rate                 */
-    unsigned            dec_fmt_id_cnt;
+    unsigned            clock_rate;     /**< Clock rate			    */
+    pjmedia_dir         dir;            /**< Direction                      */
+    unsigned            dec_fmt_id_cnt; /**< # of supported encoding source 
+                                             format IDs                     */
     pjmedia_format_id   dec_fmt_id[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
                                         /**< Supported encoding source 
                                              format IDs                     */
-    unsigned            fps_cnt;        /**< zero if support any fps        */
-    pjmedia_ratio       fps[PJMEDIA_VID_CODEC_MAX_FPS_CNT];    
-                                        /**< (?) supported FPSes, 
-                                             ffmpeg has this                */
+    unsigned            fps_cnt;        /**< # of supported frame-rates, can be
+					     zero (support any frame-rate)  */
+    pjmedia_ratio       fps[PJMEDIA_VID_CODEC_MAX_FPS_CNT];
+                                        /**< Supported frame-rates	    */
 } pjmedia_vid_codec_info;
 
+
 /** 
  * Detailed codec attributes used in configuring a codec and in querying
  * the capability of codec factories. Default attributes of any codecs could
@@ -79,13 +81,28 @@
     pjmedia_codec_fmtp  dec_fmtp;       /**< Decoder fmtp params	    */
 
     unsigned            enc_mtu;        /**< MTU or max payload size setting*/
-    unsigned            pt;             /**< Payload type.                  */
 } pjmedia_vid_codec_param;
 
 
+/**
+ * Enumeration of video codec events.
+ */
+typedef enum pjmedia_vid_codec_event
+{
+    /**
+     * Format changed event. The decoder output format is not really
+     * configurable, so that the output format setting configured in the
+     * initialization may be changed. Application can catch this event
+     * by checking the bit_info field of the pjmedia_frame of the decoder
+     * output frame.
+     */
+    PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED = 1,
 
-/*
- * Forward declaration for pjmedia_vid_codec.
+} pjmedia_vid_codec_event;
+
+
+/**
+ * Forward declaration for video codec.
  */
 typedef struct pjmedia_vid_codec pjmedia_vid_codec;
 
@@ -111,8 +128,9 @@
     /** 
      * Open the codec and initialize with the specified parameter.
      * Upon successful initialization, the codec may modify the parameter
-     * and fills in the unspecified values (such as enc_ptime, when
-     * encoder ptime is different than decoder ptime).
+     * and fills in the unspecified values (such as size or frame rate of
+     * the encoder format, as it may need to be negotiated with remote
+     * preferences via SDP fmtp).
      *
      * @param codec	The codec instance.
      * @param param	Codec initialization parameter.
@@ -138,47 +156,61 @@
      * When the parameter cannot be changed, this function will return 
      * non-PJ_SUCCESS, and the original parameters will not be changed.
      *
-     * Application can expect changing trivial codec settings such as
-     * changing VAD setting to succeed.
-     *
      * @param codec	The codec instance.
      * @param param	The new codec parameter.
      *
      * @return		PJ_SUCCESS on success.
      */
     pj_status_t	(*modify)(pjmedia_vid_codec *codec, 
-			  const pjmedia_vid_codec_param *param );
+			  const pjmedia_vid_codec_param *param);
+
+    /** 
+     * Get the codec parameter after the codec is opened. 
+     *
+     * @param codec	The codec instance.
+     * @param param	The codec parameter.
+     *
+     * @return		PJ_SUCCESS on success.
+     */
+    pj_status_t	(*get_param)(pjmedia_vid_codec *codec,
+			     pjmedia_vid_codec_param *param);
 
     /**
-     * Instruct the codec to inspect the specified payload/packet and
-     * split the packet into individual base frames. Each output frames will
-     * have ptime that is equal to basic frame ptime (i.e. the value of
-     * info.frm_ptime in #pjmedia_vid_codec_param).
+     * Instruct the codec to generate a payload/packet from a picture
+     * bitstream to be sent (via network). The maximum payload size or
+     * MTU is configurable via enc_mtu field of #pjmedia_vid_codec_param.
+     * For a long bitstream, application usually need to call this function
+     * multiple times until the whole bitstream is sent. Note that, for
+     * performance reason, the packetization will be done in-place, so the
+     * original bitstream may be modified by this function.
      *
      * @param codec	The codec instance
-     * @param pkt	The input packet.
-     * @param pkt_size	Size of the packet.
-     * @param timestamp	The timestamp of the first sample in the packet.
-     * @param frame_cnt	On input, specifies the maximum number of frames
-     *			in the array. On output, the codec must fill
-     *			with number of frames detected in the packet.
-     * @param frames	On output, specifies the frames that have been
-     *			detected in the packet.
+     * @param bits	The picture bitstream.
+     * @param bits_len	The length of the bitstream.
+     * @param bits_pos	On input, the start position of the bitstream
+     *			to be packetized. On output, the next position for
+     *			next packet.
+     * @param pkt	The pointer of the generated payload.
+     * @param pkt_len	The payload length.
      *
      * @return		PJ_SUCCESS on success.
      */
     pj_status_t (*packetize) (pjmedia_vid_codec *codec,
-                              pj_uint8_t *buf,
-                              pj_size_t buf_len,
-                              unsigned *pos,
-                              const pj_uint8_t **payload,
-                              pj_size_t *payload_len);
+                              pj_uint8_t *bits,
+                              pj_size_t bits_len,
+                              unsigned *bits_pos,
+                              const pj_uint8_t **pkt,
+                              pj_size_t *pkt_len);
 
     /**
-     * Instruct the codec to inspect the specified payload/packet and
-     * split the packet into individual base frames. Each output frames will
-     * have ptime that is equal to basic frame ptime (i.e. the value of
-     * info.frm_ptime in #pjmedia_vid_codec_param).
+     * Instruct the codec to parse a payload and append it into a picture
+     * bitstream. A picture bitstreams may need to be reconstructed from
+     * one or more payloads. Note that this function will not provide the
+     * detection of picture boundary, so application should manage the
+     * picture boundary detection by itself, e.g: for RTP delivery, payloads
+     * belong to the same picture will share the same RTP timestamp and also
+     * there is marker bit in the RTP header that is usually reserved for
+     * end-of-picture flag.
      *
      * @param codec	The codec instance
      * @param pkt	The input packet.
@@ -195,13 +227,13 @@
     pj_status_t (*unpacketize)(pjmedia_vid_codec *codec,
                                const pj_uint8_t *payload,
                                pj_size_t   payload_len,
-                               pj_uint8_t *buf,
-                               pj_size_t  *buf_len);
+                               pj_uint8_t *bits,
+                               pj_size_t  *bits_len);
 
     /** 
      * Instruct the codec to encode the specified input frame. The input
-     * PCM samples MUST have ptime that is multiplication of base frame
-     * ptime (i.e. the value of info.frm_ptime in #pjmedia_vid_codec_param).
+     * MUST contain only one picture with appropriate format as specified
+     * in opening the codec.
      *
      * @param codec	The codec instance.
      * @param input	The input frame.
@@ -217,10 +249,12 @@
 
     /** 
      * Instruct the codec to decode the specified input frame. The input
-     * frame MUST have ptime that is exactly equal to base frame
-     * ptime (i.e. the value of info.frm_ptime in #pjmedia_vid_codec_param).
-     * Application can achieve this by parsing the packet into base
-     * frames before decoding each frame.
+     * frame MUST contain exactly one picture. Note that the decoded picture
+     * format may different to the current setting, e.g: the format specified
+     * in the #pjmedia_vid_codec_param when opening the codec, in this case the
+     * PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED flag will be set in the bit_info
+     * field of the output frame and application can query the new format
+     * using #get_param().
      *
      * @param codec	The codec instance.
      * @param input	The input frame.
@@ -247,6 +281,7 @@
     pj_status_t (*recover)(pjmedia_vid_codec *codec,
 			   unsigned out_size,
 			   pjmedia_frame *output);
+
 } pjmedia_vid_codec_op;
 
 
@@ -258,7 +293,7 @@
 
 
 /**
- * This structure describes a codec instance. 
+ * This structure describes a video codec instance. 
  */
 struct pjmedia_vid_codec
 {
@@ -266,13 +301,13 @@
     PJ_DECL_LIST_MEMBER(struct pjmedia_vid_codec);
 
     /** Codec's private data. */
-    void		    *codec_data;
+    void			*codec_data;
 
     /** Codec factory where this codec was allocated. */
     pjmedia_vid_codec_factory   *factory;
 
     /** Operations to codec. */
-    pjmedia_vid_codec_op	    *op;
+    pjmedia_vid_codec_op	*op;
 };
 
 
@@ -376,55 +411,25 @@
 
 
 /**
+ * Opaque declaration for codec manager.
+ */
+typedef struct pjmedia_vid_codec_mgr pjmedia_vid_codec_mgr;
+
+/**
  * Declare maximum codecs
  */
 #define PJMEDIA_VID_CODEC_MGR_MAX_CODECS	    32
 
 
-/** 
- * Codec manager maintains array of these structs for each supported
- * codec.
- */
-typedef struct pjmedia_vid_codec_desc
-{
-    pjmedia_vid_codec_info	     info;	/**< Codec info.	    */
-    pjmedia_codec_id	             id;        /**< Fully qualified name   */
-    pjmedia_codec_priority           prio;      /**< Priority.		    */
-    pjmedia_vid_codec_factory       *factory;	/**< The factory.	    */
-    pjmedia_vid_codec_param         *def_param; /**< Default codecs 
-					             parameters.	    */
-} pjmedia_vid_codec_desc;
-
 
 /**
- * The declaration for codec manager. Application doesn't normally need
- * to see this declaration, but nevertheless this declaration is needed
- * by media endpoint to instantiate the codec manager.
- */
-typedef struct pjmedia_vid_codec_mgr
-{
-    /** Codec manager mutex. */
-    pj_mutex_t			*mutex;
-
-    /** List of codec factories registered to codec manager. */
-    pjmedia_vid_codec_factory	 factory_list;
-
-    /** Number of supported codecs. */
-    unsigned			 codec_cnt;
-
-    /** Array of codec descriptor. */
-    pjmedia_vid_codec_desc	 codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS];
-
-} pjmedia_vid_codec_mgr;
-
-
-
-/**
- * Initialize codec manager. Normally this function is called by pjmedia
+ * Initialize codec manager. If there is no the default video codec manager,
+ * this function will automatically set the default video codec manager to
+ * the new codec manager instance. Normally this function is called by pjmedia
  * endpoint's initialization code.
  *
- * @param mgr	    Codec manager instance.
- * @param pf	    Pool factory instance.
+ * @param pool	    The pool instance.
+ * @param mgr	    The pointer to the new codec manager instance.
  *
  * @return	    PJ_SUCCESS on success.
  */
@@ -436,13 +441,27 @@
  * Destroy codec manager. Normally this function is called by pjmedia
  * endpoint's deinitialization code.
  *
- * @param mgr	    Codec manager instance.
+ * @param mgr	    Codec manager instance.  If NULL, it is the default codec
+ *		    manager instance will be destroyed.
  *
  * @return	    PJ_SUCCESS on success.
  */
 PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr *mgr);
 
+
+/**
+ * Get the default codec manager instance.
+ *
+ * @return	    The default codec manager instance or NULL if none.
+ */
 PJ_DECL(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void);
+
+
+/**
+ * Set the default codec manager instance.
+ *
+ * @param mgr	    The codec manager instance.
+ */
 PJ_DECL(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr);
 
 
@@ -450,8 +469,8 @@
  * Register codec factory to codec manager. This will also register
  * all supported codecs in the factory to the codec manager.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @param factory   The codec factory to be registered.
  *
  * @return	    PJ_SUCCESS on success.
@@ -465,8 +484,8 @@
  * remove all the codecs registered by the codec factory from the
  * codec manager's list of supported codecs.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @param factory   The codec factory to be unregistered.
  *
  * @return	    PJ_SUCCESS on success.
@@ -479,8 +498,8 @@
  * Enumerate all supported codecs that have been registered to the
  * codec manager by codec factories.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @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
@@ -500,21 +519,21 @@
 /**
  * Get codec info for the specified static payload type.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @param pt	    Static payload type/number.
- * @param inf	    Pointer to receive codec info.
+ * @param info	    Pointer to receive codec info.
  *
  * @return	    PJ_SUCCESS on success.
  */
 PJ_DECL(pj_status_t) 
 pjmedia_vid_codec_mgr_get_codec_info( pjmedia_vid_codec_mgr *mgr,
 				      unsigned pt,
-				      const pjmedia_vid_codec_info **inf);
+				      const pjmedia_vid_codec_info **info);
 
 /**
  * Convert codec info struct into a unique codec identifier.
- * A codec identifier looks something like "L16/44100/2".
+ * A codec identifier looks something like "H263/90000".
  *
  * @param info	    The codec info
  * @param id	    Buffer to put the codec info string.
@@ -531,11 +550,11 @@
 /**
  * Find codecs by the unique codec identifier. This function will find
  * all codecs that match the codec identifier prefix. For example, if
- * "L16" is specified, then it will find "L16/8000/1", "L16/16000/1",
+ * "H26" is specified, then it will find "H263/90000", "H264/90000",
  * and so on, up to the maximum count specified in the argument.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @param codec_id  The full codec ID or codec ID prefix. If an empty
  *		    string is given, it will match all codecs.
  * @param count	    Maximum number of codecs to find. On return, it
@@ -548,11 +567,11 @@
  * @return	    PJ_SUCCESS if at least one codec info is found.
  */
 PJ_DECL(pj_status_t) 
-pjmedia_vid_codec_mgr_find_codecs_by_id( pjmedia_vid_codec_mgr *mgr,
-				     const pj_str_t *codec_id,
-				     unsigned *count,
-				     const pjmedia_vid_codec_info *p_info[],
-				     unsigned prio[]);
+pjmedia_vid_codec_mgr_find_codecs_by_id(pjmedia_vid_codec_mgr *mgr,
+					const pj_str_t *codec_id,
+					unsigned *count,
+					const pjmedia_vid_codec_info *p_info[],
+					unsigned prio[]);
 
 
 /**
@@ -561,8 +580,8 @@
  * are found with the same codec_id prefix, then the function sets the
  * priorities of all those codecs.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @param codec_id  The full codec ID or codec ID prefix. If an empty
  *		    string is given, it will match all codecs.
  * @param prio	    Priority to be set. The priority can have any value
@@ -573,15 +592,15 @@
  */
 PJ_DECL(pj_status_t)
 pjmedia_vid_codec_mgr_set_codec_priority(pjmedia_vid_codec_mgr *mgr, 
-				     const pj_str_t *codec_id,
-				     pj_uint8_t prio);
+					 const pj_str_t *codec_id,
+					 pj_uint8_t prio);
 
 
 /**
  * Get default codec param for the specified codec info.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @param info	    The codec info, which default parameter's is being
  *		    queried.
  * @param param	    On return, will be filled with the default codec
@@ -590,16 +609,17 @@
  * @return	    PJ_SUCCESS on success.
  */
 PJ_DECL(pj_status_t) 
-pjmedia_vid_codec_mgr_get_default_param( pjmedia_vid_codec_mgr *mgr,
-				     const pjmedia_vid_codec_info *info,
-				     pjmedia_vid_codec_param *param );
+pjmedia_vid_codec_mgr_get_default_param(pjmedia_vid_codec_mgr *mgr,
+					const pjmedia_vid_codec_info *info,
+					pjmedia_vid_codec_param *param);
 
 
 /**
  * Set default codec param for the specified codec info.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
+ * @param pool	    The pool instance.
  * @param info	    The codec info, which default parameter's is being
  *		    updated.
  * @param param	    The new default codec parameter. Set to NULL to reset
@@ -619,8 +639,8 @@
  * 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 instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @param info	    The information about the codec to be created.
  * @param p_codec   Pointer to receive the codec instance.
  *
@@ -635,8 +655,8 @@
  * Deallocate the specified codec instance. The codec manager will return
  * the instance of the codec back to its factory.
  *
- * @param mgr	    The codec manager instance. Application can get the
- *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
  * @param codec	    The codec instance.
  *
  * @return	    PJ_SUCESS on success.
@@ -654,9 +674,9 @@
  */
 
 /**
- * @defgroup PJMEDIA_CODEC_CODECS Supported codecs
+ * @defgroup PJMEDIA_CODEC_VID_CODECS Supported video codecs
  * @ingroup PJMEDIA_CODEC
- * @brief Documentation about individual codec supported by PJMEDIA
+ * @brief Documentation about individual video codec supported by PJMEDIA
  * @{
  * Please see the APIs provided by the individual codecs below.
  */
diff --git a/pjmedia/include/pjmedia/vid_stream.h b/pjmedia/include/pjmedia/vid_stream.h
new file mode 100644
index 0000000..fa20aa1
--- /dev/null
+++ b/pjmedia/include/pjmedia/vid_stream.h
@@ -0,0 +1,303 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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_VID_STREAM_H__
+#define __PJMEDIA_VID_STREAM_H__
+
+
+/**
+ * @file vid_stream.h
+ * @brief Video Stream.
+ */
+
+#include <pjmedia/endpoint.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/port.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/transport.h>
+#include <pjmedia/vid_codec.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_VID_STRM Video streams
+ * @ingroup PJMEDIA_PORT
+ * @brief Video communication via the network
+ * @{
+ *
+ * A video stream is a bidirectional video communication between two
+ * endpoints. It corresponds to a video media description ("m=video" line)
+ * in SDP session descriptor.
+ *
+ * A video stream consists of two unidirectional channels:
+ *  - encoding channel, which transmits unidirectional video to remote, and
+ *  - decoding channel, which receives unidirectional media from remote.
+ *
+ * A video stream exports two media port interface (see @ref PJMEDIA_PORT),
+ * one for each direction, and application normally uses this interface to
+ * interconnect the stream to other PJMEDIA components, e.g: the video
+ * capture port supplies frames to the encoding port and video renderer
+ * consumes frames from the decoding port.
+ *
+ * A video stream internally manages the following objects:
+ *  - an instance of video codec (see @ref PJMEDIA_VID_CODEC),
+ *  - an @ref PJMED_JBUF,
+ *  - two instances of RTP sessions (#pjmedia_rtp_session, one for each
+ *    direction),
+ *  - one instance of RTCP session (#pjmedia_rtcp_session),
+ *  - and a reference to video transport to send and receive packets
+ *    to/from the network (see @ref PJMEDIA_TRANSPORT).
+ *
+ * Video streams are created by calling #pjmedia_vid_stream_create(),
+ * specifying #pjmedia_stream_info structure in the parameter. Application
+ * can construct the #pjmedia_vid_stream_info structure manually, or use 
+ * #pjmedia_vid_stream_info_from_sdp() function to construct the
+ * #pjmedia_vid stream_info from local and remote SDP session descriptors.
+ */
+
+
+/** 
+ * This structure describes video stream information. Each video stream
+ * corresponds to one "m=" line in SDP session descriptor, and it has
+ * its own RTP/RTCP socket pair.
+ */
+typedef struct pjmedia_vid_stream_info
+{
+    pjmedia_type	type;	    /**< Media type (audio, video)	    */
+    pjmedia_tp_proto	proto;	    /**< Transport protocol (RTP/AVP, etc.) */
+    pjmedia_dir		dir;	    /**< Media direction.		    */
+    pj_sockaddr		rem_addr;   /**< Remote RTP address		    */
+    pj_sockaddr		rem_rtcp;   /**< Optional remote RTCP address. If
+					 sin_family is zero, the RTP address
+					 will be calculated from RTP.	    */
+    unsigned		tx_pt;	    /**< Outgoing codec paylaod type.	    */
+    pj_uint32_t		ssrc;	    /**< RTP SSRC.			    */
+    pj_uint32_t		rtp_ts;	    /**< Initial RTP timestamp.		    */
+    pj_uint16_t		rtp_seq;    /**< Initial RTP sequence number.	    */
+    pj_uint8_t		rtp_seq_ts_set;
+				    /**< Bitmask flags if initial RTP sequence 
+				         and/or timestamp for sender are set.
+					 bit 0/LSB : sequence flag 
+					 bit 1     : timestamp flag 	    */
+    int			jb_init;    /**< Jitter buffer init delay in msec.  
+					 (-1 for default).		    */
+    int			jb_min_pre; /**< Jitter buffer minimum prefetch
+					 delay in msec (-1 for default).    */
+    int			jb_max_pre; /**< Jitter buffer maximum prefetch
+					 delay in msec (-1 for default).    */
+    int			jb_max;	    /**< Jitter buffer max delay in msec.   */
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+    pj_bool_t		use_ka;	    /**< Stream keep-alive and NAT hole punch
+					 (see #PJMEDIA_STREAM_ENABLE_KA)
+					 is enabled?			    */
+#endif
+
+    pjmedia_vid_codec_info   codec_info;  /**< Incoming codec format info.  */
+    pjmedia_vid_codec_param *codec_param; /**< Optional codec param.	    */
+
+} pjmedia_vid_stream_info;
+
+
+/**
+ * This function will initialize the video stream info based on information
+ * in both SDP session descriptors for the specified stream index. 
+ * The remaining information will be taken from default codec parameters. 
+ * If socket info array is specified, the socket will be copied to the 
+ * session info as well.
+ *
+ * @param si		Stream info structure to be initialized.
+ * @param pool		Pool to allocate memory.
+ * @param endpt		PJMEDIA endpoint instance.
+ * @param local		Local SDP session descriptor.
+ * @param remote	Remote SDP session descriptor.
+ * @param stream_idx	Media stream index in the session descriptor.
+ *
+ * @return		PJ_SUCCESS if stream info is successfully initialized.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_stream_info_from_sdp(pjmedia_vid_stream_info *si,
+			         pj_pool_t *pool,
+				 pjmedia_endpt *endpt,
+				 const pjmedia_sdp_session *local,
+				 const pjmedia_sdp_session *remote,
+				 unsigned stream_idx);
+
+
+/*
+ * Opaque declaration for video stream.
+ */
+typedef struct pjmedia_vid_stream pjmedia_vid_stream;
+
+
+/**
+ * Create a video stream based on the specified parameter. After the video
+ * stream has been created, application normally would want to get the media
+ * port interface of the stream, by calling pjmedia_vid_stream_get_port().
+ * The media port interface exports put_frame() and get_frame() function,
+ * used to transmit and receive media frames from the stream.
+ *
+ * Without application calling put_frame() and get_frame(), there will be 
+ * no media frames transmitted or received by the stream.
+ *
+ * @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 tp		Stream transport instance used to transmit 
+ *			and receive RTP/RTCP packets to/from the underlying 
+ *			transport. 
+ * @param user_data	Arbitrary user data (for future callback feature).
+ * @param p_stream	Pointer to receive the video stream.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_create(
+					pjmedia_endpt *endpt,
+					pj_pool_t *pool,
+					const pjmedia_vid_stream_info *info,
+					pjmedia_transport *tp,
+					void *user_data,
+					pjmedia_vid_stream **p_stream);
+
+/**
+ * Destroy the video stream.
+ *
+ * @param stream	The video stream.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *stream);
+
+
+/**
+ * Get the media port interface of the stream. The media port interface
+ * declares put_frame() and get_frame() function, which is the only 
+ * way for application to transmit and receive media frames from the
+ * stream. As bidirectional video streaming may have different video
+ * formats in the encoding and decoding direction, there are two media
+ * ports exported by the video stream, one for each direction.
+ *
+ * @param stream	The video stream.
+ * @param dir		The video direction.
+ * @param p_port	Pointer to receive the port interface.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_port(
+					    pjmedia_vid_stream *stream,
+					    pjmedia_dir dir,
+					    pjmedia_port **p_port);
+
+
+/**
+ * Get the media transport object associated with this stream.
+ *
+ * @param st		The video stream.
+ *
+ * @return		The transport object being used by the stream.
+ */
+PJ_DECL(pjmedia_transport*) pjmedia_vid_stream_get_transport(
+					    pjmedia_vid_stream *st);
+
+
+/**
+ * Start the video stream. This will start the appropriate channels
+ * in the video stream, depending on the video direction that was set
+ * when the stream was created.
+ *
+ * @param stream	The video stream.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream);
+
+
+/**
+ * Get the stream statistics. See also #pjmedia_stream_get_stat_jbuf()
+ *
+ * @param stream	The video stream.
+ * @param stat		Media stream statistics.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat(
+					    const pjmedia_vid_stream *stream,
+					    pjmedia_rtcp_stat *stat);
+
+/**
+ * Reset the video stream statistics.
+ *
+ * @param stream	The video stream.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream);
+
+
+/**
+ * Get current jitter buffer state. See also #pjmedia_stream_get_stat()
+ *
+ * @param stream	The video stream.
+ * @param state		Jitter buffer state.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
+					    const pjmedia_vid_stream *stream,
+					    pjmedia_jb_state *state);
+
+
+PJ_DECL(pj_status_t) pjmedia_vid_stream_get_param(
+					    const pjmedia_vid_stream *stream,
+					    pjmedia_rtcp_stat *stat);
+
+/**
+ * Pause the individual channel in the stream.
+ *
+ * @param stream	The video channel.
+ * @param dir		Which direction to pause.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream,
+					      pjmedia_dir dir);
+
+/**
+ * Resume the individual channel in the stream.
+ *
+ * @param stream	The video channel.
+ * @param dir		Which direction to resume.
+ *
+ * @return		PJ_SUCCESS on success;
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream,
+					       pjmedia_dir dir);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_VID_STREAM_H__ */
diff --git a/pjmedia/include/pjmedia/videoport.h b/pjmedia/include/pjmedia/videoport.h
index da11db2..3b46632 100644
--- a/pjmedia/include/pjmedia/videoport.h
+++ b/pjmedia/include/pjmedia/videoport.h
@@ -108,7 +108,7 @@
  *
  * @return		The video stream.
  */
-PJ_DECL(pjmedia_vid_stream*)
+PJ_DECL(pjmedia_vid_dev_stream*)
 pjmedia_vid_port_get_stream(pjmedia_vid_port *vid_port);
 
 /**
diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
index 35128e5..b1d15c2 100644
--- a/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
+++ b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
@@ -27,6 +27,7 @@
 #include <pj/string.h>
 #include <pj/os.h>
 
+
 /*
  * Only build this file if PJMEDIA_HAS_FFMPEG_CODEC != 0
  */
@@ -37,6 +38,7 @@
 #include "../pjmedia/ffmpeg_util.h"
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
 
 
 #define PJMEDIA_FORMAT_FFMPEG_UNKNOWN  PJMEDIA_FORMAT_PACK('f','f','0','0');
@@ -65,6 +67,8 @@
 static pj_status_t  ffmpeg_codec_close( pjmedia_vid_codec *codec );
 static pj_status_t  ffmpeg_codec_modify(pjmedia_vid_codec *codec, 
 				        const pjmedia_vid_codec_param *attr );
+static pj_status_t  ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
+					   pjmedia_vid_codec_param *param);
 static pj_status_t  ffmpeg_packetize ( pjmedia_vid_codec *codec,
                                        pj_uint8_t *buf,
                                        pj_size_t buf_len,
@@ -95,6 +99,7 @@
     &ffmpeg_codec_open,
     &ffmpeg_codec_close,
     &ffmpeg_codec_modify,
+    &ffmpeg_codec_get_param,
     &ffmpeg_packetize,
     &ffmpeg_unpacketize,
     &ffmpeg_codec_encode,
@@ -113,14 +118,6 @@
 };
 
 
-typedef struct ffmpeg_codec_info {
-    PJ_DECL_LIST_MEMBER(struct ffmpeg_codec_info);
-    pjmedia_vid_codec_info       info;
-    AVCodec                     *enc;
-    AVCodec                     *dec;
-} ffmpeg_codec_info;
-
-
 /* FFMPEG codecs factory */
 static struct ffmpeg_factory {
     pjmedia_vid_codec_factory    base;
@@ -128,30 +125,359 @@
     pj_pool_factory             *pf;
     pj_pool_t		        *pool;
     pj_mutex_t		        *mutex;
-    ffmpeg_codec_info            codecs;
 } ffmpeg_factory;
 
 
+typedef struct ffmpeg_codec_desc ffmpeg_codec_desc;
+
+/* ITU resolution ID */
+typedef enum itu_res_id {
+    ITU_RES_SQCIF,
+    ITU_RES_QCIF,
+    ITU_RES_CIF,
+    ITU_RES_4CIF,
+    ITU_RES_16CIF,
+    ITU_RES_CUSTOM,
+} itu_res_id;
+
+/* ITU resolution definition */
+struct itu_res {
+    itu_res_id		id;
+    pj_str_t		name;    
+    pjmedia_rect_size	size;
+} itu_res_def [] =
+{
+    {ITU_RES_16CIF,	{"16CIF",5},    {1408,1142}},
+    {ITU_RES_4CIF,	{"4CIF",4},     {704,576}},
+    {ITU_RES_CIF,	{"CIF",3},      {352,288}},
+    {ITU_RES_QCIF,	{"QCIF",4},	{176,144}},
+    {ITU_RES_SQCIF,	{"SQCIF",5},    {88,72}},
+    {ITU_RES_CUSTOM,	{"CUSTOM",6},   {0,0}},
+};
+
 /* FFMPEG codecs private data. */
 typedef struct ffmpeg_private {
+    const ffmpeg_codec_desc	    *desc;
+    pjmedia_vid_codec_param	     param;	/**< Codec param	    */
+    pj_pool_t			    *pool;	/**< Pool for each instance */
+    pj_timestamp		     last_tx;   /**< Timestamp of last 
+						     transmit		    */
+
+    /* Format info and apply format param */
+    const pjmedia_video_format_info *enc_vfi;
+    pjmedia_video_apply_fmt_param    enc_vafp;
+    const pjmedia_video_format_info *dec_vfi;
+    pjmedia_video_apply_fmt_param    dec_vafp;
+
+    /* The ffmpeg codec states. */
+    AVCodec			    *enc;
+    AVCodec			    *dec;
+    AVCodecContext		    *enc_ctx;
+    AVCodecContext		    *dec_ctx;
+
+    /* The ffmpeg decoder cannot set the output format, so format conversion
+     * may be needed for post-decoding.
+     */
+    enum PixelFormat		     expected_dec_fmt;
+						/**< expected output format of 
+						     ffmpeg decoder	    */
+    struct SwsContext		    *sws_ctx;   /**< the format converter for 
+						     post decoding	    */
+
+} ffmpeg_private;
+
+
+typedef pj_status_t (*func_packetize)	(pj_uint8_t *buf,
+					 pj_size_t buf_len,
+					 unsigned *pos,
+					 int max_payload_len,
+					 const pj_uint8_t **payload,
+					 pj_size_t *payload_len);
+
+typedef pj_status_t (*func_unpacketize)	(const pj_uint8_t *payload,
+					 pj_size_t   payload_len,
+					 pj_uint8_t *bits,
+					 pj_size_t  *bits_len);
+
+typedef pj_status_t (*func_parse_fmtp)	(ffmpeg_private *ff);
+
+/* FFMPEG codec info */
+struct ffmpeg_codec_desc {
+    /* Predefined info */
+    pjmedia_vid_codec_info       info;
+    func_packetize		 packetize;
+    func_unpacketize		 unpacketize;
+    func_parse_fmtp		 parse_fmtp;
+    pjmedia_codec_fmtp		 dec_fmtp;
+
+    /* Init time defined info */
+    pj_bool_t			 enabled;
     AVCodec                     *enc;
     AVCodec                     *dec;
-    AVCodecContext              *enc_ctx;
-    AVCodecContext              *dec_ctx;
-    AVCodecParserContext        *dec_parser_ctx;
+};
 
-    /*
-    pjmedia_frame               *pack_frms;
-    unsigned                     pack_frm_cnt;
-    unsigned                     pack_frm_max_cnt;
-    */
+/* H263 packetizer */
+static pj_status_t h263_packetize(pj_uint8_t *buf,
+				  pj_size_t buf_len,
+				  unsigned *pos,
+				  int max_payload_len,
+				  const pj_uint8_t **payload,
+				  pj_size_t *payload_len)
+{
+    return pjmedia_h263_packetize(buf, buf_len, pos, max_payload_len, 
+				  payload, payload_len);
+}
 
-    pjmedia_vid_codec_param      param;	    /**< Codec param.		    */
-    pj_pool_t		        *pool;	    /**< Pool for each instance.    */
-    pj_timestamp	         last_tx;   /**< Timestamp of last transmit.*/
-    const pjmedia_video_format_info *vfi;
-    pjmedia_video_apply_fmt_param    vafp;
-} ffmpeg_private;
+/* H263 unpacketizer */
+static pj_status_t h263_unpacketize(const pj_uint8_t *payload,
+				    pj_size_t   payload_len,
+				    pj_uint8_t *bits,
+				    pj_size_t  *bits_len)
+{
+    return pjmedia_h263_unpacketize(payload, payload_len, bits, bits_len);
+}
+
+/* H263 fmtp parser */
+static pj_status_t h263_parse_fmtp(ffmpeg_private *ff);
+
+
+/* Internal codec info */
+ffmpeg_codec_desc codec_desc[] =
+{
+    {
+	{PJMEDIA_FORMAT_H263,	{"H263",4},	PJMEDIA_RTP_PT_H263},
+	&h263_packetize, &h263_unpacketize, &h263_parse_fmtp,
+	{2, { {{"CIF",3}, {"2",1}}, {{"QCIF",4}, {"1",1}}, } },
+    },
+    {
+	{PJMEDIA_FORMAT_H261,	{"H261",4},	PJMEDIA_RTP_PT_H261},
+    },
+    {
+	{PJMEDIA_FORMAT_MJPEG,	{"JPEG",4},	PJMEDIA_RTP_PT_JPEG},
+    },
+};
+
+/* Parse fmtp value for custom resolution, e.g: "CUSTOM=800,600,2" */
+static pj_status_t parse_fmtp_itu_custom_res(const pj_str_t *fmtp_val,
+					     pjmedia_rect_size *size,
+					     unsigned *mpi)
+{
+    const char *p, *p_end;
+    pj_str_t token;
+    unsigned long val[3] = {0};
+    unsigned i = 0;
+
+    p = token.ptr = fmtp_val->ptr;
+    p_end = p + fmtp_val->slen;
+
+    while (p<=p_end && i<PJ_ARRAY_SIZE(val)) {
+	if (*p==',' || p==p_end) {
+	    token.slen = (char*)p - token.ptr;
+	    val[i++] = pj_strtoul(&token);
+	    token.ptr = (char*)p+1;
+	}
+	++p;
+    }
+
+    if (!val[0] || !val[1])
+	return PJ_ETOOSMALL;
+
+    if (val[2]<1 || val[2]>32)
+	return PJ_EINVAL;
+
+    size->w = val[0];
+    size->h = val[1];
+    *mpi = val[2];
+    return PJ_SUCCESS;
+}
+
+#define CALC_ITU_CUSTOM_RES_SCORE(size, mpi) ((size)->w * (size)->h / mpi)
+
+/* ITU codec capabilities */
+typedef struct itu_cap
+{
+    /* Lowest MPI for each non-custom resolution */
+    unsigned		lowest_mpi[PJ_ARRAY_SIZE(itu_res_def)];
+    /* For custom resolution, we use maximum processing score */
+    unsigned		custom_res_max_score;
+} itu_cap;
+
+
+static pj_status_t load_itu_cap(const pjmedia_codec_fmtp *fmtp,
+				itu_cap *cap)
+{
+    unsigned i, j;
+    unsigned min_mpi = 0;
+
+    /* Get Minimum Picture Interval (MPI) for each resolution. If a resolution
+     * has no MPI setting in fmtp, the MPI setting is derived from the higher
+     * resolution.
+     */
+    for (i=0; i<PJ_ARRAY_SIZE(itu_res_def); ++i) {
+
+	/* Init lowest MPI */
+	cap->lowest_mpi[i] = min_mpi? min_mpi:1;
+
+	for (j=0; j<fmtp->cnt; ++j) {
+	    if (pj_stricmp(&fmtp->param[j].name, &itu_res_def[i].name)==0) {
+		pjmedia_rect_size size;
+		unsigned mpi;
+		unsigned score;
+
+		if (i != ITU_RES_CUSTOM) {
+		    size = itu_res_def[i].size;
+		    mpi = pj_strtoul(&fmtp->param[j].val);
+		    if (min_mpi)
+			min_mpi = PJ_MIN(mpi, min_mpi);
+		    else
+			min_mpi = mpi;
+
+		    /* Update the lowest MPI for this resolution */
+		    cap->lowest_mpi[i] = min_mpi;
+
+		    /* Also update the processing score for the custom 
+		     * resolution.
+		     */
+		    score = CALC_ITU_CUSTOM_RES_SCORE(&size, mpi);
+		    cap->custom_res_max_score = 
+				    PJ_MAX(score, cap->custom_res_max_score);
+		} else {
+		    
+
+		    if (parse_fmtp_itu_custom_res(&fmtp->param[j].val, 
+						  &size, &mpi) == PJ_SUCCESS)
+		    {
+			score = CALC_ITU_CUSTOM_RES_SCORE(&size, mpi);
+			cap->custom_res_max_score = 
+				    PJ_MAX(score, cap->custom_res_max_score);
+		    }
+		}
+	    }
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* H263 fmtp parser */
+static pj_status_t h263_parse_fmtp(ffmpeg_private *ff)
+{
+    pjmedia_dir dir;
+    pj_status_t status;
+
+    dir = ff->param.dir;
+
+    if (ff->param.dir & PJMEDIA_DIR_ENCODING) {
+	pjmedia_vid_codec_param param_ref;
+	pjmedia_codec_fmtp *fmtp_rem, *fmtp_ref;
+	itu_cap local_cap;
+	pjmedia_rect_size size = {0};
+	unsigned mpi = 0;
+	pj_bool_t got_good_res = PJ_FALSE;
+	pj_bool_t has_prefered_res = PJ_FALSE;
+	unsigned i, j;
+
+	fmtp_rem = &ff->param.enc_fmtp;
+	dir &= ~PJMEDIA_DIR_ENCODING;
+
+	/* Get default fmtp setting as the reference for local capabilities */
+	status = pjmedia_vid_codec_mgr_get_default_param(
+			ffmpeg_factory.mgr, &ff->desc->info, &param_ref);
+	fmtp_ref = (status==PJ_SUCCESS)? &param_ref.enc_fmtp : fmtp_rem;
+
+	/* Load default local capabilities */
+	status = load_itu_cap(fmtp_ref, &local_cap);
+	pj_assert(status == PJ_SUCCESS);
+
+	/* Negotiate resolution and MPI */
+	for (i=0; i<fmtp_rem->cnt && !got_good_res; ++i)
+	{
+	    for (j=0; j<PJ_ARRAY_SIZE(itu_res_def) && !got_good_res; ++j)
+	    {
+		if (pj_stricmp(&fmtp_rem->param[i].name, &itu_res_def[j].name))
+		    continue;
+
+		has_prefered_res = PJ_TRUE;
+		if (j == ITU_RES_CUSTOM) {
+		    unsigned score;
+
+		    if (parse_fmtp_itu_custom_res(&fmtp_rem->param[i].val, 
+						  &size, &mpi) != PJ_SUCCESS)
+		    {
+			/* Invalid custom resolution format, skip this 
+			 * custom resolution
+			 */
+			break;
+		    }
+
+		    score = CALC_ITU_CUSTOM_RES_SCORE(&size, mpi);
+		    if (score <= local_cap.custom_res_max_score)
+			got_good_res = PJ_TRUE;
+		} else {
+		    mpi = pj_strtoul(&fmtp_rem->param[i].val);
+		    if (mpi>=1 && mpi<=32 && mpi>=local_cap.lowest_mpi[j]) {
+			got_good_res = PJ_TRUE;
+			size = itu_res_def[j].size;
+		    }
+		}
+	    }
+	}
+
+	if (has_prefered_res) {
+	    if (got_good_res) {
+		pjmedia_video_format_detail *vfd;
+
+		/* Apply this size & MPI */
+		vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt,
+							     PJ_TRUE);
+		vfd->size = size;
+		vfd->fps.num = 30000;
+		vfd->fps.denum = 1001 * mpi;
+		got_good_res = PJ_TRUE;
+
+		PJ_TODO(NOTIFY_APP_ABOUT_THIS_NEW_ENCODING_FORMAT);
+	    } else {
+		return PJ_EUNKNOWN;
+	    }
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+
+static const ffmpeg_codec_desc* find_codec_info(
+			const pjmedia_vid_codec_info *info)
+{
+    int i;
+
+    for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
+	ffmpeg_codec_desc *desc = &codec_desc[i];
+
+	if (desc->enabled &&
+	    (desc->info.fmt_id == info->fmt_id) &&
+            ((desc->info.dir & info->dir) == info->dir) &&
+            pj_stricmp(&desc->info.encoding_name, &info->encoding_name)==0)
+        {
+            return desc;
+        }
+    }
+
+    return NULL;
+}
+
+
+static int find_codec_info_idx_by_fmt_id(pjmedia_format_id fmt_id)
+{
+    int i;
+    for (i=0; i<PJ_ARRAY_SIZE(codec_desc); ++i) {
+	if (codec_desc[i].info.fmt_id == fmt_id)
+	    return i;
+    }
+
+    return -1;
+}
 
 
 /*
@@ -162,7 +488,6 @@
 {
     pj_pool_t *pool;
     AVCodec *c;
-    enum CodecID last_codec_id = CODEC_ID_NONE;
     pj_status_t status;
 
     if (ffmpeg_factory.pool != NULL) {
@@ -178,7 +503,6 @@
     ffmpeg_factory.base.factory_data = NULL;
     ffmpeg_factory.mgr = mgr;
     ffmpeg_factory.pf = pf;
-    pj_list_init(&ffmpeg_factory.codecs);
 
     pool = pj_pool_create(pf, "ffmpeg codec factory", 256, 256, NULL);
     if (!pool)
@@ -196,122 +520,118 @@
     /* Enum FFMPEG codecs */
     for (c=av_codec_next(NULL); c; c=av_codec_next(c)) 
     {
-        ffmpeg_codec_info *ci;
+        ffmpeg_codec_desc *desc;
+	pjmedia_format_id fmt_id;
+	int codec_info_idx;
         
         if (c->type != CODEC_TYPE_VIDEO)
             continue;
 
         /* Video encoder and decoder are usually implemented in separate
-         * AVCodec instances.
+         * AVCodec instances. While the codec attributes (e.g: raw formats,
+	 * supported fps) are in the encoder.
          */
 
-        if (c->id == last_codec_id) {
-            /* This codec usually be the decoder, and not as much info as in
-             * encoder can be fetched here.
-             */
-            pj_assert(!pj_list_empty(&ffmpeg_factory.codecs));
-            ci = ffmpeg_factory.codecs.prev;
-            pj_assert(ci->info.dir != PJMEDIA_DIR_ENCODING_DECODING);
-            pj_assert(!ci->dec || !ci->enc);
-        } else {
-            pjmedia_format_id enc_fmt_id;
-            pjmedia_format_id raw_fmt[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
-            unsigned raw_fmt_cnt = 0;
-            unsigned raw_fmt_cnt_should_be = 0;
+	status = CodecID_to_pjmedia_format_id(c->id, &fmt_id);
+	/* Skip if format ID is unknown */
+	if (status != PJ_SUCCESS)
+	    continue;
 
-            /* Get encoded format id */
-            status = CodecID_to_pjmedia_format_id(c->id, &enc_fmt_id);
-            if (status != PJ_SUCCESS) {
-                //PJ_LOG(5, (THIS_FILE, "Unrecognized ffmpeg codec id %d, "
-                //                      "codec [%s/%s] ignored",
-                //                      c->id, c->name, c->long_name));
-                //enc_fmt_id = PJMEDIA_FORMAT_FFMPEG_UNKNOWN;
+	codec_info_idx = find_codec_info_idx_by_fmt_id(fmt_id);
+	/* Skip if codec is unwanted by this wrapper (not listed in 
+	 * the codec info array)
+	 */
+	if (codec_info_idx < 0)
+	    continue;
 
-                /* Skip unrecognized encoding format ID */
-                continue;
-            }
+	desc = &codec_desc[codec_info_idx];
 
-            /* Get raw/decoded format ids */
-            if (c->pix_fmts) {
-                const enum PixelFormat *p = c->pix_fmts;
-                for(;(p && *p != -1) &&
-                     (raw_fmt_cnt < PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT);
-                     ++p)
-                {
-                    pjmedia_format_id fmt_id;
+	/* Skip duplicated codec implementation */
+	if ((c->encode && (desc->info.dir & PJMEDIA_DIR_ENCODING)) ||
+	    (c->decode && (desc->info.dir & PJMEDIA_DIR_DECODING)))
+	{
+	    continue;
+	}
 
-                    raw_fmt_cnt_should_be++;
-                    status = PixelFormat_to_pjmedia_format_id(*p, &fmt_id);
-                    if (status != PJ_SUCCESS) {
-                        PJ_LOG(6, (THIS_FILE, "Unrecognized ffmpeg pixel "
-                                   "format %d", *p));
-                        continue;
-                    }
-                    raw_fmt[raw_fmt_cnt++] = fmt_id;
-                }
-            } else {
-                /* Unknown raw format, ignore this codec? */
-                continue;
-            }
+	/* Get raw/decoded format ids in the encoder */
+	if (c->pix_fmts && c->encode) {
+	    pjmedia_format_id raw_fmt[PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT];
+	    unsigned raw_fmt_cnt = 0;
+	    unsigned raw_fmt_cnt_should_be = 0;
+	    const enum PixelFormat *p = c->pix_fmts;
 
-            if (raw_fmt_cnt < raw_fmt_cnt_should_be) {
-                PJ_LOG(6, (THIS_FILE, "Codec [%s/%s] have %d raw formats, "
-                                      "recognized only %d raw formats",
-                                      c->name, c->long_name,
-                                      raw_fmt_cnt_should_be, raw_fmt_cnt));
-            }
-            if (raw_fmt_cnt == 0) {
-                PJ_LOG(5, (THIS_FILE, "No recognized raw format "
-                                      "for codec [%s/%s], codec ignored",
-                                      c->name, c->long_name));
-                /* Comment this to see all ffmpeg codecs */
-                continue;
-            }
+	    for(;(p && *p != -1) &&
+		 (raw_fmt_cnt < PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT);
+		 ++p)
+	    {
+		pjmedia_format_id fmt_id;
 
-            ci = PJ_POOL_ZALLOC_T(pool, ffmpeg_codec_info);
-            ci->info.fmt_id = enc_fmt_id;
-            pj_cstr(&ci->info.encoding_name, c->name);
-            ci->info.clock_rate = 90000;
-            ci->info.dec_fmt_id_cnt = raw_fmt_cnt;
-            pj_memcpy(ci->info.dec_fmt_id, raw_fmt, 
-                      sizeof(raw_fmt[0])*raw_fmt_cnt);
+		raw_fmt_cnt_should_be++;
+		status = PixelFormat_to_pjmedia_format_id(*p, &fmt_id);
+		if (status != PJ_SUCCESS) {
+		    PJ_LOG(6, (THIS_FILE, "Unrecognized ffmpeg pixel "
+			       "format %d", *p));
+		    continue;
+		}
+		raw_fmt[raw_fmt_cnt++] = fmt_id;
+	    }
 
-            switch (enc_fmt_id) {
-                case PJMEDIA_FORMAT_H263:
-                    ci->info.pt = PJMEDIA_RTP_PT_H263;
-                    break;
-                case PJMEDIA_FORMAT_H261:
-                    ci->info.pt = PJMEDIA_RTP_PT_H261;
-                    break;
-                default:
-                    break;
-            }
+	    if (raw_fmt_cnt == 0) {
+		PJ_LOG(5, (THIS_FILE, "No recognized raw format "
+				      "for codec [%s/%s], codec ignored",
+				      c->name, c->long_name));
+		/* Skip this encoder */
+		continue;
+	    }
 
-            if (c->supported_framerates) {
-                const AVRational *fr = c->supported_framerates;
-                while ((fr->num != 0 || fr->den != 0) && 
-                       ci->info.fps_cnt < PJMEDIA_VID_CODEC_MAX_FPS_CNT)
-                {
-                    ci->info.fps[ci->info.fps_cnt].num = fr->num;
-                    ci->info.fps[ci->info.fps_cnt].denum = fr->den;
-                    ++ci->info.fps_cnt;
-                    ++fr;
-                }
-            }
+	    if (raw_fmt_cnt < raw_fmt_cnt_should_be) {
+		PJ_LOG(6, (THIS_FILE, "Codec [%s/%s] have %d raw formats, "
+				      "recognized only %d raw formats",
+				      c->name, c->long_name,
+				      raw_fmt_cnt_should_be, raw_fmt_cnt));
+	    }
 
-            pj_list_push_back(&ffmpeg_factory.codecs, ci);
+	    desc->info.dec_fmt_id_cnt = raw_fmt_cnt;
+	    pj_memcpy(desc->info.dec_fmt_id, raw_fmt, 
+		      sizeof(raw_fmt[0])*raw_fmt_cnt);
+	}
+
+	/* Get supported framerates */
+	if (c->supported_framerates) {
+	    const AVRational *fr = c->supported_framerates;
+	    while ((fr->num != 0 || fr->den != 0) && 
+		   desc->info.fps_cnt < PJMEDIA_VID_CODEC_MAX_FPS_CNT)
+	    {
+		desc->info.fps[desc->info.fps_cnt].num = fr->num;
+		desc->info.fps[desc->info.fps_cnt].denum = fr->den;
+		++desc->info.fps_cnt;
+		++fr;
+	    }
+	}
+
+	/* Get ffmpeg encoder instance */
+        if (c->encode && !desc->enc) {
+            desc->info.dir |= PJMEDIA_DIR_ENCODING;
+            desc->enc = c;
+        }
+	
+	/* Get ffmpeg decoder instance */
+        if (c->decode && !desc->dec) {
+            desc->info.dir |= PJMEDIA_DIR_DECODING;
+            desc->dec = c;
         }
 
-        if (c->encode) {
-            ci->info.dir |= PJMEDIA_DIR_ENCODING;
-            ci->enc = c;
-        }
-        if (c->decode) {
-            ci->info.dir |= PJMEDIA_DIR_DECODING;
-            ci->dec = c;
-        }
+	/* Enable this codec when any ffmpeg codec instance are recognized
+	 * and the supported raw formats info has been collected.
+	 */
+	if ((desc->dec || desc->enc) && desc->info.dec_fmt_id_cnt)
+	{
+	    desc->enabled = PJ_TRUE;
+	}
 
-        last_codec_id = c->id;
+	/* Normalize default value of clock rate */
+	if (desc->info.clock_rate == 0)
+	    desc->info.clock_rate = 90000;
     }
 
     /* Register codec factory to codec manager. */
@@ -359,42 +679,19 @@
 }
 
 
-static ffmpeg_codec_info* find_codec(const pjmedia_vid_codec_info *info)
-{
-    ffmpeg_codec_info *ci = ffmpeg_factory.codecs.next;
-
-    pj_mutex_lock(ffmpeg_factory.mutex);
-
-    while (ci != &ffmpeg_factory.codecs) {
-        if ((ci->info.fmt_id == info->fmt_id) &&
-            ((ci->info.dir & info->dir) == info->dir) &&
-            pj_stricmp(&ci->info.encoding_name, &info->encoding_name)==0)
-        {
-            pj_mutex_unlock(ffmpeg_factory.mutex);
-            return ci;
-        }
-        ci = ci->next;
-    }
-
-    pj_mutex_unlock(ffmpeg_factory.mutex);
-
-    return NULL;
-}
-
-
 /* 
  * Check if factory can allocate the specified codec. 
  */
 static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory, 
 				      const pjmedia_vid_codec_info *info )
 {
-    ffmpeg_codec_info *ci;
+    const ffmpeg_codec_desc *desc;
 
     PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
     PJ_ASSERT_RETURN(info, PJ_EINVAL);
 
-    ci = find_codec(info);
-    if (!ci) {
+    desc = find_codec_info(info);
+    if (!desc) {
         return PJMEDIA_CODEC_EUNSUP;
     }
 
@@ -408,23 +705,31 @@
 				        const pjmedia_vid_codec_info *info, 
 				        pjmedia_vid_codec_param *attr )
 {
-    ffmpeg_codec_info *ci;
+    const ffmpeg_codec_desc *desc;
 
     PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
     PJ_ASSERT_RETURN(info && attr, PJ_EINVAL);
 
-    ci = find_codec(info);
-    if (!ci) {
+    desc = find_codec_info(info);
+    if (!desc) {
         return PJMEDIA_CODEC_EUNSUP;
     }
 
     pj_bzero(attr, sizeof(pjmedia_vid_codec_param));
-    attr->dir = ci->info.dir;
-    attr->pt = info->pt;
-    pjmedia_format_init_video(&attr->enc_fmt, ci->info.fmt_id,
-                              352, 288, 25, 1);
-    pjmedia_format_init_video(&attr->dec_fmt, ci->info.dec_fmt_id[0],
-                              352, 288, 25, 1);
+
+    /* Direction */
+    attr->dir = desc->info.dir;
+
+    /* Encoded format */
+    pjmedia_format_init_video(&attr->enc_fmt, desc->info.fmt_id,
+                              352, 288, 30000, 1001);
+
+    /* Decoded format */
+    pjmedia_format_init_video(&attr->dec_fmt, desc->info.dec_fmt_id[0],
+                              352, 288, 30000, 1001);
+
+    /* Decoding fmtp */
+    attr->dec_fmtp = desc->dec_fmtp;
 
     return PJ_SUCCESS;
 }
@@ -436,23 +741,17 @@
 				       unsigned *count, 
 				       pjmedia_vid_codec_info codecs[])
 {
-    ffmpeg_codec_info *ci;
-    unsigned max;
+    unsigned i;
 
     PJ_ASSERT_RETURN(codecs && *count > 0, PJ_EINVAL);
     PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
 
-    max = *count;
-    *count = 0;
-    ci = ffmpeg_factory.codecs.next;
+    *count = PJ_MIN(*count, PJ_ARRAY_SIZE(codec_desc));
 
-    pj_mutex_lock(ffmpeg_factory.mutex);
-    while (*count < max && ci != &ffmpeg_factory.codecs) {
-        pj_memcpy(&codecs[*count], &ci->info, sizeof(pjmedia_vid_codec_info));
-        *count = *count + 1;
-        ci = ci->next;
+    for (i=0; i<*count; ++i) {
+        pj_memcpy(&codecs[i], &codec_desc[i].info, 
+		  sizeof(pjmedia_vid_codec_info));
     }
-    pj_mutex_unlock(ffmpeg_factory.mutex);
 
     return PJ_SUCCESS;
 }
@@ -465,7 +764,7 @@
 				       pjmedia_vid_codec **p_codec)
 {
     ffmpeg_private *ff;
-    ffmpeg_codec_info *ci;
+    const ffmpeg_codec_desc *desc;
     pjmedia_vid_codec *codec;
     pj_pool_t *pool = NULL;
     pj_status_t status = PJ_SUCCESS;
@@ -473,8 +772,8 @@
     PJ_ASSERT_RETURN(factory && info && p_codec, PJ_EINVAL);
     PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
 
-    ci = find_codec(info);
-    if (!ci) {
+    desc = find_codec_info(info);
+    if (!desc) {
         return PJMEDIA_CODEC_EUNSUP;
     }
 
@@ -494,8 +793,9 @@
     }
     codec->codec_data = ff;
     ff->pool = pool;
-    ff->enc = ci->enc;
-    ff->dec = ci->dec;
+    ff->enc = desc->enc;
+    ff->dec = desc->dec;
+    ff->desc = desc;
 
     *p_codec = codec;
     return PJ_SUCCESS;
@@ -550,35 +850,39 @@
 
 }
 
-/*
-static enum PixelFormat ffdec_nego_format(struct AVCodecContext *s, 
+static enum PixelFormat dec_get_format(struct AVCodecContext *s, 
                                           const enum PixelFormat * fmt)
 {
-    enum PixelFormat pix_fmt;
+    ffmpeg_private *ff = (ffmpeg_private*)s->opaque;
+    enum PixelFormat def_fmt = *fmt;
 
-    PJ_UNUSED_ARG(s);
-    PJ_UNUSED_ARG(fmt);
+    while (*fmt != -1) {
+	if (*fmt == ff->expected_dec_fmt)
+	    return *fmt;
+	++fmt;
+    }
 
-    pjmedia_format_id_to_PixelFormat(PJMEDIA_FORMAT_BGRA, &pix_fmt);
-    return pix_fmt;
+    pj_assert(!"Inconsistency in supported formats");
+    return def_fmt;
 }
 
-static void enc_got_payload(struct AVCodecContext *avctx,
-                            void *data, int size, int mb_nb);
-*/
-
 
 static pj_status_t open_ffmpeg_codec(ffmpeg_private *ff,
                                      pj_mutex_t *ff_mutex)
 {
     enum PixelFormat pix_fmt;
     pj_status_t status;
+    pjmedia_video_format_detail *vfd;
 
     status = pjmedia_format_id_to_PixelFormat(ff->param.dec_fmt.id,
                                               &pix_fmt);
     if (status != PJ_SUCCESS)
         return status;
 
+    vfd = pjmedia_format_get_video_format_detail(&ff->param.enc_fmt, 
+						 PJ_TRUE);
+    ff->expected_dec_fmt = pix_fmt;
+
     while (((ff->param.dir & PJMEDIA_DIR_ENCODING) && ff->enc_ctx == NULL) ||
            ((ff->param.dir & PJMEDIA_DIR_DECODING) && ff->dec_ctx == NULL))
     {
@@ -594,12 +898,11 @@
             dir = ff->param.dir;
         }
 
+	/* Init ffmpeg codec context */
         ctx = avcodec_alloc_context();
 
         /* Common attributes */
         ctx->pix_fmt = pix_fmt;
-        ctx->width = ff->param.enc_fmt.det.vid.size.w;
-        ctx->height = ff->param.enc_fmt.det.vid.size.h;
         ctx->workaround_bugs = FF_BUG_AUTODETECT;
         ctx->opaque = ff;
 
@@ -607,45 +910,35 @@
             codec = ff->enc;
 
             /* Encoding only attributes */
-            ctx->time_base.num = ff->param.enc_fmt.det.vid.fps.denum;
-            ctx->time_base.den = ff->param.enc_fmt.det.vid.fps.num;
-            if (ff->param.enc_fmt.det.vid.avg_bps)
-                ctx->bit_rate = ff->param.enc_fmt.det.vid.avg_bps;
-            if (ff->param.enc_fmt.det.vid.max_bps)
-                ctx->rc_max_rate = ff->param.enc_fmt.det.vid.max_bps;
-#if 0
-            if (ff->param.enc_mtu) {
-                //ctx->rtp_payload_size = ff->param.enc_mtu;
-                //ctx->rtp_callback = &enc_got_payload;
+	    ctx->width = vfd->size.w;
+	    ctx->height = vfd->size.h;
+            ctx->time_base.num = vfd->fps.denum;
+            ctx->time_base.den = vfd->fps.num;
+            if (vfd->avg_bps)
+                ctx->bit_rate = vfd->avg_bps;
+            if (vfd->max_bps)
+                ctx->rc_max_rate = vfd->max_bps;
 
-                /* Allocate frame array for RTP payload packing */
-                if (ff->param.enc_fmt.det.vid.max_bps)
-                    ff->pack_frm_max_cnt = ff->param.enc_fmt.det.vid.max_bps /
-                                           ff->param.enc_mtu + 1;
-                else
-                    ff->pack_frm_max_cnt = 32;
-
-                ff->pack_frms = (pjmedia_frame*)
-                                pj_pool_calloc(ff->pool, ff->pack_frm_max_cnt,
-                                               sizeof(ff->pack_frms[0]));
-            }
-#endif
-
-            /* For encoder, should be better to be strict to the standards */
+	    /* For encoder, should be better to be strict to the standards */
             ctx->strict_std_compliance = FF_COMPLIANCE_STRICT;
         }
+
         if (dir & PJMEDIA_DIR_DECODING) {
             codec = ff->dec;
 
             /* Decoding only attributes */
-            ctx->coded_width = ctx->width;
-            ctx->coded_height = ctx->height;
+	    // this setting will be automatically fetched from the bitstream.
+            //ctx->coded_width = ff->param.dec_fmt.det.vid.size.w;
+            //ctx->coded_height = ff->param.dec_fmt.det.vid.size.h;
 
             /* For decoder, be more flexible */
-            if (ff->param.dir!=PJMEDIA_DIR_ENCODING_DECODING || ff->enc!=ff->dec)
+            if (ff->param.dir!=PJMEDIA_DIR_ENCODING_DECODING || 
+		ff->enc!=ff->dec)
+	    {
                 ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+	    }
 
-            //ctx->get_format = &ffdec_nego_format;
+            ctx->get_format = &dec_get_format;
         }
 
         /* avcodec_open() should be protected */
@@ -679,27 +972,54 @@
     PJ_ASSERT_RETURN(codec && attr, PJ_EINVAL);
     ff = (ffmpeg_private*)codec->codec_data;
 
-    ff->vfi = pjmedia_get_video_format_info(NULL, attr->dec_fmt.id);
-    if (!ff->vfi) {
-        status = PJ_EINVAL;
-        goto on_error;
-    }
-
-    pj_bzero(&ff->vafp, sizeof(ff->vafp));
-    ff->vafp.size = attr->dec_fmt.det.vid.size;
-    ff->vafp.buffer = 0;
-    status = (*ff->vfi->apply_fmt)(ff->vfi, &ff->vafp);
-    if (status != PJ_SUCCESS) {
-        goto on_error;
-    }
-
     pj_memcpy(&ff->param, attr, sizeof(*attr));
 
+    /* Apply SDP fmtp attribute */
+    if (ff->desc->parse_fmtp) {
+	status = (*ff->desc->parse_fmtp)(ff);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    /* Open the codec */
     ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex;
     status = open_ffmpeg_codec(ff, ff_mutex);
     if (status != PJ_SUCCESS)
         goto on_error;
 
+    /* Init format info and apply-param of decoder */
+    ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+    if (!ff->dec_vfi) {
+        status = PJ_EINVAL;
+        goto on_error;
+    }
+    pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+    ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+    ff->dec_vafp.buffer = NULL;
+    status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+    if (status != PJ_SUCCESS) {
+        goto on_error;
+    }
+
+    /* Init format info and apply-param of encoder */
+    ff->enc_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+    if (!ff->enc_vfi) {
+        status = PJ_EINVAL;
+        goto on_error;
+    }
+    pj_bzero(&ff->enc_vafp, sizeof(ff->enc_vafp));
+    ff->enc_vafp.size = ff->param.enc_fmt.det.vid.size;
+    ff->enc_vafp.buffer = NULL;
+    status = (*ff->enc_vfi->apply_fmt)(ff->enc_vfi, &ff->enc_vafp);
+    if (status != PJ_SUCCESS) {
+        goto on_error;
+    }
+
+    /* Update codec attributes, e.g: encoding format may be changed by
+     * SDP fmtp negotiation.
+     */
+    pj_memcpy(attr, &ff->param, sizeof(*attr));
+
     return PJ_SUCCESS;
 
 on_error:
@@ -728,13 +1048,12 @@
         avcodec_close(ff->dec_ctx);
         av_free(ff->dec_ctx);
     }
-    if (ff->dec_parser_ctx) {
-        av_parser_close(ff->dec_parser_ctx);
-
+    if (ff->sws_ctx) {
+	sws_freeContext(ff->sws_ctx);
     }
     ff->enc_ctx = NULL;
     ff->dec_ctx = NULL;
-    ff->dec_parser_ctx = NULL;
+    ff->sws_ctx = NULL;
     pj_mutex_unlock(ff_mutex);
 
     return PJ_SUCCESS;
@@ -745,7 +1064,7 @@
  * Modify codec settings.
  */
 static pj_status_t  ffmpeg_codec_modify( pjmedia_vid_codec *codec, 
-				         const pjmedia_vid_codec_param *attr )
+				         const pjmedia_vid_codec_param *attr)
 {
     ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
 
@@ -755,6 +1074,20 @@
     return PJ_ENOTSUP;
 }
 
+static pj_status_t  ffmpeg_codec_get_param(pjmedia_vid_codec *codec,
+					   pjmedia_vid_codec_param *param)
+{
+    ffmpeg_private *ff;
+
+    PJ_ASSERT_RETURN(codec && param, PJ_EINVAL);
+
+    ff = (ffmpeg_private*)codec->codec_data;
+    pj_memcpy(param, &ff->param, sizeof(*param));
+
+    return PJ_SUCCESS;
+}
+
+
 static pj_status_t  ffmpeg_packetize ( pjmedia_vid_codec *codec,
                                        pj_uint8_t *buf,
                                        pj_size_t buf_len,
@@ -764,15 +1097,13 @@
 {
     ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
 
-    switch (ff->param.enc_fmt.id) {
-        case PJMEDIA_FORMAT_H263:
-            return pjmedia_h263_packetize(buf, buf_len, pos,
-                                          ff->param.enc_mtu, payload,
-                                          payload_len);
-            break;
-        default:
-            return PJ_ENOTSUP;
+    if (ff->desc->packetize) {
+	return (*ff->desc->packetize)(buf, buf_len, pos,
+                                      ff->param.enc_mtu, payload,
+                                      payload_len);
     }
+
+    return PJ_ENOTSUP;
 }
 
 static pj_status_t  ffmpeg_unpacketize(pjmedia_vid_codec *codec,
@@ -783,110 +1114,22 @@
 {
     ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
 
-    switch (ff->param.enc_fmt.id) {
-        case PJMEDIA_FORMAT_H263:
-            return pjmedia_h263_unpacketize(payload, payload_len,
-                                            buf, buf_len);
-            break;
-        default:
-            return PJ_ENOTSUP;
+    if (ff->desc->unpacketize) {
+        return (*ff->desc->unpacketize)(payload, payload_len,
+                                        buf, buf_len);
     }
+    
+    return PJ_ENOTSUP;
 }
 
-#if 0
-/*
- * Pack encoded frame to RTP payload frames.
- */
-static pj_status_t  ffmpeg_codec_pack ( pjmedia_vid_codec *codec,
-				        const pjmedia_frame *enc_frame,
-				        unsigned *frame_cnt,
-				        pjmedia_frame frames[])
-{
-    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
-    unsigned i;
-
-    /* Can only work when encoding MTU is set */
-    PJ_ASSERT_RETURN(ff->param.enc_mtu, PJ_EINVALIDOP);
-
-    /* Validate available payload number */
-    PJ_ASSERT_RETURN(ff->pack_frm_cnt <= *frame_cnt, PJ_EINVALIDOP);
-
-    /* Validate encoded bitstream */
-    PJ_ASSERT_RETURN(ff->pack_frm_cnt==0 || 
-                     ff->pack_frms[0].buf == enc_frame->buf,
-                     PJ_EINVAL);
-
-    /* Return the payloads */
-    *frame_cnt = ff->pack_frm_cnt;
-    for (i = 0; i < *frame_cnt; ++i)
-        frames[i] = ff->pack_frms[i];
-
-    return PJ_SUCCESS;
-}
-
-/*
- * Get frames in the packet.
- */
-static pj_status_t  ffmpeg_codec_parse( pjmedia_vid_codec *codec,
-				        void *pkt,
-				        pj_size_t pkt_size,
-				        const pj_timestamp *ts,
-				        pjmedia_frame *frame)
-{
-    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
-    pj_uint8_t *buf = frame->buf;
-    int buf_size = frame->size;
-    int processed;
-
-
-    if (!ff->dec_parser_ctx) {
-        ff->dec_parser_ctx = av_parser_init(ff->dec->id);
-        if (!ff->dec_parser_ctx)
-            return PJ_ENOTSUP;
-    }
-
-#if LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72
-    processed = av_parser_parse2(ff->dec_parser_ctx, ff->dec_ctx, 
-                                 &buf, &buf_size, (uint8_t*)pkt, pkt_size,
-                                 ts->u64, ts->u64, AV_NOPTS_VALUE);
-#else
-    processed = av_parser_parse (ff->dec_parser_ctx, ff->dec_ctx, 
-                                 &buf, &buf_size, (uint8_t*)pkt, pkt_size,
-                                 ts->u64, ts->u64);
-#endif
-
-    if (buf_size) {
-        frame->timestamp = *ts;
-        frame->size = buf_size;
-        return PJ_SUCCESS;
-    }
-
-    return PJ_EPENDING;
-}
-
-static void enc_got_payload(struct AVCodecContext *avctx,
-                            void *data, int size, int mb_nb)
-{
-    ffmpeg_private *ff = (ffmpeg_private*) avctx->opaque;
-    pjmedia_frame *payload;
-
-    pj_assert(ff->pack_frm_cnt < ff->pack_frm_max_cnt);
-    payload = &ff->pack_frms[ff->pack_frm_cnt++];
-    payload->buf = data;
-    payload->size = size;
-    payload->bit_info = mb_nb;
-}
-
-#endif
-
 
 /*
  * Encode frames.
  */
 static pj_status_t ffmpeg_codec_encode( pjmedia_vid_codec *codec, 
-				        const struct pjmedia_frame *input,
+				        const pjmedia_frame *input,
 				        unsigned output_buf_len, 
-				        struct pjmedia_frame *output)
+				        pjmedia_frame *output)
 {
     ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
     pj_uint8_t *p = (pj_uint8_t*)input->buf;
@@ -899,15 +1142,12 @@
     /* Check if encoder has been opened */
     PJ_ASSERT_RETURN(ff->enc_ctx, PJ_EINVALIDOP);
 
-    /*
-    ff->pack_frm_cnt = 0;
-    */
     avcodec_get_frame_defaults(&avframe);
     
-    for (i = 0; i < ff->vfi->plane_cnt; ++i) {
+    for (i = 0; i < ff->enc_vfi->plane_cnt; ++i) {
         avframe.data[i] = p;
-        avframe.linesize[i] = ff->vafp.strides[i];
-        p += ff->vafp.plane_bytes[i];
+        avframe.linesize[i] = ff->enc_vafp.strides[i];
+        p += ff->enc_vafp.plane_bytes[i];
     }
 
 #ifdef _MSC_VER
@@ -945,9 +1185,9 @@
  * Decode frame.
  */
 static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec, 
-				        const struct pjmedia_frame *input,
+				        const pjmedia_frame *input,
 				        unsigned output_buf_len, 
-				        struct pjmedia_frame *output)
+				        pjmedia_frame *output)
 {
     ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
     AVFrame avframe;
@@ -957,7 +1197,8 @@
     /* Check if decoder has been opened */
     PJ_ASSERT_RETURN(ff->dec_ctx, PJ_EINVALIDOP);
 
-    PJ_UNUSED_ARG(output_buf_len);
+    /* Validate output buffer size */
+    PJ_ASSERT_RETURN(ff->dec_vafp.framebytes <= output_buf_len, PJ_ETOOSMALL);
 
     /* Init frame to receive the decoded data, the ffmpeg codec context will
      * automatically provide the decoded buffer (single buffer used for the
@@ -979,6 +1220,8 @@
      */
     pj_bzero(avpacket.data+avpacket.size, FF_INPUT_BUFFER_PADDING_SIZE);
 
+    output->bit_info = 0;
+
 #if LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72
     avpacket.flags = AV_PKT_FLAG_KEY;
 #else
@@ -996,32 +1239,102 @@
         print_ffmpeg_err(err);
         return PJ_EUNKNOWN;
     } else if (got_picture) {
-        pjmedia_video_apply_fmt_param *vafp = (pjmedia_video_apply_fmt_param*)
-                                              &ff->vafp;
+        pjmedia_video_apply_fmt_param *vafp = &ff->dec_vafp;
         pj_uint8_t *q = (pj_uint8_t*)output->buf;
-        unsigned i;
+	unsigned i;
 
-        /* Get the decoded data */
-        for (i = 0; i < ff->vfi->plane_cnt; ++i) {
-            pj_uint8_t *p = avframe.data[i];
+	/* Decoder output format is set by libavcodec, in case it is different
+	 * to the configured param.
+	 */
+	if (ff->dec_ctx->pix_fmt != ff->expected_dec_fmt ||
+	    ff->dec_ctx->coded_width != (int)vafp->size.w ||
+	    ff->dec_ctx->coded_height != (int)vafp->size.h)
+	{
+#if 0	    
+	    // it should not be the codec responsibility to do resizing
+	    pj_uint8_t *data[PJMEDIA_MAX_VIDEO_PLANES] = {0};
+	    unsigned i;
+	    int h;
 
-            /* The decoded data may contain padding */
-            if (avframe.linesize[i]==vafp->strides[i]) {
-                /* No padding, copy the whole plane */
-                pj_memcpy(q, p, vafp->plane_bytes[i]);
-                q += vafp->plane_bytes[i];
-            } else {
-                /* Padding exists, copy line by line */
-                pj_uint8_t *q_end;
-                
-                q_end = q+vafp->plane_bytes[i];
-                while(q < q_end) {
-                    pj_memcpy(q, p, vafp->strides[i]);
-                    q += vafp->strides[i];
-                    p += avframe.linesize[i];
-                }
-            }
-        }
+	    if (!ff->sws_ctx) {
+		pj_assert(sws_isSupportedInput(ff->dec_ctx->pix_fmt) > 0);
+		pj_assert(sws_isSupportedOutput(ff->expected_dec_fmt) > 0);
+		ff->sws_ctx = sws_getContext(ff->dec_ctx->coded_width,
+					     ff->dec_ctx->coded_height,
+					     ff->dec_ctx->pix_fmt,
+					     vafp->size.w, vafp->size.h,
+					     ff->expected_dec_fmt,
+					     SWS_BILINEAR | SWS_PRINT_INFO,
+					     NULL, NULL, NULL);
+		if (ff->sws_ctx == NULL) {
+		    return PJ_EUNKNOWN;
+		}
+	    }
+
+	    for (i = 0; i < ff->vfi->plane_cnt; ++i) {
+		data[i] = q;
+		q += vafp->plane_bytes[i];
+	    }
+	    h = sws_scale(ff->sws_ctx, avframe.data, avframe.linesize, 0, 
+			  ff->dec_ctx->coded_height, data, vafp->strides);
+	    pj_assert((int)vafp->size.h == h);
+#endif
+
+	    pjmedia_format_id new_fmt_id;
+	    pj_status_t status;
+
+	    /* Get current raw format id from ffmpeg decoder context */
+	    status = PixelFormat_to_pjmedia_format_id(ff->dec_ctx->pix_fmt, 
+						      &new_fmt_id);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    /* Update decoder format in param */
+    	    ff->param.dec_fmt.id = new_fmt_id;
+	    ff->param.dec_fmt.det.vid.size.w = ff->dec_ctx->coded_width;
+	    ff->param.dec_fmt.det.vid.size.h = ff->dec_ctx->coded_height;
+
+	    /* Re-init format info and apply-param of decoder */
+	    ff->dec_vfi = pjmedia_get_video_format_info(NULL, ff->param.dec_fmt.id);
+	    if (!ff->dec_vfi)
+		return PJ_EUNKNOWN;
+	    pj_bzero(&ff->dec_vafp, sizeof(ff->dec_vafp));
+	    ff->dec_vafp.size = ff->param.dec_fmt.det.vid.size;
+	    ff->dec_vafp.buffer = NULL;
+	    status = (*ff->dec_vfi->apply_fmt)(ff->dec_vfi, &ff->dec_vafp);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    /* Notify application via the bit_info field of pjmedia_frame */
+	    output->bit_info = PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED;
+	}
+
+	/* Check provided buffer size after format changed */
+	if (vafp->framebytes > output_buf_len)
+	    return PJ_ETOOSMALL;
+
+	/* Get the decoded data */
+	for (i = 0; i < ff->dec_vfi->plane_cnt; ++i) {
+	    pj_uint8_t *p = avframe.data[i];
+
+	    /* The decoded data may contain padding */
+	    if (avframe.linesize[i]!=vafp->strides[i]) {
+		/* Padding exists, copy line by line */
+		pj_uint8_t *q_end;
+                    
+		q_end = q+vafp->plane_bytes[i];
+		while(q < q_end) {
+		    pj_memcpy(q, p, vafp->strides[i]);
+		    q += vafp->strides[i];
+		    p += avframe.linesize[i];
+		}
+	    } else {
+		/* No padding, copy the whole plane */
+		pj_memcpy(q, p, vafp->plane_bytes[i]);
+		q += vafp->plane_bytes[i];
+	    }
+	}
+
         output->size = vafp->framebytes;
     } else {
         return PJ_EUNKNOWN;
@@ -1035,19 +1348,18 @@
  */
 static pj_status_t  ffmpeg_codec_recover( pjmedia_vid_codec *codec, 
 				          unsigned output_buf_len, 
-				          struct pjmedia_frame *output)
+				          pjmedia_frame *output)
 {
-    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
-
+    PJ_UNUSED_ARG(codec);
     PJ_UNUSED_ARG(output_buf_len);
     PJ_UNUSED_ARG(output);
-    PJ_UNUSED_ARG(ff);
 
-    return PJ_SUCCESS;
+    return PJ_ENOTSUP;
 }
 
 #ifdef _MSC_VER
 #   pragma comment( lib, "avcodec.lib")
+#   pragma comment( lib, "swscale.lib")
 #endif
 
 #endif	/* PJMEDIA_HAS_FFMPEG_CODEC */
diff --git a/pjmedia/src/pjmedia-videodev/colorbar_dev.c b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
index 9f5d2c4..74d0622 100644
--- a/pjmedia/src/pjmedia-videodev/colorbar_dev.c
+++ b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
@@ -80,12 +80,12 @@
 /* Video stream. */
 struct cbar_stream
 {
-    pjmedia_vid_stream	 base;		    /**< Base stream	       */
-    pjmedia_vid_param	 param;		    /**< Settings	       */
-    pj_pool_t           *pool;              /**< Memory pool.          */
+    pjmedia_vid_dev_stream	     base;	    /**< Base stream	    */
+    pjmedia_vid_param		     param;	    /**< Settings	    */
+    pj_pool_t			    *pool;          /**< Memory pool.       */
 
-    pjmedia_vid_cb       vid_cb;            /**< Stream callback.      */
-    void                *user_data;         /**< Application data.     */
+    pjmedia_vid_cb		     vid_cb;	    /**< Stream callback.   */
+    void			    *user_data;	    /**< Application data.  */
 
     const struct cbar_fmt_info      *cbfi;
     const pjmedia_video_format_info *vfi;
@@ -105,25 +105,26 @@
                                               pjmedia_vid_dev_factory *f,
 					      unsigned index,
 					      pjmedia_vid_param *param);
-static pj_status_t cbar_factory_create_stream(pjmedia_vid_dev_factory *f,
-					      const pjmedia_vid_param *param,
-					      const pjmedia_vid_cb *cb,
-					      void *user_data,
-					      pjmedia_vid_stream **p_vid_strm);
+static pj_status_t cbar_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					const pjmedia_vid_param *param,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm);
 
-static pj_status_t cbar_stream_get_param(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_get_param(pjmedia_vid_dev_stream *strm,
 					 pjmedia_vid_param *param);
-static pj_status_t cbar_stream_get_cap(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_get_cap(pjmedia_vid_dev_stream *strm,
 				       pjmedia_vid_dev_cap cap,
 				       void *value);
-static pj_status_t cbar_stream_set_cap(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_set_cap(pjmedia_vid_dev_stream *strm,
 				       pjmedia_vid_dev_cap cap,
 				       const void *value);
-static pj_status_t cbar_stream_get_frame(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm,
                                          pjmedia_frame *frame);
-static pj_status_t cbar_stream_start(pjmedia_vid_stream *strm);
-static pj_status_t cbar_stream_stop(pjmedia_vid_stream *strm);
-static pj_status_t cbar_stream_destroy(pjmedia_vid_stream *strm);
+static pj_status_t cbar_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t cbar_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t cbar_stream_destroy(pjmedia_vid_dev_stream *strm);
 
 /* Operations */
 static pjmedia_vid_dev_factory_op factory_op =
@@ -136,7 +137,7 @@
     &cbar_factory_create_stream
 };
 
-static pjmedia_vid_stream_op stream_op =
+static pjmedia_vid_dev_stream_op stream_op =
 {
     &cbar_stream_get_param,
     &cbar_stream_get_cap,
@@ -354,11 +355,12 @@
 }
 
 /* API: create stream */
-static pj_status_t cbar_factory_create_stream(pjmedia_vid_dev_factory *f,
-					      const pjmedia_vid_param *param,
-					      const pjmedia_vid_cb *cb,
-					      void *user_data,
-					      pjmedia_vid_stream **p_vid_strm)
+static pj_status_t cbar_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					const pjmedia_vid_param *param,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm)
 {
     struct cbar_factory *cf = (struct cbar_factory*)f;
     pj_pool_t *pool;
@@ -419,7 +421,7 @@
 }
 
 /* API: Get stream info. */
-static pj_status_t cbar_stream_get_param(pjmedia_vid_stream *s,
+static pj_status_t cbar_stream_get_param(pjmedia_vid_dev_stream *s,
 					 pjmedia_vid_param *pi)
 {
     struct cbar_stream *strm = (struct cbar_stream*)s;
@@ -438,7 +440,7 @@
 }
 
 /* API: get capability */
-static pj_status_t cbar_stream_get_cap(pjmedia_vid_stream *s,
+static pj_status_t cbar_stream_get_cap(pjmedia_vid_dev_stream *s,
 				       pjmedia_vid_dev_cap cap,
 				       void *pval)
 {
@@ -458,7 +460,7 @@
 }
 
 /* API: set capability */
-static pj_status_t cbar_stream_set_cap(pjmedia_vid_stream *s,
+static pj_status_t cbar_stream_set_cap(pjmedia_vid_dev_stream *s,
 				       pjmedia_vid_dev_cap cap,
 				       const void *pval)
 {
@@ -552,7 +554,7 @@
 }
 
 /* API: Get frame from stream */
-static pj_status_t cbar_stream_get_frame(pjmedia_vid_stream *strm,
+static pj_status_t cbar_stream_get_frame(pjmedia_vid_dev_stream *strm,
                                          pjmedia_frame *frame)
 {
     struct cbar_stream *stream = (struct cbar_stream*)strm;
@@ -561,7 +563,7 @@
 }
 
 /* API: Start stream. */
-static pj_status_t cbar_stream_start(pjmedia_vid_stream *strm)
+static pj_status_t cbar_stream_start(pjmedia_vid_dev_stream *strm)
 {
     struct cbar_stream *stream = (struct cbar_stream*)strm;
 
@@ -573,7 +575,7 @@
 }
 
 /* API: Stop stream. */
-static pj_status_t cbar_stream_stop(pjmedia_vid_stream *strm)
+static pj_status_t cbar_stream_stop(pjmedia_vid_dev_stream *strm)
 {
     struct cbar_stream *stream = (struct cbar_stream*)strm;
 
@@ -586,7 +588,7 @@
 
 
 /* API: Destroy stream. */
-static pj_status_t cbar_stream_destroy(pjmedia_vid_stream *strm)
+static pj_status_t cbar_stream_destroy(pjmedia_vid_dev_stream *strm)
 {
     struct cbar_stream *stream = (struct cbar_stream*)strm;
 
diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c
index 7b14c82..2fdd547 100644
--- a/pjmedia/src/pjmedia-videodev/dshow_dev.c
+++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c
@@ -89,19 +89,19 @@
 /* Video stream. */
 struct dshow_stream
 {
-    pjmedia_vid_stream	 base;		    /**< Base stream	       */
-    pjmedia_vid_param	 param;		    /**< Settings	       */
-    pj_pool_t           *pool;              /**< Memory pool.          */
+    pjmedia_vid_dev_stream   base;		    /**< Base stream	    */
+    pjmedia_vid_param	     param;		    /**< Settings	    */
+    pj_pool_t		    *pool;		    /**< Memory pool.	    */
 
-    pjmedia_vid_cb       vid_cb;            /**< Stream callback.     */
-    void                *user_data;         /**< Application data.     */
+    pjmedia_vid_cb	     vid_cb;		    /**< Stream callback.   */
+    void		    *user_data;		    /**< Application data.  */
 
-    pj_bool_t            quit_flag;
-    pj_bool_t		 rend_thread_exited;
-    pj_bool_t		 cap_thread_exited;
-    pj_bool_t		 cap_thread_initialized;
-    pj_thread_desc	 cap_thread_desc;
-    pj_thread_t		*cap_thread;
+    pj_bool_t		     quit_flag;
+    pj_bool_t		     rend_thread_exited;
+    pj_bool_t		     cap_thread_exited;
+    pj_bool_t		     cap_thread_initialized;
+    pj_thread_desc	     cap_thread_desc;
+    pj_thread_t		    *cap_thread;
 
     struct dshow_graph
     {
@@ -126,25 +126,26 @@
                                                pjmedia_vid_dev_factory *f,
 					       unsigned index,
 					       pjmedia_vid_param *param);
-static pj_status_t dshow_factory_create_stream(pjmedia_vid_dev_factory *f,
-					       const pjmedia_vid_param *param,
-					       const pjmedia_vid_cb *cb,
-					       void *user_data,
-					       pjmedia_vid_stream **p_vid_strm);
+static pj_status_t dshow_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					const pjmedia_vid_param *param,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm);
 
-static pj_status_t dshow_stream_get_param(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *strm,
 					  pjmedia_vid_param *param);
-static pj_status_t dshow_stream_get_cap(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *strm,
 				        pjmedia_vid_dev_cap cap,
 				        void *value);
-static pj_status_t dshow_stream_set_cap(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *strm,
 				        pjmedia_vid_dev_cap cap,
 				        const void *value);
-static pj_status_t dshow_stream_start(pjmedia_vid_stream *strm);
-static pj_status_t dshow_stream_put_frame(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
                                           const pjmedia_frame *frame);
-static pj_status_t dshow_stream_stop(pjmedia_vid_stream *strm);
-static pj_status_t dshow_stream_destroy(pjmedia_vid_stream *strm);
+static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm);
 
 /* Operations */
 static pjmedia_vid_dev_factory_op factory_op =
@@ -157,7 +158,7 @@
     &dshow_factory_create_stream
 };
 
-static pjmedia_vid_stream_op stream_op =
+static pjmedia_vid_dev_stream_op stream_op =
 {
     &dshow_stream_get_param,
     &dshow_stream_get_cap,
@@ -395,8 +396,8 @@
 
     /* Set the device capabilities here */
     param->clock_rate = DEFAULT_CLOCK_RATE;
-    param->frame_rate.num = DEFAULT_FPS;
-    param->frame_rate.denum = 1;
+    //param->frame_rate.num = DEFAULT_FPS;
+    //param->frame_rate.denum = 1;
     param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
 
     pjmedia_format_copy(&param->fmt, &di->info.fmt[0]);
@@ -438,7 +439,7 @@
 }
 
 /* API: Put frame from stream */
-static pj_status_t dshow_stream_put_frame(pjmedia_vid_stream *strm,
+static pj_status_t dshow_stream_put_frame(pjmedia_vid_dev_stream *strm,
                                           const pjmedia_frame *frame)
 {
     struct dshow_stream *stream = (struct dshow_stream*)strm;
@@ -697,11 +698,12 @@
 }
 
 /* API: create stream */
-static pj_status_t dshow_factory_create_stream(pjmedia_vid_dev_factory *f,
-					       const pjmedia_vid_param *param,
-					       const pjmedia_vid_cb *cb,
-					       void *user_data,
-					       pjmedia_vid_stream **p_vid_strm)
+static pj_status_t dshow_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					const pjmedia_vid_param *param,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm)
 {
     struct dshow_factory *df = (struct dshow_factory*)f;
     pj_pool_t *pool;
@@ -752,12 +754,12 @@
     return PJ_SUCCESS;
  
 on_error:
-    dshow_stream_destroy((pjmedia_vid_stream *)strm);
+    dshow_stream_destroy((pjmedia_vid_dev_stream *)strm);
     return PJ_EUNKNOWN;
 }
 
 /* API: Get stream info. */
-static pj_status_t dshow_stream_get_param(pjmedia_vid_stream *s,
+static pj_status_t dshow_stream_get_param(pjmedia_vid_dev_stream *s,
 					  pjmedia_vid_param *pi)
 {
     struct dshow_stream *strm = (struct dshow_stream*)s;
@@ -776,7 +778,7 @@
 }
 
 /* API: get capability */
-static pj_status_t dshow_stream_get_cap(pjmedia_vid_stream *s,
+static pj_status_t dshow_stream_get_cap(pjmedia_vid_dev_stream *s,
 				        pjmedia_vid_dev_cap cap,
 				        void *pval)
 {
@@ -796,7 +798,7 @@
 }
 
 /* API: set capability */
-static pj_status_t dshow_stream_set_cap(pjmedia_vid_stream *s,
+static pj_status_t dshow_stream_set_cap(pjmedia_vid_dev_stream *s,
 				        pjmedia_vid_dev_cap cap,
 				        const void *pval)
 {
@@ -816,7 +818,7 @@
 }
 
 /* API: Start stream. */
-static pj_status_t dshow_stream_start(pjmedia_vid_stream *strm)
+static pj_status_t dshow_stream_start(pjmedia_vid_dev_stream *strm)
 {
     struct dshow_stream *stream = (struct dshow_stream*)strm;
     unsigned i;
@@ -842,7 +844,7 @@
 }
 
 /* API: Stop stream. */
-static pj_status_t dshow_stream_stop(pjmedia_vid_stream *strm)
+static pj_status_t dshow_stream_stop(pjmedia_vid_dev_stream *strm)
 {
     struct dshow_stream *stream = (struct dshow_stream*)strm;
     unsigned i;
@@ -868,7 +870,7 @@
 
 
 /* API: Destroy stream. */
-static pj_status_t dshow_stream_destroy(pjmedia_vid_stream *strm)
+static pj_status_t dshow_stream_destroy(pjmedia_vid_dev_stream *strm)
 {
     struct dshow_stream *stream = (struct dshow_stream*)strm;
     unsigned i;
diff --git a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
index abedbf8..6e6ff8e 100644
--- a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
+++ b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
@@ -67,7 +67,7 @@
 
 typedef struct ffmpeg_stream
 {
-    pjmedia_vid_stream           base;
+    pjmedia_vid_dev_stream       base;
     ffmpeg_factory              *factory;
     pj_pool_t                   *pool;
     pjmedia_vid_param            param;
@@ -80,31 +80,32 @@
 static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f);
 static unsigned    ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f);
 static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
-					      unsigned index,
-					      pjmedia_vid_dev_info *info);
+					       unsigned index,
+					       pjmedia_vid_dev_info *info);
 static pj_status_t ffmpeg_factory_default_param(pj_pool_t *pool,
                                                 pjmedia_vid_dev_factory *f,
 					        unsigned index,
 					        pjmedia_vid_param *param);
-static pj_status_t ffmpeg_factory_create_stream(pjmedia_vid_dev_factory *f,
-					       const pjmedia_vid_param *param,
-					       const pjmedia_vid_cb *cb,
-					       void *user_data,
-					       pjmedia_vid_stream **p_vid_strm);
+static pj_status_t ffmpeg_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					const pjmedia_vid_param *param,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm);
 
-static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_stream *strm,
-					  pjmedia_vid_param *param);
-static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_stream *strm,
-				        pjmedia_vid_dev_cap cap,
-				        void *value);
-static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_stream *strm,
-				        pjmedia_vid_dev_cap cap,
-				        const void *value);
-static pj_status_t ffmpeg_stream_start(pjmedia_vid_stream *strm);
-static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_stream *s,
+static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_dev_stream *strm,
+					   pjmedia_vid_param *param);
+static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_dev_stream *strm,
+				         pjmedia_vid_dev_cap cap,
+				         void *value);
+static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_dev_stream *strm,
+				         pjmedia_vid_dev_cap cap,
+				         const void *value);
+static pj_status_t ffmpeg_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
                                            pjmedia_frame *frame);
-static pj_status_t ffmpeg_stream_stop(pjmedia_vid_stream *strm);
-static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_stream *strm);
+static pj_status_t ffmpeg_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_dev_stream *strm);
 
 /* Operations */
 static pjmedia_vid_dev_factory_op factory_op =
@@ -117,7 +118,7 @@
     &ffmpeg_factory_create_stream
 };
 
-static pjmedia_vid_stream_op stream_op =
+static pjmedia_vid_dev_stream_op stream_op =
 {
     &ffmpeg_stream_get_param,
     &ffmpeg_stream_get_cap,
@@ -152,14 +153,14 @@
                                        const pjmedia_vid_param *param)
 {
     AVFormatParameters fp;
-    pjmedia_video_format_detail *fmt_detail;
+    pjmedia_video_format_detail *vfd;
     int err;
 
     PJ_ASSERT_RETURN(ctx && ifmt && dev_name && param, PJ_EINVAL);
     PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
                      PJ_EINVAL);
 
-    fmt_detail = (pjmedia_video_format_detail*)param->fmt.detail;
+    vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
 
     /* Init ffmpeg format context */
     *ctx = avformat_alloc_context();
@@ -167,11 +168,11 @@
     /* Init ffmpeg format param */
     pj_bzero(&fp, sizeof(fp));
     fp.prealloced_context = 1;
-    fp.width = fmt_detail->size.w;
-    fp.height = fmt_detail->size.h;
+    fp.width = vfd->size.w;
+    fp.height = vfd->size.h;
     fp.pix_fmt = PIX_FMT_BGR24;
-    fp.time_base.num = param->frame_rate.denum;
-    fp.time_base.den = param->frame_rate.num;
+    fp.time_base.num = vfd->fps.denum;
+    fp.time_base.den = vfd->fps.num;
 
     /* Open capture stream */
     err = av_open_input_stream(ctx, NULL, dev_name, ifmt, &fp);
@@ -288,8 +289,8 @@
 
 /* API: get device info */
 static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
-					      unsigned index,
-					      pjmedia_vid_dev_info *info)
+					       unsigned index,
+					       pjmedia_vid_dev_info *info)
 {
     ffmpeg_factory *ff = (ffmpeg_factory*)f;
 
@@ -308,9 +309,11 @@
 {
     ffmpeg_factory *ff = (ffmpeg_factory*)f;
     ffmpeg_dev_info *info;
-    pjmedia_video_format_detail *fmt_detail;
 
     PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV);
+
+    PJ_UNUSED_ARG(pool);
+
     info = &ff->dev_info[index];
 
     pj_bzero(param, sizeof(*param));
@@ -321,13 +324,9 @@
 
     /* Set the device capabilities here */
     param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
-    pj_memcpy(&param->fmt, &info->base.fmt[0], sizeof(param->fmt));
     param->clock_rate = 90000;
-    pjmedia_format_init_video(pool, &param->fmt, 320, 240, 25, 1,
-                              0, 0);
-    fmt_detail = (pjmedia_video_format_detail*)param->fmt.detail;
-    param->frame_rate.num = fmt_detail->fps.num;
-    param->frame_rate.denum = fmt_detail->fps.denum;
+    pjmedia_format_init_video(&param->fmt, 0, 320, 240, 25, 1);
+    param->fmt.id = info->base.fmt[0].id;
 
     return PJ_SUCCESS;
 }
@@ -335,11 +334,12 @@
 
 
 /* API: create stream */
-static pj_status_t ffmpeg_factory_create_stream(pjmedia_vid_dev_factory *f,
-					       const pjmedia_vid_param *param,
-					       const pjmedia_vid_cb *cb,
-					       void *user_data,
-					       pjmedia_vid_stream **p_vid_strm)
+static pj_status_t ffmpeg_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					const pjmedia_vid_param *param,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm)
 {
     ffmpeg_factory *ff = (ffmpeg_factory*)f;
     pj_pool_t *pool;
@@ -348,8 +348,8 @@
     PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
     PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL);
     PJ_ASSERT_RETURN((unsigned)param->cap_id < ff->dev_count, PJ_EINVAL);
-    PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
-                     param->fmt.detail, PJ_EINVAL);
+    PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
+		     PJ_EINVAL);
 
     PJ_UNUSED_ARG(cb);
     PJ_UNUSED_ARG(user_data);
@@ -371,7 +371,7 @@
 }
 
 /* API: Get stream info. */
-static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_stream *s,
+static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_dev_stream *s,
 					   pjmedia_vid_param *pi)
 {
     ffmpeg_stream *strm = (ffmpeg_stream*)s;
@@ -384,9 +384,9 @@
 }
 
 /* API: get capability */
-static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_stream *s,
-				        pjmedia_vid_dev_cap cap,
-				        void *pval)
+static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_dev_stream *s,
+				         pjmedia_vid_dev_cap cap,
+				         void *pval)
 {
     ffmpeg_stream *strm = (ffmpeg_stream*)s;
 
@@ -398,9 +398,9 @@
 }
 
 /* API: set capability */
-static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_stream *s,
-				        pjmedia_vid_dev_cap cap,
-				        const void *pval)
+static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_dev_stream *s,
+				         pjmedia_vid_dev_cap cap,
+				         const void *pval)
 {
     ffmpeg_stream *strm = (ffmpeg_stream*)s;
 
@@ -413,7 +413,7 @@
 
 
 /* API: Start stream. */
-static pj_status_t ffmpeg_stream_start(pjmedia_vid_stream *s)
+static pj_status_t ffmpeg_stream_start(pjmedia_vid_dev_stream *s)
 {
     ffmpeg_stream *strm = (ffmpeg_stream*)s;
     ffmpeg_dev_info *info;
@@ -435,7 +435,7 @@
 
 
 /* API: Get frame from stream */
-static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_stream *s,
+static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
                                            pjmedia_frame *frame)
 {
     ffmpeg_stream *strm = (ffmpeg_stream*)s;
@@ -458,7 +458,7 @@
 
 
 /* API: Stop stream. */
-static pj_status_t ffmpeg_stream_stop(pjmedia_vid_stream *s)
+static pj_status_t ffmpeg_stream_stop(pjmedia_vid_dev_stream *s)
 {
     ffmpeg_stream *strm = (ffmpeg_stream*)s;
 
@@ -472,7 +472,7 @@
 
 
 /* API: Destroy stream. */
-static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_stream *s)
+static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_dev_stream *s)
 {
     ffmpeg_stream *strm = (ffmpeg_stream*)s;
 
diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev.c b/pjmedia/src/pjmedia-videodev/sdl_dev.c
index a60060c..6a05029 100644
--- a/pjmedia/src/pjmedia-videodev/sdl_dev.c
+++ b/pjmedia/src/pjmedia-videodev/sdl_dev.c
@@ -90,28 +90,28 @@
 /* Video stream. */
 struct sdl_stream
 {
-    pjmedia_vid_stream	 base;		    /**< Base stream	       */
-    pjmedia_vid_param	 param;		    /**< Settings	       */
-    pj_pool_t           *pool;              /**< Memory pool.          */
+    pjmedia_vid_dev_stream	 base;		    /**< Base stream	    */
+    pjmedia_vid_param		 param;		    /**< Settings	    */
+    pj_pool_t			*pool;              /**< Memory pool.       */
 
-    pjmedia_vid_cb       vid_cb;            /**< Stream callback.      */
-    void                *user_data;         /**< Application data.     */
+    pjmedia_vid_cb		 vid_cb;            /**< Stream callback.   */
+    void			*user_data;         /**< Application data.  */
 
-    pj_thread_t         *sdl_thread;        /**< SDL thread.           */
-    pj_bool_t            is_quitting;
-    pj_bool_t            is_running;
-    pj_bool_t            render_exited;
-    pj_status_t          status;
+    pj_thread_t			*sdl_thread;        /**< SDL thread.        */
+    pj_bool_t			 is_quitting;
+    pj_bool_t			 is_running;
+    pj_bool_t			 render_exited;
+    pj_status_t			 status;
 
-    SDL_Rect             rect;              /**< Display rectangle.    */
-    SDL_Surface         *screen;            /**< Display screen.       */
-    SDL_Surface         *surf;              /**< RGB surface.          */
-    SDL_Overlay         *overlay;           /**< YUV overlay.          */
+    SDL_Rect			 rect;              /**< Display rectangle. */
+    SDL_Surface			*screen;            /**< Display screen.    */
+    SDL_Surface			*surf;              /**< RGB surface.       */
+    SDL_Overlay			*overlay;           /**< YUV overlay.       */
 
     /* For frame conversion */
-    pjmedia_converter       *conv;
-    pjmedia_conversion_param conv_param;
-    pjmedia_frame            conv_buf;
+    pjmedia_converter		*conv;
+    pjmedia_conversion_param	 conv_param;
+    pjmedia_frame		 conv_buf;
 
     pjmedia_video_apply_fmt_param vafp;
 };
@@ -128,25 +128,26 @@
                                              pjmedia_vid_dev_factory *f,
 					     unsigned index,
 					     pjmedia_vid_param *param);
-static pj_status_t sdl_factory_create_stream(pjmedia_vid_dev_factory *f,
-					     const pjmedia_vid_param *param,
-					     const pjmedia_vid_cb *cb,
-					     void *user_data,
-					     pjmedia_vid_stream **p_vid_strm);
+static pj_status_t sdl_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					const pjmedia_vid_param *param,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm);
 
-static pj_status_t sdl_stream_get_param(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *strm,
 					pjmedia_vid_param *param);
-static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *strm,
 				      pjmedia_vid_dev_cap cap,
 				      void *value);
-static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *strm,
 				      pjmedia_vid_dev_cap cap,
 				      const void *value);
-static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
                                         const pjmedia_frame *frame);
-static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm);
-static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm);
-static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm);
+static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm);
 
 /* Operations */
 static pjmedia_vid_dev_factory_op factory_op =
@@ -159,7 +160,7 @@
     &sdl_factory_create_stream
 };
 
-static pjmedia_vid_stream_op stream_op =
+static pjmedia_vid_dev_stream_op stream_op =
 {
     &sdl_stream_get_param,
     &sdl_stream_get_cap,
@@ -407,9 +408,9 @@
                             break;
                         }
                     if (strm->is_running)
-                        pjmedia_vid_stream_stop(&strm->base);
+                        pjmedia_vid_dev_stream_stop(&strm->base);
                     else
-                        pjmedia_vid_stream_start(&strm->base);
+                        pjmedia_vid_dev_stream_start(&strm->base);
                     break;
                 case SDL_VIDEORESIZE:
                     pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
@@ -433,7 +434,8 @@
                     /**
                      * To process PJMEDIA_EVENT_WINDOW_CLOSE event,
                      * application should do this in the on_event_cb callback:
-                     * 1. stop further calls to #pjmedia_vid_stream_put_frame()
+                     * 1. stop further calls to 
+		     *    #pjmedia_vid_dev_stream_put_frame()
                      * 2. return PJ_SUCCESS
                      * Upon returning from the callback, SDL will destroy its
                      * own stream.
@@ -483,11 +485,12 @@
 }
 
 /* API: create stream */
-static pj_status_t sdl_factory_create_stream(pjmedia_vid_dev_factory *f,
-					     const pjmedia_vid_param *param,
-					     const pjmedia_vid_cb *cb,
-					     void *user_data,
-					     pjmedia_vid_stream **p_vid_strm)
+static pj_status_t sdl_factory_create_stream(
+					pjmedia_vid_dev_factory *f,
+					const pjmedia_vid_param *param,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm)
 {
     struct sdl_factory *sf = (struct sdl_factory*)f;
     pj_pool_t *pool;
@@ -548,7 +551,7 @@
 }
 
 /* API: Get stream info. */
-static pj_status_t sdl_stream_get_param(pjmedia_vid_stream *s,
+static pj_status_t sdl_stream_get_param(pjmedia_vid_dev_stream *s,
 					pjmedia_vid_param *pi)
 {
     struct sdl_stream *strm = (struct sdl_stream*)s;
@@ -567,7 +570,7 @@
 }
 
 /* API: get capability */
-static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *s,
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_dev_stream *s,
 				      pjmedia_vid_dev_cap cap,
 				      void *pval)
 {
@@ -586,7 +589,7 @@
 }
 
 /* API: set capability */
-static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *s,
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_dev_stream *s,
 				      pjmedia_vid_dev_cap cap,
 				      const void *pval)
 {
@@ -605,7 +608,7 @@
 }
 
 /* API: Put frame from stream */
-static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_dev_stream *strm,
                                         const pjmedia_frame *frame)
 {
     struct sdl_stream *stream = (struct sdl_stream*)strm;
@@ -664,7 +667,7 @@
 }
 
 /* API: Start stream. */
-static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm)
+static pj_status_t sdl_stream_start(pjmedia_vid_dev_stream *strm)
 {
     struct sdl_stream *stream = (struct sdl_stream*)strm;
 
@@ -677,7 +680,7 @@
 }
 
 /* API: Stop stream. */
-static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm)
+static pj_status_t sdl_stream_stop(pjmedia_vid_dev_stream *strm)
 {
     struct sdl_stream *stream = (struct sdl_stream*)strm;
     unsigned i;
@@ -694,7 +697,7 @@
 
 
 /* API: Destroy stream. */
-static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm)
+static pj_status_t sdl_stream_destroy(pjmedia_vid_dev_stream *strm)
 {
     struct sdl_stream *stream = (struct sdl_stream*)strm;
     SDL_Event sevent;
diff --git a/pjmedia/src/pjmedia-videodev/videodev.c b/pjmedia/src/pjmedia-videodev/videodev.c
index 7ca87cd..522f415 100644
--- a/pjmedia/src/pjmedia-videodev/videodev.c
+++ b/pjmedia/src/pjmedia-videodev/videodev.c
@@ -424,8 +424,8 @@
 {
     unsigned i;
 
-    /* Allow shutdown() to be called multiple times as long as there is matching
-     * number of init().
+    /* Allow shutdown() to be called multiple times as long as there is
+     * matching number of init().
      */
     if (vid_subsys.init_count == 0) {
 	return PJ_SUCCESS;
@@ -546,7 +546,8 @@
     PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
 
     for (drv_idx=0; drv_idx<vid_subsys.drv_cnt; ++drv_idx) {
-	if (!pj_ansi_stricmp(drv_name, vid_subsys.drv[drv_idx].name)) {
+	if (!pj_ansi_stricmp(drv_name, vid_subsys.drv[drv_idx].name))
+	{
 	    f = vid_subsys.drv[drv_idx].f;
 	    break;
 	}
@@ -555,7 +556,8 @@
     if (!f)
 	return PJ_ENOTFOUND;
 
-    for (dev_idx=0; dev_idx<vid_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
+    for (dev_idx=0; dev_idx<vid_subsys.drv[drv_idx].dev_cnt; ++dev_idx)
+    {
 	pjmedia_vid_dev_info info;
 	pj_status_t status;
 
@@ -606,10 +608,11 @@
 }
 
 /* API: Open video stream object using the specified parameters. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_create(const pjmedia_vid_param *prm,
-                                              const pjmedia_vid_cb *cb,
-					      void *user_data,
-					      pjmedia_vid_stream **p_vid_strm)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_create(
+					const pjmedia_vid_param *prm,
+					const pjmedia_vid_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm)
 {
     pjmedia_vid_dev_factory *cap_f=NULL, *rend_f=NULL, *f=NULL;
     pjmedia_vid_param param;
@@ -674,8 +677,9 @@
 }
 
 /* API: Get the running parameters for the specified video stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_get_param(pjmedia_vid_stream *strm,
-						 pjmedia_vid_param *param)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_param(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_vid_param *param)
 {
     pj_status_t status;
 
@@ -694,49 +698,54 @@
 }
 
 /* API: Get the value of a specific capability of the video stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_get_cap(pjmedia_vid_stream *strm,
-					       pjmedia_vid_dev_cap cap,
-					       void *value)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_cap(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_vid_dev_cap cap,
+					    void *value)
 {
     return strm->op->get_cap(strm, cap, value);
 }
 
 /* API: Set the value of a specific capability of the video stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_set_cap(pjmedia_vid_stream *strm,
-					       pjmedia_vid_dev_cap cap,
-					       const void *value)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_set_cap(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_vid_dev_cap cap,
+					    const void *value)
 {
     return strm->op->set_cap(strm, cap, value);
 }
 
 /* API: Start the stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *strm)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_start(pjmedia_vid_dev_stream *strm)
 {
     return strm->op->start(strm);
 }
 
-PJ_DEF(pj_status_t) pjmedia_vid_stream_get_frame(pjmedia_vid_stream *strm,
-                                                 pjmedia_frame *frame)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_frame(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_frame *frame)
 {
     pj_assert(strm->op->get_frame);
     return strm->op->get_frame(strm, frame);
 }
 
-PJ_DEF(pj_status_t) pjmedia_vid_stream_put_frame(pjmedia_vid_stream *strm,
-                                                 const pjmedia_frame *frame)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_put_frame(
+					    pjmedia_vid_dev_stream *strm,
+                                            const pjmedia_frame *frame)
 {
     pj_assert(strm->op->put_frame);
     return strm->op->put_frame(strm, frame);
 }
 
 /* API: Stop the stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_stop(pjmedia_vid_stream *strm)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm)
 {
     return strm->op->stop(strm);
 }
 
 /* API: Destroy the stream. */
-PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *strm)
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_destroy(
+						pjmedia_vid_dev_stream *strm)
 {
     return strm->op->destroy(strm);
 }
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index 7a863f2..9eb98a2 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -68,6 +68,7 @@
     int		    *frame_type;	/**< frame type array		    */
     pj_size_t	    *content_len;	/**< frame length array		    */
     pj_uint32_t	    *bit_info;		/**< frame bit info array	    */
+    pj_uint32_t	    *ts;		/**< timestamp array		    */
     
     /* States */
     unsigned	     head;		/**< index of head, pointed frame
@@ -197,7 +198,10 @@
 			      pj_pool_alloc(pool, 
 					    sizeof(framelist->bit_info[0])*
 					    framelist->max_count);
-
+    framelist->ts	    = (pj_uint32_t*)
+			      pj_pool_alloc(pool, 
+					    sizeof(framelist->ts[0])*
+					    framelist->max_count);
 
     return jb_framelist_reset(framelist);
 
@@ -258,7 +262,8 @@
 static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
 				  void *frame, pj_size_t *size,
 				  pjmedia_jb_frame_type *p_type,
-				  pj_uint32_t *bit_info) 
+				  pj_uint32_t *bit_info,
+				  pj_uint32_t *ts) 
 {
     if (framelist->size) {
 
@@ -281,6 +286,8 @@
 		*size   = framelist->content_len[framelist->head];
 	    if (bit_info)
 		*bit_info = framelist->bit_info[framelist->head];
+	    if (ts)
+		*ts = framelist->ts[framelist->head];
 
 	    //pj_bzero(framelist->content + 
 	    //	 framelist->head * framelist->frame_size,
@@ -288,6 +295,7 @@
 	    framelist->frame_type[framelist->head] = PJMEDIA_JB_MISSING_FRAME;
 	    framelist->content_len[framelist->head] = 0;
 	    framelist->bit_info[framelist->head] = 0;
+	    framelist->ts[framelist->head] = 0;
 
 	    framelist->origin++;
 	    framelist->head = (framelist->head + 1) % framelist->max_count;
@@ -304,6 +312,49 @@
 }
 
 
+static pj_bool_t jb_framelist_peek(jb_framelist_t *framelist,
+				   unsigned idx,
+				   const void **frame,
+				   pj_size_t *size,
+				   pjmedia_jb_frame_type *type,
+				   pj_uint32_t *bit_info,
+				   pj_uint32_t *ts) 
+{
+    unsigned pos;
+
+    if (idx >= jb_framelist_eff_size(framelist))
+	return PJ_FALSE;
+
+    pos = framelist->head;
+
+    /* Find actual peek position, note there may be discarded frames */
+    while (1) {
+	if (framelist->frame_type[pos] != PJMEDIA_JB_DISCARDED_FRAME) {
+	    if (idx == 0)
+		break;
+	    else
+		--idx;
+	}
+	pos = (pos + 1) % framelist->max_count;
+    }
+
+    /* Return the frame pointer */
+    if (frame)
+	*frame = framelist->content + pos*framelist->frame_size;
+    if (type)
+	*type = (pjmedia_jb_frame_type) 
+		framelist->frame_type[pos];
+    if (size)
+	*size = framelist->content_len[pos];
+    if (bit_info)
+	*bit_info = framelist->bit_info[pos];
+    if (ts)
+	*ts = framelist->ts[pos];
+
+    return PJ_TRUE;
+}
+
+
 /* Remove oldest frames as many as param 'count' */
 static unsigned jb_framelist_remove_head(jb_framelist_t *framelist,
 					 unsigned count) 
@@ -372,6 +423,7 @@
 				       const void *frame,
 				       unsigned frame_size,
 				       pj_uint32_t bit_info,
+				       pj_uint32_t ts,
 				       unsigned frame_type)
 {
     int distance;
@@ -425,6 +477,7 @@
     framelist->frame_type[pos] = frame_type;
     framelist->content_len[pos] = frame_size;
     framelist->bit_info[pos] = bit_info;
+    framelist->ts[pos] = ts;
 
     /* update framelist size */
     if (framelist->origin + (int)framelist->size <= index)
@@ -734,7 +787,7 @@
 				     pj_size_t frame_size, 
 				     int frame_seq)
 {
-    pjmedia_jbuf_put_frame2(jb, frame, frame_size, 0, frame_seq, NULL);
+    pjmedia_jbuf_put_frame3(jb, frame, frame_size, 0, frame_seq, 0, NULL);
 }
 
 PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb, 
@@ -744,6 +797,18 @@
 				     int frame_seq,
 				     pj_bool_t *discarded)
 {
+    pjmedia_jbuf_put_frame3(jb, frame, frame_size, bit_info, frame_seq, 0, 
+			    discarded);
+}
+
+PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb, 
+				     const void *frame, 
+				     pj_size_t frame_size, 
+				     pj_uint32_t bit_info,
+				     int frame_seq,
+				     pj_uint32_t ts,
+				     pj_bool_t *discarded)
+{
     pj_size_t min_frame_size;
     int new_size, cur_size, frame_type = PJMEDIA_JB_NORMAL_FRAME;
     pj_status_t status;
@@ -811,7 +876,7 @@
     /* Attempt to store the frame */
     min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
     status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
-				 min_frame_size, bit_info, frame_type);
+				 min_frame_size, bit_info, ts, frame_type);
     
     /* Jitter buffer is full, remove some older frames */
     while (status == PJ_ETOOMANY) {
@@ -834,7 +899,7 @@
 #endif
 	removed = jb_framelist_remove_head(&jb->jb_framelist, distance);
 	status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
-				     min_frame_size, bit_info, frame_type);
+				     min_frame_size, bit_info, ts, frame_type);
 
 	jb->jb_discard += removed;
     }
@@ -866,7 +931,7 @@
 				     void *frame, 
 				     char *p_frame_type)
 {
-    pjmedia_jbuf_get_frame2(jb, frame, NULL, p_frame_type, NULL);
+    pjmedia_jbuf_get_frame3(jb, frame, NULL, p_frame_type, NULL, NULL);
 }
 
 /*
@@ -878,6 +943,19 @@
 				     char *p_frame_type,
 				     pj_uint32_t *bit_info)
 {
+    pjmedia_jbuf_get_frame3(jb, frame, size, p_frame_type, bit_info, NULL);
+}
+
+/*
+ * Get frame from jitter buffer.
+ */
+PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb, 
+				     void *frame, 
+				     pj_size_t *size,
+				     char *p_frame_type,
+				     pj_uint32_t *bit_info,
+				     pj_uint32_t *ts)
+{
     if (jb->jb_status == JB_STATUS_PREFETCHING) {
 
 	/* Can't return frame because jitter buffer is filling up
@@ -901,7 +979,7 @@
 
 	/* Try to retrieve a frame from frame list */
 	res = jb_framelist_get(&jb->jb_framelist, frame, size, &ftype, 
-			       bit_info);
+			       bit_info, ts);
 	if (res) {
 	    /* We've successfully retrieved a frame from the frame list, but
 	     * the frame could be a blank frame!
@@ -969,3 +1047,48 @@
     return PJ_SUCCESS;
 }
 
+
+PJ_DEF(void) pjmedia_jbuf_peek_frame( pjmedia_jbuf *jb,
+				      unsigned idx,
+				      const void **frame, 
+				      pj_size_t *size, 
+				      char *p_frm_type,
+				      pj_uint32_t *bit_info,
+				      pj_uint32_t *ts)
+{
+    pjmedia_jb_frame_type ftype;
+    pj_bool_t res;
+
+    res = jb_framelist_peek(&jb->jb_framelist, idx, frame, size, &ftype, bit_info, ts);
+    if (!res)
+	*p_frm_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
+    else if (ftype == PJMEDIA_JB_NORMAL_FRAME)
+	*p_frm_type = PJMEDIA_JB_NORMAL_FRAME;
+    else
+	*p_frm_type = PJMEDIA_JB_MISSING_FRAME;
+}
+
+
+PJ_DEF(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb, 
+					   unsigned frame_cnt)
+{
+    unsigned count, last_discard_num;
+
+    last_discard_num = jb->jb_framelist.discarded_num;
+    count = jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
+
+    /* Remove some more when there were discarded frames included */
+    while (jb->jb_framelist.discarded_num < last_discard_num) {
+	/* Calculate frames count to be removed next */
+	frame_cnt = last_discard_num - jb->jb_framelist.discarded_num;
+
+	/* Normalize non-discarded frames count just been removed */
+	count -= frame_cnt;
+
+	/* Remove more frames */
+	last_discard_num = jb->jb_framelist.discarded_num;
+	count += jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
+    }
+
+    return count;
+}
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 74c27b3..f8468a2 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -22,6 +22,7 @@
 #include <pjmedia/rtp.h>
 #include <pjmedia/rtcp.h>
 #include <pjmedia/jbuf.h>
+#include <pjmedia/stream_common.h>
 #include <pj/array.h>
 #include <pj/assert.h>
 #include <pj/ctype.h>
@@ -2738,3 +2739,489 @@
     return PJ_SUCCESS;
 }
 
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_VIDEO = { "video", 5};
+static const pj_str_t ID_APPLICATION = { "application", 11};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_IP6 = { "IP6", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
+//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
+static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 };
+
+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 };
+
+
+/*
+ * Internal function for collecting codec info and param from the SDP media.
+ */
+static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si,
+					      pj_pool_t *pool,
+					      pjmedia_codec_mgr *mgr,
+					      const pjmedia_sdp_media *local_m,
+					      const pjmedia_sdp_media *rem_m)
+{
+    const pjmedia_sdp_attr *attr;
+    pjmedia_sdp_rtpmap *rtpmap;
+    unsigned i, fmti, pt = 0;
+    pj_status_t status;
+
+    /* Find the first codec which is not telephone-event */
+    for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) {
+	if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) )
+	    return PJMEDIA_EINVALIDPT;
+	pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+	if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 ||
+		pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS )
+		break;
+    }
+    if ( fmti >= local_m->desc.fmt_count )
+	return PJMEDIA_EINVALIDPT;
+
+    /* Get codec info.
+     * For static payload types, get the info from codec manager.
+     * For dynamic payload types, MUST get the rtpmap.
+     */
+    if (pt < 96) {
+	pj_bool_t has_rtpmap;
+
+	rtpmap = NULL;
+	has_rtpmap = PJ_TRUE;
+
+	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
+					   &local_m->desc.fmt[fmti]);
+	if (attr == NULL) {
+	    has_rtpmap = PJ_FALSE;
+	}
+	if (attr != NULL) {
+	    status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+	    if (status != PJ_SUCCESS)
+		has_rtpmap = PJ_FALSE;
+	}
+
+	/* Build codec format info: */
+	if (has_rtpmap) {
+	    si->fmt.type = si->type;
+	    si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+	    pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
+	    si->fmt.clock_rate = rtpmap->clock_rate;
+	    
+#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0)
+	    /* The session info should have the actual clock rate, because 
+	     * this info is used for calculationg buffer size, etc in stream 
+	     */
+	    if (si->fmt.pt == PJMEDIA_RTP_PT_G722)
+		si->fmt.clock_rate = 16000;
+#endif
+
+	    /* For audio codecs, rtpmap parameters denotes the number of
+	     * channels.
+	     */
+	    if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+		si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
+	    } else {
+		si->fmt.channel_cnt = 1;
+	    }
+
+	} else {	    
+	    const pjmedia_codec_info *p_info;
+
+	    status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));
+	}
+
+	/* For static payload type, pt's are symetric */
+	si->tx_pt = pt;
+
+    } else {
+
+	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
+					   &local_m->desc.fmt[fmti]);
+	if (attr == NULL)
+	    return PJMEDIA_EMISSINGRTPMAP;
+
+	status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Build codec format info: */
+
+	si->fmt.type = si->type;
+	si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+	pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
+	si->fmt.clock_rate = rtpmap->clock_rate;
+
+	/* For audio codecs, rtpmap parameters denotes the number of
+	 * channels.
+	 */
+	if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+	    si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
+	} else {
+	    si->fmt.channel_cnt = 1;
+	}
+
+	/* Determine payload type for outgoing channel, by finding
+	 * dynamic payload type in remote SDP that matches the answer.
+	 */
+	si->tx_pt = 0xFFFF;
+	for (i=0; i<rem_m->desc.fmt_count; ++i) {
+	    unsigned rpt;
+	    pjmedia_sdp_attr *r_attr;
+	    pjmedia_sdp_rtpmap r_rtpmap;
+
+	    rpt = pj_strtoul(&rem_m->desc.fmt[i]);
+	    if (rpt < 96)
+		continue;
+
+	    r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
+						 &rem_m->desc.fmt[i]);
+	    if (!r_attr)
+		continue;
+
+	    if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
+		continue;
+
+	    if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
+		rtpmap->clock_rate == r_rtpmap.clock_rate)
+	    {
+		/* Found matched codec. */
+		si->tx_pt = rpt;
+
+		break;
+	    }
+	}
+
+	if (si->tx_pt == 0xFFFF)
+	    return PJMEDIA_EMISSINGRTPMAP;
+    }
+
+  
+    /* Now that we have codec info, get the codec param. */
+    si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param);
+    status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt,
+					         si->param);
+
+    /* Get remote fmtp for our encoder. */
+    pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
+				   &si->param->setting.enc_fmtp);
+
+    /* Get local fmtp for our decoder. */
+    pjmedia_stream_info_parse_fmtp(pool, local_m, si->fmt.pt, 
+				   &si->param->setting.dec_fmtp);
+
+    /* Get the remote ptime for our encoder. */
+    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+				  "ptime", NULL);
+    if (attr) {
+	pj_str_t tmp_val = attr->value;
+	unsigned frm_per_pkt;
+ 
+	pj_strltrim(&tmp_val);
+
+	/* Round up ptime when the specified is not multiple of frm_ptime */
+	frm_per_pkt = (pj_strtoul(&tmp_val) + 
+		      si->param->info.frm_ptime/2) /
+		      si->param->info.frm_ptime;
+	if (frm_per_pkt != 0) {
+            si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt;
+        }
+    }
+
+    /* Get remote maxptime for our encoder. */
+    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+				  "maxptime", NULL);
+    if (attr) {
+	pj_str_t tmp_val = attr->value;
+
+	pj_strltrim(&tmp_val);
+	si->tx_maxptime = pj_strtoul(&tmp_val);
+    }
+
+    /* When direction is NONE (it means SDP negotiation has failed) we don't
+     * need to return a failure here, as returning failure will cause
+     * the whole SDP to be rejected. See ticket #:
+     *	http://
+     *
+     * Thanks Alain Totouom 
+     */
+    if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
+	return status;
+
+
+    /* Get incomming payload type for telephone-events */
+    si->rx_event_pt = -1;
+    for (i=0; i<local_m->attr_count; ++i) {
+	pjmedia_sdp_rtpmap r;
+
+	attr = local_m->attr[i];
+	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+	    continue;
+	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+	    continue;
+	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+	    si->rx_event_pt = pj_strtoul(&r.pt);
+	    break;
+	}
+    }
+
+    /* Get outgoing payload type for telephone-events */
+    si->tx_event_pt = -1;
+    for (i=0; i<rem_m->attr_count; ++i) {
+	pjmedia_sdp_rtpmap r;
+
+	attr = rem_m->attr[i];
+	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+	    continue;
+	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+	    continue;
+	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+	    si->tx_event_pt = pj_strtoul(&r.pt);
+	    break;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Create stream info from SDP media line.
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
+					   pjmedia_stream_info *si,
+					   pj_pool_t *pool,
+					   pjmedia_endpt *endpt,
+					   const pjmedia_sdp_session *local,
+					   const pjmedia_sdp_session *remote,
+					   unsigned stream_idx)
+{
+    pjmedia_codec_mgr *mgr;
+    const pjmedia_sdp_attr *attr;
+    const pjmedia_sdp_media *local_m;
+    const pjmedia_sdp_media *rem_m;
+    const pjmedia_sdp_conn *local_conn;
+    const pjmedia_sdp_conn *rem_conn;
+    int rem_af, local_af;
+    pj_sockaddr local_addr;
+    pj_status_t status;
+
+    
+    /* Validate arguments: */
+    PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
+    PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
+    PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
+
+    /* Keep SDP shortcuts */
+    local_m = local->media[stream_idx];
+    rem_m = remote->media[stream_idx];
+
+    local_conn = local_m->conn ? local_m->conn : local->conn;
+    if (local_conn == NULL)
+	return PJMEDIA_SDP_EMISSINGCONN;
+
+    rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
+    if (rem_conn == NULL)
+	return PJMEDIA_SDP_EMISSINGCONN;
+
+    /* Media type must be audio */
+    if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0)
+	return PJMEDIA_EINVALIMEDIATYPE;
+
+    /* Get codec manager. */
+    mgr = pjmedia_endpt_get_codec_mgr(endpt);
+
+    /* Reset: */
+
+    pj_bzero(si, sizeof(*si));
+
+#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
+    /* Set default RTCP XR enabled/disabled */
+    si->rtcp_xr_enabled = PJ_TRUE;
+#endif
+
+    /* Media type: */
+    si->type = PJMEDIA_TYPE_AUDIO;
+
+    /* Transport protocol */
+
+    /* At this point, transport type must be compatible, 
+     * the transport instance will do more validation later.
+     */
+    status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, 
+				       &local_m->desc.transport);
+    if (status != PJ_SUCCESS)
+	return PJMEDIA_SDPNEG_EINVANSTP;
+
+    if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {
+
+	si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
+
+    } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
+
+	si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
+
+    } else {
+
+	si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
+	return PJ_SUCCESS;
+    }
+
+
+    /* Check address family in remote SDP */
+    rem_af = pj_AF_UNSPEC();
+    if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
+	if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
+	    rem_af = pj_AF_INET();
+	} else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
+	    rem_af = pj_AF_INET6();
+	}
+    }
+
+    if (rem_af==pj_AF_UNSPEC()) {
+	/* Unsupported address family */
+	return PJ_EAFNOTSUP;
+    }
+
+    /* Set remote address: */
+    status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, 
+			      rem_m->desc.port);
+    if (status != PJ_SUCCESS) {
+	/* Invalid IP address. */
+	return PJMEDIA_EINVALIDIP;
+    }
+
+    /* Check address family of local info */
+    local_af = pj_AF_UNSPEC();
+    if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
+	if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
+	    local_af = pj_AF_INET();
+	} else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
+	    local_af = pj_AF_INET6();
+	}
+    }
+
+    if (local_af==pj_AF_UNSPEC()) {
+	/* Unsupported address family */
+	return PJ_SUCCESS;
+    }
+
+    /* Set remote address: */
+    status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, 
+			      local_m->desc.port);
+    if (status != PJ_SUCCESS) {
+	/* Invalid IP address. */
+	return PJMEDIA_EINVALIDIP;
+    }
+
+    /* Local and remote address family must match */
+    if (local_af != rem_af)
+	return PJ_EAFNOTSUP;
+
+    /* Media direction: */
+
+    if (local_m->desc.port == 0 || 
+	pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
+	pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
+	pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
+    {
+	/* Inactive stream. */
+
+	si->dir = PJMEDIA_DIR_NONE;
+
+    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {
+
+	/* Send only stream. */
+
+	si->dir = PJMEDIA_DIR_ENCODING;
+
+    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {
+
+	/* Recv only stream. */
+
+	si->dir = PJMEDIA_DIR_DECODING;
+
+    } else {
+
+	/* Send and receive stream. */
+
+	si->dir = PJMEDIA_DIR_ENCODING_DECODING;
+
+    }
+
+    /* No need to do anything else if stream is rejected */
+    if (local_m->desc.port == 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* If "rtcp" attribute is present in the SDP, set the RTCP address
+     * from that attribute. Otherwise, calculate from RTP address.
+     */
+    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+				  "rtcp", NULL);
+    if (attr) {
+	pjmedia_sdp_rtcp_attr rtcp;
+	status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
+	if (status == PJ_SUCCESS) {
+	    if (rtcp.addr.slen) {
+		status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
+					  (pj_uint16_t)rtcp.port);
+	    } else {
+		pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, 
+				 (pj_uint16_t)rtcp.port);
+		pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
+		          pj_sockaddr_get_addr(&si->rem_addr),
+			  pj_sockaddr_get_addr_len(&si->rem_addr));
+	    }
+	}
+    }
+    
+    if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
+	int rtcp_port;
+
+	pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
+	rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
+	pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
+    }
+
+
+    /* Get the payload number for receive channel. */
+    /*
+       Previously we used to rely on fmt[0] being the selected codec,
+       but some UA sends telephone-event as fmt[0] and this would
+       cause assert failure below.
+
+       Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch.
+
+    // And codec must be numeric!
+    if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || 
+	!pj_isdigit(*rem_m->desc.fmt[0].ptr))
+    {
+	return PJMEDIA_EINVALIDPT;
+    }
+
+    pt = pj_strtoul(&local_m->desc.fmt[0]);
+    pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 ||
+	      pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS);
+    */
+
+    /* Get codec info and param */
+    status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m);
+
+    /* Leave SSRC to random. */
+    si->ssrc = pj_rand();
+
+    /* Set default jitter buffer parameter. */
+    si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
+
+    return status;
+}
diff --git a/pjmedia/src/pjmedia/vid_codec.c b/pjmedia/src/pjmedia/vid_codec.c
index 9eaca92..567418b 100644
--- a/pjmedia/src/pjmedia/vid_codec.c
+++ b/pjmedia/src/pjmedia/vid_codec.c
@@ -28,13 +28,41 @@
 
 static pjmedia_vid_codec_mgr *def_vid_codec_mgr;
 
-/* Definition of default codecs parameters */
-struct pjmedia_vid_codec_default_param
+
+/*
+ * Codec manager maintains array of these structs for each supported
+ * codec.
+ */
+typedef struct pjmedia_vid_codec_desc
 {
-    pjmedia_vid_codec_param	*param;
+    pjmedia_vid_codec_info	     info;	/**< Codec info.	    */
+    pjmedia_codec_id	             id;        /**< Fully qualified name   */
+    pjmedia_codec_priority           prio;      /**< Priority.		    */
+    pjmedia_vid_codec_factory       *factory;	/**< The factory.	    */
+    pjmedia_vid_codec_param         *def_param; /**< Default codecs 
+					             parameters.	    */
+} pjmedia_vid_codec_desc;
+
+
+/* The declaration of video codec manager */
+struct pjmedia_vid_codec_mgr
+{
+    /** Codec manager mutex. */
+    pj_mutex_t			*mutex;
+
+    /** List of codec factories registered to codec manager. */
+    pjmedia_vid_codec_factory	 factory_list;
+
+    /** Number of supported codecs. */
+    unsigned			 codec_cnt;
+
+    /** Array of codec descriptor. */
+    pjmedia_vid_codec_desc	 codec_desc[PJMEDIA_CODEC_MGR_MAX_CODECS];
+
 };
 
 
+
 /* Sort codecs in codec manager based on priorities */
 static void sort_codecs(pjmedia_vid_codec_mgr *mgr);
 
@@ -93,7 +121,7 @@
 
 PJ_DEF(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void)
 {
-    pj_assert(def_vid_codec_mgr);
+    //pj_assert(def_vid_codec_mgr);
     return def_vid_codec_mgr;
 }
 
@@ -615,11 +643,11 @@
 	pj_strdup(pool, &p->dec_fmtp.param[i].val, 
 		  &param->dec_fmtp.param[i].val);
     }
-    for (i = 0; i < param->dec_fmtp.cnt; ++i) {
-	pj_strdup(pool, &p->dec_fmtp.param[i].name, 
-		  &param->dec_fmtp.param[i].name);
-	pj_strdup(pool, &p->dec_fmtp.param[i].val, 
-		  &param->dec_fmtp.param[i].val);
+    for (i = 0; i < param->enc_fmtp.cnt; ++i) {
+	pj_strdup(pool, &p->enc_fmtp.param[i].name, 
+		  &param->enc_fmtp.param[i].name);
+	pj_strdup(pool, &p->enc_fmtp.param[i].val, 
+		  &param->enc_fmtp.param[i].val);
     }
 
     pj_mutex_unlock(mgr->mutex);
diff --git a/pjmedia/src/pjmedia/vid_stream.c b/pjmedia/src/pjmedia/vid_stream.c
new file mode 100644
index 0000000..adbc980
--- /dev/null
+++ b/pjmedia/src/pjmedia/vid_stream.c
@@ -0,0 +1,1888 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 <pjmedia/vid_stream.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/stream_common.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+#include <pj/compat/socket.h>
+#include <pj/errno.h>
+#include <pj/ioqueue.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/sock_select.h>
+#include <pj/string.h>	    /* memcpy() */
+
+
+#define THIS_FILE			"vid_stream.c"
+#define ERRLEVEL			1
+#define LOGERR_(expr)			stream_perror expr
+#define TRC_(expr)			PJ_LOG(5,expr)
+
+/* Tracing jitter buffer operations in a stream session to a CSV file.
+ * The trace will contain JB operation timestamp, frame info, RTP info, and
+ * the JB state right after the operation.
+ */
+#define TRACE_JB			0	/* Enable/disable trace.    */
+#define TRACE_JB_PATH_PREFIX		""	/* Optional path/prefix
+						   for the CSV filename.    */
+#if TRACE_JB
+#   include <pj/file_io.h>
+#   define TRACE_JB_INVALID_FD		((pj_oshandle_t)-1)
+#   define TRACE_JB_OPENED(s)		(s->trace_jb_fd != TRACE_JB_INVALID_FD)
+#endif
+
+
+
+/**
+ * Media channel.
+ */
+typedef struct pjmedia_vid_channel
+{
+    pjmedia_vid_stream	   *stream;	    /**< Parent stream.		    */
+    pjmedia_dir		    dir;	    /**< Channel direction.	    */
+    pjmedia_port	    port;	    /**< Port interface.	    */
+    unsigned		    pt;		    /**< Payload type.		    */
+    pj_bool_t		    paused;	    /**< Paused?.		    */
+    void		   *buf;	    /**< Output buffer.		    */
+    unsigned		    buf_size;	    /**< Size of output buffer.	    */
+    unsigned                buf_len;    /**< Length of data in buffer.  */
+    pjmedia_rtp_session	    rtp;	    /**< RTP session.		    */
+} pjmedia_vid_channel;
+
+
+/**
+ * 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_vid_stream
+{
+    pjmedia_endpt	    *endpt;	    /**< Media endpoint.	    */
+    pjmedia_vid_codec_mgr   *codec_mgr;	    /**< Codec manager.		    */
+
+    pjmedia_vid_channel	    *enc;	    /**< Encoding channel.	    */
+    pjmedia_vid_channel	    *dec;	    /**< Decoding channel.	    */
+
+    pjmedia_dir		     dir;	    /**< Stream direction.	    */
+    void		    *user_data;	    /**< User data.		    */
+    pj_str_t		     name;	    /**< Stream name		    */
+    pj_str_t		     cname;	    /**< SDES CNAME		    */
+
+    pjmedia_transport	    *transport;	    /**< Stream transport.	    */
+
+    pj_mutex_t		    *jb_mutex;
+    pjmedia_jbuf	    *jb;	    /**< Jitter buffer.		    */
+    char		     jb_last_frm;   /**< Last frame type from jb    */
+    unsigned		     jb_last_frm_cnt;/**< Last JB frame type counter*/
+
+    pjmedia_rtcp_session     rtcp;	    /**< RTCP for incoming RTP.	    */
+    pj_uint32_t		     rtcp_last_tx;  /**< RTCP tx time in timestamp  */
+    pj_uint32_t		     rtcp_interval; /**< Interval, in timestamp.    */
+    pj_bool_t		     initial_rr;    /**< Initial RTCP RR sent	    */
+
+    unsigned		     frame_size;    /**< Size of encoded base frame.*/
+    unsigned		     frame_ts_len;  /**< Frame length in timestamp. */
+
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+    pj_uint32_t		     rtcp_xr_last_tx;  /**< RTCP XR tx time 
+					            in timestamp.           */
+    pj_uint32_t		     rtcp_xr_interval; /**< Interval, in timestamp. */
+    pj_sockaddr		     rtcp_xr_dest;     /**< Additional remote RTCP XR 
+						    dest. If sin_family is 
+						    zero, it will be ignored*/
+    unsigned		     rtcp_xr_dest_len; /**< Length of RTCP XR dest
+					            address		    */
+#endif
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+    pj_bool_t		     use_ka;	       /**< Stream keep-alive with non-
+						    codec-VAD mechanism is
+						    enabled?		    */
+    pj_timestamp	     last_frm_ts_sent; /**< Timestamp of last sending
+					            packet		    */
+#endif
+
+#if TRACE_JB
+    pj_oshandle_t	    trace_jb_fd;	    /**< Jitter tracing file handle.*/
+    char		   *trace_jb_buf;	    /**< Jitter tracing buffer.	    */
+#endif
+
+    pjmedia_vid_codec	    *codec;	            /**< Codec instance being used. */
+    pjmedia_vid_codec_info   codec_info;           /**< Codec param.		    */
+    pjmedia_vid_codec_param  codec_param;          /**< Codec param.		    */
+
+    pjmedia_vid_stream_info     info;
+};
+
+
+/*
+ * Print error.
+ */
+static void stream_perror(const char *sender, const char *title,
+			  pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+    PJ_LOG(4,(sender, "%s: %s [err:%d]", title, errmsg, status));
+}
+
+
+#if TRACE_JB
+
+PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len)
+{
+    pj_time_val now;
+    pj_parsed_time ptime;
+    char *p = *buf;
+
+    if (len < 14)
+	return -1;
+
+    pj_gettimeofday(&now);
+    pj_time_decode(&now, &ptime);
+    p += pj_utoa_pad(ptime.hour, p, 2, '0');
+    *p++ = ':';
+    p += pj_utoa_pad(ptime.min, p, 2, '0');
+    *p++ = ':';
+    p += pj_utoa_pad(ptime.sec, p, 2, '0');
+    *p++ = '.';
+    p += pj_utoa_pad(ptime.msec, p, 3, '0');
+    *p++ = ',';
+
+    *buf = p;
+
+    return 0;
+}
+
+PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream, 
+				    char **buf, pj_ssize_t len)
+{
+    char *p = *buf;
+    char *endp = *buf + len;
+    pjmedia_jb_state state;
+
+    pjmedia_jbuf_get_state(stream->jb, &state);
+
+    len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d",
+			   state.size, state.burst, state.prefetch);
+    if ((len < 0) || (len >= endp-p))
+	return -1;
+
+    p += len;
+    *buf = p;
+    return 0;
+}
+
+static void trace_jb_get(pjmedia_vid_stream *stream, pjmedia_jb_frame_type ft,
+			 pj_size_t fsize)
+{
+    char *p = stream->trace_jb_buf;
+    char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE;
+    pj_ssize_t len = 0;
+    const char* ft_st;
+
+    if (!TRACE_JB_OPENED(stream))
+	return;
+
+    /* Print timestamp. */
+    if (trace_jb_print_timestamp(&p, endp-p))
+	goto on_insuff_buffer;
+
+    /* Print frame type and size */
+    switch(ft) {
+	case PJMEDIA_JB_MISSING_FRAME:
+	    ft_st = "missing";
+	    break;
+	case PJMEDIA_JB_NORMAL_FRAME:
+	    ft_st = "normal";
+	    break;
+	case PJMEDIA_JB_ZERO_PREFETCH_FRAME:
+	    ft_st = "prefetch";
+	    break;
+	case PJMEDIA_JB_ZERO_EMPTY_FRAME:
+	    ft_st = "empty";
+	    break;
+	default:
+	    ft_st = "unknown";
+	    break;
+    }
+
+    /* Print operation, size, frame count, frame type */
+    len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st);
+    if ((len < 0) || (len >= endp-p))
+	goto on_insuff_buffer;
+    p += len;
+
+    /* Print JB state */
+    if (trace_jb_print_state(stream, &p, endp-p))
+	goto on_insuff_buffer;
+
+    /* Print end of line */
+    if (endp-p < 2)
+	goto on_insuff_buffer;
+    *p++ = '\n';
+
+    /* Write and flush */
+    len = p - stream->trace_jb_buf;
+    pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+    pj_file_flush(stream->trace_jb_fd);
+    return;
+
+on_insuff_buffer:
+    pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!");
+}
+
+static void trace_jb_put(pjmedia_vid_stream *stream,
+			 const pjmedia_rtp_hdr *hdr,
+			 unsigned payloadlen, unsigned frame_cnt)
+{
+    char *p = stream->trace_jb_buf;
+    char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE;
+    pj_ssize_t len = 0;
+
+    if (!TRACE_JB_OPENED(stream))
+	return;
+
+    /* Print timestamp. */
+    if (trace_jb_print_timestamp(&p, endp-p))
+	goto on_insuff_buffer;
+
+    /* Print operation, size, frame count, RTP info */
+    len = pj_ansi_snprintf(p, endp-p,
+			   "PUT,%d,%d,,%d,%d,%d,",
+			   payloadlen, frame_cnt,
+			   pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m);
+    if ((len < 0) || (len >= endp-p))
+	goto on_insuff_buffer;
+    p += len;
+
+    /* Print JB state */
+    if (trace_jb_print_state(stream, &p, endp-p))
+	goto on_insuff_buffer;
+
+    /* Print end of line */
+    if (endp-p < 2)
+	goto on_insuff_buffer;
+    *p++ = '\n';
+
+    /* Write and flush */
+    len = p - stream->trace_jb_buf;
+    pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+    pj_file_flush(stream->trace_jb_fd);
+    return;
+
+on_insuff_buffer:
+    pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!");
+}
+
+#endif /* TRACE_JB */
+
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
+/*
+ * Send keep-alive packet using non-codec frame.
+ */
+static void send_keep_alive_packet(pjmedia_vid_stream *stream)
+{
+#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP
+
+    /* Keep-alive packet is empty RTP */
+    pj_status_t status;
+    void *pkt;
+    int pkt_len;
+
+    TRC_((channel->port.info.name.ptr,
+	  "Sending keep-alive (RTCP and empty RTP)"));
+
+    /* Send RTP */
+    status = pjmedia_rtp_encode_rtp( &stream->enc->rtp,
+				     stream->enc->pt, 0,
+				     1,
+				     0,
+				     (const void**)&pkt,
+				     &pkt_len);
+    pj_assert(status == PJ_SUCCESS);
+
+    pj_memcpy(stream->enc->buf, pkt, pkt_len);
+    pjmedia_transport_send_rtp(stream->transport, stream->enc->buf,
+			       pkt_len);
+
+    /* Send RTCP */
+    pjmedia_rtcp_build_rtcp(&stream->rtcp, &pkt, &pkt_len);
+    pjmedia_transport_send_rtcp(stream->transport, pkt, pkt_len);
+
+#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER
+
+    /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */
+    int pkt_len;
+    const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT;
+
+    TRC_((channel->port.info.name.ptr,
+	  "Sending keep-alive (custom RTP/RTCP packets)"));
+
+    /* Send to RTP port */
+    pj_memcpy(stream->enc->buf, str_ka.ptr, str_ka.slen);
+    pkt_len = str_ka.slen;
+    pjmedia_transport_send_rtp(stream->transport, stream->enc->buf,
+			       pkt_len);
+
+    /* Send to RTCP port */
+    pjmedia_transport_send_rtcp(stream->transport, stream->enc->buf,
+			        pkt_len);
+
+#else
+    
+    PJ_UNUSED_ARG(stream);
+
+#endif
+}
+#endif	/* defined(PJMEDIA_STREAM_ENABLE_KA) */
+
+
+/**
+ * check_tx_rtcp()
+ *
+ * This function is can be called by either put_frame() or get_frame(),
+ * to transmit periodic RTCP SR/RR report.
+ */
+static void check_tx_rtcp(pjmedia_vid_stream *stream, pj_uint32_t timestamp)
+{
+    /* Note that timestamp may represent local or remote timestamp, 
+     * depending on whether this function is called from put_frame()
+     * or get_frame().
+     */
+
+
+    if (stream->rtcp_last_tx == 0) {
+	
+	stream->rtcp_last_tx = timestamp;
+
+    } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
+	
+	void *rtcp_pkt;
+	int len;
+
+	pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
+
+	pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+
+	stream->rtcp_last_tx = timestamp;
+    }
+
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+    if (stream->rtcp.xr_enabled) {
+
+	if (stream->rtcp_xr_last_tx == 0) {
+    	
+	    stream->rtcp_xr_last_tx = timestamp;
+
+	} else if (timestamp - stream->rtcp_xr_last_tx >= 
+		   stream->rtcp_xr_interval)
+	{
+	    int i;
+	    pjmedia_jb_state jb_state;
+	    void *rtcp_pkt;
+	    int len;
+
+	    /* Update RTCP XR with current JB states */
+	    pjmedia_jbuf_get_state(stream->jb, &jb_state);
+    	    
+	    i = jb_state.avg_delay;
+	    pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
+					PJMEDIA_RTCP_XR_INFO_JB_NOM,
+					i);
+
+	    i = jb_state.max_delay;
+	    pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
+					PJMEDIA_RTCP_XR_INFO_JB_MAX,
+					i);
+
+	    /* Build RTCP XR packet */
+	    pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0, 
+				       &rtcp_pkt, &len);
+
+	    /* Send the RTCP XR to remote address */
+	    pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+
+	    /* Send the RTCP XR to third-party destination if specified */
+	    if (stream->rtcp_xr_dest_len) {
+		pjmedia_transport_send_rtcp2(stream->transport, 
+					     &stream->rtcp_xr_dest,
+					     stream->rtcp_xr_dest_len, 
+					     rtcp_pkt, len);
+	    }
+
+	    /* Update last tx RTCP XR */
+	    stream->rtcp_xr_last_tx = timestamp;
+	}
+    }
+#endif
+}
+
+/* Build RTCP SDES packet */
+static unsigned create_rtcp_sdes(pjmedia_vid_stream *stream, pj_uint8_t *pkt,
+				 unsigned max_len)
+{
+    pjmedia_rtcp_common hdr;
+    pj_uint8_t *p = pkt;
+
+    /* SDES header */
+    hdr.version = 2;
+    hdr.p = 0;
+    hdr.count = 1;
+    hdr.pt = 202;
+    hdr.length = 2 + (4+stream->cname.slen+3)/4 - 1;
+    if (max_len < (hdr.length << 2)) {
+	pj_assert(!"Not enough buffer for SDES packet");
+	return 0;
+    }
+    hdr.length = pj_htons((pj_uint16_t)hdr.length);
+    hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
+    pj_memcpy(p, &hdr, sizeof(hdr));
+    p += sizeof(hdr);
+
+    /* CNAME item */
+    *p++ = 1;
+    *p++ = (pj_uint8_t)stream->cname.slen;
+    pj_memcpy(p, stream->cname.ptr, stream->cname.slen);
+    p += stream->cname.slen;
+
+    /* END */
+    *p++ = '\0';
+    *p++ = '\0';
+
+    /* Pad to 32bit */
+    while ((p-pkt) % 4)
+	*p++ = '\0';
+
+    return (p - pkt);
+}
+
+/* Build RTCP BYE packet */
+static unsigned create_rtcp_bye(pjmedia_vid_stream *stream, pj_uint8_t *pkt,
+				unsigned max_len)
+{
+    pjmedia_rtcp_common hdr;
+
+    /* BYE header */
+    hdr.version = 2;
+    hdr.p = 0;
+    hdr.count = 1;
+    hdr.pt = 203;
+    hdr.length = 1;
+    if (max_len < (hdr.length << 2)) {
+	pj_assert(!"Not enough buffer for SDES packet");
+	return 0;
+    }
+    hdr.length = pj_htons((pj_uint16_t)hdr.length);
+    hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
+    pj_memcpy(pkt, &hdr, sizeof(hdr));
+
+    return sizeof(hdr);
+}
+
+
+#if 0
+static void dump_bin(const char *buf, unsigned len)
+{
+    unsigned i;
+
+    PJ_LOG(3,(THIS_FILE, "begin dump"));
+    for (i=0; i<len; ++i) {
+	int j;
+	char bits[9];
+	unsigned val = buf[i] & 0xFF;
+
+	bits[8] = '\0';
+	for (j=0; j<8; ++j) {
+	    if (val & (1 << (7-j)))
+		bits[j] = '1';
+	    else
+		bits[j] = '0';
+	}
+
+	PJ_LOG(3,(THIS_FILE, "%2d %s [%d]", i, bits, val));
+    }
+    PJ_LOG(3,(THIS_FILE, "end dump"));
+}
+#endif
+
+
+/*
+ * This callback is called by stream transport on receipt of packets
+ * in the RTP socket. 
+ */
+static void on_rx_rtp( void *data, 
+		       void *pkt,
+                       pj_ssize_t bytes_read)
+
+{
+    pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
+    pjmedia_vid_channel *channel = stream->dec;
+    const pjmedia_rtp_hdr *hdr;
+    const void *payload;
+    unsigned payloadlen;
+    pjmedia_rtp_status seq_st;
+    pj_status_t status;
+    pj_bool_t pkt_discarded = PJ_FALSE;
+
+    /* Check for errors */
+    if (bytes_read < 0) {
+	LOGERR_((channel->port.info.name.ptr, "RTP recv() error", -bytes_read));
+	return;
+    }
+
+    /* Ignore keep-alive packets */
+    if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr))
+	return;
+
+    /* Update RTP and RTCP session. */
+    status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, bytes_read,
+				    &hdr, &payload, &payloadlen);
+    if (status != PJ_SUCCESS) {
+	LOGERR_((channel->port.info.name.ptr, "RTP decode error", status));
+	stream->rtcp.stat.rx.discard++;
+	return;
+    }
+
+    /* Ignore the packet if decoder is paused */
+    if (channel->paused)
+	goto on_return;
+
+    /* Update RTP session (also checks if RTP session can accept
+     * the incoming packet.
+     */
+    pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, PJ_TRUE);
+    if (seq_st.status.value) {
+	TRC_  ((channel->port.info.name.ptr, 
+		"RTP status: badpt=%d, badssrc=%d, dup=%d, "
+		"outorder=%d, probation=%d, restart=%d", 
+		seq_st.status.flag.badpt,
+		seq_st.status.flag.badssrc,
+		seq_st.status.flag.dup,
+		seq_st.status.flag.outorder,
+		seq_st.status.flag.probation,
+		seq_st.status.flag.restart));
+
+	if (seq_st.status.flag.badpt) {
+	    PJ_LOG(4,(channel->port.info.name.ptr,
+		      "Bad RTP pt %d (expecting %d)",
+		      hdr->pt, channel->rtp.out_pt));
+	}
+
+	if (seq_st.status.flag.badssrc) {
+	    PJ_LOG(4,(channel->port.info.name.ptr,
+		      "Changed RTP peer SSRC %d (previously %d)",
+		      channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc));
+	    stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc;
+	}
+
+
+    }
+
+    /* Skip bad RTP packet */
+    if (seq_st.status.flag.bad) {
+	pkt_discarded = PJ_TRUE;
+	goto on_return;
+    }
+
+    /* Ignore if payloadlen is zero */
+    if (payloadlen == 0) {
+	pkt_discarded = PJ_TRUE;
+	goto on_return;
+    }
+
+
+    /* Put "good" packet to jitter buffer, or reset the jitter buffer
+     * when RTP session is restarted.
+     */
+    pj_mutex_lock( stream->jb_mutex );
+    if (seq_st.status.flag.restart) {
+	status = pjmedia_jbuf_reset(stream->jb);
+	PJ_LOG(4,(channel->port.info.name.ptr, "Jitter buffer reset"));
+
+    } else {
+
+	/* Video stream */
+
+	/* Just put the payload into jitter buffer */
+	pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0, 
+				pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL);
+
+#if TRACE_JB
+	trace_jb_put(stream, hdr, payloadlen, count);
+#endif
+
+    }
+    pj_mutex_unlock( stream->jb_mutex );
+
+
+    /* Check if now is the time to transmit RTCP SR/RR report.
+     * We only do this when stream direction is "decoding only", 
+     * because otherwise check_tx_rtcp() will be handled by put_frame()
+     */
+    if (stream->dir == PJMEDIA_DIR_DECODING) {
+	check_tx_rtcp(stream, pj_ntohl(hdr->ts));
+    }
+
+    if (status != 0) {
+	LOGERR_((channel->port.info.name.ptr, "Jitter buffer put() error", 
+		status));
+	pkt_discarded = PJ_TRUE;
+	goto on_return;
+    }
+
+on_return:
+    /* Update RTCP session */
+    if (stream->rtcp.peer_ssrc == 0)
+	stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc;
+
+    pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq),
+			 pj_ntohl(hdr->ts), payloadlen, pkt_discarded);
+
+    /* Send RTCP RR and SDES after we receive some RTP packets */
+    if (stream->rtcp.received >= 10 && !stream->initial_rr) {
+	void *sr_rr_pkt;
+	pj_uint8_t *pkt;
+	int len;
+
+	/* Build RR or SR */
+	pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
+	pkt = (pj_uint8_t*) stream->enc->buf;
+	pj_memcpy(pkt, sr_rr_pkt, len);
+	pkt += len;
+
+	/* Append SDES */
+	len = create_rtcp_sdes(stream, (pj_uint8_t*)pkt, 
+			       stream->enc->buf_size - len);
+	if (len > 0) {
+	    pkt += len;
+	    len = ((pj_uint8_t*)pkt) - ((pj_uint8_t*)stream->enc->buf);
+	    pjmedia_transport_send_rtcp(stream->transport, 
+					stream->enc->buf, len);
+	}
+
+	stream->initial_rr = PJ_TRUE;
+    }
+}
+
+
+/*
+ * This callback is called by stream transport on receipt of packets
+ * in the RTCP socket. 
+ */
+static void on_rx_rtcp( void *data,
+                        void *pkt, 
+                        pj_ssize_t bytes_read)
+{
+    pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
+
+    /* Check for errors */
+    if (bytes_read < 0) {
+	LOGERR_((stream->cname.ptr, "RTCP recv() error", 
+		 -bytes_read));
+	return;
+    }
+
+    pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read);
+}
+
+static pj_status_t put_frame(pjmedia_port *port,
+                             pjmedia_frame *frame)
+{
+    pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
+    pjmedia_vid_channel *channel = stream->enc;
+    pj_status_t status = 0;
+    pjmedia_frame frame_out;
+    unsigned rtp_ts_len;
+    void *rtphdr;
+    int rtphdrlen;
+    unsigned processed = 0;
+
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
+    /* If the interval since last sending packet is greater than
+     * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet.
+     */
+    if (stream->use_ka)
+    {
+	pj_uint32_t dtx_duration;
+
+	dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent, 
+					   &frame->timestamp);
+	if (dtx_duration >
+	    PJMEDIA_STREAM_KA_INTERVAL * channel->port.info.clock_rate)
+	{
+	    send_keep_alive_packet(stream);
+	    stream->last_frm_ts_sent = frame->timestamp;
+	}
+    }
+#endif
+
+    /* Don't do anything if stream is paused */
+    if (channel->paused) {
+	return PJ_SUCCESS;
+    }
+
+    /* Get frame length in timestamp unit */
+    rtp_ts_len = stream->frame_ts_len;
+
+    /* Init frame_out buffer. */
+    frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr);
+    frame_out.size = 0;
+
+    /* Encode! */
+    status = (*stream->codec->op->encode)(stream->codec, frame, 
+				          channel->buf_size - 
+				          sizeof(pjmedia_rtp_hdr),
+				          &frame_out);
+    if (status != PJ_SUCCESS) {
+        LOGERR_((channel->port.info.name.ptr, 
+	        "Codec encode() error", status));
+        return status;
+    }
+
+
+    while (processed < frame_out.size) {
+        pj_uint8_t *payload, *rtp_pkt;
+        pj_size_t payload_len;
+
+        /* Generate RTP payload */
+        status = (*stream->codec->op->packetize)(
+                                           stream->codec,
+                                           (pj_uint8_t*)frame_out.buf,
+                                           frame_out.size,
+                                           &processed,
+                                           &payload, &payload_len);
+        if (status != PJ_SUCCESS) {
+            LOGERR_((channel->port.info.name.ptr, 
+	            "Codec pack() error", status));
+            return status;
+        }
+
+        /* Encapsulate. */
+        status = pjmedia_rtp_encode_rtp( &channel->rtp, 
+                                         channel->pt,
+                                         (processed==frame_out.size?1:0),
+				         payload_len,
+					 rtp_ts_len, 
+				         (const void**)&rtphdr, 
+				         &rtphdrlen);
+
+        if (status != PJ_SUCCESS) {
+	    LOGERR_((channel->port.info.name.ptr, 
+		    "RTP encode_rtp() error", status));
+	    return status;
+        }
+
+        /* Next packets use same timestamp */
+        rtp_ts_len = 0;
+
+        rtp_pkt = payload - sizeof(pjmedia_rtp_hdr);
+
+        /* Copy RTP header to the beginning of packet */
+        pj_memcpy(rtp_pkt, rtphdr, sizeof(pjmedia_rtp_hdr));
+
+        /* Send the RTP packet to the transport. */
+        pjmedia_transport_send_rtp(stream->transport, rtp_pkt, 
+			           payload_len + sizeof(pjmedia_rtp_hdr));
+    }
+
+    /* Check if now is the time to transmit RTCP SR/RR report. 
+     * We only do this when stream direction is not "decoding only", because
+     * when it is, check_tx_rtcp() will be handled by get_frame().
+     */
+    if (stream->dir != PJMEDIA_DIR_DECODING) {
+	check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts));
+    }
+
+    /* Do nothing if we have nothing to transmit */
+    if (frame_out.size == 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* Update stat */
+    pjmedia_rtcp_tx_rtp(&stream->rtcp, frame_out.size);
+    stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+    stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq);
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+    /* Update timestamp of last sending packet. */
+    stream->last_frm_ts_sent = frame->timestamp;
+#endif
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t get_frame(pjmedia_port *port,
+                             pjmedia_frame *frame)
+{
+    pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
+    pjmedia_vid_channel *channel = stream->dec;
+    pjmedia_frame frame_in;
+    pj_status_t status;
+
+    /* Return no frame is channel is paused */
+    if (channel->paused) {
+	frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	return PJ_SUCCESS;
+    }
+
+    /* Repeat get payload from the jitter buffer until all payloads with same
+     * timestamp are collected (a complete frame unpacketized).
+     */
+    {
+	pj_uint8_t *p, *data;
+	char ptype;
+	pj_size_t psize, data_len;
+	pj_uint32_t ts, last_ts;
+	pj_bool_t got_frame;
+	unsigned i;
+
+	channel->buf_len = 0;
+	last_ts = 0;
+	got_frame = PJ_FALSE;
+
+	/* Lock jitter buffer mutex first */
+	pj_mutex_lock( stream->jb_mutex );
+
+	for (i=0; ; ++i) {
+	    /* Get frame from jitter buffer. */
+	    pjmedia_jbuf_peek_frame(stream->jb, i, &p, &psize, &ptype,
+				    NULL, &ts);
+	    if (ptype == PJMEDIA_JB_NORMAL_FRAME) {
+		if (last_ts == 0)
+		    last_ts = ts;
+
+		if (ts != last_ts) {
+		    got_frame = PJ_TRUE;
+		    pjmedia_jbuf_remove_frame(stream->jb, i);
+		    break;
+		}
+		
+		data = (pj_uint8_t*)channel->buf + channel->buf_len;
+		data_len = channel->buf_size - channel->buf_len;
+		status = (*stream->codec->op->unpacketize)(stream->codec,
+							   p, psize,
+							   data, &data_len);
+		channel->buf_len += data_len;
+	    } else if (ptype == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
+		/* No more packet in the jitter buffer */
+		break;
+	    }
+	}
+
+	/* Unlock jitter buffer mutex. */
+	pj_mutex_unlock( stream->jb_mutex );
+
+	if (!got_frame) {
+	    frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	    frame->size = 0;
+	    return PJ_SUCCESS;
+	}
+    }
+
+    /* Decode */
+    frame_in.buf = channel->buf;
+    frame_in.size = channel->buf_len;
+    frame_in.bit_info = 0;
+    frame_in.type = PJMEDIA_FRAME_TYPE_VIDEO;
+
+    status = stream->codec->op->decode(stream->codec, &frame_in,
+				       frame->size, frame);
+    if (status != PJ_SUCCESS) {
+	LOGERR_((port->info.name.ptr, "codec decode() error", 
+		 status));
+	frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	frame->size = 0;
+    }
+
+    /* Check if the decoder format is changed */
+    if (frame->bit_info & PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED) {
+	/* Update param from codec */
+	stream->codec->op->get_param(stream->codec, &stream->codec_param);
+
+	/* Update decoding channel port info */
+	stream->dec->port.info.fmt = stream->codec_param.dec_fmt;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Create media channel.
+ */
+static pj_status_t create_channel( pj_pool_t *pool,
+				   pjmedia_vid_stream *stream,
+				   pjmedia_dir dir,
+				   unsigned pt,
+				   const pjmedia_vid_stream_info *info,
+				   pjmedia_vid_channel **p_channel)
+{
+    enum { M = 32 };
+    pjmedia_vid_channel *channel;
+    pj_status_t status;
+    unsigned min_out_pkt_size;
+    pj_str_t name;
+    const char *type_name;
+    pjmedia_format *fmt;
+    
+    pj_assert(info->type == PJMEDIA_TYPE_VIDEO);
+    pj_assert(dir == PJMEDIA_DIR_DECODING || dir == PJMEDIA_DIR_ENCODING);
+
+    /* Allocate memory for channel descriptor */
+    channel = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_channel);
+    PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM);
+
+    /* Init vars */
+    if (dir==PJMEDIA_DIR_DECODING) {
+	type_name = "vstrmdec";
+	fmt = &info->codec_param->dec_fmt;
+    } else {
+	type_name = "vstrmenc";
+	fmt = &info->codec_param->enc_fmt;
+    }
+
+    name.ptr = (char*) pj_pool_alloc(pool, M);
+    name.slen = pj_ansi_snprintf(name.ptr, M, "%s%p", type_name, stream);
+
+    /* Init channel info. */
+    channel->stream = stream;
+    channel->dir = dir;
+    channel->paused = 1;
+    channel->pt = pt;
+    
+    /* Allocate buffer for outgoing packet. */
+    channel->buf_size = sizeof(pjmedia_rtp_hdr) + stream->frame_size;
+
+    /* It should big enough to hold (minimally) RTCP SR with an SDES. */
+    min_out_pkt_size =  sizeof(pjmedia_rtcp_sr_pkt) +
+			sizeof(pjmedia_rtcp_common) +
+			(4 + stream->cname.slen) +
+			32;
+
+    if (channel->buf_size < min_out_pkt_size)
+	channel->buf_size = min_out_pkt_size;
+
+    channel->buf = pj_pool_alloc(pool, channel->buf_size);
+    PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM);
+
+    /* Create RTP and RTCP sessions: */
+    if (info->rtp_seq_ts_set == 0) {
+	status = pjmedia_rtp_session_init(&channel->rtp, pt, info->ssrc);
+    } else {
+	pjmedia_rtp_session_setting settings;
+
+	settings.flags = (pj_uint8_t)((info->rtp_seq_ts_set << 2) | 3);
+	settings.default_pt = pt;
+	settings.sender_ssrc = info->ssrc;
+	settings.seq = info->rtp_seq;
+	settings.ts = info->rtp_ts;
+	status = pjmedia_rtp_session_init2(&channel->rtp, settings);
+    }
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Init port. */
+    pjmedia_port_info_init2(&channel->port.info, &name,
+			    PJMEDIA_PORT_SIGNATURE('V', 'C', 'H', 'N'),
+			    dir, fmt);
+    if (dir == PJMEDIA_DIR_DECODING) {
+	channel->port.get_frame = &get_frame;
+    } else {
+	channel->port.put_frame = &put_frame;
+    }
+
+    /* Init port. */
+    channel->port.port_data.pdata = stream;
+
+    /* Done. */
+    *p_channel = channel;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Create stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
+					pjmedia_endpt *endpt,
+					pj_pool_t *pool,
+					const pjmedia_vid_stream_info *info,
+					pjmedia_transport *tp,
+					void *user_data,
+					pjmedia_vid_stream **p_stream)
+{
+    enum { M = 32 };
+    pjmedia_vid_stream *stream;
+    unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+    int frm_ptime, chunks_per_frm;
+    pjmedia_video_format_detail *vfd_enc;
+    char *p;
+    pj_status_t status;
+
+    /* Allocate stream */
+    stream = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_stream);
+    PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
+
+    /* Get codec manager */
+    stream->codec_mgr = pjmedia_vid_codec_mgr_instance();
+    PJ_ASSERT_RETURN(stream->codec_mgr, PJMEDIA_CODEC_EFAILED);
+
+    /* Init stream/port name */
+    stream->name.ptr = (char*) pj_pool_alloc(pool, M);
+    stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M, 
+					 "vstrm%p", stream);
+
+    /* Create and initialize codec: */
+    stream->codec_info = info->codec_info;
+    status = pjmedia_vid_codec_mgr_alloc_codec(stream->codec_mgr, 
+					       &info->codec_info,
+					       &stream->codec);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Get codec param: */
+    if (info->codec_param)
+	stream->codec_param = *info->codec_param;
+    else {
+	status = pjmedia_vid_codec_mgr_get_default_param(stream->codec_mgr, 
+						         &info->codec_info,
+						         &stream->codec_param);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    vfd_enc = pjmedia_format_get_video_format_detail(
+                                            &stream->codec_param.enc_fmt, 1);
+
+    /* Init stream: */
+    stream->endpt = endpt;
+    stream->dir = info->dir;
+    stream->user_data = user_data;
+    stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) *
+			    info->codec_info.clock_rate / 1000;
+
+    stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME;
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+    stream->use_ka = info->use_ka;
+#endif
+
+    /* Build random RTCP CNAME. CNAME has user@host format */
+    stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20);
+    pj_create_random_string(p, 5);
+    p += 5;
+    *p++ = '@'; *p++ = 'p'; *p++ = 'j';
+    pj_create_random_string(p, 6);
+    p += 6;
+    *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g';
+    stream->cname.slen = p - stream->cname.ptr;
+
+
+    /* Create mutex to protect jitter buffer: */
+
+    status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Init codec param */
+    stream->codec_param.dir = info->dir;
+    stream->codec_param.enc_mtu = PJMEDIA_MAX_MTU - sizeof(pjmedia_rtp_hdr);
+
+    /* Init and open the codec. */
+    status = stream->codec->op->init(stream->codec, pool);
+    if (status != PJ_SUCCESS)
+	return status;
+    status = stream->codec->op->open(stream->codec, &stream->codec_param);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Get the frame size */
+    stream->frame_size = vfd_enc->max_bps * vfd_enc->fps.denum /
+			 vfd_enc->fps.num;
+    
+    /* As the maximum frame_size is not represented directly by maximum bps
+     * (which includes intra and predicted frames), let's increase the
+     * frame size value for safety.
+     */
+    stream->frame_size <<= 2;
+
+    /* Validate the frame size */
+    if (stream->frame_size == 0 || 
+	stream->frame_size > PJMEDIA_MAX_VIDEO_FRAME_SIZE)
+    {
+	stream->frame_size = PJMEDIA_MAX_VIDEO_FRAME_SIZE;
+    }
+
+    /* Get frame length in timestamp unit */
+    stream->frame_ts_len = info->codec_info.clock_rate *
+                           vfd_enc->fps.denum / vfd_enc->fps.num;
+
+    /* Create decoder channel */
+    status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, 
+			     info->codec_info.pt, info, &stream->dec);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Create encoder channel */
+    status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, 
+			     info->tx_pt, info, &stream->enc);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Init jitter buffer parameters: */
+    frm_ptime	    = 1000 * vfd_enc->fps.denum / vfd_enc->fps.num;
+    chunks_per_frm  = stream->frame_size / PJMEDIA_MAX_MTU;
+
+    /* JB max count, default 500ms */
+    if (info->jb_max >= frm_ptime)
+	jb_max	    = info->jb_max * chunks_per_frm / frm_ptime;
+    else
+	jb_max	    = 500 * chunks_per_frm / frm_ptime;
+
+    /* JB min prefetch, default 1 frame */
+    if (info->jb_min_pre >= frm_ptime)
+	jb_min_pre  = info->jb_min_pre * chunks_per_frm / frm_ptime;
+    else
+	jb_min_pre  = 1;
+
+    /* JB max prefetch, default 4/5 JB max count */
+    if (info->jb_max_pre >= frm_ptime)
+	jb_max_pre  = info->jb_max_pre * chunks_per_frm / frm_ptime;
+    else
+	jb_max_pre  = jb_max * 4 / 5;
+
+    /* JB init prefetch, default 0 */
+    if (info->jb_init >= frm_ptime)
+	jb_init  = info->jb_init * chunks_per_frm / frm_ptime;
+    else
+	jb_init  = 0;
+
+    /* Create jitter buffer */
+    status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name,
+				 PJMEDIA_MAX_MTU, 
+				 1000 * vfd_enc->fps.denum / vfd_enc->fps.num,
+				 jb_max, &stream->jb);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Set up jitter buffer */
+    pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre);
+    //pjmedia_jbuf_enable_discard(stream->jb, PJ_FALSE);
+
+    /* Init RTCP session: */
+    {
+	pjmedia_rtcp_session_setting rtcp_setting;
+
+	pjmedia_rtcp_session_setting_default(&rtcp_setting);
+	rtcp_setting.name = stream->name.ptr;
+	rtcp_setting.ssrc = info->ssrc;
+	rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts);
+	rtcp_setting.clock_rate = info->codec_info.clock_rate;
+	rtcp_setting.samples_per_frame = 1;
+
+	pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting);
+    }
+
+    /* Only attach transport when stream is ready. */
+    status = pjmedia_transport_attach(tp, stream, &info->rem_addr, 
+				      &info->rem_rtcp, 
+				      pj_sockaddr_get_len(&info->rem_addr), 
+                                      &on_rx_rtp, &on_rx_rtcp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    stream->transport = tp;
+
+    /* Send RTCP SDES */
+    len = create_rtcp_sdes(stream, (pj_uint8_t*)stream->enc->buf, 
+			   stream->enc->buf_size);
+    if (len != 0) {
+	pjmedia_transport_send_rtcp(stream->transport, 
+				    stream->enc->buf, len);
+    }
+
+#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
+    /* NAT hole punching by sending KA packet via RTP transport. */
+    if (stream->use_ka)
+	send_keep_alive_packet(stream);
+#endif
+
+#if TRACE_JB
+    {
+	char trace_name[PJ_MAXPATH];
+	pj_ssize_t len;
+
+	pj_ansi_snprintf(trace_name, sizeof(trace_name), 
+			 TRACE_JB_PATH_PREFIX "%s.csv",
+			 channel->port.info.name.ptr);
+	status = pj_file_open(pool, trace_name, PJ_O_RDWR,
+			      &stream->trace_jb_fd);
+	if (status != PJ_SUCCESS) {
+	    stream->trace_jb_fd = TRACE_JB_INVALID_FD;
+	    PJ_LOG(3,(THIS_FILE, "Failed creating RTP trace file '%s'", 
+		      trace_name));
+	} else {
+	    stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE);
+
+	    /* Print column header */
+	    len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE,
+				   "Time, Operation, Size, Frame Count, "
+				   "Frame type, RTP Seq, RTP TS, RTP M, "
+				   "JB size, JB burst level, JB prefetch\n");
+	    pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
+	    pj_file_flush(stream->trace_jb_fd);
+	}
+    }
+#endif
+
+    /* Success! */
+    *p_stream = stream;
+
+    PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->name.ptr));
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream )
+{
+    unsigned len;
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+    /* Send RTCP XR on stream destroy */
+    if (stream->rtcp.xr_enabled) {
+	int i;
+	pjmedia_jb_state jb_state;
+	void *rtcp_pkt;
+	int len;
+
+	/* Update RTCP XR with current JB states */
+	pjmedia_jbuf_get_state(stream->jb, &jb_state);
+    	    
+	i = jb_state.avg_delay;
+	pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
+				    PJMEDIA_RTCP_XR_INFO_JB_NOM,
+				    i);
+
+	i = jb_state.max_delay;
+	pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
+				    PJMEDIA_RTCP_XR_INFO_JB_MAX,
+				    i);
+
+	/* Build RTCP XR packet */
+	pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0, 
+				   &rtcp_pkt, &len);
+
+	/* Send the RTCP XR to remote address */
+	pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+	
+	/* Send the RTCP XR to third-party destination if specified */
+	if (stream->rtcp_xr_dest_len) {
+	    pjmedia_transport_send_rtcp2(stream->transport, 
+					 &stream->rtcp_xr_dest,
+					 stream->rtcp_xr_dest_len, 
+					 rtcp_pkt, len);
+	}
+    }
+#endif
+
+    /* Send RTCP BYE */
+    if (stream->enc && stream->transport) {
+	len = create_rtcp_bye(stream, (pj_uint8_t*)stream->enc->buf,
+			      stream->enc->buf_size);
+	if (len != 0) {
+	    pjmedia_transport_send_rtcp(stream->transport, 
+					stream->enc->buf, len);
+	}
+    }
+
+    /* Detach from transport 
+     * MUST NOT hold stream mutex while detaching from transport, as
+     * it may cause deadlock. See ticket #460 for the details.
+     */
+    if (stream->transport) {
+	pjmedia_transport_detach(stream->transport, stream);
+	stream->transport = NULL;
+    }
+
+    /* This function may be called when stream is partly initialized. */
+    if (stream->jb_mutex)
+	pj_mutex_lock(stream->jb_mutex);
+
+
+    /* Free codec. */
+    if (stream->codec) {
+	stream->codec->op->close(stream->codec);
+	pjmedia_vid_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec);
+	stream->codec = NULL;
+    }
+
+    /* Free mutex */
+    
+    if (stream->jb_mutex) {
+	pj_mutex_destroy(stream->jb_mutex);
+	stream->jb_mutex = NULL;
+    }
+
+    /* Destroy jitter buffer */
+    if (stream->jb)
+	pjmedia_jbuf_destroy(stream->jb);
+
+#if TRACE_JB
+    if (TRACE_JB_OPENED(stream)) {
+	pj_file_close(stream->trace_jb_fd);
+	stream->trace_jb_fd = TRACE_JB_INVALID_FD;
+    }
+#endif
+
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Get the port interface.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream,
+						pjmedia_dir dir,
+						pjmedia_port **p_port )
+{
+    PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING,
+		     PJ_EINVAL);
+
+    if (dir == PJMEDIA_DIR_ENCODING)
+	*p_port = &stream->enc->port;
+    else
+	*p_port = &stream->dec->port;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the transport object
+ */
+PJ_DEF(pjmedia_transport*) pjmedia_vid_stream_get_transport(
+						    pjmedia_vid_stream *st)
+{
+    return st->transport;
+}
+
+
+/*
+ * Start stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream)
+{
+
+    PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP);
+
+    if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) {
+	stream->enc->paused = 0;
+	//pjmedia_snd_stream_start(stream->enc->snd_stream);
+	PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream started"));
+    } else {
+	PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
+    }
+
+    if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) {
+	stream->dec->paused = 0;
+	//pjmedia_snd_stream_start(stream->dec->snd_stream);
+	PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream started"));
+    } else {
+	PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get stream statistics.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat(
+					    const pjmedia_vid_stream *stream,
+					    pjmedia_rtcp_stat *stat)
+{
+    PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
+
+    pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat));
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Reset the stream statistics in the middle of a stream session.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream)
+{
+    PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+    pjmedia_rtcp_init_stat(&stream->rtcp.stat);
+
+    return PJ_SUCCESS;
+}
+
+
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+/*
+ * Get stream extended statistics.
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr(
+					    const pjmedia_vid_stream *stream,
+					    pjmedia_rtcp_xr_stat *stat)
+{
+    PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
+
+    if (stream->rtcp.xr_enabled) {
+	pj_memcpy(stat, &stream->rtcp.xr_session.stat,
+		  sizeof(pjmedia_rtcp_xr_stat));
+	return PJ_SUCCESS;
+    }
+    return PJ_ENOTFOUND;
+}
+#endif
+
+/*
+ * Get jitter buffer state.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
+					    const pjmedia_vid_stream *stream,
+					    pjmedia_jb_state *state)
+{
+    PJ_ASSERT_RETURN(stream && state, PJ_EINVAL);
+    return pjmedia_jbuf_get_state(stream->jb, state);
+}
+
+/*
+ * Pause stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream,
+					     pjmedia_dir dir)
+{
+    PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+    if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
+	stream->enc->paused = 1;
+	PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
+    }
+
+    if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
+	stream->dec->paused = 1;
+
+	/* Also reset jitter buffer */
+	pj_mutex_lock( stream->jb_mutex );
+	pjmedia_jbuf_reset(stream->jb);
+	pj_mutex_unlock( stream->jb_mutex );
+
+	PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Resume stream
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream,
+					      pjmedia_dir dir)
+{
+    PJ_ASSERT_RETURN(stream, PJ_EINVAL);
+
+    if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
+	stream->enc->paused = 0;
+	PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream resumed"));
+    }
+
+    if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
+	stream->dec->paused = 0;
+	PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream resumed"));
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_VIDEO = { "video", 5};
+static const pj_str_t ID_APPLICATION = { "application", 11};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_IP6 = { "IP6", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
+//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
+static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 };
+
+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 };
+
+
+/*
+ * Internal function for collecting codec info and param from the SDP media.
+ */
+static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si,
+					      pj_pool_t *pool,
+					      pjmedia_vid_codec_mgr *mgr,
+					      const pjmedia_sdp_media *local_m,
+					      const pjmedia_sdp_media *rem_m)
+{
+    const pjmedia_sdp_attr *attr;
+    pjmedia_sdp_rtpmap *rtpmap;
+    unsigned i, pt = 0;
+    pj_status_t status;
+
+    /* Get codec info.
+     * For static payload types, get the info from codec manager.
+     * For dynamic payload types, MUST get the rtpmap.
+     */
+    pt = pj_strtoul(&local_m->desc.fmt[0]);
+    if (pt < 96) {
+	const pjmedia_vid_codec_info *p_info;
+
+	status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	si->codec_info = *p_info;
+
+	/* For static payload type, pt's are symetric */
+	si->tx_pt = pt;
+
+    } else {
+	unsigned info_cnt = 1;
+	const pjmedia_vid_codec_info *p_info;
+
+	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
+					   &local_m->desc.fmt[0]);
+	if (attr == NULL)
+	    return PJMEDIA_EMISSINGRTPMAP;
+
+	status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Get codec info from codec id */
+        status = pjmedia_vid_codec_mgr_find_codecs_by_id(
+					    mgr, &rtpmap->enc_name,
+					    &info_cnt, &p_info, NULL);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	si->codec_info = *p_info;
+
+	/* Determine payload type for outgoing channel, by finding
+	 * dynamic payload type in remote SDP that matches the answer.
+	 */
+	si->tx_pt = 0xFFFF;
+	for (i=0; i<rem_m->desc.fmt_count; ++i) {
+	    unsigned rpt;
+	    pjmedia_sdp_attr *r_attr;
+	    pjmedia_sdp_rtpmap r_rtpmap;
+
+	    rpt = pj_strtoul(&rem_m->desc.fmt[i]);
+	    if (rpt < 96)
+		continue;
+
+	    r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
+						 &rem_m->desc.fmt[i]);
+	    if (!r_attr)
+		continue;
+
+	    if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
+		continue;
+
+	    if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
+		rtpmap->clock_rate == r_rtpmap.clock_rate)
+	    {
+		/* Found matched codec. */
+		si->tx_pt = rpt;
+
+		break;
+	    }
+	}
+
+	if (si->tx_pt == 0xFFFF)
+	    return PJMEDIA_EMISSINGRTPMAP;
+    }
+
+  
+    /* Now that we have codec info, get the codec param. */
+    si->codec_param = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec_param);
+    status = pjmedia_vid_codec_mgr_get_default_param(mgr, 
+						     &si->codec_info,
+						     si->codec_param);
+
+    /* Get remote fmtp for our encoder. */
+    pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
+				   &si->codec_param->enc_fmtp);
+
+    /* Get local fmtp for our decoder. */
+    pjmedia_stream_info_parse_fmtp(pool, local_m, pt,
+				   &si->codec_param->dec_fmtp);
+
+    /* When direction is NONE (it means SDP negotiation has failed) we don't
+     * need to return a failure here, as returning failure will cause
+     * the whole SDP to be rejected. See ticket #:
+     *	http://
+     *
+     * Thanks Alain Totouom 
+     */
+    if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
+	return status;
+
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Create stream info from SDP media line.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp(
+					   pjmedia_vid_stream_info *si,
+					   pj_pool_t *pool,
+					   pjmedia_endpt *endpt,
+					   const pjmedia_sdp_session *local,
+					   const pjmedia_sdp_session *remote,
+					   unsigned stream_idx)
+{
+    const pjmedia_sdp_attr *attr;
+    const pjmedia_sdp_media *local_m;
+    const pjmedia_sdp_media *rem_m;
+    const pjmedia_sdp_conn *local_conn;
+    const pjmedia_sdp_conn *rem_conn;
+    int rem_af, local_af;
+    pj_sockaddr local_addr;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(endpt);
+
+    /* Validate arguments: */
+    PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
+    PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
+    PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
+
+    /* Keep SDP shortcuts */
+    local_m = local->media[stream_idx];
+    rem_m = remote->media[stream_idx];
+
+    local_conn = local_m->conn ? local_m->conn : local->conn;
+    if (local_conn == NULL)
+	return PJMEDIA_SDP_EMISSINGCONN;
+
+    rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
+    if (rem_conn == NULL)
+	return PJMEDIA_SDP_EMISSINGCONN;
+
+    /* Media type must be audio */
+    if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) == 0)
+	return PJMEDIA_EINVALIMEDIATYPE;
+
+
+    /* Reset: */
+
+    pj_bzero(si, sizeof(*si));
+
+#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
+    /* Set default RTCP XR enabled/disabled */
+    si->rtcp_xr_enabled = PJ_TRUE;
+#endif
+
+    /* Media type: */
+    si->type = PJMEDIA_TYPE_VIDEO;
+
+    /* Transport protocol */
+
+    /* At this point, transport type must be compatible, 
+     * the transport instance will do more validation later.
+     */
+    status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, 
+				       &local_m->desc.transport);
+    if (status != PJ_SUCCESS)
+	return PJMEDIA_SDPNEG_EINVANSTP;
+
+    if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {
+
+	si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
+
+    } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
+
+	si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
+
+    } else {
+
+	si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
+	return PJ_SUCCESS;
+    }
+
+
+    /* Check address family in remote SDP */
+    rem_af = pj_AF_UNSPEC();
+    if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
+	if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
+	    rem_af = pj_AF_INET();
+	} else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
+	    rem_af = pj_AF_INET6();
+	}
+    }
+
+    if (rem_af==pj_AF_UNSPEC()) {
+	/* Unsupported address family */
+	return PJ_EAFNOTSUP;
+    }
+
+    /* Set remote address: */
+    status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, 
+			      rem_m->desc.port);
+    if (status != PJ_SUCCESS) {
+	/* Invalid IP address. */
+	return PJMEDIA_EINVALIDIP;
+    }
+
+    /* Check address family of local info */
+    local_af = pj_AF_UNSPEC();
+    if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
+	if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
+	    local_af = pj_AF_INET();
+	} else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
+	    local_af = pj_AF_INET6();
+	}
+    }
+
+    if (local_af==pj_AF_UNSPEC()) {
+	/* Unsupported address family */
+	return PJ_SUCCESS;
+    }
+
+    /* Set remote address: */
+    status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, 
+			      local_m->desc.port);
+    if (status != PJ_SUCCESS) {
+	/* Invalid IP address. */
+	return PJMEDIA_EINVALIDIP;
+    }
+
+    /* Local and remote address family must match */
+    if (local_af != rem_af)
+	return PJ_EAFNOTSUP;
+
+    /* Media direction: */
+
+    if (local_m->desc.port == 0 || 
+	pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
+	pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
+	pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
+    {
+	/* Inactive stream. */
+
+	si->dir = PJMEDIA_DIR_NONE;
+
+    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {
+
+	/* Send only stream. */
+
+	si->dir = PJMEDIA_DIR_ENCODING;
+
+    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {
+
+	/* Recv only stream. */
+
+	si->dir = PJMEDIA_DIR_DECODING;
+
+    } else {
+
+	/* Send and receive stream. */
+
+	si->dir = PJMEDIA_DIR_ENCODING_DECODING;
+
+    }
+
+    /* No need to do anything else if stream is rejected */
+    if (local_m->desc.port == 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* If "rtcp" attribute is present in the SDP, set the RTCP address
+     * from that attribute. Otherwise, calculate from RTP address.
+     */
+    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+				  "rtcp", NULL);
+    if (attr) {
+	pjmedia_sdp_rtcp_attr rtcp;
+	status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
+	if (status == PJ_SUCCESS) {
+	    if (rtcp.addr.slen) {
+		status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
+					  (pj_uint16_t)rtcp.port);
+	    } else {
+		pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, 
+				 (pj_uint16_t)rtcp.port);
+		pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
+		          pj_sockaddr_get_addr(&si->rem_addr),
+			  pj_sockaddr_get_addr_len(&si->rem_addr));
+	    }
+	}
+    }
+    
+    if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
+	int rtcp_port;
+
+	pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
+	rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
+	pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
+    }
+
+    /* Get codec info and param */
+    status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m);
+
+    /* Leave SSRC to random. */
+    si->ssrc = pj_rand();
+
+    /* Set default jitter buffer parameter. */
+    si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
+
+    return status;
+}
+
diff --git a/pjmedia/src/pjmedia/videoport.c b/pjmedia/src/pjmedia/videoport.c
index 1e66b57..9ff2340 100644
--- a/pjmedia/src/pjmedia/videoport.c
+++ b/pjmedia/src/pjmedia/videoport.c
@@ -37,7 +37,7 @@
     pj_str_t		 dev_name;
     pjmedia_dir		 dir;
     pjmedia_rect_size	 cap_size;
-    pjmedia_vid_stream	*strm;
+    pjmedia_vid_dev_stream	*strm;
     pjmedia_vid_cb       strm_cb;
     void                *strm_cb_data;
     enum role		 role,
@@ -74,13 +74,13 @@
     pjmedia_vid_port	*vp;
 };
 
-static pj_status_t vidstream_cap_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream,
 				    void *user_data,
 				    pjmedia_frame *frame);
-static pj_status_t vidstream_render_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_render_cb(pjmedia_vid_dev_stream *stream,
 				       void *user_data,
 				       pjmedia_frame *frame);
-static pj_status_t vidstream_event_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_event_cb(pjmedia_vid_dev_stream *stream,
 			              void *user_data,
                                       pjmedia_vid_event *event);
 
@@ -170,8 +170,8 @@
     vid_cb.render_cb = &vidstream_render_cb;
     vid_cb.on_event_cb = &vidstream_event_cb;
 
-    status = pjmedia_vid_stream_create(&prm->vidparam, &vid_cb, vp,
-				       &vp->strm);
+    status = pjmedia_vid_dev_stream_create(&prm->vidparam, &vid_cb, vp,
+				           &vp->strm);
     if (status != PJ_SUCCESS)
 	goto on_error;
 
@@ -277,7 +277,7 @@
     vid_port->strm_cb_data = user_data;
 }
 
-PJ_DEF(pjmedia_vid_stream*)
+PJ_DEF(pjmedia_vid_dev_stream*)
 pjmedia_vid_port_get_stream(pjmedia_vid_port *vp)
 {
     PJ_ASSERT_RETURN(vp, NULL);
@@ -356,7 +356,7 @@
 
     PJ_ASSERT_RETURN(vp, PJ_EINVAL);
 
-    status = pjmedia_vid_stream_start(vp->strm);
+    status = pjmedia_vid_dev_stream_start(vp->strm);
     if (status != PJ_SUCCESS)
 	goto on_error;
 
@@ -385,7 +385,7 @@
 
     PJ_ASSERT_RETURN(vp, PJ_EINVAL);
 
-    status = pjmedia_vid_stream_stop(vp->strm);
+    status = pjmedia_vid_dev_stream_stop(vp->strm);
 
     if (vp->enc_clock) {
 	status = pjmedia_clock_stop(vp->enc_clock);
@@ -413,7 +413,7 @@
 	vp->dec_clock = NULL;
     }
     if (vp->strm) {
-	pjmedia_vid_stream_destroy(vp->strm);
+	pjmedia_vid_dev_stream_destroy(vp->strm);
 	vp->strm = NULL;
     }
     if (vp->client_port) {
@@ -477,7 +477,7 @@
     if (!vp->client_port)
 	return;
 
-    status = pjmedia_vid_stream_get_frame(vp->strm, vp->enc_frm_buf);
+    status = pjmedia_vid_dev_stream_get_frame(vp->strm, vp->enc_frm_buf);
     if (status != PJ_SUCCESS)
 	return;
 
@@ -580,6 +580,7 @@
                     ndrop = nsync_frame;
                 } else
                     vp->rend_sync_clocksrc.nsync_progress += ndrop;
+
                 for (i = 0; i < ndrop; i++) {
                     status = pjmedia_port_get_frame(vp->client_port,
                                                     vp->dec_frm_buf);
@@ -602,7 +603,7 @@
     pj_add_timestamp32(&vp->rend_clocksrc.timestamp, frame_ts);
     pjmedia_clock_src_update(&vp->rend_clocksrc, NULL);
 
-    status = pjmedia_vid_stream_put_frame(vp->strm, vp->dec_frm_buf);
+    status = pjmedia_vid_dev_stream_put_frame(vp->strm, vp->dec_frm_buf);
 }
 
 static void copy_frame(pjmedia_frame *dst, const pjmedia_frame *src)
@@ -614,7 +615,7 @@
     dst->size = src->size;
 }
 
-static pj_status_t vidstream_cap_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_cap_cb(pjmedia_vid_dev_stream *stream,
 				    void *user_data,
 				    pjmedia_frame *frame)
 {
@@ -633,7 +634,7 @@
     return PJ_SUCCESS;
 }
 
-static pj_status_t vidstream_render_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_render_cb(pjmedia_vid_dev_stream *stream,
 				       void *user_data,
 				       pjmedia_frame *frame)
 {
@@ -652,7 +653,7 @@
     return PJ_SUCCESS;
 }
 
-static pj_status_t vidstream_event_cb(pjmedia_vid_stream *stream,
+static pj_status_t vidstream_event_cb(pjmedia_vid_dev_stream *stream,
 				      void *user_data,
 				      pjmedia_vid_event *event)
 {
@@ -670,7 +671,7 @@
     pjmedia_vid_port *vp = vpp->vp;
 
     if (vp->stream_role==ROLE_PASSIVE) {
-	return pjmedia_vid_stream_put_frame(vp->strm, frame);
+	return pjmedia_vid_dev_stream_put_frame(vp->strm, frame);
     } else {
 	pj_mutex_lock(vp->dec_frm_mutex);
 	copy_frame(vp->dec_frm_buf, frame);
@@ -687,7 +688,7 @@
     pjmedia_vid_port *vp = vpp->vp;
 
     if (vp->stream_role==ROLE_PASSIVE) {
-	return pjmedia_vid_stream_get_frame(vp->strm, frame);
+	return pjmedia_vid_dev_stream_get_frame(vp->strm, frame);
     } else {
 	pj_mutex_lock(vp->enc_frm_mutex);
 	copy_frame(frame, vp->enc_frm_buf);