Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/AudioSegment.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "AudioSegment.h"
7
8
#include "AudioMixer.h"
9
#include "AudioChannelFormat.h"
10
#include "Latency.h"
11
#include <speex/speex_resampler.h>
12
13
namespace mozilla {
14
15
const uint8_t SilentChannel::gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*SilentChannel::AUDIO_PROCESSING_FRAMES] = {0};
16
17
template<>
18
const float* SilentChannel::ZeroChannel<float>()
19
0
{
20
0
  return reinterpret_cast<const float*>(SilentChannel::gZeroChannel);
21
0
}
22
23
template<>
24
const int16_t* SilentChannel::ZeroChannel<int16_t>()
25
0
{
26
0
  return reinterpret_cast<const int16_t*>(SilentChannel::gZeroChannel);
27
0
}
28
29
void
30
AudioSegment::ApplyVolume(float aVolume)
31
0
{
32
0
  for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
33
0
    ci->mVolume *= aVolume;
34
0
  }
35
0
}
36
37
void AudioSegment::ResampleChunks(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate)
38
0
{
39
0
  if (mChunks.IsEmpty()) {
40
0
    return;
41
0
  }
42
0
43
0
  MOZ_ASSERT(aResampler || IsNull(), "We can only be here without a resampler if this segment is null.");
44
0
45
0
  AudioSampleFormat format = AUDIO_FORMAT_SILENCE;
46
0
  for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
47
0
    if (ci->mBufferFormat != AUDIO_FORMAT_SILENCE) {
48
0
      format = ci->mBufferFormat;
49
0
    }
50
0
  }
51
0
52
0
  switch (format) {
53
0
    // If the format is silence at this point, all the chunks are silent. The
54
0
    // actual function we use does not matter, it's just a matter of changing
55
0
    // the chunks duration.
56
0
    case AUDIO_FORMAT_SILENCE:
57
0
    case AUDIO_FORMAT_FLOAT32:
58
0
      Resample<float>(aResampler, aInRate, aOutRate);
59
0
    break;
60
0
    case AUDIO_FORMAT_S16:
61
0
      Resample<int16_t>(aResampler, aInRate, aOutRate);
62
0
    break;
63
0
    default:
64
0
      MOZ_ASSERT(false);
65
0
    break;
66
0
  }
67
0
}
68
69
// This helps to to safely get a pointer to the position we want to start
70
// writing a planar audio buffer, depending on the channel and the offset in the
71
// buffer.
72
static AudioDataValue*
73
PointerForOffsetInChannel(AudioDataValue* aData, size_t aLengthSamples,
74
                          uint32_t aChannelCount, uint32_t aChannel,
75
                          uint32_t aOffsetSamples)
76
0
{
77
0
  size_t samplesPerChannel = aLengthSamples / aChannelCount;
78
0
  size_t beginningOfChannel = samplesPerChannel * aChannel;
79
0
  MOZ_ASSERT(aChannel * samplesPerChannel + aOffsetSamples < aLengthSamples,
80
0
             "Offset request out of bounds.");
81
0
  return aData + beginningOfChannel + aOffsetSamples;
82
0
}
83
84
void
85
AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
86
                  uint32_t aSampleRate)
87
0
{
88
0
  AutoTArray<AudioDataValue, SilentChannel::AUDIO_PROCESSING_FRAMES* GUESS_AUDIO_CHANNELS>
89
0
  buf;
90
0
  AutoTArray<const AudioDataValue*, GUESS_AUDIO_CHANNELS> channelData;
91
0
  uint32_t offsetSamples = 0;
92
0
  uint32_t duration = GetDuration();
93
0
94
0
  if (duration <= 0) {
95
0
    MOZ_ASSERT(duration == 0);
96
0
    return;
97
0
  }
98
0
99
0
  uint32_t outBufferLength = duration * aOutputChannels;
100
0
  buf.SetLength(outBufferLength);
101
0
102
0
  for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
103
0
    AudioChunk& c = *ci;
104
0
    uint32_t frames = c.mDuration;
105
0
106
0
    // If the chunk is silent, simply write the right number of silence in the
107
0
    // buffers.
108
0
    if (c.mBufferFormat == AUDIO_FORMAT_SILENCE) {
109
0
      for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
110
0
        AudioDataValue* ptr =
111
0
          PointerForOffsetInChannel(buf.Elements(), outBufferLength,
112
0
                                    aOutputChannels, channel, offsetSamples);
113
0
        PodZero(ptr, frames);
114
0
      }
115
0
    } else {
116
0
      // Othewise, we need to upmix or downmix appropriately, depending on the
117
0
      // desired input and output channels.
118
0
      channelData.SetLength(c.mChannelData.Length());
119
0
      for (uint32_t i = 0; i < channelData.Length(); ++i) {
120
0
        channelData[i] = static_cast<const AudioDataValue*>(c.mChannelData[i]);
121
0
      }
122
0
      if (channelData.Length() < aOutputChannels) {
123
0
        // Up-mix.
124
0
        AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<AudioDataValue>());
125
0
        for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
126
0
          AudioDataValue* ptr =
127
0
            PointerForOffsetInChannel(buf.Elements(), outBufferLength,
128
0
                                      aOutputChannels, channel, offsetSamples);
129
0
          PodCopy(ptr, reinterpret_cast<const AudioDataValue*>(channelData[channel]),
130
0
                  frames);
131
0
        }
132
0
        MOZ_ASSERT(channelData.Length() == aOutputChannels);
133
0
      } else if (channelData.Length() > aOutputChannels) {
134
0
        // Down mix.
135
0
        AutoTArray<AudioDataValue*, GUESS_AUDIO_CHANNELS> outChannelPtrs;
136
0
        outChannelPtrs.SetLength(aOutputChannels);
137
0
        uint32_t offsetSamples = 0;
138
0
        for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
139
0
          outChannelPtrs[channel] =
140
0
            PointerForOffsetInChannel(buf.Elements(), outBufferLength,
141
0
                                      aOutputChannels, channel, offsetSamples);
142
0
        }
143
0
        AudioChannelsDownMix(channelData, outChannelPtrs.Elements(),
144
0
                             aOutputChannels, frames);
145
0
      } else {
146
0
        // The channel count is already what we want, just copy it over.
147
0
        for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
148
0
          AudioDataValue* ptr =
149
0
            PointerForOffsetInChannel(buf.Elements(), outBufferLength,
150
0
                                      aOutputChannels, channel, offsetSamples);
151
0
          PodCopy(ptr, reinterpret_cast<const AudioDataValue*>(channelData[channel]),
152
0
                  frames);
153
0
        }
154
0
      }
155
0
    }
156
0
    offsetSamples += frames;
157
0
  }
158
0
159
0
  if (offsetSamples) {
160
0
    MOZ_ASSERT(offsetSamples == outBufferLength / aOutputChannels,
161
0
               "We forgot to write some samples?");
162
0
    aMixer.Mix(buf.Elements(), aOutputChannels, offsetSamples, aSampleRate);
163
0
  }
164
0
}
165
166
void
167
AudioSegment::WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aOutputChannels, uint32_t aSampleRate)
168
0
{
169
0
  AutoTArray<AudioDataValue,SilentChannel::AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
170
0
  // Offset in the buffer that will be written to the mixer, in samples.
171
0
  uint32_t offset = 0;
172
0
173
0
  if (GetDuration() <= 0) {
174
0
    MOZ_ASSERT(GetDuration() == 0);
175
0
    return;
176
0
  }
177
0
178
0
  uint32_t outBufferLength = GetDuration() * aOutputChannels;
179
0
  buf.SetLength(outBufferLength);
180
0
181
0
182
0
  for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
183
0
    AudioChunk& c = *ci;
184
0
185
0
    switch (c.mBufferFormat) {
186
0
      case AUDIO_FORMAT_S16:
187
0
        WriteChunk<int16_t>(c, aOutputChannels, buf.Elements() + offset);
188
0
        break;
189
0
      case AUDIO_FORMAT_FLOAT32:
190
0
        WriteChunk<float>(c, aOutputChannels, buf.Elements() + offset);
191
0
        break;
192
0
      case AUDIO_FORMAT_SILENCE:
193
0
        // The mixer is expecting interleaved data, so this is ok.
194
0
        PodZero(buf.Elements() + offset, c.mDuration * aOutputChannels);
195
0
        break;
196
0
      default:
197
0
        MOZ_ASSERT(false, "Not handled");
198
0
    }
199
0
200
0
    offset += c.mDuration * aOutputChannels;
201
0
202
0
    if (!c.mTimeStamp.IsNull()) {
203
0
      TimeStamp now = TimeStamp::Now();
204
0
      // would be more efficient to c.mTimeStamp to ms on create time then pass here
205
0
      LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
206
0
              (now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp);
207
0
    }
208
0
  }
209
0
210
0
  if (offset) {
211
0
    aMixer.Mix(buf.Elements(), aOutputChannels, offset / aOutputChannels, aSampleRate);
212
0
  }
213
0
}
214
215
} // namespace mozilla