1 | /* |
2 | * Copyright (C) 2012, Google 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'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
15 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
16 | * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
17 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
18 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
19 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
22 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | |
27 | #if ENABLE(WEB_AUDIO) && ENABLE(MEDIA_STREAM) |
28 | |
29 | #include "MediaStreamAudioSourceNode.h" |
30 | |
31 | #include "AudioContext.h" |
32 | #include "AudioNodeOutput.h" |
33 | #include "Logging.h" |
34 | #include <wtf/IsoMallocInlines.h> |
35 | #include <wtf/Locker.h> |
36 | |
37 | namespace WebCore { |
38 | |
39 | WTF_MAKE_ISO_ALLOCATED_IMPL(MediaStreamAudioSourceNode); |
40 | |
41 | Ref<MediaStreamAudioSourceNode> MediaStreamAudioSourceNode::create(AudioContext& context, MediaStream& mediaStream, MediaStreamTrack& audioTrack) |
42 | { |
43 | return adoptRef(*new MediaStreamAudioSourceNode(context, mediaStream, audioTrack)); |
44 | } |
45 | |
46 | MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext& context, MediaStream& mediaStream, MediaStreamTrack& audioTrack) |
47 | : AudioNode(context, context.sampleRate()) |
48 | , m_mediaStream(mediaStream) |
49 | , m_audioTrack(audioTrack) |
50 | { |
51 | setNodeType(NodeTypeMediaStreamAudioSource); |
52 | |
53 | AudioSourceProvider* audioSourceProvider = m_audioTrack->audioSourceProvider(); |
54 | ASSERT(audioSourceProvider); |
55 | |
56 | audioSourceProvider->setClient(this); |
57 | |
58 | // Default to stereo. This could change depending on the format of the MediaStream's audio track. |
59 | addOutput(std::make_unique<AudioNodeOutput>(this, 2)); |
60 | |
61 | initialize(); |
62 | } |
63 | |
64 | MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode() |
65 | { |
66 | AudioSourceProvider* audioSourceProvider = m_audioTrack->audioSourceProvider(); |
67 | ASSERT(audioSourceProvider); |
68 | audioSourceProvider->setClient(nullptr); |
69 | uninitialize(); |
70 | } |
71 | |
72 | void MediaStreamAudioSourceNode::setFormat(size_t numberOfChannels, float sourceSampleRate) |
73 | { |
74 | float sampleRate = this->sampleRate(); |
75 | if (numberOfChannels == m_sourceNumberOfChannels && sourceSampleRate == sampleRate) |
76 | return; |
77 | |
78 | // The sample-rate must be equal to the context's sample-rate. |
79 | if (!numberOfChannels || numberOfChannels > AudioContext::maxNumberOfChannels()) { |
80 | // process() will generate silence for these uninitialized values. |
81 | LOG(Media, "MediaStreamAudioSourceNode::setFormat(%u, %f) - unhandled format change" , static_cast<unsigned>(numberOfChannels), sourceSampleRate); |
82 | m_sourceNumberOfChannels = 0; |
83 | return; |
84 | } |
85 | |
86 | // Synchronize with process(). |
87 | std::lock_guard<Lock> lock(m_processMutex); |
88 | |
89 | m_sourceNumberOfChannels = numberOfChannels; |
90 | m_sourceSampleRate = sourceSampleRate; |
91 | |
92 | if (sourceSampleRate == sampleRate) |
93 | m_multiChannelResampler = nullptr; |
94 | else { |
95 | double scaleFactor = sourceSampleRate / sampleRate; |
96 | m_multiChannelResampler = std::make_unique<MultiChannelResampler>(scaleFactor, numberOfChannels); |
97 | } |
98 | |
99 | m_sourceNumberOfChannels = numberOfChannels; |
100 | |
101 | { |
102 | // The context must be locked when changing the number of output channels. |
103 | AudioContext::AutoLocker contextLocker(context()); |
104 | |
105 | // Do any necesssary re-configuration to the output's number of channels. |
106 | output(0)->setNumberOfChannels(numberOfChannels); |
107 | } |
108 | } |
109 | |
110 | void MediaStreamAudioSourceNode::process(size_t numberOfFrames) |
111 | { |
112 | AudioBus* outputBus = output(0)->bus(); |
113 | AudioSourceProvider* provider = m_audioTrack->audioSourceProvider(); |
114 | |
115 | if (!mediaStream() || !m_sourceNumberOfChannels || !m_sourceSampleRate || !provider) { |
116 | outputBus->zero(); |
117 | return; |
118 | } |
119 | |
120 | // Use std::try_to_lock to avoid contention in the real-time audio thread. |
121 | // If we fail to acquire the lock then the MediaStream must be in the middle of |
122 | // a format change, so we output silence in this case. |
123 | std::unique_lock<Lock> lock(m_processMutex, std::try_to_lock); |
124 | if (!lock.owns_lock()) { |
125 | // We failed to acquire the lock. |
126 | outputBus->zero(); |
127 | return; |
128 | } |
129 | |
130 | if (m_multiChannelResampler.get()) { |
131 | ASSERT(m_sourceSampleRate != sampleRate()); |
132 | m_multiChannelResampler->process(provider, outputBus, numberOfFrames); |
133 | } else { |
134 | // Bypass the resampler completely if the source is at the context's sample-rate. |
135 | ASSERT(m_sourceSampleRate == sampleRate()); |
136 | provider->provideInput(outputBus, numberOfFrames); |
137 | } |
138 | } |
139 | |
140 | } // namespace WebCore |
141 | |
142 | #endif // ENABLE(WEB_AUDIO) && ENABLE(MEDIA_STREAM) |
143 | |