Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: content/common/gpu/media/vt_video_decode_accelerator.cc

Issue 723993003: Sandbox initialization for VideoToolbox. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@vt_queue_frames
Patch Set: Rebase. Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/common/gpu/media/vt_video_decode_accelerator.h ('k') | content/common/sandbox_mac.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <algorithm> 5 #include <algorithm>
6 6
7 #include <CoreVideo/CoreVideo.h> 7 #include <CoreVideo/CoreVideo.h>
8 #include <OpenGL/CGLIOSurface.h> 8 #include <OpenGL/CGLIOSurface.h>
9 #include <OpenGL/gl.h> 9 #include <OpenGL/gl.h>
10 10
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/command_line.h" 12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/mac/mac_logging.h"
13 #include "base/sys_byteorder.h" 15 #include "base/sys_byteorder.h"
14 #include "base/thread_task_runner_handle.h" 16 #include "base/thread_task_runner_handle.h"
15 #include "content/common/gpu/media/vt_video_decode_accelerator.h" 17 #include "content/common/gpu/media/vt_video_decode_accelerator.h"
16 #include "content/public/common/content_switches.h" 18 #include "content/public/common/content_switches.h"
17 #include "media/base/limits.h" 19 #include "media/base/limits.h"
18 #include "ui/gl/scoped_binders.h" 20 #include "ui/gl/scoped_binders.h"
19 21
20 using content_common_gpu_media::kModuleVt; 22 using content_common_gpu_media::kModuleVt;
21 using content_common_gpu_media::InitializeStubs; 23 using content_common_gpu_media::InitializeStubs;
22 using content_common_gpu_media::IsVtInitialized; 24 using content_common_gpu_media::IsVtInitialized;
23 using content_common_gpu_media::StubPathMap; 25 using content_common_gpu_media::StubPathMap;
24 26
25 #define NOTIFY_STATUS(name, status) \ 27 #define NOTIFY_STATUS(name, status) \
26 do { \ 28 do { \
27 DLOG(ERROR) << name << " failed with status " << status; \ 29 OSSTATUS_DLOG(ERROR, status) << name; \
28 NotifyError(PLATFORM_FAILURE); \ 30 NotifyError(PLATFORM_FAILURE); \
29 } while (0) 31 } while (0)
30 32
31 namespace content { 33 namespace content {
32 34
33 // Size to use for NALU length headers in AVC format (can be 1, 2, or 4). 35 // Size to use for NALU length headers in AVC format (can be 1, 2, or 4).
34 static const int kNALUHeaderLength = 4; 36 static const int kNALUHeaderLength = 4;
35 37
36 // We request 5 picture buffers from the client, each of which has a texture ID 38 // We request 5 picture buffers from the client, each of which has a texture ID
37 // that we can bind decoded frames to. We need enough to satisfy preroll, and 39 // that we can bind decoded frames to. We need enough to satisfy preroll, and
38 // enough to avoid unnecessary stalling, but no more than that. The resource 40 // enough to avoid unnecessary stalling, but no more than that. The resource
39 // requirements are low, as we don't need the textures to be backed by storage. 41 // requirements are low, as we don't need the textures to be backed by storage.
40 static const int kNumPictureBuffers = media::limits::kMaxVideoFrames + 1; 42 static const int kNumPictureBuffers = media::limits::kMaxVideoFrames + 1;
41 43
42 // Maximum number of frames to queue for reordering before we stop asking for 44 // Maximum number of frames to queue for reordering before we stop asking for
43 // more. (NotifyEndOfBitstreamBuffer() is called when frames are moved into the 45 // more. (NotifyEndOfBitstreamBuffer() is called when frames are moved into the
44 // reorder queue.) 46 // reorder queue.)
45 static const int kMaxReorderQueueSize = 16; 47 static const int kMaxReorderQueueSize = 16;
46 48
49 // Build an |image_config| dictionary for VideoToolbox initialization.
50 static base::ScopedCFTypeRef<CFMutableDictionaryRef>
51 BuildImageConfig(CMVideoDimensions coded_dimensions) {
52 // TODO(sandersd): RGBA option for 4:4:4 video.
53 int32_t pixel_format = kCVPixelFormatType_422YpCbCr8;
54
55 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i)
56 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format));
57 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width));
58 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height));
59 #undef CFINT
60
61 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config(
62 CFDictionaryCreateMutable(
63 kCFAllocatorDefault,
64 4, // capacity
65 &kCFTypeDictionaryKeyCallBacks,
66 &kCFTypeDictionaryValueCallBacks));
67 CFDictionarySetValue(image_config, kCVPixelBufferPixelFormatTypeKey,
68 cf_pixel_format);
69 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width);
70 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height);
71 CFDictionarySetValue(image_config, kCVPixelBufferOpenGLCompatibilityKey,
72 kCFBooleanTrue);
73
74 return image_config;
75 }
76
77 // The purpose of this function is to preload the generic and hardware-specific
78 // libraries required by VideoToolbox before the GPU sandbox is enabled.
79 // VideoToolbox normally loads the hardware-specific libraries lazily, so we
80 // must actually create a decompression session.
81 //
82 // If creating a decompression session fails, hardware decoding will be disabled
83 // (Initialize() will always return false). If it succeeds but a required
84 // library is not loaded yet (I have not experienced this, but the details are
85 // not documented), then VideoToolbox will fall back on software decoding
86 // internally. If that happens, the likely solution is to expand the scope of
87 // this initialization.
88 void InitializeVideoToolbox() {
89 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
90 switches::kDisableAcceleratedVideoDecode)) {
91 return;
92 }
93
94 if (!IsVtInitialized()) {
95 // CoreVideo is also required, but the loader stops after the first path is
96 // loaded. Instead we rely on the transitive dependency from VideoToolbox to
97 // CoreVideo.
98 // TODO(sandersd): Fallback to PrivateFrameworks to support OS X < 10.8.
99 StubPathMap paths;
100 paths[kModuleVt].push_back(FILE_PATH_LITERAL(
101 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox"));
102 if (!InitializeStubs(paths))
103 return;
104 }
105
106 // Create a decoding session.
107 // SPS and PPS data were taken from the 480p encoding of Big Buck Bunny.
108 const uint8_t sps[] = {0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x80, 0xd4, 0x3d,
109 0xa1, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03,
110 0x00, 0x30, 0x8f, 0x16, 0x2d, 0x9a};
111 const uint8_t pps[] = {0x68, 0xe9, 0x7b, 0xcb};
112 const uint8_t* data_ptrs[] = {sps, pps};
113 const size_t data_sizes[] = {arraysize(sps), arraysize(pps)};
114
115 base::ScopedCFTypeRef<CMFormatDescriptionRef> format;
116 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
117 kCFAllocatorDefault,
118 2, // parameter_set_count
119 data_ptrs, // &parameter_set_pointers
120 data_sizes, // &parameter_set_sizes
121 kNALUHeaderLength, // nal_unit_header_length
122 format.InitializeInto());
123 if (status) {
124 OSSTATUS_LOG(ERROR, status) << "Failed to create CMVideoFormatDescription "
125 << "while initializing VideoToolbox";
126 content_common_gpu_media::UninitializeVt();
127 return;
128 }
129
130 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config(
131 CFDictionaryCreateMutable(
132 kCFAllocatorDefault,
133 1, // capacity
134 &kCFTypeDictionaryKeyCallBacks,
135 &kCFTypeDictionaryValueCallBacks));
136
137 CFDictionarySetValue(
138 decoder_config,
139 // kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder
140 CFSTR("RequireHardwareAcceleratedVideoDecoder"),
141 kCFBooleanTrue);
142
143 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config(
144 BuildImageConfig(CMVideoFormatDescriptionGetDimensions(format)));
145
146 VTDecompressionOutputCallbackRecord callback = {0};
147
148 base::ScopedCFTypeRef<VTDecompressionSessionRef> session;
149 status = VTDecompressionSessionCreate(
150 kCFAllocatorDefault,
151 format, // video_format_description
152 decoder_config, // video_decoder_specification
153 image_config, // destination_image_buffer_attributes
154 &callback, // output_callback
155 session.InitializeInto());
156 if (status) {
157 OSSTATUS_LOG(ERROR, status) << "Failed to create VTDecompressionSession "
158 << "while initializing VideoToolbox";
159 content_common_gpu_media::UninitializeVt();
160 return;
161 }
162 }
163
47 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. 164 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator.
48 static void OutputThunk( 165 static void OutputThunk(
49 void* decompression_output_refcon, 166 void* decompression_output_refcon,
50 void* source_frame_refcon, 167 void* source_frame_refcon,
51 OSStatus status, 168 OSStatus status,
52 VTDecodeInfoFlags info_flags, 169 VTDecodeInfoFlags info_flags,
53 CVImageBufferRef image_buffer, 170 CVImageBufferRef image_buffer,
54 CMTime presentation_time_stamp, 171 CMTime presentation_time_stamp,
55 CMTime presentation_duration) { 172 CMTime presentation_duration) {
56 VTVideoDecodeAccelerator* vda = 173 VTVideoDecodeAccelerator* vda =
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
105 222
106 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { 223 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() {
107 } 224 }
108 225
109 bool VTVideoDecodeAccelerator::Initialize( 226 bool VTVideoDecodeAccelerator::Initialize(
110 media::VideoCodecProfile profile, 227 media::VideoCodecProfile profile,
111 Client* client) { 228 Client* client) {
112 DCHECK(gpu_thread_checker_.CalledOnValidThread()); 229 DCHECK(gpu_thread_checker_.CalledOnValidThread());
113 client_ = client; 230 client_ = client;
114 231
232 if (!IsVtInitialized())
233 return false;
234
115 // Only H.264 is supported. 235 // Only H.264 is supported.
116 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) 236 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX)
117 return false; 237 return false;
118 238
119 // Require --no-sandbox until VideoToolbox library loading is part of sandbox
120 // startup (and this VDA is ready for regular users).
121 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox))
122 return false;
123
124 if (!IsVtInitialized()) {
125 // CoreVideo is also required, but the loader stops after the first
126 // path is loaded. Instead we rely on the transitive dependency from
127 // VideoToolbox to CoreVideo.
128 // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox.
129 StubPathMap paths;
130 paths[kModuleVt].push_back(FILE_PATH_LITERAL(
131 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox"));
132 if (!InitializeStubs(paths))
133 return false;
134 }
135
136 // Spawn a thread to handle parsing and calling VideoToolbox. 239 // Spawn a thread to handle parsing and calling VideoToolbox.
137 if (!decoder_thread_.Start()) 240 if (!decoder_thread_.Start())
138 return false; 241 return false;
139 242
140 return true; 243 return true;
141 } 244 }
142 245
143 bool VTVideoDecodeAccelerator::FinishDelayedFrames() { 246 bool VTVideoDecodeAccelerator::FinishDelayedFrames() {
144 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); 247 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread());
145 if (session_) { 248 if (session_) {
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
206 &kCFTypeDictionaryKeyCallBacks, 309 &kCFTypeDictionaryKeyCallBacks,
207 &kCFTypeDictionaryValueCallBacks)); 310 &kCFTypeDictionaryValueCallBacks));
208 311
209 CFDictionarySetValue( 312 CFDictionarySetValue(
210 decoder_config, 313 decoder_config,
211 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder 314 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder
212 CFSTR("EnableHardwareAcceleratedVideoDecoder"), 315 CFSTR("EnableHardwareAcceleratedVideoDecoder"),
213 kCFBooleanTrue); 316 kCFBooleanTrue);
214 317
215 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( 318 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config(
216 CFDictionaryCreateMutable( 319 BuildImageConfig(coded_dimensions));
217 kCFAllocatorDefault,
218 4, // capacity
219 &kCFTypeDictionaryKeyCallBacks,
220 &kCFTypeDictionaryValueCallBacks));
221
222 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i)
223 // TODO(sandersd): RGBA option for 4:4:4 video.
224 int32_t pixel_format = kCVPixelFormatType_422YpCbCr8;
225 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format));
226 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width));
227 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height));
228 #undef CFINT
229 CFDictionarySetValue(
230 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format);
231 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width);
232 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height);
233 CFDictionarySetValue(
234 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue);
235 320
236 // TODO(sandersd): Does the old session need to be flushed first? 321 // TODO(sandersd): Does the old session need to be flushed first?
237 session_.reset(); 322 session_.reset();
238 status = VTDecompressionSessionCreate( 323 status = VTDecompressionSessionCreate(
239 kCFAllocatorDefault, 324 kCFAllocatorDefault,
240 format_, // video_format_description 325 format_, // video_format_description
241 decoder_config, // video_decoder_specification 326 decoder_config, // video_decoder_specification
242 image_config, // destination_image_buffer_attributes 327 image_config, // destination_image_buffer_attributes
243 &callback_, // output_callback 328 &callback_, // output_callback
244 session_.InitializeInto()); 329 session_.InitializeInto());
(...skipping 555 matching lines...) Expand 10 before | Expand all | Expand 10 after
800 assigned_bitstream_ids_.clear(); 885 assigned_bitstream_ids_.clear();
801 state_ = STATE_DESTROYING; 886 state_ = STATE_DESTROYING;
802 QueueFlush(TASK_DESTROY); 887 QueueFlush(TASK_DESTROY);
803 } 888 }
804 889
805 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { 890 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() {
806 return false; 891 return false;
807 } 892 }
808 893
809 } // namespace content 894 } // namespace content
OLDNEW
« no previous file with comments | « content/common/gpu/media/vt_video_decode_accelerator.h ('k') | content/common/sandbox_mac.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698