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