content/media/fmp4/MP4Reader.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: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 "MP4Reader.h"
michael@0 8 #include "MediaResource.h"
michael@0 9 #include "mp4_demuxer/mp4_demuxer.h"
michael@0 10 #include "mp4_demuxer/Streams.h"
michael@0 11 #include "nsSize.h"
michael@0 12 #include "VideoUtils.h"
michael@0 13 #include "mozilla/dom/HTMLMediaElement.h"
michael@0 14 #include "ImageContainer.h"
michael@0 15 #include "Layers.h"
michael@0 16 #include "SharedThreadPool.h"
michael@0 17 #include "mozilla/Preferences.h"
michael@0 18
michael@0 19 using mozilla::layers::Image;
michael@0 20 using mozilla::layers::LayerManager;
michael@0 21 using mozilla::layers::LayersBackend;
michael@0 22
michael@0 23 #ifdef PR_LOGGING
michael@0 24 PRLogModuleInfo* GetDemuxerLog() {
michael@0 25 static PRLogModuleInfo* log = nullptr;
michael@0 26 if (!log) {
michael@0 27 log = PR_NewLogModule("MP4Demuxer");
michael@0 28 }
michael@0 29 return log;
michael@0 30 }
michael@0 31 #define LOG(...) PR_LOG(GetDemuxerLog(), PR_LOG_DEBUG, (__VA_ARGS__))
michael@0 32 #else
michael@0 33 #define LOG(...)
michael@0 34 #endif
michael@0 35
michael@0 36 using namespace mp4_demuxer;
michael@0 37
michael@0 38 namespace mozilla {
michael@0 39
michael@0 40 // Uncomment to enable verbose per-sample logging.
michael@0 41 //#define LOG_SAMPLE_DECODE 1
michael@0 42
michael@0 43 class MP4Stream : public mp4_demuxer::Stream {
michael@0 44 public:
michael@0 45
michael@0 46 MP4Stream(MediaResource* aResource)
michael@0 47 : mResource(aResource)
michael@0 48 {
michael@0 49 MOZ_COUNT_CTOR(MP4Stream);
michael@0 50 MOZ_ASSERT(aResource);
michael@0 51 }
michael@0 52 virtual ~MP4Stream() {
michael@0 53 MOZ_COUNT_DTOR(MP4Stream);
michael@0 54 }
michael@0 55
michael@0 56 virtual bool ReadAt(int64_t aOffset,
michael@0 57 uint8_t* aBuffer,
michael@0 58 uint32_t aCount,
michael@0 59 uint32_t* aBytesRead) MOZ_OVERRIDE {
michael@0 60 uint32_t sum = 0;
michael@0 61 do {
michael@0 62 uint32_t offset = aOffset + sum;
michael@0 63 char* buffer = reinterpret_cast<char*>(aBuffer + sum);
michael@0 64 uint32_t toRead = aCount - sum;
michael@0 65 uint32_t bytesRead = 0;
michael@0 66 nsresult rv = mResource->ReadAt(offset, buffer, toRead, &bytesRead);
michael@0 67 if (NS_FAILED(rv)) {
michael@0 68 return false;
michael@0 69 }
michael@0 70 sum += bytesRead;
michael@0 71 } while (sum < aCount);
michael@0 72 *aBytesRead = sum;
michael@0 73 return true;
michael@0 74 }
michael@0 75
michael@0 76 virtual int64_t Length() const MOZ_OVERRIDE {
michael@0 77 return mResource->GetLength();
michael@0 78 }
michael@0 79
michael@0 80 private:
michael@0 81 RefPtr<MediaResource> mResource;
michael@0 82 };
michael@0 83
michael@0 84 MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
michael@0 85 : MediaDecoderReader(aDecoder)
michael@0 86 , mAudio("MP4 audio decoder data", Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
michael@0 87 , mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2))
michael@0 88 , mLastReportedNumDecodedFrames(0)
michael@0 89 , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
michael@0 90 {
michael@0 91 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
michael@0 92 MOZ_COUNT_CTOR(MP4Reader);
michael@0 93 }
michael@0 94
michael@0 95 MP4Reader::~MP4Reader()
michael@0 96 {
michael@0 97 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
michael@0 98 MOZ_COUNT_DTOR(MP4Reader);
michael@0 99 Shutdown();
michael@0 100 }
michael@0 101
michael@0 102 void
michael@0 103 MP4Reader::Shutdown()
michael@0 104 {
michael@0 105 if (mAudio.mDecoder) {
michael@0 106 Flush(kAudio);
michael@0 107 mAudio.mDecoder->Shutdown();
michael@0 108 mAudio.mDecoder = nullptr;
michael@0 109 }
michael@0 110 if (mAudio.mTaskQueue) {
michael@0 111 mAudio.mTaskQueue->Shutdown();
michael@0 112 mAudio.mTaskQueue = nullptr;
michael@0 113 }
michael@0 114 if (mVideo.mDecoder) {
michael@0 115 Flush(kVideo);
michael@0 116 mVideo.mDecoder->Shutdown();
michael@0 117 mVideo.mDecoder = nullptr;
michael@0 118 }
michael@0 119 if (mVideo.mTaskQueue) {
michael@0 120 mVideo.mTaskQueue->Shutdown();
michael@0 121 mVideo.mTaskQueue = nullptr;
michael@0 122 }
michael@0 123 }
michael@0 124
michael@0 125 void
michael@0 126 MP4Reader::InitLayersBackendType()
michael@0 127 {
michael@0 128 if (!IsVideoContentType(mDecoder->GetResource()->GetContentType())) {
michael@0 129 // Not playing video, we don't care about the layers backend type.
michael@0 130 return;
michael@0 131 }
michael@0 132 // Extract the layer manager backend type so that platform decoders
michael@0 133 // can determine whether it's worthwhile using hardware accelerated
michael@0 134 // video decoding.
michael@0 135 MediaDecoderOwner* owner = mDecoder->GetOwner();
michael@0 136 if (!owner) {
michael@0 137 NS_WARNING("MP4Reader without a decoder owner, can't get HWAccel");
michael@0 138 return;
michael@0 139 }
michael@0 140
michael@0 141 dom::HTMLMediaElement* element = owner->GetMediaElement();
michael@0 142 NS_ENSURE_TRUE_VOID(element);
michael@0 143
michael@0 144 nsRefPtr<LayerManager> layerManager =
michael@0 145 nsContentUtils::LayerManagerForDocument(element->OwnerDoc());
michael@0 146 NS_ENSURE_TRUE_VOID(layerManager);
michael@0 147
michael@0 148 mLayersBackendType = layerManager->GetCompositorBackendType();
michael@0 149 }
michael@0 150
michael@0 151 nsresult
michael@0 152 MP4Reader::Init(MediaDecoderReader* aCloneDonor)
michael@0 153 {
michael@0 154 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
michael@0 155 PlatformDecoderModule::Init();
michael@0 156 mMP4Stream = new MP4Stream(mDecoder->GetResource());
michael@0 157 mDemuxer = new MP4Demuxer(mMP4Stream);
michael@0 158
michael@0 159 InitLayersBackendType();
michael@0 160
michael@0 161 mAudio.mTaskQueue = new MediaTaskQueue(
michael@0 162 SharedThreadPool::Get(NS_LITERAL_CSTRING("MP4 Audio Decode")));
michael@0 163 NS_ENSURE_TRUE(mAudio.mTaskQueue, NS_ERROR_FAILURE);
michael@0 164
michael@0 165 mVideo.mTaskQueue = new MediaTaskQueue(
michael@0 166 SharedThreadPool::Get(NS_LITERAL_CSTRING("MP4 Video Decode")));
michael@0 167 NS_ENSURE_TRUE(mVideo.mTaskQueue, NS_ERROR_FAILURE);
michael@0 168
michael@0 169 return NS_OK;
michael@0 170 }
michael@0 171
michael@0 172 nsresult
michael@0 173 MP4Reader::ReadMetadata(MediaInfo* aInfo,
michael@0 174 MetadataTags** aTags)
michael@0 175 {
michael@0 176 bool ok = mDemuxer->Init();
michael@0 177 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
michael@0 178
michael@0 179 const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
michael@0 180 mInfo.mAudio.mHasAudio = mAudio.mActive = mDemuxer->HasAudio() &&
michael@0 181 audio.IsValidConfig();
michael@0 182 // If we have audio, we *only* allow AAC to be decoded.
michael@0 183 if (HasAudio() && audio.codec() != kCodecAAC) {
michael@0 184 return NS_ERROR_FAILURE;
michael@0 185 }
michael@0 186
michael@0 187 const VideoDecoderConfig& video = mDemuxer->VideoConfig();
michael@0 188 mInfo.mVideo.mHasVideo = mVideo.mActive = mDemuxer->HasVideo() &&
michael@0 189 video.IsValidConfig();
michael@0 190 // If we have video, we *only* allow H.264 to be decoded.
michael@0 191 if (HasVideo() && video.codec() != kCodecH264) {
michael@0 192 return NS_ERROR_FAILURE;
michael@0 193 }
michael@0 194
michael@0 195 mPlatform = PlatformDecoderModule::Create();
michael@0 196 NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
michael@0 197
michael@0 198 if (HasAudio()) {
michael@0 199 mInfo.mAudio.mRate = audio.samples_per_second();
michael@0 200 mInfo.mAudio.mChannels = ChannelLayoutToChannelCount(audio.channel_layout());
michael@0 201 mAudio.mCallback = new DecoderCallback(this, kAudio);
michael@0 202 mAudio.mDecoder = mPlatform->CreateAACDecoder(audio,
michael@0 203 mAudio.mTaskQueue,
michael@0 204 mAudio.mCallback);
michael@0 205 NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, NS_ERROR_FAILURE);
michael@0 206 nsresult rv = mAudio.mDecoder->Init();
michael@0 207 NS_ENSURE_SUCCESS(rv, rv);
michael@0 208 }
michael@0 209
michael@0 210 if (HasVideo()) {
michael@0 211 IntSize sz = video.natural_size();
michael@0 212 mInfo.mVideo.mDisplay = nsIntSize(sz.width(), sz.height());
michael@0 213 mVideo.mCallback = new DecoderCallback(this, kVideo);
michael@0 214 mVideo.mDecoder = mPlatform->CreateH264Decoder(video,
michael@0 215 mLayersBackendType,
michael@0 216 mDecoder->GetImageContainer(),
michael@0 217 mVideo.mTaskQueue,
michael@0 218 mVideo.mCallback);
michael@0 219 NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, NS_ERROR_FAILURE);
michael@0 220 nsresult rv = mVideo.mDecoder->Init();
michael@0 221 NS_ENSURE_SUCCESS(rv, rv);
michael@0 222 }
michael@0 223
michael@0 224 // Get the duration, and report it to the decoder if we have it.
michael@0 225 Microseconds duration = mDemuxer->Duration();
michael@0 226 if (duration != -1) {
michael@0 227 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
michael@0 228 mDecoder->SetMediaDuration(duration);
michael@0 229 }
michael@0 230 // We can seek if we get a duration *and* the reader reports that it's
michael@0 231 // seekable.
michael@0 232 if (!mDemuxer->CanSeek()) {
michael@0 233 mDecoder->SetMediaSeekable(false);
michael@0 234 }
michael@0 235
michael@0 236 *aInfo = mInfo;
michael@0 237 *aTags = nullptr;
michael@0 238
michael@0 239 return NS_OK;
michael@0 240 }
michael@0 241
michael@0 242 bool
michael@0 243 MP4Reader::HasAudio()
michael@0 244 {
michael@0 245 return mAudio.mActive;
michael@0 246 }
michael@0 247
michael@0 248 bool
michael@0 249 MP4Reader::HasVideo()
michael@0 250 {
michael@0 251 return mVideo.mActive;
michael@0 252 }
michael@0 253
michael@0 254 MP4Reader::DecoderData&
michael@0 255 MP4Reader::GetDecoderData(TrackType aTrack)
michael@0 256 {
michael@0 257 MOZ_ASSERT(aTrack == kAudio || aTrack == kVideo);
michael@0 258 return (aTrack == kAudio) ? mAudio : mVideo;
michael@0 259 }
michael@0 260
michael@0 261 MP4SampleQueue&
michael@0 262 MP4Reader::SampleQueue(TrackType aTrack)
michael@0 263 {
michael@0 264 return GetDecoderData(aTrack).mDemuxedSamples;
michael@0 265 }
michael@0 266
michael@0 267 MediaDataDecoder*
michael@0 268 MP4Reader::Decoder(mp4_demuxer::TrackType aTrack)
michael@0 269 {
michael@0 270 return GetDecoderData(aTrack).mDecoder;
michael@0 271 }
michael@0 272
michael@0 273 MP4Sample*
michael@0 274 MP4Reader::PopSample(TrackType aTrack)
michael@0 275 {
michael@0 276 // Unfortunately the demuxer outputs in the order samples appear in the
michael@0 277 // media, not on a per stream basis. We cache the samples we get from
michael@0 278 // streams other than the one we want.
michael@0 279 MP4SampleQueue& sampleQueue = SampleQueue(aTrack);
michael@0 280 while (sampleQueue.empty()) {
michael@0 281 nsAutoPtr<MP4Sample> sample;
michael@0 282 bool eos = false;
michael@0 283 bool ok = mDemuxer->Demux(&sample, &eos);
michael@0 284 if (!ok || eos) {
michael@0 285 MOZ_ASSERT(!sample);
michael@0 286 return nullptr;
michael@0 287 }
michael@0 288 MOZ_ASSERT(sample);
michael@0 289 MP4Sample* s = sample.forget();
michael@0 290 SampleQueue(s->type).push_back(s);
michael@0 291 }
michael@0 292 MOZ_ASSERT(!sampleQueue.empty());
michael@0 293 MP4Sample* sample = sampleQueue.front();
michael@0 294 sampleQueue.pop_front();
michael@0 295 return sample;
michael@0 296 }
michael@0 297
michael@0 298 // How async decoding works:
michael@0 299 //
michael@0 300 // When MP4Reader::Decode() is called:
michael@0 301 // * Lock the DecoderData. We assume the state machine wants
michael@0 302 // output from the decoder (in future, we'll assume decoder wants input
michael@0 303 // when the output MediaQueue isn't "full").
michael@0 304 // * Cache the value of mNumSamplesOutput, as prevFramesOutput.
michael@0 305 // * While we've not output data (mNumSamplesOutput != prevNumFramesOutput)
michael@0 306 // and while we still require input, we demux and input data in the reader.
michael@0 307 // We assume we require input if
michael@0 308 // ((mNumSamplesInput - mNumSamplesOutput) < sDecodeAheadMargin) or
michael@0 309 // mInputExhausted is true. Before we send input, we reset mInputExhausted
michael@0 310 // and increment mNumFrameInput, and drop the lock on DecoderData.
michael@0 311 // * Once we no longer require input, we wait on the DecoderData
michael@0 312 // lock for output, or for the input exhausted callback. If we receive the
michael@0 313 // input exhausted callback, we go back and input more data.
michael@0 314 // * When our output callback is called, we take the DecoderData lock and
michael@0 315 // increment mNumSamplesOutput. We notify the DecoderData lock. This will
michael@0 316 // awaken the Decode thread, and unblock it, and it will return.
michael@0 317 bool
michael@0 318 MP4Reader::Decode(TrackType aTrack)
michael@0 319 {
michael@0 320 DecoderData& data = GetDecoderData(aTrack);
michael@0 321 MOZ_ASSERT(data.mDecoder);
michael@0 322
michael@0 323 data.mMonitor.Lock();
michael@0 324 uint64_t prevNumFramesOutput = data.mNumSamplesOutput;
michael@0 325 while (prevNumFramesOutput == data.mNumSamplesOutput) {
michael@0 326 data.mMonitor.AssertCurrentThreadOwns();
michael@0 327 if (data.mError) {
michael@0 328 // Decode error!
michael@0 329 data.mMonitor.Unlock();
michael@0 330 return false;
michael@0 331 }
michael@0 332 // Send input to the decoder, if we need to. We assume the decoder
michael@0 333 // needs input if it's told us it's out of input, or we're beneath the
michael@0 334 // "low water mark" for the amount of input we've sent it vs the amount
michael@0 335 // out output we've received. We always try to keep the decoder busy if
michael@0 336 // possible, so we try to maintain at least a few input samples ahead,
michael@0 337 // if we need output.
michael@0 338 while (prevNumFramesOutput == data.mNumSamplesOutput &&
michael@0 339 (data.mInputExhausted ||
michael@0 340 (data.mNumSamplesInput - data.mNumSamplesOutput) < data.mDecodeAhead)) {
michael@0 341 data.mMonitor.AssertCurrentThreadOwns();
michael@0 342 data.mMonitor.Unlock();
michael@0 343 nsAutoPtr<MP4Sample> compressed(PopSample(aTrack));
michael@0 344 if (!compressed) {
michael@0 345 // EOS, or error. Let the state machine know there are no more
michael@0 346 // frames coming.
michael@0 347 return false;
michael@0 348 }
michael@0 349 data.mMonitor.Lock();
michael@0 350 data.mInputExhausted = false;
michael@0 351 data.mNumSamplesInput++;
michael@0 352 data.mMonitor.Unlock();
michael@0 353 if (NS_FAILED(data.mDecoder->Input(compressed))) {
michael@0 354 return false;
michael@0 355 }
michael@0 356 // If Input() failed, we let the auto pointer delete |compressed|.
michael@0 357 // Otherwise, we assume the decoder will delete it when it's finished
michael@0 358 // with it.
michael@0 359 compressed.forget();
michael@0 360 data.mMonitor.Lock();
michael@0 361 }
michael@0 362 data.mMonitor.AssertCurrentThreadOwns();
michael@0 363 while (!data.mError &&
michael@0 364 prevNumFramesOutput == data.mNumSamplesOutput &&
michael@0 365 !data.mInputExhausted ) {
michael@0 366 data.mMonitor.Wait();
michael@0 367 }
michael@0 368 }
michael@0 369 data.mMonitor.AssertCurrentThreadOwns();
michael@0 370 data.mMonitor.Unlock();
michael@0 371 return true;
michael@0 372 }
michael@0 373
michael@0 374 #ifdef LOG_SAMPLE_DECODE
michael@0 375 static const char*
michael@0 376 TrackTypeToStr(TrackType aTrack)
michael@0 377 {
michael@0 378 MOZ_ASSERT(aTrack == kAudio || aTrack == kVideo);
michael@0 379 switch (aTrack) {
michael@0 380 case kAudio: return "Audio";
michael@0 381 case kVideo: return "Video";
michael@0 382 default: return "Unknown";
michael@0 383 }
michael@0 384 }
michael@0 385 #endif
michael@0 386
michael@0 387 void
michael@0 388 MP4Reader::Output(mp4_demuxer::TrackType aTrack, MediaData* aSample)
michael@0 389 {
michael@0 390 #ifdef LOG_SAMPLE_DECODE
michael@0 391 LOG("Decoded %s sample time=%lld dur=%lld",
michael@0 392 TrackTypeToStr(aTrack), aSample->mTime, aSample->mDuration);
michael@0 393 #endif
michael@0 394
michael@0 395 DecoderData& data = GetDecoderData(aTrack);
michael@0 396 // Don't accept output while we're flushing.
michael@0 397 MonitorAutoLock mon(data.mMonitor);
michael@0 398 if (data.mIsFlushing) {
michael@0 399 mon.NotifyAll();
michael@0 400 return;
michael@0 401 }
michael@0 402
michael@0 403 switch (aTrack) {
michael@0 404 case kAudio: {
michael@0 405 MOZ_ASSERT(aSample->mType == MediaData::AUDIO_SAMPLES);
michael@0 406 AudioQueue().Push(static_cast<AudioData*>(aSample));
michael@0 407 break;
michael@0 408 }
michael@0 409 case kVideo: {
michael@0 410 MOZ_ASSERT(aSample->mType == MediaData::VIDEO_FRAME);
michael@0 411 VideoQueue().Push(static_cast<VideoData*>(aSample));
michael@0 412 break;
michael@0 413 }
michael@0 414 default:
michael@0 415 break;
michael@0 416 }
michael@0 417
michael@0 418 data.mNumSamplesOutput++;
michael@0 419 mon.NotifyAll();
michael@0 420 }
michael@0 421
michael@0 422 void
michael@0 423 MP4Reader::InputExhausted(mp4_demuxer::TrackType aTrack)
michael@0 424 {
michael@0 425 DecoderData& data = GetDecoderData(aTrack);
michael@0 426 MonitorAutoLock mon(data.mMonitor);
michael@0 427 data.mInputExhausted = true;
michael@0 428 mon.NotifyAll();
michael@0 429 }
michael@0 430
michael@0 431 void
michael@0 432 MP4Reader::Error(mp4_demuxer::TrackType aTrack)
michael@0 433 {
michael@0 434 DecoderData& data = GetDecoderData(aTrack);
michael@0 435 MonitorAutoLock mon(data.mMonitor);
michael@0 436 data.mError = true;
michael@0 437 mon.NotifyAll();
michael@0 438 }
michael@0 439
michael@0 440 bool
michael@0 441 MP4Reader::DecodeAudioData()
michael@0 442 {
michael@0 443 MOZ_ASSERT(HasAudio() && mPlatform && mAudio.mDecoder);
michael@0 444 return Decode(kAudio);
michael@0 445 }
michael@0 446
michael@0 447 void
michael@0 448 MP4Reader::Flush(mp4_demuxer::TrackType aTrack)
michael@0 449 {
michael@0 450 DecoderData& data = GetDecoderData(aTrack);
michael@0 451 if (!data.mDecoder) {
michael@0 452 return;
michael@0 453 }
michael@0 454 // Purge the current decoder's state.
michael@0 455 // Set a flag so that we ignore all output while we call
michael@0 456 // MediaDataDecoder::Flush().
michael@0 457 {
michael@0 458 data.mIsFlushing = true;
michael@0 459 MonitorAutoLock mon(data.mMonitor);
michael@0 460 }
michael@0 461 data.mDecoder->Flush();
michael@0 462 {
michael@0 463 data.mIsFlushing = false;
michael@0 464 MonitorAutoLock mon(data.mMonitor);
michael@0 465 }
michael@0 466 }
michael@0 467
michael@0 468 bool
michael@0 469 MP4Reader::SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed)
michael@0 470 {
michael@0 471 MOZ_ASSERT(mVideo.mDecoder);
michael@0 472
michael@0 473 Flush(kVideo);
michael@0 474
michael@0 475 // Loop until we reach the next keyframe after the threshold.
michael@0 476 while (true) {
michael@0 477 nsAutoPtr<MP4Sample> compressed(PopSample(kVideo));
michael@0 478 if (!compressed) {
michael@0 479 // EOS, or error. Let the state machine know.
michael@0 480 return false;
michael@0 481 }
michael@0 482 parsed++;
michael@0 483 if (!compressed->is_sync_point ||
michael@0 484 compressed->composition_timestamp < aTimeThreshold) {
michael@0 485 continue;
michael@0 486 }
michael@0 487 mVideo.mDemuxedSamples.push_front(compressed.forget());
michael@0 488 break;
michael@0 489 }
michael@0 490
michael@0 491 return true;
michael@0 492 }
michael@0 493
michael@0 494 bool
michael@0 495 MP4Reader::DecodeVideoFrame(bool &aKeyframeSkip,
michael@0 496 int64_t aTimeThreshold)
michael@0 497 {
michael@0 498 // Record number of frames decoded and parsed. Automatically update the
michael@0 499 // stats counters using the AutoNotifyDecoded stack-based class.
michael@0 500 uint32_t parsed = 0, decoded = 0;
michael@0 501 AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
michael@0 502
michael@0 503 MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
michael@0 504
michael@0 505 if (aKeyframeSkip) {
michael@0 506 bool ok = SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed);
michael@0 507 if (!ok) {
michael@0 508 NS_WARNING("Failed to skip demux up to next keyframe");
michael@0 509 return false;
michael@0 510 }
michael@0 511 aKeyframeSkip = false;
michael@0 512 nsresult rv = mVideo.mDecoder->Flush();
michael@0 513 NS_ENSURE_SUCCESS(rv, false);
michael@0 514 }
michael@0 515
michael@0 516 bool rv = Decode(kVideo);
michael@0 517 {
michael@0 518 // Report the number of "decoded" frames as the difference in the
michael@0 519 // mNumSamplesOutput field since the last time we were called.
michael@0 520 MonitorAutoLock mon(mVideo.mMonitor);
michael@0 521 uint64_t delta = mVideo.mNumSamplesOutput - mLastReportedNumDecodedFrames;
michael@0 522 decoded = static_cast<uint32_t>(delta);
michael@0 523 mLastReportedNumDecodedFrames = mVideo.mNumSamplesOutput;
michael@0 524 }
michael@0 525 return rv;
michael@0 526 }
michael@0 527
michael@0 528 nsresult
michael@0 529 MP4Reader::Seek(int64_t aTime,
michael@0 530 int64_t aStartTime,
michael@0 531 int64_t aEndTime,
michael@0 532 int64_t aCurrentTime)
michael@0 533 {
michael@0 534 if (!mDemuxer->CanSeek()) {
michael@0 535 return NS_ERROR_FAILURE;
michael@0 536 }
michael@0 537 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 538 }
michael@0 539
michael@0 540 } // namespace mozilla

mercurial