AppleVTDecoder.cpp (29942B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "AppleVTDecoder.h" 8 9 #include <CoreVideo/CVPixelBufferIOSurface.h> 10 #include <IOSurface/IOSurfaceRef.h> 11 12 #include <limits> 13 14 #include "AOMDecoder.h" 15 #include "AppleDecoderModule.h" 16 #include "CallbackThreadRegistry.h" 17 #include "H264.h" 18 #include "H265.h" 19 #include "MP4Decoder.h" 20 #include "MacIOSurfaceImage.h" 21 #include "MediaData.h" 22 #include "VPXDecoder.h" 23 #include "VideoUtils.h" 24 #include "gfxMacUtils.h" 25 #include "mozilla/Logging.h" 26 #include "mozilla/TaskQueue.h" 27 #include "mozilla/gfx/gfxVars.h" 28 29 #define LOG(...) DDMOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, __VA_ARGS__) 30 #define LOGEX(_this, ...) \ 31 DDMOZ_LOGEX(_this, sPDMLog, mozilla::LogLevel::Debug, __VA_ARGS__) 32 33 namespace mozilla { 34 35 using namespace layers; 36 37 AppleVTDecoder::AppleVTDecoder(const VideoInfo& aConfig, 38 layers::ImageContainer* aImageContainer, 39 const CreateDecoderParams::OptionSet& aOptions, 40 layers::KnowsCompositor* aKnowsCompositor, 41 Maybe<TrackingId> aTrackingId) 42 : mExtraData(aConfig.mExtraData), 43 mPictureWidth(aConfig.mImage.width), 44 mPictureHeight(aConfig.mImage.height), 45 mDisplayWidth(aConfig.mDisplay.width), 46 mDisplayHeight(aConfig.mDisplay.height), 47 mColorSpace(aConfig.mColorSpace 48 ? *aConfig.mColorSpace 49 : DefaultColorSpace({mPictureWidth, mPictureHeight})), 50 mColorPrimaries(aConfig.mColorPrimaries ? *aConfig.mColorPrimaries 51 : gfx::ColorSpace2::BT709), 52 mTransferFunction(aConfig.mTransferFunction 53 ? *aConfig.mTransferFunction 54 : gfx::TransferFunction::BT709), 55 mColorRange(aConfig.mColorRange), 56 mColorDepth(aConfig.mColorDepth), 57 mStreamType(AppleVTDecoder::GetStreamType(aConfig.mMimeType)), 58 mTaskQueue(TaskQueue::Create( 59 GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), 60 "AppleVTDecoder")), 61 mMaxRefFrames(GetMaxRefFrames( 62 aOptions.contains(CreateDecoderParams::Option::LowLatency))), 63 mImageContainer(aImageContainer), 64 mKnowsCompositor(aKnowsCompositor) 65 #ifdef MOZ_WIDGET_UIKIT 66 , 67 mUseSoftwareImages(true) 68 #else 69 , 70 mUseSoftwareImages(aKnowsCompositor && 71 aKnowsCompositor->GetWebRenderCompositorType() == 72 layers::WebRenderCompositor::SOFTWARE) 73 #endif 74 , 75 mTrackingId(aTrackingId), 76 mIsFlushing(false), 77 mCallbackThreadId(), 78 mMonitor("AppleVTDecoder"), 79 mPromise(&mMonitor), // To ensure our PromiseHolder is only ever accessed 80 // with the monitor held. 81 mFormat(nullptr), 82 mSession(nullptr), 83 mIsHardwareAccelerated(false) { 84 MOZ_COUNT_CTOR(AppleVTDecoder); 85 MOZ_ASSERT(mStreamType != StreamType::Unknown); 86 LOG("Creating AppleVTDecoder for %dx%d %s video, mMaxRefFrames=%u", 87 mDisplayWidth, mDisplayHeight, EnumValueToString(mStreamType), 88 mMaxRefFrames); 89 } 90 91 AppleVTDecoder::~AppleVTDecoder() { MOZ_COUNT_DTOR(AppleVTDecoder); } 92 93 RefPtr<MediaDataDecoder::InitPromise> AppleVTDecoder::Init() { 94 AUTO_PROFILER_LABEL("AppleVTDecoder::Init", MEDIA_PLAYBACK); 95 MediaResult rv = InitializeSession(); 96 97 if (NS_SUCCEEDED(rv)) { 98 return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__); 99 } 100 101 return InitPromise::CreateAndReject(rv, __func__); 102 } 103 104 RefPtr<MediaDataDecoder::DecodePromise> AppleVTDecoder::Decode( 105 MediaRawData* aSample) { 106 LOG("mp4 input sample %p pts %lld duration %lld us%s %zu bytes", aSample, 107 aSample->mTime.ToMicroseconds(), aSample->mDuration.ToMicroseconds(), 108 aSample->mKeyframe ? " keyframe" : "", aSample->Size()); 109 110 RefPtr<AppleVTDecoder> self = this; 111 RefPtr<MediaRawData> sample = aSample; 112 return InvokeAsync(mTaskQueue, __func__, [self, this, sample] { 113 RefPtr<DecodePromise> p; 114 { 115 MonitorAutoLock mon(mMonitor); 116 p = mPromise.Ensure(__func__); 117 } 118 ProcessDecode(sample); 119 return p; 120 }); 121 } 122 123 RefPtr<MediaDataDecoder::FlushPromise> AppleVTDecoder::Flush() { 124 mIsFlushing = true; 125 return InvokeAsync(mTaskQueue, this, __func__, &AppleVTDecoder::ProcessFlush); 126 } 127 128 RefPtr<MediaDataDecoder::DecodePromise> AppleVTDecoder::Drain() { 129 return InvokeAsync(mTaskQueue, this, __func__, &AppleVTDecoder::ProcessDrain); 130 } 131 132 RefPtr<ShutdownPromise> AppleVTDecoder::Shutdown() { 133 RefPtr<AppleVTDecoder> self = this; 134 return InvokeAsync(mTaskQueue, __func__, [self]() { 135 self->ProcessShutdown(); 136 return self->mTaskQueue->BeginShutdown(); 137 }); 138 } 139 140 // Helper to fill in a timestamp structure. 141 static CMSampleTimingInfo TimingInfoFromSample(MediaRawData* aSample) { 142 CMSampleTimingInfo timestamp; 143 144 timestamp.duration = 145 CMTimeMake(aSample->mDuration.ToMicroseconds(), USECS_PER_S); 146 timestamp.presentationTimeStamp = 147 CMTimeMake(aSample->mTime.ToMicroseconds(), USECS_PER_S); 148 timestamp.decodeTimeStamp = 149 CMTimeMake(aSample->mTimecode.ToMicroseconds(), USECS_PER_S); 150 151 return timestamp; 152 } 153 154 void AppleVTDecoder::ProcessDecode(MediaRawData* aSample) { 155 AUTO_PROFILER_LABEL("AppleVTDecoder::ProcessDecode", MEDIA_PLAYBACK); 156 AssertOnTaskQueue(); 157 PROCESS_DECODE_LOG(aSample); 158 159 if (mIsFlushing) { 160 MonitorAutoLock mon(mMonitor); 161 mPromise.Reject(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 162 return; 163 } 164 165 mTrackingId.apply([&](const auto& aId) { 166 MediaInfoFlag flag = MediaInfoFlag::None; 167 flag |= (aSample->mKeyframe ? MediaInfoFlag::KeyFrame 168 : MediaInfoFlag::NonKeyFrame); 169 flag |= (mIsHardwareAccelerated ? MediaInfoFlag::HardwareDecoding 170 : MediaInfoFlag::SoftwareDecoding); 171 switch (mStreamType) { 172 case StreamType::H264: 173 flag |= MediaInfoFlag::VIDEO_H264; 174 break; 175 case StreamType::VP9: 176 flag |= MediaInfoFlag::VIDEO_VP9; 177 break; 178 case StreamType::AV1: 179 flag |= MediaInfoFlag::VIDEO_AV1; 180 break; 181 case StreamType::HEVC: 182 flag |= MediaInfoFlag::VIDEO_HEVC; 183 break; 184 default: 185 break; 186 } 187 mPerformanceRecorder.Start(aSample->mTimecode.ToMicroseconds(), 188 "AppleVTDecoder"_ns, aId, flag); 189 }); 190 191 AutoCFTypeRef<CMBlockBufferRef> block; 192 AutoCFTypeRef<CMSampleBufferRef> sample; 193 VTDecodeInfoFlags infoFlags; 194 OSStatus rv; 195 196 // FIXME: This copies the sample data. I think we can provide 197 // a custom block source which reuses the aSample buffer. 198 // But note that there may be a problem keeping the samples 199 // alive over multiple frames. 200 rv = CMBlockBufferCreateWithMemoryBlock( 201 kCFAllocatorDefault, // Struct allocator. 202 const_cast<uint8_t*>(aSample->Data()), aSample->Size(), 203 kCFAllocatorNull, // Block allocator. 204 NULL, // Block source. 205 0, // Data offset. 206 aSample->Size(), false, block.Receive()); 207 if (rv != noErr) { 208 NS_ERROR("Couldn't create CMBlockBuffer"); 209 MonitorAutoLock mon(mMonitor); 210 mPromise.Reject( 211 MediaResult(NS_ERROR_OUT_OF_MEMORY, 212 RESULT_DETAIL("CMBlockBufferCreateWithMemoryBlock:%x", rv)), 213 __func__); 214 return; 215 } 216 217 CMSampleTimingInfo timestamp = TimingInfoFromSample(aSample); 218 rv = CMSampleBufferCreate(kCFAllocatorDefault, block, true, 0, 0, mFormat, 1, 219 1, ×tamp, 0, NULL, sample.Receive()); 220 if (rv != noErr) { 221 NS_ERROR("Couldn't create CMSampleBuffer"); 222 MonitorAutoLock mon(mMonitor); 223 mPromise.Reject(MediaResult(NS_ERROR_OUT_OF_MEMORY, 224 RESULT_DETAIL("CMSampleBufferCreate:%x", rv)), 225 __func__); 226 return; 227 } 228 229 VTDecodeFrameFlags decodeFlags = 230 kVTDecodeFrame_EnableAsynchronousDecompression; 231 rv = VTDecompressionSessionDecodeFrame( 232 mSession, sample, decodeFlags, CreateAppleFrameRef(aSample), &infoFlags); 233 if (infoFlags & kVTDecodeInfo_FrameDropped) { 234 MonitorAutoLock mon(mMonitor); 235 // Smile and nod 236 NS_WARNING("Decoder synchronously dropped frame"); 237 MaybeResolveBufferedFrames(); 238 return; 239 } 240 241 if (rv != noErr) { 242 LOG("AppleVTDecoder: Error %d VTDecompressionSessionDecodeFrame", rv); 243 NS_WARNING("Couldn't pass frame to decoder"); 244 // It appears that even when VTDecompressionSessionDecodeFrame returned a 245 // failure. Decoding sometimes actually get processed. 246 MonitorAutoLock mon(mMonitor); 247 mPromise.RejectIfExists( 248 MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, 249 RESULT_DETAIL("VTDecompressionSessionDecodeFrame:%x", rv)), 250 __func__); 251 return; 252 } 253 } 254 255 void AppleVTDecoder::ProcessShutdown() { 256 AUTO_PROFILER_LABEL("AppleVTDecoder::ProcessShutdown", MEDIA_PLAYBACK); 257 if (mSession) { 258 LOG("%s: cleaning up session", __func__); 259 VTDecompressionSessionInvalidate(mSession); 260 mSession.Reset(); 261 } 262 if (mFormat) { 263 LOG("%s: releasing format", __func__); 264 mFormat.Reset(); 265 } 266 } 267 268 RefPtr<MediaDataDecoder::FlushPromise> AppleVTDecoder::ProcessFlush() { 269 AUTO_PROFILER_LABEL("AppleVTDecoder::ProcessFlush", MEDIA_PLAYBACK); 270 AssertOnTaskQueue(); 271 nsresult rv = WaitForAsynchronousFrames(); 272 if (NS_FAILED(rv)) { 273 LOG("AppleVTDecoder::Flush failed waiting for platform decoder"); 274 } 275 MonitorAutoLock mon(mMonitor); 276 mPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 277 278 while (!mReorderQueue.IsEmpty()) { 279 mReorderQueue.Pop(); 280 } 281 mPerformanceRecorder.Record(std::numeric_limits<int64_t>::max()); 282 mSeekTargetThreshold.reset(); 283 mIsFlushing = false; 284 return FlushPromise::CreateAndResolve(true, __func__); 285 } 286 287 RefPtr<MediaDataDecoder::DecodePromise> AppleVTDecoder::ProcessDrain() { 288 AUTO_PROFILER_LABEL("AppleVTDecoder::ProcessDrain", MEDIA_PLAYBACK); 289 AssertOnTaskQueue(); 290 nsresult rv = WaitForAsynchronousFrames(); 291 if (NS_FAILED(rv)) { 292 LOG("AppleVTDecoder::Drain failed waiting for platform decoder"); 293 } 294 MonitorAutoLock mon(mMonitor); 295 DecodedData samples; 296 while (!mReorderQueue.IsEmpty()) { 297 samples.AppendElement(mReorderQueue.Pop()); 298 } 299 return DecodePromise::CreateAndResolve(std::move(samples), __func__); 300 } 301 302 AppleVTDecoder::AppleFrameRef* AppleVTDecoder::CreateAppleFrameRef( 303 const MediaRawData* aSample) { 304 MOZ_ASSERT(aSample); 305 return new AppleFrameRef(*aSample); 306 } 307 308 void AppleVTDecoder::SetSeekThreshold(const media::TimeUnit& aTime) { 309 if (aTime.IsValid()) { 310 mSeekTargetThreshold = Some(aTime); 311 } else { 312 mSeekTargetThreshold.reset(); 313 } 314 } 315 316 // 317 // Implementation details. 318 // 319 320 // Callback passed to the VideoToolbox decoder for returning data. 321 // This needs to be static because the API takes a C-style pair of 322 // function and userdata pointers. This validates parameters and 323 // forwards the decoded image back to an object method. 324 static void PlatformCallback(void* decompressionOutputRefCon, 325 void* sourceFrameRefCon, OSStatus status, 326 VTDecodeInfoFlags flags, CVImageBufferRef image, 327 CMTime presentationTimeStamp, 328 CMTime presentationDuration) { 329 AppleVTDecoder* decoder = 330 static_cast<AppleVTDecoder*>(decompressionOutputRefCon); 331 LOGEX(decoder, "AppleVideoDecoder %s status %d flags %d", __func__, 332 static_cast<int>(status), flags); 333 334 UniquePtr<AppleVTDecoder::AppleFrameRef> frameRef( 335 static_cast<AppleVTDecoder::AppleFrameRef*>(sourceFrameRefCon)); 336 337 // Validate our arguments. 338 if (status != noErr) { 339 NS_WARNING("VideoToolbox decoder returned an error"); 340 decoder->OnDecodeError(status); 341 return; 342 } 343 if (!image) { 344 NS_WARNING("VideoToolbox decoder returned no data"); 345 } else if (flags & kVTDecodeInfo_FrameDropped) { 346 NS_WARNING(" ...frame tagged as dropped..."); 347 } else { 348 MOZ_ASSERT(CFGetTypeID(image) == CVPixelBufferGetTypeID(), 349 "VideoToolbox returned an unexpected image type"); 350 } 351 352 decoder->OutputFrame(image, *frameRef); 353 } 354 355 void AppleVTDecoder::MaybeResolveBufferedFrames() { 356 mMonitor.AssertCurrentThreadOwns(); 357 358 if (mPromise.IsEmpty()) { 359 return; 360 } 361 362 DecodedData results; 363 while (mReorderQueue.Length() > mMaxRefFrames) { 364 results.AppendElement(mReorderQueue.Pop()); 365 } 366 mPromise.Resolve(std::move(results), __func__); 367 } 368 369 void AppleVTDecoder::MaybeRegisterCallbackThread() { 370 ProfilerThreadId id = profiler_current_thread_id(); 371 if (MOZ_LIKELY(id == mCallbackThreadId)) { 372 return; 373 } 374 mCallbackThreadId = id; 375 CallbackThreadRegistry::Get()->Register(mCallbackThreadId, 376 "AppleVTDecoderCallback"); 377 } 378 379 nsCString AppleVTDecoder::GetCodecName() const { 380 return nsCString(EnumValueToString(mStreamType)); 381 } 382 383 // Copy and return a decoded frame. 384 void AppleVTDecoder::OutputFrame(CVPixelBufferRef aImage, 385 AppleVTDecoder::AppleFrameRef aFrameRef) { 386 MaybeRegisterCallbackThread(); 387 388 if (mIsFlushing) { 389 // We are in the process of flushing or shutting down; ignore frame. 390 return; 391 } 392 393 LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s", 394 aFrameRef.byte_offset, aFrameRef.decode_timestamp.ToMicroseconds(), 395 aFrameRef.composition_timestamp.ToMicroseconds(), 396 aFrameRef.duration.ToMicroseconds(), 397 aFrameRef.is_sync_point ? " keyframe" : ""); 398 399 if (!aImage) { 400 // Image was dropped by decoder or none return yet. 401 // We need more input to continue. 402 MonitorAutoLock mon(mMonitor); 403 MaybeResolveBufferedFrames(); 404 return; 405 } 406 407 bool useNullSample = false; 408 if (mSeekTargetThreshold.isSome()) { 409 if ((aFrameRef.composition_timestamp + aFrameRef.duration) < 410 mSeekTargetThreshold.ref()) { 411 useNullSample = true; 412 } else { 413 mSeekTargetThreshold.reset(); 414 } 415 } 416 417 // Where our resulting image will end up. 418 RefPtr<MediaData> data; 419 // Bounds. 420 VideoInfo info; 421 info.mDisplay = gfx::IntSize(mDisplayWidth, mDisplayHeight); 422 423 if (useNullSample) { 424 data = new NullData(aFrameRef.byte_offset, aFrameRef.composition_timestamp, 425 aFrameRef.duration); 426 } else if (mUseSoftwareImages) { 427 size_t width = CVPixelBufferGetWidth(aImage); 428 size_t height = CVPixelBufferGetHeight(aImage); 429 DebugOnly<size_t> planes = CVPixelBufferGetPlaneCount(aImage); 430 MOZ_ASSERT(planes == 3, "Likely not YUV420 format and it must be."); 431 432 VideoData::YCbCrBuffer buffer; 433 434 // Lock the returned image data. 435 CVReturn rv = 436 CVPixelBufferLockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly); 437 if (rv != kCVReturnSuccess) { 438 NS_ERROR("error locking pixel data"); 439 MonitorAutoLock mon(mMonitor); 440 mPromise.Reject( 441 MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, 442 RESULT_DETAIL("CVPixelBufferLockBaseAddress:%x", rv)), 443 __func__); 444 return; 445 } 446 // Y plane. 447 buffer.mPlanes[0].mData = 448 static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 0)); 449 buffer.mPlanes[0].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 0); 450 buffer.mPlanes[0].mWidth = width; 451 buffer.mPlanes[0].mHeight = height; 452 buffer.mPlanes[0].mSkip = 0; 453 // Cb plane. 454 buffer.mPlanes[1].mData = 455 static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 1)); 456 buffer.mPlanes[1].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 1); 457 buffer.mPlanes[1].mWidth = (width + 1) / 2; 458 buffer.mPlanes[1].mHeight = (height + 1) / 2; 459 buffer.mPlanes[1].mSkip = 0; 460 // Cr plane. 461 buffer.mPlanes[2].mData = 462 static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 2)); 463 buffer.mPlanes[2].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 2); 464 buffer.mPlanes[2].mWidth = (width + 1) / 2; 465 buffer.mPlanes[2].mHeight = (height + 1) / 2; 466 buffer.mPlanes[2].mSkip = 0; 467 468 buffer.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT; 469 buffer.mYUVColorSpace = mColorSpace; 470 buffer.mColorPrimaries = mColorPrimaries; 471 buffer.mColorRange = mColorRange; 472 473 gfx::IntRect visible = gfx::IntRect(0, 0, mPictureWidth, mPictureHeight); 474 475 // Copy the image data into our own format. 476 Result<already_AddRefed<VideoData>, MediaResult> result = 477 VideoData::CreateAndCopyData( 478 info, mImageContainer, aFrameRef.byte_offset, 479 aFrameRef.composition_timestamp, aFrameRef.duration, buffer, 480 aFrameRef.is_sync_point, aFrameRef.decode_timestamp, visible, 481 mKnowsCompositor); 482 // TODO: Reject mPromise below with result's error return. 483 data = result.unwrapOr(nullptr); 484 // Unlock the returned image data. 485 CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly); 486 } else { 487 // Set pixel buffer properties on aImage before we extract its surface. 488 // This ensures that we can use defined enums to set values instead 489 // of later setting magic CFSTR values on the surface itself. 490 if (mColorSpace == gfx::YUVColorSpace::BT601) { 491 CVBufferSetAttachment(aImage, kCVImageBufferYCbCrMatrixKey, 492 kCVImageBufferYCbCrMatrix_ITU_R_601_4, 493 kCVAttachmentMode_ShouldPropagate); 494 } else if (mColorSpace == gfx::YUVColorSpace::BT709) { 495 CVBufferSetAttachment(aImage, kCVImageBufferYCbCrMatrixKey, 496 kCVImageBufferYCbCrMatrix_ITU_R_709_2, 497 kCVAttachmentMode_ShouldPropagate); 498 } else if (mColorSpace == gfx::YUVColorSpace::BT2020) { 499 CVBufferSetAttachment(aImage, kCVImageBufferYCbCrMatrixKey, 500 kCVImageBufferYCbCrMatrix_ITU_R_2020, 501 kCVAttachmentMode_ShouldPropagate); 502 } 503 504 if (mColorPrimaries == gfx::ColorSpace2::BT709) { 505 CVBufferSetAttachment(aImage, kCVImageBufferColorPrimariesKey, 506 kCVImageBufferColorPrimaries_ITU_R_709_2, 507 kCVAttachmentMode_ShouldPropagate); 508 } else if (mColorPrimaries == gfx::ColorSpace2::BT2020) { 509 CVBufferSetAttachment(aImage, kCVImageBufferColorPrimariesKey, 510 kCVImageBufferColorPrimaries_ITU_R_2020, 511 kCVAttachmentMode_ShouldPropagate); 512 } 513 514 // Transfer function is applied independently from the colorSpace. 515 CVBufferSetAttachment( 516 aImage, kCVImageBufferTransferFunctionKey, 517 gfxMacUtils::CFStringForTransferFunction(mTransferFunction), 518 kCVAttachmentMode_ShouldPropagate); 519 520 CFTypeRefPtr<IOSurfaceRef> surface = 521 CFTypeRefPtr<IOSurfaceRef>::WrapUnderGetRule( 522 CVPixelBufferGetIOSurface(aImage)); 523 MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer"); 524 525 RefPtr<MacIOSurface> macSurface = new MacIOSurface(std::move(surface)); 526 macSurface->SetYUVColorSpace(mColorSpace); 527 macSurface->mColorPrimaries = mColorPrimaries; 528 529 RefPtr<layers::Image> image = new layers::MacIOSurfaceImage(macSurface); 530 531 data = VideoData::CreateFromImage( 532 info.mDisplay, aFrameRef.byte_offset, aFrameRef.composition_timestamp, 533 aFrameRef.duration, image.forget(), aFrameRef.is_sync_point, 534 aFrameRef.decode_timestamp); 535 } 536 537 if (!data) { 538 NS_ERROR("Couldn't create VideoData for frame"); 539 MonitorAutoLock mon(mMonitor); 540 mPromise.Reject(MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__); 541 return; 542 } 543 544 mPerformanceRecorder.Record( 545 aFrameRef.decode_timestamp.ToMicroseconds(), [&](DecodeStage& aStage) { 546 aStage.SetResolution(static_cast<int>(CVPixelBufferGetWidth(aImage)), 547 static_cast<int>(CVPixelBufferGetHeight(aImage))); 548 auto format = [&]() -> Maybe<DecodeStage::ImageFormat> { 549 switch (CVPixelBufferGetPixelFormatType(aImage)) { 550 case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: 551 case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: 552 return Some(DecodeStage::NV12); 553 case kCVPixelFormatType_422YpCbCr8_yuvs: 554 case kCVPixelFormatType_422YpCbCr8FullRange: 555 return Some(DecodeStage::YUV422P); 556 case kCVPixelFormatType_32BGRA: 557 return Some(DecodeStage::RGBA32); 558 default: 559 return Nothing(); 560 } 561 }(); 562 format.apply([&](auto aFormat) { aStage.SetImageFormat(aFormat); }); 563 aStage.SetColorDepth(mColorDepth); 564 aStage.SetYUVColorSpace(mColorSpace); 565 aStage.SetColorRange(mColorRange); 566 aStage.SetStartTimeAndEndTime(data->mTime.ToMicroseconds(), 567 data->GetEndTime().ToMicroseconds()); 568 }); 569 570 // Frames come out in DTS order but we need to output them 571 // in composition order. 572 MonitorAutoLock mon(mMonitor); 573 mReorderQueue.Push(std::move(data)); 574 MaybeResolveBufferedFrames(); 575 576 LOG("%llu decoded frames queued", 577 static_cast<unsigned long long>(mReorderQueue.Length())); 578 } 579 580 void AppleVTDecoder::OnDecodeError(OSStatus aError) { 581 MonitorAutoLock mon(mMonitor); 582 mPromise.RejectIfExists( 583 MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, 584 RESULT_DETAIL("OnDecodeError:%x", aError)), 585 __func__); 586 } 587 588 nsresult AppleVTDecoder::WaitForAsynchronousFrames() { 589 OSStatus rv = VTDecompressionSessionWaitForAsynchronousFrames(mSession); 590 if (rv != noErr) { 591 NS_ERROR("AppleVTDecoder: Error waiting for asynchronous frames"); 592 return NS_ERROR_FAILURE; 593 } 594 return NS_OK; 595 } 596 597 MediaResult AppleVTDecoder::InitializeSession() { 598 OSStatus rv; 599 600 AutoCFTypeRef<CFDictionaryRef> extensions(CreateDecoderExtensions()); 601 CMVideoCodecType streamType; 602 if (mStreamType == StreamType::H264) { 603 streamType = kCMVideoCodecType_H264; 604 } else if (mStreamType == StreamType::VP9) { 605 streamType = CMVideoCodecType(AppleDecoderModule::kCMVideoCodecType_VP9); 606 } else if (mStreamType == StreamType::HEVC) { 607 streamType = kCMVideoCodecType_HEVC; 608 } else { 609 streamType = kCMVideoCodecType_AV1; 610 } 611 612 rv = CMVideoFormatDescriptionCreate( 613 kCFAllocatorDefault, streamType, AssertedCast<int32_t>(mPictureWidth), 614 AssertedCast<int32_t>(mPictureHeight), extensions, mFormat.Receive()); 615 if (rv != noErr) { 616 return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 617 RESULT_DETAIL("Couldn't create format description!")); 618 } 619 620 // Contruct video decoder selection spec. 621 AutoCFTypeRef<CFDictionaryRef> spec(CreateDecoderSpecification()); 622 623 // Contruct output configuration. 624 AutoCFTypeRef<CFDictionaryRef> outputConfiguration( 625 CreateOutputConfiguration()); 626 627 VTDecompressionOutputCallbackRecord cb = {PlatformCallback, this}; 628 rv = 629 VTDecompressionSessionCreate(kCFAllocatorDefault, mFormat, 630 spec, // Video decoder selection. 631 outputConfiguration, // Output video format. 632 &cb, mSession.Receive()); 633 634 if (rv != noErr) { 635 LOG("AppleVTDecoder: VTDecompressionSessionCreate failed: %d", rv); 636 return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 637 RESULT_DETAIL("Couldn't create decompression session!")); 638 } 639 640 CFBooleanRef isUsingHW = nullptr; 641 rv = VTSessionCopyProperty( 642 mSession, 643 kVTDecompressionPropertyKey_UsingHardwareAcceleratedVideoDecoder, 644 kCFAllocatorDefault, &isUsingHW); 645 if (rv == noErr) { 646 mIsHardwareAccelerated = isUsingHW == kCFBooleanTrue; 647 LOG("AppleVTDecoder: %s hardware accelerated decoding", 648 mIsHardwareAccelerated ? "using" : "not using"); 649 } else { 650 LOG("AppleVTDecoder: maybe hardware accelerated decoding " 651 "(VTSessionCopyProperty query failed %d)", 652 static_cast<int>(rv)); 653 } 654 if (isUsingHW) { 655 CFRelease(isUsingHW); 656 } 657 658 return NS_OK; 659 } 660 661 CFDictionaryRef AppleVTDecoder::CreateDecoderExtensions() { 662 AutoCFTypeRef<CFDataRef> data( 663 CFDataCreate(kCFAllocatorDefault, mExtraData->Elements(), 664 AssertedCast<CFIndex>(mExtraData->Length()))); 665 666 const void* atomsKey[1]; 667 if (mStreamType == StreamType::H264) { 668 atomsKey[0] = CFSTR("avcC"); 669 } else if (mStreamType == StreamType::VP9) { 670 atomsKey[0] = CFSTR("vpcC"); 671 } else if (mStreamType == StreamType::HEVC) { 672 atomsKey[0] = CFSTR("hvcC"); 673 } else { 674 atomsKey[0] = CFSTR("av1C"); 675 } 676 677 const void* atomsValue[] = {data}; 678 static_assert(std::size(atomsKey) == std::size(atomsValue), 679 "Non matching keys/values array size"); 680 681 AutoCFTypeRef<CFDictionaryRef> atoms(CFDictionaryCreate( 682 kCFAllocatorDefault, atomsKey, atomsValue, std::size(atomsKey), 683 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 684 685 const void* extensionKeys[] = { 686 kCVImageBufferChromaLocationBottomFieldKey, 687 kCVImageBufferChromaLocationTopFieldKey, 688 kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms}; 689 690 const void* extensionValues[] = {kCVImageBufferChromaLocation_Left, 691 kCVImageBufferChromaLocation_Left, atoms}; 692 static_assert(std::size(extensionKeys) == std::size(extensionValues), 693 "Non matching keys/values array size"); 694 695 return CFDictionaryCreate(kCFAllocatorDefault, extensionKeys, extensionValues, 696 std::size(extensionKeys), 697 &kCFTypeDictionaryKeyCallBacks, 698 &kCFTypeDictionaryValueCallBacks); 699 } 700 701 CFDictionaryRef AppleVTDecoder::CreateDecoderSpecification() { 702 const void* specKeys[] = { 703 kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder}; 704 const void* specValues[1]; 705 if (gfx::gfxVars::CanUseHardwareVideoDecoding()) { 706 specValues[0] = kCFBooleanTrue; 707 } else { 708 // This GPU is blacklisted for hardware decoding. 709 specValues[0] = kCFBooleanFalse; 710 } 711 static_assert(std::size(specKeys) == std::size(specValues), 712 "Non matching keys/values array size"); 713 714 return CFDictionaryCreate(kCFAllocatorDefault, specKeys, specValues, 715 std::size(specKeys), &kCFTypeDictionaryKeyCallBacks, 716 &kCFTypeDictionaryValueCallBacks); 717 } 718 719 CFDictionaryRef AppleVTDecoder::CreateOutputConfiguration() { 720 if (mUseSoftwareImages) { 721 // Output format type: 722 SInt32 PixelFormatTypeValue = kCVPixelFormatType_420YpCbCr8Planar; 723 AutoCFTypeRef<CFNumberRef> PixelFormatTypeNumber(CFNumberCreate( 724 kCFAllocatorDefault, kCFNumberSInt32Type, &PixelFormatTypeValue)); 725 const void* outputKeys[] = {kCVPixelBufferPixelFormatTypeKey}; 726 const void* outputValues[] = {PixelFormatTypeNumber}; 727 static_assert(std::size(outputKeys) == std::size(outputValues), 728 "Non matching keys/values array size"); 729 730 return CFDictionaryCreate( 731 kCFAllocatorDefault, outputKeys, outputValues, std::size(outputKeys), 732 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 733 } 734 735 // Output format type: 736 737 bool is10Bit = (gfx::BitDepthForColorDepth(mColorDepth) == 10); 738 SInt32 PixelFormatTypeValue = 739 mColorRange == gfx::ColorRange::FULL 740 ? (is10Bit ? kCVPixelFormatType_420YpCbCr10BiPlanarFullRange 741 : kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) 742 : (is10Bit ? kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange 743 : kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange); 744 AutoCFTypeRef<CFNumberRef> PixelFormatTypeNumber(CFNumberCreate( 745 kCFAllocatorDefault, kCFNumberSInt32Type, &PixelFormatTypeValue)); 746 // Construct IOSurface Properties 747 const void* IOSurfaceKeys[] = {kIOSurfaceIsGlobal}; 748 const void* IOSurfaceValues[] = {kCFBooleanTrue}; 749 static_assert(std::size(IOSurfaceKeys) == std::size(IOSurfaceValues), 750 "Non matching keys/values array size"); 751 752 // Contruct output configuration. 753 AutoCFTypeRef<CFDictionaryRef> IOSurfaceProperties(CFDictionaryCreate( 754 kCFAllocatorDefault, IOSurfaceKeys, IOSurfaceValues, 755 std::size(IOSurfaceKeys), &kCFTypeDictionaryKeyCallBacks, 756 &kCFTypeDictionaryValueCallBacks)); 757 758 const void* outputKeys[] = {kCVPixelBufferIOSurfacePropertiesKey, 759 kCVPixelBufferPixelFormatTypeKey, 760 kCVPixelBufferOpenGLCompatibilityKey}; 761 const void* outputValues[] = {IOSurfaceProperties, PixelFormatTypeNumber, 762 kCFBooleanTrue}; 763 static_assert(std::size(outputKeys) == std::size(outputValues), 764 "Non matching keys/values array size"); 765 766 return CFDictionaryCreate( 767 kCFAllocatorDefault, outputKeys, outputValues, std::size(outputKeys), 768 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 769 } 770 771 AppleVTDecoder::StreamType AppleVTDecoder::GetStreamType( 772 const nsCString& aMimeType) const { 773 if (MP4Decoder::IsH264(aMimeType)) { 774 return StreamType::H264; 775 } 776 if (MP4Decoder::IsHEVC(aMimeType)) { 777 return StreamType::HEVC; 778 } 779 if (VPXDecoder::IsVP9(aMimeType)) { 780 return StreamType::VP9; 781 } 782 if (AOMDecoder::IsAV1(aMimeType)) { 783 return StreamType::AV1; 784 } 785 return StreamType::Unknown; 786 } 787 788 uint32_t AppleVTDecoder::GetMaxRefFrames(bool aIsLowLatency) const { 789 if (mStreamType == StreamType::H264 && !aIsLowLatency) { 790 return H264::ComputeMaxRefFrames(mExtraData); 791 } 792 if (mStreamType == StreamType::HEVC && !aIsLowLatency) { 793 return H265::ComputeMaxRefFrames(mExtraData); 794 } 795 return 0; 796 } 797 798 } // namespace mozilla 799 800 #undef LOG 801 #undef LOGEX