Index: pjproject/trunk/pjmedia/src/pjmedia/vid_stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/vid_stream.c	(revision 5982)
+++ pjproject/trunk/pjmedia/src/pjmedia/vid_stream.c	(revision 6005)
@@ -1,4 +1,4 @@
 /* $Id$ */
-/* 
+/*
  * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
  *
@@ -15,5 +15,5 @@
  * 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 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include <pjmedia/vid_stream.h>
@@ -75,4 +75,6 @@
 #define MIN_CHUNKS_PER_FRM	30
 
+/*  Number of send error before repeat the report. */
+#define SEND_ERR_COUNT_TO_REPORT	50
 
 /**
@@ -115,5 +117,4 @@
 
     pjmedia_transport	    *transport;	    /**< Stream transport.	    */
-    unsigned		     send_err_cnt;  /**< Send error count.          */
 
     pj_mutex_t		    *jb_mutex;
@@ -137,5 +138,5 @@
     pjmedia_event            fmt_event;	    /**< Buffered fmt_changed event
                                                  to avoid deadlock	    */
-    pjmedia_event            miss_keyframe_event; 
+    pjmedia_event            miss_keyframe_event;
 					    /**< Buffered missing keyframe
                                                  event for delayed republish*/
@@ -148,9 +149,9 @@
 					         frame assembly.	    */
     pj_bool_t		     force_keyframe;/**< Forced to encode keyframe? */
-    unsigned		     num_keyframe;  /**< The number of keyframe needed 
+    unsigned		     num_keyframe;  /**< The number of keyframe needed
 						 to be sent, after the stream
 						 is created. */
-    pj_timestamp	     last_keyframe_tx;  
-					    /**< Timestamp of the last 
+    pj_timestamp	     last_keyframe_tx;
+					    /**< Timestamp of the last
 						 keyframe. */
 
@@ -172,5 +173,8 @@
     pj_uint32_t		     last_dec_ts;   /**< Last decoded timestamp.    */
     int			     last_dec_seq;  /**< Last decoded sequence.     */
-    pj_status_t		     rtp_rx_last_err; /**< Last RTP recv() error.   */
+    pj_uint32_t		     rtp_tx_err_cnt;/**< The number of RTP
+						 send() error		    */
+    pj_uint32_t		     rtcp_tx_err_cnt;/**< The number of RTCP
+						  send() error		    */
 
     pj_timestamp	     ts_freq;	    /**< Timestamp frequency.	    */
@@ -178,6 +182,6 @@
     pj_sockaddr		     rem_rtp_addr;  /**< Remote RTP address	    */
     unsigned		     rem_rtp_flag;  /**< Indicator flag about
-						 packet from this addr. 
-						 0=no pkt, 1=good ssrc pkts, 
+						 packet from this addr.
+						 0=no pkt, 1=good ssrc pkts,
 						 2=bad ssrc pkts	    */
     pj_sockaddr		     rtp_src_addr;  /**< Actual packet src addr.    */
@@ -203,5 +207,5 @@
 
 static void on_rx_rtcp( void *data,
-                        void *pkt, 
+                        void *pkt,
                         pj_ssize_t bytes_read);
 
@@ -233,5 +237,5 @@
 }
 
-PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream, 
+PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream,
 				    char **buf, pj_ssize_t len)
 {
@@ -405,4 +409,30 @@
 }
 
+/**
+ * Publish transport error event.
+ */
+static void publish_tp_event(pjmedia_event_type event_type,
+			     pj_status_t status,
+			     pj_bool_t is_rtp,
+			     pjmedia_dir dir,
+			     pjmedia_vid_stream *stream)
+{
+    pjmedia_event ev;
+    pj_timestamp ts_now;
+
+    pj_get_timestamp(&ts_now);
+    pj_bzero(&ev.data.med_tp_err, sizeof(ev.data.med_tp_err));
+
+    /* Publish event. */
+    pjmedia_event_init(&ev, event_type,
+		       &ts_now, stream);
+    ev.data.med_tp_err.type = PJMEDIA_TYPE_VIDEO;
+    ev.data.med_tp_err.is_rtp = is_rtp;
+    ev.data.med_tp_err.dir = dir;
+    ev.data.med_tp_err.status = status;
+
+    pjmedia_event_publish(NULL, stream, &ev, 0);
+}
+
 #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
 /*
@@ -462,5 +492,5 @@
 
 #else
-    
+
     PJ_UNUSED_ARG(stream);
 
@@ -478,4 +508,5 @@
     int len, max_len;
     pj_status_t status;
+    pjmedia_vid_channel *channel = stream->enc;
 
     /* Build RTCP RR/SR packet */
@@ -526,5 +557,13 @@
     /* Send! */
     status = pjmedia_transport_send_rtcp(stream->transport, pkt, len);
-
+    if (status != PJ_SUCCESS) {
+	if (stream->rtcp_tx_err_cnt++ == 0) {
+	    LOGERR_((channel->port.info.name.ptr, status,
+		     "Error sending RTCP"));
+	}
+	if (stream->rtcp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) {
+	    stream->rtcp_tx_err_cnt = 0;
+	}
+    }
     return status;
 }
@@ -539,5 +578,5 @@
 static void check_tx_rtcp(pjmedia_vid_stream *stream, pj_uint32_t timestamp)
 {
-    /* Note that timestamp may represent local or remote timestamp, 
+    /* Note that timestamp may represent local or remote timestamp,
      * depending on whether this function is called from put_frame()
      * or get_frame().
@@ -546,10 +585,10 @@
 
     if (stream->rtcp_last_tx == 0) {
-	
+
 	stream->rtcp_last_tx = timestamp;
 
     } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
 	pj_status_t status;
-	
+
 	status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE);
 	if (status != PJ_SUCCESS) {
@@ -591,5 +630,5 @@
 /*
  * This callback is called by stream transport on receipt of packets
- * in the RTP socket. 
+ * in the RTP socket.
  */
 static void on_rx_rtp( pjmedia_tp_cb_param *param)
@@ -612,15 +651,14 @@
 	    return;
 	}
-	if (stream->rtp_rx_last_err != status) {
-	    char errmsg[PJ_ERR_MSG_SIZE];
-	    pj_strerror(status, errmsg, sizeof(errmsg));
-	    PJ_LOG(4,(channel->port.info.name.ptr,
-		      "Unable to receive RTP packet, recv() returned %d: %s",
-		      status, errmsg));
-	    stream->rtp_rx_last_err = status;
+
+	LOGERR_((channel->port.info.name.ptr, status,
+		 "Unable to receive RTP packet"));
+
+	if (status == PJ_ESOCKETSTOP) {
+	    /* Publish receive error event. */
+	    publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_TRUE,
+			     PJMEDIA_DIR_DECODING, stream);
 	}
 	return;
-    } else {
-	stream->rtp_rx_last_err = PJ_SUCCESS;
     }
 
@@ -653,7 +691,7 @@
     pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, PJ_TRUE);
     if (seq_st.status.value) {
-	TRC_  ((channel->port.info.name.ptr, 
+	TRC_  ((channel->port.info.name.ptr,
 		"RTP status: badpt=%d, badssrc=%d, dup=%d, "
-		"outorder=%d, probation=%d, restart=%d", 
+		"outorder=%d, probation=%d, restart=%d",
 		seq_st.status.flag.badpt,
 		seq_st.status.flag.badssrc,
@@ -691,5 +729,5 @@
     }
 
-    /* See if source address of RTP packet is different than the 
+    /* See if source address of RTP packet is different than the
      * configured address, and check if we need to tell the
      * media transport to switch RTP remote address.
@@ -705,5 +743,5 @@
 	} else {
 	    stream->rtp_src_cnt++;
-	    
+
 	    if (stream->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) {
 	    	if (stream->rem_rtp_flag == 1 ||
@@ -718,5 +756,5 @@
 	    	    pkt_discarded = PJ_TRUE;
 	    	    goto on_return;
-	    	}	    	
+	    	}
 	    	if (stream->info.has_rem_ssrc && !seq_st.status.flag.badssrc
 	    	    && stream->rem_rtp_flag != 1)
@@ -793,5 +831,5 @@
     } else {
 	/* Just put the payload into jitter buffer */
-	pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0, 
+	pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0,
 				pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL);
 
@@ -844,17 +882,24 @@
 /*
  * This callback is called by stream transport on receipt of packets
- * in the RTCP socket. 
+ * in the RTCP socket.
  */
 static void on_rx_rtcp( void *data,
-                        void *pkt, 
+                        void *pkt,
                         pj_ssize_t bytes_read)
 {
     pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
+    pj_status_t status;
 
     /* Check for errors */
     if (bytes_read < 0) {
-	if (bytes_read != -PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
-	    LOGERR_((stream->cname.ptr, (pj_status_t)-bytes_read,
-		     "RTCP recv() error"));
+	status = (pj_status_t)-bytes_read;
+	if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) {
+	    return;
+	}
+	LOGERR_((stream->cname.ptr, status, "Unable to receive RTCP packet"));
+	if (status == PJ_ESOCKETSTOP) {
+	    /* Publish receive error event. */
+	    publish_tp_event(PJMEDIA_EVENT_MEDIA_TP_ERR, status, PJ_FALSE,
+			     PJMEDIA_DIR_DECODING, stream);
 	}
 	return;
@@ -889,5 +934,5 @@
 	pj_uint32_t dtx_duration;
 
-	dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent, 
+	dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent,
 					   &frame->timestamp);
 
@@ -922,7 +967,7 @@
 
     /* Check if need to send keyframe. */
-    if (stream->num_keyframe && 
-	(pj_cmp_timestamp(&null_ts, &stream->last_keyframe_tx) != 0)) 
-    {	
+    if (stream->num_keyframe &&
+	(pj_cmp_timestamp(&null_ts, &stream->last_keyframe_tx) != 0))
+    {
 	unsigned elapse_time;
 	pj_timestamp now;
@@ -935,5 +980,5 @@
 	{
 	    stream->force_keyframe = PJ_TRUE;
-	    --stream->num_keyframe;		
+	    --stream->num_keyframe;
 	}
     }
@@ -965,10 +1010,10 @@
 	return status;
     }
-    
+
     pj_get_timestamp(&initial_time);
 
-    if ((stream->num_keyframe) && 
-	((frame_out.bit_info & PJMEDIA_VID_FRM_KEYFRAME) 
-						  == PJMEDIA_VID_FRM_KEYFRAME)) 
+    if ((stream->num_keyframe) &&
+	((frame_out.bit_info & PJMEDIA_VID_FRM_KEYFRAME)
+						  == PJMEDIA_VID_FRM_KEYFRAME))
     {
 	stream->last_keyframe_tx = initial_time;
@@ -1003,14 +1048,12 @@
 						    sizeof(pjmedia_rtp_hdr));
 	    if (status != PJ_SUCCESS) {
-		enum { COUNT_TO_REPORT = 20 };
-		if (stream->send_err_cnt++ == 0) {
+		if (stream->rtp_tx_err_cnt++ == 0) {
 		    LOGERR_((channel->port.info.name.ptr, status,
-			     "Transport send_rtp() error"));
+			     "Error sending RTP"));
 		}
-		if (stream->send_err_cnt > COUNT_TO_REPORT)
-		    stream->send_err_cnt = 0;
-		/* Ignore this error */
+		if (stream->rtp_tx_err_cnt > SEND_ERR_COUNT_TO_REPORT) {
+		    stream->rtp_tx_err_cnt = 0;
+		}
 	    }
-
 	    pjmedia_rtcp_tx_rtp(&stream->rtcp, (unsigned)frame_out.size);
 	    total_sent += frame_out.size;
@@ -1082,5 +1125,5 @@
 #endif
 
-    /* Check if now is the time to transmit RTCP SR/RR report. 
+    /* Check if now is the time to transmit RTCP SR/RR report.
      * We only do this when stream direction is not "decoding only", because
      * when it is, check_tx_rtcp() will be handled by get_frame().
@@ -1244,5 +1287,5 @@
 
 	    ts_diff = frm_ts - stream->last_dec_ts;
-	    
+
 	    /* Calculate new FPS based on RTP timestamp diff */
 	    if (stream->info.codec_info.clock_rate % ts_diff == 0) {
@@ -1273,5 +1316,5 @@
 		    pjmedia_jbuf_get_state(stream->jb, &jb_state);
 
-		    stream->dec_delay_cnt = 
+		    stream->dec_delay_cnt =
 				    ((PJMEDIA_VID_STREAM_DECODE_MIN_DELAY_MSEC *
 				      vfd->fps.num) +
@@ -1425,5 +1468,5 @@
     char fourcc_name[5];
     pjmedia_port_info *pi;
-    
+
     pj_assert(info->type == PJMEDIA_TYPE_VIDEO);
     pj_assert(dir == PJMEDIA_DIR_DECODING || dir == PJMEDIA_DIR_ENCODING);
@@ -1450,5 +1493,5 @@
     channel->paused = 1;
     channel->pt = pt;
-    
+
     /* Allocate buffer for outgoing packet. */
     if (dir == PJMEDIA_DIR_ENCODING) {
@@ -1553,9 +1596,9 @@
     /* Init stream/port name */
     stream->name.ptr = (char*) pj_pool_alloc(pool, M);
-    stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M, 
+    stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M,
 					 "vstrm%p", stream);
 
     /* Create and initialize codec: */
-    status = pjmedia_vid_codec_mgr_alloc_codec(stream->codec_mgr, 
+    status = pjmedia_vid_codec_mgr_alloc_codec(stream->codec_mgr,
 					       &info->codec_info,
 					       &stream->codec);
@@ -1567,5 +1610,5 @@
 	pjmedia_vid_codec_param def_param;
 
-	status = pjmedia_vid_codec_mgr_get_default_param(stream->codec_mgr, 
+	status = pjmedia_vid_codec_mgr_get_default_param(stream->codec_mgr,
 						         &info->codec_info,
 						         &def_param);
@@ -1642,5 +1685,5 @@
     stream->frame_size = vfd_enc->max_bps/8 * vfd_enc->fps.denum /
 			 vfd_enc->fps.num;
-    
+
     /* As the maximum frame_size is not represented directly by maximum bps
      * (which includes intra and predicted frames), let's increase the
@@ -1651,5 +1694,5 @@
 
     /* Validate the frame size */
-    if (stream->frame_size == 0 || 
+    if (stream->frame_size == 0 ||
 	stream->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE)
     {
@@ -1686,5 +1729,5 @@
 
     /* Create decoder channel */
-    status = create_channel( pool, stream, PJMEDIA_DIR_DECODING, 
+    status = create_channel( pool, stream, PJMEDIA_DIR_DECODING,
 			     info->rx_pt, info, &stream->dec);
     if (status != PJ_SUCCESS)
@@ -1692,5 +1735,5 @@
 
     /* Create encoder channel */
-    status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING, 
+    status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING,
 			     info->tx_pt, info, &stream->enc);
     if (status != PJ_SUCCESS)
@@ -1793,8 +1836,8 @@
     pj_sockaddr_cp(&stream->rem_rtp_addr, &info->rem_addr);
     if (info->rtcp_mux) {
-	pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr);    	
+	pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_addr);
     } else if (pj_sockaddr_has_addr(&info->rem_rtcp.addr)) {
 	pj_sockaddr_cp(&att_param.rem_rtcp, &info->rem_rtcp);
-    }    
+    }
     att_param.addr_len = pj_sockaddr_get_len(&info->rem_addr);
     att_param.rtp_cb2 = &on_rx_rtp;
@@ -1824,5 +1867,5 @@
 	pj_ssize_t len;
 
-	pj_ansi_snprintf(trace_name, sizeof(trace_name), 
+	pj_ansi_snprintf(trace_name, sizeof(trace_name),
 			 TRACE_JB_PATH_PREFIX "%s.csv",
 			 channel->port.info.name.ptr);
@@ -1832,5 +1875,5 @@
 	    stream->trace_jb_fd = TRACE_JB_INVALID_FD;
 	    PJ_PERROR(3,(THIS_FILE, status,
-			 "Failed creating RTP trace file '%s'", 
+			 "Failed creating RTP trace file '%s'",
 			 trace_name));
 	} else {
@@ -1876,5 +1919,5 @@
 
 	total_time = pj_elapsed_msec(&stream->tx_start, &stream->tx_end);
-	PJ_LOG(5, (stream->name.ptr, 
+	PJ_LOG(5, (stream->name.ptr,
 		   "RC stat: pkt_cnt=%.2f/image, sleep=%.2fms/s, fps=%.2f",
 		   stream->rc_total_pkt*1.0/stream->rc_total_img,
@@ -1889,5 +1932,5 @@
     }
 
-    /* Detach from transport 
+    /* Detach from transport
      * MUST NOT hold stream mutex while detaching from transport, as
      * it may cause deadlock. See ticket #460 for the details.
@@ -1913,5 +1956,5 @@
 
     /* Free mutex */
-    
+
     if (stream->jb_mutex) {
 	pj_mutex_destroy(stream->jb_mutex);
