Chromium Code Reviews| 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 59251d20465c7249396029833cf575007046efbd..ab6e31f9722d20503f05cbf7763a39f588656f06 100644 |
| --- a/content/common/gpu/media/vt_video_decode_accelerator.cc |
| +++ b/content/common/gpu/media/vt_video_decode_accelerator.cc |
| @@ -2,37 +2,212 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include <CoreVideo/CoreVideo.h> |
| +#include <OpenGL/CGLIOSurface.h> |
| + |
| +#include "base/bind.h" |
| +#include "base/thread_task_runner_handle.h" |
| #include "content/common/gpu/media/vt_video_decode_accelerator.h" |
| +#include "media/filters/h264_parser.h" |
| + |
| +using content_common_gpu_media::kModuleVt; |
| +using content_common_gpu_media::InitializeStubs; |
| +using content_common_gpu_media::IsVtInitialized; |
| +using content_common_gpu_media::StubPathMap; |
| namespace content { |
| +// Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
| +static void OutputThunk( |
| + void* decompression_output_refcon, |
| + void* source_frame_refcon, |
| + OSStatus status, |
| + VTDecodeInfoFlags info_flags, |
| + CVImageBufferRef image_buffer, |
| + CMTime presentation_time_stamp, |
| + CMTime presentation_duration) { |
| + VTVideoDecodeAccelerator* vda = |
| + reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); |
| + int32_t* bitstream_id_ptr = reinterpret_cast<int32_t*>(source_frame_refcon); |
| + int32_t bitstream_id = *bitstream_id_ptr; |
| + delete bitstream_id_ptr; |
| + CFRetain(image_buffer); |
| + vda->Output(bitstream_id, status, info_flags, image_buffer); |
| +} |
| + |
| VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
| - : loop_proxy_(base::MessageLoopProxy::current()), |
| + : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| cgl_context_(cgl_context), |
| client_(NULL), |
| - weak_this_factory_(this) { |
| + format_(NULL), |
| + session_(NULL), |
| + coded_width_(0), |
| + coded_height_(0), |
| + weak_factory_(this) { |
| + callback_.decompressionOutputCallback = OutputThunk; |
| + callback_.decompressionOutputRefCon = this; |
| } |
| VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
| + // TODO(sandersd): Move these into scoped pointers. |
| + if (format_) |
| + CFRelease(format_); |
| + if (session_) |
| + CFRelease(session_); |
| } |
| bool VTVideoDecodeAccelerator::Initialize( |
| media::VideoCodecProfile profile, |
| Client* client) { |
| DCHECK(CalledOnValidThread()); |
| - DVLOG(2) << __FUNCTION__; |
| client_ = client; |
| // Only H.264 is supported. |
| if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
| return false; |
| - // Prevent anyone from using VTVideoDecoder for now. http://crbug.com/133828 |
| - return false; |
| + // TODO(sandersd): Move VideoToolbox library loading to sandbox startup; |
| + // until then, --no-sandbox is required. |
| + if (!IsVtInitialized()) { |
| + StubPathMap paths; |
| + // 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. |
| + paths[kModuleVt].push_back(FILE_PATH_LITERAL( |
| + "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); |
| + if (!InitializeStubs(paths)) |
| + return false; |
| + } |
| + |
| + // Note that --ignore-gpu-blacklist is still required to get here. |
| + return true; |
| +} |
| + |
| +// TODO(sandersd): Proper error reporting instead of CHECKs. |
| +void VTVideoDecodeAccelerator::ConfigureDecoder( |
| + const std::vector<const uint8_t*>& nalu_data_ptrs, |
| + const std::vector<size_t>& nalu_data_sizes) { |
| + if (format_ != NULL) { |
| + CFRelease(format_); |
| + format_ = NULL; |
| + } |
| + |
| + CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( |
| + kCFAllocatorDefault, |
| + nalu_data_ptrs.size(), // parameter_set_count |
| + &nalu_data_ptrs.front(), // ¶meter_set_pointers |
| + &nalu_data_sizes.front(), // ¶meter_set_sizes |
| + 4, // nal_unit_header_length |
| + &format_ |
| + )); |
| + |
| + // TODO(sandersd): Check if the size has changed and handle picture requests. |
| + CMVideoDimensions coded_size = CMVideoFormatDescriptionGetDimensions(format_); |
| + coded_width_ = coded_size.width; |
| + coded_height_ = coded_size.height; |
| + |
| + // TODO(sandersd): Scoped pointers. |
| + CFMutableDictionaryRef decoder_config = CFDictionaryCreateMutable( |
| + kCFAllocatorDefault, |
| + 1, // capacity |
| + &kCFTypeDictionaryKeyCallBacks, |
| + &kCFTypeDictionaryValueCallBacks); |
| + |
| + CFDictionarySetValue( |
| + decoder_config, |
| + // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder |
| + CFSTR("EnableHardwareAcceleratedVideoDecoder"), |
| + kCFBooleanTrue); |
| + |
| + CFMutableDictionaryRef image_config = CFDictionaryCreateMutable( |
| + kCFAllocatorDefault, |
| + 4, // capacity |
| + &kCFTypeDictionaryKeyCallBacks, |
| + &kCFTypeDictionaryValueCallBacks); |
| + |
| + // TODO(sandersd): ARGB for video that is not 4:2:0. |
| + int32_t pixel_format = '2vuy'; |
| +#define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) |
| + CFNumberRef cf_pixel_format = CFINT(pixel_format); |
| + CFNumberRef cf_width = CFINT(coded_width_); |
| + CFNumberRef cf_height = CFINT(coded_height_); |
| + CFDictionarySetValue( |
| + image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
| + CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
| + CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
| + CFDictionarySetValue( |
| + image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
| + CFRelease(cf_pixel_format); |
| + CFRelease(cf_width); |
| + CFRelease(cf_height); |
| + |
| + if (session_ != NULL) { |
| + // TODO(sandersd): Check if the session is compatable first. |
| + // TODO(sandersd): Flush frames. |
| + CFRelease(session_); |
| + session_ = NULL; |
| + } |
| + |
| + CHECK(!VTDecompressionSessionCreate( |
| + kCFAllocatorDefault, |
| + format_, // video_format_description |
| + decoder_config, // video_decoder_specification |
| + image_config, // destination_image_buffer_attributes |
| + &callback_, // output_callback |
| + &session_ |
| + )); |
| + |
| + CFRelease(decoder_config); |
| + CFRelease(image_config); |
| } |
| void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
| DCHECK(CalledOnValidThread()); |
| + |
| + // Map the bitstream buffer. |
| + base::SharedMemory memory(bitstream.handle(), true); |
| + size_t size = bitstream.size(); |
| + CHECK(memory.Map(size)); |
| + 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
|
| + |
| + // Locate relevant NALUs in the buffer. |
| + size_t slice_data_size = 0; |
| + 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
|
| + std::vector<const uint8_t*> config_nalu_data_ptrs; |
| + std::vector<size_t> config_nalu_data_sizes; |
| + parser_.SetStream(buf, size); |
| + media::H264NALU nalu; |
| + while (true) { |
| + media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); |
| + if (result == media::H264Parser::kEOStream) |
| + break; |
| + 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
|
| + 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
|
| + slice_nalus.push_back(nalu); |
| + slice_data_size += 4 + nalu.size; |
| + } else if (nalu.nal_unit_type == media::H264NALU::kSPS || |
| + nalu.nal_unit_type == media::H264NALU::kPPS || |
| + nalu.nal_unit_type == media::H264NALU::kSPSExt) { |
| + config_nalu_data_ptrs.push_back(nalu.data); |
| + config_nalu_data_sizes.push_back(nalu.size); |
| + } |
| + } |
| + |
| + if (!config_nalu_data_ptrs.empty()) |
| + ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); |
| + |
| + // TODO(sandersd): Rewrite slice NALU headers and send for decoding. |
| +} |
| + |
| +// This method may be called on any VideoToolbox thread. |
| +void VTVideoDecodeAccelerator::Output( |
| + int32_t bitstream_id, |
| + OSStatus status, |
| + VTDecodeInfoFlags info_flags, |
| + CVImageBufferRef image_buffer) { |
| + // TODO(sandersd): Store the frame in a queue. |
| + CFRelease(image_buffer); |
| } |
| void VTVideoDecodeAccelerator::AssignPictureBuffers( |
| @@ -46,14 +221,18 @@ void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
| void VTVideoDecodeAccelerator::Flush() { |
| DCHECK(CalledOnValidThread()); |
| + // TODO(sandersd): Trigger flush, sending frames. |
| } |
| void VTVideoDecodeAccelerator::Reset() { |
| DCHECK(CalledOnValidThread()); |
| + // TODO(sandersd): Trigger flush, discarding frames. |
| } |
| void VTVideoDecodeAccelerator::Destroy() { |
| DCHECK(CalledOnValidThread()); |
| + // TODO(sandersd): Trigger flush, discarding frames, and wait for them. |
| + delete this; |
| } |
| bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |