* commit '86d9181cf41edc3382bf2481f95a2fb321058689': rtpdec: Support sending RTCP feedback packets Conflicts: libavformat/version.h
Unified · Split
| @@ -30,6 +30,8 @@ · 29 unmodified lines | ||
| 30 | 30 | #include "rtpdec.h" |
| 31 | 31 | #include "rtpdec_formats.h" |
| 32 | 32 | |
| 33 | +#define MIN_FEEDBACK_INTERVAL 200000 /* 200 ms in us */ | |
| 34 | + | |
| 33 | 35 | static RTPDynamicProtocolHandler realmedia_mp3_dynamic_handler = { |
| 34 | 36 | .enc_name = "X-MP3-draft-00", |
| 35 | 37 | .codec_type = AVMEDIA_TYPE_AUDIO, |
| @@ -366,6 +368,100 @@ void ff_rtp_send_punch_packets(URLContext *rtp_handle) · 330 unmodified lines | ||
| 366 | 368 | av_free(buf); |
| 367 | 369 | } |
| 368 | 370 | |
| 371 | +static int find_missing_packets(RTPDemuxContext *s, uint16_t *first_missing, | |
| 372 | + uint16_t *missing_mask) | |
| 373 | +{ | |
| 374 | + int i; | |
| 375 | + uint16_t next_seq = s->seq + 1; | |
| 376 | + RTPPacket *pkt = s->queue; | |
| 377 | + | |
| 378 | + if (!pkt || pkt->seq == next_seq) | |
| 379 | + return 0; | |
| 380 | + | |
| 381 | + *missing_mask = 0; | |
| 382 | + for (i = 1; i <= 16; i++) { | |
| 383 | + uint16_t missing_seq = next_seq + i; | |
| 384 | + while (pkt) { | |
| 385 | + int16_t diff = pkt->seq - missing_seq; | |
| 386 | + if (diff >= 0) | |
| 387 | + break; | |
| 388 | + pkt = pkt->next; | |
| 389 | + } | |
| 390 | + if (!pkt) | |
| 391 | + break; | |
| 392 | + if (pkt->seq == missing_seq) | |
| 393 | + continue; | |
| 394 | + *missing_mask |= 1 << (i - 1); | |
| 395 | + } | |
| 396 | + | |
| 397 | + *first_missing = next_seq; | |
| 398 | + return 1; | |
| 399 | +} | |
| 400 | + | |
| 401 | +int ff_rtp_send_rtcp_feedback(RTPDemuxContext *s, URLContext *fd, | |
| 402 | + AVIOContext *avio) | |
| 403 | +{ | |
| 404 | + int len, need_keyframe, missing_packets; | |
| 405 | + AVIOContext *pb; | |
| 406 | + uint8_t *buf; | |
| 407 | + int64_t now; | |
| 408 | + uint16_t first_missing, missing_mask; | |
| 409 | + | |
| 410 | + if (!fd && !avio) | |
| 411 | + return -1; | |
| 412 | + | |
| 413 | + need_keyframe = s->handler && s->handler->need_keyframe && | |
| 414 | + s->handler->need_keyframe(s->dynamic_protocol_context); | |
| 415 | + missing_packets = find_missing_packets(s, &first_missing, &missing_mask); | |
| 416 | + | |
| 417 | + if (!need_keyframe && !missing_packets) | |
| 418 | + return 0; | |
| 419 | + | |
| 420 | + /* Send new feedback if enough time has elapsed since the last | |
| 421 | + * feedback packet. */ | |
| 422 | + | |
| 423 | + now = av_gettime(); | |
| 424 | + if (s->last_feedback_time && | |
| 425 | + (now - s->last_feedback_time) < MIN_FEEDBACK_INTERVAL) | |
| 426 | + return 0; | |
| 427 | + s->last_feedback_time = now; | |
| 428 | + | |
| 429 | + if (!fd) | |
| 430 | + pb = avio; | |
| 431 | + else if (avio_open_dyn_buf(&pb) < 0) | |
| 432 | + return -1; | |
| 433 | + | |
| 434 | + if (need_keyframe) { | |
| 435 | + avio_w8(pb, (RTP_VERSION << 6) | 1); /* PLI */ | |
| 436 | + avio_w8(pb, RTCP_PSFB); | |
| 437 | + avio_wb16(pb, 2); /* length in words - 1 */ | |
| 438 | + // our own SSRC: we use the server's SSRC + 1 to avoid conflicts | |
| 439 | + avio_wb32(pb, s->ssrc + 1); | |
| 440 | + avio_wb32(pb, s->ssrc); // server SSRC | |
| 441 | + } | |
| 442 | + | |
| 443 | + if (missing_packets) { | |
| 444 | + avio_w8(pb, (RTP_VERSION << 6) | 1); /* NACK */ | |
| 445 | + avio_w8(pb, RTCP_RTPFB); | |
| 446 | + avio_wb16(pb, 3); /* length in words - 1 */ | |
| 447 | + avio_wb32(pb, s->ssrc + 1); | |
| 448 | + avio_wb32(pb, s->ssrc); // server SSRC | |
| 449 | + | |
| 450 | + avio_wb16(pb, first_missing); | |
| 451 | + avio_wb16(pb, missing_mask); | |
| 452 | + } | |
| 453 | + | |
| 454 | + avio_flush(pb); | |
| 455 | + if (!fd) | |
| 456 | + return 0; | |
| 457 | + len = avio_close_dyn_buf(pb, &buf); | |
| 458 | + if (len > 0 && buf) { | |
| 459 | + ffurl_write(fd, buf, len); | |
| 460 | + av_free(buf); | |
| 461 | + } | |
| 462 | + return 0; | |
| 463 | +} | |
| 464 | + | |
| 369 | 465 | /** |
| 370 | 466 | * open a new RTP parse context for stream 'st'. 'st' can be NULL for |
| 371 | 467 | * 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); · 72 unmodified lines | ||
| 73 | 73 | */ |
| 74 | 74 | int ff_rtp_check_and_send_back_rr(RTPDemuxContext *s, URLContext *fd, |
| 75 | 75 | AVIOContext *avio, int count); |
| 76 | +int ff_rtp_send_rtcp_feedback(RTPDemuxContext *s, URLContext *fd, | |
| 77 | + AVIOContext *avio); | |
| 76 | 78 | |
| 77 | 79 | // these statistics are used for rtcp receiver reports... |
| 78 | 80 | typedef struct RTPStatistics { |
| @@ -130,6 +132,7 @@ struct RTPDynamicProtocolHandler { · 51 unmodified lines | ||
| 130 | 132 | void (*free)(PayloadContext *protocol_data); |
| 131 | 133 | /** Parse handler for this dynamic packet */ |
| 132 | 134 | DynamicPayloadPacketHandlerProc parse_packet; |
| 135 | + int (*need_keyframe)(PayloadContext *context); | |
| 133 | 136 | |
| 134 | 137 | struct RTPDynamicProtocolHandler *next; |
| 135 | 138 | }; |
| @@ -180,6 +183,8 @@ struct RTPDemuxContext { · 44 unmodified lines | ||
| 180 | 183 | unsigned int packet_count; |
| 181 | 184 | unsigned int octet_count; |
| 182 | 185 | unsigned int last_octet_count; |
| 186 | + int64_t last_feedback_time; | |
| 187 | + | |
| 183 | 188 | /* buffer for partially parsed packets */ |
| 184 | 189 | uint8_t buf[RTP_MAX_PACKET_LENGTH]; |
| 185 | 190 | |
| @@ -380,6 +380,8 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, · 379 unmodified lines | ||
| 380 | 380 | get_word(buf1, sizeof(buf1), &p); /* protocol */ |
| 381 | 381 | if (!strcmp(buf1, "udp")) |
| 382 | 382 | rt->transport = RTSP_TRANSPORT_RAW; |
| 383 | + else if (strstr(buf1, "/AVPF") || strstr(buf1, "/SAVPF")) | |
| 384 | + rtsp_st->feedback = 1; | |
| 383 | 385 | |
| 384 | 386 | /* XXX: handle list of formats */ |
| 385 | 387 | get_word(buf1, sizeof(buf1), &p); /* format list */ |
| @@ -1932,6 +1934,12 @@ redo: · 1546 unmodified lines | ||
| 1932 | 1934 | ret = ff_rdt_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); |
| 1933 | 1935 | } else if (rt->transport == RTSP_TRANSPORT_RTP) { |
| 1934 | 1936 | ret = ff_rtp_parse_packet(rtsp_st->transport_priv, pkt, &rt->recvbuf, len); |
| 1937 | + if (rtsp_st->feedback) { | |
| 1938 | + AVIOContext *pb = NULL; | |
| 1939 | + if (rt->lower_transport == RTSP_LOWER_TRANSPORT_CUSTOM) | |
| 1940 | + pb = s->pb; | |
| 1941 | + ff_rtp_send_rtcp_feedback(rtsp_st->transport_priv, rtsp_st->rtp_handle, pb); | |
| 1942 | + } | |
| 1935 | 1943 | if (ret < 0) { |
| 1936 | 1944 | /* Either bad packet, or a RTCP packet. Check if the |
| 1937 | 1945 | * first_rtcp_ntp_time field was initialized. */ |
| @@ -437,6 +437,9 @@ typedef struct RTSPStream { · 436 unmodified lines | ||
| 437 | 437 | /** private data associated with the dynamic protocol */ |
| 438 | 438 | PayloadContext *dynamic_protocol_context; |
| 439 | 439 | //@} |
| 440 | + | |
| 441 | + /** Enable sending RTCP feedback messages according to RFC 4585 */ | |
| 442 | + int feedback; | |
| 440 | 443 | } RTSPStream; |
| 441 | 444 | |
| 442 | 445 | void ff_rtsp_parse_line(RTSPMessageHeader *reply, const char *buf, |
| @@ -31,7 +31,7 @@ · 30 unmodified lines | ||
| 31 | 31 | |
| 32 | 32 | #define LIBAVFORMAT_VERSION_MAJOR 54 |
| 33 | 33 | #define LIBAVFORMAT_VERSION_MINOR 59 |
| 34 | −#define LIBAVFORMAT_VERSION_MICRO 106 | |
| 34 | +#define LIBAVFORMAT_VERSION_MICRO 107 | |
| 35 | 35 | |
| 36 | 36 | #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
| 37 | 37 | LIBAVFORMAT_VERSION_MINOR, \ |