Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/ogg/OpusParser.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 <algorithm>
8
#include "mozilla/EndianUtils.h"
9
10
#include "OpusParser.h"
11
#include "VideoUtils.h"
12
13
#include "opus/opus.h"
14
extern "C" {
15
#include "opus/opus_multistream.h"
16
}
17
18
#include <cmath>
19
20
namespace mozilla {
21
22
extern LazyLogModule gMediaDecoderLog;
23
0
#define OPUS_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
24
25
OpusParser::OpusParser():
26
  mRate(0),
27
  mNominalRate(0),
28
  mChannels(0),
29
  mPreSkip(0),
30
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
31
  mGain(1.0f),
32
#else
33
  mGain_Q16(65536),
34
#endif
35
  mChannelMapping(0),
36
  mStreams(0),
37
  mCoupledStreams(0),
38
  mPrevPacketGranulepos(0)
39
0
{ }
40
41
bool OpusParser::DecodeHeader(unsigned char* aData, size_t aLength)
42
0
{
43
0
    if (aLength < 19 || memcmp(aData, "OpusHead", 8)) {
44
0
      OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: unrecognized header"));
45
0
      return false;
46
0
    }
47
0
48
0
    mRate = 48000; // The Opus decoder runs at 48 kHz regardless.
49
0
50
0
    int version = aData[8];
51
0
    // Accept file format versions 0.x.
52
0
    if ((version & 0xf0) != 0) {
53
0
      OPUS_LOG(LogLevel::Debug, ("Rejecting unknown Opus file version %d", version));
54
0
      return false;
55
0
    }
56
0
57
0
    mChannels = aData[9];
58
0
    if (mChannels<1) {
59
0
      OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: Number of channels %d", mChannels));
60
0
      return false;
61
0
    }
62
0
63
0
    mPreSkip = LittleEndian::readUint16(aData + 10);
64
0
    mNominalRate = LittleEndian::readUint32(aData + 12);
65
0
    double gain_dB = LittleEndian::readInt16(aData + 16) / 256.0;
66
0
#ifdef MOZ_SAMPLE_TYPE_FLOAT32
67
0
    mGain = static_cast<float>(pow(10,0.05*gain_dB));
68
#else
69
    mGain_Q16 = static_cast<int32_t>(std::min(65536*pow(10,0.05*gain_dB)+0.5,
70
                                            static_cast<double>(INT32_MAX)));
71
#endif
72
    mChannelMapping = aData[18];
73
0
74
0
    if (mChannelMapping == 0) {
75
0
      // Mapping family 0 only allows two channels
76
0
      if (mChannels>2) {
77
0
        OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: too many channels (%d) for"
78
0
                           " mapping family 0.", mChannels));
79
0
        return false;
80
0
      }
81
0
      mStreams = 1;
82
0
      mCoupledStreams = mChannels - 1;
83
0
      mMappingTable[0] = 0;
84
0
      mMappingTable[1] = 1;
85
0
    } else if (mChannelMapping == 1 || mChannelMapping == 2 ||
86
0
               mChannelMapping == 255) {
87
0
      // Currently only up to 8 channels are defined for mapping family 1
88
0
      if (mChannelMapping == 1 && mChannels > 8) {
89
0
        OPUS_LOG(LogLevel::Debug,
90
0
                 ("Invalid Opus file: too many channels (%d) for"
91
0
                  " mapping family 1.",
92
0
                  mChannels));
93
0
        return false;
94
0
      }
95
0
      if (mChannelMapping == 2) {
96
0
        // https://tools.ietf.org/html/draft-ietf-codec-ambisonics-08#page-3
97
0
        // For both channel mapping family 2 and family 3, the allowed numbers
98
0
        // of channels: (1 + n)^2 + 2j for n = 0, 1, ..., 14 and j = 0 or 1,
99
0
        // where n denotes the (highest) ambisonic order and j denotes whether
100
0
        // or not there is a separate non-diegetic stereo stream Explicitly the
101
0
        // allowed number of channels are 1, 3, 4, 6, 9, 11, 16, 18, 25, 27, 36,
102
0
        // 38, 49, 51, 64, 66, 81, 83, 100, 102, 121, 123, 144, 146, 169, 171,
103
0
        // 196, 198, 225, and 227.
104
0
105
0
        // We use the property that int(sqrt(n)) == int(sqrt(n+2)) for n != 3
106
0
        // which is handled by the test n^2 + 2 != channel
107
0
        double val = sqrt(mChannels);
108
0
        if (val == 0 || val > 15) {
109
0
          return false;
110
0
        }
111
0
        if (val != int32_t(val)) {
112
0
          if (val * val + 2 != mChannels) {
113
0
            // Not a valid channel count.
114
0
            return false;
115
0
          }
116
0
        }
117
0
      }
118
0
      if (aLength > static_cast<unsigned>(20 + mChannels)) {
119
0
        mStreams = aData[19];
120
0
        mCoupledStreams = aData[20];
121
0
        int i;
122
0
        for (i = 0; i < mChannels; i++) {
123
0
          mMappingTable[i] = aData[21 + i];
124
0
        }
125
0
      } else {
126
0
        OPUS_LOG(LogLevel::Debug,
127
0
                 ("Invalid Opus file: channel mapping %d,"
128
0
                  " but no channel mapping table",
129
0
                  mChannelMapping));
130
0
        return false;
131
0
      }
132
0
    } else {
133
0
      OPUS_LOG(LogLevel::Debug,
134
0
               ("Invalid Opus file: unsupported channel mapping "
135
0
                "family %d",
136
0
                mChannelMapping));
137
0
      return false;
138
0
    }
139
0
    if (mStreams < 1) {
140
0
      OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: no streams"));
141
0
      return false;
142
0
    }
143
0
    if (mCoupledStreams > mStreams) {
144
0
      OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: more coupled streams (%d) than "
145
0
                         "total streams (%d)", mCoupledStreams, mStreams));
146
0
      return false;
147
0
    }
148
0
149
#ifdef DEBUG
150
    OPUS_LOG(LogLevel::Debug, ("Opus stream header:"));
151
    OPUS_LOG(LogLevel::Debug, (" channels: %d", mChannels));
152
    OPUS_LOG(LogLevel::Debug, ("  preskip: %d", mPreSkip));
153
    OPUS_LOG(LogLevel::Debug, (" original: %d Hz", mNominalRate));
154
    OPUS_LOG(LogLevel::Debug, ("     gain: %.2f dB", gain_dB));
155
    OPUS_LOG(LogLevel::Debug, ("Channel Mapping:"));
156
    OPUS_LOG(LogLevel::Debug, ("   family: %d", mChannelMapping));
157
    OPUS_LOG(LogLevel::Debug, ("  streams: %d", mStreams));
158
#endif
159
0
  return true;
160
0
}
161
162
bool OpusParser::DecodeTags(unsigned char* aData, size_t aLength)
163
0
{
164
0
  if (aLength < 16 || memcmp(aData, "OpusTags", 8))
165
0
    return false;
166
0
167
0
  // Copy out the raw comment lines, but only do basic validation
168
0
  // checks against the string packing: too little data, too many
169
0
  // comments, or comments that are too long. Rejecting these cases
170
0
  // helps reduce the propagation of broken files.
171
0
  // We do not ensure they are valid UTF-8 here, nor do we validate
172
0
  // the required ASCII_TAG=value format of the user comments.
173
0
  const unsigned char* buf = aData + 8;
174
0
  uint32_t bytes = aLength - 8;
175
0
  uint32_t len;
176
0
  // Read the vendor string.
177
0
  len = LittleEndian::readUint32(buf);
178
0
  buf += 4;
179
0
  bytes -= 4;
180
0
  if (len > bytes)
181
0
    return false;
182
0
  mVendorString = nsCString(reinterpret_cast<const char*>(buf), len);
183
0
  buf += len;
184
0
  bytes -= len;
185
0
  // Read the user comments.
186
0
  if (bytes < 4)
187
0
    return false;
188
0
  uint32_t ncomments = LittleEndian::readUint32(buf);
189
0
  buf += 4;
190
0
  bytes -= 4;
191
0
  // If there are so many comments even their length fields
192
0
  // won't fit in the packet, stop reading now.
193
0
  if (ncomments > (bytes>>2))
194
0
    return false;
195
0
  for (uint32_t i = 0; i < ncomments; i++) {
196
0
    if (bytes < 4)
197
0
      return false;
198
0
    len = LittleEndian::readUint32(buf);
199
0
    buf += 4;
200
0
    bytes -= 4;
201
0
    if (len > bytes)
202
0
      return false;
203
0
    mTags.AppendElement(nsCString(reinterpret_cast<const char*>(buf), len));
204
0
    buf += len;
205
0
    bytes -= len;
206
0
  }
207
0
208
#ifdef DEBUG
209
  OPUS_LOG(LogLevel::Debug, ("Opus metadata header:"));
210
  OPUS_LOG(LogLevel::Debug, ("  vendor: %s", mVendorString.get()));
211
  for (uint32_t i = 0; i < mTags.Length(); i++) {
212
    OPUS_LOG(LogLevel::Debug, (" %s", mTags[i].get()));
213
  }
214
#endif
215
0
  return true;
216
0
}
217
218
} // namespace mozilla