зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1319992: P1. Run demuxing operations on its own task queue. r=jwwang
We runs all demuxing operations on a dedicated task queue. MediaDataDemuxer's members using a synchronous API are handled via thread-safe copy that are updated along the operations. The buffered range calculation is now handled separately and the entire operation is made asynchronous. MozReview-Commit-ID: Gd4DCC8Ix6n --HG-- extra : rebase_source : 6a18ce2552bf4cbf88e9b8db1c9a87e70623fd15
This commit is contained in:
Родитель
e566e8864d
Коммит
c5eae5a4c0
|
@ -194,11 +194,10 @@ public:
|
||||||
virtual size_t SizeOfVideoQueueInFrames();
|
virtual size_t SizeOfVideoQueueInFrames();
|
||||||
virtual size_t SizeOfAudioQueueInFrames();
|
virtual size_t SizeOfAudioQueueInFrames();
|
||||||
|
|
||||||
void NotifyDataArrived()
|
virtual void NotifyDataArrived()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
NS_ENSURE_TRUE_VOID(!mShutdown);
|
NS_ENSURE_TRUE_VOID(!mShutdown);
|
||||||
NotifyDataArrivedInternal();
|
|
||||||
UpdateBuffered();
|
UpdateBuffered();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,24 +253,6 @@ protected:
|
||||||
// Recomputes mBuffered.
|
// Recomputes mBuffered.
|
||||||
virtual void UpdateBuffered();
|
virtual void UpdateBuffered();
|
||||||
|
|
||||||
// Populates aBuffered with the time ranges which are buffered. This may only
|
|
||||||
// be called on the decode task queue, and should only be used internally by
|
|
||||||
// UpdateBuffered - mBuffered (or mirrors of it) should be used for everything
|
|
||||||
// else.
|
|
||||||
//
|
|
||||||
// This base implementation in MediaDecoderReader estimates the time ranges
|
|
||||||
// buffered by interpolating the cached byte ranges with the duration
|
|
||||||
// of the media. Reader subclasses should override this method if they
|
|
||||||
// can quickly calculate the buffered ranges more accurately.
|
|
||||||
//
|
|
||||||
// The primary advantage of this implementation in the reader base class
|
|
||||||
// is that it's a fast approximation, which does not perform any I/O.
|
|
||||||
//
|
|
||||||
// The OggReader relies on this base implementation not performing I/O,
|
|
||||||
// since in FirefoxOS we can't do I/O on the main thread, where this is
|
|
||||||
// called.
|
|
||||||
virtual media::TimeIntervals GetBuffered();
|
|
||||||
|
|
||||||
RefPtr<MediaDataPromise> DecodeToFirstVideoData();
|
RefPtr<MediaDataPromise> DecodeToFirstVideoData();
|
||||||
|
|
||||||
// Queue of audio frames. This queue is threadsafe, and is accessed from
|
// Queue of audio frames. This queue is threadsafe, and is accessed from
|
||||||
|
@ -344,8 +325,6 @@ private:
|
||||||
|
|
||||||
virtual void VisibilityChanged();
|
virtual void VisibilityChanged();
|
||||||
|
|
||||||
virtual void NotifyDataArrivedInternal() {}
|
|
||||||
|
|
||||||
// Overrides of this function should decodes an unspecified amount of
|
// Overrides of this function should decodes an unspecified amount of
|
||||||
// audio data, enqueuing the audio data in mAudioQueue. Returns true
|
// audio data, enqueuing the audio data in mAudioQueue. Returns true
|
||||||
// when there's more audio to decode, false if the audio is finished,
|
// when there's more audio to decode, false if the audio is finished,
|
||||||
|
@ -366,6 +345,24 @@ private:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populates aBuffered with the time ranges which are buffered. This may only
|
||||||
|
// be called on the decode task queue, and should only be used internally by
|
||||||
|
// UpdateBuffered - mBuffered (or mirrors of it) should be used for everything
|
||||||
|
// else.
|
||||||
|
//
|
||||||
|
// This base implementation in MediaDecoderReader estimates the time ranges
|
||||||
|
// buffered by interpolating the cached byte ranges with the duration
|
||||||
|
// of the media. Reader subclasses should override this method if they
|
||||||
|
// can quickly calculate the buffered ranges more accurately.
|
||||||
|
//
|
||||||
|
// The primary advantage of this implementation in the reader base class
|
||||||
|
// is that it's a fast approximation, which does not perform any I/O.
|
||||||
|
//
|
||||||
|
// The OggReader relies on this base implementation not performing I/O,
|
||||||
|
// since in FirefoxOS we can't do I/O on the main thread, where this is
|
||||||
|
// called.
|
||||||
|
media::TimeIntervals GetBuffered();
|
||||||
|
|
||||||
// Promises used only for the base-class (sync->async adapter) implementation
|
// Promises used only for the base-class (sync->async adapter) implementation
|
||||||
// of Request{Audio,Video}Data.
|
// of Request{Audio,Video}Data.
|
||||||
MozPromiseHolder<MediaDataPromise> mBaseAudioPromise;
|
MozPromiseHolder<MediaDataPromise> mBaseAudioPromise;
|
||||||
|
|
|
@ -4,24 +4,27 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/CDMProxy.h"
|
#include "AutoTaskQueue.h"
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
|
||||||
#include "mozilla/dom/HTMLMediaElement.h"
|
|
||||||
#include "mozilla/Preferences.h"
|
|
||||||
#include "mozilla/Telemetry.h"
|
|
||||||
#include "nsContentUtils.h"
|
|
||||||
#include "nsPrintfCString.h"
|
|
||||||
#include "nsSize.h"
|
|
||||||
#include "Layers.h"
|
#include "Layers.h"
|
||||||
#include "MediaData.h"
|
#include "MediaData.h"
|
||||||
#include "MediaInfo.h"
|
#include "MediaInfo.h"
|
||||||
#include "MediaFormatReader.h"
|
#include "MediaFormatReader.h"
|
||||||
#include "MediaPrefs.h"
|
#include "MediaPrefs.h"
|
||||||
#include "MediaResource.h"
|
#include "MediaResource.h"
|
||||||
#include "mozilla/SharedThreadPool.h"
|
|
||||||
#include "VideoUtils.h"
|
#include "VideoUtils.h"
|
||||||
#include "VideoFrameContainer.h"
|
#include "VideoFrameContainer.h"
|
||||||
|
#include "mozilla/dom/HTMLMediaElement.h"
|
||||||
#include "mozilla/layers/ShadowLayers.h"
|
#include "mozilla/layers/ShadowLayers.h"
|
||||||
|
#include "mozilla/CDMProxy.h"
|
||||||
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
|
#include "mozilla/Preferences.h"
|
||||||
|
#include "mozilla/Telemetry.h"
|
||||||
|
#include "mozilla/Mutex.h"
|
||||||
|
#include "mozilla/SharedThreadPool.h"
|
||||||
|
#include "mozilla/SyncRunnable.h"
|
||||||
|
#include "nsContentUtils.h"
|
||||||
|
#include "nsPrintfCString.h"
|
||||||
|
#include "nsSize.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
@ -427,6 +430,339 @@ MediaFormatReader::DecoderFactory::DoInitDecoder(TrackType aTrack)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DemuxerProxy ensures that the original main demuxer is only ever accessed
|
||||||
|
// via its own dedicated task queue.
|
||||||
|
// This ensure that the reader's taskqueue will never blocked while a demuxer
|
||||||
|
// is itself blocked attempting to access the MediaCache or the MediaResource.
|
||||||
|
class MediaFormatReader::DemuxerProxy
|
||||||
|
{
|
||||||
|
using TrackType = TrackInfo::TrackType;
|
||||||
|
class Wrapper;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DemuxerProxy(MediaDataDemuxer* aDemuxer)
|
||||||
|
: mTaskQueue(new AutoTaskQueue(
|
||||||
|
GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER)))
|
||||||
|
, mData(new Data(aDemuxer))
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(DemuxerProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
~DemuxerProxy()
|
||||||
|
{
|
||||||
|
MOZ_COUNT_DTOR(DemuxerProxy);
|
||||||
|
mData->mAudioDemuxer = nullptr;
|
||||||
|
mData->mVideoDemuxer = nullptr;
|
||||||
|
RefPtr<Data> data = mData.forget();
|
||||||
|
mTaskQueue->Dispatch(
|
||||||
|
// We need to clear our reference to the demuxer now. So that in the event
|
||||||
|
// the init promise wasn't resolved, such as what can happen with the
|
||||||
|
// mediasource demuxer that is waiting on more data, it will force the
|
||||||
|
// init promise to be rejected.
|
||||||
|
NS_NewRunnableFunction([data]() { data->mDemuxer = nullptr; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<MediaDataDemuxer::InitPromise> Init();
|
||||||
|
|
||||||
|
Wrapper*
|
||||||
|
GetTrackDemuxer(TrackType aTrack, uint32_t aTrackNumber)
|
||||||
|
{
|
||||||
|
MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
|
||||||
|
|
||||||
|
switch (aTrack) {
|
||||||
|
case TrackInfo::kAudioTrack:
|
||||||
|
return mData->mAudioDemuxer;
|
||||||
|
case TrackInfo::kVideoTrack:
|
||||||
|
return mData->mVideoDemuxer;
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetNumberTracks(TrackType aTrack) const
|
||||||
|
{
|
||||||
|
MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
|
||||||
|
|
||||||
|
switch (aTrack) {
|
||||||
|
case TrackInfo::kAudioTrack:
|
||||||
|
return mData->mNumAudioTrack;
|
||||||
|
case TrackInfo::kVideoTrack:
|
||||||
|
return mData->mNumVideoTrack;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSeekable() const
|
||||||
|
{
|
||||||
|
MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
|
||||||
|
|
||||||
|
return mData->mSeekable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSeekableOnlyInBufferedRanges() const
|
||||||
|
{
|
||||||
|
MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
|
||||||
|
|
||||||
|
return mData->mSeekableOnlyInBufferedRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniquePtr<EncryptionInfo> GetCrypto() const
|
||||||
|
{
|
||||||
|
MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
|
||||||
|
|
||||||
|
if (!mData->mCrypto) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto crypto = MakeUnique<EncryptionInfo>();
|
||||||
|
*crypto = *mData->mCrypto;
|
||||||
|
return crypto;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<NotifyDataArrivedPromise> NotifyDataArrived();
|
||||||
|
|
||||||
|
bool ShouldComputeStartTime() const
|
||||||
|
{
|
||||||
|
MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
|
||||||
|
|
||||||
|
return mData->mShouldComputeStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const RefPtr<AutoTaskQueue> mTaskQueue;
|
||||||
|
struct Data
|
||||||
|
{
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Data)
|
||||||
|
|
||||||
|
explicit Data(MediaDataDemuxer* aDemuxer)
|
||||||
|
: mInitDone(false)
|
||||||
|
, mDemuxer(aDemuxer)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Atomic<bool> mInitDone;
|
||||||
|
// Only ever accessed over mTaskQueue once.
|
||||||
|
RefPtr<MediaDataDemuxer> mDemuxer;
|
||||||
|
// Only accessed once InitPromise has been resolved and immutable after.
|
||||||
|
// So we can safely access them without the use of the mutex.
|
||||||
|
uint32_t mNumAudioTrack = 0;
|
||||||
|
RefPtr<Wrapper> mAudioDemuxer;
|
||||||
|
uint32_t mNumVideoTrack = 0;
|
||||||
|
RefPtr<Wrapper> mVideoDemuxer;
|
||||||
|
bool mSeekable = false;
|
||||||
|
bool mSeekableOnlyInBufferedRange = false;
|
||||||
|
bool mShouldComputeStartTime = true;
|
||||||
|
UniquePtr<EncryptionInfo> mCrypto;
|
||||||
|
private:
|
||||||
|
~Data() { }
|
||||||
|
};
|
||||||
|
RefPtr<Data> mData;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaFormatReader::DemuxerProxy::Wrapper : public MediaTrackDemuxer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Wrapper(MediaTrackDemuxer* aTrackDemuxer, AutoTaskQueue* aTaskQueue)
|
||||||
|
: mMutex("TrackDemuxer Mutex")
|
||||||
|
, mTaskQueue(aTaskQueue)
|
||||||
|
, mGetSamplesMayBlock(aTrackDemuxer->GetSamplesMayBlock())
|
||||||
|
, mInfo(aTrackDemuxer->GetInfo())
|
||||||
|
, mTrackDemuxer(aTrackDemuxer)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
UniquePtr<TrackInfo> GetInfo() const override
|
||||||
|
{
|
||||||
|
if (!mInfo) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return mInfo->Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<SeekPromise> Seek(const media::TimeUnit& aTime) override
|
||||||
|
{
|
||||||
|
RefPtr<Wrapper> self = this;
|
||||||
|
return InvokeAsync(
|
||||||
|
mTaskQueue, __func__,
|
||||||
|
[self, aTime]() { return self->mTrackDemuxer->Seek(aTime); })
|
||||||
|
->Then(mTaskQueue, __func__,
|
||||||
|
[self]() { self->UpdateRandomAccessPoint(); },
|
||||||
|
[self]() { self->UpdateRandomAccessPoint(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples) override
|
||||||
|
{
|
||||||
|
RefPtr<Wrapper> self = this;
|
||||||
|
return InvokeAsync(mTaskQueue, __func__,
|
||||||
|
[self, aNumSamples]() {
|
||||||
|
return self->mTrackDemuxer->GetSamples(aNumSamples);
|
||||||
|
})
|
||||||
|
->Then(mTaskQueue, __func__,
|
||||||
|
[self]() { self->UpdateRandomAccessPoint(); },
|
||||||
|
[self]() { self->UpdateRandomAccessPoint(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetSamplesMayBlock() const override
|
||||||
|
{
|
||||||
|
return mGetSamplesMayBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() override
|
||||||
|
{
|
||||||
|
RefPtr<Wrapper> self = this;
|
||||||
|
mTaskQueue->Dispatch(NS_NewRunnableFunction([self]() {
|
||||||
|
self->mTrackDemuxer->Reset();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult GetNextRandomAccessPoint(TimeUnit* aTime) override
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
if (NS_SUCCEEDED(mNextRandomAccessPointResult)) {
|
||||||
|
*aTime = mNextRandomAccessPoint;
|
||||||
|
}
|
||||||
|
return mNextRandomAccessPointResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<SkipAccessPointPromise>
|
||||||
|
SkipToNextRandomAccessPoint(const media::TimeUnit& aTimeThreshold) override
|
||||||
|
{
|
||||||
|
RefPtr<Wrapper> self = this;
|
||||||
|
return InvokeAsync(
|
||||||
|
mTaskQueue, __func__,
|
||||||
|
[self, aTimeThreshold]() {
|
||||||
|
return self->mTrackDemuxer->SkipToNextRandomAccessPoint(
|
||||||
|
aTimeThreshold);
|
||||||
|
})
|
||||||
|
->Then(mTaskQueue, __func__,
|
||||||
|
[self]() { self->UpdateRandomAccessPoint(); },
|
||||||
|
[self]() { self->UpdateRandomAccessPoint(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeIntervals GetBuffered() override
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
return mBuffered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakCycles() override { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex mMutex;
|
||||||
|
const RefPtr<AutoTaskQueue> mTaskQueue;
|
||||||
|
const bool mGetSamplesMayBlock;
|
||||||
|
const UniquePtr<TrackInfo> mInfo;
|
||||||
|
// mTrackDemuxer is only ever accessed on demuxer's task queue.
|
||||||
|
RefPtr<MediaTrackDemuxer> mTrackDemuxer;
|
||||||
|
// All following members are protected by mMutex
|
||||||
|
nsresult mNextRandomAccessPointResult = NS_OK;
|
||||||
|
TimeUnit mNextRandomAccessPoint;
|
||||||
|
TimeIntervals mBuffered;
|
||||||
|
friend class DemuxerProxy;
|
||||||
|
|
||||||
|
~Wrapper()
|
||||||
|
{
|
||||||
|
RefPtr<MediaTrackDemuxer> trackDemuxer = mTrackDemuxer.forget();
|
||||||
|
mTaskQueue->Dispatch(NS_NewRunnableFunction(
|
||||||
|
[trackDemuxer]() { trackDemuxer->BreakCycles(); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateRandomAccessPoint()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||||
|
if (!mTrackDemuxer) {
|
||||||
|
// Detached.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
mNextRandomAccessPointResult =
|
||||||
|
mTrackDemuxer->GetNextRandomAccessPoint(&mNextRandomAccessPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateBuffered()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
|
||||||
|
if (!mTrackDemuxer) {
|
||||||
|
// Detached.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
mBuffered = mTrackDemuxer->GetBuffered();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RefPtr<MediaDataDemuxer::InitPromise>
|
||||||
|
MediaFormatReader::DemuxerProxy::Init()
|
||||||
|
{
|
||||||
|
RefPtr<Data> data = mData;
|
||||||
|
RefPtr<AutoTaskQueue> taskQueue = mTaskQueue;
|
||||||
|
return InvokeAsync(mTaskQueue, __func__,
|
||||||
|
[data, taskQueue]() {
|
||||||
|
if (!data->mDemuxer) {
|
||||||
|
return MediaDataDemuxer::InitPromise::CreateAndReject(
|
||||||
|
NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
||||||
|
}
|
||||||
|
return data->mDemuxer->Init();
|
||||||
|
})
|
||||||
|
->Then(taskQueue, __func__,
|
||||||
|
[data, taskQueue]() {
|
||||||
|
if (!data->mDemuxer) { // Was shutdown.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data->mNumAudioTrack =
|
||||||
|
data->mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
|
||||||
|
if (data->mNumAudioTrack) {
|
||||||
|
RefPtr<MediaTrackDemuxer> d =
|
||||||
|
data->mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
|
||||||
|
if (d) {
|
||||||
|
RefPtr<Wrapper> wrapper =
|
||||||
|
new DemuxerProxy::Wrapper(d, taskQueue);
|
||||||
|
wrapper->UpdateBuffered();
|
||||||
|
data->mAudioDemuxer = wrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data->mNumVideoTrack =
|
||||||
|
data->mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
|
||||||
|
if (data->mNumVideoTrack) {
|
||||||
|
RefPtr<MediaTrackDemuxer> d =
|
||||||
|
data->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
|
||||||
|
if (d) {
|
||||||
|
RefPtr<Wrapper> wrapper =
|
||||||
|
new DemuxerProxy::Wrapper(d, taskQueue);
|
||||||
|
wrapper->UpdateBuffered();
|
||||||
|
data->mVideoDemuxer = wrapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data->mCrypto = data->mDemuxer->GetCrypto();
|
||||||
|
data->mSeekable = data->mDemuxer->IsSeekable();
|
||||||
|
data->mSeekableOnlyInBufferedRange =
|
||||||
|
data->mDemuxer->IsSeekableOnlyInBufferedRanges();
|
||||||
|
data->mShouldComputeStartTime =
|
||||||
|
data->mDemuxer->ShouldComputeStartTime();
|
||||||
|
data->mInitDone = true;
|
||||||
|
},
|
||||||
|
[]() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<MediaFormatReader::NotifyDataArrivedPromise>
|
||||||
|
MediaFormatReader::DemuxerProxy::NotifyDataArrived()
|
||||||
|
{
|
||||||
|
RefPtr<Data> data = mData;
|
||||||
|
return InvokeAsync(mTaskQueue, __func__, [data]() {
|
||||||
|
if (!data->mDemuxer) {
|
||||||
|
// Was shutdown.
|
||||||
|
return NotifyDataArrivedPromise::CreateAndReject(
|
||||||
|
NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
||||||
|
}
|
||||||
|
data->mDemuxer->NotifyDataArrived();
|
||||||
|
if (data->mAudioDemuxer) {
|
||||||
|
data->mAudioDemuxer->UpdateBuffered();
|
||||||
|
}
|
||||||
|
if (data->mVideoDemuxer) {
|
||||||
|
data->mVideoDemuxer->UpdateBuffered();
|
||||||
|
}
|
||||||
|
return NotifyDataArrivedPromise::CreateAndResolve(true, __func__);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static const char*
|
static const char*
|
||||||
TrackTypeToStr(TrackInfo::TrackType aTrack)
|
TrackTypeToStr(TrackInfo::TrackType aTrack)
|
||||||
{
|
{
|
||||||
|
@ -453,7 +789,7 @@ MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
||||||
Preferences::GetUint("media.audio-max-decode-error", 3))
|
Preferences::GetUint("media.audio-max-decode-error", 3))
|
||||||
, mVideo(this, MediaData::VIDEO_DATA,
|
, mVideo(this, MediaData::VIDEO_DATA,
|
||||||
Preferences::GetUint("media.video-max-decode-error", 2))
|
Preferences::GetUint("media.video-max-decode-error", 2))
|
||||||
, mDemuxer(aDemuxer)
|
, mDemuxer(new DemuxerProxy(aDemuxer))
|
||||||
, mDemuxerInitDone(false)
|
, mDemuxerInitDone(false)
|
||||||
, mLastReportedNumDecodedFrames(0)
|
, mLastReportedNumDecodedFrames(0)
|
||||||
, mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
|
, mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
|
||||||
|
@ -689,7 +1025,6 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
|
||||||
}
|
}
|
||||||
mVideo.mOriginalInfo = Move(videoInfo);
|
mVideo.mOriginalInfo = Move(videoInfo);
|
||||||
mVideo.mCallback = new DecoderCallback(this, TrackInfo::kVideoTrack);
|
mVideo.mCallback = new DecoderCallback(this, TrackInfo::kVideoTrack);
|
||||||
mVideo.mTimeRanges = mVideo.mTrackDemuxer->GetBuffered();
|
|
||||||
mTrackDemuxersMayBlock |= mVideo.mTrackDemuxer->GetSamplesMayBlock();
|
mTrackDemuxersMayBlock |= mVideo.mTrackDemuxer->GetSamplesMayBlock();
|
||||||
} else {
|
} else {
|
||||||
mVideo.mTrackDemuxer->BreakCycles();
|
mVideo.mTrackDemuxer->BreakCycles();
|
||||||
|
@ -718,7 +1053,6 @@ MediaFormatReader::OnDemuxerInitDone(nsresult)
|
||||||
}
|
}
|
||||||
mAudio.mOriginalInfo = Move(audioInfo);
|
mAudio.mOriginalInfo = Move(audioInfo);
|
||||||
mAudio.mCallback = new DecoderCallback(this, TrackInfo::kAudioTrack);
|
mAudio.mCallback = new DecoderCallback(this, TrackInfo::kAudioTrack);
|
||||||
mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered();
|
|
||||||
mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock();
|
mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock();
|
||||||
} else {
|
} else {
|
||||||
mAudio.mTrackDemuxer->BreakCycles();
|
mAudio.mTrackDemuxer->BreakCycles();
|
||||||
|
@ -794,12 +1128,15 @@ MediaFormatReader::MaybeResolveMetadataPromise()
|
||||||
mInfo.mStartTime = startTime; // mInfo.mStartTime is initialized to 0.
|
mInfo.mStartTime = startTime; // mInfo.mStartTime is initialized to 0.
|
||||||
}
|
}
|
||||||
|
|
||||||
mHasStartTime = true;
|
|
||||||
UpdateBuffered();
|
|
||||||
|
|
||||||
RefPtr<MetadataHolder> metadata = new MetadataHolder();
|
RefPtr<MetadataHolder> metadata = new MetadataHolder();
|
||||||
metadata->mInfo = mInfo;
|
metadata->mInfo = mInfo;
|
||||||
metadata->mTags = mTags->Count() ? mTags.release() : nullptr;
|
metadata->mTags = mTags->Count() ? mTags.release() : nullptr;
|
||||||
|
|
||||||
|
// We now have all the informations required to calculate the initial buffered
|
||||||
|
// range.
|
||||||
|
mHasStartTime = true;
|
||||||
|
UpdateBuffered();
|
||||||
|
|
||||||
mMetadataPromise.Resolve(metadata, __func__);
|
mMetadataPromise.Resolve(metadata, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1155,9 +1492,6 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our cached TimeRange.
|
|
||||||
decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
|
|
||||||
|
|
||||||
// We do not want to clear mWaitingForData while there are pending
|
// We do not want to clear mWaitingForData while there are pending
|
||||||
// demuxing or seeking operations that could affect the value of this flag.
|
// demuxing or seeking operations that could affect the value of this flag.
|
||||||
// This is in order to ensure that we will retry once they complete as we may
|
// This is in order to ensure that we will retry once they complete as we may
|
||||||
|
@ -1185,16 +1519,6 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasLastEnd;
|
|
||||||
media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
|
|
||||||
if (hasLastEnd) {
|
|
||||||
if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() < lastEnd) {
|
|
||||||
// New data was added after our previous end, we can clear the EOS flag.
|
|
||||||
decoder.mDemuxEOS = false;
|
|
||||||
}
|
|
||||||
decoder.mLastTimeRangesEnd = Some(lastEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder.mReceivedNewData = false;
|
decoder.mReceivedNewData = false;
|
||||||
if (decoder.mTimeThreshold) {
|
if (decoder.mTimeThreshold) {
|
||||||
decoder.mTimeThreshold.ref().mWaiting = false;
|
decoder.mTimeThreshold.ref().mWaiting = false;
|
||||||
|
@ -2186,47 +2510,6 @@ MediaFormatReader::OnAudioSeekFailed(const MediaResult& aError)
|
||||||
OnSeekFailed(TrackType::kAudioTrack, aError);
|
OnSeekFailed(TrackType::kAudioTrack, aError);
|
||||||
}
|
}
|
||||||
|
|
||||||
media::TimeIntervals
|
|
||||||
MediaFormatReader::GetBuffered()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
|
||||||
media::TimeIntervals videoti;
|
|
||||||
media::TimeIntervals audioti;
|
|
||||||
media::TimeIntervals intervals;
|
|
||||||
|
|
||||||
if (!mInitDone || !mHasStartTime) {
|
|
||||||
return intervals;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we have up to date buffered time range.
|
|
||||||
if (HasVideo()) {
|
|
||||||
UpdateReceivedNewData(TrackType::kVideoTrack);
|
|
||||||
}
|
|
||||||
if (HasAudio()) {
|
|
||||||
UpdateReceivedNewData(TrackType::kAudioTrack);
|
|
||||||
}
|
|
||||||
if (HasVideo()) {
|
|
||||||
videoti = mVideo.mTimeRanges;
|
|
||||||
}
|
|
||||||
if (HasAudio()) {
|
|
||||||
audioti = mAudio.mTimeRanges;
|
|
||||||
}
|
|
||||||
if (HasAudio() && HasVideo()) {
|
|
||||||
intervals = media::Intersection(Move(videoti), Move(audioti));
|
|
||||||
} else if (HasAudio()) {
|
|
||||||
intervals = Move(audioti);
|
|
||||||
} else if (HasVideo()) {
|
|
||||||
intervals = Move(videoti);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!intervals.Length() ||
|
|
||||||
intervals.GetStart() == media::TimeUnit::FromMicroseconds(0)) {
|
|
||||||
// IntervalSet already starts at 0 or is empty, nothing to shift.
|
|
||||||
return intervals;
|
|
||||||
}
|
|
||||||
return intervals.Shift(media::TimeUnit() - mInfo.mStartTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaFormatReader::ReleaseResources()
|
void MediaFormatReader::ReleaseResources()
|
||||||
{
|
{
|
||||||
mVideo.ShutdownDecoder();
|
mVideo.ShutdownDecoder();
|
||||||
|
@ -2240,22 +2523,16 @@ MediaFormatReader::VideoIsHardwareAccelerated() const
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaFormatReader::NotifyDemuxer()
|
MediaFormatReader::NotifyTrackDemuxers()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
|
|
||||||
if (mShutdown || !mDemuxer ||
|
|
||||||
(!mDemuxerInitDone && !mDemuxerInitRequest.Exists())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGV("");
|
LOGV("");
|
||||||
|
|
||||||
mDemuxer->NotifyDataArrived();
|
|
||||||
|
|
||||||
if (!mInitDone) {
|
if (!mInitDone) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasVideo()) {
|
if (HasVideo()) {
|
||||||
mVideo.mReceivedNewData = true;
|
mVideo.mReceivedNewData = true;
|
||||||
ScheduleUpdate(TrackType::kVideoTrack);
|
ScheduleUpdate(TrackType::kVideoTrack);
|
||||||
|
@ -2267,10 +2544,85 @@ MediaFormatReader::NotifyDemuxer()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MediaFormatReader::NotifyDataArrivedInternal()
|
MediaFormatReader::NotifyDataArrived()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(OnTaskQueue());
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
NotifyDemuxer();
|
|
||||||
|
if (mShutdown || !mDemuxer ||
|
||||||
|
(!mDemuxerInitDone && !mDemuxerInitRequest.Exists())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<MediaFormatReader> self = this;
|
||||||
|
mDemuxer->NotifyDataArrived()->Then(
|
||||||
|
OwnerThread(), __func__,
|
||||||
|
[self]() {
|
||||||
|
self->UpdateBuffered();
|
||||||
|
self->NotifyTrackDemuxers();
|
||||||
|
},
|
||||||
|
[]() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MediaFormatReader::UpdateBuffered()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(OnTaskQueue());
|
||||||
|
|
||||||
|
if (mShutdown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mInitDone || !mHasStartTime) {
|
||||||
|
mBuffered = TimeIntervals();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasVideo()) {
|
||||||
|
mVideo.mTimeRanges = mVideo.mTrackDemuxer->GetBuffered();
|
||||||
|
bool hasLastEnd;
|
||||||
|
media::TimeUnit lastEnd = mVideo.mTimeRanges.GetEnd(&hasLastEnd);
|
||||||
|
if (hasLastEnd) {
|
||||||
|
if (mVideo.mLastTimeRangesEnd
|
||||||
|
&& mVideo.mLastTimeRangesEnd.ref() < lastEnd) {
|
||||||
|
// New data was added after our previous end, we can clear the EOS flag.
|
||||||
|
mVideo.mDemuxEOS = false;
|
||||||
|
ScheduleUpdate(TrackInfo::kVideoTrack);
|
||||||
|
}
|
||||||
|
mVideo.mLastTimeRangesEnd = Some(lastEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HasAudio()) {
|
||||||
|
mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered();
|
||||||
|
bool hasLastEnd;
|
||||||
|
media::TimeUnit lastEnd = mAudio.mTimeRanges.GetEnd(&hasLastEnd);
|
||||||
|
if (hasLastEnd) {
|
||||||
|
if (mAudio.mLastTimeRangesEnd
|
||||||
|
&& mAudio.mLastTimeRangesEnd.ref() < lastEnd) {
|
||||||
|
// New data was added after our previous end, we can clear the EOS flag.
|
||||||
|
mAudio.mDemuxEOS = false;
|
||||||
|
ScheduleUpdate(TrackInfo::kAudioTrack);
|
||||||
|
}
|
||||||
|
mAudio.mLastTimeRangesEnd = Some(lastEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
media::TimeIntervals intervals;
|
||||||
|
if (HasAudio() && HasVideo()) {
|
||||||
|
intervals = media::Intersection(mVideo.mTimeRanges, mAudio.mTimeRanges);
|
||||||
|
} else if (HasAudio()) {
|
||||||
|
intervals = mAudio.mTimeRanges;
|
||||||
|
} else if (HasVideo()) {
|
||||||
|
intervals = mVideo.mTimeRanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!intervals.Length() ||
|
||||||
|
intervals.GetStart() == media::TimeUnit::FromMicroseconds(0)) {
|
||||||
|
// IntervalSet already starts at 0 or is empty, nothing to shift.
|
||||||
|
mBuffered = intervals;
|
||||||
|
} else {
|
||||||
|
mBuffered =
|
||||||
|
intervals.Shift(media::TimeUnit() - mInfo.mStartTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -25,6 +25,7 @@ class CDMProxy;
|
||||||
class MediaFormatReader final : public MediaDecoderReader
|
class MediaFormatReader final : public MediaDecoderReader
|
||||||
{
|
{
|
||||||
typedef TrackInfo::TrackType TrackType;
|
typedef TrackInfo::TrackType TrackType;
|
||||||
|
typedef MozPromise<bool, MediaResult, /* IsExclusive = */ true> NotifyDataArrivedPromise;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
MediaFormatReader(AbstractMediaDecoder* aDecoder,
|
||||||
|
@ -49,11 +50,10 @@ public:
|
||||||
Seek(const SeekTarget& aTarget, int64_t aUnused) override;
|
Seek(const SeekTarget& aTarget, int64_t aUnused) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void NotifyDataArrivedInternal() override;
|
void NotifyDataArrived() override;
|
||||||
|
void UpdateBuffered() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
media::TimeIntervals GetBuffered() override;
|
|
||||||
|
|
||||||
bool ForceZeroStartTime() const override;
|
bool ForceZeroStartTime() const override;
|
||||||
|
|
||||||
// For Media Resource Management
|
// For Media Resource Management
|
||||||
|
@ -92,10 +92,8 @@ private:
|
||||||
bool IsWaitingOnCDMResource();
|
bool IsWaitingOnCDMResource();
|
||||||
|
|
||||||
bool InitDemuxer();
|
bool InitDemuxer();
|
||||||
// Notify the demuxer that new data has been received.
|
// Notify the track demuxers that new data has been received.
|
||||||
// The next queued task calling GetBuffered() is guaranteed to have up to date
|
void NotifyTrackDemuxers();
|
||||||
// buffered ranges.
|
|
||||||
void NotifyDemuxer();
|
|
||||||
void ReturnOutput(MediaData* aData, TrackType aTrack);
|
void ReturnOutput(MediaData* aData, TrackType aTrack);
|
||||||
|
|
||||||
// Enqueues a task to call Update(aTrack) on the decoder task queue.
|
// Enqueues a task to call Update(aTrack) on the decoder task queue.
|
||||||
|
@ -482,7 +480,8 @@ private:
|
||||||
DecoderData& GetDecoderData(TrackType aTrack);
|
DecoderData& GetDecoderData(TrackType aTrack);
|
||||||
|
|
||||||
// Demuxer objects.
|
// Demuxer objects.
|
||||||
RefPtr<MediaDataDemuxer> mDemuxer;
|
class DemuxerProxy;
|
||||||
|
UniquePtr<DemuxerProxy> mDemuxer;
|
||||||
bool mDemuxerInitDone;
|
bool mDemuxerInitDone;
|
||||||
void OnDemuxerInitDone(nsresult);
|
void OnDemuxerInitDone(nsresult);
|
||||||
void OnDemuxerInitFailed(const MediaResult& aError);
|
void OnDemuxerInitFailed(const MediaResult& aError);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче