OLD | NEW |
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 <CoreVideo/CoreVideo.h> |
| 6 #include <OpenGL/CGLIOSurface.h> |
| 7 |
| 8 #include "base/bind.h" |
| 9 #include "base/thread_task_runner_handle.h" |
5 #include "content/common/gpu/media/vt_video_decode_accelerator.h" | 10 #include "content/common/gpu/media/vt_video_decode_accelerator.h" |
| 11 #include "media/filters/h264_parser.h" |
| 12 |
| 13 using content_common_gpu_media::kModuleVt; |
| 14 using content_common_gpu_media::InitializeStubs; |
| 15 using content_common_gpu_media::IsVtInitialized; |
| 16 using content_common_gpu_media::StubPathMap; |
6 | 17 |
7 namespace content { | 18 namespace content { |
8 | 19 |
| 20 // Size of length headers prepended to NALUs in MPEG-4 framing. (1, 2, or 4.) |
| 21 static const int kNALUHeaderLength = 4; |
| 22 |
| 23 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
| 24 static void OutputThunk( |
| 25 void* decompression_output_refcon, |
| 26 void* source_frame_refcon, |
| 27 OSStatus status, |
| 28 VTDecodeInfoFlags info_flags, |
| 29 CVImageBufferRef image_buffer, |
| 30 CMTime presentation_time_stamp, |
| 31 CMTime presentation_duration) { |
| 32 VTVideoDecodeAccelerator* vda = |
| 33 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); |
| 34 int32_t* bitstream_id_ptr = reinterpret_cast<int32_t*>(source_frame_refcon); |
| 35 int32_t bitstream_id = *bitstream_id_ptr; |
| 36 delete bitstream_id_ptr; |
| 37 CFRetain(image_buffer); |
| 38 vda->Output(bitstream_id, status, info_flags, image_buffer); |
| 39 } |
| 40 |
9 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) | 41 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
10 : loop_proxy_(base::MessageLoopProxy::current()), | 42 : cgl_context_(cgl_context), |
11 cgl_context_(cgl_context), | |
12 client_(NULL), | 43 client_(NULL), |
| 44 decoder_thread_("VTDecoderThread"), |
| 45 format_(NULL), |
| 46 session_(NULL), |
13 weak_this_factory_(this) { | 47 weak_this_factory_(this) { |
| 48 callback_.decompressionOutputCallback = OutputThunk; |
| 49 callback_.decompressionOutputRefCon = this; |
14 } | 50 } |
15 | 51 |
16 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 52 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
17 } | 53 } |
18 | 54 |
19 bool VTVideoDecodeAccelerator::Initialize( | 55 bool VTVideoDecodeAccelerator::Initialize( |
20 media::VideoCodecProfile profile, | 56 media::VideoCodecProfile profile, |
21 Client* client) { | 57 Client* client) { |
22 DCHECK(CalledOnValidThread()); | 58 DCHECK(CalledOnValidThread()); |
23 DVLOG(2) << __FUNCTION__; | |
24 client_ = client; | 59 client_ = client; |
25 | 60 |
26 // Only H.264 is supported. | 61 // Only H.264 is supported. |
27 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) | 62 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
28 return false; | 63 return false; |
29 | 64 |
30 // Prevent anyone from using VTVideoDecoder for now. http://crbug.com/133828 | 65 // TODO(sandersd): Move VideoToolbox library loading to sandbox startup; |
31 return false; | 66 // until then, --no-sandbox is required. |
| 67 if (!IsVtInitialized()) { |
| 68 StubPathMap paths; |
| 69 // CoreVideo is also required, but the loader stops after the first |
| 70 // path is loaded. Instead we rely on the transitive dependency from |
| 71 // VideoToolbox to CoreVideo. |
| 72 // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox. |
| 73 paths[kModuleVt].push_back(FILE_PATH_LITERAL( |
| 74 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); |
| 75 if (!InitializeStubs(paths)) |
| 76 return false; |
| 77 } |
| 78 |
| 79 // Spawn a thread to handle parsing and calling VideoToolbox. |
| 80 if (!decoder_thread_.Start()) |
| 81 return false; |
| 82 |
| 83 // Note that --ignore-gpu-blacklist is still required to get here. |
| 84 return true; |
| 85 } |
| 86 |
| 87 // TODO(sandersd): Proper error reporting instead of CHECKs. |
| 88 void VTVideoDecodeAccelerator::ConfigureDecoder( |
| 89 const std::vector<const uint8_t*>& nalu_data_ptrs, |
| 90 const std::vector<size_t>& nalu_data_sizes) { |
| 91 format_.reset(); |
| 92 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( |
| 93 kCFAllocatorDefault, |
| 94 nalu_data_ptrs.size(), // parameter_set_count |
| 95 &nalu_data_ptrs.front(), // ¶meter_set_pointers |
| 96 &nalu_data_sizes.front(), // ¶meter_set_sizes |
| 97 kNALUHeaderLength, // nal_unit_header_length |
| 98 format_.InitializeInto() |
| 99 )); |
| 100 |
| 101 // TODO(sandersd): Check if the size has changed and handle picture requests. |
| 102 CMVideoDimensions coded_size = CMVideoFormatDescriptionGetDimensions(format_); |
| 103 coded_size_.SetSize(coded_size.width, coded_size.height); |
| 104 |
| 105 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( |
| 106 CFDictionaryCreateMutable( |
| 107 kCFAllocatorDefault, |
| 108 1, // capacity |
| 109 &kCFTypeDictionaryKeyCallBacks, |
| 110 &kCFTypeDictionaryValueCallBacks)); |
| 111 |
| 112 CFDictionarySetValue( |
| 113 decoder_config, |
| 114 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder |
| 115 CFSTR("EnableHardwareAcceleratedVideoDecoder"), |
| 116 kCFBooleanTrue); |
| 117 |
| 118 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
| 119 CFDictionaryCreateMutable( |
| 120 kCFAllocatorDefault, |
| 121 4, // capacity |
| 122 &kCFTypeDictionaryKeyCallBacks, |
| 123 &kCFTypeDictionaryValueCallBacks)); |
| 124 |
| 125 // TODO(sandersd): ARGB for video that is not 4:2:0. |
| 126 int32_t pixel_format = '2vuy'; |
| 127 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) |
| 128 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); |
| 129 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_size.width)); |
| 130 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_size.height)); |
| 131 #undef CFINT |
| 132 CFDictionarySetValue( |
| 133 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
| 134 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
| 135 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
| 136 CFDictionarySetValue( |
| 137 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
| 138 |
| 139 // TODO(sandersd): Skip if the session is compatible. |
| 140 // TODO(sandersd): Flush frames when resetting. |
| 141 session_.reset(); |
| 142 CHECK(!VTDecompressionSessionCreate( |
| 143 kCFAllocatorDefault, |
| 144 format_, // video_format_description |
| 145 decoder_config, // video_decoder_specification |
| 146 image_config, // destination_image_buffer_attributes |
| 147 &callback_, // output_callback |
| 148 session_.InitializeInto() |
| 149 )); |
| 150 DVLOG(2) << "Created VTDecompressionSession"; |
32 } | 151 } |
33 | 152 |
34 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 153 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
35 DCHECK(CalledOnValidThread()); | 154 DCHECK(CalledOnValidThread()); |
| 155 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
| 156 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), |
| 157 bitstream)); |
| 158 } |
| 159 |
| 160 void VTVideoDecodeAccelerator::DecodeTask( |
| 161 const media::BitstreamBuffer bitstream) { |
| 162 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 163 |
| 164 // Map the bitstream buffer. |
| 165 base::SharedMemory memory(bitstream.handle(), true); |
| 166 size_t size = bitstream.size(); |
| 167 CHECK(memory.Map(size)); |
| 168 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); |
| 169 |
| 170 // Locate relevant NALUs in the buffer. |
| 171 size_t data_size = 0; |
| 172 std::vector<media::H264NALU> nalus; |
| 173 std::vector<const uint8_t*> config_nalu_data_ptrs; |
| 174 std::vector<size_t> config_nalu_data_sizes; |
| 175 parser_.SetStream(buf, size); |
| 176 media::H264NALU nalu; |
| 177 while (true) { |
| 178 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); |
| 179 if (result == media::H264Parser::kEOStream) |
| 180 break; |
| 181 CHECK_EQ(result, media::H264Parser::kOk); |
| 182 if (nalu.nal_unit_type == media::H264NALU::kSPS || |
| 183 nalu.nal_unit_type == media::H264NALU::kPPS || |
| 184 nalu.nal_unit_type == media::H264NALU::kSPSExt) { |
| 185 config_nalu_data_ptrs.push_back(nalu.data); |
| 186 config_nalu_data_sizes.push_back(nalu.size); |
| 187 } |
| 188 nalus.push_back(nalu); |
| 189 // Each NALU will have a 4-byte length header prepended. |
| 190 data_size += kNALUHeaderLength + nalu.size; |
| 191 } |
| 192 |
| 193 if (!config_nalu_data_ptrs.empty()) |
| 194 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); |
| 195 |
| 196 // TODO(sandersd): Rewrite slice NALU headers and send for decoding. |
| 197 } |
| 198 |
| 199 // This method may be called on any VideoToolbox thread. |
| 200 void VTVideoDecodeAccelerator::Output( |
| 201 int32_t bitstream_id, |
| 202 OSStatus status, |
| 203 VTDecodeInfoFlags info_flags, |
| 204 CVImageBufferRef image_buffer) { |
| 205 // TODO(sandersd): Store the frame in a queue. |
| 206 CFRelease(image_buffer); |
36 } | 207 } |
37 | 208 |
38 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 209 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
39 const std::vector<media::PictureBuffer>& pictures) { | 210 const std::vector<media::PictureBuffer>& pictures) { |
40 DCHECK(CalledOnValidThread()); | 211 DCHECK(CalledOnValidThread()); |
41 } | 212 } |
42 | 213 |
43 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 214 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
44 DCHECK(CalledOnValidThread()); | 215 DCHECK(CalledOnValidThread()); |
45 } | 216 } |
46 | 217 |
47 void VTVideoDecodeAccelerator::Flush() { | 218 void VTVideoDecodeAccelerator::Flush() { |
48 DCHECK(CalledOnValidThread()); | 219 DCHECK(CalledOnValidThread()); |
| 220 // TODO(sandersd): Trigger flush, sending frames. |
49 } | 221 } |
50 | 222 |
51 void VTVideoDecodeAccelerator::Reset() { | 223 void VTVideoDecodeAccelerator::Reset() { |
52 DCHECK(CalledOnValidThread()); | 224 DCHECK(CalledOnValidThread()); |
| 225 // TODO(sandersd): Trigger flush, discarding frames. |
53 } | 226 } |
54 | 227 |
55 void VTVideoDecodeAccelerator::Destroy() { | 228 void VTVideoDecodeAccelerator::Destroy() { |
56 DCHECK(CalledOnValidThread()); | 229 DCHECK(CalledOnValidThread()); |
| 230 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. |
| 231 delete this; |
57 } | 232 } |
58 | 233 |
59 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 234 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
60 return false; | 235 return false; |
61 } | 236 } |
62 | 237 |
63 } // namespace content | 238 } // namespace content |
OLD | NEW |