Index: /pjproject/trunk/aconfigure
===================================================================
--- /pjproject/trunk/aconfigure	(revision 6109)
+++ /pjproject/trunk/aconfigure	(revision 6110)
@@ -651,4 +651,6 @@
 ac_ssl_has_aes_gcm
 ac_no_ssl
+ac_vpx_ldflags
+ac_vpx_cflags
 ac_openh264_ldflags
 ac_openh264_cflags
@@ -815,4 +817,6 @@
 with_openh264
 enable_openh264
+with_vpx
+enable_vpx
 enable_ipp
 with_ipp
@@ -1486,4 +1490,5 @@
   --disable-v4l2          Disable Video4Linux2 (default: not disabled)
   --disable-openh264      Disable OpenH264 (default: not disabled)
+  --disable-vpx           Disable VPX (default: not disabled)
   --enable-ipp            Enable Intel IPP support. Specify the Intel IPP
                           package and samples location using IPPROOT and
@@ -1541,4 +1546,5 @@
   --with-ffmpeg=DIR       Specify alternate FFMPEG prefix
   --with-openh264=DIR     Specify alternate OpenH264 prefix
+  --with-vpx=DIR          Specify alternate VPX prefix
   --with-ipp=DIR          Specify the Intel IPP location
   --with-ipp-samples=DIR  Specify the Intel IPP samples location
@@ -7610,4 +7616,90 @@
 
 
+# Check whether --with-vpx was given.
+if test "${with_vpx+set}" = set; then :
+  withval=$with_vpx;
+else
+  with_vpx=no
+
+fi
+
+
+if test "x$ac_cross_compile" != "x" -a "x$with_vpx" = "xno"; then
+    enable_vpx=no
+fi
+
+
+
+# Check whether --enable-vpx was given.
+if test "${enable_vpx+set}" = set; then :
+  enableval=$enable_vpx;
+		if test "$enable_vpx" = "no"; then
+		  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if VPX is disabled... yes" >&5
+$as_echo "Checking if VPX is disabled... yes" >&6; }
+		fi
+
+else
+
+		  if test "x$with_vpx" != "xno" -a "x$with_vpx" != "x"; then
+		        VPX_PREFIX=$with_vpx
+		  	VPX_CFLAGS="-I$VPX_PREFIX/include"
+			VPX_LDFLAGS="-L$VPX_PREFIX/lib"
+			{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Using VPX prefix... $with_vpx" >&5
+$as_echo "Using VPX prefix... $with_vpx" >&6; }
+		  else
+		  	VPX_CFLAGS=""
+			VPX_LDFLAGS=""
+		  fi
+
+		  { $as_echo "$as_me:${as_lineno-$LINENO}: checking VPX usability" >&5
+$as_echo_n "checking VPX usability... " >&6; }
+
+		  VPX_LIBS="-lvpx"
+
+		  SAVED_LIBS="$LIBS"
+		  SAVED_LDFLAGS="$LDFLAGS"
+		  SAVED_CFLAGS="$CFLAGS"
+
+		  LIBS="$VPX_LIBS $LIBS"
+		  LDFLAGS="$VPX_LDFLAGS $LDFLAGS"
+		  CFLAGS="$VPX_CFLAGS $CFLAGS"
+
+		  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <vpx/vpx_encoder.h>
+						    #include <vpx/vp8cx.h>
+
+int
+main ()
+{
+vpx_codec_iface_t *(*enc_if)() = &vpx_codec_vp8_cx;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+   ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS"
+		  		   ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS"
+		  		   { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+
+else
+
+				   LIBS="$SAVED_LIBS"
+				   LDFLAGS="$SAVED_LDFLAGS"
+				   CFLAGS="$SAVED_CFLAGS"
+		  		   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+
+
+fi
+
+
+
 # Check whether --enable-ipp was given.
 if test "${enable_ipp+set}" = set; then :
@@ -7962,5 +8054,5 @@
 main ()
 {
-if (__builtin_available(macOS 10.3, iOS 11.3, *)) {
+if (__builtin_available(macOS 10.12, iOS 10.0, *)) {
 			     SSLContextRef ssl_ctx;
 			     SSLReHandshake(ssl_ctx);
@@ -7981,9 +8073,9 @@
 
 		        LIBS="$LIBS -framework Security"
-	  	        { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if Security framework for Darwin SSL is available... yes" >&5
-$as_echo "Checking if Security framework for Darwin SSL is available... yes" >&6; }
+	  	        { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if Darwin SSL is available... yes" >&5
+$as_echo "Checking if Darwin SSL is available... yes" >&6; }
 	            else
-	                { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if Security framework for Darwin SSL is available... no" >&5
-$as_echo "Checking if Security framework for Darwin SSL is available... no" >&6; }
+	                { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if Darwin SSL is available... no" >&5
+$as_echo "Checking if Darwin SSL is available... no" >&6; }
 		    fi
 		    ;;
Index: /pjproject/trunk/aconfigure.ac
===================================================================
--- /pjproject/trunk/aconfigure.ac	(revision 6109)
+++ /pjproject/trunk/aconfigure.ac	(revision 6110)
@@ -1298,4 +1298,69 @@
 	      ])
 
+dnl # VPX alt prefix
+AC_ARG_WITH(vpx,
+            AS_HELP_STRING([--with-vpx=DIR],
+		           [Specify alternate VPX prefix]),
+            [],
+            [with_vpx=no]
+            )
+
+dnl # Do not use default VPX installation if we are cross-compiling
+if test "x$ac_cross_compile" != "x" -a "x$with_vpx" = "xno"; then
+    enable_vpx=no
+fi
+
+dnl # VPX
+AC_SUBST(ac_vpx_cflags)
+AC_SUBST(ac_vpx_ldflags)
+AC_ARG_ENABLE(vpx,
+	      AS_HELP_STRING([--disable-vpx],
+			     [Disable VPX (default: not disabled)]),
+	      [
+		if test "$enable_vpx" = "no"; then
+		  AC_MSG_RESULT([Checking if VPX is disabled... yes])
+		fi
+	      ],
+	      [
+		  if test "x$with_vpx" != "xno" -a "x$with_vpx" != "x"; then
+		        VPX_PREFIX=$with_vpx
+		  	VPX_CFLAGS="-I$VPX_PREFIX/include"
+			VPX_LDFLAGS="-L$VPX_PREFIX/lib" 
+			AC_MSG_RESULT([Using VPX prefix... $with_vpx])
+		  else
+		  	VPX_CFLAGS=""
+			VPX_LDFLAGS="" 
+		  fi
+
+		  AC_MSG_CHECKING([VPX usability])
+
+		  VPX_LIBS="-lvpx"
+		  
+		  SAVED_LIBS="$LIBS"
+		  SAVED_LDFLAGS="$LDFLAGS"
+		  SAVED_CFLAGS="$CFLAGS"
+		  
+		  LIBS="$VPX_LIBS $LIBS"
+		  LDFLAGS="$VPX_LDFLAGS $LDFLAGS"
+		  CFLAGS="$VPX_CFLAGS $CFLAGS"
+		  
+		  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <vpx/vpx_encoder.h>
+						    #include <vpx/vp8cx.h>
+					          ]],
+					          [vpx_codec_iface_t *(*enc_if)() = &vpx_codec_vp8_cx;]
+					         )],
+		  		 [ ac_vpx_cflags="-DPJMEDIA_HAS_VPX_CODEC=1 $VPX_CFLAGS"
+		  		   ac_vpx_ldflags="$VPX_LDFLAGS $VPX_LIBS"
+		  		   AC_MSG_RESULT(ok)
+		  		  ],
+		  		 [
+				   LIBS="$SAVED_LIBS"
+				   LDFLAGS="$SAVED_LDFLAGS"
+				   CFLAGS="$SAVED_CFLAGS"
+		  		   AC_MSG_RESULT(no)
+		  		 ])
+
+	      ])
+
 
 dnl ########################################################
Index: /pjproject/trunk/build.mak.in
===================================================================
--- /pjproject/trunk/build.mak.in	(revision 6109)
+++ /pjproject/trunk/build.mak.in	(revision 6110)
@@ -174,4 +174,8 @@
 OPENH264_LDFLAGS =  @ac_openh264_ldflags@
 
+# VPX flags
+VPX_CFLAGS = @ac_vpx_cflags@ 
+VPX_LDFLAGS =  @ac_vpx_ldflags@
+
 # QT
 AC_PJMEDIA_VIDEO_HAS_QT = @ac_pjmedia_video_has_qt@
@@ -189,7 +193,7 @@
 # PJMEDIA features exclusion
 PJ_VIDEO_CFLAGS += $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \
-		   $(OPENH264_CFLAGS) $(DARWIN_CFLAGS)
+		   $(OPENH264_CFLAGS) $(VPX_CFLAGS) $(DARWIN_CFLAGS)
 PJ_VIDEO_LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) \
-                   $(OPENH264_LDFLAGS)
+                   $(OPENH264_LDFLAGS) $(VPX_LDFLAGS)
 
 # CFLAGS, LDFLAGS, and LIBS to be used by applications
Index: /pjproject/trunk/pjmedia/build/Makefile
===================================================================
--- /pjproject/trunk/pjmedia/build/Makefile	(revision 6109)
+++ /pjproject/trunk/pjmedia/build/Makefile	(revision 6110)
@@ -140,5 +140,6 @@
 			$(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
 			ipp_codecs.o silk.o opus.o $(CODEC_OBJS) \
-                        g7221_sdp_match.o amr_sdp_match.o passthrough.o
+                        g7221_sdp_match.o amr_sdp_match.o passthrough.o \
+                        vpx.o
 export PJMEDIA_CODEC_CFLAGS += $(_CFLAGS) $(GSM_CFLAGS) $(SPEEX_CFLAGS) \
 			$(ILBC_CFLAGS) $(IPP_CFLAGS) $(G7221_CFLAGS)
Index: /pjproject/trunk/pjmedia/build/os-auto.mak.in
===================================================================
--- /pjproject/trunk/pjmedia/build/os-auto.mak.in	(revision 6109)
+++ /pjproject/trunk/pjmedia/build/os-auto.mak.in	(revision 6110)
@@ -33,4 +33,8 @@
 OPENH264_LDFLAGS = @ac_openh264_ldflags@
 
+# vpx
+VPX_CFLAGS = @ac_vpx_cflags@
+VPX_LDFLAGS = @ac_vpx_ldflags@
+
 
 # PJMEDIA features exclusion
@@ -38,7 +42,7 @@
 		 $(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \
 		 $(DARWIN_CFLAGS) $(ANDROID_CFLAGS) \
-		 $(OPENH264_CFLAGS)
+		 $(OPENH264_CFLAGS) $(VPX_CFLAGS)
 export LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) \
-		  $(OPENH264_LDFLAGS)
+		  $(OPENH264_LDFLAGS) $(VPX_LDFLAGS)
 
 # Define the desired sound device backend
Index: /pjproject/trunk/pjmedia/include/pjmedia-codec.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia-codec.h	(revision 6109)
+++ /pjproject/trunk/pjmedia/include/pjmedia-codec.h	(revision 6110)
@@ -27,20 +27,20 @@
 
 #include <pjmedia-codec/audio_codecs.h>
-#include <pjmedia-codec/l16.h>
+#include <pjmedia-codec/bcg729.h>
 #include <pjmedia-codec/ffmpeg_vid_codecs.h>
-#include <pjmedia-codec/gsm.h>
-#include <pjmedia-codec/speex.h>
-#include <pjmedia-codec/ilbc.h>
 #include <pjmedia-codec/g722.h>
 #include <pjmedia-codec/g7221.h>
+#include <pjmedia-codec/gsm.h>
+#include <pjmedia-codec/ilbc.h>
 #include <pjmedia-codec/ipp_codecs.h>
+#include <pjmedia-codec/l16.h>
 #include <pjmedia-codec/opencore_amr.h>
-#include <pjmedia-codec/vid_toolbox.h>
 #include <pjmedia-codec/openh264.h>
+#include <pjmedia-codec/opus.h>
 #include <pjmedia-codec/passthrough.h>
 #include <pjmedia-codec/silk.h>
-#include <pjmedia-codec/opus.h>
-#include <pjmedia-codec/bcg729.h>
-
+#include <pjmedia-codec/speex.h>
+#include <pjmedia-codec/vid_toolbox.h>
+#include <pjmedia-codec/vpx.h>
 
 #endif	/* __PJMEDIA_CODEC_PJMEDIA_CODEC_H__ */
Index: /pjproject/trunk/pjmedia/include/pjmedia-codec/config.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia-codec/config.h	(revision 6109)
+++ /pjproject/trunk/pjmedia/include/pjmedia-codec/config.h	(revision 6110)
@@ -581,4 +581,23 @@
 #   define PJMEDIA_CODEC_OPENH264_LOG_LEVEL  WELS_LOG_ERROR
 #endif
+
+/**
+ * Enable VPX VP8 codec.
+ *
+ * Default: 1
+ */
+#ifndef PJMEDIA_HAS_VPX_CODEC_VP8
+#   define PJMEDIA_HAS_VPX_CODEC_VP8		1
+#endif
+
+/**
+ * Enable VPX VP9 codec.
+ *
+ * Default: 0 (disabled)
+ */
+#ifndef PJMEDIA_HAS_VPX_CODEC_VP9
+#   define PJMEDIA_HAS_VPX_CODEC_VP9		0
+#endif
+
 /**
  * @}
Index: /pjproject/trunk/pjmedia/include/pjmedia-codec/types.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia-codec/types.h	(revision 6109)
+++ /pjproject/trunk/pjmedia/include/pjmedia-codec/types.h	(revision 6110)
@@ -126,4 +126,7 @@
      PJMEDIA_RTP_PT_H264_RSV4,
 
+     PJMEDIA_RTP_PT_VP8,
+     PJMEDIA_RTP_PT_VP9,
+
      /* Caution!
       * Ensure the value of the last pt above is <= 127.
Index: /pjproject/trunk/pjmedia/include/pjmedia-codec/vpx.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia-codec/vpx.h	(revision 6110)
+++ /pjproject/trunk/pjmedia/include/pjmedia-codec/vpx.h	(revision 6110)
@@ -0,0 +1,69 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2019 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_CODEC_VPX_H__
+#define __PJMEDIA_CODEC_VPX_H__
+
+#include <pjmedia-codec/types.h>
+#include <pjmedia/vid_codec.h>
+
+/**
+ * @file pjmedia-codec/vpx.h
+ * @brief VPX codec
+ */
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMEDIA_CODEC_VPX VPX Codec
+ * @ingroup PJMEDIA_CODEC_VID_CODECS
+ * @{
+ */
+
+/**
+ * Initialize and register VPX codec factory.
+ *
+ * @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_vpx_vid_init(pjmedia_vid_codec_mgr *mgr,
+                                                pj_pool_factory *pf);
+
+/**
+ * Unregister VPX video codecs factory from the video codec manager and
+ * deinitialize the codec library.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_codec_vpx_vid_deinit(void);
+
+
+/**
+ * @}  PJMEDIA_CODEC_VPX
+ */
+
+
+PJ_END_DECL
+
+#endif	/* __PJMEDIA_CODEC_VPX_H__ */
Index: /pjproject/trunk/pjmedia/include/pjmedia/format.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/format.h	(revision 6109)
+++ /pjproject/trunk/pjmedia/include/pjmedia/format.h	(revision 6110)
@@ -219,4 +219,7 @@
     PJMEDIA_FORMAT_H263P    = PJMEDIA_FORMAT_PACK('P', '2', '6', '3'),
     PJMEDIA_FORMAT_H264     = PJMEDIA_FORMAT_PACK('H', '2', '6', '4'),
+
+    PJMEDIA_FORMAT_VP8      = PJMEDIA_FORMAT_PACK('V', 'P', '8', '0'),
+    PJMEDIA_FORMAT_VP9      = PJMEDIA_FORMAT_PACK('V', 'P', '9', '0'),
 
     PJMEDIA_FORMAT_MJPEG    = PJMEDIA_FORMAT_PACK('M', 'J', 'P', 'G'),
Index: /pjproject/trunk/pjmedia/include/pjmedia/vid_codec_util.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/vid_codec_util.h	(revision 6109)
+++ /pjproject/trunk/pjmedia/include/pjmedia/vid_codec_util.h	(revision 6110)
@@ -153,4 +153,41 @@
 
 
+/**
+ * Definition of VPX parameters.
+ */
+typedef struct pjmedia_vid_codec_vpx_fmtp
+{
+    unsigned	    max_fr;	    /**< Max frame rate    		    */
+    unsigned	    max_fs;	    /**< Max frame size (in macroblocks)    */
+    pj_uint8_t	    profile_id;     /**< Profile ID			    */
+} pjmedia_vid_codec_vpx_fmtp;
+
+
+/**
+ * Parse SDP fmtp of VPX.
+ *
+ * @param fmtp		The VPX SDP fmtp to be parsed.
+ * @param vpx_fmtp	The parsing result.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_vpx_parse_fmtp(
+				const pjmedia_codec_fmtp *fmtp,
+				pjmedia_vid_codec_vpx_fmtp *vpx_fmtp);
+
+
+/**
+ * Parse and apply the encoding and decoding SDP fmtp of VPX in the
+ * specified codec parameter. This will validate size and fps to conform
+ * to VPX level specified in SDP fmtp "max-fr" and "max-fs".
+ *
+ * @param param		The codec parameter.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_vpx_apply_fmtp(
+				pjmedia_vid_codec_param *param);
+
+
 PJ_END_DECL
 
Index: /pjproject/trunk/pjmedia/src/pjmedia-codec/vpx.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-codec/vpx.c	(revision 6110)
+++ /pjproject/trunk/pjmedia/src/pjmedia-codec/vpx.c	(revision 6110)
@@ -0,0 +1,945 @@
+/* $Id$ */
+/* 
+ * Copyright (C)2019 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/vpx.h>
+#include <pjmedia/vid_codec_util.h>
+#include <pjmedia/errno.h>
+#include <pj/log.h>
+#include <pj/math.h>
+
+#if defined(PJMEDIA_HAS_VPX_CODEC) && \
+            PJMEDIA_HAS_VPX_CODEC != 0 && \
+    defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+#ifdef _MSC_VER
+#   pragma comment( lib, "vpx.lib")
+#endif
+
+/* VPX */
+#include <vpx/vpx_encoder.h>
+#include <vpx/vpx_decoder.h>
+#include <vpx/vp8cx.h>
+#include <vpx/vp8dx.h>
+
+/*
+ * Constants
+ */
+#define THIS_FILE		"vpx.c"
+
+#if (defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE) || \
+     defined(__ANDROID__)
+#  define DEFAULT_WIDTH		352
+#  define DEFAULT_HEIGHT	288
+#else
+#  define DEFAULT_WIDTH		720
+#  define DEFAULT_HEIGHT	480
+#endif
+
+#define DEFAULT_FPS		15
+#define DEFAULT_AVG_BITRATE	200000
+#define DEFAULT_MAX_BITRATE	200000
+
+#define MAX_RX_RES		1200
+
+
+/*
+ * Factory operations.
+ */
+static pj_status_t vpx_test_alloc(pjmedia_vid_codec_factory *factory,
+                                  const pjmedia_vid_codec_info *info );
+static pj_status_t vpx_default_attr(pjmedia_vid_codec_factory *factory,
+                                    const pjmedia_vid_codec_info *info,
+                                    pjmedia_vid_codec_param *attr );
+static pj_status_t vpx_enum_info(pjmedia_vid_codec_factory *factory,
+                                 unsigned *count,
+                                 pjmedia_vid_codec_info codecs[]);
+static pj_status_t vpx_alloc_codec(pjmedia_vid_codec_factory *factory,
+                                   const pjmedia_vid_codec_info *info,
+                                   pjmedia_vid_codec **p_codec);
+static pj_status_t vpx_dealloc_codec(pjmedia_vid_codec_factory *factory,
+                                     pjmedia_vid_codec *codec );
+
+
+/*
+ * Codec operations
+ */
+static pj_status_t vpx_codec_init(pjmedia_vid_codec *codec,
+                                  pj_pool_t *pool );
+static pj_status_t vpx_codec_open(pjmedia_vid_codec *codec,
+                                  pjmedia_vid_codec_param *param );
+static pj_status_t vpx_codec_close(pjmedia_vid_codec *codec);
+static pj_status_t vpx_codec_modify(pjmedia_vid_codec *codec,
+                                    const pjmedia_vid_codec_param *param);
+static pj_status_t vpx_codec_get_param(pjmedia_vid_codec *codec,
+                                       pjmedia_vid_codec_param *param);
+static pj_status_t vpx_codec_encode_begin(pjmedia_vid_codec *codec,
+                                          const pjmedia_vid_encode_opt *opt,
+                                          const pjmedia_frame *input,
+                                          unsigned out_size,
+                                          pjmedia_frame *output,
+                                          pj_bool_t *has_more);
+static pj_status_t vpx_codec_encode_more(pjmedia_vid_codec *codec,
+                                         unsigned out_size,
+                                         pjmedia_frame *output,
+                                         pj_bool_t *has_more);
+static pj_status_t vpx_codec_decode_(pjmedia_vid_codec *codec,
+                                     pj_size_t count,
+                                     pjmedia_frame packets[],
+                                     unsigned out_size,
+                                     pjmedia_frame *output);
+
+/* Definition for VPX codecs operations. */
+static pjmedia_vid_codec_op vpx_codec_op =
+{
+    &vpx_codec_init,
+    &vpx_codec_open,
+    &vpx_codec_close,
+    &vpx_codec_modify,
+    &vpx_codec_get_param,
+    &vpx_codec_encode_begin,
+    &vpx_codec_encode_more,
+    &vpx_codec_decode_,
+    NULL
+};
+
+/* Definition for VPX codecs factory operations. */
+static pjmedia_vid_codec_factory_op vpx_factory_op =
+{
+    &vpx_test_alloc,
+    &vpx_default_attr,
+    &vpx_enum_info,
+    &vpx_alloc_codec,
+    &vpx_dealloc_codec
+};
+
+
+static struct vpx_factory
+{
+    pjmedia_vid_codec_factory    base;
+    pjmedia_vid_codec_mgr	*mgr;
+    pj_pool_factory             *pf;
+    pj_pool_t		        *pool;
+} vpx_factory;
+
+
+typedef struct vpx_codec_data
+{
+    pj_pool_t			*pool;
+    pjmedia_vid_codec_param	*prm;
+    pj_bool_t			 whole;
+
+    /* Encoder */
+    vpx_codec_iface_t 		*(*enc_if)();
+    vpx_codec_ctx_t 		 enc;
+    vpx_codec_iter_t 		 enc_iter;
+    unsigned		 	 enc_input_size;
+    pj_uint8_t			*enc_frame_whole;
+    unsigned			 enc_frame_size;
+    unsigned			 enc_processed;
+    pj_bool_t			 enc_frame_is_keyframe;
+    pj_timestamp		 ets;
+
+    /* Decoder */
+    vpx_codec_iface_t 		*(*dec_if)();
+    vpx_codec_ctx_t 		 dec;
+    pj_uint8_t			*dec_buf;
+    unsigned			 dec_buf_size;
+} vpx_codec_data;
+
+
+PJ_DEF(pj_status_t) pjmedia_codec_vpx_vid_init(pjmedia_vid_codec_mgr *mgr,
+                                               pj_pool_factory *pf)
+{
+    pj_status_t status;
+
+    if (vpx_factory.pool != NULL) {
+	/* Already initialized. */
+	return PJ_SUCCESS;
+    }
+
+    if (!mgr) mgr = pjmedia_vid_codec_mgr_instance();
+    PJ_ASSERT_RETURN(mgr, PJ_EINVAL);
+
+    /* Create VPX codec factory. */
+    vpx_factory.base.op = &vpx_factory_op;
+    vpx_factory.base.factory_data = NULL;
+    vpx_factory.mgr = mgr;
+    vpx_factory.pf = pf;
+    vpx_factory.pool = pj_pool_create(pf, "vpxfactory", 256, 256, NULL);
+    if (!vpx_factory.pool)
+	return PJ_ENOMEM;
+
+    /* Register codec factory to codec manager. */
+    status = pjmedia_vid_codec_mgr_register_factory(mgr,
+						    &vpx_factory.base);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    PJ_LOG(4,(THIS_FILE, "VPX codec initialized"));
+
+    /* Done. */
+    return PJ_SUCCESS;
+
+on_error:
+    pj_pool_release(vpx_factory.pool);
+    vpx_factory.pool = NULL;
+    return status;
+}
+
+/*
+ * Unregister VPX codecs factory from pjmedia endpoint.
+ */
+PJ_DEF(pj_status_t) pjmedia_codec_vpx_vid_deinit(void)
+{
+    pj_status_t status = PJ_SUCCESS;
+
+    if (vpx_factory.pool == NULL) {
+	/* Already deinitialized */
+	return PJ_SUCCESS;
+    }
+
+    /* Unregister VPX codecs factory. */
+    status = pjmedia_vid_codec_mgr_unregister_factory(vpx_factory.mgr,
+						      &vpx_factory.base);
+
+    /* Destroy pool. */
+    pj_pool_release(vpx_factory.pool);
+    vpx_factory.pool = NULL;
+
+    return status;
+}
+
+static pj_status_t vpx_test_alloc(pjmedia_vid_codec_factory *factory,
+                                  const pjmedia_vid_codec_info *info )
+{
+    PJ_ASSERT_RETURN(factory == &vpx_factory.base, PJ_EINVAL);
+
+    if ((info->fmt_id == PJMEDIA_FORMAT_VP8 ||
+         info->fmt_id == PJMEDIA_FORMAT_VP9) && info->pt != 0)
+    {
+	return PJ_SUCCESS;
+    }
+
+    return PJMEDIA_CODEC_EUNSUP;
+}
+
+static pj_status_t vpx_default_attr(pjmedia_vid_codec_factory *factory,
+                                    const pjmedia_vid_codec_info *info,
+                                    pjmedia_vid_codec_param *attr )
+{
+    PJ_ASSERT_RETURN(factory == &vpx_factory.base, PJ_EINVAL);
+    PJ_ASSERT_RETURN(info && attr, PJ_EINVAL);
+
+    pj_bzero(attr, sizeof(pjmedia_vid_codec_param));
+
+    attr->dir = PJMEDIA_DIR_ENCODING_DECODING;
+    attr->packing = PJMEDIA_VID_PACKING_PACKETS;
+
+    /* Encoded format */
+    pjmedia_format_init_video(&attr->enc_fmt, info->fmt_id,
+                              DEFAULT_WIDTH, DEFAULT_HEIGHT,
+			      DEFAULT_FPS, 1);
+
+    /* Decoded format */
+    pjmedia_format_init_video(&attr->dec_fmt, PJMEDIA_FORMAT_I420,
+                              DEFAULT_WIDTH, DEFAULT_HEIGHT,
+			      DEFAULT_FPS, 1);
+
+    /* Decoding fmtp */
+    /* If the implementation is willing to receive media, both parameters
+     * MUST be provided.
+     */
+    attr->dec_fmtp.cnt = 2;
+    attr->dec_fmtp.param[0].name = pj_str((char*)"max-fr");
+    attr->dec_fmtp.param[0].val = pj_str((char*)"30");
+    attr->dec_fmtp.param[1].name = pj_str((char*)" max-fs");
+    attr->dec_fmtp.param[1].val = pj_str((char*)"580");
+
+    /* Bitrate */
+    attr->enc_fmt.det.vid.avg_bps = DEFAULT_AVG_BITRATE;
+    attr->enc_fmt.det.vid.max_bps = DEFAULT_MAX_BITRATE;
+
+    /* Encoding MTU */
+    attr->enc_mtu = PJMEDIA_MAX_VID_PAYLOAD_SIZE;
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vpx_enum_info(pjmedia_vid_codec_factory *factory,
+                                 unsigned *count,
+                                 pjmedia_vid_codec_info info[])
+{
+    unsigned i = 0;
+
+    PJ_ASSERT_RETURN(info && *count > 0, PJ_EINVAL);
+    PJ_ASSERT_RETURN(factory == &vpx_factory.base, PJ_EINVAL);
+
+#if PJMEDIA_HAS_VPX_CODEC_VP8
+    info[i].fmt_id = PJMEDIA_FORMAT_VP8;
+    info[i].pt = PJMEDIA_RTP_PT_VP8;
+    info[i].encoding_name = pj_str((char*)"VP8");
+    info[i].encoding_desc = pj_str((char*)"VPX VP8 codec");
+    i++;
+#endif
+
+#if PJMEDIA_HAS_VPX_CODEC_VP9
+    if (i + 1 < *count) {
+    	info[i].fmt_id = PJMEDIA_FORMAT_VP9;
+    	info[i].pt = PJMEDIA_RTP_PT_VP9;
+    	info[i].encoding_name = pj_str((char*)"VP9");
+    	info[i].encoding_desc = pj_str((char*)"VPX VP9 codec");
+    	i++;
+    }
+#endif
+
+    *count = i;
+    for (i = 0; i < *count; i++) {
+    	info[i].clock_rate = 90000;
+    	info[i].dir = PJMEDIA_DIR_ENCODING_DECODING;
+    	info[i].dec_fmt_id_cnt = 1;
+    	info[i].dec_fmt_id[0] = PJMEDIA_FORMAT_I420;
+    	info[i].packings = PJMEDIA_VID_PACKING_PACKETS;
+    	info[i].fps_cnt = 3;
+    	info[i].fps[0].num = 15;
+    	info[i].fps[0].denum = 1;
+    	info[i].fps[1].num = 25;
+    	info[i].fps[1].denum = 1;
+    	info[i].fps[2].num = 30;
+    	info[i].fps[2].denum = 1;
+    }
+
+    return PJ_SUCCESS;
+
+}
+
+static pj_status_t vpx_alloc_codec(pjmedia_vid_codec_factory *factory,
+                                   const pjmedia_vid_codec_info *info,
+                                   pjmedia_vid_codec **p_codec)
+{
+    pj_pool_t *pool;
+    pjmedia_vid_codec *codec;
+    vpx_codec_data *vpx_data;
+
+    PJ_ASSERT_RETURN(factory == &vpx_factory.base && info && p_codec,
+                     PJ_EINVAL);
+
+    *p_codec = NULL;
+
+    pool = pj_pool_create(vpx_factory.pf, "vpx%p", 512, 512, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    /* codec instance */
+    codec = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_codec);
+    codec->factory = factory;
+    codec->op = &vpx_codec_op;
+
+    /* codec data */
+    vpx_data = PJ_POOL_ZALLOC_T(pool, vpx_codec_data);
+    vpx_data->pool = pool;
+    codec->codec_data = vpx_data;
+
+    /* encoder and decoder interfaces */
+    if (info->fmt_id == PJMEDIA_FORMAT_VP8) {
+    	vpx_data->enc_if = &vpx_codec_vp8_cx;
+    	vpx_data->dec_if = &vpx_codec_vp8_dx;
+    } else if (info->fmt_id == PJMEDIA_FORMAT_VP9) {
+    	vpx_data->enc_if = &vpx_codec_vp9_cx;
+    	vpx_data->dec_if = &vpx_codec_vp9_dx;
+    }
+
+    *p_codec = codec;
+    return PJ_SUCCESS;
+
+on_error:
+    vpx_dealloc_codec(factory, codec);
+    return PJMEDIA_CODEC_EFAILED;
+}
+
+static pj_status_t vpx_dealloc_codec(pjmedia_vid_codec_factory *factory,
+                                     pjmedia_vid_codec *codec )
+{
+    vpx_codec_data *vpx_data;
+
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(factory);
+
+    vpx_data = (vpx_codec_data*) codec->codec_data;
+    vpx_data->enc_if = NULL;
+    vpx_data->dec_if = NULL;
+
+    pj_pool_release(vpx_data->pool);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vpx_codec_init(pjmedia_vid_codec *codec,
+                                  pj_pool_t *pool )
+{
+    PJ_ASSERT_RETURN(codec && pool, PJ_EINVAL);
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(pool);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vpx_codec_open(pjmedia_vid_codec *codec,
+                                  pjmedia_vid_codec_param *codec_param )
+{
+    vpx_codec_data 		*vpx_data;
+    pjmedia_vid_codec_param	*param;
+    pjmedia_vid_codec_vpx_fmtp   vpx_fmtp;
+    vpx_codec_enc_cfg_t cfg;
+    vpx_codec_err_t res;
+    unsigned max_res = MAX_RX_RES;
+    pj_status_t	status;
+
+    PJ_ASSERT_RETURN(codec && codec_param, PJ_EINVAL);
+
+    PJ_LOG(5,(THIS_FILE, "Opening codec.."));
+
+    vpx_data = (vpx_codec_data*) codec->codec_data;
+    vpx_data->prm = pjmedia_vid_codec_param_clone(vpx_data->pool,
+                                                  codec_param);
+    param = vpx_data->prm;
+    vpx_data->whole = (param->packing == PJMEDIA_VID_PACKING_WHOLE);
+
+    /* Apply SDP fmtp to format in codec param */
+    if (!param->ignore_fmtp) {
+	status = pjmedia_vid_codec_vpx_apply_fmtp(param);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /*
+     * Encoder
+     */
+
+    /* Init encoder parameters */
+    res = vpx_codec_enc_config_default(vpx_data->enc_if(), &cfg, 0);
+    if (res) {
+    	PJ_LOG(3, (THIS_FILE, "Failed to get encoder default config"));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+    
+    cfg.g_w = vpx_data->prm->enc_fmt.det.vid.size.w;
+    cfg.g_h = vpx_data->prm->enc_fmt.det.vid.size.h;
+    /* timebase is the inverse of fps */
+    cfg.g_timebase.num = vpx_data->prm->enc_fmt.det.vid.fps.denum;
+    cfg.g_timebase.den = vpx_data->prm->enc_fmt.det.vid.fps.num;
+    /* bitrate in KBps */
+    cfg.rc_target_bitrate = vpx_data->prm->enc_fmt.det.vid.avg_bps / 1000;
+    
+    cfg.g_pass = VPX_RC_ONE_PASS;
+    cfg.rc_end_usage = VPX_CBR;
+    cfg.g_threads = 4;
+    cfg.g_lag_in_frames = 0;
+    cfg.g_error_resilient = 0;
+    cfg.rc_undershoot_pct = 95;
+    cfg.rc_min_quantizer = 4;
+    cfg.rc_max_quantizer = 56;
+    cfg.rc_buf_initial_sz = 400;
+    cfg.rc_buf_optimal_sz = 500;
+    cfg.rc_buf_sz = 600;
+    cfg.kf_max_dist = 999999;
+    cfg.rc_resize_allowed = 0;
+    cfg.rc_dropframe_thresh = 25;
+
+    vpx_data->enc_input_size = cfg.g_w * cfg.g_h * 3 >> 1;
+
+    /* Initialize encoder */
+    res = vpx_codec_enc_init(&vpx_data->enc, vpx_data->enc_if(), &cfg, 0);
+    if (res) {
+    	PJ_LOG(3, (THIS_FILE, "Failed to initialize encoder"));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    /* Values greater than 0 will increase encoder speed at the expense of
+     * quality.
+     * Valid range for VP8: -16..16
+     * Valid range for VP9: -9..9
+     */
+    vpx_codec_control(&vpx_data->enc, VP8E_SET_CPUUSED, 9);
+
+    /*
+     * Decoder
+     */
+    res = vpx_codec_dec_init(&vpx_data->dec, vpx_data->dec_if(), NULL, 0);
+    if (res) {
+    	PJ_LOG(3, (THIS_FILE, "Failed to initialize decoder"));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    /* Parse local fmtp */
+    status = pjmedia_vid_codec_vpx_parse_fmtp(&param->dec_fmtp, &vpx_fmtp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (vpx_fmtp.max_fs > 0) {
+    	max_res = ((int)pj_isqrt(vpx_fmtp.max_fs * 8)) * 16;
+    }
+    vpx_data->dec_buf_size = (max_res * max_res * 3 >> 1) + (max_res);
+    vpx_data->dec_buf = (pj_uint8_t*)pj_pool_alloc(vpx_data->pool,
+                                                   vpx_data->dec_buf_size);
+
+    /* Need to update param back after values are negotiated */
+    pj_memcpy(codec_param, param, sizeof(*codec_param));
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vpx_codec_close(pjmedia_vid_codec *codec)
+{
+    struct vpx_codec_data *vpx_data;
+
+    PJ_ASSERT_RETURN(codec, PJ_EINVAL);
+
+    vpx_data = (vpx_codec_data*) codec->codec_data;
+    vpx_codec_destroy(&vpx_data->enc);
+    vpx_codec_destroy(&vpx_data->dec);
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vpx_codec_modify(pjmedia_vid_codec *codec,
+                                    const pjmedia_vid_codec_param *param)
+{
+    PJ_ASSERT_RETURN(codec && param, PJ_EINVAL);
+    PJ_UNUSED_ARG(codec);
+    PJ_UNUSED_ARG(param);
+    return PJ_EINVALIDOP;
+}
+
+static pj_status_t vpx_codec_get_param(pjmedia_vid_codec *codec,
+                                       pjmedia_vid_codec_param *param)
+{
+    struct vpx_codec_data *vpx_data;
+
+    PJ_ASSERT_RETURN(codec && param, PJ_EINVAL);
+
+    vpx_data = (vpx_codec_data*) codec->codec_data;
+    pj_memcpy(param, vpx_data->prm, sizeof(*param));
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t vpx_codec_encode_begin(pjmedia_vid_codec *codec,
+                                          const pjmedia_vid_encode_opt *opt,
+                                          const pjmedia_frame *input,
+                                          unsigned out_size,
+                                          pjmedia_frame *output,
+                                          pj_bool_t *has_more)
+{
+    struct vpx_codec_data *vpx_data;
+    vpx_image_t img;
+    vpx_enc_frame_flags_t flags = 0;
+    vpx_codec_err_t res;
+
+    PJ_ASSERT_RETURN(codec && input && out_size && output && has_more,
+                     PJ_EINVAL);
+
+    vpx_data = (vpx_codec_data*) codec->codec_data;
+
+    PJ_ASSERT_RETURN(input->size == vpx_data->enc_input_size,
+                     PJMEDIA_CODEC_EFRMINLEN);
+
+    vpx_img_wrap(&img, VPX_IMG_FMT_I420,
+            	 vpx_data->prm->enc_fmt.det.vid.size.w,
+            	 vpx_data->prm->enc_fmt.det.vid.size.h,
+            	 1, (unsigned char *)input->buf);
+
+    if (opt && opt->force_keyframe) {
+	flags |= VPX_EFLAG_FORCE_KF;
+    }
+
+    vpx_data->ets = input->timestamp;
+    vpx_data->enc_frame_size = vpx_data->enc_processed = 0;
+    vpx_data->enc_iter = NULL;
+
+    res = vpx_codec_encode(&vpx_data->enc, &img, vpx_data->ets.u64, 1,
+    			   flags, VPX_DL_REALTIME);
+    if (res) {
+	PJ_LOG(4, (THIS_FILE, "Failed to encode frame %s",
+		   vpx_codec_error(&vpx_data->enc)));
+	return PJMEDIA_CODEC_EFAILED;
+    }
+
+    do {
+    	const vpx_codec_cx_pkt_t *pkt;
+
+    	pkt = vpx_codec_get_cx_data(&vpx_data->enc, &vpx_data->enc_iter);
+    	if (!pkt) break;
+    	if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
+    	    /* We have a valid frame packet */
+    	    vpx_data->enc_frame_whole = pkt->data.frame.buf;
+            vpx_data->enc_frame_size = pkt->data.frame.sz;
+            vpx_data->enc_processed = 0;
+            if (pkt->data.frame.flags & VPX_FRAME_IS_KEY)
+            	vpx_data->enc_frame_is_keyframe = PJ_TRUE;
+            else
+            	vpx_data->enc_frame_is_keyframe = PJ_FALSE;
+            	
+            break;
+    	}
+    } while (1);
+
+    if (vpx_data->enc_frame_size == 0) {
+    	*has_more = PJ_FALSE;
+    	output->size = 0;
+    	output->type = PJMEDIA_FRAME_TYPE_NONE;
+
+	if (vpx_data->enc.err) {
+	    PJ_LOG(4, (THIS_FILE, "Failed to get encoded frame %s",
+		   		  vpx_codec_error(&vpx_data->enc)));
+	} else {
+	    return PJ_SUCCESS;
+	}
+    }
+    
+    if (vpx_data->whole) {
+    	*has_more = PJ_FALSE;
+	if (vpx_data->enc_frame_size > out_size)
+	    return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+        output->timestamp = vpx_data->ets;
+        output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+        output->size = vpx_data->enc_frame_size;
+        output->bit_info = 0;
+        if (vpx_data->enc_frame_is_keyframe) {
+            output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
+        }
+
+        pj_memcpy(output->buf, vpx_data->enc_frame_whole, output->size);
+        
+        return PJ_SUCCESS;
+    }
+
+    return vpx_codec_encode_more(codec, out_size, output, has_more);
+}
+
+
+static pj_status_t vpx_codec_encode_more(pjmedia_vid_codec *codec,
+                                         unsigned out_size,
+                                         pjmedia_frame *output,
+                                         pj_bool_t *has_more)
+{
+    struct vpx_codec_data *vpx_data;
+
+    PJ_ASSERT_RETURN(codec && out_size && output && has_more,
+                     PJ_EINVAL);
+
+    vpx_data = (vpx_codec_data*) codec->codec_data;
+    
+    if (vpx_data->enc_processed < vpx_data->enc_frame_size) {
+    	unsigned payload_desc_size = 1;
+        unsigned max_size = vpx_data->prm->enc_mtu - payload_desc_size;
+        unsigned remaining_size = vpx_data->enc_frame_size -
+        			  vpx_data->enc_processed;
+        unsigned payload_len = PJ_MIN(remaining_size, max_size);
+        pj_uint8_t *p = (pj_uint8_t *)output->buf;
+
+	if (payload_len + payload_desc_size > out_size)
+	    return PJMEDIA_CODEC_EFRMTOOSHORT;
+
+        output->timestamp = vpx_data->ets;
+        output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+        output->bit_info = 0;
+        if (vpx_data->enc_frame_is_keyframe) {
+            output->bit_info |= PJMEDIA_VID_FRM_KEYFRAME;
+        }
+
+        /* Set payload header */
+        p[0] = 0;
+        if (vpx_data->prm->enc_fmt.id == PJMEDIA_FORMAT_VP8) {
+	    /* Set N: Non-reference frame */
+            if (!vpx_data->enc_frame_is_keyframe) p[0] |= 0x20;
+            /* Set S: Start of VP8 partition. */
+            if (vpx_data->enc_processed == 0) p[0] |= 0x10;
+        } else if (vpx_data->prm->enc_fmt.id == PJMEDIA_FORMAT_VP9) {
+	    /* Set P: Inter-picture predicted frame */
+            if (!vpx_data->enc_frame_is_keyframe) p[0] |= 0x40;
+            /* Set B: Start of a frame */
+            if (vpx_data->enc_processed == 0) p[0] |= 0x8;
+            /* Set E: End of a frame */
+            if (vpx_data->enc_processed + payload_len ==
+            	vpx_data->enc_frame_size)
+            {
+	    	p[0] |= 0x4;
+	    }
+	}
+
+        pj_memcpy(p + payload_desc_size,
+        	  (vpx_data->enc_frame_whole + vpx_data->enc_processed),
+        	  payload_len);
+        output->size = payload_len + payload_desc_size;
+
+        vpx_data->enc_processed += payload_len;
+        *has_more = (vpx_data->enc_processed < vpx_data->enc_frame_size);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t vpx_unpacketize(struct vpx_codec_data *vpx_data,
+				   const pj_uint8_t *buf,
+                                   pj_size_t   packet_size,
+				   unsigned   *p_desc_len)
+{
+    unsigned desc_len = 1;
+    pj_uint8_t *p = (pj_uint8_t *)buf;
+
+#define INC_DESC_LEN() {if (++desc_len >= packet_size) return PJ_ETOOSMALL;}
+
+    if (packet_size <= desc_len) return PJ_ETOOSMALL;
+
+    if (vpx_data->prm->enc_fmt.id == PJMEDIA_FORMAT_VP8) {
+        /*  0 1 2 3 4 5 6 7
+         * +-+-+-+-+-+-+-+-+
+         * |X|R|N|S|R| PID | (REQUIRED)
+         */
+	/* X: Extended control bits present. */
+	if (p[0] & 0x80) {
+	    INC_DESC_LEN();
+	    /* |I|L|T|K| RSV   | */
+	    /* I: PictureID present. */
+	    if (p[1] & 0x80) {
+	    	INC_DESC_LEN();
+	    	/* If M bit is set, the PID field MUST contain 15 bits. */
+	    	if (p[2] & 0x80) INC_DESC_LEN();
+	    }
+	    /* L: TL0PICIDX present. */
+	    if (p[1] & 0x40) INC_DESC_LEN();
+	    /* T: TID present or K: KEYIDX present. */
+	    if ((p[1] & 0x20) || (p[1] & 0x10)) INC_DESC_LEN();
+	}
+
+    } else if (vpx_data->prm->enc_fmt.id == PJMEDIA_FORMAT_VP9) {
+        /*  0 1 2 3 4 5 6 7
+         * +-+-+-+-+-+-+-+-+
+         * |I|P|L|F|B|E|V|-| (REQUIRED)
+         */
+        /* I: Picture ID (PID) present. */
+	if (p[0] & 0x80) {
+	    INC_DESC_LEN();
+	    /* If M bit is set, the PID field MUST contain 15 bits. */
+	    if (p[1] & 0x80) INC_DESC_LEN();
+	}
+	/* L: Layer indices present. */
+	if (p[0] & 0x20) {
+	    INC_DESC_LEN();
+	    if (!(p[0] & 0x10)) INC_DESC_LEN();
+	}
+	/* F: Flexible mode.
+	 * I must also be set to 1, and if P is set, there's up to 3
+	 * reference index.
+	 */
+	if ((p[0] & 0x10) && (p[0] & 0x80) && (p[0] & 0x40)) {
+	    unsigned char *q = p + desc_len;
+
+	    INC_DESC_LEN();
+	    if (*q & 0x1) {
+	    	q++;
+	    	INC_DESC_LEN();
+	    	if (*q & 0x1) {
+	    	    q++;
+	    	    INC_DESC_LEN();
+	    	}
+	    }
+	}
+	/* V: Scalability structure (SS) data present. */
+	if (p[0] & 0x2) {
+	    unsigned char *q = p + desc_len;
+	    unsigned N_S = (*q >> 5) + 1;
+	    
+	    INC_DESC_LEN();
+	    /* Y: Each spatial layer's frame resolution present. */
+	    if (*q & 0x10) desc_len += N_S * 4;
+	    
+	    /* G: PG description present flag. */
+	    if (*q & 0x8) {
+	    	unsigned j;
+	    	unsigned N_G = *(p + desc_len);
+
+	    	INC_DESC_LEN();
+	    	for (j = 0; j< N_G; j++) {
+	    	    unsigned R;
+
+	    	    q = p + desc_len;
+	    	    INC_DESC_LEN();
+	    	    R = (*q & 0x0F) >> 2;
+	    	    desc_len += R;
+	    	    if (desc_len >= packet_size)
+	    	    	return PJ_ETOOSMALL;
+	    	}
+	    }
+	}
+    }
+#undef INC_DESC_LEN
+
+    *p_desc_len = desc_len;
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t vpx_codec_decode_(pjmedia_vid_codec *codec,
+                                     pj_size_t count,
+                                     pjmedia_frame packets[],
+                                     unsigned out_size,
+                                     pjmedia_frame *output)
+{
+    struct vpx_codec_data *vpx_data;
+    pj_bool_t has_frame = PJ_FALSE;
+    unsigned i, whole_len = 0;
+    vpx_codec_iter_t iter = NULL;
+    vpx_image_t *img = NULL;
+    vpx_codec_err_t res;
+    unsigned pos = 0;
+    int plane;
+
+    PJ_ASSERT_RETURN(codec && count && packets && out_size && output,
+                     PJ_EINVAL);
+    PJ_ASSERT_RETURN(output->buf, PJ_EINVAL);
+
+    vpx_data = (vpx_codec_data*) codec->codec_data;
+
+    /*
+     * Step 1: unpacketize the packets/frames
+     */
+    whole_len = 0;
+    if (vpx_data->whole) {
+	for (i = 0; i < count; ++i) {
+	    if (whole_len + packets[i].size > vpx_data->dec_buf_size) {
+		PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [1]"));
+		return PJMEDIA_CODEC_EFRMTOOSHORT;
+	    }
+
+	    pj_memcpy( vpx_data->dec_buf + whole_len,
+	               (pj_uint8_t*)packets[i].buf,
+	               packets[i].size);
+	    whole_len += packets[i].size;
+	}
+
+    } else {
+    	for (i = 0; i < count; ++i) {
+    	    unsigned desc_len;
+    	    unsigned packet_size = packets[i].size;
+    	    pj_status_t status;
+    	
+    	    status = vpx_unpacketize(vpx_data, packets[i].buf, packet_size,
+    				     &desc_len);
+    	    if (status != PJ_SUCCESS) {
+	    	PJ_LOG(4,(THIS_FILE, "Unpacketize error"));
+	    	return status;
+    	    }
+
+    	    packet_size -= desc_len;	
+    	    if (whole_len + packet_size > vpx_data->dec_buf_size) {
+	    	PJ_LOG(4,(THIS_FILE, "Decoding buffer overflow [2]"));
+	    	return PJMEDIA_CODEC_EFRMTOOSHORT;
+            }
+        
+	    pj_memcpy(vpx_data->dec_buf + whole_len,
+		      packets[i].buf + desc_len, packet_size);
+	    whole_len += packet_size;
+    	}
+    }
+
+    /* Decode */
+    res = vpx_codec_decode(&vpx_data->dec, vpx_data->dec_buf, whole_len,
+            		   0, VPX_DL_REALTIME);
+    if (res) {
+	PJ_LOG(4, (THIS_FILE, "Failed to decode frame %s",
+		   vpx_codec_error(&vpx_data->dec)));
+	goto on_return;
+    }
+
+    img = vpx_codec_get_frame(&vpx_data->dec, &iter);
+    if (!img) {
+	PJ_LOG(4, (THIS_FILE, "Failed to get decoded frame %s",
+		   vpx_codec_error(&vpx_data->dec)));
+	goto on_return;
+    }
+
+    has_frame = PJ_TRUE;
+
+    /* Detect format change */
+    if (img->d_w != vpx_data->prm->dec_fmt.det.vid.size.w ||
+	img->d_h != vpx_data->prm->dec_fmt.det.vid.size.h)
+    {
+	pjmedia_event event;
+
+	PJ_LOG(4,(THIS_FILE, "Frame size changed: %dx%d --> %dx%d",
+		  vpx_data->prm->dec_fmt.det.vid.size.w,
+		  vpx_data->prm->dec_fmt.det.vid.size.h,
+		  img->d_w, img->d_h));
+
+	vpx_data->prm->dec_fmt.det.vid.size.w = img->d_w;
+	vpx_data->prm->dec_fmt.det.vid.size.h = img->d_h;
+
+	/* Broadcast format changed event */
+	pjmedia_event_init(&event, PJMEDIA_EVENT_FMT_CHANGED,
+	                   &packets[0].timestamp, codec);
+	event.data.fmt_changed.dir = PJMEDIA_DIR_DECODING;
+	pjmedia_format_copy(&event.data.fmt_changed.new_fmt,
+	                    &vpx_data->prm->dec_fmt);
+	pjmedia_event_publish(NULL, codec, &event,
+	                      PJMEDIA_EVENT_PUBLISH_DEFAULT);
+    }
+
+    output->type = PJMEDIA_FRAME_TYPE_VIDEO;
+    output->timestamp = packets[0].timestamp;
+
+    for (plane = 0; plane < 3; ++plane) {
+        const unsigned char *buf = img->planes[plane];
+    	const int stride = img->stride[plane];
+    	const int w = (plane? img->d_w / 2: img->d_w);
+    	const int h = (plane? img->d_h / 2: img->d_h);
+    	int y;
+
+    	for (y = 0; y < h; ++y) {
+    	    pj_memcpy(output->buf + pos, buf, w);
+    	    pos += w;
+      	    buf += stride;
+    	}
+    }
+
+    output->size = pos;
+	
+on_return:
+    if (!has_frame) {
+	pjmedia_event event;
+
+	/* Broadcast missing keyframe event */
+	pjmedia_event_init(&event, PJMEDIA_EVENT_KEYFRAME_MISSING,
+	                   &packets[0].timestamp, codec);
+	pjmedia_event_publish(NULL, codec, &event,
+	                      PJMEDIA_EVENT_PUBLISH_DEFAULT);
+
+	PJ_LOG(4,(THIS_FILE, "Decode couldn't produce picture, "
+		  "input nframes=%d, concatenated size=%d bytes",
+		  count, whole_len));
+
+	output->type = PJMEDIA_FRAME_TYPE_NONE;
+	output->size = 0;
+	output->timestamp = packets[0].timestamp;
+    }
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_HAS_VPX_CODEC */
Index: /pjproject/trunk/pjmedia/src/pjmedia/vid_codec_util.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/vid_codec_util.c	(revision 6109)
+++ /pjproject/trunk/pjmedia/src/pjmedia/vid_codec_util.c	(revision 6110)
@@ -763,3 +763,79 @@
 
 
+/* VPX fmtp parser */
+PJ_DEF(pj_status_t) pjmedia_vid_codec_vpx_parse_fmtp(
+				    const pjmedia_codec_fmtp *fmtp,
+				    pjmedia_vid_codec_vpx_fmtp *vpx_fmtp)
+{
+    const pj_str_t PROFILE_ID	= {"profile-id", 10};
+    const pj_str_t MAX_FR	= {"max-fr", 6};
+    const pj_str_t MAX_FS	= {"max-fs", 6};
+
+    unsigned i;
+
+    pj_bzero(vpx_fmtp, sizeof(*vpx_fmtp));
+
+    for (i = 0; i < fmtp->cnt; ++i) {
+    	unsigned tmp;
+
+	if (pj_stricmp(&fmtp->param[i].name, &MAX_FS) == 0) {
+	    tmp = pj_strtoul(&fmtp->param[i].val);
+	    vpx_fmtp->max_fs = PJ_MAX(tmp, vpx_fmtp->max_fs);
+	} else if (pj_stricmp(&fmtp->param[i].name, &MAX_FR) == 0) {
+	    tmp = pj_strtoul(&fmtp->param[i].val);
+	    vpx_fmtp->max_fr = PJ_MAX(tmp, vpx_fmtp->max_fr);
+	} else if (pj_stricmp(&fmtp->param[i].name, &PROFILE_ID) == 0) {
+	    tmp = pj_strtoul(&fmtp->param[i].val);
+	    vpx_fmtp->profile_id = PJ_MAX(tmp, vpx_fmtp->profile_id);
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_vid_codec_vpx_apply_fmtp(
+				pjmedia_vid_codec_param *param)
+{
+    if (param->dir & PJMEDIA_DIR_ENCODING) {
+	pjmedia_vid_codec_vpx_fmtp fmtp;
+	pjmedia_video_format_detail *vfd;
+	pj_status_t status;
+
+	/* Get remote param */
+	status = pjmedia_vid_codec_vpx_parse_fmtp(&param->enc_fmtp,
+						  &fmtp);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Adjust fps and size to conform to the parameter
+	 * specified by remote SDP fmtp.
+	 */
+	vfd = pjmedia_format_get_video_format_detail(&param->enc_fmt,
+						     PJ_TRUE);
+
+	if (fmtp.max_fr > 0) {
+	    vfd->fps.num   = fmtp.max_fr;
+	    vfd->fps.denum = 1;
+	}
+	
+	if (fmtp.max_fs > 0) {
+	    unsigned max_res = ((int)pj_isqrt(fmtp.max_fs * 8)) * 16;
+	    
+	    /* Do we need to maintain the aspect ratio or scale down
+	     * to some predetermined resolution instead (for example,
+	     * if the requested resolution is 640x480 and max_res is
+	     * 600, should we scale down to 480xx360)?
+	     */
+	    if (vfd->size.w > max_res)
+		vfd->size.w = max_res;
+	    if (vfd->size.h > max_res)
+	    	vfd->size.h = max_res;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
 #endif /* PJMEDIA_HAS_VIDEO */
Index: /pjproject/trunk/pjmedia/src/test/vid_codec_test.c
===================================================================
--- /pjproject/trunk/pjmedia/src/test/vid_codec_test.c	(revision 6109)
+++ /pjproject/trunk/pjmedia/src/test/vid_codec_test.c	(revision 6110)
@@ -470,4 +470,11 @@
 #endif
 
+#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VPX_CODEC
+    status = pjmedia_codec_vpx_vid_init(NULL, mem);
+    if (status != PJ_SUCCESS) {
+	return -22;
+    }
+#endif
+
 #if PJMEDIA_HAS_FFMPEG_VID_CODEC
     status = pjmedia_codec_ffmpeg_vid_init(NULL, mem);
@@ -502,4 +509,24 @@
 #endif
 
+#if PJMEDIA_HAS_VPX_CODEC && PJMEDIA_HAS_VPX_CODEC_VP8
+    rc = encode_decode_test(pool, "vp8", PJMEDIA_VID_PACKING_WHOLE);
+    if (rc != 0)
+	goto on_return;
+
+    rc = encode_decode_test(pool, "vp8", PJMEDIA_VID_PACKING_PACKETS);
+    if (rc != 0)
+	goto on_return;
+#endif
+
+#if PJMEDIA_HAS_VPX_CODEC && PJMEDIA_HAS_VPX_CODEC_VP9
+    rc = encode_decode_test(pool, "vp9", PJMEDIA_VID_PACKING_WHOLE);
+    if (rc != 0)
+	goto on_return;
+
+    rc = encode_decode_test(pool, "vp9", PJMEDIA_VID_PACKING_PACKETS);
+    if (rc != 0)
+	goto on_return;
+#endif
+
 
 on_return:
@@ -513,4 +540,7 @@
     pjmedia_codec_vid_toolbox_deinit();
 #endif
+#if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
+    pjmedia_codec_vpx_vid_deinit();
+#endif
     pjmedia_vid_dev_subsys_shutdown();
     pj_pool_release(pool);
Index: /pjproject/trunk/pjsip-apps/src/samples/simpleua.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/simpleua.c	(revision 6109)
+++ /pjproject/trunk/pjsip-apps/src/samples/simpleua.c	(revision 6110)
@@ -396,4 +396,9 @@
 #   endif
 
+#   if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
+    status = pjmedia_codec_vpx_vid_init(NULL, &cp.factory);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+#   endif
+
 #  if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0
     /* Init ffmpeg video codecs */
@@ -596,4 +601,7 @@
 #   if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VID_TOOLBOX_CODEC
     pjmedia_codec_vid_toolbox_deinit();
+#   endif
+#   if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
+    pjmedia_codec_vpx_vid_deinit();
 #   endif
 
Index: /pjproject/trunk/pjsip-apps/src/samples/vid_codec_test.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/vid_codec_test.c	(revision 6109)
+++ /pjproject/trunk/pjsip-apps/src/samples/vid_codec_test.c	(revision 6110)
@@ -84,4 +84,15 @@
 #endif
 
+#if defined(PJMEDIA_HAS_VID_TOOLBOX_CODEC) && \
+    PJMEDIA_HAS_VID_TOOLBOX_CODEC != 0
+    status = pjmedia_codec_vid_toolbox_init(NULL, pf);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+#endif
+
+#if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
+    status = pjmedia_codec_vpx_vid_init(NULL, pf);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+#endif
+
 #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0
     status = pjmedia_codec_ffmpeg_vid_init(NULL, pf);
@@ -103,4 +114,13 @@
 #if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0
     pjmedia_codec_openh264_vid_deinit();
+#endif
+
+#if defined(PJMEDIA_HAS_VID_TOOLBOX_CODEC) && \
+    PJMEDIA_HAS_VID_TOOLBOX_CODEC != 0
+    pjmedia_codec_vid_toolbox_deinit();
+#endif
+
+#if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
+    pjmedia_codec_vpx_vid_deinit();
 #endif
 
Index: /pjproject/trunk/pjsip-apps/src/samples/vid_streamutil.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/vid_streamutil.c	(revision 6109)
+++ /pjproject/trunk/pjsip-apps/src/samples/vid_streamutil.c	(revision 6110)
@@ -139,4 +139,15 @@
 #endif
 
+#if defined(PJMEDIA_HAS_VID_TOOLBOX_CODEC) && \
+    PJMEDIA_HAS_VID_TOOLBOX_CODEC != 0
+    status = pjmedia_codec_vid_toolbox_init(NULL, pf);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+#endif
+
+#if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
+    status = pjmedia_codec_vpx_vid_init(NULL, pf);
+    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+#endif
+
 #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0
     status = pjmedia_codec_ffmpeg_vid_init(NULL, pf);
@@ -158,4 +169,13 @@
 #if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0
     pjmedia_codec_openh264_vid_deinit();
+#endif
+
+#if defined(PJMEDIA_HAS_VID_TOOLBOX_CODEC) && \
+    PJMEDIA_HAS_VID_TOOLBOX_CODEC != 0
+    pjmedia_codec_vid_toolbox_deinit();
+#endif
+
+#if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
+    pjmedia_codec_vpx_vid_deinit();
 #endif
 
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c	(revision 6109)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c	(revision 6110)
@@ -103,4 +103,13 @@
 #endif
 
+#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VPX_CODEC
+    status = pjmedia_codec_vpx_vid_init(NULL, &pjsua_var.cp.factory);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error initializing VPX library",
+		     status);
+	goto on_error;
+    }
+#endif
+
     status = pjmedia_vid_dev_subsys_init(&pjsua_var.cp.factory);
     if (status != PJ_SUCCESS) {
@@ -165,4 +174,8 @@
 #if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0
     pjmedia_codec_openh264_vid_deinit();
+#endif
+
+#if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0
+    pjmedia_codec_vpx_vid_deinit();
 #endif
 
