Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / ui / gfx / video_decode_acceleration_support_mac.mm
blob4cc2045ed7ad940667c8d774602f63ae8e707168
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/gfx/video_decode_acceleration_support_mac.h"
7 #include <dlfcn.h>
9 #include "base/bind.h"
10 #include "base/files/file_path.h"
11 #include "base/location.h"
12 #import "base/mac/foundation_util.h"
13 #include "base/mac/scoped_nsautorelease_pool.h"
14 #include "base/native_library.h"
16 using base::mac::CFToNSCast;
17 using base::mac::NSToCFCast;
19 const CFStringRef kVDADecoderConfiguration_Width = CFSTR("width");
20 const CFStringRef kVDADecoderConfiguration_Height = CFSTR("height");
21 const CFStringRef kVDADecoderConfiguration_SourceFormat = CFSTR("format");
22 const CFStringRef kVDADecoderConfiguration_avcCData = CFSTR("avcC");
23 const CFStringRef kCVPixelBufferIOSurfacePropertiesKey =
24     CFSTR("IOSurfaceProperties");
26 namespace {
28 // These are 10.6.3 APIs that we look up at run time.
29 typedef OSStatus (*VDADecoderCreateFunc)(
30     CFDictionaryRef decoderConfiguration,
31     CFDictionaryRef destinationImageBufferAttributes,
32     VDADecoderOutputCallback* outputCallback,
33     void* decoderOutputCallbackRefcon,
34     VDADecoder* decoderOut);
36 typedef OSStatus (*VDADecoderDecodeFunc)(
37     VDADecoder decoder,
38     uint32_t decodeFlags,
39     CFTypeRef compressedBuffer,
40     CFDictionaryRef frameInfo);
42 typedef OSStatus (*VDADecoderFlushFunc)(
43     VDADecoder decoder,
44     uint32_t flushFlags);
46 typedef OSStatus (*VDADecoderDestroyFunc)(
47     VDADecoder decoder);
49 VDADecoderCreateFunc g_VDADecoderCreate;
50 VDADecoderDecodeFunc g_VDADecoderDecode;
51 VDADecoderFlushFunc g_VDADecoderFlush;
52 VDADecoderDestroyFunc g_VDADecoderDestroy;
54 NSString* const kFrameIdKey = @"frame_id";
56 bool InitializeVdaApis() {
57   static bool should_initialize = true;
58   if (should_initialize) {
59     should_initialize = false;
60     base::FilePath path;
61     if (!base::mac::GetSearchPathDirectory(
62             NSLibraryDirectory, NSSystemDomainMask, &path)) {
63       return false;
64     }
66     path  = path.AppendASCII("Frameworks")
67                 .AppendASCII("VideoDecodeAcceleration.framework")
68                 .AppendASCII("VideoDecodeAcceleration");
69     base::NativeLibrary vda_library = base::LoadNativeLibrary(path, NULL);
70     if (!vda_library)
71       return false;
73     g_VDADecoderCreate = reinterpret_cast<VDADecoderCreateFunc>(
74         base::GetFunctionPointerFromNativeLibrary(
75             vda_library, "VDADecoderCreate"));
76     g_VDADecoderDecode = reinterpret_cast<VDADecoderDecodeFunc>(
77         base::GetFunctionPointerFromNativeLibrary(
78             vda_library, "VDADecoderDecode"));
79     g_VDADecoderFlush = reinterpret_cast<VDADecoderFlushFunc>(
80         base::GetFunctionPointerFromNativeLibrary(
81             vda_library, "VDADecoderFlush"));
82     g_VDADecoderDestroy = reinterpret_cast<VDADecoderDestroyFunc>(
83         base::GetFunctionPointerFromNativeLibrary(
84             vda_library, "VDADecoderDestroy"));
85   }
87   return g_VDADecoderCreate && g_VDADecoderDecode && g_VDADecoderFlush &&
88          g_VDADecoderDestroy;
91 }  // namespace
93 namespace gfx {
95 VideoDecodeAccelerationSupport::VideoDecodeAccelerationSupport()
96     : decoder_(NULL),
97       frame_id_count_(0),
98       loop_(base::MessageLoopProxy::current()) {
101 VideoDecodeAccelerationSupport::Status VideoDecodeAccelerationSupport::Create(
102     int width,
103     int height,
104     int pixel_format,
105     const void* avc_bytes,
106     size_t avc_size) {
107   if (!InitializeVdaApis())
108     return LOAD_FRAMEWORK_ERROR;
110   NSData* avc_data = [NSData dataWithBytes:avc_bytes length:avc_size];
111   NSDictionary* config = [NSDictionary dictionaryWithObjectsAndKeys:
112       [NSNumber numberWithInt:width],
113       CFToNSCast(kVDADecoderConfiguration_Width),
114       [NSNumber numberWithInt:height],
115       CFToNSCast(kVDADecoderConfiguration_Height),
116       [NSNumber numberWithInt:'avc1'],
117       CFToNSCast(kVDADecoderConfiguration_SourceFormat),
118       avc_data,
119       CFToNSCast(kVDADecoderConfiguration_avcCData),
120       nil];
122   NSDictionary* format_info = [NSDictionary dictionaryWithObjectsAndKeys:
123       [NSNumber numberWithInt:pixel_format],
124       CFToNSCast(kCVPixelBufferPixelFormatTypeKey),
125       // Must set to an empty dictionary as per documentation.
126       [NSDictionary dictionary],
127       CFToNSCast(kCVPixelBufferIOSurfacePropertiesKey),
128       nil];
130   OSStatus status = g_VDADecoderCreate(NSToCFCast(config),
131       NSToCFCast(format_info),
132       reinterpret_cast<VDADecoderOutputCallback*>(OnFrameReadyCallback), this,
133       &decoder_);
134   switch (status) {
135     case kVDADecoderNoErr:
136       return SUCCESS;
137     case kVDADecoderHardwareNotSupportedErr:
138       return HARDWARE_NOT_SUPPORTED_ERROR;
139     default:
140       return OTHER_ERROR;
141   }
144 VideoDecodeAccelerationSupport::Status VideoDecodeAccelerationSupport::Decode(
145     const void* bytes,
146     size_t size,
147     const FrameReadyCallback& cb) {
148   DCHECK(decoder_);
149   ++frame_id_count_;
150   frame_ready_callbacks_[frame_id_count_] = cb;
152   NSData* data = [NSData dataWithBytes:bytes length:size];
153   NSDictionary* frame_info = [NSDictionary
154       dictionaryWithObject:[NSNumber numberWithInt:frame_id_count_]
155                     forKey:kFrameIdKey];
157   OSStatus status = g_VDADecoderDecode(decoder_, 0, NSToCFCast(data),
158       NSToCFCast(frame_info));
159   if (status != kVDADecoderNoErr) {
160     frame_ready_callbacks_.erase(frame_id_count_);
161     return OTHER_ERROR;
162   }
163   return SUCCESS;
166 VideoDecodeAccelerationSupport::Status VideoDecodeAccelerationSupport::Flush(
167     bool emit_frames) {
168   DCHECK(decoder_);
169   OSStatus status = g_VDADecoderFlush(
170       decoder_, emit_frames ? kVDADecoderFlush_EmitFrames : 0);
171   if (!emit_frames)
172     frame_ready_callbacks_.clear();
173   return status == kVDADecoderNoErr ? SUCCESS : OTHER_ERROR;
176 VideoDecodeAccelerationSupport::Status
177 VideoDecodeAccelerationSupport::Destroy() {
178   // Decoder might be NULL if Create() fails.
179   if (!decoder_)
180     return SUCCESS;
181   OSStatus status = g_VDADecoderDestroy(decoder_);
182   decoder_ = NULL;
183   return status == kVDADecoderNoErr ? SUCCESS : OTHER_ERROR;
186 VideoDecodeAccelerationSupport::~VideoDecodeAccelerationSupport() {
187   DCHECK(!decoder_);
190 // static
191 void VideoDecodeAccelerationSupport::OnFrameReadyCallback(
192     void* callback_data,
193     CFDictionaryRef frame_info,
194     OSStatus status,
195     uint32_t flags,
196     CVImageBufferRef image_buffer) {
197   base::mac::ScopedNSAutoreleasePool pool;
198   NSNumber* frame_id_num = base::mac::ObjCCastStrict<NSNumber>(
199       [CFToNSCast(frame_info) objectForKey:kFrameIdKey]);
200   int frame_id = [frame_id_num intValue];
202   // Retain the image while the callback is in flight.
203   if (image_buffer)
204     CFRetain(image_buffer);
206   VideoDecodeAccelerationSupport* vda =
207       reinterpret_cast<VideoDecodeAccelerationSupport*>(callback_data);
208   DCHECK(vda);
209   vda->loop_->PostTask(
210       FROM_HERE, base::Bind(&VideoDecodeAccelerationSupport::OnFrameReady, vda,
211       image_buffer, status, frame_id));
214 void VideoDecodeAccelerationSupport::OnFrameReady(CVImageBufferRef image_buffer,
215                                                   OSStatus status,
216                                                   int frame_id) {
217   frame_ready_callbacks_[frame_id].Run(image_buffer, status);
218   // Match the retain in OnFrameReadyCallback.
219   if (image_buffer)
220     CFRelease(image_buffer);
221   frame_ready_callbacks_.erase(frame_id);
224 }  // namespace