1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MediaStreamGraphImpl.h"
7 #include "MediaStreamListener.h"
8 #include "mozilla/MathAlgorithms.h"
9 #include "mozilla/Unused.h"
11 #include "AudioSegment.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/Attributes.h"
14 #include "AudioCaptureStream.h"
15 #include "ImageContainer.h"
16 #include "AudioNodeEngine.h"
17 #include "AudioNodeStream.h"
18 #include "AudioNodeExternalInputStream.h"
19 #include "webaudio/MediaStreamAudioDestinationNode.h"
21 #include "DOMMediaStream.h"
23 using namespace mozilla::layers
;
24 using namespace mozilla::dom
;
25 using namespace mozilla::gfx
;
30 // We are mixing to mono until PeerConnection can accept stereo
31 static const uint32_t MONO
= 1;
33 AudioCaptureStream::AudioCaptureStream(TrackID aTrackId
)
34 : ProcessedMediaStream()
37 , mTrackCreated(false)
39 MOZ_ASSERT(NS_IsMainThread());
40 MOZ_COUNT_CTOR(AudioCaptureStream
);
41 mMixer
.AddCallback(this);
44 AudioCaptureStream::~AudioCaptureStream()
46 MOZ_COUNT_DTOR(AudioCaptureStream
);
47 mMixer
.RemoveCallback(this);
51 AudioCaptureStream::Start()
53 class Message
: public ControlMessage
{
55 explicit Message(AudioCaptureStream
* aStream
)
56 : ControlMessage(aStream
), mStream(aStream
) {}
60 mStream
->mStarted
= true;
64 AudioCaptureStream
* mStream
;
66 GraphImpl()->AppendMessage(MakeUnique
<Message
>(this));
70 AudioCaptureStream::ProcessInput(GraphTime aFrom
, GraphTime aTo
,
77 uint32_t inputCount
= mInputs
.Length();
78 StreamTracks::Track
* track
= EnsureTrack(mTrackId
);
79 // Notify the DOM everything is in order.
81 for (uint32_t i
= 0; i
< mListeners
.Length(); i
++) {
82 MediaStreamListener
* l
= mListeners
[i
];
84 l
->NotifyQueuedTrackChanges(
85 Graph(), mTrackId
, 0, TrackEventCommand::TRACK_EVENT_CREATED
, tmp
);
86 l
->NotifyFinishedTrackCreation(Graph());
91 if (IsFinishedOnGraphThread()) {
95 // If the captured stream is connected back to a object on the page (be it an
96 // HTMLMediaElement with a stream as source, or an AudioContext), a cycle
97 // situation occur. This can work if it's an AudioContext with at least one
98 // DelayNode, but the MSG will mute the whole cycle otherwise.
99 if (InMutedCycle() || inputCount
== 0) {
100 track
->Get
<AudioSegment
>()->AppendNullData(aTo
- aFrom
);
102 // We mix down all the tracks of all inputs, to a stereo track. Everything
103 // is {up,down}-mixed to stereo.
104 mMixer
.StartMixing();
106 for (uint32_t i
= 0; i
< inputCount
; i
++) {
107 MediaStream
* s
= mInputs
[i
]->GetSource();
108 StreamTracks::TrackIter
track(s
->GetStreamTracks(), MediaSegment::AUDIO
);
109 if (track
.IsEnded()) {
110 // No tracks for this input. Still we append data to trigger the mixer.
112 toMix
.AppendNullData(aTo
- aFrom
);
113 toMix
.Mix(mMixer
, MONO
, Graph()->GraphRate());
115 for (; !track
.IsEnded(); track
.Next()) {
116 AudioSegment
* inputSegment
= track
->Get
<AudioSegment
>();
117 StreamTime inputStart
= s
->GraphTimeToStreamTimeWithBlocking(aFrom
);
118 StreamTime inputEnd
= s
->GraphTimeToStreamTimeWithBlocking(aTo
);
120 if (track
->IsEnded() && inputSegment
->GetDuration() <= inputStart
) {
121 toMix
.AppendNullData(aTo
- aFrom
);
123 toMix
.AppendSlice(*inputSegment
, inputStart
, inputEnd
);
124 // Care for streams blocked in the [aTo, aFrom] range.
125 if (inputEnd
- inputStart
< aTo
- aFrom
) {
126 toMix
.AppendNullData((aTo
- aFrom
) - (inputEnd
- inputStart
));
129 toMix
.Mix(mMixer
, MONO
, Graph()->GraphRate());
132 // This calls MixerCallback below
133 mMixer
.FinishMixing();
136 // Regardless of the status of the input tracks, we go foward.
137 mTracks
.AdvanceKnownTracksTime(GraphTimeToStreamTimeWithBlocking((aTo
)));
141 AudioCaptureStream::MixerCallback(AudioDataValue
* aMixedBuffer
,
142 AudioSampleFormat aFormat
, uint32_t aChannels
,
143 uint32_t aFrames
, uint32_t aSampleRate
)
145 AutoTArray
<nsTArray
<AudioDataValue
>, MONO
> output
;
146 AutoTArray
<const AudioDataValue
*, MONO
> bufferPtrs
;
147 output
.SetLength(MONO
);
148 bufferPtrs
.SetLength(MONO
);
150 uint32_t written
= 0;
151 // We need to copy here, because the mixer will reuse the storage, we should
152 // not hold onto it. Buffers are in planar format.
153 for (uint32_t channel
= 0; channel
< aChannels
; channel
++) {
154 AudioDataValue
* out
= output
[channel
].AppendElements(aFrames
);
155 PodCopy(out
, aMixedBuffer
+ written
, aFrames
);
156 bufferPtrs
[channel
] = out
;
160 chunk
.mBuffer
= new mozilla::SharedChannelArrayBuffer
<AudioDataValue
>(&output
);
161 chunk
.mDuration
= aFrames
;
162 chunk
.mBufferFormat
= aFormat
;
163 chunk
.mChannelData
.SetLength(MONO
);
164 for (uint32_t channel
= 0; channel
< aChannels
; channel
++) {
165 chunk
.mChannelData
[channel
] = bufferPtrs
[channel
];
168 // Now we have mixed data, simply append it to out track.
169 EnsureTrack(mTrackId
)->Get
<AudioSegment
>()->AppendAndConsumeChunk(&chunk
);