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 "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 "mozilla/SizePrintfMacros.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, ...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, \
25 : ("OpusDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
26 :
27 : namespace mozilla {
28 :
29 0 : OpusDataDecoder::OpusDataDecoder(const CreateDecoderParams& aParams)
30 0 : : mInfo(aParams.AudioConfig())
31 0 : , mTaskQueue(aParams.mTaskQueue)
32 : , mOpusDecoder(nullptr)
33 : , mSkip(0)
34 : , mDecodedHeader(false)
35 : , mPaddingDiscarded(false)
36 0 : , mFrames(0)
37 : {
38 0 : }
39 :
40 0 : OpusDataDecoder::~OpusDataDecoder()
41 : {
42 0 : if (mOpusDecoder) {
43 0 : opus_multistream_decoder_destroy(mOpusDecoder);
44 0 : mOpusDecoder = nullptr;
45 : }
46 0 : }
47 :
48 : RefPtr<ShutdownPromise>
49 0 : OpusDataDecoder::Shutdown()
50 : {
51 0 : RefPtr<OpusDataDecoder> self = this;
52 0 : return InvokeAsync(mTaskQueue, __func__, [self]() {
53 : return ShutdownPromise::CreateAndResolve(true, __func__);
54 0 : });
55 : }
56 :
57 : void
58 0 : OpusDataDecoder::AppendCodecDelay(MediaByteBuffer* config, uint64_t codecDelayUS)
59 : {
60 : uint8_t buffer[sizeof(uint64_t)];
61 0 : BigEndian::writeUint64(buffer, codecDelayUS);
62 0 : config->AppendElements(buffer, sizeof(uint64_t));
63 0 : }
64 :
65 : RefPtr<MediaDataDecoder::InitPromise>
66 0 : OpusDataDecoder::Init()
67 : {
68 0 : size_t length = mInfo.mCodecSpecificConfig->Length();
69 0 : uint8_t *p = mInfo.mCodecSpecificConfig->Elements();
70 0 : if (length < sizeof(uint64_t)) {
71 0 : OPUS_DEBUG("CodecSpecificConfig too short to read codecDelay!");
72 0 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
73 : }
74 0 : int64_t codecDelay = BigEndian::readUint64(p);
75 0 : length -= sizeof(uint64_t);
76 0 : p += sizeof(uint64_t);
77 0 : if (NS_FAILED(DecodeHeader(p, length))) {
78 0 : OPUS_DEBUG("Error decoding header!");
79 0 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
80 : }
81 :
82 : int r;
83 0 : mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
84 0 : mOpusParser->mChannels,
85 0 : mOpusParser->mStreams,
86 0 : mOpusParser->mCoupledStreams,
87 : mMappingTable,
88 : &r);
89 0 : mSkip = mOpusParser->mPreSkip;
90 0 : mPaddingDiscarded = false;
91 :
92 0 : if (codecDelay != FramesToUsecs(mOpusParser->mPreSkip,
93 0 : mOpusParser->mRate).value()) {
94 0 : NS_WARNING("Invalid Opus header: CodecDelay and pre-skip do not match!");
95 0 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
96 : }
97 :
98 0 : if (mInfo.mRate != (uint32_t)mOpusParser->mRate) {
99 0 : NS_WARNING("Invalid Opus header: container and codec rate do not match!");
100 : }
101 0 : if (mInfo.mChannels != (uint32_t)mOpusParser->mChannels) {
102 0 : NS_WARNING("Invalid Opus header: container and codec channels do not match!");
103 : }
104 :
105 0 : return r == OPUS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
106 0 : : InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
107 : }
108 :
109 : nsresult
110 0 : OpusDataDecoder::DecodeHeader(const unsigned char* aData, size_t aLength)
111 : {
112 0 : MOZ_ASSERT(!mOpusParser);
113 0 : MOZ_ASSERT(!mOpusDecoder);
114 0 : MOZ_ASSERT(!mDecodedHeader);
115 0 : mDecodedHeader = true;
116 :
117 0 : mOpusParser = new OpusParser;
118 0 : if (!mOpusParser->DecodeHeader(const_cast<unsigned char*>(aData), aLength)) {
119 0 : return NS_ERROR_FAILURE;
120 : }
121 0 : int channels = mOpusParser->mChannels;
122 :
123 0 : AudioConfig::ChannelLayout layout(channels);
124 0 : if (!layout.IsValid()) {
125 0 : OPUS_DEBUG("Invalid channel mapping. Source is %d channels", channels);
126 0 : return NS_ERROR_FAILURE;
127 : }
128 :
129 : AudioConfig::ChannelLayout vorbisLayout(
130 0 : channels, VorbisDataDecoder::VorbisLayout(channels));
131 0 : AudioConfig::ChannelLayout smpteLayout(channels);
132 : static_assert(sizeof(mOpusParser->mMappingTable) / sizeof(mOpusParser->mMappingTable[0]) >= MAX_AUDIO_CHANNELS,
133 : "Invalid size set");
134 : uint8_t map[sizeof(mOpusParser->mMappingTable) / sizeof(mOpusParser->mMappingTable[0])];
135 0 : if (vorbisLayout.MappingTable(smpteLayout, map)) {
136 0 : for (int i = 0; i < channels; i++) {
137 0 : mMappingTable[i] = mOpusParser->mMappingTable[map[i]];
138 : }
139 : } else {
140 : // Should never get here as vorbis layout is always convertible to SMPTE
141 : // default layout.
142 0 : PodCopy(mMappingTable, mOpusParser->mMappingTable, MAX_AUDIO_CHANNELS);
143 : }
144 :
145 0 : return NS_OK;
146 : }
147 :
148 : RefPtr<MediaDataDecoder::DecodePromise>
149 0 : OpusDataDecoder::Decode(MediaRawData* aSample)
150 : {
151 : return InvokeAsync<MediaRawData*>(mTaskQueue, this, __func__,
152 0 : &OpusDataDecoder::ProcessDecode, aSample);
153 : }
154 :
155 : RefPtr<MediaDataDecoder::DecodePromise>
156 0 : OpusDataDecoder::ProcessDecode(MediaRawData* aSample)
157 : {
158 0 : uint32_t channels = mOpusParser->mChannels;
159 :
160 0 : if (mPaddingDiscarded) {
161 : // Discard padding should be used only on the final packet, so
162 : // decoding after a padding discard is invalid.
163 0 : OPUS_DEBUG("Opus error, discard padding on interstitial packet");
164 : return DecodePromise::CreateAndReject(
165 0 : MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
166 0 : RESULT_DETAIL("Discard padding on interstitial packet")),
167 0 : __func__);
168 : }
169 :
170 0 : if (!mLastFrameTime ||
171 0 : mLastFrameTime.ref() != aSample->mTime.ToMicroseconds()) {
172 : // We are starting a new block.
173 0 : mFrames = 0;
174 0 : mLastFrameTime = Some(aSample->mTime.ToMicroseconds());
175 : }
176 :
177 : // Maximum value is 63*2880, so there's no chance of overflow.
178 : int frames_number =
179 0 : opus_packet_get_nb_frames(aSample->Data(), aSample->Size());
180 0 : if (frames_number <= 0) {
181 0 : OPUS_DEBUG("Invalid packet header: r=%d length=%" PRIuSIZE, frames_number,
182 : aSample->Size());
183 : return DecodePromise::CreateAndReject(
184 0 : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
185 0 : RESULT_DETAIL("Invalid packet header: r=%d length=%u",
186 : frames_number, uint32_t(aSample->Size()))),
187 0 : __func__);
188 : }
189 :
190 0 : int samples = opus_packet_get_samples_per_frame(
191 0 : aSample->Data(), opus_int32(mOpusParser->mRate));
192 :
193 : // A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
194 : CheckedInt32 totalFrames =
195 0 : CheckedInt32(frames_number) * CheckedInt32(samples);
196 0 : if (!totalFrames.isValid()) {
197 : return DecodePromise::CreateAndReject(
198 0 : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
199 0 : RESULT_DETAIL("Frames count overflow")),
200 0 : __func__);
201 : }
202 :
203 0 : int frames = totalFrames.value();
204 0 : if (frames < 120 || frames > 5760) {
205 0 : OPUS_DEBUG("Invalid packet frames: %d", frames);
206 : return DecodePromise::CreateAndReject(
207 0 : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
208 0 : RESULT_DETAIL("Invalid packet frames:%d", frames)),
209 0 : __func__);
210 : }
211 :
212 0 : AlignedAudioBuffer buffer(frames * channels);
213 0 : if (!buffer) {
214 : return DecodePromise::CreateAndReject(
215 0 : MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
216 : }
217 :
218 : // Decode to the appropriate sample type.
219 : #ifdef MOZ_SAMPLE_TYPE_FLOAT32
220 0 : int ret = opus_multistream_decode_float(mOpusDecoder,
221 0 : aSample->Data(), aSample->Size(),
222 0 : buffer.get(), frames, false);
223 : #else
224 : int ret = opus_multistream_decode(mOpusDecoder,
225 : aSample->Data(), aSample->Size(),
226 : buffer.get(), frames, false);
227 : #endif
228 0 : if (ret < 0) {
229 : return DecodePromise::CreateAndReject(
230 0 : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
231 0 : RESULT_DETAIL("Opus decoding error:%d", ret)),
232 0 : __func__);
233 : }
234 0 : NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
235 0 : auto startTime = aSample->mTime;
236 :
237 : // Trim the initial frames while the decoder is settling.
238 0 : if (mSkip > 0) {
239 0 : int32_t skipFrames = std::min<int32_t>(mSkip, frames);
240 0 : int32_t keepFrames = frames - skipFrames;
241 0 : OPUS_DEBUG(
242 : "Opus decoder skipping %d of %d frames", skipFrames, frames);
243 0 : PodMove(buffer.get(),
244 0 : buffer.get() + skipFrames * channels,
245 0 : keepFrames * channels);
246 0 : startTime = startTime + FramesToTimeUnit(skipFrames, mOpusParser->mRate);
247 0 : frames = keepFrames;
248 0 : mSkip -= skipFrames;
249 : }
250 :
251 0 : if (aSample->mDiscardPadding > 0) {
252 0 : OPUS_DEBUG("Opus decoder discarding %u of %d frames",
253 : aSample->mDiscardPadding, frames);
254 : // Padding discard is only supposed to happen on the final packet.
255 : // Record the discard so we can return an error if another packet is
256 : // decoded.
257 0 : if (aSample->mDiscardPadding > uint32_t(frames)) {
258 : // Discarding more than the entire packet is invalid.
259 0 : OPUS_DEBUG("Opus error, discard padding larger than packet");
260 : return DecodePromise::CreateAndReject(
261 0 : MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
262 0 : RESULT_DETAIL("Discard padding larger than packet")),
263 0 : __func__);
264 : }
265 :
266 0 : mPaddingDiscarded = true;
267 0 : frames = frames - aSample->mDiscardPadding;
268 : }
269 :
270 : // Apply the header gain if one was specified.
271 : #ifdef MOZ_SAMPLE_TYPE_FLOAT32
272 0 : if (mOpusParser->mGain != 1.0f) {
273 0 : float gain = mOpusParser->mGain;
274 0 : uint32_t samples = frames * channels;
275 0 : for (uint32_t i = 0; i < samples; i++) {
276 0 : buffer[i] *= gain;
277 : }
278 : }
279 : #else
280 : if (mOpusParser->mGain_Q16 != 65536) {
281 : int64_t gain_Q16 = mOpusParser->mGain_Q16;
282 : uint32_t samples = frames * channels;
283 : for (uint32_t i = 0; i < samples; i++) {
284 : int32_t val = static_cast<int32_t>((gain_Q16*buffer[i] + 32768)>>16);
285 : buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
286 : }
287 : }
288 : #endif
289 :
290 0 : auto duration = FramesToTimeUnit(frames, mOpusParser->mRate);
291 0 : if (!duration.IsValid()) {
292 : return DecodePromise::CreateAndReject(
293 0 : MediaResult(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
294 0 : RESULT_DETAIL("Overflow converting WebM audio duration")),
295 0 : __func__);
296 : }
297 0 : auto time = startTime -
298 0 : FramesToTimeUnit(mOpusParser->mPreSkip, mOpusParser->mRate) +
299 0 : FramesToTimeUnit(mFrames, mOpusParser->mRate);
300 0 : if (!time.IsValid()) {
301 : return DecodePromise::CreateAndReject(
302 0 : MediaResult(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
303 0 : RESULT_DETAIL("Overflow shifting tstamp by codec delay")),
304 0 : __func__);
305 : };
306 :
307 :
308 0 : mFrames += frames;
309 :
310 : return DecodePromise::CreateAndResolve(
311 0 : DecodedData{ new AudioData(aSample->mOffset, time, duration,
312 0 : frames, Move(buffer), mOpusParser->mChannels,
313 0 : mOpusParser->mRate) },
314 0 : __func__);
315 : }
316 :
317 : RefPtr<MediaDataDecoder::DecodePromise>
318 0 : OpusDataDecoder::Drain()
319 : {
320 0 : RefPtr<OpusDataDecoder> self = this;
321 : // InvokeAsync dispatches a task that will be run after any pending decode
322 : // completes. As such, once the drain task run, there's nothing more to do.
323 0 : return InvokeAsync(mTaskQueue, __func__, [] {
324 0 : return DecodePromise::CreateAndResolve(DecodedData(), __func__);
325 0 : });
326 : }
327 :
328 : RefPtr<MediaDataDecoder::FlushPromise>
329 0 : OpusDataDecoder::Flush()
330 : {
331 0 : if (!mOpusDecoder) {
332 0 : return FlushPromise::CreateAndResolve(true, __func__);
333 : }
334 :
335 0 : RefPtr<OpusDataDecoder> self = this;
336 0 : return InvokeAsync(mTaskQueue, __func__, [self, this]() {
337 0 : MOZ_ASSERT(mOpusDecoder);
338 : // Reset the decoder.
339 0 : opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
340 0 : mSkip = mOpusParser->mPreSkip;
341 0 : mPaddingDiscarded = false;
342 0 : mLastFrameTime.reset();
343 0 : return FlushPromise::CreateAndResolve(true, __func__);
344 0 : });
345 : }
346 :
347 : /* static */
348 : bool
349 0 : OpusDataDecoder::IsOpus(const nsACString& aMimeType)
350 : {
351 0 : return aMimeType.EqualsLiteral("audio/opus");
352 : }
353 :
354 : } // namespace mozilla
355 : #undef OPUS_DEBUG
|