* commit '86d9181cf41edc3382bf2481f95a2fb321058689': rtpdec: Support sending RTCP feedback packets Conflicts: libavformat/version.h Merged-by: Michael Niedermayer <michaelni@gmx.at>tags/n1.2
| @@ -30,6 +30,8 @@ | |||
| #include "rtpdec.h" | |||
| #include "rtpdec_formats.h" | |||
| #define MIN_FEEDBACK_INTERVAL 200000 /* 200 ms in us */ | |||
| static RTPDynamicProtocolHandler realmedia_mp3_dynamic_handler = { | |||
| .enc_name = "X-MP3-draft-00", | |||
| .codec_type = AVMEDIA_TYPE_AUDIO, | |||
| @@ -366,6 +368,100 @@ void ff_rtp_send_punch_packets(URLContext *rtp_handle) | |||
| av_free(buf); | |||
| } | |||
| static int find_missing_packets(RTPDemuxContext *s, uint16_t *first_missing, | |||
| uint16_t *missing_mask) | |||
| { | |||
| int i; | |||
| uint16_t next_seq = s->seq + 1; | |||
| RTPPacket *pkt = s->queue; | |||
| if (!pkt || pkt->seq == next_seq) | |||
| return 0; | |||
| *missing_mask = 0; | |||
| for (i = 1; i <= 16; i++) { | |||
| uint16_t missing_seq = next_seq + i; | |||
| while (pkt) { | |||
| int16_t diff = pkt->seq - missing_seq; | |||
| if (diff >= 0) | |||
| break; | |||
| pkt = pkt->next; | |||
| } | |||
| if (!pkt) | |||
| break; | |||
| if (pkt->seq == missing_seq) | |||
| continue; | |||
| *missing_mask |= 1 << (i - 1); | |||
| } | |||
| *first_missing = next_seq; | |||
| return 1; | |||
| } | |||
| int ff_rtp_send_rtcp_feedback(RTPDemuxContext *s, URLContext *fd, | |||
| AVIOContext *avio) | |||
| { | |||
| int len, need_keyframe, missing_packets; | |||
| AVIOContext *pb; | |||
| uint8_t *buf; | |||
| int64_t now; | |||
| uint16_t first_missing, missing_mask; | |||
| if (!fd && !avio) | |||
| return -1; | |||
| need_keyframe = s->handler && s->handler->need_keyframe && | |||
| s->handler->need_keyframe(s->dynamic_protocol_context); | |||
| missing_packets = find_missing_packets(s, &first_missing, &missing_mask); | |||
| if (!need_keyframe && !missing_packets) | |||
| return 0; | |||
| /* Send new feedback if enough time has elapsed since the last | |||
| * feedback packet. */ | |||
| now = av_gettime(); | |||
| if (s->last_feedback_time && | |||
| (now - s->last_feedback_time) < MIN_FEEDBACK_INTERVAL) | |||
| return 0; | |||
| s->last_feedback_time = now; | |||
| if (!fd) | |||
| pb = avio; | |||
| else if (avio_open_dyn_buf(&pb) < 0) | |||
| return -1; | |||
| if (need_keyframe) { | |||
| avio_w8(pb, (RTP_VERSION << 6) | 1); /* PLI */ | |||
| avio_w8(pb, RTCP_PSFB); | |||
| avio_wb16(pb, 2); /* length in words - 1 */ | |||
| // our own SSRC: we use the server's SSRC + 1 to avoid conflicts | |||
| avio_wb32(pb, s->ssrc + 1); | |||
| avio_wb32(pb, s->ssrc); // server SSRC | |||
| } | |||
| if (missing_packets) { | |||
| avio_w8(pb, (RTP_VERSION << 6) | 1); /* NACK */ | |||
| avio_w8(pb, RTCP_RTPFB); | |||
| avio_wb16(pb, 3); /* length in words - 1 */ | |||
| avio_wb32(pb, s->ssrc + 1); | |||
| avio_wb32(pb, s->ssrc); // server SSRC | |||
| avio_wb16(pb, first_missing); | |||
| avio_wb16(pb, missing_mask); | |||
| } | |||
| avio_flush(pb); | |||
| if (!fd) | |||
| return 0; | |||
| len = avio_close_dyn_buf(pb, &buf); | |||
| if (len > 0 && buf) { | |||
| ffurl_write(fd, buf, len); | |||
| av_free(buf); | |||
| } | |||
| return 0; | |||
| } | |||
| /** | |||
| * open a new RTP parse context for stream 'st'. 'st' can be NULL for | |||
| * MPEG2-TS streams to indicate that they should be demuxed inside the | |||
| @@ -73,6 +73,8 @@ void ff_rtp_send_punch_packets(URLContext* rtp_handle); | |||
| */ | |||
| int ff_rtp_check_and_send_back_rr(RTPDemuxContext *s, URLContext *fd, | |||
| AVIOContext *avio, int count); | |||
| int ff_rtp_send_rtcp_feedback(RTPDemuxContext *s, URLContext *fd, | |||
| AVIOContext *avio); | |||
| // these statistics are used for rtcp receiver reports... | |||
| typedef struct RTPStatistics { | |||
| @@ -130,6 +132,7 @@ struct RTPDynamicProtocolHandler { | |||
| void (*free)(PayloadContext *protocol_data); | |||
| /** Parse handler for this dynamic packet */ | |||
| DynamicPayloadPacketHandlerProc parse_packet; | |||
| int (*need_keyframe)(PayloadContext *context); | |||
| struct RTPDynamicProtocolHandler *next; | |||
| }; | |||
| @@ -180,6 +183,8 @@ struct RTPDemuxContext { | |||
| unsigned int packet_count; | |||
| unsigned int octet_count; | |||
| unsigned int last_octet_count; | |||
| int64_t last_feedback_time; | |||
| /* buffer for partially parsed packets */ | |||
| uint8_t buf[RTP_MAX_PACKET_LENGTH]; | |||
| @@ -380,6 +380,8 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, | |||
| get_word(buf1, sizeof(buf1), &p); /* protocol */ | |||
| if (!strcmp(buf1, "udp")) | |||
| rt->transport = RTSP_TRANSPORT_RAW; | |||
| else if (strstr(buf1, "/AVPF") || strstr(buf1, "/SAVPF")) | |||
| rtsp_st->feedback = 1; | |||
| /* XXX: handle list of formats */ | |||
| get_word(buf1, sizeof(buf1), &p); /* format list */ | |||
| @@ -1932,6 +1934,12 @@ redo: | |||
| ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); | |||
| } else if (rt->transport == RTSP_TRANSPORT_RTP) { | |||
| ret = ff_rtp_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); | |||
| if (rtsp_st->feedback) { | |||
| AVIOContext *pb = NULL; | |||
| if (rt->lower_transport == RTSP_LOWER_TRANSPORT_CUSTOM) | |||
| pb = s->pb; | |||
| ff_rtp_send_rtcp_feedback(rtsp_st->transport_priv, rtsp_st->rtp_handle, pb); | |||
| } | |||
| if (ret < 0) { | |||
| /* Either bad packet, or a RTCP packet. Check if the | |||
| * first_rtcp_ntp_time field was initialized. */ | |||
| @@ -437,6 +437,9 @@ typedef struct RTSPStream { | |||
| /** private data associated with the dynamic protocol */ | |||
| PayloadContext *dynamic_protocol_context; | |||
| //@} | |||
| /** Enable sending RTCP feedback messages according to RFC 4585 */ | |||
| int feedback; | |||
| } RTSPStream; | |||
| void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, | |||
| @@ -31,7 +31,7 @@ | |||
| #define LIBAVFORMAT_VERSION_MAJOR 54 | |||
| #define LIBAVFORMAT_VERSION_MINOR 59 | |||
| #define LIBAVFORMAT_VERSION_MICRO 106 | |||
| #define LIBAVFORMAT_VERSION_MICRO 107 | |||
| #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | |||
| LIBAVFORMAT_VERSION_MINOR, \ | |||