LCOV - code coverage report
Current view: top level - dom/media/webm - WebMDemuxer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 688 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 48 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 "nsError.h"
       8             : #include "MediaDecoderStateMachine.h"
       9             : #include "AbstractMediaDecoder.h"
      10             : #include "MediaResource.h"
      11             : #ifdef MOZ_AV1
      12             : #include "AOMDecoder.h"
      13             : #endif
      14             : #include "OpusDecoder.h"
      15             : #include "VPXDecoder.h"
      16             : #include "WebMDemuxer.h"
      17             : #include "WebMBufferedParser.h"
      18             : #include "gfx2DGlue.h"
      19             : #include "mozilla/Atomics.h"
      20             : #include "mozilla/EndianUtils.h"
      21             : #include "mozilla/SharedThreadPool.h"
      22             : #include "MediaDataDemuxer.h"
      23             : #include "nsAutoPtr.h"
      24             : #include "nsAutoRef.h"
      25             : #include "NesteggPacketHolder.h"
      26             : #include "XiphExtradata.h"
      27             : #include "prprf.h"           // leaving it for PR_vsnprintf()
      28             : #include "mozilla/IntegerPrintfMacros.h"
      29             : #include "mozilla/SizePrintfMacros.h"
      30             : #include "mozilla/Sprintf.h"
      31             : 
      32             : #include <algorithm>
      33             : #include <numeric>
      34             : #include <stdint.h>
      35             : 
      36             : #define WEBM_DEBUG(arg, ...) MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, ("WebMDemuxer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
      37             : extern mozilla::LazyLogModule gMediaDemuxerLog;
      38             : 
      39             : namespace mozilla {
      40             : 
      41             : using namespace gfx;
      42             : using media::TimeUnit;
      43             : 
      44             : LazyLogModule gNesteggLog("Nestegg");
      45             : 
      46             : // How far ahead will we look when searching future keyframe. In microseconds.
      47             : // This value is based on what appears to be a reasonable value as most webm
      48             : // files encountered appear to have keyframes located < 4s.
      49             : #define MAX_LOOK_AHEAD 10000000
      50             : 
      51             : static Atomic<uint32_t> sStreamSourceID(0u);
      52             : 
      53             : // Functions for reading and seeking using WebMDemuxer required for
      54             : // nestegg_io. The 'user data' passed to these functions is the
      55             : // demuxer.
      56           0 : static int webmdemux_read(void* aBuffer, size_t aLength, void* aUserData)
      57             : {
      58           0 :   MOZ_ASSERT(aUserData);
      59           0 :   MOZ_ASSERT(aLength < UINT32_MAX);
      60             :   WebMDemuxer::NestEggContext* context =
      61           0 :     reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
      62           0 :   uint32_t count = aLength;
      63           0 :   if (context->IsMediaSource()) {
      64           0 :     int64_t length = context->GetEndDataOffset();
      65           0 :     int64_t position = context->GetResource()->Tell();
      66           0 :     MOZ_ASSERT(position <= context->GetResource()->GetLength());
      67           0 :     MOZ_ASSERT(position <= length);
      68           0 :     if (length >= 0 && count + position > length) {
      69           0 :       count = length - position;
      70             :     }
      71           0 :     MOZ_ASSERT(count <= aLength);
      72             :   }
      73           0 :   uint32_t bytes = 0;
      74             :   nsresult rv =
      75           0 :     context->GetResource()->Read(static_cast<char*>(aBuffer), count, &bytes);
      76           0 :   bool eof = bytes < aLength;
      77           0 :   return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
      78             : }
      79             : 
      80           0 : static int webmdemux_seek(int64_t aOffset, int aWhence, void* aUserData)
      81             : {
      82           0 :   MOZ_ASSERT(aUserData);
      83             :   WebMDemuxer::NestEggContext* context =
      84           0 :     reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
      85           0 :   nsresult rv = context->GetResource()->Seek(aWhence, aOffset);
      86           0 :   return NS_SUCCEEDED(rv) ? 0 : -1;
      87             : }
      88             : 
      89           0 : static int64_t webmdemux_tell(void* aUserData)
      90             : {
      91           0 :   MOZ_ASSERT(aUserData);
      92             :   WebMDemuxer::NestEggContext* context =
      93           0 :     reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
      94           0 :   return context->GetResource()->Tell();
      95             : }
      96             : 
      97           0 : static void webmdemux_log(nestegg* aContext,
      98             :                           unsigned int aSeverity,
      99             :                           char const* aFormat, ...)
     100             : {
     101           0 :   if (!MOZ_LOG_TEST(gNesteggLog, LogLevel::Debug)) {
     102           0 :     return;
     103             :   }
     104             : 
     105             :   va_list args;
     106             :   char msg[256];
     107             :   const char* sevStr;
     108             : 
     109           0 :   switch(aSeverity) {
     110             :     case NESTEGG_LOG_DEBUG:
     111           0 :       sevStr = "DBG";
     112           0 :       break;
     113             :     case NESTEGG_LOG_INFO:
     114           0 :       sevStr = "INF";
     115           0 :       break;
     116             :     case NESTEGG_LOG_WARNING:
     117           0 :       sevStr = "WRN";
     118           0 :       break;
     119             :     case NESTEGG_LOG_ERROR:
     120           0 :       sevStr = "ERR";
     121           0 :       break;
     122             :     case NESTEGG_LOG_CRITICAL:
     123           0 :       sevStr = "CRT";
     124           0 :       break;
     125             :     default:
     126           0 :       sevStr = "UNK";
     127           0 :       break;
     128             :   }
     129             : 
     130           0 :   va_start(args, aFormat);
     131             : 
     132           0 :   SprintfLiteral(msg, "%p [Nestegg-%s] ", aContext, sevStr);
     133           0 :   PR_vsnprintf(msg+strlen(msg), sizeof(msg)-strlen(msg), aFormat, args);
     134           0 :   MOZ_LOG(gNesteggLog, LogLevel::Debug, ("%s", msg));
     135             : 
     136           0 :   va_end(args);
     137             : }
     138             : 
     139           0 : WebMDemuxer::NestEggContext::~NestEggContext()
     140             : {
     141           0 :   if (mContext) {
     142           0 :     nestegg_destroy(mContext);
     143             :   }
     144           0 : }
     145             : 
     146             : int
     147           0 : WebMDemuxer::NestEggContext::Init()
     148             : {
     149             :   nestegg_io io;
     150           0 :   io.read = webmdemux_read;
     151           0 :   io.seek = webmdemux_seek;
     152           0 :   io.tell = webmdemux_tell;
     153           0 :   io.userdata = this;
     154             : 
     155             :   // While reading the metadata, we do not really care about which nestegg
     156             :   // context is being used so long that they are both initialised.
     157             :   // For reading the metadata however, we will use mVideoContext.
     158           0 :   return nestegg_init(&mContext, io, &webmdemux_log,
     159           0 :                       mParent->IsMediaSource() ? mResource.GetLength() : -1);
     160             : }
     161             : 
     162           0 : WebMDemuxer::WebMDemuxer(MediaResource* aResource)
     163           0 :   : WebMDemuxer(aResource, false)
     164             : {
     165           0 : }
     166             : 
     167           0 : WebMDemuxer::WebMDemuxer(MediaResource* aResource, bool aIsMediaSource)
     168             :   : mVideoContext(this, aResource)
     169             :   , mAudioContext(this, aResource)
     170             :   , mBufferedState(nullptr)
     171             :   , mInitData(nullptr)
     172             :   , mVideoTrack(0)
     173             :   , mAudioTrack(0)
     174             :   , mSeekPreroll(0)
     175             :   , mAudioCodec(-1)
     176             :   , mVideoCodec(-1)
     177             :   , mHasVideo(false)
     178             :   , mHasAudio(false)
     179             :   , mNeedReIndex(true)
     180             :   , mLastWebMBlockOffset(-1)
     181           0 :   , mIsMediaSource(aIsMediaSource)
     182             : {
     183           0 : }
     184             : 
     185           0 : WebMDemuxer::~WebMDemuxer()
     186             : {
     187           0 :   Reset(TrackInfo::kVideoTrack);
     188           0 :   Reset(TrackInfo::kAudioTrack);
     189           0 : }
     190             : 
     191             : RefPtr<WebMDemuxer::InitPromise>
     192           0 : WebMDemuxer::Init()
     193             : {
     194           0 :   InitBufferedState();
     195             : 
     196           0 :   if (NS_FAILED(ReadMetadata())) {
     197             :     return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
     198           0 :                                         __func__);
     199             :   }
     200             : 
     201           0 :   if (!GetNumberTracks(TrackInfo::kAudioTrack)
     202           0 :       && !GetNumberTracks(TrackInfo::kVideoTrack)) {
     203             :     return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
     204           0 :                                         __func__);
     205             :   }
     206             : 
     207           0 :   return InitPromise::CreateAndResolve(NS_OK, __func__);
     208             : }
     209             : 
     210             : void
     211           0 : WebMDemuxer::InitBufferedState()
     212             : {
     213           0 :   MOZ_ASSERT(!mBufferedState);
     214           0 :   mBufferedState = new WebMBufferedState;
     215           0 : }
     216             : 
     217             : bool
     218           0 : WebMDemuxer::HasTrackType(TrackInfo::TrackType aType) const
     219             : {
     220           0 :   return !!GetNumberTracks(aType);
     221             : }
     222             : 
     223             : uint32_t
     224           0 : WebMDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
     225             : {
     226           0 :   switch(aType) {
     227             :     case TrackInfo::kAudioTrack:
     228           0 :       return mHasAudio ? 1 : 0;
     229             :     case TrackInfo::kVideoTrack:
     230           0 :       return mHasVideo ? 1 : 0;
     231             :     default:
     232           0 :       return 0;
     233             :   }
     234             : }
     235             : 
     236             : UniquePtr<TrackInfo>
     237           0 : WebMDemuxer::GetTrackInfo(TrackInfo::TrackType aType,
     238             :                           size_t aTrackNumber) const
     239             : {
     240           0 :   switch(aType) {
     241             :     case TrackInfo::kAudioTrack:
     242           0 :       return mInfo.mAudio.Clone();
     243             :     case TrackInfo::kVideoTrack:
     244           0 :       return mInfo.mVideo.Clone();
     245             :     default:
     246           0 :       return nullptr;
     247             :   }
     248             : }
     249             : 
     250             : already_AddRefed<MediaTrackDemuxer>
     251           0 : WebMDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
     252             : {
     253           0 :   if (GetNumberTracks(aType) <= aTrackNumber) {
     254           0 :     return nullptr;
     255             :   }
     256             :   RefPtr<WebMTrackDemuxer> e =
     257           0 :     new WebMTrackDemuxer(this, aType, aTrackNumber);
     258           0 :   mDemuxers.AppendElement(e);
     259             : 
     260           0 :   return e.forget();
     261             : }
     262             : 
     263             : nsresult
     264           0 : WebMDemuxer::Reset(TrackInfo::TrackType aType)
     265             : {
     266           0 :   if (aType == TrackInfo::kVideoTrack) {
     267           0 :     mVideoPackets.Reset();
     268             :   } else {
     269           0 :     mAudioPackets.Reset();
     270             :   }
     271           0 :   return NS_OK;
     272             : }
     273             : 
     274             : nsresult
     275           0 : WebMDemuxer::ReadMetadata()
     276             : {
     277           0 :   int r = mVideoContext.Init();
     278           0 :   if (r == -1) {
     279           0 :     return NS_ERROR_FAILURE;
     280             :   }
     281           0 :   if (mAudioContext.Init() == -1) {
     282           0 :     return NS_ERROR_FAILURE;
     283             :   }
     284             : 
     285             :   // For reading the metadata we can only use the video resource/context.
     286           0 :   MediaResourceIndex& resource = Resource(TrackInfo::kVideoTrack);
     287           0 :   nestegg* context = Context(TrackInfo::kVideoTrack);
     288             : 
     289             :   {
     290             :     // Check how much data nestegg read and force feed it to BufferedState.
     291           0 :     RefPtr<MediaByteBuffer> buffer = resource.MediaReadAt(0, resource.Tell());
     292           0 :     if (!buffer) {
     293           0 :       return NS_ERROR_FAILURE;
     294             :     }
     295           0 :     mBufferedState->NotifyDataArrived(buffer->Elements(), buffer->Length(), 0);
     296           0 :     if (mBufferedState->GetInitEndOffset() < 0) {
     297           0 :       return NS_ERROR_FAILURE;
     298             :     }
     299           0 :     MOZ_ASSERT(mBufferedState->GetInitEndOffset() <= resource.Tell());
     300             :   }
     301           0 :   mInitData = resource.MediaReadAt(0, mBufferedState->GetInitEndOffset());
     302           0 :   if (!mInitData
     303           0 :       || mInitData->Length() != size_t(mBufferedState->GetInitEndOffset())) {
     304           0 :     return NS_ERROR_FAILURE;
     305             :   }
     306             : 
     307           0 :   unsigned int ntracks = 0;
     308           0 :   r = nestegg_track_count(context, &ntracks);
     309           0 :   if (r == -1) {
     310           0 :     return NS_ERROR_FAILURE;
     311             :   }
     312             : 
     313           0 :   for (unsigned int track = 0; track < ntracks; ++track) {
     314           0 :     int id = nestegg_track_codec_id(context, track);
     315           0 :     if (id == -1) {
     316           0 :       return NS_ERROR_FAILURE;
     317             :     }
     318           0 :     int type = nestegg_track_type(context, track);
     319           0 :     if (type == NESTEGG_TRACK_VIDEO && !mHasVideo) {
     320             :       nestegg_video_params params;
     321           0 :       r = nestegg_track_video_params(context, track, &params);
     322           0 :       if (r == -1) {
     323           0 :         return NS_ERROR_FAILURE;
     324             :       }
     325           0 :       mVideoCodec = nestegg_track_codec_id(context, track);
     326           0 :       switch(mVideoCodec) {
     327             :         case NESTEGG_CODEC_VP8:
     328           0 :           mInfo.mVideo.mMimeType = "video/webm; codecs=vp8";
     329           0 :           break;
     330             :         case NESTEGG_CODEC_VP9:
     331           0 :           mInfo.mVideo.mMimeType = "video/webm; codecs=vp9";
     332           0 :           break;
     333             :         case NESTEGG_CODEC_AV1:
     334           0 :           mInfo.mVideo.mMimeType = "video/webm; codecs=av1";
     335           0 :           break;
     336             :         default:
     337           0 :           NS_WARNING("Unknown WebM video codec");
     338           0 :           return NS_ERROR_FAILURE;
     339             :       }
     340             :       // Picture region, taking into account cropping, before scaling
     341             :       // to the display size.
     342           0 :       unsigned int cropH = params.crop_right + params.crop_left;
     343           0 :       unsigned int cropV = params.crop_bottom + params.crop_top;
     344             :       nsIntRect pictureRect(params.crop_left,
     345             :                             params.crop_top,
     346           0 :                             params.width - cropH,
     347           0 :                             params.height - cropV);
     348             : 
     349             :       // If the cropping data appears invalid then use the frame data
     350           0 :       if (pictureRect.width <= 0
     351           0 :           || pictureRect.height <= 0
     352           0 :           || pictureRect.x < 0
     353           0 :           || pictureRect.y < 0) {
     354           0 :         pictureRect.x = 0;
     355           0 :         pictureRect.y = 0;
     356           0 :         pictureRect.width = params.width;
     357           0 :         pictureRect.height = params.height;
     358             :       }
     359             : 
     360             :       // Validate the container-reported frame and pictureRect sizes. This
     361             :       // ensures that our video frame creation code doesn't overflow.
     362           0 :       nsIntSize displaySize(params.display_width, params.display_height);
     363           0 :       nsIntSize frameSize(params.width, params.height);
     364           0 :       if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
     365             :         // Video track's frame sizes will overflow. Ignore the video track.
     366           0 :         continue;
     367             :       }
     368             : 
     369           0 :       mVideoTrack = track;
     370           0 :       mHasVideo = true;
     371             : 
     372           0 :       mInfo.mVideo.mDisplay = displaySize;
     373           0 :       mInfo.mVideo.mImage = frameSize;
     374           0 :       mInfo.mVideo.SetImageRect(pictureRect);
     375           0 :       mInfo.mVideo.SetAlpha(params.alpha_mode);
     376             : 
     377           0 :       switch (params.stereo_mode) {
     378             :         case NESTEGG_VIDEO_MONO:
     379           0 :           mInfo.mVideo.mStereoMode = StereoMode::MONO;
     380           0 :           break;
     381             :         case NESTEGG_VIDEO_STEREO_LEFT_RIGHT:
     382           0 :           mInfo.mVideo.mStereoMode = StereoMode::LEFT_RIGHT;
     383           0 :           break;
     384             :         case NESTEGG_VIDEO_STEREO_BOTTOM_TOP:
     385           0 :           mInfo.mVideo.mStereoMode = StereoMode::BOTTOM_TOP;
     386           0 :           break;
     387             :         case NESTEGG_VIDEO_STEREO_TOP_BOTTOM:
     388           0 :           mInfo.mVideo.mStereoMode = StereoMode::TOP_BOTTOM;
     389           0 :           break;
     390             :         case NESTEGG_VIDEO_STEREO_RIGHT_LEFT:
     391           0 :           mInfo.mVideo.mStereoMode = StereoMode::RIGHT_LEFT;
     392           0 :           break;
     393             :       }
     394           0 :       uint64_t duration = 0;
     395           0 :       r = nestegg_duration(context, &duration);
     396           0 :       if (!r) {
     397           0 :         mInfo.mVideo.mDuration = TimeUnit::FromNanoseconds(duration);
     398             :       }
     399           0 :       mInfo.mVideo.mCrypto = GetTrackCrypto(TrackInfo::kVideoTrack, track);
     400           0 :       if (mInfo.mVideo.mCrypto.mValid) {
     401           0 :         mCrypto.AddInitData(NS_LITERAL_STRING("webm"),
     402           0 :                             mInfo.mVideo.mCrypto.mKeyId);
     403           0 :       }
     404           0 :     } else if (type == NESTEGG_TRACK_AUDIO && !mHasAudio) {
     405             :       nestegg_audio_params params;
     406           0 :       r = nestegg_track_audio_params(context, track, &params);
     407           0 :       if (r == -1) {
     408           0 :         return NS_ERROR_FAILURE;
     409             :       }
     410             : 
     411           0 :       mAudioTrack = track;
     412           0 :       mHasAudio = true;
     413           0 :       mAudioCodec = nestegg_track_codec_id(context, track);
     414           0 :       if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
     415           0 :         mInfo.mAudio.mMimeType = "audio/vorbis";
     416           0 :       } else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
     417           0 :         mInfo.mAudio.mMimeType = "audio/opus";
     418           0 :         OpusDataDecoder::AppendCodecDelay(
     419             :           mInfo.mAudio.mCodecSpecificConfig,
     420           0 :           TimeUnit::FromNanoseconds(params.codec_delay).ToMicroseconds());
     421             :       }
     422           0 :       mSeekPreroll = params.seek_preroll;
     423           0 :       mInfo.mAudio.mRate = params.rate;
     424           0 :       mInfo.mAudio.mChannels = params.channels;
     425             : 
     426           0 :       unsigned int nheaders = 0;
     427           0 :       r = nestegg_track_codec_data_count(context, track, &nheaders);
     428           0 :       if (r == -1) {
     429           0 :         return NS_ERROR_FAILURE;
     430             :       }
     431             : 
     432           0 :       AutoTArray<const unsigned char*,4> headers;
     433           0 :       AutoTArray<size_t,4> headerLens;
     434           0 :       for (uint32_t header = 0; header < nheaders; ++header) {
     435           0 :         unsigned char* data = 0;
     436           0 :         size_t length = 0;
     437           0 :         r = nestegg_track_codec_data(context, track, header, &data, &length);
     438           0 :         if (r == -1) {
     439           0 :           return NS_ERROR_FAILURE;
     440             :         }
     441           0 :         headers.AppendElement(data);
     442           0 :         headerLens.AppendElement(length);
     443             :       }
     444             : 
     445             :       // Vorbis has 3 headers, convert to Xiph extradata format to send them to
     446             :       // the demuxer.
     447             :       // TODO: This is already the format WebM stores them in. Would be nice
     448             :       // to avoid having libnestegg split them only for us to pack them again,
     449             :       // but libnestegg does not give us an API to access this data directly.
     450           0 :       if (nheaders > 1) {
     451           0 :         if (!XiphHeadersToExtradata(mInfo.mAudio.mCodecSpecificConfig,
     452             :                                     headers, headerLens)) {
     453           0 :           return NS_ERROR_FAILURE;
     454             :         }
     455             :       }
     456             :       else {
     457           0 :         mInfo.mAudio.mCodecSpecificConfig->AppendElements(headers[0],
     458           0 :                                                           headerLens[0]);
     459             :       }
     460           0 :       uint64_t duration = 0;
     461           0 :       r = nestegg_duration(context, &duration);
     462           0 :       if (!r) {
     463           0 :         mInfo.mAudio.mDuration = TimeUnit::FromNanoseconds(duration);
     464             :       }
     465           0 :       mInfo.mAudio.mCrypto = GetTrackCrypto(TrackInfo::kAudioTrack, track);
     466           0 :       if (mInfo.mAudio.mCrypto.mValid) {
     467           0 :         mCrypto.AddInitData(NS_LITERAL_STRING("webm"),
     468           0 :                             mInfo.mAudio.mCrypto.mKeyId);
     469             :       }
     470             :     }
     471             :   }
     472           0 :   return NS_OK;
     473             : }
     474             : 
     475             : bool
     476           0 : WebMDemuxer::IsSeekable() const
     477             : {
     478           0 :   return Context(TrackInfo::kVideoTrack)
     479           0 :          && nestegg_has_cues(Context(TrackInfo::kVideoTrack));
     480             : }
     481             : 
     482             : bool
     483           0 : WebMDemuxer::IsSeekableOnlyInBufferedRanges() const
     484             : {
     485           0 :   return Context(TrackInfo::kVideoTrack)
     486           0 :          && !nestegg_has_cues(Context(TrackInfo::kVideoTrack));
     487             : }
     488             : 
     489             : void
     490           0 : WebMDemuxer::EnsureUpToDateIndex()
     491             : {
     492           0 :   if (!mNeedReIndex || !mInitData) {
     493           0 :     return;
     494             :   }
     495             :   AutoPinned<MediaResource> resource(
     496           0 :     Resource(TrackInfo::kVideoTrack).GetResource());
     497           0 :   MediaByteRangeSet byteRanges;
     498           0 :   nsresult rv = resource->GetCachedRanges(byteRanges);
     499           0 :   if (NS_FAILED(rv) || !byteRanges.Length()) {
     500           0 :     return;
     501             :   }
     502           0 :   mBufferedState->UpdateIndex(byteRanges, resource);
     503             : 
     504           0 :   mNeedReIndex = false;
     505             : 
     506           0 :   if (!mIsMediaSource) {
     507           0 :     return;
     508             :   }
     509           0 :   mLastWebMBlockOffset = mBufferedState->GetLastBlockOffset();
     510           0 :   MOZ_ASSERT(mLastWebMBlockOffset <= resource->GetLength());
     511             : }
     512             : 
     513             : void
     514           0 : WebMDemuxer::NotifyDataArrived()
     515             : {
     516           0 :   WEBM_DEBUG("");
     517           0 :   mNeedReIndex = true;
     518           0 : }
     519             : 
     520             : void
     521           0 : WebMDemuxer::NotifyDataRemoved()
     522             : {
     523           0 :   mBufferedState->Reset();
     524           0 :   if (mInitData) {
     525           0 :     mBufferedState->NotifyDataArrived(mInitData->Elements(),
     526           0 :                                       mInitData->Length(), 0);
     527             :   }
     528           0 :   mNeedReIndex = true;
     529           0 : }
     530             : 
     531             : UniquePtr<EncryptionInfo>
     532           0 : WebMDemuxer::GetCrypto()
     533             : {
     534           0 :   return mCrypto.IsEncrypted() ? MakeUnique<EncryptionInfo>(mCrypto) : nullptr;
     535             : }
     536             : 
     537             : CryptoTrack
     538           0 : WebMDemuxer::GetTrackCrypto(TrackInfo::TrackType aType, size_t aTrackNumber)
     539             : {
     540           0 :   const int WEBM_IV_SIZE = 16;
     541             :   const unsigned char * contentEncKeyId;
     542             :   size_t contentEncKeyIdLength;
     543           0 :   CryptoTrack crypto;
     544           0 :   nestegg* context = Context(aType);
     545             : 
     546           0 :   int r = nestegg_track_content_enc_key_id(
     547           0 :     context, aTrackNumber, &contentEncKeyId, &contentEncKeyIdLength);
     548             : 
     549           0 :   if (r == -1) {
     550           0 :     WEBM_DEBUG("nestegg_track_content_enc_key_id failed r=%d", r);
     551           0 :     return crypto;
     552             :   }
     553             : 
     554             :   uint32_t i;
     555           0 :   nsTArray<uint8_t> initData;
     556           0 :   for (i = 0; i < contentEncKeyIdLength; i++) {
     557           0 :     initData.AppendElement(contentEncKeyId[i]);
     558             :   }
     559             : 
     560           0 :   if (!initData.IsEmpty()) {
     561           0 :     crypto.mValid = true;
     562             :     // crypto.mMode is not used for WebMs
     563           0 :     crypto.mIVSize = WEBM_IV_SIZE;
     564           0 :     crypto.mKeyId = Move(initData);
     565             :   }
     566             : 
     567           0 :   return crypto;
     568             : }
     569             : 
     570             : nsresult
     571           0 : WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType,
     572             :                            MediaRawDataQueue *aSamples)
     573             : {
     574           0 :   if (mIsMediaSource) {
     575             :     // To ensure mLastWebMBlockOffset is properly up to date.
     576           0 :     EnsureUpToDateIndex();
     577             :   }
     578             : 
     579           0 :   RefPtr<NesteggPacketHolder> holder;
     580           0 :   nsresult rv = NextPacket(aType, holder);
     581             : 
     582           0 :   if (NS_FAILED(rv)) {
     583           0 :     return rv;
     584             :   }
     585             : 
     586           0 :   int r = 0;
     587           0 :   unsigned int count = 0;
     588           0 :   r = nestegg_packet_count(holder->Packet(), &count);
     589           0 :   if (r == -1) {
     590           0 :     return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
     591             :   }
     592           0 :   int64_t tstamp = holder->Timestamp();
     593           0 :   int64_t duration = holder->Duration();
     594             : 
     595             :   // The end time of this frame is the start time of the next frame. Fetch
     596             :   // the timestamp of the next packet for this track.  If we've reached the
     597             :   // end of the resource, use the file's duration as the end time of this
     598             :   // video frame.
     599           0 :   int64_t next_tstamp = INT64_MIN;
     600           0 :   if (aType == TrackInfo::kAudioTrack) {
     601           0 :     RefPtr<NesteggPacketHolder> next_holder;
     602           0 :     rv = NextPacket(aType, next_holder);
     603           0 :     if (NS_FAILED(rv) && rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
     604           0 :       return rv;
     605             :     }
     606           0 :     if (next_holder) {
     607           0 :       next_tstamp = next_holder->Timestamp();
     608           0 :       PushAudioPacket(next_holder);
     609           0 :     } else if (duration >= 0) {
     610           0 :       next_tstamp = tstamp + duration;
     611           0 :     } else if (!mIsMediaSource
     612           0 :                || (mIsMediaSource && mLastAudioFrameTime.isSome())) {
     613           0 :       next_tstamp = tstamp;
     614           0 :       next_tstamp += tstamp - mLastAudioFrameTime.refOr(0);
     615             :     } else {
     616           0 :       PushAudioPacket(holder);
     617             :     }
     618           0 :     mLastAudioFrameTime = Some(tstamp);
     619           0 :   } else if (aType == TrackInfo::kVideoTrack) {
     620           0 :     RefPtr<NesteggPacketHolder> next_holder;
     621           0 :     rv = NextPacket(aType, next_holder);
     622           0 :     if (NS_FAILED(rv) && rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
     623           0 :       return rv;
     624             :     }
     625           0 :     if (next_holder) {
     626           0 :       next_tstamp = next_holder->Timestamp();
     627           0 :       PushVideoPacket(next_holder);
     628           0 :     } else if (duration >= 0) {
     629           0 :       next_tstamp = tstamp + duration;
     630           0 :     } else if (!mIsMediaSource
     631           0 :                || (mIsMediaSource && mLastVideoFrameTime.isSome())) {
     632           0 :       next_tstamp = tstamp;
     633           0 :       next_tstamp += tstamp - mLastVideoFrameTime.refOr(0);
     634             :     } else {
     635           0 :       PushVideoPacket(holder);
     636             :     }
     637           0 :     mLastVideoFrameTime = Some(tstamp);
     638             :   }
     639             : 
     640           0 :   if (mIsMediaSource && next_tstamp == INT64_MIN) {
     641           0 :     return NS_ERROR_DOM_MEDIA_END_OF_STREAM;
     642             :   }
     643             : 
     644           0 :   int64_t discardPadding = 0;
     645           0 :   if (aType == TrackInfo::kAudioTrack) {
     646           0 :     (void) nestegg_packet_discard_padding(holder->Packet(), &discardPadding);
     647             :   }
     648             : 
     649           0 :   int packetEncryption = nestegg_packet_encryption(holder->Packet());
     650             : 
     651           0 :   for (uint32_t i = 0; i < count; ++i) {
     652             :     unsigned char* data;
     653             :     size_t length;
     654           0 :     r = nestegg_packet_data(holder->Packet(), i, &data, &length);
     655           0 :     if (r == -1) {
     656           0 :       WEBM_DEBUG("nestegg_packet_data failed r=%d", r);
     657           0 :       return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
     658             :     }
     659             :     unsigned char* alphaData;
     660           0 :     size_t alphaLength = 0;
     661             :     // Check packets for alpha information if file has declared alpha frames
     662             :     // may be present.
     663           0 :     if (mInfo.mVideo.HasAlpha()) {
     664           0 :       r = nestegg_packet_additional_data(holder->Packet(),
     665             :                                          1,
     666             :                                          &alphaData,
     667           0 :                                          &alphaLength);
     668           0 :       if (r == -1) {
     669           0 :         WEBM_DEBUG(
     670             :           "nestegg_packet_additional_data failed to retrieve alpha data r=%d",
     671             :           r);
     672             :       }
     673             :     }
     674           0 :     bool isKeyframe = false;
     675           0 :     if (aType == TrackInfo::kAudioTrack) {
     676           0 :       isKeyframe = true;
     677           0 :     } else if (aType == TrackInfo::kVideoTrack) {
     678           0 :       if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
     679             :         // Packet is encrypted, can't peek, use packet info
     680           0 :         isKeyframe = nestegg_packet_has_keyframe(holder->Packet())
     681             :                      == NESTEGG_PACKET_HAS_KEYFRAME_TRUE;
     682             :       } else {
     683           0 :         auto sample = MakeSpan(data, length);
     684           0 :         switch (mVideoCodec) {
     685             :         case NESTEGG_CODEC_VP8:
     686           0 :           isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP8);
     687           0 :           break;
     688             :         case NESTEGG_CODEC_VP9:
     689           0 :           isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP9);
     690           0 :           break;
     691             : #ifdef MOZ_AV1
     692             :         case NESTEGG_CODEC_AV1:
     693           0 :           isKeyframe = AOMDecoder::IsKeyframe(sample);
     694           0 :           break;
     695             : #endif
     696             :         default:
     697           0 :           NS_WARNING("Cannot detect keyframes in unknown WebM video codec");
     698           0 :           return NS_ERROR_FAILURE;
     699             :         }
     700           0 :         if (isKeyframe) {
     701             :           // For both VP8 and VP9, we only look for resolution changes
     702             :           // on keyframes. Other resolution changes are invalid.
     703           0 :           auto dimensions = nsIntSize(0, 0);
     704           0 :           switch (mVideoCodec) {
     705             :           case NESTEGG_CODEC_VP8:
     706           0 :             dimensions = VPXDecoder::GetFrameSize(sample, VPXDecoder::Codec::VP8);
     707           0 :             break;
     708             :           case NESTEGG_CODEC_VP9:
     709           0 :             dimensions = VPXDecoder::GetFrameSize(sample, VPXDecoder::Codec::VP9);
     710           0 :             break;
     711             : #ifdef MOZ_AV1
     712             :           case NESTEGG_CODEC_AV1:
     713           0 :             dimensions = AOMDecoder::GetFrameSize(sample);
     714           0 :             break;
     715             : #endif
     716             :           }
     717           0 :           if (mLastSeenFrameSize.isSome()
     718           0 :               && (dimensions != mLastSeenFrameSize.value())) {
     719           0 :             mInfo.mVideo.mDisplay = dimensions;
     720             :             mSharedVideoTrackInfo =
     721           0 :               new TrackInfoSharedPtr(mInfo.mVideo, ++sStreamSourceID);
     722             :           }
     723           0 :           mLastSeenFrameSize = Some(dimensions);
     724             :         }
     725             :       }
     726             :     }
     727             : 
     728           0 :     WEBM_DEBUG("push sample tstamp: %" PRId64 " next_tstamp: %" PRId64 " length: %" PRIuSIZE " kf: %d",
     729             :                tstamp, next_tstamp, length, isKeyframe);
     730           0 :     RefPtr<MediaRawData> sample;
     731           0 :     if (mInfo.mVideo.HasAlpha() && alphaLength != 0) {
     732           0 :       sample = new MediaRawData(data, length, alphaData, alphaLength);
     733           0 :       if ((length && !sample->Data()) || (alphaLength && !sample->AlphaData())) {
     734             :         // OOM.
     735           0 :         return NS_ERROR_OUT_OF_MEMORY;
     736             :       }
     737             :     } else {
     738           0 :       sample = new MediaRawData(data, length);
     739           0 :       if (length && !sample->Data()) {
     740             :         // OOM.
     741           0 :         return NS_ERROR_OUT_OF_MEMORY;
     742             :       }
     743             :     }
     744           0 :     sample->mTimecode = TimeUnit::FromMicroseconds(tstamp);
     745           0 :     sample->mTime = TimeUnit::FromMicroseconds(tstamp);
     746           0 :     sample->mDuration = TimeUnit::FromMicroseconds(next_tstamp - tstamp);
     747           0 :     sample->mOffset = holder->Offset();
     748           0 :     sample->mKeyframe = isKeyframe;
     749           0 :     if (discardPadding && i == count - 1) {
     750           0 :       CheckedInt64 discardFrames;
     751           0 :       if (discardPadding < 0) {
     752             :         // This is an invalid value as discard padding should never be negative.
     753             :         // Set to maximum value so that the decoder will reject it as it's
     754             :         // greater than the number of frames available.
     755           0 :         discardFrames = INT32_MAX;
     756           0 :         WEBM_DEBUG("Invalid negative discard padding");
     757             :       } else {
     758           0 :         discardFrames = TimeUnitToFrames(
     759           0 :           TimeUnit::FromNanoseconds(discardPadding), mInfo.mAudio.mRate);
     760             :       }
     761           0 :       if (discardFrames.isValid()) {
     762           0 :         sample->mDiscardPadding = discardFrames.value();
     763             :       }
     764             :     }
     765             : 
     766           0 :     if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED
     767           0 :         || packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED
     768           0 :         || packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
     769           0 :       nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
     770             :       unsigned char const* iv;
     771             :       size_t ivLength;
     772           0 :       nestegg_packet_iv(holder->Packet(), &iv, &ivLength);
     773           0 :       writer->mCrypto.mValid = true;
     774           0 :       writer->mCrypto.mIVSize = ivLength;
     775           0 :       if (ivLength == 0) {
     776             :         // Frame is not encrypted
     777           0 :         writer->mCrypto.mPlainSizes.AppendElement(length);
     778           0 :         writer->mCrypto.mEncryptedSizes.AppendElement(0);
     779             :       } else {
     780             :         // Frame is encrypted
     781           0 :         writer->mCrypto.mIV.AppendElements(iv, 8);
     782             :         // Iv from a sample is 64 bits, must be padded with 64 bits more 0s
     783             :         // in compliance with spec
     784           0 :         for (uint32_t i = 0; i < 8; i++) {
     785           0 :           writer->mCrypto.mIV.AppendElement(0);
     786             :         }
     787             : 
     788           0 :         if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
     789           0 :           writer->mCrypto.mPlainSizes.AppendElement(0);
     790           0 :           writer->mCrypto.mEncryptedSizes.AppendElement(length);
     791           0 :         } else if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
     792           0 :           uint8_t numPartitions = 0;
     793           0 :           const uint32_t* partitions = NULL;
     794           0 :           nestegg_packet_offsets(holder->Packet(), &partitions, &numPartitions);
     795             : 
     796             :           // WebM stores a list of 'partitions' in the data, which alternate
     797             :           // clear, encrypted. The data in the first partition is always clear.
     798             :           // So, and sample might look as follows:
     799             :           // 00|XXXX|000|XX, where | represents a partition, 0 a clear byte and
     800             :           // X an encrypted byte. If the first bytes in sample are unencrypted,
     801             :           // the first partition will be at zero |XXXX|000|XX.
     802             :           //
     803             :           // As GMP expects the lengths of the clear and encrypted chunks of
     804             :           // data, we calculate these from the difference between the last two
     805             :           // partitions.
     806           0 :           uint32_t lastOffset = 0;
     807           0 :           bool encrypted = false;
     808             : 
     809           0 :           for (uint8_t i = 0; i < numPartitions; i++) {
     810           0 :             uint32_t partition = partitions[i];
     811           0 :             uint32_t currentLength = partition - lastOffset;
     812             : 
     813           0 :             if (encrypted) {
     814           0 :               writer->mCrypto.mEncryptedSizes.AppendElement(currentLength);
     815             :             } else {
     816           0 :               writer->mCrypto.mPlainSizes.AppendElement(currentLength);
     817             :             }
     818             : 
     819           0 :             encrypted = !encrypted;
     820           0 :             lastOffset = partition;
     821             : 
     822           0 :             assert(lastOffset <= length);
     823             :           }
     824             : 
     825             :           // Add the data between the last offset and the end of the data.
     826             :           // 000|XXX|000
     827             :           //        ^---^
     828           0 :           if (encrypted) {
     829           0 :             writer->mCrypto.mEncryptedSizes.AppendElement(length - lastOffset);
     830             :           } else {
     831           0 :             writer->mCrypto.mPlainSizes.AppendElement(length - lastOffset);
     832             :           }
     833             : 
     834             :           // Make sure we have an equal number of encrypted and plain sizes (GMP
     835             :           // expects this). This simple check is sufficient as there are two
     836             :           // possible cases at this point:
     837             :           // 1. The number of samples are even (so we don't need to do anything)
     838             :           // 2. There is one more clear sample than encrypted samples, so add a
     839             :           // zero length encrypted chunk.
     840             :           // There can never be more encrypted partitions than clear partitions
     841             :           // due to the alternating structure of the WebM samples and the
     842             :           // restriction that the first chunk is always clear.
     843           0 :           if (numPartitions % 2 == 0) {
     844           0 :             writer->mCrypto.mEncryptedSizes.AppendElement(0);
     845             :           }
     846             : 
     847             :           // Assert that the lengths of the encrypted and plain samples add to
     848             :           // the length of the data.
     849           0 :           assert(((size_t)(std::accumulate(writer->mCrypto.mPlainSizes.begin(), writer->mCrypto.mPlainSizes.end(), 0) \
     850             :                  + std::accumulate(writer->mCrypto.mEncryptedSizes.begin(), writer->mCrypto.mEncryptedSizes.end(), 0)) \
     851           0 :                  == length));
     852             :         }
     853             :       }
     854             :     }
     855           0 :     if (aType == TrackInfo::kVideoTrack) {
     856           0 :       sample->mTrackInfo = mSharedVideoTrackInfo;
     857             :     }
     858           0 :     aSamples->Push(sample);
     859             :   }
     860           0 :   return NS_OK;
     861             : }
     862             : 
     863             : nsresult
     864           0 : WebMDemuxer::NextPacket(TrackInfo::TrackType aType,
     865             :                         RefPtr<NesteggPacketHolder>& aPacket)
     866             : {
     867           0 :   bool isVideo = aType == TrackInfo::kVideoTrack;
     868             : 
     869             :   // Flag to indicate that we do need to playback these types of
     870             :   // packets.
     871           0 :   bool hasType = isVideo ? mHasVideo : mHasAudio;
     872             : 
     873           0 :   if (!hasType) {
     874           0 :     return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
     875             :   }
     876             : 
     877             :   // The packet queue for the type that we are interested in.
     878           0 :   WebMPacketQueue &packets = isVideo ? mVideoPackets : mAudioPackets;
     879             : 
     880           0 :   if (packets.GetSize() > 0) {
     881           0 :     aPacket = packets.PopFront();
     882           0 :     return NS_OK;
     883             :   }
     884             : 
     885             :   // Track we are interested in
     886           0 :   uint32_t ourTrack = isVideo ? mVideoTrack : mAudioTrack;
     887             : 
     888             :   do {
     889           0 :     RefPtr<NesteggPacketHolder> holder;
     890           0 :     nsresult rv = DemuxPacket(aType, holder);
     891           0 :     if (NS_FAILED(rv)) {
     892           0 :       return rv;
     893             :     }
     894           0 :     if (!holder) {
     895           0 :       return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
     896             :     }
     897             : 
     898           0 :     if (ourTrack == holder->Track()) {
     899           0 :       aPacket = holder;
     900           0 :       return NS_OK;
     901           0 :     }
     902             :   } while (true);
     903             : }
     904             : 
     905             : nsresult
     906           0 : WebMDemuxer::DemuxPacket(TrackInfo::TrackType aType,
     907             :                          RefPtr<NesteggPacketHolder>& aPacket)
     908             : {
     909             :   nestegg_packet* packet;
     910           0 :   int r = nestegg_read_packet(Context(aType), &packet);
     911           0 :   if (r == 0) {
     912           0 :     nestegg_read_reset(Context(aType));
     913           0 :     return NS_ERROR_DOM_MEDIA_END_OF_STREAM;
     914           0 :   } else if (r < 0) {
     915           0 :     return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
     916             :   }
     917             : 
     918           0 :   unsigned int track = 0;
     919           0 :   r = nestegg_packet_track(packet, &track);
     920           0 :   if (r == -1) {
     921           0 :     return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
     922             :   }
     923             : 
     924           0 :   int64_t offset = Resource(aType).Tell();
     925           0 :   RefPtr<NesteggPacketHolder> holder = new NesteggPacketHolder();
     926           0 :   if (!holder->Init(packet, offset, track, false)) {
     927           0 :     return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
     928             :   }
     929             : 
     930           0 :   aPacket = holder;
     931           0 :   return NS_OK;
     932             : }
     933             : 
     934             : void
     935           0 : WebMDemuxer::PushAudioPacket(NesteggPacketHolder* aItem)
     936             : {
     937           0 :   mAudioPackets.PushFront(aItem);
     938           0 : }
     939             : 
     940             : void
     941           0 : WebMDemuxer::PushVideoPacket(NesteggPacketHolder* aItem)
     942             : {
     943           0 :   mVideoPackets.PushFront(aItem);
     944           0 : }
     945             : 
     946             : nsresult
     947           0 : WebMDemuxer::SeekInternal(TrackInfo::TrackType aType,
     948             :                           const TimeUnit& aTarget)
     949             : {
     950           0 :   EnsureUpToDateIndex();
     951           0 :   uint32_t trackToSeek = mHasVideo ? mVideoTrack : mAudioTrack;
     952           0 :   uint64_t target = aTarget.ToNanoseconds();
     953             : 
     954           0 :   if (NS_FAILED(Reset(aType))) {
     955           0 :     return NS_ERROR_FAILURE;
     956             :   }
     957             : 
     958           0 :   if (mSeekPreroll) {
     959           0 :     uint64_t startTime = 0;
     960           0 :     if (!mBufferedState->GetStartTime(&startTime)) {
     961           0 :       startTime = 0;
     962             :     }
     963           0 :     WEBM_DEBUG("Seek Target: %f",
     964             :                TimeUnit::FromNanoseconds(target).ToSeconds());
     965           0 :     if (target < mSeekPreroll || target - mSeekPreroll < startTime) {
     966           0 :       target = startTime;
     967             :     } else {
     968           0 :       target -= mSeekPreroll;
     969             :     }
     970           0 :     WEBM_DEBUG("SeekPreroll: %f StartTime: %f Adjusted Target: %f",
     971             :                TimeUnit::FromNanoseconds(mSeekPreroll).ToSeconds(),
     972             :                TimeUnit::FromNanoseconds(startTime).ToSeconds(),
     973             :                TimeUnit::FromNanoseconds(target).ToSeconds());
     974             :   }
     975           0 :   int r = nestegg_track_seek(Context(aType), trackToSeek, target);
     976           0 :   if (r == -1) {
     977           0 :     WEBM_DEBUG("track_seek for track %u to %f failed, r=%d", trackToSeek,
     978             :                TimeUnit::FromNanoseconds(target).ToSeconds(), r);
     979             :     // Try seeking directly based on cluster information in memory.
     980           0 :     int64_t offset = 0;
     981           0 :     bool rv = mBufferedState->GetOffsetForTime(target, &offset);
     982           0 :     if (!rv) {
     983           0 :       WEBM_DEBUG("mBufferedState->GetOffsetForTime failed too");
     984           0 :       return NS_ERROR_FAILURE;
     985             :     }
     986             : 
     987           0 :     r = nestegg_offset_seek(Context(aType), offset);
     988           0 :     if (r == -1) {
     989           0 :       WEBM_DEBUG("and nestegg_offset_seek to %" PRIu64 " failed", offset);
     990           0 :       return NS_ERROR_FAILURE;
     991             :     }
     992           0 :     WEBM_DEBUG("got offset from buffered state: %" PRIu64 "", offset);
     993             :   }
     994             : 
     995           0 :   if (aType == TrackInfo::kAudioTrack) {
     996           0 :     mLastAudioFrameTime.reset();
     997             :   } else {
     998           0 :     mLastVideoFrameTime.reset();
     999             :   }
    1000             : 
    1001           0 :   return NS_OK;
    1002             : }
    1003             : 
    1004             : media::TimeIntervals
    1005           0 : WebMDemuxer::GetBuffered()
    1006             : {
    1007           0 :   EnsureUpToDateIndex();
    1008             :   AutoPinned<MediaResource> resource(
    1009           0 :     Resource(TrackInfo::kVideoTrack).GetResource());
    1010             : 
    1011           0 :   media::TimeIntervals buffered;
    1012             : 
    1013           0 :   MediaByteRangeSet ranges;
    1014           0 :   nsresult rv = resource->GetCachedRanges(ranges);
    1015           0 :   if (NS_FAILED(rv)) {
    1016           0 :     return media::TimeIntervals();
    1017             :   }
    1018           0 :   uint64_t duration = 0;
    1019           0 :   uint64_t startOffset = 0;
    1020           0 :   if (!nestegg_duration(Context(TrackInfo::kVideoTrack), &duration)) {
    1021           0 :     if(mBufferedState->GetStartTime(&startOffset)) {
    1022           0 :       duration += startOffset;
    1023             :     }
    1024           0 :     WEBM_DEBUG("Duration: %f StartTime: %f",
    1025             :                TimeUnit::FromNanoseconds(duration).ToSeconds(),
    1026             :                TimeUnit::FromNanoseconds(startOffset).ToSeconds());
    1027             :   }
    1028           0 :   for (uint32_t index = 0; index < ranges.Length(); index++) {
    1029             :     uint64_t start, end;
    1030           0 :     bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
    1031           0 :                                                         ranges[index].mEnd,
    1032           0 :                                                         &start, &end);
    1033           0 :     if (rv) {
    1034           0 :       NS_ASSERTION(startOffset <= start,
    1035             :           "startOffset negative or larger than start time");
    1036             : 
    1037           0 :       if (duration && end > duration) {
    1038           0 :         WEBM_DEBUG("limit range to duration, end: %f duration: %f",
    1039             :                    TimeUnit::FromNanoseconds(end).ToSeconds(),
    1040             :                    TimeUnit::FromNanoseconds(duration).ToSeconds());
    1041           0 :         end = duration;
    1042             :       }
    1043           0 :       auto startTime = TimeUnit::FromNanoseconds(start);
    1044           0 :       auto endTime = TimeUnit::FromNanoseconds(end);
    1045           0 :       WEBM_DEBUG("add range %f-%f", startTime.ToSeconds(), endTime.ToSeconds());
    1046           0 :       buffered += media::TimeInterval(startTime, endTime);
    1047             :     }
    1048             :   }
    1049           0 :   return buffered;
    1050             : }
    1051             : 
    1052           0 : bool WebMDemuxer::GetOffsetForTime(uint64_t aTime, int64_t* aOffset)
    1053             : {
    1054           0 :   EnsureUpToDateIndex();
    1055           0 :   return mBufferedState && mBufferedState->GetOffsetForTime(aTime, aOffset);
    1056             : }
    1057             : 
    1058             : 
    1059             : //WebMTrackDemuxer
    1060           0 : WebMTrackDemuxer::WebMTrackDemuxer(WebMDemuxer* aParent,
    1061             :                                    TrackInfo::TrackType aType,
    1062           0 :                                    uint32_t aTrackNumber)
    1063             :   : mParent(aParent)
    1064             :   , mType(aType)
    1065           0 :   , mNeedKeyframe(true)
    1066             : {
    1067           0 :   mInfo = mParent->GetTrackInfo(aType, aTrackNumber);
    1068           0 :   MOZ_ASSERT(mInfo);
    1069           0 : }
    1070             : 
    1071           0 : WebMTrackDemuxer::~WebMTrackDemuxer()
    1072             : {
    1073           0 :   mSamples.Reset();
    1074           0 : }
    1075             : 
    1076             : UniquePtr<TrackInfo>
    1077           0 : WebMTrackDemuxer::GetInfo() const
    1078             : {
    1079           0 :   return mInfo->Clone();
    1080             : }
    1081             : 
    1082             : RefPtr<WebMTrackDemuxer::SeekPromise>
    1083           0 : WebMTrackDemuxer::Seek(const TimeUnit& aTime)
    1084             : {
    1085             :   // Seeks to aTime. Upon success, SeekPromise will be resolved with the
    1086             :   // actual time seeked to. Typically the random access point time
    1087             : 
    1088           0 :   auto seekTime = aTime;
    1089           0 :   mSamples.Reset();
    1090           0 :   mParent->SeekInternal(mType, aTime);
    1091           0 :   nsresult rv = mParent->GetNextPacket(mType, &mSamples);
    1092           0 :   if (NS_FAILED(rv)) {
    1093           0 :     if (rv == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
    1094             :       // Ignore the error for now, the next GetSample will be rejected with EOS.
    1095           0 :       return SeekPromise::CreateAndResolve(TimeUnit::Zero(), __func__);
    1096             :     }
    1097           0 :     return SeekPromise::CreateAndReject(rv, __func__);
    1098             :   }
    1099           0 :   mNeedKeyframe = true;
    1100             : 
    1101             :   // Check what time we actually seeked to.
    1102           0 :   if (mSamples.GetSize() > 0) {
    1103           0 :     const RefPtr<MediaRawData>& sample = mSamples.First();
    1104           0 :     seekTime = sample->mTime;
    1105             :   }
    1106           0 :   SetNextKeyFrameTime();
    1107             : 
    1108           0 :   return SeekPromise::CreateAndResolve(seekTime, __func__);
    1109             : }
    1110             : 
    1111             : nsresult
    1112           0 : WebMTrackDemuxer::NextSample(RefPtr<MediaRawData>& aData)
    1113             : {
    1114           0 :   nsresult rv = NS_ERROR_DOM_MEDIA_END_OF_STREAM;;
    1115           0 :   while (mSamples.GetSize() < 1 &&
    1116           0 :          NS_SUCCEEDED((rv = mParent->GetNextPacket(mType, &mSamples)))) {
    1117             :   }
    1118           0 :   if (mSamples.GetSize()) {
    1119           0 :     aData = mSamples.PopFront();
    1120           0 :     return NS_OK;
    1121             :   }
    1122           0 :   return rv;
    1123             : }
    1124             : 
    1125             : RefPtr<WebMTrackDemuxer::SamplesPromise>
    1126           0 : WebMTrackDemuxer::GetSamples(int32_t aNumSamples)
    1127             : {
    1128           0 :   RefPtr<SamplesHolder> samples = new SamplesHolder;
    1129           0 :   MOZ_ASSERT(aNumSamples);
    1130             : 
    1131           0 :   nsresult rv = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
    1132             : 
    1133           0 :   while (aNumSamples) {
    1134           0 :     RefPtr<MediaRawData> sample;
    1135           0 :     rv = NextSample(sample);
    1136           0 :     if (NS_FAILED(rv)) {
    1137           0 :       break;
    1138             :     }
    1139           0 :     if (mNeedKeyframe && !sample->mKeyframe) {
    1140           0 :       continue;
    1141             :     }
    1142           0 :     mNeedKeyframe = false;
    1143           0 :     samples->mSamples.AppendElement(sample);
    1144           0 :     aNumSamples--;
    1145             :   }
    1146             : 
    1147           0 :   if (samples->mSamples.IsEmpty()) {
    1148           0 :     return SamplesPromise::CreateAndReject(rv, __func__);
    1149             :   } else {
    1150           0 :     UpdateSamples(samples->mSamples);
    1151           0 :     return SamplesPromise::CreateAndResolve(samples, __func__);
    1152             :   }
    1153             : }
    1154             : 
    1155             : void
    1156           0 : WebMTrackDemuxer::SetNextKeyFrameTime()
    1157             : {
    1158           0 :   if (mType != TrackInfo::kVideoTrack || mParent->IsMediaSource()) {
    1159           0 :     return;
    1160             :   }
    1161             : 
    1162           0 :   auto frameTime = TimeUnit::Invalid();
    1163             : 
    1164           0 :   mNextKeyframeTime.reset();
    1165             : 
    1166           0 :   MediaRawDataQueue skipSamplesQueue;
    1167           0 :   bool foundKeyframe = false;
    1168           0 :   while (!foundKeyframe && mSamples.GetSize()) {
    1169           0 :     RefPtr<MediaRawData> sample = mSamples.PopFront();
    1170           0 :     if (sample->mKeyframe) {
    1171           0 :       frameTime = sample->mTime;
    1172           0 :       foundKeyframe = true;
    1173             :     }
    1174           0 :     skipSamplesQueue.Push(sample.forget());
    1175             :   }
    1176           0 :   Maybe<int64_t> startTime;
    1177           0 :   if (skipSamplesQueue.GetSize()) {
    1178           0 :     const RefPtr<MediaRawData>& sample = skipSamplesQueue.First();
    1179           0 :     startTime.emplace(sample->mTimecode.ToMicroseconds());
    1180             :   }
    1181             :   // Demux and buffer frames until we find a keyframe.
    1182           0 :   RefPtr<MediaRawData> sample;
    1183           0 :   nsresult rv = NS_OK;
    1184           0 :   while (!foundKeyframe && NS_SUCCEEDED((rv = NextSample(sample)))) {
    1185           0 :     if (sample->mKeyframe) {
    1186           0 :       frameTime = sample->mTime;
    1187           0 :       foundKeyframe = true;
    1188             :     }
    1189           0 :     int64_t sampleTimecode = sample->mTimecode.ToMicroseconds();
    1190           0 :     skipSamplesQueue.Push(sample.forget());
    1191           0 :     if (!startTime) {
    1192           0 :       startTime.emplace(sampleTimecode);
    1193           0 :     } else if (!foundKeyframe
    1194           0 :                && sampleTimecode > startTime.ref() + MAX_LOOK_AHEAD) {
    1195           0 :       WEBM_DEBUG("Couldn't find keyframe in a reasonable time, aborting");
    1196           0 :       break;
    1197             :     }
    1198             :   }
    1199             :   // We may have demuxed more than intended, so ensure that all frames are kept
    1200             :   // in the right order.
    1201           0 :   mSamples.PushFront(Move(skipSamplesQueue));
    1202             : 
    1203           0 :   if (frameTime.IsValid()) {
    1204           0 :     mNextKeyframeTime.emplace(frameTime);
    1205           0 :     WEBM_DEBUG("Next Keyframe %f (%u queued %.02fs)",
    1206             :                mNextKeyframeTime.value().ToSeconds(),
    1207             :                uint32_t(mSamples.GetSize()),
    1208             :                (mSamples.Last()->mTimecode - mSamples.First()->mTimecode).ToSeconds());
    1209             :   } else {
    1210           0 :     WEBM_DEBUG("Couldn't determine next keyframe time  (%u queued)",
    1211             :                uint32_t(mSamples.GetSize()));
    1212             :   }
    1213             : }
    1214             : 
    1215             : void
    1216           0 : WebMTrackDemuxer::Reset()
    1217             : {
    1218           0 :   mSamples.Reset();
    1219           0 :   media::TimeIntervals buffered = GetBuffered();
    1220           0 :   mNeedKeyframe = true;
    1221           0 :   if (buffered.Length()) {
    1222           0 :     WEBM_DEBUG("Seek to start point: %f", buffered.Start(0).ToSeconds());
    1223           0 :     mParent->SeekInternal(mType, buffered.Start(0));
    1224           0 :     SetNextKeyFrameTime();
    1225             :   } else {
    1226           0 :     mNextKeyframeTime.reset();
    1227             :   }
    1228           0 : }
    1229             : 
    1230             : void
    1231           0 : WebMTrackDemuxer::UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples)
    1232             : {
    1233           0 :   for (const auto& sample : aSamples) {
    1234           0 :     if (sample->mCrypto.mValid) {
    1235           0 :       nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
    1236           0 :       writer->mCrypto.mMode = mInfo->mCrypto.mMode;
    1237           0 :       writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
    1238           0 :       writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
    1239             :     }
    1240             :   }
    1241           0 :   if (mNextKeyframeTime.isNothing()
    1242           0 :       || aSamples.LastElement()->mTime >= mNextKeyframeTime.value()) {
    1243           0 :     SetNextKeyFrameTime();
    1244             :   }
    1245           0 : }
    1246             : 
    1247             : nsresult
    1248           0 : WebMTrackDemuxer::GetNextRandomAccessPoint(TimeUnit* aTime)
    1249             : {
    1250           0 :   if (mNextKeyframeTime.isNothing()) {
    1251             :     // There's no next key frame.
    1252           0 :     *aTime = TimeUnit::FromInfinity();
    1253             :   } else {
    1254           0 :     *aTime = mNextKeyframeTime.ref();
    1255             :   }
    1256           0 :   return NS_OK;
    1257             : }
    1258             : 
    1259             : RefPtr<WebMTrackDemuxer::SkipAccessPointPromise>
    1260           0 : WebMTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold)
    1261             : {
    1262           0 :   uint32_t parsed = 0;
    1263           0 :   bool found = false;
    1264           0 :   RefPtr<MediaRawData> sample;
    1265           0 :   nsresult rv = NS_OK;
    1266             : 
    1267           0 :   WEBM_DEBUG("TimeThreshold: %f", aTimeThreshold.ToSeconds());
    1268           0 :   while (!found && NS_SUCCEEDED((rv = NextSample(sample)))) {
    1269           0 :     parsed++;
    1270           0 :     if (sample->mKeyframe && sample->mTime >= aTimeThreshold) {
    1271           0 :       WEBM_DEBUG("next sample: %f (parsed: %d)",
    1272             :                  sample->mTime.ToSeconds(), parsed);
    1273           0 :       found = true;
    1274           0 :       mSamples.Reset();
    1275           0 :       mSamples.PushFront(sample.forget());
    1276             :     }
    1277             :   }
    1278           0 :   if (NS_SUCCEEDED(rv)) {
    1279           0 :     SetNextKeyFrameTime();
    1280             :   }
    1281           0 :   if (found) {
    1282           0 :     return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
    1283             :   } else {
    1284           0 :     SkipFailureHolder failure(NS_ERROR_DOM_MEDIA_END_OF_STREAM, parsed);
    1285           0 :     return SkipAccessPointPromise::CreateAndReject(Move(failure), __func__);
    1286             :   }
    1287             : }
    1288             : 
    1289             : media::TimeIntervals
    1290           0 : WebMTrackDemuxer::GetBuffered()
    1291             : {
    1292           0 :   return mParent->GetBuffered();
    1293             : }
    1294             : 
    1295             : void
    1296           0 : WebMTrackDemuxer::BreakCycles()
    1297             : {
    1298           0 :   mParent = nullptr;
    1299           0 : }
    1300             : 
    1301             : int64_t
    1302           0 : WebMTrackDemuxer::GetEvictionOffset(const TimeUnit& aTime)
    1303             : {
    1304             :   int64_t offset;
    1305           0 :   if (!mParent->GetOffsetForTime(aTime.ToNanoseconds(), &offset)) {
    1306           0 :     return 0;
    1307             :   }
    1308             : 
    1309           0 :   return offset;
    1310             : }
    1311             : 
    1312             : #undef WEBM_DEBUG
    1313             : } // namespace mozilla

Generated by: LCOV version 1.13