Make passing PlatformAudioData in audioSamplesAvaliable const-correct.
[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 "CoreMediaSoftLink.h"
36
37 #if ENABLE(VIDEO_TRACK)
38
39 namespace WebCore {
40
41 const int renderBufferSize = 128;
42
43 AudioTrackPrivateMediaStreamCocoa::AudioTrackPrivateMediaStreamCocoa(MediaStreamTrackPrivate& track)
44     : AudioTrackPrivateMediaStream(track)
45 {
46     track.source().addObserver(*this);
47 }
48
49 AudioTrackPrivateMediaStreamCocoa::~AudioTrackPrivateMediaStreamCocoa()
50 {
51     std::lock_guard<Lock> lock(m_internalStateLock);
52
53     streamTrack().source().removeObserver(*this);
54
55     if (m_dataSource)
56         m_dataSource->setPaused(true);
57
58     if (m_remoteIOUnit) {
59         AudioOutputUnitStop(m_remoteIOUnit);
60         AudioComponentInstanceDispose(m_remoteIOUnit);
61         m_remoteIOUnit = nullptr;
62     }
63
64     m_dataSource = nullptr;
65     m_inputDescription = nullptr;
66     m_outputDescription = nullptr;
67 }
68
69 void AudioTrackPrivateMediaStreamCocoa::playInternal()
70 {
71     ASSERT(m_internalStateLock.isHeld());
72
73     if (m_isPlaying)
74         return;
75
76     if (m_remoteIOUnit) {
77         ASSERT(m_dataSource);
78         m_dataSource->setPaused(false);
79         if (!AudioOutputUnitStart(m_remoteIOUnit))
80             m_isPlaying = true;
81     }
82
83     m_autoPlay = !m_isPlaying;
84 }
85
86 void AudioTrackPrivateMediaStreamCocoa::play()
87 {
88     std::lock_guard<Lock> lock(m_internalStateLock);
89     playInternal();
90 }
91
92 void AudioTrackPrivateMediaStreamCocoa::pause()
93 {
94     std::lock_guard<Lock> lock(m_internalStateLock);
95
96     m_isPlaying = false;
97     m_autoPlay = false;
98
99     if (m_remoteIOUnit)
100         AudioOutputUnitStop(m_remoteIOUnit);
101     if (m_dataSource)
102         m_dataSource->setPaused(true);
103 }
104
105 void AudioTrackPrivateMediaStreamCocoa::setVolume(float volume)
106 {
107     m_volume = volume;
108     if (m_dataSource)
109         m_dataSource->setVolume(m_volume);
110 }
111
112 OSStatus AudioTrackPrivateMediaStreamCocoa::setupAudioUnit()
113 {
114     ASSERT(m_internalStateLock.isHeld());
115
116     AudioComponentDescription ioUnitDescription { kAudioUnitType_Output, 0, kAudioUnitManufacturer_Apple, 0, 0 };
117 #if PLATFORM(IOS)
118     ioUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
119 #else
120     ioUnitDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
121 #endif
122
123     AudioComponent ioComponent = AudioComponentFindNext(nullptr, &ioUnitDescription);
124     ASSERT(ioComponent);
125     if (!ioComponent) {
126         LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnit(%p) unable to find remote IO unit component", this);
127         return -1;
128     }
129
130     OSStatus err = AudioComponentInstanceNew(ioComponent, &m_remoteIOUnit);
131     if (err) {
132         LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnit(%p) unable to open vpio unit, error %d (%.4s)", this, (int)err, (char*)&err);
133         return -1;
134     }
135
136 #if PLATFORM(IOS)
137     UInt32 param = 1;
138     err = AudioUnitSetProperty(m_remoteIOUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &param, sizeof(param));
139     if (err) {
140         LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnit(%p) unable to enable vpio unit output, error %d (%.4s)", this, (int)err, (char*)&err);
141         return err;
142     }
143 #endif
144
145     AURenderCallbackStruct callback = { inputProc, this };
146     err = AudioUnitSetProperty(m_remoteIOUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
147     if (err) {
148         LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnit(%p) unable to set vpio unit speaker proc, error %d (%.4s)", this, (int)err, (char*)&err);
149         return err;
150     }
151
152     AudioStreamBasicDescription outputDescription = { };
153     UInt32 size = sizeof(outputDescription);
154     err  = AudioUnitGetProperty(m_remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outputDescription, &size);
155     if (err) {
156         LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnits(%p) unable to get input stream format, error %d (%.4s)", this, (int)err, (char*)&err);
157         return err;
158     }
159
160     outputDescription = m_inputDescription->streamDescription();
161     outputDescription.mSampleRate = AudioSession::sharedSession().sampleRate();
162
163     err = AudioUnitSetProperty(m_remoteIOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outputDescription, sizeof(outputDescription));
164     if (err) {
165         LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnits(%p) unable to set input stream format, error %d (%.4s)", this, (int)err, (char*)&err);
166         return err;
167     }
168     m_outputDescription = std::make_unique<CAAudioStreamDescription>(outputDescription);
169
170     err = AudioUnitInitialize(m_remoteIOUnit);
171     if (err) {
172         LOG(Media, "AudioTrackPrivateMediaStreamCocoa::setupAudioUnits(%p) AudioUnitInitialize() failed, error %d (%.4s)", this, (int)err, (char*)&err);
173         return err;
174     }
175
176     AudioSession::sharedSession().setPreferredBufferSize(renderBufferSize);
177
178     return err;
179 }
180
181 void AudioTrackPrivateMediaStreamCocoa::audioSamplesAvailable(const MediaTime& sampleTime, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t sampleCount)
182 {
183     ASSERT(description.platformDescription().type == PlatformDescription::CAAudioStreamBasicType);
184
185     std::lock_guard<Lock> lock(m_internalStateLock);
186
187     CAAudioStreamDescription streamDescription = toCAAudioStreamDescription(description);
188     if (!m_inputDescription || *m_inputDescription != description) {
189
190         m_inputDescription = nullptr;
191         m_outputDescription = nullptr;
192
193         if (m_remoteIOUnit) {
194             AudioOutputUnitStop(m_remoteIOUnit);
195             AudioComponentInstanceDispose(m_remoteIOUnit);
196             m_remoteIOUnit = nullptr;
197         }
198
199         m_inputDescription = std::make_unique<CAAudioStreamDescription>(streamDescription);
200         if (setupAudioUnit()) {
201             m_inputDescription = nullptr;
202             return;
203         }
204
205         if (!m_dataSource)
206             m_dataSource = AudioSampleDataSource::create(description.sampleRate() * 2);
207         if (!m_dataSource)
208             return;
209
210         if (m_dataSource->setInputFormat(streamDescription))
211             return;
212         if (m_dataSource->setOutputFormat(*m_outputDescription.get()))
213             return;
214
215         m_dataSource->setVolume(m_volume);
216     }
217
218     m_dataSource->pushSamples(sampleTime, audioData, sampleCount);
219
220     if (m_autoPlay)
221         playInternal();
222 }
223
224 void AudioTrackPrivateMediaStreamCocoa::sourceStopped()
225 {
226     pause();
227 }
228
229 OSStatus AudioTrackPrivateMediaStreamCocoa::render(UInt32 sampleCount, AudioBufferList& ioData, UInt32 /*inBusNumber*/, const AudioTimeStamp& timeStamp, AudioUnitRenderActionFlags& actionFlags)
230 {
231     std::unique_lock<Lock> lock(m_internalStateLock, std::try_to_lock);
232     if (!lock.owns_lock())
233         return kAudioConverterErr_UnspecifiedError;
234
235     if (!m_isPlaying || m_muted || !m_dataSource || streamTrack().muted() || streamTrack().ended() || !streamTrack().enabled()) {
236         AudioSampleBufferList::zeroABL(ioData, static_cast<size_t>(sampleCount));
237         actionFlags = kAudioUnitRenderAction_OutputIsSilence;
238         return 0;
239     }
240
241     m_dataSource->pullSamples(ioData, static_cast<size_t>(sampleCount), timeStamp.mSampleTime, timeStamp.mHostTime, AudioSampleDataSource::Copy);
242
243     return 0;
244 }
245
246 OSStatus AudioTrackPrivateMediaStreamCocoa::inputProc(void* userData, AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* timeStamp, UInt32 inBusNumber, UInt32 sampleCount, AudioBufferList* ioData)
247 {
248     return static_cast<AudioTrackPrivateMediaStreamCocoa*>(userData)->render(sampleCount, *ioData, inBusNumber, *timeStamp, *actionFlags);
249 }
250
251
252 } // namespace WebCore
253
254 #endif // ENABLE(VIDEO_TRACK)