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"
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"
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.
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",
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
39 class AVFoundationInternal {
41 AVFoundationInternal() {
43 bundleWithPath:@"/System/Library/Frameworks/AVFoundation.framework"];
45 const char* path = [[bundle_ executablePath] fileSystemRepresentation];
47 library_handle_ = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
48 CHECK(library_handle_) << dlerror();
51 NSString** loaded_string;
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"},
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();
77 NSBundle* bundle() const { return bundle_; }
79 NSString* AVCaptureDeviceWasConnectedNotification() const {
80 return AVCaptureDeviceWasConnectedNotification_;
82 NSString* AVCaptureDeviceWasDisconnectedNotification() const {
83 return AVCaptureDeviceWasDisconnectedNotification_;
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_;
91 NSString* AVCaptureSessionDidStopRunningNotification() const {
92 return AVCaptureSessionDidStopRunningNotification_;
94 NSString* AVCaptureSessionErrorKey() const {
95 return AVCaptureSessionErrorKey_;
97 NSString* AVVideoScalingModeKey() const { return AVVideoScalingModeKey_; }
98 NSString* AVVideoScalingModeResizeAspectFill() const {
99 return AVVideoScalingModeResizeAspectFill_;
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 IsAVFoundationSupportedHelper() {
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);
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);
137 if (!command_line->HasSwitch(switches::kEnableAVFoundation)) {
138 LogCaptureApi(CAPTURE_API_QTKIT_DUE_TO_NO_FLAG);
141 const bool ret = [AVFoundationGlue::AVFoundationBundle() load];
142 LogCaptureApi(ret ? CAPTURE_API_AVFOUNDATION_LOADED_OK
143 : CAPTURE_API_QTKIT_DUE_TO_AVFOUNDATION_LOAD_ERROR);
149 static base::LazyInstance<AVFoundationInternal>::Leaky g_avfoundation_handle =
150 LAZY_INSTANCE_INITIALIZER;
152 bool AVFoundationGlue::IsAVFoundationSupported() {
153 // DeviceMonitorMac will initialize this static bool from the main UI thread
154 // once, during Chrome startup so this construction is thread safe.
155 static const bool is_supported = IsAVFoundationSupportedHelper();
159 NSBundle const* AVFoundationGlue::AVFoundationBundle() {
160 return g_avfoundation_handle.Get().bundle();
163 NSString* AVFoundationGlue::AVCaptureDeviceWasConnectedNotification() {
164 return g_avfoundation_handle.Get().AVCaptureDeviceWasConnectedNotification();
167 NSString* AVFoundationGlue::AVCaptureDeviceWasDisconnectedNotification() {
169 g_avfoundation_handle.Get().AVCaptureDeviceWasDisconnectedNotification();
172 NSString* AVFoundationGlue::AVMediaTypeVideo() {
173 return g_avfoundation_handle.Get().AVMediaTypeVideo();
176 NSString* AVFoundationGlue::AVMediaTypeAudio() {
177 return g_avfoundation_handle.Get().AVMediaTypeAudio();
180 NSString* AVFoundationGlue::AVMediaTypeMuxed() {
181 return g_avfoundation_handle.Get().AVMediaTypeMuxed();
184 NSString* AVFoundationGlue::AVCaptureSessionRuntimeErrorNotification() {
185 return g_avfoundation_handle.Get().AVCaptureSessionRuntimeErrorNotification();
188 NSString* AVFoundationGlue::AVCaptureSessionDidStopRunningNotification() {
190 g_avfoundation_handle.Get().AVCaptureSessionDidStopRunningNotification();
193 NSString* AVFoundationGlue::AVCaptureSessionErrorKey() {
194 return g_avfoundation_handle.Get().AVCaptureSessionErrorKey();
197 NSString* AVFoundationGlue::AVVideoScalingModeKey() {
198 return g_avfoundation_handle.Get().AVVideoScalingModeKey();
201 NSString* AVFoundationGlue::AVVideoScalingModeResizeAspectFill() {
202 return g_avfoundation_handle.Get().AVVideoScalingModeResizeAspectFill();
205 Class AVFoundationGlue::AVCaptureSessionClass() {
206 return [AVFoundationBundle() classNamed:@"AVCaptureSession"];
209 Class AVFoundationGlue::AVCaptureVideoDataOutputClass() {
210 return [AVFoundationBundle() classNamed:@"AVCaptureVideoDataOutput"];
213 @implementation AVCaptureDeviceGlue
215 + (NSArray*)devices {
217 [AVFoundationGlue::AVFoundationBundle() classNamed:@"AVCaptureDevice"];
218 if ([avcClass respondsToSelector:@selector(devices)]) {
219 return [avcClass performSelector:@selector(devices)];
224 + (CrAVCaptureDevice*)deviceWithUniqueID:(NSString*)deviceUniqueID {
226 [AVFoundationGlue::AVFoundationBundle() classNamed:@"AVCaptureDevice"];
227 return [avcClass performSelector:@selector(deviceWithUniqueID:)
228 withObject:deviceUniqueID];
231 @end // @implementation AVCaptureDeviceGlue
233 @implementation AVCaptureDeviceInputGlue
235 + (CrAVCaptureDeviceInput*)deviceInputWithDevice:(CrAVCaptureDevice*)device
236 error:(NSError**)outError {
237 return [[AVFoundationGlue::AVFoundationBundle()
238 classNamed:@"AVCaptureDeviceInput"] deviceInputWithDevice:device
242 @end // @implementation AVCaptureDeviceInputGlue