Bug 1266027 part 1 - make the MediaDecoderReaderWrapper as a proxy of requesting media data; r=jwwang

MozReview-Commit-ID: CgTBPmtbNfh

--HG--
extra : transplant_source : %16%86%F676/S%C8%5Dj%86%9C%09%0E%D9%16L%DB%92y
This commit is contained in:
Kaku Kuo 2016-04-27 14:50:23 +08:00
Родитель c089c3f45c
Коммит f78f42e9df
6 изменённых файлов: 405 добавлений и 20 удалений

Просмотреть файл

@ -0,0 +1,50 @@
#include "MediaCallbackID.h"
namespace mozilla {
char const* CallbackID::INVALID_TAG = "INVALID_TAG";
int32_t const CallbackID::INVALID_ID = -1;
CallbackID::CallbackID()
: mTag(INVALID_TAG), mID(INVALID_ID)
{
}
CallbackID::CallbackID(char const* aTag, int32_t aID /* = 0*/)
: mTag(aTag), mID(aID)
{
}
CallbackID&
CallbackID::operator++()
{
++mID;
return *this;
}
CallbackID
CallbackID::operator++(int)
{
CallbackID ret = *this;
++(*this); // call prefix++
return ret;
}
bool
CallbackID::operator==(const CallbackID& rhs) const
{
return (strcmp(mTag, rhs.mTag) == 0) && (mID == rhs.mID);
}
bool
CallbackID::operator!=(const CallbackID& rhs) const
{
return !(*this == rhs);
}
CallbackID::operator int() const
{
return mID;
}
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,38 @@
/* -*- 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/. */
#ifndef MediaCallbackID_h_
#define MediaCallbackID_h_
namespace mozilla {
struct CallbackID
{
static char const* INVALID_TAG;
static int32_t const INVALID_ID;
CallbackID();
explicit CallbackID(char const* aTag, int32_t aID = 0);
CallbackID& operator++(); // prefix++
CallbackID operator++(int); // postfix++
bool operator==(const CallbackID& rhs) const;
bool operator!=(const CallbackID& rhs) const;
operator int() const;
private:
char const* mTag;
int32_t mID;
};
} // namespace mozilla
#endif // MediaCallbackID_h_

Просмотреть файл

@ -144,6 +144,8 @@ MediaDecoderReaderWrapper::MediaDecoderReaderWrapper(bool aIsRealTime,
: mForceZeroStartTime(aIsRealTime || aReader->ForceZeroStartTime())
, mOwnerThread(aOwnerThread)
, mReader(aReader)
, mAudioCallbackID("AudioCallbackID")
, mVideoCallbackID("VideoCallbackID")
{}
MediaDecoderReaderWrapper::~MediaDecoderReaderWrapper()
@ -178,11 +180,30 @@ MediaDecoderReaderWrapper::AwaitStartTime()
return mStartTimeRendezvous->AwaitStartTime();
}
RefPtr<MediaDecoderReaderWrapper::MediaDataPromise>
void
MediaDecoderReaderWrapper::CancelAudioCallback(CallbackID aID)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(aID == mAudioCallbackID);
++mAudioCallbackID;
mRequestAudioDataCB = nullptr;
}
void
MediaDecoderReaderWrapper::CancelVideoCallback(CallbackID aID)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(aID == mVideoCallbackID);
++mVideoCallbackID;
mRequestVideoDataCB = nullptr;
}
void
MediaDecoderReaderWrapper::RequestAudioData()
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mShutdown);
MOZ_ASSERT(mRequestAudioDataCB, "Request audio data without callback!");
auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
&MediaDecoderReader::RequestAudioData);
@ -194,18 +215,31 @@ MediaDecoderReaderWrapper::RequestAudioData()
->CompletionPromise();
}
return p->Then(mOwnerThread, __func__, this,
&MediaDecoderReaderWrapper::OnSampleDecoded,
&MediaDecoderReaderWrapper::OnNotDecoded)
->CompletionPromise();
RefPtr<MediaDecoderReaderWrapper> self = this;
mAudioDataRequest.Begin(p->Then(mOwnerThread, __func__,
[self] (MediaData* aAudioSample) {
MOZ_ASSERT(self->mRequestAudioDataCB);
self->mAudioDataRequest.Complete();
self->OnSampleDecoded(self->mRequestAudioDataCB.get(), aAudioSample, TimeStamp());
},
[self] (MediaDecoderReader::NotDecodedReason aReason) {
MOZ_ASSERT(self->mRequestAudioDataCB);
self->mAudioDataRequest.Complete();
self->OnNotDecoded(self->mRequestAudioDataCB.get(), aReason);
}));
}
RefPtr<MediaDecoderReaderWrapper::MediaDataPromise>
void
MediaDecoderReaderWrapper::RequestVideoData(bool aSkipToNextKeyframe,
media::TimeUnit aTimeThreshold)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mShutdown);
MOZ_ASSERT(mRequestVideoDataCB, "Request video data without callback!");
// Time the video decode and send this value back to callbacks who accept
// a TimeStamp as its second parameter.
TimeStamp videoDecodeStartTime = TimeStamp::Now();
if (aTimeThreshold.ToMicroseconds() > 0 &&
mStartTimeRendezvous->HaveStartTime()) {
@ -223,10 +257,32 @@ MediaDecoderReaderWrapper::RequestVideoData(bool aSkipToNextKeyframe,
->CompletionPromise();
}
return p->Then(mOwnerThread, __func__, this,
&MediaDecoderReaderWrapper::OnSampleDecoded,
&MediaDecoderReaderWrapper::OnNotDecoded)
->CompletionPromise();
RefPtr<MediaDecoderReaderWrapper> self = this;
mVideoDataRequest.Begin(p->Then(mOwnerThread, __func__,
[self, videoDecodeStartTime] (MediaData* aVideoSample) {
MOZ_ASSERT(self->mRequestVideoDataCB);
self->mVideoDataRequest.Complete();
self->OnSampleDecoded(self->mRequestVideoDataCB.get(), aVideoSample, videoDecodeStartTime);
},
[self] (MediaDecoderReader::NotDecodedReason aReason) {
MOZ_ASSERT(self->mRequestVideoDataCB);
self->mVideoDataRequest.Complete();
self->OnNotDecoded(self->mRequestVideoDataCB.get(), aReason);
}));
}
bool
MediaDecoderReaderWrapper::IsRequestingAudioData() const
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
return mAudioDataRequest.Exists();
}
bool
MediaDecoderReaderWrapper::IsRequestingVidoeData() const
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
return mVideoDataRequest.Exists();
}
RefPtr<MediaDecoderReader::SeekPromise>
@ -277,6 +333,10 @@ void
MediaDecoderReaderWrapper::ResetDecode()
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
mAudioDataRequest.DisconnectIfExists();
mVideoDataRequest.DisconnectIfExists();
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
mReader->OwnerThread()->Dispatch(r.forget());
@ -286,6 +346,11 @@ RefPtr<ShutdownPromise>
MediaDecoderReaderWrapper::Shutdown()
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mRequestAudioDataCB);
MOZ_ASSERT(!mRequestVideoDataCB);
MOZ_ASSERT(!mAudioDataRequest.Exists());
MOZ_ASSERT(!mVideoDataRequest.Exists());
mShutdown = true;
if (mStartTimeRendezvous) {
mStartTimeRendezvous->Destroy();
@ -323,12 +388,25 @@ MediaDecoderReaderWrapper::OnMetadataRead(MetadataHolder* aMetadata)
}
void
MediaDecoderReaderWrapper::OnSampleDecoded(MediaData* aSample)
MediaDecoderReaderWrapper::OnSampleDecoded(CallbackBase* aCallback,
MediaData* aSample,
TimeStamp aDecodeStartTime)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
if (!mShutdown) {
aSample->AdjustForStartTime(StartTime().ToMicroseconds());
}
MOZ_ASSERT(!mShutdown);
aSample->AdjustForStartTime(StartTime().ToMicroseconds());
aCallback->OnResolved(aSample, aDecodeStartTime);
}
void
MediaDecoderReaderWrapper::OnNotDecoded(CallbackBase* aCallback,
MediaDecoderReader::NotDecodedReason aReason)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mShutdown);
aCallback->OnRejected(aReason);
}
} // namespace mozilla

Просмотреть файл

@ -12,6 +12,7 @@
#include "nsISupportsImpl.h"
#include "MediaDecoderReader.h"
#include "MediaCallbackID.h"
namespace mozilla {
@ -33,6 +34,135 @@ class MediaDecoderReaderWrapper {
typedef MediaDecoderReader::BufferedUpdatePromise BufferedUpdatePromise;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReaderWrapper);
/*
* Type 1: void(MediaData*)
* void(RefPtr<MediaData>)
*/
template <typename T>
class ArgType1CheckHelper {
template<typename C, typename... Ts>
static TrueType
test(void(C::*aMethod)(Ts...),
decltype((DeclVal<C>().*aMethod)(DeclVal<MediaData*>()), 0));
template <typename F>
static TrueType
test(F&&, decltype(DeclVal<F>()(DeclVal<MediaData*>()), 0));
static FalseType test(...);
public:
typedef decltype(test(DeclVal<T>(), 0)) Type;
};
template <typename T>
struct ArgType1Check : public ArgType1CheckHelper<T>::Type {};
/*
* Type 2: void(MediaData*, TimeStamp)
* void(RefPtr<MediaData>, TimeStamp)
* void(MediaData*, TimeStamp&)
* void(RefPtr<MediaData>, const TimeStamp&&)
*/
template <typename T>
class ArgType2CheckHelper {
template<typename C, typename... Ts>
static TrueType
test(void(C::*aMethod)(Ts...),
decltype((DeclVal<C>().*aMethod)(DeclVal<MediaData*>(), DeclVal<TimeStamp>()), 0));
template <typename F>
static TrueType
test(F&&, decltype(DeclVal<F>()(DeclVal<MediaData*>(), DeclVal<TimeStamp>()), 0));
static FalseType test(...);
public:
typedef decltype(test(DeclVal<T>(), 0)) Type;
};
template <typename T>
struct ArgType2Check : public ArgType2CheckHelper<T>::Type {};
struct CallbackBase
{
virtual ~CallbackBase() {}
virtual void OnResolved(MediaData*, TimeStamp) = 0;
virtual void OnRejected(MediaDecoderReader::NotDecodedReason) = 0;
};
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
struct MethodCallback : public CallbackBase
{
MethodCallback(ThisType* aThis, ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
: mThis(aThis), mResolveMethod(aResolveMethod), mRejectMethod(aRejectMethod)
{
}
template<typename F>
typename EnableIf<ArgType1Check<F>::value, void>::Type
CallHelper(MediaData* aSample, TimeStamp)
{
(mThis->*mResolveMethod)(aSample);
}
template<typename F>
typename EnableIf<ArgType2Check<F>::value, void>::Type
CallHelper(MediaData* aSample, TimeStamp aDecodeStartTime)
{
(mThis->*mResolveMethod)(aSample, aDecodeStartTime);
}
void OnResolved(MediaData* aSample, TimeStamp aDecodeStartTime) override
{
CallHelper<ResolveMethodType>(aSample, aDecodeStartTime);
}
void OnRejected(MediaDecoderReader::NotDecodedReason aReason) override
{
(mThis->*mRejectMethod)(aReason);
}
RefPtr<ThisType> mThis;
ResolveMethodType mResolveMethod;
RejectMethodType mRejectMethod;
};
template<typename ResolveFunctionType, typename RejectFunctionType>
struct FunctionCallback : public CallbackBase
{
FunctionCallback(ResolveFunctionType&& aResolveFuntion, RejectFunctionType&& aRejectFunction)
: mResolveFuntion(Move(aResolveFuntion)), mRejectFunction(Move(aRejectFunction))
{
}
template<typename F>
typename EnableIf<ArgType1Check<F>::value, void>::Type
CallHelper(MediaData* aSample, TimeStamp)
{
mResolveFuntion(aSample);
}
template<typename F>
typename EnableIf<ArgType2Check<F>::value, void>::Type
CallHelper(MediaData* aSample, TimeStamp aDecodeStartTime)
{
mResolveFuntion(aSample, aDecodeStartTime);
}
void OnResolved(MediaData* aSample, TimeStamp aDecodeStartTime) override
{
CallHelper<ResolveFunctionType>(aSample, aDecodeStartTime);
}
void OnRejected(MediaDecoderReader::NotDecodedReason aReason) override
{
mRejectFunction(aReason);
}
ResolveFunctionType mResolveFuntion;
RejectFunctionType mRejectFunction;
};
public:
MediaDecoderReaderWrapper(bool aIsRealTime,
AbstractThread* aOwnerThread,
@ -41,9 +171,83 @@ public:
media::TimeUnit StartTime() const;
RefPtr<MetadataPromise> ReadMetadata();
RefPtr<HaveStartTimePromise> AwaitStartTime();
RefPtr<MediaDataPromise> RequestAudioData();
RefPtr<MediaDataPromise> RequestVideoData(bool aSkipToNextKeyframe,
media::TimeUnit aTimeThreshold);
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
CallbackID
SetAudioCallback(ThisType* aThisVal,
ResolveMethodType aResolveMethod,
RejectMethodType aRejectMethod)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mRequestAudioDataCB,
"Please cancel the original callback before setting a new one.");
mRequestAudioDataCB.reset(
new MethodCallback<ThisType, ResolveMethodType, RejectMethodType>(
aThisVal, aResolveMethod, aRejectMethod));
return mAudioCallbackID;
}
template<typename ResolveFunction, typename RejectFunction>
CallbackID
SetAudioCallback(ResolveFunction&& aResolveFunction,
RejectFunction&& aRejectFunction)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mRequestAudioDataCB,
"Please cancel the original callback before setting a new one.");
mRequestAudioDataCB.reset(
new FunctionCallback<ResolveFunction, RejectFunction>(
Move(aResolveFunction), Move(aRejectFunction)));
return mAudioCallbackID;
}
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
CallbackID
SetVideoCallback(ThisType* aThisVal,
ResolveMethodType aResolveMethod,
RejectMethodType aRejectMethod)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mRequestVideoDataCB,
"Please cancel the original callback before setting a new one.");
mRequestVideoDataCB.reset(
new MethodCallback<ThisType, ResolveMethodType, RejectMethodType>(
aThisVal, aResolveMethod, aRejectMethod));
return mVideoCallbackID;
}
template<typename ResolveFunction, typename RejectFunction>
CallbackID
SetVideoCallback(ResolveFunction&& aResolveFunction,
RejectFunction&& aRejectFunction)
{
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_ASSERT(!mRequestVideoDataCB,
"Please cancel the original callback before setting a new one.");
mRequestVideoDataCB.reset(
new FunctionCallback<ResolveFunction, RejectFunction>(
Move(aResolveFunction), Move(aRejectFunction)));
return mVideoCallbackID;
}
void CancelAudioCallback(CallbackID aID);
void CancelVideoCallback(CallbackID aID);
// NOTE: please set callbacks before requesting audio/video data!
void RequestAudioData();
void RequestVideoData(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
bool IsRequestingAudioData() const;
bool IsRequestingVidoeData() const;
RefPtr<SeekPromise> Seek(SeekTarget aTarget, media::TimeUnit aEndTime);
RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType);
RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise();
@ -96,8 +300,10 @@ private:
void OnMetadataRead(MetadataHolder* aMetadata);
void OnMetadataNotRead() {}
void OnSampleDecoded(MediaData* aSample);
void OnNotDecoded() {}
void OnSampleDecoded(CallbackBase* aCallback, MediaData* aSample,
TimeStamp aVideoDecodeStartTime);
void OnNotDecoded(CallbackBase* aCallback,
MediaDecoderReader::NotDecodedReason aReason);
const bool mForceZeroStartTime;
const RefPtr<AbstractThread> mOwnerThread;
@ -105,6 +311,17 @@ private:
bool mShutdown = false;
RefPtr<StartTimeRendezvous> mStartTimeRendezvous;
UniquePtr<CallbackBase> mRequestAudioDataCB;
UniquePtr<CallbackBase> mRequestVideoDataCB;
MozPromiseRequestHolder<MediaDataPromise> mAudioDataRequest;
MozPromiseRequestHolder<MediaDataPromise> mVideoDataRequest;
/*
* These callback ids are used to prevent mis-canceling callback.
*/
CallbackID mAudioCallbackID;
CallbackID mVideoCallbackID;
};
} // namespace mozilla

Просмотреть файл

@ -108,6 +108,7 @@ EXPORTS += [
'Intervals.h',
'Latency.h',
'MediaCache.h',
'MediaCallbackID.h',
'MediaData.h',
'MediaDataDemuxer.h',
'MediaDecoder.h',
@ -218,6 +219,7 @@ UNIFIED_SOURCES += [
'GraphDriver.cpp',
'Latency.cpp',
'MediaCache.cpp',
'MediaCallbackID.cpp',
'MediaData.cpp',
'MediaDecoder.cpp',
'MediaDecoderReader.cpp',

Просмотреть файл

@ -892,7 +892,7 @@ public:
}
}
bool Exists() { return !!mRequest; }
bool Exists() const { return !!mRequest; }
private:
RefPtr<typename PromiseType::Request> mRequest;