[iOS] Unset active media capture source when stopped capturing
[WebKit-https.git] / Source / WebCore / platform / mediastream / mac / AVAudioCaptureSource.mm
1 /*
2  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "AVAudioCaptureSource.h"
28
29 #if ENABLE(MEDIA_STREAM) && USE(AVFOUNDATION)
30
31 #import "AudioSampleBufferList.h"
32 #import "CAAudioStreamDescription.h"
33 #import "CaptureDevice.h"
34 #import "Logging.h"
35 #import "MediaConstraints.h"
36 #import "MediaSampleAVFObjC.h"
37 #import "RealtimeMediaSourceSettings.h"
38 #import "SoftLinking.h"
39 #import "WebAudioSourceProviderAVFObjC.h"
40 #import <AVFoundation/AVCaptureInput.h>
41 #import <AVFoundation/AVCaptureOutput.h>
42 #import <AVFoundation/AVCaptureSession.h>
43 #import <CoreAudio/CoreAudioTypes.h>
44 #import <wtf/HashSet.h>
45 #import <wtf/NeverDestroyed.h>
46
47 #import "CoreMediaSoftLink.h"
48
49 typedef AVCaptureAudioChannel AVCaptureAudioChannelType;
50 typedef AVCaptureAudioDataOutput AVCaptureAudioDataOutputType;
51 typedef AVCaptureConnection AVCaptureConnectionType;
52 typedef AVCaptureDevice AVCaptureDeviceTypedef;
53 typedef AVCaptureDeviceInput AVCaptureDeviceInputType;
54 typedef AVCaptureOutput AVCaptureOutputType;
55
56 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
57
58 SOFT_LINK_CLASS(AVFoundation, AVCaptureAudioChannel)
59 SOFT_LINK_CLASS(AVFoundation, AVCaptureAudioDataOutput)
60 SOFT_LINK_CLASS(AVFoundation, AVCaptureConnection)
61 SOFT_LINK_CLASS(AVFoundation, AVCaptureDevice)
62 SOFT_LINK_CLASS(AVFoundation, AVCaptureDeviceInput)
63 SOFT_LINK_CLASS(AVFoundation, AVCaptureOutput)
64
65 #define AVCaptureAudioChannel getAVCaptureAudioChannelClass()
66 #define AVCaptureAudioDataOutput getAVCaptureAudioDataOutputClass()
67 #define AVCaptureConnection getAVCaptureConnectionClass()
68 #define AVCaptureDevice getAVCaptureDeviceClass()
69 #define AVCaptureDeviceFormat getAVCaptureDeviceFormatClass()
70 #define AVCaptureDeviceInput getAVCaptureDeviceInputClass()
71 #define AVCaptureOutput getAVCaptureOutputClass()
72 #define AVFrameRateRange getAVFrameRateRangeClass()
73
74 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *)
75
76 #define AVMediaTypeAudio getAVMediaTypeAudio()
77
78 namespace WebCore {
79
80 class AVAudioCaptureSourceFactory : public RealtimeMediaSource::AudioCaptureFactory
81 #if PLATFORM(IOS)
82     , public RealtimeMediaSource::SingleSourceFactory<AVAudioCaptureSource>
83 #endif
84 {
85 public:
86     CaptureSourceOrError createAudioCaptureSource(const String& deviceID, const MediaConstraints* constraints) final {
87         AVCaptureDeviceTypedef *device = [getAVCaptureDeviceClass() deviceWithUniqueID:deviceID];
88         return device ? AVAudioCaptureSource::create(device, emptyString(), constraints) : CaptureSourceOrError();
89     }
90 };
91
92 CaptureSourceOrError AVAudioCaptureSource::create(AVCaptureDeviceTypedef* device, const AtomicString& id, const MediaConstraints* constraints)
93 {
94     auto source = adoptRef(*new AVAudioCaptureSource(device, id));
95     if (constraints) {
96         auto result = source->applyConstraints(*constraints);
97         if (result)
98             return String(result.value().first);
99     }
100
101     return CaptureSourceOrError(WTFMove(source));
102 }
103
104 static AVAudioCaptureSourceFactory& avAudioCaptureSourceFactory()
105 {
106     static NeverDestroyed<AVAudioCaptureSourceFactory> factory;
107     return factory.get();
108 }
109
110 RealtimeMediaSource::AudioCaptureFactory& AVAudioCaptureSource::factory()
111 {
112     return avAudioCaptureSourceFactory();
113 }
114
115 AVAudioCaptureSource::AVAudioCaptureSource(AVCaptureDeviceTypedef* device, const AtomicString& id)
116     : AVMediaCaptureSource(device, id, Type::Audio)
117 {
118 }
119
120 AVAudioCaptureSource::~AVAudioCaptureSource()
121 {
122 #if PLATFORM(IOS)
123     avAudioCaptureSourceFactory().unsetActiveSource(*this);
124 #endif
125     shutdownCaptureSession();
126 }
127
128 void AVAudioCaptureSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities)
129 {
130     // FIXME: finish this implementation - https://webkit.org/b/122430
131     capabilities.setVolume(CapabilityValueOrRange(0.0, 1.0));
132 }
133
134 void AVAudioCaptureSource::initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints& supportedConstraints)
135 {
136     supportedConstraints.setSupportsVolume(true);
137 }
138
139 void AVAudioCaptureSource::updateSettings(RealtimeMediaSourceSettings& settings)
140 {
141     // FIXME: support volume
142
143     settings.setDeviceId(id());
144 }
145
146 void AVAudioCaptureSource::setupCaptureSession()
147 {
148 #if PLATFORM(IOS)
149     avAudioCaptureSourceFactory().setActiveSource(*this);
150 #endif
151
152     RetainPtr<AVCaptureDeviceInputType> audioIn = adoptNS([allocAVCaptureDeviceInputInstance() initWithDevice:device() error:nil]);
153
154     if (![session() canAddInput:audioIn.get()]) {
155         LOG(Media, "AVVideoCaptureSource::setupCaptureSession(%p), unable to add audio input device", this);
156         return;
157     }
158     [session() addInput:audioIn.get()];
159
160     RetainPtr<AVCaptureAudioDataOutputType> audioOutput = adoptNS([allocAVCaptureAudioDataOutputInstance() init]);
161     setAudioSampleBufferDelegate(audioOutput.get());
162
163     if (![session() canAddOutput:audioOutput.get()]) {
164         LOG(Media, "AVVideoCaptureSource::setupCaptureSession(%p), unable to add audio sample buffer output delegate", this);
165         return;
166     }
167     [session() addOutput:audioOutput.get()];
168     m_audioConnection = [audioOutput.get() connectionWithMediaType:AVMediaTypeAudio];
169 }
170
171 void AVAudioCaptureSource::shutdownCaptureSession()
172 {
173     {
174         LockHolder lock(m_lock);
175
176         m_audioConnection = nullptr;
177         m_inputDescription = nullptr;
178
179         if (m_audioSourceProvider)
180             m_audioSourceProvider->unprepare();
181     }
182
183     // Don't hold the lock when destroying the audio provider, it will call back into this object
184     // to remove itself as an observer.
185     m_audioSourceProvider = nullptr;
186 }
187
188 void AVAudioCaptureSource::captureOutputDidOutputSampleBufferFromConnection(AVCaptureOutputType*, CMSampleBufferRef sampleBuffer, AVCaptureConnectionType*)
189 {
190     if (muted())
191         return;
192
193     CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
194     if (!formatDescription)
195         return;
196
197     std::unique_lock<Lock> lock(m_lock, std::try_to_lock);
198     if (!lock.owns_lock()) {
199         // Failed to acquire the lock, just return instead of blocking.
200         return;
201     }
202
203     const AudioStreamBasicDescription* streamDescription = CMAudioFormatDescriptionGetStreamBasicDescription(formatDescription);
204     if (!m_inputDescription || *m_inputDescription != *streamDescription) {
205         m_inputDescription = std::make_unique<CAAudioStreamDescription>(*streamDescription);
206
207         if (m_audioSourceProvider)
208             m_audioSourceProvider->prepare(streamDescription);
209     }
210
211     m_list = std::make_unique<WebAudioBufferList>(*m_inputDescription, sampleBuffer);
212     audioSamplesAvailable(toMediaTime(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)), *m_list, CAAudioStreamDescription(*streamDescription), CMSampleBufferGetNumSamples(sampleBuffer));
213 }
214
215 AudioSourceProvider* AVAudioCaptureSource::audioSourceProvider()
216 {
217     if (!m_audioSourceProvider)
218         m_audioSourceProvider = WebAudioSourceProviderAVFObjC::create(*this);
219
220     return m_audioSourceProvider.get();
221 }
222
223 } // namespace WebCore
224
225 #endif // ENABLE(MEDIA_STREAM)