content/media/MediaDecoderReader.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial