Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/platforms/agnostic/OpusDecoder.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sw=2 sts=2 et cindent: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "OpusDecoder.h"
8
#include "OpusParser.h"
9
#include "TimeUnits.h"
10
#include "VorbisUtils.h"
11
#include "VorbisDecoder.h" // For VorbisLayout
12
#include "mozilla/EndianUtils.h"
13
#include "mozilla/PodOperations.h"
14
#include "mozilla/SyncRunnable.h"
15
#include "VideoUtils.h"
16
17
#include <inttypes.h>  // For PRId64
18
19
#include "opus/opus.h"
20
extern "C" {
21
#include "opus/opus_multistream.h"
22
}
23
24
#define OPUS_DEBUG(arg, ...)                                                   \
25
0
  DDMOZ_LOG(                                                                   \
26
0
    sPDMLog, mozilla::LogLevel::Debug, "::%s: " arg, __func__, ##__VA_ARGS__)
27
28
namespace mozilla {
29
30
OpusDataDecoder::OpusDataDecoder(const CreateDecoderParams& aParams)
31
  : mInfo(aParams.AudioConfig())
32
  , mTaskQueue(aParams.mTaskQueue)
33
  , mOpusDecoder(nullptr)
34
  , mSkip(0)
35
  , mDecodedHeader(false)
36
  , mPaddingDiscarded(false)
37
  , mFrames(0)
38
  , mChannelMap(AudioConfig::ChannelLayout::UNKNOWN_MAP)
39
0
{
40
0
}
41
42
OpusDataDecoder::~OpusDataDecoder()
43
0
{
44
0
  if (mOpusDecoder) {
45
0
    opus_multistream_decoder_destroy(mOpusDecoder);
46
0
    mOpusDecoder = nullptr;
47
0
  }
48
0
}
49
50
RefPtr<ShutdownPromise>
51
OpusDataDecoder::Shutdown()
52
0
{
53
0
  RefPtr<OpusDataDecoder> self = this;
54
0
  return InvokeAsync(mTaskQueue, __func__, [self]() {
55
0
    return ShutdownPromise::CreateAndResolve(true, __func__);
56
0
  });
57
0
}
58
59
void
60
OpusDataDecoder::AppendCodecDelay(MediaByteBuffer* config, uint64_t codecDelayUS)
61
0
{
62
0
  uint8_t buffer[sizeof(uint64_t)];
63
0
  BigEndian::writeUint64(buffer, codecDelayUS);
64
0
  config->AppendElements(buffer, sizeof(uint64_t));
65
0
}
66
67
RefPtr<MediaDataDecoder::InitPromise>
68
OpusDataDecoder::Init()
69
0
{
70
0
  size_t length = mInfo.mCodecSpecificConfig->Length();
71
0
  uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
72
0
  if (length < sizeof(uint64_t)) {
73
0
    OPUS_DEBUG("CodecSpecificConfig too short to read codecDelay!");
74
0
    return InitPromise::CreateAndReject(
75
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
76
0
                  RESULT_DETAIL("CodecSpecificConfig too short to read codecDelay!")),
77
0
      __func__);
78
0
  }
79
0
  int64_t codecDelay = BigEndian::readUint64(p);
80
0
  length -= sizeof(uint64_t);
81
0
  p += sizeof(uint64_t);
82
0
  if (NS_FAILED(DecodeHeader(p, length))) {
83
0
    OPUS_DEBUG("Error decoding header!");
84
0
    return InitPromise::CreateAndReject(
85
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
86
0
                  RESULT_DETAIL("Error decoding header!")),
87
0
      __func__);
88
0
  }
89
0
90
0
  MOZ_ASSERT(mMappingTable.Length() >= uint32_t(mOpusParser->mChannels));
91
0
  int r;
92
0
  mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
93
0
                                                 mOpusParser->mChannels,
94
0
                                                 mOpusParser->mStreams,
95
0
                                                 mOpusParser->mCoupledStreams,
96
0
                                                 mMappingTable.Elements(),
97
0
                                                 &r);
98
0
99
0
  // Opus has a special feature for stereo coding where it represent wide
100
0
  // stereo channels by 180-degree out of phase. This improves quality, but
101
0
  // needs to be disabled when the output is downmixed to mono. Playback number
102
0
  // of channels are set in AudioSink, using the same method
103
0
  // `DecideAudioPlaybackChannels()`, and triggers downmix if needed.
104
0
  if (IsDefaultPlaybackDeviceMono() ||
105
0
      DecideAudioPlaybackChannels(mInfo) == 1) {
106
0
    opus_multistream_decoder_ctl(mOpusDecoder, OPUS_SET_PHASE_INVERSION_DISABLED(1));
107
0
  }
108
0
109
0
  mSkip = mOpusParser->mPreSkip;
110
0
  mPaddingDiscarded = false;
111
0
112
0
  if (codecDelay != FramesToUsecs(mOpusParser->mPreSkip,
113
0
                                  mOpusParser->mRate).value()) {
114
0
    NS_WARNING("Invalid Opus header: CodecDelay and pre-skip do not match!");
115
0
    return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
116
0
  }
117
0
118
0
  if (mInfo.mRate != (uint32_t)mOpusParser->mRate) {
119
0
    NS_WARNING("Invalid Opus header: container and codec rate do not match!");
120
0
  }
121
0
  if (mInfo.mChannels != (uint32_t)mOpusParser->mChannels) {
122
0
    NS_WARNING("Invalid Opus header: container and codec channels do not match!");
123
0
  }
124
0
125
0
  return r == OPUS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
126
0
                      : InitPromise::CreateAndReject(
127
0
                          MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
128
0
                          RESULT_DETAIL("could not create opus multistream decoder!")),
129
0
                          __func__);
130
0
}
131
132
nsresult
133
OpusDataDecoder::DecodeHeader(const unsigned char* aData, size_t aLength)
134
0
{
135
0
  MOZ_ASSERT(!mOpusParser);
136
0
  MOZ_ASSERT(!mOpusDecoder);
137
0
  MOZ_ASSERT(!mDecodedHeader);
138
0
  mDecodedHeader = true;
139
0
140
0
  mOpusParser = new OpusParser;
141
0
  if (!mOpusParser->DecodeHeader(const_cast<unsigned char*>(aData), aLength)) {
142
0
    return NS_ERROR_FAILURE;
143
0
  }
144
0
  int channels = mOpusParser->mChannels;
145
0
146
0
  mMappingTable.SetLength(channels);
147
0
  AudioConfig::ChannelLayout vorbisLayout(
148
0
    channels, VorbisDataDecoder::VorbisLayout(channels));
149
0
  if (vorbisLayout.IsValid()) {
150
0
    mChannelMap = vorbisLayout.Map();
151
0
152
0
    AudioConfig::ChannelLayout smpteLayout(
153
0
      AudioConfig::ChannelLayout::SMPTEDefault(vorbisLayout));
154
0
155
0
    AutoTArray<uint8_t, 8> map;
156
0
    map.SetLength(channels);
157
0
    if (vorbisLayout.MappingTable(smpteLayout, &map)) {
158
0
      for (int i = 0; i < channels; i++) {
159
0
        mMappingTable[i] = mOpusParser->mMappingTable[map[i]];
160
0
      }
161
0
    } else {
162
0
      // Should never get here as vorbis layout is always convertible to SMPTE
163
0
      // default layout.
164
0
      PodCopy(mMappingTable.Elements(), mOpusParser->mMappingTable, channels);
165
0
    }
166
0
  } else {
167
0
    // Create a dummy mapping table so that channel ordering stay the same
168
0
    // during decoding.
169
0
    for (int i = 0; i < channels; i++) {
170
0
      mMappingTable[i] = i;
171
0
    }
172
0
  }
173
0
174
0
  return NS_OK;
175
0
}
176
177
RefPtr<MediaDataDecoder::DecodePromise>
178
OpusDataDecoder::Decode(MediaRawData* aSample)
179
0
{
180
0
  return InvokeAsync<MediaRawData*>(mTaskQueue, this, __func__,
181
0
                                    &OpusDataDecoder::ProcessDecode, aSample);
182
0
}
183
184
RefPtr<MediaDataDecoder::DecodePromise>
185
OpusDataDecoder::ProcessDecode(MediaRawData* aSample)
186
0
{
187
0
  uint32_t channels = mOpusParser->mChannels;
188
0
189
0
  if (mPaddingDiscarded) {
190
0
    // Discard padding should be used only on the final packet, so
191
0
    // decoding after a padding discard is invalid.
192
0
    OPUS_DEBUG("Opus error, discard padding on interstitial packet");
193
0
    return DecodePromise::CreateAndReject(
194
0
      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
195
0
                  RESULT_DETAIL("Discard padding on interstitial packet")),
196
0
      __func__);
197
0
  }
198
0
199
0
  if (!mLastFrameTime ||
200
0
      mLastFrameTime.ref() != aSample->mTime.ToMicroseconds()) {
201
0
    // We are starting a new block.
202
0
    mFrames = 0;
203
0
    mLastFrameTime = Some(aSample->mTime.ToMicroseconds());
204
0
  }
205
0
206
0
  // Maximum value is 63*2880, so there's no chance of overflow.
207
0
  int frames_number =
208
0
    opus_packet_get_nb_frames(aSample->Data(), aSample->Size());
209
0
  if (frames_number <= 0) {
210
0
    OPUS_DEBUG("Invalid packet header: r=%d length=%zu", frames_number,
211
0
               aSample->Size());
212
0
    return DecodePromise::CreateAndReject(
213
0
      MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
214
0
                  RESULT_DETAIL("Invalid packet header: r=%d length=%u",
215
0
                                frames_number, uint32_t(aSample->Size()))),
216
0
      __func__);
217
0
  }
218
0
219
0
  int samples = opus_packet_get_samples_per_frame(
220
0
    aSample->Data(), opus_int32(mOpusParser->mRate));
221
0
222
0
  // A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
223
0
  CheckedInt32 totalFrames =
224
0
    CheckedInt32(frames_number) * CheckedInt32(samples);
225
0
  if (!totalFrames.isValid()) {
226
0
    return DecodePromise::CreateAndReject(
227
0
      MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
228
0
                  RESULT_DETAIL("Frames count overflow")),
229
0
      __func__);
230
0
  }
231
0
232
0
  int frames = totalFrames.value();
233
0
  if (frames < 120 || frames > 5760) {
234
0
    OPUS_DEBUG("Invalid packet frames: %d", frames);
235
0
    return DecodePromise::CreateAndReject(
236
0
      MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
237
0
                  RESULT_DETAIL("Invalid packet frames:%d", frames)),
238
0
      __func__);
239
0
  }
240
0
241
0
  AlignedAudioBuffer buffer(frames * channels);
242
0
  if (!buffer) {
243
0
    return DecodePromise::CreateAndReject(
244
0
      MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
245
0
  }
246
0
247
0
  // Decode to the appropriate sample type.
248
0
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
249
0
  int ret = opus_multistream_decode_float(mOpusDecoder,
250
0
                                          aSample->Data(), aSample->Size(),
251
0
                                          buffer.get(), frames, false);
252
#else
253
  int ret = opus_multistream_decode(mOpusDecoder,
254
                                    aSample->Data(), aSample->Size(),
255
                                    buffer.get(), frames, false);
256
#endif
257
0
  if (ret < 0) {
258
0
    return DecodePromise::CreateAndReject(
259
0
      MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
260
0
                  RESULT_DETAIL("Opus decoding error:%d", ret)),
261
0
      __func__);
262
0
  }
263
0
  NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
264
0
  auto startTime = aSample->mTime;
265
0
266
0
  // Trim the initial frames while the decoder is settling.
267
0
  if (mSkip > 0) {
268
0
    int32_t skipFrames = std::min<int32_t>(mSkip, frames);
269
0
    int32_t keepFrames = frames - skipFrames;
270
0
    OPUS_DEBUG(
271
0
      "Opus decoder skipping %d of %d frames", skipFrames, frames);
272
0
    PodMove(buffer.get(),
273
0
            buffer.get() + skipFrames * channels,
274
0
            keepFrames * channels);
275
0
    startTime = startTime + FramesToTimeUnit(skipFrames, mOpusParser->mRate);
276
0
    frames = keepFrames;
277
0
    mSkip -= skipFrames;
278
0
  }
279
0
280
0
  if (aSample->mDiscardPadding > 0) {
281
0
    OPUS_DEBUG("Opus decoder discarding %u of %d frames",
282
0
               aSample->mDiscardPadding, frames);
283
0
    // Padding discard is only supposed to happen on the final packet.
284
0
    // Record the discard so we can return an error if another packet is
285
0
    // decoded.
286
0
    if (aSample->mDiscardPadding > uint32_t(frames)) {
287
0
      // Discarding more than the entire packet is invalid.
288
0
      OPUS_DEBUG("Opus error, discard padding larger than packet");
289
0
      return DecodePromise::CreateAndReject(
290
0
        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
291
0
                    RESULT_DETAIL("Discard padding larger than packet")),
292
0
        __func__);
293
0
    }
294
0
295
0
    mPaddingDiscarded = true;
296
0
    frames = frames - aSample->mDiscardPadding;
297
0
  }
298
0
299
0
  // Apply the header gain if one was specified.
300
0
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
301
0
  if (mOpusParser->mGain != 1.0f) {
302
0
    float gain = mOpusParser->mGain;
303
0
    uint32_t samples = frames * channels;
304
0
    for (uint32_t i = 0; i < samples; i++) {
305
0
      buffer[i] *= gain;
306
0
    }
307
0
  }
308
#else
309
  if (mOpusParser->mGain_Q16 != 65536) {
310
    int64_t gain_Q16 = mOpusParser->mGain_Q16;
311
    uint32_t samples = frames * channels;
312
    for (uint32_t i = 0; i < samples; i++) {
313
      int32_t val = static_cast<int32_t>((gain_Q16*buffer[i] + 32768)>>16);
314
      buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
315
    }
316
  }
317
#endif
318
319
0
  auto duration = FramesToTimeUnit(frames, mOpusParser->mRate);
320
0
  if (!duration.IsValid()) {
321
0
    return DecodePromise::CreateAndReject(
322
0
      MediaResult(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
323
0
                  RESULT_DETAIL("Overflow converting WebM audio duration")),
324
0
      __func__);
325
0
  }
326
0
  auto time = startTime -
327
0
              FramesToTimeUnit(mOpusParser->mPreSkip, mOpusParser->mRate) +
328
0
              FramesToTimeUnit(mFrames, mOpusParser->mRate);
329
0
  if (!time.IsValid()) {
330
0
    return DecodePromise::CreateAndReject(
331
0
      MediaResult(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
332
0
                  RESULT_DETAIL("Overflow shifting tstamp by codec delay")),
333
0
      __func__);
334
0
  };
335
0
336
0
  mFrames += frames;
337
0
338
0
  if (!frames) {
339
0
    return DecodePromise::CreateAndResolve(DecodedData(), __func__);
340
0
  }
341
0
342
0
  return DecodePromise::CreateAndResolve(
343
0
    DecodedData{ new AudioData(aSample->mOffset,
344
0
                               time,
345
0
                               duration,
346
0
                               frames,
347
0
                               std::move(buffer),
348
0
                               mOpusParser->mChannels,
349
0
                               mOpusParser->mRate,
350
0
                               mChannelMap) },
351
0
    __func__);
352
0
}
353
354
RefPtr<MediaDataDecoder::DecodePromise>
355
OpusDataDecoder::Drain()
356
0
{
357
0
  RefPtr<OpusDataDecoder> self = this;
358
0
  // InvokeAsync dispatches a task that will be run after any pending decode
359
0
  // completes. As such, once the drain task run, there's nothing more to do.
360
0
  return InvokeAsync(mTaskQueue, __func__, [] {
361
0
    return DecodePromise::CreateAndResolve(DecodedData(), __func__);
362
0
  });
363
0
}
364
365
RefPtr<MediaDataDecoder::FlushPromise>
366
OpusDataDecoder::Flush()
367
0
{
368
0
  if (!mOpusDecoder) {
369
0
    return FlushPromise::CreateAndResolve(true, __func__);
370
0
  }
371
0
372
0
  RefPtr<OpusDataDecoder> self = this;
373
0
  return InvokeAsync(mTaskQueue, __func__, [self, this]() {
374
0
    MOZ_ASSERT(mOpusDecoder);
375
0
    // Reset the decoder.
376
0
    opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
377
0
    mSkip = mOpusParser->mPreSkip;
378
0
    mPaddingDiscarded = false;
379
0
    mLastFrameTime.reset();
380
0
    return FlushPromise::CreateAndResolve(true, __func__);
381
0
  });
382
0
}
383
384
/* static */
385
bool
386
OpusDataDecoder::IsOpus(const nsACString& aMimeType)
387
0
{
388
0
  return aMimeType.EqualsLiteral("audio/opus");
389
0
}
390
391
} // namespace mozilla
392
#undef OPUS_DEBUG