Index: pjproject/trunk/pjmedia/include/pjmedia/config.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 6105)
+++ pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 6106)
@@ -466,8 +466,17 @@
 
 /**
- * Interval to send RTCP packets, in msec
+ * Interval to send regular RTCP packets, in msec.
  */
 #ifndef PJMEDIA_RTCP_INTERVAL
-#	define PJMEDIA_RTCP_INTERVAL		5000	/* msec*/
+#   define PJMEDIA_RTCP_INTERVAL		5000	/* msec*/
+#endif
+
+
+/**
+ * Minimum interval between two consecutive outgoing RTCP-FB packets,
+ * such as Picture Loss Indication, in msec.
+ */
+#ifndef PJMEDIA_RTCP_FB_INTERVAL
+#   define PJMEDIA_RTCP_FB_INTERVAL		50	/* msec*/
 #endif
 
@@ -1528,4 +1537,14 @@
 
 /**
+ * Specify the minimum interval to send video keyframe, in msec.
+ *
+ * Default : 1000
+ */
+#ifndef PJMEDIA_VID_STREAM_MIN_KEYFRAME_INTERVAL_MSEC
+#   define PJMEDIA_VID_STREAM_MIN_KEYFRAME_INTERVAL_MSEC    1000
+#endif
+
+
+/**
  * Specify minimum delay of video decoding, in milliseconds. Lower value may
  * degrade video quality significantly in a bad network environment (e.g:
Index: pjproject/trunk/pjmedia/include/pjmedia/event.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/event.h	(revision 6105)
+++ pjproject/trunk/pjmedia/include/pjmedia/event.h	(revision 6106)
@@ -26,4 +26,5 @@
 #include <pjmedia/audiodev.h>
 #include <pjmedia/format.h>
+#include <pjmedia/rtcp_fb.h>
 #include <pjmedia/signatures.h>
 #include <pjmedia/videodev.h>
@@ -297,4 +298,7 @@
 	/** Media transport error event data */
 	pjmedia_event_media_tp_err_data		med_tp_err;
+
+	/** Receiving RTCP-FB event data */
+	pjmedia_event_rx_rtcp_fb_data		rx_rtcp_fb;
 
 	/** Pointer to storage to user event data, if it's outside
Index: pjproject/trunk/pjmedia/include/pjmedia/rtcp_fb.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/rtcp_fb.h	(revision 6105)
+++ pjproject/trunk/pjmedia/include/pjmedia/rtcp_fb.h	(revision 6106)
@@ -136,10 +136,11 @@
     /**
      * Specify whether transport protocol in SDP media description uses
-     * RTP/AVP instead of RTP/AVPF. Note that the standard mandates to signal
-     * AVPF profile, but it may cause SDP negotiation failure when negotiating
-     * with endpoints that does not support RTCP Feedback (including older
-     * version of PJSIP).
+     * RTP/AVP instead of RTP/AVPF. Note that RFC4585 mandates to signal
+     * RTP/AVPF profile, but it may cause SDP negotiation failure when
+     * negotiating with endpoints that does not support RTP/AVPF (including
+     * older version of PJSIP), furthermore, there is RFC8643 that promotes
+     * interoperability over the strictness of RTP profile specifications.
      *
-     * Default: PJ_FALSE.
+     * Default: PJ_TRUE.
      */
     pj_bool_t		     dont_use_avpf;
@@ -295,4 +296,29 @@
 
 /**
+ * Decode RTCP Feedback specific information from SDP media for the specified
+ * payload type. If the payload type is set to negative value, it will decode
+ * RTCP Feedback info for all payload types in the SDP media.
+ *
+ * @param pool		Pool object to allocate memory.
+ * @param endpt		The media endpoint.
+ * @param opt		Options, currently it must be NULL.
+ * @param sdp		The SDP.
+ * @param med_idx	The SDP media index.
+ * @param pt		The payload type.
+ * @param info		The RTCP-FB info fetched from SDP.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_rtcp_fb_decode_sdp2(
+				    pj_pool_t *pool,
+				    pjmedia_endpt *endpt,
+				    const void *opt,
+				    const pjmedia_sdp_session *sdp,
+				    unsigned med_idx,
+				    int pt,
+				    pjmedia_rtcp_fb_info *info);
+
+
+/**
  * Build an RTCP Feedback Generic NACK packet. This packet can be appended to
  * other RTCP packets, e.g: RTCP RR/SR, to compose a compound RTCP packet.
Index: pjproject/trunk/pjmedia/include/pjmedia/types.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 6105)
+++ pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 6106)
@@ -306,4 +306,15 @@
  */
 PJ_DECL(const char*) pjmedia_type_name(pjmedia_type t);
+
+
+/**
+ * Utility function to return the media type for a media name string.
+ *
+ * @param name		The media name string.
+ *
+ * @return		media type.
+ */
+PJ_DECL(pjmedia_type) pjmedia_get_type(const pj_str_t *name);
+
 
 /**
Index: pjproject/trunk/pjmedia/include/pjmedia/vid_codec.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/vid_codec.h	(revision 6105)
+++ pjproject/trunk/pjmedia/include/pjmedia/vid_codec.h	(revision 6106)
@@ -525,5 +525,6 @@
 /**
  * Convert codec info struct into a unique codec identifier.
- * A codec identifier looks something like "H263/90000".
+ * A codec identifier looks something like "H263/34", where "H263" is the
+ * codec name and "34" is the (default) payload type.
  *
  * @param info	    The codec info
@@ -541,6 +542,6 @@
  * Find codecs by the unique codec identifier. This function will find
  * all codecs that match the codec identifier prefix. For example, if
- * "H26" is specified, then it will find "H263/90000", "H264/90000",
- * and so on, up to the maximum count specified in the argument.
+ * "H26" is specified, then it will find "H263", "H264", and so on,
+ * up to the maximum count specified in the argument.
  *
  * @param mgr	    The codec manager instance. If NULL, the default codec
Index: pjproject/trunk/pjmedia/include/pjmedia/vid_stream.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/vid_stream.h	(revision 6105)
+++ pjproject/trunk/pjmedia/include/pjmedia/vid_stream.h	(revision 6106)
@@ -30,4 +30,5 @@
 #include <pjmedia/port.h>
 #include <pjmedia/rtcp.h>
+#include <pjmedia/rtcp_fb.h>
 #include <pjmedia/transport.h>
 #include <pjmedia/vid_codec.h>
@@ -157,4 +158,6 @@
 					 will be calculated from RTP.	    */
     pj_bool_t		rtcp_mux;   /**< Use RTP and RTCP multiplexing.     */
+    pjmedia_rtcp_fb_info loc_rtcp_fb; /**< Local RTCP-FB info.		    */
+    pjmedia_rtcp_fb_info rem_rtcp_fb; /**< Remote RTCP-FB info.		    */
     unsigned		tx_pt;	    /**< Outgoing codec paylaod type.	    */
     unsigned		rx_pt;	    /**< Incoming codec paylaod type.	    */
Index: pjproject/trunk/pjmedia/src/pjmedia/rtcp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/rtcp.c	(revision 6105)
+++ pjproject/trunk/pjmedia/src/pjmedia/rtcp.c	(revision 6106)
@@ -780,23 +780,23 @@
     //pjmedia_rtcp_fb_rpsi rpsi;
     pjmedia_event ev;
-    pjmedia_event_rx_rtcp_fb_data ev_data;
     pj_timestamp ts_now;
 
     pj_get_timestamp(&ts_now);
-    pj_bzero(&ev_data, sizeof(ev_data));
 
     if (pjmedia_rtcp_fb_parse_nack(pkt, size, &cnt, nack)==PJ_SUCCESS)
     {
 	pjmedia_event_init(&ev, PJMEDIA_EVENT_RX_RTCP_FB, &ts_now, sess);
-	ev_data.cap.type = PJMEDIA_RTCP_FB_NACK;
-	ev_data.msg.nack = nack[0];
-	ev.data.ptr = &ev_data;
-
-	/* Sync publish, i.e: don't use PJMEDIA_EVENT_PUBLISH_POST_EVENT */
+	ev.data.rx_rtcp_fb.cap.type = PJMEDIA_RTCP_FB_NACK;
+	ev.data.rx_rtcp_fb.msg.nack = nack[0];
 	pjmedia_event_publish(NULL, sess, &ev, 0);
 
-	/*  For other FB type implementations later
     } else if (pjmedia_rtcp_fb_parse_pli(pkt, size)==PJ_SUCCESS)
     {
+	pjmedia_event_init(&ev, PJMEDIA_EVENT_RX_RTCP_FB, &ts_now, sess);
+	ev.data.rx_rtcp_fb.cap.type = PJMEDIA_RTCP_FB_NACK;
+	pj_strset2(&ev.data.rx_rtcp_fb.cap.param, (char*)"pli");
+	pjmedia_event_publish(NULL, sess, &ev, 0);
+
+	/*  For other FB type implementations later
     } else if (pjmedia_rtcp_fb_parse_sli(pkt, size, &cnt, sli)==PJ_SUCCESS)
     {
Index: pjproject/trunk/pjmedia/src/pjmedia/rtcp_fb.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/rtcp_fb.c	(revision 6105)
+++ pjproject/trunk/pjmedia/src/pjmedia/rtcp_fb.c	(revision 6106)
@@ -22,4 +22,5 @@
 #include <pjmedia/endpoint.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/vid_codec.h>
 #include <pj/assert.h>
 #include <pj/log.h>
@@ -215,4 +216,5 @@
 {
     pj_bzero(opt, sizeof(*opt));
+    opt->dont_use_avpf = PJ_TRUE;
 
     return PJ_SUCCESS;
@@ -332,5 +334,10 @@
     pjmedia_codec_mgr *codec_mgr;
     unsigned j, cnt = 0;
+    pjmedia_type type = PJMEDIA_TYPE_UNKNOWN;
     pj_status_t status;
+
+    type = pjmedia_get_type(&m->desc.media);
+    if (type != PJMEDIA_TYPE_AUDIO && type != PJMEDIA_TYPE_VIDEO)
+	return PJMEDIA_EUNSUPMEDIATYPE;
 
     codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
@@ -339,11 +346,20 @@
 	pt = pj_strtoul(&m->desc.fmt[j]);
 	if (pt < 96) {
-	    const pjmedia_codec_info *ci;
-	    status = pjmedia_codec_mgr_get_codec_info(codec_mgr,
-						      pt, &ci);
-	    if (status != PJ_SUCCESS)
-		continue;
-
-	    pjmedia_codec_info_to_id(ci, sci[cnt].id, sizeof(sci[0].id));
+	    if (type == PJMEDIA_TYPE_AUDIO) {
+		const pjmedia_codec_info *ci;
+		status = pjmedia_codec_mgr_get_codec_info(codec_mgr, pt, &ci);
+		if (status != PJ_SUCCESS)
+		    continue;
+
+		pjmedia_codec_info_to_id(ci, sci[cnt].id, sizeof(sci[0].id));
+	    } else {
+		const pjmedia_vid_codec_info *ci;
+		status = pjmedia_vid_codec_mgr_get_codec_info(NULL, pt, &ci);
+		if (status != PJ_SUCCESS)
+		    continue;
+
+		pjmedia_vid_codec_info_to_id(ci, sci[cnt].id,
+					     sizeof(sci[0].id));
+	    }
 	} else {
 	    pjmedia_sdp_attr *a;
@@ -356,15 +372,24 @@
 	    if (status != PJ_SUCCESS)
 		continue;
-	    if (r.param.slen) {
+
+	    if (type == PJMEDIA_TYPE_AUDIO) {
+		/* Audio codec id format: "name/clock-rate/channel-count" */
+		if (r.param.slen) {
+		    pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
+				     "%.*s/%d/%.*s",
+				     (int)r.enc_name.slen, r.enc_name.ptr,
+				     r.clock_rate,
+				     (int)r.param.slen, r.param.ptr);
+		} else {
+		    pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
+				     "%.*s/%d/1",
+				     (int)r.enc_name.slen, r.enc_name.ptr,
+				     r.clock_rate);
+		}
+	    } else {
+		/* Video codec id format: "name/payload-type" */
 		pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
-				 "%.*s/%d/%.*s",
-				 (int)r.enc_name.slen, r.enc_name.ptr,
-				 r.clock_rate,
-				 (int)r.param.slen, r.param.ptr);
-	    } else {
-		pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
-				 "%.*s/%d/1",
-				 (int)r.enc_name.slen, r.enc_name.ptr,
-				 r.clock_rate);
+				 "%.*s/%d",
+				 (int)r.enc_name.slen, r.enc_name.ptr, pt);
 	    }
 	}
@@ -396,5 +421,5 @@
     PJ_UNUSED_ARG(sdp_remote);
 
-    PJ_ASSERT_RETURN(pool && endpt&& opt && sdp_local, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pool && endpt && opt && sdp_local, PJ_EINVAL);
     PJ_ASSERT_RETURN(med_idx < sdp_local->media_count, PJ_EINVAL);
 
@@ -403,10 +428,8 @@
 	unsigned proto = pjmedia_sdp_transport_get_proto(&m->desc.transport);
 	if (!PJMEDIA_TP_PROTO_HAS_FLAG(proto, PJMEDIA_TP_PROFILE_RTCP_FB)) {
-	    char *new_tp;
-	    new_tp = (char*)pj_pool_zalloc(pool, m->desc.transport.slen+1);
-	    pj_ansi_strncpy(new_tp, m->desc.transport.ptr,
-			    m->desc.transport.slen);
-	    pj_ansi_strcat(new_tp, "F");
-	    pj_strset2(&m->desc.transport, new_tp);
+	    pj_str_t new_tp;
+	    pj_strdup_with_null(pool, &new_tp, &m->desc.transport);
+	    new_tp.ptr[new_tp.slen++] = 'F';
+	    m->desc.transport = new_tp;
 	}
     }
@@ -477,4 +500,20 @@
 				    pjmedia_rtcp_fb_info *info)
 {
+    return pjmedia_rtcp_fb_decode_sdp2(pool, endpt, opt, sdp, med_idx, -1,
+				       info);
+}
+
+/*
+ * Decode RTCP Feedback specific information from SDP media.
+ */
+PJ_DEF(pj_status_t) pjmedia_rtcp_fb_decode_sdp2(
+				    pj_pool_t *pool,
+				    pjmedia_endpt *endpt,
+				    const void *opt,
+				    const pjmedia_sdp_session *sdp,
+				    unsigned med_idx,
+				    int pt,
+				    pjmedia_rtcp_fb_info *info)
+{
     unsigned sci_cnt = PJMEDIA_MAX_SDP_FMT;
     sdp_codec_info_t sci[PJMEDIA_MAX_SDP_FMT];
@@ -487,4 +526,5 @@
     PJ_ASSERT_RETURN(pool && endpt && opt==NULL && sdp, PJ_EINVAL);
     PJ_ASSERT_RETURN(med_idx < sdp->media_count, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pt <= 127, PJ_EINVAL);
 
     m = sdp->media[med_idx];
@@ -515,11 +555,12 @@
 
 	if (pj_strcmp2(&token, "*") == 0) {
-	    /* Asterisk (all codecs) */
+	    /* All codecs */
 	    codec_id = "*";
 	} else {
-	    /* Specific PT */
-	    unsigned pt = (unsigned) pj_strtoul2(&token, NULL, 10);
+	    /* Specific PT/codec */
+	    unsigned pt_ = (unsigned) pj_strtoul2(&token, NULL, 10);
 	    for (j = 0; j < sci_cnt; ++j) {
-		if (pt == sci[j].pt) {
+		/* Check if payload type is valid and requested */
+		if (pt_ == sci[j].pt && (pt < 0 || pt == (int)pt_)) {
 		    codec_id = sci[j].id;
 		    break;
@@ -528,5 +569,5 @@
 	}
 
-	/* Skip this a=rtcp-fb if PT is not recognized */
+	/* Skip this a=rtcp-fb if PT is not recognized or not requested */
 	if (!codec_id)
 	    continue;
Index: pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 6105)
+++ pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 6106)
@@ -2252,5 +2252,5 @@
     {
 	pjmedia_event_rx_rtcp_fb_data *data = (pjmedia_event_rx_rtcp_fb_data*)
-					      event->data.ptr;
+					      &event->data.rx_rtcp_fb;
 
 	/* Application not configured to listen to NACK, discard this event */
@@ -2262,5 +2262,6 @@
 
     /* Republish events */
-    return pjmedia_event_publish(NULL, stream, event, 0);
+    return pjmedia_event_publish(NULL, stream, event,
+				 PJMEDIA_EVENT_PUBLISH_POST_EVENT);
 }
 
@@ -2743,18 +2744,16 @@
 #endif
 
-    /* Check if we should send RTCP-FB generic NACK for this codec */
+    /* Check if we should send RTCP-FB */
     if (stream->si.rem_rtcp_fb.cap_count) {
 	pjmedia_rtcp_fb_info *rfi = &stream->si.rem_rtcp_fb;
-	char cid[32];
 	unsigned i;
-
-	pjmedia_codec_info_to_id(&stream->si.fmt, cid, sizeof(cid));
 
 	for (i = 0; i < rfi->cap_count; ++i) {
 	    if (rfi->caps[i].type == PJMEDIA_RTCP_FB_NACK &&
-		(!pj_strcmp2( &rfi->caps[i].codec_id, "*") ||
-		 !pj_stricmp2(&rfi->caps[i].codec_id, cid)))
+		rfi->caps[i].param.slen == 0)
 	    {
 		stream->send_rtcp_fb_nack = PJ_TRUE;
+		PJ_LOG(4,(stream->port.info.name.ptr,
+			  "Send RTCP-FB generic NACK"));
 		break;
 	    }
@@ -2762,19 +2761,17 @@
     }
 
-    /* Check if we handle incoming RTCP-FB generic NACK for this codec */
+    /* Check if we should process incoming RTCP-FB */
     stream->rtcp_fb_nack_cap_idx = -1;
     if (stream->si.loc_rtcp_fb.cap_count) {
 	pjmedia_rtcp_fb_info *lfi = &stream->si.loc_rtcp_fb;
-	char cid[32];
 	unsigned i;
-
-	pjmedia_codec_info_to_id(&stream->si.fmt, cid, sizeof(cid));
 
 	for (i = 0; i < lfi->cap_count; ++i) {
 	    if (lfi->caps[i].type == PJMEDIA_RTCP_FB_NACK &&
-		(!pj_strcmp2( &lfi->caps[i].codec_id, "*") ||
-		 !pj_stricmp2(&lfi->caps[i].codec_id, cid)))
+		lfi->caps[i].param.slen == 0)
 	    {
 		stream->rtcp_fb_nack_cap_idx = i;
+		PJ_LOG(4,(stream->port.info.name.ptr,
+			  "Receive RTCP-FB generic NACK"));
 		break;
 	    }
Index: pjproject/trunk/pjmedia/src/pjmedia/stream_info.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream_info.c	(revision 6105)
+++ pjproject/trunk/pjmedia/src/pjmedia/stream_info.c	(revision 6106)
@@ -24,5 +24,4 @@
 #include <pj/rand.h>
 
-static const pj_str_t ID_AUDIO = { "audio", 5};
 static const pj_str_t ID_IN = { "IN", 2 };
 static const pj_str_t ID_IP4 = { "IP4", 3};
@@ -387,5 +386,5 @@
 
     /* Media type must be audio */
-    if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) != 0)
+    if (pjmedia_get_type(&local_m->desc.media) != PJMEDIA_TYPE_AUDIO)
 	return PJMEDIA_EINVALIMEDIATYPE;
 
@@ -602,12 +601,12 @@
 
     /* Get local RTCP-FB info */
-    status = pjmedia_rtcp_fb_decode_sdp(pool, endpt, NULL, local, stream_idx,
-					&si->loc_rtcp_fb);
+    status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, stream_idx,
+					 si->rx_pt, &si->loc_rtcp_fb);
     if (status != PJ_SUCCESS)
 	return status;
 
     /* Get remote RTCP-FB info */
-    status = pjmedia_rtcp_fb_decode_sdp(pool, endpt, NULL, remote, stream_idx,
-					&si->rem_rtcp_fb);
+    status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, stream_idx,
+					 si->tx_pt, &si->rem_rtcp_fb);
     if (status != PJ_SUCCESS)
 	return status;
Index: pjproject/trunk/pjmedia/src/pjmedia/types.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/types.c	(revision 6105)
+++ pjproject/trunk/pjmedia/src/pjmedia/types.c	(revision 6106)
@@ -20,28 +20,46 @@
 #include <pjmedia/types.h>
 #include <pj/assert.h>
+#include <pj/string.h>
 
-/**
+
+/* Map structure for pjmedia type names */
+typedef struct pjmedia_type_map {
+    pjmedia_type type;
+    const char* name;
+} pjmedia_type_map;
+
+/* Internal mapping for pjmedia type names */
+static pjmedia_type_map media_type_names[] = {
+    {PJMEDIA_TYPE_NONE,		"none"},
+    {PJMEDIA_TYPE_AUDIO,	"audio"},
+    {PJMEDIA_TYPE_VIDEO,	"video"},
+    {PJMEDIA_TYPE_APPLICATION,	"application"},
+    {PJMEDIA_TYPE_UNKNOWN,	"unknown"}
+};
+
+/*
  * Utility function to return the string name for a pjmedia_type.
- *
- * @param t		The media type.
- *
- * @return		String.
  */
 PJ_DEF(const char*) pjmedia_type_name(pjmedia_type t)
 {
-    const char *type_names[] = {
-	"none",
-	"audio",
-	"video",
-	"application",
-	"unknown"
-    };
-
-    pj_assert(t < (int)PJ_ARRAY_SIZE(type_names));
+    pj_assert(t < (int)PJ_ARRAY_SIZE(media_type_names));
     pj_assert(PJMEDIA_TYPE_UNKNOWN == 4);
 
-    if (t < (int)PJ_ARRAY_SIZE(type_names))
-	return type_names[t];
+    if (t < (int)PJ_ARRAY_SIZE(media_type_names))
+	return media_type_names[t].name;
     else
 	return "??";
 }
+
+/*
+ * Utility function to return the media type for a media name string.
+ */
+PJ_DEF(pjmedia_type) pjmedia_get_type(const pj_str_t *name)
+{
+    int i;
+    for (i = 0; i < PJ_ARRAY_SIZE(media_type_names); ++i) {
+	if (pj_stricmp2(name, media_type_names[i].name)==0)
+	    return media_type_names[i].type;
+    }
+    return PJMEDIA_TYPE_UNKNOWN;
+}
Index: pjproject/trunk/pjmedia/src/pjmedia/vid_codec.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/vid_codec.c	(revision 6105)
+++ pjproject/trunk/pjmedia/src/pjmedia/vid_codec.c	(revision 6106)
@@ -410,8 +410,5 @@
 
 /*
- * 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.
+ * Find codecs by the unique codec identifier.
  */
 PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_find_codecs_by_id(
Index: pjproject/trunk/pjmedia/src/pjmedia/vid_stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/vid_stream.c	(revision 6105)
+++ pjproject/trunk/pjmedia/src/pjmedia/vid_stream.c	(revision 6106)
@@ -20,7 +20,8 @@
 #include <pjmedia/errno.h>
 #include <pjmedia/event.h>
+#include <pjmedia/jbuf.h>
 #include <pjmedia/rtp.h>
 #include <pjmedia/rtcp.h>
-#include <pjmedia/jbuf.h>
+#include <pjmedia/rtcp_fb.h>
 #include <pj/array.h>
 #include <pj/assert.h>
@@ -124,6 +125,7 @@
 
     pjmedia_rtcp_session     rtcp;	    /**< RTCP for incoming RTP.	    */
-    pj_uint32_t		     rtcp_last_tx;  /**< RTCP tx time in timestamp  */
-    pj_uint32_t		     rtcp_interval; /**< Interval, in timestamp.    */
+    pj_timestamp	     rtcp_last_tx;  /**< Last RTCP tx time.	    */
+    pj_timestamp	     rtcp_fb_last_tx;/**< Last RTCP-FB tx time.	    */
+    pj_uint32_t		     rtcp_interval; /**< Interval, in msec.	    */
     pj_bool_t		     initial_rr;    /**< Initial RTCP RR sent	    */
     pj_bool_t                rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/
@@ -150,6 +152,6 @@
     pj_bool_t		     force_keyframe;/**< Forced to encode keyframe? */
     unsigned		     num_keyframe;  /**< The number of keyframe needed
-						 to be sent, after the stream
-						 is created. */
+						 to be sent, e.g: after the
+						 stream is created. */
     pj_timestamp	     last_keyframe_tx;
 					    /**< Timestamp of the last
@@ -187,4 +189,15 @@
     pj_sockaddr		     rtp_src_addr;  /**< Actual packet src addr.    */
     unsigned		     rtp_src_cnt;   /**< How many pkt from this addr*/
+
+
+    /* RTCP Feedback */
+    pj_bool_t		     send_rtcp_fb_nack;	    /**< Send NACK?	    */
+    int			     pending_rtcp_fb_nack;  /**< Any pending NACK?  */
+    int			     rtcp_fb_nack_cap_idx;  /**< RX NACK cap idx.   */
+    pjmedia_rtcp_fb_nack     rtcp_fb_nack;	    /**< TX NACK state.	    */
+
+    pj_bool_t		     send_rtcp_fb_pli;	    /**< Send PLI?	    */
+    int			     pending_rtcp_fb_pli;   /**< Any pending PLI?   */
+    int			     rtcp_fb_pli_cap_idx;   /**< RX PLI cap idx.    */
 
 #if TRACE_RC
@@ -204,5 +217,7 @@
 static pj_status_t send_rtcp(pjmedia_vid_stream *stream,
 			     pj_bool_t with_sdes,
-			     pj_bool_t with_bye);
+			     pj_bool_t with_bye,
+			     pj_bool_t with_fb_nack,
+			     pj_bool_t with_fb_pli);
 
 static void on_rx_rtcp( void *data,
@@ -399,4 +414,12 @@
 	    /* Republish this event later from get_frame(). */
 	    pj_memcpy(&stream->miss_keyframe_event, event, sizeof(*event));
+
+	    if (stream->send_rtcp_fb_pli) {
+		/* Schedule sending RTCP-FB PLI to encoder, if configured,
+		 * also perhaps better to make it redundant, in case the first
+		 * packet is lost.
+		 */
+		stream->pending_rtcp_fb_pli = 2;
+	    }
 	    return PJ_SUCCESS;
 
@@ -404,8 +427,43 @@
 	    break;
 	}
-    }
-
-    return pjmedia_event_publish(NULL, stream, event, 0);
-}
+    } else if (event->epub == &stream->rtcp && 
+	       event->type==PJMEDIA_EVENT_RX_RTCP_FB)
+    {
+	/* This is RX RTCP-FB event */
+	pjmedia_event_rx_rtcp_fb_data *data = 
+		    (pjmedia_event_rx_rtcp_fb_data*)&event->data.rx_rtcp_fb;
+
+	/* Check if configured to listen to the RTCP-FB type */
+	if (data->cap.type == PJMEDIA_RTCP_FB_NACK) {
+	    if (data->cap.param.slen == 0 &&
+		stream->rtcp_fb_nack_cap_idx >= 0)
+	    {
+		/* Generic NACK */
+
+		/* Update event data capability before republishing */
+		data->cap = stream->info.loc_rtcp_fb.caps[
+					stream->rtcp_fb_nack_cap_idx];
+	    }
+	    else if (pj_strcmp2(&data->cap.param, "pli") == 0 &&
+		     stream->rtcp_fb_pli_cap_idx >= 0)
+	    {
+		/* PLI */
+
+		/* Tell encoder to generate keyframe */
+		pjmedia_vid_stream_send_keyframe(stream);
+
+		/* Update event data capability before republishing */
+		data->cap = stream->info.loc_rtcp_fb.caps[
+					stream->rtcp_fb_pli_cap_idx];
+
+	    }
+	}
+    }
+
+    /* Republish events */
+    return pjmedia_event_publish(NULL, stream, event,
+				 PJMEDIA_EVENT_PUBLISH_POST_EVENT);
+}
+
 
 /**
@@ -502,5 +560,7 @@
 static pj_status_t send_rtcp(pjmedia_vid_stream *stream,
 			     pj_bool_t with_sdes,
-			     pj_bool_t with_bye)
+			     pj_bool_t with_bye,
+			     pj_bool_t with_fb_nack,
+			     pj_bool_t with_fb_pli)
 {
     void *sr_rr_pkt;
@@ -513,5 +573,5 @@
     pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
 
-    if (with_sdes || with_bye) {
+    if (with_sdes || with_bye || with_fb_nack || with_fb_pli) {
 	pkt = (pj_uint8_t*) stream->out_rtcp_pkt;
 	pj_memcpy(pkt, sr_rr_pkt, len);
@@ -522,5 +582,6 @@
     }
 
-    /* Build RTCP SDES packet */
+    /* Build RTCP SDES packet, forced if also send RTCP-FB */
+    with_sdes = with_sdes || with_fb_pli || with_fb_nack;
     if (with_sdes) {
 	pjmedia_rtcp_sdes sdes;
@@ -542,7 +603,5 @@
     /* Build RTCP BYE packet */
     if (with_bye) {
-	pj_size_t bye_len;
-
-	bye_len = max_len - len;
+	pj_size_t bye_len = max_len - len;
 	status = pjmedia_rtcp_build_rtcp_bye(&stream->rtcp, pkt+len,
 					     &bye_len, NULL);
@@ -555,4 +614,30 @@
     }
 
+    /* Build RTCP-FB generic NACK packet */
+    if (with_fb_nack && stream->rtcp_fb_nack.pid >= 0) {
+	pj_size_t fb_len = max_len - len;
+	status = pjmedia_rtcp_fb_build_nack(&stream->rtcp, pkt+len, &fb_len,
+					    1, &stream->rtcp_fb_nack);
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(4,(stream->name.ptr, status,
+        			     "Error generating RTCP-FB NACK"));
+	} else {
+	    len += (int)fb_len;
+	}
+    }
+
+    /* Build RTCP-FB PLI packet */
+    if (with_fb_pli) {
+	pj_size_t fb_len = max_len - len;
+	status = pjmedia_rtcp_fb_build_pli(&stream->rtcp, pkt+len, &fb_len);
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(4,(stream->name.ptr, status,
+        			     "Error generating RTCP-FB PLI"));
+	} else {
+	    len += (int)fb_len;
+	    PJ_LOG(5,(stream->name.ptr, "Sending RTCP-FB PLI packet"));
+	}
+    }
+
     /* Send! */
     status = pjmedia_transport_send_rtcp(stream->transport, pkt, len);
@@ -575,21 +660,36 @@
  * This function is can be called by either put_frame() or get_frame(),
  * to transmit periodic RTCP SR/RR report.
- */
-static void check_tx_rtcp(pjmedia_vid_stream *stream, pj_uint32_t timestamp)
-{
-    /* Note that timestamp may represent local or remote timestamp,
-     * depending on whether this function is called from put_frame()
-     * or get_frame().
+ * If 'fb_pli' is set to PJ_TRUE, this will send immediate RTCP-FB PLI.
+ */
+static void check_tx_rtcp(pjmedia_vid_stream *stream)
+{
+    pj_timestamp now;
+    pj_bool_t early;
+
+    /* Check if early RTCP mode is required (i.e: RTCP-FB) and allowed (i.e:
+     * elapsed timestamp from previous RTCP-FB >= PJMEDIA_RTCP_FB_INTERVAL).
      */
-
-
-    if (stream->rtcp_last_tx == 0) {
-
-	stream->rtcp_last_tx = timestamp;
-
-    } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
+    pj_get_timestamp(&now);
+    early = ((stream->pending_rtcp_fb_pli || stream->pending_rtcp_fb_nack)
+	     &&
+	     (stream->rtcp_fb_last_tx.u64 == 0 ||
+	      pj_elapsed_msec(&stream->rtcp_fb_last_tx, &now) >=
+					    PJMEDIA_RTCP_FB_INTERVAL));
+
+    /* First check, unless RTCP is 'urgent', just init rtcp_last_tx. */
+    if (stream->rtcp_last_tx.u64 == 0 && !early) {
+	pj_get_timestamp(&stream->rtcp_last_tx);
+	return;
+    } 
+
+    /* Build & send RTCP */
+    if (early ||
+	pj_elapsed_msec(&stream->rtcp_last_tx, &now) >= stream->rtcp_interval)
+    {
 	pj_status_t status;
 
-	status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE);
+	status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE,
+			   stream->pending_rtcp_fb_nack,
+			   stream->pending_rtcp_fb_pli);
 	if (status != PJ_SUCCESS) {
 	    PJ_PERROR(4,(stream->name.ptr, status,
@@ -597,5 +697,14 @@
 	}
 
-	stream->rtcp_last_tx = timestamp;
+	stream->rtcp_last_tx = now;
+
+	if (early)
+	    stream->rtcp_fb_last_tx = now;
+
+	if (stream->pending_rtcp_fb_pli)
+	    stream->pending_rtcp_fb_pli--;
+
+	if (stream->pending_rtcp_fb_nack)
+	    stream->pending_rtcp_fb_nack--;
     }
 }
@@ -841,4 +950,17 @@
     pj_mutex_unlock( stream->jb_mutex );
 
+    /* Check if we need to send RTCP-FB generic NACK */
+    if (stream->send_rtcp_fb_nack && seq_st.diff > 1 &&
+	pj_ntohs(hdr->seq) >= seq_st.diff)
+    {
+	int i;
+	pj_bzero(&stream->rtcp_fb_nack, sizeof(stream->rtcp_fb_nack));
+	stream->rtcp_fb_nack.pid = pj_ntohs(hdr->seq) - seq_st.diff + 1;
+	for (i = 0; i < (seq_st.diff - 1); ++i) {
+	    stream->rtcp_fb_nack.blp <<= 1;
+	    stream->rtcp_fb_nack.blp |= 1;
+	}
+	stream->pending_rtcp_fb_nack = 1;
+    }
 
     /* Check if now is the time to transmit RTCP SR/RR report.
@@ -848,5 +970,5 @@
      */
     if (stream->dir == PJMEDIA_DIR_DECODING || stream->enc->paused) {
-	check_tx_rtcp(stream, pj_ntohl(hdr->ts));
+	check_tx_rtcp(stream);
     }
 
@@ -869,5 +991,5 @@
     if (stream->rtcp.received >= 10 && !stream->initial_rr) {
 	status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled,
-			   PJ_FALSE);
+			   PJ_FALSE, PJ_FALSE, PJ_FALSE);
         if (status != PJ_SUCCESS) {
             PJ_PERROR(4,(stream->name.ptr, status,
@@ -924,4 +1046,5 @@
     unsigned pkt_cnt = 0;
     pj_timestamp initial_time;
+    pj_timestamp now;
     pj_timestamp null_ts ={{0}};
 
@@ -967,14 +1090,11 @@
 
     /* Check if need to send keyframe. */
+    pj_get_timestamp(&now);
     if (stream->num_keyframe &&
 	(pj_cmp_timestamp(&null_ts, &stream->last_keyframe_tx) != 0))
     {
 	unsigned elapse_time;
-	pj_timestamp now;
-
-	pj_get_timestamp(&now);
 
 	elapse_time = pj_elapsed_msec(&stream->last_keyframe_tx, &now);
-
 	if (elapse_time > stream->info.sk_cfg.interval)
 	{
@@ -986,5 +1106,8 @@
     /* Init encoding option */
     pj_bzero(&enc_opt, sizeof(enc_opt));
-    if (stream->force_keyframe) {
+    if (stream->force_keyframe &&
+	pj_elapsed_msec(&stream->last_keyframe_tx, &now) >=
+			PJMEDIA_VID_STREAM_MIN_KEYFRAME_INTERVAL_MSEC)
+    {
 	/* Force encoder to generate keyframe */
 	enc_opt.force_keyframe = PJ_TRUE;
@@ -1013,9 +1136,9 @@
     pj_get_timestamp(&initial_time);
 
-    if ((stream->num_keyframe) &&
-	((frame_out.bit_info & PJMEDIA_VID_FRM_KEYFRAME)
-						  == PJMEDIA_VID_FRM_KEYFRAME))
+    if ((frame_out.bit_info & PJMEDIA_VID_FRM_KEYFRAME)
+						  == PJMEDIA_VID_FRM_KEYFRAME)
     {
 	stream->last_keyframe_tx = initial_time;
+	TRC_((channel->port.info.name.ptr, "Keyframe generated"));
     }
 
@@ -1130,5 +1253,5 @@
      */
     if (stream->dir != PJMEDIA_DIR_DECODING) {
-	check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts));
+	check_tx_rtcp(stream);
     }
 
@@ -1637,6 +1760,5 @@
     stream->dir = info->dir;
     stream->user_data = user_data;
-    stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) *
-			    info->codec_info.clock_rate / 1000;
+    stream->rtcp_interval = PJMEDIA_RTCP_INTERVAL + pj_rand()%1000 - 500;
     stream->rtcp_sdes_bye_disabled = info->rtcp_sdes_bye_disabled;
 
@@ -1816,13 +1938,17 @@
 
 	pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting);
+
+	/* Subscribe to RTCP events */
+	pjmedia_event_subscribe(NULL, &stream_event_cb, stream,
+				&stream->rtcp);
     }
 
     /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES,
-     * BYE, and XR.
+     * BYE, Feedback, and XR.
      */
     stream->out_rtcp_pkt_size =  sizeof(pjmedia_rtcp_sr_pkt) +
 				 sizeof(pjmedia_rtcp_common) +
 				 (4 + (unsigned)stream->cname.slen) +
-				 32;
+				 32 + 32;
     if (stream->out_rtcp_pkt_size > PJMEDIA_MAX_MTU)
 	stream->out_rtcp_pkt_size = PJMEDIA_MAX_MTU;
@@ -1897,4 +2023,47 @@
     stream->info.codec_param = pjmedia_vid_codec_param_clone(
 						pool, info->codec_param);
+    pjmedia_rtcp_fb_info_dup(pool, &stream->info.loc_rtcp_fb,
+			     &info->loc_rtcp_fb);
+    pjmedia_rtcp_fb_info_dup(pool, &stream->info.rem_rtcp_fb,
+			     &info->rem_rtcp_fb);
+
+    /* Check if we should send RTCP-FB */
+    if (stream->info.rem_rtcp_fb.cap_count) {
+	pjmedia_rtcp_fb_info *rfi = &stream->info.rem_rtcp_fb;
+	unsigned i;
+
+	for (i = 0; i < rfi->cap_count; ++i) {
+	    if (rfi->caps[i].type == PJMEDIA_RTCP_FB_NACK) {
+		if (rfi->caps[i].param.slen == 0) {
+		    stream->send_rtcp_fb_nack = PJ_TRUE;
+		    PJ_LOG(5,(stream->name.ptr, "Send RTCP-FB generic NACK"));
+		} else if (pj_stricmp2(&rfi->caps[i].param, "pli")==0) {
+		    stream->send_rtcp_fb_pli = PJ_TRUE;
+		    PJ_LOG(5,(stream->name.ptr, "Send RTCP-FB PLI"));
+		}
+	    }
+	}
+    }
+
+    /* Check if we should process incoming RTCP-FB */
+    stream->rtcp_fb_nack_cap_idx = -1;
+    stream->rtcp_fb_pli_cap_idx = -1;
+    if (stream->info.loc_rtcp_fb.cap_count) {
+	pjmedia_rtcp_fb_info *lfi = &stream->info.loc_rtcp_fb;
+	unsigned i;
+
+	for (i = 0; i < lfi->cap_count; ++i) {
+	    if (lfi->caps[i].type == PJMEDIA_RTCP_FB_NACK) {
+		if (lfi->caps[i].param.slen == 0) {
+		    stream->rtcp_fb_nack_cap_idx = i;
+		    PJ_LOG(5,(stream->name.ptr,
+			      "Receive RTCP-FB generic NACK"));
+		} else if (pj_stricmp2(&lfi->caps[i].param, "pli")==0) {
+		    stream->rtcp_fb_pli_cap_idx = i;
+		    PJ_LOG(5,(stream->name.ptr, "Receive RTCP-FB PLI"));
+		}
+	    }
+	}
+    }
 
     /* Success! */
@@ -1931,7 +2100,10 @@
 #endif
 
+    /* Unsubscribe events from RTCP */
+    pjmedia_event_unsubscribe(NULL, &stream_event_cb, stream, &stream->rtcp);
+
     /* Send RTCP BYE (also SDES) */
     if (stream->transport && !stream->rtcp_sdes_bye_disabled) {
-	send_rtcp(stream, PJ_TRUE, PJ_TRUE);
+	send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE);
     }
 
@@ -2173,4 +2345,6 @@
 						pjmedia_vid_stream *stream)
 {
+    pj_timestamp now;
+
     PJ_ASSERT_RETURN(stream, PJ_EINVAL);
 
@@ -2178,4 +2352,11 @@
 	return PJ_EINVALIDOP;
 
+    pj_get_timestamp(&now);
+    if (pj_elapsed_msec(&stream->last_keyframe_tx, &now) <
+			PJMEDIA_VID_STREAM_MIN_KEYFRAME_INTERVAL_MSEC)
+    {
+	return PJ_ETOOMANY;
+    }
+
     stream->force_keyframe = PJ_TRUE;
 
@@ -2192,5 +2373,5 @@
     PJ_ASSERT_RETURN(stream, PJ_EINVAL);
 
-    return send_rtcp(stream, PJ_TRUE, PJ_FALSE);
+    return send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE, PJ_FALSE);
 }
 
@@ -2205,5 +2386,5 @@
 
     if (stream->enc && stream->transport) {
-	return send_rtcp(stream, PJ_TRUE, PJ_TRUE);
+	return send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE, PJ_FALSE);
     }
 
Index: pjproject/trunk/pjmedia/src/pjmedia/vid_stream_info.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/vid_stream_info.c	(revision 6105)
+++ pjproject/trunk/pjmedia/src/pjmedia/vid_stream_info.c	(revision 6106)
@@ -25,5 +25,4 @@
 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
 
-static const pj_str_t ID_VIDEO = { "video", 5};
 static const pj_str_t ID_IN = { "IN", 2 };
 static const pj_str_t ID_IP4 = { "IP4", 3};
@@ -217,5 +216,5 @@
 
     /* Media type must be video */
-    if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) != 0)
+    if (pjmedia_get_type(&local_m->desc.media) != PJMEDIA_TYPE_VIDEO)
 	return PJMEDIA_EINVALIMEDIATYPE;
 
@@ -403,4 +402,16 @@
     si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
 
+    /* Get local RTCP-FB info */
+    status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, stream_idx,
+					 si->rx_pt, &si->loc_rtcp_fb);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Get remote RTCP-FB info */
+    status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, stream_idx,
+					 si->tx_pt, &si->rem_rtcp_fb);
+    if (status != PJ_SUCCESS)
+	return status;
+
     return status;
 }
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 6105)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 6106)
@@ -861,5 +861,6 @@
      * the call. Value is bitmask of #pjsua_vid_req_keyframe_method.
      *
-     * Default: PJSUA_VID_REQ_KEYFRAME_SIP_INFO
+     * Default: (PJSUA_VID_REQ_KEYFRAME_SIP_INFO | 
+     *		 PJSUA_VID_REQ_KEYFRAME_RTCP_PLI)
      */
     unsigned	     req_keyframe_method;
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 6105)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 6106)
@@ -618,5 +618,5 @@
     opt->vid_cnt = 1;
     opt->req_keyframe_method = PJSUA_VID_REQ_KEYFRAME_SIP_INFO |
-			     PJSUA_VID_REQ_KEYFRAME_RTCP_PLI;
+			       PJSUA_VID_REQ_KEYFRAME_RTCP_PLI;
 #endif
 }
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 6105)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 6106)
@@ -1336,5 +1336,5 @@
 	const pjmedia_sdp_media *m = sdp->media[i];
 	const pjmedia_sdp_conn *c;
-	static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
+	pj_uint32_t proto;
 
 	/* Skip different media */
@@ -1347,5 +1347,7 @@
 
 	/* Supported transports */
-	if (pj_stristr(&m->desc.transport, &ID_RTP_SAVP)) {
+	proto = pjmedia_sdp_transport_get_proto(&m->desc.transport);
+	if (PJMEDIA_TP_PROTO_HAS_FLAG(proto, PJMEDIA_TP_PROTO_RTP_SAVP))
+	{
 	    switch (use_srtp) {
 	    case PJMEDIA_SRTP_MANDATORY:
@@ -1358,5 +1360,6 @@
 		break;
 	    }
-	} else if (pj_stricmp2(&m->desc.transport, "RTP/AVP")==0) {
+	} else if (PJMEDIA_TP_PROTO_HAS_FLAG(proto, PJMEDIA_TP_PROTO_RTP_AVP))
+	{
 	    switch (use_srtp) {
 	    case PJMEDIA_SRTP_MANDATORY:
@@ -1482,7 +1485,12 @@
 pj_status_t on_media_event(pjmedia_event *event, void *user_data)
 {
+    char ev_name[5];
     pj_status_t status = PJ_SUCCESS;
 
     PJ_UNUSED_ARG(user_data);
+
+    pjmedia_fourcc_name(event->type, ev_name);
+    PJ_LOG(4,(THIS_FILE, "Received media event type=%s, src=%p, epub=%p",
+			 ev_name, event->src, event->epub));
 
     /* Forward the event */
@@ -1500,6 +1508,14 @@
     pjsua_call_media *call_med = (pjsua_call_media*)user_data;
     pjsua_call *call = call_med? call_med->call : NULL;
+    char ev_name[5];
     pj_status_t status = PJ_SUCCESS;
-  
+
+    pj_assert(call && call_med);
+    pjmedia_fourcc_name(event->type, ev_name);
+    PJ_LOG(5,(THIS_FILE, "Call %d: Media %d: Received media event, type=%s, "
+			 "src=%p, epub=%p",
+			 call->index, call_med->idx, ev_name,
+			 event->src, event->epub));
+
     switch(event->type) {
 	case PJMEDIA_EVENT_KEYFRAME_MISSING:
@@ -2675,14 +2691,60 @@
 	}
 
-	/* Add RTCP-FB info in SDP if we are offerer */
-	if (rem_sdp == NULL && acc->cfg.rtcp_fb_cfg.cap_count) {
+	/* Setup RTCP-FB */
+	{
+	    pjmedia_rtcp_fb_setting rtcp_cfg;
+	    pjmedia_rtcp_fb_setting_default(&rtcp_cfg);
+
+	    /* Add RTCP-FB PLI if PJSUA_VID_REQ_KEYFRAME_RTCP_PLI is set */
+	    if (call_med->type == PJMEDIA_TYPE_VIDEO &&
+		(call->opt.req_keyframe_method &
+		 PJSUA_VID_REQ_KEYFRAME_RTCP_PLI))
+	    {
+		rtcp_cfg.cap_count = 1;
+		pj_strset2(&rtcp_cfg.caps[0].codec_id, (char*)"*");
+		rtcp_cfg.caps[0].type = PJMEDIA_RTCP_FB_NACK;
+		pj_strset2(&rtcp_cfg.caps[0].param, (char*)"pli");
+	    }
+
+	    /* Should we put "RTP/AVPF" in SDP?*/
+	    if (rem_sdp) {
+		/* For answer, match remote offer */
+		unsigned rem_proto = 0;
+		rem_proto = pjmedia_sdp_transport_get_proto(
+					&rem_sdp->media[mi]->desc.transport);
+		rtcp_cfg.dont_use_avpf =
+			!PJMEDIA_TP_PROTO_HAS_FLAG(rem_proto, 
+						PJMEDIA_TP_PROFILE_RTCP_FB);
+	    } else {
+		/* For offer, check account setting */
+		rtcp_cfg.dont_use_avpf = acc->cfg.rtcp_fb_cfg.dont_use_avpf ||
+					 (acc->cfg.rtcp_fb_cfg.cap_count == 0
+					  && rtcp_cfg.cap_count == 0);
+	    }
+
 	    status = pjmedia_rtcp_fb_encode_sdp(pool, pjsua_var.med_endpt,
-						&acc->cfg.rtcp_fb_cfg, sdp,
+						&rtcp_cfg, sdp,
 						mi, rem_sdp);
 	    if (status != PJ_SUCCESS) {
 		PJ_PERROR(3,(THIS_FILE, status,
-			     "Call %d media %d: Failed to encode RTCP-FB "
+			     "Call %d media %d: Failed to encode RTCP-FB PLI "
 			     "setting to SDP",
 			     call_id, mi));
+	    }
+
+	    /* Add any other RTCP-FB setting configured in account setting */
+	    if (acc->cfg.rtcp_fb_cfg.cap_count) {
+		pj_bool_t tmp = rtcp_cfg.dont_use_avpf;
+		rtcp_cfg = acc->cfg.rtcp_fb_cfg;
+		rtcp_cfg.dont_use_avpf = tmp;
+		status = pjmedia_rtcp_fb_encode_sdp(pool, pjsua_var.med_endpt,
+						    &rtcp_cfg, sdp,
+						    mi, rem_sdp);
+		if (status != PJ_SUCCESS) {
+		    PJ_PERROR(3,(THIS_FILE, status,
+				 "Call %d media %d: Failed to encode account "
+				 "RTCP-FB setting to SDP",
+				 call_id, mi));
+		}
 	    }
 	}
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c	(revision 6105)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c	(revision 6106)
@@ -1083,4 +1083,8 @@
 	    goto on_error;
 
+	/* Subscribe to video stream events */
+	pjmedia_event_subscribe(NULL, &call_media_on_event,
+				call_med, call_med->strm.v.stream);
+
 	/* Start stream */
 	status = pjmedia_vid_stream_start(call_med->strm.v.stream);
@@ -1287,4 +1291,7 @@
 	call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
     }
+
+    /* Unsubscribe from video stream events */
+    pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med, strm);
 
     pjmedia_vid_stream_destroy(strm);
