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. 4 5 #import "media/base/mac/avfoundation_glue.h" 6 7 #include <dlfcn.h> 8 9 #include "base/command_line.h" 10 #include "base/lazy_instance.h" 11 #include "base/mac/mac_util.h" 12 #include "base/metrics/field_trial.h" 13 #include "media/base/media_switches.h" 14 15 namespace { 16 17 // This class is used to retrieve AVFoundation NSBundle and library handle. It 18 // must be used as a LazyInstance so that it is initialised once and in a 19 // thread-safe way. Normally no work is done in constructors: LazyInstance is 20 // an exception. 21 class AVFoundationInternal { 22 public: 23 AVFoundationInternal() { 24 bundle_ = [NSBundle 25 bundleWithPath:@"/System/Library/Frameworks/AVFoundation.framework"]; 26 27 const char* path = [[bundle_ executablePath] fileSystemRepresentation]; 28 CHECK(path); 29 library_handle_ = dlopen(path, RTLD_LAZY | RTLD_LOCAL); 30 CHECK(library_handle_) << dlerror(); 31 32 struct { 33 NSString** loaded_string; 34 const char* symbol; 35 } av_strings[] = { 36 {&AVCaptureDeviceWasConnectedNotification_, 37 "AVCaptureDeviceWasConnectedNotification"}, 38 {&AVCaptureDeviceWasDisconnectedNotification_, 39 "AVCaptureDeviceWasDisconnectedNotification"}, 40 {&AVMediaTypeVideo_, "AVMediaTypeVideo"}, 41 {&AVMediaTypeAudio_, "AVMediaTypeAudio"}, 42 {&AVMediaTypeMuxed_, "AVMediaTypeMuxed"}, 43 {&AVCaptureSessionRuntimeErrorNotification_, 44 "AVCaptureSessionRuntimeErrorNotification"}, 45 {&AVCaptureSessionDidStopRunningNotification_, 46 "AVCaptureSessionDidStopRunningNotification"}, 47 {&AVCaptureSessionErrorKey_, "AVCaptureSessionErrorKey"}, 48 {&AVVideoScalingModeKey_, "AVVideoScalingModeKey"}, 49 {&AVVideoScalingModeResizeAspectFill_, 50 "AVVideoScalingModeResizeAspectFill"}, 51 }; 52 for (size_t i = 0; i < arraysize(av_strings); ++i) { 53 *av_strings[i].loaded_string = *reinterpret_cast<NSString**>( 54 dlsym(library_handle_, av_strings[i].symbol)); 55 DCHECK(*av_strings[i].loaded_string) << dlerror(); 56 } 57 } 58 59 NSBundle* bundle() const { return bundle_; } 60 void* library_handle() const { return library_handle_; } 61 62 NSString* AVCaptureDeviceWasConnectedNotification() const { 63 return AVCaptureDeviceWasConnectedNotification_; 64 } 65 NSString* AVCaptureDeviceWasDisconnectedNotification() const { 66 return AVCaptureDeviceWasDisconnectedNotification_; 67 } 68 NSString* AVMediaTypeVideo() const { return AVMediaTypeVideo_; } 69 NSString* AVMediaTypeAudio() const { return AVMediaTypeAudio_; } 70 NSString* AVMediaTypeMuxed() const { return AVMediaTypeMuxed_; } 71 NSString* AVCaptureSessionRuntimeErrorNotification() const { 72 return AVCaptureSessionRuntimeErrorNotification_; 73 } 74 NSString* AVCaptureSessionDidStopRunningNotification() const { 75 return AVCaptureSessionDidStopRunningNotification_; 76 } 77 NSString* AVCaptureSessionErrorKey() const { 78 return AVCaptureSessionErrorKey_; 79 } 80 NSString* AVVideoScalingModeKey() const { return AVVideoScalingModeKey_; } 81 NSString* AVVideoScalingModeResizeAspectFill() const { 82 return AVVideoScalingModeResizeAspectFill_; 83 } 84 85 private: 86 NSBundle* bundle_; 87 void* library_handle_; 88 // The following members are replicas of the respectives in AVFoundation. 89 NSString* AVCaptureDeviceWasConnectedNotification_; 90 NSString* AVCaptureDeviceWasDisconnectedNotification_; 91 NSString* AVMediaTypeVideo_; 92 NSString* AVMediaTypeAudio_; 93 NSString* AVMediaTypeMuxed_; 94 NSString* AVCaptureSessionRuntimeErrorNotification_; 95 NSString* AVCaptureSessionDidStopRunningNotification_; 96 NSString* AVCaptureSessionErrorKey_; 97 NSString* AVVideoScalingModeKey_; 98 NSString* AVVideoScalingModeResizeAspectFill_; 99 100 DISALLOW_COPY_AND_ASSIGN(AVFoundationInternal); 101 }; 102 103 } // namespace 104 105 static base::LazyInstance<AVFoundationInternal> g_avfoundation_handle = 106 LAZY_INSTANCE_INITIALIZER; 107 108 bool AVFoundationGlue::IsAVFoundationSupported() { 109 // DeviceMonitorMac will initialize this static bool from the main UI thread 110 // once, during Chrome startup so this construction is thread safe. 111 // Use AVFoundation if possible, enabled, and QTKit is not explicitly forced. 112 static CommandLine* command_line = CommandLine::ForCurrentProcess(); 113 114 // AVFoundation is only available on OS Lion and above. 115 if (!base::mac::IsOSLionOrLater()) 116 return false; 117 118 // The force-qtkit flag takes precedence over enable-avfoundation. 119 if (command_line->HasSwitch(switches::kForceQTKit)) 120 return false; 121 122 // Next in precedence is the enable-avfoundation flag. 123 // TODO(vrk): Does this really need to be static? 124 static bool should_enable_avfoundation = 125 command_line->HasSwitch(switches::kEnableAVFoundation) || 126 base::FieldTrialList::FindFullName("AVFoundationMacVideoCapture") 127 == "Enabled"; 128 // Try to load AVFoundation. Save result in static bool to avoid loading 129 // AVFoundationBundle every call. 130 static bool loaded_successfully = [AVFoundationBundle() load]; 131 return should_enable_avfoundation && loaded_successfully; 132 } 133 134 NSBundle const* AVFoundationGlue::AVFoundationBundle() { 135 return g_avfoundation_handle.Get().bundle(); 136 } 137 138 void* AVFoundationGlue::AVFoundationLibraryHandle() { 139 return g_avfoundation_handle.Get().library_handle(); 140 } 141 142 NSString* AVFoundationGlue::AVCaptureDeviceWasConnectedNotification() { 143 return g_avfoundation_handle.Get().AVCaptureDeviceWasConnectedNotification(); 144 } 145 146 NSString* AVFoundationGlue::AVCaptureDeviceWasDisconnectedNotification() { 147 return 148 g_avfoundation_handle.Get().AVCaptureDeviceWasDisconnectedNotification(); 149 } 150 151 NSString* AVFoundationGlue::AVMediaTypeVideo() { 152 return g_avfoundation_handle.Get().AVMediaTypeVideo(); 153 } 154 155 NSString* AVFoundationGlue::AVMediaTypeAudio() { 156 return g_avfoundation_handle.Get().AVMediaTypeAudio(); 157 } 158 159 NSString* AVFoundationGlue::AVMediaTypeMuxed() { 160 return g_avfoundation_handle.Get().AVMediaTypeMuxed(); 161 } 162 163 NSString* AVFoundationGlue::AVCaptureSessionRuntimeErrorNotification() { 164 return g_avfoundation_handle.Get().AVCaptureSessionRuntimeErrorNotification(); 165 } 166 167 NSString* AVFoundationGlue::AVCaptureSessionDidStopRunningNotification() { 168 return 169 g_avfoundation_handle.Get().AVCaptureSessionDidStopRunningNotification(); 170 } 171 172 NSString* AVFoundationGlue::AVCaptureSessionErrorKey() { 173 return g_avfoundation_handle.Get().AVCaptureSessionErrorKey(); 174 } 175 176 NSString* AVFoundationGlue::AVVideoScalingModeKey() { 177 return g_avfoundation_handle.Get().AVVideoScalingModeKey(); 178 } 179 180 NSString* AVFoundationGlue::AVVideoScalingModeResizeAspectFill() { 181 return g_avfoundation_handle.Get().AVVideoScalingModeResizeAspectFill(); 182 } 183 184 Class AVFoundationGlue::AVCaptureSessionClass() { 185 return [AVFoundationBundle() classNamed:@"AVCaptureSession"]; 186 } 187 188 Class AVFoundationGlue::AVCaptureVideoDataOutputClass() { 189 return [AVFoundationBundle() classNamed:@"AVCaptureVideoDataOutput"]; 190 } 191 192 @implementation AVCaptureDeviceGlue 193 194 + (NSArray*)devices { 195 Class avcClass = 196 [AVFoundationGlue::AVFoundationBundle() classNamed:@"AVCaptureDevice"]; 197 if ([avcClass respondsToSelector:@selector(devices)]) { 198 return [avcClass performSelector:@selector(devices)]; 199 } 200 return nil; 201 } 202 203 + (CrAVCaptureDevice*)deviceWithUniqueID:(NSString*)deviceUniqueID { 204 Class avcClass = 205 [AVFoundationGlue::AVFoundationBundle() classNamed:@"AVCaptureDevice"]; 206 return [avcClass performSelector:@selector(deviceWithUniqueID:) 207 withObject:deviceUniqueID]; 208 } 209 210 @end // @implementation AVCaptureDeviceGlue 211 212 @implementation AVCaptureDeviceInputGlue 213 214 + (CrAVCaptureDeviceInput*)deviceInputWithDevice:(CrAVCaptureDevice*)device 215 error:(NSError**)outError { 216 return [[AVFoundationGlue::AVFoundationBundle() 217 classNamed:@"AVCaptureDeviceInput"] deviceInputWithDevice:device 218 error:outError]; 219 } 220 221 @end // @implementation AVCaptureDeviceInputGlue 222