Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / media / base / mac / avfoundation_glue.mm
blobaed571d5d4702c098c9fb1cfb7e1b944e8d7447d
1 // Copyright 2013 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 #import "media/base/mac/avfoundation_glue.h"
7 #include <dlfcn.h>
9 #include "base/command_line.h"
10 #include "base/lazy_instance.h"
11 #include "base/mac/mac_util.h"
12 #include "base/metrics/histogram.h"
13 #include "media/base/media_switches.h"
15 namespace {
17 // Used for logging capture API usage. Classes are a partition. Elements in this
18 // enum should not be deleted or rearranged; the only permitted operation is to
19 // add new elements before CAPTURE_API_MAX, that must be equal to the last item.
20 enum CaptureApi {
21   CAPTURE_API_QTKIT_DUE_TO_OS_PREVIOUS_TO_LION = 0,
22   CAPTURE_API_QTKIT_FORCED_BY_FLAG = 1,
23   CAPTURE_API_QTKIT_DUE_TO_NO_FLAG = 2,
24   CAPTURE_API_QTKIT_DUE_TO_AVFOUNDATION_LOAD_ERROR = 3,
25   CAPTURE_API_AVFOUNDATION_LOADED_OK = 4,
26   CAPTURE_API_MAX = CAPTURE_API_AVFOUNDATION_LOADED_OK
29 void LogCaptureApi(CaptureApi api) {
30   UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureApi.Mac",
31                             api,
32                             CAPTURE_API_MAX + 1);
35 // This class is used to retrieve AVFoundation NSBundle and library handle. It
36 // must be used as a LazyInstance so that it is initialised once and in a
37 // thread-safe way. Normally no work is done in constructors: LazyInstance is
38 // an exception.
39 class AVFoundationInternal {
40  public:
41   AVFoundationInternal() {
42     bundle_ = [NSBundle
43         bundleWithPath:@"/System/Library/Frameworks/AVFoundation.framework"];
45     const char* path = [[bundle_ executablePath] fileSystemRepresentation];
46     CHECK(path);
47     library_handle_ = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
48     CHECK(library_handle_) << dlerror();
50     struct {
51       NSString** loaded_string;
52       const char* symbol;
53     } av_strings[] = {
54         {&AVCaptureDeviceWasConnectedNotification_,
55          "AVCaptureDeviceWasConnectedNotification"},
56         {&AVCaptureDeviceWasDisconnectedNotification_,
57          "AVCaptureDeviceWasDisconnectedNotification"},
58         {&AVMediaTypeVideo_, "AVMediaTypeVideo"},
59         {&AVMediaTypeAudio_, "AVMediaTypeAudio"},
60         {&AVMediaTypeMuxed_, "AVMediaTypeMuxed"},
61         {&AVCaptureSessionRuntimeErrorNotification_,
62          "AVCaptureSessionRuntimeErrorNotification"},
63         {&AVCaptureSessionDidStopRunningNotification_,
64          "AVCaptureSessionDidStopRunningNotification"},
65         {&AVCaptureSessionErrorKey_, "AVCaptureSessionErrorKey"},
66         {&AVVideoScalingModeKey_, "AVVideoScalingModeKey"},
67         {&AVVideoScalingModeResizeAspectFill_,
68          "AVVideoScalingModeResizeAspectFill"},
69     };
70     for (size_t i = 0; i < arraysize(av_strings); ++i) {
71       *av_strings[i].loaded_string = *reinterpret_cast<NSString**>(
72           dlsym(library_handle_, av_strings[i].symbol));
73       DCHECK(*av_strings[i].loaded_string) << dlerror();
74     }
75   }
77   NSBundle* bundle() const { return bundle_; }
79   NSString* AVCaptureDeviceWasConnectedNotification() const {
80     return AVCaptureDeviceWasConnectedNotification_;
81   }
82   NSString* AVCaptureDeviceWasDisconnectedNotification() const {
83     return AVCaptureDeviceWasDisconnectedNotification_;
84   }
85   NSString* AVMediaTypeVideo() const { return AVMediaTypeVideo_; }
86   NSString* AVMediaTypeAudio() const { return AVMediaTypeAudio_; }
87   NSString* AVMediaTypeMuxed() const { return AVMediaTypeMuxed_; }
88   NSString* AVCaptureSessionRuntimeErrorNotification() const {
89     return AVCaptureSessionRuntimeErrorNotification_;
90   }
91   NSString* AVCaptureSessionDidStopRunningNotification() const {
92     return AVCaptureSessionDidStopRunningNotification_;
93   }
94   NSString* AVCaptureSessionErrorKey() const {
95     return AVCaptureSessionErrorKey_;
96   }
97   NSString* AVVideoScalingModeKey() const { return AVVideoScalingModeKey_; }
98   NSString* AVVideoScalingModeResizeAspectFill() const {
99     return AVVideoScalingModeResizeAspectFill_;
100   }
102  private:
103   NSBundle* bundle_;
104   void* library_handle_;
105   // The following members are replicas of the respectives in AVFoundation.
106   NSString* AVCaptureDeviceWasConnectedNotification_;
107   NSString* AVCaptureDeviceWasDisconnectedNotification_;
108   NSString* AVMediaTypeVideo_;
109   NSString* AVMediaTypeAudio_;
110   NSString* AVMediaTypeMuxed_;
111   NSString* AVCaptureSessionRuntimeErrorNotification_;
112   NSString* AVCaptureSessionDidStopRunningNotification_;
113   NSString* AVCaptureSessionErrorKey_;
114   NSString* AVVideoScalingModeKey_;
115   NSString* AVVideoScalingModeResizeAspectFill_;
117   DISALLOW_COPY_AND_ASSIGN(AVFoundationInternal);
120 // This contains the logic of checking whether AVFoundation is supported.
121 // It's called only once and the results are cached in a static bool.
122 bool LoadAVFoundationInternal() {
123   // AVFoundation is only available on OS Lion and above.
124   if (!base::mac::IsOSLionOrLater()) {
125     LogCaptureApi(CAPTURE_API_QTKIT_DUE_TO_OS_PREVIOUS_TO_LION);
126     return false;
127   }
129   const base::CommandLine* command_line =
130       base::CommandLine::ForCurrentProcess();
131   // The force-qtkit flag takes precedence over enable-avfoundation.
132   if (command_line->HasSwitch(switches::kForceQTKit)) {
133     LogCaptureApi(CAPTURE_API_QTKIT_FORCED_BY_FLAG);
134     return false;
135   }
137   if (!command_line->HasSwitch(switches::kEnableAVFoundation)) {
138     LogCaptureApi(CAPTURE_API_QTKIT_DUE_TO_NO_FLAG);
139     return false;
140   }
141   const bool ret = [AVFoundationGlue::AVFoundationBundle() load];
142   LogCaptureApi(ret ? CAPTURE_API_AVFOUNDATION_LOADED_OK
143                     : CAPTURE_API_QTKIT_DUE_TO_AVFOUNDATION_LOAD_ERROR);
144   return ret;
147 }  // namespace
149 static base::LazyInstance<AVFoundationInternal>::Leaky g_avfoundation_handle =
150     LAZY_INSTANCE_INITIALIZER;
152 enum {
153   INITIALIZE_NOT_CALLED = 0,
154   AVFOUNDATION_IS_SUPPORTED,
155   AVFOUNDATION_NOT_SUPPORTED
156 } static g_avfoundation_initialization = INITIALIZE_NOT_CALLED;
158 void AVFoundationGlue::InitializeAVFoundation() {
159   CHECK([NSThread isMainThread]);
160   if (g_avfoundation_initialization != INITIALIZE_NOT_CALLED)
161     return;
162   g_avfoundation_initialization = LoadAVFoundationInternal() ?
163       AVFOUNDATION_IS_SUPPORTED : AVFOUNDATION_NOT_SUPPORTED;
166 bool AVFoundationGlue::IsAVFoundationSupported() {
167   CHECK_NE(g_avfoundation_initialization, INITIALIZE_NOT_CALLED);
168   return g_avfoundation_initialization == AVFOUNDATION_IS_SUPPORTED;
171 NSBundle const* AVFoundationGlue::AVFoundationBundle() {
172   return g_avfoundation_handle.Get().bundle();
175 NSString* AVFoundationGlue::AVCaptureDeviceWasConnectedNotification() {
176   return g_avfoundation_handle.Get().AVCaptureDeviceWasConnectedNotification();
179 NSString* AVFoundationGlue::AVCaptureDeviceWasDisconnectedNotification() {
180   return
181       g_avfoundation_handle.Get().AVCaptureDeviceWasDisconnectedNotification();
184 NSString* AVFoundationGlue::AVMediaTypeVideo() {
185   return g_avfoundation_handle.Get().AVMediaTypeVideo();
188 NSString* AVFoundationGlue::AVMediaTypeAudio() {
189   return g_avfoundation_handle.Get().AVMediaTypeAudio();
192 NSString* AVFoundationGlue::AVMediaTypeMuxed() {
193   return g_avfoundation_handle.Get().AVMediaTypeMuxed();
196 NSString* AVFoundationGlue::AVCaptureSessionRuntimeErrorNotification() {
197   return g_avfoundation_handle.Get().AVCaptureSessionRuntimeErrorNotification();
200 NSString* AVFoundationGlue::AVCaptureSessionDidStopRunningNotification() {
201   return
202       g_avfoundation_handle.Get().AVCaptureSessionDidStopRunningNotification();
205 NSString* AVFoundationGlue::AVCaptureSessionErrorKey() {
206   return g_avfoundation_handle.Get().AVCaptureSessionErrorKey();
209 NSString* AVFoundationGlue::AVVideoScalingModeKey() {
210   return g_avfoundation_handle.Get().AVVideoScalingModeKey();
213 NSString* AVFoundationGlue::AVVideoScalingModeResizeAspectFill() {
214   return g_avfoundation_handle.Get().AVVideoScalingModeResizeAspectFill();
217 Class AVFoundationGlue::AVCaptureSessionClass() {
218   return [AVFoundationBundle() classNamed:@"AVCaptureSession"];
221 Class AVFoundationGlue::AVCaptureVideoDataOutputClass() {
222   return [AVFoundationBundle() classNamed:@"AVCaptureVideoDataOutput"];
225 @implementation AVCaptureDeviceGlue
227 + (NSArray*)devices {
228   Class avcClass =
229       [AVFoundationGlue::AVFoundationBundle() classNamed:@"AVCaptureDevice"];
230   if ([avcClass respondsToSelector:@selector(devices)]) {
231     return [avcClass performSelector:@selector(devices)];
232   }
233   return nil;
236 + (CrAVCaptureDevice*)deviceWithUniqueID:(NSString*)deviceUniqueID {
237   Class avcClass =
238       [AVFoundationGlue::AVFoundationBundle() classNamed:@"AVCaptureDevice"];
239   return [avcClass performSelector:@selector(deviceWithUniqueID:)
240                         withObject:deviceUniqueID];
243 @end  // @implementation AVCaptureDeviceGlue
245 @implementation AVCaptureDeviceInputGlue
247 + (CrAVCaptureDeviceInput*)deviceInputWithDevice:(CrAVCaptureDevice*)device
248                                            error:(NSError**)outError {
249   return [[AVFoundationGlue::AVFoundationBundle()
250       classNamed:@"AVCaptureDeviceInput"] deviceInputWithDevice:device
251                                                           error:outError];
254 @end  // @implementation AVCaptureDeviceInputGlue