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 #if !defined(DISABLE_YUV) 33 #include "libyuv/compare.h" 34 #include "libyuv/planar_functions.h" 35 #include "libyuv/scale.h" 36 #endif 37 38 #include "talk/media/base/videocommon.h" 39 #include "webrtc/base/logging.h" 40 41 namespace cricket { 42 43 // Round to 2 pixels because Chroma channels are half size. 44 #define ROUNDTO2(v) (v & ~1) 45 46 rtc::StreamResult VideoFrame::Write(rtc::StreamInterface* stream, 47 int* error) { 48 rtc::StreamResult result = rtc::SR_SUCCESS; 49 const uint8* src_y = GetYPlane(); 50 const uint8* src_u = GetUPlane(); 51 const uint8* src_v = GetVPlane(); 52 if (!src_y || !src_u || !src_v) { 53 return result; // Nothing to write. 54 } 55 const int32 y_pitch = GetYPitch(); 56 const int32 u_pitch = GetUPitch(); 57 const int32 v_pitch = GetVPitch(); 58 const size_t width = GetWidth(); 59 const size_t height = GetHeight(); 60 const size_t half_width = (width + 1) >> 1; 61 const size_t half_height = (height + 1) >> 1; 62 // Write Y. 63 for (size_t row = 0; row < height; ++row) { 64 result = stream->Write(src_y + row * y_pitch, width, NULL, error); 65 if (result != rtc::SR_SUCCESS) { 66 return result; 67 } 68 } 69 // Write U. 70 for (size_t row = 0; row < half_height; ++row) { 71 result = stream->Write(src_u + row * u_pitch, half_width, NULL, error); 72 if (result != rtc::SR_SUCCESS) { 73 return result; 74 } 75 } 76 // Write V. 77 for (size_t row = 0; row < half_height; ++row) { 78 result = stream->Write(src_v + row * v_pitch, half_width, NULL, error); 79 if (result != rtc::SR_SUCCESS) { 80 return result; 81 } 82 } 83 return result; 84 } 85 86 bool VideoFrame::CopyToPlanes( 87 uint8* dst_y, uint8* dst_u, uint8* dst_v, 88 int32 dst_pitch_y, int32 dst_pitch_u, int32 dst_pitch_v) const { 89 #if !defined(DISABLE_YUV) 90 int32 src_width = static_cast<int>(GetWidth()); 91 int32 src_height = static_cast<int>(GetHeight()); 92 return libyuv::I420Copy(GetYPlane(), GetYPitch(), 93 GetUPlane(), GetUPitch(), 94 GetVPlane(), GetVPitch(), 95 dst_y, dst_pitch_y, 96 dst_u, dst_pitch_u, 97 dst_v, dst_pitch_v, 98 src_width, src_height) == 0; 99 #else 100 int uv_size = GetUPitch() * GetChromaHeight(); 101 memcpy(dst_y, GetYPlane(), GetWidth() * GetHeight()); 102 memcpy(dst_u, GetUPlane(), uv_size); 103 memcpy(dst_v, GetVPlane(), uv_size); 104 return true; 105 #endif 106 } 107 108 void VideoFrame::CopyToFrame(VideoFrame* dst) const { 109 if (!dst) { 110 LOG(LS_ERROR) << "NULL dst pointer."; 111 return; 112 } 113 114 CopyToPlanes(dst->GetYPlane(), dst->GetUPlane(), dst->GetVPlane(), 115 dst->GetYPitch(), dst->GetUPitch(), dst->GetVPitch()); 116 } 117 118 // TODO(fbarchard): Handle odd width/height with rounding. 119 void VideoFrame::StretchToPlanes( 120 uint8* dst_y, uint8* dst_u, uint8* dst_v, 121 int32 dst_pitch_y, int32 dst_pitch_u, int32 dst_pitch_v, 122 size_t width, size_t height, bool interpolate, bool vert_crop) const { 123 if (!GetYPlane() || !GetUPlane() || !GetVPlane()) { 124 LOG(LS_ERROR) << "NULL plane pointer."; 125 return; 126 } 127 128 size_t src_width = GetWidth(); 129 size_t src_height = GetHeight(); 130 if (width == src_width && height == src_height) { 131 CopyToPlanes(dst_y, dst_u, dst_v, dst_pitch_y, dst_pitch_u, dst_pitch_v); 132 return; 133 } 134 const uint8* src_y = GetYPlane(); 135 const uint8* src_u = GetUPlane(); 136 const uint8* src_v = GetVPlane(); 137 138 if (vert_crop) { 139 // Adjust the input width:height ratio to be the same as the output ratio. 140 if (src_width * height > src_height * width) { 141 // Reduce the input width, but keep size/position aligned for YuvScaler 142 src_width = ROUNDTO2(src_height * width / height); 143 int32 iwidth_offset = ROUNDTO2((GetWidth() - src_width) / 2); 144 src_y += iwidth_offset; 145 src_u += iwidth_offset / 2; 146 src_v += iwidth_offset / 2; 147 } else if (src_width * height < src_height * width) { 148 // Reduce the input height. 149 src_height = src_width * height / width; 150 int32 iheight_offset = static_cast<int32>( 151 (GetHeight() - src_height) >> 2); 152 iheight_offset <<= 1; // Ensure that iheight_offset is even. 153 src_y += iheight_offset * GetYPitch(); 154 src_u += iheight_offset / 2 * GetUPitch(); 155 src_v += iheight_offset / 2 * GetVPitch(); 156 } 157 } 158 159 // TODO(fbarchard): Implement a simple scale for non-libyuv. 160 #if !defined(DISABLE_YUV) 161 // Scale to the output I420 frame. 162 libyuv::Scale(src_y, src_u, src_v, 163 GetYPitch(), GetUPitch(), GetVPitch(), 164 static_cast<int>(src_width), static_cast<int>(src_height), 165 dst_y, dst_u, dst_v, dst_pitch_y, dst_pitch_u, dst_pitch_v, 166 static_cast<int>(width), static_cast<int>(height), interpolate); 167 #endif 168 } 169 170 size_t VideoFrame::StretchToBuffer(size_t dst_width, size_t dst_height, 171 uint8* dst_buffer, size_t size, 172 bool interpolate, bool vert_crop) const { 173 if (!dst_buffer) { 174 LOG(LS_ERROR) << "NULL dst_buffer pointer."; 175 return 0; 176 } 177 178 size_t needed = SizeOf(dst_width, dst_height); 179 if (needed <= size) { 180 uint8* dst_y = dst_buffer; 181 uint8* dst_u = dst_y + dst_width * dst_height; 182 uint8* dst_v = dst_u + ((dst_width + 1) >> 1) * ((dst_height + 1) >> 1); 183 StretchToPlanes(dst_y, dst_u, dst_v, 184 static_cast<int32>(dst_width), 185 static_cast<int32>((dst_width + 1) >> 1), 186 static_cast<int32>((dst_width + 1) >> 1), 187 dst_width, dst_height, interpolate, vert_crop); 188 } 189 return needed; 190 } 191 192 void VideoFrame::StretchToFrame(VideoFrame* dst, 193 bool interpolate, bool vert_crop) const { 194 if (!dst) { 195 LOG(LS_ERROR) << "NULL dst pointer."; 196 return; 197 } 198 199 StretchToPlanes(dst->GetYPlane(), dst->GetUPlane(), dst->GetVPlane(), 200 dst->GetYPitch(), dst->GetUPitch(), dst->GetVPitch(), 201 dst->GetWidth(), dst->GetHeight(), 202 interpolate, vert_crop); 203 dst->SetElapsedTime(GetElapsedTime()); 204 dst->SetTimeStamp(GetTimeStamp()); 205 } 206 207 VideoFrame* VideoFrame::Stretch(size_t dst_width, size_t dst_height, 208 bool interpolate, bool vert_crop) const { 209 VideoFrame* dest = CreateEmptyFrame(static_cast<int>(dst_width), 210 static_cast<int>(dst_height), 211 GetPixelWidth(), GetPixelHeight(), 212 GetElapsedTime(), GetTimeStamp()); 213 if (dest) { 214 StretchToFrame(dest, interpolate, vert_crop); 215 } 216 return dest; 217 } 218 219 bool VideoFrame::SetToBlack() { 220 #if !defined(DISABLE_YUV) 221 return libyuv::I420Rect(GetYPlane(), GetYPitch(), 222 GetUPlane(), GetUPitch(), 223 GetVPlane(), GetVPitch(), 224 0, 0, 225 static_cast<int>(GetWidth()), 226 static_cast<int>(GetHeight()), 227 16, 128, 128) == 0; 228 #else 229 int uv_size = GetUPitch() * GetChromaHeight(); 230 memset(GetYPlane(), 16, GetWidth() * GetHeight()); 231 memset(GetUPlane(), 128, uv_size); 232 memset(GetVPlane(), 128, uv_size); 233 return true; 234 #endif 235 } 236 237 static const size_t kMaxSampleSize = 1000000000u; 238 // Returns whether a sample is valid. 239 bool VideoFrame::Validate(uint32 fourcc, int w, int h, 240 const uint8 *sample, size_t sample_size) { 241 if (h < 0) { 242 h = -h; 243 } 244 // 16384 is maximum resolution for VP8 codec. 245 if (w < 1 || w > 16384 || h < 1 || h > 16384) { 246 LOG(LS_ERROR) << "Invalid dimensions: " << w << "x" << h; 247 return false; 248 } 249 uint32 format = CanonicalFourCC(fourcc); 250 int expected_bpp = 8; 251 switch (format) { 252 case FOURCC_I400: 253 case FOURCC_RGGB: 254 case FOURCC_BGGR: 255 case FOURCC_GRBG: 256 case FOURCC_GBRG: 257 expected_bpp = 8; 258 break; 259 case FOURCC_I420: 260 case FOURCC_I411: 261 case FOURCC_YU12: 262 case FOURCC_YV12: 263 case FOURCC_M420: 264 case FOURCC_Q420: 265 case FOURCC_NV21: 266 case FOURCC_NV12: 267 expected_bpp = 12; 268 break; 269 case FOURCC_I422: 270 case FOURCC_YV16: 271 case FOURCC_YUY2: 272 case FOURCC_UYVY: 273 case FOURCC_RGBP: 274 case FOURCC_RGBO: 275 case FOURCC_R444: 276 expected_bpp = 16; 277 break; 278 case FOURCC_I444: 279 case FOURCC_YV24: 280 case FOURCC_24BG: 281 case FOURCC_RAW: 282 expected_bpp = 24; 283 break; 284 285 case FOURCC_ABGR: 286 case FOURCC_BGRA: 287 case FOURCC_ARGB: 288 expected_bpp = 32; 289 break; 290 291 case FOURCC_MJPG: 292 case FOURCC_H264: 293 expected_bpp = 0; 294 break; 295 default: 296 expected_bpp = 8; // Expect format is at least 8 bits per pixel. 297 break; 298 } 299 size_t expected_size = (w * expected_bpp + 7) / 8 * h; 300 // For compressed formats, expect 4 bits per 16 x 16 macro. I420 would be 301 // 6 bits, but grey can be 4 bits. 302 if (expected_bpp == 0) { 303 expected_size = ((w + 15) / 16) * ((h + 15) / 16) * 4 / 8; 304 } 305 if (sample == NULL) { 306 LOG(LS_ERROR) << "NULL sample pointer." 307 << " format: " << GetFourccName(format) 308 << " bpp: " << expected_bpp 309 << " size: " << w << "x" << h 310 << " expected: " << expected_size 311 << " " << sample_size; 312 return false; 313 } 314 // TODO(fbarchard): Make function to dump information about frames. 315 uint8 four_samples[4] = { 0, 0, 0, 0 }; 316 for (size_t i = 0; i < ARRAY_SIZE(four_samples) && i < sample_size; ++i) { 317 four_samples[i] = sample[i]; 318 } 319 if (sample_size < expected_size) { 320 LOG(LS_ERROR) << "Size field is too small." 321 << " format: " << GetFourccName(format) 322 << " bpp: " << expected_bpp 323 << " size: " << w << "x" << h 324 << " " << sample_size 325 << " expected: " << expected_size 326 << " sample[0..3]: " << static_cast<int>(four_samples[0]) 327 << ", " << static_cast<int>(four_samples[1]) 328 << ", " << static_cast<int>(four_samples[2]) 329 << ", " << static_cast<int>(four_samples[3]); 330 return false; 331 } 332 if (sample_size > kMaxSampleSize) { 333 LOG(LS_WARNING) << "Size field is invalid." 334 << " format: " << GetFourccName(format) 335 << " bpp: " << expected_bpp 336 << " size: " << w << "x" << h 337 << " " << sample_size 338 << " expected: " << 2 * expected_size 339 << " sample[0..3]: " << static_cast<int>(four_samples[0]) 340 << ", " << static_cast<int>(four_samples[1]) 341 << ", " << static_cast<int>(four_samples[2]) 342 << ", " << static_cast<int>(four_samples[3]); 343 return false; 344 } 345 // Show large size warning once every 100 frames. 346 // TODO(fbarchard): Make frame counter atomic for thread safety. 347 static int large_warn100 = 0; 348 size_t large_expected_size = expected_size * 2; 349 if (expected_bpp >= 8 && 350 (sample_size > large_expected_size || sample_size > kMaxSampleSize) && 351 large_warn100 % 100 == 0) { 352 ++large_warn100; 353 LOG(LS_WARNING) << "Size field is too large." 354 << " format: " << GetFourccName(format) 355 << " bpp: " << expected_bpp 356 << " size: " << w << "x" << h 357 << " bytes: " << sample_size 358 << " expected: " << large_expected_size 359 << " sample[0..3]: " << static_cast<int>(four_samples[0]) 360 << ", " << static_cast<int>(four_samples[1]) 361 << ", " << static_cast<int>(four_samples[2]) 362 << ", " << static_cast<int>(four_samples[3]); 363 } 364 365 // TODO(fbarchard): Add duplicate pixel check. 366 // TODO(fbarchard): Use frame counter atomic for thread safety. 367 static bool valid_once = true; 368 if (valid_once) { 369 valid_once = false; 370 LOG(LS_INFO) << "Validate frame passed." 371 << " format: " << GetFourccName(format) 372 << " bpp: " << expected_bpp 373 << " size: " << w << "x" << h 374 << " bytes: " << sample_size 375 << " expected: " << expected_size 376 << " sample[0..3]: " << static_cast<int>(four_samples[0]) 377 << ", " << static_cast<int>(four_samples[1]) 378 << ", " << static_cast<int>(four_samples[2]) 379 << ", " << static_cast<int>(four_samples[3]); 380 } 381 return true; 382 } 383 384 } // namespace cricket 385