AVAudioCaptureSource.mm   [plain text]


/*
 * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#import "config.h"
#import "AVAudioCaptureSource.h"

#if ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)

#import "Logging.h"
#import "MediaConstraints.h"
#import "NotImplemented.h"
#import "RealtimeMediaSourceSettings.h"
#import "SoftLinking.h"
#import "WebAudioSourceProviderAVFObjC.h"
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
#import <wtf/HashSet.h>

#import "CoreMediaSoftLink.h"

typedef AVCaptureAudioChannel AVCaptureAudioChannelType;
typedef AVCaptureAudioDataOutput AVCaptureAudioDataOutputType;
typedef AVCaptureConnection AVCaptureConnectionType;
typedef AVCaptureDevice AVCaptureDeviceTypedef;
typedef AVCaptureDeviceInput AVCaptureDeviceInputType;
typedef AVCaptureOutput AVCaptureOutputType;

SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)

SOFT_LINK_CLASS(AVFoundation, AVCaptureAudioChannel)
SOFT_LINK_CLASS(AVFoundation, AVCaptureAudioDataOutput)
SOFT_LINK_CLASS(AVFoundation, AVCaptureConnection)
SOFT_LINK_CLASS(AVFoundation, AVCaptureDevice)
SOFT_LINK_CLASS(AVFoundation, AVCaptureDeviceInput)
SOFT_LINK_CLASS(AVFoundation, AVCaptureOutput)

SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *)

#define AVMediaTypeAudio getAVMediaTypeAudio()

namespace WebCore {

RefPtr<AVMediaCaptureSource> AVAudioCaptureSource::create(AVCaptureDeviceTypedef* device, const AtomicString& id, PassRefPtr<MediaConstraints> constraint)
{
    return adoptRef(new AVAudioCaptureSource(device, id, constraint));
}
    
AVAudioCaptureSource::AVAudioCaptureSource(AVCaptureDeviceTypedef* device, const AtomicString& id, PassRefPtr<MediaConstraints> constraints)
    : AVMediaCaptureSource(device, id, RealtimeMediaSource::Audio, constraints)
{
    m_inputDescription = std::make_unique<AudioStreamBasicDescription>();
}
    
AVAudioCaptureSource::~AVAudioCaptureSource()
{
}

void AVAudioCaptureSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities)
{
    // FIXME: finish this implementation - https://webkit.org/b/122430
    capabilities.setVolume(CapabilityValueOrRange(0, 1.0));
}

void AVAudioCaptureSource::initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints& supportedConstraints)
{
    supportedConstraints.setSupportsVolume(true);
}

void AVAudioCaptureSource::updateSettings(RealtimeMediaSourceSettings& settings)
{
    // FIXME: use [AVCaptureAudioPreviewOutput volume] for volume

    settings.setDeviceId(id());
}

void AVAudioCaptureSource::addObserver(AVAudioCaptureSource::Observer* observer)
{
    LockHolder lock(m_lock);
    m_observers.append(observer);
    if (m_inputDescription->mSampleRate)
        observer->prepare(m_inputDescription.get());
}

void AVAudioCaptureSource::removeObserver(AVAudioCaptureSource::Observer* observer)
{
    LockHolder lock(m_lock);
    size_t pos = m_observers.find(observer);
    if (pos != notFound)
        m_observers.remove(pos);
}

void AVAudioCaptureSource::setupCaptureSession()
{
    RetainPtr<AVCaptureDeviceInputType> audioIn = adoptNS([allocAVCaptureDeviceInputInstance() initWithDevice:device() error:nil]);

    if (![session() canAddInput:audioIn.get()]) {
        LOG(Media, "AVVideoCaptureSource::setupCaptureSession(%p), unable to add audio input device", this);
        return;
    }
    [session() addInput:audioIn.get()];

    RetainPtr<AVCaptureAudioDataOutputType> audioOutput = adoptNS([allocAVCaptureAudioDataOutputInstance() init]);
    setAudioSampleBufferDelegate(audioOutput.get());

    if (![session() canAddOutput:audioOutput.get()]) {
        LOG(Media, "AVVideoCaptureSource::setupCaptureSession(%p), unable to add audio sample buffer output delegate", this);
        return;
    }
    [session() addOutput:audioOutput.get()];
    m_audioConnection = [audioOutput.get() connectionWithMediaType:AVMediaTypeAudio];
}

void AVAudioCaptureSource::shutdownCaptureSession()
{
    {
        LockHolder lock(m_lock);

        m_audioConnection = nullptr;
        m_inputDescription = std::make_unique<AudioStreamBasicDescription>();

        for (auto& observer : m_observers)
            observer->unprepare();
        m_observers.shrink(0);
    }

    // Don't hold the lock when destroying the audio provider, it will call back into this object
    // to remove itself as an observer.
    m_audioSourceProvider = nullptr;
}

static bool operator==(const AudioStreamBasicDescription& a, const AudioStreamBasicDescription& b)
{
    return a.mSampleRate == b.mSampleRate
        && a.mFormatID == b.mFormatID
        && a.mFormatFlags == b.mFormatFlags
        && a.mBytesPerPacket == b.mBytesPerPacket
        && a.mFramesPerPacket == b.mFramesPerPacket
        && a.mBytesPerFrame == b.mBytesPerFrame
        && a.mChannelsPerFrame == b.mChannelsPerFrame
        && a.mBitsPerChannel == b.mBitsPerChannel;
}

static bool operator!=(const AudioStreamBasicDescription& a, const AudioStreamBasicDescription& b)
{
    return !(a == b);
}

void AVAudioCaptureSource::captureOutputDidOutputSampleBufferFromConnection(AVCaptureOutputType*, CMSampleBufferRef sampleBuffer, AVCaptureConnectionType*)
{
    if (muted())
        return;

    CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
    if (!formatDescription)
        return;

    std::unique_lock<Lock> lock(m_lock, std::try_to_lock);
    if (!lock.owns_lock()) {
        // Failed to acquire the lock, just return instead of blocking.
        return;
    }

    if (m_observers.isEmpty())
        return;

    const AudioStreamBasicDescription* streamDescription = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription);
    if (*m_inputDescription != *streamDescription) {
        m_inputDescription = std::make_unique<AudioStreamBasicDescription>(*streamDescription);
        for (auto& observer : m_observers)
            observer->prepare(m_inputDescription.get());
    }

    for (auto& observer : m_observers)
        observer->process(formatDescription, sampleBuffer);
}

AudioSourceProvider* AVAudioCaptureSource::audioSourceProvider()
{
    if (!m_audioSourceProvider)
        m_audioSourceProvider = WebAudioSourceProviderAVFObjC::create(*this);

    return m_audioSourceProvider.get();
}

} // namespace WebCore

#endif // ENABLE(MEDIA_STREAM)