Introduce a ThreadSafeRefCounted parameter to ensure being destroyed on the main...
[WebKit-https.git] / Source / WebCore / platform / mediastream / mac / AudioTrackPrivateMediaStreamCocoa.cpp
1 /*
2  * Copyright (C) 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "AudioTrackPrivateMediaStreamCocoa.h"
28
29 #include "AudioSampleBufferList.h"
30 #include "AudioSampleDataSource.h"
31 #include "AudioSession.h"
32 #include "CAAudioStreamDescription.h"
33 #include "Logging.h"
34
35 #include <pal/cf/CoreMediaSoftLink.h>
36 #include <pal/spi/cocoa/AudioToolboxSPI.h>
37
38 #if ENABLE(VIDEO_TRACK) && ENABLE(MEDIA_STREAM)
39
40 namespace WebCore {
41
42 AudioTrackPrivateMediaStreamCocoa::AudioTrackPrivateMediaStreamCocoa(MediaStreamTrackPrivate& track)
43     : AudioTrackPrivateMediaStream(track)
44 {
45     track.source().addObserver(*this);
46 }
47
48 AudioTrackPrivateMediaStreamCocoa::~AudioTrackPrivateMediaStreamCocoa()
49 {
50     streamTrack().source().removeObserver(*this);
51
52     if (m_dataSource)
53         m_dataSource->setPaused(true);
54
55     if (m_remoteIOUnit) {
56         AudioOutputUnitStop(m_remoteIOUnit);
57         AudioComponentInstanceDispose(m_remoteIOUnit);
58         m_remoteIOUnit = nullptr;
59     }
60
61     m_dataSource = nullptr;
62     m_inputDescription = nullptr;
63     m_outputDescription = nullptr;
64 }
65
66 void AudioTrackPrivateMediaStreamCocoa::playInternal()
67 {
68     if (m_isPlaying)
69         return;
70
71     if (m_remoteIOUnit) {
72         ASSERT(m_dataSource);
73         m_dataSource->setPaused(false);
74         if (!AudioOutputUnitStart(m_remoteIOUnit))
75             m_isPlaying = true;
76     }
77
78     m_autoPlay = !m_isPlaying;
79 }
80
81 void AudioTrackPrivateMediaStreamCocoa::play()
82 {
83     playInternal();
84 }
85
86 void AudioTrackPrivateMediaStreamCocoa::pause()
87 {
88     m_isPlaying = false;
89     m_autoPlay = false;
90
91     if (m_remoteIOUnit)
92         AudioOutputUnitStop(m_remoteIOUnit);
93     if (m_dataSource)
94         m_dataSource->setPaused(true);
95 }
96
97 void AudioTrackPrivateMediaStreamCocoa::setVolume(float volume)
98 {
99     m_volume = volume;
100     if (m_dataSource)
101         m_dataSource->setVolume(m_volume);
102 }
103
104 AudioComponentInstance AudioTrackPrivateMediaStreamCocoa::createAudioUnit(CAAudioStreamDescription& outputDescription)
105 {
106     AudioComponentInstance remoteIOUnit { nullptr };
107
108     AudioComponentDescription ioUnitDescription { kAudioUnitType_Output, 0, kAudioUnitManufacturer_Apple, 0, 0 };
109 #if PLATFORM(IOS)
110     ioUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
111 #else
112     ioUnitDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
113 #endif
114
115     AudioComponent ioComponent = AudioComponentFindNext(nullptr, &ioUnitDescription);
116     ASSERT(ioComponent);
117     if (!ioComponent) {
118         ERROR_LOG(LOGIDENTIFIER, "unable to find remote IO unit component");
119         return nullptr;
120     }
121
122     OSStatus err = AudioComponentInstanceNew(ioComponent, &remoteIOUnit);
123     if (err) {
124         ERROR_LOG(LOGIDENTIFIER, "unable to open vpio unit, error = ", err, " (", (const char*)&err, ")");
125         return nullptr;
126     }
127
128 #if PLATFORM(IOS)
129     UInt32 param = 1;
130     err = AudioUnitSetProperty(remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &param, sizeof(param));
131     if (err) {
132         ERROR_LOG(LOGIDENTIFIER, "unable to enable vpio unit output, error = ", err, " (", (const char*)&err, ")");
133         return nullptr;
134     }
135 #endif
136
137     AURenderCallbackStruct callback = { inputProc, this };
138     err = AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
139     if (err) {
140         ERROR_LOG(LOGIDENTIFIER, "unable to set vpio unit speaker proc, error = ", err, " (", (const char*)&err, ")");
141         return nullptr;
142     }
143
144     UInt32 size = sizeof(outputDescription.streamDescription());
145     err  = AudioUnitGetProperty(remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outputDescription.streamDescription(), &size);
146     if (err) {
147         ERROR_LOG(LOGIDENTIFIER, "unable to get input stream format, error = ", err, " (", (const char*)&err, ")");
148         return nullptr;
149     }
150
151     outputDescription.streamDescription().mSampleRate = AudioSession::sharedSession().sampleRate();
152
153     err = AudioUnitSetProperty(remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outputDescription.streamDescription(), sizeof(outputDescription.streamDescription()));
154     if (err) {
155         ERROR_LOG(LOGIDENTIFIER, "unable to set input stream format, error = ", err, " (", (const char*)&err, ")");
156         return nullptr;
157     }
158
159     err = AudioUnitInitialize(remoteIOUnit);
160     if (err) {
161         ERROR_LOG(LOGIDENTIFIER, "AudioUnitInitialize() failed, error = ", err, " (", (const char*)&err, ")");
162         return nullptr;
163     }
164
165     return remoteIOUnit;
166 }
167
168 void AudioTrackPrivateMediaStreamCocoa::audioSamplesAvailable(const MediaTime& sampleTime, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t sampleCount)
169 {
170     // This function is called on a background thread. The following protectedThis object ensures the object is not
171     // destroyed on the main thread before this function exits.
172     Ref<AudioTrackPrivateMediaStreamCocoa> protectedThis { *this };
173
174     ASSERT(description.platformDescription().type == PlatformDescription::CAAudioStreamBasicType);
175
176     if (!m_inputDescription || *m_inputDescription != description) {
177
178         m_inputDescription = nullptr;
179         m_outputDescription = nullptr;
180
181         if (m_remoteIOUnit) {
182             AudioOutputUnitStop(m_remoteIOUnit);
183             AudioComponentInstanceDispose(m_remoteIOUnit);
184             m_remoteIOUnit = nullptr;
185         }
186
187         CAAudioStreamDescription inputDescription = toCAAudioStreamDescription(description);
188         CAAudioStreamDescription outputDescription;
189
190         auto remoteIOUnit = createAudioUnit(outputDescription);
191         if (!remoteIOUnit)
192             return;
193
194         m_inputDescription = std::make_unique<CAAudioStreamDescription>(inputDescription);
195         m_outputDescription = std::make_unique<CAAudioStreamDescription>(outputDescription);
196
197         if (!m_dataSource)
198             m_dataSource = AudioSampleDataSource::create(description.sampleRate() * 2);
199
200         if (m_dataSource->setInputFormat(inputDescription) || m_dataSource->setOutputFormat(outputDescription)) {
201             AudioComponentInstanceDispose(remoteIOUnit);
202             return;
203         }
204
205         m_dataSource->setVolume(m_volume);
206         m_remoteIOUnit = remoteIOUnit;
207     }
208
209     m_dataSource->pushSamples(sampleTime, audioData, sampleCount);
210
211     if (m_autoPlay)
212         playInternal();
213 }
214
215 void AudioTrackPrivateMediaStreamCocoa::sourceStopped()
216 {
217     pause();
218 }
219
220 OSStatus AudioTrackPrivateMediaStreamCocoa::render(UInt32 sampleCount, AudioBufferList& ioData, UInt32 /*inBusNumber*/, const AudioTimeStamp& timeStamp, AudioUnitRenderActionFlags& actionFlags)
221 {
222     // This function is called on a high-priority background thread. The following protectedThis object ensures the object is not
223     // destroyed on the main thread before this function exits.
224     Ref<AudioTrackPrivateMediaStreamCocoa> protectedThis { *this };
225
226     if (!m_isPlaying || m_muted || !m_dataSource || streamTrack().muted() || streamTrack().ended() || !streamTrack().enabled()) {
227         AudioSampleBufferList::zeroABL(ioData, static_cast<size_t>(sampleCount * m_outputDescription->bytesPerFrame()));
228         actionFlags = kAudioUnitRenderAction_OutputIsSilence;
229         return 0;
230     }
231
232     m_dataSource->pullSamples(ioData, static_cast<size_t>(sampleCount), timeStamp.mSampleTime, timeStamp.mHostTime, AudioSampleDataSource::Copy);
233
234     return 0;
235 }
236
237 OSStatus AudioTrackPrivateMediaStreamCocoa::inputProc(void* userData, AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* timeStamp, UInt32 inBusNumber, UInt32 sampleCount, AudioBufferList* ioData)
238 {
239     return static_cast<AudioTrackPrivateMediaStreamCocoa*>(userData)->render(sampleCount, *ioData, inBusNumber, *timeStamp, *actionFlags);
240 }
241
242
243 } // namespace WebCore
244
245 #endif // ENABLE(VIDEO_TRACK) && ENABLE(MEDIA_STREAM)