content/media/MediaDecoderReader.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
     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/. */
     7 #include "MediaDecoderReader.h"
     8 #include "AbstractMediaDecoder.h"
     9 #include "VideoUtils.h"
    10 #include "ImageContainer.h"
    12 #include "mozilla/mozalloc.h"
    13 #include <stdint.h>
    14 #include <algorithm>
    16 namespace mozilla {
    18 // Un-comment to enable logging of seek bisections.
    19 //#define SEEK_LOGGING
    21 #ifdef PR_LOGGING
    22 extern PRLogModuleInfo* gMediaDecoderLog;
    23 #define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
    24 #ifdef SEEK_LOGGING
    25 #define SEEK_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
    26 #else
    27 #define SEEK_LOG(type, msg)
    28 #endif
    29 #else
    30 #define DECODER_LOG(type, msg)
    31 #define SEEK_LOG(type, msg)
    32 #endif
    34 class VideoQueueMemoryFunctor : public nsDequeFunctor {
    35 public:
    36   VideoQueueMemoryFunctor() : mSize(0) {}
    38   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
    40   virtual void* operator()(void* aObject) {
    41     const VideoData* v = static_cast<const VideoData*>(aObject);
    42     mSize += v->SizeOfIncludingThis(MallocSizeOf);
    43     return nullptr;
    44   }
    46   size_t mSize;
    47 };
    50 class AudioQueueMemoryFunctor : public nsDequeFunctor {
    51 public:
    52   AudioQueueMemoryFunctor() : mSize(0) {}
    54   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
    56   virtual void* operator()(void* aObject) {
    57     const AudioData* audioData = static_cast<const AudioData*>(aObject);
    58     mSize += audioData->SizeOfIncludingThis(MallocSizeOf);
    59     return nullptr;
    60   }
    62   size_t mSize;
    63 };
    65 MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
    66   : mAudioCompactor(mAudioQueue),
    67     mDecoder(aDecoder),
    68     mIgnoreAudioOutputFormat(false)
    69 {
    70   MOZ_COUNT_CTOR(MediaDecoderReader);
    71 }
    73 MediaDecoderReader::~MediaDecoderReader()
    74 {
    75   ResetDecode();
    76   MOZ_COUNT_DTOR(MediaDecoderReader);
    77 }
    79 size_t MediaDecoderReader::SizeOfVideoQueueInBytes() const
    80 {
    81   VideoQueueMemoryFunctor functor;
    82   mVideoQueue.LockedForEach(functor);
    83   return functor.mSize;
    84 }
    86 size_t MediaDecoderReader::SizeOfAudioQueueInBytes() const
    87 {
    88   AudioQueueMemoryFunctor functor;
    89   mAudioQueue.LockedForEach(functor);
    90   return functor.mSize;
    91 }
    93 nsresult MediaDecoderReader::ResetDecode()
    94 {
    95   nsresult res = NS_OK;
    97   VideoQueue().Reset();
    98   AudioQueue().Reset();
   100   return res;
   101 }
   103 VideoData* MediaDecoderReader::DecodeToFirstVideoData()
   104 {
   105   bool eof = false;
   106   while (!eof && VideoQueue().GetSize() == 0) {
   107     {
   108       ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   109       if (mDecoder->IsShutdown()) {
   110         return nullptr;
   111       }
   112     }
   113     bool keyframeSkip = false;
   114     eof = !DecodeVideoFrame(keyframeSkip, 0);
   115   }
   116   if (eof) {
   117     VideoQueue().Finish();
   118   }
   119   VideoData* d = nullptr;
   120   return (d = VideoQueue().PeekFront()) ? d : nullptr;
   121 }
   123 AudioData* MediaDecoderReader::DecodeToFirstAudioData()
   124 {
   125   bool eof = false;
   126   while (!eof && AudioQueue().GetSize() == 0) {
   127     {
   128       ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   129       if (mDecoder->IsShutdown()) {
   130         return nullptr;
   131       }
   132     }
   133     eof = !DecodeAudioData();
   134   }
   135   if (eof) {
   136     AudioQueue().Finish();
   137   }
   138   AudioData* d = nullptr;
   139   return (d = AudioQueue().PeekFront()) ? d : nullptr;
   140 }
   142 VideoData* MediaDecoderReader::FindStartTime(int64_t& aOutStartTime)
   143 {
   144   NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
   145                "Should be on state machine or decode thread.");
   147   // Extract the start times of the bitstreams in order to calculate
   148   // the duration.
   149   int64_t videoStartTime = INT64_MAX;
   150   int64_t audioStartTime = INT64_MAX;
   151   VideoData* videoData = nullptr;
   153   if (HasVideo()) {
   154     videoData = DecodeToFirstVideoData();
   155     if (videoData) {
   156       videoStartTime = videoData->mTime;
   157       DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::FindStartTime() video=%lld", videoStartTime));
   158     }
   159   }
   160   if (HasAudio()) {
   161     AudioData* audioData = DecodeToFirstAudioData();
   162     if (audioData) {
   163       audioStartTime = audioData->mTime;
   164       DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::FindStartTime() audio=%lld", audioStartTime));
   165     }
   166   }
   168   int64_t startTime = std::min(videoStartTime, audioStartTime);
   169   if (startTime != INT64_MAX) {
   170     aOutStartTime = startTime;
   171   }
   173   return videoData;
   174 }
   176 nsresult MediaDecoderReader::DecodeToTarget(int64_t aTarget)
   177 {
   178   DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::DecodeToTarget(%lld) Begin", aTarget));
   180   // Decode forward to the target frame. Start with video, if we have it.
   181   if (HasVideo()) {
   182     // Note: when decoding hits the end of stream we must keep the last frame
   183     // in the video queue so that we'll have something to display after the
   184     // seek completes. This makes our logic a bit messy.
   185     bool eof = false;
   186     nsAutoPtr<VideoData> video;
   187     while (HasVideo() && !eof) {
   188       while (VideoQueue().GetSize() == 0 && !eof) {
   189         bool skip = false;
   190         eof = !DecodeVideoFrame(skip, 0);
   191         {
   192           ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   193           if (mDecoder->IsShutdown()) {
   194             return NS_ERROR_FAILURE;
   195           }
   196         }
   197       }
   198       if (eof) {
   199         // Hit end of file, we want to display the last frame of the video.
   200         if (video) {
   201           DECODER_LOG(PR_LOG_DEBUG,
   202             ("MediaDecoderReader::DecodeToTarget(%lld) repushing video frame [%lld, %lld] at EOF",
   203             aTarget, video->mTime, video->GetEndTime()));
   204           VideoQueue().PushFront(video.forget());
   205         }
   206         VideoQueue().Finish();
   207         break;
   208       }
   209       video = VideoQueue().PeekFront();
   210       // If the frame end time is less than the seek target, we won't want
   211       // to display this frame after the seek, so discard it.
   212       if (video && video->GetEndTime() <= aTarget) {
   213         DECODER_LOG(PR_LOG_DEBUG,
   214                     ("MediaDecoderReader::DecodeToTarget(%lld) pop video frame [%lld, %lld]",
   215                      aTarget, video->mTime, video->GetEndTime()));
   216         VideoQueue().PopFront();
   217       } else {
   218         // Found a frame after or encompasing the seek target.
   219         if (aTarget >= video->mTime && video->GetEndTime() >= aTarget) {
   220           // The seek target lies inside this frame's time slice. Adjust the frame's
   221           // start time to match the seek target. We do this by replacing the
   222           // first frame with a shallow copy which has the new timestamp.
   223           VideoQueue().PopFront();
   224           VideoData* temp = VideoData::ShallowCopyUpdateTimestamp(video, aTarget);
   225           video = temp;
   226           VideoQueue().PushFront(video);
   227         }
   228         DECODER_LOG(PR_LOG_DEBUG,
   229                     ("MediaDecoderReader::DecodeToTarget(%lld) found target video frame [%lld,%lld]",
   230                      aTarget, video->mTime, video->GetEndTime()));
   232         video.forget();
   233         break;
   234       }
   235     }
   236     {
   237       ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   238       if (mDecoder->IsShutdown()) {
   239         return NS_ERROR_FAILURE;
   240       }
   241     }
   242 #ifdef PR_LOGGING
   243     const VideoData* front =  VideoQueue().PeekFront();
   244     DECODER_LOG(PR_LOG_DEBUG, ("First video frame after decode is %lld",
   245                 front ? front->mTime : -1));
   246 #endif
   247   }
   249   if (HasAudio()) {
   250     // Decode audio forward to the seek target.
   251     bool eof = false;
   252     while (HasAudio() && !eof) {
   253       while (!eof && AudioQueue().GetSize() == 0) {
   254         eof = !DecodeAudioData();
   255         {
   256           ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   257           if (mDecoder->IsShutdown()) {
   258             return NS_ERROR_FAILURE;
   259           }
   260         }
   261       }
   262       const AudioData* audio = AudioQueue().PeekFront();
   263       if (!audio || eof) {
   264         AudioQueue().Finish();
   265         break;
   266       }
   267       CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudio.mRate);
   268       CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudio.mRate);
   269       if (!startFrame.isValid() || !targetFrame.isValid()) {
   270         return NS_ERROR_FAILURE;
   271       }
   272       if (startFrame.value() + audio->mFrames <= targetFrame.value()) {
   273         // Our seek target lies after the frames in this AudioData. Pop it
   274         // off the queue, and keep decoding forwards.
   275         delete AudioQueue().PopFront();
   276         audio = nullptr;
   277         continue;
   278       }
   279       if (startFrame.value() > targetFrame.value()) {
   280         // The seek target doesn't lie in the audio block just after the last
   281         // audio frames we've seen which were before the seek target. This
   282         // could have been the first audio data we've seen after seek, i.e. the
   283         // seek terminated after the seek target in the audio stream. Just
   284         // abort the audio decode-to-target, the state machine will play
   285         // silence to cover the gap. Typically this happens in poorly muxed
   286         // files.
   287         NS_WARNING("Audio not synced after seek, maybe a poorly muxed file?");
   288         break;
   289       }
   291       // The seek target lies somewhere in this AudioData's frames, strip off
   292       // any frames which lie before the seek target, so we'll begin playback
   293       // exactly at the seek target.
   294       NS_ASSERTION(targetFrame.value() >= startFrame.value(),
   295                    "Target must at or be after data start.");
   296       NS_ASSERTION(targetFrame.value() < startFrame.value() + audio->mFrames,
   297                    "Data must end after target.");
   299       int64_t framesToPrune = targetFrame.value() - startFrame.value();
   300       if (framesToPrune > audio->mFrames) {
   301         // We've messed up somehow. Don't try to trim frames, the |frames|
   302         // variable below will overflow.
   303         NS_WARNING("Can't prune more frames that we have!");
   304         break;
   305       }
   306       uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune);
   307       uint32_t channels = audio->mChannels;
   308       nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
   309       memcpy(audioData.get(),
   310              audio->mAudioData.get() + (framesToPrune * channels),
   311              frames * channels * sizeof(AudioDataValue));
   312       CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
   313       if (!duration.isValid()) {
   314         return NS_ERROR_FAILURE;
   315       }
   316       nsAutoPtr<AudioData> data(new AudioData(audio->mOffset,
   317                                               aTarget,
   318                                               duration.value(),
   319                                               frames,
   320                                               audioData.forget(),
   321                                               channels));
   322       delete AudioQueue().PopFront();
   323       AudioQueue().PushFront(data.forget());
   324       break;
   325     }
   326   }
   328 #ifdef PR_LOGGING
   329   const VideoData* v = VideoQueue().PeekFront();
   330   const AudioData* a = AudioQueue().PeekFront();
   331   DECODER_LOG(PR_LOG_DEBUG,
   332               ("MediaDecoderReader::DecodeToTarget(%lld) finished v=%lld a=%lld",
   333               aTarget, v ? v->mTime : -1, a ? a->mTime : -1));
   334 #endif
   336   return NS_OK;
   337 }
   339 nsresult
   340 MediaDecoderReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered,
   341                                 int64_t aStartTime)
   342 {
   343   MediaResource* stream = mDecoder->GetResource();
   344   int64_t durationUs = 0;
   345   {
   346     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   347     durationUs = mDecoder->GetMediaDuration();
   348   }
   349   GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
   350   return NS_OK;
   351 }
   353 } // namespace mozilla

mercurial