xref: /aosp_15_r20/external/webrtc/modules/rtp_rtcp/source/video_rtp_depacketizer_vp8.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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