| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * libjingle | |
| 3 * Copyright 2011 Google Inc. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions are met: | |
| 7 * | |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | |
| 9 * this list of conditions and the following disclaimer. | |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 11 * this list of conditions and the following disclaimer in the documentation | |
| 12 * and/or other materials provided with the distribution. | |
| 13 * 3. The name of the author may not be used to endorse or promote products | |
| 14 * derived from this software without specific prior written permission. | |
| 15 * | |
| 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
| 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
| 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
| 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
| 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
| 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
| 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 26 */ | |
| 27 | |
| 28 #include "talk/media/base/videoframe.h" | |
| 29 | |
| 30 #include <string.h> | |
| 31 | |
| 32 #include "libyuv/compare.h" | |
| 33 #include "libyuv/planar_functions.h" | |
| 34 #include "libyuv/scale.h" | |
| 35 #include "talk/media/base/videocommon.h" | |
| 36 #include "webrtc/base/arraysize.h" | |
| 37 #include "webrtc/base/checks.h" | |
| 38 #include "webrtc/base/logging.h" | |
| 39 | |
| 40 namespace cricket { | |
| 41 | |
| 42 // Round to 2 pixels because Chroma channels are half size. | |
| 43 #define ROUNDTO2(v) (v & ~1) | |
| 44 | |
| 45 rtc::StreamResult VideoFrame::Write(rtc::StreamInterface* stream, | |
| 46 int* error) const { | |
| 47 rtc::StreamResult result = rtc::SR_SUCCESS; | |
| 48 const uint8_t* src_y = GetYPlane(); | |
| 49 const uint8_t* src_u = GetUPlane(); | |
| 50 const uint8_t* src_v = GetVPlane(); | |
| 51 if (!src_y || !src_u || !src_v) { | |
| 52 return result; // Nothing to write. | |
| 53 } | |
| 54 const int32_t y_pitch = GetYPitch(); | |
| 55 const int32_t u_pitch = GetUPitch(); | |
| 56 const int32_t v_pitch = GetVPitch(); | |
| 57 const size_t width = GetWidth(); | |
| 58 const size_t height = GetHeight(); | |
| 59 const size_t half_width = (width + 1) >> 1; | |
| 60 const size_t half_height = (height + 1) >> 1; | |
| 61 // Write Y. | |
| 62 for (size_t row = 0; row < height; ++row) { | |
| 63 result = stream->Write(src_y + row * y_pitch, width, NULL, error); | |
| 64 if (result != rtc::SR_SUCCESS) { | |
| 65 return result; | |
| 66 } | |
| 67 } | |
| 68 // Write U. | |
| 69 for (size_t row = 0; row < half_height; ++row) { | |
| 70 result = stream->Write(src_u + row * u_pitch, half_width, NULL, error); | |
| 71 if (result != rtc::SR_SUCCESS) { | |
| 72 return result; | |
| 73 } | |
| 74 } | |
| 75 // Write V. | |
| 76 for (size_t row = 0; row < half_height; ++row) { | |
| 77 result = stream->Write(src_v + row * v_pitch, half_width, NULL, error); | |
| 78 if (result != rtc::SR_SUCCESS) { | |
| 79 return result; | |
| 80 } | |
| 81 } | |
| 82 return result; | |
| 83 } | |
| 84 | |
| 85 size_t VideoFrame::CopyToBuffer(uint8_t* buffer, size_t size) const { | |
| 86 const size_t y_size = GetHeight() * GetYPitch(); | |
| 87 const size_t u_size = GetUPitch() * GetChromaHeight(); | |
| 88 const size_t v_size = GetVPitch() * GetChromaHeight(); | |
| 89 const size_t needed = y_size + u_size + v_size; | |
| 90 if (size < needed) | |
| 91 return needed; | |
| 92 CopyToPlanes(buffer, buffer + y_size, buffer + y_size + u_size, | |
| 93 GetYPitch(), GetUPitch(), GetVPitch()); | |
| 94 return needed; | |
| 95 } | |
| 96 | |
| 97 bool VideoFrame::CopyToPlanes(uint8_t* dst_y, | |
| 98 uint8_t* dst_u, | |
| 99 uint8_t* dst_v, | |
| 100 int32_t dst_pitch_y, | |
| 101 int32_t dst_pitch_u, | |
| 102 int32_t dst_pitch_v) const { | |
| 103 if (!GetYPlane() || !GetUPlane() || !GetVPlane()) { | |
| 104 LOG(LS_ERROR) << "NULL plane pointer."; | |
| 105 return false; | |
| 106 } | |
| 107 int32_t src_width = static_cast<int>(GetWidth()); | |
| 108 int32_t src_height = static_cast<int>(GetHeight()); | |
| 109 return libyuv::I420Copy(GetYPlane(), GetYPitch(), | |
| 110 GetUPlane(), GetUPitch(), | |
| 111 GetVPlane(), GetVPitch(), | |
| 112 dst_y, dst_pitch_y, | |
| 113 dst_u, dst_pitch_u, | |
| 114 dst_v, dst_pitch_v, | |
| 115 src_width, src_height) == 0; | |
| 116 } | |
| 117 | |
| 118 void VideoFrame::CopyToFrame(VideoFrame* dst) const { | |
| 119 if (!dst) { | |
| 120 LOG(LS_ERROR) << "NULL dst pointer."; | |
| 121 return; | |
| 122 } | |
| 123 | |
| 124 CopyToPlanes(dst->GetYPlane(), dst->GetUPlane(), dst->GetVPlane(), | |
| 125 dst->GetYPitch(), dst->GetUPitch(), dst->GetVPitch()); | |
| 126 } | |
| 127 | |
| 128 size_t VideoFrame::ConvertToRgbBuffer(uint32_t to_fourcc, | |
| 129 uint8_t* buffer, | |
| 130 size_t size, | |
| 131 int stride_rgb) const { | |
| 132 const size_t needed = std::abs(stride_rgb) * GetHeight(); | |
| 133 if (size < needed) { | |
| 134 LOG(LS_WARNING) << "RGB buffer is not large enough"; | |
| 135 return needed; | |
| 136 } | |
| 137 | |
| 138 if (libyuv::ConvertFromI420(GetYPlane(), GetYPitch(), GetUPlane(), | |
| 139 GetUPitch(), GetVPlane(), GetVPitch(), buffer, | |
| 140 stride_rgb, static_cast<int>(GetWidth()), | |
| 141 static_cast<int>(GetHeight()), to_fourcc)) { | |
| 142 LOG(LS_ERROR) << "RGB type not supported: " << to_fourcc; | |
| 143 return 0; // 0 indicates error | |
| 144 } | |
| 145 return needed; | |
| 146 } | |
| 147 | |
| 148 // TODO(fbarchard): Handle odd width/height with rounding. | |
| 149 void VideoFrame::StretchToPlanes(uint8_t* dst_y, | |
| 150 uint8_t* dst_u, | |
| 151 uint8_t* dst_v, | |
| 152 int32_t dst_pitch_y, | |
| 153 int32_t dst_pitch_u, | |
| 154 int32_t dst_pitch_v, | |
| 155 size_t width, | |
| 156 size_t height, | |
| 157 bool interpolate, | |
| 158 bool vert_crop) const { | |
| 159 if (!GetYPlane() || !GetUPlane() || !GetVPlane()) { | |
| 160 LOG(LS_ERROR) << "NULL plane pointer."; | |
| 161 return; | |
| 162 } | |
| 163 | |
| 164 size_t src_width = GetWidth(); | |
| 165 size_t src_height = GetHeight(); | |
| 166 if (width == src_width && height == src_height) { | |
| 167 CopyToPlanes(dst_y, dst_u, dst_v, dst_pitch_y, dst_pitch_u, dst_pitch_v); | |
| 168 return; | |
| 169 } | |
| 170 const uint8_t* src_y = GetYPlane(); | |
| 171 const uint8_t* src_u = GetUPlane(); | |
| 172 const uint8_t* src_v = GetVPlane(); | |
| 173 | |
| 174 if (vert_crop) { | |
| 175 // Adjust the input width:height ratio to be the same as the output ratio. | |
| 176 if (src_width * height > src_height * width) { | |
| 177 // Reduce the input width, but keep size/position aligned for YuvScaler | |
| 178 src_width = ROUNDTO2(src_height * width / height); | |
| 179 int32_t iwidth_offset = ROUNDTO2((GetWidth() - src_width) / 2); | |
| 180 src_y += iwidth_offset; | |
| 181 src_u += iwidth_offset / 2; | |
| 182 src_v += iwidth_offset / 2; | |
| 183 } else if (src_width * height < src_height * width) { | |
| 184 // Reduce the input height. | |
| 185 src_height = src_width * height / width; | |
| 186 int32_t iheight_offset = | |
| 187 static_cast<int32_t>((GetHeight() - src_height) >> 2); | |
| 188 iheight_offset <<= 1; // Ensure that iheight_offset is even. | |
| 189 src_y += iheight_offset * GetYPitch(); | |
| 190 src_u += iheight_offset / 2 * GetUPitch(); | |
| 191 src_v += iheight_offset / 2 * GetVPitch(); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 // Scale to the output I420 frame. | |
| 196 libyuv::Scale(src_y, src_u, src_v, | |
| 197 GetYPitch(), GetUPitch(), GetVPitch(), | |
| 198 static_cast<int>(src_width), static_cast<int>(src_height), | |
| 199 dst_y, dst_u, dst_v, dst_pitch_y, dst_pitch_u, dst_pitch_v, | |
| 200 static_cast<int>(width), static_cast<int>(height), interpolate); | |
| 201 } | |
| 202 | |
| 203 void VideoFrame::StretchToFrame(VideoFrame* dst, | |
| 204 bool interpolate, bool vert_crop) const { | |
| 205 if (!dst) { | |
| 206 LOG(LS_ERROR) << "NULL dst pointer."; | |
| 207 return; | |
| 208 } | |
| 209 | |
| 210 StretchToPlanes(dst->GetYPlane(), dst->GetUPlane(), dst->GetVPlane(), | |
| 211 dst->GetYPitch(), dst->GetUPitch(), dst->GetVPitch(), | |
| 212 dst->GetWidth(), dst->GetHeight(), | |
| 213 interpolate, vert_crop); | |
| 214 dst->SetTimeStamp(GetTimeStamp()); | |
| 215 // Stretched frame should have the same rotation as the source. | |
| 216 dst->SetRotation(GetVideoRotation()); | |
| 217 } | |
| 218 | |
| 219 VideoFrame* VideoFrame::Stretch(size_t dst_width, size_t dst_height, | |
| 220 bool interpolate, bool vert_crop) const { | |
| 221 VideoFrame* dest = CreateEmptyFrame(static_cast<int>(dst_width), | |
| 222 static_cast<int>(dst_height), | |
| 223 GetTimeStamp()); | |
| 224 if (dest) { | |
| 225 StretchToFrame(dest, interpolate, vert_crop); | |
| 226 } | |
| 227 return dest; | |
| 228 } | |
| 229 | |
| 230 bool VideoFrame::SetToBlack() { | |
| 231 return libyuv::I420Rect(GetYPlane(), GetYPitch(), | |
| 232 GetUPlane(), GetUPitch(), | |
| 233 GetVPlane(), GetVPitch(), | |
| 234 0, 0, | |
| 235 static_cast<int>(GetWidth()), | |
| 236 static_cast<int>(GetHeight()), | |
| 237 16, 128, 128) == 0; | |
| 238 } | |
| 239 | |
| 240 static const size_t kMaxSampleSize = 1000000000u; | |
| 241 // Returns whether a sample is valid. | |
| 242 bool VideoFrame::Validate(uint32_t fourcc, | |
| 243 int w, | |
| 244 int h, | |
| 245 const uint8_t* sample, | |
| 246 size_t sample_size) { | |
| 247 if (h < 0) { | |
| 248 h = -h; | |
| 249 } | |
| 250 // 16384 is maximum resolution for VP8 codec. | |
| 251 if (w < 1 || w > 16384 || h < 1 || h > 16384) { | |
| 252 LOG(LS_ERROR) << "Invalid dimensions: " << w << "x" << h; | |
| 253 return false; | |
| 254 } | |
| 255 uint32_t format = CanonicalFourCC(fourcc); | |
| 256 int expected_bpp = 8; | |
| 257 switch (format) { | |
| 258 case FOURCC_I400: | |
| 259 case FOURCC_RGGB: | |
| 260 case FOURCC_BGGR: | |
| 261 case FOURCC_GRBG: | |
| 262 case FOURCC_GBRG: | |
| 263 expected_bpp = 8; | |
| 264 break; | |
| 265 case FOURCC_I420: | |
| 266 case FOURCC_I411: | |
| 267 case FOURCC_YU12: | |
| 268 case FOURCC_YV12: | |
| 269 case FOURCC_M420: | |
| 270 case FOURCC_NV21: | |
| 271 case FOURCC_NV12: | |
| 272 expected_bpp = 12; | |
| 273 break; | |
| 274 case FOURCC_I422: | |
| 275 case FOURCC_YV16: | |
| 276 case FOURCC_YUY2: | |
| 277 case FOURCC_UYVY: | |
| 278 case FOURCC_RGBP: | |
| 279 case FOURCC_RGBO: | |
| 280 case FOURCC_R444: | |
| 281 expected_bpp = 16; | |
| 282 break; | |
| 283 case FOURCC_I444: | |
| 284 case FOURCC_YV24: | |
| 285 case FOURCC_24BG: | |
| 286 case FOURCC_RAW: | |
| 287 expected_bpp = 24; | |
| 288 break; | |
| 289 | |
| 290 case FOURCC_ABGR: | |
| 291 case FOURCC_BGRA: | |
| 292 case FOURCC_ARGB: | |
| 293 expected_bpp = 32; | |
| 294 break; | |
| 295 | |
| 296 case FOURCC_MJPG: | |
| 297 case FOURCC_H264: | |
| 298 expected_bpp = 0; | |
| 299 break; | |
| 300 default: | |
| 301 expected_bpp = 8; // Expect format is at least 8 bits per pixel. | |
| 302 break; | |
| 303 } | |
| 304 size_t expected_size = (w * expected_bpp + 7) / 8 * h; | |
| 305 // For compressed formats, expect 4 bits per 16 x 16 macro. I420 would be | |
| 306 // 6 bits, but grey can be 4 bits. | |
| 307 if (expected_bpp == 0) { | |
| 308 expected_size = ((w + 15) / 16) * ((h + 15) / 16) * 4 / 8; | |
| 309 } | |
| 310 if (sample == NULL) { | |
| 311 LOG(LS_ERROR) << "NULL sample pointer." | |
| 312 << " format: " << GetFourccName(format) | |
| 313 << " bpp: " << expected_bpp | |
| 314 << " size: " << w << "x" << h | |
| 315 << " expected: " << expected_size | |
| 316 << " " << sample_size; | |
| 317 return false; | |
| 318 } | |
| 319 // TODO(fbarchard): Make function to dump information about frames. | |
| 320 uint8_t four_samples[4] = {0, 0, 0, 0}; | |
| 321 for (size_t i = 0; i < arraysize(four_samples) && i < sample_size; ++i) { | |
| 322 four_samples[i] = sample[i]; | |
| 323 } | |
| 324 if (sample_size < expected_size) { | |
| 325 LOG(LS_ERROR) << "Size field is too small." | |
| 326 << " format: " << GetFourccName(format) | |
| 327 << " bpp: " << expected_bpp | |
| 328 << " size: " << w << "x" << h | |
| 329 << " " << sample_size | |
| 330 << " expected: " << expected_size | |
| 331 << " sample[0..3]: " << static_cast<int>(four_samples[0]) | |
| 332 << ", " << static_cast<int>(four_samples[1]) | |
| 333 << ", " << static_cast<int>(four_samples[2]) | |
| 334 << ", " << static_cast<int>(four_samples[3]); | |
| 335 return false; | |
| 336 } | |
| 337 if (sample_size > kMaxSampleSize) { | |
| 338 LOG(LS_WARNING) << "Size field is invalid." | |
| 339 << " format: " << GetFourccName(format) | |
| 340 << " bpp: " << expected_bpp | |
| 341 << " size: " << w << "x" << h | |
| 342 << " " << sample_size | |
| 343 << " expected: " << 2 * expected_size | |
| 344 << " sample[0..3]: " << static_cast<int>(four_samples[0]) | |
| 345 << ", " << static_cast<int>(four_samples[1]) | |
| 346 << ", " << static_cast<int>(four_samples[2]) | |
| 347 << ", " << static_cast<int>(four_samples[3]); | |
| 348 return false; | |
| 349 } | |
| 350 // Show large size warning once every 100 frames. | |
| 351 // TODO(fbarchard): Make frame counter atomic for thread safety. | |
| 352 static int large_warn100 = 0; | |
| 353 size_t large_expected_size = expected_size * 2; | |
| 354 if (expected_bpp >= 8 && | |
| 355 (sample_size > large_expected_size || sample_size > kMaxSampleSize) && | |
| 356 large_warn100 % 100 == 0) { | |
| 357 ++large_warn100; | |
| 358 LOG(LS_WARNING) << "Size field is too large." | |
| 359 << " format: " << GetFourccName(format) | |
| 360 << " bpp: " << expected_bpp | |
| 361 << " size: " << w << "x" << h | |
| 362 << " bytes: " << sample_size | |
| 363 << " expected: " << large_expected_size | |
| 364 << " sample[0..3]: " << static_cast<int>(four_samples[0]) | |
| 365 << ", " << static_cast<int>(four_samples[1]) | |
| 366 << ", " << static_cast<int>(four_samples[2]) | |
| 367 << ", " << static_cast<int>(four_samples[3]); | |
| 368 } | |
| 369 | |
| 370 // TODO(fbarchard): Add duplicate pixel check. | |
| 371 // TODO(fbarchard): Use frame counter atomic for thread safety. | |
| 372 static bool valid_once = true; | |
| 373 if (valid_once) { | |
| 374 valid_once = false; | |
| 375 LOG(LS_INFO) << "Validate frame passed." | |
| 376 << " format: " << GetFourccName(format) | |
| 377 << " bpp: " << expected_bpp | |
| 378 << " size: " << w << "x" << h | |
| 379 << " bytes: " << sample_size | |
| 380 << " expected: " << expected_size | |
| 381 << " sample[0..3]: " << static_cast<int>(four_samples[0]) | |
| 382 << ", " << static_cast<int>(four_samples[1]) | |
| 383 << ", " << static_cast<int>(four_samples[2]) | |
| 384 << ", " << static_cast<int>(four_samples[3]); | |
| 385 } | |
| 386 return true; | |
| 387 } | |
| 388 | |
| 389 } // namespace cricket | |
| OLD | NEW |