Index: pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev.h	(revision 3757)
+++ pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev.h	(revision 3758)
@@ -221,5 +221,11 @@
     pjmedia_dir dir;
 
-    /** Specify whether the device supports callback */
+    /**
+     * Specify whether the device supports callback. Devices that implement
+     * "active interface" will actively call the callbacks to give or ask for
+     * video frames. If the device doesn't support callback, application
+     * must actively request or give video frames from/to the device by using
+     * pjmedia_vid_dev_stream_get_frame()/pjmedia_vid_dev_stream_put_frame().
+     */
     pj_bool_t has_callback;
 
@@ -642,4 +648,14 @@
 
 /**
+ * Query whether the stream has been started.
+ *
+ * @param strm	    The video stream
+ *
+ * @return	    PJ_TRUE if the video stream has been started.
+ */
+PJ_DECL(pj_bool_t) pjmedia_vid_dev_stream_is_running(pjmedia_vid_dev_stream *strm);
+
+
+/**
  * Get the event publisher object for the video stream. Caller typically use
  * the returned object to subscribe or unsubscribe events from the video
@@ -653,9 +669,31 @@
 pjmedia_vid_dev_stream_get_event_publisher(pjmedia_vid_dev_stream *strm);
 
-/* Get/put frame API for passive stream */
+
+/**
+ * Request one frame from the stream. Application needs to call this function
+ * periodically only if the stream doesn't support "active interface", i.e.
+ * the pjmedia_vid_dev_info.has_callback member is PJ_FALSE.
+ *
+ * @param strm      The video stream.
+ * @param frame	    The video frame to be filled by the device.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
 PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_get_frame(
 					    pjmedia_vid_dev_stream *strm,
                                             pjmedia_frame *frame);
 
+/**
+ * Put one frame to the stream. Application needs to call this function
+ * periodically only if the stream doesn't support "active interface", i.e.
+ * the pjmedia_vid_dev_info.has_callback member is PJ_FALSE.
+ *
+ * @param strm      The video stream.
+ * @param frame	    The video frame to put to the device.
+ *
+ * @return          PJ_SUCCESS on successful operation or the appropriate
+ *                  error code.
+ */
 PJ_DECL(pj_status_t) pjmedia_vid_dev_stream_put_frame(
 					    pjmedia_vid_dev_stream *strm,
Index: pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev_imp.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev_imp.h	(revision 3757)
+++ pjproject/trunk/pjmedia/include/pjmedia-videodev/videodev_imp.h	(revision 3758)
@@ -182,4 +182,7 @@
 	/** Driver index */
 	unsigned drv_idx;
+
+	/** Has it been started? */
+	pj_bool_t is_running;
     } sys;
 
Index: pjproject/trunk/pjmedia/include/pjmedia/vid_port.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/vid_port.h	(revision 3757)
+++ pjproject/trunk/pjmedia/include/pjmedia/vid_port.h	(revision 3758)
@@ -214,4 +214,13 @@
 
 /**
+ * Query whether the video port has been started.
+ *
+ * @param vid_port	The video port.
+ *
+ * @return		PJ_TRUE if the video port has been started.
+ */
+PJ_DECL(pj_bool_t) pjmedia_vid_port_is_running(pjmedia_vid_port *vid_port);
+
+/**
  * Stop the video port.
  *
Index: pjproject/trunk/pjmedia/src/pjmedia-videodev/videodev.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-videodev/videodev.c	(revision 3757)
+++ pjproject/trunk/pjmedia/src/pjmedia-videodev/videodev.c	(revision 3758)
@@ -782,5 +782,15 @@
 PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_start(pjmedia_vid_dev_stream *strm)
 {
-    return strm->op->start(strm);
+    pj_status_t status = strm->op->start(strm);
+    if (status == PJ_SUCCESS)
+	strm->sys.is_running = PJ_TRUE;
+    return status;
+}
+
+/* API: has it been started? */
+PJ_DEF(pj_bool_t)
+pjmedia_vid_dev_stream_is_running(pjmedia_vid_dev_stream *strm)
+{
+    return strm->sys.is_running;
 }
 
@@ -804,4 +814,5 @@
 PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm)
 {
+    strm->sys.is_running = PJ_FALSE;
     return strm->op->stop(strm);
 }
@@ -811,4 +822,5 @@
 						pjmedia_vid_dev_stream *strm)
 {
+    strm->sys.is_running = PJ_FALSE;
     return strm->op->destroy(strm);
 }
Index: pjproject/trunk/pjmedia/src/pjmedia/vid_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/vid_port.c	(revision 3757)
+++ pjproject/trunk/pjmedia/src/pjmedia/vid_port.c	(revision 3758)
@@ -478,4 +478,9 @@
     pjmedia_vid_port_stop(vp);
     return status;
+}
+
+PJ_DEF(pj_bool_t) pjmedia_vid_port_is_running(pjmedia_vid_port *vp)
+{
+    return pjmedia_vid_dev_stream_is_running(vp->strm);
 }
 
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 3757)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 3758)
@@ -325,4 +325,5 @@
     pjmedia_port		*tee;		/**< Video tee		*/
     pjmedia_vid_dev_index	 preview_cap_id;/**< Capture dev id	*/
+    pj_bool_t			 preview_running;/**< Preview is started*/
     pj_bool_t			 is_native; 	/**< Preview is by dev  */
 } pjsua_vid_win;
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c	(revision 3757)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c	(revision 3758)
@@ -324,8 +324,6 @@
  */
 
-/*
- * Get the preview window handle associated with the capture device, if any.
- */
-PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
+static pjsua_vid_win_id vid_preview_get_win(pjmedia_vid_dev_index id,
+                                            pj_bool_t running_only)
 {
     pjsua_vid_win_id wid = PJSUA_INVALID_ID;
@@ -348,7 +346,23 @@
 	}
     }
+
+    if (wid != PJSUA_INVALID_ID && running_only) {
+	pjsua_vid_win *w = &pjsua_var.win[wid];
+	wid = w->preview_running ? wid : PJSUA_INVALID_ID;
+    }
+
     PJSUA_UNLOCK();
 
     return wid;
+}
+
+/*
+ * NOTE: internal function don't use this!!! Use vid_preview_get_win()
+ *       instead. This is because this function will only return window ID
+ *       if preview is currently running.
+ */
+PJ_DEF(pjsua_vid_win_id) pjsua_vid_preview_get_win(pjmedia_vid_dev_index id)
+{
+    return vid_preview_get_win(id, PJ_TRUE);
 }
 
@@ -394,5 +408,5 @@
     /* If type is preview, check if it exists already */
     if (type == PJSUA_WND_TYPE_PREVIEW) {
-	wid = pjsua_vid_preview_get_win(cap_id);
+	wid = vid_preview_get_win(cap_id, PJ_FALSE);
 	if (wid != PJSUA_INVALID_ID) {
 	    /* Yes, it exists */
@@ -421,7 +435,7 @@
 	    /* Done */
 	    *id = wid;
-	    PJ_LOG(4,(THIS_FILE, "Window already exist: %d", wid));
 	    pj_log_pop_indent();
-	    return PJ_SUCCESS;
+
+	    return status;
 	}
     }
@@ -826,5 +840,10 @@
 	    }
 
-	    wid = pjsua_vid_preview_get_win(call_med->strm.v.cap_dev);
+	    /* Note: calling pjsua_vid_preview_get_win() even though
+	     * create_vid_win() will automatically create the window
+	     * if it doesn't exist, because create_vid_win() will modify
+	     * existing window SHOW/HIDE value.
+	     */
+	    wid = vid_preview_get_win(call_med->strm.v.cap_dev, PJ_FALSE);
 	    if (wid == PJSUA_INVALID_ID) {
 		/* Create preview video window */
@@ -1043,7 +1062,28 @@
 
     w = &pjsua_var.win[wid];
+    if (w->preview_running) {
+	PJSUA_UNLOCK();
+	pj_log_pop_indent();
+	return PJ_SUCCESS;
+    }
 
     /* Start renderer, unless it's native preview */
-    if (!w->is_native) {
+    if (w->is_native && !pjmedia_vid_port_is_running(w->vp_cap)) {
+	pjmedia_vid_dev_stream *cap_dev;
+	pj_bool_t enabled = PJ_TRUE;
+
+	cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
+	status = pjmedia_vid_dev_stream_set_cap(
+			cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
+			&enabled);
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(1,(THIS_FILE, status,
+			 "Error activating native preview, falling back "
+			 "to software preview.."));
+	    w->is_native = PJ_FALSE;
+	}
+    }
+
+    if (!w->is_native && !pjmedia_vid_port_is_running(w->vp_rend)) {
 	status = pjmedia_vid_port_start(w->vp_rend);
 	if (status != PJ_SUCCESS) {
@@ -1055,12 +1095,15 @@
 
     /* Start capturer */
-    status = pjmedia_vid_port_start(w->vp_cap);
-    if (status != PJ_SUCCESS) {
-	PJSUA_UNLOCK();
-	pj_log_pop_indent();
-	return status;
+    if (!pjmedia_vid_port_is_running(w->vp_cap)) {
+	status = pjmedia_vid_port_start(w->vp_cap);
+	if (status != PJ_SUCCESS) {
+	    PJSUA_UNLOCK();
+	    pj_log_pop_indent();
+	    return status;
+	}
     }
 
     inc_vid_win(wid);
+    w->preview_running = PJ_TRUE;
 
     PJSUA_UNLOCK();
@@ -1075,7 +1118,6 @@
 {
     pjsua_vid_win_id wid = PJSUA_INVALID_ID;
-
-    PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
-    pj_log_push_indent();
+    pjsua_vid_win *w;
+    pj_status_t status;
 
     PJSUA_LOCK();
@@ -1087,5 +1129,32 @@
     }
 
-    dec_vid_win(wid);
+    PJ_LOG(4,(THIS_FILE, "Stopping preview for cap_dev=%d", id));
+    pj_log_push_indent();
+
+    w = &pjsua_var.win[wid];
+    if (w->preview_running) {
+	if (w->is_native) {
+	    pjmedia_vid_dev_stream *cap_dev;
+	    pj_bool_t enabled = PJ_FALSE;
+
+	    cap_dev = pjmedia_vid_port_get_stream(w->vp_cap);
+	    status = pjmedia_vid_dev_stream_set_cap(
+			    cap_dev, PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW,
+			    &enabled);
+	} else {
+	    status = pjmedia_vid_port_stop(w->vp_rend);
+	}
+
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(1,(THIS_FILE, status, "Error stopping %spreview",
+		         (w->is_native ? "native " : "")));
+	    PJSUA_UNLOCK();
+	    pj_log_pop_indent();
+	    return status;
+	}
+
+	dec_vid_win(wid);
+	w->preview_running = PJ_FALSE;
+    }
 
     PJSUA_UNLOCK();
@@ -1674,5 +1743,10 @@
     /* = Attach stream port to the new capture device = */
 
-    new_wid = pjsua_vid_preview_get_win(cap_dev);
+    /* Note: calling pjsua_vid_preview_get_win() even though
+     * create_vid_win() will automatically create the window
+     * if it doesn't exist, because create_vid_win() will modify
+     * existing window SHOW/HIDE value.
+     */
+    new_wid = vid_preview_get_win(cap_dev, PJ_FALSE);
     if (new_wid == PJSUA_INVALID_ID) {
 	/* Create preview video window */
