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 15namespace { 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. 21class 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 105static base::LazyInstance<AVFoundationInternal> g_avfoundation_handle = 106 LAZY_INSTANCE_INITIALIZER; 107 108bool 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 134NSBundle const* AVFoundationGlue::AVFoundationBundle() { 135 return g_avfoundation_handle.Get().bundle(); 136} 137 138void* AVFoundationGlue::AVFoundationLibraryHandle() { 139 return g_avfoundation_handle.Get().library_handle(); 140} 141 142NSString* AVFoundationGlue::AVCaptureDeviceWasConnectedNotification() { 143 return g_avfoundation_handle.Get().AVCaptureDeviceWasConnectedNotification(); 144} 145 146NSString* AVFoundationGlue::AVCaptureDeviceWasDisconnectedNotification() { 147 return 148 g_avfoundation_handle.Get().AVCaptureDeviceWasDisconnectedNotification(); 149} 150 151NSString* AVFoundationGlue::AVMediaTypeVideo() { 152 return g_avfoundation_handle.Get().AVMediaTypeVideo(); 153} 154 155NSString* AVFoundationGlue::AVMediaTypeAudio() { 156 return g_avfoundation_handle.Get().AVMediaTypeAudio(); 157} 158 159NSString* AVFoundationGlue::AVMediaTypeMuxed() { 160 return g_avfoundation_handle.Get().AVMediaTypeMuxed(); 161} 162 163NSString* AVFoundationGlue::AVCaptureSessionRuntimeErrorNotification() { 164 return g_avfoundation_handle.Get().AVCaptureSessionRuntimeErrorNotification(); 165} 166 167NSString* AVFoundationGlue::AVCaptureSessionDidStopRunningNotification() { 168 return 169 g_avfoundation_handle.Get().AVCaptureSessionDidStopRunningNotification(); 170} 171 172NSString* AVFoundationGlue::AVCaptureSessionErrorKey() { 173 return g_avfoundation_handle.Get().AVCaptureSessionErrorKey(); 174} 175 176NSString* AVFoundationGlue::AVVideoScalingModeKey() { 177 return g_avfoundation_handle.Get().AVVideoScalingModeKey(); 178} 179 180NSString* AVFoundationGlue::AVVideoScalingModeResizeAspectFill() { 181 return g_avfoundation_handle.Get().AVVideoScalingModeResizeAspectFill(); 182} 183 184Class AVFoundationGlue::AVCaptureSessionClass() { 185 return [AVFoundationBundle() classNamed:@"AVCaptureSession"]; 186} 187 188Class 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