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 <cstring> 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/base/logging.h" 39#include "talk/media/base/videocommon.h" 40 41namespace cricket { 42 43// Round to 2 pixels because Chroma channels are half size. 44#define ROUNDTO2(v) (v & ~1) 45 46talk_base::StreamResult VideoFrame::Write(talk_base::StreamInterface* stream, 47 int* error) { 48 talk_base::StreamResult result = talk_base::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 != talk_base::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 != talk_base::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 != talk_base::SR_SUCCESS) { 80 return result; 81 } 82 } 83 return result; 84} 85 86bool 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 108void 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. 119void 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 170size_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 192void 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 207VideoFrame* 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 219bool 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 237static const size_t kMaxSampleSize = 1000000000u; 238// Returns whether a sample is valid 239bool 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 if (sample_size < expected_size) { 315 LOG(LS_ERROR) << "Size field is too small." 316 << " format: " << GetFourccName(format) 317 << " bpp: " << expected_bpp 318 << " size: " << w << "x" << h 319 << " " << sample_size 320 << " expected: " << expected_size 321 << " sample[0..3]: " << static_cast<int>(sample[0]) 322 << ", " << static_cast<int>(sample[1]) 323 << ", " << static_cast<int>(sample[2]) 324 << ", " << static_cast<int>(sample[3]); 325 return false; 326 } 327 if (sample_size > kMaxSampleSize) { 328 LOG(LS_WARNING) << "Size field is invalid." 329 << " format: " << GetFourccName(format) 330 << " bpp: " << expected_bpp 331 << " size: " << w << "x" << h 332 << " " << sample_size 333 << " expected: " << 2 * expected_size 334 << " sample[0..3]: " << static_cast<int>(sample[0]) 335 << ", " << static_cast<int>(sample[1]) 336 << ", " << static_cast<int>(sample[2]) 337 << ", " << static_cast<int>(sample[3]); 338 return false; 339 } 340 // Show large size warning once every 100 frames. 341 static int large_warn100 = 0; 342 size_t large_expected_size = expected_size * 2; 343 if (expected_bpp >= 8 && 344 (sample_size > large_expected_size || sample_size > kMaxSampleSize) && 345 large_warn100 % 100 == 0) { 346 ++large_warn100; 347 LOG(LS_WARNING) << "Size field is too large." 348 << " format: " << GetFourccName(format) 349 << " bpp: " << expected_bpp 350 << " size: " << w << "x" << h 351 << " bytes: " << sample_size 352 << " expected: " << large_expected_size 353 << " sample[0..3]: " << static_cast<int>(sample[0]) 354 << ", " << static_cast<int>(sample[1]) 355 << ", " << static_cast<int>(sample[2]) 356 << ", " << static_cast<int>(sample[3]); 357 } 358 // Scan pages to ensure they are there and don't contain a single value and 359 // to generate an error. 360 if (!memcmp(sample + sample_size - 8, sample + sample_size - 4, 4) && 361 !memcmp(sample, sample + 4, sample_size - 4)) { 362 LOG(LS_WARNING) << "Duplicate value for all pixels." 363 << " format: " << GetFourccName(format) 364 << " bpp: " << expected_bpp 365 << " size: " << w << "x" << h 366 << " bytes: " << sample_size 367 << " expected: " << expected_size 368 << " sample[0..3]: " << static_cast<int>(sample[0]) 369 << ", " << static_cast<int>(sample[1]) 370 << ", " << static_cast<int>(sample[2]) 371 << ", " << static_cast<int>(sample[3]); 372 } 373 374 static bool valid_once = true; 375 if (valid_once) { 376 valid_once = false; 377 LOG(LS_INFO) << "Validate frame passed." 378 << " format: " << GetFourccName(format) 379 << " bpp: " << expected_bpp 380 << " size: " << w << "x" << h 381 << " bytes: " << sample_size 382 << " expected: " << expected_size 383 << " sample[0..3]: " << static_cast<int>(sample[0]) 384 << ", " << static_cast<int>(sample[1]) 385 << ", " << static_cast<int>(sample[2]) 386 << ", " << static_cast<int>(sample[3]); 387 } 388 return true; 389} 390 391} // namespace cricket 392