GMPVideoi420FrameImpl.cpp (16211B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "GMPVideoi420FrameImpl.h" 7 8 #include <algorithm> 9 10 #include "GMPSharedMemManager.h" 11 #include "GMPVideoHost.h" 12 #include "mozilla/CheckedInt.h" 13 #include "mozilla/gmp/GMPTypes.h" 14 15 namespace mozilla::gmp { 16 17 GMPVideoi420FrameImpl::GMPFramePlane::GMPFramePlane( 18 const GMPPlaneData& aPlaneData) 19 : mOffset(aPlaneData.mOffset()), 20 mSize(aPlaneData.mSize()), 21 mStride(aPlaneData.mStride()) {} 22 23 void GMPVideoi420FrameImpl::GMPFramePlane::InitPlaneData( 24 GMPPlaneData& aPlaneData) { 25 aPlaneData.mOffset() = mOffset; 26 aPlaneData.mSize() = mSize; 27 aPlaneData.mStride() = mStride; 28 } 29 30 void GMPVideoi420FrameImpl::GMPFramePlane::Copy(uint8_t* aDst, 31 int32_t aDstOffset, 32 const uint8_t* aSrc, 33 int32_t aSize, 34 int32_t aStride) { 35 mOffset = aDstOffset; 36 mSize = aSize; 37 mStride = aStride; 38 if (aDst && aSrc && aSize > 0) { 39 memcpy(aDst + aDstOffset, aSrc, aSize); 40 } 41 } 42 43 GMPVideoi420FrameImpl::GMPVideoi420FrameImpl( 44 GMPVideoHostImpl* aHost, 45 HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/) 46 : mReportPolicy(aReportPolicy), 47 mHost(aHost), 48 mWidth(0), 49 mHeight(0), 50 mTimestamp(0ll), 51 mDuration(0ll) { 52 MOZ_ASSERT(aHost); 53 aHost->DecodedFrameCreated(this); 54 } 55 56 GMPVideoi420FrameImpl::GMPVideoi420FrameImpl( 57 const GMPVideoi420FrameData& aFrameData, ipc::Shmem&& aShmemBuffer, 58 GMPVideoHostImpl* aHost, 59 HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/) 60 : mReportPolicy(aReportPolicy), 61 mHost(aHost), 62 mShmemBuffer(std::move(aShmemBuffer)), 63 mYPlane(aFrameData.mYPlane()), 64 mUPlane(aFrameData.mUPlane()), 65 mVPlane(aFrameData.mVPlane()), 66 mWidth(aFrameData.mWidth()), 67 mHeight(aFrameData.mHeight()), 68 mTimestamp(aFrameData.mTimestamp()), 69 mUpdatedTimestamp(aFrameData.mUpdatedTimestamp()), 70 mDuration(aFrameData.mDuration()) { 71 MOZ_ASSERT(aHost); 72 aHost->DecodedFrameCreated(this); 73 } 74 75 GMPVideoi420FrameImpl::GMPVideoi420FrameImpl( 76 const GMPVideoi420FrameData& aFrameData, nsTArray<uint8_t>&& aArrayBuffer, 77 GMPVideoHostImpl* aHost, 78 HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/) 79 : mReportPolicy(aReportPolicy), 80 mHost(aHost), 81 mArrayBuffer(std::move(aArrayBuffer)), 82 mYPlane(aFrameData.mYPlane()), 83 mUPlane(aFrameData.mUPlane()), 84 mVPlane(aFrameData.mVPlane()), 85 mWidth(aFrameData.mWidth()), 86 mHeight(aFrameData.mHeight()), 87 mTimestamp(aFrameData.mTimestamp()), 88 mUpdatedTimestamp(aFrameData.mUpdatedTimestamp()), 89 mDuration(aFrameData.mDuration()) { 90 MOZ_ASSERT(aHost); 91 aHost->DecodedFrameCreated(this); 92 } 93 94 GMPVideoi420FrameImpl::~GMPVideoi420FrameImpl() { 95 DestroyBuffer(); 96 if (mHost) { 97 mHost->DecodedFrameDestroyed(this); 98 } 99 } 100 101 void GMPVideoi420FrameImpl::DoneWithAPI() { 102 DestroyBuffer(); 103 104 // Do this after destroying the buffer because destruction 105 // involves deallocation, which requires a host. 106 mHost = nullptr; 107 } 108 109 void GMPVideoi420FrameImpl::InitFrameData(GMPVideoi420FrameData& aFrameData) { 110 mYPlane.InitPlaneData(aFrameData.mYPlane()); 111 mUPlane.InitPlaneData(aFrameData.mUPlane()); 112 mVPlane.InitPlaneData(aFrameData.mVPlane()); 113 aFrameData.mWidth() = mWidth; 114 aFrameData.mHeight() = mHeight; 115 aFrameData.mTimestamp() = mTimestamp; 116 aFrameData.mUpdatedTimestamp() = mUpdatedTimestamp; 117 aFrameData.mDuration() = mDuration; 118 } 119 120 bool GMPVideoi420FrameImpl::InitFrameData(GMPVideoi420FrameData& aFrameData, 121 ipc::Shmem& aShmemBuffer) { 122 if (!mShmemBuffer.IsReadable()) { 123 return false; 124 } 125 126 aShmemBuffer = mShmemBuffer; 127 128 // This method is called right before Shmem is sent to another process. 129 // We need to effectively zero out our member copy so that we don't 130 // try to delete memory we don't own later. 131 mShmemBuffer = ipc::Shmem(); 132 133 InitFrameData(aFrameData); 134 return true; 135 } 136 137 bool GMPVideoi420FrameImpl::InitFrameData(GMPVideoi420FrameData& aFrameData, 138 nsTArray<uint8_t>& aArrayBuffer) { 139 if (mShmemBuffer.IsReadable()) { 140 return false; 141 } 142 143 aArrayBuffer = std::move(mArrayBuffer); 144 InitFrameData(aFrameData); 145 return true; 146 } 147 148 GMPVideoFrameFormat GMPVideoi420FrameImpl::GetFrameFormat() { 149 return kGMPI420VideoFrame; 150 } 151 152 void GMPVideoi420FrameImpl::Destroy() { delete this; } 153 154 /* static */ 155 bool GMPVideoi420FrameImpl::CheckFrameData( 156 const GMPVideoi420FrameData& aFrameData, size_t aBufferSize) { 157 // We may be passed the "wrong" shmem (one smaller than the actual size). 158 // This implies a bug or serious error on the child size. Ignore this frame 159 // if so. Note: Size() greater than expected is also an error, but with no 160 // negative consequences 161 int32_t half_width = (aFrameData.mWidth() + 1) / 2; 162 if ((aFrameData.mYPlane().mStride() <= 0) || 163 (aFrameData.mYPlane().mSize() <= 0) || 164 (aFrameData.mYPlane().mOffset() < 0) || 165 (aFrameData.mUPlane().mStride() <= 0) || 166 (aFrameData.mUPlane().mSize() <= 0) || 167 (aFrameData.mUPlane().mOffset() < 168 aFrameData.mYPlane().mOffset() + aFrameData.mYPlane().mSize()) || 169 (aFrameData.mVPlane().mStride() <= 0) || 170 (aFrameData.mVPlane().mSize() <= 0) || 171 (aFrameData.mVPlane().mOffset() < 172 aFrameData.mUPlane().mOffset() + aFrameData.mUPlane().mSize()) || 173 (aBufferSize < static_cast<size_t>(aFrameData.mVPlane().mOffset()) + 174 static_cast<size_t>(aFrameData.mVPlane().mSize())) || 175 (aFrameData.mYPlane().mStride() < aFrameData.mWidth()) || 176 (aFrameData.mUPlane().mStride() < half_width) || 177 (aFrameData.mVPlane().mStride() < half_width) || 178 (aFrameData.mYPlane().mSize() < 179 aFrameData.mYPlane().mStride() * aFrameData.mHeight()) || 180 (aFrameData.mUPlane().mSize() < 181 aFrameData.mUPlane().mStride() * ((aFrameData.mHeight() + 1) / 2)) || 182 (aFrameData.mVPlane().mSize() < 183 aFrameData.mVPlane().mStride() * ((aFrameData.mHeight() + 1) / 2))) { 184 return false; 185 } 186 return true; 187 } 188 189 bool GMPVideoi420FrameImpl::CheckDimensions(int32_t aWidth, int32_t aHeight, 190 int32_t aStride_y, 191 int32_t aStride_u, 192 int32_t aStride_v, int32_t aSize_y, 193 int32_t aSize_u, int32_t aSize_v) { 194 if (aWidth < 1 || aHeight < 1 || aStride_y < aWidth || aSize_y < 1 || 195 aSize_u < 1 || aSize_v < 1) { 196 return false; 197 } 198 auto halfWidth = (CheckedInt<int32_t>(aWidth) + 1) / 2; 199 if (!halfWidth.isValid() || aStride_u < halfWidth.value() || 200 aStride_v < halfWidth.value()) { 201 return false; 202 } 203 auto height = CheckedInt<int32_t>(aHeight); 204 auto halfHeight = (height + 1) / 2; 205 auto minSizeY = height * aStride_y; 206 auto minSizeU = halfHeight * aStride_u; 207 auto minSizeV = halfHeight * aStride_v; 208 auto totalSize = minSizeY + minSizeU + minSizeV; 209 if (!minSizeY.isValid() || !minSizeU.isValid() || !minSizeV.isValid() || 210 !totalSize.isValid() || minSizeY.value() > aSize_y || 211 minSizeU.value() > aSize_u || minSizeV.value() > aSize_v) { 212 return false; 213 } 214 return true; 215 } 216 217 bool GMPVideoi420FrameImpl::CheckDimensions(int32_t aWidth, int32_t aHeight, 218 int32_t aStride_y, 219 int32_t aStride_u, 220 int32_t aStride_v) { 221 int32_t half_width = (aWidth + 1) / 2; 222 if (aWidth < 1 || aHeight < 1 || aStride_y < aWidth || 223 aStride_u < half_width || aStride_v < half_width || 224 !(CheckedInt<int32_t>(aHeight) * aStride_y + 225 ((CheckedInt<int32_t>(aHeight) + 1) / 2) * 226 (CheckedInt<int32_t>(aStride_u) + aStride_v)) 227 .isValid()) { 228 return false; 229 } 230 return true; 231 } 232 233 const GMPVideoi420FrameImpl::GMPFramePlane* GMPVideoi420FrameImpl::GetPlane( 234 GMPPlaneType aType) const { 235 switch (aType) { 236 case kGMPYPlane: 237 return &mYPlane; 238 case kGMPUPlane: 239 return &mUPlane; 240 case kGMPVPlane: 241 return &mVPlane; 242 default: 243 MOZ_CRASH("Unknown plane type!"); 244 } 245 return nullptr; 246 } 247 248 GMPVideoi420FrameImpl::GMPFramePlane* GMPVideoi420FrameImpl::GetPlane( 249 GMPPlaneType aType) { 250 switch (aType) { 251 case kGMPYPlane: 252 return &mYPlane; 253 case kGMPUPlane: 254 return &mUPlane; 255 case kGMPVPlane: 256 return &mVPlane; 257 default: 258 MOZ_CRASH("Unknown plane type!"); 259 } 260 return nullptr; 261 } 262 263 GMPErr GMPVideoi420FrameImpl::MaybeResize(int32_t aNewSize) { 264 if (aNewSize <= AllocatedSize()) { 265 return GMPNoErr; 266 } 267 268 if (!mHost) { 269 return GMPGenericErr; 270 } 271 272 if (!mArrayBuffer.IsEmpty()) { 273 if (!mArrayBuffer.SetLength(aNewSize, fallible)) { 274 return GMPAllocErr; 275 } 276 return GMPNoErr; 277 } 278 279 ipc::Shmem new_mem; 280 if (!mHost->SharedMemMgr()->MgrTakeShmem(GMPSharedMemClass::Decoded, aNewSize, 281 &new_mem) && 282 !mArrayBuffer.SetLength(aNewSize, fallible)) { 283 return GMPAllocErr; 284 } 285 286 if (mShmemBuffer.IsReadable()) { 287 if (new_mem.IsWritable()) { 288 memcpy(new_mem.get<uint8_t>(), mShmemBuffer.get<uint8_t>(), aNewSize); 289 } 290 mHost->SharedMemMgr()->MgrGiveShmem(GMPSharedMemClass::Decoded, 291 std::move(mShmemBuffer)); 292 } 293 294 mShmemBuffer = new_mem; 295 296 return GMPNoErr; 297 } 298 299 void GMPVideoi420FrameImpl::DestroyBuffer() { 300 if (mHost && mShmemBuffer.IsWritable()) { 301 mHost->SharedMemMgr()->MgrGiveShmem(GMPSharedMemClass::Decoded, 302 std::move(mShmemBuffer)); 303 } 304 mShmemBuffer = ipc::Shmem(); 305 mArrayBuffer.Clear(); 306 } 307 308 GMPErr GMPVideoi420FrameImpl::CreateEmptyFrame(int32_t aWidth, int32_t aHeight, 309 int32_t aStride_y, 310 int32_t aStride_u, 311 int32_t aStride_v) { 312 if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) { 313 return GMPGenericErr; 314 } 315 316 int32_t size_y = aStride_y * aHeight; 317 int32_t half_height = (aHeight + 1) / 2; 318 int32_t size_u = aStride_u * half_height; 319 int32_t size_v = aStride_v * half_height; 320 321 int32_t bufferSize = size_y + size_u + size_v; 322 GMPErr err = MaybeResize(bufferSize); 323 if (err != GMPNoErr) { 324 return err; 325 } 326 327 mYPlane.mOffset = 0; 328 mYPlane.mSize = size_y; 329 mYPlane.mStride = aStride_y; 330 331 mUPlane.mOffset = size_y; 332 mUPlane.mSize = size_u; 333 mUPlane.mStride = aStride_u; 334 335 mVPlane.mOffset = size_y + size_u; 336 mVPlane.mSize = size_v; 337 mVPlane.mStride = aStride_v; 338 339 mWidth = aWidth; 340 mHeight = aHeight; 341 mTimestamp = 0ll; 342 mUpdatedTimestamp.reset(); 343 mDuration = 0ll; 344 345 return GMPNoErr; 346 } 347 348 GMPErr GMPVideoi420FrameImpl::CreateFrame( 349 int32_t aSize_y, const uint8_t* aBuffer_y, int32_t aSize_u, 350 const uint8_t* aBuffer_u, int32_t aSize_v, const uint8_t* aBuffer_v, 351 int32_t aWidth, int32_t aHeight, int32_t aStride_y, int32_t aStride_u, 352 int32_t aStride_v) { 353 MOZ_ASSERT(aBuffer_y); 354 MOZ_ASSERT(aBuffer_u); 355 MOZ_ASSERT(aBuffer_v); 356 357 if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v, 358 aSize_y, aSize_u, aSize_v)) { 359 return GMPGenericErr; 360 } 361 362 int32_t bufferSize = aSize_y + aSize_u + aSize_v; 363 GMPErr err = MaybeResize(bufferSize); 364 if (err != GMPNoErr) { 365 return err; 366 } 367 368 uint8_t* bufferPtr = Buffer(); 369 mYPlane.Copy(bufferPtr, 0, aBuffer_y, aSize_y, aStride_y); 370 mUPlane.Copy(bufferPtr, aSize_y, aBuffer_u, aSize_u, aStride_u); 371 mVPlane.Copy(bufferPtr, aSize_y + aSize_u, aBuffer_v, aSize_v, aStride_v); 372 373 mWidth = aWidth; 374 mHeight = aHeight; 375 376 return GMPNoErr; 377 } 378 379 GMPErr GMPVideoi420FrameImpl::CopyFrame(const GMPVideoi420Frame& aFrame) { 380 auto& f = static_cast<const GMPVideoi420FrameImpl&>(aFrame); 381 382 int32_t bufferSize = f.mYPlane.mSize + f.mUPlane.mSize + f.mVPlane.mSize; 383 if (bufferSize != AllocatedSize()) { 384 return GMPGenericErr; 385 } 386 387 GMPErr err = MaybeResize(bufferSize); 388 if (err != GMPNoErr) { 389 return err; 390 } 391 392 mYPlane = f.mYPlane; 393 mUPlane = f.mUPlane; 394 mVPlane = f.mVPlane; 395 mWidth = f.mWidth; 396 mHeight = f.mHeight; 397 mTimestamp = f.mTimestamp; 398 mUpdatedTimestamp = f.mUpdatedTimestamp; 399 mDuration = f.mDuration; 400 401 memcpy(Buffer(), f.Buffer(), bufferSize); 402 403 return GMPNoErr; 404 } 405 406 void GMPVideoi420FrameImpl::SwapFrame(GMPVideoi420Frame* aFrame) { 407 auto f = static_cast<GMPVideoi420FrameImpl*>(aFrame); 408 mArrayBuffer.SwapElements(f->mArrayBuffer); 409 std::swap(mShmemBuffer, f->mShmemBuffer); 410 std::swap(mYPlane, f->mYPlane); 411 std::swap(mUPlane, f->mUPlane); 412 std::swap(mVPlane, f->mVPlane); 413 std::swap(mWidth, f->mWidth); 414 std::swap(mHeight, f->mHeight); 415 std::swap(mTimestamp, f->mTimestamp); 416 std::swap(mUpdatedTimestamp, f->mUpdatedTimestamp); 417 std::swap(mDuration, f->mDuration); 418 } 419 420 uint8_t* GMPVideoi420FrameImpl::Buffer() { 421 if (mShmemBuffer.IsWritable()) { 422 return mShmemBuffer.get<uint8_t>(); 423 } 424 if (!mArrayBuffer.IsEmpty()) { 425 return mArrayBuffer.Elements(); 426 } 427 return nullptr; 428 } 429 430 const uint8_t* GMPVideoi420FrameImpl::Buffer() const { 431 if (mShmemBuffer.IsReadable()) { 432 return mShmemBuffer.get<uint8_t>(); 433 } 434 if (!mArrayBuffer.IsEmpty()) { 435 return mArrayBuffer.Elements(); 436 } 437 return nullptr; 438 } 439 440 uint8_t* GMPVideoi420FrameImpl::Buffer(GMPPlaneType aType) { 441 if (auto* p = GetPlane(aType)) { 442 if (auto* buffer = Buffer()) { 443 return buffer + p->mOffset; 444 } 445 } 446 return nullptr; 447 } 448 449 const uint8_t* GMPVideoi420FrameImpl::Buffer(GMPPlaneType aType) const { 450 if (const auto* p = GetPlane(aType)) { 451 if (const auto* buffer = Buffer()) { 452 return buffer + p->mOffset; 453 } 454 } 455 return nullptr; 456 } 457 458 int32_t GMPVideoi420FrameImpl::AllocatedSize() const { 459 if (mShmemBuffer.IsWritable()) { 460 return static_cast<int32_t>(mShmemBuffer.Size<uint8_t>()); 461 } 462 return static_cast<int32_t>(mArrayBuffer.Length()); 463 } 464 465 int32_t GMPVideoi420FrameImpl::AllocatedSize(GMPPlaneType aType) const { 466 if (const auto* p = GetPlane(aType)) { 467 return p->mSize; 468 } 469 return -1; 470 } 471 472 int32_t GMPVideoi420FrameImpl::Stride(GMPPlaneType aType) const { 473 if (const auto* p = GetPlane(aType)) { 474 return p->mStride; 475 } 476 return -1; 477 } 478 479 GMPErr GMPVideoi420FrameImpl::SetWidth(int32_t aWidth) { 480 if (!CheckDimensions(aWidth, mHeight, mYPlane.mStride, mUPlane.mStride, 481 mVPlane.mStride)) { 482 return GMPGenericErr; 483 } 484 mWidth = aWidth; 485 return GMPNoErr; 486 } 487 488 GMPErr GMPVideoi420FrameImpl::SetHeight(int32_t aHeight) { 489 if (!CheckDimensions(mWidth, aHeight, mYPlane.mStride, mUPlane.mStride, 490 mVPlane.mStride)) { 491 return GMPGenericErr; 492 } 493 mHeight = aHeight; 494 return GMPNoErr; 495 } 496 497 int32_t GMPVideoi420FrameImpl::Width() const { return mWidth; } 498 499 int32_t GMPVideoi420FrameImpl::Height() const { return mHeight; } 500 501 void GMPVideoi420FrameImpl::SetTimestamp(uint64_t aTimestamp) { 502 mTimestamp = aTimestamp; 503 } 504 505 uint64_t GMPVideoi420FrameImpl::Timestamp() const { return mTimestamp; } 506 507 void GMPVideoi420FrameImpl::SetUpdatedTimestamp(uint64_t aTimestamp) { 508 mUpdatedTimestamp = Some(aTimestamp); 509 } 510 511 uint64_t GMPVideoi420FrameImpl::UpdatedTimestamp() const { 512 return mUpdatedTimestamp ? *mUpdatedTimestamp : mTimestamp; 513 } 514 515 void GMPVideoi420FrameImpl::SetDuration(uint64_t aDuration) { 516 mDuration = aDuration; 517 } 518 519 uint64_t GMPVideoi420FrameImpl::Duration() const { return mDuration; } 520 521 bool GMPVideoi420FrameImpl::IsZeroSize() const { 522 return (mYPlane.mSize == 0 && mUPlane.mSize == 0 && mVPlane.mSize == 0); 523 } 524 525 void GMPVideoi420FrameImpl::ResetSize() { 526 mYPlane.mSize = 0; 527 mUPlane.mSize = 0; 528 mVPlane.mSize = 0; 529 } 530 531 } // namespace mozilla::gmp