Home | History | Annotate | Download | only in mac
      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