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 payload descriptor
23 // https://datatracker.ietf.org/doc/html/rfc7741#section-4.2
24 //
25 // 0 1 2 3 4 5 6 7
26 // +-+-+-+-+-+-+-+-+
27 // |X|R|N|S|R| PID | (REQUIRED)
28 // +-+-+-+-+-+-+-+-+
29 // X: |I|L|T|K| RSV | (OPTIONAL)
30 // +-+-+-+-+-+-+-+-+
31 // I: |M| PictureID | (OPTIONAL)
32 // +-+-+-+-+-+-+-+-+
33 // | PictureID |
34 // +-+-+-+-+-+-+-+-+
35 // L: | TL0PICIDX | (OPTIONAL)
36 // +-+-+-+-+-+-+-+-+
37 // T/K: |TID|Y| KEYIDX | (OPTIONAL)
38 // +-+-+-+-+-+-+-+-+
39 //
40 // VP8 payload header. Considered part of the actual payload, sent to decoder.
41 // https://datatracker.ietf.org/doc/html/rfc7741#section-4.3
42 //
43 // 0 1 2 3 4 5 6 7
44 // +-+-+-+-+-+-+-+-+
45 // |Size0|H| VER |P|
46 // +-+-+-+-+-+-+-+-+
47 // : ... :
48 // +-+-+-+-+-+-+-+-+
49
50 namespace webrtc {
51 namespace {
52
53 constexpr int kFailedToParse = 0;
54
ParseVP8Descriptor(RTPVideoHeaderVP8 * vp8,const uint8_t * data,size_t data_length)55 int ParseVP8Descriptor(RTPVideoHeaderVP8* vp8,
56 const uint8_t* data,
57 size_t data_length) {
58 RTC_DCHECK_GT(data_length, 0);
59 int parsed_bytes = 0;
60 // Parse mandatory first byte of payload descriptor.
61 bool extension = (*data & 0x80) ? true : false; // X bit
62 vp8->nonReference = (*data & 0x20) ? true : false; // N bit
63 vp8->beginningOfPartition = (*data & 0x10) ? true : false; // S bit
64 vp8->partitionId = (*data & 0x07); // PID field
65
66 data++;
67 parsed_bytes++;
68 data_length--;
69
70 if (!extension)
71 return parsed_bytes;
72
73 if (data_length == 0)
74 return kFailedToParse;
75 // Optional X field is present.
76 bool has_picture_id = (*data & 0x80) ? true : false; // I bit
77 bool has_tl0_pic_idx = (*data & 0x40) ? true : false; // L bit
78 bool has_tid = (*data & 0x20) ? true : false; // T bit
79 bool has_key_idx = (*data & 0x10) ? true : false; // K bit
80
81 // Advance data and decrease remaining payload size.
82 data++;
83 parsed_bytes++;
84 data_length--;
85
86 if (has_picture_id) {
87 if (data_length == 0)
88 return kFailedToParse;
89
90 vp8->pictureId = (*data & 0x7F);
91 if (*data & 0x80) {
92 data++;
93 parsed_bytes++;
94 if (--data_length == 0)
95 return kFailedToParse;
96 // PictureId is 15 bits
97 vp8->pictureId = (vp8->pictureId << 8) + *data;
98 }
99 data++;
100 parsed_bytes++;
101 data_length--;
102 }
103
104 if (has_tl0_pic_idx) {
105 if (data_length == 0)
106 return kFailedToParse;
107
108 vp8->tl0PicIdx = *data;
109 data++;
110 parsed_bytes++;
111 data_length--;
112 }
113
114 if (has_tid || has_key_idx) {
115 if (data_length == 0)
116 return kFailedToParse;
117
118 if (has_tid) {
119 vp8->temporalIdx = ((*data >> 6) & 0x03);
120 vp8->layerSync = (*data & 0x20) ? true : false; // Y bit
121 }
122 if (has_key_idx) {
123 vp8->keyIdx = *data & 0x1F;
124 }
125 data++;
126 parsed_bytes++;
127 data_length--;
128 }
129 return parsed_bytes;
130 }
131
132 } // namespace
133
134 absl::optional<VideoRtpDepacketizer::ParsedRtpPayload>
Parse(rtc::CopyOnWriteBuffer rtp_payload)135 VideoRtpDepacketizerVp8::Parse(rtc::CopyOnWriteBuffer rtp_payload) {
136 rtc::ArrayView<const uint8_t> payload(rtp_payload.cdata(),
137 rtp_payload.size());
138 absl::optional<ParsedRtpPayload> result(absl::in_place);
139 int offset = ParseRtpPayload(payload, &result->video_header);
140 if (offset == kFailedToParse)
141 return absl::nullopt;
142 RTC_DCHECK_LT(offset, rtp_payload.size());
143 result->video_payload =
144 rtp_payload.Slice(offset, rtp_payload.size() - offset);
145 return result;
146 }
147
ParseRtpPayload(rtc::ArrayView<const uint8_t> rtp_payload,RTPVideoHeader * video_header)148 int VideoRtpDepacketizerVp8::ParseRtpPayload(
149 rtc::ArrayView<const uint8_t> rtp_payload,
150 RTPVideoHeader* video_header) {
151 RTC_DCHECK(video_header);
152 if (rtp_payload.empty()) {
153 RTC_LOG(LS_ERROR) << "Empty rtp payload.";
154 return kFailedToParse;
155 }
156
157 video_header->simulcastIdx = 0;
158 video_header->codec = kVideoCodecVP8;
159 auto& vp8_header =
160 video_header->video_type_header.emplace<RTPVideoHeaderVP8>();
161 vp8_header.InitRTPVideoHeaderVP8();
162
163 const int descriptor_size =
164 ParseVP8Descriptor(&vp8_header, rtp_payload.data(), rtp_payload.size());
165 if (descriptor_size == kFailedToParse)
166 return kFailedToParse;
167
168 RTC_DCHECK_LT(vp8_header.partitionId, 8);
169
170 video_header->is_first_packet_in_frame =
171 vp8_header.beginningOfPartition && vp8_header.partitionId == 0;
172
173 int vp8_payload_size = rtp_payload.size() - descriptor_size;
174 if (vp8_payload_size == 0) {
175 RTC_LOG(LS_WARNING) << "Empty vp8 payload.";
176 return kFailedToParse;
177 }
178 const uint8_t* vp8_payload = rtp_payload.data() + descriptor_size;
179
180 // Read P bit from payload header (only at beginning of first partition).
181 if (video_header->is_first_packet_in_frame && (*vp8_payload & 0x01) == 0) {
182 video_header->frame_type = VideoFrameType::kVideoFrameKey;
183
184 if (vp8_payload_size < 10) {
185 // For an I-frame we should always have the uncompressed VP8 header
186 // in the beginning of the partition.
187 return kFailedToParse;
188 }
189 video_header->width = ((vp8_payload[7] << 8) + vp8_payload[6]) & 0x3FFF;
190 video_header->height = ((vp8_payload[9] << 8) + vp8_payload[8]) & 0x3FFF;
191 } else {
192 video_header->frame_type = VideoFrameType::kVideoFrameDelta;
193
194 video_header->width = 0;
195 video_header->height = 0;
196 }
197
198 return descriptor_size;
199 }
200
201 } // namespace webrtc
202