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 "VorbisDecoder.h"
8 #include "VorbisUtils.h"
9 #include "XiphExtradata.h"
10 
11 #include "mozilla/PodOperations.h"
12 #include "mozilla/SyncRunnable.h"
13 
14 #undef LOG
15 #define LOG(type, msg) MOZ_LOG(sPDMLog, type, msg)
16 
17 namespace mozilla {
18 
InitVorbisPacket(const unsigned char * aData,size_t aLength,bool aBOS,bool aEOS,int64_t aGranulepos,int64_t aPacketNo)19 ogg_packet InitVorbisPacket(const unsigned char* aData, size_t aLength,
20                          bool aBOS, bool aEOS,
21                          int64_t aGranulepos, int64_t aPacketNo)
22 {
23   ogg_packet packet;
24   packet.packet = const_cast<unsigned char*>(aData);
25   packet.bytes = aLength;
26   packet.b_o_s = aBOS;
27   packet.e_o_s = aEOS;
28   packet.granulepos = aGranulepos;
29   packet.packetno = aPacketNo;
30   return packet;
31 }
32 
VorbisDataDecoder(const CreateDecoderParams & aParams)33 VorbisDataDecoder::VorbisDataDecoder(const CreateDecoderParams& aParams)
34   : mInfo(aParams.AudioConfig())
35   , mTaskQueue(aParams.mTaskQueue)
36   , mCallback(aParams.mCallback)
37   , mPacketCount(0)
38   , mFrames(0)
39   , mIsFlushing(false)
40 {
41   // Zero these member vars to avoid crashes in Vorbis clear functions when
42   // destructor is called before |Init|.
43   PodZero(&mVorbisBlock);
44   PodZero(&mVorbisDsp);
45   PodZero(&mVorbisInfo);
46   PodZero(&mVorbisComment);
47 }
48 
~VorbisDataDecoder()49 VorbisDataDecoder::~VorbisDataDecoder()
50 {
51   vorbis_block_clear(&mVorbisBlock);
52   vorbis_dsp_clear(&mVorbisDsp);
53   vorbis_info_clear(&mVorbisInfo);
54   vorbis_comment_clear(&mVorbisComment);
55 }
56 
57 void
Shutdown()58 VorbisDataDecoder::Shutdown()
59 {
60 }
61 
62 RefPtr<MediaDataDecoder::InitPromise>
Init()63 VorbisDataDecoder::Init()
64 {
65   vorbis_info_init(&mVorbisInfo);
66   vorbis_comment_init(&mVorbisComment);
67   PodZero(&mVorbisDsp);
68   PodZero(&mVorbisBlock);
69 
70   AutoTArray<unsigned char*,4> headers;
71   AutoTArray<size_t,4> headerLens;
72   if (!XiphExtradataToHeaders(headers, headerLens,
73                               mInfo.mCodecSpecificConfig->Elements(),
74                               mInfo.mCodecSpecificConfig->Length())) {
75     return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
76   }
77   for (size_t i = 0; i < headers.Length(); i++) {
78     if (NS_FAILED(DecodeHeader(headers[i], headerLens[i]))) {
79       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
80     }
81   }
82 
83   MOZ_ASSERT(mPacketCount == 3);
84 
85   int r = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
86   if (r) {
87     return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
88   }
89 
90   r = vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
91   if (r) {
92     return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
93   }
94 
95   if (mInfo.mRate != (uint32_t)mVorbisDsp.vi->rate) {
96     LOG(LogLevel::Warning,
97         ("Invalid Vorbis header: container and codec rate do not match!"));
98   }
99   if (mInfo.mChannels != (uint32_t)mVorbisDsp.vi->channels) {
100     LOG(LogLevel::Warning,
101         ("Invalid Vorbis header: container and codec channels do not match!"));
102   }
103 
104   AudioConfig::ChannelLayout layout(mVorbisDsp.vi->channels);
105   if (!layout.IsValid()) {
106     return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
107   }
108 
109   return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
110 }
111 
112 nsresult
DecodeHeader(const unsigned char * aData,size_t aLength)113 VorbisDataDecoder::DecodeHeader(const unsigned char* aData, size_t aLength)
114 {
115   bool bos = mPacketCount == 0;
116   ogg_packet pkt = InitVorbisPacket(aData, aLength, bos, false, 0, mPacketCount++);
117   MOZ_ASSERT(mPacketCount <= 3);
118 
119   int r = vorbis_synthesis_headerin(&mVorbisInfo,
120                                     &mVorbisComment,
121                                     &pkt);
122   return r == 0 ? NS_OK : NS_ERROR_FAILURE;
123 }
124 
125 void
Input(MediaRawData * aSample)126 VorbisDataDecoder::Input(MediaRawData* aSample)
127 {
128   MOZ_ASSERT(mCallback->OnReaderTaskQueue());
129   mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
130                        this, &VorbisDataDecoder::ProcessDecode, aSample));
131 }
132 
133 void
ProcessDecode(MediaRawData * aSample)134 VorbisDataDecoder::ProcessDecode(MediaRawData* aSample)
135 {
136   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
137   if (mIsFlushing) {
138     return;
139   }
140 
141   MediaResult rv = DoDecode(aSample);
142   if (NS_FAILED(rv)) {
143     mCallback->Error(rv);
144   } else {
145     mCallback->InputExhausted();
146   }
147 }
148 
149 MediaResult
DoDecode(MediaRawData * aSample)150 VorbisDataDecoder::DoDecode(MediaRawData* aSample)
151 {
152   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
153 
154   const unsigned char* aData = aSample->Data();
155   size_t aLength = aSample->Size();
156   int64_t aOffset = aSample->mOffset;
157   int64_t aTstampUsecs = aSample->mTime;
158   int64_t aTotalFrames = 0;
159 
160   MOZ_ASSERT(mPacketCount >= 3);
161 
162   if (!mLastFrameTime || mLastFrameTime.ref() != aSample->mTime) {
163     // We are starting a new block.
164     mFrames = 0;
165     mLastFrameTime = Some(aSample->mTime);
166   }
167 
168   ogg_packet pkt = InitVorbisPacket(aData, aLength, false, aSample->mEOS,
169                                     aSample->mTimecode, mPacketCount++);
170 
171   int err = vorbis_synthesis(&mVorbisBlock, &pkt);
172   if (err) {
173     return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
174                        RESULT_DETAIL("vorbis_synthesis:%d", err));
175   }
176 
177   err = vorbis_synthesis_blockin(&mVorbisDsp, &mVorbisBlock);
178   if (err) {
179     return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
180                        RESULT_DETAIL("vorbis_synthesis_blockin:%d", err));
181   }
182 
183   VorbisPCMValue** pcm = 0;
184   int32_t frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
185   if (frames == 0) {
186     return NS_OK;
187   }
188   while (frames > 0) {
189     uint32_t channels = mVorbisDsp.vi->channels;
190     uint32_t rate = mVorbisDsp.vi->rate;
191     AlignedAudioBuffer buffer(frames*channels);
192     if (!buffer) {
193       return MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__);
194     }
195     for (uint32_t j = 0; j < channels; ++j) {
196       VorbisPCMValue* channel = pcm[j];
197       for (uint32_t i = 0; i < uint32_t(frames); ++i) {
198         buffer[i*channels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
199       }
200     }
201 
202     CheckedInt64 duration = FramesToUsecs(frames, rate);
203     if (!duration.isValid()) {
204       return MediaResult(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
205                          RESULT_DETAIL("Overflow converting audio duration"));
206     }
207     CheckedInt64 total_duration = FramesToUsecs(mFrames, rate);
208     if (!total_duration.isValid()) {
209       return MediaResult(
210         NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
211         RESULT_DETAIL("Overflow converting audio total_duration"));
212     }
213 
214     CheckedInt64 time = total_duration + aTstampUsecs;
215     if (!time.isValid()) {
216       return MediaResult(
217         NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
218         RESULT_DETAIL("Overflow adding total_duration and aTstampUsecs"));
219     };
220 
221     if (!mAudioConverter) {
222       AudioConfig in(AudioConfig::ChannelLayout(channels, VorbisLayout(channels)),
223                      rate);
224       AudioConfig out(channels, rate);
225       if (!in.IsValid() || !out.IsValid()) {
226         return MediaResult(
227           NS_ERROR_DOM_MEDIA_FATAL_ERR,
228           RESULT_DETAIL("Invalid channel layout:%u", channels));
229       }
230       mAudioConverter = MakeUnique<AudioConverter>(in, out);
231     }
232     MOZ_ASSERT(mAudioConverter->CanWorkInPlace());
233     AudioSampleBuffer data(Move(buffer));
234     data = mAudioConverter->Process(Move(data));
235 
236     aTotalFrames += frames;
237     mCallback->Output(new AudioData(aOffset,
238                                     time.value(),
239                                     duration.value(),
240                                     frames,
241                                     data.Forget(),
242                                     channels,
243                                     rate));
244     mFrames += frames;
245     err = vorbis_synthesis_read(&mVorbisDsp, frames);
246     if (err) {
247       return MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
248                          RESULT_DETAIL("vorbis_synthesis_read:%d", err));
249     }
250 
251     frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm);
252   }
253 
254   return NS_OK;
255 }
256 
257 void
ProcessDrain()258 VorbisDataDecoder::ProcessDrain()
259 {
260   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
261   mCallback->DrainComplete();
262 }
263 
264 void
Drain()265 VorbisDataDecoder::Drain()
266 {
267   MOZ_ASSERT(mCallback->OnReaderTaskQueue());
268   mTaskQueue->Dispatch(NewRunnableMethod(this, &VorbisDataDecoder::ProcessDrain));
269 }
270 
271 void
Flush()272 VorbisDataDecoder::Flush()
273 {
274   MOZ_ASSERT(mCallback->OnReaderTaskQueue());
275   mIsFlushing = true;
276   RefPtr<VorbisDataDecoder> self = this;
277   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
278     // Ignore failed results from vorbis_synthesis_restart. They
279     // aren't fatal and it fails when ResetDecode is called at a
280     // time when no vorbis data has been read.
281     vorbis_synthesis_restart(&self->mVorbisDsp);
282     self->mLastFrameTime.reset();
283   });
284   SyncRunnable::DispatchToThread(mTaskQueue, r);
285   mIsFlushing = false;
286 }
287 
288 /* static */
289 bool
IsVorbis(const nsACString & aMimeType)290 VorbisDataDecoder::IsVorbis(const nsACString& aMimeType)
291 {
292   return aMimeType.EqualsLiteral("audio/vorbis");
293 }
294 
295 /* static */ const AudioConfig::Channel*
VorbisLayout(uint32_t aChannels)296 VorbisDataDecoder::VorbisLayout(uint32_t aChannels)
297 {
298   // From https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
299   // Section 4.3.9.
300   typedef AudioConfig::Channel Channel;
301 
302   switch (aChannels) {
303     case 1: // the stream is monophonic
304     {
305       static const Channel config[] = { AudioConfig::CHANNEL_MONO };
306       return config;
307     }
308     case 2: // the stream is stereo. channel order: left, right
309     {
310       static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_RIGHT };
311       return config;
312     }
313     case 3: // the stream is a 1d-surround encoding. channel order: left, center, right
314     {
315       static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT };
316       return config;
317     }
318     case 4: // the stream is quadraphonic surround. channel order: front left, front right, rear left, rear right
319     {
320       static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS };
321       return config;
322     }
323     case 5: // the stream is five-channel surround. channel order: front left, center, front right, rear left, rear right
324     {
325       static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS };
326       return config;
327     }
328     case 6: // the stream is 5.1 surround. channel order: front left, center, front right, rear left, rear right, LFE
329     {
330       static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS, AudioConfig::CHANNEL_LFE };
331       return config;
332     }
333     case 7: // surround. channel order: front left, center, front right, side left, side right, rear center, LFE
334     {
335       static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS, AudioConfig::CHANNEL_RCENTER, AudioConfig::CHANNEL_LFE };
336       return config;
337     }
338     case 8: // the stream is 7.1 surround. channel order: front left, center, front right, side left, side right, rear left, rear right, LFE
339     {
340       static const Channel config[] = { AudioConfig::CHANNEL_LEFT, AudioConfig::CHANNEL_CENTER, AudioConfig::CHANNEL_RIGHT, AudioConfig::CHANNEL_LS, AudioConfig::CHANNEL_RS, AudioConfig::CHANNEL_RLS, AudioConfig::CHANNEL_RRS, AudioConfig::CHANNEL_LFE };
341       return config;
342     }
343     default:
344       return nullptr;
345   }
346 }
347 
348 } // namespace mozilla
349 #undef LOG
350