Migration of current video works from private repository to this repository. This closed #1176

git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/2.0-dev@3392 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
index b0e2014..6f30e20 100644
--- a/pjmedia/build/Makefile
+++ b/pjmedia/build/Makefile
@@ -18,6 +18,7 @@
 export PJMEDIA_CODEC_LIB:=../lib/libpjmedia-codec-$(TARGET_NAME)$(LIBEXT)
 export PJSDP_LIB:=../lib/libpjsdp-$(TARGET_NAME)$(LIBEXT)
 export PJMEDIA_AUDIODEV_LIB:=../lib/libpjmedia-audiodev-$(TARGET_NAME)$(LIBEXT)
+export PJMEDIA_VIDEODEV_LIB:=../lib/libpjmedia-videodev-$(TARGET_NAME)$(LIBEXT)
 
 
 ###############################################################################
@@ -34,6 +35,7 @@
 		   $(HOST_CXXFLAGS) $(CXXFLAGS)
 export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJMEDIA_LIB)) \
 		   $(subst /,$(HOST_PSEP),$(PJMEDIA_AUDIODEV_LIB)) \
+		   $(subst /,$(HOST_PSEP),$(PJMEDIA_VIDEODEV_LIB)) \
 		   $(subst /,$(HOST_PSEP),$(PJMEDIA_CODEC_LIB)) \
 		   $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \
 		   $(subst /,$(HOST_PSEP),$(PJLIB_UTIL_LIB)) \
@@ -49,9 +51,12 @@
 #
 export PJMEDIA_SRCDIR = ../src/pjmedia
 export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
-			alaw_ulaw.o alaw_ulaw_table.o bidirectional.o clock_thread.o codec.o \
-			conference.o conf_switch.o delaybuf.o echo_common.o \
+			alaw_ulaw.o alaw_ulaw_table.o avi_player.o \
+			bidirectional.o clock_thread.o codec.o conference.o \
+			conf_switch.o converter.o  converter_libswscale.o \
+			delaybuf.o echo_common.o \
 			echo_port.o echo_suppress.o endpoint.o errno.o \
+			format.o ffmpeg_util.o \
 			g711.o jbuf.o master_port.o mem_capture.o mem_player.o \
 			null_port.o plc_common.o port.o splitcomb.o \
 			resample_resample.o resample_libsamplerate.o \
@@ -60,7 +65,7 @@
 			sound_legacy.o sound_port.o stereo_port.o \
 			stream.o tonegen.o transport_adapter_sample.o \
 			transport_ice.o transport_loop.o \
-			transport_srtp.o transport_udp.o \
+			transport_srtp.o transport_udp.o vid_codec.o videoport.o \
 			wav_player.o wav_playlist.o wav_writer.o wave.o \
 			wsola.o
 
@@ -78,6 +83,15 @@
 
 
 ###############################################################################
+# Defines for building PJMEDIA-VIDEODEV library
+#
+export PJMEDIA_VIDEODEV_SRCDIR = ../src/pjmedia-videodev
+export PJMEDIA_VIDEODEV_OBJS +=  videodev.o ffmpeg_dev.o sdl_dev.o colorbar_dev.o \
+				 v4l2_dev.o
+export PJMEDIA_VIDEODEV_CFLAGS += $(_CFLAGS)
+
+
+###############################################################################
 # Defines for building PJSDP library
 # Note that SDP functionality is already INCLUDED in PJMEDIA.
 # The PJSDP library should only be used for applications that want SDP
@@ -93,7 +107,7 @@
 # Defines for building PJMEDIA-Codec library
 #
 export PJMEDIA_CODEC_SRCDIR = ../src/pjmedia-codec
-export PJMEDIA_CODEC_OBJS += \
+export PJMEDIA_CODEC_OBJS += ffmpeg_codecs.o \
 			$(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
 			ipp_codecs.o $(CODEC_OBJS)
 export PJMEDIA_CODEC_CFLAGS += $(_CFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \
@@ -104,7 +118,8 @@
 # Defines for building test application
 #
 export PJMEDIA_TEST_SRCDIR = ../src/test
-export PJMEDIA_TEST_OBJS += codec_vectors.o jbuf_test.o main.o mips_test.o rtp_test.o test.o
+export PJMEDIA_TEST_OBJS += codec_vectors.o jbuf_test.o main.o mips_test.o \
+			    vid_dev_test.o vid_codec_test.o rtp_test.o test.o
 export PJMEDIA_TEST_OBJS += sdp_neg_test.o 
 export PJMEDIA_TEST_CFLAGS += $(_CFLAGS)
 export PJMEDIA_TEST_LDFLAGS += $(_LDFLAGS)
@@ -117,7 +132,7 @@
 #
 # $(TARGET) is defined in os-$(OS_NAME).mak file in current directory.
 #
-TARGETS := pjmedia pjmedia-audiodev pjmedia-codec pjsdp pjmedia-test
+TARGETS := pjmedia pjmedia-videodev pjmedia-audiodev pjmedia-codec pjsdp pjmedia-test
 
 all: $(TARGETS)
 
@@ -132,7 +147,7 @@
 dep: depend
 distclean: realclean
 
-.PHONY: dep depend pjmedia pjmedia-codec pjmedia-audiodev pjmedia-test clean realclean distclean
+.PHONY: dep depend pjmedia pjmedia-codec pjmedia-videodev pjmedia-audiodev pjmedia-test clean realclean distclean
 
 pjmedia:
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $(PJMEDIA_LIB)
@@ -140,6 +155,9 @@
 pjmedia-codec:
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $(PJMEDIA_CODEC_LIB)
 
+pjmedia-videodev:
+	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $(PJMEDIA_VIDEODEV_LIB)
+
 pjmedia-audiodev:
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $(PJMEDIA_AUDIODEV_LIB)
 
@@ -166,18 +184,21 @@
 clean:
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@
+	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@
 	$(MAKE) -f $(RULES_MAK) APP=PJSDP app=pjsdp $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@
 
 realclean:
 	$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-$(TARGET_NAME).depend),$(HOST_RMR))
+	$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-videodev-$(TARGET_NAME).depend),$(HOST_RMR))
 	$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-audiodev-$(TARGET_NAME).depend),$(HOST_RMR))
 	$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-codec-$(TARGET_NAME).depend),$(HOST_RMR))
 	$(subst @@,$(subst /,$(HOST_PSEP),.pjmedia-test-$(TARGET_NAME).depend),$(HOST_RMR))
 	$(subst @@,$(subst /,$(HOST_PSEP),.pjsdp-$(TARGET_NAME).depend),$(HOST_RMR))
 	
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@
+	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@
@@ -185,6 +206,7 @@
 
 depend:
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA app=pjmedia $@
+	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_VIDEODEV app=pjmedia-videodev $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_AUDIODEV app=pjmedia-audiodev $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_CODEC app=pjmedia-codec $@
 	$(MAKE) -f $(RULES_MAK) APP=PJMEDIA_TEST app=pjmedia-test $@
diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in
index a329f76..11c96bc 100644
--- a/pjmedia/build/os-auto.mak.in
+++ b/pjmedia/build/os-auto.mak.in
@@ -1,7 +1,21 @@
 # @configure_input@
 
+# SDL flags
+SDL_CFLAGS = @ac_sdl_cflags@
+SDL_LDFLAGS = @ac_sdl_ldflags@
+
+# FFMPEG dlags
+FFMPEG_CFLAGS = @ac_ffmpeg_cflags@ 
+FFMPEG_LDFLAGS =  @ac_ffmpeg_ldflags@
+
+# Video4Linux2
+V4L2_CFLAGS = @ac_v4l2_cflags@
+V4L2_LDFLAGS = @ac_v4l2_ldflags@
+
 # PJMEDIA features exclusion
-export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_speex_aec@
+export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_speex_aec@ \
+		 $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS)
+export LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS)
 
 # Define the desired sound device backend
 # Valid values are:
diff --git a/pjmedia/include/pjmedia-audiodev/audiodev.h b/pjmedia/include/pjmedia-audiodev/audiodev.h
index ba28bc0..2a0dc67 100644
--- a/pjmedia/include/pjmedia-audiodev/audiodev.h
+++ b/pjmedia/include/pjmedia-audiodev/audiodev.h
@@ -26,6 +26,8 @@
  */
 #include <pjmedia-audiodev/config.h>
 #include <pjmedia-audiodev/errno.h>
+#include <pjmedia/format.h>
+#include <pjmedia/frame.h>
 #include <pjmedia/types.h>
 #include <pj/pool.h>
 
diff --git a/pjmedia/include/pjmedia-audiodev/config.h b/pjmedia/include/pjmedia-audiodev/config.h
index 5e5fa75..b08cefb 100644
--- a/pjmedia/include/pjmedia-audiodev/config.h
+++ b/pjmedia/include/pjmedia-audiodev/config.h
@@ -21,8 +21,8 @@
 #define __PJMEDIA_AUDIODEV_CONFIG_H__
 
 /**
- * @file audiodev.h
- * @brief Audio device API.
+ * @file config.h
+ * @brief Audio config.
  */
 #include <pjmedia/types.h>
 #include <pj/pool.h>
diff --git a/pjmedia/include/pjmedia-codec.h b/pjmedia/include/pjmedia-codec.h
index e6d211d..942d529 100644
--- a/pjmedia/include/pjmedia-codec.h
+++ b/pjmedia/include/pjmedia-codec.h
@@ -26,6 +26,7 @@
  */
 
 #include <pjmedia-codec/l16.h>
+#include <pjmedia-codec/ffmpeg_codecs.h>
 #include <pjmedia-codec/gsm.h>
 #include <pjmedia-codec/speex.h>
 #include <pjmedia-codec/ilbc.h>
diff --git a/pjmedia/include/pjmedia-codec/config.h b/pjmedia/include/pjmedia-codec/config.h
index 3dbb5c5..1480e22 100644
--- a/pjmedia/include/pjmedia-codec/config.h
+++ b/pjmedia/include/pjmedia-codec/config.h
@@ -34,7 +34,6 @@
 
 #include <pjmedia/types.h>
 
-
 /*
  * Include config_auto.h if autoconf is used (PJ_AUTOCONF is set)
  */
@@ -42,6 +41,7 @@
 #   include <pjmedia-codec/config_auto.h>
 #endif
 
+
 /**
  * Unless specified otherwise, L16 codec is included by default.
  */
@@ -343,8 +343,20 @@
 	  to control which implementation to be used.
 #endif
 
+
+/**
+ * Specify if FFMPEG codecs are available.
+ *
+ * Default: PJMEDIA_HAS_LIBAVCODEC
+ */
+#ifndef PJMEDIA_HAS_FFMPEG_CODEC
+#   define PJMEDIA_HAS_FFMPEG_CODEC		PJMEDIA_HAS_LIBAVCODEC
+#endif
+
 /**
  * @}
  */
 
+
+
 #endif	/* __PJMEDIA_CODEC_CONFIG_H__ */
diff --git a/pjmedia/include/pjmedia-codec/ffmpeg_codecs.h b/pjmedia/include/pjmedia-codec/ffmpeg_codecs.h
new file mode 100644
index 0000000..d928044
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/ffmpeg_codecs.h
@@ -0,0 +1,62 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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_CODECS_FFMPEG_H__
+#define __PJMEDIA_CODECS_FFMPEG_H__
+
+
+#include <pjmedia-codec/types.h>
+#include <pjmedia/vid_codec.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Initialize and register FFMPEG codecs factory to pjmedia endpoint.
+ *
+ * @param mgr	    The video codec manager instance where this codec will
+ * 		    be registered to. Specify NULL to use default instance
+ * 		    (in that case, an instance of video codec manager must
+ * 		    have been created beforehand).
+ * @param pf	    Pool factory.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_ffmpeg_init(pjmedia_vid_codec_mgr *mgr,
+                                               pj_pool_factory *pf);
+
+
+/**
+ * Unregister FFMPEG codecs factory from the video codec manager and
+ * deinitialize the codecs library.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_ffmpeg_deinit(void);
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+#endif	/* __PJMEDIA_CODECS_FFMPEG_H__ */
+
diff --git a/pjmedia/include/pjmedia-codec/h263_packetizer.h b/pjmedia/include/pjmedia-codec/h263_packetizer.h
new file mode 100644
index 0000000..4208fc0
--- /dev/null
+++ b/pjmedia/include/pjmedia-codec/h263_packetizer.h
@@ -0,0 +1,140 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2009 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_H263_PACKETIZER_H__
+#define __PJMEDIA_H263_PACKETIZER_H__
+
+
+/**
+ * @file h263_packetizer.h
+ * @brief Packetizes H.263 bitstream into RTP payload.
+ */
+
+#include <pj/errno.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * Find synchronization point (PSC, slice, GSBC, EOS, EOSBS) in H.263 
+ * bitstream, in reversed manner.
+ */
+PJ_INLINE(pj_uint8_t*) pjmedia_h263_find_sync_point_rev(pj_uint8_t *data,
+                                                        pj_size_t data_len)
+{
+    pj_uint8_t *p = data+data_len-1;
+
+    while (p > data && *p && *(p+1))
+        --p;
+
+    if (p == data)
+        return (data + data_len);
+        
+    return p;
+}
+
+/**
+ * Generate an RTP payload from H.263 frame bitstream, in-place processing.
+ */
+PJ_INLINE(pj_status_t) pjmedia_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)
+{
+    pj_uint8_t *p, *p_end;
+
+    p = buf + *pos;
+    p_end = buf + buf_len;
+
+    /* Put two octets payload header */
+    if ((p_end-p > 2) && *p==0 && *(p+1)==0) {
+        /* The bitstream starts with synchronization point, just override
+         * the two zero octets (sync point mark) for payload header.
+         */
+        *p = 0x04;
+    } else {
+        /* Not started in synchronization point, we will use two octets
+         * preceeding the bitstream for payload header!
+         */
+        pj_assert(*pos>=2);
+        p -= 2;
+        *p = 0;
+    }
+    *(p+1) = 0;
+
+    /* When bitstream truncation needed because of payload length/MTU 
+     * limitation, try to use sync point for the payload boundary.
+     */
+    if (p_end-p > max_payload_len) {
+        p_end = pjmedia_h263_find_sync_point_rev(p+2, max_payload_len-2);
+    }
+
+    *payload = p;
+    *payload_len = p_end-p;
+    *pos = p_end - buf;
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Append RTP payload to a H.263 picture bitstream.
+ */
+PJ_INLINE(pj_status_t) pjmedia_h263_unpacketize(const pj_uint8_t *payload,
+                                                pj_size_t   payload_len,
+                                                pj_uint8_t *bits,
+                                                pj_size_t  *bits_len)
+{
+    pj_uint8_t P, V, PLEN;
+    const pj_uint8_t *p=payload;
+    pj_size_t max_len = *bits_len;
+
+    P = *p & 0x04;
+    V = *p & 0x02;
+    PLEN = ((*p & 0x01) << 5) + ((*(p+1) & 0xF8)>>3);
+
+    /* Get bitstream pointer */
+    p += 2;
+    if (V)
+        p += 1; /* Skip VRC data */
+    if (PLEN)
+        p += PLEN; /* Skip extra picture header data */
+
+    /* Get bitstream length */
+    payload_len -= (p-payload);
+
+    *bits_len = payload_len + (P?2:0);
+    PJ_ASSERT_RETURN(max_len >= *bits_len, PJ_ETOOSMALL);
+
+    /* Add two zero octets when payload flagged with sync point */
+    if (P) {
+        *bits++ = 0;
+        *bits++ = 0;
+    }
+
+    /* Add the bitstream */
+    pj_memcpy(bits, p, payload_len);
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_H263_PACKETIZER_H__ */
diff --git a/pjmedia/include/pjmedia-videodev/config.h b/pjmedia/include/pjmedia-videodev/config.h
new file mode 100644
index 0000000..6ee3b3d
--- /dev/null
+++ b/pjmedia/include/pjmedia-videodev/config.h
@@ -0,0 +1,160 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2010 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_VIDEODEV_CONFIG_H__
+#define __PJMEDIA_VIDEODEV_CONFIG_H__
+
+/**
+ * @file config.h
+ * @brief Video config.
+ */
+#include <pjmedia/types.h>
+#include <pj/pool.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup video_device_api Video Device API
+ * @brief PJMEDIA video device abstraction API.
+ */
+
+/**
+ * @defgroup s1_video_device_config Compile time configurations
+ * @ingroup video_device_api
+ * @brief Compile time configurations
+ * @{
+ */
+
+/**
+ * This setting controls whether SDL support should be included.
+ *
+ * Default: 0 (or detected by configure)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_SDL
+#   define PJMEDIA_VIDEO_DEV_HAS_SDL		0
+#endif
+
+
+/**
+ * This setting controls whether Direct Show support should be included.
+ *
+ * Default: 0 (unfinished)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_DSHOW
+#   define PJMEDIA_VIDEO_DEV_HAS_DSHOW		0 //PJ_WIN32
+#endif
+
+
+/**
+ * This setting controls whether colorbar source support should be included.
+ *
+ * Default: 1
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+#   define PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC	1
+#endif
+
+
+/**
+ * This setting controls whether ffmpeg support should be included.
+ *
+ * Default: 0 (unfinished)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+#   define PJMEDIA_VIDEO_DEV_HAS_FFMPEG	        0
+#endif
+
+
+/**
+ * Video4Linux2
+ *
+ * Default: 0 (or detected by configure)
+ */
+#ifndef PJMEDIA_VIDEO_DEV_HAS_V4L2
+#   define PJMEDIA_VIDEO_DEV_HAS_V4L2		0
+#endif
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_VIDEODEV_CONFIG_H__ */
+
+/*
+ --------------------- DOCUMENTATION FOLLOWS ---------------------------
+ */
+
+/**
+ * @addtogroup video_device_api Video Device API
+ * @{
+
+PJMEDIA Video Device API is a cross-platform video API appropriate for use with
+VoIP applications and many other types of video streaming applications. 
+
+The API abstracts many different video API's on various platforms, such as:
+ - native Direct Show video for Win32 and Windows Mobile devices
+ - null-video implementation
+ - and more to be implemented in the future
+
+The Video Device API/library is an evolution from PJMEDIA @ref PJMED_SND and 
+contains many enhancements:
+
+ - Forward compatibility:
+\n
+   The new API has been designed to be extensible, it will support new API's as 
+   well as new features that may be introduced in the future without breaking 
+   compatibility with applications that use this API as well as compatibility 
+   with existing device implementations. 
+
+ - Device capabilities:
+\n
+   At the heart of the API is device capabilities management, where all possible
+   video capabilities of video devices should be able to be handled in a generic
+   manner. With this framework, new capabilities that may be discovered in the 
+   future can be handled in manner without breaking existing applications. 
+
+ - Built-in features:
+\n
+   The device capabilities framework enables applications to use and control 
+   video features built-in in the device, such as:
+    - built-in formats, 
+    - etc.
+
+ - Codec support:
+\n
+   Some video devices support built-in hardware video codecs, and application
+   can use the video device in encoded mode to make use of these hardware 
+   codecs. 
+
+ - Multiple backends:
+\n
+   The new API supports multiple video backends (called factories or drivers in 
+   the code) to be active simultaneously, and video backends may be added or 
+   removed during run-time. 
+
+*/
+
+
+/**
+ * @}
+ */
+
diff --git a/pjmedia/include/pjmedia-videodev/errno.h b/pjmedia/include/pjmedia-videodev/errno.h
new file mode 100644
index 0000000..8c6e89d
--- /dev/null
+++ b/pjmedia/include/pjmedia-videodev/errno.h
@@ -0,0 +1,159 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2010 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_VIDEODEV_VIDEODEV_ERRNO_H__
+#define __PJMEDIA_VIDEODEV_VIDEODEV_ERRNO_H__
+
+/**
+ * @file errno.h Error Codes
+ * @brief Videodev specific error codes.
+ */
+
+#include <pjmedia-videodev/config.h>
+#include <pj/errno.h>
+
+/**
+ * @defgroup error_codes Error Codes
+ * @ingroup video_device_api
+ * @brief Video device library specific error codes.
+ * @{
+ */
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Start of error code relative to PJ_ERRNO_START_USER.
+ * This value is 470000.
+ */
+#define PJMEDIA_VIDEODEV_ERRNO_START \
+	    (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*6)
+#define PJMEDIA_VIDEODEV_ERRNO_END   \
+	    (PJMEDIA_VIDEODEV_ERRNO_START + PJ_ERRNO_SPACE_SIZE - 1)
+
+
+/************************************************************
+ * Video Device API error codes
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * General/unknown error.
+ */
+#define PJMEDIA_EVID_ERR	(PJMEDIA_VIDEODEV_ERRNO_START+1) /* 470001 */
+
+/**
+ * @hideinitializer
+ * Unknown error from video driver
+ */
+#define PJMEDIA_EVID_SYSERR	(PJMEDIA_VIDEODEV_ERRNO_START+2) /* 470002 */
+
+/**
+ * @hideinitializer
+ * Video subsystem not initialized
+ */
+#define PJMEDIA_EVID_INIT	(PJMEDIA_VIDEODEV_ERRNO_START+3) /* 470003 */
+
+/**
+ * @hideinitializer
+ * Invalid video device
+ */
+#define PJMEDIA_EVID_INVDEV	(PJMEDIA_VIDEODEV_ERRNO_START+4) /* 470004 */
+
+/**
+ * @hideinitializer
+ * Found no devices
+ */
+#define PJMEDIA_EVID_NODEV	(PJMEDIA_VIDEODEV_ERRNO_START+5) /* 470005 */
+
+/**
+ * @hideinitializer
+ * Unable to find default device
+ */
+#define PJMEDIA_EVID_NODEFDEV	(PJMEDIA_VIDEODEV_ERRNO_START+6) /* 470006 */
+
+/**
+ * @hideinitializer
+ * Device not ready
+ */
+#define PJMEDIA_EVID_NOTREADY	(PJMEDIA_VIDEODEV_ERRNO_START+7) /* 470007 */
+
+/**
+ * @hideinitializer
+ * The video capability is invalid or not supported
+ */
+#define PJMEDIA_EVID_INVCAP	(PJMEDIA_VIDEODEV_ERRNO_START+8) /* 470008 */
+
+/**
+ * @hideinitializer
+ * The operation is invalid or not supported
+ */
+#define PJMEDIA_EVID_INVOP	(PJMEDIA_VIDEODEV_ERRNO_START+9) /* 470009 */
+
+/**
+ * @hideinitializer
+ * Bad or invalid video device format
+ */
+#define PJMEDIA_EVID_BADFORMAT	(PJMEDIA_VIDEODEV_ERRNO_START+10) /* 4700010 */
+
+/**
+ * @hideinitializer
+ * Invalid video device sample format
+ */
+#define PJMEDIA_EVID_SAMPFORMAT	(PJMEDIA_VIDEODEV_ERRNO_START+11) /* 4700011 */
+
+/**
+ * @hideinitializer
+ * Bad latency setting
+ */
+#define PJMEDIA_EVID_BADLATENCY	(PJMEDIA_VIDEODEV_ERRNO_START+12) /* 4700012 */
+
+/**
+ * @hideinitializer
+ * Bad/unsupported video size
+ */
+#define PJMEDIA_EVID_BADSIZE	(PJMEDIA_VIDEODEV_ERRNO_START+13) /* 4700013 */
+
+
+/**
+ * Get error message for the specified error code. Note that this
+ * function is only able to decode PJMEDIA Videodev specific error code.
+ * Application should use pj_strerror(), which should be able to
+ * decode all error codes belonging to all subsystems (e.g. pjlib,
+ * pjmedia, pjsip, etc).
+ *
+ * @param status    The error code.
+ * @param buffer    The buffer where to put the error message.
+ * @param bufsize   Size of the buffer.
+ *
+ * @return	    The error message as NULL terminated string,
+ *                  wrapped with pj_str_t.
+ */
+PJ_DECL(pj_str_t) pjmedia_videodev_strerror(pj_status_t status, char *buffer,
+					    pj_size_t bufsize);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif	/* __PJMEDIA_VIDEODEV_VIDEODEV_ERRNO_H__ */
+
diff --git a/pjmedia/include/pjmedia-videodev/videodev.h b/pjmedia/include/pjmedia-videodev/videodev.h
new file mode 100644
index 0000000..2b4d894
--- /dev/null
+++ b/pjmedia/include/pjmedia-videodev/videodev.h
@@ -0,0 +1,574 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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_VIDEODEV_VIDEODEV_H__
+#define __PJMEDIA_VIDEODEV_VIDEODEV_H__
+
+/**
+ * @file videodev.h
+ * @brief Video device API.
+ */
+#include <pjmedia-videodev/config.h>
+#include <pjmedia-videodev/errno.h>
+#include <pjmedia/frame.h>
+#include <pjmedia/format.h>
+#include <pj/pool.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup video_device_reference Video Device API Reference
+ * @ingroup video_device_api
+ * @brief API Reference
+ * @{
+ */
+ 
+/**
+ * Type for device index.
+ */
+typedef pj_int32_t pjmedia_vid_dev_index;
+
+/**
+ * Device index constants.
+ */
+enum
+{
+    /**
+     * Constant to denote default capture device
+     */
+    PJMEDIA_VID_DEFAULT_CAPTURE_DEV = -1,
+
+    /**
+     * Constant to denote default render device
+     */
+    PJMEDIA_VID_DEFAULT_RENDER_DEV = -2,
+
+    /**
+     * Constant to denote invalid device index.
+     */
+    PJMEDIA_VID_INVALID_DEV = -3
+};
+
+
+/**
+ * This enumeration identifies various video device capabilities. These video
+ * capabilities indicates what features are supported by the underlying
+ * video device implementation.
+ *
+ * Applications get these capabilities in the #pjmedia_vid_dev_info structure.
+ *
+ * Application can also set the specific features/capabilities when opening
+ * the video stream by setting the \a flags member of #pjmedia_vid_param
+ * 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
+ * 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.
+ */
+typedef enum pjmedia_vid_dev_cap
+{
+    /**
+     * Support for video formats. The value of this capability
+     * is represented by #pjmedia_format structure.
+     */
+    PJMEDIA_VID_DEV_CAP_FORMAT = 1,
+
+    /**
+     * Support for video input scaling
+     */
+    PJMEDIA_VID_DEV_CAP_INPUT_SCALE = 2,
+
+    /**
+     * The application can provide a window for the renderer to
+     * display the video.
+     */
+    PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW = 4,
+
+    /**
+     * Support for resizing video output. This capability SHOULD be 
+     * implemented by renderer, to alter the video output dimension on the fly.
+     * Value is pjmedia_rect_size. 
+     */
+    PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE = 8,
+
+    /**
+     * End of capability
+     */
+    PJMEDIA_VID_DEV_CAP_MAX = 16384
+
+} pjmedia_vid_dev_cap;
+
+/**
+ * Device information structure returned by #pjmedia_vid_dev_get_info().
+ */
+typedef struct pjmedia_vid_dev_info
+{
+    /** The device name */
+    char name[64];
+
+    /** The underlying driver name */
+    char driver[32];
+
+    /** 
+     * The supported direction of the video device, i.e. whether it supports 
+     * capture only, render only, or both.
+     */
+    pjmedia_dir dir;
+
+    /** Specify whether the device supports callback */
+    pj_bool_t has_callback;
+
+    /** Device capabilities, as bitmask combination of #pjmedia_vid_dev_cap */
+    unsigned caps;
+
+    /** Number of video formats supported by this device */
+    unsigned fmt_cnt;
+
+    /** 
+     * Array of supported video formats. Some fields in each supported video
+     * format may be set to zero or of "unknown" value, to indicate that the
+     * value is unknown or should be ignored. When these value are not set
+     * to zero, it indicates that the exact format combination is being used. 
+     */
+    pjmedia_format *fmt;
+
+} pjmedia_vid_dev_info;
+
+
+/** Forward declaration for pjmedia_vid_stream */
+typedef struct pjmedia_vid_stream pjmedia_vid_stream;
+
+typedef enum pjmedia_event_type
+{
+    PJMEDIA_EVENT_NONE,
+    PJMEDIA_EVENT_MOUSEBUTTONDOWN,
+    PJMEDIA_EVENT_WINDOW_RESIZE,
+    PJMEDIA_EVENT_WINDOW_FULLSCREEN,
+    PJMEDIA_EVENT_WINDOW_CLOSE,
+} pjmedia_event_type;
+
+typedef struct pjmedia_vid_event
+{
+    pjmedia_event_type  event_type;
+    long	        lparam;
+    void	       *pparam;
+} pjmedia_vid_event;
+
+
+typedef struct pjmedia_vid_cb
+{
+    /**
+    * This callback is called by capturer stream when it has captured the
+    * whole packet worth of video samples.
+    *
+    * @param stream	   The video stream.
+    * @param user_data     User data associated with the stream.
+    * @param frame         Captured frame.
+    *
+    * @return              Returning non-PJ_SUCCESS will cause the video
+    *                      stream to stop
+    */
+    pj_status_t (*capture_cb)(pjmedia_vid_stream *stream,
+		              void *user_data,
+                              pjmedia_frame *frame);
+
+    /**
+    * This callback is called by renderer stream when it needs additional
+    * data to be rendered by the device. Application must fill in the whole
+    * of output buffer with video samples.
+    *
+    * The frame argument contains the following values:
+    *  - timestamp         Rendering timestamp, in samples.
+    *  - buf               Buffer to be filled out by application.
+    *  - size              The size requested in bytes, which will be equal
+    *                      to the size of one whole packet.
+    *
+    * @param stream	   The video stream.
+    * @param user_data     User data associated with the stream.
+    * @param frame         Video frame, which buffer is to be filled in by
+    *                      the application.
+    *
+    * @return              Returning non-PJ_SUCCESS will cause the video 
+    *                      stream to stop
+    */
+    pj_status_t (*render_cb)(pjmedia_vid_stream *stream,
+			     void *user_data,
+                             pjmedia_frame *frame);
+
+    /**
+    * This callback is called by the stream to report the occurence of an
+    * event to the application.
+    *
+    * @param stream	   The video stream.
+    * @param user_data     User data associated with the stream.
+    * @param event	   The event.
+    *
+    * @return              Return PJ_SUCCESS will invoke the video stream's
+    *                      default event-handler (if any), otherwise the
+    *                      video stream will ignore the particular event.
+    */
+    pj_status_t (*on_event_cb)(pjmedia_vid_stream *stream,
+			       void *user_data,
+                               pjmedia_vid_event *event);
+
+} pjmedia_vid_cb;
+
+
+/**
+ * This structure specifies the parameters to open the video stream.
+ */
+typedef struct pjmedia_vid_param
+{
+    /**
+     * The video direction. This setting is mandatory.
+     */
+    pjmedia_dir dir;
+
+    /**
+     * The video capture device ID. This setting is mandatory if the video
+     * direction includes input/capture direction.
+     */
+    pjmedia_vid_dev_index cap_id;
+
+    /**
+     * The video render device ID. This setting is mandatory if the video
+     * direction includes output/render direction.
+     */
+    pjmedia_vid_dev_index rend_id;
+
+    /** 
+     * Video clock rate. This setting is mandatory if the video
+     * direction includes input/capture direction
+     */
+    unsigned clock_rate;
+
+    /**
+     * Video frame rate. This setting is mandatory if the video
+     * direction includes input/capture direction
+     */
+    pjmedia_ratio frame_rate;
+
+    /**
+     * This flags specifies which of the optional settings are valid in this
+     * structure. The flags is bitmask combination of pjmedia_vid_dev_cap.
+     */
+    unsigned flags;
+
+    /**
+     * Set the video format. This setting is mandatory.
+     */
+    pjmedia_format fmt;
+
+    /**
+     * Window for the renderer to display the video. This setting is optional,
+     * and will only be used if PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW is set in 
+     * the flags.
+     */
+    void *window;
+
+    /**
+     * Video display size. This setting is optional, and will only be used 
+     * if PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE is set in the flags.
+     */
+    pjmedia_rect_size disp_size;
+
+} pjmedia_vid_param;
+
+
+/** Forward declaration for video device factory */
+typedef struct pjmedia_vid_dev_factory pjmedia_vid_dev_factory;
+
+/* typedef for factory creation function */
+typedef pjmedia_vid_dev_factory*
+(*pjmedia_vid_dev_factory_create_func_ptr)(pj_pool_factory*);
+
+
+/**
+ * Get string info for the specified capability.
+ *
+ * @param cap       The capability ID.
+ * @param p_desc    Optional pointer which will be filled with longer
+ *                  description about the capability.
+ *
+ * @return          Capability name.
+ */
+PJ_DECL(const char*) pjmedia_vid_dev_cap_name(pjmedia_vid_dev_cap cap,
+                                              const char **p_desc);
+
+
+/**
+ * Set a capability field value in #pjmedia_vid_param structure. This will
+ * also set the flags field for the specified capability in the structure.
+ *
+ * @param param     The structure.
+ * @param cap       The video capability which value is to be set.
+ * @param pval      Pointer to value. Please see the type of value to
+ *                  be supplied in the pjmedia_vid_dev_cap documentation.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_param_set_cap(pjmedia_vid_param *param,
+                                               pjmedia_vid_dev_cap cap,
+                                               const void *pval);
+
+
+/**
+ * Get a capability field value from #pjmedia_vid_param structure. This
+ * function will return PJMEDIA_EVID_INVCAP error if the flag for that
+ * capability is not set in the flags field in the structure.
+ *
+ * @param param     The structure.
+ * @param cap       The video capability which value is to be retrieved.
+ * @param pval      Pointer to value. Please see the type of value to
+ *                  be supplied in the pjmedia_vid_dev_cap documentation.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_param_get_cap(const pjmedia_vid_param *param,
+                                               pjmedia_vid_dev_cap cap,
+                                               void *pval);
+
+/**
+ * Initialize the video subsystem. This will register all supported video
+ * device factories to the video subsystem. This function may be called
+ * more than once, but each call to this function must have the
+ * corresponding #pjmedia_vid_subsys_shutdown() call.
+ *
+ * @param pf        The pool factory.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_subsys_init(pj_pool_factory *pf);
+
+
+/**
+ * Get the pool factory registered to the video subsystem.
+ *
+ * @return          The pool factory.
+ */
+PJ_DECL(pj_pool_factory*) pjmedia_vid_subsys_get_pool_factory(void);
+
+
+/**
+ * Shutdown the video subsystem. This will destroy all video device factories
+ * registered in the video subsystem. Note that currently opened video streams
+ * may or may not be closed, depending on the implementation of the video
+ * device factories.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_subsys_shutdown(void);
+
+
+/**
+ * Register a supported video device factory to the video subsystem. This
+ * function can only be called after calling #pjmedia_vid_subsys_init().
+ *
+ * @param vdf       The video device factory.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_register_factory(pjmedia_vid_dev_factory_create_func_ptr vdf);
+
+
+/**
+ * Unregister an video device factory from the video subsystem. This
+ * function can only be called after calling #pjmedia_vid_subsys_init().
+ * Devices from this factory will be unlisted. If a device from this factory
+ * is currently in use, then the behavior is undefined.
+ *
+ * @param vdf       The video device factory.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_vid_unregister_factory(pjmedia_vid_dev_factory_create_func_ptr vdf);
+
+
+/**
+ * Get the number of video devices installed in the system.
+ *
+ * @return          The number of video devices installed in the system.
+ */
+PJ_DECL(unsigned) pjmedia_vid_dev_count(void);
+
+
+/**
+ * Get device information.
+ *
+ * @param id        The video device ID.
+ * @param info      The device information which will be filled in by this
+ *                  function once it returns successfully.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_get_info(pjmedia_vid_dev_index id,
+                                              pjmedia_vid_dev_info *info);
+
+
+/**
+ * Lookup device index based on the driver and device name.
+ *
+ * @param drv_name  The driver name.
+ * @param dev_name  The device name.
+ * @param id        Pointer to store the returned device ID.
+ *
+ * @return          PJ_SUCCESS if the device can be found.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_lookup(const char *drv_name,
+                                            const char *dev_name,
+                                            pjmedia_vid_dev_index *id);
+
+
+/**
+ * Initialize the video device parameters with default values for the
+ * specified device.
+ *
+ * @param id        The video device ID.
+ * @param param     The video device parameters which will be initialized
+ *                  by this function once it returns successfully.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_default_param(pj_pool_t *pool,
+                                                   pjmedia_vid_dev_index id,
+                                                   pjmedia_vid_param *param);
+
+
+/**
+ * Open video stream object using the specified parameters.
+ *
+ * @param param         Sound device parameters to be used for the stream.
+ * @param cb            Pointer to structure containing video stream
+ *                      callbacks.
+ * @param user_data     Arbitrary user data, which will be given back in the
+ *                      callbacks.
+ * @param p_strm        Pointer to receive the video stream.
+ *
+ * @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);
+
+/**
+ * Get the running parameters for the specified video stream.
+ *
+ * @param strm      The video stream.
+ * @param param     Video stream parameters to be filled in by this
+ *                  function once it returns successfully.
+ *
+ * @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);
+
+/**
+ * Get the value of a specific capability of the video stream.
+ *
+ * @param strm      The video stream.
+ * @param cap       The video capability which value is to be retrieved.
+ * @param value     Pointer to value to be filled in by this function
+ *                  once it returns successfully.  Please see the type
+ *                  of value to be supplied in the pjmedia_vid_dev_cap
+ *                  documentation.
+ *
+ * @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);
+
+/**
+ * Set the value of a specific capability of the video stream.
+ *
+ * @param strm      The video stream.
+ * @param cap       The video capability which value is to be set.
+ * @param value     Pointer to value. Please see the type of value to
+ *                  be supplied in the pjmedia_vid_dev_cap documentation.
+ *
+ * @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);
+
+/**
+ * Start the stream.
+ *
+ * @param strm      The video stream.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_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_stream_put_frame(pjmedia_vid_stream *strm,
+                                                  const pjmedia_frame *frame);
+
+/**
+ * Stop the stream.
+ *
+ * @param strm      The video stream.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_stream_stop(pjmedia_vid_stream *strm);
+
+/**
+ * Destroy the stream.
+ *
+ * @param strm      The video stream.
+ *
+ * @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_END_DECL
+
+
+#endif    /* __PJMEDIA_VIDEODEV_VIDEODEV_H__ */
diff --git a/pjmedia/include/pjmedia-videodev/videodev_imp.h b/pjmedia/include/pjmedia-videodev/videodev_imp.h
new file mode 100644
index 0000000..e16b90a
--- /dev/null
+++ b/pjmedia/include/pjmedia-videodev/videodev_imp.h
@@ -0,0 +1,192 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2010 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 __VIDEODEV_IMP_H__
+#define __VIDEODEV_IMP_H__
+
+#include <pjmedia-videodev/videodev.h>
+
+/**
+ * @defgroup s8_video_device_implementors_api Video Device Implementors API
+ * @ingroup video_device_api
+ * @brief API for video device implementors
+ * @{
+ */
+
+/**
+ * Video device factory operations.
+ */
+typedef struct pjmedia_vid_dev_factory_op
+{
+    /**
+     * Initialize the video device factory.
+     *
+     * @param f		The video device factory.
+     */
+    pj_status_t (*init)(pjmedia_vid_dev_factory *f);
+
+    /**
+     * Close this video device factory and release all resources back to the
+     * operating system.
+     *
+     * @param f		The video device factory.
+     */
+    pj_status_t (*destroy)(pjmedia_vid_dev_factory *f);
+
+    /**
+     * Get the number of video devices installed in the system.
+     *
+     * @param f		The video device factory.
+     */
+    unsigned (*get_dev_count)(pjmedia_vid_dev_factory *f);
+
+    /**
+     * Get the video device information and capabilities.
+     *
+     * @param f		The video device factory.
+     * @param index	Device index.
+     * @param info	The video device information structure which will be
+     *			initialized by this function once it returns 
+     *			successfully.
+     */
+    pj_status_t	(*get_dev_info)(pjmedia_vid_dev_factory *f, 
+				unsigned index,
+				pjmedia_vid_dev_info *info);
+
+    /**
+     * Initialize the specified video device parameter with the default
+     * values for the specified device.
+     *
+     * @param f		The video device factory.
+     * @param index	Device index.
+     * @param param	The video device parameter.
+     */
+    pj_status_t (*default_param)(pj_pool_t *pool,
+                                 pjmedia_vid_dev_factory *f,
+				 unsigned index,
+				 pjmedia_vid_param *param);
+
+    /**
+     * Open the video device and create video stream. See
+     * #pjmedia_vid_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_factory_op;
+
+
+/**
+ * This structure describes a video device factory. 
+ */
+struct pjmedia_vid_dev_factory
+{
+    /** Internal data to be initialized by video subsystem. */
+    struct {
+	/** Driver index */
+	unsigned drv_idx;
+    } sys;
+
+    /** Operations */
+    pjmedia_vid_dev_factory_op *op;
+};
+
+
+/**
+ * Video stream operations.
+ */
+typedef struct pjmedia_vid_stream_op
+{
+    /**
+     * See #pjmedia_vid_stream_get_param()
+     */
+    pj_status_t (*get_param)(pjmedia_vid_stream *strm,
+			     pjmedia_vid_param *param);
+
+    /**
+     * See #pjmedia_vid_stream_get_cap()
+     */
+    pj_status_t (*get_cap)(pjmedia_vid_stream *strm,
+			   pjmedia_vid_dev_cap cap,
+			   void *value);
+
+    /**
+     * See #pjmedia_vid_stream_set_cap()
+     */
+    pj_status_t (*set_cap)(pjmedia_vid_stream *strm,
+			   pjmedia_vid_dev_cap cap,
+			   const void *value);
+
+    /**
+     * See #pjmedia_vid_stream_start()
+     */
+    pj_status_t (*start)(pjmedia_vid_stream *strm);
+
+    /**
+     * See #pjmedia_vid_stream_get_frame()
+     */
+    pj_status_t (*get_frame)(pjmedia_vid_stream *strm,
+                             pjmedia_frame *frame);
+
+    /**
+     * See #pjmedia_vid_stream_put_frame()
+     */
+    pj_status_t (*put_frame)(pjmedia_vid_stream *strm,
+                             const pjmedia_frame *frame);
+
+    /**
+     * See #pjmedia_vid_stream_stop().
+     */
+    pj_status_t (*stop)(pjmedia_vid_stream *strm);
+
+    /**
+     * See #pjmedia_vid_stream_destroy().
+     */
+    pj_status_t (*destroy)(pjmedia_vid_stream *strm);
+
+} pjmedia_vid_stream_op;
+
+
+/**
+ * This structure describes the video device stream.
+ */
+struct pjmedia_vid_stream
+{
+    /** Internal data to be initialized by video subsystem */
+    struct {
+	/** Driver index */
+	unsigned drv_idx;
+    } sys;
+
+    /** Operations */
+    pjmedia_vid_stream_op *op;
+};
+
+
+
+
+/**
+ * @}
+ */
+
+
+
+#endif /* __VIDEODEV_IMP_H__ */
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index 55e8977..8187b4c 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -1,6 +1,6 @@
 /* $Id$ */
 /* 
- * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -24,19 +24,21 @@
  * @file pjmedia.h
  * @brief PJMEDIA main header file.
  */
-
-#include <pjmedia/types.h>
 #include <pjmedia/alaw_ulaw.h>
+#include <pjmedia/avi_stream.h>
 #include <pjmedia/bidirectional.h>
 #include <pjmedia/circbuf.h>
 #include <pjmedia/clock.h>
 #include <pjmedia/codec.h>
 #include <pjmedia/conference.h>
+#include <pjmedia/converter.h>
 #include <pjmedia/delaybuf.h>
 #include <pjmedia/echo.h>
 #include <pjmedia/echo_port.h>
-#include <pjmedia/errno.h>
 #include <pjmedia/endpoint.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/frame.h>
+#include <pjmedia/format.h>
 #include <pjmedia/g711.h>
 #include <pjmedia/jbuf.h>
 #include <pjmedia/master_port.h>
@@ -64,6 +66,8 @@
 #include <pjmedia/transport_loop.h>
 #include <pjmedia/transport_srtp.h>
 #include <pjmedia/transport_udp.h>
+#include <pjmedia/videoport.h>
+#include <pjmedia/vid_codec.h>
 #include <pjmedia/wav_playlist.h>
 #include <pjmedia/wav_port.h>
 #include <pjmedia/wave.h>
diff --git a/pjmedia/include/pjmedia/avi.h b/pjmedia/include/pjmedia/avi.h
new file mode 100644
index 0000000..685f00e
--- /dev/null
+++ b/pjmedia/include/pjmedia/avi.h
@@ -0,0 +1,202 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2010 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_AVI_H__
+#define __PJMEDIA_AVI_H__
+
+
+/**
+ * @file avi.h
+ * @brief AVI file manipulation.
+ */
+
+/**
+ * @defgroup PJMEDIA_FILE_FORMAT File Formats
+ * @brief Supported file formats
+ */
+
+
+/**
+ * @defgroup PJMEDIA_AVI AVI Header
+ * @ingroup PJMEDIA_FILE_FORMAT
+ * @brief Representation of RIFF/AVI file format
+ * @{
+ *
+ * This the the low level representation of RIFF/AVI file format. For
+ * higher abstraction, please see \ref PJMEDIA_FILE_PLAY and 
+ * \ref PJMEDIA_FILE_REC.
+ */
+
+
+PJ_BEGIN_DECL
+
+#define PJMEDIA_AVI_MAX_NUM_STREAMS 4
+
+static const char avi_tags[][4] = {
+    { 'R', 'I', 'F', 'F' }, { 'A', 'V', 'I', ' ' },
+    { 'h', 'd', 'r', 'l' }, { 'a', 'v', 'i', 'h' },
+    { 's', 't', 'r', 'l' }, { 's', 't', 'r', 'h' },
+    { 'a', 'u', 'd', 's' }, { 'v', 'i', 'd', 's' },
+    { 's', 't', 'r', 'f' }, { 'm', 'o', 'v', 'i' },
+    { 'L', 'I', 'S', 'T' }, { 'J', 'U', 'N', 'K' },
+};
+
+typedef enum {
+    PJMEDIA_AVI_RIFF_TAG = 0,
+    PJMEDIA_AVI_AVI_TAG,
+    PJMEDIA_AVI_HDRL_TAG,
+    PJMEDIA_AVI_AVIH_TAG,
+    PJMEDIA_AVI_STRL_TAG,
+    PJMEDIA_AVI_STRH_TAG,
+    PJMEDIA_AVI_AUDS_TAG,
+    PJMEDIA_AVI_VIDS_TAG,
+    PJMEDIA_AVI_STRF_TAG,
+    PJMEDIA_AVI_MOVI_TAG,
+    PJMEDIA_AVI_LIST_TAG,
+    PJMEDIA_AVI_JUNK_TAG,
+} pjmedia_avi_tag;
+
+
+/**
+ * These types describe the simpler/canonical version of an AVI file.
+ * They do not support the full AVI RIFF format specification.
+ */
+#pragma pack(2)
+
+/** This structure describes RIFF AVI file header */
+typedef struct riff_hdr_t {
+    pj_uint32_t riff;		/**< "RIFF" ASCII tag.		*/
+    pj_uint32_t file_len;       /**< File length minus 8 bytes	*/
+    pj_uint32_t avi;		/**< "AVI" ASCII tag.		*/
+} riff_hdr_t;
+
+/** This structure describes avih header  */
+typedef struct avih_hdr_t {
+    pj_uint32_t list_tag;
+    pj_uint32_t list_sz;
+    pj_uint32_t hdrl_tag;
+    pj_uint32_t avih;
+    pj_uint32_t size;
+    pj_uint32_t msec_per_frame;     /**< microsecs between frames   */
+    pj_uint32_t max_Bps;
+    pj_uint32_t pad;
+    pj_uint32_t flags;
+    pj_uint32_t tot_frames;
+    pj_uint32_t init_frames;
+    pj_uint32_t num_streams;
+    pj_uint32_t buf_size;
+    pj_uint32_t width;
+    pj_uint32_t height;
+    pj_uint32_t reserved[4];
+} avih_hdr_t;
+
+/** This structure describes strl header  */
+typedef struct strl_hdr_t {
+    pj_uint32_t list_tag;
+    pj_uint32_t list_sz;
+    pj_uint32_t strl_tag;
+
+    pj_uint32_t strh;
+    pj_uint32_t strh_size;
+    pj_uint32_t data_type;
+    pj_uint32_t codec;
+    pj_uint32_t flags;
+    pj_uint32_t bogus_priority_language; /**< Do not access this data */
+    pj_uint32_t init_frames;
+    pj_uint32_t scale;
+    pj_uint32_t rate;
+    pj_uint32_t start;
+    pj_uint32_t length;
+    pj_uint32_t buf_size;
+    pj_uint32_t quality;
+    pj_uint32_t sample_size;
+    pj_uint32_t bogus_frame[2];          /**< Do not access this data */
+} strl_hdr_t;
+
+typedef struct {
+    pj_uint32_t strf;
+    pj_uint32_t strf_size;
+    pj_uint16_t fmt_tag;	    /**< 1 for PCM			*/
+    pj_uint16_t nchannels;          /**< Number of channels.	        */
+    pj_uint32_t sample_rate;	    /**< Sampling rate.		        */
+    pj_uint32_t bytes_per_sec;	    /**< Average bytes per second.	*/
+    pj_uint16_t block_align;	    /**< nchannels * bits / 8	        */
+    pj_uint16_t bits_per_sample;    /**< Bits per sample.		*/
+    pj_uint16_t extra_size;
+} strf_audio_hdr_t;
+
+/**
+ * Sizes of strf_audio_hdr_t struct, started by the size (in bytes) of
+ * 32-bits struct members, alternated with the size of 16-bits members.
+ */
+static const pj_uint8_t strf_audio_hdr_sizes [] = {8, 4, 8, 6};
+
+typedef struct {
+    pj_uint32_t strf;
+    pj_uint32_t strf_size;
+    pj_uint32_t biSize; 
+    pj_int32_t biWidth; 
+    pj_int32_t biHeight; 
+    pj_uint16_t biPlanes; 
+    pj_uint16_t biBitCount;
+    pj_uint32_t biCompression; 
+    pj_uint32_t biSizeImage; 
+    pj_int32_t biXPelsPerMeter; 
+    pj_int32_t biYPelsPerMeter; 
+    pj_uint32_t biClrUsed; 
+    pj_uint32_t biClrImportant; 
+} strf_video_hdr_t;
+
+static const pj_uint8_t strf_video_hdr_sizes [] = {20, 4, 24};
+
+struct pjmedia_avi_hdr
+{
+    riff_hdr_t  riff_hdr;
+    avih_hdr_t  avih_hdr;
+    strl_hdr_t  strl_hdr[PJMEDIA_AVI_MAX_NUM_STREAMS];
+    union {
+        strf_audio_hdr_t strf_audio_hdr;
+        strf_video_hdr_t strf_video_hdr;
+    } strf_hdr[PJMEDIA_AVI_MAX_NUM_STREAMS];
+};
+
+#pragma pack()
+
+/**
+ * @see pjmedia_avi_hdr
+ */
+typedef struct pjmedia_avi_hdr pjmedia_avi_hdr;
+
+/**
+ * This structure describes generic RIFF subchunk header.
+ */
+typedef struct pjmedia_avi_subchunk
+{
+    pj_uint32_t	    id;			/**< Subchunk ASCII tag.	    */
+    pj_uint32_t	    len;		/**< Length following this field    */
+} pjmedia_avi_subchunk;
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif	/* __PJMEDIA_AVI_H__ */
diff --git a/pjmedia/include/pjmedia/avi_stream.h b/pjmedia/include/pjmedia/avi_stream.h
new file mode 100644
index 0000000..0c534ce
--- /dev/null
+++ b/pjmedia/include/pjmedia/avi_stream.h
@@ -0,0 +1,132 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2010 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_AVI_STREAM_H__
+#define __PJMEDIA_AVI_STREAM_H__
+
+/**
+ * @file avi_stream.h
+ * @brief AVI file player.
+ */
+#include <pjmedia/port.h>
+
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMEDIA_FILE_PLAY AVI File Player
+ * @ingroup PJMEDIA_PORT
+ * @brief Video and audio playback from AVI file
+ * @{
+ */
+
+/**
+ * AVI file player options.
+ */
+enum pjmedia_avi_file_player_option
+{
+    /**
+     * Tell the file player to return NULL frame when the whole
+     * file has been played.
+     */
+    PJMEDIA_AVI_FILE_NO_LOOP = 1
+};
+
+typedef pjmedia_port pjmedia_avi_stream;
+
+typedef struct pjmedia_avi_streams pjmedia_avi_streams;
+
+/**
+ * Create avi streams to play an AVI file. AVI player supports 
+ * reading AVI file with uncompressed video format and 
+ * 16 bit PCM or compressed G.711 A-law/U-law audio format.
+ *
+ * @param pool		Pool to create the streams.
+ * @param filename	File name to open.
+ * @param flags		Avi streams creation flags.
+ * @param p_streams	Pointer to receive the avi streams instance.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_avi_player_create_streams(pj_pool_t *pool,
+                                  const char *filename,
+                                  unsigned flags,
+                                  pjmedia_avi_streams **p_streams);
+
+PJ_DECL(pj_uint8_t)
+pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams);
+
+PJ_DECL(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams,
+                               pj_uint8_t idx);
+
+PJ_DECL(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams,
+                                        pj_uint8_t start_idx,
+                                        pjmedia_type media_type);
+
+PJ_INLINE(pjmedia_port *)
+pjmedia_avi_stream_get_port(pjmedia_avi_stream *stream)
+{
+    return (pjmedia_port *)stream;
+}
+
+/**
+ * Get the data length, in bytes.
+ *
+ * @param port		The AVI stream.
+ *
+ * @return		The length of the data, in bytes. Upon error it will
+ *			return negative value.
+ */
+PJ_DECL(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream);
+
+
+/**
+ * Register a callback to be called when the file reading has reached the
+ * end of file. If the file is set to play repeatedly, then the callback
+ * will be called multiple times. Note that only one callback can be 
+ * registered for each AVI stream.
+ *
+ * @param port		The AVI stream.
+ * @param user_data	User data to be specified in the callback
+ * @param cb		Callback to be called. If the callback returns non-
+ *			PJ_SUCCESS, the playback will stop. Note that if
+ *			application destroys the file port in the callback,
+ *			it must return non-PJ_SUCCESS here.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_avi_stream_set_eof_cb(pjmedia_avi_stream *stream,
+			      void *user_data,
+			      pj_status_t (*cb)(pjmedia_avi_stream *stream,
+					        void *usr_data));
+
+/**
+ * @}
+ */
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_AVI_STREAM_H__ */
diff --git a/pjmedia/include/pjmedia/circbuf.h b/pjmedia/include/pjmedia/circbuf.h
index 708efba..ffbc41b 100644
--- a/pjmedia/include/pjmedia/circbuf.h
+++ b/pjmedia/include/pjmedia/circbuf.h
@@ -29,6 +29,7 @@
 #include <pj/assert.h>
 #include <pj/errno.h>
 #include <pj/pool.h>
+#include <pjmedia/frame.h>
 
 /**
  * @defgroup PJMED_CIRCBUF Circular Buffer
diff --git a/pjmedia/include/pjmedia/clock.h b/pjmedia/include/pjmedia/clock.h
index 2784838..8ca28f9 100644
--- a/pjmedia/include/pjmedia/clock.h
+++ b/pjmedia/include/pjmedia/clock.h
@@ -118,7 +118,12 @@
 
 
 /**
- * Create media clock.
+ * Create media clock. This creates a media clock object that will run
+ * periodically at an interval that is calculated from the audio parameters.
+ * Once created, application must call #pjmedia_clock_start() to actually
+ * start the clock.
+ *
+ * @see pjmedia_clock_create2()
  *
  * @param pool		    Pool to allocate memory.
  * @param clock_rate	    Number of samples per second.
@@ -143,6 +148,32 @@
 					   void *user_data,
 					   pjmedia_clock **p_clock);
 
+
+/**
+ * Create media clock. This creates a media clock object that will run
+ * periodically at the specified interval. Once created, application must
+ * call #pjmedia_clock_start() to actually start the clock.
+ *
+ * @param pool		    Pool to allocate memory.
+ * @param usec_interval	    The frame interval, in microseconds.
+ * @param clock_rate	    The media clock rate, to determine timestamp
+ * 			    increment for each call.
+ * @param options	    Bitmask of pjmedia_clock_options.
+ * @param cb		    Callback to be called for each clock tick.
+ * @param user_data	    User data, which will be passed to the callback.
+ * @param p_clock	    Pointer to receive the clock instance.
+ *
+ * @return		    PJ_SUCCESS on success, or the appropriate error
+ *			    code.
+ */
+PJ_DECL(pj_status_t) pjmedia_clock_create2(pj_pool_t *pool,
+					   unsigned usec_interval,
+					   unsigned clock_rate,
+					   unsigned options,
+					   pjmedia_clock_callback *cb,
+					   void *user_data,
+					   pjmedia_clock **p_clock);
+
 /**
  * Start the clock. For clock created with asynchronous flag set to TRUE,
  * this may start a worker thread for the clock (depending on the 
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 4d4f7ed..8491297 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -880,6 +880,93 @@
 #endif
 
 
+/*
+ * .... new stuffs ...
+ */
+
+/**
+ * Specify if FFMPEG is available. The value here will be used as the default
+ * value for other FFMPEG settings below.
+ *
+ * Default: 0
+ */
+#ifndef PJMEDIA_HAS_FFMPEG
+#   define PJMEDIA_HAS_FFMPEG				0
+#endif
+
+/**
+ * Specify if FFMPEG libavformat is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVFORMAT
+#   define PJMEDIA_HAS_LIBAVFORMAT			PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libavformat is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVCODEC
+#   define PJMEDIA_HAS_LIBAVCODEC			PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libavutil is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVUTIL
+#   define PJMEDIA_HAS_LIBAVUTIL			PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libswscale is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBSWSCALE
+#   define PJMEDIA_HAS_LIBSWSCALE			PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libavdevice is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVDEVICE
+#   define PJMEDIA_HAS_LIBAVDEVICE			PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Specify if FFMPEG libavcore is available.
+ *
+ * Default: PJMEDIA_HAS_FFMPEG (or detected by configure)
+ */
+#ifndef PJMEDIA_HAS_LIBAVCORE
+#   define PJMEDIA_HAS_LIBAVCORE			PJMEDIA_HAS_FFMPEG
+#endif
+
+/**
+ * Maximum video planes.
+ *
+ * Default: 4
+ */
+#ifndef PJMEDIA_MAX_VIDEO_PLANES
+#   define PJMEDIA_MAX_VIDEO_PLANES			4
+#endif
+
+/**
+ * Maximum number of video formats.
+ *
+ * Default: 32
+ */
+#ifndef PJMEDIA_MAX_VIDEO_FORMATS
+#   define PJMEDIA_MAX_VIDEO_FORMATS			32
+#endif
+
+
 /**
  * @}
  */
diff --git a/pjmedia/include/pjmedia/converter.h b/pjmedia/include/pjmedia/converter.h
new file mode 100644
index 0000000..8a9dd11
--- /dev/null
+++ b/pjmedia/include/pjmedia/converter.h
@@ -0,0 +1,322 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-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_CONVERTER_H__
+#define __PJMEDIA_CONVERTER_H__
+
+
+/**
+ * @file pjmedia/converter.h Format conversion utilities
+ * @brief Format conversion utilities
+ */
+
+#include <pjmedia/frame.h>
+#include <pjmedia/format.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+
+
+/**
+ * @defgroup PJMEDIA_CONVERTER Format converter
+ * @ingroup PJMEDIA_FRAME_OP
+ * @brief Audio and video converter utilities
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * This describes conversion parameter. It specifies the source and
+ * destination formats of the conversion.
+ */
+typedef struct pjmedia_conversion_param
+{
+    pjmedia_format	src;	/**< Source format.		*/
+    pjmedia_format	dst;	/**< Destination format.	*/
+} pjmedia_conversion_param;
+
+
+/** Forward declaration of factory operation structure */
+typedef struct pjmedia_converter_factory_op pjmedia_converter_factory_op;
+
+/**
+ * Converter priority guides. Converter priority determines which converter
+ * instance to be used if more than one converters are able to perform the
+ * requested conversion. Converter implementor can use this value to order
+ * the preference based on attributes such as quality or performance. Higher
+ * number indicates higher priority.
+ */
+typedef enum pjmedia_converter_priority_guide
+{
+    /** Lowest priority. */
+    PJMEDIA_CONVERTER_PRIORITY_LOWEST 		= 0,
+
+    /** Normal priority. */
+    PJMEDIA_CONVERTER_PRIORITY_NORMAL 		= 15000,
+
+    /** Highest priority. */
+    PJMEDIA_CONVERTER_PRIORITY_HIGHEST 		= 32000
+} pjmedia_converter_priority_guide;
+
+/**
+ * Converter factory. The converter factory registers a callback function
+ * to create converters.
+ */
+typedef struct pjmedia_converter_factory
+{
+    /**
+     * Standard list members.
+     */
+    PJ_DECL_LIST_MEMBER(struct pjmedia_converter_factory);
+
+    /**
+     * Factory name.
+     */
+    const char 			 *name;
+
+    /**
+     * Converter priority determines which converter instance to be used if
+     * more than one converters are able to perform the requested conversion.
+     * Converter implementor can use this value to order the preference based
+     * on attributes such as quality or performance. Higher number indicates
+     * higher priority. The pjmedia_converter_priority_guide enumeration shall
+     * be used as the base value to set the priority.
+     */
+    int priority;
+
+    /**
+     * Pointer to factory operation.
+     */
+    pjmedia_converter_factory_op *op;
+
+} pjmedia_converter_factory;
+
+/** Forward declaration for converter operation. */
+typedef struct pjmedia_converter_op pjmedia_converter_op;
+
+/**
+ * This structure describes a converter instance.
+ */
+typedef struct pjmedia_converter
+{
+    /**
+     * Pointer to converter operation.
+     */
+    pjmedia_converter_op *op;
+
+} pjmedia_converter;
+
+
+/**
+ * Converter factory operation.
+ */
+struct pjmedia_converter_factory_op
+{
+    /**
+     * This function creates a converter with the specified conversion format,
+     * if such format is supported.
+     *
+     * @param cf	The converter factory.
+     * @param pool	Pool to allocate memory from.
+     * @param prm	Conversion parameter.
+     * @param p_cv	Pointer to hold the created converter instance.
+     *
+     * @return		PJ_SUCCESS if converter has been created successfully.
+     */
+    pj_status_t (*create_converter)(pjmedia_converter_factory *cf,
+				    pj_pool_t *pool,
+				    const pjmedia_conversion_param *prm,
+				    pjmedia_converter **p_cv);
+
+    /**
+     * Destroy the factory.
+     *
+     * @param cf	The converter factory.
+     */
+    void (*destroy_factory)(pjmedia_converter_factory *cf);
+};
+
+/**
+ * Converter operation.
+ */
+struct pjmedia_converter_op
+{
+    /**
+     * Convert the buffer in the source frame and save the result in the
+     * buffer of the destination frame, according to conversion format that
+     * was specified when the converter was created.
+     *
+     * Note that application should use #pjmedia_converter_convert() instead
+     * of calling this function directly.
+     *
+     * @param cv	The converter instance.
+     * @param src_frame	The source frame.
+     * @param dst_frame	The destination frame.
+     *
+     * @return		PJ_SUCCESS if conversion has been performed
+     * 			successfully.
+     */
+    pj_status_t (*convert)(pjmedia_converter *cv,
+			   pjmedia_frame *src_frame,
+			   pjmedia_frame *dst_frame);
+
+    /**
+     * Destroy the converter instance.
+     *
+     * Note that application should use #pjmedia_converter_destroy() instead
+     * of calling this function directly.
+     *
+     * @param cv	The converter.
+     */
+    void (*destroy)(pjmedia_converter *cv);
+
+};
+
+
+/**
+ * Opaque data type for conversion manager. Typically, the conversion manager
+ * is a singleton instance, although application may instantiate more than one
+ * instances of this if required.
+ */
+typedef struct pjmedia_converter_mgr pjmedia_converter_mgr;
+
+
+/**
+ * Create a new conversion manager instance. This will also set the pointer
+ * to the singleton instance if the value is still NULL.
+ *
+ * @param pool		Pool to allocate memory from.
+ * @param mgr		Pointer to hold the created instance of the
+ * 			conversion manager.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool,
+						  pjmedia_converter_mgr **mgr);
+
+/**
+ * Get the singleton instance of the conversion manager.
+ *
+ * @return		The instance.
+ */
+PJ_DECL(pjmedia_converter_mgr*) pjmedia_converter_mgr_instance(void);
+
+/**
+ * Manually assign a specific video manager instance as the singleton
+ * instance. Normally this is not needed if only one instance is ever
+ * going to be created, as the library automatically assign the singleton
+ * instance.
+ *
+ * @param mgr		The instance to be used as the singleton instance.
+ * 			Application may specify NULL to clear the singleton
+ * 			singleton instance.
+ */
+PJ_DECL(void) pjmedia_converter_mgr_set_instance(pjmedia_converter_mgr *mgr);
+
+/**
+ * Destroy a converter manager. If the manager happens to be the singleton
+ * instance, the singleton instance will be set to NULL.
+ *
+ * @param mgr		The converter manager. Specify NULL to use
+ * 			the singleton instance.
+ */
+PJ_DECL(void) pjmedia_converter_mgr_destroy(pjmedia_converter_mgr *mgr);
+
+/**
+ * Register a converter factory to the converter manager.
+ *
+ * @param mgr		The converter manager. Specify NULL to use
+ * 			the singleton instance.
+ * @param f		The converter factory to be registered.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_converter_mgr_register_factory(pjmedia_converter_mgr *mgr,
+				       pjmedia_converter_factory *f);
+
+/**
+ * Unregister a previously registered converter factory from the converter
+ * manager.
+ *
+ * @param mgr		The converter manager. Specify NULL to use
+ * 			the singleton instance.
+ * @param f		The converter factory to be unregistered.
+ * @param call_destroy	If this is set to non-zero, the \a destroy_factory()
+ * 			callback of the factory will be called while
+ * 			unregistering the factory from the manager.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_converter_mgr_unregister_factory(pjmedia_converter_mgr *mgr,
+				         pjmedia_converter_factory *f,
+				         pj_bool_t call_destroy);
+
+/**
+ * Create a converter instance to perform the specified format conversion
+ * as specified in \a param.
+ *
+ * @param mgr		The converter manager. Specify NULL to use
+ * 			the singleton instance.
+ * @param pool		Pool to allocate the memory from.
+ * @param param		Conversion parameter.
+ * @param p_cv		Pointer to hold the created converter.
+ *
+ * @return		PJ_SUCCESS if a converter has been created successfully
+ * 			or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_converter_create(pjmedia_converter_mgr *mgr,
+					      pj_pool_t *pool,
+					      pjmedia_conversion_param *param,
+					      pjmedia_converter **p_cv);
+
+/**
+ * Convert the buffer in the source frame and save the result in the
+ * buffer of the destination frame, according to conversion format that
+ * was specified when the converter was created.
+ *
+ * @param cv		The converter instance.
+ * @param src_frame	The source frame.
+ * @param dst_frame	The destination frame.
+ *
+ * @return		PJ_SUCCESS if conversion has been performed
+ * 			successfully.
+ */
+PJ_DECL(pj_status_t) pjmedia_converter_convert(pjmedia_converter *cv,
+					       pjmedia_frame *src_frame,
+					       pjmedia_frame *dst_frame);
+
+/**
+ * Destroy the converter.
+ *
+ * @param cv		The converter instance.
+ */
+PJ_DECL(void) pjmedia_converter_destroy(pjmedia_converter *cv);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+
+#endif /* __PJMEDIA_CONVERTER_H__ */
+
+
diff --git a/pjmedia/include/pjmedia/endpoint.h b/pjmedia/include/pjmedia/endpoint.h
index 725c975..6d12923 100644
--- a/pjmedia/include/pjmedia/endpoint.h
+++ b/pjmedia/include/pjmedia/endpoint.h
@@ -39,6 +39,7 @@
 
 #include <pjmedia/codec.h>
 #include <pjmedia/sdp.h>
+#include <pjmedia/transport.h>
 
 
 PJ_BEGIN_DECL
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index 362936b..af6a7df 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -391,7 +391,11 @@
  * Remote does not support RFC 2833
  */
 #define PJMEDIA_RTP_EREMNORFC2833   (PJMEDIA_ERRNO_START+107)    /* 220107 */
-
+/**
+ * @hideinitializer
+ * Invalid or bad format
+ */
+#define PJMEDIA_EBADFMT             (PJMEDIA_ERRNO_START+108)    /* 220108 */
 
 
 /************************************************************
@@ -517,6 +521,11 @@
  * Sound frame is too large for file buffer.
  */
 #define PJMEDIA_EFRMFILETOOBIG	    (PJMEDIA_ERRNO_START+183)    /* 220183 */
+/**
+ * @hideinitializer
+ * Unsupported AVI file.
+ */
+#define PJMEDIA_EAVIUNSUPP	    (PJMEDIA_ERRNO_START+191)    /* 220191 */
 
 
 /************************************************************
diff --git a/pjmedia/include/pjmedia/format.h b/pjmedia/include/pjmedia/format.h
new file mode 100644
index 0000000..2487340
--- /dev/null
+++ b/pjmedia/include/pjmedia/format.h
@@ -0,0 +1,706 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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_FORMAT_H__
+#define __PJMEDIA_FORMAT_H__
+
+/**
+ * @file pjmedia/format.h Media format
+ * @brief Media format
+ */
+#include <pjmedia/types.h>
+
+/**
+ * @defgroup PJMEDIA_FORMAT Media format
+ * @ingroup PJMEDIA_TYPES
+ * @brief Media format
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * Macro for packing format from a four character code, similar to FOURCC.
+ * This macro is used for building the constants in pjmedia_format_id
+ * enumeration.
+ */
+#define PJMEDIA_FORMAT_PACK(C1, C2, C3, C4) ( C4<<24 | C3<<16 | C2<<8 | C1 )
+
+/**
+ * This enumeration uniquely identify audio sample and/or video pixel formats.
+ * Some well known formats are listed here. The format ids are built by
+ * combining four character codes, similar to FOURCC. The format id is
+ * extensible, as application may define and use format ids not declared
+ * on this enumeration.
+ *
+ * This format id along with other information will fully describe the media
+ * in #pjmedia_format structure.
+ */
+typedef enum pjmedia_format_id
+{
+    /*
+     * Audio formats
+     */
+
+    /** 16bit signed integer linear PCM audio */
+    PJMEDIA_FORMAT_L16	    = 0,
+
+    /** Alias for PJMEDIA_FORMAT_L16 */
+    PJMEDIA_FORMAT_PCM	    = PJMEDIA_FORMAT_L16,
+
+    /** G.711 ALAW */
+    PJMEDIA_FORMAT_PCMA	    = PJMEDIA_FORMAT_PACK('A', 'L', 'A', 'W'),
+
+    /** Alias for PJMEDIA_FORMAT_PCMA */
+    PJMEDIA_FORMAT_ALAW	    = PJMEDIA_FORMAT_PCMA,
+
+    /** G.711 ULAW */
+    PJMEDIA_FORMAT_PCMU	    = PJMEDIA_FORMAT_PACK('u', 'L', 'A', 'W'),
+
+    /** Aliaw for PJMEDIA_FORMAT_PCMU */
+    PJMEDIA_FORMAT_ULAW	    = PJMEDIA_FORMAT_PCMU,
+
+    /** AMR narrowband */
+    PJMEDIA_FORMAT_AMR	    = PJMEDIA_FORMAT_PACK(' ', 'A', 'M', 'R'),
+
+    /** ITU G.729 */
+    PJMEDIA_FORMAT_G729	    = PJMEDIA_FORMAT_PACK('G', '7', '2', '9'),
+
+    /** Internet Low Bit-Rate Codec (ILBC) */
+    PJMEDIA_FORMAT_ILBC	    = PJMEDIA_FORMAT_PACK('I', 'L', 'B', 'C'),
+
+
+    /*
+     * Video formats.
+     */
+    /**
+     * 24bit RGB
+     */
+    PJMEDIA_FORMAT_RGB24    = PJMEDIA_FORMAT_PACK('R', 'G', 'B', '3'),
+
+    /**
+     * 32bit RGB with alpha channel
+     */
+    PJMEDIA_FORMAT_RGBA     = PJMEDIA_FORMAT_PACK('R', 'G', 'B', 'A'),
+    PJMEDIA_FORMAT_BGRA     = PJMEDIA_FORMAT_PACK('B', 'G', 'R', 'A'),
+
+    /**
+     * Alias for PJMEDIA_FORMAT_RGBA
+     */
+    PJMEDIA_FORMAT_RGB32    = PJMEDIA_FORMAT_RGBA,
+
+    /**
+     * Device Independent Bitmap, alias for 24 bit RGB
+     */
+    PJMEDIA_FORMAT_DIB      = PJMEDIA_FORMAT_PACK('D', 'I', 'B', ' '),
+
+    /**
+     * This is a packed 4:4:4/32bpp format, where each pixel is encoded as
+     * four consecutive bytes, arranged in the following sequence: V0, U0,
+     * Y0, A0. Source:
+     * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#ayuv
+     */
+    PJMEDIA_FORMAT_AYUV	    = PJMEDIA_FORMAT_PACK('A', 'Y', 'U', 'V'),
+
+    /**
+     * This is packed 4:2:2/16bpp YUV format, the data can be treated as
+     * an array of unsigned char values, where the first byte contains
+     * the first Y sample, the second byte contains the first U (Cb) sample,
+     * the third byte contains the second Y sample, and the fourth byte
+     * contains the first V (Cr) sample, and so forth. Source:
+     * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#yuy2
+     */
+    PJMEDIA_FORMAT_YUY2	    = PJMEDIA_FORMAT_PACK('Y', 'U', 'Y', '2'),
+
+    /**
+     * This format is the same as the YUY2 format except the byte order is
+     * reversed -- that is, the chroma and luma bytes are flipped. If the
+     * image is addressed as an array of two little-endian WORD values, the
+     * first WORD contains U in the LSBs and Y0 in the MSBs, and the second
+     * WORD contains V in the LSBs and Y1 in the MSBs. Source:
+     * http://msdn.microsoft.com/en-us/library/dd206750%28v=VS.85%29.aspx#uyvy
+     */
+    PJMEDIA_FORMAT_UYVY	    = PJMEDIA_FORMAT_PACK('U', 'Y', 'V', 'Y'),
+
+    /**
+     * This format is the same as the YUY2 and UYVY format except the byte
+     * order is reversed -- that is, the chroma and luma bytes are flipped.
+     * If the image is addressed as an array of two little-endian WORD values,
+     * the first WORD contains Y0 in the LSBs and V in the MSBs, and the second
+     * WORD contains Y1 in the LSBs and U in the MSBs.
+     */
+    PJMEDIA_FORMAT_YVYU	    = PJMEDIA_FORMAT_PACK('Y', 'V', 'Y', 'U'),
+
+    /**
+     * This is planar 4:2:0/12bpp YUV format, the data can be treated as
+     * three planes of color components, where the first plane contains
+     * only the Y samples, the second plane contains only the U (Cb) samples,
+     * and the third plane contains only the V (Cr) sample.
+     */
+    PJMEDIA_FORMAT_I420	    = PJMEDIA_FORMAT_PACK('I', '4', '2', '0'),
+
+    /**
+     * IYUV is alias for I420.
+     */
+    PJMEDIA_FORMAT_IYUV	    = PJMEDIA_FORMAT_I420,
+
+    /**
+     * This is planar 4:2:2/16bpp YUV format.
+     */
+    PJMEDIA_FORMAT_YV12	    = PJMEDIA_FORMAT_PACK('Y', 'V', '1', '2'),
+
+    /**
+     * The JPEG version of planar 4:2:0/12bpp YUV format.
+     */
+    PJMEDIA_FORMAT_I420JPEG = PJMEDIA_FORMAT_PACK('J', '4', '2', '0'),
+
+    /**
+     * The JPEG version of planar 4:2:2/16bpp YUV format.
+     */
+    PJMEDIA_FORMAT_I422JPEG = PJMEDIA_FORMAT_PACK('J', '4', '2', '2'),
+
+    /**
+     * Encoded video formats
+     */
+
+    PJMEDIA_FORMAT_H261     = PJMEDIA_FORMAT_PACK('H', '2', '6', '1'),
+    PJMEDIA_FORMAT_H263     = PJMEDIA_FORMAT_PACK('H', '2', '6', '3'),
+
+    PJMEDIA_FORMAT_MJPEG    = PJMEDIA_FORMAT_PACK('M', 'J', 'P', 'G'),
+    PJMEDIA_FORMAT_MPEG1VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '1', 'V'),
+    PJMEDIA_FORMAT_MPEG2VIDEO = PJMEDIA_FORMAT_PACK('M', 'P', '2', 'V'),
+    PJMEDIA_FORMAT_MPEG4    = PJMEDIA_FORMAT_PACK('M', 'P', 'G', '4'),
+
+} pjmedia_format_id;
+
+/**
+ * This enumeration specifies what type of detail is included in a
+ * #pjmedia_format structure.
+ */
+typedef enum pjmedia_format_detail_type
+{
+    /** Format detail is not specified. */
+    PJMEDIA_FORMAT_DETAIL_NONE,
+
+    /** Audio format detail. */
+    PJMEDIA_FORMAT_DETAIL_AUDIO,
+
+    /** Video format detail. */
+    PJMEDIA_FORMAT_DETAIL_VIDEO,
+
+    /** Number of format detail type that has been defined. */
+    PJMEDIA_FORMAT_DETAIL_MAX
+
+} pjmedia_format_detail_type;
+
+/**
+ * This structure is put in \a detail field of #pjmedia_format to describe
+ * detail information about an audio media.
+ */
+typedef struct pjmedia_audio_format_detail
+{
+    unsigned	clock_rate;	/**< Audio clock rate in samples or Hz. */
+    unsigned	channel_count;	/**< Number of channels.		*/
+    unsigned	frame_time_usec;/**< Frame interval, in microseconds.	*/
+    unsigned	bits_per_sample;/**< Number of bits per sample.		*/
+    pj_uint32_t	avg_bps;	/**< Average bitrate			*/
+    pj_uint32_t	max_bps;	/**< Maximum bitrate			*/
+} pjmedia_audio_format_detail;
+
+/**
+ * This structure is put in \a detail field of #pjmedia_format to describe
+ * detail information about a video media.
+ *
+ * Additional information about a video format can also be retrieved by
+ * calling #pjmedia_get_video_format_info().
+ */
+typedef struct pjmedia_video_format_detail
+{
+    pjmedia_rect_size	size;	/**< Video size (width, height) 	*/
+    pjmedia_ratio	fps;	/**< Number of frames per second.	*/
+    pj_uint32_t		avg_bps;/**< Average bitrate.			*/
+    pj_uint32_t		max_bps;/**< Maximum bitrate.			*/
+} pjmedia_video_format_detail;
+
+/**
+ * This macro declares the size of the detail section in #pjmedia_format
+ * to be reserved for user defined detail.
+ */
+#ifndef PJMEDIA_FORMAT_DETAIL_USER_SIZE
+#   define PJMEDIA_FORMAT_DETAIL_USER_SIZE		1
+#endif
+
+/**
+ * This structure contains all the information needed to completely describe
+ * a media.
+ */
+typedef struct pjmedia_format
+{
+    /**
+     * The format id that specifies the audio sample or video pixel format.
+     * Some well known formats ids are declared in pjmedia_format_id
+     * enumeration.
+     *
+     * @see pjmedia_format_id
+     */
+    pj_uint32_t		 	 id;
+
+    /**
+     * The top-most type of the media, as an information.
+     */
+    pjmedia_type		 type;
+
+    /**
+     * The type of detail structure in the \a detail pointer.
+     */
+    pjmedia_format_detail_type	 detail_type;
+
+    /**
+     * Detail section to describe the media.
+     */
+    union
+    {
+	/**
+	 * Detail section for audio format.
+	 */
+	pjmedia_audio_format_detail	aud;
+
+	/**
+	 * Detail section for video format.
+	 */
+	pjmedia_video_format_detail	vid;
+
+	/**
+	 * Reserved area for user-defined format detail.
+	 */
+	char				user[PJMEDIA_FORMAT_DETAIL_USER_SIZE];
+    } det;
+
+} pjmedia_format;
+
+/**
+ * This enumeration describes video color model. It mostly serves as
+ * information only.
+ */
+typedef enum pjmedia_color_model
+{
+    /** The color model is unknown or unspecified. */
+    PJMEDIA_COLOR_MODEL_NONE,
+
+    /** RGB color model. */
+    PJMEDIA_COLOR_MODEL_RGB,
+
+    /** YUV color model. */
+    PJMEDIA_COLOR_MODEL_YUV
+} pjmedia_color_model;
+
+/**
+ * This structure holds information to apply a specific video format
+ * against size and buffer information, and get additional information
+ * from it. To do that, application fills up the input fields of this
+ * structure, and give this structure to \a apply_fmt() function
+ * of #pjmedia_video_format_info structure.
+ */
+typedef struct pjmedia_video_apply_fmt_param
+{
+    /* input fields: */
+
+    /**
+     * [IN] The image size. This field is mandatory, and has to be set
+     * correctly prior to calling \a apply_fmt() function.
+     */
+    pjmedia_rect_size	 size;
+
+    /**
+     * [IN] Pointer to the buffer that holds the frame. The \a apply_fmt()
+     * function uses this pointer to calculate the pointer for each video
+     * planes of the media. This field is optional -- however, the
+     * \a apply_fmt() would still fill up the \a planes[] array with the
+     * correct pointer even though the buffer is set to NULL. This could be
+     * useful to calculate the size (in bytes) of each plane.
+     */
+    pj_uint8_t		*buffer;
+
+    /* output fields: */
+
+    /**
+     * [OUT] The size (in bytes) required of the buffer to hold the video
+     * frame of the particular frame size (width, height).
+     */
+    pj_size_t		 framebytes;
+
+    /**
+     * [OUT] Array of strides value (in bytes) for each video plane.
+     */
+    int		         strides[PJMEDIA_MAX_VIDEO_PLANES];
+
+    /**
+     * [OUT] Array of pointers to each of the video planes. The values are
+     * calculated from the \a buffer field.
+     */
+    pj_uint8_t		*planes[PJMEDIA_MAX_VIDEO_PLANES];
+
+    /**
+     * [OUT] Array of video plane sizes.
+     */
+    pj_size_t		 plane_bytes[PJMEDIA_MAX_VIDEO_PLANES];
+
+} pjmedia_video_apply_fmt_param;
+
+/**
+ * This structure holds information to describe a video format. Application
+ * can retrieve this structure by calling #pjmedia_get_video_format_info()
+ * funcion.
+ */
+typedef struct pjmedia_video_format_info
+{
+    /**
+     * The unique format ID of the media. Well known format ids are declared
+     * in pjmedia_format_id enumeration.
+     */
+    pj_uint32_t		id;
+
+    /**
+     * Null terminated string containing short identification about the
+     * format.
+     */
+    char		name[8];
+
+    /**
+     * Information about the color model of this video format.
+     */
+    pjmedia_color_model	color_model;
+
+    /**
+     * Number of bits needed to store one pixel of this video format.
+     */
+    pj_uint8_t		bpp;
+
+    /**
+     * Number of video planes that this format uses. Value 1 indicates
+     * packed format, while value greater than 1 indicates planar format.
+     */
+    pj_uint8_t		plane_cnt;
+
+    /**
+     * Pointer to function to apply this format against size and buffer
+     * information in pjmedia_video_apply_fmt_param argument. Application
+     * uses this function to obtain various information such as the
+     * memory size of a frame buffer, strides value of the image, the
+     * location of the planes, and so on. See pjmedia_video_apply_fmt_param
+     * for additional information.
+     *
+     * @param vfi	The video format info.
+     * @param vafp	The parameters to investigate.
+     *
+     * @return		PJ_SUCCESS if the function has calculated the
+     * 			information in \a vafp successfully.
+     */
+    pj_status_t (*apply_fmt)(const struct pjmedia_video_format_info *vfi,
+	                     pjmedia_video_apply_fmt_param *vafp);
+
+} pjmedia_video_format_info;
+
+
+/*****************************************************************************
+ * UTILITIES:
+ */
+
+/**
+ * General utility routine to calculate samples per frame value from clock
+ * rate, ptime (in usec), and channel count. Application should use this
+ * macro whenever possible due to possible overflow in the math calculation.
+ *
+ * @param clock_rate		Clock rate.
+ * @param usec_ptime		Frame interval, in microsecond.
+ * @param channel_count		Number of channels.
+ *
+ * @return			The samples per frame value.
+ */
+PJ_INLINE(unsigned) PJMEDIA_SPF(unsigned clock_rate, unsigned usec_ptime,
+				unsigned channel_count)
+{
+#if PJ_HAS_INT64
+    return ((unsigned)((pj_uint64_t)usec_ptime * \
+		       clock_rate / channel_count / 1000000));
+#elif PJ_HAS_FLOATING_POINT
+    return ((unsigned)(usec_ptime * clock_rate / channel_count / 1000000.0));
+#else
+    return ((unsigned)(usec_ptime / 1000L * clock_rate / \
+		       channel_count / 1000));
+#endif
+}
+
+/**
+ * Utility routine to calculate frame size (in bytes) from bitrate and frame
+ * interval values. Application should use this macro whenever possible due
+ * to possible overflow in the math calculation.
+ *
+ * @param bps			The bitrate of the stream.
+ * @param usec_ptime		Frame interval, in microsecond.
+ *
+ * @return			Frame size in bytes.
+ */
+PJ_INLINE(unsigned) PJMEDIA_FSZ(unsigned bps, unsigned usec_ptime)
+{
+#if PJ_HAS_INT64
+    return ((unsigned)(bps * usec_ptime / PJ_UINT64(8000000)));
+#elif PJ_HAS_FLOATING_POINT
+    return ((unsigned)(bps * usec_ptime / 8000000.0));
+#else
+    return ((unsigned)(bps / 8L * usec_ptime / 1000000));
+#endif
+}
+
+
+/**
+ * Utility to retrieve samples_per_frame value from
+ * pjmedia_audio_format_detail.
+ *
+ * @param pafd		Pointer to pjmedia_audio_format_detail
+ * @return		Samples per frame
+ */
+PJ_INLINE(unsigned) PJMEDIA_AFD_SPF(const pjmedia_audio_format_detail *pafd)
+{
+    return PJMEDIA_SPF(pafd->clock_rate, pafd->frame_time_usec,
+		       pafd->channel_count);
+}
+
+/**
+ * Utility to retrieve average frame size from pjmedia_audio_format_detail.
+ * The average frame size is derived from the average bitrate of the audio
+ * stream.
+ *
+ * @param afd		Pointer to pjmedia_audio_format_detail
+ * @return		Average frame size.
+ */
+PJ_INLINE(unsigned) PJMEDIA_AFD_AVG_FSZ(const pjmedia_audio_format_detail *afd)
+{
+    return PJMEDIA_FSZ(afd->avg_bps, afd->frame_time_usec);
+}
+
+/**
+ * Utility to retrieve maximum frame size from pjmedia_audio_format_detail.
+ * The maximum frame size is derived from the maximum bitrate of the audio
+ * stream.
+ *
+ * @param afd		Pointer to pjmedia_audio_format_detail
+ * @return		Average frame size.
+ */
+PJ_INLINE(unsigned) PJMEDIA_AFD_MAX_FSZ(const pjmedia_audio_format_detail *afd)
+{
+    return PJMEDIA_FSZ(afd->max_bps, afd->frame_time_usec);
+}
+
+
+/**
+ * Initialize the format as audio format with the specified parameters.
+ *
+ * @param fmt			The format to be initialized.
+ * @param fmt_id		Format ID. See #pjmedia_format_id
+ * @param clock_rate		Audio clock rate.
+ * @param channel_count		Number of channels.
+ * @param bits_per_sample	Number of bits per sample.
+ * @param frame_time_usec	Frame interval, in microsecond.
+ * @param avg_bps		Average bitrate.
+ * @param max_bps		Maximum bitrate.
+ */
+PJ_DECL(void) pjmedia_format_init_audio(pjmedia_format *fmt,
+				        pj_uint32_t fmt_id,
+					unsigned clock_rate,
+					unsigned channel_count,
+					unsigned bits_per_sample,
+					unsigned frame_time_usec,
+					pj_uint32_t avg_bps,
+					pj_uint32_t max_bps);
+
+/**
+ * Initialize the format as video format with the specified parameters.
+ * A format manager should have been created, as this function will need
+ * to consult to a format manager in order to fill in detailed
+ * information about the format.
+ *
+ * @param fmt		The format to be initialised.
+ * @param fmt_id	Format ID. See #pjmedia_format_id
+ * @param width		Image width.
+ * @param height	Image heigth.
+ * @param fps_num	FPS numerator.
+ * @param fps_denum	FPS denumerator.
+ * @param avg_bps	Average bitrate.
+ * @param max_bps	Maximum bitrate.
+ */
+PJ_DECL(void) pjmedia_format_init_video(pjmedia_format *fmt,
+					pj_uint32_t fmt_id,
+					unsigned width,
+					unsigned height,
+					unsigned fps_num,
+					unsigned fps_denum);
+
+/**
+ * Copy format to another.
+ *
+ * @param dst		The destination format.
+ * @param src		The source format.
+ *
+ * @return		Pointer to destination format.
+ */
+PJ_DECL(pjmedia_format*) pjmedia_format_copy(pjmedia_format *dst,
+					     const pjmedia_format *src);
+
+/**
+ * Check if the format contains audio format, and retrieve the audio format
+ * detail in the format.
+ *
+ * @param fmt		The format structure.
+ * @param assert_valid	If this is set to non-zero, an assertion will be
+ * 			raised if the detail type is not audio or if the
+ * 			the detail is NULL.
+ *
+ * @return		The instance of audio format detail in the format
+ * 			structure, or NULL if the format doesn't contain
+ * 			audio detail.
+ */
+PJ_DECL(pjmedia_audio_format_detail*)
+pjmedia_format_get_audio_format_detail(const pjmedia_format *fmt,
+				       pj_bool_t assert_valid);
+
+/**
+ * Check if the format contains video format, and retrieve the video format
+ * detail in the format.
+ *
+ * @param fmt		The format structure.
+ * @param assert_valid	If this is set to non-zero, an assertion will be
+ * 			raised if the detail type is not video or if the
+ * 			the detail is NULL.
+ *
+ * @return		The instance of video format detail in the format
+ * 			structure, or NULL if the format doesn't contain
+ * 			video detail.
+ */
+PJ_DECL(pjmedia_video_format_detail*)
+pjmedia_format_get_video_format_detail(const pjmedia_format *fmt,
+				       pj_bool_t assert_valid);
+
+/*****************************************************************************
+ * FORMAT MANAGEMENT:
+ */
+
+/**
+ * Opaque data type for video format manager. The video format manager manages
+ * the repository of video formats that the framework recognises. Typically it
+ * is a singleton instance, although application may instantiate more than one
+ * instances of this if required.
+ */
+typedef struct pjmedia_video_format_mgr pjmedia_video_format_mgr;
+
+
+/**
+ * Create a new video format manager instance. This will also set the pointer
+ * to the singleton instance if the value is still NULL.
+ *
+ * @param pool		The pool to allocate memory.
+ * @param max_fmt	Maximum number of formats to accommodate.
+ * @param options	Option flags. Must be zero for now.
+ * @param p_mgr		Pointer to hold the created instance.
+ *
+ * @return		PJ_SUCCESS on success, or the appripriate error value.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_video_format_mgr_create(pj_pool_t *pool,
+				unsigned max_fmt,
+				unsigned options,
+				pjmedia_video_format_mgr **p_mgr);
+
+/**
+ * Get the singleton instance of the video format manager.
+ *
+ * @return		The instance.
+ */
+PJ_DECL(pjmedia_video_format_mgr*) pjmedia_video_format_mgr_instance(void);
+
+/**
+ * Manually assign a specific video manager instance as the singleton
+ * instance. Normally this is not needed if only one instance is ever
+ * going to be created, as the library automatically assign the singleton
+ * instance.
+ *
+ * @param mgr		The instance to be used as the singleton instance.
+ * 			Application may specify NULL to clear the singleton
+ * 			singleton instance.
+ */
+PJ_DECL(void)
+pjmedia_video_format_mgr_set_instance(pjmedia_video_format_mgr *mgr);
+
+/**
+ * Retrieve a video format info for the specified format id.
+ *
+ * @param mgr		The video format manager. Specify NULL to use
+ * 			the singleton instance (however, a video format
+ * 			manager still must have been created prior to
+ * 			calling this function).
+ * @param id		The format id which format info is to be
+ * 			retrieved.
+ *
+ * @return		The video format info.
+ */
+PJ_DECL(const pjmedia_video_format_info*)
+pjmedia_get_video_format_info(pjmedia_video_format_mgr *mgr,
+			      pj_uint32_t id);
+
+/**
+ * Register a new video format to the framework. By default, built-in
+ * formats will be registered automatically to the format manager when
+ * it is created (note: built-in formats are ones which format id is
+ * listed in pjmedia_format_id enumeration). This function allows
+ * application to use user defined format id by registering that format
+ * into the framework.
+ *
+ * @param mgr		The video format manager. Specify NULL to use
+ * 			the singleton instance (however, a video format
+ * 			manager still must have been created prior to
+ * 			calling this function).
+ * @param vfi		The video format info to be registered. This
+ * 			structure must remain valid until the format
+ * 			manager is destroyed.
+ *
+ * @return		PJ_SUCCESS on success, or the appripriate error value.
+ */
+PJ_DECL(pj_status_t)
+pjmedia_register_video_format_info(pjmedia_video_format_mgr *mgr,
+				   pjmedia_video_format_info *vfi);
+
+/**
+ * Destroy a video format manager. If the manager happens to be the singleton
+ * instance, the singleton instance will be set to NULL.
+ *
+ * @param mgr		The video format manager. Specify NULL to use
+ * 			the singleton instance (however, a video format
+ * 			manager still must have been created prior to
+ * 			calling this function).
+ */
+PJ_DECL(void) pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr);
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif	/* __PJMEDIA_FORMAT_H__ */
+
diff --git a/pjmedia/include/pjmedia/frame.h b/pjmedia/include/pjmedia/frame.h
new file mode 100644
index 0000000..0df67e9
--- /dev/null
+++ b/pjmedia/include/pjmedia/frame.h
@@ -0,0 +1,316 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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_FRAME_H__
+#define __PJMEDIA_FRAME_H__
+
+/**
+ * @file pjmedia/frame.h Media frame
+ * @brief Frame
+ */
+#include <pjmedia/types.h>
+#include <pj/string.h>
+
+/**
+ * @defgroup PJMEDIA_FRAME Media frame
+ * @ingroup PJMEDIA_TYPES
+ * @brief Frame
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Types of media frame.
+ */
+typedef enum pjmedia_frame_type
+{
+    PJMEDIA_FRAME_TYPE_NONE,	    /**< No frame.		*/
+    PJMEDIA_FRAME_TYPE_AUDIO,	    /**< Normal audio frame.	*/
+    PJMEDIA_FRAME_TYPE_EXTENDED,    /**< Extended audio frame.	*/
+    PJMEDIA_FRAME_TYPE_VIDEO        /**< Video frame.           */
+
+} pjmedia_frame_type;
+
+
+/**
+ * This structure describes a media frame.
+ */
+typedef struct pjmedia_frame
+{
+    pjmedia_frame_type	 type;	    /**< Frame type.			    */
+    void		*buf;	    /**< Pointer to buffer.		    */
+    pj_size_t		 size;	    /**< Frame size in bytes.		    */
+    pj_timestamp	 timestamp; /**< Frame timestamp.		    */
+    pj_uint32_t		 bit_info;  /**< Bit info of the frame, sample case:
+					 a frame may not exactly start and end
+					 at the octet boundary, so this field
+					 may be used for specifying start &
+					 end bit offset.		    */
+} pjmedia_frame;
+
+
+/**
+ * The pjmedia_frame_ext is used to carry a more complex audio frames than
+ * the typical PCM audio frames, and it is signaled by setting the "type"
+ * field of a pjmedia_frame to PJMEDIA_FRAME_TYPE_EXTENDED. With this set,
+ * application may typecast pjmedia_frame to pjmedia_frame_ext.
+ *
+ * This structure may contain more than one audio frames, which subsequently
+ * will be called subframes in this structure. The subframes section
+ * immediately follows the end of this structure, and each subframe is
+ * represented by pjmedia_frame_ext_subframe structure. Every next
+ * subframe immediately follows the previous subframe, and all subframes
+ * are byte-aligned although its payload may not be byte-aligned.
+ */
+
+#pragma pack(1)
+typedef struct pjmedia_frame_ext {
+    pjmedia_frame   base;	    /**< Base frame info */
+    pj_uint16_t     samples_cnt;    /**< Number of samples in this frame */
+    pj_uint16_t     subframe_cnt;   /**< Number of (sub)frames in this frame */
+
+    /* Zero or more (sub)frames follows immediately after this,
+     * each will be represented by pjmedia_frame_ext_subframe
+     */
+} pjmedia_frame_ext;
+#pragma pack()
+
+/**
+ * This structure represents the individual subframes in the
+ * pjmedia_frame_ext structure.
+ */
+#pragma pack(1)
+typedef struct pjmedia_frame_ext_subframe {
+    pj_uint16_t     bitlen;	    /**< Number of bits in the data */
+    pj_uint8_t      data[1];	    /**< Start of encoded data */
+} pjmedia_frame_ext_subframe;
+
+#pragma pack()
+
+
+/**
+ * Append one subframe to #pjmedia_frame_ext.
+ *
+ * @param frm		    The #pjmedia_frame_ext.
+ * @param src		    Subframe data.
+ * @param bitlen	    Lenght of subframe, in bits.
+ * @param samples_cnt	    Number of audio samples in subframe.
+ */
+PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm,
+						  const void *src,
+					          unsigned bitlen,
+						  unsigned samples_cnt)
+{
+    pjmedia_frame_ext_subframe *fsub;
+    pj_uint8_t *p;
+    unsigned i;
+
+    p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
+    for (i = 0; i < frm->subframe_cnt; ++i) {
+	fsub = (pjmedia_frame_ext_subframe*) p;
+	p += sizeof(fsub->bitlen) + ((fsub->bitlen+7) >> 3);
+    }
+
+    fsub = (pjmedia_frame_ext_subframe*) p;
+    fsub->bitlen = (pj_uint16_t)bitlen;
+    if (bitlen)
+	pj_memcpy(fsub->data, src, (bitlen+7) >> 3);
+
+    frm->subframe_cnt++;
+    frm->samples_cnt = (pj_uint16_t)(frm->samples_cnt + samples_cnt);
+}
+
+/**
+ * Get a subframe from #pjmedia_frame_ext.
+ *
+ * @param frm		    The #pjmedia_frame_ext.
+ * @param n		    Subframe index, zero based.
+ *
+ * @return		    The n-th subframe, or NULL if n is out-of-range.
+ */
+PJ_INLINE(pjmedia_frame_ext_subframe*)
+pjmedia_frame_ext_get_subframe(const pjmedia_frame_ext *frm, unsigned n)
+{
+    pjmedia_frame_ext_subframe *sf = NULL;
+
+    if (n < frm->subframe_cnt) {
+	pj_uint8_t *p;
+	unsigned i;
+
+	p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
+	for (i = 0; i < n; ++i) {
+	    sf = (pjmedia_frame_ext_subframe*) p;
+	    p += sizeof(sf->bitlen) + ((sf->bitlen+7) >> 3);
+	}
+
+	sf = (pjmedia_frame_ext_subframe*) p;
+    }
+
+    return sf;
+}
+
+/**
+ * Extract all frame payload to the specified buffer.
+ *
+ * @param frm		    The frame.
+ * @param dst		    Destination buffer.
+ * @param maxlen	    Maximum size to copy (i.e. the size of the
+ *			    destination buffer).
+ *
+ * @return		    Total size of payload copied.
+ */
+PJ_INLINE(unsigned)
+pjmedia_frame_ext_copy_payload(const pjmedia_frame_ext *frm,
+			       void *dst,
+			       unsigned maxlen)
+{
+    unsigned i, copied=0;
+    for (i=0; i<frm->subframe_cnt; ++i) {
+	pjmedia_frame_ext_subframe *sf;
+	unsigned sz;
+
+	sf = pjmedia_frame_ext_get_subframe(frm, i);
+	if (!sf)
+	    continue;
+
+	sz = ((sf->bitlen + 7) >> 3);
+	if (sz + copied > maxlen)
+	    break;
+
+	pj_memcpy(((pj_uint8_t*)dst) + copied, sf->data, sz);
+	copied += sz;
+    }
+    return copied;
+}
+
+
+/**
+ * Pop out first n subframes from #pjmedia_frame_ext.
+ *
+ * @param frm		    The #pjmedia_frame_ext.
+ * @param n		    Number of first subframes to be popped out.
+ *
+ * @return		    PJ_SUCCESS when successful.
+ */
+PJ_INLINE(pj_status_t)
+pjmedia_frame_ext_pop_subframes(pjmedia_frame_ext *frm, unsigned n)
+{
+    pjmedia_frame_ext_subframe *sf;
+    pj_uint8_t *move_src;
+    unsigned move_len;
+
+    if (frm->subframe_cnt <= n) {
+	frm->subframe_cnt = 0;
+	frm->samples_cnt = 0;
+	return PJ_SUCCESS;
+    }
+
+    move_src = (pj_uint8_t*)pjmedia_frame_ext_get_subframe(frm, n);
+    sf = pjmedia_frame_ext_get_subframe(frm, frm->subframe_cnt-1);
+    move_len = (pj_uint8_t*)sf - move_src + sizeof(sf->bitlen) +
+	       ((sf->bitlen+7) >> 3);
+    pj_memmove((pj_uint8_t*)frm+sizeof(pjmedia_frame_ext),
+	       move_src, move_len);
+
+    frm->samples_cnt = (pj_uint16_t)
+		   (frm->samples_cnt - n*frm->samples_cnt/frm->subframe_cnt);
+    frm->subframe_cnt = (pj_uint16_t) (frm->subframe_cnt - n);
+
+    return PJ_SUCCESS;
+}
+
+
+/**
+ * This is a general purpose function set PCM samples to zero.
+ * Since this function is needed by many parts of the library,
+ * by putting this functionality in one place, it enables some.
+ * clever people to optimize this function.
+ *
+ * @param samples	The 16bit PCM samples.
+ * @param count		Number of samples.
+ */
+PJ_INLINE(void) pjmedia_zero_samples(pj_int16_t *samples, unsigned count)
+{
+#if 1
+    pj_bzero(samples, (count<<1));
+#elif 0
+    unsigned i;
+    for (i=0; i<count; ++i) samples[i] = 0;
+#else
+    unsigned i;
+    count >>= 1;
+    for (i=0; i<count; ++i) ((pj_int32_t*)samples)[i] = (pj_int32_t)0;
+#endif
+}
+
+
+/**
+ * This is a general purpose function to copy samples from/to buffers with
+ * equal size. Since this function is needed by many parts of the library,
+ * by putting this functionality in one place, it enables some.
+ * clever people to optimize this function.
+ */
+PJ_INLINE(void) pjmedia_copy_samples(pj_int16_t *dst, const pj_int16_t *src,
+				     unsigned count)
+{
+#if 1
+    pj_memcpy(dst, src, (count<<1));
+#elif 0
+    unsigned i;
+    for (i=0; i<count; ++i) dst[i] = src[i];
+#else
+    unsigned i;
+    count >>= 1;
+    for (i=0; i<count; ++i)
+	((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
+#endif
+}
+
+
+/**
+ * This is a general purpose function to copy samples from/to buffers with
+ * equal size. Since this function is needed by many parts of the library,
+ * by putting this functionality in one place, it enables some.
+ * clever people to optimize this function.
+ */
+PJ_INLINE(void) pjmedia_move_samples(pj_int16_t *dst, const pj_int16_t *src,
+				     unsigned count)
+{
+#if 1
+    pj_memmove(dst, src, (count<<1));
+#elif 0
+    unsigned i;
+    for (i=0; i<count; ++i) dst[i] = src[i];
+#else
+    unsigned i;
+    count >>= 1;
+    for (i=0; i<count; ++i)
+	((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
+#endif
+}
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_FRAME_H__ */
diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h
index c460575..594f618 100644
--- a/pjmedia/include/pjmedia/port.h
+++ b/pjmedia/include/pjmedia/port.h
@@ -1,6 +1,6 @@
 /* $Id$ */
 /* 
- * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -24,7 +24,8 @@
  * @file port.h
  * @brief Port interface declaration
  */
-#include <pjmedia/types.h>
+#include <pjmedia/format.h>
+#include <pjmedia/frame.h>
 #include <pj/assert.h>
 #include <pj/os.h>
 
@@ -184,6 +185,13 @@
 
 
 /**
+ * Create 32bit port signature from ASCII characters.
+ */
+#define PJMEDIA_PORT_SIGNATURE(a,b,c,d)	    \
+	    (a<<24 | b<<16 | c<<8 | d)
+
+
+/**
  * Port operation setting.
  */
 typedef enum pjmedia_port_op
@@ -220,19 +228,133 @@
 {
     pj_str_t	    name;		/**< Port name.			    */
     pj_uint32_t	    signature;		/**< Port signature.		    */
-    pjmedia_type    type;		/**< Media type.		    */
-    pj_bool_t	    has_info;		/**< Has info?			    */
-    pj_bool_t	    need_info;		/**< Need info on connect?	    */
-    unsigned	    pt;			/**< Payload type (can be dynamic). */
-    pjmedia_format  format;		/**< Format.			    */
-    pj_str_t	    encoding_name;	/**< Encoding name.		    */
-    unsigned	    clock_rate;		/**< Sampling rate.		    */
-    unsigned	    channel_count;	/**< Number of channels.	    */
-    unsigned	    bits_per_sample;	/**< Bits/sample		    */
-    unsigned	    samples_per_frame;	/**< No of samples per frame.	    */
-    unsigned	    bytes_per_frame;	/**< No of bytes per frame.	    */
+    pjmedia_dir     dir;                /**< Port direction.                */
+    pjmedia_format  fmt;                /**< Format.		            */
 } pjmedia_port_info;
 
+/**
+ * Utility to retrieve audio clock rate/sampling rate value from
+ * pjmedia_port_info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Audio clock rate.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return pia->fmt.det.aud.clock_rate;
+}
+
+/**
+ * Utility to retrieve audio channel count value from pjmedia_port_info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Audio channel count.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_CCNT(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return pia->fmt.det.aud.channel_count;
+}
+
+/**
+ * Utility to retrieve audio bits per sample value from pjmedia_port_info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Number of bits per sample.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_BITS(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return pia->fmt.det.aud.bits_per_sample;
+}
+
+/**
+ * Utility to retrieve audio frame interval (ptime) value from
+ * pjmedia_port_info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Frame interval in msec.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_PTIME(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return pia->fmt.det.aud.frame_time_usec / 1000;
+}
+
+/**
+ * This is a utility routine to retrieve the audio samples_per_frame value
+ * from port info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Samples per frame value.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_SPF(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return PJMEDIA_AFD_SPF(&pia->fmt.det.aud);
+}
+
+/**
+ * This is a utility routine to retrieve the average bitrate value
+ * from port info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Bitrate, in bits per second.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_BPS(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return pia->fmt.det.aud.avg_bps;
+}
+
+/**
+ * This is a utility routine to retrieve the maximum bitrate value
+ * from port info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Bitrate, in bits per second.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_BPS(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return pia->fmt.det.aud.max_bps;
+}
+
+/**
+ * This is a utility routine to retrieve the average audio frame size value
+ * from pjmedia_port_info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Frame size in bytes.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_AVG_FSZ(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return PJMEDIA_AFD_AVG_FSZ(&pia->fmt.det.aud);
+}
+
+/**
+ * Utility to retrieve audio frame size from maximum bitrate from
+ * pjmedia_port_info.
+ *
+ * @param pia		Pointer to port info containing audio format.
+ * @return		Frame size in bytes.
+ */
+PJ_INLINE(unsigned) PJMEDIA_PIA_MAX_FSZ(const pjmedia_port_info *pia)
+{
+    pj_assert(pia->fmt.type==PJMEDIA_TYPE_AUDIO &&
+	      pia->fmt.detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO);
+    return PJMEDIA_AFD_MAX_FSZ(&pia->fmt.det.aud);
+}
 
 /**
  * Port interface.
@@ -254,7 +376,7 @@
      * This should only be called by #pjmedia_port_put_frame().
      */
     pj_status_t (*put_frame)(struct pjmedia_port *this_port, 
-			     const pjmedia_frame *frame);
+			     pjmedia_frame *frame);
 
     /**
      * Source interface. 
@@ -276,6 +398,7 @@
  * ports which deal with PCM audio.
  *
  * @param info		    The port info to be initialized.
+ * @param pool		    Pool to allocate memory from.
  * @param name		    Port name.
  * @param signature	    Port signature.
  * @param clock_rate	    Port's clock rate.
@@ -293,6 +416,24 @@
 					     unsigned bits_per_sample,
 					     unsigned samples_per_frame);
 
+/**
+ * This is an auxiliary function to initialize port info for
+ * ports which deal with PCM audio.
+ *
+ * @param info		    The port info to be initialized.
+ * @param name		    Port name.
+ * @param signature	    Port signature.
+ * @param dir	            Port's direction.
+ * @param fmt	            Port's media format.
+ *
+ * @return		    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_port_info_init2(pjmedia_port_info *info,
+					     const pj_str_t *name,
+					     unsigned signature,
+					     pjmedia_dir dir,
+					     const pjmedia_format *fmt);
+
 
 /**
  * Get a frame from the port (and subsequent downstream ports).
@@ -314,7 +455,7 @@
  * @return	    PJ_SUCCESS on success, or the appropriate error code.
  */
 PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port,
-					     const pjmedia_frame *frame );
+					     pjmedia_frame *frame );
 
 
 /**
diff --git a/pjmedia/include/pjmedia/sdp.h b/pjmedia/include/pjmedia/sdp.h
index 456a047..3a4e7ae 100644
--- a/pjmedia/include/pjmedia/sdp.h
+++ b/pjmedia/include/pjmedia/sdp.h
@@ -25,7 +25,7 @@
  * @brief SDP header file.
  */
 #include <pjmedia/types.h>
-
+#include <pj/sock.h>
 
 /**
  * @defgroup PJMEDIA_SDP SDP Parsing and Data Structure
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index c3454ac..3c30810 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -32,6 +32,7 @@
 #include <pjmedia/port.h>
 #include <pjmedia/rtcp.h>
 #include <pjmedia/transport.h>
+#include <pjmedia/vid_codec.h>
 #include <pj/sock.h>
 
 PJ_BEGIN_DECL
@@ -133,6 +134,9 @@
 					 (see #PJMEDIA_STREAM_ENABLE_KA)
 					 is enabled?			    */
 #endif
+
+    pjmedia_vid_codec_info       vid_codec_info;
+    pjmedia_vid_codec_param     *vid_codec_param;
 };
 
 
diff --git a/pjmedia/include/pjmedia/transport.h b/pjmedia/include/pjmedia/transport.h
index a6a6ff4..6ccb493 100644
--- a/pjmedia/include/pjmedia/transport.h
+++ b/pjmedia/include/pjmedia/transport.h
@@ -28,6 +28,7 @@
 
 #include <pjmedia/types.h>
 #include <pjmedia/errno.h>
+#include <pj/string.h>
 
 /**
  * @defgroup PJMEDIA_TRANSPORT Media Transport
@@ -257,6 +258,35 @@
 
 
 /**
+ * Media socket info is used to describe the underlying sockets
+ * to be used as media transport.
+ */
+typedef struct pjmedia_sock_info
+{
+    /** The RTP socket handle */
+    pj_sock_t	    rtp_sock;
+
+    /** Address to be advertised as the local address for the RTP
+     *  socket, which does not need to be equal as the bound
+     *  address (for example, this address can be the address resolved
+     *  with STUN).
+     */
+    pj_sockaddr	    rtp_addr_name;
+
+    /** The RTCP socket handle. */
+    pj_sock_t	    rtcp_sock;
+
+    /** Address to be advertised as the local address for the RTCP
+     *  socket, which does not need to be equal as the bound
+     *  address (for example, this address can be the address resolved
+     *  with STUN).
+     */
+    pj_sockaddr	    rtcp_addr_name;
+
+} pjmedia_sock_info;
+
+
+/**
  * This structure describes the operations for the stream transport.
  */
 struct pjmedia_transport_op
diff --git a/pjmedia/include/pjmedia/types.h b/pjmedia/include/pjmedia/types.h
index c39a424..5fd3dae 100644
--- a/pjmedia/include/pjmedia/types.h
+++ b/pjmedia/include/pjmedia/types.h
@@ -1,6 +1,6 @@
 /* $Id$ */
 /* 
- * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -26,8 +26,9 @@
  */
 
 #include <pjmedia/config.h>
-#include <pj/sock.h>	    /* pjmedia_sock_info	*/
-#include <pj/string.h>	    /* pj_memcpy(), pj_memset() */
+#include <pj/sock.h>
+#include <pj/types.h>
+
 
 /**
  * @defgroup PJMEDIA_PORT Media Ports Framework
@@ -52,22 +53,20 @@
  */
 typedef enum pjmedia_type
 {
-    /** No type. */
-    PJMEDIA_TYPE_NONE = 0,
+    /** Type is not specified. */
+    PJMEDIA_TYPE_NONE,
 
     /** The media is audio */
-    PJMEDIA_TYPE_AUDIO = 1,
+    PJMEDIA_TYPE_AUDIO,
 
     /** The media is video. */
-    PJMEDIA_TYPE_VIDEO = 2,
-
-    /** Unknown media type, in this case the name will be specified in
-     *  encoding_name.
-     */
-    PJMEDIA_TYPE_UNKNOWN = 3,
+    PJMEDIA_TYPE_VIDEO,
 
     /** The media is application. */
-    PJMEDIA_TYPE_APPLICATION = 4
+    PJMEDIA_TYPE_APPLICATION,
+
+    /** The media type is unknown or unsupported. */
+    PJMEDIA_TYPE_UNKNOWN
 
 } pjmedia_type;
 
@@ -100,428 +99,98 @@
     /** None */
     PJMEDIA_DIR_NONE = 0,
 
-    /** Encoding (outgoing to network) stream */
+    /** Encoding (outgoing to network) stream, also known as capture */
     PJMEDIA_DIR_ENCODING = 1,
 
-    /** Decoding (incoming from network) stream. */
+    /** Same as encoding direction. */
+    PJMEDIA_DIR_CAPTURE = PJMEDIA_DIR_ENCODING,
+
+    /** Decoding (incoming from network) stream, also known as playback. */
     PJMEDIA_DIR_DECODING = 2,
 
-    /** Incoming and outgoing stream. */
-    PJMEDIA_DIR_ENCODING_DECODING = 3
+    /** Same as decoding. */
+    PJMEDIA_DIR_PLAYBACK = PJMEDIA_DIR_DECODING,
+
+    /** Same as decoding. */
+    PJMEDIA_DIR_RENDER = PJMEDIA_DIR_DECODING,
+
+    /** Incoming and outgoing stream, same as PJMEDIA_DIR_CAPTURE_PLAYBACK */
+    PJMEDIA_DIR_ENCODING_DECODING = 3,
+
+    /** Same as ENCODING_DECODING */
+    PJMEDIA_DIR_CAPTURE_PLAYBACK = PJMEDIA_DIR_ENCODING_DECODING,
+
+    /** Same as ENCODING_DECODING */
+    PJMEDIA_DIR_CAPTURE_RENDER = PJMEDIA_DIR_ENCODING_DECODING
 
 } pjmedia_dir;
 
 
-
-/* Alternate names for media direction: */
-
-/**
- * Direction is capturing audio frames.
- */
-#define PJMEDIA_DIR_CAPTURE	PJMEDIA_DIR_ENCODING
-
-/**
- * Direction is playback of audio frames.
- */
-#define PJMEDIA_DIR_PLAYBACK	PJMEDIA_DIR_DECODING
-
-/**
- * Direction is both capture and playback.
- */
-#define PJMEDIA_DIR_CAPTURE_PLAYBACK	PJMEDIA_DIR_ENCODING_DECODING
-
-
-/**
- * Create 32bit port signature from ASCII characters.
- */
-#define PJMEDIA_PORT_SIGNATURE(a,b,c,d)	    \
-	    (a<<24 | b<<16 | c<<8 | d)
-
-
 /**
  * Opaque declaration of media endpoint.
  */
 typedef struct pjmedia_endpt pjmedia_endpt;
 
-
 /*
  * Forward declaration for stream (needed by transport).
  */
 typedef struct pjmedia_stream pjmedia_stream;
 
-
 /**
- * Media socket info is used to describe the underlying sockets
- * to be used as media transport.
+ * Enumeration for picture coordinate base.
  */
-typedef struct pjmedia_sock_info
-{
-    /** The RTP socket handle */
-    pj_sock_t	    rtp_sock;
-
-    /** Address to be advertised as the local address for the RTP
-     *  socket, which does not need to be equal as the bound
-     *  address (for example, this address can be the address resolved
-     *  with STUN).
-     */
-    pj_sockaddr	    rtp_addr_name;
-
-    /** The RTCP socket handle. */
-    pj_sock_t	    rtcp_sock;
-
-    /** Address to be advertised as the local address for the RTCP
-     *  socket, which does not need to be equal as the bound
-     *  address (for example, this address can be the address resolved
-     *  with STUN).
-     */
-    pj_sockaddr	    rtcp_addr_name;
-
-} pjmedia_sock_info;
-
-
-/**
- * Macro for packing format.
- */
-#define PJMEDIA_FORMAT_PACK(C1, C2, C3, C4) ( C4<<24 | C3<<16 | C2<<8 | C1 )
-
-/**
- * This enumeration describes format ID. 
- */
-typedef enum pjmedia_format_id
+typedef enum pjmedia_coord_base
 {
     /**
-     * 16bit linear
+     * This specifies that the pixel [0, 0] location is at the left-top
+     * position.
      */
-    PJMEDIA_FORMAT_L16	    = 0,
-    
-    /**
-     * Alias for PJMEDIA_FORMAT_L16
-     */
-    PJMEDIA_FORMAT_PCM	    = PJMEDIA_FORMAT_L16,
+    PJMEDIA_COORD_BASE_LEFT_TOP,
 
     /**
-     * G.711 ALAW
+     * This specifies that the pixel [0, 0] location is at the left-bottom
+     * position.
      */
-    PJMEDIA_FORMAT_PCMA	    = PJMEDIA_FORMAT_PACK('A', 'L', 'A', 'W'),
+    PJMEDIA_COORD_BASE_LEFT_BOTTOM
 
-    /**
-     * Alias for PJMEDIA_FORMAT_PCMA
-     */
-    PJMEDIA_FORMAT_ALAW	    = PJMEDIA_FORMAT_PCMA,
-
-    /**
-     * G.711 ULAW
-     */
-    PJMEDIA_FORMAT_PCMU	    = PJMEDIA_FORMAT_PACK('u', 'L', 'A', 'W'),
-
-    /**
-     * Aliaw for PJMEDIA_FORMAT_PCMU
-     */
-    PJMEDIA_FORMAT_ULAW	    = PJMEDIA_FORMAT_PCMU,
-
-    /**
-     * AMR narrowband
-     */
-    PJMEDIA_FORMAT_AMR	    = PJMEDIA_FORMAT_PACK(' ', 'A', 'M', 'R'),
-
-    /**
-     * ITU G.729
-     */
-    PJMEDIA_FORMAT_G729	    = PJMEDIA_FORMAT_PACK('G', '7', '2', '9'),
-
-    /**
-     * Internet Low Bit-Rate Codec (ILBC)
-     */
-    PJMEDIA_FORMAT_ILBC	    = PJMEDIA_FORMAT_PACK('I', 'L', 'B', 'C')
-
-} pjmedia_format_id;
-
+} pjmedia_coord_base;
 
 /**
- * Media format information.
+ * This structure is used to represent rational numbers.
  */
-typedef struct pjmedia_format
+typedef struct pjmedia_ratio
 {
-    /** Format ID */
-    pjmedia_format_id	id;
-
-    /** Bitrate. */
-    pj_uint32_t		bitrate;
-
-    /** Flag to indicate whether VAD is enabled */
-    pj_bool_t		vad;
-
-} pjmedia_format;
-
-
+    int		num;    /** < Numerator. */
+    int		denum;  /** < Denumerator. */
+} pjmedia_ratio;
 
 /**
- * This is a general purpose function set PCM samples to zero.
- * Since this function is needed by many parts of the library,
- * by putting this functionality in one place, it enables some.
- * clever people to optimize this function.
- *
- * @param samples	The 16bit PCM samples.
- * @param count		Number of samples.
+ * This structure represent a coordinate.
  */
-PJ_INLINE(void) pjmedia_zero_samples(pj_int16_t *samples, unsigned count)
+typedef struct pjmedia_coord
 {
-#if 1
-    pj_bzero(samples, (count<<1));
-#elif 0
-    unsigned i;
-    for (i=0; i<count; ++i) samples[i] = 0;
-#else
-    unsigned i;
-    count >>= 1;
-    for (i=0; i<count; ++i) ((pj_int32_t*)samples)[i] = (pj_int32_t)0;
-#endif
-}
-
+    int		x;	/**< X position of the coordinate */
+    int		y;	/**< Y position of the coordinate */
+} pjmedia_coord;
 
 /**
- * This is a general purpose function to copy samples from/to buffers with
- * equal size. Since this function is needed by many parts of the library,
- * by putting this functionality in one place, it enables some.
- * clever people to optimize this function.
+ * This structure represents rectangle size.
  */
-PJ_INLINE(void) pjmedia_copy_samples(pj_int16_t *dst, const pj_int16_t *src,
-				     unsigned count)
+typedef struct pjmedia_rect_size
 {
-#if 1
-    pj_memcpy(dst, src, (count<<1));
-#elif 0
-    unsigned i;
-    for (i=0; i<count; ++i) dst[i] = src[i];
-#else
-    unsigned i;
-    count >>= 1;
-    for (i=0; i<count; ++i)
-	((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
-#endif
-}
-
+    unsigned	w;	/**< The width.		*/
+    unsigned 	h;	/**< The height.	*/
+} pjmedia_rect_size;
 
 /**
- * This is a general purpose function to copy samples from/to buffers with
- * equal size. Since this function is needed by many parts of the library,
- * by putting this functionality in one place, it enables some.
- * clever people to optimize this function.
+ * This structure describes a rectangle.
  */
-PJ_INLINE(void) pjmedia_move_samples(pj_int16_t *dst, const pj_int16_t *src,
-				     unsigned count)
+typedef struct pjmedia_rect
 {
-#if 1
-    pj_memmove(dst, src, (count<<1));
-#elif 0
-    unsigned i;
-    for (i=0; i<count; ++i) dst[i] = src[i];
-#else
-    unsigned i;
-    count >>= 1;
-    for (i=0; i<count; ++i)
-	((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
-#endif
-}
+    pjmedia_coord	coord;	/**< The position.	*/
+    pjmedia_rect_size	size;	/**< The size.		*/
+} pjmedia_rect;
 
-/** 
- * Types of media frame. 
- */
-typedef enum pjmedia_frame_type
-{
-    PJMEDIA_FRAME_TYPE_NONE,	    /**< No frame.		*/
-    PJMEDIA_FRAME_TYPE_AUDIO,	    /**< Normal audio frame.	*/
-    PJMEDIA_FRAME_TYPE_EXTENDED	    /**< Extended audio frame.	*/
-
-} pjmedia_frame_type;
-
-
-/** 
- * This structure describes a media frame. 
- */
-typedef struct pjmedia_frame
-{
-    pjmedia_frame_type	 type;	    /**< Frame type.			    */
-    void		*buf;	    /**< Pointer to buffer.		    */
-    pj_size_t		 size;	    /**< Frame size in bytes.		    */
-    pj_timestamp	 timestamp; /**< Frame timestamp.		    */
-    pj_uint32_t		 bit_info;  /**< Bit info of the frame, sample case:
-					 a frame may not exactly start and end
-					 at the octet boundary, so this field 
-					 may be used for specifying start & 
-					 end bit offset.		    */
-} pjmedia_frame;
-
-
-/**
- * The pjmedia_frame_ext is used to carry a more complex audio frames than
- * the typical PCM audio frames, and it is signaled by setting the "type"
- * field of a pjmedia_frame to PJMEDIA_FRAME_TYPE_EXTENDED. With this set,
- * application may typecast pjmedia_frame to pjmedia_frame_ext.
- *
- * This structure may contain more than one audio frames, which subsequently
- * will be called subframes in this structure. The subframes section
- * immediately follows the end of this structure, and each subframe is
- * represented by pjmedia_frame_ext_subframe structure. Every next
- * subframe immediately follows the previous subframe, and all subframes
- * are byte-aligned although its payload may not be byte-aligned.
- */
-
-#pragma pack(1)
-typedef struct pjmedia_frame_ext {
-    pjmedia_frame   base;	    /**< Base frame info */
-    pj_uint16_t     samples_cnt;    /**< Number of samples in this frame */
-    pj_uint16_t     subframe_cnt;   /**< Number of (sub)frames in this frame */
-
-    /* Zero or more (sub)frames follows immediately after this,
-     * each will be represented by pjmedia_frame_ext_subframe
-     */
-} pjmedia_frame_ext;
-#pragma pack()
-
-/**
- * This structure represents the individual subframes in the
- * pjmedia_frame_ext structure.
- */
-#pragma pack(1)
-typedef struct pjmedia_frame_ext_subframe {
-    pj_uint16_t     bitlen;	    /**< Number of bits in the data */
-    pj_uint8_t      data[1];	    /**< Start of encoded data */
-} pjmedia_frame_ext_subframe;
-
-#pragma pack()
-
-
-/**
- * Append one subframe to #pjmedia_frame_ext.
- *
- * @param frm		    The #pjmedia_frame_ext.
- * @param src		    Subframe data.
- * @param bitlen	    Lenght of subframe, in bits.
- * @param samples_cnt	    Number of audio samples in subframe.
- */
-PJ_INLINE(void) pjmedia_frame_ext_append_subframe(pjmedia_frame_ext *frm,
-						  const void *src,
-					          unsigned bitlen,
-						  unsigned samples_cnt)
-{
-    pjmedia_frame_ext_subframe *fsub;
-    pj_uint8_t *p;
-    unsigned i;
-
-    p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
-    for (i = 0; i < frm->subframe_cnt; ++i) {
-	fsub = (pjmedia_frame_ext_subframe*) p;
-	p += sizeof(fsub->bitlen) + ((fsub->bitlen+7) >> 3);
-    }
-
-    fsub = (pjmedia_frame_ext_subframe*) p;
-    fsub->bitlen = (pj_uint16_t)bitlen;
-    if (bitlen)
-	pj_memcpy(fsub->data, src, (bitlen+7) >> 3);
-
-    frm->subframe_cnt++;
-    frm->samples_cnt = (pj_uint16_t)(frm->samples_cnt + samples_cnt);
-}
-
-/**
- * Get a subframe from #pjmedia_frame_ext.
- *
- * @param frm		    The #pjmedia_frame_ext.
- * @param n		    Subframe index, zero based.
- *
- * @return		    The n-th subframe, or NULL if n is out-of-range.
- */
-PJ_INLINE(pjmedia_frame_ext_subframe*) 
-pjmedia_frame_ext_get_subframe(const pjmedia_frame_ext *frm, unsigned n)
-{
-    pjmedia_frame_ext_subframe *sf = NULL;
-
-    if (n < frm->subframe_cnt) {
-	pj_uint8_t *p;
-	unsigned i;
-
-	p = (pj_uint8_t*)frm + sizeof(pjmedia_frame_ext);
-	for (i = 0; i < n; ++i) {	
-	    sf = (pjmedia_frame_ext_subframe*) p;
-	    p += sizeof(sf->bitlen) + ((sf->bitlen+7) >> 3);
-	}
-        
-	sf = (pjmedia_frame_ext_subframe*) p;
-    }
-
-    return sf;
-}
-	
-/**
- * Extract all frame payload to the specified buffer. 
- *
- * @param frm		    The frame.
- * @param dst		    Destination buffer.
- * @param maxlen	    Maximum size to copy (i.e. the size of the
- *			    destination buffer).
- *
- * @return		    Total size of payload copied.
- */
-PJ_INLINE(unsigned) 
-pjmedia_frame_ext_copy_payload(const pjmedia_frame_ext *frm,
-			       void *dst, 
-			       unsigned maxlen)
-{
-    unsigned i, copied=0;
-    for (i=0; i<frm->subframe_cnt; ++i) {
-	pjmedia_frame_ext_subframe *sf;
-	unsigned sz;
-
-	sf = pjmedia_frame_ext_get_subframe(frm, i);
-	if (!sf)
-	    continue;
-
-	sz = ((sf->bitlen + 7) >> 3);
-	if (sz + copied > maxlen)
-	    break;
-
-	pj_memcpy(((pj_uint8_t*)dst) + copied, sf->data, sz);
-	copied += sz;
-    }
-    return copied;
-}
-
-
-/**
- * Pop out first n subframes from #pjmedia_frame_ext.
- *
- * @param frm		    The #pjmedia_frame_ext.
- * @param n		    Number of first subframes to be popped out.
- *
- * @return		    PJ_SUCCESS when successful.
- */
-PJ_INLINE(pj_status_t) 
-pjmedia_frame_ext_pop_subframes(pjmedia_frame_ext *frm, unsigned n)
-{
-    pjmedia_frame_ext_subframe *sf;
-    pj_uint8_t *move_src;
-    unsigned move_len;
-
-    if (frm->subframe_cnt <= n) {
-	frm->subframe_cnt = 0;
-	frm->samples_cnt = 0;
-	return PJ_SUCCESS;
-    }
-
-    move_src = (pj_uint8_t*)pjmedia_frame_ext_get_subframe(frm, n);
-    sf = pjmedia_frame_ext_get_subframe(frm, frm->subframe_cnt-1);
-    move_len = (pj_uint8_t*)sf - move_src + sizeof(sf->bitlen) + 
-	       ((sf->bitlen+7) >> 3);
-    pj_memmove((pj_uint8_t*)frm+sizeof(pjmedia_frame_ext), 
-	       move_src, move_len);
-	    
-    frm->samples_cnt = (pj_uint16_t)
-		   (frm->samples_cnt - n*frm->samples_cnt/frm->subframe_cnt);
-    frm->subframe_cnt = (pj_uint16_t) (frm->subframe_cnt - n);
-
-    return PJ_SUCCESS;
-}
 
 
 /**
diff --git a/pjmedia/include/pjmedia/vid_codec.h b/pjmedia/include/pjmedia/vid_codec.h
new file mode 100644
index 0000000..d5321b5
--- /dev/null
+++ b/pjmedia/include/pjmedia/vid_codec.h
@@ -0,0 +1,673 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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_CODEC_H__
+#define __PJMEDIA_VID_CODEC_H__
+
+
+/**
+ * @file codec.h
+ * @brief Codec framework.
+ */
+
+#include <pjmedia/codec.h>
+#include <pjmedia/format.h>
+#include <pjmedia/types.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+
+PJ_BEGIN_DECL
+
+#define PJMEDIA_VID_CODEC_MAX_DEC_FMT_CNT    8
+#define PJMEDIA_VID_CODEC_MAX_FPS_CNT        16
+
+/** 
+ * Identification used to search for codec factory that supports specific 
+ * codec specification. 
+ */
+typedef struct pjmedia_vid_codec_info
+{
+    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
+                                             dynamic payload type)          */
+    unsigned            clock_rate;     /**< (?) Clock rate                 */
+    unsigned            dec_fmt_id_cnt;
+    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                */
+} 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
+ * be queried using #pjmedia_vid_codec_mgr_get_default_param() and modified
+ * using #pjmedia_vid_codec_mgr_set_default_param().
+ *
+ * Please note that codec parameter also contains SDP specific setting, 
+ * #dec_fmtp and #enc_fmtp, which may need to be set appropriately based on
+ * the effective setting. See each codec documentation for more detail.
+ */
+typedef struct pjmedia_vid_codec_param
+{
+    pjmedia_dir         dir;            /**< Direction                      */
+    pjmedia_format      enc_fmt;        /**< Encoded format	            */
+    pjmedia_format      dec_fmt;        /**< Decoded format	            */
+
+    pjmedia_codec_fmtp  enc_fmtp;       /**< Encoder fmtp params	    */
+    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;
+
+
+
+/*
+ * Forward declaration for pjmedia_vid_codec.
+ */
+typedef struct pjmedia_vid_codec pjmedia_vid_codec;
+
+
+/**
+ * This structure describes codec operations. Each codec MUST implement
+ * all of these functions.
+ */
+typedef struct pjmedia_vid_codec_op
+{
+    /** 
+     * Initialize codec using the specified attribute.
+     *
+     * @param codec	The codec instance.
+     * @param pool	Pool to use when the codec needs to allocate
+     *			some memory.
+     *
+     * @return		PJ_SUCCESS on success.
+     */
+    pj_status_t	(*init)(pjmedia_vid_codec *codec, 
+			pj_pool_t *pool );
+
+    /** 
+     * 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).
+     *
+     * @param codec	The codec instance.
+     * @param param	Codec initialization parameter.
+     *
+     * @return		PJ_SUCCESS on success.
+     */
+    pj_status_t	(*open)(pjmedia_vid_codec *codec, 
+			pjmedia_vid_codec_param *param );
+
+    /** 
+     * Close and shutdown codec, releasing all resources allocated by
+     * this codec, if any.
+     *
+     * @param codec	The codec instance.
+     *
+     * @return		PJ_SUCCESS on success.
+     */
+    pj_status_t (*close)(pjmedia_vid_codec *codec);
+
+    /** 
+     * Modify the codec parameter after the codec is open. 
+     * Note that not all codec parameters can be modified during run-time. 
+     * 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 );
+
+    /**
+     * 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).
+     *
+     * @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.
+     *
+     * @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);
+
+    /**
+     * 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).
+     *
+     * @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.
+     *
+     * @return		PJ_SUCCESS on success.
+     */
+    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);
+
+    /** 
+     * 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).
+     *
+     * @param codec	The codec instance.
+     * @param input	The input frame.
+     * @param out_size	The length of buffer in the output frame.
+     * @param output	The output frame.
+     *
+     * @return		PJ_SUCCESS on success;
+     */
+    pj_status_t (*encode)(pjmedia_vid_codec *codec, 
+			  const pjmedia_frame *input,
+			  unsigned out_size, 
+			  pjmedia_frame *output);
+
+    /** 
+     * 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.
+     *
+     * @param codec	The codec instance.
+     * @param input	The input frame.
+     * @param out_size	The length of buffer in the output frame.
+     * @param output	The output frame.
+     *
+     * @return		PJ_SUCCESS on success;
+     */
+    pj_status_t (*decode)(pjmedia_vid_codec *codec, 
+			  const pjmedia_frame *input,
+			  unsigned out_size, 
+			  pjmedia_frame *output);
+
+    /**
+     * Instruct the codec to recover a missing frame.
+     *
+     * @param codec	The codec instance.
+     * @param out_size	The length of buffer in the output frame.
+     * @param output	The output frame where generated signal
+     *			will be placed.
+     *
+     * @return		PJ_SUCCESS on success;
+     */
+    pj_status_t (*recover)(pjmedia_vid_codec *codec,
+			   unsigned out_size,
+			   pjmedia_frame *output);
+} pjmedia_vid_codec_op;
+
+
+
+/*
+ * Forward declaration for pjmedia_vid_codec_factory.
+ */
+typedef struct pjmedia_vid_codec_factory pjmedia_vid_codec_factory;
+
+
+/**
+ * This structure describes a codec instance. 
+ */
+struct pjmedia_vid_codec
+{
+    /** Entries to put this codec instance in codec factory's list. */
+    PJ_DECL_LIST_MEMBER(struct pjmedia_vid_codec);
+
+    /** Codec's private data. */
+    void		    *codec_data;
+
+    /** Codec factory where this codec was allocated. */
+    pjmedia_vid_codec_factory   *factory;
+
+    /** Operations to codec. */
+    pjmedia_vid_codec_op	    *op;
+};
+
+
+
+/**
+ * This structure describes operations that must be supported by codec 
+ * factories.
+ */
+typedef struct pjmedia_vid_codec_factory_op
+{
+    /** 
+     * Check whether the factory can create codec with the specified 
+     * codec info.
+     *
+     * @param factory	The codec factory.
+     * @param info	The codec info.
+     *
+     * @return		PJ_SUCCESS if this factory is able to create an
+     *			instance of codec with the specified info.
+     */
+    pj_status_t	(*test_alloc)(pjmedia_vid_codec_factory *factory, 
+			      const pjmedia_vid_codec_info *info );
+
+    /** 
+     * Create default attributes for the specified codec ID. This function
+     * can be called by application to get the capability of the codec.
+     *
+     * @param factory	The codec factory.
+     * @param info	The codec info.
+     * @param attr	The attribute to be initialized.
+     *
+     * @return		PJ_SUCCESS if success.
+     */
+    pj_status_t (*default_attr)(pjmedia_vid_codec_factory *factory, 
+    				const pjmedia_vid_codec_info *info,
+    				pjmedia_vid_codec_param *attr );
+
+    /** 
+     * Enumerate supported codecs that can be created using this factory.
+     * 
+     *  @param factory	The codec factory.
+     *  @param count	On input, specifies the number of elements in
+     *			the array. On output, the value will be set to
+     *			the number of elements that have been initialized
+     *			by this function.
+     *  @param info	The codec info array, which contents will be 
+     *			initialized upon return.
+     *
+     *  @return		PJ_SUCCESS on success.
+     */
+    pj_status_t (*enum_info)(pjmedia_vid_codec_factory *factory, 
+			     unsigned *count, 
+			     pjmedia_vid_codec_info codecs[]);
+
+    /** 
+     * Create one instance of the codec with the specified codec info.
+     *
+     * @param factory	The codec factory.
+     * @param info	The codec info.
+     * @param p_codec	Pointer to receive the codec instance.
+     *
+     * @return		PJ_SUCCESS on success.
+     */
+    pj_status_t (*alloc_codec)(pjmedia_vid_codec_factory *factory, 
+			       const pjmedia_vid_codec_info *info,
+			       pjmedia_vid_codec **p_codec);
+
+    /** 
+     * This function is called by codec manager to return a particular 
+     * instance of codec back to the codec factory.
+     *
+     * @param factory	The codec factory.
+     * @param codec	The codec instance to be returned.
+     *
+     * @return		PJ_SUCCESS on success.
+     */
+    pj_status_t (*dealloc_codec)(pjmedia_vid_codec_factory *factory, 
+				 pjmedia_vid_codec *codec );
+
+} pjmedia_vid_codec_factory_op;
+
+
+
+/**
+ * Codec factory describes a module that is able to create codec with specific
+ * capabilities. These capabilities can be queried by codec manager to create
+ * instances of codec.
+ */
+struct pjmedia_vid_codec_factory
+{
+    /** Entries to put this structure in the codec manager list. */
+    PJ_DECL_LIST_MEMBER(struct pjmedia_vid_codec_factory);
+
+    /** The factory's private data. */
+    void		     *factory_data;
+
+    /** Operations to the factory. */
+    pjmedia_vid_codec_factory_op *op;
+
+};
+
+
+/**
+ * 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
+ * endpoint's initialization code.
+ *
+ * @param mgr	    Codec manager instance.
+ * @param pf	    Pool factory instance.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_create(pj_pool_t *pool,
+                                                  pjmedia_vid_codec_mgr **mgr);
+
+
+/**
+ * Destroy codec manager. Normally this function is called by pjmedia
+ * endpoint's deinitialization code.
+ *
+ * @param mgr	    Codec manager instance.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr *mgr);
+
+PJ_DECL(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void);
+PJ_DECL(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr);
+
+
+/** 
+ * 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 factory   The codec factory to be registered.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_vid_codec_mgr_register_factory( pjmedia_vid_codec_mgr *mgr,
+				        pjmedia_vid_codec_factory *factory);
+
+/**
+ * Unregister codec factory from the codec manager. This will also
+ * 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 factory   The codec factory to be unregistered.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_vid_codec_mgr_unregister_factory( pjmedia_vid_codec_mgr *mgr, 
+				          pjmedia_vid_codec_factory *factory);
+
+/**
+ * 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 count	    On input, specifies the number of elements in
+ *		    the array. On output, the value will be set to
+ *		    the number of elements that have been initialized
+ *		    by this function.
+ * @param info	    The codec info array, which contents will be 
+ *		    initialized upon return.
+ * @param prio	    Optional pointer to receive array of codec priorities.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs( 
+                                            pjmedia_vid_codec_mgr *mgr, 
+					    unsigned *count, 
+					    pjmedia_vid_codec_info info[],
+					    unsigned *prio);
+
+/**
+ * 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 pt	    Static payload type/number.
+ * @param inf	    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);
+
+/**
+ * Convert codec info struct into a unique codec identifier.
+ * A codec identifier looks something like "L16/44100/2".
+ *
+ * @param info	    The codec info
+ * @param id	    Buffer to put the codec info string.
+ * @param max_len   The length of the buffer.
+ *
+ * @return	    The null terminated codec info string, or NULL if
+ *		    the buffer is not long enough.
+ */
+PJ_DECL(char*) pjmedia_vid_codec_info_to_id(
+                                        const pjmedia_vid_codec_info *info,
+				        char *id, unsigned max_len );
+
+
+/**
+ * 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",
+ * 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 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
+ *		    contains the actual number of codecs found.
+ * @param p_info    Array of pointer to codec info to be filled. This
+ *		    argument may be NULL, which in this case, only
+ *		    codec count will be returned.
+ * @param prio	    Optional array of codec priorities.
+ *
+ * @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[]);
+
+
+/**
+ * Set codec priority. The codec priority determines the order of
+ * the codec in the SDP created by the endpoint. If more than one codecs
+ * 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 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
+ *		    between 1 to 255. When the priority is set to zero,
+ *		    the codec will be disabled.
+ *
+ * @return	    PJ_SUCCESS if at least one codec info is found.
+ */
+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);
+
+
+/**
+ * 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 info	    The codec info, which default parameter's is being
+ *		    queried.
+ * @param param	    On return, will be filled with the default codec
+ *		    parameter.
+ *
+ * @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 );
+
+
+/**
+ * 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 info	    The codec info, which default parameter's is being
+ *		    updated.
+ * @param param	    The new default codec parameter. Set to NULL to reset
+ *		    codec parameter to library default settings.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_vid_codec_mgr_set_default_param(pjmedia_vid_codec_mgr *mgr,
+                                        pj_pool_t *pool,
+				        const pjmedia_vid_codec_info *info,
+				        const pjmedia_vid_codec_param *param);
+
+
+/**
+ * Request the codec manager to allocate one instance of codec with the
+ * specified codec info. The codec will enumerate all codec factories
+ * until it finds factory that is able to create the specified codec.
+ *
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param info	    The information about the codec to be created.
+ * @param p_codec   Pointer to receive the codec instance.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) 
+pjmedia_vid_codec_mgr_alloc_codec( pjmedia_vid_codec_mgr *mgr, 
+			           const pjmedia_vid_codec_info *info,
+			           pjmedia_vid_codec **p_codec);
+
+/**
+ * Deallocate the specified codec instance. The codec manager will return
+ * the instance of the codec back to its factory.
+ *
+ * @param mgr	    The codec manager instance. Application can get the
+ *		    instance by calling #pjmedia_endpt_get_codec_mgr().
+ * @param codec	    The codec instance.
+ *
+ * @return	    PJ_SUCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_mgr_dealloc_codec(
+                                                pjmedia_vid_codec_mgr *mgr, 
+						pjmedia_vid_codec *codec);
+
+
+
+
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup PJMEDIA_CODEC_CODECS Supported codecs
+ * @ingroup PJMEDIA_CODEC
+ * @brief Documentation about individual codec supported by PJMEDIA
+ * @{
+ * Please see the APIs provided by the individual codecs below.
+ */
+/**
+ * @}
+ */
+
+
+
+
+PJ_END_DECL
+
+
+#endif	/* __PJMEDIA_VID_CODEC_H__ */
diff --git a/pjmedia/include/pjmedia/videoport.h b/pjmedia/include/pjmedia/videoport.h
new file mode 100644
index 0000000..0cd0b3e
--- /dev/null
+++ b/pjmedia/include/pjmedia/videoport.h
@@ -0,0 +1,206 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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_VIDPORT_H__
+#define __PJMEDIA_VIDPORT_H__
+
+/**
+ * @file pjmedia/videoport.h Video media port
+ * @brief Video media port
+ */
+
+#include <pjmedia-videodev/videodev.h>
+#include <pjmedia/port.h>
+
+/**
+ * @defgroup PJMEDIA_VIDEO_PORT Video media port
+ * @ingroup PJMEDIA_PORT_CLOCK
+ * @brief Video media port
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+/**
+ * This structure describes the parameters to create a video port
+ */
+typedef struct pjmedia_vid_port_param
+{
+    /**
+     * Video stream parameter.
+     */
+    pjmedia_vid_param	vidparam;
+
+    /**
+     * Specify whether the video port should use active or passive interface.
+     * If active interface is selected, the video port will perform as
+     * a media clock, automatically calls pjmedia_port_get_frame() and
+     * pjmedia_port_put_frame() of its slave port (depending on the direction
+     * that is specified when opening the video stream). If passive interface
+     * is selected, application can retrieve the media port of this video
+     * port by calling pjmedia_vid_port_get_passive_port(), and subsequently
+     * calls pjmedia_port_put_frame() or pjmedia_port_get_frame() to that
+     * media port.
+     */
+    pj_bool_t		active;
+
+} pjmedia_vid_port_param;
+
+/**
+ * Opaque data type for video port.
+ */
+typedef struct pjmedia_vid_port pjmedia_vid_port;
+
+/**
+ * Initialize the parameter with the default values. Note that this typically
+ * would only fill the structure to zeroes.
+ *
+ * @param prm	The parameter.
+ */
+PJ_DECL(void) pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm);
+
+/**
+ * Create a video port with the specified parameter.
+ *
+ * @param pool		Pool to allocate memory from.
+ * @param prm		The video port parameter.
+ * @param p_vp		Pointer to receive the result.
+ *
+ * @return		PJ_SUCCESS if video port has been created
+ * 			successfully, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_create(pj_pool_t *pool,
+					     const pjmedia_vid_port_param *prm,
+					     pjmedia_vid_port **p_vp);
+
+/**
+ * Set the callbacks of the video port's underlying video stream.
+ *
+ * @param vid_port	The video port.
+ * @param cb            Pointer to structure containing video stream
+ *                      callbacks.
+ * @param user_data     Arbitrary user data, which will be given back in the
+ *                      callbacks.
+ */
+PJ_DECL(void) pjmedia_vid_port_set_cb(pjmedia_vid_port *vid_port,
+				      const pjmedia_vid_cb *cb,
+                                      void *user_data);
+
+/**
+ * Return the underlying video stream of the video port.
+ *
+ * @param vid_port	The video port.
+ *
+ * @return		The video stream.
+ */
+PJ_DECL(pjmedia_vid_stream*)
+pjmedia_vid_port_get_stream(pjmedia_vid_port *vid_port);
+
+/**
+ * Return the (passive) media port of the video port. This operation
+ * is only valid for video ports created with passive interface selected.
+ * Retrieving the media port for active video ports may raise an
+ * assertion.
+ *
+ *  @param vid_port	The video port.
+ *
+ *  @return		The media port instance, or NULL.
+ */
+PJ_DECL(pjmedia_port*)
+pjmedia_vid_port_get_passive_port(pjmedia_vid_port *vid_port);
+
+/**
+ * Connect the video port to a downstream (slave) media port. This operation
+ * is only valid for video ports created with active interface selected.
+ * Connecting a passive video port may raise an assertion.
+ *
+ * @param vid_port	The video port.
+ * @param port		A downstream media port to be connected to
+ * 			this video port.
+ * @param destroy	Specify if the downstream media port should also be
+ * 			destroyed by this video port when the video port
+ * 			is destroyed.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_connect(pjmedia_vid_port *vid_port,
+					      pjmedia_port *port,
+					      pj_bool_t destroy);
+
+/**
+ * Connect the video port from its downstream (slave) media port, if any.
+ * This operation is only valid for video ports created with active interface
+ * selected, and assertion may be triggered if this is invoked on a passive
+ * video port.
+ *
+ * @param vid_port	The video port.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_disconnect(pjmedia_vid_port *vid_port);
+
+/**
+ * Retrieve the media port currently connected as downstream media port of the
+ * specified video port. This operation is only valid for video ports created
+ * with active interface selected, and assertion may be triggered if this is
+ * invoked on a passive video port.
+ *
+ * @param vid_port	The video port.
+ *
+ * @return		Media port currently connected to the video port,
+ * 			if any.
+ */
+PJ_DECL(pjmedia_port*)
+pjmedia_vid_port_get_connected_port(pjmedia_vid_port *vid_port);
+
+/**
+ * Start the video port.
+ *
+ * @param vid_port	The video port.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_start(pjmedia_vid_port *vid_port);
+
+/**
+ * Stop the video port.
+ *
+ * @param vid_port	The video port.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_port_stop(pjmedia_vid_port *vid_port);
+
+/**
+ * Destroy the video port, along with its video stream. If the video port is
+ * an active one, this may also destroy the downstream media port, if the
+ * destroy flag is set when the media port is connected.
+ *
+ * @param vid_port	The video port.
+ */
+PJ_DECL(void) pjmedia_vid_port_destroy(pjmedia_vid_port *vid_port);
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_VIDPORT_H__ */
+
diff --git a/pjmedia/include/pjmedia_videodev.h b/pjmedia/include/pjmedia_videodev.h
new file mode 100644
index 0000000..cf22e61
--- /dev/null
+++ b/pjmedia/include/pjmedia_videodev.h
@@ -0,0 +1,30 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2010 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_VIDEODEV_H__
+#define __PJMEDIA_VIDEODEV_H__
+
+/**
+ * @file pjmedia_videodev.h
+ * @brief PJMEDIA main header file.
+ */
+
+#include <pjmedia-videodev/videodev.h>
+#include <pjmedia-videodev/videodev_imp.h>
+
+#endif	/* __PJMEDIA_VIDEODEV_H__ */
diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c
index 2a5a8e6..fa381c9 100644
--- a/pjmedia/src/pjmedia-audiodev/audiodev.c
+++ b/pjmedia/src/pjmedia-audiodev/audiodev.c
@@ -190,9 +190,11 @@
     case PJMEDIA_AUD_DEV_CAP_EC_TAIL:
 	FIELD_INFO(ec_tail_ms);
 	break;
+    /* vad is no longer in "fmt" in 2.0.
     case PJMEDIA_AUD_DEV_CAP_VAD:
 	FIELD_INFO(ext_fmt.vad);
 	break;
+    */
     case PJMEDIA_AUD_DEV_CAP_CNG:
 	FIELD_INFO(cng_enabled);
 	break;
diff --git a/pjmedia/src/pjmedia-audiodev/wmme_dev.c b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
index 4a577c7..c69c5c8 100644
--- a/pjmedia/src/pjmedia-audiodev/wmme_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
@@ -250,12 +250,12 @@
     /* Extended formats */
     wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT;
     wdi->info.ext_fmt_cnt = 2;
-    wdi->info.ext_fmt[0].id = PJMEDIA_FORMAT_PCMU;
-    wdi->info.ext_fmt[0].bitrate = 64000;
-    wdi->info.ext_fmt[0].vad = 0;
-    wdi->info.ext_fmt[1].id = PJMEDIA_FORMAT_PCMA;
-    wdi->info.ext_fmt[1].bitrate = 64000;
-    wdi->info.ext_fmt[1].vad = 0;
+    pjmedia_format_init_audio(&wdi->info.ext_fmt[0],
+			      PJMEDIA_FORMAT_PCMU, 8000, 1, 8,
+			      20000, 64000, 64000);
+    pjmedia_format_init_audio(&wdi->info.ext_fmt[0],
+			      PJMEDIA_FORMAT_PCMA, 8000, 1, 8,
+			      20000, 64000, 64000);
 }
 
 /* API: init factory */
diff --git a/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
new file mode 100644
index 0000000..35128e5
--- /dev/null
+++ b/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
@@ -0,0 +1,1054 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2010 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-codec/ffmpeg_codecs.h>
+#include <pjmedia-codec/h263_packetizer.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/list.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/os.h>
+
+/*
+ * Only build this file if PJMEDIA_HAS_FFMPEG_CODEC != 0
+ */
+#if defined(PJMEDIA_HAS_FFMPEG_CODEC) && PJMEDIA_HAS_FFMPEG_CODEC != 0
+
+#define THIS_FILE   "ffmpeg_codecs.c"
+
+#include "../pjmedia/ffmpeg_util.h"
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+
+
+#define PJMEDIA_FORMAT_FFMPEG_UNKNOWN  PJMEDIA_FORMAT_PACK('f','f','0','0');
+
+
+/* Prototypes for FFMPEG codecs factory */
+static pj_status_t ffmpeg_test_alloc( pjmedia_vid_codec_factory *factory, 
+				      const pjmedia_vid_codec_info *id );
+static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory, 
+				        const pjmedia_vid_codec_info *info, 
+				        pjmedia_vid_codec_param *attr );
+static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory, 
+				       unsigned *count, 
+				       pjmedia_vid_codec_info codecs[]);
+static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory, 
+				       const pjmedia_vid_codec_info *info, 
+				       pjmedia_vid_codec **p_codec);
+static pj_status_t ffmpeg_dealloc_codec( pjmedia_vid_codec_factory *factory, 
+				         pjmedia_vid_codec *codec );
+
+/* Prototypes for FFMPEG codecs implementation. */
+static pj_status_t  ffmpeg_codec_init( pjmedia_vid_codec *codec, 
+				       pj_pool_t *pool );
+static pj_status_t  ffmpeg_codec_open( pjmedia_vid_codec *codec, 
+				       pjmedia_vid_codec_param *attr );
+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_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);
+static pj_status_t  ffmpeg_unpacketize(pjmedia_vid_codec *codec,
+                                       const pj_uint8_t *payload,
+                                       pj_size_t   payload_len,
+                                       pj_uint8_t *buf,
+                                       pj_size_t  *buf_len);
+static pj_status_t  ffmpeg_codec_encode( pjmedia_vid_codec *codec, 
+				         const pjmedia_frame *input,
+				         unsigned output_buf_len, 
+				         pjmedia_frame *output);
+static pj_status_t  ffmpeg_codec_decode( pjmedia_vid_codec *codec, 
+				         const pjmedia_frame *input,
+				         unsigned output_buf_len, 
+				         pjmedia_frame *output);
+static pj_status_t  ffmpeg_codec_recover( pjmedia_vid_codec *codec, 
+				          unsigned output_buf_len, 
+				          pjmedia_frame *output);
+
+/* Definition for FFMPEG codecs operations. */
+static pjmedia_vid_codec_op ffmpeg_op = 
+{
+    &ffmpeg_codec_init,
+    &ffmpeg_codec_open,
+    &ffmpeg_codec_close,
+    &ffmpeg_codec_modify,
+    &ffmpeg_packetize,
+    &ffmpeg_unpacketize,
+    &ffmpeg_codec_encode,
+    &ffmpeg_codec_decode,
+    NULL //&ffmpeg_codec_recover
+};
+
+/* Definition for FFMPEG codecs factory operations. */
+static pjmedia_vid_codec_factory_op ffmpeg_factory_op =
+{
+    &ffmpeg_test_alloc,
+    &ffmpeg_default_attr,
+    &ffmpeg_enum_codecs,
+    &ffmpeg_alloc_codec,
+    &ffmpeg_dealloc_codec
+};
+
+
+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;
+    pjmedia_vid_codec_mgr	*mgr;
+    pj_pool_factory             *pf;
+    pj_pool_t		        *pool;
+    pj_mutex_t		        *mutex;
+    ffmpeg_codec_info            codecs;
+} ffmpeg_factory;
+
+
+/* FFMPEG codecs private data. */
+typedef struct ffmpeg_private {
+    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;
+    */
+
+    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;
+
+
+/*
+ * Initialize and register FFMPEG codec factory to pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_init(pjmedia_vid_codec_mgr *mgr,
+                                              pj_pool_factory *pf)
+{
+    pj_pool_t *pool;
+    AVCodec *c;
+    enum CodecID last_codec_id = CODEC_ID_NONE;
+    pj_status_t status;
+
+    if (ffmpeg_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    if (!mgr) mgr = pjmedia_vid_codec_mgr_instance();
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    /* Create FFMPEG codec factory. */
+    ffmpeg_factory.base.op = &ffmpeg_factory_op;
+    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)
+	return PJ_ENOMEM;
+
+    /* Create mutex. */
+    status = pj_mutex_create_simple(pool, "ffmpeg codec factory", 
+				    &ffmpeg_factory.mutex);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    avcodec_init();
+    avcodec_register_all();
+
+    /* Enum FFMPEG codecs */
+    for (c=av_codec_next(NULL); c; c=av_codec_next(c)) 
+    {
+        ffmpeg_codec_info *ci;
+        
+        if (c->type != CODEC_TYPE_VIDEO)
+            continue;
+
+        /* Video encoder and decoder are usually implemented in separate
+         * AVCodec instances.
+         */
+
+        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;
+
+            /* 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;
+
+                /* Skip unrecognized encoding format ID */
+                continue;
+            }
+
+            /* 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;
+
+                    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;
+            }
+
+            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;
+            }
+
+            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);
+
+            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 (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;
+                }
+            }
+
+            pj_list_push_back(&ffmpeg_factory.codecs, ci);
+        }
+
+        if (c->encode) {
+            ci->info.dir |= PJMEDIA_DIR_ENCODING;
+            ci->enc = c;
+        }
+        if (c->decode) {
+            ci->info.dir |= PJMEDIA_DIR_DECODING;
+            ci->dec = c;
+        }
+
+        last_codec_id = c->id;
+    }
+
+    /* Register codec factory to codec manager. */
+    status = pjmedia_vid_codec_mgr_register_factory(mgr, 
+						    &ffmpeg_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    ffmpeg_factory.pool = pool;
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(pool);
+    return status;
+}
+
+/*
+ * Unregister FFMPEG codecs factory from pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_ffmpeg_deinit(void)
+{
+    pj_status_t status = PJ_SUCCESS;
+
+    if (ffmpeg_factory.pool == NULL) {
+	/* Already deinitialized */
+	return PJ_SUCCESS;
+    }
+
+    pj_mutex_lock(ffmpeg_factory.mutex);
+
+    /* Unregister FFMPEG codecs factory. */
+    status = pjmedia_vid_codec_mgr_unregister_factory(ffmpeg_factory.mgr,
+						      &ffmpeg_factory.base);
+
+    /* Destroy mutex. */
+    pj_mutex_destroy(ffmpeg_factory.mutex);
+
+    /* Destroy pool. */
+    pj_pool_release(ffmpeg_factory.pool);
+    ffmpeg_factory.pool = NULL;
+
+    return status;
+}
+
+
+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;
+
+    PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(info, PJ_EINVAL);
+
+    ci = find_codec(info);
+    if (!ci) {
+        return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Generate default attribute.
+ */
+static pj_status_t ffmpeg_default_attr( pjmedia_vid_codec_factory *factory, 
+				        const pjmedia_vid_codec_info *info, 
+				        pjmedia_vid_codec_param *attr )
+{
+    ffmpeg_codec_info *ci;
+
+    PJ_ASSERT_RETURN(factory==&ffmpeg_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(info && attr, PJ_EINVAL);
+
+    ci = find_codec(info);
+    if (!ci) {
+        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);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Enum codecs supported by this factory.
+ */
+static pj_status_t ffmpeg_enum_codecs( pjmedia_vid_codec_factory *factory,
+				       unsigned *count, 
+				       pjmedia_vid_codec_info codecs[])
+{
+    ffmpeg_codec_info *ci;
+    unsigned max;
+
+    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;
+
+    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;
+    }
+    pj_mutex_unlock(ffmpeg_factory.mutex);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Allocate a new codec instance.
+ */
+static pj_status_t ffmpeg_alloc_codec( pjmedia_vid_codec_factory *factory, 
+				       const pjmedia_vid_codec_info *info,
+				       pjmedia_vid_codec **p_codec)
+{
+    ffmpeg_private *ff;
+    ffmpeg_codec_info *ci;
+    pjmedia_vid_codec *codec;
+    pj_pool_t *pool = NULL;
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(factory && info && p_codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
+
+    ci = find_codec(info);
+    if (!ci) {
+        return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    /* Create pool for codec instance */
+    pool = pj_pool_create(ffmpeg_factory.pf, "ffmpeg codec", 512, 512, NULL);
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec);
+    if (!codec) {
+        status = PJ_ENOMEM;
+        goto on_error;
+    }
+    codec->op = &ffmpeg_op;
+    codec->factory = factory;
+    ff = PJ_POOL_ZALLOC_T(pool, ffmpeg_private);
+    if (!ff) {
+        status = PJ_ENOMEM;
+        goto on_error;
+    }
+    codec->codec_data = ff;
+    ff->pool = pool;
+    ff->enc = ci->enc;
+    ff->dec = ci->dec;
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+
+on_error:
+    if (pool)
+        pj_pool_release(pool);
+    return status;
+}
+
+/*
+ * Free codec.
+ */
+static pj_status_t ffmpeg_dealloc_codec( pjmedia_vid_codec_factory *factory, 
+				         pjmedia_vid_codec *codec )
+{
+    ffmpeg_private *ff;
+    pj_pool_t *pool;
+
+    PJ_ASSERT_RETURN(factory && codec, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &ffmpeg_factory.base, PJ_EINVAL);
+
+    /* Close codec, if it's not closed. */
+    ff = (ffmpeg_private*) codec->codec_data;
+    pool = ff->pool;
+    codec->codec_data = NULL;
+    pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Init codec.
+ */
+static pj_status_t ffmpeg_codec_init( pjmedia_vid_codec *codec, 
+				      pj_pool_t *pool )
+{
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+static void print_ffmpeg_err(int err)
+{
+#if LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72
+    char errbuf[512];
+    if (av_strerror(err, errbuf, sizeof(errbuf)) >= 0)
+        PJ_LOG(1, (THIS_FILE, "ffmpeg err %d: %s", err, errbuf));
+#else
+    PJ_LOG(1, (THIS_FILE, "ffmpeg err %d", err));
+#endif
+
+}
+
+/*
+static enum PixelFormat ffdec_nego_format(struct AVCodecContext *s, 
+                                          const enum PixelFormat * fmt)
+{
+    enum PixelFormat pix_fmt;
+
+    PJ_UNUSED_ARG(s);
+    PJ_UNUSED_ARG(fmt);
+
+    pjmedia_format_id_to_PixelFormat(PJMEDIA_FORMAT_BGRA, &pix_fmt);
+    return pix_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;
+
+    status = pjmedia_format_id_to_PixelFormat(ff->param.dec_fmt.id,
+                                              &pix_fmt);
+    if (status != PJ_SUCCESS)
+        return status;
+
+    while (((ff->param.dir & PJMEDIA_DIR_ENCODING) && ff->enc_ctx == NULL) ||
+           ((ff->param.dir & PJMEDIA_DIR_DECODING) && ff->dec_ctx == NULL))
+    {
+        pjmedia_dir dir;
+        AVCodecContext *ctx = NULL;
+        AVCodec *codec = NULL;
+        int err;
+
+        /* Set which direction to open */
+        if (ff->param.dir==PJMEDIA_DIR_ENCODING_DECODING && ff->enc!=ff->dec) {
+            dir = ff->enc_ctx? PJMEDIA_DIR_DECODING : PJMEDIA_DIR_ENCODING;
+        } else {
+            dir = ff->param.dir;
+        }
+
+        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;
+
+        if (dir & PJMEDIA_DIR_ENCODING) {
+            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;
+
+                /* 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 */
+            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;
+
+            /* For decoder, be more flexible */
+            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;
+        }
+
+        /* avcodec_open() should be protected */
+        pj_mutex_lock(ff_mutex);
+        err = avcodec_open(ctx, codec);
+        pj_mutex_unlock(ff_mutex);
+        if (err < 0) {
+            print_ffmpeg_err(err);
+            return PJ_EUNKNOWN;
+        }
+
+        if (dir & PJMEDIA_DIR_ENCODING)
+            ff->enc_ctx = ctx;
+        if (dir & PJMEDIA_DIR_DECODING)
+            ff->dec_ctx = ctx;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/*
+ * Open codec.
+ */
+static pj_status_t ffmpeg_codec_open( pjmedia_vid_codec *codec, 
+				      pjmedia_vid_codec_param *attr )
+{
+    ffmpeg_private *ff;
+    pj_status_t status;
+    pj_mutex_t *ff_mutex;
+
+    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));
+
+    ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex;
+    status = open_ffmpeg_codec(ff, ff_mutex);
+    if (status != PJ_SUCCESS)
+        goto on_error;
+
+    return PJ_SUCCESS;
+
+on_error:
+    ffmpeg_codec_close(codec);
+    return status;
+}
+
+/*
+ * Close codec.
+ */
+static pj_status_t ffmpeg_codec_close( pjmedia_vid_codec *codec )
+{
+    ffmpeg_private *ff;
+    pj_mutex_t *ff_mutex;
+
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+    ff = (ffmpeg_private*)codec->codec_data;
+    ff_mutex = ((struct ffmpeg_factory*)codec->factory)->mutex;
+
+    pj_mutex_lock(ff_mutex);
+    if (ff->enc_ctx) {
+        avcodec_close(ff->enc_ctx);
+        av_free(ff->enc_ctx);
+    }
+    if (ff->dec_ctx && ff->dec_ctx!=ff->enc_ctx) {
+        avcodec_close(ff->dec_ctx);
+        av_free(ff->dec_ctx);
+    }
+    if (ff->dec_parser_ctx) {
+        av_parser_close(ff->dec_parser_ctx);
+
+    }
+    ff->enc_ctx = NULL;
+    ff->dec_ctx = NULL;
+    ff->dec_parser_ctx = NULL;
+    pj_mutex_unlock(ff_mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Modify codec settings.
+ */
+static pj_status_t  ffmpeg_codec_modify( pjmedia_vid_codec *codec, 
+				         const pjmedia_vid_codec_param *attr )
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+
+    PJ_UNUSED_ARG(attr);
+    PJ_UNUSED_ARG(ff);
+
+    return PJ_ENOTSUP;
+}
+
+static pj_status_t  ffmpeg_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)
+{
+    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;
+    }
+}
+
+static pj_status_t  ffmpeg_unpacketize(pjmedia_vid_codec *codec,
+                                       const pj_uint8_t *payload,
+                                       pj_size_t   payload_len,
+                                       pj_uint8_t *buf,
+                                       pj_size_t  *buf_len)
+{
+    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 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,
+				        unsigned output_buf_len, 
+				        struct pjmedia_frame *output)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    pj_uint8_t *p = (pj_uint8_t*)input->buf;
+    AVFrame avframe;
+    pj_uint8_t *out_buf = (pj_uint8_t*)output->buf;
+    int out_buf_len = output_buf_len;
+    int err;
+    unsigned i;
+
+    /* 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) {
+        avframe.data[i] = p;
+        avframe.linesize[i] = ff->vafp.strides[i];
+        p += ff->vafp.plane_bytes[i];
+    }
+
+#ifdef _MSC_VER
+    /* Align stack for MSVC environment to avoid 'random' crash, as advised in
+     * http://ffmpeg.arrozcru.org/forum/viewtopic.php?f=1&t=549
+     */
+#   define VHALIGNCALL16(x) \
+    {\
+        _asm { mov ebx, esp }\
+        _asm { and esp, 0xfffffff0 }\
+        _asm { sub esp, 12 }\
+        _asm { push ebx }\
+        x;\
+        _asm { pop ebx }\
+        _asm { mov esp, ebx }\
+     }
+#else
+#   define VHALIGNCALL16(x)
+#endif
+
+    VHALIGNCALL16(err = avcodec_encode_video(ff->enc_ctx, out_buf, 
+                                             out_buf_len, &avframe));
+
+    if (err < 0) {
+        print_ffmpeg_err(err);
+        return PJ_EUNKNOWN;
+    } else {
+        output->size = err;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Decode frame.
+ */
+static pj_status_t ffmpeg_codec_decode( pjmedia_vid_codec *codec, 
+				        const struct pjmedia_frame *input,
+				        unsigned output_buf_len, 
+				        struct pjmedia_frame *output)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+    AVFrame avframe;
+    AVPacket avpacket;
+    int err, got_picture;
+
+    /* Check if decoder has been opened */
+    PJ_ASSERT_RETURN(ff->dec_ctx, PJ_EINVALIDOP);
+
+    PJ_UNUSED_ARG(output_buf_len);
+
+    /* Init frame to receive the decoded data, the ffmpeg codec context will
+     * automatically provide the decoded buffer (single buffer used for the
+     * whole decoding session, and seems to be freed when the codec context
+     * closed).
+     */
+    avcodec_get_frame_defaults(&avframe);
+
+    /* Init packet, the container of the encoded data */
+    av_init_packet(&avpacket);
+    avpacket.data = (pj_uint8_t*)input->buf;
+    avpacket.size = input->size;
+
+    /* ffmpeg warns:
+     * - input buffer padding, at least FF_INPUT_BUFFER_PADDING_SIZE
+     * - null terminated
+     * Normally, encoded buffer is allocated more than needed, so lets just
+     * bzero the input buffer end/pad, hope it will be just fine.
+     */
+    pj_bzero(avpacket.data+avpacket.size, FF_INPUT_BUFFER_PADDING_SIZE);
+
+#if LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72
+    avpacket.flags = AV_PKT_FLAG_KEY;
+#else
+    avpacket.flags = 0;
+#endif
+
+#if LIBAVCODEC_VERSION_MAJOR >= 52 && LIBAVCODEC_VERSION_MINOR >= 72
+    err = avcodec_decode_video2(ff->dec_ctx, &avframe, 
+                                &got_picture, &avpacket);
+#else
+    err = avcodec_decode_video(ff->dec_ctx, &avframe,
+                               &got_picture, avpacket.data, avpacket.size);
+#endif
+    if (err < 0) {
+        print_ffmpeg_err(err);
+        return PJ_EUNKNOWN;
+    } else if (got_picture) {
+        pjmedia_video_apply_fmt_param *vafp = (pjmedia_video_apply_fmt_param*)
+                                              &ff->vafp;
+        pj_uint8_t *q = (pj_uint8_t*)output->buf;
+        unsigned i;
+
+        /* Get the decoded data */
+        for (i = 0; i < ff->vfi->plane_cnt; ++i) {
+            pj_uint8_t *p = avframe.data[i];
+
+            /* 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];
+                }
+            }
+        }
+        output->size = vafp->framebytes;
+    } else {
+        return PJ_EUNKNOWN;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* 
+ * Recover lost frame.
+ */
+static pj_status_t  ffmpeg_codec_recover( pjmedia_vid_codec *codec, 
+				          unsigned output_buf_len, 
+				          struct pjmedia_frame *output)
+{
+    ffmpeg_private *ff = (ffmpeg_private*)codec->codec_data;
+
+    PJ_UNUSED_ARG(output_buf_len);
+    PJ_UNUSED_ARG(output);
+    PJ_UNUSED_ARG(ff);
+
+    return PJ_SUCCESS;
+}
+
+#ifdef _MSC_VER
+#   pragma comment( lib, "avcodec.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
new file mode 100644
index 0000000..93b61a5
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
@@ -0,0 +1,604 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/rand.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+
+#define THIS_FILE		"colorbar_dev.c"
+#define DEFAULT_CLOCK_RATE	90000
+#define DEFAULT_WIDTH		352 //640
+#define DEFAULT_HEIGHT		288 //480
+#define DEFAULT_FPS		25
+
+/* cbar_ device info */
+struct cbar_dev_info
+{
+    pjmedia_vid_dev_info	 info;
+};
+
+/* cbar_ factory */
+struct cbar_factory
+{
+    pjmedia_vid_dev_factory	 base;
+    pj_pool_t			*pool;
+    pj_pool_factory		*pf;
+
+    unsigned			 dev_count;
+    struct cbar_dev_info	*dev_info;
+};
+
+struct cbar_fmt_info {
+    pjmedia_format_id            fmt_id;        /* Format ID                */
+
+    /* Info for packed formats. */
+    unsigned                     c_offset[3];   /* Color component offset, 
+                                                   in bytes                 */
+    unsigned                     c_stride[3];   /* Color component stride,
+                                                   or distance between two 
+                                                   consecutive same color
+                                                   components, in bytes     */
+};
+
+/* Colorbar video source supports */
+static struct cbar_fmt_info cbar_fmts[] =
+{
+    /* Packed formats */
+    { PJMEDIA_FORMAT_YUY2,      {0, 1, 3}, {2, 4, 4} },
+    { PJMEDIA_FORMAT_UYVY,      {1, 0, 2}, {2, 4, 4} },
+    { PJMEDIA_FORMAT_YVYU,      {0, 3, 1}, {2, 4, 4} },
+    { PJMEDIA_FORMAT_RGBA,      {0, 1, 2}, {4, 4, 4} },
+    { PJMEDIA_FORMAT_RGB24,     {0, 1, 2}, {3, 3, 3} },
+    { PJMEDIA_FORMAT_BGRA,      {2, 1, 0}, {4, 4, 4} },
+
+    /* Planar formats */
+    { PJMEDIA_FORMAT_YV12 },
+    { PJMEDIA_FORMAT_I420 },
+    { PJMEDIA_FORMAT_I420JPEG },
+    { PJMEDIA_FORMAT_I422JPEG },
+};
+
+/* Video stream. */
+struct cbar_stream
+{
+    pjmedia_vid_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.     */
+
+    const struct cbar_fmt_info      *cbfi;
+    const pjmedia_video_format_info *vfi;
+    pjmedia_video_apply_fmt_param    vafp;
+    pj_uint8_t                      *first_line[PJMEDIA_MAX_VIDEO_PLANES];
+};
+
+
+/* Prototypes */
+static pj_status_t cbar_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t cbar_factory_destroy(pjmedia_vid_dev_factory *f);
+static unsigned    cbar_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t cbar_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					     unsigned index,
+					     pjmedia_vid_dev_info *info);
+static pj_status_t cbar_factory_default_param(pj_pool_t *pool,
+                                              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_stream_get_param(pjmedia_vid_stream *strm,
+					 pjmedia_vid_param *param);
+static pj_status_t cbar_stream_get_cap(pjmedia_vid_stream *strm,
+				       pjmedia_vid_dev_cap cap,
+				       void *value);
+static pj_status_t cbar_stream_set_cap(pjmedia_vid_stream *strm,
+				       pjmedia_vid_dev_cap cap,
+				       const void *value);
+static pj_status_t cbar_stream_get_frame(pjmedia_vid_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);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+    &cbar_factory_init,
+    &cbar_factory_destroy,
+    &cbar_factory_get_dev_count,
+    &cbar_factory_get_dev_info,
+    &cbar_factory_default_param,
+    &cbar_factory_create_stream
+};
+
+static pjmedia_vid_stream_op stream_op =
+{
+    &cbar_stream_get_param,
+    &cbar_stream_get_cap,
+    &cbar_stream_set_cap,
+    &cbar_stream_start,
+    &cbar_stream_get_frame,
+    NULL,
+    &cbar_stream_stop,
+    &cbar_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init cbar_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf)
+{
+    struct cbar_factory *f;
+    pj_pool_t *pool;
+
+    pool = pj_pool_create(pf, "cbar video", 512, 512, NULL);
+    f = PJ_POOL_ZALLOC_T(pool, struct cbar_factory);
+    f->pf = pf;
+    f->pool = pool;
+    f->base.op = &factory_op;
+
+    return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t cbar_factory_init(pjmedia_vid_dev_factory *f)
+{
+    struct cbar_factory *cf = (struct cbar_factory*)f;
+    struct cbar_dev_info *ddi;
+    unsigned i;
+
+    cf->dev_count = 1;
+    cf->dev_info = (struct cbar_dev_info*)
+ 		   pj_pool_calloc(cf->pool, cf->dev_count,
+ 				  sizeof(struct cbar_dev_info));
+
+    ddi = &cf->dev_info[0];
+    pj_bzero(ddi, sizeof(*ddi));
+    strncpy(ddi->info.name, "Colorbar generator",
+            sizeof(ddi->info.name));
+    ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+    strncpy(ddi->info.driver, "Colorbar", sizeof(ddi->info.driver));
+    ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+    ddi->info.dir = PJMEDIA_DIR_CAPTURE;
+    ddi->info.has_callback = PJ_FALSE;
+
+    ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+    ddi->info.fmt_cnt = sizeof(cbar_fmts)/sizeof(cbar_fmts[0]);
+    ddi->info.fmt = (pjmedia_format*)
+ 		    pj_pool_calloc(cf->pool, ddi->info.fmt_cnt,
+ 				   sizeof(pjmedia_format));
+    for (i = 0; i < ddi->info.fmt_cnt; i++) {
+        pjmedia_format *fmt = &ddi->info.fmt[i];
+        pjmedia_format_init_video(fmt, cbar_fmts[i].fmt_id,
+				  DEFAULT_WIDTH, DEFAULT_HEIGHT,
+				  DEFAULT_FPS, 1);
+    }
+
+    PJ_LOG(4, (THIS_FILE, "Colorbar video src initialized with %d devices:",
+	       cf->dev_count));
+
+    return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t cbar_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    struct cbar_factory *cf = (struct cbar_factory*)f;
+    pj_pool_t *pool = cf->pool;
+
+    cf->pool = NULL;
+    pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned cbar_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+    struct cbar_factory *cf = (struct cbar_factory*)f;
+    return cf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t cbar_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					     unsigned index,
+					     pjmedia_vid_dev_info *info)
+{
+    struct cbar_factory *cf = (struct cbar_factory*)f;
+
+    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
+
+    pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
+
+    return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t cbar_factory_default_param(pj_pool_t *pool,
+                                              pjmedia_vid_dev_factory *f,
+					      unsigned index,
+					      pjmedia_vid_param *param)
+{
+    struct cbar_factory *cf = (struct cbar_factory*)f;
+    struct cbar_dev_info *di = &cf->dev_info[index];
+
+    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
+
+    PJ_UNUSED_ARG(pool);
+
+    pj_bzero(param, sizeof(*param));
+    param->dir = PJMEDIA_DIR_CAPTURE;
+    param->cap_id = index;
+    param->rend_id = PJMEDIA_VID_INVALID_DEV;
+    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+    param->clock_rate = DEFAULT_CLOCK_RATE;
+    param->frame_rate.num = DEFAULT_FPS;
+    param->frame_rate.denum = 1;
+    pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
+
+    return PJ_SUCCESS;
+}
+
+static const struct cbar_fmt_info* get_cbar_fmt_info(pjmedia_format_id id)
+{
+    unsigned i;
+
+    for (i = 0; i < sizeof(cbar_fmts)/sizeof(cbar_fmts[0]); i++) {
+        if (cbar_fmts[i].fmt_id == id)
+            return &cbar_fmts[i];
+    }
+
+    return NULL;
+}
+
+static void fill_first_line(pj_uint8_t *first_lines[], 
+                            const struct cbar_fmt_info *cbfi,
+                            const pjmedia_video_format_info *vfi,
+                            const pjmedia_video_apply_fmt_param *vafp)
+{
+    typedef pj_uint8_t color_comp_t[3];
+    color_comp_t rgb_colors[] = 
+    { 
+        {255,255,255}, {255,255,0}, {0,255,255}, {0,255,0},
+        {255,0,255}, {255,0,0}, {0,0,255}, {0,0,0}
+    };
+    color_comp_t yuv_colors[] = 
+    { 
+        //{235,128,128}, {162,44,142}, {131,156,44}, {112,72,58},
+        //{84,184,198}, {65,100,212}, {35,212,114}, {16,128,128}
+        {235,128,128}, {210,16,146}, {170,166,16}, {145,54,34},
+        {106,202,222}, {81,90,240}, {41,240,110}, {16,128,128}
+    };
+
+    unsigned i, j, k;
+
+    if (vfi->plane_cnt == 1) {
+        /* Packed */
+
+        for (i = 0; i < 8; ++i) {
+            /* iterate bars */
+            for (j = 0; j < 3; ++j) {
+                /* iterate color components */
+                pj_uint8_t *p = NULL, c;
+                unsigned bar_width, inc_p;
+
+                if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB)
+                    c = rgb_colors[i][j];
+                else
+                    c = yuv_colors[i][j];
+
+                bar_width = vafp->size.w/8;
+                bar_width /= (cbfi->c_stride[j] * 8 / vfi->bpp);
+                inc_p = cbfi->c_stride[j];
+                p = first_lines[0] + bar_width*i*inc_p + cbfi->c_offset[j];
+
+                /* draw this color */
+                for (k = 0; k < bar_width; ++k) {
+                    *p = c;
+                    p += inc_p;
+                }
+            }
+        }
+
+    } else if (vfi->plane_cnt == 3) {
+
+        for (i = 0; i < 8; ++i) {
+            /* iterate bars */
+            for (j = 0; j < 3; ++j) {
+                /* iterate planes/color components */
+                pj_uint8_t *p = NULL, c;
+                unsigned bar_width;
+
+                if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB)
+                    c = rgb_colors[i][j];
+                else
+                    c = yuv_colors[i][j];
+
+                bar_width = vafp->strides[j]/8;
+                p = first_lines[j] + bar_width*i;
+
+                /* draw this plane/color */
+                for (k = 0; k < bar_width; ++k)
+                    *p++ = c;
+            }
+        }
+    }
+}
+
+/* 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)
+{
+    struct cbar_factory *cf = (struct cbar_factory*)f;
+    pj_pool_t *pool;
+    struct cbar_stream *strm;
+    const pjmedia_video_format_info *vfi;
+    pjmedia_video_apply_fmt_param vafp;
+    const struct cbar_fmt_info *cbfi;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+    PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
+		     param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
+		     PJ_EINVAL);
+
+    pj_bzero(&vafp, sizeof(vafp));
+
+    vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
+    cbfi = get_cbar_fmt_info(param->fmt.id);
+    if (!vfi || !cbfi)
+        return PJMEDIA_EVID_BADFORMAT;
+
+    vafp.size = param->fmt.det.vid.size;
+    if (vfi->apply_fmt(vfi, &vafp) != PJ_SUCCESS)
+        return PJMEDIA_EVID_BADFORMAT;
+
+    /* Create and Initialize stream descriptor */
+    pool = pj_pool_create(cf->pf, "cbar-dev", 512, 512, NULL);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    strm = PJ_POOL_ZALLOC_T(pool, struct cbar_stream);
+    pj_memcpy(&strm->param, param, sizeof(*param));
+    strm->pool = pool;
+    pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+    strm->user_data = user_data;
+    strm->vfi = vfi;
+    strm->cbfi = cbfi;
+    pj_memcpy(&strm->vafp, &vafp, sizeof(vafp));
+
+    for (i = 0; i < vfi->plane_cnt; ++i) {
+        strm->first_line[i] = pj_pool_alloc(pool, vafp.strides[i]);
+        pj_memset(strm->first_line[i], 255, vafp.strides[i]);
+    }
+
+    fill_first_line(strm->first_line, strm->cbfi, vfi, &strm->vafp);
+
+    /* Apply the remaining settings */
+/*    if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
+	cbar_stream_set_cap(&strm->base,
+		            PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+                            &param->fmt);
+    }
+*/
+    /* Done */
+    strm->base.op = &stream_op;
+    *p_vid_strm = &strm->base;
+
+    return PJ_SUCCESS;
+}
+
+/* API: Get stream info. */
+static pj_status_t cbar_stream_get_param(pjmedia_vid_stream *s,
+					 pjmedia_vid_param *pi)
+{
+    struct cbar_stream *strm = (struct cbar_stream*)s;
+
+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+    pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+/*    if (cbar_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+                            &pi->fmt.info_size) == PJ_SUCCESS)
+    {
+        pi->flags |= PJMEDIA_VID_DEV_CAP_INPUT_SCALE;
+    }
+*/
+    return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t cbar_stream_get_cap(pjmedia_vid_stream *s,
+				       pjmedia_vid_dev_cap cap,
+				       void *pval)
+{
+    struct cbar_stream *strm = (struct cbar_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+    {
+        return PJMEDIA_EVID_INVCAP;
+//	return PJ_SUCCESS;
+    } else {
+	return PJMEDIA_EVID_INVCAP;
+    }
+}
+
+/* API: set capability */
+static pj_status_t cbar_stream_set_cap(pjmedia_vid_stream *s,
+				       pjmedia_vid_dev_cap cap,
+				       const void *pval)
+{
+    struct cbar_stream *strm = (struct cbar_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+    {
+	return PJ_SUCCESS;
+    }
+
+    return PJMEDIA_EVID_INVCAP;
+}
+
+static pj_status_t spectrum_run(struct cbar_stream *d, pj_uint8_t *p,
+                                pj_size_t size)
+{
+    unsigned i;
+    pj_uint8_t *ptr = p;
+    pj_time_val tv;
+
+    PJ_UNUSED_ARG(size);
+
+    /* Subsequent lines */
+    for (i=0; i<d->vfi->plane_cnt; ++i) {
+        pj_uint8_t *plane_end;
+
+        plane_end = ptr + d->vafp.plane_bytes[i];
+        while (ptr < plane_end) {
+            pj_memcpy(ptr, d->first_line[i], d->vafp.strides[i]);
+            ptr += d->vafp.strides[i]; 
+        }
+    }
+
+    /* blinking dot */
+    pj_gettimeofday(&tv);
+    if (tv.msec < 660) {
+	enum { DOT_SIZE = 8 };
+        pj_uint8_t dot_clr_rgb[3] = {255, 255, 255};
+        pj_uint8_t dot_clr_yuv[3] = {235, 128, 128};
+
+        if (d->vfi->plane_cnt == 1) {
+            for (i = 0; i < 3; ++i) {
+                pj_uint8_t *ptr;
+                unsigned j, k, inc_ptr;
+                pj_size_t dot_size = DOT_SIZE;
+
+                dot_size /= (d->cbfi->c_stride[i] * 8 / d->vfi->bpp);
+                inc_ptr = d->cbfi->c_stride[i];
+                for (j = 0; j < dot_size; ++j) {
+                    ptr = p + d->vafp.strides[0]*(dot_size+j+1) - 
+                          2*dot_size*inc_ptr + d->cbfi->c_offset[i];
+                    for (k = 0; k < dot_size; ++k) {
+                        if (d->vfi->color_model == PJMEDIA_COLOR_MODEL_RGB)
+                            *ptr = dot_clr_rgb[i];
+                        else
+                            *ptr = dot_clr_yuv[i];
+                        ptr += inc_ptr;
+                    }
+                }
+            }
+        } else {
+            pj_size_t offset_p = 0;
+
+            for (i = 0; i < 3; ++i) {
+                pj_uint8_t *ptr, c;
+                unsigned j;
+                pj_size_t dot_size = DOT_SIZE;
+
+                if (d->vfi->color_model == PJMEDIA_COLOR_MODEL_RGB)
+                    c = dot_clr_rgb[i];
+                else
+                    c = dot_clr_yuv[i];
+
+                dot_size /= (d->vafp.size.w / d->vafp.strides[i]);
+                ptr = p + offset_p + d->vafp.strides[i]*(dot_size+1) - 
+                      2*dot_size;
+                for (j = 0; j < dot_size; ++j) {
+                    pj_memset(ptr, c, dot_size);
+                    ptr += d->vafp.strides[i];
+                }
+                offset_p += d->vafp.plane_bytes[i];
+            }
+        }
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* API: Get frame from stream */
+static pj_status_t cbar_stream_get_frame(pjmedia_vid_stream *strm,
+                                         pjmedia_frame *frame)
+{
+    struct cbar_stream *stream = (struct cbar_stream*)strm;
+
+    return spectrum_run(stream, frame->buf, frame->size);
+}
+
+/* API: Start stream. */
+static pj_status_t cbar_stream_start(pjmedia_vid_stream *strm)
+{
+    struct cbar_stream *stream = (struct cbar_stream*)strm;
+
+    PJ_UNUSED_ARG(stream);
+
+    PJ_LOG(4, (THIS_FILE, "Starting cbar video stream"));
+
+    return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t cbar_stream_stop(pjmedia_vid_stream *strm)
+{
+    struct cbar_stream *stream = (struct cbar_stream*)strm;
+
+    PJ_UNUSED_ARG(stream);
+
+    PJ_LOG(4, (THIS_FILE, "Stopping cbar video stream"));
+
+    return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t cbar_stream_destroy(pjmedia_vid_stream *strm)
+{
+    struct cbar_stream *stream = (struct cbar_stream*)strm;
+
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+    cbar_stream_stop(strm);
+
+    pj_pool_release(stream->pool);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC */
diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c
new file mode 100644
index 0000000..f4b95ac
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c
@@ -0,0 +1,903 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/unicode.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+
+#ifdef _MSC_VER
+#   pragma warning(push, 3)
+#endif
+
+#include <windows.h>
+#define COBJMACROS
+#include <DShow.h>
+
+#ifdef _MSC_VER
+#   pragma warning(pop)
+#endif
+
+#pragma comment(lib, "Strmiids.lib")
+#pragma comment(lib, "Rpcrt4.lib")
+
+#define THIS_FILE		"dshow_dev.c"
+#define DEFAULT_WIDTH		640
+#define DEFAULT_HEIGHT		480
+#define DEFAULT_FPS		25
+
+typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample);
+typedef struct NullRenderer NullRenderer;
+IBaseFilter* NullRenderer_Create(input_callback input_cb,
+                                 void *user_data);
+typedef struct SourceFilter SourceFilter;
+IBaseFilter* SourceFilter_Create(SourceFilter **pSrc);
+HRESULT SourceFilter_Deliver(SourceFilter *src, void *buf, long size);
+void SourceFilter_SetBufferSize(SourceFilter *src, long size);
+
+typedef struct dshow_fmt_info
+{
+    pjmedia_format_id    pjmedia_format;
+    const GUID          *dshow_format;
+} dshow_fmt_info;
+
+static dshow_fmt_info dshow_fmts[] =
+{
+    {PJMEDIA_FORMAT_YUY2, &MEDIASUBTYPE_YUY2} ,
+    {PJMEDIA_FORMAT_RGB24, &MEDIASUBTYPE_RGB24} ,
+    {PJMEDIA_FORMAT_RGB32, &MEDIASUBTYPE_RGB32} ,
+};
+
+/* dshow_ device info */
+struct dshow_dev_info
+{
+    pjmedia_vid_dev_info	 info;
+    unsigned			 dev_id;
+    WCHAR                        display_name[192];
+};
+
+/* dshow_ factory */
+struct dshow_factory
+{
+    pjmedia_vid_dev_factory	 base;
+    pj_pool_t			*pool;
+    pj_pool_factory		*pf;
+
+    unsigned			 dev_count;
+    struct dshow_dev_info	*dev_info;
+};
+
+/* Video stream. */
+struct dshow_stream
+{
+    pjmedia_vid_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.     */
+
+    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
+    {
+        IFilterGraph        *filter_graph;
+        IMediaFilter        *media_filter;
+        SourceFilter        *csource_filter;
+        IBaseFilter         *source_filter;
+        IBaseFilter         *rend_filter;
+        AM_MEDIA_TYPE       *mediatype;
+    } dgraph[2];
+};
+
+
+/* Prototypes */
+static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f);
+static unsigned    dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					      unsigned index,
+					      pjmedia_vid_dev_info *info);
+static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
+                                               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_stream_get_param(pjmedia_vid_stream *strm,
+					  pjmedia_vid_param *param);
+static pj_status_t dshow_stream_get_cap(pjmedia_vid_stream *strm,
+				        pjmedia_vid_dev_cap cap,
+				        void *value);
+static pj_status_t dshow_stream_set_cap(pjmedia_vid_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,
+                                          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);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+    &dshow_factory_init,
+    &dshow_factory_destroy,
+    &dshow_factory_get_dev_count,
+    &dshow_factory_get_dev_info,
+    &dshow_factory_default_param,
+    &dshow_factory_create_stream
+};
+
+static pjmedia_vid_stream_op stream_op =
+{
+    &dshow_stream_get_param,
+    &dshow_stream_get_cap,
+    &dshow_stream_set_cap,
+    &dshow_stream_start,
+    NULL,
+    &dshow_stream_put_frame,
+    &dshow_stream_stop,
+    &dshow_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init dshow_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf)
+{
+    struct dshow_factory *f;
+    pj_pool_t *pool;
+
+    pool = pj_pool_create(pf, "dshow video", 1000, 1000, NULL);
+    f = PJ_POOL_ZALLOC_T(pool, struct dshow_factory);
+    f->pf = pf;
+    f->pool = pool;
+    f->base.op = &factory_op;
+
+    return &f->base;
+}
+
+/* API: init factory */
+static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f)
+{
+    struct dshow_factory *df = (struct dshow_factory*)f;
+    struct dshow_dev_info *ddi;
+    int dev_count = 0;
+    unsigned c, i;
+    ICreateDevEnum *dev_enum = NULL;
+    IEnumMoniker *enum_cat = NULL;
+    IMoniker *moniker = NULL;
+    HRESULT hr;
+    ULONG fetched;
+
+    df->dev_count = 0;
+
+    CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+    hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
+                          CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
+                          (void**)&dev_enum);
+    if (FAILED(hr) ||
+        ICreateDevEnum_CreateClassEnumerator(dev_enum,
+            &CLSID_VideoInputDeviceCategory, &enum_cat, 0) != S_OK) 
+    {
+	PJ_LOG(4,(THIS_FILE, "Windows found no video input devices"));
+        if (dev_enum)
+            ICreateDevEnum_Release(dev_enum);
+	dev_count = 0;
+    } else {
+        while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
+            dev_count++;
+        }
+    }
+
+    /* Add renderer device */
+    dev_count += 1;
+    df->dev_info = (struct dshow_dev_info*)
+ 		   pj_pool_calloc(df->pool, dev_count,
+ 				  sizeof(struct dshow_dev_info));
+
+    if (dev_count > 1) {
+        IEnumMoniker_Reset(enum_cat);
+        while (IEnumMoniker_Next(enum_cat, 1, &moniker, &fetched) == S_OK) {
+            IPropertyBag *prop_bag;
+
+            hr = IMoniker_BindToStorage(moniker, 0, 0, &IID_IPropertyBag,
+                                        (void**)&prop_bag);
+            if (SUCCEEDED(hr)) {
+                VARIANT var_name;
+
+                VariantInit(&var_name);
+                hr = IPropertyBag_Read(prop_bag, L"FriendlyName",
+                                       &var_name, NULL);
+                if (SUCCEEDED(hr) && var_name.bstrVal) {
+                    WCHAR *wszDisplayName = NULL;
+
+                    ddi = &df->dev_info[df->dev_count++];
+                    pj_bzero(ddi, sizeof(*ddi));
+                    pj_unicode_to_ansi(var_name.bstrVal,
+                                       wcslen(var_name.bstrVal),
+                                       ddi->info.name,
+                                       sizeof(ddi->info.name));
+
+                    hr = IMoniker_GetDisplayName(moniker, NULL, NULL,
+                                                 &wszDisplayName);
+                    if (hr == S_OK && wszDisplayName) {
+                        pj_memcpy(ddi->display_name, wszDisplayName,
+                                  (wcslen(wszDisplayName)+1) * sizeof(WCHAR));
+                        CoTaskMemFree(wszDisplayName);
+                    }
+
+                    strncpy(ddi->info.driver, "dshow", 
+                            sizeof(ddi->info.driver));
+                    ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+                    ddi->info.dir = PJMEDIA_DIR_CAPTURE;
+                    ddi->info.has_callback = PJ_TRUE;
+
+                    /* Set the device capabilities here */
+                    ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+                    // TODO: Query the default width, height, fps, and
+                    // supported formats
+                }
+                VariantClear(&var_name);
+
+                IPropertyBag_Release(prop_bag);
+            }
+            IMoniker_Release(moniker);
+        }
+
+        IEnumMoniker_Release(enum_cat);
+        ICreateDevEnum_Release(dev_enum);
+    }
+
+    ddi = &df->dev_info[df->dev_count++];
+    pj_bzero(ddi, sizeof(*ddi));
+    strncpy(ddi->info.name,  "Video Mixing Renderer",
+            sizeof(ddi->info.name));
+    ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
+    strncpy(ddi->info.driver, "dshow", sizeof(ddi->info.driver));
+    ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+    ddi->info.dir = PJMEDIA_DIR_RENDER;
+    ddi->info.has_callback = PJ_FALSE;
+    ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+
+    for (c = 0; c < df->dev_count; c++) {
+        ddi = &df->dev_info[c];
+        ddi->info.fmt_cnt = sizeof(dshow_fmts)/sizeof(dshow_fmts[0]);
+        ddi->info.fmt = (pjmedia_format*)
+                        pj_pool_calloc(df->pool, ddi->info.fmt_cnt,
+                                       sizeof(pjmedia_format));
+
+        for (i = 0; i < ddi->info.fmt_cnt; i++) {
+            pjmedia_format *fmt = &ddi->info.fmt[i];
+
+            if (ddi->info.dir == PJMEDIA_DIR_RENDER && i > 0)
+                break;
+            pjmedia_format_init_video(fmt, dshow_fmts[i].pjmedia_format, 
+				      DEFAULT_WIDTH, DEFAULT_HEIGHT, 
+				      DEFAULT_FPS, 1);
+        }
+    }
+
+//    TODO:
+//    ddi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+
+    PJ_LOG(4, (THIS_FILE, "DShow initialized, found %d devices:", 
+	       df->dev_count));
+    for (c = 0; c < df->dev_count; ++c) {
+	PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)", 
+	       c,
+	       df->dev_info[c].info.name,
+	       df->dev_info[c].info.dir & PJMEDIA_DIR_CAPTURE ?
+               "capture" : "render"));
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    struct dshow_factory *df = (struct dshow_factory*)f;
+    pj_pool_t *pool = df->pool;
+
+    df->pool = NULL;
+    pj_pool_release(pool);
+
+    CoUninitialize();
+
+    return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+    struct dshow_factory *df = (struct dshow_factory*)f;
+    return df->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					      unsigned index,
+					      pjmedia_vid_dev_info *info)
+{
+    struct dshow_factory *df = (struct dshow_factory*)f;
+
+    PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
+
+    pj_memcpy(info, &df->dev_info[index].info, sizeof(*info));
+
+    return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t dshow_factory_default_param(pj_pool_t *pool,
+                                               pjmedia_vid_dev_factory *f,
+					       unsigned index,
+					       pjmedia_vid_param *param)
+{
+    struct dshow_factory *df = (struct dshow_factory*)f;
+    struct dshow_dev_info *di = &df->dev_info[index];
+
+    PJ_ASSERT_RETURN(index < df->dev_count, PJMEDIA_EVID_INVDEV);
+
+    PJ_UNUSED_ARG(pool);
+
+    pj_bzero(param, sizeof(*param));
+    if (di->info.dir & PJMEDIA_DIR_CAPTURE_RENDER) {
+	param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
+	param->cap_id = index;
+	param->rend_id = index;
+    } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
+	param->dir = PJMEDIA_DIR_CAPTURE;
+	param->cap_id = index;
+	param->rend_id = PJMEDIA_VID_INVALID_DEV;
+    } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
+	param->dir = PJMEDIA_DIR_RENDER;
+	param->rend_id = index;
+	param->cap_id = PJMEDIA_VID_INVALID_DEV;
+    } else {
+	return PJMEDIA_EVID_INVDEV;
+    }
+
+    /* Set the device capabilities here */
+    param->clock_rate = 9000;
+    param->frame_rate.num = 14;
+    param->frame_rate.denum = 1;
+    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+
+    pjmedia_format_copy(&param->fmt, &di->info.fmt[0]);
+
+    return PJ_SUCCESS;
+}
+
+static void input_cb(void *user_data, IMediaSample *pMediaSample)
+{
+    struct dshow_stream *strm = (struct dshow_stream*)user_data;
+    unsigned char *buffer;
+    pjmedia_frame frame;
+
+    if (strm->quit_flag) {
+        strm->cap_thread_exited = PJ_TRUE;
+        return;
+    }
+
+    if (strm->cap_thread_initialized == 0 || !pj_thread_is_registered())
+    {
+        pj_status_t status;
+
+	status = pj_thread_register("ds_cap", strm->cap_thread_desc, 
+				    &strm->cap_thread);
+        if (status != PJ_SUCCESS)
+            return;
+	strm->cap_thread_initialized = 1;
+	PJ_LOG(5,(THIS_FILE, "Capture thread started"));
+    }
+
+    IMediaSample_GetPointer(pMediaSample, &buffer);
+
+    frame.type = PJMEDIA_TYPE_VIDEO;
+    IMediaSample_GetPointer(pMediaSample, (BYTE **)&frame.buf);
+    frame.size = IMediaSample_GetActualDataLength(pMediaSample);
+    frame.bit_info = 0;
+    if (strm->vid_cb.capture_cb)
+        (*strm->vid_cb.capture_cb)(&strm->base, strm->user_data, &frame);
+}
+
+/* API: Put frame from stream */
+static pj_status_t dshow_stream_put_frame(pjmedia_vid_stream *strm,
+                                          const pjmedia_frame *frame)
+{
+    struct dshow_stream *stream = (struct dshow_stream*)strm;
+    unsigned i;
+
+    if (stream->quit_flag) {
+        stream->rend_thread_exited = PJ_TRUE;
+        return PJ_SUCCESS;
+    }
+
+    for (i = 0; i < 2; i++) {
+        if (stream->dgraph[i].csource_filter) {
+            HRESULT hr = SourceFilter_Deliver(stream->dgraph[i].csource_filter,
+                frame->buf, frame->size);
+
+            if (FAILED(hr)) {
+                return hr;
+            }
+            break;
+        }
+    }
+
+    return PJ_SUCCESS;
+}
+
+static dshow_fmt_info* get_dshow_format_info(pjmedia_format_id id)
+{
+    unsigned i;
+
+    for (i = 0; i < sizeof(dshow_fmts)/sizeof(dshow_fmts[0]); i++) {
+        if (dshow_fmts[i].pjmedia_format == id)
+            return &dshow_fmts[i];
+    }
+
+    return NULL;
+}
+
+static pj_status_t create_filter_graph(pjmedia_dir dir,
+                                       unsigned id,
+                                       struct dshow_factory *df,
+                                       struct dshow_stream *strm,
+                                       struct dshow_graph *graph)
+{
+    HRESULT hr;
+    IEnumPins *pEnum;
+    IPin *srcpin = NULL;
+    IPin *sinkpin = NULL;
+    AM_MEDIA_TYPE *mediatype= NULL, mtype;
+    VIDEOINFOHEADER *video_info, *vi = NULL;
+    pjmedia_video_format_detail *vfd;
+    const pjmedia_video_format_info *vfi;
+
+    vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
+                                        strm->param.fmt.id);
+    if (!vfi)
+        return PJMEDIA_EVID_BADFORMAT;
+
+    hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC,
+                          &IID_IFilterGraph, (LPVOID *)&graph->filter_graph);
+    if (FAILED(hr)) {
+        goto on_error;
+    }
+
+    hr = IFilterGraph_QueryInterface(graph->filter_graph, &IID_IMediaFilter,
+                                     (LPVOID *)&graph->media_filter);
+    if (FAILED(hr)) {
+        goto on_error;
+    }
+
+    if (dir == PJMEDIA_DIR_CAPTURE) {
+        IBindCtx *pbc;
+
+        hr = CreateBindCtx(0, &pbc);
+        if (SUCCEEDED (hr)) {
+            IMoniker *moniker;
+            DWORD pchEaten;
+
+            hr = MkParseDisplayName(pbc, 
+                                    df->dev_info[id].display_name,
+                                    &pchEaten, &moniker);
+            if (SUCCEEDED(hr)) {
+                hr = IMoniker_BindToObject(moniker, pbc, NULL,
+                                           &IID_IBaseFilter,
+                                           (LPVOID *)&graph->source_filter);
+                IMoniker_Release(moniker);
+            }
+            IBindCtx_Release(pbc);
+        }
+        if (FAILED(hr)) {
+            goto on_error;
+        }
+    } else {
+        graph->source_filter = SourceFilter_Create(&graph->csource_filter);
+    }
+
+    hr = IFilterGraph_AddFilter(graph->filter_graph, graph->source_filter,
+                                L"capture");
+    if (FAILED(hr)) {
+        goto on_error;
+    }
+
+    if (dir == PJMEDIA_DIR_CAPTURE) {
+        graph->rend_filter = NullRenderer_Create(input_cb, strm);
+    } else {
+        hr = CoCreateInstance(&CLSID_VideoMixingRenderer, NULL,
+                              CLSCTX_INPROC, &IID_IBaseFilter,
+                              (LPVOID *)&graph->rend_filter);
+        if (FAILED (hr)) {
+            goto on_error;
+        }
+    }
+
+    IBaseFilter_EnumPins(graph->rend_filter, &pEnum);
+    if (SUCCEEDED(hr)) {
+        // Loop through all the pins
+	IPin *pPin = NULL;
+
+        while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
+            PIN_DIRECTION pindirtmp;
+
+            hr = IPin_QueryDirection(pPin, &pindirtmp);
+            if (hr == S_OK && pindirtmp == PINDIR_INPUT) {
+                sinkpin = pPin;
+                break;
+            }
+	    IPin_Release(pPin);
+        }
+        IEnumPins_Release(pEnum);
+    }
+
+    vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
+
+    IBaseFilter_EnumPins(graph->source_filter, &pEnum);
+    if (SUCCEEDED(hr)) {
+        // Loop through all the pins
+	IPin *pPin = NULL;
+
+        while (IEnumPins_Next(pEnum, 1, &pPin, NULL) == S_OK) {
+            PIN_DIRECTION pindirtmp;
+            const GUID *dshow_format;
+                
+            dshow_format = get_dshow_format_info(strm->param.fmt.id)->
+                                                 dshow_format;
+
+            hr = IPin_QueryDirection(pPin, &pindirtmp);
+            if (hr != S_OK || pindirtmp != PINDIR_OUTPUT) {
+                if (SUCCEEDED(hr))
+                    IPin_Release(pPin);
+                continue;
+            }
+
+            if (dir == PJMEDIA_DIR_CAPTURE) {
+                IAMStreamConfig *streamcaps;
+
+                hr = IPin_QueryInterface(pPin, &IID_IAMStreamConfig,
+                                         (LPVOID *)&streamcaps);
+                if (SUCCEEDED(hr)) {
+                    VIDEO_STREAM_CONFIG_CAPS vscc;
+                    int i, isize, icount;
+
+                    IAMStreamConfig_GetNumberOfCapabilities(streamcaps,
+                                                            &icount, &isize);
+
+                    for (i = 0; i < icount; i++) {
+                        RPC_STATUS rpcstatus;
+
+                        hr = IAMStreamConfig_GetStreamCaps(streamcaps, i,
+                                                           &mediatype,
+                                                           (BYTE *)&vscc);
+                        if (FAILED (hr))
+                            continue;
+
+                        if (UuidCompare(&mediatype->subtype,
+                                        (UUID *)dshow_format,
+                                        &rpcstatus) == 0 &&
+                            rpcstatus == RPC_S_OK)
+                        {
+                            srcpin = pPin;
+                            graph->mediatype = mediatype;
+                            break;
+                        }
+                    }
+                    IAMStreamConfig_Release(streamcaps);
+                }
+            } else {
+                srcpin = pPin;
+                mediatype = graph->mediatype = &mtype;
+
+                memset (mediatype, 0, sizeof(AM_MEDIA_TYPE));
+                mediatype->majortype = MEDIATYPE_Video;
+                mediatype->subtype = *dshow_format;
+                mediatype->bFixedSizeSamples = TRUE;
+                mediatype->bTemporalCompression = FALSE;
+
+                vi = (VIDEOINFOHEADER *)
+                     CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
+                memset (vi, 0, sizeof(VIDEOINFOHEADER));
+                mediatype->formattype = FORMAT_VideoInfo;
+                mediatype->cbFormat = sizeof(VIDEOINFOHEADER);
+                mediatype->pbFormat = (BYTE *)vi;
+
+                vi->rcSource.bottom = vfd->size.h;
+                vi->rcSource.right = vfd->size.w;
+                vi->rcTarget.bottom = vfd->size.h;
+                vi->rcTarget.right = vfd->size.w;
+
+                vi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+                vi->bmiHeader.biPlanes = 1;
+                vi->bmiHeader.biBitCount = vfi->bpp;
+                vi->bmiHeader.biCompression = strm->param.fmt.id;
+            }
+            if (srcpin)
+                break;
+            IPin_Release(pPin);
+	}
+        IEnumPins_Release(pEnum);
+    }
+
+    if (!srcpin || !sinkpin || !mediatype) {
+        hr = S_FALSE;
+        goto on_error;
+    }
+    video_info = (VIDEOINFOHEADER *) mediatype->pbFormat;
+    video_info->bmiHeader.biWidth = vfd->size.w;
+    video_info->bmiHeader.biHeight = vfd->size.h;
+    if (vfd->fps.num != 0)
+        video_info->AvgTimePerFrame = (LONGLONG) (10000000 * 
+                                      vfd->fps.denum /
+                                      (double)vfd->fps.num);
+    video_info->bmiHeader.biSizeImage = DIBSIZE(video_info->bmiHeader);
+    mediatype->lSampleSize = DIBSIZE(video_info->bmiHeader);
+    if (graph->csource_filter)
+        SourceFilter_SetBufferSize(graph->csource_filter,
+                                   mediatype->lSampleSize);
+
+    hr = IFilterGraph_AddFilter(graph->filter_graph,
+                                (IBaseFilter *)graph->rend_filter,
+                                L"renderer");
+    if (FAILED(hr))
+        goto on_error;
+
+    hr = IFilterGraph_ConnectDirect(graph->filter_graph, srcpin, sinkpin,
+                                    mediatype);
+
+on_error:
+    if (srcpin)
+        IPin_Release(srcpin);
+    if (sinkpin)
+        IPin_Release(sinkpin);
+    if (vi)
+        CoTaskMemFree(vi);
+    if (FAILED(hr))
+        return hr;
+
+    return PJ_SUCCESS;
+}
+
+/* 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)
+{
+    struct dshow_factory *df = (struct dshow_factory*)f;
+    pj_pool_t *pool;
+    struct dshow_stream *strm;
+    unsigned ngraph = 0;
+    pj_status_t status;
+
+    if (!get_dshow_format_info(param->fmt.id))
+        return PJMEDIA_EVID_BADFORMAT;
+
+    /* Create and Initialize stream descriptor */
+    pool = pj_pool_create(df->pf, "dshow-dev", 1000, 1000, NULL);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    strm = PJ_POOL_ZALLOC_T(pool, struct dshow_stream);
+    pj_memcpy(&strm->param, param, sizeof(*param));
+    strm->pool = pool;
+    pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+    strm->user_data = user_data;
+
+    /* Create capture stream here */
+    if (param->dir & PJMEDIA_DIR_CAPTURE) {
+        status = create_filter_graph(PJMEDIA_DIR_CAPTURE, param->cap_id,
+                                     df, strm, &strm->dgraph[ngraph++]);
+        if (status != PJ_SUCCESS)
+            goto on_error;
+    }
+
+    /* Create render stream here */
+    if (param->dir & PJMEDIA_DIR_RENDER) {
+        status = create_filter_graph(PJMEDIA_DIR_RENDER, param->rend_id,
+                                     df, strm, &strm->dgraph[ngraph++]);
+        if (status != PJ_SUCCESS)
+            goto on_error;
+    }
+
+    /* Apply the remaining settings */
+    if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
+	dshow_stream_set_cap(&strm->base,
+		            PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+		            &param->window);
+    }
+
+    /* Done */
+    strm->base.op = &stream_op;
+    *p_vid_strm = &strm->base;
+
+    return PJ_SUCCESS;
+ 
+on_error:
+    dshow_stream_destroy((pjmedia_vid_stream *)strm);
+    return PJ_EUNKNOWN;
+}
+
+/* API: Get stream info. */
+static pj_status_t dshow_stream_get_param(pjmedia_vid_stream *s,
+					  pjmedia_vid_param *pi)
+{
+    struct dshow_stream *strm = (struct dshow_stream*)s;
+
+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+    pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+    if (dshow_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+			     &pi->window) == PJ_SUCCESS)
+    {
+        pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t dshow_stream_get_cap(pjmedia_vid_stream *s,
+				        pjmedia_vid_dev_cap cap,
+				        void *pval)
+{
+    struct dshow_stream *strm = (struct dshow_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+    {
+	*(unsigned*)pval = 0;
+	return PJ_SUCCESS;
+    } else {
+	return PJMEDIA_EVID_INVCAP;
+    }
+}
+
+/* API: set capability */
+static pj_status_t dshow_stream_set_cap(pjmedia_vid_stream *s,
+				        pjmedia_vid_dev_cap cap,
+				        const void *pval)
+{
+    struct dshow_stream *strm = (struct dshow_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+    {
+	// set renderer's window here
+	return PJ_SUCCESS;
+    }
+
+    return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: Start stream. */
+static pj_status_t dshow_stream_start(pjmedia_vid_stream *strm)
+{
+    struct dshow_stream *stream = (struct dshow_stream*)strm;
+    unsigned i;
+
+    stream->quit_flag = PJ_FALSE;
+    stream->cap_thread_exited = PJ_FALSE;
+    stream->rend_thread_exited = PJ_FALSE;
+
+    for (i = 0; i < 2; i++) {
+        HRESULT hr;
+
+        if (!stream->dgraph[i].media_filter)
+            continue;
+        hr = IMediaFilter_Run(stream->dgraph[i].media_filter, 0);
+        if (FAILED(hr)) {
+            return hr;
+        }
+    }
+
+    PJ_LOG(4, (THIS_FILE, "Starting dshow video stream"));
+
+    return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t dshow_stream_stop(pjmedia_vid_stream *strm)
+{
+    struct dshow_stream *stream = (struct dshow_stream*)strm;
+    unsigned i;
+
+    stream->quit_flag = PJ_TRUE;
+    if (stream->cap_thread) {
+        for (i=0; !stream->cap_thread_exited && i<100; ++i)
+	    pj_thread_sleep(10);
+    }
+    for (i=0; !stream->rend_thread_exited && i<100; ++i)
+	pj_thread_sleep(10);
+
+    for (i = 0; i < 2; i++) {
+        if (!stream->dgraph[i].media_filter)
+            continue;
+        IMediaFilter_Stop(stream->dgraph[i].media_filter);
+    }
+
+    PJ_LOG(4, (THIS_FILE, "Stopping dshow video stream"));
+
+    return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t dshow_stream_destroy(pjmedia_vid_stream *strm)
+{
+    struct dshow_stream *stream = (struct dshow_stream*)strm;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+    dshow_stream_stop(strm);
+
+    for (i = 0; i < 2; i++) {
+        if (stream->dgraph[i].source_filter) {
+            IBaseFilter_Release(stream->dgraph[i].source_filter);
+            stream->dgraph[i].source_filter = NULL;
+        }
+        if (stream->dgraph[i].rend_filter) {
+            IBaseFilter_Release(stream->dgraph[i].rend_filter);
+            stream->dgraph[i].rend_filter = NULL;
+        }
+        if (stream->dgraph[i].media_filter) {
+            IMediaFilter_Release(stream->dgraph[i].media_filter);
+            stream->dgraph[i].media_filter = NULL;
+        }
+        if (stream->dgraph[i].filter_graph) {
+            IFilterGraph_Release(stream->dgraph[i].filter_graph);
+            stream->dgraph[i].filter_graph = NULL;
+        }
+    }
+
+    pj_pool_release(stream->pool);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
diff --git a/pjmedia/src/pjmedia-videodev/dshowclasses.cpp b/pjmedia/src/pjmedia-videodev/dshowclasses.cpp
new file mode 100644
index 0000000..ad9d070
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/dshowclasses.cpp
@@ -0,0 +1,209 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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
+ */
+#undef _DEBUG
+#undef DEBUG
+
+#include <streams.h>
+
+#pragma comment(lib, "Strmbase.lib")
+
+typedef void (*input_callback)(void *user_data, IMediaSample *pMediaSample);
+
+const GUID CLSID_NullRenderer = {0xF9168C5E, 0xCEB2, 0x4FAA, {0xB6, 0xBF,
+                                 0x32, 0x9B, 0xF3, 0x9F, 0xA1, 0xE4}};
+
+const GUID CLSID_SourceFilter = {0xF9168C5E, 0xCEB2, 0x4FAA, {0xB6, 0xBF,
+                                 0x32, 0x9B, 0xF3, 0x9F, 0xA1, 0xE5}};
+
+class NullRenderer: public CBaseRenderer
+{
+public:
+    NullRenderer(HRESULT *pHr);
+    virtual ~NullRenderer();
+
+    virtual HRESULT CheckMediaType(const CMediaType *pmt);
+    virtual HRESULT DoRenderSample(IMediaSample *pMediaSample);
+
+    input_callback  input_cb;
+    void           *user_data;
+};
+
+class OutputPin: public CBaseOutputPin
+{
+public:
+    OutputPin(CBaseFilter *pFilter, CCritSec *pLock, HRESULT *pHr);
+    ~OutputPin();
+
+    HRESULT Push(void *buf, long size);
+
+    virtual HRESULT CheckMediaType(const CMediaType *pmt);
+    virtual HRESULT DecideBufferSize(IMemAllocator *pAlloc, 
+                                     ALLOCATOR_PROPERTIES *ppropInputRequest);
+
+    long bufSize;
+};
+
+class SourceFilter: public CBaseFilter
+{
+public:
+    SourceFilter();
+    ~SourceFilter();
+
+    int GetPinCount();
+    CBasePin* GetPin(int n);
+
+protected:
+    CCritSec lock;
+    OutputPin* outPin;
+};
+
+OutputPin::OutputPin(CBaseFilter *pFilter, CCritSec *pLock, HRESULT *pHr):
+    CBaseOutputPin("OutputPin", pFilter, pLock, pHr, L"OutputPin")
+{
+}
+
+OutputPin::~OutputPin()
+{
+}
+
+HRESULT OutputPin::CheckMediaType(const CMediaType *pmt)
+{
+    return S_OK;
+}
+
+HRESULT OutputPin::DecideBufferSize(IMemAllocator *pAlloc, 
+                                    ALLOCATOR_PROPERTIES *ppropInputRequest)
+{
+    ALLOCATOR_PROPERTIES properties;
+
+    ppropInputRequest->cbBuffer = bufSize;
+    ppropInputRequest->cBuffers = 1;
+
+    /* First set the buffer descriptions we're interested in */
+    pAlloc->SetProperties(ppropInputRequest, &properties);
+
+    return S_OK;
+}
+
+HRESULT OutputPin::Push(void *buf, long size)
+{
+    HRESULT hr;
+    IMediaSample *pSample;
+    BYTE *dst_buf;
+
+    /**
+     * Hold the critical section here as the pin might get disconnected
+     * during the Deliver() method call.
+     */
+    m_pLock->Lock();
+
+    hr = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
+    if (FAILED(hr))
+        goto on_error;
+
+    pSample->GetPointer(&dst_buf);
+    memcpy(dst_buf, buf, size);
+    pSample->SetActualDataLength(size);
+
+    hr = Deliver(pSample);
+
+    pSample->Release();
+
+on_error:
+    m_pLock->Unlock();
+    return hr;
+}
+
+SourceFilter::SourceFilter(): CBaseFilter("SourceFilter", NULL, &lock, 
+                                          CLSID_SourceFilter)
+{
+    HRESULT hr;
+    outPin = new OutputPin(this, &lock, &hr);
+}
+
+SourceFilter::~SourceFilter()
+{
+}
+
+int SourceFilter::GetPinCount()
+{
+    return 1;
+}
+
+CBasePin* SourceFilter::GetPin(int n)
+{
+    return outPin;
+}
+
+NullRenderer::NullRenderer(HRESULT *pHr): CBaseRenderer(CLSID_NullRenderer,
+                                                        "NullRenderer",
+                                                        NULL, pHr)
+{
+    input_cb = NULL;
+}
+
+NullRenderer::~NullRenderer()
+{
+}
+
+HRESULT NullRenderer::CheckMediaType(const CMediaType *pmt)
+{
+    return S_OK;
+}
+
+HRESULT NullRenderer::DoRenderSample(IMediaSample *pMediaSample)
+{
+    if (input_cb)
+        input_cb(user_data, pMediaSample);
+
+    return S_OK;
+}
+
+extern "C" IBaseFilter* NullRenderer_Create(input_callback input_cb,
+                                             void *user_data)
+{
+    HRESULT hr;
+    NullRenderer *renderer = new NullRenderer(&hr);
+    renderer->AddRef();
+    renderer->input_cb = input_cb;
+    renderer->user_data = user_data;
+
+    return (CBaseFilter *)renderer;
+}
+
+extern "C" IBaseFilter* SourceFilter_Create(SourceFilter **pSrc)
+{
+    SourceFilter *src = new SourceFilter();
+    src->AddRef();
+    *pSrc = src;
+
+    return (CBaseFilter *)src;
+}
+
+extern "C" HRESULT SourceFilter_Deliver(SourceFilter *src,
+                                        void *buf, long size)
+{
+    return ((OutputPin *)src->GetPin(0))->Push(buf, size);
+}
+
+extern "C" void SourceFilter_SetBufferSize(SourceFilter *src,
+                                           long size)
+{
+    ((OutputPin *)src->GetPin(0))->bufSize = size;
+}
diff --git a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
new file mode 100644
index 0000000..abedbf8
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
@@ -0,0 +1,494 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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
+ */
+
+/* Video device with ffmpeg backend, currently only capture devices are
+ * implemented.
+ *
+ * Issues:
+ * - no device enumeration (ffmpeg limitation), so this uses "host API" enum
+ *   instead
+ * - need stricter filter on "host API" enum, currently audio capture devs are
+ *   still listed.
+ * - no format enumeration, currently hardcoded to PJMEDIA_FORMAT_RGB24 only
+ * - tested on Vista only (vfw backend) with virtual cam
+ * - vfw backend produce bottom up pictures
+ * - using VS IDE, this cannot run under debugger!
+ */
+
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/unicode.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+
+#define THIS_FILE		"ffmpeg.c"
+
+#include "../pjmedia/ffmpeg_util.h"
+#include <libavdevice/avdevice.h>
+#include <libavformat/avformat.h>
+
+#define MAX_DEV_CNT     8
+
+typedef struct ffmpeg_dev_info
+{
+    pjmedia_vid_dev_info         base;
+    AVInputFormat               *host_api;
+    const char                  *def_devname;
+} ffmpeg_dev_info;
+
+
+typedef struct ffmpeg_factory
+{
+    pjmedia_vid_dev_factory	 base;
+    pj_pool_factory		*pf;
+    pj_pool_t                   *pool;
+    unsigned                     dev_count;
+    ffmpeg_dev_info              dev_info[MAX_DEV_CNT];
+} ffmpeg_factory;
+
+
+typedef struct ffmpeg_stream
+{
+    pjmedia_vid_stream           base;
+    ffmpeg_factory              *factory;
+    pj_pool_t                   *pool;
+    pjmedia_vid_param            param;
+    AVFormatContext             *ff_fmt_ctx;
+} ffmpeg_stream;
+
+
+/* Prototypes */
+static pj_status_t ffmpeg_factory_init(pjmedia_vid_dev_factory *f);
+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);
+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_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,
+                                           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);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+    &ffmpeg_factory_init,
+    &ffmpeg_factory_destroy,
+    &ffmpeg_factory_get_dev_count,
+    &ffmpeg_factory_get_dev_info,
+    &ffmpeg_factory_default_param,
+    &ffmpeg_factory_create_stream
+};
+
+static pjmedia_vid_stream_op stream_op =
+{
+    &ffmpeg_stream_get_param,
+    &ffmpeg_stream_get_cap,
+    &ffmpeg_stream_set_cap,
+    &ffmpeg_stream_start,
+    &ffmpeg_stream_get_frame,
+    NULL,
+    &ffmpeg_stream_stop,
+    &ffmpeg_stream_destroy
+};
+
+
+static void print_ffmpeg_err(int err)
+{
+    char errbuf[512];
+    if (av_strerror(err, errbuf, sizeof(errbuf)) >= 0)
+        PJ_LOG(1, (THIS_FILE, "ffmpeg err %d: %s", err, errbuf));
+
+}
+
+static void print_ffmpeg_log(void* ptr, int level, const char* fmt, va_list vl)
+{
+    PJ_UNUSED_ARG(ptr);
+    PJ_UNUSED_ARG(level);
+    vfprintf(stdout, fmt, vl);
+}
+
+
+static pj_status_t ffmpeg_capture_open(AVFormatContext **ctx,
+                                       AVInputFormat *ifmt,
+                                       const char *dev_name,
+                                       const pjmedia_vid_param *param)
+{
+    AVFormatParameters fp;
+    pjmedia_video_format_detail *fmt_detail;
+    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;
+
+    /* Init ffmpeg format context */
+    *ctx = avformat_alloc_context();
+
+    /* 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.pix_fmt = PIX_FMT_BGR24;
+    fp.time_base.num = param->frame_rate.denum;
+    fp.time_base.den = param->frame_rate.num;
+
+    /* Open capture stream */
+    err = av_open_input_stream(ctx, NULL, dev_name, ifmt, &fp);
+    if (err < 0) {
+        *ctx = NULL; /* ffmpeg freed its states on failure, do we must too */
+        print_ffmpeg_err(err);
+        return PJ_EUNKNOWN;
+    }
+
+    return PJ_SUCCESS;
+}
+
+static void ffmpeg_capture_close(AVFormatContext *ctx)
+{
+    if (ctx)
+        av_close_input_stream(ctx);
+}
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init ffmpeg_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_ffmpeg_factory(pj_pool_factory *pf)
+{
+    ffmpeg_factory *f;
+    pj_pool_t *pool;
+
+    pool = pj_pool_create(pf, "ffmpeg_cap_dev", 1000, 1000, NULL);
+    f = PJ_POOL_ZALLOC_T(pool, ffmpeg_factory);
+
+    f->pool = pool;
+    f->pf = pf;
+    f->base.op = &factory_op;
+
+    avdevice_register_all();
+
+    return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t ffmpeg_factory_init(pjmedia_vid_dev_factory *f)
+{
+    ffmpeg_factory *ff = (ffmpeg_factory*)f;
+    AVInputFormat *p;
+    ffmpeg_dev_info *info;
+
+    av_log_set_callback(&print_ffmpeg_log);
+    av_log_set_level(AV_LOG_DEBUG);
+
+    /* TODO: this should enumerate devices, now it enumerates host APIs */
+    ff->dev_count = 0;
+    p = av_iformat_next(NULL);
+    while (p) {
+        if (p->flags & AVFMT_NOFILE) {
+            unsigned i;
+
+            info = &ff->dev_info[ff->dev_count++];
+            pj_bzero(info, sizeof(*info));
+            pj_ansi_strncpy(info->base.name, "default", 
+                            sizeof(info->base.name));
+            pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver),
+                             "%s (ffmpeg)", p->name);
+            info->base.dir = PJMEDIA_DIR_CAPTURE;
+            info->base.has_callback = PJ_FALSE;
+
+            info->host_api = p;
+
+#if defined(PJ_WIN32) && PJ_WIN32!=0
+            info->def_devname = "0";
+#elif defined(PJ_LINUX) && PJ_LINUX!=0
+            info->def_devname = "/dev/video0";
+#endif
+
+            /* Set supported formats, currently hardcoded to RGB24 only */
+            info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+            info->base.fmt_cnt = 1;
+            info->base.fmt = (pjmedia_format*)
+ 		             pj_pool_calloc(ff->pool, info->base.fmt_cnt,
+ 				            sizeof(pjmedia_format));
+            for (i = 0; i < info->base.fmt_cnt; ++i) {
+                pjmedia_format *fmt = &info->base.fmt[i];
+
+                fmt->id = PJMEDIA_FORMAT_RGB24;
+                fmt->type = PJMEDIA_TYPE_VIDEO;
+                fmt->detail_type = PJMEDIA_FORMAT_DETAIL_NONE;
+            }
+        }
+        p = av_iformat_next(p);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    ffmpeg_factory *ff = (ffmpeg_factory*)f;
+
+    ff->dev_count = 0;
+
+    return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+    ffmpeg_factory *ff = (ffmpeg_factory*)f;
+    return ff->dev_count;
+}
+
+/* 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)
+{
+    ffmpeg_factory *ff = (ffmpeg_factory*)f;
+
+    PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV);
+
+    pj_memcpy(info, &ff->dev_info[index].base, sizeof(*info));
+
+    return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t ffmpeg_factory_default_param(pj_pool_t *pool,
+                                                pjmedia_vid_dev_factory *f,
+					        unsigned index,
+					        pjmedia_vid_param *param)
+{
+    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);
+    info = &ff->dev_info[index];
+
+    pj_bzero(param, sizeof(*param));
+    param->dir = PJMEDIA_DIR_CAPTURE;
+    param->cap_id = index;
+    param->rend_id = PJMEDIA_VID_INVALID_DEV;
+    param->clock_rate = 0;
+
+    /* 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;
+
+    return PJ_SUCCESS;
+}
+
+
+
+/* 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)
+{
+    ffmpeg_factory *ff = (ffmpeg_factory*)f;
+    pj_pool_t *pool;
+    ffmpeg_stream *strm;
+
+    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_UNUSED_ARG(cb);
+    PJ_UNUSED_ARG(user_data);
+
+    /* Create and Initialize stream descriptor */
+    pool = pj_pool_create(ff->pf, "ffmpeg-dev", 1000, 1000, NULL);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    strm = PJ_POOL_ZALLOC_T(pool, struct ffmpeg_stream);
+    strm->factory = (ffmpeg_factory*)f;
+    strm->pool = pool;
+    pj_memcpy(&strm->param, param, sizeof(*param));
+
+    /* Done */
+    strm->base.op = &stream_op;
+    *p_vid_strm = &strm->base;
+
+    return PJ_SUCCESS;
+}
+
+/* API: Get stream info. */
+static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_stream *s,
+					   pjmedia_vid_param *pi)
+{
+    ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+    pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+    return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_stream *s,
+				        pjmedia_vid_dev_cap cap,
+				        void *pval)
+{
+    ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+    PJ_UNUSED_ARG(cap);
+    PJ_UNUSED_ARG(pval);
+
+    return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: set capability */
+static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_stream *s,
+				        pjmedia_vid_dev_cap cap,
+				        const void *pval)
+{
+    ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+    PJ_UNUSED_ARG(cap);
+    PJ_UNUSED_ARG(pval);
+
+    return PJMEDIA_EVID_INVCAP;
+}
+
+
+/* API: Start stream. */
+static pj_status_t ffmpeg_stream_start(pjmedia_vid_stream *s)
+{
+    ffmpeg_stream *strm = (ffmpeg_stream*)s;
+    ffmpeg_dev_info *info;
+    pj_status_t status;
+
+    info = &strm->factory->dev_info[strm->param.cap_id];
+
+    PJ_LOG(4, (THIS_FILE, "Starting ffmpeg capture stream"));
+
+    status = ffmpeg_capture_open(&strm->ff_fmt_ctx, info->host_api,
+                                 info->def_devname, &strm->param);
+    if (status != PJ_SUCCESS) {
+        /* must set ffmpeg states to NULL on any failure */
+        strm->ff_fmt_ctx = NULL;
+    }
+
+    return status;
+}
+
+
+/* API: Get frame from stream */
+static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_stream *s,
+                                           pjmedia_frame *frame)
+{
+    ffmpeg_stream *strm = (ffmpeg_stream*)s;
+    AVPacket p;
+    int err;
+
+    err = av_read_frame(strm->ff_fmt_ctx, &p);
+    if (err < 0) {
+        print_ffmpeg_err(err);
+        return PJ_EUNKNOWN;
+    }
+
+    pj_bzero(frame, sizeof(*frame));
+    frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
+    frame->buf = p.data;
+    frame->size = p.size;
+
+    return PJ_SUCCESS;
+}
+
+
+/* API: Stop stream. */
+static pj_status_t ffmpeg_stream_stop(pjmedia_vid_stream *s)
+{
+    ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+    PJ_LOG(4, (THIS_FILE, "Stopping ffmpeg capture stream"));
+
+    ffmpeg_capture_close(strm->ff_fmt_ctx);
+    strm->ff_fmt_ctx = NULL;
+
+    return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_stream *s)
+{
+    ffmpeg_stream *strm = (ffmpeg_stream*)s;
+
+    PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+    ffmpeg_stream_stop(s);
+
+    pj_pool_release(strm->pool);
+
+    return PJ_SUCCESS;
+}
+
+#ifdef _MSC_VER
+#   pragma comment( lib, "avdevice.lib")
+#   pragma comment( lib, "avformat.lib")
+#   pragma comment( lib, "avutil.lib")
+#endif
+
+#endif	/* PJMEDIA_VIDEO_DEV_HAS_FFMPEG */
diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev.c b/pjmedia/src/pjmedia-videodev/sdl_dev.c
new file mode 100644
index 0000000..4b9b2d8
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/sdl_dev.c
@@ -0,0 +1,708 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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/converter.h>
+#include <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+
+#include <SDL.h>
+
+#define THIS_FILE		"sdl_dev.c"
+#define DEFAULT_CLOCK_RATE	90000
+#define DEFAULT_WIDTH		640
+#define DEFAULT_HEIGHT		480
+#define DEFAULT_FPS		25
+
+
+typedef struct sdl_fmt_info
+{
+    pjmedia_format_id   fmt_id;
+    Uint32              sdl_format;
+    Uint32              Rmask;
+    Uint32              Gmask;
+    Uint32              Bmask;
+    Uint32              Amask;
+} sdl_fmt_info;
+
+static sdl_fmt_info sdl_fmts[] =
+{
+#if PJ_IS_BIG_ENDIAN
+    {PJMEDIA_FORMAT_RGBA,  0, 0xFF000000, 0xFF0000, 0xFF00, 0xFF} ,
+    {PJMEDIA_FORMAT_RGB24, 0, 0xFF0000, 0xFF00, 0xFF, 0} ,
+    {PJMEDIA_FORMAT_BGRA,  0, 0xFF00, 0xFF0000, 0xFF000000, 0xFF} ,
+#else
+    {PJMEDIA_FORMAT_RGBA,  0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000} ,
+    {PJMEDIA_FORMAT_RGB24, 0, 0xFF, 0xFF00, 0xFF0000, 0} ,
+    {PJMEDIA_FORMAT_BGRA,  0, 0xFF0000, 0xFF00, 0xFF, 0xFF000000} ,
+#endif
+
+    {PJMEDIA_FORMAT_DIB  , 0, 0xFF0000, 0xFF00, 0xFF, 0} ,
+
+    {PJMEDIA_FORMAT_YUY2, SDL_YUY2_OVERLAY, 0, 0, 0, 0} ,
+    {PJMEDIA_FORMAT_UYVY, SDL_UYVY_OVERLAY, 0, 0, 0, 0} ,
+    {PJMEDIA_FORMAT_YVYU, SDL_YVYU_OVERLAY, 0, 0, 0, 0} ,
+    {PJMEDIA_FORMAT_I420, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
+    {PJMEDIA_FORMAT_YV12, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
+    {PJMEDIA_FORMAT_I420JPEG, SDL_IYUV_OVERLAY, 0, 0, 0, 0} ,
+    {PJMEDIA_FORMAT_I422JPEG, SDL_YV12_OVERLAY, 0, 0, 0, 0} ,
+};
+
+/* sdl_ device info */
+struct sdl_dev_info
+{
+    pjmedia_vid_dev_info	 info;
+};
+
+/* sdl_ factory */
+struct sdl_factory
+{
+    pjmedia_vid_dev_factory	 base;
+    pj_pool_t			*pool;
+    pj_pool_factory		*pf;
+
+    unsigned			 dev_count;
+    struct sdl_dev_info	        *dev_info;
+};
+
+/* Video stream. */
+struct sdl_stream
+{
+    pjmedia_vid_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.     */
+
+    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.          */
+
+    /* For frame conversion */
+    pjmedia_converter       *conv;
+    pjmedia_conversion_param conv_param;
+    pjmedia_frame            conv_buf;
+
+    pjmedia_video_apply_fmt_param vafp;
+};
+
+
+/* Prototypes */
+static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
+static unsigned    sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					    unsigned index,
+					    pjmedia_vid_dev_info *info);
+static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
+                                             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_stream_get_param(pjmedia_vid_stream *strm,
+					pjmedia_vid_param *param);
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *strm,
+				      pjmedia_vid_dev_cap cap,
+				      void *value);
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *strm,
+				      pjmedia_vid_dev_cap cap,
+				      const void *value);
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_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);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+    &sdl_factory_init,
+    &sdl_factory_destroy,
+    &sdl_factory_get_dev_count,
+    &sdl_factory_get_dev_info,
+    &sdl_factory_default_param,
+    &sdl_factory_create_stream
+};
+
+static pjmedia_vid_stream_op stream_op =
+{
+    &sdl_stream_get_param,
+    &sdl_stream_get_cap,
+    &sdl_stream_set_cap,
+    &sdl_stream_start,
+    NULL,
+    &sdl_stream_put_frame,
+    &sdl_stream_stop,
+    &sdl_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init sdl_ video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf)
+{
+    struct sdl_factory *f;
+    pj_pool_t *pool;
+
+    pool = pj_pool_create(pf, "sdl video", 1000, 1000, NULL);
+    f = PJ_POOL_ZALLOC_T(pool, struct sdl_factory);
+    f->pf = pf;
+    f->pool = pool;
+    f->base.op = &factory_op;
+
+    return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f)
+{
+    struct sdl_factory *sf = (struct sdl_factory*)f;
+    struct sdl_dev_info *ddi;
+    unsigned i;
+
+    sf->dev_count = 1;
+    sf->dev_info = (struct sdl_dev_info*)
+ 		   pj_pool_calloc(sf->pool, sf->dev_count,
+ 				  sizeof(struct sdl_dev_info));
+
+    ddi = &sf->dev_info[0];
+    pj_bzero(ddi, sizeof(*ddi));
+    strncpy(ddi->info.name, "SDL renderer", sizeof(ddi->info.name));
+    ddi->info.name[sizeof(ddi->info.name)-1] = '\0';
+    strncpy(ddi->info.driver, "SDL", sizeof(ddi->info.driver));
+    ddi->info.driver[sizeof(ddi->info.driver)-1] = '\0';
+    ddi->info.dir = PJMEDIA_DIR_RENDER;
+    ddi->info.has_callback = PJ_FALSE;
+    ddi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT |
+                     PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE;
+
+    ddi->info.fmt_cnt = PJ_ARRAY_SIZE(sdl_fmts);
+    ddi->info.fmt = (pjmedia_format*)
+ 		    pj_pool_calloc(sf->pool, ddi->info.fmt_cnt,
+ 				   sizeof(pjmedia_format));
+    for (i = 0; i < ddi->info.fmt_cnt; i++) {
+        pjmedia_format *fmt = &ddi->info.fmt[i];
+        pjmedia_format_init_video(fmt, sdl_fmts[i].fmt_id,
+				  DEFAULT_WIDTH, DEFAULT_HEIGHT,
+				  DEFAULT_FPS, 1);
+    }
+
+    PJ_LOG(4, (THIS_FILE, "SDL initialized"));
+
+    return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    struct sdl_factory *sf = (struct sdl_factory*)f;
+    pj_pool_t *pool = sf->pool;
+
+    sf->pool = NULL;
+    pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+    struct sdl_factory *sf = (struct sdl_factory*)f;
+    return sf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					    unsigned index,
+					    pjmedia_vid_dev_info *info)
+{
+    struct sdl_factory *sf = (struct sdl_factory*)f;
+
+    PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
+
+    pj_memcpy(info, &sf->dev_info[index].info, sizeof(*info));
+
+    return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t sdl_factory_default_param(pj_pool_t *pool,
+                                             pjmedia_vid_dev_factory *f,
+					     unsigned index,
+					     pjmedia_vid_param *param)
+{
+    struct sdl_factory *sf = (struct sdl_factory*)f;
+    struct sdl_dev_info *di = &sf->dev_info[index];
+
+    PJ_ASSERT_RETURN(index < sf->dev_count, PJMEDIA_EVID_INVDEV);
+    
+    PJ_UNUSED_ARG(pool);
+
+    pj_bzero(param, sizeof(*param));
+    if (di->info.dir & PJMEDIA_DIR_CAPTURE_RENDER) {
+	param->dir = PJMEDIA_DIR_CAPTURE_RENDER;
+	param->cap_id = index;
+	param->rend_id = index;
+    } else if (di->info.dir & PJMEDIA_DIR_CAPTURE) {
+	param->dir = PJMEDIA_DIR_CAPTURE;
+	param->cap_id = index;
+	param->rend_id = PJMEDIA_VID_INVALID_DEV;
+    } else if (di->info.dir & PJMEDIA_DIR_RENDER) {
+	param->dir = PJMEDIA_DIR_RENDER;
+	param->rend_id = index;
+	param->cap_id = PJMEDIA_VID_INVALID_DEV;
+    } else {
+	return PJMEDIA_EVID_INVDEV;
+    }
+
+    /* Set the device capabilities here */
+    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+    param->fmt.type = PJMEDIA_TYPE_VIDEO;
+    param->clock_rate = DEFAULT_CLOCK_RATE;
+    param->frame_rate.num = DEFAULT_FPS;
+    param->frame_rate.denum = 1;
+    pjmedia_format_init_video(&param->fmt, sdl_fmts[0].fmt_id,
+			      DEFAULT_WIDTH, DEFAULT_HEIGHT,
+			      DEFAULT_FPS, 1);
+
+    return PJ_SUCCESS;
+}
+
+static sdl_fmt_info* get_sdl_format_info(pjmedia_format_id id)
+{
+    unsigned i;
+
+    for (i = 0; i < sizeof(sdl_fmts)/sizeof(sdl_fmts[0]); i++) {
+        if (sdl_fmts[i].fmt_id == id)
+            return &sdl_fmts[i];
+    }
+
+    return NULL;
+}
+
+static int create_sdl_thread(void * data)
+{
+    struct sdl_stream *strm = (struct sdl_stream*)data;
+    sdl_fmt_info *sdl_info = get_sdl_format_info(strm->param.fmt.id);
+    const pjmedia_video_format_info *vfi;
+    pjmedia_video_format_detail *vfd;
+
+    vfi = pjmedia_get_video_format_info(pjmedia_video_format_mgr_instance(),
+                                        strm->param.fmt.id);
+    if (!vfi || !sdl_info) {
+        strm->status = PJMEDIA_EVID_BADFORMAT;
+        return strm->status;
+    }
+
+    strm->vafp.size = strm->param.fmt.det.vid.size;
+    strm->vafp.buffer = NULL;
+    if (vfi->apply_fmt(vfi, &strm->vafp) != PJ_SUCCESS) {
+        strm->status = PJMEDIA_EVID_BADFORMAT;
+        return strm->status;
+    }
+
+    /* Initialize the SDL library */
+    if (SDL_Init(SDL_INIT_VIDEO)) {
+        PJ_LOG(4, (THIS_FILE, "Cannot initialize SDL"));
+        strm->status = PJMEDIA_EVID_INIT;
+        return strm->status;
+    }
+
+    vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
+    strm->rect.x = strm->rect.y = 0;
+    strm->rect.w = (Uint16)vfd->size.w;
+    strm->rect.h = (Uint16)vfd->size.h;
+
+    /* Initialize the display, requesting a software surface */
+    strm->screen = SDL_SetVideoMode(strm->rect.w, strm->rect.h,
+                                    0, SDL_RESIZABLE | SDL_SWSURFACE);
+    if (strm->screen == NULL) {
+        strm->status = PJMEDIA_EVID_SYSERR;
+        return strm->status;
+    }
+    SDL_WM_SetCaption("pjmedia-SDL video", NULL);
+
+    if (vfi->color_model == PJMEDIA_COLOR_MODEL_RGB) {
+        strm->surf = SDL_CreateRGBSurface(SDL_SWSURFACE,
+            strm->rect.w, strm->rect.h,
+            vfi->bpp,
+            sdl_info->Rmask,
+            sdl_info->Gmask,
+            sdl_info->Bmask,
+            sdl_info->Amask);
+        if (strm->surf == NULL) {
+            strm->status = PJMEDIA_EVID_SYSERR;
+            return strm->status;
+        }
+    } else if (vfi->color_model == PJMEDIA_COLOR_MODEL_YUV) {
+        strm->overlay = SDL_CreateYUVOverlay(strm->rect.w, strm->rect.h,
+            sdl_info->sdl_format,
+            strm->screen);
+        if (strm->overlay == NULL) {
+            strm->status = PJMEDIA_EVID_SYSERR;
+            return strm->status;
+        }
+    }
+
+    while(!strm->is_quitting) {
+        SDL_Event sevent;
+        pjmedia_vid_event pevent;
+
+        while (SDL_WaitEvent(&sevent)) {
+            pj_bzero(&pevent, sizeof(pevent));
+
+            switch(sevent.type) {
+		case SDL_USEREVENT:
+		    return 0;
+                case SDL_MOUSEBUTTONDOWN:
+                    pevent.event_type = PJMEDIA_EVENT_MOUSEBUTTONDOWN;
+                    if (strm->vid_cb.on_event_cb)
+                        if ((*strm->vid_cb.on_event_cb)(&strm->base,
+                                                       strm->user_data,
+                                                       &pevent) != PJ_SUCCESS)
+                        {
+                            /* Application wants us to ignore this event */
+                            break;
+                        }
+                    if (strm->is_running)
+                        pjmedia_vid_stream_stop(&strm->base);
+                    else
+                        pjmedia_vid_stream_start(&strm->base);
+                    break;
+                case SDL_VIDEORESIZE:
+                    pevent.event_type = PJMEDIA_EVENT_WINDOW_RESIZE;
+                    if (strm->vid_cb.on_event_cb)
+                        if ((*strm->vid_cb.on_event_cb)(&strm->base,
+                                                       strm->user_data,
+                                                       &pevent) != PJ_SUCCESS)
+                        {
+                            /* Application wants us to ignore this event */
+                            break;
+                        }
+                    /* TODO: move this to OUTPUT_RESIZE cap
+                    strm->screen = SDL_SetVideoMode(sevent.resize.w,
+                                                    sevent.resize.h,
+                                                    0, SDL_RESIZABLE |
+                                                    SDL_SWSURFACE);
+                    */                  
+                    break;
+                case SDL_QUIT:
+                    pevent.event_type = PJMEDIA_EVENT_WINDOW_CLOSE;
+                    /**
+                     * 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()
+                     * 2. return PJ_SUCCESS
+                     * Upon returning from the callback, SDL will destroy its
+                     * own stream.
+                     *
+                     * Returning non-PJ_SUCCESS will cause SDL to ignore
+                     * the event
+                     */
+                    if (strm->vid_cb.on_event_cb) {
+                        strm->is_quitting = PJ_TRUE;
+                        if ((*strm->vid_cb.on_event_cb)(&strm->base,
+                                                       strm->user_data,
+                                                       &pevent) != PJ_SUCCESS)
+                        {
+                            /* Application wants us to ignore this event */
+                            strm->is_quitting = PJ_FALSE;
+                            break;
+                        }
+
+                        /* Destroy the stream */
+                        sdl_stream_destroy(&strm->base);
+                        return 0;
+                    }
+
+                    /**
+                     * Default event-handler when there is no user-specified
+                     * callback: close the renderer window. We cannot destroy
+                     * the stream here since there is no callback to notify
+                     * the application.
+                     */
+                    sdl_stream_stop(&strm->base);
+                    SDL_Quit();
+                    strm->screen = NULL;
+                    return 0;
+                default:
+                    break;
+            }
+        }
+
+    }
+
+    return 0;
+}
+
+/* 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)
+{
+    struct sdl_factory *sf = (struct sdl_factory*)f;
+    pj_pool_t *pool;
+    struct sdl_stream *strm;
+    pj_status_t status;
+
+    /* Create and Initialize stream descriptor */
+    pool = pj_pool_create(sf->pf, "sdl-dev", 1000, 1000, NULL);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    strm = PJ_POOL_ZALLOC_T(pool, struct sdl_stream);
+    pj_memcpy(&strm->param, param, sizeof(*param));
+    strm->pool = pool;
+    pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+    strm->user_data = user_data;
+
+    /* Create capture stream here */
+    if (param->dir & PJMEDIA_DIR_CAPTURE) {
+    }
+
+    /* Create render stream here */
+    if (param->dir & PJMEDIA_DIR_RENDER) {
+        strm->status = PJ_SUCCESS;
+        status = pj_thread_create(pool, "sdl_thread", create_sdl_thread,
+                                  strm, 0, 0, &strm->sdl_thread);
+        if (status != PJ_SUCCESS) {
+            goto on_error;
+        }
+        while (strm->status == PJ_SUCCESS && !strm->surf && !strm->overlay)
+            pj_thread_sleep(10);
+        if ((status = strm->status) != PJ_SUCCESS) {
+            goto on_error;
+        }
+
+        pjmedia_format_copy(&strm->conv_param.src, &param->fmt);
+        pjmedia_format_copy(&strm->conv_param.dst, &param->fmt);
+
+        status = pjmedia_converter_create(NULL, pool, &strm->conv_param,
+                                          &strm->conv);
+    }
+
+    /* Apply the remaining settings */
+    if (param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
+	sdl_stream_set_cap(&strm->base,
+		            PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+                            &param->window);
+    }
+
+    /* Done */
+    strm->base.op = &stream_op;
+    *p_vid_strm = &strm->base;
+
+    return PJ_SUCCESS;
+
+on_error:
+    sdl_stream_destroy(&strm->base);
+    return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t sdl_stream_get_param(pjmedia_vid_stream *s,
+					pjmedia_vid_param *pi)
+{
+    struct sdl_stream *strm = (struct sdl_stream*)s;
+
+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+    pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+/*    if (sdl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+                            &pi->fmt.info_size) == PJ_SUCCESS)
+    {
+        pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+    }
+*/
+    return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t sdl_stream_get_cap(pjmedia_vid_stream *s,
+				      pjmedia_vid_dev_cap cap,
+				      void *pval)
+{
+    struct sdl_stream *strm = (struct sdl_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+    {
+	return PJ_SUCCESS;
+    } else {
+	return PJMEDIA_EVID_INVCAP;
+    }
+}
+
+/* API: set capability */
+static pj_status_t sdl_stream_set_cap(pjmedia_vid_stream *s,
+				      pjmedia_vid_dev_cap cap,
+				      const void *pval)
+{
+    struct sdl_stream *strm = (struct sdl_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW)
+    {
+	return PJ_SUCCESS;
+    }
+
+    return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: Put frame from stream */
+static pj_status_t sdl_stream_put_frame(pjmedia_vid_stream *strm,
+                                        const pjmedia_frame *frame)
+{
+    struct sdl_stream *stream = (struct sdl_stream*)strm;
+
+    if (!stream->is_running) {
+        stream->render_exited = PJ_TRUE;
+        return PJ_SUCCESS;
+    }
+
+    if (stream->surf) {
+        if (SDL_MUSTLOCK(stream->surf)) {
+            if (SDL_LockSurface(stream->surf) < 0) {
+                PJ_LOG(3, (THIS_FILE, "Unable to lock SDL surface"));
+                return PJMEDIA_EVID_NOTREADY;
+            }
+        }
+
+        pj_memcpy(stream->surf->pixels, frame->buf, frame->size);
+
+        if (SDL_MUSTLOCK(stream->surf)) {
+            SDL_UnlockSurface(stream->surf);
+        }
+        SDL_BlitSurface(stream->surf, NULL, stream->screen, NULL);
+        SDL_UpdateRect(stream->screen, 0, 0, 0, 0);
+    } else if (stream->overlay) {
+        int i, sz, offset;
+
+        if (SDL_LockYUVOverlay(stream->overlay) < 0) {
+            PJ_LOG(3, (THIS_FILE, "Unable to lock SDL overlay"));
+            return PJMEDIA_EVID_NOTREADY;
+        }
+
+        for (i = 0, offset = 0; i < stream->overlay->planes; i++) {
+            sz = stream->vafp.plane_bytes[i];
+            pj_memcpy(stream->overlay->pixels[i],
+                      (char *)frame->buf + offset, sz);
+            offset += sz;
+        }
+
+        SDL_UnlockYUVOverlay(stream->overlay);
+        SDL_DisplayYUVOverlay(stream->overlay, &stream->rect);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* API: Start stream. */
+static pj_status_t sdl_stream_start(pjmedia_vid_stream *strm)
+{
+    struct sdl_stream *stream = (struct sdl_stream*)strm;
+
+    PJ_LOG(4, (THIS_FILE, "Starting sdl video stream"));
+
+    stream->is_running = PJ_TRUE;
+    stream->render_exited = PJ_FALSE;
+
+    return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t sdl_stream_stop(pjmedia_vid_stream *strm)
+{
+    struct sdl_stream *stream = (struct sdl_stream*)strm;
+    unsigned i;
+
+    PJ_LOG(4, (THIS_FILE, "Stopping sdl video stream"));
+
+    /* Wait for renderer put_frame() to finish */
+    stream->is_running = PJ_FALSE;
+    for (i=0; !stream->render_exited && i<100; ++i)
+	pj_thread_sleep(10);
+
+    return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t sdl_stream_destroy(pjmedia_vid_stream *strm)
+{
+    struct sdl_stream *stream = (struct sdl_stream*)strm;
+    SDL_Event sevent;
+
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+    if (!stream->is_quitting) {
+        sevent.type = SDL_USEREVENT;
+        SDL_PushEvent(&sevent);
+        pj_thread_join(stream->sdl_thread);
+    }
+
+    sdl_stream_stop(strm);
+
+    if (stream->surf) {
+        SDL_FreeSurface(stream->surf);
+        stream->surf = NULL;
+    }
+
+    if (stream->overlay) {
+        SDL_FreeYUVOverlay(stream->overlay);
+        stream->overlay = NULL;
+    }
+
+    SDL_Quit();
+    stream->screen = NULL;
+
+    pj_pool_release(stream->pool);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_VIDEO_DEV_HAS_SDL */
diff --git a/pjmedia/src/pjmedia-videodev/v4l2_dev.c b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
new file mode 100644
index 0000000..88ef611
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
@@ -0,0 +1,805 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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-videodev/videodev_imp.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/file_access.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/rand.h>
+
+#if PJMEDIA_VIDEO_DEV_HAS_V4L2
+
+#include <linux/videodev2.h>
+#include <libv4l2.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#define THIS_FILE		"v4l2_dev.c"
+#define DRIVER_NAME		"v4l2"
+#define V4L2_MAX_DEVS		4
+#define DEFAULT_WIDTH		640
+#define DEFAULT_HEIGHT		480
+#define DEFAULT_FPS		25
+#define DEFAULT_CLOCK_RATE	90000
+#define INVALID_FD		-1
+#define BUFFER_CNT		2
+#define MAX_IOCTL_RETRY		20
+
+
+/* mapping between pjmedia_fmt_id and v4l2 pixel format */
+typedef struct vid4lin_fmt_map
+{
+    pj_uint32_t pjmedia_fmt_id;
+    pj_uint32_t	v4l2_fmt_id;
+} vid4lin_fmt_map;
+
+/* I/O type being used */
+enum vid4lin_io_type
+{
+    IO_TYPE_NONE,
+    IO_TYPE_READ,
+    IO_TYPE_MMAP,
+    IO_TYPE_MMAP_USER
+};
+
+/* descriptor for each mmap-ed buffer */
+typedef struct vid4lin_buffer
+{
+    void   *start;
+    size_t  length;
+} vid4lin_buffer;
+
+/* v4l2 device info */
+typedef struct vid4lin_dev_info
+{
+    pjmedia_vid_dev_info	 info;
+    char			 dev_name[32];
+    struct v4l2_capability 	 v4l2_cap;
+} vid4lin_dev_info;
+
+/* v4l2 factory */
+typedef struct vid4lin_factory
+{
+    pjmedia_vid_dev_factory	 base;
+    pj_pool_t			*pool;
+    pj_pool_factory		*pf;
+
+    unsigned			 dev_count;
+    vid4lin_dev_info		*dev_info;
+} vid4lin_factory;
+
+/* Video stream. */
+typedef struct vid4lin_stream
+{
+    pjmedia_vid_stream	 	 base;		/**< Base stream	*/
+    pjmedia_vid_param	 	 param;		/**< Settings		*/
+    pj_pool_t           	*pool;		/**< Memory pool.	*/
+
+    int			 	 fd;		/**< Video fd.		*/
+    char			 name[64];	/**< Name for log	*/
+    enum vid4lin_io_type	 io_type;	/**< I/O method.	*/
+    unsigned			 buf_cnt;	/**< MMap buf cnt.  	*/
+    vid4lin_buffer		*buffers;	/**< MMap buffers.  	*/
+    pj_time_val			 start_time;	/**< Time when started	*/
+
+    pjmedia_vid_cb       	 vid_cb;	/**< Stream callback  	*/
+    void                	*user_data;	/**< Application data 	*/
+} vid4lin_stream;
+
+/* Use this to convert between pjmedia_format_id and V4L2 fourcc */
+static vid4lin_fmt_map v4l2_fmt_maps[] =
+{
+    { PJMEDIA_FORMAT_RGB24,	V4L2_PIX_FMT_BGR24 },
+    { PJMEDIA_FORMAT_RGBA,	V4L2_PIX_FMT_BGR32 },
+    { PJMEDIA_FORMAT_RGB32,	V4L2_PIX_FMT_BGR32 },
+    { PJMEDIA_FORMAT_AYUV,	V4L2_PIX_FMT_YUV32 },
+    { PJMEDIA_FORMAT_YUY2,	V4L2_PIX_FMT_YUYV },
+    { PJMEDIA_FORMAT_UYVY,	V4L2_PIX_FMT_UYVY }
+};
+
+/* Prototypes */
+static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f);
+static unsigned    vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					        unsigned index,
+					        pjmedia_vid_dev_info *info);
+static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool,
+                                                 pjmedia_vid_dev_factory *f,
+					         unsigned index,
+					         pjmedia_vid_param *param);
+static pj_status_t vid4lin_factory_create_stream(pjmedia_vid_dev_factory *f,
+						 const pjmedia_vid_param *prm,
+					         const pjmedia_vid_cb *cb,
+					         void *user_data,
+					         pjmedia_vid_stream **p_strm);
+
+static pj_status_t vid4lin_stream_get_param(pjmedia_vid_stream *strm,
+					    pjmedia_vid_param *param);
+static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_stream *strm,
+				          pjmedia_vid_dev_cap cap,
+				          void *value);
+static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_stream *strm,
+				          pjmedia_vid_dev_cap cap,
+				          const void *value);
+static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_stream *strm,
+                                            pjmedia_frame *frame);
+static pj_status_t vid4lin_stream_start(pjmedia_vid_stream *strm);
+static pj_status_t vid4lin_stream_stop(pjmedia_vid_stream *strm);
+static pj_status_t vid4lin_stream_destroy(pjmedia_vid_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+    &vid4lin_factory_init,
+    &vid4lin_factory_destroy,
+    &vid4lin_factory_get_dev_count,
+    &vid4lin_factory_get_dev_info,
+    &vid4lin_factory_default_param,
+    &vid4lin_factory_create_stream
+};
+
+static pjmedia_vid_stream_op stream_op =
+{
+    &vid4lin_stream_get_param,
+    &vid4lin_stream_get_cap,
+    &vid4lin_stream_set_cap,
+    &vid4lin_stream_start,
+    &vid4lin_stream_get_frame,
+    NULL,
+    &vid4lin_stream_stop,
+    &vid4lin_stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Factory creation function.
+ */
+pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf)
+{
+    vid4lin_factory *f;
+    pj_pool_t *pool;
+
+    pool = pj_pool_create(pf, DRIVER_NAME, 512, 512, NULL);
+    f = PJ_POOL_ZALLOC_T(pool, vid4lin_factory);
+    f->pf = pf;
+    f->pool = pool;
+    f->base.op = &factory_op;
+
+    return &f->base;
+}
+
+/* util: ioctl that tries harder. */
+static pj_status_t xioctl(int fh, int request, void *arg)
+{
+    enum { RETRY = MAX_IOCTL_RETRY };
+    int r, c=0;
+
+    do {
+	r = v4l2_ioctl(fh, request, arg);
+    } while (r==-1 && c++<RETRY && ((errno==EINTR) || (errno==EAGAIN)));
+
+    return (r == -1) ? pj_get_os_error() : PJ_SUCCESS;
+}
+
+/* Scan V4L2 devices */
+static pj_status_t v4l2_scan_devs(vid4lin_factory *f)
+{
+    vid4lin_dev_info vdi[V4L2_MAX_DEVS];
+    char dev_name[32];
+    unsigned i, old_count;
+    pj_status_t status;
+
+    pj_bzero(vdi, sizeof(vdi));
+    old_count = f->dev_count;
+    f->dev_count = 0;
+
+    for (i=0; i<V4L2_MAX_DEVS && f->dev_count < V4L2_MAX_DEVS; ++i) {
+	int fd;
+	vid4lin_dev_info *pdi;
+	pj_pool_t *pool = f->pool;
+	pj_uint32_t fmt_cap[8];
+	int j, fmt_cnt=0;
+
+	pdi = &vdi[f->dev_count];
+
+	snprintf(dev_name, sizeof(dev_name), "/dev/video%d", i);
+	if (!pj_file_exists(dev_name))
+	    continue;
+
+	fd = v4l2_open(dev_name, O_RDWR, 0);
+	if (fd == -1)
+	    continue;
+
+	status = xioctl(fd, VIDIOC_QUERYCAP, &pdi->v4l2_cap);
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(4,(THIS_FILE, status, "Error querying %s", dev_name));
+	    v4l2_close(fd);
+	    continue;
+	}
+
+	if ((pdi->v4l2_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
+	    v4l2_close(fd);
+	    continue;
+	}
+
+	PJ_LOG(5,(THIS_FILE, "Found capture device %s", pdi->v4l2_cap.card));
+	PJ_LOG(5,(THIS_FILE, "  Enumerating formats:"));
+	for (j=0; fmt_cnt<PJ_ARRAY_SIZE(fmt_cap); ++j) {
+	    struct v4l2_fmtdesc fdesc;
+	    unsigned k;
+
+	    pj_bzero(&fdesc, sizeof(fdesc));
+	    fdesc.index = j;
+	    fdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	    status = xioctl(fd, VIDIOC_ENUM_FMT, &fdesc);
+	    if (status != PJ_SUCCESS)
+		break;
+
+	    for (k=0; k<PJ_ARRAY_SIZE(v4l2_fmt_maps); ++k) {
+		if (v4l2_fmt_maps[k].v4l2_fmt_id == fdesc.pixelformat) {
+		    fmt_cap[fmt_cnt++] = v4l2_fmt_maps[k].pjmedia_fmt_id;
+		    PJ_LOG(5,(THIS_FILE, "   Supported: %s",
+			      fdesc.description));
+		    break;
+		}
+	    }
+	    if (k==PJ_ARRAY_SIZE(v4l2_fmt_maps)) {
+		PJ_LOG(5,(THIS_FILE, "   Unsupported: %s", fdesc.description));
+	    }
+	}
+
+	v4l2_close(fd);
+
+	if (fmt_cnt==0) {
+	    PJ_LOG(5,(THIS_FILE, "    Found no common format"));
+	    continue;
+	}
+
+	strncpy(pdi->dev_name, dev_name, sizeof(pdi->dev_name));
+	pdi->dev_name[sizeof(pdi->dev_name)-1] = '\0';
+	strncpy(pdi->info.name, (char*)pdi->v4l2_cap.card,
+		sizeof(pdi->info.name));
+	pdi->info.name[sizeof(pdi->info.name)-1] = '\0';
+	strncpy(pdi->info.driver, DRIVER_NAME, sizeof(pdi->info.driver));
+	pdi->info.driver[sizeof(pdi->info.driver)-1] = '\0';
+	pdi->info.dir = PJMEDIA_DIR_CAPTURE;
+	pdi->info.has_callback = PJ_FALSE;
+	pdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
+
+	pdi->info.fmt_cnt = fmt_cnt;
+	pdi->info.fmt = (pjmedia_format*)
+			pj_pool_calloc(pool, sizeof(pjmedia_format), fmt_cnt);
+
+	for (j=0; j<fmt_cnt; ++j) {
+	    pjmedia_format_init_video(&pdi->info.fmt[j],
+				      fmt_cap[j],
+				      DEFAULT_WIDTH,
+				      DEFAULT_HEIGHT,
+				      DEFAULT_FPS, 1);
+	}
+	if (j < fmt_cnt)
+	    continue;
+
+	f->dev_count++;
+    }
+
+    if (f->dev_count == 0)
+	return PJ_SUCCESS;
+
+    if (f->dev_count > old_count || f->dev_info == NULL) {
+	f->dev_info = (vid4lin_dev_info*)
+		      pj_pool_calloc(f->pool,
+				     f->dev_count,
+				     sizeof(vid4lin_dev_info));
+    }
+    pj_memcpy(f->dev_info, vdi, f->dev_count * sizeof(vid4lin_dev_info));
+
+    return PJ_SUCCESS;
+}
+
+
+/* API: init factory */
+static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f)
+{
+    vid4lin_factory *cf = (vid4lin_factory*)f;
+    pj_status_t status;
+
+    status = v4l2_scan_devs(cf);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    PJ_LOG(4, (THIS_FILE, "Video4Linux2 initialized with %d devices",
+	       cf->dev_count));
+
+    return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    vid4lin_factory *cf = (vid4lin_factory*)f;
+    pj_pool_t *pool = cf->pool;
+
+    if (cf->pool) {
+	cf->pool = NULL;
+	pj_pool_release(pool);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+    vid4lin_factory *cf = (vid4lin_factory*)f;
+    return cf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+					     unsigned index,
+					     pjmedia_vid_dev_info *info)
+{
+    vid4lin_factory *cf = (vid4lin_factory*)f;
+
+    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
+
+    pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
+
+    return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t vid4lin_factory_default_param(pj_pool_t *pool,
+                                                 pjmedia_vid_dev_factory *f,
+                                                 unsigned index,
+                                                 pjmedia_vid_param *param)
+{
+    vid4lin_factory *cf = (vid4lin_factory*)f;
+
+    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EVID_INVDEV);
+
+    pj_bzero(param, sizeof(*param));
+    param->dir = PJMEDIA_DIR_CAPTURE;
+    param->cap_id = index;
+    param->rend_id = PJMEDIA_VID_INVALID_DEV;
+    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+    param->clock_rate = DEFAULT_CLOCK_RATE;
+    param->frame_rate.num = DEFAULT_FPS;
+    param->frame_rate.denum = 1;
+    pjmedia_format_copy(&param->fmt, &cf->dev_info[index].info.fmt[0]);
+
+    return PJ_SUCCESS;
+}
+
+static vid4lin_fmt_map* get_v4l2_format_info(pjmedia_format_id id)
+{
+    unsigned i;
+
+    for (i = 0; i < PJ_ARRAY_SIZE(v4l2_fmt_maps); i++) {
+        if (v4l2_fmt_maps[i].pjmedia_fmt_id == id)
+            return &v4l2_fmt_maps[i];
+    }
+
+    return NULL;
+}
+
+/* util: setup format */
+static pj_status_t vid4lin_stream_init_fmt(vid4lin_stream *stream,
+					const pjmedia_vid_param *param,
+					pj_uint32_t pix_fmt)
+{
+    const pjmedia_video_format_detail *vfd;
+    struct v4l2_format v4l2_fmt;
+    pj_status_t status;
+
+    vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
+    if (vfd == NULL)
+	return PJMEDIA_EVID_BADFORMAT;
+
+    pj_bzero(&v4l2_fmt, sizeof(v4l2_fmt));
+    v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    v4l2_fmt.fmt.pix.width       = vfd->size.w;
+    v4l2_fmt.fmt.pix.height      = vfd->size.h;
+    v4l2_fmt.fmt.pix.pixelformat = pix_fmt;
+    v4l2_fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
+    status = xioctl(stream->fd, VIDIOC_S_FMT, &v4l2_fmt);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (v4l2_fmt.fmt.pix.pixelformat != pix_fmt) {
+	status = PJMEDIA_EVID_BADFORMAT;
+	return status;
+    }
+
+    if ((v4l2_fmt.fmt.pix.width != vfd->size.w) ||
+	(v4l2_fmt.fmt.pix.height != vfd->size.h))
+    {
+	status = PJMEDIA_EVID_BADSIZE;
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Util: initiate v4l2 streaming via mmap */
+static pj_status_t vid4lin_stream_init_streaming(vid4lin_stream *stream)
+{
+    struct v4l2_requestbuffers req;
+    unsigned i;
+    pj_status_t status;
+
+    pj_bzero(&req, sizeof(req));
+    req.count = BUFFER_CNT;
+    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    req.memory = V4L2_MEMORY_MMAP;
+    status = xioctl(stream->fd, VIDIOC_REQBUFS, &req);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    stream->buffers = pj_pool_calloc(stream->pool, req.count,
+				     sizeof(*stream->buffers));
+    stream->buf_cnt = 0;
+
+    for (i = 0; i < req.count; ++i) {
+	struct v4l2_buffer buf;
+
+	pj_bzero(&buf, sizeof(buf));
+
+	buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	buf.memory      = V4L2_MEMORY_MMAP;
+	buf.index       = i;
+
+	status = xioctl(stream->fd, VIDIOC_QUERYBUF, &buf);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	stream->buffers[i].length = buf.length;
+	stream->buffers[i].start = v4l2_mmap(NULL, buf.length,
+					     PROT_READ | PROT_WRITE,
+					     MAP_SHARED, stream->fd,
+					     buf.m.offset);
+
+	if (MAP_FAILED == stream->buffers[i].start) {
+	    status = pj_get_os_error();
+	    goto on_error;
+	}
+
+	stream->buf_cnt++;
+    }
+
+    PJ_LOG(5,(THIS_FILE, "  mmap streaming initialized"));
+
+    stream->io_type = IO_TYPE_MMAP;
+    return PJ_SUCCESS;
+
+on_error:
+    return status;
+}
+
+/* init streaming with user pointer */
+static pj_status_t vid4lin_stream_init_streaming_user(vid4lin_stream *stream)
+{
+    return PJ_ENOTSUP;
+}
+
+/* init streaming with read() */
+static pj_status_t vid4lin_stream_init_read_write(vid4lin_stream *stream)
+{
+    return PJ_ENOTSUP;
+}
+
+/* API: create stream */
+static pj_status_t vid4lin_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)
+{
+    vid4lin_factory *cf = (vid4lin_factory*)f;
+    pj_pool_t *pool;
+    vid4lin_stream *stream;
+    vid4lin_dev_info *vdi;
+    const vid4lin_fmt_map *fmt_map;
+    const pjmedia_video_format_info *fmt_info;
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+    PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
+		     param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
+		     PJ_EINVAL);
+    PJ_ASSERT_RETURN(param->cap_id >= 0 && param->cap_id < cf->dev_count,
+		     PJMEDIA_EVID_INVDEV);
+
+    fmt_info = pjmedia_get_video_format_info(NULL, param->fmt.id);
+    if (!fmt_info || (fmt_map=get_v4l2_format_info(param->fmt.id))==NULL)
+        return PJMEDIA_EVID_BADFORMAT;
+
+    vdi = &cf->dev_info[param->cap_id];
+
+    /* Create and Initialize stream descriptor */
+    pool = pj_pool_create(cf->pf, vdi->info.name, 512, 512, NULL);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    stream = PJ_POOL_ZALLOC_T(pool, vid4lin_stream);
+    pj_memcpy(&stream->param, param, sizeof(*param));
+    stream->pool = pool;
+    pj_memcpy(&stream->vid_cb, cb, sizeof(*cb));
+    strncpy(stream->name, vdi->info.name, sizeof(stream->name));
+    stream->name[sizeof(stream->name)-1] = '\0';
+    stream->user_data = user_data;
+    stream->fd = INVALID_FD;
+
+    PJ_LOG(4,(THIS_FILE, "Opening video4linux2 device %s: format=%s..",
+	      stream->name, fmt_info->name));
+
+    stream->fd = v4l2_open(vdi->dev_name, O_RDWR | O_NONBLOCK, 0);
+    if (stream->fd < 0)
+	goto on_error;
+
+    status = vid4lin_stream_init_fmt(stream, param, fmt_map->v4l2_fmt_id);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING)
+	status = vid4lin_stream_init_streaming(stream);
+
+    if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_STREAMING)
+	status = vid4lin_stream_init_streaming_user(stream);
+
+    if (status!=PJ_SUCCESS && vdi->v4l2_cap.capabilities & V4L2_CAP_READWRITE)
+	status = vid4lin_stream_init_read_write(stream);
+
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(1,(THIS_FILE, "Error: unable to initiate I/O on %s",
+		  stream->name));
+	goto on_error;
+    }
+
+    /* Done */
+    stream->base.op = &stream_op;
+    *p_vid_strm = &stream->base;
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (status == PJ_SUCCESS)
+	status = PJ_RETURN_OS_ERROR(errno);
+
+    vid4lin_stream_destroy(&stream->base);
+    return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t vid4lin_stream_get_param(pjmedia_vid_stream *s,
+					 pjmedia_vid_param *pi)
+{
+    vid4lin_stream *strm = (vid4lin_stream*)s;
+
+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+    pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+    return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t vid4lin_stream_get_cap(pjmedia_vid_stream *s,
+				       pjmedia_vid_dev_cap cap,
+				       void *pval)
+{
+    vid4lin_stream *strm = (vid4lin_stream*)s;
+
+    PJ_UNUSED_ARG(strm);
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+    {
+        return PJMEDIA_EVID_INVCAP;
+//	return PJ_SUCCESS;
+    } else {
+	return PJMEDIA_EVID_INVCAP;
+    }
+}
+
+/* API: set capability */
+static pj_status_t vid4lin_stream_set_cap(pjmedia_vid_stream *s,
+				       pjmedia_vid_dev_cap cap,
+				       const void *pval)
+{
+    vid4lin_stream *strm = (vid4lin_stream*)s;
+
+
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+    /*
+    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE)
+    {
+	return PJ_SUCCESS;
+    }
+    */
+    PJ_UNUSED_ARG(strm);
+    PJ_UNUSED_ARG(cap);
+    PJ_UNUSED_ARG(pval);
+
+    return PJMEDIA_EVID_INVCAP;
+}
+
+/* get frame from mmap */
+static pj_status_t vid4lin_stream_get_frame_mmap(vid4lin_stream *stream,
+                                                 pjmedia_frame *frame)
+{
+    struct v4l2_buffer buf;
+    pj_time_val time;
+    pj_status_t status = PJ_SUCCESS;
+
+    pj_bzero(&buf, sizeof(buf));
+    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buf.memory = V4L2_MEMORY_MMAP;
+    status = xioctl(stream->fd, VIDIOC_DQBUF, &buf);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (frame->size < buf.bytesused) {
+	/* supplied buffer is too small */
+	pj_assert(!"frame buffer is too small for v4l2");
+	status = PJ_ETOOSMALL;
+	goto on_return;
+    }
+
+    time.sec = buf.timestamp.tv_sec;
+    time.msec = buf.timestamp.tv_usec / 1000;
+    PJ_TIME_VAL_SUB(time, stream->start_time);
+
+    frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
+    frame->size = buf.bytesused;
+    frame->timestamp.u64 = PJ_TIME_VAL_MSEC(time) * stream->param.clock_rate
+			   / PJ_UINT64(1000);
+    pj_memcpy(frame->buf, stream->buffers[buf.index].start, buf.bytesused);
+
+on_return:
+    pj_bzero(&buf, sizeof(buf));
+    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    buf.memory = V4L2_MEMORY_MMAP;
+    xioctl(stream->fd, VIDIOC_QBUF, &buf);
+
+    return status;
+}
+
+/* API: Get frame from stream */
+static pj_status_t vid4lin_stream_get_frame(pjmedia_vid_stream *strm,
+                                            pjmedia_frame *frame)
+{
+    vid4lin_stream *stream = (vid4lin_stream*)strm;
+
+    if (stream->io_type == IO_TYPE_MMAP)
+	return vid4lin_stream_get_frame_mmap(stream, frame);
+    else {
+	pj_assert(!"Unsupported i/o type");
+	return PJ_EINVALIDOP;
+    }
+}
+
+/* API: Start stream. */
+static pj_status_t vid4lin_stream_start(pjmedia_vid_stream *strm)
+{
+    vid4lin_stream *stream = (vid4lin_stream*)strm;
+    struct v4l2_buffer buf;
+    enum v4l2_buf_type type;
+    unsigned i;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(stream->fd != -1, PJ_EINVALIDOP);
+
+    PJ_LOG(4, (THIS_FILE, "Starting v4l2 video stream %s", stream->name));
+
+    pj_gettimeofday(&stream->start_time);
+
+    for (i = 0; i < stream->buf_cnt; ++i) {
+	pj_bzero(&buf, sizeof(buf));
+	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	buf.memory = V4L2_MEMORY_MMAP;
+	buf.index = i;
+	status = xioctl(stream->fd, VIDIOC_QBUF, &buf);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+    status = xioctl(stream->fd, VIDIOC_STREAMON, &type);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (i > 0) {
+	/* Dequeue already enqueued buffers. Can we do this while streaming
+	 * is not started?
+	 */
+	unsigned n = i;
+	for (i=0; i<n; ++i) {
+	    pj_bzero(&buf, sizeof(buf));
+	    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	    buf.memory = V4L2_MEMORY_MMAP;
+	    xioctl(stream->fd, VIDIOC_DQBUF, &buf);
+	}
+    }
+    return status;
+}
+
+/* API: Stop stream. */
+static pj_status_t vid4lin_stream_stop(pjmedia_vid_stream *strm)
+{
+    vid4lin_stream *stream = (vid4lin_stream*)strm;
+    enum v4l2_buf_type type;
+    pj_status_t status;
+
+    if (stream->fd < 0)
+	return PJ_SUCCESS;
+
+    PJ_LOG(4, (THIS_FILE, "Stopping v4l2 video stream %s", stream->name));
+
+    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    status = xioctl(stream->fd, VIDIOC_STREAMOFF, &type);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t vid4lin_stream_destroy(pjmedia_vid_stream *strm)
+{
+    vid4lin_stream *stream = (vid4lin_stream*)strm;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+    vid4lin_stream_stop(strm);
+
+    PJ_LOG(4, (THIS_FILE, "Destroying v4l2 video stream %s", stream->name));
+
+    for (i=0; i<stream->buf_cnt; ++i) {
+	if (stream->buffers[i].start != MAP_FAILED) {
+	    v4l2_munmap(stream->buffers[i].start, stream->buffers[i].length);
+	    stream->buffers[i].start = MAP_FAILED;
+	}
+    }
+
+    if (stream->fd >= 0) {
+	v4l2_close(stream->fd);
+	stream->fd = -1;
+    }
+    pj_pool_release(stream->pool);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_VIDEO_DEV_HAS_V4L2 */
diff --git a/pjmedia/src/pjmedia-videodev/videodev.c b/pjmedia/src/pjmedia-videodev/videodev.c
new file mode 100644
index 0000000..57a55de
--- /dev/null
+++ b/pjmedia/src/pjmedia-videodev/videodev.c
@@ -0,0 +1,728 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2010 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-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#define THIS_FILE   "videodev.c"
+
+#define DEFINE_CAP(name, info)	{name, info}
+
+/* Capability names */
+static struct cap_info
+{
+    const char *name;
+    const char *info;
+} cap_infos[] = 
+{
+    DEFINE_CAP("format",        "Video format"),
+    DEFINE_CAP("scale",         "Input dimension"),
+    DEFINE_CAP("window",        "Renderer window"),
+};
+
+
+/*
+ * The device index seen by application and driver is different. 
+ *
+ * At application level, device index is index to global list of device.
+ * At driver level, device index is index to device list on that particular
+ * factory only.
+ */
+#define MAKE_DEV_ID(f_id, index)   (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
+#define GET_INDEX(dev_id)	   ((dev_id) & 0xFFFF)
+#define GET_FID(dev_id)		   ((dev_id) >> 16)
+#define DEFAULT_DEV_ID		    0
+
+
+/* extern functions to create factories */
+#if PJMEDIA_VIDEO_DEV_HAS_NULL_VIDEO
+pjmedia_vid_dev_factory* pjmedia_null_video_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+pjmedia_vid_dev_factory* pjmedia_ffmpeg_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_V4L2
+pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf);
+#endif
+
+#define MAX_DRIVERS	16
+#define MAX_DEVS	64
+
+
+/* driver structure */
+struct driver
+{
+    /* Creation function */
+    pjmedia_vid_dev_factory_create_func_ptr create;
+    /* Factory instance */
+    pjmedia_vid_dev_factory *f;
+    char		     name[32];	    /* Driver name		    */
+    unsigned		     dev_cnt;	    /* Number of devices	    */
+    unsigned		     start_idx;	    /* Start index in global list   */
+    int			     cap_dev_idx;   /* Default capture device.	    */
+    int			     rend_dev_idx;  /* Default render device	    */
+};
+
+/* The video subsystem */
+static struct vid_subsys
+{
+    unsigned	     init_count;	/* How many times init() is called  */
+    pj_pool_factory *pf;		/* The pool factory.		    */
+
+    unsigned	     drv_cnt;		/* Number of drivers.		    */
+    struct driver    drv[MAX_DRIVERS];	/* Array of drivers.		    */
+
+    unsigned	     dev_cnt;		/* Total number of devices.	    */
+    pj_uint32_t	     dev_list[MAX_DEVS];/* Array of device IDs.		    */
+
+} vid_subsys;
+
+/* API: get capability name/info */
+PJ_DEF(const char*) pjmedia_vid_dev_cap_name(pjmedia_vid_dev_cap cap,
+					     const char **p_desc)
+{
+    const char *desc;
+    unsigned i;
+
+    if (p_desc==NULL) p_desc = &desc;
+
+    for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
+	if ((1 << i)==cap)
+	    break;
+    }
+
+    if (i==32) {
+	*p_desc = "??";
+	return "??";
+    }
+
+    *p_desc = cap_infos[i].info;
+    return cap_infos[i].name;
+}
+
+static pj_status_t get_cap_pointer(const pjmedia_vid_param *param,
+				   pjmedia_vid_dev_cap cap,
+				   void **ptr,
+				   unsigned *size)
+{
+#define FIELD_INFO(name)    *ptr = (void*)&param->name; \
+			    *size = sizeof(param->name)
+
+    switch (cap) {
+    case PJMEDIA_VID_DEV_CAP_FORMAT:
+	FIELD_INFO(fmt);
+	break;
+    case PJMEDIA_VID_DEV_CAP_INPUT_SCALE:
+	FIELD_INFO(disp_size);
+	break;
+    case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW:
+	FIELD_INFO(window);
+	break;
+    default:
+	return PJMEDIA_EVID_INVCAP;
+    }
+
+#undef FIELD_INFO
+
+    return PJ_SUCCESS;
+}
+
+/* API: set cap value to param */
+PJ_DEF(pj_status_t) pjmedia_vid_param_set_cap( pjmedia_vid_param *param,
+					       pjmedia_vid_dev_cap cap,
+					       const void *pval)
+{
+    void *cap_ptr;
+    unsigned cap_size;
+    pj_status_t status;
+
+    status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_memcpy(cap_ptr, pval, cap_size);
+    param->flags |= cap;
+
+    return PJ_SUCCESS;
+}
+
+/* API: get cap value from param */
+PJ_DEF(pj_status_t) pjmedia_vid_param_get_cap( const pjmedia_vid_param *param,
+					       pjmedia_vid_dev_cap cap,
+					       void *pval)
+{
+    void *cap_ptr;
+    unsigned cap_size;
+    pj_status_t status;
+
+    status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if ((param->flags & cap) == 0) {
+	pj_bzero(cap_ptr, cap_size);
+	return PJMEDIA_EVID_INVCAP;
+    }
+
+    pj_memcpy(pval, cap_ptr, cap_size);
+    return PJ_SUCCESS;
+}
+
+/* Internal: init driver */
+static pj_status_t init_driver(unsigned drv_idx)
+{
+    struct driver *drv = &vid_subsys.drv[drv_idx];
+    pjmedia_vid_dev_factory *f;
+    unsigned i, dev_cnt;
+    pj_status_t status;
+
+    /* Create the factory */
+    f = (*drv->create)(vid_subsys.pf);
+    if (!f)
+	return PJ_EUNKNOWN;
+
+    /* Call factory->init() */
+    status = f->op->init(f);
+    if (status != PJ_SUCCESS) {
+	f->op->destroy(f);
+	return status;
+    }
+
+    /* Get number of devices */
+    dev_cnt = f->op->get_dev_count(f);
+    if (dev_cnt + vid_subsys.dev_cnt > MAX_DEVS) {
+	PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
+			      " there are too many devices",
+			      vid_subsys.dev_cnt + dev_cnt - MAX_DEVS));
+	dev_cnt = MAX_DEVS - vid_subsys.dev_cnt;
+    }
+
+    /* enabling this will cause pjsua-lib initialization to fail when there
+     * is no video device installed in the system, even when pjsua has been
+     * run with --null-video
+     *
+    if (dev_cnt == 0) {
+	f->op->destroy(f);
+	return PJMEDIA_EVID_NODEV;
+    }
+    */
+
+    /* Fill in default devices */
+    drv->rend_dev_idx = drv->cap_dev_idx = -1;
+    for (i=0; i<dev_cnt; ++i) {
+	pjmedia_vid_dev_info info;
+
+	status = f->op->get_dev_info(f, i, &info);
+	if (status != PJ_SUCCESS) {
+	    f->op->destroy(f);
+	    return status;
+	}
+
+	if (drv->name[0]=='\0') {
+	    /* Set driver name */
+	    pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
+	    drv->name[sizeof(drv->name)-1] = '\0';
+	}
+
+	if (drv->rend_dev_idx < 0 && (info.dir & PJMEDIA_DIR_RENDER)) {
+	    /* Set default render device */
+	    drv->rend_dev_idx = i;
+	}
+	if (drv->cap_dev_idx < 0 && (info.dir & PJMEDIA_DIR_CAPTURE)) {
+	    /* Set default capture device */
+	    drv->cap_dev_idx = i;
+	}
+
+        if (drv->rend_dev_idx >= 0 && drv->cap_dev_idx >= 0) {
+	    /* Done. */
+	    break;
+	}
+    }
+
+    /* Register the factory */
+    drv->f = f;
+    drv->f->sys.drv_idx = drv_idx;
+    drv->start_idx = vid_subsys.dev_cnt;
+    drv->dev_cnt = dev_cnt;
+
+    /* Register devices to global list */
+    for (i=0; i<dev_cnt; ++i) {
+	vid_subsys.dev_list[vid_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Internal: deinit driver */
+static void deinit_driver(unsigned drv_idx)
+{
+    struct driver *drv = &vid_subsys.drv[drv_idx];
+
+    if (drv->f) {
+	drv->f->op->destroy(drv->f);
+	drv->f = NULL;
+    }
+
+    drv->dev_cnt = 0;
+    drv->rend_dev_idx = drv->cap_dev_idx = -1;
+}
+
+/* API: Initialize the video subsystem. */
+PJ_DEF(pj_status_t) pjmedia_vid_subsys_init(pj_pool_factory *pf)
+{
+    unsigned i;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Allow init() to be called multiple times as long as there is matching
+     * number of shutdown().
+     */
+    if (vid_subsys.init_count++ != 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* Register error subsystem */
+    pj_register_strerror(PJMEDIA_VIDEODEV_ERRNO_START, 
+			 PJ_ERRNO_SPACE_SIZE, 
+			 &pjmedia_videodev_strerror);
+
+    /* Init */
+    vid_subsys.pf = pf;
+    vid_subsys.drv_cnt = 0;
+    vid_subsys.dev_cnt = 0;
+
+    /* Register creation functions */
+#if PJMEDIA_VIDEO_DEV_HAS_V4L2
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_v4l2_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_dshow_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ffmpeg_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_cbar_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_sdl_factory;
+#endif
+
+    /* Initialize each factory and build the device ID list */
+    for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	status = init_driver(i);
+	if (status != PJ_SUCCESS) {
+	    deinit_driver(i);
+	    continue;
+	}
+    }
+
+    return vid_subsys.dev_cnt ? PJ_SUCCESS : status;
+}
+
+/* API: register an video device factory to the video subsystem. */
+PJ_DEF(pj_status_t)
+pjmedia_vid_register_factory(pjmedia_vid_dev_factory_create_func_ptr adf)
+{
+    pj_status_t status;
+
+    if (vid_subsys.init_count == 0)
+	return PJMEDIA_EVID_INIT;
+
+    vid_subsys.drv[vid_subsys.drv_cnt].create = adf;
+    status = init_driver(vid_subsys.drv_cnt);
+    if (status == PJ_SUCCESS) {
+	vid_subsys.drv_cnt++;
+    } else {
+	deinit_driver(vid_subsys.drv_cnt);
+    }
+
+    return status;
+}
+
+/* API: unregister an video device factory from the video subsystem. */
+PJ_DEF(pj_status_t)
+pjmedia_vid_unregister_factory(pjmedia_vid_dev_factory_create_func_ptr adf)
+{
+    unsigned i, j;
+
+    if (vid_subsys.init_count == 0)
+	return PJMEDIA_EVID_INIT;
+
+    for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	struct driver *drv = &vid_subsys.drv[i];
+
+	if (drv->create == adf) {
+	    for (j = drv->start_idx; j < drv->start_idx + drv->dev_cnt; j++)
+	    {
+		vid_subsys.dev_list[j] = (pj_uint32_t)PJMEDIA_VID_INVALID_DEV;
+	    }
+
+	    deinit_driver(i);
+	    pj_bzero(drv, sizeof(*drv));
+	    return PJ_SUCCESS;
+	}
+    }
+
+    return PJMEDIA_EVID_ERR;
+}
+
+/* API: get the pool factory registered to the video subsystem. */
+PJ_DEF(pj_pool_factory*) pjmedia_vid_subsys_get_pool_factory(void)
+{
+    return vid_subsys.pf;
+}
+
+/* API: Shutdown the video subsystem. */
+PJ_DEF(pj_status_t) pjmedia_vid_subsys_shutdown(void)
+{
+    unsigned i;
+
+    /* 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;
+    }
+    --vid_subsys.init_count;
+
+    for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	deinit_driver(i);
+    }
+
+    vid_subsys.pf = NULL;
+    return PJ_SUCCESS;
+}
+
+/* API: Get the number of video devices installed in the system. */
+PJ_DEF(unsigned) pjmedia_vid_dev_count(void)
+{
+    return vid_subsys.dev_cnt;
+}
+
+/* Internal: convert local index to global device index */
+static pj_status_t make_global_index(unsigned drv_idx, 
+				     pjmedia_vid_dev_index *id)
+{
+    if (*id < 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* Check that factory still exists */
+    PJ_ASSERT_RETURN(vid_subsys.drv[drv_idx].f, PJ_EBUG);
+
+    /* Check that device index is valid */
+    PJ_ASSERT_RETURN(*id>=0 && *id<(int)vid_subsys.drv[drv_idx].dev_cnt, 
+		     PJ_EBUG);
+
+    *id += vid_subsys.drv[drv_idx].start_idx;
+    return PJ_SUCCESS;
+}
+
+/* Internal: lookup device id */
+static pj_status_t lookup_dev(pjmedia_vid_dev_index id,
+			      pjmedia_vid_dev_factory **p_f,
+			      unsigned *p_local_index)
+{
+    int f_id, index;
+
+    if (id < 0) {
+	unsigned i;
+
+	if (id == PJMEDIA_VID_INVALID_DEV)
+	    return PJMEDIA_EVID_INVDEV;
+
+	for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	    struct driver *drv = &vid_subsys.drv[i];
+	    if (id==PJMEDIA_VID_DEFAULT_CAPTURE_DEV && 
+		drv->cap_dev_idx >= 0) 
+	    {
+		id = drv->cap_dev_idx;
+		make_global_index(i, &id);
+		break;
+	    } else if (id==PJMEDIA_VID_DEFAULT_RENDER_DEV && 
+		drv->rend_dev_idx >= 0) 
+	    {
+		id = drv->rend_dev_idx;
+		make_global_index(i, &id);
+		break;
+	    }
+	}
+
+	if (id < 0) {
+	    return PJMEDIA_EVID_NODEFDEV;
+	}
+    }
+
+    f_id = GET_FID(vid_subsys.dev_list[id]);
+    index = GET_INDEX(vid_subsys.dev_list[id]);
+
+    if (f_id < 0 || f_id >= (int)vid_subsys.drv_cnt)
+	return PJMEDIA_EVID_INVDEV;
+
+    if (index < 0 || index >= (int)vid_subsys.drv[f_id].dev_cnt)
+	return PJMEDIA_EVID_INVDEV;
+
+    *p_f = vid_subsys.drv[f_id].f;
+    *p_local_index = (unsigned)index;
+
+    return PJ_SUCCESS;
+
+}
+
+/* API: Get device information. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_get_info(pjmedia_vid_dev_index id,
+					     pjmedia_vid_dev_info *info)
+{
+    pjmedia_vid_dev_factory *f;
+    unsigned index;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(info && id!=PJMEDIA_VID_INVALID_DEV, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+    status = lookup_dev(id, &f, &index);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    return f->op->get_dev_info(f, index, info);
+}
+
+/* API: find device */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_lookup( const char *drv_name,
+					    const char *dev_name,
+					    pjmedia_vid_dev_index *id)
+{
+    pjmedia_vid_dev_factory *f = NULL;
+    unsigned drv_idx, dev_idx;
+
+    PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
+    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)) {
+	    f = vid_subsys.drv[drv_idx].f;
+	    break;
+	}
+    }
+
+    if (!f)
+	return PJ_ENOTFOUND;
+
+    for (dev_idx=0; dev_idx<vid_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
+	pjmedia_vid_dev_info info;
+	pj_status_t status;
+
+	status = f->op->get_dev_info(f, dev_idx, &info);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	if (!pj_ansi_stricmp(dev_name, info.name))
+	    break;
+    }
+
+    if (dev_idx==vid_subsys.drv[drv_idx].dev_cnt)
+	return PJ_ENOTFOUND;
+
+    *id = dev_idx;
+    make_global_index(drv_idx, id);
+
+    return PJ_SUCCESS;
+}
+
+/* API: Initialize the video device parameters with default values for the
+ * specified device.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_default_param(pj_pool_t *pool,
+                                                  pjmedia_vid_dev_index id,
+						  pjmedia_vid_param *param)
+{
+    pjmedia_vid_dev_factory *f;
+    unsigned index;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(param && id!=PJMEDIA_VID_INVALID_DEV, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+    status = lookup_dev(id, &f, &index);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = f->op->default_param(pool, f, index, param);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Normalize device IDs */
+    make_global_index(f->sys.drv_idx, &param->cap_id);
+    make_global_index(f->sys.drv_idx, &param->rend_id);
+
+    return PJ_SUCCESS;
+}
+
+/* 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)
+{
+    pjmedia_vid_dev_factory *cap_f=NULL, *rend_f=NULL, *f=NULL;
+    pjmedia_vid_param param;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(prm && prm->dir && p_vid_strm, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+    PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
+		     prm->dir==PJMEDIA_DIR_RENDER ||
+		     prm->dir==PJMEDIA_DIR_CAPTURE_RENDER,
+		     PJ_EINVAL);
+
+    /* Must make copy of param because we're changing device ID */
+    pj_memcpy(&param, prm, sizeof(param));
+
+    /* Normalize cap_id */
+    if (param.dir & PJMEDIA_DIR_CAPTURE) {
+	unsigned index;
+
+	if (param.cap_id < 0)
+	    param.cap_id = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+
+	status = lookup_dev(param.cap_id, &cap_f, &index);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	param.cap_id = index;
+	f = cap_f;
+    }
+
+    /* Normalize rend_id */
+    if (param.dir & PJMEDIA_DIR_RENDER) {
+	unsigned index;
+
+	if (param.rend_id < 0)
+	    param.rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
+
+	status = lookup_dev(param.rend_id, &rend_f, &index);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	param.rend_id = index;
+	f = rend_f;
+    }
+
+    PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
+
+    /* For now, cap_id and rend_id must belong to the same factory */
+    PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_RENDER) || 
+		     (cap_f == rend_f),
+		     PJMEDIA_EVID_INVDEV);
+
+    /* Create the stream */
+    status = f->op->create_stream(f, &param, cb,
+				  user_data, p_vid_strm);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Assign factory id to the stream */
+    (*p_vid_strm)->sys.drv_idx = f->sys.drv_idx;
+    return PJ_SUCCESS;
+}
+
+/* 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_status_t status;
+
+    PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+    status = strm->op->get_param(strm, param);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Normalize device id's */
+    make_global_index(strm->sys.drv_idx, &param->cap_id);
+    make_global_index(strm->sys.drv_idx, &param->rend_id);
+
+    return PJ_SUCCESS;
+}
+
+/* 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)
+{
+    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)
+{
+    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)
+{
+    return strm->op->start(strm);
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_stream_get_frame(pjmedia_vid_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_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)
+{
+    return strm->op->stop(strm);
+}
+
+/* API: Destroy the stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy(pjmedia_vid_stream *strm)
+{
+    return strm->op->destroy(strm);
+}
diff --git a/pjmedia/src/pjmedia/avi_player.c b/pjmedia/src/pjmedia/avi_player.c
new file mode 100644
index 0000000..8289a3b
--- /dev/null
+++ b/pjmedia/src/pjmedia/avi_player.c
@@ -0,0 +1,710 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2010 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 
+ */
+
+/**
+ * Default file player/writer buffer size.
+ */
+#include <pjmedia/avi_stream.h>
+#include <pjmedia/avi.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/wave.h>
+#include <pj/assert.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE   "avi_player.c"
+
+#define AVIF_MUSTUSEINDEX       0x00000020
+#define AVIF_ISINTERLEAVED      0x00000100
+#define AVISF_DISABLED          0x00000001
+#define AVISF_VIDEO_PALCHANGES  0x00010000
+
+#define AVI_EOF 0xFFEEFFEE
+
+#define COMPARE_TAG(doc_tag, tag) (doc_tag == *((pj_uint32_t *)avi_tags[tag]))
+
+#define SIGNATURE	    PJMEDIA_PORT_SIGNATURE('A', 'V', 'I', 'P')
+
+#if 0
+#   define TRACE_(x)	PJ_LOG(4,x)
+#else
+#   define TRACE_(x)
+#endif
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+    static void data_to_host(void *data, pj_uint8_t bits, unsigned count)
+    {
+	unsigned i;
+        pj_int32_t *data32 = (pj_int32_t *)data;
+        pj_int16_t *data16 = (pj_int16_t *)data;
+        count /= (bits == 32? 4 : 2);
+	for (i=0; i<count; ++i) {
+	    if (bits == 32)
+                data32[i] = pj_swap32(data32[i]);
+            else
+                data16[i] = pj_swap16(data16[i]);
+	}
+    }
+    static void data_to_host2(void *data, pj_uint8_t nsizes,
+                              pj_uint8_t *sizes)
+    {
+	unsigned i;
+        pj_int8_t *datap = (pj_int8_t *)data;
+        for (i = 0; i < nsizes; i++) {
+            data_to_host(datap, 32, sizes[i]);
+            datap += sizes[i++];
+            if (i >= nsizes)
+                break;
+            data_to_host(datap, 16, sizes[i]);
+            datap += sizes[i];
+	}
+    }
+#else
+#   define data_to_host(data, bits, count)
+#   define data_to_host2(data, nsizes, sizes)
+#endif
+
+struct pjmedia_avi_streams
+{
+    pj_uint8_t      num_streams;
+    pjmedia_port  **streams;
+};
+
+struct avi_reader_port
+{
+    pjmedia_port     base;
+    pj_uint8_t       stream_id;
+    unsigned	     options;
+    pj_uint16_t	     bits_per_sample;
+    pj_bool_t	     eof;
+    pj_off_t	     fsize;
+    pj_off_t	     start_data;
+    pj_uint8_t       pad;
+    pj_oshandle_t    fd;
+    pj_ssize_t       size_left;
+
+    pj_status_t	   (*cb)(pjmedia_port*, void*);
+};
+
+
+static pj_status_t avi_get_frame(pjmedia_port *this_port, 
+			         pjmedia_frame *frame);
+static pj_status_t avi_on_destroy(pjmedia_port *this_port);
+
+static struct avi_reader_port *create_avi_port(pj_pool_t *pool)
+{
+    const pj_str_t name = pj_str("file");
+    struct avi_reader_port *port;
+
+    port = PJ_POOL_ZALLOC_T(pool, struct avi_reader_port);
+    if (!port)
+	return NULL;
+
+    /* Put in default values.
+     * These will be overriden once the file is read.
+     */
+    pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, 
+			   8000, 1, 16, 80);
+
+    port->fd = (pj_oshandle_t)-1;
+    port->base.get_frame = &avi_get_frame;
+    port->base.on_destroy = &avi_on_destroy;
+
+    return port;
+}
+
+#define file_read(fd, data, size) file_read2(fd, data, size, 32)
+#define file_read2(fd, data, size, bits) file_read3(fd, data, size, bits, NULL)
+
+static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size,
+                              pj_uint16_t bits, pj_ssize_t *psz_read)
+{
+    pj_ssize_t size_read = size, size_to_read = size;
+    pj_status_t status = pj_file_read(fd, data, &size_read);
+    if (status != PJ_SUCCESS)
+        return status;
+
+    /* Normalize AVI header fields values from little-endian to host
+     * byte order.
+     */
+    if (bits > 0)
+        data_to_host(data, bits, size_read);
+
+    if (size_read != size_to_read) {
+        if (psz_read)
+            *psz_read = size_read;
+        return AVI_EOF;
+    }
+
+    return status;
+}
+
+/*
+ * Create AVI player port.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_avi_player_create_streams(pj_pool_t *pool,
+                                  const char *filename,
+				  unsigned options,
+				  pjmedia_avi_streams **p_streams)
+{
+    pjmedia_avi_hdr avi_hdr;
+    struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS];
+    pj_off_t pos;
+    pj_uint8_t i, nstr = 0;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(pool && filename && p_streams, PJ_EINVAL);
+
+    /* Check the file really exists. */
+    if (!pj_file_exists(filename)) {
+	return PJ_ENOTFOUND;
+    }
+
+    /* Create fport instance. */
+    fport[0] = create_avi_port(pool);
+    if (!fport[0]) {
+	return PJ_ENOMEM;
+    }
+
+    /* Get the file size. */
+    fport[0]->fsize = pj_file_size(filename);
+
+    /* Size must be more than AVI header size */
+    if (fport[0]->fsize <= sizeof(riff_hdr_t) + sizeof(avih_hdr_t) + 
+                           sizeof(strl_hdr_t))
+    {
+	return PJMEDIA_EINVALIMEDIATYPE;
+    }
+
+    /* Open file. */
+    status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[0]->fd);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Read the RIFF + AVIH header. */
+    status = file_read(fport[0]->fd, &avi_hdr,
+                       sizeof(riff_hdr_t) + sizeof(avih_hdr_t));
+    if (status != PJ_SUCCESS)
+        goto on_error;
+
+    /* Validate AVI file. */
+    if (!COMPARE_TAG(avi_hdr.riff_hdr.riff, PJMEDIA_AVI_RIFF_TAG) ||
+	!COMPARE_TAG(avi_hdr.riff_hdr.avi, PJMEDIA_AVI_AVI_TAG) ||
+        !COMPARE_TAG(avi_hdr.avih_hdr.list_tag, PJMEDIA_AVI_LIST_TAG) ||
+        !COMPARE_TAG(avi_hdr.avih_hdr.hdrl_tag, PJMEDIA_AVI_HDRL_TAG) ||
+        !COMPARE_TAG(avi_hdr.avih_hdr.avih, PJMEDIA_AVI_AVIH_TAG))
+    {
+	status = PJMEDIA_EINVALIMEDIATYPE;
+        goto on_error;
+    }
+
+    PJ_LOG(5, (THIS_FILE, "The AVI file has %d streams.",
+               avi_hdr.avih_hdr.num_streams));
+
+    /* Unsupported AVI format. */
+    if (avi_hdr.avih_hdr.num_streams > PJMEDIA_AVI_MAX_NUM_STREAMS) {
+        status = PJMEDIA_EAVIUNSUPP;
+        goto on_error;
+    }
+
+    /** 
+     * TODO: Possibly unsupported AVI format.
+     * If you encounter this warning, verify whether the avi player
+     * is working properly.
+     */
+    if (avi_hdr.avih_hdr.flags & AVIF_MUSTUSEINDEX ||
+        avi_hdr.avih_hdr.pad > 1)
+    {
+        PJ_LOG(3, (THIS_FILE, "Warning!!! Possibly unsupported AVI format: "
+                   "flags:%d, pad:%d", avi_hdr.avih_hdr.flags, 
+                   avi_hdr.avih_hdr.pad));
+    }
+
+    /* Read the headers of each stream. */
+    for (i = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
+        pj_size_t elem = 0;
+        pj_ssize_t size_to_read;
+
+        /* Read strl header */
+        status = file_read(fport[0]->fd, &avi_hdr.strl_hdr[i],
+                           sizeof(strl_hdr_t));
+        if (status != PJ_SUCCESS)
+            goto on_error;
+        
+        elem = COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, 
+                           PJMEDIA_AVI_VIDS_TAG) ? 
+               sizeof(strf_video_hdr_t) :
+               COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, 
+                           PJMEDIA_AVI_AUDS_TAG) ?
+               sizeof(strf_audio_hdr_t) : 0;
+
+        /* Read strf header */
+        status = file_read2(fport[0]->fd, &avi_hdr.strf_hdr[i],
+                            elem, 0);
+        if (status != PJ_SUCCESS)
+            goto on_error;
+
+        /* Normalize the endian */
+        if (elem == sizeof(strf_video_hdr_t))
+            data_to_host2(&avi_hdr.strf_hdr[i],
+                          sizeof(strf_video_hdr_sizes)/
+                          sizeof(strf_video_hdr_sizes[0]),
+                          strf_video_hdr_sizes);
+        else if (elem == sizeof(strf_audio_hdr_t))
+            data_to_host2(&avi_hdr.strf_hdr[i],
+                          sizeof(strf_audio_hdr_sizes)/
+                          sizeof(strf_audio_hdr_sizes[0]),
+                          strf_audio_hdr_sizes);
+
+        /* Skip the remainder of the header */
+        size_to_read = avi_hdr.strl_hdr[i].list_sz - (sizeof(strl_hdr_t) -
+                       8) - elem;
+	status = pj_file_setpos(fport[0]->fd, size_to_read, PJ_SEEK_CUR);
+	if (status != PJ_SUCCESS) {
+            goto on_error;
+	}
+    }
+
+    /* Finish reading the AVIH header */
+    status = pj_file_setpos(fport[0]->fd, avi_hdr.avih_hdr.list_sz +
+                            sizeof(riff_hdr_t) + 8, PJ_SEEK_SET);
+    if (status != PJ_SUCCESS) {
+        goto on_error;
+    }
+
+    /* Skip any JUNK or LIST INFO until we get MOVI tag */
+    do {
+        pjmedia_avi_subchunk ch;
+        int read = 0;
+
+        status = file_read(fport[0]->fd, &ch, sizeof(pjmedia_avi_subchunk));
+        if (status != PJ_SUCCESS) {
+            goto on_error;
+        }
+
+        if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
+        {
+            read = 4;
+            status = file_read(fport[0]->fd, &ch, read);
+            if (COMPARE_TAG(ch.id, PJMEDIA_AVI_MOVI_TAG))
+                break;
+        }
+
+        status = pj_file_setpos(fport[0]->fd, ch.len-read, PJ_SEEK_CUR);
+        if (status != PJ_SUCCESS) {
+            goto on_error;
+        }
+    } while(1);
+
+    status = pj_file_getpos(fport[0]->fd, &pos);
+    if (status != PJ_SUCCESS)
+        goto on_error;
+
+    for (i = 0, nstr = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
+        /* Skip non-audio, non-video, or disabled streams) */
+        if ((!COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, 
+                          PJMEDIA_AVI_VIDS_TAG) &&
+             !COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, 
+                          PJMEDIA_AVI_AUDS_TAG)) ||
+            avi_hdr.strl_hdr[i].flags & AVISF_DISABLED)
+        {
+            continue;
+        }
+
+        if (COMPARE_TAG(avi_hdr.strl_hdr[i].data_type, 
+                        PJMEDIA_AVI_VIDS_TAG))
+        {
+            /* Check supported video formats here */
+            if (avi_hdr.strl_hdr[i].flags & AVISF_VIDEO_PALCHANGES ||
+                (avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_MJPEG &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_UYVY &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_YUY2 &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_IYUV &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_I420 &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_DIB &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_RGB24 &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_RGB32))
+            {
+                PJ_LOG(4, (THIS_FILE, "Unsupported video stream"));
+                continue;
+            }
+        } else {
+            /* Check supported audio formats here */
+            if ((avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_PCM &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ALAW &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ULAW &&
+                 avi_hdr.strl_hdr[i].codec != PJMEDIA_WAVE_FMT_TAG_PCM) ||
+                avi_hdr.strf_hdr[i].strf_audio_hdr.bits_per_sample != 16)
+            {
+                PJ_LOG(4, (THIS_FILE, "Unsupported audio stream"));
+                continue;
+            }
+        }
+
+        if (nstr == 0) {
+            fport[0]->stream_id = i;
+            nstr++;
+            continue;
+        }
+
+        /* Create fport instance. */
+        fport[nstr] = create_avi_port(pool);
+        if (!fport[nstr]) {
+	    status = PJ_ENOMEM;
+            goto on_error;
+        }
+
+        fport[nstr]->stream_id = i;
+
+        /* Open file. */
+        status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[nstr]->fd);
+        if (status != PJ_SUCCESS)
+            goto on_error;
+
+        /* Set the file position */
+        status = pj_file_setpos(fport[nstr]->fd, pos, PJ_SEEK_SET);
+        if (status != PJ_SUCCESS) {
+            goto on_error;
+        }
+
+        nstr++;
+    }
+
+    if (nstr == 0) {
+        status = PJMEDIA_EAVIUNSUPP;
+        goto on_error;
+    }
+
+    for (i = 0; i < nstr; i++) {
+        strl_hdr_t *strl_hdr = &avi_hdr.strl_hdr[fport[i]->stream_id];
+
+        /* Initialize */
+        fport[i]->options = options;
+        fport[i]->fsize = fport[0]->fsize;
+        /* Current file position now points to start of data */
+        fport[i]->start_data = pos;
+        
+        if (COMPARE_TAG(strl_hdr->data_type, PJMEDIA_AVI_VIDS_TAG)) {
+            strf_video_hdr_t *strf_hdr =
+                &avi_hdr.strf_hdr[fport[i]->stream_id].strf_video_hdr;
+            const pjmedia_video_format_info *vfi;
+
+            vfi = pjmedia_get_video_format_info(
+                pjmedia_video_format_mgr_instance(),
+                strl_hdr->codec);
+
+            fport[i]->bits_per_sample = (vfi ? vfi->bpp : 0);
+            pjmedia_format_init_video(&fport[i]->base.info.fmt,
+                                      strl_hdr->codec,
+                                      strf_hdr->biWidth,
+                                      strf_hdr->biHeight,
+                                      strl_hdr->rate,
+                                      strl_hdr->scale);
+            
+        } else {
+            strf_audio_hdr_t *strf_hdr =
+                &avi_hdr.strf_hdr[fport[i]->stream_id].strf_audio_hdr;
+
+            fport[i]->bits_per_sample = strf_hdr->bits_per_sample;
+            pjmedia_format_init_audio(&fport[i]->base.info.fmt,
+                                      strl_hdr->codec,
+                                      strf_hdr->sample_rate,
+                                      strf_hdr->nchannels,
+                                      strf_hdr->bits_per_sample,
+                                      20000,
+                                      strf_hdr->bytes_per_sec,
+                                      strf_hdr->bytes_per_sec);
+        }
+
+        pj_strdup2(pool, &fport[i]->base.info.name, filename);
+    }
+
+    /* Done. */
+    *p_streams = pj_pool_alloc(pool, sizeof(pjmedia_avi_streams));
+    (*p_streams)->num_streams = nstr;
+    (*p_streams)->streams = pj_pool_calloc(pool, (*p_streams)->num_streams,
+                                           sizeof(pjmedia_port *));
+    for (i = 0; i < nstr; i++)
+        (*p_streams)->streams[i] = &fport[i]->base;
+
+    PJ_LOG(4,(THIS_FILE, 
+	      "AVI file player '%.*s' created with "
+	      "%d media ports",
+	      (int)fport[0]->base.info.name.slen,
+	      fport[0]->base.info.name.ptr,
+              (*p_streams)->num_streams));
+
+    return PJ_SUCCESS;
+
+on_error:
+    fport[0]->base.on_destroy(&fport[0]->base);
+    for (i = 1; i < nstr; i++)
+        fport[i]->base.on_destroy(&fport[i]->base);
+    if (status == AVI_EOF)
+        return PJMEDIA_EINVALIMEDIATYPE;
+    return status;
+}
+
+PJ_DEF(pj_uint8_t)
+pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams)
+{
+    pj_assert(streams);
+    return streams->num_streams;
+}
+
+PJ_DEF(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams,
+                               pj_uint8_t idx)
+{
+    pj_assert(streams);
+    return (idx >=0 && idx < streams->num_streams ?
+            streams->streams[idx] : NULL);
+}
+
+PJ_DEF(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams,
+                                        pj_uint8_t start_idx,
+                                        pjmedia_type media_type)
+{
+    pj_uint8_t i;
+
+    pj_assert(streams);
+    for (i = start_idx; i < streams->num_streams; i++)
+        if (streams->streams[i]->info.fmt.type == media_type)
+            return streams->streams[i];
+    return NULL;
+}
+
+
+/*
+ * Get the data length, in bytes.
+ */
+PJ_DEF(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream)
+{
+    struct avi_reader_port *fport;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
+
+    /* Check that this is really a player port */
+    PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+    fport = (struct avi_reader_port*) stream;
+
+    return (pj_ssize_t)(fport->fsize - fport->start_data);
+}
+
+
+/*
+ * Register a callback to be called when the file reading has reached the
+ * end of file.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_avi_stream_set_eof_cb( pjmedia_avi_stream *stream,
+			       void *user_data,
+			       pj_status_t (*cb)(pjmedia_avi_stream *stream,
+						 void *usr_data))
+{
+    struct avi_reader_port *fport;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
+
+    /* Check that this is really a player port */
+    PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+    fport = (struct avi_reader_port*) stream;
+
+    fport->base.port_data.pdata = user_data;
+    fport->cb = cb;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frame from file.
+ */
+static pj_status_t avi_get_frame(pjmedia_port *this_port, 
+			         pjmedia_frame *frame)
+{
+    struct avi_reader_port *fport = (struct avi_reader_port*)this_port;
+    pj_status_t status;
+    pj_ssize_t size_read = 0, size_to_read = 0;
+
+    pj_assert(fport->base.info.signature == SIGNATURE);
+
+    /* We encountered end of file */
+    if (fport->eof) {
+	pj_status_t status = PJ_SUCCESS;
+
+	PJ_LOG(5,(THIS_FILE, "File port %.*s EOF",
+		  (int)fport->base.info.name.slen,
+		  fport->base.info.name.ptr));
+
+	/* Call callback, if any */
+	if (fport->cb)
+	    status = (*fport->cb)(this_port, fport->base.port_data.pdata);
+
+	/* If callback returns non PJ_SUCCESS or 'no loop' is specified,
+	 * return immediately (and don't try to access player port since
+	 * it might have been destroyed by the callback).
+	 */
+	if ((status != PJ_SUCCESS) ||
+            (fport->options & PJMEDIA_AVI_FILE_NO_LOOP)) 
+        {
+	    frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	    frame->size = 0;
+	    return PJ_EEOF;
+	}
+
+        /* Rewind file */
+	PJ_LOG(5,(THIS_FILE, "File port %.*s rewinding..",
+		  (int)fport->base.info.name.slen,
+		  fport->base.info.name.ptr));
+	fport->eof = PJ_FALSE;
+        pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET);
+    }
+
+    /* Fill frame buffer. */
+    size_to_read = frame->size;
+    do {
+        pjmedia_avi_subchunk ch = {0, 0};
+        char *cid;
+        pj_uint8_t stream_id;
+
+        /* We need to read data from the file past the chunk boundary */
+        if (fport->size_left > 0 && fport->size_left < size_to_read) {
+            status = file_read3(fport->fd, frame->buf, fport->size_left,
+                                fport->bits_per_sample, &size_read);
+            if (status != PJ_SUCCESS)
+                goto on_error2;
+            size_to_read -= fport->size_left;
+            fport->size_left = 0;
+        }
+
+        /* Read new chunk data */
+        if (fport->size_left == 0) {
+            pj_off_t pos;
+            pj_file_getpos(fport->fd, &pos);
+
+            /* Data is padded to the nearest WORD boundary */
+            if (fport->pad) {
+                status = pj_file_setpos(fport->fd, fport->pad, PJ_SEEK_CUR);
+                fport->pad = 0;
+            }
+
+            status = file_read(fport->fd, &ch, sizeof(pjmedia_avi_subchunk));
+            if (status != PJ_SUCCESS) {
+                size_read = 0;
+                goto on_error2;
+            }
+
+            cid = (char *)&ch.id;
+            if (cid[0] >= '0' && cid[0] <= '9' &&
+                cid[1] >= '0' && cid[1] <= '9') 
+            {
+                stream_id = (cid[0] - '0') * 10 + (cid[1] - '0');
+            } else
+                stream_id = 100;
+            fport->pad = (pj_uint8_t)ch.len & 1;
+
+            TRACE_((THIS_FILE, "Reading movi data at pos %u (%x), id: %.*s, "
+                               "length: %u", (unsigned long)pos,
+                               (unsigned long)pos, 4, cid, ch.len));
+
+            /* We are only interested in data with our stream id */
+            if (stream_id != fport->stream_id) {
+                if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
+                    PJ_LOG(5, (THIS_FILE, "Unsupported LIST tag found in "
+                                          "the movi data."));
+                else if (COMPARE_TAG(ch.id, PJMEDIA_AVI_RIFF_TAG)) {
+                    PJ_LOG(3, (THIS_FILE, "Unsupported format: multiple "
+                           "AVIs in a single file."));
+                    status = AVI_EOF;
+                    goto on_error2;
+                }
+
+                status = pj_file_setpos(fport->fd, ch.len,
+                                        PJ_SEEK_CUR);
+                continue;
+            }
+            fport->size_left = ch.len;
+        }
+
+        frame->type = (fport->base.info.fmt.type == PJMEDIA_TYPE_VIDEO ?
+                       PJMEDIA_FRAME_TYPE_VIDEO : PJMEDIA_FRAME_TYPE_AUDIO);
+        frame->timestamp.u64 = 0;
+        if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+            if (size_to_read > fport->size_left)
+                size_to_read = fport->size_left;
+            status = file_read3(fport->fd, (char *)frame->buf + frame->size -
+                                size_to_read, size_to_read,
+                                fport->bits_per_sample, &size_read);
+            if (status != PJ_SUCCESS)
+                goto on_error2;
+            fport->size_left -= size_to_read;
+        } else {
+            pj_assert(frame->size >= ch.len);
+            status = file_read3(fport->fd, frame->buf, ch.len,
+                                0, &size_read);
+            if (status != PJ_SUCCESS)
+                goto on_error2;
+            frame->size = ch.len;
+            fport->size_left = 0;
+        }
+
+        break;
+
+    } while(1);
+
+    return PJ_SUCCESS;
+
+on_error2:
+    if (status == AVI_EOF) {
+        size_to_read -= size_read;
+        pj_bzero((char *)frame->buf + frame->size - size_to_read,
+                 size_to_read);
+        fport->eof = PJ_TRUE;
+
+        return PJ_SUCCESS;
+    }
+
+    return status;
+}
+
+/*
+ * Destroy port.
+ */
+static pj_status_t avi_on_destroy(pjmedia_port *this_port)
+{
+    struct avi_reader_port *fport = (struct avi_reader_port*) this_port;
+
+    pj_assert(this_port->info.signature == SIGNATURE);
+
+    if (fport->fd != (pj_oshandle_t) -1)
+        pj_file_close(fport->fd);
+    return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/bidirectional.c b/pjmedia/src/pjmedia/bidirectional.c
index 286ec1c..05be97e 100644
--- a/pjmedia/src/pjmedia/bidirectional.c
+++ b/pjmedia/src/pjmedia/bidirectional.c
@@ -33,7 +33,7 @@
 
 
 static pj_status_t put_frame(pjmedia_port *this_port, 
-			     const pjmedia_frame *frame)
+			     pjmedia_frame *frame)
 {
     struct bidir_port *p = (struct bidir_port*)this_port;
     return pjmedia_port_put_frame(p->put_port, frame);
@@ -54,14 +54,16 @@
 						       pjmedia_port **p_port )
 {
     struct bidir_port *port;
+    const pjmedia_audio_format_detail *gafd;
 
     port = PJ_POOL_ZALLOC_T(pool, struct bidir_port);
+    gafd = pjmedia_format_get_audio_format_detail(&get_port->info.fmt, 1);
 
     pjmedia_port_info_init(&port->base.info, &get_port->info.name, SIGNATURE,
-			   get_port->info.clock_rate,
-			   get_port->info.channel_count,
-			   get_port->info.bits_per_sample,
-			   get_port->info.samples_per_frame);
+			   gafd->clock_rate,
+			   gafd->channel_count,
+			   gafd->bits_per_sample,
+			   PJMEDIA_AFD_SPF(gafd));
 
     port->get_port = get_port;
     port->put_port = put_port;
diff --git a/pjmedia/src/pjmedia/clock_thread.c b/pjmedia/src/pjmedia/clock_thread.c
index 9da38e3..cbab95f 100644
--- a/pjmedia/src/pjmedia/clock_thread.c
+++ b/pjmedia/src/pjmedia/clock_thread.c
@@ -50,6 +50,7 @@
 static int clock_thread(void *arg);
 
 #define MAX_JUMP_MSEC	500
+#define USEC_IN_SEC	(pj_uint64_t)1000000
 
 /*
  * Create media clock.
@@ -63,25 +64,38 @@
 					  void *user_data,
 					  pjmedia_clock **p_clock)
 {
+    return pjmedia_clock_create2(pool,
+			         (unsigned)(samples_per_frame * USEC_IN_SEC /
+					    channel_count / clock_rate),
+				 clock_rate, options, cb, user_data, p_clock);
+}
+
+PJ_DEF(pj_status_t) pjmedia_clock_create2(pj_pool_t *pool,
+				          unsigned usec_interval,
+				          unsigned clock_rate,
+				          unsigned options,
+				          pjmedia_clock_callback *cb,
+				          void *user_data,
+				          pjmedia_clock **p_clock)
+{
     pjmedia_clock *clock;
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(pool && clock_rate && samples_per_frame && p_clock,
+    PJ_ASSERT_RETURN(pool && usec_interval && clock_rate && p_clock,
 		     PJ_EINVAL);
 
     clock = PJ_POOL_ALLOC_T(pool, pjmedia_clock);
-
     
     status = pj_get_timestamp_freq(&clock->freq);
     if (status != PJ_SUCCESS)
 	return status;
 
-    clock->interval.u64 = samples_per_frame * clock->freq.u64 / 
-			  channel_count / clock_rate;
+    clock->interval.u64 = usec_interval * clock->freq.u64 / USEC_IN_SEC;
     clock->next_tick.u64 = 0;
     clock->timestamp.u64 = 0;
     clock->max_jump = MAX_JUMP_MSEC * clock->freq.u64 / 1000;
-    clock->timestamp_inc = samples_per_frame / channel_count;
+    clock->timestamp_inc = (unsigned)(usec_interval * clock_rate /
+				      USEC_IN_SEC);
     clock->options = options;
     clock->cb = cb;
     clock->user_data = user_data;
@@ -110,6 +124,7 @@
 }
 
 
+
 /*
  * Start the clock. 
  */
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
index d2b874a..a325c47 100644
--- a/pjmedia/src/pjmedia/conf_switch.c
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -23,10 +23,10 @@
 #include <pjmedia/port.h>
 #include <pjmedia/silencedet.h>
 #include <pjmedia/sound_port.h>
-#include <pjmedia/stream.h>
 #include <pj/array.h>
 #include <pj/assert.h>
 #include <pj/log.h>
+#include <pj/math.h>
 #include <pj/pool.h>
 #include <pj/string.h>
 
@@ -80,6 +80,7 @@
 
     /* Shortcut for port info. */
     pjmedia_port_info	*info;
+    unsigned		 samples_per_frame;
 
     /* Calculated signal levels: */
     unsigned		 tx_level;	/**< Last tx level to this port.    */
@@ -123,7 +124,7 @@
 
 /* Prototypes */
 static pj_status_t put_frame(pjmedia_port *this_port, 
-			     const pjmedia_frame *frame);
+			     pjmedia_frame *frame);
 static pj_status_t get_frame(pjmedia_port *this_port, 
 			     pjmedia_frame *frame);
 static pj_status_t destroy_port(pjmedia_port *this_port);
@@ -166,6 +167,7 @@
     /* Save some port's infos, for convenience. */
     conf_port->port = port;
     conf_port->info = &port->info;
+    conf_port->samples_per_frame= PJMEDIA_PINFO_SAMPLES_PER_FRAME(&port->info);
 
     /* Init pjmedia_frame structure in the TX buffer. */
     f = (pjmedia_frame*)conf_port->tx_buf;
@@ -504,6 +506,7 @@
 {
     struct conf_port *src_port, *dst_port;
     pj_bool_t start_sound = PJ_FALSE;
+    pjmedia_audio_format_detail *src_afd, *dst_afd;
     unsigned i;
 
     /* Check arguments */
@@ -523,31 +526,32 @@
 	return PJ_EINVAL;
     }
 
+    src_afd = pjmedia_format_get_audio_format_detail(&src_port->info->fmt, 1);
+    dst_afd = pjmedia_format_get_audio_format_detail(&dst_port->info->fmt, 1);
+
     /* Format must match. */
-    if (src_port->info->format.id != dst_port->info->format.id ||
-	src_port->info->format.bitrate != dst_port->info->format.bitrate) 
+    if (src_port->info->fmt.id != dst_port->info->fmt.id ||
+	src_afd->avg_bps != dst_afd->avg_bps)
     {
 	pj_mutex_unlock(conf->mutex);
 	return PJMEDIA_ENOTCOMPATIBLE;
     }
 
     /* Clock rate must match. */
-    if (src_port->info->clock_rate != dst_port->info->clock_rate) {
+    if (src_afd->clock_rate != dst_afd->clock_rate) {
 	pj_mutex_unlock(conf->mutex);
 	return PJMEDIA_ENCCLOCKRATE;
     }
 
     /* Channel count must match. */
-    if (src_port->info->channel_count != dst_port->info->channel_count) {
+    if (src_afd->channel_count != dst_afd->channel_count) {
 	pj_mutex_unlock(conf->mutex);
 	return PJMEDIA_ENCCLOCKRATE;
     }
 
     /* Source and sink ptime must be equal or a multiplication factor. */
-    if ((src_port->info->samples_per_frame % 
-	 dst_port->info->samples_per_frame != 0) &&
-        (dst_port->info->samples_per_frame % 
-         src_port->info->samples_per_frame != 0))
+    if ((src_afd->frame_time_usec % dst_afd->frame_time_usec != 0) &&
+        (dst_afd->frame_time_usec % src_afd->frame_time_usec != 0))
     {
 	pj_mutex_unlock(conf->mutex);
 	return PJMEDIA_ENCSAMPLESPFRAME;
@@ -829,6 +833,7 @@
 						pjmedia_conf_port_info *info)
 {
     struct conf_port *conf_port;
+    const pjmedia_audio_format_detail *afd;
 
     /* Check arguments */
     PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
@@ -843,6 +848,8 @@
 	return PJ_EINVAL;
     }
 
+    afd = pjmedia_format_get_audio_format_detail(&conf_port->info->fmt, 1);
+
     pj_bzero(info, sizeof(pjmedia_conf_port_info));
 
     info->slot = slot;
@@ -852,11 +859,11 @@
     info->listener_cnt = conf_port->listener_cnt;
     info->listener_slots = conf_port->listener_slots;
     info->transmitter_cnt = conf_port->transmitter_cnt;
-    info->clock_rate = conf_port->info->clock_rate;
-    info->channel_count = conf_port->info->channel_count;
-    info->samples_per_frame = conf_port->info->samples_per_frame;
-    info->bits_per_sample = conf_port->info->bits_per_sample;
-    info->format = conf_port->port->info.format;
+    info->clock_rate = afd->clock_rate;
+    info->channel_count = afd->channel_count;
+    info->samples_per_frame = conf_port->samples_per_frame;
+    info->bits_per_sample = afd->bits_per_sample;
+    info->format = conf_port->port->info.fmt;
     info->tx_adj_level = conf_port->tx_adj_level - NORMAL_LEVEL;
     info->rx_adj_level = conf_port->rx_adj_level - NORMAL_LEVEL;
 
@@ -960,7 +967,7 @@
     }
 
     /* Level adjustment is applicable only for ports that work with raw PCM. */
-    PJ_ASSERT_RETURN(conf_port->info->format.id == PJMEDIA_FORMAT_L16,
+    PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
 		     PJ_EIGNORED);
 
     /* Set normalized adjustment level. */
@@ -1002,7 +1009,7 @@
     }
 
     /* Level adjustment is applicable only for ports that work with raw PCM. */
-    PJ_ASSERT_RETURN(conf_port->info->format.id == PJMEDIA_FORMAT_L16,
+    PJ_ASSERT_RETURN(conf_port->info->fmt.id == PJMEDIA_FORMAT_L16,
 		     PJ_EIGNORED);
 
     /* Set normalized adjustment level. */
@@ -1046,7 +1053,7 @@
 	     * i.e: samples count in TX buffer equal to listener's
 	     * samples per frame.
 	     */
-	    if (f_dst->samples_cnt >= cport_dst->info->samples_per_frame)
+	    if (f_dst->samples_cnt >= cport_dst->samples_per_frame)
 	    {
 		if (cport_dst->slot) {
 		    pjmedia_port_put_frame(cport_dst->port, 
@@ -1058,8 +1065,8 @@
 		}
 
 		/* Update TX timestamp. */
-		pj_add_timestamp32(&cport_dst->ts_tx, 
-				   cport_dst->info->samples_per_frame);
+		pj_add_timestamp32(&cport_dst->ts_tx,
+				   cport_dst->samples_per_frame);
 	    }
 	}
 
@@ -1075,7 +1082,7 @@
 
 	    /* Copy frame to listener's TX buffer. */
 	    nsamples_to_copy = f_end - f_start;
-	    nsamples_req = cport_dst->info->samples_per_frame - 
+	    nsamples_req = cport_dst->samples_per_frame -
 			  (frm_dst->size>>1);
 	    if (nsamples_to_copy > nsamples_req)
 		nsamples_to_copy = nsamples_req;
@@ -1112,7 +1119,7 @@
 	     * i.e: samples count in TX buffer equal to listener's
 	     * samples per frame.
 	     */
-	    if ((frm_dst->size >> 1) == cport_dst->info->samples_per_frame)
+	    if ((frm_dst->size >> 1) == cport_dst->samples_per_frame)
 	    {
 		if (cport_dst->slot) {
 		    pjmedia_port_put_frame(cport_dst->port, frm_dst);
@@ -1123,7 +1130,7 @@
 
 		/* Update TX timestamp. */
 		pj_add_timestamp32(&cport_dst->ts_tx, 
-				   cport_dst->info->samples_per_frame);
+				   cport_dst->samples_per_frame);
 	    }
 	}
 
@@ -1131,18 +1138,18 @@
 
 	/* Check port format. */
 	if (cport_dst->port &&
-	    cport_dst->port->info.format.id == PJMEDIA_FORMAT_L16)
+	    cport_dst->port->info.fmt.id == PJMEDIA_FORMAT_L16)
 	{
 	    /* When there is already some samples in listener's TX buffer, 
 	     * pad the buffer with "zero samples".
 	     */
 	    if (frm_dst->size != 0) {
 		pjmedia_zero_samples((pj_int16_t*)frm_dst->buf,
-				     cport_dst->info->samples_per_frame - 
+				     cport_dst->samples_per_frame -
 				     (frm_dst->size>>1));
 
 		frm_dst->type = PJMEDIA_FRAME_TYPE_AUDIO;
-		frm_dst->size = cport_dst->info->samples_per_frame << 1;
+		frm_dst->size = cport_dst->samples_per_frame << 1;
 		if (cport_dst->slot) {
 		    pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
@@ -1152,7 +1159,7 @@
 
 		/* Update TX timestamp. */
 		pj_add_timestamp32(&cport_dst->ts_tx, 
-				   cport_dst->info->samples_per_frame);
+				   cport_dst->samples_per_frame);
 	    }
 	} else {
 	    pjmedia_frame_ext *f_dst = (pjmedia_frame_ext*)frm_dst;
@@ -1160,7 +1167,7 @@
 	    if (f_dst->samples_cnt != 0) {
 		frm_dst->type = PJMEDIA_FRAME_TYPE_EXTENDED;
 		pjmedia_frame_ext_append_subframe(f_dst, NULL, 0, (pj_uint16_t)
-		    (cport_dst->info->samples_per_frame - f_dst->samples_cnt));
+		    (cport_dst->samples_per_frame - f_dst->samples_cnt));
 		if (cport_dst->slot) {
 		    pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
@@ -1171,7 +1178,7 @@
 
 		/* Update TX timestamp. */
 		pj_add_timestamp32(&cport_dst->ts_tx, 
-				   cport_dst->info->samples_per_frame);
+				   cport_dst->samples_per_frame);
 	    }
 	}
 
@@ -1185,7 +1192,7 @@
 		pjmedia_port_put_frame(cport_dst->port, frm_dst);
 
 	    /* Update TX timestamp. */
-	    pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->info->samples_per_frame);
+	    pj_add_timestamp32(&cport_dst->ts_tx, cport_dst->samples_per_frame);
 	}
     }
 
@@ -1211,6 +1218,7 @@
      */
     for (i=1, ci=1; i<conf->max_ports && ci<conf->port_cnt; ++i) {
 	struct conf_port *cport = conf->ports[i];
+	unsigned master_samples_per_frame;
 
 	/* Skip empty port. */
 	if (!cport)
@@ -1219,9 +1227,11 @@
 	/* Var "ci" is to count how many ports have been visited so far. */
 	++ci;
 
+	master_samples_per_frame = PJMEDIA_PINFO_SAMPLES_PER_FRAME(
+					&conf->master_port->info);
+
 	/* Update clock of the port. */
-	pj_add_timestamp32(&cport->ts_clock, 
-			   conf->master_port->info.samples_per_frame);
+	pj_add_timestamp32(&cport->ts_clock, master_samples_per_frame);
 
 	/* Skip if we're not allowed to receive from this port or 
 	 * the port doesn't have listeners.
@@ -1230,8 +1240,7 @@
 	    cport->listener_cnt == 0)
 	{
 	    cport->rx_level = 0;
-	    pj_add_timestamp32(&cport->ts_rx, 
-			       conf->master_port->info.samples_per_frame);
+	    pj_add_timestamp32(&cport->ts_rx, master_samples_per_frame);
 	    continue;
 	}
 
@@ -1245,10 +1254,10 @@
 	    unsigned j;
 	    pj_int32_t level = 0;
 
-	    pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
+	    pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
 	    
 	    f->buf = &conf->buf[sizeof(pjmedia_frame)];
-	    f->size = cport->info->samples_per_frame<<1;
+	    f->size = cport->samples_per_frame<<1;
 
 	    /* Get frame from port. */
 	    status = pjmedia_port_get_frame(cport->port, f);
@@ -1302,7 +1311,7 @@
 		/* Skip if this listener doesn't want to receive audio */
 		if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
 		    pj_add_timestamp32(&listener->ts_tx, 
-				       listener->info->samples_per_frame);
+				       listener->samples_per_frame);
 		    listener->tx_level = 0;
 		    continue;
 		}
@@ -1361,7 +1370,7 @@
 		    tmp_f.size = 0;
 
 		    pjmedia_port_put_frame(cport->port, &tmp_f);
-		    pj_add_timestamp32(&cport->ts_tx, cport->info->samples_per_frame);
+		    pj_add_timestamp32(&cport->ts_tx, cport->samples_per_frame);
 		}
 	    }
 	}
@@ -1380,7 +1389,7 @@
 	    pjmedia_frame_ext_subframe *sf;
 	    unsigned samples_per_subframe;
 	    
-	    if (f_src_->samples_cnt < this_cport->info->samples_per_frame) {
+	    if (f_src_->samples_cnt < this_cport->samples_per_frame) {
 		f_dst->base.type = PJMEDIA_FRAME_TYPE_NONE;
 		f_dst->samples_cnt = 0;
 		f_dst->subframe_cnt = 0;
@@ -1393,7 +1402,7 @@
 	    samples_per_subframe = f_src_->samples_cnt / f_src_->subframe_cnt;
 
 
-	    while (f_dst->samples_cnt < this_cport->info->samples_per_frame) {
+	    while (f_dst->samples_cnt < this_cport->samples_per_frame) {
 		sf = pjmedia_frame_ext_get_subframe(f_src_, i++);
 		pj_assert(sf);
 		pjmedia_frame_ext_append_subframe(f_dst, sf->data, sf->bitlen,
@@ -1404,7 +1413,7 @@
 	    pjmedia_frame_ext_pop_subframes(f_src_, i);
 
 	} else if (f_src->type == PJMEDIA_FRAME_TYPE_AUDIO) {
-	    if ((f_src->size>>1) < this_cport->info->samples_per_frame) {
+	    if ((f_src->size>>1) < this_cport->samples_per_frame) {
 		frame->type = PJMEDIA_FRAME_TYPE_NONE;
 		frame->size = 0;
 		break;
@@ -1412,15 +1421,15 @@
 
 	    pjmedia_copy_samples((pj_int16_t*)frame->buf, 
 				 (pj_int16_t*)f_src->buf, 
-				 this_cport->info->samples_per_frame);
-	    frame->size = this_cport->info->samples_per_frame << 1;
+				 this_cport->samples_per_frame);
+	    frame->size = this_cport->samples_per_frame << 1;
 
 	    /* Shift left TX buffer. */
 	    f_src->size -= frame->size;
 	    if (f_src->size)
 		pjmedia_move_samples((pj_int16_t*)f_src->buf,
 				     (pj_int16_t*)f_src->buf + 
-				     this_cport->info->samples_per_frame,
+				     this_cport->samples_per_frame,
 				     f_src->size >> 1);
 	} else { /* PJMEDIA_FRAME_TYPE_NONE */
 	    pjmedia_frame_ext *f_src_ = (pjmedia_frame_ext*)f_src;
@@ -1442,7 +1451,7 @@
  * Recorder callback.
  */
 static pj_status_t put_frame(pjmedia_port *this_port, 
-			     const pjmedia_frame *f)
+			     pjmedia_frame *f)
 {
     pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
     struct conf_port *cport;
@@ -1460,7 +1469,7 @@
 	return PJ_SUCCESS;
     }
 
-    pj_add_timestamp32(&cport->ts_rx, cport->info->samples_per_frame);
+    pj_add_timestamp32(&cport->ts_rx, cport->samples_per_frame);
     
     /* Skip if this port is muted/disabled. */
     if (cport->rx_setting == PJMEDIA_PORT_DISABLE) {
@@ -1526,7 +1535,7 @@
 	/* Skip if this listener doesn't want to receive audio */
 	if (listener->tx_setting == PJMEDIA_PORT_DISABLE) {
 	    pj_add_timestamp32(&listener->ts_tx, 
-			       listener->info->samples_per_frame);
+			       listener->samples_per_frame);
 	    listener->tx_level = 0;
 	    continue;
 	}
@@ -1534,7 +1543,7 @@
 	/* Skip loopback for now. */
 	if (listener == cport) {
 	    pj_add_timestamp32(&listener->ts_tx, 
-			       listener->info->samples_per_frame);
+			       listener->samples_per_frame);
 	    listener->tx_level = 0;
 	    continue;
 	}
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index fd6477c..4fb31c2 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -26,7 +26,6 @@
 #include <pjmedia/silencedet.h>
 #include <pjmedia/sound_port.h>
 #include <pjmedia/stereo.h>
-#include <pjmedia/stream.h>
 #include <pj/array.h>
 #include <pj/assert.h>
 #include <pj/log.h>
@@ -241,7 +240,7 @@
 
 /* Prototypes */
 static pj_status_t put_frame(pjmedia_port *this_port, 
-			     const pjmedia_frame *frame);
+			     pjmedia_frame *frame);
 static pj_status_t get_frame(pjmedia_port *this_port, 
 			     pjmedia_frame *frame);
 static pj_status_t get_frame_pasv(pjmedia_port *this_port, 
@@ -285,10 +284,13 @@
 
     /* Save some port's infos, for convenience. */
     if (port) {
+	pjmedia_audio_format_detail *afd;
+
+	afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1);
 	conf_port->port = port;
-	conf_port->clock_rate = port->info.clock_rate;
-	conf_port->samples_per_frame = port->info.samples_per_frame;
-	conf_port->channel_count = port->info.channel_count;
+	conf_port->clock_rate = afd->clock_rate;
+	conf_port->samples_per_frame = PJMEDIA_AFD_SPF(afd);
+	conf_port->channel_count = afd->channel_count;
     } else {
 	conf_port->port = NULL;
 	conf_port->clock_rate = conf->clock_rate;
@@ -750,8 +752,9 @@
      * - same between port & conference bridge.
      * - monochannel on port or conference bridge.
      */
-    if (strm_port->info.channel_count != conf->channel_count && 
-	(strm_port->info.channel_count != 1 && conf->channel_count != 1)) 
+    if (PJMEDIA_PIA_CCNT(&strm_port->info) != conf->channel_count &&
+	(PJMEDIA_PIA_CCNT(&strm_port->info) != 1 &&
+	 conf->channel_count != 1))
     {
 	pj_assert(!"Number of channels mismatch");
 	return PJMEDIA_ENCCHANNEL;
@@ -2050,7 +2053,7 @@
  * Recorder (or passive port) callback.
  */
 static pj_status_t put_frame(pjmedia_port *this_port, 
-			     const pjmedia_frame *frame)
+			     pjmedia_frame *frame)
 {
     pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
     struct conf_port *port = conf->ports[this_port->port_data.ldata];
diff --git a/pjmedia/src/pjmedia/converter.c b/pjmedia/src/pjmedia/converter.c
new file mode 100644
index 0000000..9e7280f
--- /dev/null
+++ b/pjmedia/src/pjmedia/converter.c
@@ -0,0 +1,162 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-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/converter.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+
+struct pjmedia_converter_mgr
+{
+    pjmedia_converter_factory  factory_list;
+};
+
+static pjmedia_converter_mgr *converter_manager_instance;
+
+PJ_DEF(pj_status_t) pjmedia_converter_mgr_create(pj_pool_t *pool,
+					         pjmedia_converter_mgr **p_mgr)
+{
+    pjmedia_converter_mgr *mgr;
+
+    mgr = PJ_POOL_ALLOC_T(pool, pjmedia_converter_mgr);
+    pj_list_init(&mgr->factory_list);
+
+    if (!converter_manager_instance)
+	converter_manager_instance = mgr;
+
+    if (p_mgr)
+	*p_mgr = mgr;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pjmedia_converter_mgr*) pjmedia_converter_mgr_instance(void)
+{
+    pj_assert(converter_manager_instance != NULL);
+    return converter_manager_instance;
+}
+
+PJ_DEF(void) pjmedia_converter_mgr_set_instance(pjmedia_converter_mgr *mgr)
+{
+    converter_manager_instance = mgr;
+}
+
+PJ_DEF(void) pjmedia_converter_mgr_destroy(pjmedia_converter_mgr *mgr)
+{
+    pjmedia_converter_factory *f;
+
+    if (!mgr) mgr = pjmedia_converter_mgr_instance();
+
+    PJ_ASSERT_ON_FAIL(mgr != NULL, return);
+
+    f = mgr->factory_list.next;
+    while (f != &mgr->factory_list) {
+	pjmedia_converter_factory *next = f->next;
+	pj_list_erase(f);
+	(*f->op->destroy_factory)(f);
+	f = next;
+    }
+
+    if (converter_manager_instance == mgr)
+	converter_manager_instance = NULL;
+}
+
+PJ_DEF(pj_status_t)
+pjmedia_converter_mgr_register_factory(pjmedia_converter_mgr *mgr,
+				       pjmedia_converter_factory *factory)
+{
+    pjmedia_converter_factory *pf;
+
+    if (!mgr) mgr = pjmedia_converter_mgr_instance();
+
+    PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL);
+
+    PJ_ASSERT_RETURN(!pj_list_find_node(&mgr->factory_list, factory),
+		     PJ_EEXISTS);
+
+    pf = mgr->factory_list.next;
+    while (pf != &mgr->factory_list) {
+	if (pf->priority > factory->priority)
+	    break;
+	pf = pf->next;
+    }
+    pj_list_insert_before(pf, factory);
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t)
+pjmedia_converter_mgr_unregister_factory(pjmedia_converter_mgr *mgr,
+				         pjmedia_converter_factory *f,
+				         pj_bool_t destroy)
+{
+    if (!mgr) mgr = pjmedia_converter_mgr_instance();
+
+    PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL);
+
+    PJ_ASSERT_RETURN(pj_list_find_node(&mgr->factory_list, f), PJ_ENOTFOUND);
+    pj_list_erase(f);
+    if (destroy)
+	(*f->op->destroy_factory)(f);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_converter_create(pjmedia_converter_mgr *mgr,
+					      pj_pool_t *pool,
+					      pjmedia_conversion_param *param,
+					      pjmedia_converter **p_cv)
+{
+    pjmedia_converter_factory *f;
+    pjmedia_converter *cv = NULL;
+    pj_status_t status = PJ_ENOTFOUND;
+
+    if (!mgr) mgr = pjmedia_converter_mgr_instance();
+
+    PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVAL);
+
+    *p_cv = NULL;
+
+    f = mgr->factory_list.next;
+    while (f != &mgr->factory_list) {
+	status = (*f->op->create_converter)(f, pool, param, &cv);
+	if (status == PJ_SUCCESS)
+	    break;
+	f = f->next;
+    }
+
+    if (status != PJ_SUCCESS)
+	return status;
+
+    *p_cv = cv;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_converter_convert(pjmedia_converter *cv,
+					       pjmedia_frame *src_frame,
+					       pjmedia_frame *dst_frame)
+{
+    return (*cv->op->convert)(cv, src_frame, dst_frame);
+}
+
+PJ_DEF(void) pjmedia_converter_destroy(pjmedia_converter *cv)
+{
+    (*cv->op->destroy)(cv);
+}
+
+
diff --git a/pjmedia/src/pjmedia/converter_libswscale.c b/pjmedia/src/pjmedia/converter_libswscale.c
new file mode 100644
index 0000000..dfe0c45
--- /dev/null
+++ b/pjmedia/src/pjmedia/converter_libswscale.c
@@ -0,0 +1,202 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-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/converter.h>
+#include <pj/errno.h>
+
+#if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL
+
+#include "ffmpeg_util.h"
+#include <libswscale/swscale.h>
+
+static pj_status_t factory_create_converter(pjmedia_converter_factory *cf,
+					    pj_pool_t *pool,
+					    const pjmedia_conversion_param*prm,
+					    pjmedia_converter **p_cv);
+static void factory_destroy_factory(pjmedia_converter_factory *cf);
+static pj_status_t libswscale_conv_convert(pjmedia_converter *converter,
+					   pjmedia_frame *src_frame,
+					   pjmedia_frame *dst_frame);
+static void libswscale_conv_destroy(pjmedia_converter *converter);
+
+
+struct fmt_info
+{
+    const pjmedia_video_format_info 	*fmt_info;
+    pjmedia_video_apply_fmt_param 	 apply_param;
+};
+
+struct ffmpeg_converter
+{
+    pjmedia_converter 			 base;
+    struct SwsContext 			*sws_ctx;
+    struct fmt_info			 src,
+					 dst;
+};
+
+static pjmedia_converter_factory_op libswscale_factory_op =
+{
+    &factory_create_converter,
+    &factory_destroy_factory
+};
+
+static pjmedia_converter_op liswscale_converter_op =
+{
+    &libswscale_conv_convert,
+    &libswscale_conv_destroy
+};
+
+static pj_status_t factory_create_converter(pjmedia_converter_factory *cf,
+					    pj_pool_t *pool,
+					    const pjmedia_conversion_param *prm,
+					    pjmedia_converter **p_cv)
+{
+    enum PixelFormat srcFormat, dstFormat;
+    const pjmedia_video_format_detail *src_detail, *dst_detail;
+    const pjmedia_video_format_info *src_fmt_info, *dst_fmt_info;
+    struct SwsContext *sws_ctx;
+    struct ffmpeg_converter *fcv;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(cf);
+
+    /* Only supports video */
+    if (prm->src.type != PJMEDIA_TYPE_VIDEO ||
+	prm->dst.type != prm->src.type ||
+	prm->src.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO ||
+	prm->dst.detail_type != prm->src.detail_type)
+    {
+	return PJ_ENOTSUP;
+    }
+
+    /* lookup source format info */
+    src_fmt_info = pjmedia_get_video_format_info(
+		      pjmedia_video_format_mgr_instance(),
+		      prm->src.id);
+    if (!src_fmt_info)
+	return PJ_ENOTSUP;
+
+    /* lookup destination format info */
+    dst_fmt_info = pjmedia_get_video_format_info(
+		      pjmedia_video_format_mgr_instance(),
+		      prm->dst.id);
+    if (!dst_fmt_info)
+	return PJ_ENOTSUP;
+
+    src_detail = pjmedia_format_get_video_format_detail(&prm->src, PJ_TRUE);
+    dst_detail = pjmedia_format_get_video_format_detail(&prm->dst, PJ_TRUE);
+
+    status = pjmedia_format_id_to_PixelFormat(prm->src.id, &srcFormat);
+    if (status != PJ_SUCCESS)
+	return PJ_ENOTSUP;
+
+    status = pjmedia_format_id_to_PixelFormat(prm->dst.id, &dstFormat);
+    if (status != PJ_SUCCESS)
+	return PJ_ENOTSUP;
+
+    sws_ctx = sws_getContext(src_detail->size.w, src_detail->size.h, srcFormat,
+		             dst_detail->size.w, dst_detail->size.h, dstFormat,
+			     SWS_BICUBIC,
+			     NULL, NULL, NULL);
+    if (sws_ctx == NULL)
+	return PJ_ENOTSUP;
+
+    fcv = PJ_POOL_ZALLOC_T(pool, struct ffmpeg_converter);
+    fcv->base.op = &liswscale_converter_op;
+    fcv->sws_ctx = sws_ctx;
+    fcv->src.apply_param.size = src_detail->size;
+    fcv->src.fmt_info = src_fmt_info;
+    fcv->dst.apply_param.size = dst_detail->size;
+    fcv->dst.fmt_info = dst_fmt_info;
+
+    *p_cv = &fcv->base;
+
+    return PJ_SUCCESS;
+}
+
+static void factory_destroy_factory(pjmedia_converter_factory *cf)
+{
+    PJ_UNUSED_ARG(cf);
+}
+
+static pj_status_t libswscale_conv_convert(pjmedia_converter *converter,
+					   pjmedia_frame *src_frame,
+					   pjmedia_frame *dst_frame)
+{
+    struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter;
+    struct fmt_info *src = &fcv->src,
+	            *dst = &fcv->dst;
+    int h;
+
+    src->apply_param.buffer = src_frame->buf;
+    (*src->fmt_info->apply_fmt)(src->fmt_info, &src->apply_param);
+
+    dst->apply_param.buffer = dst_frame->buf;
+    (*dst->fmt_info->apply_fmt)(dst->fmt_info, &dst->apply_param);
+
+    h = sws_scale(fcv->sws_ctx,
+	          src->apply_param.planes, src->apply_param.strides,
+		  0, src->apply_param.size.h,
+		  dst->apply_param.planes, dst->apply_param.strides);
+
+    return h==(int)dst->apply_param.size.h ? PJ_SUCCESS : PJ_EUNKNOWN;
+}
+
+static void libswscale_conv_destroy(pjmedia_converter *converter)
+{
+    struct ffmpeg_converter *fcv = (struct ffmpeg_converter*)converter;
+    if (fcv->sws_ctx) {
+	struct SwsContext *tmp = fcv->sws_ctx;
+	fcv->sws_ctx = NULL;
+	sws_freeContext(tmp);
+    }
+}
+
+static pjmedia_converter_factory libswscale_factory =
+{
+    NULL, NULL,					/* list */
+    "libswscale",				/* name */
+    PJMEDIA_CONVERTER_PRIORITY_NORMAL+1,	/* priority */
+    NULL					/* op will be init-ed later  */
+};
+
+PJ_DEF(pj_status_t)
+pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr,
+				  pj_pool_t *pool)
+{
+    libswscale_factory.op = &libswscale_factory_op;
+    PJ_UNUSED_ARG(pool);
+    return pjmedia_converter_mgr_register_factory(mgr, &libswscale_factory);
+}
+
+
+PJ_DEF(pj_status_t)
+pjmedia_libswscale_converter_shutdown(pjmedia_converter_mgr *mgr,
+				      pj_pool_t *pool)
+{
+    PJ_UNUSED_ARG(pool);
+    return pjmedia_converter_mgr_unregister_factory(mgr, &libswscale_factory,
+						    PJ_TRUE);
+}
+
+#ifdef _MSC_VER
+#   pragma comment( lib, "avutil.lib")
+#   pragma comment( lib, "swscale.lib")
+#endif
+
+#endif /* #if PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL */
diff --git a/pjmedia/src/pjmedia/delaybuf.c b/pjmedia/src/pjmedia/delaybuf.c
index c0a316f..c6be7ed 100644
--- a/pjmedia/src/pjmedia/delaybuf.c
+++ b/pjmedia/src/pjmedia/delaybuf.c
@@ -21,6 +21,7 @@
 #include <pjmedia/delaybuf.h>
 #include <pjmedia/circbuf.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/frame.h>
 #include <pjmedia/wsola.h>
 #include <pj/assert.h>
 #include <pj/lock.h>
diff --git a/pjmedia/src/pjmedia/dummy.c b/pjmedia/src/pjmedia/dummy.c
new file mode 100644
index 0000000..805de1c
--- /dev/null
+++ b/pjmedia/src/pjmedia/dummy.c
@@ -0,0 +1,24 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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/frame.h>
+#include <pjmedia/port.h>
+#include <pjmedia/types.h>
+
diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c
index 82eec6e..345603f 100644
--- a/pjmedia/src/pjmedia/echo_common.c
+++ b/pjmedia/src/pjmedia/echo_common.c
@@ -20,6 +20,7 @@
 
 #include <pjmedia/echo.h>
 #include <pjmedia/delaybuf.h>
+#include <pjmedia/frame.h>
 #include <pjmedia/errno.h>
 #include <pj/assert.h>
 #include <pj/list.h>
diff --git a/pjmedia/src/pjmedia/echo_port.c b/pjmedia/src/pjmedia/echo_port.c
index 16b970d..de23d73 100644
--- a/pjmedia/src/pjmedia/echo_port.c
+++ b/pjmedia/src/pjmedia/echo_port.c
@@ -38,7 +38,7 @@
 
 
 static pj_status_t ec_put_frame(pjmedia_port *this_port, 
-				const pjmedia_frame *frame);
+				pjmedia_frame *frame);
 static pj_status_t ec_get_frame(pjmedia_port *this_port, 
 				pjmedia_frame *frame);
 static pj_status_t ec_on_destroy(pjmedia_port *this_port);
@@ -52,25 +52,29 @@
 					     pjmedia_port **p_port )
 {
     const pj_str_t AEC = { "EC", 2 };
+    pjmedia_audio_format_detail *afd;
     struct ec *ec;
     pj_status_t status;
 
     PJ_ASSERT_RETURN(pool && dn_port && p_port, PJ_EINVAL);
-    PJ_ASSERT_RETURN(dn_port->info.bits_per_sample==16 && tail_ms, 
+
+    afd = pjmedia_format_get_audio_format_detail(&dn_port->info.fmt, PJ_TRUE);
+
+    PJ_ASSERT_RETURN(afd->bits_per_sample==16 && tail_ms,
 		     PJ_EINVAL);
 
     /* Create the port and the AEC itself */
     ec = PJ_POOL_ZALLOC_T(pool, struct ec);
     
     pjmedia_port_info_init(&ec->base.info, &AEC, SIGNATURE,
-			   dn_port->info.clock_rate, 
-			   dn_port->info.channel_count, 
-			   dn_port->info.bits_per_sample,
-			   dn_port->info.samples_per_frame);
+			   afd->clock_rate,
+			   afd->channel_count,
+			   afd->bits_per_sample,
+			   PJMEDIA_AFD_SPF(afd));
 
-    status = pjmedia_echo_create2(pool, dn_port->info.clock_rate, 
-				  dn_port->info.channel_count,
-				  dn_port->info.samples_per_frame,
+    status = pjmedia_echo_create2(pool, afd->clock_rate,
+				  afd->channel_count,
+				  PJMEDIA_AFD_SPF(afd),
 				  tail_ms, latency_ms, options, &ec->ec);
     if (status != PJ_SUCCESS)
 	return status;
@@ -89,7 +93,7 @@
 
 
 static pj_status_t ec_put_frame( pjmedia_port *this_port, 
-				 const pjmedia_frame *frame)
+				 pjmedia_frame *frame)
 {
     struct ec *ec = (struct ec*)this_port;
 
@@ -99,7 +103,7 @@
 	return pjmedia_port_put_frame(ec->dn_port, frame);
     }
 
-    PJ_ASSERT_RETURN(frame->size == this_port->info.samples_per_frame * 2,
+    PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
 		     PJ_EINVAL);
 
     pjmedia_echo_capture(ec->ec, (pj_int16_t*)frame->buf, 0);
@@ -119,7 +123,7 @@
     status = pjmedia_port_get_frame(ec->dn_port, frame);
     if (status!=PJ_SUCCESS || frame->type!=PJMEDIA_FRAME_TYPE_AUDIO) {
 	pjmedia_zero_samples((pj_int16_t*)frame->buf, 
-			     this_port->info.samples_per_frame);
+			      PJMEDIA_PIA_SPF(&this_port->info));
     }
 
     pjmedia_echo_playback(ec->ec, (pj_int16_t*)frame->buf);
diff --git a/pjmedia/src/pjmedia/echo_speex.c b/pjmedia/src/pjmedia/echo_speex.c
index 5d5203a..40357aa 100644
--- a/pjmedia/src/pjmedia/echo_speex.c
+++ b/pjmedia/src/pjmedia/echo_speex.c
@@ -20,6 +20,7 @@
 
 #include <pjmedia/echo.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/frame.h>
 #include <pj/assert.h>
 #include <pj/log.h>
 #include <pj/pool.h>
diff --git a/pjmedia/src/pjmedia/echo_suppress.c b/pjmedia/src/pjmedia/echo_suppress.c
index 3102f3b..296e98d 100644
--- a/pjmedia/src/pjmedia/echo_suppress.c
+++ b/pjmedia/src/pjmedia/echo_suppress.c
@@ -20,6 +20,7 @@
 #include <pjmedia/types.h>
 #include <pjmedia/alaw_ulaw.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/frame.h>
 #include <pjmedia/silencedet.h>
 #include <pj/array.h>
 #include <pj/assert.h>
diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c
index d86eb58..7e24026 100644
--- a/pjmedia/src/pjmedia/errno.c
+++ b/pjmedia/src/pjmedia/errno.c
@@ -114,6 +114,7 @@
     PJ_BUILD_ERR( PJMEDIA_EREMOTENODTMF,    "Remote does not support DTMF" ),
     PJ_BUILD_ERR( PJMEDIA_RTP_EINDTMF,	    "Invalid DTMF digit" ),
     PJ_BUILD_ERR( PJMEDIA_RTP_EREMNORFC2833,"Remote does not support RFC 2833" ),
+    PJ_BUILD_ERR( PJMEDIA_EBADFMT,	    "Bad format"),
 
     /* RTP session errors. */
     PJ_BUILD_ERR( PJMEDIA_RTP_EINPKT,	    "Invalid RTP packet" ),
@@ -142,6 +143,7 @@
     PJ_BUILD_ERR( PJMEDIA_EWAVEUNSUPP,	    "Unsupported WAVE file format" ),
     PJ_BUILD_ERR( PJMEDIA_EWAVETOOSHORT,    "WAVE file too short" ),
     PJ_BUILD_ERR( PJMEDIA_EFRMFILETOOBIG,   "Sound frame too large for file buffer"),
+    PJ_BUILD_ERR( PJMEDIA_EAVIUNSUPP,	    "Unsupported AVI file"),
 
     /* Sound device errors: */
     PJ_BUILD_ERR( PJMEDIA_ENOSNDREC,	    "No suitable sound capture device" ),
@@ -266,3 +268,20 @@
     return errstr;
 }
 
+/*
+ * pjmedia_videodev_strerror()
+ */
+PJ_DEF(pj_str_t) pjmedia_videodev_strerror(pj_status_t statcode, 
+					   char *buf, pj_size_t bufsize )
+{
+    pj_str_t errstr;
+
+    /* Error not found. */
+    errstr.ptr = buf;
+    errstr.slen = pj_ansi_snprintf(buf, bufsize, 
+				   "Unknown pjmedia-videodev error %d",
+				   statcode);
+
+    return errstr;
+}
+
diff --git a/pjmedia/src/pjmedia/ffmpeg_util.c b/pjmedia/src/pjmedia/ffmpeg_util.c
new file mode 100644
index 0000000..a2def0e
--- /dev/null
+++ b/pjmedia/src/pjmedia/ffmpeg_util.c
@@ -0,0 +1,148 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-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/types.h>
+#include <pj/errno.h>
+
+#if PJMEDIA_HAS_LIBAVFORMAT && PJMEDIA_HAS_LIBAVUTIL
+
+#include "ffmpeg_util.h"
+#include <libavformat/avformat.h>
+
+/* Conversion table between pjmedia_format_id and PixelFormat */
+static const struct ffmpeg_fmt_table_t
+{
+    pjmedia_format_id	id;
+    enum PixelFormat	pf;
+} ffmpeg_fmt_table[] =
+{
+    { PJMEDIA_FORMAT_RGBA, PIX_FMT_RGBA},
+    { PJMEDIA_FORMAT_RGB24,PIX_FMT_RGB24},
+    { PJMEDIA_FORMAT_BGRA, PIX_FMT_BGRA},
+
+    { PJMEDIA_FORMAT_AYUV, PIX_FMT_NONE},
+    { PJMEDIA_FORMAT_YUY2, PIX_FMT_YUYV422},
+    { PJMEDIA_FORMAT_UYVY, PIX_FMT_UYVY422},
+    { PJMEDIA_FORMAT_I420, PIX_FMT_YUV420P},
+    { PJMEDIA_FORMAT_YV12, PIX_FMT_YUV422P},
+    { PJMEDIA_FORMAT_I420JPEG, PIX_FMT_YUVJ420P},
+    { PJMEDIA_FORMAT_I422JPEG, PIX_FMT_YUVJ422P},
+};
+
+/* Conversion table between pjmedia_format_id and CodecID */
+static const struct ffmpeg_codec_table_t
+{
+    pjmedia_format_id	id;
+    enum CodecID	codec_id;
+} ffmpeg_codec_table[] =
+{
+    {PJMEDIA_FORMAT_H261,  CODEC_ID_H261},
+    {PJMEDIA_FORMAT_H263,  CODEC_ID_H263},
+    {PJMEDIA_FORMAT_MPEG1VIDEO,  CODEC_ID_MPEG1VIDEO},
+    {PJMEDIA_FORMAT_MPEG2VIDEO,  CODEC_ID_MPEG2VIDEO},
+    {PJMEDIA_FORMAT_MPEG4,  CODEC_ID_MPEG4},    
+    {PJMEDIA_FORMAT_MJPEG,  CODEC_ID_MJPEG},
+};
+
+static int pjmedia_ffmpeg_ref_cnt;
+
+void pjmedia_ffmpeg_add_ref()
+{
+    if (pjmedia_ffmpeg_ref_cnt++ == 0) {
+	av_register_all();
+    }
+}
+
+void pjmedia_ffmpeg_dec_ref()
+{
+    if (pjmedia_ffmpeg_ref_cnt-- == 1) {
+	/* How to shutdown ffmpeg? */
+    }
+
+    if (pjmedia_ffmpeg_ref_cnt < 0) pjmedia_ffmpeg_ref_cnt = 0;
+}
+
+pj_status_t pjmedia_format_id_to_PixelFormat(pjmedia_format_id fmt_id,
+					     enum PixelFormat *pixel_format)
+{
+    unsigned i;
+    for (i=0; i<PJ_ARRAY_SIZE(ffmpeg_fmt_table); ++i) {
+	const struct ffmpeg_fmt_table_t *t = &ffmpeg_fmt_table[i];
+	if (t->id==fmt_id && t->pf != PIX_FMT_NONE) {
+	    *pixel_format = t->pf;
+	    return PJ_SUCCESS;
+	}
+    }
+
+    *pixel_format = PIX_FMT_NONE;
+    return PJ_ENOTFOUND;
+}
+
+pj_status_t PixelFormat_to_pjmedia_format_id(enum PixelFormat pf,
+					     pjmedia_format_id *fmt_id)
+{
+    unsigned i;
+    for (i=0; i<PJ_ARRAY_SIZE(ffmpeg_fmt_table); ++i) {
+	const struct ffmpeg_fmt_table_t *t = &ffmpeg_fmt_table[i];
+	if (t->pf == pf) {
+	    if (fmt_id) *fmt_id = t->id;
+	    return PJ_SUCCESS;
+	}
+    }
+
+    return PJ_ENOTFOUND;
+}
+
+pj_status_t pjmedia_format_id_to_CodecID(pjmedia_format_id fmt_id,
+					 enum CodecID *codec_id)
+{
+    unsigned i;
+    for (i=0; i<PJ_ARRAY_SIZE(ffmpeg_codec_table); ++i) {
+	const struct ffmpeg_codec_table_t *t = &ffmpeg_codec_table[i];
+	if (t->id==fmt_id && t->codec_id != PIX_FMT_NONE) {
+	    *codec_id = t->codec_id;
+	    return PJ_SUCCESS;
+	}
+    }
+
+    *codec_id = PIX_FMT_NONE;
+    return PJ_ENOTFOUND;
+}
+
+pj_status_t CodecID_to_pjmedia_format_id(enum CodecID codec_id,
+					 pjmedia_format_id *fmt_id)
+{
+    unsigned i;
+    for (i=0; i<PJ_ARRAY_SIZE(ffmpeg_codec_table); ++i) {
+	const struct ffmpeg_codec_table_t *t = &ffmpeg_codec_table[i];
+	if (t->codec_id == codec_id) {
+	    if (fmt_id) *fmt_id = t->id;
+	    return PJ_SUCCESS;
+	}
+    }
+
+    return PJ_ENOTFOUND;
+}
+
+
+#ifdef _MSC_VER
+#   pragma comment( lib, "avformat.lib")
+#   pragma comment( lib, "avutil.lib")
+#endif
+
+#endif	/* #if PJMEDIA_HAS_LIBAVFORMAT && PJMEDIA_HAS_LIBAVUTIL */
diff --git a/pjmedia/src/pjmedia/ffmpeg_util.h b/pjmedia/src/pjmedia/ffmpeg_util.h
new file mode 100644
index 0000000..70e9aaa
--- /dev/null
+++ b/pjmedia/src/pjmedia/ffmpeg_util.h
@@ -0,0 +1,55 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2010-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
+ */
+
+/*
+ * This file contains common utilities that are useful for pjmedia components
+ * that use ffmpeg. This is not a public API.
+ */
+
+#ifndef __PJMEDIA_FFMPEG_UTIL_H__
+#define __PJMEDIA_FFMPEG_UTIL_H__
+
+#include <pjmedia/format.h>
+
+#ifdef _MSC_VER
+#   ifndef __cplusplus
+#	define inline _inline
+#   endif
+#   pragma warning(disable:4244) /* possible loss of data */
+#endif
+
+#include <libavutil/avutil.h>
+#include <libavcodec/avcodec.h>
+
+void pjmedia_ffmpeg_add_ref();
+void pjmedia_ffmpeg_dec_ref();
+
+pj_status_t pjmedia_format_id_to_PixelFormat(pjmedia_format_id fmt_id,
+					     enum PixelFormat *pixel_format);
+
+pj_status_t PixelFormat_to_pjmedia_format_id(enum PixelFormat pf,
+					     pjmedia_format_id *fmt_id);
+
+pj_status_t pjmedia_format_id_to_CodecID(pjmedia_format_id fmt_id,
+					 enum CodecID *codec_id);
+
+pj_status_t CodecID_to_pjmedia_format_id(enum CodecID codec_id,
+					 pjmedia_format_id *fmt_id);
+
+#endif /* __PJMEDIA_FFMPEG_UTIL_H__ */
diff --git a/pjmedia/src/pjmedia/format.c b/pjmedia/src/pjmedia/format.c
new file mode 100644
index 0000000..a5612da
--- /dev/null
+++ b/pjmedia/src/pjmedia/format.c
@@ -0,0 +1,366 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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/format.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+static pj_status_t apply_packed_fmt(const pjmedia_video_format_info *fi,
+	                            pjmedia_video_apply_fmt_param *aparam);
+
+static pj_status_t apply_planar_420(const pjmedia_video_format_info *fi,
+	                            pjmedia_video_apply_fmt_param *aparam);
+
+static pj_status_t apply_planar_422(const pjmedia_video_format_info *fi,
+	                            pjmedia_video_apply_fmt_param *aparam);
+
+struct pjmedia_video_format_mgr
+{
+    unsigned			max_info;
+    unsigned			info_cnt;
+    pjmedia_video_format_info **infos;
+};
+
+static pjmedia_video_format_mgr *video_format_mgr_instance;
+static pjmedia_video_format_info built_in_vid_fmt_info[] =
+{
+    {PJMEDIA_FORMAT_RGB24, "RGB24", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt},
+    {PJMEDIA_FORMAT_RGBA,  "RGBA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt},
+    {PJMEDIA_FORMAT_BGRA,  "BGRA", PJMEDIA_COLOR_MODEL_RGB, 32, 1, &apply_packed_fmt},
+    {PJMEDIA_FORMAT_DIB ,  "DIB ", PJMEDIA_COLOR_MODEL_RGB, 24, 1, &apply_packed_fmt},
+    {PJMEDIA_FORMAT_AYUV,  "AYUV", PJMEDIA_COLOR_MODEL_YUV, 32, 1, &apply_packed_fmt},
+    {PJMEDIA_FORMAT_YUY2,  "YUY2", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
+    {PJMEDIA_FORMAT_UYVY,  "UYVY", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
+    {PJMEDIA_FORMAT_YVYU,  "YVYU", PJMEDIA_COLOR_MODEL_YUV, 16, 1, &apply_packed_fmt},
+    {PJMEDIA_FORMAT_I420,  "I420", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420},
+    {PJMEDIA_FORMAT_YV12,  "YV12", PJMEDIA_COLOR_MODEL_YUV, 16, 3, &apply_planar_422},
+    {PJMEDIA_FORMAT_I420JPEG, "I420JPG", PJMEDIA_COLOR_MODEL_YUV, 12, 3, &apply_planar_420},
+    {PJMEDIA_FORMAT_I422JPEG, "I422JPG", PJMEDIA_COLOR_MODEL_YUV, 16, 3, &apply_planar_422},
+};
+
+
+PJ_DEF(void) pjmedia_format_init_audio( pjmedia_format *fmt,
+				        pj_uint32_t fmt_id,
+					unsigned clock_rate,
+					unsigned channel_count,
+					unsigned bits_per_sample,
+					unsigned frame_time_usec,
+					pj_uint32_t avg_bps,
+					pj_uint32_t max_bps)
+{
+    fmt->id = fmt_id;
+    fmt->type = PJMEDIA_TYPE_AUDIO;
+    fmt->detail_type = PJMEDIA_FORMAT_DETAIL_AUDIO;
+
+    fmt->det.aud.clock_rate = clock_rate;
+    fmt->det.aud.channel_count = channel_count;
+    fmt->det.aud.bits_per_sample = bits_per_sample;
+    fmt->det.aud.frame_time_usec = frame_time_usec;
+    fmt->det.aud.avg_bps = avg_bps;
+    fmt->det.aud.max_bps = max_bps;
+}
+
+PJ_DEF(void) pjmedia_format_init_video( pjmedia_format *fmt,
+					pj_uint32_t fmt_id,
+					unsigned width,
+					unsigned height,
+					unsigned fps_num,
+					unsigned fps_denum)
+{
+    fmt->id = fmt_id;
+    fmt->type = PJMEDIA_TYPE_VIDEO;
+    fmt->detail_type = PJMEDIA_FORMAT_DETAIL_VIDEO;
+
+    fmt->det.vid.size.w = width;
+    fmt->det.vid.size.h = height;
+    fmt->det.vid.fps.num = fps_num;
+    fmt->det.vid.fps.denum = fps_denum;
+    fmt->det.vid.avg_bps = fmt->det.vid.max_bps = 0;
+
+    if (pjmedia_video_format_mgr_instance()) {
+	const pjmedia_video_format_info *vfi;
+	pjmedia_video_apply_fmt_param vafp;
+	pj_uint32_t bps;
+
+	vfi = pjmedia_get_video_format_info(NULL, fmt->id);
+        if (vfi) {
+	    pj_bzero(&vafp, sizeof(vafp));
+	    vafp.size = fmt->det.vid.size;
+	    vfi->apply_fmt(vfi, &vafp);
+
+	    bps = vafp.framebytes * fps_num * (pj_size_t)8 / fps_denum;
+	    fmt->det.vid.avg_bps = fmt->det.vid.max_bps = bps;
+        }
+    }
+}
+
+PJ_DEF(pjmedia_audio_format_detail*)
+pjmedia_format_get_audio_format_detail(const pjmedia_format *fmt,
+				       pj_bool_t assert_valid)
+{
+    if (fmt->detail_type==PJMEDIA_FORMAT_DETAIL_AUDIO) {
+	return (pjmedia_audio_format_detail*) &fmt->det.aud;
+    } else {
+	pj_assert(!assert_valid || !"Invalid audio format detail");
+	return NULL;
+    }
+}
+
+PJ_DEF(pjmedia_video_format_detail*)
+pjmedia_format_get_video_format_detail(const pjmedia_format *fmt,
+				       pj_bool_t assert_valid)
+{
+    if (fmt->detail_type==PJMEDIA_FORMAT_DETAIL_VIDEO) {
+	return (pjmedia_video_format_detail*)&fmt->det.vid;
+    } else {
+	pj_assert(!assert_valid || !"Invalid video format detail");
+	return NULL;
+    }
+}
+
+PJ_DEF(pjmedia_format*) pjmedia_format_copy(pjmedia_format *dst,
+					    const pjmedia_format *src)
+{
+    return (pjmedia_format*)pj_memcpy(dst, src, sizeof(*src));
+}
+
+
+static pj_status_t apply_packed_fmt(const pjmedia_video_format_info *fi,
+	                            pjmedia_video_apply_fmt_param *aparam)
+{
+    unsigned i;
+    pj_size_t stride;
+
+    stride = (pj_size_t)((aparam->size.w*fi->bpp) >> 3);
+
+    /* Calculate memsize */
+    aparam->framebytes = stride * aparam->size.h;
+
+    /* Packed formats only use 1 plane */
+    aparam->planes[0] = aparam->buffer;
+    aparam->strides[0] = stride;
+    aparam->plane_bytes[0] = aparam->framebytes;
+
+    /* Zero unused planes */
+    for (i=1; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
+	aparam->strides[i] = 0;
+	aparam->planes[i] = NULL;
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t apply_planar_420(const pjmedia_video_format_info *fi,
+	                             pjmedia_video_apply_fmt_param *aparam)
+{
+    unsigned i;
+    pj_size_t Y_bytes;
+
+    PJ_UNUSED_ARG(fi);
+
+    /* Calculate memsize */
+    Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h);
+    aparam->framebytes = Y_bytes + (Y_bytes>>1);
+
+    /* Planar formats use 3 plane */
+    aparam->strides[0] = aparam->size.w;
+    aparam->strides[1] = aparam->strides[2] = (aparam->size.w>>1);
+
+    aparam->planes[0] = aparam->buffer;
+    aparam->planes[1] = aparam->planes[0] + Y_bytes;
+    aparam->planes[2] = aparam->planes[1] + (Y_bytes>>2);
+
+    aparam->plane_bytes[0] = Y_bytes;
+    aparam->plane_bytes[1] = aparam->plane_bytes[2] = (Y_bytes>>2);
+
+    /* Zero unused planes */
+    for (i=3; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
+	aparam->strides[i] = 0;
+	aparam->planes[i] = NULL;
+        aparam->plane_bytes[i] = 0;
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t apply_planar_422(const pjmedia_video_format_info *fi,
+	                             pjmedia_video_apply_fmt_param *aparam)
+{
+    unsigned i;
+    pj_size_t Y_bytes;
+
+    PJ_UNUSED_ARG(fi);
+
+    /* Calculate memsize */
+    Y_bytes = (pj_size_t)(aparam->size.w * aparam->size.h);
+    aparam->framebytes = (Y_bytes << 1);
+
+    /* Planar formats use 3 plane */
+    aparam->strides[0] = aparam->size.w;
+    aparam->strides[1] = aparam->strides[2] = (aparam->size.w>>1);
+
+    aparam->planes[0] = aparam->buffer;
+    aparam->planes[1] = aparam->planes[0] + Y_bytes;
+    aparam->planes[2] = aparam->planes[1] + (Y_bytes>>1);
+
+    aparam->plane_bytes[0] = Y_bytes;
+    aparam->plane_bytes[1] = aparam->plane_bytes[2] = (Y_bytes>>1);
+
+    /* Zero unused planes */
+    for (i=3; i<PJMEDIA_MAX_VIDEO_PLANES; ++i) {
+	aparam->strides[i] = 0;
+	aparam->planes[i] = NULL;
+        aparam->plane_bytes[i] = 0;
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t)
+pjmedia_video_format_mgr_create(pj_pool_t *pool,
+				unsigned max_fmt,
+				unsigned options,
+				pjmedia_video_format_mgr **p_mgr)
+{
+    pjmedia_video_format_mgr *mgr;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(pool && options==0, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(options);
+
+    mgr = PJ_POOL_ALLOC_T(pool, pjmedia_video_format_mgr);
+    mgr->max_info = max_fmt;
+    mgr->info_cnt = 0;
+    mgr->infos = pj_pool_calloc(pool, max_fmt, sizeof(pjmedia_video_format_info *));
+
+    if (video_format_mgr_instance == NULL)
+	video_format_mgr_instance = mgr;
+
+    for (i=0; i<PJ_ARRAY_SIZE(built_in_vid_fmt_info); ++i) {
+	pjmedia_register_video_format_info(mgr,
+					   &built_in_vid_fmt_info[i]);
+    }
+
+    if (p_mgr)
+	*p_mgr = mgr;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(const pjmedia_video_format_info*)
+pjmedia_get_video_format_info(pjmedia_video_format_mgr *mgr,
+			      pj_uint32_t id)
+{
+    pjmedia_video_format_info **first;
+    int		 comp;
+    unsigned	 n;
+
+    if (!mgr)
+	mgr = pjmedia_video_format_mgr_instance();
+
+    PJ_ASSERT_RETURN(mgr != NULL, NULL);
+
+    /* Binary search for the appropriate format id */
+    comp = -1;
+    first = &mgr->infos[0];
+    n = mgr->info_cnt;
+    for (; n > 0; ) {
+	unsigned half = n / 2;
+	pjmedia_video_format_info **mid = first + half;
+
+	if ((*mid)->id < id) {
+	    first = ++mid;
+	    n -= half + 1;
+	} else if ((*mid)->id==id) {
+	    return *mid;
+	} else {
+	    n = half;
+	}
+    }
+
+    return NULL;
+}
+
+
+PJ_DEF(pj_status_t)
+pjmedia_register_video_format_info(pjmedia_video_format_mgr *mgr,
+				   pjmedia_video_format_info *info)
+{
+    unsigned i;
+
+    if (!mgr)
+	mgr = pjmedia_video_format_mgr_instance();
+
+    PJ_ASSERT_RETURN(mgr != NULL, PJ_EINVALIDOP);
+
+    if (mgr->info_cnt >= mgr->max_info)
+	return PJ_ETOOMANY;
+
+    /* Insert to the array, sorted */
+    for (i=0; i<mgr->info_cnt; ++i) {
+	if (mgr->infos[i]->id >= info->id)
+	    break;
+    }
+
+    if (i < mgr->info_cnt) {
+	if (mgr->infos[i]->id == info->id) {
+	    /* just overwrite */
+	    mgr->infos[i] = info;
+	    return PJ_SUCCESS;
+	}
+
+	pj_memmove(&mgr->infos[i+1], &mgr->infos[i],
+		   (mgr->info_cnt - i) * sizeof(pjmedia_video_format_info*));
+    }
+
+    mgr->infos[i] = info;
+    mgr->info_cnt++;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pjmedia_video_format_mgr*) pjmedia_video_format_mgr_instance(void)
+{
+    pj_assert(video_format_mgr_instance != NULL);
+    return video_format_mgr_instance;
+}
+
+PJ_DEF(void)
+pjmedia_video_format_mgr_set_instance(pjmedia_video_format_mgr *mgr)
+{
+    video_format_mgr_instance = mgr;
+}
+
+
+PJ_DEF(void) pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr *mgr)
+{
+    if (!mgr)
+	mgr = pjmedia_video_format_mgr_instance();
+
+    PJ_ASSERT_ON_FAIL(mgr != NULL, return);
+
+    mgr->info_cnt = 0;
+    if (video_format_mgr_instance == mgr)
+	video_format_mgr_instance = NULL;
+}
+
diff --git a/pjmedia/src/pjmedia/master_port.c b/pjmedia/src/pjmedia/master_port.c
index 800b091..d247e7e 100644
--- a/pjmedia/src/pjmedia/master_port.c
+++ b/pjmedia/src/pjmedia/master_port.c
@@ -56,38 +56,41 @@
     unsigned channel_count;
     unsigned samples_per_frame;
     unsigned bytes_per_frame;
+    pjmedia_audio_format_detail *u_afd, *d_afd;
     pj_status_t status;
 
     /* Sanity check */
     PJ_ASSERT_RETURN(pool && u_port && d_port && p_m, PJ_EINVAL);
 
+    u_afd = pjmedia_format_get_audio_format_detail(&u_port->info.fmt, PJ_TRUE);
+    d_afd = pjmedia_format_get_audio_format_detail(&d_port->info.fmt, PJ_TRUE);
 
     /* Both ports MUST have equal clock rate */
-    PJ_ASSERT_RETURN(u_port->info.clock_rate == d_port->info.clock_rate,
+    PJ_ASSERT_RETURN(u_afd->clock_rate == d_afd->clock_rate,
 		     PJMEDIA_ENCCLOCKRATE);
 
     /* Both ports MUST have equal samples per frame */
-    PJ_ASSERT_RETURN(u_port->info.samples_per_frame==
-		     d_port->info.samples_per_frame,
+    PJ_ASSERT_RETURN(PJMEDIA_PIA_SPF(&u_port->info)==
+			PJMEDIA_PIA_SPF(&d_port->info),
 		     PJMEDIA_ENCSAMPLESPFRAME);
 
     /* Both ports MUST have equal channel count */
-    PJ_ASSERT_RETURN(u_port->info.channel_count == d_port->info.channel_count,
+    PJ_ASSERT_RETURN(u_afd->channel_count == d_afd->channel_count,
 		     PJMEDIA_ENCCHANNEL);
 
 
     /* Get clock_rate and samples_per_frame from one of the port. */
-    clock_rate = u_port->info.clock_rate;
-    samples_per_frame = u_port->info.samples_per_frame;
-    channel_count = u_port->info.channel_count;
+    clock_rate = u_afd->clock_rate;
+    samples_per_frame = PJMEDIA_PIA_SPF(&u_port->info);
+    channel_count = u_afd->channel_count;
 
 
     /* Get the bytes_per_frame value, to determine the size of the
      * buffer. We take the larger size of the two ports.
      */
-    bytes_per_frame = u_port->info.bytes_per_frame;
-    if (d_port->info.bytes_per_frame > bytes_per_frame)
-	bytes_per_frame = d_port->info.bytes_per_frame;
+    bytes_per_frame = PJMEDIA_AFD_AVG_FSZ(u_afd);
+    if (PJMEDIA_AFD_AVG_FSZ(d_afd) > bytes_per_frame)
+	bytes_per_frame = PJMEDIA_AFD_AVG_FSZ(d_afd);
 
 
     /* Create the master port instance */
@@ -207,13 +210,16 @@
 {
     PJ_ASSERT_RETURN(m && port, PJ_EINVAL);
 
+    /* Only supports audio for now */
+    PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_AUDIO, PJ_ENOTSUP);
+
     /* If we have downstream port, make sure they have matching samples per
      * frame.
      */
     if (m->d_port) {
 	PJ_ASSERT_RETURN(
-	    port->info.clock_rate/port->info.samples_per_frame==
-	    m->d_port->info.clock_rate/m->d_port->info.samples_per_frame,
+	    PJMEDIA_PIA_PTIME(&port->info) ==
+		PJMEDIA_PIA_PTIME(&m->d_port->info),
 	    PJMEDIA_ENCSAMPLESPFRAME
 	);
     }
@@ -246,13 +252,16 @@
 {
     PJ_ASSERT_RETURN(m && port, PJ_EINVAL);
 
+    /* Only supports audio for now */
+    PJ_ASSERT_RETURN(port->info.fmt.type==PJMEDIA_TYPE_AUDIO, PJ_ENOTSUP);
+
     /* If we have upstream port, make sure they have matching samples per
      * frame.
      */
     if (m->u_port) {
 	PJ_ASSERT_RETURN(
-	    port->info.clock_rate/port->info.samples_per_frame==
-	    m->u_port->info.clock_rate/m->u_port->info.samples_per_frame,
+	    PJMEDIA_PIA_PTIME(&port->info) ==
+		    PJMEDIA_PIA_PTIME(&m->u_port->info),
 	    PJMEDIA_ENCSAMPLESPFRAME
 	);
     }
diff --git a/pjmedia/src/pjmedia/mem_capture.c b/pjmedia/src/pjmedia/mem_capture.c
index f8e5218..2a1f605 100644
--- a/pjmedia/src/pjmedia/mem_capture.c
+++ b/pjmedia/src/pjmedia/mem_capture.c
@@ -46,7 +46,7 @@
 
 
 static pj_status_t rec_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame);
+				 pjmedia_frame *frame);
 static pj_status_t rec_get_frame(pjmedia_port *this_port, 
 				  pjmedia_frame *frame);
 static pj_status_t rec_on_destroy(pjmedia_port *this_port);
@@ -140,7 +140,7 @@
 
 
 static pj_status_t rec_put_frame( pjmedia_port *this_port, 
-				  const pjmedia_frame *frame)
+				  pjmedia_frame *frame)
 {
     struct mem_rec *rec;
     char *endpos;
diff --git a/pjmedia/src/pjmedia/mem_player.c b/pjmedia/src/pjmedia/mem_player.c
index 9a4e2cc..40fa567 100644
--- a/pjmedia/src/pjmedia/mem_player.c
+++ b/pjmedia/src/pjmedia/mem_player.c
@@ -48,7 +48,7 @@
 
 
 static pj_status_t mem_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame);
+				 pjmedia_frame *frame);
 static pj_status_t mem_get_frame(pjmedia_port *this_port, 
 				  pjmedia_frame *frame);
 static pj_status_t mem_on_destroy(pjmedia_port *this_port);
@@ -125,7 +125,7 @@
 
 
 static pj_status_t mem_put_frame( pjmedia_port *this_port, 
-				  const pjmedia_frame *frame)
+				  pjmedia_frame *frame)
 {
     PJ_UNUSED_ARG(this_port);
     PJ_UNUSED_ARG(frame);
@@ -165,7 +165,7 @@
 	player->eof = PJ_FALSE;
     }
 
-    size_needed = this_port->info.bytes_per_frame;
+    size_needed = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
     size_written = 0;
     endpos = player->buffer + player->buf_size;
 
@@ -200,11 +200,11 @@
 	}
     }
 
-    frame->size = this_port->info.bytes_per_frame;
+    frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
     frame->timestamp.u64 = player->timestamp.u64;
     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
 
-    player->timestamp.u64 += this_port->info.samples_per_frame;
+    player->timestamp.u64 += PJMEDIA_PIA_SPF(&this_port->info);
 
     return PJ_SUCCESS;
 }
diff --git a/pjmedia/src/pjmedia/null_port.c b/pjmedia/src/pjmedia/null_port.c
index 0557583..c775a7c 100644
--- a/pjmedia/src/pjmedia/null_port.c
+++ b/pjmedia/src/pjmedia/null_port.c
@@ -29,7 +29,7 @@
 static pj_status_t null_get_frame(pjmedia_port *this_port, 
 				  pjmedia_frame *frame);
 static pj_status_t null_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame);
+				  pjmedia_frame *frame);
 static pj_status_t null_on_destroy(pjmedia_port *this_port);
 
 
@@ -67,7 +67,7 @@
  * Put frame to file.
  */
 static pj_status_t null_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame)
+				  pjmedia_frame *frame)
 {
     PJ_UNUSED_ARG(this_port);
     PJ_UNUSED_ARG(frame);
@@ -82,10 +82,10 @@
 				  pjmedia_frame *frame)
 {
     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
-    frame->size = this_port->info.samples_per_frame * 2;
-    frame->timestamp.u32.lo += this_port->info.samples_per_frame;
+    frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
+    frame->timestamp.u32.lo += PJMEDIA_PIA_SPF(&this_port->info);
     pjmedia_zero_samples((pj_int16_t*)frame->buf, 
-			 this_port->info.samples_per_frame);
+			  PJMEDIA_PIA_SPF(&this_port->info));
 
     return PJ_SUCCESS;
 }
diff --git a/pjmedia/src/pjmedia/port.c b/pjmedia/src/pjmedia/port.c
index 922489c..5089f4f 100644
--- a/pjmedia/src/pjmedia/port.c
+++ b/pjmedia/src/pjmedia/port.c
@@ -21,6 +21,7 @@
 #include <pjmedia/errno.h>
 #include <pj/assert.h>
 #include <pj/log.h>
+#include <pj/pool.h>
 
 #define THIS_FILE	"port.c"
 
@@ -37,24 +38,41 @@
 					    unsigned bits_per_sample,
 					    unsigned samples_per_frame)
 {
+#define USEC_IN_SEC (pj_uint64_t)1000000
+    unsigned frame_time_usec, avg_bps;
+
     pj_bzero(info, sizeof(*info));
 
-    info->name = *name;
     info->signature = signature;
-    info->type = PJMEDIA_TYPE_AUDIO;
-    info->has_info = PJ_TRUE;
-    info->need_info = PJ_FALSE;
-    info->pt = 0xFF;
-    info->encoding_name = pj_str("pcm");
-    info->clock_rate = clock_rate;
-    info->channel_count = channel_count;
-    info->bits_per_sample = bits_per_sample;
-    info->samples_per_frame = samples_per_frame;
-    info->bytes_per_frame = samples_per_frame * bits_per_sample / 8;
+    info->dir = PJMEDIA_DIR_ENCODING_DECODING;
+    info->name = *name;
+
+    frame_time_usec = (unsigned)(samples_per_frame * USEC_IN_SEC /
+				 channel_count / clock_rate);
+    avg_bps = clock_rate * channel_count * bits_per_sample;
+
+    pjmedia_format_init_audio(&info->fmt, PJMEDIA_FORMAT_L16, clock_rate,
+			      channel_count, bits_per_sample, frame_time_usec,
+			      avg_bps, avg_bps);
 
     return PJ_SUCCESS;
 }
 
+PJ_DEF(pj_status_t) pjmedia_port_info_init2( pjmedia_port_info *info,
+					     const pj_str_t *name,
+					     unsigned signature,
+					     pjmedia_dir dir,
+					     const pjmedia_format *fmt)
+{
+    pj_bzero(info, sizeof(*info));
+    info->signature = signature;
+    info->dir = dir;
+    info->name = *name;
+
+    pjmedia_format_copy(&info->fmt, fmt);
+
+    return PJ_SUCCESS;
+}
 
 /**
  * Get a frame from the port (and subsequent downstream ports).
@@ -77,7 +95,7 @@
  * Put a frame to the port (and subsequent downstream ports).
  */
 PJ_DEF(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port,
-					    const pjmedia_frame *frame )
+					    pjmedia_frame *frame )
 {
     PJ_ASSERT_RETURN(port && frame, PJ_EINVAL);
 
diff --git a/pjmedia/src/pjmedia/resample_port.c b/pjmedia/src/pjmedia/resample_port.c
index 7813aaf..44de582 100644
--- a/pjmedia/src/pjmedia/resample_port.c
+++ b/pjmedia/src/pjmedia/resample_port.c
@@ -42,7 +42,7 @@
 
 
 static pj_status_t resample_put_frame(pjmedia_port *this_port,
-				      const pjmedia_frame *frame);
+				      pjmedia_frame *frame);
 static pj_status_t resample_get_frame(pjmedia_port *this_port, 
 				      pjmedia_frame *frame);
 static pj_status_t resample_destroy(pjmedia_port *this_port);
@@ -57,25 +57,25 @@
 {
     const pj_str_t name = pj_str("resample");
     struct resample_port *rport;
-    unsigned ptime;
+    pjmedia_audio_format_detail *d_afd, *r_afd;
     pj_status_t status;
 
     /* Validate arguments. */
     PJ_ASSERT_RETURN(pool && dn_port && clock_rate && p_port, PJ_EINVAL);
 
     /* Only supports 16bit samples per frame */
-    PJ_ASSERT_RETURN(dn_port->info.bits_per_sample == 16, PJMEDIA_ENCBITS);
+    PJ_ASSERT_RETURN(PJMEDIA_PIA_BITS(&dn_port->info) == 16, PJMEDIA_ENCBITS);
 
-    ptime = dn_port->info.samples_per_frame * 1000 / 
-	    dn_port->info.clock_rate;
-    
+    d_afd = pjmedia_format_get_audio_format_detail(&dn_port->info.fmt, 1);
+    r_afd = pjmedia_format_get_audio_format_detail(&rport->base.info.fmt, 1);
+
     /* Create and initialize port. */
     rport = PJ_POOL_ZALLOC_T(pool, struct resample_port);
     PJ_ASSERT_RETURN(rport != NULL, PJ_ENOMEM);
 
     pjmedia_port_info_init(&rport->base.info, &name, SIGNATURE, clock_rate,
-			   dn_port->info.channel_count, BYTES_PER_SAMPLE * 8, 
-			   clock_rate * ptime / 1000);
+		           d_afd->channel_count, BYTES_PER_SAMPLE * 8,
+			   clock_rate * d_afd->frame_time_usec / 1000000);
 
     rport->dn_port = dn_port;
     rport->options = opt;
@@ -86,11 +86,11 @@
      * both functions may run simultaneously.
      */
     rport->get_buf = (pj_int16_t*)
-		     pj_pool_alloc(pool, dn_port->info.bytes_per_frame);
+		     pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info));
     PJ_ASSERT_RETURN(rport->get_buf != NULL, PJ_ENOMEM);
 
     rport->put_buf = (pj_int16_t*)
-		     pj_pool_alloc(pool, dn_port->info.bytes_per_frame);
+		     pj_pool_alloc(pool, PJMEDIA_PIA_AVG_FSZ(&dn_port->info));
     PJ_ASSERT_RETURN(rport->put_buf != NULL, PJ_ENOMEM);
 
 
@@ -98,10 +98,10 @@
     status = pjmedia_resample_create(pool, 
 				     (opt&PJMEDIA_RESAMPLE_USE_LINEAR)==0,
 				     (opt&PJMEDIA_RESAMPLE_USE_SMALL_FILTER)==0,
-				     dn_port->info.channel_count,
-				     dn_port->info.clock_rate, 
-				     rport->base.info.clock_rate,
-				     dn_port->info.samples_per_frame, 
+				     d_afd->channel_count,
+				     d_afd->clock_rate,
+				     r_afd->clock_rate,
+				     PJMEDIA_PIA_SPF(&dn_port->info),
 				     &rport->resample_get);
     if (status != PJ_SUCCESS)
 	return status;
@@ -110,10 +110,10 @@
     status = pjmedia_resample_create(pool, 
 				     (opt&PJMEDIA_RESAMPLE_USE_LINEAR)==0, 
 				     (opt&PJMEDIA_RESAMPLE_USE_SMALL_FILTER)==0,
-				     dn_port->info.channel_count,
-				     rport->base.info.clock_rate, 
-				     dn_port->info.clock_rate,
-				     rport->base.info.samples_per_frame,
+				     d_afd->channel_count,
+				     r_afd->clock_rate,
+				     d_afd->clock_rate,
+				     PJMEDIA_PIA_SPF(&rport->base.info),
 				     &rport->resample_put);
 
     /* Media port interface */
@@ -131,7 +131,7 @@
 
 
 static pj_status_t resample_put_frame(pjmedia_port *this_port,
-				      const pjmedia_frame *frame)
+				      pjmedia_frame *frame)
 {
     struct resample_port *rport = (struct resample_port*) this_port;
     pjmedia_frame downstream_frame;
@@ -147,7 +147,7 @@
 			      rport->put_buf);
 
 	downstream_frame.buf = rport->put_buf;
-	downstream_frame.size = rport->dn_port->info.bytes_per_frame;
+	downstream_frame.size = PJMEDIA_PIA_AVG_FSZ(&rport->dn_port->info);
     } else {
 	downstream_frame.buf = frame->buf;
 	downstream_frame.size = frame->size;
@@ -175,7 +175,7 @@
     }
 
     tmp_frame.buf = rport->get_buf;
-    tmp_frame.size = rport->dn_port->info.bytes_per_frame;
+    tmp_frame.size = PJMEDIA_PIA_AVG_FSZ(&rport->dn_port->info);
     tmp_frame.timestamp.u64 = frame->timestamp.u64;
     tmp_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
@@ -187,8 +187,8 @@
 	frame->type = tmp_frame.type;
 	frame->timestamp = tmp_frame.timestamp;
 	/* Copy whatever returned as long as the buffer size is enough */
-	frame->size = tmp_frame.size < rport->base.info.bytes_per_frame ?
-		      tmp_frame.size : rport->base.info.bytes_per_frame;
+	frame->size = tmp_frame.size < PJMEDIA_PIA_AVG_FSZ(&rport->base.info) ?
+		      tmp_frame.size : PJMEDIA_PIA_AVG_FSZ(&rport->base.info);
 	if (tmp_frame.size) {
 	    pjmedia_copy_samples((pj_int16_t*)frame->buf, 
 				 (const pj_int16_t*)tmp_frame.buf, 
@@ -201,7 +201,7 @@
 			  (const pj_int16_t*) tmp_frame.buf, 
 			  (pj_int16_t*) frame->buf);
 
-    frame->size = rport->base.info.bytes_per_frame;
+    frame->size = PJMEDIA_PIA_AVG_FSZ(&rport->base.info);
     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
 
     return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/silencedet.c b/pjmedia/src/pjmedia/silencedet.c
index 40c9099..dba663e 100644
--- a/pjmedia/src/pjmedia/silencedet.c
+++ b/pjmedia/src/pjmedia/silencedet.c
@@ -23,6 +23,7 @@
 #include <pj/assert.h>
 #include <pj/log.h>
 #include <pj/pool.h>
+#include <pj/string.h>
 
 #define THIS_FILE   "silencedet.c"
 
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 1029fc3..66324fc 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -637,24 +637,25 @@
 PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port,
 					      pjmedia_port *port)
 {
-    pjmedia_port_info *pinfo;
+    pjmedia_audio_format_detail *afd;
 
     PJ_ASSERT_RETURN(snd_port && port, PJ_EINVAL);
 
+    afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, PJ_TRUE);
+
     /* Check that port has the same configuration as the sound device
      * port.
      */
-    pinfo = &port->info;
-    if (pinfo->clock_rate != snd_port->clock_rate)
+    if (afd->clock_rate != snd_port->clock_rate)
 	return PJMEDIA_ENCCLOCKRATE;
 
-    if (pinfo->samples_per_frame != snd_port->samples_per_frame)
+    if (PJMEDIA_AFD_SPF(afd) != snd_port->samples_per_frame)
 	return PJMEDIA_ENCSAMPLESPFRAME;
 
-    if (pinfo->channel_count != snd_port->channel_count)
+    if (afd->channel_count != snd_port->channel_count)
 	return PJMEDIA_ENCCHANNEL;
 
-    if (pinfo->bits_per_sample != snd_port->bits_per_sample)
+    if (afd->bits_per_sample != snd_port->bits_per_sample)
 	return PJMEDIA_ENCBITS;
 
     /* Port is okay. */
diff --git a/pjmedia/src/pjmedia/splitcomb.c b/pjmedia/src/pjmedia/splitcomb.c
index 4e2e628..05f96dd 100644
--- a/pjmedia/src/pjmedia/splitcomb.c
+++ b/pjmedia/src/pjmedia/splitcomb.c
@@ -189,13 +189,13 @@
  * Prototypes.
  */
 static pj_status_t put_frame(pjmedia_port *this_port, 
-			     const pjmedia_frame *frame);
+			     pjmedia_frame *frame);
 static pj_status_t get_frame(pjmedia_port *this_port, 
 			     pjmedia_frame *frame);
 static pj_status_t on_destroy(pjmedia_port *this_port);
 
 static pj_status_t rport_put_frame(pjmedia_port *this_port, 
-				   const pjmedia_frame *frame);
+				   pjmedia_frame *frame);
 static pj_status_t rport_get_frame(pjmedia_port *this_port, 
 				   pjmedia_frame *frame);
 static pj_status_t rport_on_destroy(pjmedia_port *this_port);
@@ -284,7 +284,7 @@
     PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
 
     /* Check the channel number */
-    PJ_ASSERT_RETURN(ch_num < sc->base.info.channel_count, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
 
     /* options is unused for now */
     PJ_UNUSED_ARG(options);
@@ -308,7 +308,8 @@
     const pj_str_t name = pj_str("scomb-rev");
     struct splitcomb *sc = (struct splitcomb*) splitcomb;
     struct reverse_port *rport;
-    unsigned buf_cnt, ptime;
+    unsigned buf_cnt;
+    const pjmedia_audio_format_detail *sc_afd, *p_afd;
     pjmedia_port *port;
     pj_status_t status;
 
@@ -319,11 +320,13 @@
     PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL);
 
     /* Check the channel number */
-    PJ_ASSERT_RETURN(ch_num < sc->base.info.channel_count, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ch_num < PJMEDIA_PIA_CCNT(&sc->base.info), PJ_EINVAL);
 
     /* options is unused for now */
     PJ_UNUSED_ARG(options);
 
+    sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1);
+
     /* Create the port */
     rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port);
     rport->parent = sc;
@@ -332,10 +335,12 @@
     /* Initialize port info... */
     port = &rport->base;
     pjmedia_port_info_init(&port->info, &name, SIGNATURE_PORT, 
-			   splitcomb->info.clock_rate, 1, 
-			   splitcomb->info.bits_per_sample, 
-			   splitcomb->info.samples_per_frame / 
-				   splitcomb->info.channel_count);
+			   sc_afd->clock_rate, 1,
+			   sc_afd->bits_per_sample,
+			   PJMEDIA_PIA_SPF(&splitcomb->info) /
+				   sc_afd->channel_count);
+
+    p_afd = pjmedia_format_get_audio_format_detail(&port->info.fmt, 1);
 
     /* ... and the callbacks */
     port->put_frame = &rport_put_frame;
@@ -347,30 +352,27 @@
     if (buf_cnt == 0)
 	buf_cnt = MAX_BUF_CNT;
 
-    ptime = port->info.samples_per_frame * 1000 / port->info.clock_rate /
-	    port->info.channel_count;
-
     rport->max_burst = MAX_BURST;
     rport->max_null_frames = MAX_NULL_FRAMES;
 
     /* Create downstream/put buffers */
     status = pjmedia_delay_buf_create(pool, "scombdb-dn",
-				      port->info.clock_rate,
-				      port->info.samples_per_frame,
-				      port->info.channel_count,
-				      buf_cnt * ptime, 0,
-				      &rport->buf[DIR_DOWNSTREAM].dbuf);
+				      p_afd->clock_rate,
+				      PJMEDIA_PIA_SPF(&port->info),
+				      p_afd->channel_count,
+				      buf_cnt * p_afd->frame_time_usec / 1000,
+				      0, &rport->buf[DIR_DOWNSTREAM].dbuf);
     if (status != PJ_SUCCESS) {
 	return status;
     }
 
     /* Create upstream/get buffers */
     status = pjmedia_delay_buf_create(pool, "scombdb-up",
-				      port->info.clock_rate,
-				      port->info.samples_per_frame,
-				      port->info.channel_count,
-				      buf_cnt * ptime, 0,
-				      &rport->buf[DIR_UPSTREAM].dbuf);
+				      p_afd->clock_rate,
+				      PJMEDIA_PIA_SPF(&port->info),
+				      p_afd->channel_count,
+				      buf_cnt * p_afd->frame_time_usec / 1000,
+				      0, &rport->buf[DIR_UPSTREAM].dbuf);
     if (status != PJ_SUCCESS) {
 	pjmedia_delay_buf_destroy(rport->buf[DIR_DOWNSTREAM].dbuf);
 	return status;
@@ -378,7 +380,8 @@
 
     /* And temporary upstream/get buffer */
     rport->tmp_up_buf = (pj_int16_t*)
-	                pj_pool_alloc(pool, port->info.bytes_per_frame);
+	                pj_pool_alloc(pool,
+				      PJMEDIA_PIA_AVG_FSZ(&port->info));
 
     /* Save port in the splitcomb */
     sc->port_desc[ch_num].port = &rport->base;
@@ -436,7 +439,7 @@
     rport->buf[dir].level += op;
 
     if (op == OP_PUT) {
-	rport->buf[dir].ts.u64 += rport->base.info.samples_per_frame;
+	rport->buf[dir].ts.u64 += PJMEDIA_PIA_SPF(&rport->base.info);
     }
 
     if (rport->buf[dir].paused) {
@@ -484,14 +487,14 @@
  * it to the appropriate port.
  */
 static pj_status_t put_frame(pjmedia_port *this_port, 
-			     const pjmedia_frame *frame)
+			     pjmedia_frame *frame)
 {
     struct splitcomb *sc = (struct splitcomb*) this_port;
     unsigned ch;
 
     /* Handle null frame */
     if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
-	for (ch=0; ch < this_port->info.channel_count; ++ch) {
+	for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
 	    pjmedia_port *port = sc->port_desc[ch].port;
 
 	    if (!port) continue;
@@ -533,7 +536,7 @@
 
 		/* Generate zero frame. */
 		pjmedia_zero_samples(sc->put_buf, 
-				     this_port->info.samples_per_frame);
+				     PJMEDIA_PIA_SPF(&this_port->info));
 
 		/* Put frame to delay buffer */
 		pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
@@ -547,13 +550,13 @@
     /* Not sure how we would handle partial frame, so better reject
      * it for now.
      */
-    PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame,
+    PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
 		     PJ_EINVAL);
 
     /* 
      * Write mono frame into each channels 
      */
-    for (ch=0; ch < this_port->info.channel_count; ++ch) {
+    for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
 	pjmedia_port *port = sc->port_desc[ch].port;
 
 	if (!port)
@@ -561,17 +564,17 @@
 
 	/* Extract the mono frame to temporary buffer */
 	extract_mono_frame((const pj_int16_t*)frame->buf, sc->put_buf, ch, 
-			   this_port->info.channel_count, 
+			   PJMEDIA_PIA_CCNT(&this_port->info),
 			   frame->size * 8 / 
-			     this_port->info.bits_per_sample /
-			     this_port->info.channel_count);
+			     PJMEDIA_PIA_BITS(&this_port->info) /
+			     PJMEDIA_PIA_CCNT(&this_port->info));
 
 	if (!sc->port_desc[ch].reversed) {
 	    /* Write to normal port */
 	    pjmedia_frame mono_frame;
 
 	    mono_frame.buf = sc->put_buf;
-	    mono_frame.size = frame->size / this_port->info.channel_count;
+	    mono_frame.size = frame->size / PJMEDIA_PIA_CCNT(&this_port->info);
 	    mono_frame.type = frame->type;
 	    mono_frame.timestamp.u64 = frame->timestamp.u64;
 
@@ -612,20 +615,20 @@
     pj_bool_t has_frame = PJ_FALSE;
 
     /* Read frame from each port */
-    for (ch=0; ch < this_port->info.channel_count; ++ch) {
+    for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) {
 	pjmedia_port *port = sc->port_desc[ch].port;
 	pjmedia_frame mono_frame;
 	pj_status_t status;
 
 	if (!port) {
 	    pjmedia_zero_samples(sc->get_buf, 
-				 this_port->info.samples_per_frame /
-				    this_port->info.channel_count);
+				 PJMEDIA_PIA_SPF(&this_port->info) /
+				  PJMEDIA_PIA_CCNT(&this_port->info));
 
 	} else if (sc->port_desc[ch].reversed == PJ_FALSE) {
 	    /* Read from normal port */
 	    mono_frame.buf = sc->get_buf;
-	    mono_frame.size = port->info.bytes_per_frame;
+	    mono_frame.size = PJMEDIA_PIA_AVG_FSZ(&port->info);
 	    mono_frame.timestamp.u64 = frame->timestamp.u64;
 
 	    status = pjmedia_port_get_frame(port, &mono_frame);
@@ -633,7 +636,7 @@
 		mono_frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
 	    {
 		pjmedia_zero_samples(sc->get_buf, 
-				     port->info.samples_per_frame);
+				     PJMEDIA_PIA_SPF(&port->info));
 	    }
 
 	    frame->timestamp.u64 = mono_frame.timestamp.u64;
@@ -651,7 +654,7 @@
 
 	    } else {
 		pjmedia_zero_samples(sc->get_buf, 
-				     port->info.samples_per_frame);
+				     PJMEDIA_PIA_SPF(&port->info));
 	    }
 
 	    frame->timestamp.u64 = rport->buf[DIR_UPSTREAM].ts.u64;
@@ -660,9 +663,9 @@
 	/* Combine the mono frame into multichannel frame */
 	store_mono_frame(sc->get_buf, 
 			 (pj_int16_t*)frame->buf, ch,
-			 this_port->info.channel_count,
-			 this_port->info.samples_per_frame /
-			 this_port->info.channel_count);
+			 PJMEDIA_PIA_CCNT(&this_port->info),
+			 PJMEDIA_PIA_SPF(&this_port->info) /
+			  PJMEDIA_PIA_CCNT(&this_port->info));
 
 	has_frame = PJ_TRUE;
     }
@@ -670,7 +673,7 @@
     /* Return NO_FRAME is we don't get any frames from downstream ports */
     if (has_frame) {
 	frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
-	frame->size = this_port->info.bytes_per_frame;
+	frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
     } else
 	frame->type = PJMEDIA_FRAME_TYPE_NONE;
 
@@ -694,11 +697,11 @@
  * will be picked up by get_frame() above.
  */
 static pj_status_t rport_put_frame(pjmedia_port *this_port, 
-				   const pjmedia_frame *frame)
+				   pjmedia_frame *frame)
 {
     struct reverse_port *rport = (struct reverse_port*) this_port;
 
-    pj_assert(frame->size <= rport->base.info.bytes_per_frame);
+    pj_assert(frame->size <= PJMEDIA_PIA_AVG_FSZ(&rport->base.info));
 
     /* Handle NULL frame */
     if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) {
@@ -730,7 +733,7 @@
 
 	/* Generate zero frame. */
 	pjmedia_zero_samples(rport->tmp_up_buf, 
-			     this_port->info.samples_per_frame);
+			     PJMEDIA_PIA_SPF(&this_port->info));
 
 	/* Put frame to delay buffer */
 	return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, 
@@ -738,7 +741,7 @@
     }
 
     /* Not sure how to handle partial frame, so better reject for now */
-    PJ_ASSERT_RETURN(frame->size == this_port->info.bytes_per_frame,
+    PJ_ASSERT_RETURN(frame->size == PJMEDIA_PIA_AVG_FSZ(&this_port->info),
 		     PJ_EINVAL);
 
     /* Reset NULL frame counter */
@@ -755,7 +758,7 @@
      * modifies the frame content.
      */
     pjmedia_copy_samples(rport->tmp_up_buf, (const pj_int16_t*)frame->buf,
-		         this_port->info.samples_per_frame);
+		         PJMEDIA_PIA_SPF(&this_port->info));
 
     /* Put frame to delay buffer */
     return pjmedia_delay_buf_put(rport->buf[DIR_UPSTREAM].dbuf, 
@@ -783,7 +786,7 @@
     }
 
     /* Get frame from delay buffer */
-    frame->size = this_port->info.bytes_per_frame;
+    frame->size = PJMEDIA_PIA_AVG_FSZ(&this_port->info);
     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
     frame->timestamp.u64 = rport->buf[DIR_DOWNSTREAM].ts.u64;
 
diff --git a/pjmedia/src/pjmedia/stereo_port.c b/pjmedia/src/pjmedia/stereo_port.c
index fe9b51c..15ffac9 100644
--- a/pjmedia/src/pjmedia/stereo_port.c
+++ b/pjmedia/src/pjmedia/stereo_port.c
@@ -39,7 +39,7 @@
 
 
 static pj_status_t stereo_put_frame(pjmedia_port *this_port,
-				    const pjmedia_frame *frame);
+				    pjmedia_frame *frame);
 static pj_status_t stereo_get_frame(pjmedia_port *this_port, 
 				    pjmedia_frame *frame);
 static pj_status_t stereo_destroy(pjmedia_port *this_port);
@@ -60,24 +60,27 @@
     PJ_ASSERT_RETURN(pool && dn_port && channel_count && p_port, PJ_EINVAL);
 
     /* Only supports 16bit samples per frame */
-    PJ_ASSERT_RETURN(dn_port->info.bits_per_sample == 16, PJMEDIA_ENCBITS);
+    PJ_ASSERT_RETURN(PJMEDIA_PIA_BITS(&dn_port->info) == 16,
+		     PJMEDIA_ENCBITS);
 
     /* Validate channel counts */
-    PJ_ASSERT_RETURN(((dn_port->info.channel_count>1 && channel_count==1) ||
-		      (dn_port->info.channel_count==1 && channel_count>1)),
+    PJ_ASSERT_RETURN(((PJMEDIA_PIA_CCNT(&dn_port->info)>1 &&
+			      channel_count==1) ||
+		      (PJMEDIA_PIA_CCNT(&dn_port->info)==1 &&
+			      channel_count>1)),
 		      PJ_EINVAL);
 
     /* Create and initialize port. */
     sport = PJ_POOL_ZALLOC_T(pool, struct stereo_port);
     PJ_ASSERT_RETURN(sport != NULL, PJ_ENOMEM);
 
-    samples_per_frame = dn_port->info.samples_per_frame * channel_count /
-			dn_port->info.channel_count;
+    samples_per_frame = PJMEDIA_PIA_SPF(&dn_port->info) * channel_count /
+	                  PJMEDIA_PIA_CCNT(&dn_port->info);
 
     pjmedia_port_info_init(&sport->base.info, &name, SIGNATURE, 
-			   dn_port->info.clock_rate,
+	                   PJMEDIA_PIA_SRATE(&dn_port->info),
 			   channel_count, 
-			   dn_port->info.bits_per_sample, 
+			   PJMEDIA_PIA_BITS(&dn_port->info),
 			   samples_per_frame);
 
     sport->dn_port = dn_port;
@@ -85,12 +88,14 @@
 
     /* We always need buffer for put_frame */
     sport->put_buf = (pj_int16_t*)
-		     pj_pool_alloc(pool, dn_port->info.bytes_per_frame);
+		     pj_pool_alloc(pool,
+				   PJMEDIA_PIA_AVG_FSZ(&dn_port->info));
 
     /* See if we need buffer for get_frame */
-    if (dn_port->info.channel_count > channel_count) {
+    if (PJMEDIA_PIA_CCNT(&dn_port->info) > channel_count) {
 	sport->get_buf = (pj_int16_t*)
-			 pj_pool_alloc(pool, dn_port->info.bytes_per_frame);
+			 pj_pool_alloc(pool,
+				       PJMEDIA_PIA_AVG_FSZ(&dn_port->info));
     }
 
     /* Media port interface */
@@ -106,9 +111,10 @@
 }
 
 static pj_status_t stereo_put_frame(pjmedia_port *this_port,
-				    const pjmedia_frame *frame)
+				    pjmedia_frame *frame)
 {
     struct stereo_port *sport = (struct stereo_port*) this_port;
+    const pjmedia_audio_format_detail *s_afd, *dn_afd;
     pjmedia_frame tmp_frame;
 
     /* Return if we don't have downstream port. */
@@ -116,23 +122,27 @@
 	return PJ_SUCCESS;
     }
 
+    s_afd = pjmedia_format_get_audio_format_detail(&this_port->info.fmt, 1);
+    dn_afd = pjmedia_format_get_audio_format_detail(&sport->dn_port->info.fmt,
+						    1);
+
     if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
 	tmp_frame.buf = sport->put_buf;
-	if (sport->dn_port->info.channel_count == 1) {
+	if (dn_afd->channel_count == 1) {
 	    pjmedia_convert_channel_nto1((pj_int16_t*)tmp_frame.buf, 
 					 (const pj_int16_t*)frame->buf,
-					 sport->base.info.channel_count, 
-					 sport->base.info.samples_per_frame, 
+					 s_afd->channel_count,
+					 PJMEDIA_AFD_SPF(s_afd),
 					 (sport->options & PJMEDIA_STEREO_MIX),
 					 0);
 	} else {
 	    pjmedia_convert_channel_1ton((pj_int16_t*)tmp_frame.buf, 
 					 (const pj_int16_t*)frame->buf,
-					 sport->dn_port->info.channel_count, 
-					 sport->base.info.samples_per_frame,
+					 dn_afd->channel_count,
+					 PJMEDIA_AFD_SPF(s_afd),
 					 sport->options);
 	}
-	tmp_frame.size = sport->dn_port->info.bytes_per_frame;
+	tmp_frame.size = PJMEDIA_AFD_AVG_FSZ(dn_afd);
     } else {
 	tmp_frame.buf = frame->buf;
 	tmp_frame.size = frame->size;
@@ -150,6 +160,7 @@
 				    pjmedia_frame *frame)
 {
     struct stereo_port *sport = (struct stereo_port*) this_port;
+    const pjmedia_audio_format_detail *s_afd, *dn_afd;
     pjmedia_frame tmp_frame;
     pj_status_t status;
 
@@ -159,8 +170,12 @@
 	return PJ_SUCCESS;
     }
 
+    s_afd = pjmedia_format_get_audio_format_detail(&this_port->info.fmt, 1);
+    dn_afd = pjmedia_format_get_audio_format_detail(&sport->dn_port->info.fmt,
+						    1);
+
     tmp_frame.buf = sport->get_buf? sport->get_buf : frame->buf;
-    tmp_frame.size = sport->dn_port->info.bytes_per_frame;
+    tmp_frame.size = PJMEDIA_PIA_AVG_FSZ(&sport->dn_port->info);
     tmp_frame.timestamp.u64 = frame->timestamp.u64;
     tmp_frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
@@ -177,21 +192,21 @@
 	return PJ_SUCCESS;
     }
 
-    if (sport->base.info.channel_count == 1) {
+    if (s_afd->channel_count == 1) {
 	pjmedia_convert_channel_nto1((pj_int16_t*)frame->buf, 
 				     (const pj_int16_t*)tmp_frame.buf,
-				     sport->dn_port->info.channel_count, 
-				     sport->dn_port->info.samples_per_frame, 
+				     dn_afd->channel_count,
+				     PJMEDIA_AFD_SPF(s_afd),
 				     (sport->options & PJMEDIA_STEREO_MIX), 0);
     } else {
 	pjmedia_convert_channel_1ton((pj_int16_t*)frame->buf, 
 				     (const pj_int16_t*)tmp_frame.buf,
-				     sport->base.info.channel_count, 
-				     sport->dn_port->info.samples_per_frame,
+				     s_afd->channel_count,
+				     PJMEDIA_AFD_SPF(dn_afd),
 				     sport->options);
     }
 
-    frame->size = sport->base.info.bytes_per_frame;
+    frame->size = PJMEDIA_AFD_AVG_FSZ(s_afd);
     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
 
     return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 7a62dbe..f0bcf37 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -77,6 +77,7 @@
     pj_bool_t		    paused;	    /**< Paused?.		    */
     unsigned		    out_pkt_size;   /**< Size of output buffer.	    */
     void		   *out_pkt;	    /**< Output buffer.		    */
+    unsigned                out_pkt_len;    /**< Length of data in buffer.  */
     pjmedia_rtp_session	    rtp;	    /**< RTP session.		    */
 };
 
@@ -177,8 +178,6 @@
 					    /**< Normalized ts length per frame
 						 received according to 
 						 'erroneous' definition	    */
-    pj_uint32_t		     rtp_rx_last_ts;/**< Last received RTP timestamp
-						 for timestamp checking	    */
     unsigned		     rtp_rx_last_cnt;/**< Nb of frames in last pkt  */
     unsigned		     rtp_rx_check_cnt;
 					    /**< Counter of remote timestamp
@@ -208,6 +207,13 @@
     pj_oshandle_t	    trace_jb_fd;	    /**< Jitter tracing file handle.*/
     char		   *trace_jb_buf;	    /**< Jitter tracing buffer.	    */
 #endif
+
+    pj_uint32_t		     rtp_rx_last_ts;        /**< Last received RTP timestamp*/
+
+    pjmedia_vid_codec	    *vcodec;	            /**< Codec instance being used. */
+    pjmedia_vid_codec_info   vcodec_info;           /**< Codec param.		    */
+    pjmedia_vid_codec_param  vcodec_param;          /**< Codec param.		    */
+
 };
 
 
@@ -471,7 +477,7 @@
     /* Lock jitter buffer mutex first */
     pj_mutex_lock( stream->jb_mutex );
 
-    samples_required = stream->port.info.samples_per_frame;
+    samples_required = PJMEDIA_PIA_SPF(&stream->port.info);
     samples_per_frame = stream->codec_param.info.frm_ptime *
 			stream->codec_param.info.clock_rate *
 			stream->codec_param.info.channel_cnt / 
@@ -725,7 +731,7 @@
      * until we have enough frames according to codec's ptime.
      */
 
-    samples_required = stream->port.info.samples_per_frame;
+    samples_required = PJMEDIA_PIA_SPF(&stream->port.info);
     samples_per_frame = stream->codec_param.info.frm_ptime *
 			stream->codec_param.info.clock_rate *
 			stream->codec_param.info.channel_cnt / 
@@ -876,7 +882,7 @@
 	*first = 1;
     }
 
-    digit->duration += stream->port.info.samples_per_frame;
+    digit->duration += PJMEDIA_PIA_SPF(&stream->port.info);
 
     event->event = (pj_uint8_t)digit->event;
     event->e_vol = 10;
@@ -1087,7 +1093,7 @@
 
     /* How many samples are needed */
     count = stream->codec_param.info.enc_ptime * 
-		stream->port.info.clock_rate / 1000;
+	    PJMEDIA_PIA_SRATE(&stream->port.info) / 1000;
 
     /* See if we have enough samples */
     if (stream->enc_buf_count >= count) {
@@ -1110,7 +1116,7 @@
  * put_frame_imp()
  */
 static pj_status_t put_frame_imp( pjmedia_port *port, 
-				  const pjmedia_frame *frame )
+				  pjmedia_frame *frame )
 {
     pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
     pjmedia_channel *channel = stream->enc;
@@ -1151,8 +1157,8 @@
     if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO)
 	ts_len = (frame->size >> 1) / stream->codec_param.info.channel_cnt;
     else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED)
-	ts_len = stream->port.info.samples_per_frame / 
-		 stream->port.info.channel_count;
+	ts_len = PJMEDIA_PIA_SPF(&stream->port.info) /
+		 PJMEDIA_PIA_CCNT(&stream->port.info);
     else
 	ts_len = 0;
 
@@ -1219,7 +1225,7 @@
      */
     } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO &&
 	       frame->buf == NULL &&
-	       stream->port.info.format.id == PJMEDIA_FORMAT_L16 &&
+	       stream->port.info.fmt.id == PJMEDIA_FORMAT_L16 &&
 	       (stream->dir & PJMEDIA_DIR_ENCODING) &&
 	       stream->codec_param.info.frm_ptime *
 	       stream->codec_param.info.channel_cnt *
@@ -1362,7 +1368,7 @@
  * RTP packet, and transmit to peer.
  */
 static pj_status_t put_frame( pjmedia_port *port, 
-			      const pjmedia_frame *frame )
+			      pjmedia_frame *frame )
 {
     pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
     pjmedia_frame tmp_zero_frame;
@@ -1410,7 +1416,8 @@
      */
     if (stream->vad_enabled != stream->codec_param.setting.vad &&
 	(stream->tx_duration - stream->ts_vad_disabled) > 
-	stream->port.info.clock_rate * PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000)
+           PJMEDIA_PIA_SRATE(&stream->port.info) *
+	  PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000)
     {
 	stream->codec_param.setting.vad = stream->vad_enabled;
 	stream->codec->op->modify(stream->codec, &stream->codec_param);
@@ -1573,7 +1580,6 @@
     const void *payload;
     unsigned payloadlen;
     pjmedia_rtp_status seq_st;
-    pj_bool_t check_pt;
     pj_status_t status;
     pj_bool_t pkt_discarded = PJ_FALSE;
 
@@ -1603,15 +1609,8 @@
     /* Update RTP session (also checks if RTP session can accept
      * the incoming packet.
      */
-    check_pt = (hdr->pt != stream->rx_event_pt) && PJMEDIA_STREAM_CHECK_RTP_PT;
-    pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, check_pt);
-#if !PJMEDIA_STREAM_CHECK_RTP_PT
-    if (!check_pt && hdr->pt != channel->rtp.out_pt &&
-	hdr->pt != stream->rx_event_pt)
-    {
-	seq_st.status.flag.badpt = 1;
-    }
-#endif
+    pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st,
+			        hdr->pt != stream->rx_event_pt);
     if (seq_st.status.value) {
 	TRC_  ((stream->port.info.name.ptr, 
 		"RTP status: badpt=%d, badssrc=%d, dup=%d, "
@@ -1672,6 +1671,63 @@
 	status = pjmedia_jbuf_reset(stream->jb);
 	PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset"));
 
+    } else if (stream->vcodec) {
+        pj_timestamp ts;
+        pj_uint8_t *p;
+        pj_size_t p_len;
+
+        /* Video stream */
+
+        ts.u64 = pj_ntohl(hdr->ts);
+
+        /* Put any buffered bitstream if timestamp is changed,
+         * in case of RTP packet lost.
+         */
+        if (stream->rtp_rx_last_ts != ts.u64 && channel->out_pkt_len)
+        {
+            int seq;
+
+            seq = stream->rtp_rx_last_ts/stream->rtp_rx_ts_len_per_frame;
+            pjmedia_jbuf_put_frame(stream->jb, channel->out_pkt,
+                                   channel->out_pkt_len, seq);
+            channel->out_pkt_len = 0;
+        }
+
+        p = (pj_uint8_t*)channel->out_pkt + channel->out_pkt_len;
+        p_len = channel->out_pkt_size - channel->out_pkt_len;
+
+	/* Parse the payload. */
+	status = (*stream->vcodec->op->unpacketize)(
+                                            stream->vcodec,
+					    payload, payloadlen,
+                                            p, &p_len);
+	if (status != PJ_SUCCESS) {
+	    LOGERR_((stream->port.info.name.ptr, 
+		     "Codec parse() error", 
+		     status));
+            channel->out_pkt_len = 0;
+        } else {
+            channel->out_pkt_len += p_len;
+            if (channel->out_pkt_len > channel->out_pkt_size) {
+                channel->out_pkt_len = channel->out_pkt_size;
+                PJ_LOG(3, (THIS_FILE, "Video bitstream trucated because of"
+                           "not enough buffer"));
+            }
+
+            /* Check if RTP header specifies end of frame mark */
+            PJ_TODO(find_better_way_to_find_out_end_of_frame_mark);
+            if (hdr->m) {
+                int seq;
+
+                seq = (int)(ts.u64/stream->rtp_rx_ts_len_per_frame);
+                pjmedia_jbuf_put_frame(stream->jb, channel->out_pkt,
+                                       channel->out_pkt_len, seq);
+                channel->out_pkt_len = 0;
+            }
+        }
+
+        stream->rtp_rx_last_ts = (pj_uint32_t)ts.u64;
+        
     } else {
 	/*
 	 * Packets may contain more than one frames, while the jitter
@@ -1726,10 +1782,10 @@
 		     * are only 160 and 320, this validation is needed
 		     * to avoid wrong decision because of silence frames.
 		     */
-		    if (stream->codec_param.info.pt == PJMEDIA_RTP_PT_G722 &&
-			(peer_frm_ts_diff==stream->port.info.samples_per_frame
+		    if(stream->codec_param.info.pt == PJMEDIA_RTP_PT_G722 &&
+		       (peer_frm_ts_diff==PJMEDIA_PIA_SPF(&stream->port.info)
 			 || peer_frm_ts_diff == 
-				    stream->port.info.samples_per_frame >> 1))
+				    PJMEDIA_PIA_SPF(&stream->port.info) >>1))
 		    {
 			if (peer_frm_ts_diff < stream->rtp_rx_ts_len_per_frame)
 			    stream->rtp_rx_ts_len_per_frame = peer_frm_ts_diff;
@@ -1882,13 +1938,19 @@
     
     /* Allocate buffer for outgoing packet. */
 
-    channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) + 
-			    stream->codec_param.info.max_bps * 
-			    PJMEDIA_MAX_FRAME_DURATION_MS / 
-			    8 / 1000;
-
-    if (channel->out_pkt_size > PJMEDIA_MAX_MTU)
-	channel->out_pkt_size = PJMEDIA_MAX_MTU;
+    if (param->type == PJMEDIA_TYPE_VIDEO) {
+        channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) + 
+                                stream->frame_size;
+    } else if (param->type == PJMEDIA_TYPE_AUDIO) {
+        channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) + 
+			        stream->codec_param.info.max_bps * 
+			        PJMEDIA_MAX_FRAME_DURATION_MS / 
+			        8 / 1000;
+        if (channel->out_pkt_size > PJMEDIA_MAX_MTU)
+	    channel->out_pkt_size = PJMEDIA_MAX_MTU;
+    } else {
+        return PJ_ENOTSUP;
+    }
 
     /* It should big enough to hold (minimally) RTCP SR with an SDES. */
     min_out_pkt_size =  sizeof(pjmedia_rtcp_sr_pkt) +
@@ -1927,6 +1989,13 @@
 }
 
 
+static pj_status_t video_stream_create(pjmedia_endpt *endpt,
+				       pj_pool_t *pool,
+				       const pjmedia_stream_info *info,
+				       pjmedia_transport *tp,
+				       void *user_data,
+				       pjmedia_stream **p_stream);
+
 /*
  * Create media stream.
  */
@@ -1942,12 +2011,23 @@
     pjmedia_stream *stream;
     pj_str_t name;
     unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+    pjmedia_audio_format_detail *afd;
     char *p;
     pj_status_t status;
 
     PJ_ASSERT_RETURN(pool && info && p_stream, PJ_EINVAL);
 
 
+    if (info->type == PJMEDIA_TYPE_VIDEO) {
+        status = video_stream_create(endpt, pool, info, tp, 
+                                     user_data, &stream);
+        if (status != PJ_SUCCESS)
+            goto err_cleanup;
+
+        *p_stream = stream;
+        return PJ_SUCCESS;
+    }
+
     /* Allocate the media stream: */
 
     stream = PJ_POOL_ZALLOC_T(pool, pjmedia_stream);
@@ -1964,12 +2044,14 @@
 			   PJMEDIA_PORT_SIGNATURE('S', 'T', 'R', 'M'),
 			   info->fmt.clock_rate, info->fmt.channel_cnt,
 			   16, 80);
+    afd = pjmedia_format_get_audio_format_detail(&stream->port.info.fmt, 1);
 
     /* Init port. */
 
-    pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
-    stream->port.info.clock_rate = info->fmt.clock_rate;
-    stream->port.info.channel_count = info->fmt.channel_cnt;
+    //No longer there in 2.0
+    //pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
+    afd->clock_rate = info->fmt.clock_rate;
+    afd->channel_count = info->fmt.channel_cnt;
     stream->port.port_data.pdata = stream;
 
     /* Init stream: */
@@ -2040,33 +2122,33 @@
 	goto err_cleanup;
 
     /* Set additional info and callbacks. */
-    stream->port.info.bits_per_sample = 16;
-    stream->port.info.samples_per_frame = info->fmt.clock_rate * 
-					  stream->codec_param.info.channel_cnt *
-					  stream->codec_param.info.frm_ptime *
-					  stream->codec_param.setting.frm_per_pkt /
-					  1000;
-    stream->port.info.format.id = stream->codec_param.info.fmt_id;
+    afd->bits_per_sample = 16;
+    afd->frame_time_usec = stream->codec_param.info.frm_ptime *
+		           stream->codec_param.setting.frm_per_pkt * 1000;
+    stream->port.info.fmt.id = stream->codec_param.info.fmt_id;
     if (stream->codec_param.info.fmt_id == PJMEDIA_FORMAT_L16) {
 	/* Raw format */
-	stream->port.info.bytes_per_frame = stream->port.info.samples_per_frame *
-					    stream->port.info.bits_per_sample / 8;
+	afd->avg_bps = afd->max_bps = afd->clock_rate *
+				      afd->bits_per_sample / 8;
+
 
 	stream->port.put_frame = &put_frame;
 	stream->port.get_frame = &get_frame;
     } else {
 	/* Encoded format */
-	stream->port.info.bytes_per_frame = stream->codec_param.info.max_bps * 
-					    stream->codec_param.info.frm_ptime *
-					    stream->codec_param.setting.frm_per_pkt /
-					    8 / 1000;
-	if ((stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime *
-	    stream->codec_param.setting.frm_per_pkt) % 8000 != 0)
+	afd->avg_bps = stream->codec_param.info.avg_bps;
+	afd->max_bps = stream->codec_param.info.max_bps;
+
+	/* Not applicable for 2.0
+	if ((stream->codec_param.info.max_bps *
+	     stream->codec_param.info.frm_ptime *
+	     stream->codec_param.setting.frm_per_pkt) % 8000 != 0)
 	{
 	    ++stream->port.info.bytes_per_frame;
 	}
 	stream->port.info.format.bitrate = stream->codec_param.info.avg_bps;
 	stream->port.info.format.vad = (stream->codec_param.setting.vad != 0);
+	*/
 
 	stream->port.put_frame = &put_frame;
 	stream->port.get_frame = &get_frame_ext;
@@ -2083,14 +2165,13 @@
 
 	stream->enc_samples_per_pkt = stream->codec_param.info.enc_ptime *
 				      stream->codec_param.info.channel_cnt *
-				      stream->port.info.clock_rate / 1000;
+				      afd->clock_rate / 1000;
 
 	/* Set buffer size as twice the largest ptime value between
 	 * stream's ptime, encoder ptime, or decoder ptime.
 	 */
 
-	ptime = stream->port.info.samples_per_frame * 1000 /
-		stream->port.info.clock_rate;
+	ptime = afd->frame_time_usec / 1000;
 
 	if (stream->codec_param.info.enc_ptime > ptime)
 	    ptime = stream->codec_param.info.enc_ptime;
@@ -2101,12 +2182,12 @@
 	ptime <<= 1;
 
 	/* Allocate buffer */
-	stream->enc_buf_size = stream->port.info.clock_rate * ptime / 1000;
+	stream->enc_buf_size = afd->clock_rate * ptime / 1000;
 	stream->enc_buf = (pj_int16_t*)
 			  pj_pool_alloc(pool, stream->enc_buf_size * 2);
 
     } else {
-	stream->enc_samples_per_pkt = stream->port.info.samples_per_frame;
+	stream->enc_samples_per_pkt = PJMEDIA_AFD_SPF(afd);
     }
 
 
@@ -2139,7 +2220,7 @@
     stream->rtp_rx_last_cnt = 0;
     stream->rtp_tx_ts_len_per_pkt = stream->enc_samples_per_pkt /
 				     stream->codec_param.info.channel_cnt;
-    stream->rtp_rx_ts_len_per_frame = stream->port.info.samples_per_frame / 
+    stream->rtp_rx_ts_len_per_frame = PJMEDIA_AFD_SPF(afd) /
 				       stream->codec_param.info.channel_cnt;
 
     if (info->fmt.pt == PJMEDIA_RTP_PT_G722) {
@@ -2212,7 +2293,7 @@
 	rtcp_setting.ssrc = info->ssrc;
 	rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts);
 	rtcp_setting.clock_rate = info->fmt.clock_rate;
-	rtcp_setting.samples_per_frame = stream->port.info.samples_per_frame;
+	rtcp_setting.samples_per_frame = PJMEDIA_AFD_SPF(afd);
 
 #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0)
 	/* Special case for G.722 */
@@ -2423,6 +2504,12 @@
 	stream->codec = NULL;
     }
 
+    if (stream->vcodec) {
+	stream->vcodec->op->close(stream->vcodec);
+	pjmedia_vid_codec_mgr_dealloc_codec(NULL, stream->vcodec);
+	stream->vcodec = NULL;
+    }
+
     /* Free mutex */
     
     if (stream->jb_mutex) {
@@ -2738,3 +2825,525 @@
     return PJ_SUCCESS;
 }
 
+static pj_status_t put_vid_frame(pjmedia_port *port,
+                                 pjmedia_frame *frame)
+{
+    pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
+    pjmedia_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 * stream->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) {
+	stream->enc_buf_pos = stream->enc_buf_count = 0;
+	return PJ_SUCCESS;
+    }
+
+    /* Increment transmit duration */
+    rtp_ts_len = stream->rtp_tx_ts_len_per_pkt;
+    stream->tx_duration += rtp_ts_len;
+
+    /* Init frame_out buffer. */
+    frame_out.buf = ((char*)channel->out_pkt) + sizeof(pjmedia_rtp_hdr);
+    frame_out.size = 0;
+
+    /* Encode! */
+    status = (*stream->vcodec->op->encode)(stream->vcodec, frame, 
+				           channel->out_pkt_size - 
+				           sizeof(pjmedia_rtp_hdr),
+				           &frame_out);
+    if (status != PJ_SUCCESS) {
+        LOGERR_((stream->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->vcodec->op->packetize)(
+                                           stream->vcodec,
+                                           (pj_uint8_t*)frame_out.buf,
+                                           frame_out.size,
+                                           &processed,
+                                           &payload, &payload_len);
+        if (status != PJ_SUCCESS) {
+            LOGERR_((stream->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_((stream->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) {
+	if (stream->is_streaming) {
+	    PJ_LOG(5,(stream->port.info.name.ptr,"Starting silence"));
+	    stream->is_streaming = PJ_FALSE;
+	}
+
+	return PJ_SUCCESS;
+    }
+
+
+    /* Set RTP marker bit if currently not streaming */
+    if (stream->is_streaming == PJ_FALSE) {
+        // RTP header M in video packet is usually for end of frame flag.
+	//pjmedia_rtp_hdr *rtp = (pjmedia_rtp_hdr*) channel->out_pkt;
+	//rtp->m = 1;
+	PJ_LOG(5,(stream->port.info.name.ptr,"Start talksprut.."));
+    }
+
+    stream->is_streaming = PJ_TRUE;
+
+    /* 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_vid_frame(pjmedia_port *port,
+                                 pjmedia_frame *frame)
+{
+    pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
+    pjmedia_channel *channel = stream->dec;
+    pj_uint8_t *p_out_samp;
+    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 frame from the jitter buffer and decode the frame
+     * until we have enough frames according to codec's ptime.
+     */
+
+    /* Lock jitter buffer mutex first */
+    pj_mutex_lock( stream->jb_mutex );
+
+    p_out_samp = (pj_uint8_t*) frame->buf;
+    {
+	char frame_type;
+	pj_size_t frame_size;
+	pj_uint32_t bit_info;
+
+	/* Get frame from jitter buffer. */
+	pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, &frame_size,
+			        &frame_type, &bit_info);
+
+#if TRACE_JB
+	trace_jb_get(stream, frame_type, frame_size);
+#endif
+
+	if (frame_type != PJMEDIA_JB_NORMAL_FRAME) {
+            const char *with_plc = "";
+	    
+	    /* Activate PLC */
+	    if (stream->vcodec->op->recover && 
+		//stream->codec_param.setting.plc &&
+		stream->plc_cnt < stream->max_plc_cnt) 
+	    {
+		status = (*stream->codec->op->recover)(stream->codec,
+						       frame->size,
+						       frame);
+
+		++stream->plc_cnt;
+                with_plc = ", plc invoked";
+	    } else {
+		status = -1;
+	    }
+
+	    if (status != PJ_SUCCESS) {
+		/* Either PLC failed or PLC not supported/enabled */
+		//pjmedia_zero_samples(p_out_samp + samples_count,
+		//		     samples_required - samples_count);
+                frame->size = 0;
+	    }
+
+	    if (frame_type != stream->jb_last_frm) {
+                if (frame_type == PJMEDIA_JB_MISSING_FRAME) {
+		    pjmedia_jb_state jb_state;
+
+		    /* Report changing frame type event */
+		    pjmedia_jbuf_get_state(stream->jb, &jb_state);
+		    PJ_LOG(5,(stream->port.info.name.ptr, 
+			      "Jitter buffer empty (prefetch=%d)%s", 
+			      jb_state.prefetch, with_plc));
+                } else {
+		    /* Report changing frame type event */
+		    PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost%s!",
+		              (status == PJ_SUCCESS? ", recovered":"")));
+                }
+
+		stream->jb_last_frm = frame_type;
+		stream->jb_last_frm_cnt = 1;
+	    } else {
+		stream->jb_last_frm_cnt++;
+	    }
+
+	} else {
+	    /* Got "NORMAL" frame from jitter buffer */
+	    pjmedia_frame frame_in, frame_out;
+
+	    stream->plc_cnt = 0;
+
+	    /* Decode */
+	    frame_in.buf = channel->out_pkt;
+	    frame_in.size = frame_size;
+	    frame_in.bit_info = bit_info;
+	    frame_in.type = PJMEDIA_FRAME_TYPE_VIDEO;  /* ignored */
+
+	    frame_out.buf = p_out_samp;
+	    frame_out.size = frame->size;
+	    status = stream->vcodec->op->decode(stream->vcodec, &frame_in,
+						frame_out.size, &frame_out);
+	    if (status != 0) {
+		LOGERR_((port->info.name.ptr, "codec decode() error", 
+			 status));
+
+		//pjmedia_zero_samples(p_out_samp + samples_count, 
+		//		     samples_per_frame);
+                frame_out.size = 0;
+	    }
+
+	    if (stream->jb_last_frm != frame_type) {
+		/* Report changing frame type event */
+		PJ_LOG(5,(stream->port.info.name.ptr, 
+			  "Jitter buffer starts returning normal frames "
+			  "(after %d empty/lost)",
+			  stream->jb_last_frm_cnt, stream->jb_last_frm));
+
+		stream->jb_last_frm = frame_type;
+		stream->jb_last_frm_cnt = 1;
+	    } else {
+		stream->jb_last_frm_cnt++;
+	    }
+
+            frame->size = frame_out.size;
+	}
+    }
+
+
+    /* Unlock jitter buffer mutex. */
+    pj_mutex_unlock( stream->jb_mutex );
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t video_stream_create(pjmedia_endpt *endpt,
+				       pj_pool_t *pool,
+				       const pjmedia_stream_info *info,
+				       pjmedia_transport *tp,
+				       void *user_data,
+				       pjmedia_stream **p_stream)
+{
+    enum { M = 32 };
+    pjmedia_stream *stream;
+    pj_str_t name;
+    unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+    pjmedia_video_format_detail *vfd_enc;
+    char *p;
+    pj_status_t status;
+
+    stream = PJ_POOL_ZALLOC_T(pool, pjmedia_stream);
+    PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
+    PJ_ASSERT_RETURN(pjmedia_vid_codec_mgr_instance(), PJMEDIA_CODEC_EFAILED);
+
+    /* Init stream/port name */
+    name.ptr = (char*) pj_pool_alloc(pool, M);
+    name.slen = pj_ansi_snprintf(name.ptr, M, "vstrm%p", stream);
+
+    /* Create and initialize codec: */
+    stream->vcodec_info = info->vid_codec_info;
+    status = pjmedia_vid_codec_mgr_alloc_codec(NULL, &info->vid_codec_info,
+					       &stream->vcodec);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Get codec param: */
+    if (info->vid_codec_param)
+	stream->vcodec_param = *info->vid_codec_param;
+    else {
+	status = pjmedia_vid_codec_mgr_get_default_param(NULL, 
+						         &info->vid_codec_info,
+						         &stream->vcodec_param);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    vfd_enc = pjmedia_format_get_video_format_detail(
+                                            &stream->vcodec_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->vid_codec_info.clock_rate / 1000;
+
+    stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME;
+    stream->tx_event_pt = -1;
+    stream->rx_event_pt = -1;
+
+#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;
+
+    /* Check for invalid max_bps. */
+ //   if (stream->codec_param.info.max_bps < stream->codec_param.info.avg_bps)
+	//stream->codec_param.info.max_bps = stream->codec_param.info.avg_bps;
+
+    /* Check for invalid frame per packet. */
+ //   if (stream->codec_param.setting.frm_per_pkt < 1)
+	//stream->codec_param.setting.frm_per_pkt = 1;
+
+    /* Open the codec. */
+    stream->vcodec_param.dir = info->dir;
+    stream->vcodec_param.enc_mtu = PJMEDIA_MAX_MTU - 40;
+    status = stream->vcodec->op->init(stream->vcodec, pool);
+    if (status != PJ_SUCCESS)
+	return status;
+    status = stream->vcodec->op->open(stream->vcodec, &stream->vcodec_param);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Set additional info and callbacks. */
+    stream->port.put_frame = &put_vid_frame;
+    stream->port.get_frame = &get_vid_frame;
+
+    /* Get the frame size */
+    //stream->frame_size = vfd_enc->max_bps *
+    //                     vfd_enc->fps.denum / vfd_enc->fps.num;
+    stream->frame_size = 128000;
+
+    /* How many consecutive PLC frames can be generated */
+    stream->max_plc_cnt = MAX_PLC_MSEC *
+                          vfd_enc->fps.num / vfd_enc->fps.denum / 1000;
+
+    stream->rtp_rx_check_cnt = 5;
+    stream->rtp_rx_last_ts = 0;
+    stream->rtp_rx_last_cnt = 0;
+    stream->rtp_tx_ts_len_per_pkt = 
+                            info->vid_codec_info.clock_rate *
+                            vfd_enc->fps.denum / vfd_enc->fps.num;
+    stream->rtp_rx_ts_len_per_frame = stream->rtp_tx_ts_len_per_pkt;
+
+    /* Init jitter buffer parameters: */
+    jb_max     = info->jb_max *
+                 vfd_enc->fps.num / vfd_enc->fps.denum / 1000;
+    jb_min_pre = info->jb_min_pre *
+                 vfd_enc->fps.num / vfd_enc->fps.denum / 1000;
+    jb_max_pre = info->jb_max_pre *
+                 vfd_enc->fps.num / vfd_enc->fps.denum / 1000;
+    jb_init    = info->jb_init *
+                 vfd_enc->fps.num / vfd_enc->fps.denum / 1000;
+
+    /* When JB max frame count==0, set to 0.5s */
+    if (jb_max == 0)
+        jb_max = (vfd_enc->fps.num + vfd_enc->fps.denum/2) /
+                  vfd_enc->fps.denum / 2;
+    
+    /* When JB max prefetch==0, set to (4/5 * jb_max) */
+    if (jb_max_pre == 0)
+        jb_max_pre = jb_max * 4 / 5;
+
+    /* Create jitter buffer */
+    status = pjmedia_jbuf_create(pool, &stream->port.info.name,
+				 stream->frame_size, 
+				 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);
+
+    /* Create decoder channel: */
+
+    status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, 
+			     stream->vcodec_param.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 RTCP session: */
+
+    {
+	pjmedia_rtcp_session_setting rtcp_setting;
+
+	pjmedia_rtcp_session_setting_default(&rtcp_setting);
+	rtcp_setting.name = stream->port.info.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->vid_codec_info.clock_rate;
+	rtcp_setting.samples_per_frame = stream->rtp_tx_ts_len_per_pkt;
+
+	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->out_pkt, 
+			   stream->enc->out_pkt_size);
+    if (len != 0) {
+	pjmedia_transport_send_rtcp(stream->transport, 
+				    stream->enc->out_pkt, 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",
+			 stream->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
+
+    /* Init some port-info. Some parts of the info will be set later
+     * once we have more info about the codec.
+     */
+    pjmedia_port_info_init2(&stream->port.info, &name,
+			    PJMEDIA_PORT_SIGNATURE('S', 'T', 'R', 'M'),
+                            info->dir, &stream->vcodec_param.dec_fmt);
+
+    /* Init port. */
+    stream->port.port_data.pdata = stream;
+
+    /* Success! */
+    *p_stream = stream;
+
+    PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->port.info.name.ptr));
+
+    return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c
index 2a54505..17cc993 100644
--- a/pjmedia/src/pjmedia/tonegen.c
+++ b/pjmedia/src/pjmedia/tonegen.c
@@ -558,7 +558,7 @@
 {
     struct tonegen *tonegen = (struct tonegen*) port;
     short *dst, *end;
-    unsigned clock_rate = tonegen->base.info.clock_rate;
+    unsigned clock_rate = PJMEDIA_PIA_SRATE(&tonegen->base.info);
 
     PJ_ASSERT_RETURN(port->info.signature == SIGNATURE, PJ_EINVAL);
 
@@ -622,7 +622,7 @@
     }
     
     dst = (short*) frame->buf;
-    end = dst + port->info.samples_per_frame;
+    end = dst + PJMEDIA_PIA_SPF(&port->info);
 
     while (dst < end) {
 	pjmedia_tone_desc *dig = &tonegen->digits[tonegen->cur_digit];
@@ -636,7 +636,8 @@
 	if (tonegen->dig_samples == 0 && 
 	    (tonegen->count!=1 || !(dig->flags & PJMEDIA_TONE_INITIALIZED)))
 	{
-	    init_generate_tone(&tonegen->state, port->info.clock_rate,
+	    init_generate_tone(&tonegen->state,
+		               PJMEDIA_PIA_SRATE(&port->info),
 			       dig->freq1, dig->freq2, dig->volume);
 	    dig->flags |= PJMEDIA_TONE_INITIALIZED;
 	    if (tonegen->cur_digit > 0) {
@@ -651,7 +652,8 @@
 	    cnt = on_samp - tonegen->dig_samples;
 	    if (cnt > required)
 		cnt = required;
-	    generate_tone(&tonegen->state, port->info.channel_count,
+	    generate_tone(&tonegen->state,
+			  PJMEDIA_PIA_CCNT(&port->info),
 			  cnt, dst);
 
 	    dst += cnt;
@@ -729,7 +731,7 @@
 	pjmedia_zero_samples(dst, end-dst);
 
     frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
-    frame->size = port->info.bytes_per_frame;
+    frame->size = PJMEDIA_PIA_AVG_FSZ(&port->info);
 
     TRACE_((THIS_FILE, "tonegen_get_frame(): frame created, level=%u",
 	    pjmedia_calc_avg_signal((pj_int16_t*)frame->buf, frame->size/2)));
diff --git a/pjmedia/src/pjmedia/types.c b/pjmedia/src/pjmedia/types.c
new file mode 100644
index 0000000..09219e3
--- /dev/null
+++ b/pjmedia/src/pjmedia/types.c
@@ -0,0 +1,24 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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/frame.h>
+#include <pjmedia/port.h>
+#include <pjmedia/types.h>
+#include <pj/pool.h>
+
diff --git a/pjmedia/src/pjmedia/vid_codec.c b/pjmedia/src/pjmedia/vid_codec.c
new file mode 100644
index 0000000..9eaca92
--- /dev/null
+++ b/pjmedia/src/pjmedia/vid_codec.c
@@ -0,0 +1,645 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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_codec.h>
+#include <pjmedia/errno.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/string.h>
+
+#define THIS_FILE   "vid_codec.c"
+
+static pjmedia_vid_codec_mgr *def_vid_codec_mgr;
+
+/* Definition of default codecs parameters */
+struct pjmedia_vid_codec_default_param
+{
+    pjmedia_vid_codec_param	*param;
+};
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_vid_codec_mgr *mgr);
+
+
+/*
+ * Initialize codec manager.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_create(
+                                            pj_pool_t *pool,
+                                            pjmedia_vid_codec_mgr **p_mgr)
+{
+    pjmedia_vid_codec_mgr *mgr;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool, PJ_EINVAL);
+
+    mgr = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_mgr);
+    pj_list_init (&mgr->factory_list);
+    mgr->codec_cnt = 0;
+
+    /* Create mutex */
+    status = pj_mutex_create_recursive(pool, "vid-codec-mgr", &mgr->mutex);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (!def_vid_codec_mgr)
+        def_vid_codec_mgr = mgr;
+
+    if (p_mgr)
+        *p_mgr = mgr;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Initialize codec manager.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_destroy (pjmedia_vid_codec_mgr *mgr)
+{
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    /* Destroy mutex */
+    if (mgr->mutex)
+	pj_mutex_destroy(mgr->mutex);
+
+    /* Just for safety, set codec manager states to zero */
+    pj_bzero(mgr, sizeof(pjmedia_vid_codec_mgr));
+
+    if (mgr == def_vid_codec_mgr)
+        def_vid_codec_mgr = NULL;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pjmedia_vid_codec_mgr*) pjmedia_vid_codec_mgr_instance(void)
+{
+    pj_assert(def_vid_codec_mgr);
+    return def_vid_codec_mgr;
+}
+
+PJ_DEF(void) pjmedia_vid_codec_mgr_set_instance(pjmedia_vid_codec_mgr* mgr)
+{
+    def_vid_codec_mgr = mgr;
+}
+
+
+/*
+ * Register a codec factory.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_register_factory(
+                                    pjmedia_vid_codec_mgr *mgr,
+				    pjmedia_vid_codec_factory *factory)
+{
+    pjmedia_vid_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS];
+    unsigned i, count;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(factory, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    /* Enum codecs */
+    count = PJ_ARRAY_SIZE(info);
+    status = factory->op->enum_info(factory, &count, info);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* Check codec count */
+    if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_ETOOMANY;
+    }
+
+
+    /* Save the codecs */
+    for (i=0; i<count; ++i) {
+	pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i],
+		   &info[i], sizeof(pjmedia_vid_codec_info));
+	mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL;
+	mgr->codec_desc[mgr->codec_cnt+i].factory = factory;
+	pjmedia_vid_codec_info_to_id( &info[i],
+				  mgr->codec_desc[mgr->codec_cnt+i].id,
+				  sizeof(pjmedia_codec_id));
+    }
+
+    /* Update count */
+    mgr->codec_cnt += count;
+
+    /* Re-sort codec based on priorities */
+    sort_codecs(mgr);
+
+    /* Add factory to the list */
+    pj_list_push_back(&mgr->factory_list, factory);
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Unregister a codec factory.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_unregister_factory(
+				pjmedia_vid_codec_mgr *mgr, 
+				pjmedia_vid_codec_factory *factory)
+{
+    unsigned i;
+    PJ_ASSERT_RETURN(factory, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* Factory must be registered. */
+    if (pj_list_find_node(&mgr->factory_list, factory) != factory) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_ENOTFOUND;
+    }
+
+    /* Erase factory from the factory list */
+    pj_list_erase(factory);
+
+
+    /* Remove all supported codecs from the codec manager that were created 
+     * by the specified factory.
+     */
+    for (i=0; i<mgr->codec_cnt; ) {
+
+	if (mgr->codec_desc[i].factory == factory) {
+	    /* Remove the codec from array of codec descriptions */
+	    pj_array_erase(mgr->codec_desc, sizeof(mgr->codec_desc[0]), 
+			   mgr->codec_cnt, i);
+	    --mgr->codec_cnt;
+
+	} else {
+	    ++i;
+	}
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Enum all codecs.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_enum_codecs(
+                                pjmedia_vid_codec_mgr *mgr, 
+			        unsigned *count, 
+			        pjmedia_vid_codec_info codecs[],
+			        unsigned *prio)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(count && codecs, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    if (*count > mgr->codec_cnt)
+	*count = mgr->codec_cnt;
+    
+    for (i=0; i<*count; ++i) {
+	pj_memcpy(&codecs[i], 
+		  &mgr->codec_desc[i].info, 
+		  sizeof(pjmedia_vid_codec_info));
+    }
+
+    if (prio) {
+	for (i=0; i < *count; ++i)
+	    prio[i] = mgr->codec_desc[i].prio;
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get codec info for static payload type.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info(
+                                    pjmedia_vid_codec_mgr *mgr,
+				    unsigned pt,
+				    const pjmedia_vid_codec_info **p_info)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(p_info && pt>=0 && pt < 96, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	if (mgr->codec_desc[i].info.pt == pt) {
+	    *p_info = &mgr->codec_desc[i].info;
+
+	    pj_mutex_unlock(mgr->mutex);
+	    return PJ_SUCCESS;
+	}
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Convert codec info struct into a unique codec identifier.
+ * A codec identifier looks something like "L16/44100/2".
+ */
+PJ_DEF(char*) pjmedia_vid_codec_info_to_id(
+                                        const pjmedia_vid_codec_info *info,
+				        char *id, unsigned max_len )
+{
+    int len;
+
+    PJ_ASSERT_RETURN(info && id && max_len, NULL);
+
+    len = pj_ansi_snprintf(id, max_len, "%.*s/%u",
+			   (int)info->encoding_name.slen,
+			   info->encoding_name.ptr,
+			   info->clock_rate);
+
+    if (len < 1 || len >= (int)max_len) {
+	id[0] = '\0';
+	return NULL;
+    }
+
+    return id;
+}
+
+
+/*
+ * 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",
+ * and so on, up to the maximum count specified in the argument.
+ */
+PJ_DEF(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[])
+{
+    unsigned i, found = 0;
+
+    PJ_ASSERT_RETURN(codec_id && count && *count, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    for (i=0; i<mgr->codec_cnt; ++i) {
+
+	if (codec_id->slen == 0 ||
+	    pj_strnicmp2(codec_id, mgr->codec_desc[i].id, 
+			 codec_id->slen) == 0) 
+	{
+
+	    if (p_info)
+		p_info[found] = &mgr->codec_desc[i].info;
+	    if (prio)
+		prio[found] = mgr->codec_desc[i].prio;
+
+	    ++found;
+
+	    if (found >= *count)
+		break;
+	}
+
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    *count = found;
+
+    return found ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+
+/* Swap two codecs positions in codec manager */
+static void swap_codec(pjmedia_vid_codec_mgr *mgr, unsigned i, unsigned j)
+{
+    pjmedia_vid_codec_desc tmp;
+
+    pj_memcpy(&tmp, &mgr->codec_desc[i], sizeof(pjmedia_vid_codec_desc));
+
+    pj_memcpy(&mgr->codec_desc[i], &mgr->codec_desc[j], 
+	       sizeof(pjmedia_vid_codec_desc));
+
+    pj_memcpy(&mgr->codec_desc[j], &tmp, sizeof(pjmedia_vid_codec_desc));
+}
+
+
+/* Sort codecs in codec manager based on priorities */
+static void sort_codecs(pjmedia_vid_codec_mgr *mgr)
+{
+    unsigned i;
+
+   /* Re-sort */
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	unsigned j, max;
+
+	for (max=i, j=i+1; j<mgr->codec_cnt; ++j) {
+	    if (mgr->codec_desc[j].prio > mgr->codec_desc[max].prio)
+		max = j;
+	}
+
+	if (max != i)
+	    swap_codec(mgr, i, max);
+    }
+
+    /* Change PJMEDIA_CODEC_PRIO_HIGHEST codecs to NEXT_HIGHER */
+    for (i=0; i<mgr->codec_cnt; ++i) {
+	if (mgr->codec_desc[i].prio == PJMEDIA_CODEC_PRIO_HIGHEST)
+	    mgr->codec_desc[i].prio = PJMEDIA_CODEC_PRIO_NEXT_HIGHER;
+	else
+	    break;
+    }
+}
+
+
+/**
+ * Set codec priority. The codec priority determines the order of
+ * the codec in the SDP created by the endpoint. If more than one codecs
+ * are found with the same codec_id prefix, then the function sets the
+ * priorities of all those codecs.
+ */
+PJ_DEF(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)
+{
+    unsigned i, found = 0;
+
+    PJ_ASSERT_RETURN(codec_id, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* Update the priorities of affected codecs */
+    for (i=0; i<mgr->codec_cnt; ++i) 
+    {
+	if (codec_id->slen == 0 ||
+	    pj_strnicmp2(codec_id, mgr->codec_desc[i].id, 
+			 codec_id->slen) == 0) 
+	{
+	    mgr->codec_desc[i].prio = (pjmedia_codec_priority) prio;
+	    ++found;
+	}
+    }
+
+    if (!found) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_ENOTFOUND;
+    }
+
+    /* Re-sort codecs */
+    sort_codecs(mgr);
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Allocate one codec.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_alloc_codec(
+                                        pjmedia_vid_codec_mgr *mgr, 
+					const pjmedia_vid_codec_info *info,
+					pjmedia_vid_codec **p_codec)
+{
+    pjmedia_vid_codec_factory *factory;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(info && p_codec, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    *p_codec = NULL;
+
+    pj_mutex_lock(mgr->mutex);
+
+    factory = mgr->factory_list.next;
+    while (factory != &mgr->factory_list) {
+
+	if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) {
+
+	    status = (*factory->op->alloc_codec)(factory, info, p_codec);
+	    if (status == PJ_SUCCESS) {
+		pj_mutex_unlock(mgr->mutex);
+		return PJ_SUCCESS;
+	    }
+
+	}
+
+	factory = factory->next;
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Get default codec parameter.
+ */
+PJ_DEF(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_factory *factory;
+    pj_status_t status;
+    pjmedia_codec_id codec_id;
+    pjmedia_vid_codec_desc *codec_desc = NULL;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(info && param, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, 
+                                      sizeof(codec_id)))
+	return PJ_EINVAL;
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* First, lookup default param in codec desc */
+    for (i=0; i < mgr->codec_cnt; ++i) {
+	if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) {
+	    codec_desc = &mgr->codec_desc[i];
+	    break;
+	}
+    }
+
+    /* If we found the codec and its default param is set, return it */
+    if (codec_desc && codec_desc->def_param) {
+	pj_memcpy(param, codec_desc->def_param, 
+		  sizeof(pjmedia_vid_codec_param));
+
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_SUCCESS;
+    }
+
+    /* Otherwise query the default param from codec factory */
+    factory = mgr->factory_list.next;
+    while (factory != &mgr->factory_list) {
+
+	if ( (*factory->op->test_alloc)(factory, info) == PJ_SUCCESS ) {
+
+	    status = (*factory->op->default_attr)(factory, info, param);
+	    if (status == PJ_SUCCESS) {
+		/* Check for invalid max_bps. */
+		//if (param->info.max_bps < param->info.avg_bps)
+		//    param->info.max_bps = param->info.avg_bps;
+
+		pj_mutex_unlock(mgr->mutex);
+		return PJ_SUCCESS;
+	    }
+
+	}
+
+	factory = factory->next;
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+
+/*
+ * Set default codec parameter.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_set_default_param( 
+					    pjmedia_vid_codec_mgr *mgr,
+                                            pj_pool_t *pool,
+					    const pjmedia_vid_codec_info *info,
+					    const pjmedia_vid_codec_param *param )
+{
+    unsigned i;
+    pjmedia_codec_id codec_id;
+    pjmedia_vid_codec_desc *codec_desc = NULL;
+    pjmedia_vid_codec_param *p;
+
+    PJ_ASSERT_RETURN(info, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    if (!pjmedia_vid_codec_info_to_id(info, (char*)&codec_id, sizeof(codec_id)))
+	return PJ_EINVAL;
+
+    pj_mutex_lock(mgr->mutex);
+
+    /* Lookup codec desc */
+    for (i=0; i < mgr->codec_cnt; ++i) {
+	if (pj_ansi_stricmp(codec_id, mgr->codec_desc[i].id) == 0) {
+	    codec_desc = &mgr->codec_desc[i];
+	    break;
+	}
+    }
+
+    /* Codec not found */
+    if (!codec_desc) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJMEDIA_CODEC_EUNSUP;
+    }
+
+    /* If codec param is previously set */
+    if (codec_desc->def_param) {
+	codec_desc->def_param = NULL;
+    }
+
+    /* When param is set to NULL, i.e: setting default codec param to library
+     * default setting, just return PJ_SUCCESS.
+     */
+    if (NULL == param) {
+	pj_mutex_unlock(mgr->mutex);
+	return PJ_SUCCESS;
+    }
+
+    p = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec_param);
+    p = codec_desc->def_param;
+
+    /* Update codec param */
+    pj_memcpy(p, param, sizeof(pjmedia_vid_codec_param));
+    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->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);
+    }
+
+    pj_mutex_unlock(mgr->mutex);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Dealloc codec.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_vid_codec_mgr_dealloc_codec(pjmedia_vid_codec_mgr *mgr,
+				    pjmedia_vid_codec *codec)
+{
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+    if (!mgr) mgr = def_vid_codec_mgr;
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    return (*codec->factory->op->dealloc_codec)(codec->factory, codec);
+}
+
diff --git a/pjmedia/src/pjmedia/videoport.c b/pjmedia/src/pjmedia/videoport.c
new file mode 100644
index 0000000..0d10938
--- /dev/null
+++ b/pjmedia/src/pjmedia/videoport.c
@@ -0,0 +1,549 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 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/videoport.h>
+#include <pjmedia/clock.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+#define THIS_FILE	"videoport.c"
+
+typedef struct vid_pasv_port vid_pasv_port;
+
+enum role
+{
+    ROLE_NONE,
+    ROLE_ACTIVE,
+    ROLE_PASSIVE
+};
+
+struct pjmedia_vid_port
+{
+    pj_str_t		 dev_name;
+    pjmedia_dir		 dir;
+    pjmedia_rect_size	 cap_size;
+    pjmedia_vid_stream	*strm;
+    pjmedia_vid_cb       strm_cb;
+    void                *strm_cb_data;
+    enum role		 role,
+			 stream_role;
+    vid_pasv_port	*pasv_port;
+    pjmedia_port	*client_port;
+    pj_bool_t		 destroy_client_port;
+
+    pjmedia_clock	*enc_clock,
+		        *dec_clock;
+
+    pjmedia_frame	*enc_frm_buf,
+		        *dec_frm_buf;
+
+    pj_mutex_t		*enc_frm_mutex,
+			*dec_frm_mutex;
+};
+
+struct vid_pasv_port
+{
+    pjmedia_port	 base;
+    pjmedia_vid_port	*vp;
+};
+
+static pj_status_t vidstream_cap_cb(pjmedia_vid_stream *stream,
+				    void *user_data,
+				    pjmedia_frame *frame);
+static pj_status_t vidstream_render_cb(pjmedia_vid_stream *stream,
+				       void *user_data,
+				       pjmedia_frame *frame);
+static pj_status_t vidstream_event_cb(pjmedia_vid_stream *stream,
+			              void *user_data,
+                                      pjmedia_vid_event *event);
+
+static void enc_clock_cb(const pj_timestamp *ts, void *user_data);
+static void dec_clock_cb(const pj_timestamp *ts, void *user_data);
+
+static pj_status_t vid_pasv_port_put_frame(struct pjmedia_port *this_port,
+					   pjmedia_frame *frame);
+
+static pj_status_t vid_pasv_port_get_frame(struct pjmedia_port *this_port,
+					   pjmedia_frame *frame);
+
+
+PJ_DEF(void) pjmedia_vid_port_param_default(pjmedia_vid_port_param *prm)
+{
+    pj_bzero(prm, sizeof(*prm));
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_create( pj_pool_t *pool,
+					     const pjmedia_vid_port_param *prm,
+					     pjmedia_vid_port **p_vid_port)
+{
+    enum { USEC_IN_SEC = 1000000 };
+    pjmedia_vid_port *vp;
+    pjmedia_vid_dev_index dev_id = PJMEDIA_VID_INVALID_DEV;
+    pjmedia_vid_dev_info di;
+    const pjmedia_video_format_detail *vfd;
+    pjmedia_vid_cb vid_cb;
+    pj_bool_t need_frame_buf = PJ_FALSE;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && prm && p_vid_port, PJ_EINVAL);
+    PJ_ASSERT_RETURN(prm->vidparam.frame_rate.num, PJ_EINVAL);
+    PJ_ASSERT_RETURN(prm->vidparam.fmt.type == PJMEDIA_TYPE_VIDEO,
+		     PJ_EINVAL);
+
+    /* Retrieve the video format detail */
+    vfd = pjmedia_format_get_video_format_detail(&prm->vidparam.fmt, PJ_TRUE);
+    if (!vfd)
+	return PJ_EINVAL;
+
+    /* Allocate videoport */
+    vp = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_port);
+    vp->role = prm->active ? ROLE_ACTIVE : ROLE_PASSIVE;
+    vp->dir = prm->vidparam.dir;
+    vp->cap_size = vfd->size;
+
+    /* Determine the device id to get the video device info */
+    if ((vp->dir & PJMEDIA_DIR_CAPTURE) &&
+        prm->vidparam.cap_id != PJMEDIA_VID_INVALID_DEV)
+    {
+	dev_id = prm->vidparam.cap_id;
+    } else if ((vp->dir & PJMEDIA_DIR_RENDER) &&
+               prm->vidparam.rend_id != PJMEDIA_VID_INVALID_DEV)
+    {
+	dev_id = prm->vidparam.rend_id;
+    } else
+	return PJ_EINVAL;
+
+    /* Get device info */
+    status = pjmedia_vid_dev_get_info(dev_id, &di);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    PJ_LOG(4,(THIS_FILE, "Opening %s..", di.name));
+
+    pj_strdup2_with_null(pool, &vp->dev_name, di.name);
+    vp->stream_role = di.has_callback ? ROLE_ACTIVE : ROLE_PASSIVE;
+
+    /* Create the video stream */
+    pj_bzero(&vid_cb, sizeof(vid_cb));
+    vid_cb.capture_cb = &vidstream_cap_cb;
+    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);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (vp->role==ROLE_ACTIVE && vp->stream_role==ROLE_PASSIVE) {
+	/* Active role is wanted, but our device is passive, so create
+	 * master clocks to run the media flow.
+	 */
+	need_frame_buf = PJ_TRUE;
+
+	if (vp->dir & PJMEDIA_DIR_ENCODING) {
+	    status = pjmedia_clock_create2(pool,
+					   USEC_IN_SEC *
+					      prm->vidparam.frame_rate.denum /
+					      prm->vidparam.frame_rate.num,
+					   prm->vidparam.clock_rate,
+					   PJMEDIA_CLOCK_NO_HIGHEST_PRIO,
+					   &enc_clock_cb, vp, &vp->enc_clock);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+	}
+
+	if (vp->dir & PJMEDIA_DIR_DECODING) {
+	    status = pjmedia_clock_create2(pool,
+					   USEC_IN_SEC *
+					      prm->vidparam.frame_rate.denum /
+					      prm->vidparam.frame_rate.num,
+					   prm->vidparam.clock_rate,
+					   PJMEDIA_CLOCK_NO_HIGHEST_PRIO,
+					   &dec_clock_cb, vp, &vp->dec_clock);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+	}
+
+    } else if (vp->role==ROLE_PASSIVE) {
+	vid_pasv_port *pp;
+
+	/* Always need to create media port for passive role */
+	vp->pasv_port = pp = PJ_POOL_ZALLOC_T(pool, vid_pasv_port);
+	pp->vp = vp;
+	pp->base.get_frame = &vid_pasv_port_get_frame;
+	pp->base.put_frame = &vid_pasv_port_put_frame;
+	pjmedia_port_info_init2(&pp->base.info, &vp->dev_name,
+				PJMEDIA_PORT_SIGNATURE('v', 'i', 'd', 'p'),
+			        prm->vidparam.dir, &prm->vidparam.fmt);
+
+	if (vp->stream_role == ROLE_ACTIVE) {
+	    need_frame_buf = PJ_TRUE;
+	}
+    }
+
+    if (need_frame_buf) {
+	const pjmedia_video_format_info *vfi;
+	pjmedia_video_apply_fmt_param vafp;
+
+	vfi = pjmedia_get_video_format_info(NULL, prm->vidparam.fmt.id);
+	if (!vfi) {
+	    status = PJ_ENOTFOUND;
+	    goto on_error;
+	}
+
+	pj_bzero(&vafp, sizeof(vafp));
+	vafp.size = vfd->size;
+	status = vfi->apply_fmt(vfi, &vafp);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+
+	if (vp->dir & PJMEDIA_DIR_ENCODING) {
+	    vp->enc_frm_buf = PJ_POOL_ZALLOC_T(pool, pjmedia_frame);
+	    vp->enc_frm_buf->buf = pj_pool_alloc(pool, vafp.framebytes);
+	    vp->enc_frm_buf->size = vafp.framebytes;
+	    vp->enc_frm_buf->type = PJMEDIA_FRAME_TYPE_NONE;
+
+	    status = pj_mutex_create_simple(pool, vp->dev_name.ptr,
+					    &vp->enc_frm_mutex);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+	}
+
+	if (vp->dir & PJMEDIA_DIR_DECODING) {
+	    vp->dec_frm_buf = PJ_POOL_ZALLOC_T(pool, pjmedia_frame);
+	    vp->dec_frm_buf->buf = pj_pool_alloc(pool, vafp.framebytes);
+	    vp->dec_frm_buf->size = vafp.framebytes;
+	    vp->dec_frm_buf->type = PJMEDIA_FRAME_TYPE_NONE;
+
+	    status = pj_mutex_create_simple(pool, vp->dev_name.ptr,
+					    &vp->dec_frm_mutex);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+	}
+    }
+
+    *p_vid_port = vp;
+
+    return PJ_SUCCESS;
+
+on_error:
+    pjmedia_vid_port_destroy(vp);
+    return status;
+}
+
+PJ_DEF(void) pjmedia_vid_port_set_cb(pjmedia_vid_port *vid_port,
+				     const pjmedia_vid_cb *cb,
+                                     void *user_data)
+{
+    pj_assert(vid_port && cb);
+    pj_memcpy(&vid_port->strm_cb, cb, sizeof(*cb));
+    vid_port->strm_cb_data = user_data;
+}
+
+PJ_DEF(pjmedia_vid_stream*)
+pjmedia_vid_port_get_stream(pjmedia_vid_port *vp)
+{
+    PJ_ASSERT_RETURN(vp, NULL);
+    return vp->strm;
+}
+
+
+PJ_DEF(pjmedia_port*)
+pjmedia_vid_port_get_passive_port(pjmedia_vid_port *vp)
+{
+    PJ_ASSERT_RETURN(vp && vp->role==ROLE_PASSIVE, NULL);
+    return &vp->pasv_port->base;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_connect(pjmedia_vid_port *vp,
+					      pjmedia_port *port,
+					      pj_bool_t destroy)
+{
+    PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, PJ_EINVAL);
+    vp->destroy_client_port = destroy;
+    vp->client_port = port;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_disconnect(pjmedia_vid_port *vp)
+{
+    PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, PJ_EINVAL);
+    vp->client_port = NULL;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pjmedia_port*)
+pjmedia_vid_port_get_connected_port(pjmedia_vid_port *vp)
+{
+    PJ_ASSERT_RETURN(vp && vp->role==ROLE_ACTIVE, NULL);
+    return vp->client_port;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_start(pjmedia_vid_port *vp)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(vp, PJ_EINVAL);
+
+    status = pjmedia_vid_stream_start(vp->strm);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (vp->enc_clock) {
+	status = pjmedia_clock_start(vp->enc_clock);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    if (vp->dec_clock) {
+	status = pjmedia_clock_start(vp->dec_clock);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    return PJ_SUCCESS;
+
+on_error:
+    pjmedia_vid_port_stop(vp);
+    return status;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_port_stop(pjmedia_vid_port *vp)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(vp, PJ_EINVAL);
+
+    status = pjmedia_vid_stream_stop(vp->strm);
+
+    if (vp->enc_clock) {
+	status = pjmedia_clock_stop(vp->enc_clock);
+    }
+
+    if (vp->dec_clock) {
+	status = pjmedia_clock_stop(vp->dec_clock);
+    }
+
+    return status;
+}
+
+PJ_DEF(void) pjmedia_vid_port_destroy(pjmedia_vid_port *vp)
+{
+    PJ_ASSERT_ON_FAIL(vp, return);
+
+    PJ_LOG(4,(THIS_FILE, "Closing %s..", vp->dev_name.ptr));
+
+    if (vp->enc_clock) {
+	pjmedia_clock_destroy(vp->enc_clock);
+	vp->enc_clock = NULL;
+    }
+    if (vp->dec_clock) {
+	pjmedia_clock_destroy(vp->dec_clock);
+	vp->dec_clock = NULL;
+    }
+    if (vp->strm) {
+	pjmedia_vid_stream_destroy(vp->strm);
+	vp->strm = NULL;
+    }
+    if (vp->client_port) {
+	if (vp->destroy_client_port)
+	    pjmedia_port_destroy(vp->client_port);
+	vp->client_port = NULL;
+    }
+    if (vp->enc_frm_mutex) {
+	pj_mutex_destroy(vp->enc_frm_mutex);
+	vp->enc_frm_mutex = NULL;
+    }
+    if (vp->dec_frm_mutex) {
+	pj_mutex_destroy(vp->dec_frm_mutex);
+	vp->dec_frm_mutex = NULL;
+    }
+
+}
+
+/*
+static void save_rgb_frame(int width, int height, const pjmedia_frame *frm)
+{
+    static int counter;
+    FILE *pFile;
+    char szFilename[32];
+    const pj_uint8_t *pFrame = (const pj_uint8_t*)frm->buf;
+    int  y;
+
+    if (counter > 10)
+	return;
+
+    // Open file
+    sprintf(szFilename, "frame%02d.ppm", counter++);
+    pFile=fopen(szFilename, "wb");
+    if(pFile==NULL)
+      return;
+
+    // Write header
+    fprintf(pFile, "P6\n%d %d\n255\n", width, height);
+
+    // Write pixel data
+    for(y=0; y<height; y++)
+      fwrite(pFrame+y*width*3, 1, width*3, pFile);
+
+    // Close file
+    fclose(pFile);
+}
+*/
+
+static void enc_clock_cb(const pj_timestamp *ts, void *user_data)
+{
+    /* We are here because user wants us to be active but the stream is
+     * passive. So get a frame from the stream and push it to user.
+     */
+    pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+    pj_status_t status;
+
+    pj_assert(vp->role==ROLE_ACTIVE && vp->stream_role==ROLE_PASSIVE);
+
+    PJ_UNUSED_ARG(ts);
+
+    if (!vp->client_port)
+	return;
+
+    status = pjmedia_vid_stream_get_frame(vp->strm, vp->enc_frm_buf);
+    if (status != PJ_SUCCESS)
+	return;
+
+    //save_rgb_frame(vp->cap_size.w, vp->cap_size.h, vp->enc_frm_buf);
+
+    status = pjmedia_port_put_frame(vp->client_port, vp->enc_frm_buf);
+}
+
+static void dec_clock_cb(const pj_timestamp *ts, void *user_data)
+{
+    /* We are here because user wants us to be active but the stream is
+     * passive. So get a frame from the stream and push it to user.
+     */
+    pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+    pj_status_t status;
+
+    pj_assert(vp->role==ROLE_ACTIVE && vp->stream_role==ROLE_PASSIVE);
+
+    PJ_UNUSED_ARG(ts);
+
+    if (!vp->client_port)
+	return;
+
+    status = pjmedia_port_get_frame(vp->client_port, vp->dec_frm_buf);
+    if (status != PJ_SUCCESS)
+	return;
+
+    status = pjmedia_vid_stream_put_frame(vp->strm, vp->dec_frm_buf);
+}
+
+static void copy_frame(pjmedia_frame *dst, const pjmedia_frame *src)
+{
+    PJ_ASSERT_ON_FAIL(dst->size >= src->size, return);
+
+    pj_memcpy(dst, src, sizeof(*src));
+    pj_memcpy(dst->buf, src->buf, src->size);
+    dst->size = src->size;
+}
+
+static pj_status_t vidstream_cap_cb(pjmedia_vid_stream *stream,
+				    void *user_data,
+				    pjmedia_frame *frame)
+{
+    pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+
+    if (vp->role==ROLE_ACTIVE) {
+	if (vp->client_port)
+	    return pjmedia_port_put_frame(vp->client_port, frame);
+    } else {
+	pj_mutex_lock(vp->enc_frm_mutex);
+	copy_frame(vp->enc_frm_buf, frame);
+	pj_mutex_unlock(vp->enc_frm_mutex);
+    }
+    if (vp->strm_cb.capture_cb)
+        return (*vp->strm_cb.capture_cb)(stream, vp->strm_cb_data, frame);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vidstream_render_cb(pjmedia_vid_stream *stream,
+				       void *user_data,
+				       pjmedia_frame *frame)
+{
+    pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+
+    if (vp->role==ROLE_ACTIVE) {
+	if (vp->client_port)
+	    return pjmedia_port_get_frame(vp->client_port, frame);
+    } else {
+	pj_mutex_lock(vp->dec_frm_mutex);
+	copy_frame(frame, vp->dec_frm_buf);
+	pj_mutex_unlock(vp->dec_frm_mutex);
+    }
+    if (vp->strm_cb.render_cb)
+        return (*vp->strm_cb.render_cb)(stream, vp->strm_cb_data, frame);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vidstream_event_cb(pjmedia_vid_stream *stream,
+				      void *user_data,
+				      pjmedia_vid_event *event)
+{
+    pjmedia_vid_port *vp = (pjmedia_vid_port*)user_data;
+
+    if (vp->strm_cb.on_event_cb)
+        return (*vp->strm_cb.on_event_cb)(stream, vp->strm_cb_data, event);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vid_pasv_port_put_frame(struct pjmedia_port *this_port,
+					   pjmedia_frame *frame)
+{
+    struct vid_pasv_port *vpp = (struct vid_pasv_port*)this_port;
+    pjmedia_vid_port *vp = vpp->vp;
+
+    if (vp->stream_role==ROLE_PASSIVE) {
+	return pjmedia_vid_stream_put_frame(vp->strm, frame);
+    } else {
+	pj_mutex_lock(vp->dec_frm_mutex);
+	copy_frame(vp->dec_frm_buf, frame);
+	pj_mutex_unlock(vp->dec_frm_mutex);
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vid_pasv_port_get_frame(struct pjmedia_port *this_port,
+					   pjmedia_frame *frame)
+{
+    struct vid_pasv_port *vpp = (struct vid_pasv_port*)this_port;
+    pjmedia_vid_port *vp = vpp->vp;
+
+    if (vp->stream_role==ROLE_PASSIVE) {
+	return pjmedia_vid_stream_get_frame(vp->strm, frame);
+    } else {
+	pj_mutex_lock(vp->enc_frm_mutex);
+	copy_frame(frame, vp->enc_frm_buf);
+	pj_mutex_unlock(vp->enc_frm_mutex);
+    }
+
+    return PJ_SUCCESS;
+}
+
diff --git a/pjmedia/src/pjmedia/wav_player.c b/pjmedia/src/pjmedia/wav_player.c
index e0279b2..0504b69 100644
--- a/pjmedia/src/pjmedia/wav_player.c
+++ b/pjmedia/src/pjmedia/wav_player.c
@@ -168,7 +168,10 @@
     pjmedia_wave_hdr wave_hdr;
     pj_ssize_t size_to_read, size_read;
     struct file_reader_port *fport;
+    pjmedia_audio_format_detail *ad;
     pj_off_t pos;
+    pj_str_t name;
+    unsigned samples_per_frame;
     pj_status_t status = PJ_SUCCESS;
 
 
@@ -331,17 +334,15 @@
     fport->options = options;
 
     /* Update port info. */
-    fport->base.info.channel_count = wave_hdr.fmt_hdr.nchan;
-    fport->base.info.clock_rate = wave_hdr.fmt_hdr.sample_rate;
-    fport->base.info.bits_per_sample = BITS_PER_SAMPLE;
-    fport->base.info.samples_per_frame = fport->base.info.clock_rate *
-					 wave_hdr.fmt_hdr.nchan *
-					 ptime / 1000;
-    fport->base.info.bytes_per_frame = 
-	fport->base.info.samples_per_frame * 
-	fport->base.info.bits_per_sample / 8;
-
-    pj_strdup2(pool, &fport->base.info.name, filename);
+    ad = pjmedia_format_get_audio_format_detail(&fport->base.info.fmt, 1);
+    pj_strdup2(pool, &name, filename);
+    samples_per_frame = ptime * wave_hdr.fmt_hdr.sample_rate *
+		        wave_hdr.fmt_hdr.nchan / 1000;
+    pjmedia_port_info_init(&fport->base.info, &name, SIGNATURE,
+			   wave_hdr.fmt_hdr.sample_rate,
+			   wave_hdr.fmt_hdr.nchan,
+			   BITS_PER_SAMPLE,
+			   samples_per_frame);
 
     /* If file is shorter than buffer size, adjust buffer size to file
      * size. Otherwise EOF callback will be called multiple times when
@@ -358,9 +359,7 @@
     /* samples_per_frame must be smaller than bufsize (because get_frame()
      * doesn't handle this case).
      */
-    if (fport->base.info.samples_per_frame * fport->bytes_per_sample >=
-	fport->bufsize)
-    {
+    if (samples_per_frame * fport->bytes_per_sample >= fport->bufsize) {
 	pj_file_close(fport->fd);
 	return PJ_EINVAL;
     }
@@ -394,8 +393,8 @@
 	      "filesize=%luKB",
 	      (int)fport->base.info.name.slen,
 	      fport->base.info.name.ptr,
-	      fport->base.info.clock_rate,
-	      fport->base.info.channel_count,
+	      ad->clock_rate,
+	      ad->channel_count,
 	      fport->bufsize / 1000,
 	      (unsigned long)(fport->fsize / 1000)));
 
diff --git a/pjmedia/src/pjmedia/wav_playlist.c b/pjmedia/src/pjmedia/wav_playlist.c
index 33db0df..b8cfbb8 100644
--- a/pjmedia/src/pjmedia/wav_playlist.c
+++ b/pjmedia/src/pjmedia/wav_playlist.c
@@ -236,6 +236,7 @@
 						pjmedia_port **p_port)
 {
     struct playlist_port *fport;
+    pjmedia_audio_format_detail *afd;
     pj_off_t pos;
     pj_status_t status;
     int index;
@@ -280,6 +281,8 @@
 	return PJ_ENOMEM;
     }
 
+    afd = pjmedia_format_get_audio_format_detail(&fport->base.info.fmt, 1);
+
     /* start with the first file. */
     fport->current_file = 0;
     fport->max_file = file_count;
@@ -466,15 +469,13 @@
 	 * that the WAV file has the same attributes as previous files. 
 	 */
 	if (!has_wave_info) {
-	    fport->base.info.channel_count = wavehdr.fmt_hdr.nchan;
-	    fport->base.info.clock_rate = wavehdr.fmt_hdr.sample_rate;
-	    fport->base.info.bits_per_sample = wavehdr.fmt_hdr.bits_per_sample;
-	    fport->base.info.samples_per_frame = fport->base.info.clock_rate *
-						 wavehdr.fmt_hdr.nchan *
-						 ptime / 1000;
-	    fport->base.info.bytes_per_frame =
-		fport->base.info.samples_per_frame *
-		fport->base.info.bits_per_sample / 8;
+	    afd->channel_count = wavehdr.fmt_hdr.nchan;
+	    afd->clock_rate = wavehdr.fmt_hdr.sample_rate;
+	    afd->bits_per_sample = wavehdr.fmt_hdr.bits_per_sample;
+	    afd->frame_time_usec = ptime * 1000;
+	    afd->avg_bps = afd->max_bps = afd->clock_rate *
+					  afd->channel_count *
+					  afd->bits_per_sample / 8;
 
 	    has_wave_info = PJ_TRUE;
 
@@ -483,9 +484,9 @@
 	    /* Check that this file has the same characteristics as the other
 	     * files.
 	     */
-	    if (wavehdr.fmt_hdr.nchan != fport->base.info.channel_count ||
-		wavehdr.fmt_hdr.sample_rate != fport->base.info.clock_rate ||
-		wavehdr.fmt_hdr.bits_per_sample != fport->base.info.bits_per_sample)
+	    if (wavehdr.fmt_hdr.nchan != afd->channel_count ||
+		wavehdr.fmt_hdr.sample_rate != afd->clock_rate ||
+		wavehdr.fmt_hdr.bits_per_sample != afd->bits_per_sample)
 	    {
 		/* This file has different characteristics than the other 
 		 * files. 
@@ -519,8 +520,8 @@
 	     "WAV playlist '%.*s' created: samp.rate=%d, ch=%d, bufsize=%uKB",
 	     (int)port_label->slen,
 	     port_label->ptr,
-	     fport->base.info.clock_rate,
-	     fport->base.info.channel_count,
+	     afd->clock_rate,
+	     afd->channel_count,
 	     fport->bufsize / 1000));
     
     return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/wav_writer.c b/pjmedia/src/pjmedia/wav_writer.c
index a24fbbd..6c749c8 100644
--- a/pjmedia/src/pjmedia/wav_writer.c
+++ b/pjmedia/src/pjmedia/wav_writer.c
@@ -51,7 +51,7 @@
 };
 
 static pj_status_t file_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame);
+				  pjmedia_frame *frame);
 static pj_status_t file_get_frame(pjmedia_port *this_port, 
 				  pjmedia_frame *frame);
 static pj_status_t file_on_destroy(pjmedia_port *this_port);
@@ -198,7 +198,7 @@
     fport->bufsize = buff_size;
 
     /* Check that buffer size is greater than bytes per frame */
-    pj_assert(fport->bufsize >= fport->base.info.bytes_per_frame);
+    pj_assert(fport->bufsize >= PJMEDIA_PIA_AVG_FSZ(&fport->base.info));
 
 
     /* Allocate buffer and set initial write position */
@@ -216,7 +216,7 @@
 	      "File writer '%.*s' created: samp.rate=%d, bufsize=%uKB",
 	      (int)fport->base.info.name.slen,
 	      fport->base.info.name.ptr,
-	      fport->base.info.clock_rate,
+	      PJMEDIA_PIA_SRATE(&fport->base.info),
 	      fport->bufsize / 1000));
 
 
@@ -308,7 +308,7 @@
  * to the file.
  */
 static pj_status_t file_put_frame(pjmedia_port *this_port, 
-				  const pjmedia_frame *frame)
+				  pjmedia_frame *frame)
 {
     struct file_port *fport = (struct file_port *)this_port;
     unsigned frame_size;
diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c
index 392c634..d315606 100644
--- a/pjmedia/src/test/mips_test.c
+++ b/pjmedia/src/test/mips_test.c
@@ -684,7 +684,7 @@
 
 
 static pj_status_t codec_put_frame(struct pjmedia_port *this_port, 
-				   const pjmedia_frame *frame)
+				   pjmedia_frame *frame)
 {
     struct codec_port *cp = (struct codec_port*)this_port;
     pjmedia_frame out_frame;
@@ -1131,13 +1131,13 @@
     pj_status_t status;
 
     while (pjmedia_circ_buf_get_len(wp->circbuf) <
-		wp->base.info.samples_per_frame * (CIRC_BUF_FRAME_CNT-1))
+		PJMEDIA_PIA_SPF(&wp->base.info) * (CIRC_BUF_FRAME_CNT-1))
     {
 	status = pjmedia_port_get_frame(wp->gen_port, frame);
 	pj_assert(status==PJ_SUCCESS);
 
 	status = pjmedia_circ_buf_write(wp->circbuf, (short*)frame->buf, 
-					wp->base.info.samples_per_frame);
+					PJMEDIA_PIA_SPF(&wp->base.info));
 	pj_assert(status==PJ_SUCCESS);
     }
     
@@ -1149,7 +1149,7 @@
 	pjmedia_circ_buf_get_read_regions(wp->circbuf, &reg1, &reg1_len,
 					  &reg2, &reg2_len);
 
-	del_cnt = wp->base.info.samples_per_frame;
+	del_cnt = PJMEDIA_PIA_SPF(&wp->base.info);
 	status = pjmedia_wsola_discard(wp->wsola, reg1, reg1_len, reg2, 
 				       reg2_len, &del_cnt);
 	pj_assert(status==PJ_SUCCESS);
@@ -2010,7 +2010,7 @@
 }
 
 static pj_status_t delaybuf_put_frame(struct pjmedia_port *this_port, 
-				      const pjmedia_frame *frame)
+				      pjmedia_frame *frame)
 {
     struct delaybuf_port *dp = (struct delaybuf_port*)this_port;
     pj_status_t status;
@@ -2219,7 +2219,7 @@
     }
 
     /* Port may decide to use different ptime (e.g. iLBC) */
-    samples_per_frame = port->info.samples_per_frame;
+    samples_per_frame = PJMEDIA_PIA_SPF(&port->info);
 
     gen_port = create_gen_port(pool, clock_rate, 1, 
 			       samples_per_frame, 100);
diff --git a/pjmedia/src/test/test.c b/pjmedia/src/test/test.c
index 385eee1..bd4e406 100644
--- a/pjmedia/src/test/test.c
+++ b/pjmedia/src/test/test.c
@@ -56,6 +56,14 @@
 
     mem = &caching_pool.factory;
 
+#if HAS_VID_DEV_TEST
+    DO_TEST(vid_dev_test());
+#endif
+
+#if HAS_VID_CODEC_TEST
+    DO_TEST(vid_codec_test());
+#endif
+
 #if HAS_SDP_NEG_TEST
     DO_TEST(sdp_neg_test());
 #endif
diff --git a/pjmedia/src/test/test.h b/pjmedia/src/test/test.h
index 96dc9d5..085f4c0 100644
--- a/pjmedia/src/test/test.h
+++ b/pjmedia/src/test/test.h
@@ -23,6 +23,8 @@
 #include <pjmedia.h>
 #include <pjlib.h>
 
+#define HAS_VID_DEV_TEST	1
+#define HAS_VID_CODEC_TEST	1
 #define HAS_SDP_NEG_TEST	1
 #define HAS_JBUF_TEST		1
 #define HAS_MIPS_TEST		1
@@ -35,6 +37,8 @@
 int sdp_neg_test(void);
 int mips_test(void);
 int codec_test_vectors(void);
+int vid_codec_test(void);
+int vid_dev_test(void);
 
 extern pj_pool_factory *mem;
 void app_perror(pj_status_t status, const char *title);
diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c
new file mode 100644
index 0000000..d32e176
--- /dev/null
+++ b/pjmedia/src/test/vid_codec_test.c
@@ -0,0 +1,359 @@
+#include "test.h"
+#include <pjmedia-codec/ffmpeg_codecs.h>
+#include <pjmedia-videodev/videodev.h>
+#include <pjmedia/vid_codec.h>
+#include <pjmedia/port.h>
+
+#define THIS_FILE "vid_codec.c"
+
+#define BYPASS_CODEC 0
+
+typedef struct codec_port_data_t
+{
+    pjmedia_vid_codec   *codec;
+    pjmedia_port        *dn_port;
+    pj_uint8_t          *enc_buf;
+    pj_size_t            enc_buf_size;
+} codec_port_data_t;
+
+static pj_status_t codec_put_frame(pjmedia_port *port,
+			           pjmedia_frame *frame)
+{
+    codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata;
+    pjmedia_vid_codec *codec = port_data->codec;
+    pjmedia_frame enc_frame;
+    pj_status_t status;
+
+    enc_frame.buf = port_data->enc_buf;
+    enc_frame.size = port_data->enc_buf_size;
+
+#if !BYPASS_CODEC
+    status = codec->op->encode(codec, frame, enc_frame.size, &enc_frame);
+    if (status != PJ_SUCCESS) goto on_error;
+    status = codec->op->decode(codec, &enc_frame, frame->size, frame);
+    if (status != PJ_SUCCESS) goto on_error;
+#endif
+
+    status = pjmedia_port_put_frame(port_data->dn_port, frame);
+    if (status != PJ_SUCCESS) goto on_error;
+
+    return PJ_SUCCESS;
+
+on_error:
+    pj_perror(3, THIS_FILE, status, "codec_put_frame() error");
+    return status;
+}
+
+static const char* dump_codec_info(const pjmedia_vid_codec_info *info)
+{
+    static char str[80];
+    unsigned i;
+    char *p = str;
+
+    /* Raw format ids */
+    for (i=0; (i<info->dec_fmt_id_cnt) && (p-str+5<sizeof(str)); ++i) {
+        pj_memcpy(p, &info->dec_fmt_id[i], 4);
+        p += 4;
+        *p++ = ' ';
+    }
+    *p = '\0';
+
+    return str;
+}
+
+static int enum_codecs()
+{
+    unsigned i, cnt;
+    pjmedia_vid_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS];
+    pj_status_t status;
+
+    PJ_LOG(3, (THIS_FILE, "  codec enums"));
+    cnt = PJ_ARRAY_SIZE(info);
+    status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &cnt, info, NULL);
+    if (status != PJ_SUCCESS)
+        return 100;
+
+    for (i = 0; i < cnt; ++i) {
+        PJ_LOG(3, (THIS_FILE, "  %16.*s %c%c %s",
+                   info[i].encoding_name.slen, info[i].encoding_name.ptr,
+                   (info[i].dir & PJMEDIA_DIR_ENCODING? 'E' : ' '),
+                   (info[i].dir & PJMEDIA_DIR_DECODING? 'D' : ' '),
+                   dump_codec_info(&info[i])));
+    }
+
+    return PJ_SUCCESS;
+}
+
+static int encode_decode_test(pj_pool_t *pool, const char *codec_id,
+                              pjmedia_format_id raw_fmt_id)
+{
+
+    pjmedia_vid_codec *codec=NULL;
+    pjmedia_port codec_port;
+    codec_port_data_t codec_port_data;
+    pjmedia_vid_codec_param codec_param;
+    const pjmedia_vid_codec_info *codec_info;
+
+    pjmedia_vid_dev_index cap_idx, rdr_idx;
+    pjmedia_vid_port *capture=NULL, *renderer=NULL;
+    pjmedia_vid_port_param vport_param;
+    pjmedia_video_format_detail *vfd;
+    pj_status_t status;
+    int rc = 0;
+
+    PJ_LOG(3, (THIS_FILE, "  encode decode test"));
+
+    /* Lookup codec */
+    {
+        pj_str_t codec_id_st;
+        unsigned info_cnt = 1;
+
+        /* Lookup codec */
+        pj_cstr(&codec_id_st, codec_id);
+        status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, &codec_id_st, 
+                                                         &info_cnt, 
+                                                         &codec_info, NULL);
+        if (status != PJ_SUCCESS) {
+            rc = 205; goto on_return;
+        }
+    }
+
+    /* Lookup colorbar source */
+    status = pjmedia_vid_dev_lookup("Colorbar", "Colorbar generator", &cap_idx);
+    if (status != PJ_SUCCESS) {
+	rc = 206; goto on_return;
+    }
+
+    /* Lookup SDL renderer */
+    status = pjmedia_vid_dev_lookup("SDL", "SDL renderer", &rdr_idx);
+    if (status != PJ_SUCCESS) {
+	rc = 207; goto on_return;
+    }
+
+    /* Raw format ID "not specified", lets find common format among the codec
+     * and the video devices
+     */
+    if (raw_fmt_id == 0) {
+        pjmedia_vid_dev_info cap_info, rdr_info;
+        unsigned i, j, k;
+
+        pjmedia_vid_dev_get_info(cap_idx, &cap_info);
+        pjmedia_vid_dev_get_info(rdr_idx, &rdr_info);
+
+        for (i=0; i<codec_info->dec_fmt_id_cnt && !raw_fmt_id; ++i) {
+            for (j=0; j<cap_info.fmt_cnt && !raw_fmt_id; ++j) {
+                if (codec_info->dec_fmt_id[i]==(int)cap_info.fmt[j].id) {
+                    for (k=0; k<rdr_info.fmt_cnt && !raw_fmt_id; ++k) {
+                        if (codec_info->dec_fmt_id[i]==(int)rdr_info.fmt[k].id)
+                        {
+                            raw_fmt_id = codec_info->dec_fmt_id[i];
+                        }
+                    }
+                }
+            }
+        }
+
+        if (raw_fmt_id == 0) {
+            PJ_LOG(3, (THIS_FILE, "  No common format ID among the codec "
+                       "and the video devices"));
+            status = PJ_ENOTFOUND;
+            rc = 210;
+            goto on_return;
+        }
+    }
+
+    pjmedia_vid_port_param_default(&vport_param);
+
+    /* Create capture, set it to active (master) */
+    status = pjmedia_vid_dev_default_param(pool, cap_idx,
+					   &vport_param.vidparam);
+    if (status != PJ_SUCCESS) {
+	rc = 220; goto on_return;
+    }
+    vport_param.vidparam.fmt.id = raw_fmt_id;
+    vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
+    vport_param.active = PJ_TRUE;
+
+    if (vport_param.vidparam.fmt.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO) {
+	rc = 221; goto on_return;
+    }
+
+    vfd = pjmedia_format_get_video_format_detail(&vport_param.vidparam.fmt,
+						 PJ_TRUE);
+    if (vfd == NULL) {
+	rc = 225; goto on_return;
+    }
+
+    status = pjmedia_vid_port_create(pool, &vport_param, &capture);
+    if (status != PJ_SUCCESS) {
+	rc = 226; goto on_return;
+    }
+
+    /* Create renderer, set it to passive (slave)  */
+    vport_param.active = PJ_FALSE;
+    vport_param.vidparam.dir = PJMEDIA_DIR_RENDER;
+    vport_param.vidparam.rend_id = rdr_idx;
+    vport_param.vidparam.disp_size = vfd->size;
+
+    status = pjmedia_vid_port_create(pool, &vport_param, &renderer);
+    if (status != PJ_SUCCESS) {
+	rc = 230; goto on_return;
+    }
+
+    /* Prepare codec */
+    {
+        pj_str_t codec_id_st;
+        unsigned info_cnt = 1;
+        const pjmedia_vid_codec_info *codec_info;
+        pj_str_t port_name = {"codec", 5};
+        pj_uint8_t *enc_buf = NULL;
+        pj_size_t enc_buf_size = 0;
+
+
+        /* Lookup codec */
+        pj_cstr(&codec_id_st, codec_id);
+        status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, &codec_id_st, 
+                                                         &info_cnt, 
+                                                         &codec_info, NULL);
+        if (status != PJ_SUCCESS) {
+            rc = 245; goto on_return;
+        }
+        status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info,
+                                                         &codec_param);
+        if (status != PJ_SUCCESS) {
+            rc = 246; goto on_return;
+        }
+
+        pjmedia_format_copy(&codec_param.dec_fmt, &vport_param.vidparam.fmt);
+
+#if !BYPASS_CODEC
+
+        /* Open codec */
+        status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info,
+                                                   &codec);
+        if (status != PJ_SUCCESS) {
+	    rc = 250; goto on_return;
+        }
+
+        status = codec->op->init(codec, pool);
+        if (status != PJ_SUCCESS) {
+	    rc = 251; goto on_return;
+        }
+
+        status = codec->op->open(codec, &codec_param);
+        if (status != PJ_SUCCESS) {
+	    rc = 252; goto on_return;
+        }
+
+        /* Alloc encoding buffer */
+        enc_buf_size =  codec_param.dec_fmt.det.vid.size.w *
+                        codec_param.dec_fmt.det.vid.size.h * 4
+                        + 16; /*< padding, just in case */
+        enc_buf = pj_pool_alloc(pool,enc_buf_size);
+
+#endif /* !BYPASS_CODEC */
+
+        /* Init codec port */
+        pj_bzero(&codec_port, sizeof(codec_port));
+        status = pjmedia_port_info_init2(&codec_port.info, &port_name, 0x1234,
+                                         PJMEDIA_DIR_ENCODING, 
+                                         &codec_param.dec_fmt);
+        if (status != PJ_SUCCESS) {
+	    rc = 260; goto on_return;
+        }
+        codec_port_data.codec = codec;
+        codec_port_data.dn_port = pjmedia_vid_port_get_passive_port(renderer);
+        codec_port_data.enc_buf = enc_buf;
+        codec_port_data.enc_buf_size = enc_buf_size;
+
+        codec_port.put_frame = &codec_put_frame;
+        codec_port.port_data.pdata = &codec_port_data;
+    }
+
+
+    /* Connect capture to codec port */
+    status = pjmedia_vid_port_connect(capture,
+				      &codec_port,
+				      PJ_FALSE);
+    if (status != PJ_SUCCESS) {
+	rc = 270; goto on_return;
+    }
+
+    PJ_LOG(3, (THIS_FILE, "  starting codec test:  %c%c%c%c<->%s %dx%d",
+        ((codec_param.dec_fmt.id & 0x000000FF) >> 0),
+        ((codec_param.dec_fmt.id & 0x0000FF00) >> 8),
+        ((codec_param.dec_fmt.id & 0x00FF0000) >> 16),
+        ((codec_param.dec_fmt.id & 0xFF000000) >> 24),
+        codec_id, 
+        codec_param.dec_fmt.det.vid.size.w,
+        codec_param.dec_fmt.det.vid.size.h
+        ));
+
+    /* Start streaming.. */
+    status = pjmedia_vid_port_start(renderer);
+    if (status != PJ_SUCCESS) {
+	rc = 275; goto on_return;
+    }
+    status = pjmedia_vid_port_start(capture);
+    if (status != PJ_SUCCESS) {
+	rc = 280; goto on_return;
+    }
+
+    /* Sleep while the video is being displayed... */
+    pj_thread_sleep(10000);
+
+on_return:
+    if (status != PJ_SUCCESS) {
+        PJ_PERROR(3, (THIS_FILE, status, "  error"));
+    }
+    if (capture) {
+        pjmedia_vid_port_stop(capture);
+	pjmedia_vid_port_destroy(capture);
+    }
+    if (renderer) {
+        pjmedia_vid_port_stop(renderer);
+	pjmedia_vid_port_destroy(renderer);
+    }
+    if (codec) {
+        codec->op->close(codec);
+        pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec);
+    }
+
+    return rc;
+}
+
+int vid_codec_test(void)
+{
+    pj_pool_t *pool;
+    int rc = 0;
+    pj_status_t status;
+
+    PJ_LOG(3, (THIS_FILE, "Performing video codec tests.."));
+
+    pool = pj_pool_create(mem, "Vid codec test", 256, 256, 0);
+
+    status = pjmedia_vid_subsys_init(mem);
+    if (status != PJ_SUCCESS)
+        return -10;
+
+    status = pjmedia_codec_ffmpeg_init(NULL, mem);
+    if (status != PJ_SUCCESS)
+        return -20;
+
+    rc = enum_codecs();
+    if (rc != 0)
+	goto on_return;
+
+    rc = encode_decode_test(pool, "mjpeg", 0);
+    if (rc != 0)
+	goto on_return;
+
+on_return:
+    pjmedia_codec_ffmpeg_deinit();
+    pjmedia_vid_subsys_shutdown();
+    pj_pool_release(pool);
+
+    return rc;
+}
+
+
diff --git a/pjmedia/src/test/vid_dev_test.c b/pjmedia/src/test/vid_dev_test.c
new file mode 100644
index 0000000..007ef3b
--- /dev/null
+++ b/pjmedia/src/test/vid_dev_test.c
@@ -0,0 +1,529 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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 "test.h"
+#include <pjmedia-audiodev/audiodev.h>
+#include <pjmedia-codec/ffmpeg_codecs.h>
+#include <pjmedia/vid_codec.h>
+#include <pjmedia_videodev.h>
+
+#define THIS_FILE "vid_dev_test.c"
+
+pj_status_t pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr,
+				              pj_pool_t *pool);
+
+typedef struct codec_port_data_t
+{
+    pjmedia_vid_codec   *codec;
+    pjmedia_port        *src_port;
+    pj_uint8_t          *enc_buf;
+    pj_size_t            enc_buf_size;
+
+    pjmedia_converter   *conv;
+} codec_port_data_t;
+
+typedef struct avi_port_t
+{
+    pjmedia_vid_port   *vid_port;
+    pjmedia_aud_stream *aud_stream;
+    pj_bool_t           is_running;
+} avi_port_t;
+
+static int enum_devs(void)
+{
+    unsigned i, dev_cnt;
+    pj_status_t status;
+
+    PJ_LOG(3, (THIS_FILE, "  device enums"));
+    dev_cnt = pjmedia_vid_dev_count();
+    for (i = 0; i < dev_cnt; ++i) {
+        pjmedia_vid_dev_info info;
+        status = pjmedia_vid_dev_get_info(i, &info);
+        if (status == PJ_SUCCESS) {
+            PJ_LOG(3, (THIS_FILE, "%3d: %s - %s", i, info.driver, info.name));
+        }
+    }
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t avi_play_cb(void *user_data, pjmedia_frame *frame)
+{
+    return pjmedia_port_get_frame((pjmedia_port*)user_data, frame);
+}
+
+static pj_status_t avi_event_cb(pjmedia_vid_stream *stream,
+				void *user_data,
+				pjmedia_vid_event *event)
+{
+    avi_port_t *ap = (avi_port_t *)user_data;
+
+    PJ_UNUSED_ARG(stream);
+
+    if (event->event_type != PJMEDIA_EVENT_MOUSEBUTTONDOWN)
+        return PJ_SUCCESS;
+
+    if (ap->is_running) {
+        pjmedia_vid_port_stop(ap->vid_port);
+        if (ap->aud_stream)
+            pjmedia_aud_stream_stop(ap->aud_stream);
+    } else {
+        pjmedia_vid_port_start(ap->vid_port);
+        if (ap->aud_stream)
+            pjmedia_aud_stream_start(ap->aud_stream);
+    }
+    ap->is_running = !ap->is_running;
+
+    /* We handled the event on our own, so return non-PJ_SUCCESS here */
+    return -1;
+}
+
+static pj_status_t codec_get_frame(pjmedia_port *port,
+			           pjmedia_frame *frame)
+{
+    codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata;
+    pjmedia_vid_codec *codec = port_data->codec;
+    pjmedia_frame enc_frame;
+    pj_status_t status;
+
+    enc_frame.buf = port_data->enc_buf;
+    enc_frame.size = port_data->enc_buf_size;
+
+    if (port_data->conv) {
+        pj_size_t frame_size = frame->size;
+
+        status = pjmedia_port_get_frame(port_data->src_port, frame);
+        if (status != PJ_SUCCESS) goto on_error;
+
+        status = codec->op->decode(codec, frame, frame->size, &enc_frame);
+        if (status != PJ_SUCCESS) goto on_error;
+
+        frame->size = frame_size;
+        status = pjmedia_converter_convert(port_data->conv, &enc_frame, frame);
+        if (status != PJ_SUCCESS) goto on_error;
+
+        return PJ_SUCCESS;
+    }
+
+    status = pjmedia_port_get_frame(port_data->src_port, &enc_frame);
+    if (status != PJ_SUCCESS) goto on_error;
+
+    status = codec->op->decode(codec, &enc_frame, frame->size, frame);
+    if (status != PJ_SUCCESS) goto on_error;
+
+    return PJ_SUCCESS;
+
+on_error:
+    pj_perror(3, THIS_FILE, status, "codec_get_frame() error");
+    return status;
+}
+
+static int aviplay_test(pj_pool_t *pool)
+{
+    pjmedia_vid_port *renderer=NULL;
+    pjmedia_vid_port_param param;
+    pjmedia_aud_param aparam;
+    pjmedia_video_format_detail *vfd;
+    pjmedia_audio_format_detail *afd;
+    pjmedia_aud_stream *strm = NULL;
+    pj_status_t status;
+    int rc = 0;
+    pjmedia_avi_streams *avi_streams;
+    pjmedia_avi_stream *vid_stream, *aud_stream;
+    pjmedia_port *vid_port = NULL, *aud_port = NULL;
+    pjmedia_vid_codec *codec=NULL;
+    avi_port_t avi_port;
+#if PJ_WIN32
+    const char *fname = "C:\\Users\\Liong Sauw Ming\\Desktop\\piratesmjpg.avi";
+#else
+    const char *fname = "/home/bennylp/Desktop/installer/video/movies/pirates.avi";
+#endif
+
+    pj_bzero(&avi_port, sizeof(avi_port));
+
+    status = pjmedia_avi_player_create_streams(pool, fname, 0, &avi_streams);
+    if (status != PJ_SUCCESS) {
+	PJ_PERROR(2,("", status, "    Error playing %s (ignored)", fname));
+	rc = 210; goto on_return;
+    }
+
+    vid_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams,
+                                                         0,
+                                                         PJMEDIA_TYPE_VIDEO);
+    vid_port = pjmedia_avi_stream_get_port(vid_stream);
+
+    if (vid_port) {
+        pjmedia_vid_port_param_default(&param);
+
+        status = pjmedia_vid_dev_default_param(pool,
+                                               PJMEDIA_VID_DEFAULT_RENDER_DEV,
+                                               &param.vidparam);
+        if (status != PJ_SUCCESS) {
+    	    rc = 220; goto on_return;
+        }
+
+        /* Create renderer, set it to active  */
+        param.active = PJ_TRUE;
+        param.vidparam.dir = PJMEDIA_DIR_RENDER;
+        vfd = pjmedia_format_get_video_format_detail(&vid_port->info.fmt,
+                                                     PJ_TRUE);
+        pjmedia_format_init_video(&param.vidparam.fmt, 
+                                  vid_port->info.fmt.id,
+                                  vfd->size.w, vfd->size.h,
+                                  vfd->fps.num, vfd->fps.denum);
+
+        if (vid_port->info.fmt.id == PJMEDIA_FORMAT_MJPEG ||
+            vid_port->info.fmt.id == PJMEDIA_FORMAT_H263)
+        {
+            /* Prepare codec */
+            pj_str_t codec_id_st;
+            unsigned info_cnt = 1, i, k;
+            const pjmedia_vid_codec_info *codec_info;
+            pj_str_t port_name = {"codec", 5};
+            pj_uint8_t *enc_buf = NULL;
+            pj_size_t enc_buf_size = 0;
+            pjmedia_vid_dev_info rdr_info;
+            pjmedia_port codec_port;
+            codec_port_data_t codec_port_data;
+            pjmedia_vid_codec_param codec_param;
+            struct {
+                pj_uint32_t     pjmedia_id;
+                const char     *codec_id;
+            } codec_fmts[] = 
+                {{PJMEDIA_FORMAT_MJPEG, "mjpeg"},
+                 {PJMEDIA_FORMAT_H263, "h263"}};
+            const char *codec_id = NULL;
+
+
+            status = pjmedia_codec_ffmpeg_init(NULL, mem);
+            if (status != PJ_SUCCESS)
+                return -20;
+
+            /* Lookup codec */
+            for (i = 0; i < sizeof(codec_fmts)/sizeof(codec_fmts[0]); i++) {
+                if (vid_port->info.fmt.id == codec_fmts[i].pjmedia_id) {
+                    codec_id = codec_fmts[i].codec_id;
+                    break;
+                }
+            }
+            if (!codec_id) {
+                rc = 242; goto on_return;
+            }
+            pj_cstr(&codec_id_st, codec_id);
+            status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL,
+                                                             &codec_id_st, 
+                                                             &info_cnt, 
+                                                             &codec_info,
+                                                             NULL);
+            if (status != PJ_SUCCESS) {
+                rc = 245; goto on_return;
+            }
+            status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info,
+                                                             &codec_param);
+            if (status != PJ_SUCCESS) {
+                rc = 246; goto on_return;
+            }
+
+            pjmedia_vid_dev_get_info(param.vidparam.rend_id, &rdr_info);
+            for (i=0; i<codec_info->dec_fmt_id_cnt; ++i) {
+                for (k=0; k<rdr_info.fmt_cnt; ++k) {
+                    if (codec_info->dec_fmt_id[i]==(int)rdr_info.fmt[k].id)
+                    {
+                        param.vidparam.fmt.id = codec_info->dec_fmt_id[i];
+                    }
+                }
+            }
+
+            /* Open codec */
+            status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info,
+                                                       &codec);
+            if (status != PJ_SUCCESS) {
+                rc = 250; goto on_return;
+            }
+
+            status = codec->op->init(codec, pool);
+            if (status != PJ_SUCCESS) {
+                rc = 251; goto on_return;
+            }
+
+            pjmedia_format_copy(&codec_param.dec_fmt, &param.vidparam.fmt);
+
+            status = codec->op->open(codec, &codec_param);
+            if (status != PJ_SUCCESS) {
+                rc = 252; goto on_return;
+            }
+
+            /* Alloc encoding buffer */
+            enc_buf_size =  codec_param.dec_fmt.det.vid.size.w *
+                            codec_param.dec_fmt.det.vid.size.h * 4
+                            + 16; /*< padding, just in case */
+            enc_buf = pj_pool_alloc(pool,enc_buf_size);
+
+            /* Init codec port */
+            pj_bzero(&codec_port, sizeof(codec_port));
+            status = pjmedia_port_info_init2(&codec_port.info, &port_name,
+                                             0x1234,
+                                             PJMEDIA_DIR_ENCODING, 
+                                             &codec_param.dec_fmt);
+            if (status != PJ_SUCCESS) {
+                rc = 260; goto on_return;
+            }
+            pj_bzero(&codec_port_data, sizeof(codec_port_data));
+            codec_port_data.codec = codec;
+            codec_port_data.src_port = vid_port;
+            codec_port_data.enc_buf = enc_buf;
+            codec_port_data.enc_buf_size = enc_buf_size;
+
+            codec_port.get_frame = &codec_get_frame;
+            codec_port.port_data.pdata = &codec_port_data;
+
+            if (vid_port->info.fmt.id == PJMEDIA_FORMAT_MJPEG) {
+                pjmedia_conversion_param conv_param;
+
+                status = pjmedia_libswscale_converter_init(NULL, pool);
+
+                pjmedia_format_copy(&conv_param.src, &param.vidparam.fmt);
+                pjmedia_format_copy(&conv_param.dst, &param.vidparam.fmt);
+                conv_param.dst.id = PJMEDIA_FORMAT_I420;
+                param.vidparam.fmt.id = conv_param.dst.id;
+
+                status = pjmedia_converter_create(NULL, pool, &conv_param,
+                                                  &codec_port_data.conv);
+                if (status != PJ_SUCCESS) {
+                    rc = 270; goto on_return;
+                }
+            }
+
+            status = pjmedia_vid_port_create(pool, &param, &renderer);
+            if (status != PJ_SUCCESS) {
+                rc = 230; goto on_return;
+            }
+
+            status = pjmedia_vid_port_connect(renderer, &codec_port,
+                                              PJ_FALSE);
+        } else {
+            status = pjmedia_vid_port_create(pool, &param, &renderer);
+            if (status != PJ_SUCCESS) {
+                rc = 230; goto on_return;
+            }
+
+            /* Connect avi port to renderer */
+            status = pjmedia_vid_port_connect(renderer, vid_port,
+                                              PJ_FALSE);
+        }
+
+        if (status != PJ_SUCCESS) {
+            rc = 240; goto on_return;
+        }
+    }
+
+    aud_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams,
+                                                         0,
+                                                         PJMEDIA_TYPE_AUDIO);
+    aud_port = pjmedia_avi_stream_get_port(aud_stream);
+
+    if (aud_port) {
+        status = pjmedia_aud_dev_default_param(
+                     PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV,
+                     &aparam);
+        if (status != PJ_SUCCESS) {
+            rc = 310; goto on_return;
+        }
+
+        aparam.dir = PJMEDIA_DIR_PLAYBACK;
+        afd = pjmedia_format_get_audio_format_detail(&aud_port->info.fmt,
+                                                     PJ_TRUE);
+        aparam.clock_rate = afd->clock_rate;
+        aparam.channel_count = afd->channel_count;
+        aparam.bits_per_sample = afd->bits_per_sample;
+        aparam.samples_per_frame = afd->frame_time_usec * aparam.clock_rate *
+                                   aparam.channel_count / 1000000;
+
+        status = pjmedia_aud_stream_create(&aparam, NULL, &avi_play_cb,
+                                           aud_port,
+                                           &strm);
+        if (status != PJ_SUCCESS) {
+            rc = 320; goto on_return;
+        }
+
+        /* Start audio streaming.. */
+        status = pjmedia_aud_stream_start(strm);
+        if (status != PJ_SUCCESS) {
+            rc = 330; goto on_return;
+        }
+    }
+
+    if (vid_port) {
+        pjmedia_vid_cb cb;
+
+        pj_bzero(&cb, sizeof(cb));
+        cb.on_event_cb = avi_event_cb;
+        avi_port.aud_stream = strm;
+        avi_port.vid_port = renderer;
+        avi_port.is_running = PJ_TRUE;
+        pjmedia_vid_port_set_cb(renderer, &cb, &avi_port);
+
+        /* Start video streaming.. */
+        status = pjmedia_vid_port_start(renderer);
+        if (status != PJ_SUCCESS) {
+            rc = 270; goto on_return;
+        }
+    }
+
+    pj_thread_sleep(150000);
+
+on_return:
+    if (strm) {
+	pjmedia_aud_stream_stop(strm);
+	pjmedia_aud_stream_destroy(strm);
+    }
+    if (renderer)
+        pjmedia_vid_port_destroy(renderer);
+    if (vid_port)
+        pjmedia_port_destroy(vid_port);
+    if (codec) {
+        codec->op->close(codec);
+        pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec);
+    }
+
+    return rc;
+}
+
+static int loopback_test(pj_pool_t *pool)
+{
+    pjmedia_vid_port *capture=NULL, *renderer=NULL;
+    pjmedia_vid_port_param param;
+    pjmedia_video_format_detail *vfd;
+    pj_status_t status;
+    int rc = 0;
+
+    PJ_LOG(3, (THIS_FILE, "  loopback test"));
+
+    pjmedia_vid_port_param_default(&param);
+
+    /* Create capture, set it to active (master) */
+    status = pjmedia_vid_dev_default_param(pool,
+                                           PJMEDIA_VID_DEFAULT_CAPTURE_DEV,
+//                                           3, /* Hard-coded capture device */
+					   &param.vidparam);
+    if (status != PJ_SUCCESS) {
+	rc = 100; goto on_return;
+    }
+    param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
+    param.active = PJ_TRUE;
+
+    if (param.vidparam.fmt.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO) {
+	rc = 103; goto on_return;
+    }
+
+    vfd = pjmedia_format_get_video_format_detail(&param.vidparam.fmt, PJ_TRUE);
+    if (vfd == NULL) {
+	rc = 105; goto on_return;
+    }
+
+    status = pjmedia_vid_port_create(pool, &param, &capture);
+    if (status != PJ_SUCCESS) {
+	rc = 110; goto on_return;
+    }
+
+    /* Create renderer, set it to passive (slave)  */
+    param.active = PJ_FALSE;
+    param.vidparam.dir = PJMEDIA_DIR_RENDER;
+    param.vidparam.rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
+//    param.vidparam.rend_id = 6; /* Hard-coded render device */
+    param.vidparam.disp_size = vfd->size;
+
+    status = pjmedia_vid_port_create(pool, &param, &renderer);
+    if (status != PJ_SUCCESS) {
+	rc = 130; goto on_return;
+    }
+
+    /* Connect capture to renderer */
+    status = pjmedia_vid_port_connect(capture,
+				      pjmedia_vid_port_get_passive_port(renderer),
+				      PJ_FALSE);
+    if (status != PJ_SUCCESS) {
+	rc = 140; goto on_return;
+    }
+
+    /* Start streaming.. */
+    status = pjmedia_vid_port_start(renderer);
+    if (status != PJ_SUCCESS) {
+	rc = 150; goto on_return;
+    }
+    status = pjmedia_vid_port_start(capture);
+    if (status != PJ_SUCCESS) {
+	rc = 160; goto on_return;
+    }
+
+    /* Sleep while the webcam is being displayed... */
+    pj_thread_sleep(20000);
+
+on_return:
+    PJ_PERROR(3, (THIS_FILE, status, "  error"));
+    if (capture)
+	pjmedia_vid_port_destroy(capture);
+    if (renderer)
+	pjmedia_vid_port_destroy(renderer);
+
+    return rc;
+}
+
+int vid_dev_test(void)
+{
+    pj_pool_t *pool;
+    int rc = 0;
+    pj_status_t status;
+
+    PJ_LOG(3, (THIS_FILE, "Video device tests.."));
+
+    pool = pj_pool_create(mem, "Viddev test", 256, 256, 0);
+
+    status = pjmedia_vid_subsys_init(mem);
+    if (status != PJ_SUCCESS)
+        return -10;
+
+    status = pjmedia_aud_subsys_init(mem);
+    if (status != PJ_SUCCESS) {
+        return -20;
+    }
+
+    rc = enum_devs();
+    if (rc != 0)
+	goto on_return;
+
+    rc = aviplay_test(pool);
+    //if (rc != 0)
+    //    goto on_return;
+    // Ignore error
+    rc = 0;
+
+    rc = loopback_test(pool);
+    if (rc != 0)
+	goto on_return;
+
+on_return:
+    pjmedia_aud_subsys_shutdown();
+    pjmedia_vid_subsys_shutdown();
+    pj_pool_release(pool);
+
+    return rc;
+}
+
+