content/media/MediaDecoderReader.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/MediaDecoderReader.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,353 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "MediaDecoderReader.h"
    1.11 +#include "AbstractMediaDecoder.h"
    1.12 +#include "VideoUtils.h"
    1.13 +#include "ImageContainer.h"
    1.14 +
    1.15 +#include "mozilla/mozalloc.h"
    1.16 +#include <stdint.h>
    1.17 +#include <algorithm>
    1.18 +
    1.19 +namespace mozilla {
    1.20 +
    1.21 +// Un-comment to enable logging of seek bisections.
    1.22 +//#define SEEK_LOGGING
    1.23 +
    1.24 +#ifdef PR_LOGGING
    1.25 +extern PRLogModuleInfo* gMediaDecoderLog;
    1.26 +#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
    1.27 +#ifdef SEEK_LOGGING
    1.28 +#define SEEK_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
    1.29 +#else
    1.30 +#define SEEK_LOG(type, msg)
    1.31 +#endif
    1.32 +#else
    1.33 +#define DECODER_LOG(type, msg)
    1.34 +#define SEEK_LOG(type, msg)
    1.35 +#endif
    1.36 +
    1.37 +class VideoQueueMemoryFunctor : public nsDequeFunctor {
    1.38 +public:
    1.39 +  VideoQueueMemoryFunctor() : mSize(0) {}
    1.40 +
    1.41 +  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
    1.42 +
    1.43 +  virtual void* operator()(void* aObject) {
    1.44 +    const VideoData* v = static_cast<const VideoData*>(aObject);
    1.45 +    mSize += v->SizeOfIncludingThis(MallocSizeOf);
    1.46 +    return nullptr;
    1.47 +  }
    1.48 +
    1.49 +  size_t mSize;
    1.50 +};
    1.51 +
    1.52 +
    1.53 +class AudioQueueMemoryFunctor : public nsDequeFunctor {
    1.54 +public:
    1.55 +  AudioQueueMemoryFunctor() : mSize(0) {}
    1.56 +
    1.57 +  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
    1.58 +
    1.59 +  virtual void* operator()(void* aObject) {
    1.60 +    const AudioData* audioData = static_cast<const AudioData*>(aObject);
    1.61 +    mSize += audioData->SizeOfIncludingThis(MallocSizeOf);
    1.62 +    return nullptr;
    1.63 +  }
    1.64 +
    1.65 +  size_t mSize;
    1.66 +};
    1.67 +
    1.68 +MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
    1.69 +  : mAudioCompactor(mAudioQueue),
    1.70 +    mDecoder(aDecoder),
    1.71 +    mIgnoreAudioOutputFormat(false)
    1.72 +{
    1.73 +  MOZ_COUNT_CTOR(MediaDecoderReader);
    1.74 +}
    1.75 +
    1.76 +MediaDecoderReader::~MediaDecoderReader()
    1.77 +{
    1.78 +  ResetDecode();
    1.79 +  MOZ_COUNT_DTOR(MediaDecoderReader);
    1.80 +}
    1.81 +
    1.82 +size_t MediaDecoderReader::SizeOfVideoQueueInBytes() const
    1.83 +{
    1.84 +  VideoQueueMemoryFunctor functor;
    1.85 +  mVideoQueue.LockedForEach(functor);
    1.86 +  return functor.mSize;
    1.87 +}
    1.88 +
    1.89 +size_t MediaDecoderReader::SizeOfAudioQueueInBytes() const
    1.90 +{
    1.91 +  AudioQueueMemoryFunctor functor;
    1.92 +  mAudioQueue.LockedForEach(functor);
    1.93 +  return functor.mSize;
    1.94 +}
    1.95 +
    1.96 +nsresult MediaDecoderReader::ResetDecode()
    1.97 +{
    1.98 +  nsresult res = NS_OK;
    1.99 +
   1.100 +  VideoQueue().Reset();
   1.101 +  AudioQueue().Reset();
   1.102 +
   1.103 +  return res;
   1.104 +}
   1.105 +
   1.106 +VideoData* MediaDecoderReader::DecodeToFirstVideoData()
   1.107 +{
   1.108 +  bool eof = false;
   1.109 +  while (!eof && VideoQueue().GetSize() == 0) {
   1.110 +    {
   1.111 +      ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   1.112 +      if (mDecoder->IsShutdown()) {
   1.113 +        return nullptr;
   1.114 +      }
   1.115 +    }
   1.116 +    bool keyframeSkip = false;
   1.117 +    eof = !DecodeVideoFrame(keyframeSkip, 0);
   1.118 +  }
   1.119 +  if (eof) {
   1.120 +    VideoQueue().Finish();
   1.121 +  }
   1.122 +  VideoData* d = nullptr;
   1.123 +  return (d = VideoQueue().PeekFront()) ? d : nullptr;
   1.124 +}
   1.125 +
   1.126 +AudioData* MediaDecoderReader::DecodeToFirstAudioData()
   1.127 +{
   1.128 +  bool eof = false;
   1.129 +  while (!eof && AudioQueue().GetSize() == 0) {
   1.130 +    {
   1.131 +      ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   1.132 +      if (mDecoder->IsShutdown()) {
   1.133 +        return nullptr;
   1.134 +      }
   1.135 +    }
   1.136 +    eof = !DecodeAudioData();
   1.137 +  }
   1.138 +  if (eof) {
   1.139 +    AudioQueue().Finish();
   1.140 +  }
   1.141 +  AudioData* d = nullptr;
   1.142 +  return (d = AudioQueue().PeekFront()) ? d : nullptr;
   1.143 +}
   1.144 +
   1.145 +VideoData* MediaDecoderReader::FindStartTime(int64_t& aOutStartTime)
   1.146 +{
   1.147 +  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
   1.148 +               "Should be on state machine or decode thread.");
   1.149 +
   1.150 +  // Extract the start times of the bitstreams in order to calculate
   1.151 +  // the duration.
   1.152 +  int64_t videoStartTime = INT64_MAX;
   1.153 +  int64_t audioStartTime = INT64_MAX;
   1.154 +  VideoData* videoData = nullptr;
   1.155 +
   1.156 +  if (HasVideo()) {
   1.157 +    videoData = DecodeToFirstVideoData();
   1.158 +    if (videoData) {
   1.159 +      videoStartTime = videoData->mTime;
   1.160 +      DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::FindStartTime() video=%lld", videoStartTime));
   1.161 +    }
   1.162 +  }
   1.163 +  if (HasAudio()) {
   1.164 +    AudioData* audioData = DecodeToFirstAudioData();
   1.165 +    if (audioData) {
   1.166 +      audioStartTime = audioData->mTime;
   1.167 +      DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::FindStartTime() audio=%lld", audioStartTime));
   1.168 +    }
   1.169 +  }
   1.170 +
   1.171 +  int64_t startTime = std::min(videoStartTime, audioStartTime);
   1.172 +  if (startTime != INT64_MAX) {
   1.173 +    aOutStartTime = startTime;
   1.174 +  }
   1.175 +
   1.176 +  return videoData;
   1.177 +}
   1.178 +
   1.179 +nsresult MediaDecoderReader::DecodeToTarget(int64_t aTarget)
   1.180 +{
   1.181 +  DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoderReader::DecodeToTarget(%lld) Begin", aTarget));
   1.182 +
   1.183 +  // Decode forward to the target frame. Start with video, if we have it.
   1.184 +  if (HasVideo()) {
   1.185 +    // Note: when decoding hits the end of stream we must keep the last frame
   1.186 +    // in the video queue so that we'll have something to display after the
   1.187 +    // seek completes. This makes our logic a bit messy.
   1.188 +    bool eof = false;
   1.189 +    nsAutoPtr<VideoData> video;
   1.190 +    while (HasVideo() && !eof) {
   1.191 +      while (VideoQueue().GetSize() == 0 && !eof) {
   1.192 +        bool skip = false;
   1.193 +        eof = !DecodeVideoFrame(skip, 0);
   1.194 +        {
   1.195 +          ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   1.196 +          if (mDecoder->IsShutdown()) {
   1.197 +            return NS_ERROR_FAILURE;
   1.198 +          }
   1.199 +        }
   1.200 +      }
   1.201 +      if (eof) {
   1.202 +        // Hit end of file, we want to display the last frame of the video.
   1.203 +        if (video) {
   1.204 +          DECODER_LOG(PR_LOG_DEBUG,
   1.205 +            ("MediaDecoderReader::DecodeToTarget(%lld) repushing video frame [%lld, %lld] at EOF",
   1.206 +            aTarget, video->mTime, video->GetEndTime()));
   1.207 +          VideoQueue().PushFront(video.forget());
   1.208 +        }
   1.209 +        VideoQueue().Finish();
   1.210 +        break;
   1.211 +      }
   1.212 +      video = VideoQueue().PeekFront();
   1.213 +      // If the frame end time is less than the seek target, we won't want
   1.214 +      // to display this frame after the seek, so discard it.
   1.215 +      if (video && video->GetEndTime() <= aTarget) {
   1.216 +        DECODER_LOG(PR_LOG_DEBUG,
   1.217 +                    ("MediaDecoderReader::DecodeToTarget(%lld) pop video frame [%lld, %lld]",
   1.218 +                     aTarget, video->mTime, video->GetEndTime()));
   1.219 +        VideoQueue().PopFront();
   1.220 +      } else {
   1.221 +        // Found a frame after or encompasing the seek target.
   1.222 +        if (aTarget >= video->mTime && video->GetEndTime() >= aTarget) {
   1.223 +          // The seek target lies inside this frame's time slice. Adjust the frame's
   1.224 +          // start time to match the seek target. We do this by replacing the
   1.225 +          // first frame with a shallow copy which has the new timestamp.
   1.226 +          VideoQueue().PopFront();
   1.227 +          VideoData* temp = VideoData::ShallowCopyUpdateTimestamp(video, aTarget);
   1.228 +          video = temp;
   1.229 +          VideoQueue().PushFront(video);
   1.230 +        }
   1.231 +        DECODER_LOG(PR_LOG_DEBUG,
   1.232 +                    ("MediaDecoderReader::DecodeToTarget(%lld) found target video frame [%lld,%lld]",
   1.233 +                     aTarget, video->mTime, video->GetEndTime()));
   1.234 +
   1.235 +        video.forget();
   1.236 +        break;
   1.237 +      }
   1.238 +    }
   1.239 +    {
   1.240 +      ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   1.241 +      if (mDecoder->IsShutdown()) {
   1.242 +        return NS_ERROR_FAILURE;
   1.243 +      }
   1.244 +    }
   1.245 +#ifdef PR_LOGGING
   1.246 +    const VideoData* front =  VideoQueue().PeekFront();
   1.247 +    DECODER_LOG(PR_LOG_DEBUG, ("First video frame after decode is %lld",
   1.248 +                front ? front->mTime : -1));
   1.249 +#endif
   1.250 +  }
   1.251 +
   1.252 +  if (HasAudio()) {
   1.253 +    // Decode audio forward to the seek target.
   1.254 +    bool eof = false;
   1.255 +    while (HasAudio() && !eof) {
   1.256 +      while (!eof && AudioQueue().GetSize() == 0) {
   1.257 +        eof = !DecodeAudioData();
   1.258 +        {
   1.259 +          ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
   1.260 +          if (mDecoder->IsShutdown()) {
   1.261 +            return NS_ERROR_FAILURE;
   1.262 +          }
   1.263 +        }
   1.264 +      }
   1.265 +      const AudioData* audio = AudioQueue().PeekFront();
   1.266 +      if (!audio || eof) {
   1.267 +        AudioQueue().Finish();
   1.268 +        break;
   1.269 +      }
   1.270 +      CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudio.mRate);
   1.271 +      CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudio.mRate);
   1.272 +      if (!startFrame.isValid() || !targetFrame.isValid()) {
   1.273 +        return NS_ERROR_FAILURE;
   1.274 +      }
   1.275 +      if (startFrame.value() + audio->mFrames <= targetFrame.value()) {
   1.276 +        // Our seek target lies after the frames in this AudioData. Pop it
   1.277 +        // off the queue, and keep decoding forwards.
   1.278 +        delete AudioQueue().PopFront();
   1.279 +        audio = nullptr;
   1.280 +        continue;
   1.281 +      }
   1.282 +      if (startFrame.value() > targetFrame.value()) {
   1.283 +        // The seek target doesn't lie in the audio block just after the last
   1.284 +        // audio frames we've seen which were before the seek target. This
   1.285 +        // could have been the first audio data we've seen after seek, i.e. the
   1.286 +        // seek terminated after the seek target in the audio stream. Just
   1.287 +        // abort the audio decode-to-target, the state machine will play
   1.288 +        // silence to cover the gap. Typically this happens in poorly muxed
   1.289 +        // files.
   1.290 +        NS_WARNING("Audio not synced after seek, maybe a poorly muxed file?");
   1.291 +        break;
   1.292 +      }
   1.293 +
   1.294 +      // The seek target lies somewhere in this AudioData's frames, strip off
   1.295 +      // any frames which lie before the seek target, so we'll begin playback
   1.296 +      // exactly at the seek target.
   1.297 +      NS_ASSERTION(targetFrame.value() >= startFrame.value(),
   1.298 +                   "Target must at or be after data start.");
   1.299 +      NS_ASSERTION(targetFrame.value() < startFrame.value() + audio->mFrames,
   1.300 +                   "Data must end after target.");
   1.301 +
   1.302 +      int64_t framesToPrune = targetFrame.value() - startFrame.value();
   1.303 +      if (framesToPrune > audio->mFrames) {
   1.304 +        // We've messed up somehow. Don't try to trim frames, the |frames|
   1.305 +        // variable below will overflow.
   1.306 +        NS_WARNING("Can't prune more frames that we have!");
   1.307 +        break;
   1.308 +      }
   1.309 +      uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune);
   1.310 +      uint32_t channels = audio->mChannels;
   1.311 +      nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
   1.312 +      memcpy(audioData.get(),
   1.313 +             audio->mAudioData.get() + (framesToPrune * channels),
   1.314 +             frames * channels * sizeof(AudioDataValue));
   1.315 +      CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
   1.316 +      if (!duration.isValid()) {
   1.317 +        return NS_ERROR_FAILURE;
   1.318 +      }
   1.319 +      nsAutoPtr<AudioData> data(new AudioData(audio->mOffset,
   1.320 +                                              aTarget,
   1.321 +                                              duration.value(),
   1.322 +                                              frames,
   1.323 +                                              audioData.forget(),
   1.324 +                                              channels));
   1.325 +      delete AudioQueue().PopFront();
   1.326 +      AudioQueue().PushFront(data.forget());
   1.327 +      break;
   1.328 +    }
   1.329 +  }
   1.330 +
   1.331 +#ifdef PR_LOGGING
   1.332 +  const VideoData* v = VideoQueue().PeekFront();
   1.333 +  const AudioData* a = AudioQueue().PeekFront();
   1.334 +  DECODER_LOG(PR_LOG_DEBUG,
   1.335 +              ("MediaDecoderReader::DecodeToTarget(%lld) finished v=%lld a=%lld",
   1.336 +              aTarget, v ? v->mTime : -1, a ? a->mTime : -1));
   1.337 +#endif
   1.338 +
   1.339 +  return NS_OK;
   1.340 +}
   1.341 +
   1.342 +nsresult
   1.343 +MediaDecoderReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered,
   1.344 +                                int64_t aStartTime)
   1.345 +{
   1.346 +  MediaResource* stream = mDecoder->GetResource();
   1.347 +  int64_t durationUs = 0;
   1.348 +  {
   1.349 +    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   1.350 +    durationUs = mDecoder->GetMediaDuration();
   1.351 +  }
   1.352 +  GetEstimatedBufferedTimeRanges(stream, durationUs, aBuffered);
   1.353 +  return NS_OK;
   1.354 +}
   1.355 +
   1.356 +} // namespace mozilla

mercurial