1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "media/cast/rtcp/rtcp_receiver.h" 6 7#include "base/logging.h" 8#include "media/cast/rtcp/rtcp_utility.h" 9 10namespace { 11 12media::cast::CastLoggingEvent TranslateToLogEventFromWireFormat(uint8 event) { 13 switch (event) { 14 case 1: 15 return media::cast::kAckSent; 16 case 2: 17 return media::cast::kAudioPlayoutDelay; 18 case 3: 19 return media::cast::kAudioFrameDecoded; 20 case 4: 21 return media::cast::kVideoFrameDecoded; 22 case 5: 23 return media::cast::kVideoRenderDelay; 24 case 6: 25 return media::cast::kPacketReceived; 26 default: 27 // If the sender adds new log messages we will end up here until we add 28 // the new messages in the receiver. 29 VLOG(1) << "Unexpected log message received: " << static_cast<int>(event); 30 NOTREACHED(); 31 return media::cast::kUnknown; 32 } 33} 34 35media::cast::RtcpSenderFrameStatus TranslateToFrameStatusFromWireFormat( 36 uint8 status) { 37 switch (status) { 38 case 0: 39 return media::cast::kRtcpSenderFrameStatusUnknown; 40 case 1: 41 return media::cast::kRtcpSenderFrameStatusDroppedByEncoder; 42 case 2: 43 return media::cast::kRtcpSenderFrameStatusDroppedByFlowControl; 44 case 3: 45 return media::cast::kRtcpSenderFrameStatusSentToNetwork; 46 default: 47 // If the sender adds new log messages we will end up here until we add 48 // the new messages in the receiver. 49 NOTREACHED(); 50 VLOG(1) << "Unexpected status received: " << static_cast<int>(status); 51 return media::cast::kRtcpSenderFrameStatusUnknown; 52 } 53} 54 55} // namespace 56 57namespace media { 58namespace cast { 59 60RtcpReceiver::RtcpReceiver(scoped_refptr<CastEnvironment> cast_environment, 61 RtcpSenderFeedback* sender_feedback, 62 RtcpReceiverFeedback* receiver_feedback, 63 RtcpRttFeedback* rtt_feedback, 64 uint32 local_ssrc) 65 : ssrc_(local_ssrc), 66 remote_ssrc_(0), 67 sender_feedback_(sender_feedback), 68 receiver_feedback_(receiver_feedback), 69 rtt_feedback_(rtt_feedback), 70 cast_environment_(cast_environment) {} 71 72RtcpReceiver::~RtcpReceiver() {} 73 74void RtcpReceiver::SetRemoteSSRC(uint32 ssrc) { 75 remote_ssrc_ = ssrc; 76} 77 78void RtcpReceiver::IncomingRtcpPacket(RtcpParser* rtcp_parser) { 79 RtcpFieldTypes field_type = rtcp_parser->Begin(); 80 while (field_type != kRtcpNotValidCode) { 81 // Each "case" is responsible for iterate the parser to the next top 82 // level packet. 83 switch (field_type) { 84 case kRtcpSrCode: 85 HandleSenderReport(rtcp_parser); 86 break; 87 case kRtcpRrCode: 88 HandleReceiverReport(rtcp_parser); 89 break; 90 case kRtcpSdesCode: 91 HandleSDES(rtcp_parser); 92 break; 93 case kRtcpByeCode: 94 HandleBYE(rtcp_parser); 95 break; 96 case kRtcpXrCode: 97 HandleXr(rtcp_parser); 98 break; 99 case kRtcpGenericRtpFeedbackNackCode: 100 HandleNACK(rtcp_parser); 101 break; 102 case kRtcpGenericRtpFeedbackSrReqCode: 103 HandleSendReportRequest(rtcp_parser); 104 break; 105 case kRtcpPayloadSpecificPliCode: 106 HandlePLI(rtcp_parser); 107 break; 108 case kRtcpPayloadSpecificRpsiCode: 109 HandleRpsi(rtcp_parser); 110 break; 111 case kRtcpPayloadSpecificFirCode: 112 HandleFIR(rtcp_parser); 113 break; 114 case kRtcpPayloadSpecificAppCode: 115 HandlePayloadSpecificApp(rtcp_parser); 116 break; 117 case kRtcpApplicationSpecificCastReceiverLogCode: 118 HandleApplicationSpecificCastReceiverLog(rtcp_parser); 119 break; 120 case kRtcpApplicationSpecificCastSenderLogCode: 121 HandleApplicationSpecificCastSenderLog(rtcp_parser); 122 break; 123 case kRtcpPayloadSpecificRembCode: 124 case kRtcpPayloadSpecificRembItemCode: 125 case kRtcpPayloadSpecificCastCode: 126 case kRtcpPayloadSpecificCastNackItemCode: 127 case kRtcpApplicationSpecificCastReceiverLogFrameCode: 128 case kRtcpApplicationSpecificCastReceiverLogEventCode: 129 case kRtcpNotValidCode: 130 case kRtcpReportBlockItemCode: 131 case kRtcpSdesChunkCode: 132 case kRtcpGenericRtpFeedbackNackItemCode: 133 case kRtcpPayloadSpecificFirItemCode: 134 case kRtcpXrRrtrCode: 135 case kRtcpXrDlrrCode: 136 case kRtcpXrUnknownItemCode: 137 rtcp_parser->Iterate(); 138 NOTREACHED() << "Invalid state"; 139 break; 140 } 141 field_type = rtcp_parser->FieldType(); 142 } 143} 144 145void RtcpReceiver::HandleSenderReport(RtcpParser* rtcp_parser) { 146 RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType(); 147 const RtcpField& rtcp_field = rtcp_parser->Field(); 148 149 DCHECK(rtcp_field_type == kRtcpSrCode) << "Invalid state"; 150 151 // Synchronization source identifier for the originator of this SR packet. 152 uint32 remote_ssrc = rtcp_field.sender_report.sender_ssrc; 153 154 VLOG(1) << "Cast RTCP received SR from SSRC " << remote_ssrc; 155 156 if (remote_ssrc_ == remote_ssrc) { 157 RtcpSenderInfo remote_sender_info; 158 remote_sender_info.ntp_seconds = 159 rtcp_field.sender_report.ntp_most_significant; 160 remote_sender_info.ntp_fraction = 161 rtcp_field.sender_report.ntp_least_significant; 162 remote_sender_info.rtp_timestamp = 163 rtcp_field.sender_report.rtp_timestamp; 164 remote_sender_info.send_packet_count = 165 rtcp_field.sender_report.sender_packet_count; 166 remote_sender_info.send_octet_count = 167 rtcp_field.sender_report.sender_octet_count; 168 if (receiver_feedback_) { 169 receiver_feedback_->OnReceivedSenderReport(remote_sender_info); 170 } 171 } 172 rtcp_field_type = rtcp_parser->Iterate(); 173 while (rtcp_field_type == kRtcpReportBlockItemCode) { 174 HandleReportBlock(&rtcp_field, remote_ssrc); 175 rtcp_field_type = rtcp_parser->Iterate(); 176 } 177} 178 179void RtcpReceiver::HandleReceiverReport(RtcpParser* rtcp_parser) { 180 RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType(); 181 const RtcpField& rtcp_field = rtcp_parser->Field(); 182 183 DCHECK(rtcp_field_type == kRtcpRrCode) << "Invalid state"; 184 185 uint32 remote_ssrc = rtcp_field.receiver_report.sender_ssrc; 186 187 VLOG(1) << "Cast RTCP received RR from SSRC " << remote_ssrc; 188 189 rtcp_field_type = rtcp_parser->Iterate(); 190 while (rtcp_field_type == kRtcpReportBlockItemCode) { 191 HandleReportBlock(&rtcp_field, remote_ssrc); 192 rtcp_field_type = rtcp_parser->Iterate(); 193 } 194} 195 196void RtcpReceiver::HandleReportBlock(const RtcpField* rtcp_field, 197 uint32 remote_ssrc) { 198 // This will be called once per report block in the Rtcp packet. 199 // We filter out all report blocks that are not for us. 200 // Each packet has max 31 RR blocks. 201 // 202 // We can calculate RTT if we send a send report and get a report block back. 203 204 // |rtcp_field.ReportBlockItem.ssrc| is the ssrc identifier of the source to 205 // which the information in this reception report block pertains. 206 207 const RtcpFieldReportBlockItem& rb = rtcp_field->report_block_item; 208 209 // Filter out all report blocks that are not for us. 210 if (rb.ssrc != ssrc_) { 211 // This block is not for us ignore it. 212 return; 213 } 214 VLOG(1) << "Cast RTCP received RB from SSRC " << remote_ssrc; 215 cast_environment_->Logging()->InsertGenericEvent(kPacketLoss, 216 rb.fraction_lost); 217 cast_environment_->Logging()->InsertGenericEvent(kJitterMs, 218 rb.jitter); 219 220 RtcpReportBlock report_block; 221 report_block.remote_ssrc = remote_ssrc; 222 report_block.media_ssrc = rb.ssrc; 223 report_block.fraction_lost = rb.fraction_lost; 224 report_block.cumulative_lost = rb.cumulative_number_of_packets_lost; 225 report_block.extended_high_sequence_number = 226 rb.extended_highest_sequence_number; 227 report_block.jitter = rb.jitter; 228 report_block.last_sr = rb.last_sender_report; 229 report_block.delay_since_last_sr = rb.delay_last_sender_report; 230 231 if (rtt_feedback_) { 232 rtt_feedback_->OnReceivedDelaySinceLastReport(rb.ssrc, 233 rb.last_sender_report, 234 rb.delay_last_sender_report); 235 } 236} 237 238void RtcpReceiver::HandleSDES(RtcpParser* rtcp_parser) { 239 RtcpFieldTypes field_type = rtcp_parser->Iterate(); 240 while (field_type == kRtcpSdesChunkCode) { 241 HandleSDESChunk(rtcp_parser); 242 field_type = rtcp_parser->Iterate(); 243 } 244} 245 246void RtcpReceiver::HandleSDESChunk(RtcpParser* rtcp_parser) { 247 const RtcpField& rtcp_field = rtcp_parser->Field(); 248 VLOG(1) << "Cast RTCP received SDES with cname " << rtcp_field.c_name.name; 249} 250 251void RtcpReceiver::HandleXr(RtcpParser* rtcp_parser) { 252 RtcpFieldTypes rtcp_field_type = rtcp_parser->FieldType(); 253 const RtcpField& rtcp_field = rtcp_parser->Field(); 254 255 DCHECK(rtcp_field_type == kRtcpXrCode) << "Invalid state"; 256 257 uint32 remote_ssrc = rtcp_field.extended_report.sender_ssrc; 258 rtcp_field_type = rtcp_parser->Iterate(); 259 260 while (rtcp_field_type == kRtcpXrDlrrCode || 261 rtcp_field_type == kRtcpXrRrtrCode || 262 rtcp_field_type == kRtcpXrUnknownItemCode) { 263 if (rtcp_field_type == kRtcpXrRrtrCode) { 264 HandleRrtr(rtcp_parser, remote_ssrc); 265 } else if (rtcp_field_type == kRtcpXrDlrrCode) { 266 HandleDlrr(rtcp_parser); 267 } 268 rtcp_field_type = rtcp_parser->Iterate(); 269 } 270} 271 272void RtcpReceiver::HandleRrtr(RtcpParser* rtcp_parser, uint32 remote_ssrc) { 273 if (remote_ssrc_ != remote_ssrc) { 274 // Not to us. 275 return; 276 } 277 const RtcpField& rtcp_field = rtcp_parser->Field(); 278 RtcpReceiverReferenceTimeReport remote_time_report; 279 remote_time_report.remote_ssrc = remote_ssrc; 280 remote_time_report.ntp_seconds = rtcp_field.rrtr.ntp_most_significant; 281 remote_time_report.ntp_fraction = rtcp_field.rrtr.ntp_least_significant; 282 283 if (receiver_feedback_) { 284 receiver_feedback_->OnReceiverReferenceTimeReport(remote_time_report); 285 } 286} 287 288void RtcpReceiver::HandleDlrr(RtcpParser* rtcp_parser) { 289 const RtcpField& rtcp_field = rtcp_parser->Field(); 290 if (remote_ssrc_ != rtcp_field.dlrr.receivers_ssrc) { 291 // Not to us. 292 return; 293 } 294 if (rtt_feedback_) { 295 rtt_feedback_->OnReceivedDelaySinceLastReport( 296 rtcp_field.dlrr.receivers_ssrc, 297 rtcp_field.dlrr.last_receiver_report, 298 rtcp_field.dlrr.delay_last_receiver_report); 299 } 300} 301 302void RtcpReceiver::HandleNACK(RtcpParser* rtcp_parser) { 303 const RtcpField& rtcp_field = rtcp_parser->Field(); 304 if (ssrc_ != rtcp_field.nack.media_ssrc) { 305 RtcpFieldTypes field_type; 306 // Message not to us. Iterate until we have passed this message. 307 do { 308 field_type = rtcp_parser->Iterate(); 309 } while (field_type == kRtcpGenericRtpFeedbackNackItemCode); 310 return; 311 } 312 std::list<uint16> nackSequenceNumbers; 313 314 RtcpFieldTypes field_type = rtcp_parser->Iterate(); 315 while (field_type == kRtcpGenericRtpFeedbackNackItemCode) { 316 HandleNACKItem(&rtcp_field, &nackSequenceNumbers); 317 field_type = rtcp_parser->Iterate(); 318 } 319} 320 321void RtcpReceiver::HandleNACKItem(const RtcpField* rtcp_field, 322 std::list<uint16>* nack_sequence_numbers) { 323 nack_sequence_numbers->push_back(rtcp_field->nack_item.packet_id); 324 325 uint16 bitmask = rtcp_field->nack_item.bitmask; 326 if (bitmask) { 327 for (int i = 1; i <= 16; ++i) { 328 if (bitmask & 1) { 329 nack_sequence_numbers->push_back(rtcp_field->nack_item.packet_id + i); 330 } 331 bitmask = bitmask >> 1; 332 } 333 } 334} 335 336void RtcpReceiver::HandleBYE(RtcpParser* rtcp_parser) { 337 const RtcpField& rtcp_field = rtcp_parser->Field(); 338 uint32 remote_ssrc = rtcp_field.bye.sender_ssrc; 339 if (remote_ssrc_ == remote_ssrc) { 340 VLOG(1) << "Cast RTCP received BYE from SSRC " << remote_ssrc; 341 } 342 rtcp_parser->Iterate(); 343} 344 345void RtcpReceiver::HandlePLI(RtcpParser* rtcp_parser) { 346 const RtcpField& rtcp_field = rtcp_parser->Field(); 347 if (ssrc_ == rtcp_field.pli.media_ssrc) { 348 // Received a signal that we need to send a new key frame. 349 VLOG(1) << "Cast RTCP received PLI on our SSRC " << ssrc_; 350 } 351 rtcp_parser->Iterate(); 352} 353 354void RtcpReceiver::HandleSendReportRequest(RtcpParser* rtcp_parser) { 355 if (receiver_feedback_) { 356 receiver_feedback_->OnReceivedSendReportRequest(); 357 } 358 rtcp_parser->Iterate(); 359} 360 361void RtcpReceiver::HandleRpsi(RtcpParser* rtcp_parser) { 362 const RtcpField& rtcp_field = rtcp_parser->Field(); 363 if (rtcp_parser->Iterate() != kRtcpPayloadSpecificRpsiCode) { 364 return; 365 } 366 if (rtcp_field.rpsi.number_of_valid_bits % 8 != 0) { 367 // Continue 368 return; 369 } 370 uint64 rpsi_picture_id = 0; 371 372 // Convert native_bit_string to rpsi_picture_id 373 uint8 bytes = rtcp_field.rpsi.number_of_valid_bits / 8; 374 for (uint8 n = 0; n < (bytes - 1); ++n) { 375 rpsi_picture_id += (rtcp_field.rpsi.native_bit_string[n] & 0x7f); 376 rpsi_picture_id <<= 7; // Prepare next. 377 } 378 rpsi_picture_id += (rtcp_field.rpsi.native_bit_string[bytes - 1] & 0x7f); 379 380 VLOG(1) << "Cast RTCP received RPSI with picture_id " << rpsi_picture_id; 381} 382 383void RtcpReceiver::HandlePayloadSpecificApp(RtcpParser* rtcp_parser) { 384 const RtcpField& rtcp_field = rtcp_parser->Field(); 385 uint32 remote_ssrc = rtcp_field.application_specific.sender_ssrc; 386 if (remote_ssrc_ != remote_ssrc) { 387 // Message not to us. Iterate until we have passed this message. 388 RtcpFieldTypes field_type; 389 do { 390 field_type = rtcp_parser->Iterate(); 391 } while (field_type == kRtcpPayloadSpecificRembCode || 392 field_type == kRtcpPayloadSpecificRembItemCode || 393 field_type == kRtcpPayloadSpecificCastCode || 394 field_type == kRtcpPayloadSpecificCastNackItemCode); 395 return; 396 } 397 398 RtcpFieldTypes packet_type = rtcp_parser->Iterate(); 399 switch (packet_type) { 400 case kRtcpPayloadSpecificRembCode: 401 packet_type = rtcp_parser->Iterate(); 402 if (packet_type == kRtcpPayloadSpecificRembItemCode) { 403 HandlePayloadSpecificRembItem(rtcp_parser); 404 rtcp_parser->Iterate(); 405 } 406 break; 407 case kRtcpPayloadSpecificCastCode: 408 packet_type = rtcp_parser->Iterate(); 409 if (packet_type == kRtcpPayloadSpecificCastCode) { 410 HandlePayloadSpecificCastItem(rtcp_parser); 411 } 412 break; 413 default: 414 return; 415 } 416} 417 418void RtcpReceiver::HandlePayloadSpecificRembItem(RtcpParser* rtcp_parser) { 419 const RtcpField& rtcp_field = rtcp_parser->Field(); 420 421 for (int i = 0; i < rtcp_field.remb_item.number_of_ssrcs; ++i) { 422 if (rtcp_field.remb_item.ssrcs[i] == ssrc_) { 423 // Found matching ssrc. 424 VLOG(1) << "Cast RTCP received REMB with received_bitrate " 425 << rtcp_field.remb_item.bitrate; 426 return; 427 } 428 } 429} 430 431void RtcpReceiver::HandleApplicationSpecificCastReceiverLog( 432 RtcpParser* rtcp_parser) { 433 const RtcpField& rtcp_field = rtcp_parser->Field(); 434 435 uint32 remote_ssrc = rtcp_field.cast_receiver_log.sender_ssrc; 436 if (remote_ssrc_ != remote_ssrc) { 437 // Message not to us. Iterate until we have passed this message. 438 RtcpFieldTypes field_type; 439 do { 440 field_type = rtcp_parser->Iterate(); 441 } while (field_type == kRtcpApplicationSpecificCastReceiverLogFrameCode || 442 field_type == kRtcpApplicationSpecificCastReceiverLogEventCode); 443 return; 444 } 445 RtcpReceiverLogMessage receiver_log; 446 RtcpFieldTypes field_type = rtcp_parser->Iterate(); 447 while (field_type == kRtcpApplicationSpecificCastReceiverLogFrameCode) { 448 RtcpReceiverFrameLogMessage frame_log( 449 rtcp_field.cast_receiver_log.rtp_timestamp); 450 451 field_type = rtcp_parser->Iterate(); 452 while (field_type == kRtcpApplicationSpecificCastReceiverLogEventCode) { 453 HandleApplicationSpecificCastReceiverEventLog(rtcp_parser, 454 &frame_log.event_log_messages_); 455 field_type = rtcp_parser->Iterate(); 456 } 457 receiver_log.push_back(frame_log); 458 } 459 460 if (receiver_feedback_ && !receiver_log.empty()) { 461 receiver_feedback_->OnReceivedReceiverLog(receiver_log); 462 } 463} 464 465void RtcpReceiver::HandleApplicationSpecificCastReceiverEventLog( 466 RtcpParser* rtcp_parser, 467 RtcpReceiverEventLogMessages* event_log_messages) { 468 const RtcpField& rtcp_field = rtcp_parser->Field(); 469 470 RtcpReceiverEventLogMessage event_log; 471 event_log.type = TranslateToLogEventFromWireFormat( 472 rtcp_field.cast_receiver_log.event); 473 event_log.event_timestamp = base::TimeTicks() + 474 base::TimeDelta::FromMilliseconds( 475 rtcp_field.cast_receiver_log.event_timestamp_base + 476 rtcp_field.cast_receiver_log.event_timestamp_delta); 477 event_log.delay_delta = base::TimeDelta::FromMilliseconds( 478 rtcp_field.cast_receiver_log.delay_delta_or_packet_id); 479 event_log.packet_id = 480 rtcp_field.cast_receiver_log.delay_delta_or_packet_id; 481 event_log_messages->push_back(event_log); 482} 483 484void RtcpReceiver::HandleApplicationSpecificCastSenderLog( 485 RtcpParser* rtcp_parser) { 486 const RtcpField& rtcp_field = rtcp_parser->Field(); 487 uint32 remote_ssrc = rtcp_field.cast_sender_log.sender_ssrc; 488 489 if (remote_ssrc_ != remote_ssrc) { 490 RtcpFieldTypes field_type; 491 // Message not to us. Iterate until we have passed this message. 492 do { 493 field_type = rtcp_parser->Iterate(); 494 } while (field_type == kRtcpApplicationSpecificCastSenderLogCode); 495 return; 496 } 497 RtcpSenderLogMessage sender_log; 498 499 RtcpFieldTypes field_type = rtcp_parser->Iterate(); 500 while (field_type == kRtcpApplicationSpecificCastSenderLogCode) { 501 const RtcpField& rtcp_field = rtcp_parser->Field(); 502 RtcpSenderFrameLogMessage frame_log; 503 frame_log.frame_status = 504 TranslateToFrameStatusFromWireFormat(rtcp_field.cast_sender_log.status); 505 frame_log.rtp_timestamp = rtcp_field.cast_sender_log.rtp_timestamp; 506 sender_log.push_back(frame_log); 507 field_type = rtcp_parser->Iterate(); 508 } 509 if (receiver_feedback_) { 510 receiver_feedback_->OnReceivedSenderLog(sender_log); 511 } 512} 513 514void RtcpReceiver::HandlePayloadSpecificCastItem(RtcpParser* rtcp_parser) { 515 const RtcpField& rtcp_field = rtcp_parser->Field(); 516 RtcpCastMessage cast_message(remote_ssrc_); 517 cast_message.ack_frame_id_ = ack_frame_id_wrap_helper_.MapTo32bitsFrameId( 518 rtcp_field.cast_item.last_frame_id); 519 520 RtcpFieldTypes packet_type = rtcp_parser->Iterate(); 521 while (packet_type == kRtcpPayloadSpecificCastNackItemCode) { 522 const RtcpField& rtcp_field = rtcp_parser->Field(); 523 HandlePayloadSpecificCastNackItem( 524 &rtcp_field, &cast_message.missing_frames_and_packets_); 525 packet_type = rtcp_parser->Iterate(); 526 } 527 if (sender_feedback_) { 528 sender_feedback_->OnReceivedCastFeedback(cast_message); 529 } 530} 531 532void RtcpReceiver::HandlePayloadSpecificCastNackItem( 533 const RtcpField* rtcp_field, 534 MissingFramesAndPacketsMap* missing_frames_and_packets) { 535 536 MissingFramesAndPacketsMap::iterator frame_it = 537 missing_frames_and_packets->find(rtcp_field->cast_nack_item.frame_id); 538 539 if (frame_it == missing_frames_and_packets->end()) { 540 // First missing packet in a frame. 541 PacketIdSet empty_set; 542 std::pair<MissingFramesAndPacketsMap::iterator, bool> ret = 543 missing_frames_and_packets->insert(std::pair<uint8, PacketIdSet>( 544 rtcp_field->cast_nack_item.frame_id, empty_set)); 545 frame_it = ret.first; 546 DCHECK(frame_it != missing_frames_and_packets->end()) << "Invalid state"; 547 } 548 if (rtcp_field->cast_nack_item.packet_id == kRtcpCastAllPacketsLost) { 549 // Special case all packets in a frame is missing. 550 return; 551 } 552 uint16 packet_id = rtcp_field->cast_nack_item.packet_id; 553 uint8 bitmask = rtcp_field->cast_nack_item.bitmask; 554 555 frame_it->second.insert(packet_id); 556 557 if (bitmask) { 558 for (int i = 1; i <= 8; ++i) { 559 if (bitmask & 1) { 560 frame_it->second.insert(packet_id + i); 561 } 562 bitmask = bitmask >> 1; 563 } 564 } 565} 566 567void RtcpReceiver::HandleFIR(RtcpParser* rtcp_parser) { 568 const RtcpField& rtcp_field = rtcp_parser->Field(); 569 570 RtcpFieldTypes field_type = rtcp_parser->Iterate(); 571 while (field_type == kRtcpPayloadSpecificFirItemCode) { 572 HandleFIRItem(&rtcp_field); 573 field_type = rtcp_parser->Iterate(); 574 } 575} 576 577void RtcpReceiver::HandleFIRItem(const RtcpField* rtcp_field) { 578 // Is it our sender that is requested to generate a new keyframe. 579 if (ssrc_ != rtcp_field->fir_item.ssrc) return; 580 581 VLOG(1) << "Cast RTCP received FIR on our SSRC " << ssrc_; 582} 583 584} // namespace cast 585} // namespace media 586