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