blob: c9985c4e0c6126ca2c11e68b716efb0eaefc1d2a [file] [log] [blame]
// 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