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