LCOV - code coverage report
Current view: directory - content/media - nsBuiltinDecoder.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 501 0 0.0 %
Date: 2012-06-02 Functions: 61 0 0.0 %

       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                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *  Chris Double <chris.double@double.co.nz>
      24                 :  *  Chris Pearce <chris@pearce.org.nz>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include <limits>
      41                 : #include "nsNetUtil.h"
      42                 : #include "nsAudioStream.h"
      43                 : #include "nsHTMLVideoElement.h"
      44                 : #include "nsIObserver.h"
      45                 : #include "nsIObserverService.h"
      46                 : #include "nsTArray.h"
      47                 : #include "VideoUtils.h"
      48                 : #include "nsBuiltinDecoder.h"
      49                 : #include "nsBuiltinDecoderStateMachine.h"
      50                 : #include "nsTimeRanges.h"
      51                 : #include "nsContentUtils.h"
      52                 : 
      53                 : using namespace mozilla;
      54                 : 
      55                 : #ifdef PR_LOGGING
      56                 : PRLogModuleInfo* gBuiltinDecoderLog;
      57                 : #define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
      58                 : #else
      59                 : #define LOG(type, msg)
      60                 : #endif
      61                 : 
      62               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsBuiltinDecoder, nsIObserver)
      63                 : 
      64               0 : void nsBuiltinDecoder::Pause() 
      65                 : {
      66               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
      67               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
      68               0 :   if (mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_ENDED) {
      69               0 :     mNextState = PLAY_STATE_PAUSED;
      70                 :     return;
      71                 :   }
      72                 : 
      73               0 :   ChangeState(PLAY_STATE_PAUSED);
      74                 : }
      75                 : 
      76               0 : void nsBuiltinDecoder::SetVolume(double aVolume)
      77                 : {
      78               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
      79               0 :   mInitialVolume = aVolume;
      80               0 :   if (mDecoderStateMachine) {
      81               0 :     mDecoderStateMachine->SetVolume(aVolume);
      82                 :   }
      83               0 : }
      84                 : 
      85               0 : double nsBuiltinDecoder::GetDuration()
      86                 : {
      87               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
      88               0 :   if (mInfiniteStream) {
      89               0 :     return std::numeric_limits<double>::infinity();
      90                 :   }
      91               0 :   if (mDuration >= 0) {
      92               0 :      return static_cast<double>(mDuration) / static_cast<double>(USECS_PER_S);
      93                 :   }
      94               0 :   return std::numeric_limits<double>::quiet_NaN();
      95                 : }
      96                 : 
      97               0 : void nsBuiltinDecoder::SetInfinite(bool aInfinite)
      98                 : {
      99               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     100               0 :   mInfiniteStream = aInfinite;
     101               0 : }
     102                 : 
     103               0 : bool nsBuiltinDecoder::IsInfinite()
     104                 : {
     105               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     106               0 :   return mInfiniteStream;
     107                 : }
     108                 : 
     109               0 : nsBuiltinDecoder::nsBuiltinDecoder() :
     110                 :   mDecoderPosition(0),
     111                 :   mPlaybackPosition(0),
     112                 :   mCurrentTime(0.0),
     113                 :   mInitialVolume(0.0),
     114                 :   mRequestedSeekTime(-1.0),
     115                 :   mDuration(-1),
     116                 :   mSeekable(true),
     117                 :   mReentrantMonitor("media.decoder"),
     118                 :   mPlayState(PLAY_STATE_PAUSED),
     119                 :   mNextState(PLAY_STATE_PAUSED),
     120                 :   mResourceLoaded(false),
     121                 :   mIgnoreProgressData(false),
     122               0 :   mInfiniteStream(false)
     123                 : {
     124               0 :   MOZ_COUNT_CTOR(nsBuiltinDecoder);
     125               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     126                 : #ifdef PR_LOGGING
     127               0 :   if (!gBuiltinDecoderLog) {
     128               0 :     gBuiltinDecoderLog = PR_NewLogModule("nsBuiltinDecoder");
     129                 :   }
     130                 : #endif
     131               0 : }
     132                 : 
     133               0 : bool nsBuiltinDecoder::Init(nsHTMLMediaElement* aElement)
     134                 : {
     135               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     136               0 :   if (!nsMediaDecoder::Init(aElement))
     137               0 :     return false;
     138                 : 
     139               0 :   nsContentUtils::RegisterShutdownObserver(this);
     140               0 :   return true;
     141                 : }
     142                 : 
     143               0 : void nsBuiltinDecoder::Shutdown()
     144                 : {
     145               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     146                 :   
     147               0 :   if (mShuttingDown)
     148               0 :     return;
     149                 : 
     150               0 :   mShuttingDown = true;
     151                 : 
     152                 :   // This changes the decoder state to SHUTDOWN and does other things
     153                 :   // necessary to unblock the state machine thread if it's blocked, so
     154                 :   // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
     155               0 :   if (mDecoderStateMachine) {
     156               0 :     mDecoderStateMachine->Shutdown();
     157                 :   }
     158                 : 
     159                 :   // Force any outstanding seek and byterange requests to complete
     160                 :   // to prevent shutdown from deadlocking.
     161               0 :   if (mResource) {
     162               0 :     mResource->Close();
     163                 :   }
     164                 : 
     165               0 :   ChangeState(PLAY_STATE_SHUTDOWN);
     166               0 :   nsMediaDecoder::Shutdown();
     167                 : 
     168               0 :   nsContentUtils::UnregisterShutdownObserver(this);
     169                 : }
     170                 : 
     171               0 : nsBuiltinDecoder::~nsBuiltinDecoder()
     172                 : {
     173               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     174               0 :   UnpinForSeek();
     175               0 :   MOZ_COUNT_DTOR(nsBuiltinDecoder);
     176               0 : }
     177                 : 
     178               0 : nsresult nsBuiltinDecoder::Load(MediaResource* aResource,
     179                 :                                 nsIStreamListener** aStreamListener,
     180                 :                                 nsMediaDecoder* aCloneDonor)
     181                 : {
     182               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     183               0 :   if (aStreamListener) {
     184               0 :     *aStreamListener = nsnull;
     185                 :   }
     186                 : 
     187                 :   {
     188                 :     // Hold the lock while we do this to set proper lock ordering
     189                 :     // expectations for dynamic deadlock detectors: decoder lock(s)
     190                 :     // should be grabbed before the cache lock
     191               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     192                 : 
     193               0 :     nsresult rv = aResource->Open(aStreamListener);
     194               0 :     if (NS_FAILED(rv)) {
     195               0 :       LOG(PR_LOG_DEBUG, ("%p Failed to open stream!", this));
     196               0 :       delete aResource;
     197               0 :       return rv;
     198                 :     }
     199                 : 
     200               0 :     mResource = aResource;
     201                 :   }
     202                 : 
     203               0 :   mDecoderStateMachine = CreateStateMachine();
     204               0 :   if (!mDecoderStateMachine) {
     205               0 :     LOG(PR_LOG_DEBUG, ("%p Failed to create state machine!", this));
     206               0 :     return NS_ERROR_FAILURE;
     207                 :   }
     208                 : 
     209               0 :   nsBuiltinDecoder* cloneDonor = static_cast<nsBuiltinDecoder*>(aCloneDonor);
     210               0 :   if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
     211                 :                                            cloneDonor->mDecoderStateMachine : nsnull))) {
     212               0 :     LOG(PR_LOG_DEBUG, ("%p Failed to init state machine!", this));
     213               0 :     return NS_ERROR_FAILURE;
     214                 :   }
     215                 :   {
     216               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     217               0 :     mDecoderStateMachine->SetSeekable(mSeekable);
     218               0 :     mDecoderStateMachine->SetDuration(mDuration);
     219               0 :     mDecoderStateMachine->SetVolume(mInitialVolume);
     220                 :     
     221               0 :     if (mFrameBufferLength > 0) {
     222                 :       // The valid mFrameBufferLength value was specified earlier
     223               0 :       mDecoderStateMachine->SetFrameBufferLength(mFrameBufferLength);
     224                 :     }
     225                 :   }
     226                 : 
     227               0 :   ChangeState(PLAY_STATE_LOADING);
     228                 : 
     229               0 :   return ScheduleStateMachineThread();
     230                 : }
     231                 : 
     232               0 : nsresult nsBuiltinDecoder::RequestFrameBufferLength(PRUint32 aLength)
     233                 : {
     234               0 :   nsresult res = nsMediaDecoder::RequestFrameBufferLength(aLength);
     235               0 :   NS_ENSURE_SUCCESS(res,res);
     236                 : 
     237               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     238               0 :   if (mDecoderStateMachine) {
     239               0 :       mDecoderStateMachine->SetFrameBufferLength(aLength);
     240                 :   }
     241               0 :   return res;
     242                 : }
     243                 : 
     244               0 : nsresult nsBuiltinDecoder::ScheduleStateMachineThread()
     245                 : {
     246               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     247               0 :   NS_ASSERTION(mDecoderStateMachine,
     248                 :                "Must have state machine to start state machine thread");
     249               0 :   NS_ENSURE_STATE(mDecoderStateMachine);
     250                 : 
     251               0 :   if (mShuttingDown)
     252               0 :     return NS_OK;
     253               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     254                 :   nsBuiltinDecoderStateMachine* m =
     255               0 :     static_cast<nsBuiltinDecoderStateMachine*>(mDecoderStateMachine.get());
     256               0 :   return m->ScheduleStateMachine();
     257                 : }
     258                 : 
     259               0 : nsresult nsBuiltinDecoder::Play()
     260                 : {
     261               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     262               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     263               0 :   NS_ASSERTION(mDecoderStateMachine != nsnull, "Should have state machine.");
     264               0 :   nsresult res = ScheduleStateMachineThread();
     265               0 :   NS_ENSURE_SUCCESS(res,res);
     266               0 :   if (mPlayState == PLAY_STATE_SEEKING) {
     267               0 :     mNextState = PLAY_STATE_PLAYING;
     268               0 :     return NS_OK;
     269                 :   }
     270               0 :   if (mPlayState == PLAY_STATE_ENDED)
     271               0 :     return Seek(0);
     272                 : 
     273               0 :   ChangeState(PLAY_STATE_PLAYING);
     274               0 :   return NS_OK;
     275                 : }
     276                 : 
     277                 : /**
     278                 :  * Returns true if aValue is inside a range of aRanges, and put the range
     279                 :  * index in aIntervalIndex if it is not null.
     280                 :  * If aValue is not inside a range, false is returned, and aIntervalIndex, if
     281                 :  * not null, is set to the index of the range which ends immediately before aValue
     282                 :  * (and can be -1 if aValue is before aRanges.Start(0)).
     283                 :  */
     284               0 : static bool IsInRanges(nsTimeRanges& aRanges, double aValue, PRInt32& aIntervalIndex) {
     285                 :   PRUint32 length;
     286               0 :   aRanges.GetLength(&length);
     287               0 :   for (PRUint32 i = 0; i < length; i++) {
     288                 :     double start, end;
     289               0 :     aRanges.Start(i, &start);
     290               0 :     if (start > aValue) {
     291               0 :       aIntervalIndex = i - 1;
     292               0 :       return false;
     293                 :     }
     294               0 :     aRanges.End(i, &end);
     295               0 :     if (aValue <= end) {
     296               0 :       aIntervalIndex = i;
     297               0 :       return true;
     298                 :     }
     299                 :   }
     300               0 :   aIntervalIndex = length - 1;
     301               0 :   return false;
     302                 : }
     303                 : 
     304               0 : nsresult nsBuiltinDecoder::Seek(double aTime)
     305                 : {
     306               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     307               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     308                 : 
     309               0 :   NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value.");
     310                 : 
     311               0 :   nsTimeRanges seekable;
     312                 :   nsresult res;
     313               0 :   PRUint32 length = 0;
     314               0 :   res = GetSeekable(&seekable);
     315               0 :   NS_ENSURE_SUCCESS(res, NS_OK);
     316                 : 
     317               0 :   seekable.GetLength(&length);
     318               0 :   if (!length) {
     319               0 :     return NS_OK;
     320                 :   }
     321                 : 
     322                 :   // If the position we want to seek to is not in a seekable range, we seek
     323                 :   // to the closest position in the seekable ranges instead. If two positions
     324                 :   // are equally close, we seek to the closest position from the currentTime.
     325                 :   // See seeking spec, point 7 :
     326                 :   // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#seeking
     327               0 :   PRInt32 range = 0;
     328               0 :   if (!IsInRanges(seekable, aTime, range)) {
     329               0 :     if (range != -1) {
     330                 :       // |range + 1| can't be negative, because the only possible negative value
     331                 :       // for |range| is -1.
     332               0 :       if (PRUint32(range + 1) < length) {
     333                 :         double leftBound, rightBound;
     334               0 :         res = seekable.End(range, &leftBound);
     335               0 :         NS_ENSURE_SUCCESS(res, NS_OK);
     336               0 :         res = seekable.Start(range + 1, &rightBound);
     337               0 :         NS_ENSURE_SUCCESS(res, NS_OK);
     338               0 :         double distanceLeft = NS_ABS(leftBound - aTime);
     339               0 :         double distanceRight = NS_ABS(rightBound - aTime);
     340               0 :         if (distanceLeft == distanceRight) {
     341               0 :           distanceLeft = NS_ABS(leftBound - mCurrentTime);
     342               0 :           distanceRight = NS_ABS(rightBound - mCurrentTime);
     343                 :         } 
     344               0 :         aTime = (distanceLeft < distanceRight) ? leftBound : rightBound;
     345                 :       } else {
     346                 :         // Seek target is after the end last range in seekable data.
     347                 :         // Clamp the seek target to the end of the last seekable range.
     348               0 :         res = seekable.End(length - 1, &aTime);
     349               0 :         NS_ENSURE_SUCCESS(res, NS_OK);
     350                 :       }
     351                 :     } else {
     352                 :       // aTime is before the first range in |seekable|, the closest point we can
     353                 :       // seek to is the start of the first range.
     354               0 :       seekable.Start(0, &aTime);
     355                 :     }
     356                 :   }
     357                 : 
     358               0 :   mRequestedSeekTime = aTime;
     359               0 :   mCurrentTime = aTime;
     360                 : 
     361                 :   // If we are already in the seeking state, then setting mRequestedSeekTime
     362                 :   // above will result in the new seek occurring when the current seek
     363                 :   // completes.
     364               0 :   if (mPlayState != PLAY_STATE_SEEKING) {
     365               0 :     bool paused = false;
     366               0 :     if (mElement) {
     367               0 :       mElement->GetPaused(&paused);
     368                 :     }
     369               0 :     mNextState = paused ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING;
     370               0 :     PinForSeek();
     371               0 :     ChangeState(PLAY_STATE_SEEKING);
     372                 :   }
     373                 : 
     374               0 :   return ScheduleStateMachineThread();
     375                 : }
     376                 : 
     377               0 : nsresult nsBuiltinDecoder::PlaybackRateChanged()
     378                 : {
     379               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     380               0 :   return NS_ERROR_NOT_IMPLEMENTED;
     381                 : }
     382                 : 
     383               0 : double nsBuiltinDecoder::GetCurrentTime()
     384                 : {
     385               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     386               0 :   return mCurrentTime;
     387                 : }
     388                 : 
     389               0 : already_AddRefed<nsIPrincipal> nsBuiltinDecoder::GetCurrentPrincipal()
     390                 : {
     391               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     392               0 :   return mResource ? mResource->GetCurrentPrincipal() : nsnull;
     393                 : }
     394                 : 
     395               0 : void nsBuiltinDecoder::AudioAvailable(float* aFrameBuffer,
     396                 :                                       PRUint32 aFrameBufferLength,
     397                 :                                       float aTime)
     398                 : {
     399                 :   // Auto manage the frame buffer's memory. If we return due to an error
     400                 :   // here, this ensures we free the memory. Otherwise, we pass off ownership
     401                 :   // to HTMLMediaElement::NotifyAudioAvailable().
     402               0 :   nsAutoArrayPtr<float> frameBuffer(aFrameBuffer);
     403               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     404               0 :   if (mShuttingDown || !mElement) {
     405                 :     return;
     406                 :   }
     407               0 :   mElement->NotifyAudioAvailable(frameBuffer.forget(), aFrameBufferLength, aTime);
     408                 : }
     409                 : 
     410               0 : void nsBuiltinDecoder::MetadataLoaded(PRUint32 aChannels,
     411                 :                                       PRUint32 aRate)
     412                 : {
     413               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     414               0 :   if (mShuttingDown) {
     415               0 :     return;
     416                 :   }
     417                 : 
     418                 :   // Only inform the element of MetadataLoaded if not doing a load() in order
     419                 :   // to fulfill a seek, otherwise we'll get multiple metadataloaded events.
     420               0 :   bool notifyElement = true;
     421                 :   {
     422               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     423               0 :     mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
     424                 :     // Duration has changed so we should recompute playback rate
     425               0 :     UpdatePlaybackRate();
     426                 : 
     427               0 :     notifyElement = mNextState != PLAY_STATE_SEEKING;
     428                 :   }
     429                 : 
     430               0 :   if (mDuration == -1) {
     431               0 :     SetInfinite(true);
     432                 :   }
     433                 : 
     434               0 :   if (mElement && notifyElement) {
     435                 :     // Make sure the element and the frame (if any) are told about
     436                 :     // our new size.
     437               0 :     Invalidate();
     438               0 :     mElement->MetadataLoaded(aChannels, aRate);
     439                 :   }
     440                 : 
     441               0 :   if (!mResourceLoaded) {
     442               0 :     StartProgress();
     443               0 :   } else if (mElement) {
     444                 :     // Resource was loaded during metadata loading, when progress
     445                 :     // events are being ignored. Fire the final progress event.
     446               0 :     mElement->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
     447                 :   }
     448                 : 
     449                 :   // Only inform the element of FirstFrameLoaded if not doing a load() in order
     450                 :   // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events.
     451               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     452               0 :   bool resourceIsLoaded = !mResourceLoaded && mResource &&
     453               0 :     mResource->IsDataCachedToEndOfResource(mDecoderPosition);
     454               0 :   if (mElement && notifyElement) {
     455               0 :     mElement->FirstFrameLoaded(resourceIsLoaded);
     456                 :   }
     457                 : 
     458                 :   // This can run cache callbacks.
     459               0 :   mResource->EnsureCacheUpToDate();
     460                 : 
     461                 :   // The element can run javascript via events
     462                 :   // before reaching here, so only change the
     463                 :   // state if we're still set to the original
     464                 :   // loading state.
     465               0 :   if (mPlayState == PLAY_STATE_LOADING) {
     466               0 :     if (mRequestedSeekTime >= 0.0) {
     467               0 :       ChangeState(PLAY_STATE_SEEKING);
     468                 :     }
     469                 :     else {
     470               0 :       ChangeState(mNextState);
     471                 :     }
     472                 :   }
     473                 : 
     474               0 :   if (resourceIsLoaded) {
     475               0 :     ResourceLoaded();
     476                 :   }
     477                 : 
     478                 :   // Run NotifySuspendedStatusChanged now to give us a chance to notice
     479                 :   // that autoplay should run.
     480               0 :   NotifySuspendedStatusChanged();
     481                 : }
     482                 : 
     483               0 : void nsBuiltinDecoder::ResourceLoaded()
     484                 : {
     485               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     486                 : 
     487                 :   // Don't handle ResourceLoaded if we are shutting down, or if
     488                 :   // we need to ignore progress data due to seeking (in the case
     489                 :   // that the seek results in reaching end of file, we get a bogus call
     490                 :   // to ResourceLoaded).
     491               0 :   if (mShuttingDown)
     492               0 :     return;
     493                 : 
     494                 :   {
     495                 :     // If we are seeking or loading then the resource loaded notification we get
     496                 :     // should be ignored, since it represents the end of the seek request.
     497               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     498               0 :     if (mIgnoreProgressData || mResourceLoaded || mPlayState == PLAY_STATE_LOADING)
     499                 :       return;
     500                 : 
     501               0 :     Progress(false);
     502                 : 
     503               0 :     mResourceLoaded = true;
     504               0 :     StopProgress();
     505                 :   }
     506                 : 
     507                 :   // Ensure the final progress event gets fired
     508               0 :   if (mElement) {
     509               0 :     mElement->ResourceLoaded();
     510                 :   }
     511                 : }
     512                 : 
     513               0 : void nsBuiltinDecoder::NetworkError()
     514                 : {
     515               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     516               0 :   if (mShuttingDown)
     517               0 :     return;
     518                 : 
     519               0 :   if (mElement)
     520               0 :     mElement->NetworkError();
     521                 : 
     522               0 :   Shutdown();
     523                 : }
     524                 : 
     525               0 : void nsBuiltinDecoder::DecodeError()
     526                 : {
     527               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     528               0 :   if (mShuttingDown)
     529               0 :     return;
     530                 : 
     531               0 :   if (mElement)
     532               0 :     mElement->DecodeError();
     533                 : 
     534               0 :   Shutdown();
     535                 : }
     536                 : 
     537               0 : bool nsBuiltinDecoder::IsSeeking() const
     538                 : {
     539               0 :   return mPlayState == PLAY_STATE_SEEKING || mNextState == PLAY_STATE_SEEKING;
     540                 : }
     541                 : 
     542               0 : bool nsBuiltinDecoder::IsEnded() const
     543                 : {
     544               0 :   return mPlayState == PLAY_STATE_ENDED || mPlayState == PLAY_STATE_SHUTDOWN;
     545                 : }
     546                 : 
     547               0 : void nsBuiltinDecoder::PlaybackEnded()
     548                 : {
     549               0 :   if (mShuttingDown || mPlayState == nsBuiltinDecoder::PLAY_STATE_SEEKING)
     550               0 :     return;
     551                 : 
     552               0 :   PlaybackPositionChanged();
     553               0 :   ChangeState(PLAY_STATE_ENDED);
     554                 : 
     555               0 :   if (mElement)  {
     556               0 :     UpdateReadyStateForData();
     557               0 :     mElement->PlaybackEnded();
     558                 :   }
     559                 : 
     560                 :   // This must be called after |mElement->PlaybackEnded()| call above, in order
     561                 :   // to fire the required durationchange.
     562               0 :   if (IsInfinite()) {
     563               0 :     SetInfinite(false);
     564                 :   }
     565                 : }
     566                 : 
     567               0 : NS_IMETHODIMP nsBuiltinDecoder::Observe(nsISupports *aSubjet,
     568                 :                                         const char *aTopic,
     569                 :                                         const PRUnichar *someData)
     570                 : {
     571               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     572               0 :   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
     573               0 :     Shutdown();
     574                 :   }
     575                 : 
     576               0 :   return NS_OK;
     577                 : }
     578                 : 
     579                 : nsMediaDecoder::Statistics
     580               0 : nsBuiltinDecoder::GetStatistics()
     581                 : {
     582               0 :   NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
     583                 :                "Should be on main or state machine thread.");
     584                 :   Statistics result;
     585                 : 
     586               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     587               0 :   if (mResource) {
     588                 :     result.mDownloadRate = 
     589               0 :       mResource->GetDownloadRate(&result.mDownloadRateReliable);
     590                 :     result.mDownloadPosition =
     591               0 :       mResource->GetCachedDataEnd(mDecoderPosition);
     592               0 :     result.mTotalBytes = mResource->GetLength();
     593               0 :     result.mPlaybackRate = ComputePlaybackRate(&result.mPlaybackRateReliable);
     594               0 :     result.mDecoderPosition = mDecoderPosition;
     595               0 :     result.mPlaybackPosition = mPlaybackPosition;
     596                 :   }
     597                 :   else {
     598               0 :     result.mDownloadRate = 0;
     599               0 :     result.mDownloadRateReliable = true;
     600               0 :     result.mPlaybackRate = 0;
     601               0 :     result.mPlaybackRateReliable = true;
     602               0 :     result.mDecoderPosition = 0;
     603               0 :     result.mPlaybackPosition = 0;
     604               0 :     result.mDownloadPosition = 0;
     605               0 :     result.mTotalBytes = 0;
     606                 :   }
     607                 : 
     608                 :   return result;
     609                 : }
     610                 : 
     611               0 : double nsBuiltinDecoder::ComputePlaybackRate(bool* aReliable)
     612                 : {
     613               0 :   GetReentrantMonitor().AssertCurrentThreadIn();
     614               0 :   NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
     615                 :                "Should be on main or state machine thread.");
     616                 : 
     617               0 :   PRInt64 length = mResource ? mResource->GetLength() : -1;
     618               0 :   if (mDuration >= 0 && length >= 0) {
     619               0 :     *aReliable = true;
     620               0 :     return length * static_cast<double>(USECS_PER_S) / mDuration;
     621                 :   }
     622               0 :   return mPlaybackStatistics.GetRateAtLastStop(aReliable);
     623                 : }
     624                 : 
     625               0 : void nsBuiltinDecoder::UpdatePlaybackRate()
     626                 : {
     627               0 :   NS_ASSERTION(NS_IsMainThread() || OnStateMachineThread(),
     628                 :                "Should be on main or state machine thread.");
     629               0 :   GetReentrantMonitor().AssertCurrentThreadIn();
     630               0 :   if (!mResource)
     631               0 :     return;
     632                 :   bool reliable;
     633               0 :   PRUint32 rate = PRUint32(ComputePlaybackRate(&reliable));
     634               0 :   if (reliable) {
     635                 :     // Avoid passing a zero rate
     636               0 :     rate = NS_MAX(rate, 1u);
     637                 :   }
     638                 :   else {
     639                 :     // Set a minimum rate of 10,000 bytes per second ... sometimes we just
     640                 :     // don't have good data
     641               0 :     rate = NS_MAX(rate, 10000u);
     642                 :   }
     643               0 :   mResource->SetPlaybackRate(rate);
     644                 : }
     645                 : 
     646               0 : void nsBuiltinDecoder::NotifySuspendedStatusChanged()
     647                 : {
     648               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     649               0 :   if (!mResource)
     650               0 :     return;
     651                 :   MediaResource* activeStream;
     652               0 :   bool suspended = mResource->IsSuspendedByCache(&activeStream);
     653                 :   
     654               0 :   if (suspended && mElement) {
     655                 :     // if this is an autoplay element, we need to kick off its autoplaying
     656                 :     // now so we consume data and hopefully free up cache space
     657               0 :     mElement->NotifyAutoplayDataReady();
     658                 :   }
     659                 : }
     660                 : 
     661               0 : void nsBuiltinDecoder::NotifyBytesDownloaded()
     662                 : {
     663               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     664               0 :   UpdateReadyStateForData();
     665               0 :   Progress(false);
     666               0 : }
     667                 : 
     668               0 : void nsBuiltinDecoder::NotifyDownloadEnded(nsresult aStatus)
     669                 : {
     670               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     671                 : 
     672               0 :   if (aStatus == NS_BINDING_ABORTED) {
     673                 :     // Download has been cancelled by user.
     674               0 :     if (mElement) {
     675               0 :       mElement->LoadAborted();
     676                 :     }
     677               0 :     return;
     678                 :   }
     679                 : 
     680                 :   {
     681               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     682               0 :     UpdatePlaybackRate();
     683                 :   }
     684                 : 
     685               0 :   if (NS_SUCCEEDED(aStatus)) {
     686               0 :     ResourceLoaded();
     687                 :   }
     688               0 :   else if (aStatus != NS_BASE_STREAM_CLOSED) {
     689               0 :     NetworkError();
     690                 :   }
     691               0 :   UpdateReadyStateForData();
     692                 : }
     693                 : 
     694               0 : void nsBuiltinDecoder::NotifyBytesConsumed(PRInt64 aBytes)
     695                 : {
     696               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     697               0 :   NS_ASSERTION(OnStateMachineThread() || mDecoderStateMachine->OnDecodeThread(),
     698                 :                "Should be on play state machine or decode thread.");
     699               0 :   if (!mIgnoreProgressData) {
     700               0 :     mDecoderPosition += aBytes;
     701               0 :     mPlaybackStatistics.AddBytes(aBytes);
     702                 :   }
     703               0 : }
     704                 : 
     705               0 : void nsBuiltinDecoder::NextFrameUnavailableBuffering()
     706                 : {
     707               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
     708               0 :   if (!mElement || mShuttingDown || !mDecoderStateMachine)
     709               0 :     return;
     710                 : 
     711               0 :   mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING);
     712                 : }
     713                 : 
     714               0 : void nsBuiltinDecoder::NextFrameAvailable()
     715                 : {
     716               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
     717               0 :   if (!mElement || mShuttingDown || !mDecoderStateMachine)
     718               0 :     return;
     719                 : 
     720               0 :   mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_AVAILABLE);
     721                 : }
     722                 : 
     723               0 : void nsBuiltinDecoder::NextFrameUnavailable()
     724                 : {
     725               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
     726               0 :   if (!mElement || mShuttingDown || !mDecoderStateMachine)
     727               0 :     return;
     728               0 :   mElement->UpdateReadyStateForData(nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE);
     729                 : }
     730                 : 
     731               0 : void nsBuiltinDecoder::UpdateReadyStateForData()
     732                 : {
     733               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be called on main thread");
     734               0 :   if (!mElement || mShuttingDown || !mDecoderStateMachine)
     735               0 :     return;
     736                 :   nsHTMLMediaElement::NextFrameStatus frameStatus =
     737               0 :     mDecoderStateMachine->GetNextFrameStatus();
     738               0 :   mElement->UpdateReadyStateForData(frameStatus);
     739                 : }
     740                 : 
     741               0 : void nsBuiltinDecoder::SeekingStopped()
     742                 : {
     743               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     744                 : 
     745               0 :   if (mShuttingDown)
     746               0 :     return;
     747                 : 
     748               0 :   bool seekWasAborted = false;
     749                 :   {
     750               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     751                 : 
     752                 :     // An additional seek was requested while the current seek was
     753                 :     // in operation.
     754               0 :     if (mRequestedSeekTime >= 0.0) {
     755               0 :       ChangeState(PLAY_STATE_SEEKING);
     756               0 :       seekWasAborted = true;
     757                 :     } else {
     758               0 :       UnpinForSeek();
     759               0 :       ChangeState(mNextState);
     760                 :     }
     761                 :   }
     762                 : 
     763               0 :   if (mElement) {
     764               0 :     UpdateReadyStateForData();
     765               0 :     if (!seekWasAborted) {
     766               0 :       mElement->SeekCompleted();
     767                 :     }
     768                 :   }
     769                 : }
     770                 : 
     771                 : // This is called when seeking stopped *and* we're at the end of the
     772                 : // media.
     773               0 : void nsBuiltinDecoder::SeekingStoppedAtEnd()
     774                 : {
     775               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     776                 : 
     777               0 :   if (mShuttingDown)
     778               0 :     return;
     779                 : 
     780               0 :   bool fireEnded = false;
     781               0 :   bool seekWasAborted = false;
     782                 :   {
     783               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     784                 : 
     785                 :     // An additional seek was requested while the current seek was
     786                 :     // in operation.
     787               0 :     if (mRequestedSeekTime >= 0.0) {
     788               0 :       ChangeState(PLAY_STATE_SEEKING);
     789               0 :       seekWasAborted = true;
     790                 :     } else {
     791               0 :       UnpinForSeek();
     792               0 :       fireEnded = true;
     793               0 :       ChangeState(PLAY_STATE_ENDED);
     794                 :     }
     795                 :   }
     796                 : 
     797               0 :   if (mElement) {
     798               0 :     UpdateReadyStateForData();
     799               0 :     if (!seekWasAborted) {
     800               0 :       mElement->SeekCompleted();
     801               0 :       if (fireEnded) {
     802               0 :         mElement->PlaybackEnded();
     803                 :       }
     804                 :     }
     805                 :   }
     806                 : }
     807                 : 
     808               0 : void nsBuiltinDecoder::SeekingStarted()
     809                 : {
     810               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     811               0 :   if (mShuttingDown)
     812               0 :     return;
     813                 : 
     814               0 :   if (mElement) {
     815               0 :     UpdateReadyStateForData();
     816               0 :     mElement->SeekStarted();
     817                 :   }
     818                 : }
     819                 : 
     820               0 : void nsBuiltinDecoder::ChangeState(PlayState aState)
     821                 : {
     822               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");   
     823               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     824                 : 
     825               0 :   if (mNextState == aState) {
     826               0 :     mNextState = PLAY_STATE_PAUSED;
     827                 :   }
     828                 : 
     829               0 :   if (mPlayState == PLAY_STATE_SHUTDOWN) {
     830               0 :     mReentrantMonitor.NotifyAll();
     831                 :     return;
     832                 :   }
     833                 : 
     834               0 :   mPlayState = aState;
     835               0 :   if (mDecoderStateMachine) {
     836               0 :     switch (aState) {
     837                 :     case PLAY_STATE_PLAYING:
     838               0 :       mDecoderStateMachine->Play();
     839               0 :       break;
     840                 :     case PLAY_STATE_SEEKING:
     841               0 :       mDecoderStateMachine->Seek(mRequestedSeekTime);
     842               0 :       mRequestedSeekTime = -1.0;
     843               0 :       break;
     844                 :     default:
     845                 :       /* No action needed */
     846               0 :       break;
     847                 :     }
     848                 :   }
     849               0 :   mReentrantMonitor.NotifyAll();
     850                 : }
     851                 : 
     852               0 : void nsBuiltinDecoder::PlaybackPositionChanged()
     853                 : {
     854               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     855               0 :   if (mShuttingDown)
     856               0 :     return;
     857                 : 
     858               0 :   double lastTime = mCurrentTime;
     859                 : 
     860                 :   // Control the scope of the monitor so it is not
     861                 :   // held while the timeupdate and the invalidate is run.
     862                 :   {
     863               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     864               0 :     if (mDecoderStateMachine) {
     865               0 :       if (!IsSeeking()) {
     866                 :         // Only update the current playback position if we're not seeking.
     867                 :         // If we are seeking, the update could have been scheduled on the
     868                 :         // state machine thread while we were playing but after the seek
     869                 :         // algorithm set the current playback position on the main thread,
     870                 :         // and we don't want to override the seek algorithm and change the
     871                 :         // current time after the seek has started but before it has
     872                 :         // completed.
     873               0 :         mCurrentTime = mDecoderStateMachine->GetCurrentTime();
     874                 :       }
     875               0 :       mDecoderStateMachine->ClearPositionChangeFlag();
     876                 :     }
     877                 :   }
     878                 : 
     879                 :   // Invalidate the frame so any video data is displayed.
     880                 :   // Do this before the timeupdate event so that if that
     881                 :   // event runs JavaScript that queries the media size, the
     882                 :   // frame has reflowed and the size updated beforehand.
     883               0 :   Invalidate();
     884                 : 
     885               0 :   if (mElement && lastTime != mCurrentTime) {
     886               0 :     FireTimeUpdate();
     887                 :   }
     888                 : }
     889                 : 
     890               0 : void nsBuiltinDecoder::DurationChanged()
     891                 : {
     892               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     893               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     894               0 :   PRInt64 oldDuration = mDuration;
     895               0 :   mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
     896                 :   // Duration has changed so we should recompute playback rate
     897               0 :   UpdatePlaybackRate();
     898                 : 
     899               0 :   if (mElement && oldDuration != mDuration && !IsInfinite()) {
     900               0 :     LOG(PR_LOG_DEBUG, ("%p duration changed to %lld", this, mDuration));
     901               0 :     mElement->DispatchEvent(NS_LITERAL_STRING("durationchange"));
     902                 :   }
     903               0 : }
     904                 : 
     905               0 : void nsBuiltinDecoder::SetDuration(double aDuration)
     906                 : {
     907               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     908               0 :   mDuration = static_cast<PRInt64>(NS_round(aDuration * static_cast<double>(USECS_PER_S)));
     909                 : 
     910               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     911               0 :   if (mDecoderStateMachine) {
     912               0 :     mDecoderStateMachine->SetDuration(mDuration);
     913                 :   }
     914                 : 
     915                 :   // Duration has changed so we should recompute playback rate
     916               0 :   UpdatePlaybackRate();
     917               0 : }
     918                 : 
     919               0 : void nsBuiltinDecoder::SetSeekable(bool aSeekable)
     920                 : {
     921               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     922               0 :   mSeekable = aSeekable;
     923               0 :   if (mDecoderStateMachine) {
     924               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     925               0 :     mDecoderStateMachine->SetSeekable(aSeekable);
     926                 :   }
     927               0 : }
     928                 : 
     929               0 : bool nsBuiltinDecoder::IsSeekable()
     930                 : {
     931               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     932               0 :   return mSeekable;
     933                 : }
     934                 : 
     935               0 : nsresult nsBuiltinDecoder::GetSeekable(nsTimeRanges* aSeekable)
     936                 : {
     937                 :   //TODO : change 0.0 to GetInitialTime() when available
     938               0 :   double initialTime = 0.0;
     939                 : 
     940               0 :   if (IsSeekable()) {
     941               0 :     double end = IsInfinite() ? std::numeric_limits<double>::infinity()
     942               0 :                               : initialTime + GetDuration();
     943               0 :     aSeekable->Add(initialTime, end);
     944               0 :     return NS_OK;
     945                 :   }
     946                 : 
     947               0 :   return GetBuffered(aSeekable);
     948                 : }
     949                 : 
     950               0 : void nsBuiltinDecoder::SetEndTime(double aTime)
     951                 : {
     952               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     953               0 :   if (mDecoderStateMachine) {
     954               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     955               0 :     mDecoderStateMachine->SetFragmentEndTime(static_cast<PRInt64>(aTime * USECS_PER_S));
     956                 :   }
     957               0 : }
     958                 : 
     959               0 : void nsBuiltinDecoder::Suspend()
     960                 : {
     961               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     962               0 :   if (mResource) {
     963               0 :     mResource->Suspend(true);
     964                 :   }
     965               0 : }
     966                 : 
     967               0 : void nsBuiltinDecoder::Resume(bool aForceBuffering)
     968                 : {
     969               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     970               0 :   if (mResource) {
     971               0 :     mResource->Resume();
     972                 :   }
     973               0 :   if (aForceBuffering) {
     974               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     975               0 :     if (mDecoderStateMachine) {
     976               0 :       mDecoderStateMachine->StartBuffering();
     977                 :     }
     978                 :   }
     979               0 : }
     980                 : 
     981               0 : void nsBuiltinDecoder::StopProgressUpdates()
     982                 : {
     983               0 :   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
     984                 :                "Should be on state machine or decode thread.");
     985               0 :   GetReentrantMonitor().AssertCurrentThreadIn();
     986               0 :   mIgnoreProgressData = true;
     987               0 :   if (mResource) {
     988               0 :     mResource->SetReadMode(nsMediaCacheStream::MODE_METADATA);
     989                 :   }
     990               0 : }
     991                 : 
     992               0 : void nsBuiltinDecoder::StartProgressUpdates()
     993                 : {
     994               0 :   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
     995                 :                "Should be on state machine or decode thread.");
     996               0 :   GetReentrantMonitor().AssertCurrentThreadIn();
     997               0 :   mIgnoreProgressData = false;
     998               0 :   if (mResource) {
     999               0 :     mResource->SetReadMode(nsMediaCacheStream::MODE_PLAYBACK);
    1000               0 :     mDecoderPosition = mPlaybackPosition = mResource->Tell();
    1001                 :   }
    1002               0 : }
    1003                 : 
    1004               0 : void nsBuiltinDecoder::MoveLoadsToBackground()
    1005                 : {
    1006               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    1007               0 :   if (mResource) {
    1008               0 :     mResource->MoveLoadsToBackground();
    1009                 :   }
    1010               0 : }
    1011                 : 
    1012               0 : void nsBuiltinDecoder::UpdatePlaybackOffset(PRInt64 aOffset)
    1013                 : {
    1014               0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    1015               0 :   mPlaybackPosition = NS_MAX(aOffset, mPlaybackPosition);
    1016               0 : }
    1017                 : 
    1018               0 : bool nsBuiltinDecoder::OnStateMachineThread() const
    1019                 : {
    1020               0 :   return IsCurrentThread(nsBuiltinDecoderStateMachine::GetStateMachineThread());
    1021                 : }
    1022                 : 
    1023               0 : void nsBuiltinDecoder::NotifyAudioAvailableListener()
    1024                 : {
    1025               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    1026               0 :   if (mDecoderStateMachine) {
    1027               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    1028               0 :     mDecoderStateMachine->NotifyAudioAvailableListener();
    1029                 :   }
    1030               0 : }

Generated by: LCOV version 1.7