video_frame.cc (8846B)
1 /* 2 * Copyright (c) 2012 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 "api/video/video_frame.h" 12 13 #include <algorithm> 14 #include <cstdint> 15 #include <optional> 16 #include <utility> 17 18 #include "api/rtp_packet_infos.h" 19 #include "api/scoped_refptr.h" 20 #include "api/units/timestamp.h" 21 #include "api/video/color_space.h" 22 #include "api/video/video_frame_buffer.h" 23 #include "api/video/video_rotation.h" 24 #include "rtc_base/checks.h" 25 #include "rtc_base/time_utils.h" 26 27 namespace webrtc { 28 29 void VideoFrame::UpdateRect::Union(const UpdateRect& other) { 30 if (other.IsEmpty()) 31 return; 32 if (IsEmpty()) { 33 *this = other; 34 return; 35 } 36 int right = std::max(offset_x + width, other.offset_x + other.width); 37 int bottom = std::max(offset_y + height, other.offset_y + other.height); 38 offset_x = std::min(offset_x, other.offset_x); 39 offset_y = std::min(offset_y, other.offset_y); 40 width = right - offset_x; 41 height = bottom - offset_y; 42 RTC_DCHECK_GT(width, 0); 43 RTC_DCHECK_GT(height, 0); 44 } 45 46 void VideoFrame::UpdateRect::Intersect(const UpdateRect& other) { 47 if (other.IsEmpty() || IsEmpty()) { 48 MakeEmptyUpdate(); 49 return; 50 } 51 52 int right = std::min(offset_x + width, other.offset_x + other.width); 53 int bottom = std::min(offset_y + height, other.offset_y + other.height); 54 offset_x = std::max(offset_x, other.offset_x); 55 offset_y = std::max(offset_y, other.offset_y); 56 width = right - offset_x; 57 height = bottom - offset_y; 58 if (width <= 0 || height <= 0) { 59 MakeEmptyUpdate(); 60 } 61 } 62 63 void VideoFrame::UpdateRect::MakeEmptyUpdate() { 64 width = height = offset_x = offset_y = 0; 65 } 66 67 bool VideoFrame::UpdateRect::IsEmpty() const { 68 return width == 0 && height == 0; 69 } 70 71 VideoFrame::UpdateRect VideoFrame::UpdateRect::ScaleWithFrame( 72 int frame_width, 73 int frame_height, 74 int crop_x, 75 int crop_y, 76 int crop_width, 77 int crop_height, 78 int scaled_width, 79 int scaled_height) const { 80 RTC_DCHECK_GT(frame_width, 0); 81 RTC_DCHECK_GT(frame_height, 0); 82 83 RTC_DCHECK_GT(crop_width, 0); 84 RTC_DCHECK_GT(crop_height, 0); 85 86 RTC_DCHECK_LE(crop_width + crop_x, frame_width); 87 RTC_DCHECK_LE(crop_height + crop_y, frame_height); 88 89 RTC_DCHECK_GT(scaled_width, 0); 90 RTC_DCHECK_GT(scaled_height, 0); 91 92 // Check if update rect is out of the cropped area. 93 if (offset_x + width < crop_x || offset_x > crop_x + crop_width || 94 offset_y + height < crop_y || offset_y > crop_y + crop_width) { 95 return {.offset_x = 0, .offset_y = 0, .width = 0, .height = 0}; 96 } 97 98 int x = offset_x - crop_x; 99 int w = width; 100 if (x < 0) { 101 w += x; 102 x = 0; 103 } 104 int y = offset_y - crop_y; 105 int h = height; 106 if (y < 0) { 107 h += y; 108 y = 0; 109 } 110 111 // Lower corner is rounded down. 112 x = x * scaled_width / crop_width; 113 y = y * scaled_height / crop_height; 114 // Upper corner is rounded up. 115 w = (w * scaled_width + crop_width - 1) / crop_width; 116 h = (h * scaled_height + crop_height - 1) / crop_height; 117 118 // Round to full 2x2 blocks due to possible subsampling in the pixel data. 119 if (x % 2) { 120 --x; 121 ++w; 122 } 123 if (y % 2) { 124 --y; 125 ++h; 126 } 127 if (w % 2) { 128 ++w; 129 } 130 if (h % 2) { 131 ++h; 132 } 133 134 // Expand the update rect by 2 pixels in each direction to include any 135 // possible scaling artifacts. 136 if (scaled_width != crop_width || scaled_height != crop_height) { 137 if (x > 0) { 138 x -= 2; 139 w += 2; 140 } 141 if (y > 0) { 142 y -= 2; 143 h += 2; 144 } 145 w += 2; 146 h += 2; 147 } 148 149 // Ensure update rect is inside frame dimensions. 150 if (x + w > scaled_width) { 151 w = scaled_width - x; 152 } 153 if (y + h > scaled_height) { 154 h = scaled_height - y; 155 } 156 RTC_DCHECK_GE(w, 0); 157 RTC_DCHECK_GE(h, 0); 158 if (w == 0 || h == 0) { 159 w = 0; 160 h = 0; 161 x = 0; 162 y = 0; 163 } 164 165 return {.offset_x = x, .offset_y = y, .width = w, .height = h}; 166 } 167 168 VideoFrame::Builder::Builder() = default; 169 170 VideoFrame::Builder::~Builder() = default; 171 172 VideoFrame VideoFrame::Builder::build() { 173 RTC_CHECK(video_frame_buffer_ != nullptr); 174 return VideoFrame(id_, video_frame_buffer_, timestamp_us_, 175 presentation_timestamp_, reference_time_, timestamp_rtp_, 176 ntp_time_ms_, rotation_, color_space_, render_parameters_, 177 update_rect_, packet_infos_, is_repeat_frame_); 178 } 179 180 VideoFrame::Builder& VideoFrame::Builder::set_video_frame_buffer( 181 const scoped_refptr<VideoFrameBuffer>& buffer) { 182 video_frame_buffer_ = buffer; 183 return *this; 184 } 185 186 VideoFrame::Builder& VideoFrame::Builder::set_timestamp_ms( 187 int64_t timestamp_ms) { 188 timestamp_us_ = timestamp_ms * kNumMicrosecsPerMillisec; 189 return *this; 190 } 191 192 VideoFrame::Builder& VideoFrame::Builder::set_timestamp_us( 193 int64_t timestamp_us) { 194 timestamp_us_ = timestamp_us; 195 return *this; 196 } 197 198 VideoFrame::Builder& VideoFrame::Builder::set_capture_time_identifier( 199 const std::optional<Timestamp>& presentation_timestamp) { 200 presentation_timestamp_ = presentation_timestamp; 201 return *this; 202 } 203 204 VideoFrame::Builder& VideoFrame::Builder::set_presentation_timestamp( 205 const std::optional<Timestamp>& presentation_timestamp) { 206 presentation_timestamp_ = presentation_timestamp; 207 return *this; 208 } 209 210 VideoFrame::Builder& VideoFrame::Builder::set_reference_time( 211 const std::optional<Timestamp>& reference_time) { 212 reference_time_ = reference_time; 213 return *this; 214 } 215 216 VideoFrame::Builder& VideoFrame::Builder::set_rtp_timestamp( 217 uint32_t rtp_timestamp) { 218 timestamp_rtp_ = rtp_timestamp; 219 return *this; 220 } 221 222 VideoFrame::Builder& VideoFrame::Builder::set_timestamp_rtp( 223 uint32_t timestamp_rtp) { 224 timestamp_rtp_ = timestamp_rtp; 225 return *this; 226 } 227 228 VideoFrame::Builder& VideoFrame::Builder::set_ntp_time_ms(int64_t ntp_time_ms) { 229 ntp_time_ms_ = ntp_time_ms; 230 return *this; 231 } 232 233 VideoFrame::Builder& VideoFrame::Builder::set_rotation(VideoRotation rotation) { 234 rotation_ = rotation; 235 return *this; 236 } 237 238 VideoFrame::Builder& VideoFrame::Builder::set_color_space( 239 const std::optional<ColorSpace>& color_space) { 240 color_space_ = color_space; 241 return *this; 242 } 243 244 VideoFrame::Builder& VideoFrame::Builder::set_color_space( 245 const ColorSpace* color_space) { 246 color_space_ = color_space ? std::make_optional(*color_space) : std::nullopt; 247 return *this; 248 } 249 250 VideoFrame::Builder& VideoFrame::Builder::set_id(uint16_t id) { 251 id_ = id; 252 return *this; 253 } 254 255 VideoFrame::Builder& VideoFrame::Builder::set_update_rect( 256 const std::optional<VideoFrame::UpdateRect>& update_rect) { 257 update_rect_ = update_rect; 258 return *this; 259 } 260 261 VideoFrame::Builder& VideoFrame::Builder::set_packet_infos( 262 RtpPacketInfos packet_infos) { 263 packet_infos_ = std::move(packet_infos); 264 return *this; 265 } 266 267 VideoFrame::Builder& VideoFrame::Builder::set_is_repeat_frame( 268 bool is_repeat_frame) { 269 is_repeat_frame_ = is_repeat_frame; 270 return *this; 271 } 272 273 VideoFrame::VideoFrame(const scoped_refptr<VideoFrameBuffer>& buffer, 274 VideoRotation rotation, 275 int64_t timestamp_us) 276 : video_frame_buffer_(buffer), 277 timestamp_rtp_(0), 278 ntp_time_ms_(0), 279 timestamp_us_(timestamp_us), 280 rotation_(rotation), 281 is_repeat_frame_(false) {} 282 283 VideoFrame::VideoFrame(const scoped_refptr<VideoFrameBuffer>& buffer, 284 uint32_t timestamp_rtp, 285 int64_t render_time_ms, 286 VideoRotation rotation) 287 : video_frame_buffer_(buffer), 288 timestamp_rtp_(timestamp_rtp), 289 ntp_time_ms_(0), 290 timestamp_us_(render_time_ms * kNumMicrosecsPerMillisec), 291 rotation_(rotation), 292 is_repeat_frame_(false) { 293 RTC_DCHECK(buffer); 294 } 295 296 VideoFrame::~VideoFrame() = default; 297 298 VideoFrame::VideoFrame(const VideoFrame&) = default; 299 VideoFrame::VideoFrame(VideoFrame&&) = default; 300 VideoFrame& VideoFrame::operator=(const VideoFrame&) = default; 301 VideoFrame& VideoFrame::operator=(VideoFrame&&) = default; 302 303 int VideoFrame::width() const { 304 return video_frame_buffer_ ? video_frame_buffer_->width() : 0; 305 } 306 307 int VideoFrame::height() const { 308 return video_frame_buffer_ ? video_frame_buffer_->height() : 0; 309 } 310 311 uint32_t VideoFrame::size() const { 312 return width() * height(); 313 } 314 315 scoped_refptr<VideoFrameBuffer> VideoFrame::video_frame_buffer() const { 316 return video_frame_buffer_; 317 } 318 319 void VideoFrame::set_video_frame_buffer( 320 const scoped_refptr<VideoFrameBuffer>& buffer) { 321 RTC_CHECK(buffer); 322 video_frame_buffer_ = buffer; 323 } 324 325 int64_t VideoFrame::render_time_ms() const { 326 return timestamp_us() / kNumMicrosecsPerMillisec; 327 } 328 329 } // namespace webrtc