| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "media/base/mac/avfoundation_glue.h" |
| |
| #include <dlfcn.h> |
| |
| #include "base/command_line.h" |
| #include "base/lazy_instance.h" |
| #include "base/mac/mac_util.h" |
| #include "base/metrics/field_trial.h" |
| #include "media/base/media_switches.h" |
| |
| namespace { |
| |
| // This class is used to retrieve AVFoundation NSBundle and library handle. It |
| // must be used as a LazyInstance so that it is initialised once and in a |
| // thread-safe way. Normally no work is done in constructors: LazyInstance is |
| // an exception. |
| class AVFoundationInternal { |
| public: |
| AVFoundationInternal() { |
| bundle_ = [NSBundle |
| bundleWithPath:@"/System/Library/Frameworks/AVFoundation.framework"]; |
| |
| const char* path = [[bundle_ executablePath] fileSystemRepresentation]; |
| CHECK(path); |
| library_handle_ = dlopen(path, RTLD_LAZY | RTLD_LOCAL); |
| CHECK(library_handle_) << dlerror(); |
| |
| struct { |
| NSString** loaded_string; |
| const char* symbol; |
| } av_strings[] = { |
| {&AVCaptureDeviceWasConnectedNotification_, |
| "AVCaptureDeviceWasConnectedNotification"}, |
| {&AVCaptureDeviceWasDisconnectedNotification_, |
| "AVCaptureDeviceWasDisconnectedNotification"}, |
| {&AVMediaTypeVideo_, "AVMediaTypeVideo"}, |
| {&AVMediaTypeAudio_, "AVMediaTypeAudio"}, |
| {&AVMediaTypeMuxed_, "AVMediaTypeMuxed"}, |
| {&AVCaptureSessionRuntimeErrorNotification_, |
| "AVCaptureSessionRuntimeErrorNotification"}, |
| {&AVCaptureSessionDidStopRunningNotification_, |
| "AVCaptureSessionDidStopRunningNotification"}, |
| {&AVCaptureSessionErrorKey_, "AVCaptureSessionErrorKey"}, |
| {&AVVideoScalingModeKey_, "AVVideoScalingModeKey"}, |
| {&AVVideoScalingModeResizeAspectFill_, |
| "AVVideoScalingModeResizeAspectFill"}, |
| }; |
| for (size_t i = 0; i < arraysize(av_strings); ++i) { |
| *av_strings[i].loaded_string = *reinterpret_cast<NSString**>( |
| dlsym(library_handle_, av_strings[i].symbol)); |
| DCHECK(*av_strings[i].loaded_string) << dlerror(); |
| } |
| } |
| |
| NSBundle* bundle() const { return bundle_; } |
| void* library_handle() const { return library_handle_; } |
| |
| NSString* AVCaptureDeviceWasConnectedNotification() const { |
| return AVCaptureDeviceWasConnectedNotification_; |
| } |
| NSString* AVCaptureDeviceWasDisconnectedNotification() const { |
| return AVCaptureDeviceWasDisconnectedNotification_; |
| } |
| NSString* AVMediaTypeVideo() const { return AVMediaTypeVideo_; } |
| NSString* AVMediaTypeAudio() const { return AVMediaTypeAudio_; } |
| NSString* AVMediaTypeMuxed() const { return AVMediaTypeMuxed_; } |
| NSString* AVCaptureSessionRuntimeErrorNotification() const { |
| return AVCaptureSessionRuntimeErrorNotification_; |
| } |
| NSString* AVCaptureSessionDidStopRunningNotification() const { |
| return AVCaptureSessionDidStopRunningNotification_; |
| } |
| NSString* AVCaptureSessionErrorKey() const { |
| return AVCaptureSessionErrorKey_; |
| } |
| NSString* AVVideoScalingModeKey() const { return AVVideoScalingModeKey_; } |
| NSString* AVVideoScalingModeResizeAspectFill() const { |
| return AVVideoScalingModeResizeAspectFill_; |
| } |
| |
| private: |
| NSBundle* bundle_; |
| void* library_handle_; |
| // The following members are replicas of the respectives in AVFoundation. |
| NSString* AVCaptureDeviceWasConnectedNotification_; |
| NSString* AVCaptureDeviceWasDisconnectedNotification_; |
| NSString* AVMediaTypeVideo_; |
| NSString* AVMediaTypeAudio_; |
| NSString* AVMediaTypeMuxed_; |
| NSString* AVCaptureSessionRuntimeErrorNotification_; |
| NSString* AVCaptureSessionDidStopRunningNotification_; |
| NSString* AVCaptureSessionErrorKey_; |
| NSString* AVVideoScalingModeKey_; |
| NSString* AVVideoScalingModeResizeAspectFill_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AVFoundationInternal); |
| }; |
| |
| } // namespace |
| |
| static base::LazyInstance<AVFoundationInternal> g_avfoundation_handle = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| bool AVFoundationGlue::IsAVFoundationSupported() { |
| // DeviceMonitorMac will initialize this static bool from the main UI thread |
| // once, during Chrome startup so this construction is thread safe. |
| // Use AVFoundation if possible, enabled, and QTKit is not explicitly forced. |
| static CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| |
| // AVFoundation is only available on OS Lion and above. |
| if (!base::mac::IsOSLionOrLater()) |
| return false; |
| |
| // The force-qtkit flag takes precedence over enable-avfoundation. |
| if (command_line->HasSwitch(switches::kForceQTKit)) |
| return false; |
| |
| // Next in precedence is the enable-avfoundation flag. |
| // TODO(vrk): Does this really need to be static? |
| static bool should_enable_avfoundation = |
| command_line->HasSwitch(switches::kEnableAVFoundation) || |
| base::FieldTrialList::FindFullName("AVFoundationMacVideoCapture") |
| == "Enabled"; |
| // Try to load AVFoundation. Save result in static bool to avoid loading |
| // AVFoundationBundle every call. |
| static bool loaded_successfully = [AVFoundationBundle() load]; |
| return should_enable_avfoundation && loaded_successfully; |
| } |
| |
| NSBundle const* AVFoundationGlue::AVFoundationBundle() { |
| return g_avfoundation_handle.Get().bundle(); |
| } |
| |
| void* AVFoundationGlue::AVFoundationLibraryHandle() { |
| return g_avfoundation_handle.Get().library_handle(); |
| } |
| |
| NSString* AVFoundationGlue::AVCaptureDeviceWasConnectedNotification() { |
| return g_avfoundation_handle.Get().AVCaptureDeviceWasConnectedNotification(); |
| } |
| |
| NSString* AVFoundationGlue::AVCaptureDeviceWasDisconnectedNotification() { |
| return |
| g_avfoundation_handle.Get().AVCaptureDeviceWasDisconnectedNotification(); |
| } |
| |
| NSString* AVFoundationGlue::AVMediaTypeVideo() { |
| return g_avfoundation_handle.Get().AVMediaTypeVideo(); |
| } |
| |
| NSString* AVFoundationGlue::AVMediaTypeAudio() { |
| return g_avfoundation_handle.Get().AVMediaTypeAudio(); |
| } |
| |
| NSString* AVFoundationGlue::AVMediaTypeMuxed() { |
| return g_avfoundation_handle.Get().AVMediaTypeMuxed(); |
| } |
| |
| NSString* AVFoundationGlue::AVCaptureSessionRuntimeErrorNotification() { |
| return g_avfoundation_handle.Get().AVCaptureSessionRuntimeErrorNotification(); |
| } |
| |
| NSString* AVFoundationGlue::AVCaptureSessionDidStopRunningNotification() { |
| return |
| g_avfoundation_handle.Get().AVCaptureSessionDidStopRunningNotification(); |
| } |
| |
| NSString* AVFoundationGlue::AVCaptureSessionErrorKey() { |
| return g_avfoundation_handle.Get().AVCaptureSessionErrorKey(); |
| } |
| |
| NSString* AVFoundationGlue::AVVideoScalingModeKey() { |
| return g_avfoundation_handle.Get().AVVideoScalingModeKey(); |
| } |
| |
| NSString* AVFoundationGlue::AVVideoScalingModeResizeAspectFill() { |
| return g_avfoundation_handle.Get().AVVideoScalingModeResizeAspectFill(); |
| } |
| |
| Class AVFoundationGlue::AVCaptureSessionClass() { |
| return [AVFoundationBundle() classNamed:@"AVCaptureSession"]; |
| } |
| |
| Class AVFoundationGlue::AVCaptureVideoDataOutputClass() { |
| return [AVFoundationBundle() classNamed:@"AVCaptureVideoDataOutput"]; |
| } |
| |
| @implementation AVCaptureDeviceGlue |
| |
| + (NSArray*)devices { |
| Class avcClass = |
| [AVFoundationGlue::AVFoundationBundle() classNamed:@"AVCaptureDevice"]; |
| if ([avcClass respondsToSelector:@selector(devices)]) { |
| return [avcClass performSelector:@selector(devices)]; |
| } |
| return nil; |
| } |
| |
| + (CrAVCaptureDevice*)deviceWithUniqueID:(NSString*)deviceUniqueID { |
| Class avcClass = |
| [AVFoundationGlue::AVFoundationBundle() classNamed:@"AVCaptureDevice"]; |
| return [avcClass performSelector:@selector(deviceWithUniqueID:) |
| withObject:deviceUniqueID]; |
| } |
| |
| @end // @implementation AVCaptureDeviceGlue |
| |
| @implementation AVCaptureDeviceInputGlue |
| |
| + (CrAVCaptureDeviceInput*)deviceInputWithDevice:(CrAVCaptureDevice*)device |
| error:(NSError**)outError { |
| return [[AVFoundationGlue::AVFoundationBundle() |
| classNamed:@"AVCaptureDeviceInput"] deviceInputWithDevice:device |
| error:outError]; |
| } |
| |
| @end // @implementation AVCaptureDeviceInputGlue |