content/media/AudioSegment.h

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:0acf7367f803
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/. */
5
6 #ifndef MOZILLA_AUDIOSEGMENT_H_
7 #define MOZILLA_AUDIOSEGMENT_H_
8
9 #include "MediaSegment.h"
10 #include "AudioSampleFormat.h"
11 #include "SharedBuffer.h"
12 #include "WebAudioUtils.h"
13 #ifdef MOZILLA_INTERNAL_API
14 #include "mozilla/TimeStamp.h"
15 #endif
16
17 namespace mozilla {
18
19 template<typename T>
20 class SharedChannelArrayBuffer : public ThreadSharedObject {
21 public:
22 SharedChannelArrayBuffer(nsTArray<nsTArray<T> >* aBuffers)
23 {
24 mBuffers.SwapElements(*aBuffers);
25 }
26
27 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
28 {
29 size_t amount = 0;
30 amount += mBuffers.SizeOfExcludingThis(aMallocSizeOf);
31 for (size_t i = 0; i < mBuffers.Length(); i++) {
32 amount += mBuffers[i].SizeOfExcludingThis(aMallocSizeOf);
33 }
34
35 return amount;
36 }
37
38 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
39 {
40 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
41 }
42
43 nsTArray<nsTArray<T> > mBuffers;
44 };
45
46 class AudioStream;
47 class AudioMixer;
48
49 /**
50 * For auto-arrays etc, guess this as the common number of channels.
51 */
52 const int GUESS_AUDIO_CHANNELS = 2;
53
54 // We ensure that the graph advances in steps that are multiples of the Web
55 // Audio block size
56 const uint32_t WEBAUDIO_BLOCK_SIZE_BITS = 7;
57 const uint32_t WEBAUDIO_BLOCK_SIZE = 1 << WEBAUDIO_BLOCK_SIZE_BITS;
58
59 void InterleaveAndConvertBuffer(const void** aSourceChannels,
60 AudioSampleFormat aSourceFormat,
61 int32_t aLength, float aVolume,
62 int32_t aChannels,
63 AudioDataValue* aOutput);
64
65 /**
66 * Given an array of input channels (aChannelData), downmix to aOutputChannels,
67 * interleave the channel data. A total of aOutputChannels*aDuration
68 * interleaved samples will be copied to a channel buffer in aOutput.
69 */
70 void DownmixAndInterleave(const nsTArray<const void*>& aChannelData,
71 AudioSampleFormat aSourceFormat, int32_t aDuration,
72 float aVolume, uint32_t aOutputChannels,
73 AudioDataValue* aOutput);
74
75 /**
76 * An AudioChunk represents a multi-channel buffer of audio samples.
77 * It references an underlying ThreadSharedObject which manages the lifetime
78 * of the buffer. An AudioChunk maintains its own duration and channel data
79 * pointers so it can represent a subinterval of a buffer without copying.
80 * An AudioChunk can store its individual channels anywhere; it maintains
81 * separate pointers to each channel's buffer.
82 */
83 struct AudioChunk {
84 typedef mozilla::AudioSampleFormat SampleFormat;
85
86 // Generic methods
87 void SliceTo(TrackTicks aStart, TrackTicks aEnd)
88 {
89 NS_ASSERTION(aStart >= 0 && aStart < aEnd && aEnd <= mDuration,
90 "Slice out of bounds");
91 if (mBuffer) {
92 MOZ_ASSERT(aStart < INT32_MAX, "Can't slice beyond 32-bit sample lengths");
93 for (uint32_t channel = 0; channel < mChannelData.Length(); ++channel) {
94 mChannelData[channel] = AddAudioSampleOffset(mChannelData[channel],
95 mBufferFormat, int32_t(aStart));
96 }
97 }
98 mDuration = aEnd - aStart;
99 }
100 TrackTicks GetDuration() const { return mDuration; }
101 bool CanCombineWithFollowing(const AudioChunk& aOther) const
102 {
103 if (aOther.mBuffer != mBuffer) {
104 return false;
105 }
106 if (mBuffer) {
107 NS_ASSERTION(aOther.mBufferFormat == mBufferFormat,
108 "Wrong metadata about buffer");
109 NS_ASSERTION(aOther.mChannelData.Length() == mChannelData.Length(),
110 "Mismatched channel count");
111 if (mDuration > INT32_MAX) {
112 return false;
113 }
114 for (uint32_t channel = 0; channel < mChannelData.Length(); ++channel) {
115 if (aOther.mChannelData[channel] != AddAudioSampleOffset(mChannelData[channel],
116 mBufferFormat, int32_t(mDuration))) {
117 return false;
118 }
119 }
120 }
121 return true;
122 }
123 bool IsNull() const { return mBuffer == nullptr; }
124 void SetNull(TrackTicks aDuration)
125 {
126 mBuffer = nullptr;
127 mChannelData.Clear();
128 mDuration = aDuration;
129 mVolume = 1.0f;
130 mBufferFormat = AUDIO_FORMAT_SILENCE;
131 }
132 int ChannelCount() const { return mChannelData.Length(); }
133
134 size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
135 {
136 return SizeOfExcludingThis(aMallocSizeOf, true);
137 }
138
139 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf, bool aUnshared) const
140 {
141 size_t amount = 0;
142
143 // Possibly owned:
144 // - mBuffer - Can hold data that is also in the decoded audio queue. If it
145 // is not shared, or unshared == false it gets counted.
146 if (mBuffer && (!aUnshared || !mBuffer->IsShared())) {
147 amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
148 }
149
150 // Memory in the array is owned by mBuffer.
151 amount += mChannelData.SizeOfExcludingThis(aMallocSizeOf);
152 return amount;
153 }
154
155 TrackTicks mDuration; // in frames within the buffer
156 nsRefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes
157 nsTArray<const void*> mChannelData; // one pointer per channel; empty if and only if mBuffer is null
158 float mVolume; // volume multiplier to apply (1.0f if mBuffer is nonnull)
159 SampleFormat mBufferFormat; // format of frames in mBuffer (only meaningful if mBuffer is nonnull)
160 #ifdef MOZILLA_INTERNAL_API
161 mozilla::TimeStamp mTimeStamp; // time at which this has been fetched from the MediaEngine
162 #endif
163 };
164
165
166 /**
167 * A list of audio samples consisting of a sequence of slices of SharedBuffers.
168 * The audio rate is determined by the track, not stored in this class.
169 */
170 class AudioSegment : public MediaSegmentBase<AudioSegment, AudioChunk> {
171 public:
172 typedef mozilla::AudioSampleFormat SampleFormat;
173
174 AudioSegment() : MediaSegmentBase<AudioSegment, AudioChunk>(AUDIO) {}
175
176 // Resample the whole segment in place.
177 template<typename T>
178 void Resample(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate)
179 {
180 mDuration = 0;
181
182 for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
183 nsAutoTArray<nsTArray<T>, GUESS_AUDIO_CHANNELS> output;
184 nsAutoTArray<const T*, GUESS_AUDIO_CHANNELS> bufferPtrs;
185 AudioChunk& c = *ci;
186 // If this chunk is null, don't bother resampling, just alter its duration
187 if (c.IsNull()) {
188 c.mDuration = (c.mDuration * aOutRate) / aInRate;
189 mDuration += c.mDuration;
190 continue;
191 }
192 uint32_t channels = c.mChannelData.Length();
193 output.SetLength(channels);
194 bufferPtrs.SetLength(channels);
195 uint32_t inFrames = c.mDuration;
196 // Round up to allocate; the last frame may not be used.
197 NS_ASSERTION((UINT32_MAX - aInRate + 1) / c.mDuration >= aOutRate,
198 "Dropping samples");
199 uint32_t outSize = (c.mDuration * aOutRate + aInRate - 1) / aInRate;
200 for (uint32_t i = 0; i < channels; i++) {
201 const T* in = static_cast<const T*>(c.mChannelData[i]);
202 T* out = output[i].AppendElements(outSize);
203 uint32_t outFrames = outSize;
204
205 dom::WebAudioUtils::SpeexResamplerProcess(aResampler, i,
206 in, &inFrames,
207 out, &outFrames);
208 MOZ_ASSERT(inFrames == c.mDuration);
209 bufferPtrs[i] = out;
210 output[i].SetLength(outFrames);
211 }
212 MOZ_ASSERT(channels > 0);
213 c.mDuration = output[0].Length();
214 c.mBuffer = new mozilla::SharedChannelArrayBuffer<T>(&output);
215 for (uint32_t i = 0; i < channels; i++) {
216 c.mChannelData[i] = bufferPtrs[i];
217 }
218 mDuration += c.mDuration;
219 }
220 }
221
222 void ResampleChunks(SpeexResamplerState* aResampler);
223
224 void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer,
225 const nsTArray<const float*>& aChannelData,
226 int32_t aDuration)
227 {
228 AudioChunk* chunk = AppendChunk(aDuration);
229 chunk->mBuffer = aBuffer;
230 for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) {
231 chunk->mChannelData.AppendElement(aChannelData[channel]);
232 }
233 chunk->mVolume = 1.0f;
234 chunk->mBufferFormat = AUDIO_FORMAT_FLOAT32;
235 #ifdef MOZILLA_INTERNAL_API
236 chunk->mTimeStamp = TimeStamp::Now();
237 #endif
238 }
239 void AppendFrames(already_AddRefed<ThreadSharedObject> aBuffer,
240 const nsTArray<const int16_t*>& aChannelData,
241 int32_t aDuration)
242 {
243 AudioChunk* chunk = AppendChunk(aDuration);
244 chunk->mBuffer = aBuffer;
245 for (uint32_t channel = 0; channel < aChannelData.Length(); ++channel) {
246 chunk->mChannelData.AppendElement(aChannelData[channel]);
247 }
248 chunk->mVolume = 1.0f;
249 chunk->mBufferFormat = AUDIO_FORMAT_S16;
250 #ifdef MOZILLA_INTERNAL_API
251 chunk->mTimeStamp = TimeStamp::Now();
252 #endif
253 }
254 // Consumes aChunk, and returns a pointer to the persistent copy of aChunk
255 // in the segment.
256 AudioChunk* AppendAndConsumeChunk(AudioChunk* aChunk)
257 {
258 AudioChunk* chunk = AppendChunk(aChunk->mDuration);
259 chunk->mBuffer = aChunk->mBuffer.forget();
260 chunk->mChannelData.SwapElements(aChunk->mChannelData);
261 chunk->mVolume = aChunk->mVolume;
262 chunk->mBufferFormat = aChunk->mBufferFormat;
263 #ifdef MOZILLA_INTERNAL_API
264 chunk->mTimeStamp = TimeStamp::Now();
265 #endif
266 return chunk;
267 }
268 void ApplyVolume(float aVolume);
269 void WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer = nullptr);
270
271 int ChannelCount() {
272 NS_WARN_IF_FALSE(!mChunks.IsEmpty(),
273 "Cannot query channel count on a AudioSegment with no chunks.");
274 // Find the first chunk that has non-zero channels. A chunk that hs zero
275 // channels is just silence and we can simply discard it.
276 for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
277 if (ci->ChannelCount()) {
278 return ci->ChannelCount();
279 }
280 }
281 return 0;
282 }
283
284 static Type StaticType() { return AUDIO; }
285
286 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
287 {
288 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
289 }
290 };
291
292 }
293
294 #endif /* MOZILLA_AUDIOSEGMENT_H_ */

mercurial