Line data Source code
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 : namespace mozilla {
19 :
20 : extern LazyLogModule gMediaDecoderLog;
21 : #define OPUS_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
22 :
23 0 : OpusParser::OpusParser():
24 : mRate(0),
25 : mNominalRate(0),
26 : mChannels(0),
27 : mPreSkip(0),
28 : #ifdef MOZ_SAMPLE_TYPE_FLOAT32
29 : mGain(1.0f),
30 : #else
31 : mGain_Q16(65536),
32 : #endif
33 : mChannelMapping(0),
34 : mStreams(0),
35 0 : mCoupledStreams(0)
36 0 : { }
37 :
38 0 : bool OpusParser::DecodeHeader(unsigned char* aData, size_t aLength)
39 : {
40 0 : if (aLength < 19 || memcmp(aData, "OpusHead", 8)) {
41 0 : OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: unrecognized header"));
42 0 : return false;
43 : }
44 :
45 0 : mRate = 48000; // The Opus decoder runs at 48 kHz regardless.
46 :
47 0 : int version = aData[8];
48 : // Accept file format versions 0.x.
49 0 : if ((version & 0xf0) != 0) {
50 0 : OPUS_LOG(LogLevel::Debug, ("Rejecting unknown Opus file version %d", version));
51 0 : return false;
52 : }
53 :
54 0 : mChannels = aData[9];
55 0 : if (mChannels<1) {
56 0 : OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: Number of channels %d", mChannels));
57 0 : return false;
58 : }
59 :
60 0 : mPreSkip = LittleEndian::readUint16(aData + 10);
61 0 : mNominalRate = LittleEndian::readUint32(aData + 12);
62 0 : double gain_dB = LittleEndian::readInt16(aData + 16) / 256.0;
63 : #ifdef MOZ_SAMPLE_TYPE_FLOAT32
64 0 : mGain = static_cast<float>(pow(10,0.05*gain_dB));
65 : #else
66 : mGain_Q16 = static_cast<int32_t>(std::min(65536*pow(10,0.05*gain_dB)+0.5,
67 : static_cast<double>(INT32_MAX)));
68 : #endif
69 0 : mChannelMapping = aData[18];
70 :
71 0 : if (mChannelMapping == 0) {
72 : // Mapping family 0 only allows two channels
73 0 : if (mChannels>2) {
74 0 : OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: too many channels (%d) for"
75 : " mapping family 0.", mChannels));
76 0 : return false;
77 : }
78 0 : mStreams = 1;
79 0 : mCoupledStreams = mChannels - 1;
80 0 : mMappingTable[0] = 0;
81 0 : mMappingTable[1] = 1;
82 0 : } else if (mChannelMapping == 1 || mChannelMapping == 255) {
83 : // Currently only up to 8 channels are defined for mapping family 1 and we
84 : // only supports only up to 8 channels for mapping family 255.
85 0 : if (mChannels>8) {
86 0 : OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: too many channels (%d) for"
87 : " mapping family 1.", mChannels));
88 0 : return false;
89 : }
90 0 : if (aLength>static_cast<unsigned>(20+mChannels)) {
91 0 : mStreams = aData[19];
92 0 : mCoupledStreams = aData[20];
93 : int i;
94 0 : for (i=0; i<mChannels; i++)
95 0 : mMappingTable[i] = aData[21+i];
96 : } else {
97 0 : OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: channel mapping %d,"
98 : " but no channel mapping table", mChannelMapping));
99 0 : return false;
100 0 : }
101 : } else {
102 0 : OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: unsupported channel mapping "
103 : "family %d", mChannelMapping));
104 0 : return false;
105 : }
106 0 : if (mStreams < 1) {
107 0 : OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: no streams"));
108 0 : return false;
109 : }
110 0 : if (mCoupledStreams > mStreams) {
111 0 : OPUS_LOG(LogLevel::Debug, ("Invalid Opus file: more coupled streams (%d) than "
112 : "total streams (%d)", mCoupledStreams, mStreams));
113 0 : return false;
114 : }
115 :
116 : #ifdef DEBUG
117 0 : OPUS_LOG(LogLevel::Debug, ("Opus stream header:"));
118 0 : OPUS_LOG(LogLevel::Debug, (" channels: %d", mChannels));
119 0 : OPUS_LOG(LogLevel::Debug, (" preskip: %d", mPreSkip));
120 0 : OPUS_LOG(LogLevel::Debug, (" original: %d Hz", mNominalRate));
121 0 : OPUS_LOG(LogLevel::Debug, (" gain: %.2f dB", gain_dB));
122 0 : OPUS_LOG(LogLevel::Debug, ("Channel Mapping:"));
123 0 : OPUS_LOG(LogLevel::Debug, (" family: %d", mChannelMapping));
124 0 : OPUS_LOG(LogLevel::Debug, (" streams: %d", mStreams));
125 : #endif
126 0 : return true;
127 : }
128 :
129 0 : bool OpusParser::DecodeTags(unsigned char* aData, size_t aLength)
130 : {
131 0 : if (aLength < 16 || memcmp(aData, "OpusTags", 8))
132 0 : return false;
133 :
134 : // Copy out the raw comment lines, but only do basic validation
135 : // checks against the string packing: too little data, too many
136 : // comments, or comments that are too long. Rejecting these cases
137 : // helps reduce the propagation of broken files.
138 : // We do not ensure they are valid UTF-8 here, nor do we validate
139 : // the required ASCII_TAG=value format of the user comments.
140 0 : const unsigned char* buf = aData + 8;
141 0 : uint32_t bytes = aLength - 8;
142 : uint32_t len;
143 : // Read the vendor string.
144 0 : len = LittleEndian::readUint32(buf);
145 0 : buf += 4;
146 0 : bytes -= 4;
147 0 : if (len > bytes)
148 0 : return false;
149 0 : mVendorString = nsCString(reinterpret_cast<const char*>(buf), len);
150 0 : buf += len;
151 0 : bytes -= len;
152 : // Read the user comments.
153 0 : if (bytes < 4)
154 0 : return false;
155 0 : uint32_t ncomments = LittleEndian::readUint32(buf);
156 0 : buf += 4;
157 0 : bytes -= 4;
158 : // If there are so many comments even their length fields
159 : // won't fit in the packet, stop reading now.
160 0 : if (ncomments > (bytes>>2))
161 0 : return false;
162 0 : for (uint32_t i = 0; i < ncomments; i++) {
163 0 : if (bytes < 4)
164 0 : return false;
165 0 : len = LittleEndian::readUint32(buf);
166 0 : buf += 4;
167 0 : bytes -= 4;
168 0 : if (len > bytes)
169 0 : return false;
170 0 : mTags.AppendElement(nsCString(reinterpret_cast<const char*>(buf), len));
171 0 : buf += len;
172 0 : bytes -= len;
173 : }
174 :
175 : #ifdef DEBUG
176 0 : OPUS_LOG(LogLevel::Debug, ("Opus metadata header:"));
177 0 : OPUS_LOG(LogLevel::Debug, (" vendor: %s", mVendorString.get()));
178 0 : for (uint32_t i = 0; i < mTags.Length(); i++) {
179 0 : OPUS_LOG(LogLevel::Debug, (" %s", mTags[i].get()));
180 : }
181 : #endif
182 0 : return true;
183 : }
184 :
185 : } // namespace mozilla
|