LCOV - code coverage report
Current view: top level - dom/media/platforms/agnostic - VorbisDecoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 165 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 20 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13