• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.h"
12 
13 #include <stddef.h>
14 #include <stdint.h>
15 
16 #include "absl/types/optional.h"
17 #include "api/array_view.h"
18 #include "modules/rtp_rtcp/source/rtp_video_header.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/logging.h"
21 
22 // VP8 format:
23 //
24 // Payload descriptor
25 //       0 1 2 3 4 5 6 7
26 //      +-+-+-+-+-+-+-+-+
27 //      |X|R|N|S|PartID | (REQUIRED)
28 //      +-+-+-+-+-+-+-+-+
29 // X:   |I|L|T|K|  RSV  | (OPTIONAL)
30 //      +-+-+-+-+-+-+-+-+
31 // I:   |   PictureID   | (OPTIONAL)
32 //      +-+-+-+-+-+-+-+-+
33 // L:   |   TL0PICIDX   | (OPTIONAL)
34 //      +-+-+-+-+-+-+-+-+
35 // T/K: |TID:Y| KEYIDX  | (OPTIONAL)
36 //      +-+-+-+-+-+-+-+-+
37 //
38 // Payload header (considered part of the actual payload, sent to decoder)
39 //       0 1 2 3 4 5 6 7
40 //      +-+-+-+-+-+-+-+-+
41 //      |Size0|H| VER |P|
42 //      +-+-+-+-+-+-+-+-+
43 //      |      ...      |
44 //      +               +
45 namespace webrtc {
46 namespace {
47 
48 constexpr int kFailedToParse = 0;
49 
ParseVP8Descriptor(RTPVideoHeaderVP8 * vp8,const uint8_t * data,size_t data_length)50 int ParseVP8Descriptor(RTPVideoHeaderVP8* vp8,
51                        const uint8_t* data,
52                        size_t data_length) {
53   RTC_DCHECK_GT(data_length, 0);
54   int parsed_bytes = 0;
55   // Parse mandatory first byte of payload descriptor.
56   bool extension = (*data & 0x80) ? true : false;             // X bit
57   vp8->nonReference = (*data & 0x20) ? true : false;          // N bit
58   vp8->beginningOfPartition = (*data & 0x10) ? true : false;  // S bit
59   vp8->partitionId = (*data & 0x0F);                          // PartID field
60 
61   data++;
62   parsed_bytes++;
63   data_length--;
64 
65   if (!extension)
66     return parsed_bytes;
67 
68   if (data_length == 0)
69     return kFailedToParse;
70   // Optional X field is present.
71   bool has_picture_id = (*data & 0x80) ? true : false;   // I bit
72   bool has_tl0_pic_idx = (*data & 0x40) ? true : false;  // L bit
73   bool has_tid = (*data & 0x20) ? true : false;          // T bit
74   bool has_key_idx = (*data & 0x10) ? true : false;      // K bit
75 
76   // Advance data and decrease remaining payload size.
77   data++;
78   parsed_bytes++;
79   data_length--;
80 
81   if (has_picture_id) {
82     if (data_length == 0)
83       return kFailedToParse;
84 
85     vp8->pictureId = (*data & 0x7F);
86     if (*data & 0x80) {
87       data++;
88       parsed_bytes++;
89       if (--data_length == 0)
90         return kFailedToParse;
91       // PictureId is 15 bits
92       vp8->pictureId = (vp8->pictureId << 8) + *data;
93     }
94     data++;
95     parsed_bytes++;
96     data_length--;
97   }
98 
99   if (has_tl0_pic_idx) {
100     if (data_length == 0)
101       return kFailedToParse;
102 
103     vp8->tl0PicIdx = *data;
104     data++;
105     parsed_bytes++;
106     data_length--;
107   }
108 
109   if (has_tid || has_key_idx) {
110     if (data_length == 0)
111       return kFailedToParse;
112 
113     if (has_tid) {
114       vp8->temporalIdx = ((*data >> 6) & 0x03);
115       vp8->layerSync = (*data & 0x20) ? true : false;  // Y bit
116     }
117     if (has_key_idx) {
118       vp8->keyIdx = *data & 0x1F;
119     }
120     data++;
121     parsed_bytes++;
122     data_length--;
123   }
124   return parsed_bytes;
125 }
126 
127 }  // namespace
128 
129 absl::optional<VideoRtpDepacketizer::ParsedRtpPayload>
Parse(rtc::CopyOnWriteBuffer rtp_payload)130 VideoRtpDepacketizerVp8::Parse(rtc::CopyOnWriteBuffer rtp_payload) {
131   rtc::ArrayView<const uint8_t> payload(rtp_payload.cdata(),
132                                         rtp_payload.size());
133   absl::optional<ParsedRtpPayload> result(absl::in_place);
134   int offset = ParseRtpPayload(payload, &result->video_header);
135   if (offset == kFailedToParse)
136     return absl::nullopt;
137   RTC_DCHECK_LT(offset, rtp_payload.size());
138   result->video_payload =
139       rtp_payload.Slice(offset, rtp_payload.size() - offset);
140   return result;
141 }
142 
ParseRtpPayload(rtc::ArrayView<const uint8_t> rtp_payload,RTPVideoHeader * video_header)143 int VideoRtpDepacketizerVp8::ParseRtpPayload(
144     rtc::ArrayView<const uint8_t> rtp_payload,
145     RTPVideoHeader* video_header) {
146   RTC_DCHECK(video_header);
147   if (rtp_payload.empty()) {
148     RTC_LOG(LS_ERROR) << "Empty rtp payload.";
149     return kFailedToParse;
150   }
151 
152   video_header->simulcastIdx = 0;
153   video_header->codec = kVideoCodecVP8;
154   auto& vp8_header =
155       video_header->video_type_header.emplace<RTPVideoHeaderVP8>();
156   vp8_header.InitRTPVideoHeaderVP8();
157 
158   const int descriptor_size =
159       ParseVP8Descriptor(&vp8_header, rtp_payload.data(), rtp_payload.size());
160   if (descriptor_size == kFailedToParse)
161     return kFailedToParse;
162 
163   if (vp8_header.partitionId > 8) {
164     // Weak check for corrupt payload_data: PartID MUST NOT be larger than 8.
165     return kFailedToParse;
166   }
167   video_header->is_first_packet_in_frame =
168       vp8_header.beginningOfPartition && vp8_header.partitionId == 0;
169 
170   int vp8_payload_size = rtp_payload.size() - descriptor_size;
171   if (vp8_payload_size == 0) {
172     RTC_LOG(LS_WARNING) << "Empty vp8 payload.";
173     return kFailedToParse;
174   }
175   const uint8_t* vp8_payload = rtp_payload.data() + descriptor_size;
176 
177   // Read P bit from payload header (only at beginning of first partition).
178   if (video_header->is_first_packet_in_frame && (*vp8_payload & 0x01) == 0) {
179     video_header->frame_type = VideoFrameType::kVideoFrameKey;
180 
181     if (vp8_payload_size < 10) {
182       // For an I-frame we should always have the uncompressed VP8 header
183       // in the beginning of the partition.
184       return kFailedToParse;
185     }
186     video_header->width = ((vp8_payload[7] << 8) + vp8_payload[6]) & 0x3FFF;
187     video_header->height = ((vp8_payload[9] << 8) + vp8_payload[8]) & 0x3FFF;
188   } else {
189     video_header->frame_type = VideoFrameType::kVideoFrameDelta;
190 
191     video_header->width = 0;
192     video_header->height = 0;
193   }
194 
195   return descriptor_size;
196 }
197 
198 }  // namespace webrtc
199