Chromium Code Reviews| 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 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | |
| 21 static void OutputThunk( | |
| 22 void* decompression_output_refcon, | |
| 23 void* source_frame_refcon, | |
| 24 OSStatus status, | |
| 25 VTDecodeInfoFlags info_flags, | |
| 26 CVImageBufferRef image_buffer, | |
| 27 CMTime presentation_time_stamp, | |
| 28 CMTime presentation_duration) { | |
| 29 VTVideoDecodeAccelerator* vda = | |
| 30 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); | |
| 31 int32_t* bitstream_id_ptr = reinterpret_cast<int32_t*>(source_frame_refcon); | |
| 32 int32_t bitstream_id = *bitstream_id_ptr; | |
| 33 delete bitstream_id_ptr; | |
| 34 CFRetain(image_buffer); | |
| 35 vda->Output(bitstream_id, status, info_flags, image_buffer); | |
| 36 } | |
| 37 | |
| 9 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) | 38 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
| 10 : loop_proxy_(base::MessageLoopProxy::current()), | 39 : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 11 cgl_context_(cgl_context), | 40 cgl_context_(cgl_context), |
| 12 client_(NULL), | 41 client_(NULL), |
| 13 weak_this_factory_(this) { | 42 format_(NULL), |
| 43 session_(NULL), | |
| 44 coded_width_(0), | |
| 45 coded_height_(0), | |
| 46 weak_factory_(this) { | |
| 47 callback_.decompressionOutputCallback = OutputThunk; | |
| 48 callback_.decompressionOutputRefCon = this; | |
| 14 } | 49 } |
| 15 | 50 |
| 16 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 51 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
| 52 // TODO(sandersd): Move these into scoped pointers. | |
| 53 if (format_) | |
| 54 CFRelease(format_); | |
| 55 if (session_) | |
| 56 CFRelease(session_); | |
| 17 } | 57 } |
| 18 | 58 |
| 19 bool VTVideoDecodeAccelerator::Initialize( | 59 bool VTVideoDecodeAccelerator::Initialize( |
| 20 media::VideoCodecProfile profile, | 60 media::VideoCodecProfile profile, |
| 21 Client* client) { | 61 Client* client) { |
| 22 DCHECK(CalledOnValidThread()); | 62 DCHECK(CalledOnValidThread()); |
| 23 DVLOG(2) << __FUNCTION__; | |
| 24 client_ = client; | 63 client_ = client; |
| 25 | 64 |
| 26 // Only H.264 is supported. | 65 // Only H.264 is supported. |
| 27 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) | 66 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
| 28 return false; | 67 return false; |
| 29 | 68 |
| 30 // Prevent anyone from using VTVideoDecoder for now. http://crbug.com/133828 | 69 // TODO(sandersd): Move VideoToolbox library loading to sandbox startup; |
| 31 return false; | 70 // until then, --no-sandbox is required. |
| 71 if (!IsVtInitialized()) { | |
| 72 StubPathMap paths; | |
| 73 // CoreVideo is also required, but the loader stops after the first | |
| 74 // path is loaded. Instead we rely on the transitive dependency from | |
| 75 // VideoToolbox to CoreVideo. | |
| 76 // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox. | |
| 77 paths[kModuleVt].push_back(FILE_PATH_LITERAL( | |
| 78 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); | |
| 79 if (!InitializeStubs(paths)) | |
| 80 return false; | |
| 81 } | |
| 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 if (format_ != NULL) { | |
| 92 CFRelease(format_); | |
| 93 format_ = NULL; | |
| 94 } | |
| 95 | |
| 96 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( | |
| 97 kCFAllocatorDefault, | |
| 98 nalu_data_ptrs.size(), // parameter_set_count | |
| 99 &nalu_data_ptrs.front(), // ¶meter_set_pointers | |
| 100 &nalu_data_sizes.front(), // ¶meter_set_sizes | |
| 101 4, // nal_unit_header_length | |
| 102 &format_ | |
| 103 )); | |
| 104 | |
| 105 // TODO(sandersd): Check if the size has changed and handle picture requests. | |
| 106 CMVideoDimensions coded_size = CMVideoFormatDescriptionGetDimensions(format_); | |
| 107 coded_width_ = coded_size.width; | |
| 108 coded_height_ = coded_size.height; | |
| 109 | |
| 110 // TODO(sandersd): Scoped pointers. | |
| 111 CFMutableDictionaryRef decoder_config = CFDictionaryCreateMutable( | |
| 112 kCFAllocatorDefault, | |
| 113 1, // capacity | |
| 114 &kCFTypeDictionaryKeyCallBacks, | |
| 115 &kCFTypeDictionaryValueCallBacks); | |
| 116 | |
| 117 CFDictionarySetValue( | |
| 118 decoder_config, | |
| 119 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder | |
| 120 CFSTR("EnableHardwareAcceleratedVideoDecoder"), | |
| 121 kCFBooleanTrue); | |
| 122 | |
| 123 CFMutableDictionaryRef image_config = CFDictionaryCreateMutable( | |
| 124 kCFAllocatorDefault, | |
| 125 4, // capacity | |
| 126 &kCFTypeDictionaryKeyCallBacks, | |
| 127 &kCFTypeDictionaryValueCallBacks); | |
| 128 | |
| 129 // TODO(sandersd): ARGB for video that is not 4:2:0. | |
| 130 int32_t pixel_format = '2vuy'; | |
| 131 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | |
| 132 CFNumberRef cf_pixel_format = CFINT(pixel_format); | |
| 133 CFNumberRef cf_width = CFINT(coded_width_); | |
| 134 CFNumberRef cf_height = CFINT(coded_height_); | |
| 135 CFDictionarySetValue( | |
| 136 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | |
| 137 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | |
| 138 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | |
| 139 CFDictionarySetValue( | |
| 140 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | |
| 141 CFRelease(cf_pixel_format); | |
| 142 CFRelease(cf_width); | |
| 143 CFRelease(cf_height); | |
| 144 | |
| 145 if (session_ != NULL) { | |
| 146 // TODO(sandersd): Check if the session is compatable first. | |
| 147 // TODO(sandersd): Flush frames. | |
| 148 CFRelease(session_); | |
| 149 session_ = NULL; | |
| 150 } | |
| 151 | |
| 152 CHECK(!VTDecompressionSessionCreate( | |
| 153 kCFAllocatorDefault, | |
| 154 format_, // video_format_description | |
| 155 decoder_config, // video_decoder_specification | |
| 156 image_config, // destination_image_buffer_attributes | |
| 157 &callback_, // output_callback | |
| 158 &session_ | |
| 159 )); | |
| 160 | |
| 161 CFRelease(decoder_config); | |
| 162 CFRelease(image_config); | |
| 32 } | 163 } |
| 33 | 164 |
| 34 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 165 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
| 35 DCHECK(CalledOnValidThread()); | 166 DCHECK(CalledOnValidThread()); |
| 167 | |
| 168 // Map the bitstream buffer. | |
| 169 base::SharedMemory memory(bitstream.handle(), true); | |
| 170 size_t size = bitstream.size(); | |
| 171 CHECK(memory.Map(size)); | |
| 172 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); | |
|
Pawel Osciak
2014/07/09 00:56:05
Please try to do as little as possible in Decode()
sandersd (OOO until July 31)
2014/07/09 02:16:01
Done. I've moved this into a decoder thread, simil
| |
| 173 | |
| 174 // Locate relevant NALUs in the buffer. | |
| 175 size_t slice_data_size = 0; | |
| 176 std::vector<media::H264NALU> slice_nalus; | |
|
Pawel Osciak
2014/07/09 00:56:05
Is this to store the slice nalus that come before
sandersd (OOO until July 31)
2014/07/09 02:16:01
No, it's an implementation to take any that are at
| |
| 177 std::vector<const uint8_t*> config_nalu_data_ptrs; | |
| 178 std::vector<size_t> config_nalu_data_sizes; | |
| 179 parser_.SetStream(buf, size); | |
| 180 media::H264NALU nalu; | |
| 181 while (true) { | |
| 182 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); | |
| 183 if (result == media::H264Parser::kEOStream) | |
| 184 break; | |
| 185 CHECK(result == media::H264Parser::kOk); | |
|
Pawel Osciak
2014/07/09 00:56:05
It's better not to crash the GPU process on a inva
sandersd (OOO until July 31)
2014/07/09 02:16:01
Indeed, but I was hoping to keep the boilerplate l
| |
| 186 if (nalu.nal_unit_type >= 1 && nalu.nal_unit_type <= 5) { | |
|
Pawel Osciak
2014/07/09 00:56:05
Please use nalu type defines instead of hardcoded
sandersd (OOO until July 31)
2014/07/09 02:16:01
Done. Changed to accept all types, as that is clos
| |
| 187 slice_nalus.push_back(nalu); | |
| 188 slice_data_size += 4 + nalu.size; | |
| 189 } else if (nalu.nal_unit_type == media::H264NALU::kSPS || | |
| 190 nalu.nal_unit_type == media::H264NALU::kPPS || | |
| 191 nalu.nal_unit_type == media::H264NALU::kSPSExt) { | |
| 192 config_nalu_data_ptrs.push_back(nalu.data); | |
| 193 config_nalu_data_sizes.push_back(nalu.size); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 if (!config_nalu_data_ptrs.empty()) | |
| 198 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); | |
| 199 | |
| 200 // TODO(sandersd): Rewrite slice NALU headers and send for decoding. | |
| 201 } | |
| 202 | |
| 203 // This method may be called on any VideoToolbox thread. | |
| 204 void VTVideoDecodeAccelerator::Output( | |
| 205 int32_t bitstream_id, | |
| 206 OSStatus status, | |
| 207 VTDecodeInfoFlags info_flags, | |
| 208 CVImageBufferRef image_buffer) { | |
| 209 // TODO(sandersd): Store the frame in a queue. | |
| 210 CFRelease(image_buffer); | |
| 36 } | 211 } |
| 37 | 212 |
| 38 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 213 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
| 39 const std::vector<media::PictureBuffer>& pictures) { | 214 const std::vector<media::PictureBuffer>& pictures) { |
| 40 DCHECK(CalledOnValidThread()); | 215 DCHECK(CalledOnValidThread()); |
| 41 } | 216 } |
| 42 | 217 |
| 43 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 218 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
| 44 DCHECK(CalledOnValidThread()); | 219 DCHECK(CalledOnValidThread()); |
| 45 } | 220 } |
| 46 | 221 |
| 47 void VTVideoDecodeAccelerator::Flush() { | 222 void VTVideoDecodeAccelerator::Flush() { |
| 48 DCHECK(CalledOnValidThread()); | 223 DCHECK(CalledOnValidThread()); |
| 224 // TODO(sandersd): Trigger flush, sending frames. | |
| 49 } | 225 } |
| 50 | 226 |
| 51 void VTVideoDecodeAccelerator::Reset() { | 227 void VTVideoDecodeAccelerator::Reset() { |
| 52 DCHECK(CalledOnValidThread()); | 228 DCHECK(CalledOnValidThread()); |
| 229 // TODO(sandersd): Trigger flush, discarding frames. | |
| 53 } | 230 } |
| 54 | 231 |
| 55 void VTVideoDecodeAccelerator::Destroy() { | 232 void VTVideoDecodeAccelerator::Destroy() { |
| 56 DCHECK(CalledOnValidThread()); | 233 DCHECK(CalledOnValidThread()); |
| 234 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. | |
| 235 delete this; | |
| 57 } | 236 } |
| 58 | 237 |
| 59 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 238 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
| 60 return false; | 239 return false; |
| 61 } | 240 } |
| 62 | 241 |
| 63 } // namespace content | 242 } // namespace content |
| OLD | NEW |