summaryrefslogtreecommitdiff
path: root/libavformat/rtpdec.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat/rtpdec.c')
-rw-r--r--libavformat/rtpdec.c96
1 files changed, 96 insertions, 0 deletions
diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c
index 348b796d83..4064e70192 100644
--- a/libavformat/rtpdec.c
+++ b/libavformat/rtpdec.c
@@ -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