Files
palemoon27/dom/media/MediaDecoderReader.cpp
T
roytam1 3d36fa43e7 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1173641 - Hoist shutdown promise resolution into a helper. r=jww (1a02bd90a)
- Bug 1173641 - Null out the thread pool when resolving shutdown. r=jww (ab3f723d5)
- Bug 1173641 - Remove now-unnecessary null-out in MediaDecoderReader::BreakCycles. r=jww (3330778c6)
- Bug 1173656 - Disallow TrackID reuse in TrackUnionStream. r=roc (7f4da1ea2)
- Bug 1175768 - Implement SilentReadAt. r=jya (ece3c2ffa)
- Bug 1178437 - Assert OnTaskQueue for most of the remaining MDSM methods. r=jww (da13ec549)
- Bug 1178437 - Remove ReadOnWrongThread. r=jww (f9cf8946d)
- Bug 1178437 - Dispatch SetFragmentEndTime. r=jww (740ce9882)
- Bug 1178437 - Make mRealTime const and allow it to be accessed on any thread. r=jww (a65c22f1f)
- Bug 1139964 part 1. Factor out the guts of BackstagePass::Resolve and BackstagePass::Enumerate to allow reuse for other globals that want to opt in to Exposed=System WebIDL annotations. r=smaug (d5eb8c704)
- Bug 1139964 part 2. Add classinfo helpers for the various message manager stuff to install WebIDL Exposed=System things on those globals. r=smaug (47085f2a6)
- Bug 1139964 part 3. Add a test. r=bzbarsky (d87e0907b)
- Bug 1130028 - Custom elements, set registered prototype in compartment of caller of registerElement. r=mrbkap (5bd643614)
- Bug 1130028 - Send inputmethod-contextchange to systemapp to hide keyboard when frame crash. r=yxl (1e100121f)
- Bug 1156629 - OpenGL core context deprecated default VAO. r=jgilbert (5ecabb650)
-  Bug 1110120 - Remove use of UniquePtr for XFB and UB tracking.; r=smaug (92ebc132a)
- Bug 1167504 - Part 11: Clean up buffer binding constraints. r=jgilbert (4f3005203)
- Bug 1167504 - Part 13: Unbind buffers from cached state on buffer deletion. r=jgilbert (bb9e3f53d)
- Bug 1180523 - Part 1: Store the audio mute/volume information on the outer window; r=baku (3b686c6b9)
- line endings dos->unix (d7491a87c)
- Bug 1153258 - directly instantiate nsStandardURL in nsChromeProtocolHandler.cpp; r=bsmedberg (01150c663)
- Bug 1175344 - Include nsContentUtils.h explicitly to avoid compile error on unified building. r=ehsan (10a3d42ac)
- Bug 959752 - Make the network predictor work under e10s. r=mcmanus (3b46a6b65)
- Bug 1159747 - delete h2 static compression table in such a way to avoid crashes after network changes. r=mcmanus (ed34f8d80)
- Bug 1173016 - Cache the basic waveform PeriodicWaves. r=karlt (d64c962f0)
- part of Bug 1165816 - Cancel remote application reputation requests after a certain timeout. r=gcp (4cdc98d99)
- Bug 1082837 - Call content policies on cached image redirects in imgLoader::ValidateSecurityInfo. Content policies check the last hop (final uri) of the cached image. For Mixed Content Blocker, we do an additional check to see if any of the intermediary hops went through an insecure redirect. r=smaug, feedback=seth (ffaf3debe)
- Bug 1082837 - Use nsresult for static ShouldLoad and use NS_IMETHODIMP for nsIContentPolicy::ShouldLoad(). CLOSED TREE (acde35e25)
2021-04-13 09:40:42 +08:00

383 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaDecoderReader.h"
#include "AbstractMediaDecoder.h"
#include "MediaResource.h"
#include "VideoUtils.h"
#include "ImageContainer.h"
#include "nsPrintfCString.h"
#include "mozilla/mozalloc.h"
#include <stdint.h>
#include <algorithm>
using namespace mozilla::media;
namespace mozilla {
// Un-comment to enable logging of seek bisections.
//#define SEEK_LOGGING
extern PRLogModuleInfo* gMediaDecoderLog;
#define DECODER_LOG(x, ...) \
MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("Decoder=%p " x, mDecoder, ##__VA_ARGS__))
// Same workaround as MediaDecoderStateMachine.cpp.
#define DECODER_WARN_HELPER(a, b) NS_WARNING b
#define DECODER_WARN(x, ...) \
DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoder, ##__VA_ARGS__).get()))
class VideoQueueMemoryFunctor : public nsDequeFunctor {
public:
VideoQueueMemoryFunctor() : mSize(0) {}
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
virtual void* operator()(void* aObject) {
const VideoData* v = static_cast<const VideoData*>(aObject);
mSize += v->SizeOfIncludingThis(MallocSizeOf);
return nullptr;
}
size_t mSize;
};
class AudioQueueMemoryFunctor : public nsDequeFunctor {
public:
AudioQueueMemoryFunctor() : mSize(0) {}
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
virtual void* operator()(void* aObject) {
const AudioData* audioData = static_cast<const AudioData*>(aObject);
mSize += audioData->SizeOfIncludingThis(MallocSizeOf);
return nullptr;
}
size_t mSize;
};
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder,
MediaTaskQueue* aBorrowedTaskQueue)
: mAudioCompactor(mAudioQueue)
, mDecoder(aDecoder)
, mTaskQueue(aBorrowedTaskQueue ? aBorrowedTaskQueue
: new MediaTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
/* aSupportsTailDispatch = */ true))
, mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)")
, mIgnoreAudioOutputFormat(false)
, mStartTime(-1)
, mHitAudioDecodeError(false)
, mShutdown(false)
, mTaskQueueIsBorrowed(!!aBorrowedTaskQueue)
, mAudioDiscontinuity(false)
, mVideoDiscontinuity(false)
{
MOZ_COUNT_CTOR(MediaDecoderReader);
MOZ_ASSERT(NS_IsMainThread());
// Dispatch initialization that needs to happen on that task queue.
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &MediaDecoderReader::InitializationTask);
mTaskQueue->Dispatch(r.forget());
}
void
MediaDecoderReader::InitializationTask()
{
if (mDecoder->CanonicalDurationOrNull()) {
mDuration.Connect(mDecoder->CanonicalDurationOrNull());
}
}
MediaDecoderReader::~MediaDecoderReader()
{
MOZ_ASSERT(mShutdown);
MOZ_ASSERT(!mDecoder);
ResetDecode();
MOZ_COUNT_DTOR(MediaDecoderReader);
}
size_t MediaDecoderReader::SizeOfVideoQueueInBytes() const
{
VideoQueueMemoryFunctor functor;
mVideoQueue.LockedForEach(functor);
return functor.mSize;
}
size_t MediaDecoderReader::SizeOfAudioQueueInBytes() const
{
AudioQueueMemoryFunctor functor;
mAudioQueue.LockedForEach(functor);
return functor.mSize;
}
size_t MediaDecoderReader::SizeOfVideoQueueInFrames()
{
return mVideoQueue.GetSize();
}
size_t MediaDecoderReader::SizeOfAudioQueueInFrames()
{
return mAudioQueue.GetSize();
}
nsresult MediaDecoderReader::ResetDecode()
{
VideoQueue().Reset();
AudioQueue().Reset();
mAudioDiscontinuity = true;
mVideoDiscontinuity = true;
mBaseAudioPromise.RejectIfExists(CANCELED, __func__);
mBaseVideoPromise.RejectIfExists(CANCELED, __func__);
return NS_OK;
}
VideoData* MediaDecoderReader::DecodeToFirstVideoData()
{
bool eof = false;
while (!eof && VideoQueue().GetSize() == 0) {
{
ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
if (mDecoder->IsShutdown()) {
return nullptr;
}
}
bool keyframeSkip = false;
eof = !DecodeVideoFrame(keyframeSkip, 0);
}
if (eof) {
VideoQueue().Finish();
}
VideoData* d = nullptr;
return (d = VideoQueue().PeekFront()) ? d : nullptr;
}
void
MediaDecoderReader::SetStartTime(int64_t aStartTime)
{
mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
MOZ_ASSERT(mStartTime == -1);
mStartTime = aStartTime;
}
media::TimeIntervals
MediaDecoderReader::GetBuffered()
{
NS_ENSURE_TRUE(mStartTime >= 0, media::TimeIntervals());
AutoPinned<MediaResource> stream(mDecoder->GetResource());
if (!mDuration.Ref().isSome()) {
return TimeIntervals();
}
return GetEstimatedBufferedTimeRanges(stream, mDuration.Ref().ref().ToMicroseconds());
}
nsRefPtr<MediaDecoderReader::MetadataPromise>
MediaDecoderReader::AsyncReadMetadata()
{
typedef ReadMetadataFailureReason Reason;
MOZ_ASSERT(OnTaskQueue());
mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
DECODER_LOG("MediaDecoderReader::AsyncReadMetadata");
if (IsWaitingMediaResources()) {
return MetadataPromise::CreateAndReject(Reason::WAITING_FOR_RESOURCES, __func__);
}
// Attempt to read the metadata.
nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
nsresult rv = ReadMetadata(&metadata->mInfo, getter_Transfers(metadata->mTags));
// Reading metadata can cause us to discover that we need resources (a hardware
// resource initialized but not yet ready for use).
if (IsWaitingMediaResources()) {
return MetadataPromise::CreateAndReject(Reason::WAITING_FOR_RESOURCES, __func__);
}
// We're not waiting for anything. If we didn't get the metadata, that's an error.
if (NS_FAILED(rv) || !metadata->mInfo.HasValidMedia()) {
DECODER_WARN("ReadMetadata failed, rv=%x HasValidMedia=%d", rv, metadata->mInfo.HasValidMedia());
return MetadataPromise::CreateAndReject(Reason::METADATA_ERROR, __func__);
}
// Success!
return MetadataPromise::CreateAndResolve(metadata, __func__);
}
class ReRequestVideoWithSkipTask : public nsRunnable
{
public:
ReRequestVideoWithSkipTask(MediaDecoderReader* aReader,
int64_t aTimeThreshold)
: mReader(aReader)
, mTimeThreshold(aTimeThreshold)
{
}
NS_METHOD Run()
{
MOZ_ASSERT(mReader->OnTaskQueue());
// Make sure ResetDecode hasn't been called in the mean time.
if (!mReader->mBaseVideoPromise.IsEmpty()) {
mReader->RequestVideoData(/* aSkip = */ true, mTimeThreshold);
}
return NS_OK;
}
private:
nsRefPtr<MediaDecoderReader> mReader;
const int64_t mTimeThreshold;
};
class ReRequestAudioTask : public nsRunnable
{
public:
explicit ReRequestAudioTask(MediaDecoderReader* aReader)
: mReader(aReader)
{
}
NS_METHOD Run()
{
MOZ_ASSERT(mReader->OnTaskQueue());
// Make sure ResetDecode hasn't been called in the mean time.
if (!mReader->mBaseAudioPromise.IsEmpty()) {
mReader->RequestAudioData();
}
return NS_OK;
}
private:
nsRefPtr<MediaDecoderReader> mReader;
};
nsRefPtr<MediaDecoderReader::VideoDataPromise>
MediaDecoderReader::RequestVideoData(bool aSkipToNextKeyframe,
int64_t aTimeThreshold)
{
nsRefPtr<VideoDataPromise> p = mBaseVideoPromise.Ensure(__func__);
bool skip = aSkipToNextKeyframe;
while (VideoQueue().GetSize() == 0 &&
!VideoQueue().IsFinished()) {
if (!DecodeVideoFrame(skip, aTimeThreshold)) {
VideoQueue().Finish();
} else if (skip) {
// We still need to decode more data in order to skip to the next
// keyframe. Post another task to the decode task queue to decode
// again. We don't just decode straight in a loop here, as that
// would hog the decode task queue.
RefPtr<nsIRunnable> task(new ReRequestVideoWithSkipTask(this, aTimeThreshold));
mTaskQueue->Dispatch(task.forget());
return p;
}
}
if (VideoQueue().GetSize() > 0) {
nsRefPtr<VideoData> v = VideoQueue().PopFront();
if (v && mVideoDiscontinuity) {
v->mDiscontinuity = true;
mVideoDiscontinuity = false;
}
mBaseVideoPromise.Resolve(v, __func__);
} else if (VideoQueue().IsFinished()) {
mBaseVideoPromise.Reject(END_OF_STREAM, __func__);
} else {
MOZ_ASSERT(false, "Dropping this promise on the floor");
}
return p;
}
nsRefPtr<MediaDecoderReader::AudioDataPromise>
MediaDecoderReader::RequestAudioData()
{
nsRefPtr<AudioDataPromise> p = mBaseAudioPromise.Ensure(__func__);
while (AudioQueue().GetSize() == 0 &&
!AudioQueue().IsFinished()) {
if (!DecodeAudioData()) {
AudioQueue().Finish();
break;
}
// AudioQueue size is still zero, post a task to try again.
// (|mVideoSinkBufferCount| > 0)
if (AudioQueue().GetSize() == 0 && mTaskQueue) {
RefPtr<nsIRunnable> task(new ReRequestAudioTask(this));
mTaskQueue->Dispatch(task.forget());
return p;
}
}
if (AudioQueue().GetSize() > 0) {
nsRefPtr<AudioData> a = AudioQueue().PopFront();
if (mAudioDiscontinuity) {
a->mDiscontinuity = true;
mAudioDiscontinuity = false;
}
mBaseAudioPromise.Resolve(a, __func__);
} else if (AudioQueue().IsFinished()) {
mBaseAudioPromise.Reject(mHitAudioDecodeError ? DECODE_ERROR : END_OF_STREAM, __func__);
mHitAudioDecodeError = false;
} else {
MOZ_ASSERT(false, "Dropping this promise on the floor");
}
return p;
}
void
MediaDecoderReader::BreakCycles()
{
// Nothing left to do here these days. We keep this method around so that, if
// we need it, we don't have to make all of the subclass implementations call
// the superclass method again.
}
nsRefPtr<ShutdownPromise>
MediaDecoderReader::Shutdown()
{
MOZ_ASSERT(OnTaskQueue());
mShutdown = true;
mBaseAudioPromise.RejectIfExists(END_OF_STREAM, __func__);
mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__);
ReleaseMediaResources();
mDuration.DisconnectIfConnected();
nsRefPtr<ShutdownPromise> p;
// Spin down the task queue if necessary. We wait until BreakCycles to null
// out mTaskQueue, since otherwise any remaining tasks could crash when they
// invoke OnTaskQueue().
if (mTaskQueue && !mTaskQueueIsBorrowed) {
// If we own our task queue, shutdown ends when the task queue is done.
p = mTaskQueue->BeginShutdown();
} else {
// If we don't own our task queue, we resolve immediately (though
// asynchronously).
p = ShutdownPromise::CreateAndResolve(true, __func__);
}
mDecoder = nullptr;
return p;
}
} // namespace mozilla
#undef DECODER_LOG
#undef DECODER_WARN
#undef DECODER_WARN_HELPER