Index: pjproject/trunk/pjsip-apps/src/swig/pjsua2.i
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/pjsua2.i	(revision 5971)
+++ pjproject/trunk/pjsip-apps/src/swig/pjsua2.i	(revision 5972)
@@ -109,4 +109,5 @@
 %template(AudioMediaVector)		std::vector<pj::AudioMedia*>;
 %template(AudioMediaVector2)		std::vector<pj::AudioMedia>;
+%template(VideoMediaVector)		std::vector<pj::VideoMedia>;
 %template(ToneDescVector)		std::vector<pj::ToneDesc>;
 %template(ToneDigitVector)		std::vector<pj::ToneDigit>;
Index: pjproject/trunk/pjsip/include/pjsua2/call.hpp
===================================================================
--- pjproject/trunk/pjsip/include/pjsua2/call.hpp	(revision 5971)
+++ pjproject/trunk/pjsip/include/pjsua2/call.hpp	(revision 5972)
@@ -334,4 +334,9 @@
 /**
  * Call media information.
+ *
+ * Application can query conference bridge port of this media using
+ * Call::getAudioMedia() if the media type is audio,
+ * or Call::getEncodingVideoMedia()/Call::getDecodingVideoMedia()
+ * if the media type is video.
  */
 struct CallMediaInfo
@@ -358,4 +363,7 @@
     
     /**
+     * Warning: this is deprecated, application can query conference bridge
+     * port of this media using Call::getAudioMedia().
+     *
      * The conference port number for the call. Only valid if the media type
      * is audio.
@@ -375,5 +383,5 @@
      */
     VideoWindow	    	    videoWindow;
-    
+
     /**
      * The video capture device for outgoing transmission, if any,
@@ -1251,9 +1259,36 @@
      * index is not audio or invalid or inactive, exception will be thrown.
      *
-     * @param med_idx       Media index.
+     * @param med_idx       Media index, or -1 to specify any first audio
+     *                      media registered in the conference bridge.
      *
      * @return              The audio media.
      */
-    AudioMedia getAudioMedia(unsigned med_idx) const throw(Error);
+    AudioMedia getAudioMedia(int med_idx) const throw(Error);
+
+    /**
+     * Get video media in encoding direction for the specified media index.
+     * If the specified media index is not video or invalid or the direction
+     * is receive only, exception will be thrown.
+     *
+     * @param med_idx       Media index, or -1 to specify any first video
+     *			    media with encoding direction registered in the
+     *			    conference bridge.
+     *
+     * @return              The video media.
+     */
+    VideoMedia getEncodingVideoMedia(int med_idx) const throw(Error);
+
+    /**
+     * Get video media in decoding direction for the specified media index.
+     * If the specified media index is not video or invalid or the direction
+     * is send only, exception will be thrown.
+     *
+     * @param med_idx       Media index, or -1 to specify any first video
+     *			    media with decoding direction registered in the
+     *			    conference bridge.
+     *
+     * @return              The video media.
+     */
+    VideoMedia getDecodingVideoMedia(int med_idx) const throw(Error);
 
     /**
Index: pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp
===================================================================
--- pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp	(revision 5971)
+++ pjproject/trunk/pjsip/include/pjsua2/endpoint.hpp	(revision 5972)
@@ -1397,4 +1397,11 @@
      */
     AudioMediaVector2 mediaEnumPorts2() const throw(Error);
+
+    /**
+     * Enumerate all video media port.
+     *
+     * @return		The list of video media port.
+     */
+    VideoMediaVector mediaEnumVidPorts() const throw(Error);
 
     /**
Index: pjproject/trunk/pjsip/include/pjsua2/media.hpp
===================================================================
--- pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 5971)
+++ pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 5972)
@@ -149,5 +149,5 @@
     /**
      * Array of listeners (in other words, ports where this port is
-     * transmitting to.
+     * transmitting to).
      */
     IntVector		listeners;
@@ -196,4 +196,7 @@
 };
 
+/**
+ * Parameters for AudioMedia::startTransmit2() method.
+ */
 struct AudioMediaTransmitParam
 {
@@ -348,6 +351,9 @@
 
     /**
-     * Default Constructor. Normally application will not create AudioMedia
-     * object directly, but it may instantiate an AudioMedia derived class.
+     * Default Constructor.
+     *
+     * Normally application will not create AudioMedia object directly,
+     * but it instantiates an AudioMedia derived class. This is set as public
+     * because some STL vector implementations require it.
      */
     AudioMedia();
@@ -401,6 +407,4 @@
     pj_caching_pool 	 mediaCachingPool;
     pj_pool_t 		*mediaPool;
-
-    friend class Endpoint;
 };
 
@@ -1547,4 +1551,143 @@
 };
 
+
+/**
+ * This structure descibes information about a particular media port that
+ * has been registered into the conference bridge. 
+ */
+struct VidConfPortInfo
+{
+    /**
+     * Conference port number.
+     */
+    int			portId;
+
+    /**
+     * Port name.
+     */
+    string		name;
+
+    /**
+     * Media audio format information
+     */
+    MediaFormatVideo	format;
+
+    /**
+     * Array of listeners (in other words, ports where this port is
+     * transmitting to).
+     */
+    IntVector		listeners;
+
+    /**
+     * Array of listeners (in other words, ports where this port is
+     * listening to).
+     */
+    IntVector		transmitters;
+
+public:
+    /**
+     * Construct from pjsua_conf_port_info.
+     */
+    void fromPj(const pjsua_vid_conf_port_info &port_info);
+};
+
+/**
+ * Parameters for VideoMedia::startTransmit() method.
+ */
+struct VideoMediaTransmitParam
+{
+};
+
+/**
+ * Video Media.
+ */
+class VideoMedia : public Media
+{
+public:
+    /**
+    * Get information about the specified conference port.
+    */
+    VidConfPortInfo getPortInfo() const throw(Error);
+
+    /**
+     * Get port Id.
+     */
+    int getPortId() const;
+
+    /**
+     * Get information from specific port id.
+     */
+    static VidConfPortInfo getPortInfoFromId(int port_id) throw(Error);
+
+    /**
+     * Establish unidirectional media flow to sink. This media port
+     * will act as a source, and it may transmit to multiple destinations/sink.
+     * And if multiple sources are transmitting to the same sink, the media
+     * will be mixed together. Source and sink may refer to the same Media,
+     * effectively looping the media.
+     *
+     * If bidirectional media flow is desired, application needs to call
+     * this method twice, with the second one called from the opposite source
+     * media.
+     *
+     * @param sink		The destination Media.
+     * @param param		The parameter.
+     */
+    void startTransmit(const VideoMedia &sink, 
+		       const VideoMediaTransmitParam &param) const
+         throw(Error);
+
+    /**
+     *  Stop media flow to destination/sink port.
+     *
+     * @param sink		The destination media.
+     *
+     */
+    void stopTransmit(const VideoMedia &sink) const throw(Error);
+
+    /**
+     * Default Constructor.
+     *
+     * Normally application will not create VideoMedia object directly,
+     * but it instantiates a VideoMedia derived class. This is set as public
+     * because some STL vector implementations require it.
+     */
+    VideoMedia();
+
+    /**
+     * Virtual Destructor
+     */
+    virtual ~VideoMedia();
+
+protected:
+    /**
+     * Conference port Id.
+     */
+    int			 id;
+
+protected:
+    /**
+     * This method needs to be called by descendants of this class to register
+     * the media port created to the conference bridge and Endpoint's
+     * media list.
+     *
+     * param port  The media port to be registered to the conference bridge.
+     * param pool  The memory pool.
+     */
+    void registerMediaPort(MediaPort port, pj_pool_t *pool) throw(Error);
+
+    /**
+     * This method needs to be called by descendants of this class to remove
+     * the media port from the conference bridge and Endpoint's media list.
+     * Descendant should only call this method if it has registered the media
+     * with the previous call to registerMediaPort().
+     */
+    void unregisterMediaPort();
+};
+
+/** Array of Video Media */
+typedef std::vector<VideoMedia> VideoMediaVector;
+
+
 /**
  * Window handle.
@@ -1628,4 +1771,12 @@
      */
     VideoWindowInfo getInfo() const throw(Error);
+
+    /**
+     * Get video media or conference bridge port of the renderer of
+     * this video window.
+     *
+     * @return			Video media of this renderer window.
+     */
+    VideoMedia getVideoMedia() throw(Error);
     
     /**
@@ -1781,4 +1932,11 @@
      */
     VideoWindow getVideoWindow();
+
+    /**
+     * Get video media or conference bridge port of the video capture device.
+     *
+     * @return			Video media of the video capture device.
+     */
+    VideoMedia getVideoMedia() throw(Error);
 
 private:
Index: pjproject/trunk/pjsip/src/pjsua2/call.cpp
===================================================================
--- pjproject/trunk/pjsip/src/pjsua2/call.cpp	(revision 5971)
+++ pjproject/trunk/pjsip/src/pjsua2/call.cpp	(revision 5972)
@@ -152,31 +152,4 @@
 //////////////////////////////////////////////////////////////////////////////
 
-/* Call Audio Media. */
-class CallAudioMedia : public AudioMedia
-{
-public:
-    /*
-     * Set the conference port identification associated with the
-     * call audio media.
-     */
-    void setPortId(int id);
-
-    /**
-     * Destructor
-     */
-    virtual ~CallAudioMedia();
-};
-
-
-void CallAudioMedia::setPortId(int pid)
-{
-    this->id = pid;
-}
-
-CallAudioMedia::~CallAudioMedia()
-{
-    id = PJSUA_INVALID_ID;
-}
-
 CallOpParam::CallOpParam(bool useDefaultCallSetting)
 : statusCode(pjsip_status_code(0)), reason(""), options(0)
@@ -271,5 +244,8 @@
 
 CallMediaInfo::CallMediaInfo()
-: videoWindow(PJSUA_INVALID_ID)
+: audioConfSlot(PJSUA_INVALID_ID),
+  videoWindow(PJSUA_INVALID_ID),
+  videoIncomingWindowId(PJSUA_INVALID_ID),
+  videoCapDev(PJMEDIA_VID_INVALID_DEV)
 {
 }
@@ -522,15 +498,116 @@
 }
 
-AudioMedia Call::getAudioMedia(unsigned med_idx) const throw(Error)
-{
-    CallAudioMedia cam;
-    CallInfo ci = getInfo();
-
-    if (med_idx < ci.media.size() &&
-	ci.media[med_idx].type == PJMEDIA_TYPE_AUDIO)
-    {
-	cam.setPortId(ci.media[med_idx].audioConfSlot);
-    }
-    return cam;
+AudioMedia Call::getAudioMedia(int med_idx) const throw(Error)
+{
+    pjsua_call_info pj_ci;
+    pjsua_call_get_info(id, &pj_ci);
+    
+    if (med_idx < 0) {
+	for (unsigned i = 0; i < pj_ci.media_cnt; ++i) {
+	    if (pj_ci.media[med_idx].type == PJMEDIA_TYPE_AUDIO &&
+		pj_ci.media[med_idx].stream.aud.conf_slot != PJSUA_INVALID_ID)
+	    {
+		med_idx = i;
+		break;
+	    }
+	}
+	if (med_idx < 0) {
+    	    PJSUA2_RAISE_ERROR3(PJ_ENOTFOUND, "getAudioMedia()",
+				"no active audio media");
+	}
+    }
+
+    if (med_idx >= (int)pj_ci.media_cnt) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getAudioMedia()",
+			    "invalid media index");
+    }
+    if (pj_ci.media[med_idx].type != PJMEDIA_TYPE_AUDIO) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getAudioMedia()",
+			    "media is not audio");
+    }
+    if (pj_ci.media[med_idx].stream.aud.conf_slot == PJSUA_INVALID_ID) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getAudioMedia()",
+			    "no audio slot (inactive?)");
+    }
+
+    AudioMediaHelper am;
+    am.setPortId(pj_ci.media[med_idx].stream.aud.conf_slot);
+    return am;
+}
+
+VideoMedia Call::getEncodingVideoMedia(int med_idx) const throw(Error)
+{
+    pjsua_call_info pj_ci;
+    pjsua_call_get_info(id, &pj_ci);
+
+    if (med_idx < 0) {
+	for (unsigned i = 0; i < pj_ci.media_cnt; ++i) {
+	    if (pj_ci.media[med_idx].type == PJMEDIA_TYPE_VIDEO &&
+		pj_ci.media[med_idx].stream.vid.enc_slot != PJSUA_INVALID_ID)
+	    {
+		med_idx = i;
+		break;
+	    }
+	}
+	if (med_idx < 0) {
+    	    PJSUA2_RAISE_ERROR3(PJ_ENOTFOUND, "getEncodingVideoMedia()",
+				"no active encoding video media");
+	}
+    }
+
+    if (med_idx >= (int)pj_ci.media_cnt) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getEncodingVideoMedia()",
+			    "invalid media index");
+    }
+    if (pj_ci.media[med_idx].type != PJMEDIA_TYPE_VIDEO) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getEncodingVideoMedia()",
+			    "media is not video");
+    }
+    if (pj_ci.media[med_idx].stream.vid.enc_slot == PJSUA_INVALID_ID) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getEncodingVideoMedia()",
+			    "no encoding slot (recvonly?)");
+    }
+
+    VideoMediaHelper vm;
+    vm.setPortId(pj_ci.media[med_idx].stream.vid.enc_slot);
+    return vm;
+}
+
+VideoMedia Call::getDecodingVideoMedia(int med_idx) const throw(Error)
+{
+    pjsua_call_info pj_ci;
+    pjsua_call_get_info(id, &pj_ci);
+
+    if (med_idx < 0) {
+	for (unsigned i = 0; i < pj_ci.media_cnt; ++i) {
+	    if (pj_ci.media[med_idx].type == PJMEDIA_TYPE_VIDEO &&
+		pj_ci.media[med_idx].stream.vid.dec_slot != PJSUA_INVALID_ID)
+	    {
+		med_idx = i;
+		break;
+	    }
+	}
+	if (med_idx < 0) {
+    	    PJSUA2_RAISE_ERROR3(PJ_ENOTFOUND, "getDecodingVideoMedia()",
+				"no active decoding video media");
+	}
+    }
+
+    if (med_idx >= (int)pj_ci.media_cnt) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getDecodingVideoMedia()",
+			    "invalid media index");
+    }
+    if (pj_ci.media[med_idx].type != PJMEDIA_TYPE_VIDEO) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getDecodingVideoMedia()",
+			    "media is not video");
+    }
+    if (pj_ci.media[med_idx].stream.vid.dec_slot == PJSUA_INVALID_ID) {
+    	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "getDecodingVideoMedia()",
+			    "no decoding slot (sendonly?)");
+    }
+
+    VideoMediaHelper vm;
+    vm.setPortId(pj_ci.media[med_idx].stream.vid.dec_slot);
+    return vm;
 }
 
@@ -791,5 +868,5 @@
             if (mi >= medias.size()) {
                 if (pj_ci.media[mi].type == PJMEDIA_TYPE_AUDIO) {
-                    medias.push_back(new CallAudioMedia);
+                    medias.push_back(new AudioMediaHelper);
                 } else {
                     medias.push_back(NULL);
@@ -798,8 +875,8 @@
             
             if (pj_ci.media[mi].type == PJMEDIA_TYPE_AUDIO) {
-                CallAudioMedia *aud_med = (CallAudioMedia *)medias[mi];
-                
+                AudioMediaHelper *aud_med = (AudioMediaHelper*)medias[mi];
                 aud_med->setPortId(pj_ci.media[mi].stream.aud.conf_slot);
-                /* Add media if the conference slot ID is valid. */
+
+		/* Add media if the conference slot ID is valid. */
                 if (pj_ci.media[mi].stream.aud.conf_slot != PJSUA_INVALID_ID)
                 {
@@ -808,5 +885,5 @@
                     Endpoint::instance().mediaRemove((AudioMedia &)*aud_med);
                 }
-            }
+	    }
         }
     }
Index: pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp
===================================================================
--- pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp	(revision 5971)
+++ pjproject/trunk/pjsip/src/pjsua2/endpoint.cpp	(revision 5972)
@@ -2055,10 +2055,26 @@
     PJSUA2_CHECK_EXPR( pjsua_enum_conf_ports(ids, &count) );
     for (i = 0; i < count; ++i) {
-	AudioMedia am;
-	am.id = ids[i];
+	AudioMediaHelper am;
+	am.setPortId(ids[i]);
 	amv2.push_back(am);
     }
 
     return amv2;
+}
+
+VideoMediaVector Endpoint::mediaEnumVidPorts() const throw(Error)
+{
+    VideoMediaVector vmv;
+    pjsua_conf_port_id ids[PJSUA_MAX_CONF_PORTS];
+    unsigned i, count = PJSUA_MAX_CONF_PORTS;
+
+    PJSUA2_CHECK_EXPR( pjsua_vid_conf_enum_ports(ids, &count) );
+    for (i = 0; i < count; ++i) {
+	VideoMediaHelper vm;
+	vm.setPortId(ids[i]);
+	vmv.push_back(vm);
+    }
+
+    return vmv;
 }
 
Index: pjproject/trunk/pjsip/src/pjsua2/media.cpp
===================================================================
--- pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 5971)
+++ pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 5972)
@@ -1195,5 +1195,24 @@
     return vwi;
 }
-    
+
+VideoMedia VideoWindow::getVideoMedia() throw(Error)
+{
+#if PJSUA_HAS_VIDEO
+    pjsua_vid_win_info pj_vwi;
+    PJSUA2_CHECK_EXPR( pjsua_vid_win_get_info(winId, &pj_vwi) );
+
+    pjsua_conf_port_id id = pj_vwi.slot_id;
+    if (id != PJSUA_INVALID_ID) {
+	VideoMediaHelper vm;
+	vm.setPortId(id);
+	return vm;
+    } else {
+	PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
+    }
+#else
+    PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
+#endif
+}
+
 void VideoWindow::Show(bool show) throw(Error)
 {
@@ -1333,4 +1352,20 @@
 #else
     return (VideoWindow(PJSUA_INVALID_ID));
+#endif
+}
+
+VideoMedia VideoPreview::getVideoMedia() throw(Error)
+{
+#if PJSUA_HAS_VIDEO
+    pjsua_conf_port_id id = pjsua_vid_preview_get_vid_conf_port(devId);
+    if (id != PJSUA_INVALID_ID) {
+	VideoMediaHelper vm;
+	vm.setPortId(id);
+	return vm;
+    } else {
+	PJSUA2_RAISE_ERROR2(PJ_EINVALIDOP, "Preview not started");
+    }
+#else
+    PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
 #endif
 }
@@ -1808,2 +1843,91 @@
 }
 
+
+///////////////////////////////////////////////////////////////////////////////
+
+void VidConfPortInfo::fromPj(const pjsua_vid_conf_port_info &port_info)
+{
+    portId = port_info.slot_id;
+    name = pj2Str(port_info.name);
+    format.fromPj(port_info.format);
+    listeners.clear();
+    for (unsigned i=0; i<port_info.listener_cnt; ++i) {
+	listeners.push_back(port_info.listeners[i]);
+    }
+    transmitters.clear();
+    for (unsigned i=0; i<port_info.transmitter_cnt; ++i) {
+	transmitters.push_back(port_info.transmitters[i]);
+    }
+}
+
+VideoMedia::VideoMedia() 
+: Media(PJMEDIA_TYPE_VIDEO), id(PJSUA_INVALID_ID)
+{
+
+}
+
+void VideoMedia::registerMediaPort(MediaPort port, pj_pool_t *pool)
+     throw(Error)
+{
+    if (!pool) {
+	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "registerMediaPort()",
+			    "pool must be supplied");
+    }
+    if (!port) {
+	PJSUA2_RAISE_ERROR3(PJ_EINVAL, "registerMediaPort()",
+			    "port must be supplied");
+    }
+    if (id != PJSUA_INVALID_ID) {
+	PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "registerMediaPort()",
+			    "VideoMedia is occupied");
+    }
+
+    PJSUA2_CHECK_EXPR( pjsua_vid_conf_add_port(pool,
+					       (pjmedia_port*)port, NULL,
+					       &id) );
+}
+
+void VideoMedia::unregisterMediaPort()
+{
+    if (id != PJSUA_INVALID_ID) {
+	pjsua_vid_conf_remove_port(id);
+	id = PJSUA_INVALID_ID;
+    }
+}
+
+VideoMedia::~VideoMedia() 
+{
+}
+
+VidConfPortInfo VideoMedia::getPortInfo() const throw(Error)
+{
+    return VideoMedia::getPortInfoFromId(id);
+}
+
+int VideoMedia::getPortId() const
+{
+    return id;
+}
+
+VidConfPortInfo VideoMedia::getPortInfoFromId(int port_id) throw(Error)
+{
+    pjsua_vid_conf_port_info pj_info;
+    VidConfPortInfo pi;
+
+    PJSUA2_CHECK_EXPR( pjsua_vid_conf_get_port_info(port_id, &pj_info) );
+    pi.fromPj(pj_info);
+    return pi;
+}
+
+void VideoMedia::startTransmit(const VideoMedia &sink,
+			       const VideoMediaTransmitParam &param) const
+     throw(Error)
+{
+    PJ_UNUSED_ARG(param);
+    PJSUA2_CHECK_EXPR( pjsua_vid_conf_connect(id, sink.id, NULL) );
+}
+
+void VideoMedia::stopTransmit(const VideoMedia &sink) const throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_vid_conf_disconnect(id, sink.id) );
+}
Index: pjproject/trunk/pjsip/src/pjsua2/util.hpp
===================================================================
--- pjproject/trunk/pjsip/src/pjsua2/util.hpp	(revision 5971)
+++ pjproject/trunk/pjsip/src/pjsua2/util.hpp	(revision 5972)
@@ -19,4 +19,5 @@
 
 #include <pjsua2/types.hpp>
+#include <pjsua2/media.hpp>
 #include <string>
 
@@ -42,4 +43,15 @@
 }
 
+class AudioMediaHelper : public AudioMedia
+{
+public:
+    void setPortId(int port_id) { id = port_id; }
+};
 
-}	// namespace
+class VideoMediaHelper : public VideoMedia
+{
+public:
+    void setPortId(int port_id) { id = port_id; }
+};
+
+} // namespace pj
