LCOV - code coverage report
Current view: top level - dom/media/platforms/agnostic - OpusDecoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 170 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 19 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 "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

Generated by: LCOV version 1.13