Index: content/common/gpu/media/vt_video_decode_accelerator.cc |
diff --git a/content/common/gpu/media/vt_video_decode_accelerator.cc b/content/common/gpu/media/vt_video_decode_accelerator.cc |
index 62658fb70dfd64018f7a1c6a256adde726639372..136a308a6be6f5650aa533ac7f9c41656590f453 100644 |
--- a/content/common/gpu/media/vt_video_decode_accelerator.cc |
+++ b/content/common/gpu/media/vt_video_decode_accelerator.cc |
@@ -10,6 +10,8 @@ |
#include "base/bind.h" |
#include "base/command_line.h" |
+#include "base/logging.h" |
+#include "base/mac/mac_logging.h" |
#include "base/sys_byteorder.h" |
#include "base/thread_task_runner_handle.h" |
#include "content/common/gpu/media/vt_video_decode_accelerator.h" |
@@ -22,10 +24,10 @@ using content_common_gpu_media::InitializeStubs; |
using content_common_gpu_media::IsVtInitialized; |
using content_common_gpu_media::StubPathMap; |
-#define NOTIFY_STATUS(name, status) \ |
- do { \ |
- DLOG(ERROR) << name << " failed with status " << status; \ |
- NotifyError(PLATFORM_FAILURE); \ |
+#define NOTIFY_STATUS(name, status) \ |
+ do { \ |
+ OSSTATUS_DLOG(ERROR, status) << name; \ |
+ NotifyError(PLATFORM_FAILURE); \ |
} while (0) |
namespace content { |
@@ -44,6 +46,121 @@ static const int kNumPictureBuffers = media::limits::kMaxVideoFrames + 1; |
// reorder queue.) |
static const int kMaxReorderQueueSize = 16; |
+// Build an |image_config| dictionary for VideoToolbox initialization. |
+static base::ScopedCFTypeRef<CFMutableDictionaryRef> |
+BuildImageConfig(CMVideoDimensions coded_dimensions) { |
+ // TODO(sandersd): RGBA option for 4:4:4 video. |
+ int32_t pixel_format = kCVPixelFormatType_422YpCbCr8; |
+ |
+#define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) |
+ base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); |
+ base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); |
+ base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); |
+#undef CFINT |
+ |
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
+ CFDictionaryCreateMutable( |
+ kCFAllocatorDefault, |
+ 4, // capacity |
+ &kCFTypeDictionaryKeyCallBacks, |
+ &kCFTypeDictionaryValueCallBacks)); |
+ CFDictionarySetValue(image_config, kCVPixelBufferPixelFormatTypeKey, |
+ cf_pixel_format); |
+ CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
+ CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
+ CFDictionarySetValue(image_config, kCVPixelBufferOpenGLCompatibilityKey, |
+ kCFBooleanTrue); |
+ |
+ return image_config; |
+} |
+ |
+// The purpose of this function is to preload the generic and hardware-specific |
+// libraries required by VideoToolbox before the GPU sandbox is enabled. |
+// VideoToolbox normally loads the hardware-specific libraries lazily, so we |
+// must actually create a decompression session. |
+// |
+// If creating a decompression session fails, hardware decoding will be disabled |
+// (Initialize() will always return false). If it succeeds but a required |
+// library is not loaded yet (I have not experienced this, but the details are |
+// not documented), then VideoToolbox will fall back on software decoding |
+// internally. If that happens, the likely solution is to expand the scope of |
+// this initialization. |
+void InitializeVideoToolbox() { |
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kDisableAcceleratedVideoDecode)) { |
+ return; |
+ } |
+ |
+ if (!IsVtInitialized()) { |
+ // CoreVideo is also required, but the loader stops after the first path is |
+ // loaded. Instead we rely on the transitive dependency from VideoToolbox to |
+ // CoreVideo. |
+ // TODO(sandersd): Fallback to PrivateFrameworks to support OS X < 10.8. |
+ StubPathMap paths; |
+ paths[kModuleVt].push_back(FILE_PATH_LITERAL( |
+ "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); |
+ if (!InitializeStubs(paths)) |
+ return; |
+ } |
+ |
+ // Create a decoding session. |
+ // SPS and PPS data were taken from the 480p encoding of Big Buck Bunny. |
+ const uint8_t sps[] = {0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x80, 0xd4, 0x3d, |
+ 0xa1, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, |
+ 0x00, 0x30, 0x8f, 0x16, 0x2d, 0x9a}; |
+ const uint8_t pps[] = {0x68, 0xe9, 0x7b, 0xcb}; |
+ const uint8_t* data_ptrs[] = {sps, pps}; |
+ const size_t data_sizes[] = {arraysize(sps), arraysize(pps)}; |
+ |
+ base::ScopedCFTypeRef<CMFormatDescriptionRef> format; |
+ OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( |
+ kCFAllocatorDefault, |
+ 2, // parameter_set_count |
+ data_ptrs, // ¶meter_set_pointers |
+ data_sizes, // ¶meter_set_sizes |
+ kNALUHeaderLength, // nal_unit_header_length |
+ format.InitializeInto()); |
+ if (status) { |
+ OSSTATUS_LOG(ERROR, status) << "Failed to create CMVideoFormatDescription " |
+ << "while initializing VideoToolbox"; |
+ content_common_gpu_media::UninitializeVt(); |
+ return; |
+ } |
+ |
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( |
+ CFDictionaryCreateMutable( |
+ kCFAllocatorDefault, |
+ 1, // capacity |
+ &kCFTypeDictionaryKeyCallBacks, |
+ &kCFTypeDictionaryValueCallBacks)); |
+ |
+ CFDictionarySetValue( |
+ decoder_config, |
+ // kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder |
+ CFSTR("RequireHardwareAcceleratedVideoDecoder"), |
+ kCFBooleanTrue); |
+ |
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
+ BuildImageConfig(CMVideoFormatDescriptionGetDimensions(format))); |
+ |
+ VTDecompressionOutputCallbackRecord callback = {0}; |
+ |
+ base::ScopedCFTypeRef<VTDecompressionSessionRef> session; |
+ status = VTDecompressionSessionCreate( |
+ kCFAllocatorDefault, |
+ format, // video_format_description |
+ decoder_config, // video_decoder_specification |
+ image_config, // destination_image_buffer_attributes |
+ &callback, // output_callback |
+ session.InitializeInto()); |
+ if (status) { |
+ OSSTATUS_LOG(ERROR, status) << "Failed to create VTDecompressionSession " |
+ << "while initializing VideoToolbox"; |
+ content_common_gpu_media::UninitializeVt(); |
+ return; |
+ } |
+} |
+ |
// Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
static void OutputThunk( |
void* decompression_output_refcon, |
@@ -112,27 +229,13 @@ bool VTVideoDecodeAccelerator::Initialize( |
DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
client_ = client; |
- // Only H.264 is supported. |
- if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
+ if (!IsVtInitialized()) |
return false; |
- // Require --no-sandbox until VideoToolbox library loading is part of sandbox |
- // startup (and this VDA is ready for regular users). |
- if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox)) |
+ // Only H.264 is supported. |
+ if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
return false; |
- if (!IsVtInitialized()) { |
- // CoreVideo is also required, but the loader stops after the first |
- // path is loaded. Instead we rely on the transitive dependency from |
- // VideoToolbox to CoreVideo. |
- // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox. |
- StubPathMap paths; |
- paths[kModuleVt].push_back(FILE_PATH_LITERAL( |
- "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); |
- if (!InitializeStubs(paths)) |
- return false; |
- } |
- |
// Spawn a thread to handle parsing and calling VideoToolbox. |
if (!decoder_thread_.Start()) |
return false; |
@@ -213,25 +316,7 @@ bool VTVideoDecodeAccelerator::ConfigureDecoder() { |
kCFBooleanTrue); |
base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
- CFDictionaryCreateMutable( |
- kCFAllocatorDefault, |
- 4, // capacity |
- &kCFTypeDictionaryKeyCallBacks, |
- &kCFTypeDictionaryValueCallBacks)); |
- |
-#define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) |
- // TODO(sandersd): RGBA option for 4:4:4 video. |
- int32_t pixel_format = kCVPixelFormatType_422YpCbCr8; |
- base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); |
- base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); |
- base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); |
-#undef CFINT |
- CFDictionarySetValue( |
- image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
- CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
- CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
- CFDictionarySetValue( |
- image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
+ BuildImageConfig(coded_dimensions)); |
// TODO(sandersd): Does the old session need to be flushed first? |
session_.reset(); |