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