| Index: webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
 | 
| diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
 | 
| index 28685b4640a9e123c55ecb5a2003dd018ddf9c89..70796eac7b0d022ea812406ad9e9d845271825ab 100644
 | 
| --- a/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
 | 
| +++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
 | 
| @@ -380,4 +380,106 @@ constexpr const char RepairedRtpStreamId::kUri[];
 | 
|  constexpr RTPExtensionType RtpMid::kId;
 | 
|  constexpr const char RtpMid::kUri[];
 | 
|  
 | 
| +// For Frame Marking RTP Header Extension:
 | 
| +//
 | 
| +// https://tools.ietf.org/html/draft-ietf-avtext-framemarking-04#page-4
 | 
| +// This extensions provides meta-information about the RTP streams outside the
 | 
| +// encrypted media payload, an RTP switch can do codec-agnostic
 | 
| +// selective forwarding without decrypting the payload.
 | 
| +//
 | 
| +// for Non-Scalable Streams:
 | 
| +//
 | 
| +//     0                   1
 | 
| +//     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
 | 
| +//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
| +//    |  ID=? |  L=0  |S|E|I|D|0 0 0 0|
 | 
| +//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
| +//
 | 
| +// for Scalable Streams:
 | 
| +//
 | 
| +//     0                   1                   2                   3
 | 
| +//     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 | 
| +//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
| +//    |  ID=? |  L=2  |S|E|I|D|B| TID |   LID         |    TL0PICIDX  |
 | 
| +//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
| +//
 | 
| +constexpr RTPExtensionType FrameMarking::kId;
 | 
| +constexpr const char* FrameMarking::kUri;
 | 
| +
 | 
| +bool FrameMarking::Parse(rtc::ArrayView<const uint8_t> data,
 | 
| +                         FrameMarks* frame_marks) {
 | 
| +  RTC_DCHECK(frame_marks);
 | 
| +
 | 
| +  if (data.empty())
 | 
| +    return false;
 | 
| +
 | 
| +  // Set frame marking data
 | 
| +  frame_marks->start_of_frame = (data[0] & 0x80) != 0;
 | 
| +  frame_marks->end_of_frame = (data[0] & 0x40) != 0;
 | 
| +  frame_marks->independent = (data[0] & 0x20) != 0;
 | 
| +  frame_marks->discardable = (data[0] & 0x10) != 0;
 | 
| +
 | 
| +  // Check variable length.
 | 
| +  if (data.size() == 1) {
 | 
| +    // We are non-scalable.
 | 
| +    frame_marks->base_layer_sync = false;
 | 
| +    frame_marks->temporal_layer_id = 0;
 | 
| +    frame_marks->layer_id = 0;
 | 
| +    frame_marks->tl0_pic_idx = 0;
 | 
| +  } else if (data.size() == 3) {
 | 
| +    // Set scalable parts
 | 
| +    frame_marks->base_layer_sync = (data[0] & 0x08) != 0;
 | 
| +    frame_marks->temporal_layer_id = data[0] & 0x07;
 | 
| +    frame_marks->layer_id = data[1];
 | 
| +    frame_marks->tl0_pic_idx = data[2];
 | 
| +  } else {
 | 
| +    // Incorrect length.
 | 
| +    return false;
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +bool FrameMarking::IsScalable(const FrameMarks& frame_marks) {
 | 
| +  return (frame_marks.base_layer_sync ||
 | 
| +          (frame_marks.temporal_layer_id &&
 | 
| +           frame_marks.temporal_layer_id != kNoTemporalIdx) ||
 | 
| +          (frame_marks.layer_id && frame_marks.layer_id != kNoSpatialIdx) ||
 | 
| +          (frame_marks.tl0_pic_idx && frame_marks.tl0_pic_idx != kNoTl0PicIdx));
 | 
| +}
 | 
| +
 | 
| +size_t FrameMarking::ValueSize(const FrameMarks& frame_marks) {
 | 
| +  return IsScalable(frame_marks) ? 3 : 1;
 | 
| +}
 | 
| +
 | 
| +bool FrameMarking::Write(uint8_t* data, const FrameMarks& frame_marks) {
 | 
| +  data[0] = frame_marks.start_of_frame ? 0x80 : 0x00;
 | 
| +  data[0] |= frame_marks.end_of_frame ? 0x40 : 0x00;
 | 
| +  data[0] |= frame_marks.independent ? 0x20 : 0x00;
 | 
| +  data[0] |= frame_marks.discardable ? 0x10 : 0x00;
 | 
| +
 | 
| +  // Check if it is scalable.
 | 
| +  if (IsScalable(frame_marks)) {
 | 
| +    data[0] |= frame_marks.base_layer_sync ? 0x08 : 0x00;
 | 
| +    data[0] |= (frame_marks.temporal_layer_id & 0x07);
 | 
| +    data[1] = frame_marks.layer_id;
 | 
| +    data[2] = static_cast<uint8_t>(frame_marks.tl0_pic_idx);
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +uint8_t FrameMarking::CreateLayerId(const RTPVideoHeaderVP9& vp9) {
 | 
| +  // The following  shows VP9 Layer encoding information (3 bits for
 | 
| +  // spatial and temporal layer) mapped to the generic LID and TID fields.
 | 
| +  // The P and U bits MUST match the corresponding bits in the VP9 Payload
 | 
| +  // Description.
 | 
| +  //    0                   1                   2                   3
 | 
| +  //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 | 
| +  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
| +  //   |  ID=2 |  L=2  |S|E|I|D|B| TID |0|0|0|P|U| SID |    TL0PICIDX  |
 | 
| +  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
| +  return (vp9.spatial_idx != kNoSpatialIdx ? vp9.spatial_idx & 0x07 : 0x00) |
 | 
| +         (vp9.temporal_up_switch ? 0x08 : 0x00) |
 | 
| +         (vp9.inter_pic_predicted ? 0x10 : 0x00);
 | 
| +}
 | 
| +
 | 
|  }  // namespace webrtc
 | 
| 
 |