зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 10 changesets (bug 1756260) for causing build bustages on ExternalEngineStateMachine.obj CLOSED TREE
Backed out changeset d2339ec8194e (bug 1756260) Backed out changeset 391201cf338b (bug 1756260) Backed out changeset 1a9aa3f175d0 (bug 1756260) Backed out changeset d624c80ba67f (bug 1756260) Backed out changeset 46e8a8457044 (bug 1756260) Backed out changeset 93245b0e2118 (bug 1756260) Backed out changeset b69ee552b64e (bug 1756260) Backed out changeset 9fc71b8478d5 (bug 1756260) Backed out changeset 34b10d427f0f (bug 1756260) Backed out changeset ed9640359f3a (bug 1756260)
This commit is contained in:
Родитель
45276205df
Коммит
df3db4d641
|
@ -200,7 +200,7 @@ already_AddRefed<ChannelMediaDecoder> ChannelMediaDecoder::Clone(
|
|||
return decoder.forget();
|
||||
}
|
||||
|
||||
MediaDecoderStateMachineBase* ChannelMediaDecoder::CreateStateMachine() {
|
||||
MediaDecoderStateMachine* ChannelMediaDecoder::CreateStateMachine() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MediaFormatReaderInit init;
|
||||
init.mVideoFrameContainer = GetVideoFrameContainer();
|
||||
|
|
|
@ -102,7 +102,7 @@ class ChannelMediaDecoder
|
|||
void DownloadProgressed();
|
||||
|
||||
// Create a new state machine to run this decoder.
|
||||
MediaDecoderStateMachineBase* CreateStateMachine();
|
||||
MediaDecoderStateMachine* CreateStateMachine();
|
||||
|
||||
nsresult Load(BaseMediaResource* aOriginal);
|
||||
|
||||
|
|
|
@ -1,884 +0,0 @@
|
|||
/* 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 "ExternalEngineStateMachine.h"
|
||||
|
||||
#include "PerformanceRecorder.h"
|
||||
#ifdef MOZ_WMF
|
||||
# include "mozilla/MFMediaEngineChild.h"
|
||||
#endif
|
||||
#include "mozilla/ProfilerLabels.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern LazyLogModule gMediaDecoderLog;
|
||||
|
||||
#define FMT(x, ...) \
|
||||
"Decoder=%p, State=%s, " x, mDecoderID, GetStateStr(), ##__VA_ARGS__
|
||||
#define LOG(x, ...) \
|
||||
DDMOZ_LOG(gMediaDecoderLog, LogLevel::Debug, "Decoder=%p, State=%s, " x, \
|
||||
mDecoderID, GetStateStr(), ##__VA_ARGS__)
|
||||
#define LOGV(x, ...) \
|
||||
DDMOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, "Decoder=%p, State=%s, " x, \
|
||||
mDecoderID, GetStateStr(), ##__VA_ARGS__)
|
||||
#define LOGW(x, ...) NS_WARNING(nsPrintfCString(FMT(x, ##__VA_ARGS__)).get())
|
||||
#define LOGE(x, ...) \
|
||||
NS_DebugBreak(NS_DEBUG_WARNING, \
|
||||
nsPrintfCString(FMT(x, ##__VA_ARGS__)).get(), nullptr, \
|
||||
__FILE__, __LINE__)
|
||||
|
||||
const char* ExternalEngineEventToStr(ExternalEngineEvent aEvent) {
|
||||
#define EVENT_TO_STR(event) \
|
||||
case ExternalEngineEvent::event: \
|
||||
return #event
|
||||
switch (aEvent) {
|
||||
EVENT_TO_STR(LoadedMetaData);
|
||||
EVENT_TO_STR(LoadedFirstFrame);
|
||||
EVENT_TO_STR(LoadedData);
|
||||
EVENT_TO_STR(Waiting);
|
||||
EVENT_TO_STR(Playing);
|
||||
EVENT_TO_STR(Seeked);
|
||||
EVENT_TO_STR(BufferingStarted);
|
||||
EVENT_TO_STR(BufferingEnded);
|
||||
EVENT_TO_STR(Timeupdate);
|
||||
EVENT_TO_STR(Ended);
|
||||
EVENT_TO_STR(RequestForAudio);
|
||||
EVENT_TO_STR(RequestForVideo);
|
||||
EVENT_TO_STR(AudioEnough);
|
||||
EVENT_TO_STR(VideoEnough);
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Undefined event!");
|
||||
return "Undefined";
|
||||
}
|
||||
#undef EVENT_TO_STR
|
||||
}
|
||||
|
||||
/* static */
|
||||
const char* ExternalEngineStateMachine::StateToStr(State aNextState) {
|
||||
#define STATE_TO_STR(state) \
|
||||
case State::state: \
|
||||
return #state
|
||||
switch (aNextState) {
|
||||
STATE_TO_STR(InitEngine);
|
||||
STATE_TO_STR(ReadingMetadata);
|
||||
STATE_TO_STR(RunningEngine);
|
||||
STATE_TO_STR(SeekingData);
|
||||
STATE_TO_STR(ShutdownEngine);
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Undefined state!");
|
||||
return "Undefined";
|
||||
}
|
||||
#undef STATE_TO_STR
|
||||
}
|
||||
|
||||
const char* ExternalEngineStateMachine::GetStateStr() const {
|
||||
return StateToStr(mState.mName);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::ChangeStateTo(State aNextState) {
|
||||
LOG("Change state : '%s' -> '%s'", StateToStr(mState.mName),
|
||||
StateToStr(aNextState));
|
||||
// Assert the possible state transitions.
|
||||
MOZ_ASSERT_IF(mState.IsInitEngine(), aNextState == State::ReadingMetadata ||
|
||||
aNextState == State::ShutdownEngine);
|
||||
MOZ_ASSERT_IF(mState.IsReadingMetadata(),
|
||||
aNextState == State::RunningEngine ||
|
||||
aNextState == State::ShutdownEngine);
|
||||
MOZ_ASSERT_IF(
|
||||
mState.IsRunningEngine(),
|
||||
aNextState == State::SeekingData || aNextState == State::ShutdownEngine);
|
||||
MOZ_ASSERT_IF(mState.IsSeekingData(),
|
||||
aNextState == State::RunningEngine ||
|
||||
aNextState == State::ShutdownEngine);
|
||||
MOZ_ASSERT_IF(mState.IsShutdownEngine(), aNextState == State::ShutdownEngine);
|
||||
if (aNextState == State::SeekingData) {
|
||||
mState = StateObject({StateObject::SeekingData()});
|
||||
} else if (aNextState == State::ReadingMetadata) {
|
||||
mState = StateObject({StateObject::ReadingMetadata()});
|
||||
} else if (aNextState == State::RunningEngine) {
|
||||
mState = StateObject({StateObject::RunningEngine()});
|
||||
} else if (aNextState == State::ShutdownEngine) {
|
||||
mState = StateObject({StateObject::ShutdownEngine()});
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Wrong state!");
|
||||
}
|
||||
}
|
||||
|
||||
ExternalEngineStateMachine::ExternalEngineStateMachine(
|
||||
MediaDecoder* aDecoder, MediaFormatReader* aReader)
|
||||
: MediaDecoderStateMachineBase(aDecoder, aReader) {
|
||||
LOG("Created ExternalEngineStateMachine");
|
||||
MOZ_ASSERT(mState.IsInitEngine());
|
||||
#ifdef MOZ_WMF
|
||||
mEngine.reset(new MFMediaEngineWrapper(this));
|
||||
#endif
|
||||
if (mEngine) {
|
||||
auto* state = mState.AsInitEngine();
|
||||
state->mInitPromise = mEngine->Init(!mMinimizePreroll);
|
||||
state->mInitPromise
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&ExternalEngineStateMachine::OnEngineInitSuccess,
|
||||
&ExternalEngineStateMachine::OnEngineInitFailure)
|
||||
->Track(state->mEngineInitRequest);
|
||||
} else {
|
||||
ShutdownInternal();
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnEngineInitSuccess() {
|
||||
AssertOnTaskQueue();
|
||||
AUTO_PROFILER_LABEL("ExternalEngineStateMachine::OnEngineInitSuccess",
|
||||
MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(mState.IsInitEngine());
|
||||
LOG("Initialized the external playback engine %" PRIu64
|
||||
", start reading metadata",
|
||||
mEngine->Id());
|
||||
auto* state = mState.AsInitEngine();
|
||||
state->mEngineInitRequest.Complete();
|
||||
mReader->UpdateMediaEngineId(mEngine->Id());
|
||||
state->mInitPromise = nullptr;
|
||||
ChangeStateTo(State::ReadingMetadata);
|
||||
ReadMetadata();
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnEngineInitFailure() {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsInitEngine());
|
||||
LOGE("Failed to initialize the external playback engine");
|
||||
auto* state = mState.AsInitEngine();
|
||||
state->mEngineInitRequest.Complete();
|
||||
state->mInitPromise = nullptr;
|
||||
// TODO : Should fallback to the normal playback with media engine.
|
||||
DecodeError(MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__));
|
||||
ShutdownInternal();
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::ReadMetadata() {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsReadingMetadata());
|
||||
mReader->ReadMetadata()
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&ExternalEngineStateMachine::OnMetadataRead,
|
||||
&ExternalEngineStateMachine::OnMetadataNotRead)
|
||||
->Track(mState.AsReadingMetadata()->mMetadataRequest);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnMetadataRead(MetadataHolder&& aMetadata) {
|
||||
AssertOnTaskQueue();
|
||||
AUTO_PROFILER_LABEL("ExternalEngineStateMachine::OnMetadataRead",
|
||||
MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(mState.IsReadingMetadata());
|
||||
LOG("OnMetadataRead");
|
||||
|
||||
mState.AsReadingMetadata()->mMetadataRequest.Complete();
|
||||
mInfo.emplace(*aMetadata.mInfo);
|
||||
mMediaSeekable = Info().mMediaSeekable;
|
||||
mMediaSeekableOnlyInBufferedRanges =
|
||||
Info().mMediaSeekableOnlyInBufferedRanges;
|
||||
|
||||
mEngine->SetMediaInfo(*mInfo);
|
||||
|
||||
if (Info().mMetadataDuration.isSome()) {
|
||||
mDuration = Info().mMetadataDuration;
|
||||
} else if (Info().mUnadjustedMetadataEndTime.isSome()) {
|
||||
const TimeUnit unadjusted = Info().mUnadjustedMetadataEndTime.ref();
|
||||
const TimeUnit adjustment = Info().mStartTime;
|
||||
mInfo->mMetadataDuration.emplace(unadjusted - adjustment);
|
||||
mDuration = Info().mMetadataDuration;
|
||||
}
|
||||
|
||||
// If we don't know the duration by this point, we assume infinity, per spec.
|
||||
if (mDuration.Ref().isNothing()) {
|
||||
mDuration = Some(TimeUnit::FromInfinity());
|
||||
}
|
||||
MOZ_ASSERT(mDuration.Ref().isSome());
|
||||
|
||||
mMetadataLoadedEvent.Notify(std::move(aMetadata.mInfo),
|
||||
std::move(aMetadata.mTags),
|
||||
MediaDecoderEventVisibility::Observable);
|
||||
StartRunningEngine();
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnMetadataNotRead(const MediaResult& aError) {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsReadingMetadata());
|
||||
LOGE("Decode metadata failed, shutting down decoder");
|
||||
mState.AsReadingMetadata()->mMetadataRequest.Complete();
|
||||
DecodeError(aError);
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> ExternalEngineStateMachine::Seek(
|
||||
const SeekTarget& aTarget) {
|
||||
AssertOnTaskQueue();
|
||||
if (!mState.IsRunningEngine() && !mState.IsSeekingData()) {
|
||||
MOZ_ASSERT(false, "Can't seek due to unsupported state.");
|
||||
return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
|
||||
}
|
||||
// We don't support these type of seek, because they're depending on the
|
||||
// implementation of the external engine, which might not be supported.
|
||||
if (aTarget.IsNextFrame() || aTarget.IsVideoOnly()) {
|
||||
return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
|
||||
}
|
||||
|
||||
LOG("Start seeking to %" PRId64, aTarget.GetTime().ToMicroseconds());
|
||||
auto* state = mState.AsSeekingData();
|
||||
if (!state) {
|
||||
// We're in the running engine state, and change the state to seeking.
|
||||
ChangeStateTo(State::SeekingData);
|
||||
state = mState.AsSeekingData();
|
||||
}
|
||||
state->SetTarget(aTarget);
|
||||
|
||||
// Update related status.
|
||||
mSentPlaybackEndedEvent = false;
|
||||
mOnPlaybackEvent.Notify(MediaPlaybackEvent::SeekStarted);
|
||||
mOnNextFrameStatus.Notify(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING);
|
||||
|
||||
// Notify the external playback engine about seeking. After the engine changes
|
||||
// its current time, it would send `seeked` event.
|
||||
mEngine->Seek(aTarget.GetTime());
|
||||
state->mWaitingEngineSeeked = true;
|
||||
SeekReader();
|
||||
return state->mSeekJob.mPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::SeekReader() {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsSeekingData());
|
||||
auto* state = mState.AsSeekingData();
|
||||
|
||||
// Reset the reader first and ask it to perform a demuxer seek.
|
||||
ResetDecode();
|
||||
state->mWaitingReaderSeeked = true;
|
||||
LOG("Seek reader to %" PRId64,
|
||||
state->mSeekJob.mTarget->GetTime().ToMicroseconds());
|
||||
mReader->Seek(state->mSeekJob.mTarget.ref())
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&ExternalEngineStateMachine::OnSeekResolved,
|
||||
&ExternalEngineStateMachine::OnSeekRejected)
|
||||
->Track(state->mSeekRequest);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnSeekResolved(const media::TimeUnit& aUnit) {
|
||||
AUTO_PROFILER_LABEL("ExternalEngineStateMachine::OnSeekResolved",
|
||||
MEDIA_PLAYBACK);
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsSeekingData());
|
||||
auto* state = mState.AsSeekingData();
|
||||
|
||||
LOG("OnSeekResolved");
|
||||
state->mSeekRequest.Complete();
|
||||
state->mWaitingReaderSeeked = false;
|
||||
|
||||
// Start sending new data to the external playback engine.
|
||||
if (HasAudio()) {
|
||||
mHasEnoughAudio = false;
|
||||
OnRequestAudio();
|
||||
}
|
||||
if (HasVideo()) {
|
||||
mHasEnoughVideo = false;
|
||||
OnRequestVideo();
|
||||
}
|
||||
CheckIfSeekCompleted();
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnSeekRejected(
|
||||
const SeekRejectValue& aReject) {
|
||||
AUTO_PROFILER_LABEL("ExternalEngineStateMachine::OnSeekRejected",
|
||||
MEDIA_PLAYBACK);
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsSeekingData());
|
||||
auto* state = mState.AsSeekingData();
|
||||
|
||||
LOG("OnSeekRejected");
|
||||
state->mSeekRequest.Complete();
|
||||
if (aReject.mError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
|
||||
LOG("OnSeekRejected reason=WAITING_FOR_DATA type=%s",
|
||||
MediaData::TypeToStr(aReject.mType));
|
||||
MOZ_ASSERT_IF(aReject.mType == MediaData::Type::AUDIO_DATA,
|
||||
!IsRequestingAudioData());
|
||||
MOZ_ASSERT_IF(aReject.mType == MediaData::Type::VIDEO_DATA,
|
||||
!IsRequestingVideoData());
|
||||
MOZ_ASSERT_IF(aReject.mType == MediaData::Type::AUDIO_DATA,
|
||||
!IsWaitingAudioData());
|
||||
MOZ_ASSERT_IF(aReject.mType == MediaData::Type::VIDEO_DATA,
|
||||
!IsWaitingVideoData());
|
||||
|
||||
// Fire 'waiting' to notify the player that we are waiting for data.
|
||||
mOnNextFrameStatus.Notify(
|
||||
MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING);
|
||||
WaitForData(aReject.mType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aReject.mError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
|
||||
EndOfStream(aReject.mType);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_FAILED(aReject.mError),
|
||||
"Cancels should also disconnect mSeekRequest");
|
||||
state->RejectIfExists(__func__);
|
||||
DecodeError(aReject.mError);
|
||||
}
|
||||
|
||||
bool ExternalEngineStateMachine::IsSeeking() {
|
||||
AssertOnTaskQueue();
|
||||
const auto* state = mState.AsSeekingData();
|
||||
return state && state->IsSeeking();
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::CheckIfSeekCompleted() {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsSeekingData());
|
||||
auto* state = mState.AsSeekingData();
|
||||
if (state->mWaitingEngineSeeked || state->mWaitingReaderSeeked) {
|
||||
LOG("Seek hasn't been completed yet, waitEngineSeeked=%d, "
|
||||
"waitReaderSeeked=%d",
|
||||
state->mWaitingEngineSeeked, state->mWaitingReaderSeeked);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("Seek completed");
|
||||
state->Resolve(__func__);
|
||||
mOnPlaybackEvent.Notify(MediaPlaybackEvent::Invalidate);
|
||||
mOnNextFrameStatus.Notify(MediaDecoderOwner::NEXT_FRAME_AVAILABLE);
|
||||
StartRunningEngine();
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::ResetDecode() {
|
||||
AssertOnTaskQueue();
|
||||
if (!mInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("ResetDecode");
|
||||
MediaFormatReader::TrackSet tracks;
|
||||
if (HasVideo()) {
|
||||
mVideoDataRequest.DisconnectIfExists();
|
||||
mVideoWaitRequest.DisconnectIfExists();
|
||||
tracks += TrackInfo::kVideoTrack;
|
||||
}
|
||||
if (HasAudio()) {
|
||||
mAudioDataRequest.DisconnectIfExists();
|
||||
mAudioWaitRequest.DisconnectIfExists();
|
||||
tracks += TrackInfo::kAudioTrack;
|
||||
}
|
||||
mReader->ResetDecode(tracks);
|
||||
}
|
||||
|
||||
RefPtr<GenericPromise> ExternalEngineStateMachine::InvokeSetSink(
|
||||
const RefPtr<AudioDeviceInfo>& aSink) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// TODO : can media engine support this?
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise> ExternalEngineStateMachine::Shutdown() {
|
||||
AssertOnTaskQueue();
|
||||
LOG("Shutdown");
|
||||
return ShutdownInternal();
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise> ExternalEngineStateMachine::ShutdownInternal() {
|
||||
AssertOnTaskQueue();
|
||||
|
||||
LOG("ShutdownInternal");
|
||||
ChangeStateTo(State::ShutdownEngine);
|
||||
ResetDecode();
|
||||
|
||||
mAudioDataRequest.DisconnectIfExists();
|
||||
mVideoDataRequest.DisconnectIfExists();
|
||||
mAudioWaitRequest.DisconnectIfExists();
|
||||
mVideoWaitRequest.DisconnectIfExists();
|
||||
mBuffered.DisconnectIfConnected();
|
||||
mPlayState.DisconnectIfConnected();
|
||||
mVolume.DisconnectIfConnected();
|
||||
mPreservesPitch.DisconnectIfConnected();
|
||||
mLooping.DisconnectIfConnected();
|
||||
mDuration.DisconnectAll();
|
||||
mCurrentPosition.DisconnectAll();
|
||||
// TODO : implement audible check
|
||||
mIsAudioDataAudible.DisconnectAll();
|
||||
|
||||
mMetadataManager.Disconnect();
|
||||
|
||||
mEngine->Shutdown();
|
||||
return mReader->Shutdown()->Then(
|
||||
OwnerThread(), __func__, [self = RefPtr{this}, this]() {
|
||||
LOG("Shutting down state machine task queue");
|
||||
return OwnerThread()->BeginShutdown();
|
||||
});
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::SetPlaybackRate(double aPlaybackRate) {
|
||||
AssertOnTaskQueue();
|
||||
mEngine->SetVolume(aPlaybackRate);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::BufferedRangeUpdated() {
|
||||
AssertOnTaskQueue();
|
||||
AUTO_PROFILER_LABEL("ExternalEngineStateMachine::BufferedRangeUpdated",
|
||||
MEDIA_PLAYBACK);
|
||||
|
||||
// While playing an unseekable stream of unknown duration, mDuration
|
||||
// is updated as we play. But if data is being downloaded
|
||||
// faster than played, mDuration won't reflect the end of playable data
|
||||
// since we haven't played the frame at the end of buffered data. So update
|
||||
// mDuration here as new data is downloaded to prevent such a lag.
|
||||
if (mBuffered.Ref().IsInvalid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool exists;
|
||||
media::TimeUnit end{mBuffered.Ref().GetEnd(&exists)};
|
||||
if (!exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use estimated duration from buffer ranges when mDuration is unknown or
|
||||
// the estimated duration is larger.
|
||||
if (mDuration.Ref().isNothing() || mDuration.Ref()->IsInfinite() ||
|
||||
end > mDuration.Ref().ref()) {
|
||||
mDuration = Some(end);
|
||||
DDLOG(DDLogCategory::Property, "duration_us",
|
||||
mDuration.Ref()->ToMicroseconds());
|
||||
}
|
||||
}
|
||||
|
||||
#define PERFORM_WHEN_ALLOW(Func) \
|
||||
do { \
|
||||
/* Initialzation is not done yet, posepone the operation */ \
|
||||
if (mState.IsInitEngine() && mState.AsInitEngine()->mInitPromise) { \
|
||||
LOG("%s is called before init", __func__); \
|
||||
mState.AsInitEngine()->mInitPromise->Then( \
|
||||
OwnerThread(), __func__, \
|
||||
[self = RefPtr{this}]( \
|
||||
const GenericNonExclusivePromise::ResolveOrRejectValue& aVal) { \
|
||||
if (aVal.IsResolve()) { \
|
||||
self->Func(); \
|
||||
} \
|
||||
}); \
|
||||
return; \
|
||||
} else if (mState.IsShutdownEngine()) { \
|
||||
return; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
void ExternalEngineStateMachine::VolumeChanged() {
|
||||
AssertOnTaskQueue();
|
||||
PERFORM_WHEN_ALLOW(VolumeChanged);
|
||||
mEngine->SetVolume(mVolume);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::PreservesPitchChanged() {
|
||||
AssertOnTaskQueue();
|
||||
PERFORM_WHEN_ALLOW(PreservesPitchChanged);
|
||||
mEngine->SetPreservesPitch(mPreservesPitch);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::PlayStateChanged() {
|
||||
AssertOnTaskQueue();
|
||||
PERFORM_WHEN_ALLOW(PlayStateChanged);
|
||||
if (mPlayState == MediaDecoder::PLAY_STATE_PLAYING) {
|
||||
mEngine->Play();
|
||||
} else if (mPlayState == MediaDecoder::PLAY_STATE_PAUSED) {
|
||||
mEngine->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::LoopingChanged() {
|
||||
AssertOnTaskQueue();
|
||||
PERFORM_WHEN_ALLOW(LoopingChanged);
|
||||
mEngine->SetLooping(mLooping);
|
||||
}
|
||||
|
||||
#undef PERFORM_WHEN_ALLOW
|
||||
|
||||
void ExternalEngineStateMachine::EndOfStream(MediaData::Type aType) {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsRunningEngine() || mState.IsSeekingData());
|
||||
static auto DataTypeToTrackType = [](const MediaData::Type& aType) {
|
||||
if (aType == MediaData::Type::VIDEO_DATA) {
|
||||
return TrackInfo::TrackType::kVideoTrack;
|
||||
}
|
||||
if (aType == MediaData::Type::AUDIO_DATA) {
|
||||
return TrackInfo::TrackType::kAudioTrack;
|
||||
}
|
||||
return TrackInfo::TrackType::kUndefinedTrack;
|
||||
};
|
||||
mEngine->NotifyEndOfStream(DataTypeToTrackType(aType));
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::WaitForData(MediaData::Type aType) {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsRunningEngine() || mState.IsSeekingData());
|
||||
AUTO_PROFILER_LABEL("ExternalEngineStateMachine::WaitForData",
|
||||
MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(aType == MediaData::Type::AUDIO_DATA ||
|
||||
aType == MediaData::Type::VIDEO_DATA);
|
||||
|
||||
LOG("WaitForData");
|
||||
RefPtr<ExternalEngineStateMachine> self = this;
|
||||
if (aType == MediaData::Type::AUDIO_DATA) {
|
||||
MOZ_ASSERT(HasAudio());
|
||||
mReader->WaitForData(MediaData::Type::AUDIO_DATA)
|
||||
->Then(
|
||||
OwnerThread(), __func__,
|
||||
[self, this](MediaData::Type aType) {
|
||||
AUTO_PROFILER_LABEL(
|
||||
"ExternalEngineStateMachine::WaitForData:AudioResolved",
|
||||
MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(aType == MediaData::Type::AUDIO_DATA);
|
||||
LOG("Done waiting for audio data");
|
||||
mAudioWaitRequest.Complete();
|
||||
MaybeFinishWaitForData();
|
||||
},
|
||||
[self, this](const WaitForDataRejectValue& aRejection) {
|
||||
AUTO_PROFILER_LABEL(
|
||||
"ExternalEngineStateMachine::WaitForData:AudioRejected",
|
||||
MEDIA_PLAYBACK);
|
||||
mAudioWaitRequest.Complete();
|
||||
DecodeError(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA);
|
||||
})
|
||||
->Track(mAudioWaitRequest);
|
||||
} else {
|
||||
MOZ_ASSERT(HasVideo());
|
||||
mReader->WaitForData(MediaData::Type::VIDEO_DATA)
|
||||
->Then(
|
||||
OwnerThread(), __func__,
|
||||
[self, this](MediaData::Type aType) {
|
||||
AUTO_PROFILER_LABEL(
|
||||
"ExternalEngineStateMachine::WaitForData:VideoResolved",
|
||||
MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(aType == MediaData::Type::VIDEO_DATA);
|
||||
LOG("Done waiting for video data");
|
||||
mVideoWaitRequest.Complete();
|
||||
MaybeFinishWaitForData();
|
||||
},
|
||||
[self, this](const WaitForDataRejectValue& aRejection) {
|
||||
AUTO_PROFILER_LABEL(
|
||||
"ExternalEngineStateMachine::WaitForData:VideoRejected",
|
||||
MEDIA_PLAYBACK);
|
||||
mVideoWaitRequest.Complete();
|
||||
DecodeError(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA);
|
||||
})
|
||||
->Track(mVideoWaitRequest);
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::MaybeFinishWaitForData() {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsRunningEngine() || mState.IsSeekingData());
|
||||
|
||||
bool isWaitingForAudio = HasAudio() && mAudioWaitRequest.Exists();
|
||||
bool isWaitingForVideo = HasVideo() && mVideoWaitRequest.Exists();
|
||||
if (isWaitingForAudio || isWaitingForVideo) {
|
||||
LOG("Still waiting for data (waitAudio=%d, waitVideo=%d)",
|
||||
isWaitingForAudio, isWaitingForVideo);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("Finished waiting for data");
|
||||
if (mState.IsSeekingData()) {
|
||||
SeekReader();
|
||||
return;
|
||||
}
|
||||
if (HasAudio()) {
|
||||
RunningEngineUpdate(MediaData::Type::AUDIO_DATA);
|
||||
}
|
||||
if (HasVideo()) {
|
||||
RunningEngineUpdate(MediaData::Type::VIDEO_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::StartRunningEngine() {
|
||||
ChangeStateTo(State::RunningEngine);
|
||||
if (HasAudio()) {
|
||||
RunningEngineUpdate(MediaData::Type::AUDIO_DATA);
|
||||
}
|
||||
if (HasVideo()) {
|
||||
RunningEngineUpdate(MediaData::Type::VIDEO_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::RunningEngineUpdate(MediaData::Type aType) {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsRunningEngine() || mState.IsSeekingData());
|
||||
LOG("RunningEngineUpdate");
|
||||
if (aType == MediaData::Type::AUDIO_DATA && !mHasEnoughAudio) {
|
||||
OnRequestAudio();
|
||||
}
|
||||
if (aType == MediaData::Type::VIDEO_DATA && !mHasEnoughVideo) {
|
||||
OnRequestVideo();
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnRequestAudio() {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsRunningEngine() || mState.IsSeekingData());
|
||||
LOGV("OnRequestAudio");
|
||||
|
||||
if (!HasAudio()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsRequestingAudioData() || mAudioWaitRequest.Exists() || IsSeeking()) {
|
||||
LOGV(
|
||||
"No need to request audio, isRequesting=%d, waitingAudio=%d, "
|
||||
"isSeeking=%d",
|
||||
IsRequestingAudioData(), mAudioWaitRequest.Exists(), IsSeeking());
|
||||
return;
|
||||
}
|
||||
|
||||
LOGV("Start requesting audio");
|
||||
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::RequestData);
|
||||
perfRecorder.Start();
|
||||
RefPtr<ExternalEngineStateMachine> self = this;
|
||||
mReader->RequestAudioData()
|
||||
->Then(
|
||||
OwnerThread(), __func__,
|
||||
[this, self, perfRecorder(std::move(perfRecorder))](
|
||||
const RefPtr<AudioData>& aAudio) mutable {
|
||||
perfRecorder.End();
|
||||
mAudioDataRequest.Complete();
|
||||
LOGV("Completed requesting audio");
|
||||
AUTO_PROFILER_LABEL(
|
||||
"ExternalEngineStateMachine::OnRequestAudio:Resolved",
|
||||
MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(aAudio);
|
||||
RunningEngineUpdate(MediaData::Type::AUDIO_DATA);
|
||||
},
|
||||
[this, self](const MediaResult& aError) {
|
||||
mAudioDataRequest.Complete();
|
||||
AUTO_PROFILER_LABEL(
|
||||
"ExternalEngineStateMachine::OnRequestAudio:Rejected",
|
||||
MEDIA_PLAYBACK);
|
||||
LOG("OnRequestAudio ErrorName=%s Message=%s",
|
||||
aError.ErrorName().get(), aError.Message().get());
|
||||
switch (aError.Code()) {
|
||||
case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
|
||||
WaitForData(MediaData::Type::AUDIO_DATA);
|
||||
break;
|
||||
case NS_ERROR_DOM_MEDIA_CANCELED:
|
||||
OnRequestAudio();
|
||||
break;
|
||||
case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
|
||||
LOG("Reach to the end, no more audio data");
|
||||
EndOfStream(MediaData::Type::AUDIO_DATA);
|
||||
break;
|
||||
default:
|
||||
DecodeError(aError);
|
||||
}
|
||||
})
|
||||
->Track(mAudioDataRequest);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnRequestVideo() {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsRunningEngine() || mState.IsSeekingData());
|
||||
LOGV("OnRequestVideo");
|
||||
|
||||
if (!HasVideo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsRequestingVideoData() || mVideoWaitRequest.Exists() || IsSeeking()) {
|
||||
LOGV(
|
||||
"No need to request video, isRequesting=%d, waitingVideo=%d, "
|
||||
"isSeeking=%d",
|
||||
IsRequestingVideoData(), mVideoWaitRequest.Exists(), IsSeeking());
|
||||
return;
|
||||
}
|
||||
|
||||
LOGV("Start requesting video");
|
||||
PerformanceRecorder perfRecorder(PerformanceRecorder::Stage::RequestData,
|
||||
Info().mVideo.mImage.height);
|
||||
perfRecorder.Start();
|
||||
RefPtr<ExternalEngineStateMachine> self = this;
|
||||
mReader->RequestVideoData(mCurrentPosition.Ref(), false)
|
||||
->Then(
|
||||
OwnerThread(), __func__,
|
||||
[this, self, perfRecorder(std::move(perfRecorder))](
|
||||
const RefPtr<VideoData>& aVideo) mutable {
|
||||
perfRecorder.End();
|
||||
mVideoDataRequest.Complete();
|
||||
LOGV("Completed requesting video");
|
||||
AUTO_PROFILER_LABEL(
|
||||
"ExternalEngineStateMachine::OnRequestVideo:Resolved",
|
||||
MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(aVideo);
|
||||
RunningEngineUpdate(MediaData::Type::VIDEO_DATA);
|
||||
// TODO : set video into video container? Will implement this when
|
||||
// starting implementing the video output for the media engine.
|
||||
},
|
||||
[this, self](const MediaResult& aError) {
|
||||
mVideoDataRequest.Complete();
|
||||
AUTO_PROFILER_LABEL(
|
||||
"ExternalEngineStateMachine::OnRequestVideo:Rejected",
|
||||
MEDIA_PLAYBACK);
|
||||
LOG("OnRequestVideo ErrorName=%s Message=%s",
|
||||
aError.ErrorName().get(), aError.Message().get());
|
||||
switch (aError.Code()) {
|
||||
case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA:
|
||||
WaitForData(MediaData::Type::VIDEO_DATA);
|
||||
break;
|
||||
case NS_ERROR_DOM_MEDIA_CANCELED:
|
||||
OnRequestVideo();
|
||||
break;
|
||||
case NS_ERROR_DOM_MEDIA_END_OF_STREAM:
|
||||
LOG("Reach to the end, no more video data");
|
||||
EndOfStream(MediaData::Type::VIDEO_DATA);
|
||||
break;
|
||||
default:
|
||||
DecodeError(aError);
|
||||
}
|
||||
})
|
||||
->Track(mVideoDataRequest);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnLoadedFirstFrame() {
|
||||
AssertOnTaskQueue();
|
||||
MediaDecoderEventVisibility visibility =
|
||||
mSentFirstFrameLoadedEvent ? MediaDecoderEventVisibility::Suppressed
|
||||
: MediaDecoderEventVisibility::Observable;
|
||||
mSentFirstFrameLoadedEvent = true;
|
||||
mFirstFrameLoadedEvent.Notify(UniquePtr<MediaInfo>(new MediaInfo(Info())),
|
||||
visibility);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnLoadedData() {
|
||||
AssertOnTaskQueue();
|
||||
// In case the external engine doesn't send the first frame loaded event
|
||||
// correctly.
|
||||
if (!mSentFirstFrameLoadedEvent) {
|
||||
OnLoadedFirstFrame();
|
||||
}
|
||||
mOnNextFrameStatus.Notify(MediaDecoderOwner::NEXT_FRAME_AVAILABLE);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnWaiting() {
|
||||
AssertOnTaskQueue();
|
||||
mOnNextFrameStatus.Notify(
|
||||
MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnPlaying() {
|
||||
AssertOnTaskQueue();
|
||||
mOnNextFrameStatus.Notify(MediaDecoderOwner::NEXT_FRAME_AVAILABLE);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnSeeked() {
|
||||
AssertOnTaskQueue();
|
||||
MOZ_ASSERT(mState.IsSeekingData());
|
||||
auto* state = mState.AsSeekingData();
|
||||
// Engine's event could arrive before finishing reader's seeking.
|
||||
state->mWaitingEngineSeeked = false;
|
||||
CheckIfSeekCompleted();
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnBufferingStarted() {
|
||||
AssertOnTaskQueue();
|
||||
mOnNextFrameStatus.Notify(
|
||||
MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING);
|
||||
if (HasAudio()) {
|
||||
WaitForData(MediaData::Type::AUDIO_DATA);
|
||||
}
|
||||
if (HasVideo()) {
|
||||
WaitForData(MediaData::Type::VIDEO_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnBufferingEnded() {
|
||||
AssertOnTaskQueue();
|
||||
mOnNextFrameStatus.Notify(MediaDecoderOwner::NEXT_FRAME_AVAILABLE);
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnEnded() {
|
||||
AssertOnTaskQueue();
|
||||
if (mSentPlaybackEndedEvent) {
|
||||
return;
|
||||
}
|
||||
LOG("Playback is ended");
|
||||
mOnNextFrameStatus.Notify(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE);
|
||||
mOnPlaybackEvent.Notify(MediaPlaybackEvent::PlaybackEnded);
|
||||
mSentPlaybackEndedEvent = true;
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::OnTimeupdate() {
|
||||
AssertOnTaskQueue();
|
||||
if (IsSeeking()) {
|
||||
return;
|
||||
}
|
||||
mCurrentPosition = mEngine->GetCurrentPosition();
|
||||
if (mDuration.Ref().ref() < mCurrentPosition.Ref()) {
|
||||
mDuration = Some(mCurrentPosition.Ref());
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEngineStateMachine::NotifyEventInternal(
|
||||
ExternalEngineEvent aEvent) {
|
||||
AssertOnTaskQueue();
|
||||
AUTO_PROFILER_LABEL("ExternalEngineStateMachine::NotifyEventInternal",
|
||||
MEDIA_PLAYBACK);
|
||||
LOG("Receive event %s", ExternalEngineEventToStr(aEvent));
|
||||
switch (aEvent) {
|
||||
case ExternalEngineEvent::LoadedMetaData:
|
||||
// We read metadata by ourselves, ignore this if there is any.
|
||||
MOZ_ASSERT(mInfo);
|
||||
break;
|
||||
case ExternalEngineEvent::LoadedFirstFrame:
|
||||
OnLoadedFirstFrame();
|
||||
break;
|
||||
case ExternalEngineEvent::LoadedData:
|
||||
OnLoadedData();
|
||||
break;
|
||||
case ExternalEngineEvent::Waiting:
|
||||
OnWaiting();
|
||||
break;
|
||||
case ExternalEngineEvent::Playing:
|
||||
OnPlaying();
|
||||
break;
|
||||
case ExternalEngineEvent::Seeked:
|
||||
OnSeeked();
|
||||
break;
|
||||
case ExternalEngineEvent::BufferingStarted:
|
||||
OnBufferingStarted();
|
||||
break;
|
||||
case ExternalEngineEvent::BufferingEnded:
|
||||
OnBufferingEnded();
|
||||
break;
|
||||
case ExternalEngineEvent::Timeupdate:
|
||||
OnTimeupdate();
|
||||
break;
|
||||
case ExternalEngineEvent::Ended:
|
||||
OnEnded();
|
||||
break;
|
||||
case ExternalEngineEvent::RequestForAudio:
|
||||
mHasEnoughAudio = false;
|
||||
RunningEngineUpdate(MediaData::Type::AUDIO_DATA);
|
||||
break;
|
||||
case ExternalEngineEvent::RequestForVideo:
|
||||
mHasEnoughVideo = false;
|
||||
RunningEngineUpdate(MediaData::Type::VIDEO_DATA);
|
||||
break;
|
||||
case ExternalEngineEvent::AudioEnough:
|
||||
mHasEnoughAudio = true;
|
||||
break;
|
||||
case ExternalEngineEvent::VideoEnough:
|
||||
mHasEnoughVideo = true;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Undefined event!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef FMT
|
||||
#undef LOG
|
||||
#undef LOGV
|
||||
#undef LOGW
|
||||
#undef LOGE
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,278 +0,0 @@
|
|||
/* 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 DOM_MEDIA_EXTERNALENGINESTATEMACHINE_H_
|
||||
#define DOM_MEDIA_EXTERNALENGINESTATEMACHINE_H_
|
||||
|
||||
#include "MediaDecoderStateMachineBase.h"
|
||||
#include "SeekJob.h"
|
||||
#include "mozilla/Variant.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* ExternalPlaybackEngine represents a media engine which is responsible for
|
||||
* decoding and playback, which are not controlled by Gecko.
|
||||
*/
|
||||
class ExternalPlaybackEngine;
|
||||
|
||||
enum class ExternalEngineEvent {
|
||||
LoadedMetaData,
|
||||
LoadedFirstFrame,
|
||||
LoadedData,
|
||||
Waiting,
|
||||
Playing,
|
||||
Seeked,
|
||||
BufferingStarted,
|
||||
BufferingEnded,
|
||||
Timeupdate,
|
||||
Ended,
|
||||
RequestForAudio,
|
||||
RequestForVideo,
|
||||
AudioEnough,
|
||||
VideoEnough,
|
||||
};
|
||||
const char* ExternalEngineEventToStr(ExternalEngineEvent aEvent);
|
||||
|
||||
/**
|
||||
* When using ExternalEngineStateMachine, that means we use an external engine
|
||||
* to control decoding and playback (including A/V sync). Eg. Media Foundation
|
||||
* Media Engine on Windows.
|
||||
*
|
||||
* The external engine does most of playback works, and uses ExternalEngineEvent
|
||||
* to tell us its internal state. Therefore, this state machine is responsible
|
||||
* to address those events from the engine and coordinate the format reader in
|
||||
* order to provide data to the engine correctly.
|
||||
*/
|
||||
DDLoggedTypeDeclName(ExternalEngineStateMachine);
|
||||
|
||||
class ExternalEngineStateMachine final
|
||||
: public MediaDecoderStateMachineBase,
|
||||
public DecoderDoctorLifeLogger<ExternalEngineStateMachine> {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExternalEngineStateMachine, override)
|
||||
|
||||
ExternalEngineStateMachine(MediaDecoder* aDecoder,
|
||||
MediaFormatReader* aReader);
|
||||
|
||||
RefPtr<GenericPromise> InvokeSetSink(
|
||||
const RefPtr<AudioDeviceInfo>& aSink) override;
|
||||
|
||||
// The media sample would be managed by the external engine so we won't store
|
||||
// any samples in our side.
|
||||
size_t SizeOfVideoQueue() const override { return 0; }
|
||||
size_t SizeOfAudioQueue() const override { return 0; }
|
||||
|
||||
// Not supported.
|
||||
void SetVideoDecodeMode(VideoDecodeMode aMode) override {}
|
||||
void InvokeSuspendMediaSink() override {}
|
||||
void InvokeResumeMediaSink() override {}
|
||||
RefPtr<GenericPromise> RequestDebugInfo(
|
||||
dom::MediaDecoderStateMachineDebugInfo& aInfo) override {
|
||||
// This debug info doesn't fit in this scenario because most decoding
|
||||
// details are only visible inside the external engine.
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
|
||||
void NotifyEvent(ExternalEngineEvent aEvent) {
|
||||
// On the engine manager thread.
|
||||
Unused << OwnerThread()->Dispatch(NS_NewRunnableFunction(
|
||||
"ExternalEngineStateMachine::NotifyEvent",
|
||||
[self = RefPtr{this}, aEvent] { self->NotifyEventInternal(aEvent); }));
|
||||
}
|
||||
|
||||
const char* GetStateStr() const;
|
||||
|
||||
private:
|
||||
~ExternalEngineStateMachine() = default;
|
||||
|
||||
void AssertOnTaskQueue() const { MOZ_ASSERT(OnTaskQueue()); }
|
||||
|
||||
// A light-weight state object that helps to store some variables which would
|
||||
// only be used in a certain state. Also be able to do the cleaning for the
|
||||
// state transition. Only modify on the task queue.
|
||||
struct StateObject final {
|
||||
enum class State {
|
||||
InitEngine,
|
||||
ReadingMetadata,
|
||||
RunningEngine,
|
||||
SeekingData,
|
||||
ShutdownEngine,
|
||||
};
|
||||
struct InitEngine {
|
||||
InitEngine() = default;
|
||||
~InitEngine() { mEngineInitRequest.DisconnectIfExists(); }
|
||||
MozPromiseRequestHolder<GenericNonExclusivePromise> mEngineInitRequest;
|
||||
RefPtr<GenericNonExclusivePromise> mInitPromise;
|
||||
};
|
||||
struct ReadingMetadata {
|
||||
ReadingMetadata() = default;
|
||||
~ReadingMetadata() { mMetadataRequest.DisconnectIfExists(); }
|
||||
MozPromiseRequestHolder<MediaFormatReader::MetadataPromise>
|
||||
mMetadataRequest;
|
||||
};
|
||||
struct RunningEngine {};
|
||||
struct SeekingData {
|
||||
SeekingData() = default;
|
||||
SeekingData(SeekingData&&) = default;
|
||||
SeekingData(const SeekingData&) = delete;
|
||||
SeekingData& operator=(const SeekingData&) = delete;
|
||||
~SeekingData() {
|
||||
mSeekJob.RejectIfExists(__func__);
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
}
|
||||
void SetTarget(const SeekTarget& aTarget) {
|
||||
// If there is any promise for previous seeking, reject it first.
|
||||
mSeekJob.RejectIfExists(__func__);
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
// Then create a new seek job.
|
||||
mSeekJob = SeekJob();
|
||||
mSeekJob.mTarget = Some(aTarget);
|
||||
}
|
||||
void Resolve(const char* aCallSite) {
|
||||
MOZ_ASSERT(mSeekJob.Exists());
|
||||
mSeekJob.Resolve(aCallSite);
|
||||
mSeekJob = SeekJob();
|
||||
}
|
||||
void RejectIfExists(const char* aCallSite) {
|
||||
mSeekJob.RejectIfExists(aCallSite);
|
||||
}
|
||||
bool IsSeeking() const { return mSeekRequest.Exists(); }
|
||||
// Set it to true when starting seeking, and would be set to false after
|
||||
// receiving engine's `seeked` event. Used on thhe task queue only.
|
||||
bool mWaitingEngineSeeked = false;
|
||||
bool mWaitingReaderSeeked = false;
|
||||
MozPromiseRequestHolder<MediaFormatReader::SeekPromise> mSeekRequest;
|
||||
SeekJob mSeekJob;
|
||||
};
|
||||
struct ShutdownEngine {};
|
||||
|
||||
StateObject() : mData(InitEngine()), mName(State::InitEngine){};
|
||||
explicit StateObject(ReadingMetadata&& aArg)
|
||||
: mData(std::move(aArg)), mName(State::ReadingMetadata){};
|
||||
explicit StateObject(RunningEngine&& aArg)
|
||||
: mData(std::move(aArg)), mName(State::RunningEngine){};
|
||||
explicit StateObject(SeekingData&& aArg)
|
||||
: mData(std::move(aArg)), mName(State::SeekingData){};
|
||||
explicit StateObject(ShutdownEngine&& aArg)
|
||||
: mData(std::move(aArg)), mName(State::ShutdownEngine){};
|
||||
|
||||
bool IsInitEngine() const { return mData.is<InitEngine>(); }
|
||||
bool IsReadingMetadata() const { return mData.is<ReadingMetadata>(); }
|
||||
bool IsRunningEngine() const { return mData.is<RunningEngine>(); }
|
||||
bool IsSeekingData() const { return mData.is<SeekingData>(); }
|
||||
bool IsShutdownEngine() const { return mData.is<ShutdownEngine>(); }
|
||||
|
||||
InitEngine* AsInitEngine() {
|
||||
return IsInitEngine() ? &mData.as<InitEngine>() : nullptr;
|
||||
}
|
||||
ReadingMetadata* AsReadingMetadata() {
|
||||
return IsReadingMetadata() ? &mData.as<ReadingMetadata>() : nullptr;
|
||||
}
|
||||
SeekingData* AsSeekingData() {
|
||||
return IsSeekingData() ? &mData.as<SeekingData>() : nullptr;
|
||||
}
|
||||
|
||||
Variant<InitEngine, ReadingMetadata, RunningEngine, SeekingData,
|
||||
ShutdownEngine>
|
||||
mData;
|
||||
State mName;
|
||||
} mState;
|
||||
using State = StateObject::State;
|
||||
|
||||
void NotifyEventInternal(ExternalEngineEvent aEvent);
|
||||
|
||||
RefPtr<ShutdownPromise> Shutdown() override;
|
||||
RefPtr<ShutdownPromise> ShutdownInternal();
|
||||
|
||||
void SetPlaybackRate(double aPlaybackRate) override;
|
||||
void BufferedRangeUpdated() override;
|
||||
void VolumeChanged() override;
|
||||
void PreservesPitchChanged() override;
|
||||
void PlayStateChanged() override;
|
||||
void LoopingChanged() override;
|
||||
|
||||
// Not supported.
|
||||
void SetIsLiveStream(bool aIsLiveStream) override {}
|
||||
void SetCanPlayThrough(bool aCanPlayThrough) override {}
|
||||
void SetFragmentEndTime(const media::TimeUnit& aFragmentEndTime) override {}
|
||||
|
||||
void OnEngineInitSuccess();
|
||||
void OnEngineInitFailure();
|
||||
|
||||
void ReadMetadata();
|
||||
void OnMetadataRead(MetadataHolder&& aMetadata);
|
||||
void OnMetadataNotRead(const MediaResult& aError);
|
||||
|
||||
// Functions for handling external engine event.
|
||||
void OnLoadedFirstFrame();
|
||||
void OnLoadedData();
|
||||
void OnWaiting();
|
||||
void OnPlaying();
|
||||
void OnSeeked();
|
||||
void OnBufferingStarted();
|
||||
void OnBufferingEnded();
|
||||
void OnTimeupdate();
|
||||
void OnEnded();
|
||||
void OnRequestAudio();
|
||||
void OnRequestVideo();
|
||||
|
||||
void ResetDecode();
|
||||
|
||||
void EndOfStream(MediaData::Type aType);
|
||||
void WaitForData(MediaData::Type aType);
|
||||
|
||||
void StartRunningEngine();
|
||||
void RunningEngineUpdate(MediaData::Type aType);
|
||||
|
||||
void ChangeStateTo(State aNextState);
|
||||
static const char* StateToStr(State aState);
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> Seek(const SeekTarget& aTarget) override;
|
||||
void SeekReader();
|
||||
void OnSeekResolved(const media::TimeUnit& aUnit);
|
||||
void OnSeekRejected(const SeekRejectValue& aReject);
|
||||
bool IsSeeking();
|
||||
void CheckIfSeekCompleted();
|
||||
|
||||
void MaybeFinishWaitForData();
|
||||
|
||||
UniquePtr<ExternalPlaybackEngine> mEngine;
|
||||
|
||||
bool mHasEnoughAudio = false;
|
||||
bool mHasEnoughVideo = false;
|
||||
bool mSentPlaybackEndedEvent = false;
|
||||
};
|
||||
|
||||
class ExternalPlaybackEngine {
|
||||
public:
|
||||
explicit ExternalPlaybackEngine(ExternalEngineStateMachine* aOwner)
|
||||
: mOwner(aOwner) {}
|
||||
|
||||
virtual ~ExternalPlaybackEngine() = default;
|
||||
|
||||
// Init the engine and specify the preload request.
|
||||
virtual RefPtr<GenericNonExclusivePromise> Init(bool aShouldPreload);
|
||||
virtual void Shutdown() = 0;
|
||||
virtual uint64_t Id() const = 0;
|
||||
|
||||
// Following methods should only be called after successfully initialize the
|
||||
// external engine.
|
||||
virtual void Play() = 0;
|
||||
virtual void Pause() = 0;
|
||||
virtual void Seek(const media::TimeUnit& aTargetTime) = 0;
|
||||
virtual void SetPlaybackRate(double aPlaybackRate) = 0;
|
||||
virtual void SetVolume(double aVolume) = 0;
|
||||
virtual void SetLooping(bool aLooping);
|
||||
virtual void SetPreservesPitch(bool aPreservesPitch) = 0;
|
||||
virtual media::TimeUnit GetCurrentPosition() = 0;
|
||||
virtual void NotifyEndOfStream(TrackInfo::TrackType aType) = 0;
|
||||
virtual void SetMediaInfo(const MediaInfo& aInfo) = 0;
|
||||
|
||||
ExternalEngineStateMachine* const MOZ_NON_OWNING_REF mOwner;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_EXTERNALENGINESTATEMACHINE_H_
|
|
@ -11,7 +11,7 @@
|
|||
#include "DecoderBenchmark.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "Layers.h"
|
||||
#include "MediaDecoderStateMachineBase.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "MediaFormatReader.h"
|
||||
#include "MediaResource.h"
|
||||
#include "MediaShutdownManager.h"
|
||||
|
@ -1188,7 +1188,7 @@ void MediaDecoder::SetStreamName(const nsAutoString& aStreamName) {
|
|||
mStreamName = aStreamName;
|
||||
}
|
||||
|
||||
void MediaDecoder::ConnectMirrors(MediaDecoderStateMachineBase* aObject) {
|
||||
void MediaDecoder::ConnectMirrors(MediaDecoderStateMachine* aObject) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aObject);
|
||||
mStateMachineDuration.Connect(aObject->CanonicalDuration());
|
||||
|
@ -1205,17 +1205,16 @@ void MediaDecoder::DisconnectMirrors() {
|
|||
mIsAudioDataAudible.DisconnectIfConnected();
|
||||
}
|
||||
|
||||
void MediaDecoder::SetStateMachine(
|
||||
MediaDecoderStateMachineBase* aStateMachine) {
|
||||
void MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine);
|
||||
if (aStateMachine) {
|
||||
mDecoderStateMachine = aStateMachine;
|
||||
LOG("set state machine %p", mDecoderStateMachine.get());
|
||||
DDLINKCHILD("decoder state machine", mDecoderStateMachine.get());
|
||||
ConnectMirrors(aStateMachine);
|
||||
UpdateVideoDecodeMode();
|
||||
} else if (mDecoderStateMachine) {
|
||||
LOG("null out state machine %p", mDecoderStateMachine.get());
|
||||
DDUNLINKCHILD(mDecoderStateMachine.get());
|
||||
mDecoderStateMachine = nullptr;
|
||||
DisconnectMirrors();
|
||||
}
|
||||
|
@ -1283,7 +1282,7 @@ void MediaDecoder::NotifyReaderDataArrived() {
|
|||
}
|
||||
|
||||
// Provide access to the state machine object
|
||||
MediaDecoderStateMachineBase* MediaDecoder::GetStateMachine() const {
|
||||
MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mDecoderStateMachine;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class ProcessedMediaTrack;
|
|||
class FrameStatistics;
|
||||
class VideoFrameContainer;
|
||||
class MediaFormatReader;
|
||||
class MediaDecoderStateMachineBase;
|
||||
class MediaDecoderStateMachine;
|
||||
struct MediaPlaybackEvent;
|
||||
struct SharedDummyTrack;
|
||||
|
||||
|
@ -258,8 +258,8 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
|
|||
// SetLoadInBackground() on mResource.
|
||||
virtual void SetLoadInBackground(bool aLoadInBackground) {}
|
||||
|
||||
MediaDecoderStateMachineBase* GetStateMachine() const;
|
||||
void SetStateMachine(MediaDecoderStateMachineBase* aStateMachine);
|
||||
MediaDecoderStateMachine* GetStateMachine() const;
|
||||
void SetStateMachine(MediaDecoderStateMachine* aStateMachine);
|
||||
|
||||
// Constructs the time ranges representing what segments of the media
|
||||
// are buffered and playable.
|
||||
|
@ -495,7 +495,7 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
|
|||
|
||||
void FinishShutdown();
|
||||
|
||||
void ConnectMirrors(MediaDecoderStateMachineBase* aObject);
|
||||
void ConnectMirrors(MediaDecoderStateMachine* aObject);
|
||||
void DisconnectMirrors();
|
||||
|
||||
virtual bool CanPlayThroughImpl() = 0;
|
||||
|
@ -507,7 +507,7 @@ class MediaDecoder : public DecoderDoctorLifeLogger<MediaDecoder> {
|
|||
// is safe to access it during this period.
|
||||
//
|
||||
// Explicitly prievate to force access via accessors.
|
||||
RefPtr<MediaDecoderStateMachineBase> mDecoderStateMachine;
|
||||
RefPtr<MediaDecoderStateMachine> mDecoderStateMachine;
|
||||
|
||||
protected:
|
||||
void NotifyReaderDataArrived();
|
||||
|
|
|
@ -2757,17 +2757,32 @@ RefPtr<ShutdownPromise> MediaDecoderStateMachine::ShutdownState::Enter() {
|
|||
|
||||
MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
MediaFormatReader* aReader)
|
||||
: MediaDecoderStateMachineBase(aDecoder, aReader),
|
||||
: mDecoderID(aDecoder),
|
||||
mAbstractMainThread(aDecoder->AbstractMainThread()),
|
||||
mFrameStats(&aDecoder->GetFrameStatistics()),
|
||||
mVideoFrameContainer(aDecoder->GetVideoFrameContainer()),
|
||||
mTaskQueue(TaskQueue::Create(GetMediaThreadPool(MediaThreadType::MDSM),
|
||||
"MDSM::mTaskQueue",
|
||||
/* aSupportsTailDispatch = */ true)),
|
||||
mWatchManager(this, mTaskQueue),
|
||||
mDispatchedStateMachine(false),
|
||||
mDelayedScheduler(mTaskQueue, true /*aFuzzy*/),
|
||||
mCurrentFrameID(0),
|
||||
mReader(new ReaderProxy(mTaskQueue, aReader)),
|
||||
mPlaybackRate(1.0),
|
||||
mAmpleAudioThreshold(detail::AMPLE_AUDIO_THRESHOLD),
|
||||
mMinimizePreroll(aDecoder->GetMinimizePreroll()),
|
||||
mSentFirstFrameLoadedEvent(false),
|
||||
mVideoDecodeSuspended(false),
|
||||
mVideoDecodeSuspendTimer(mTaskQueue),
|
||||
mVideoDecodeMode(VideoDecodeMode::Normal),
|
||||
mIsMSE(aDecoder->IsMSE()),
|
||||
mSeamlessLoopingAllowed(false),
|
||||
INIT_MIRROR(mBuffered, TimeIntervals()),
|
||||
INIT_MIRROR(mPlayState, MediaDecoder::PLAY_STATE_LOADING),
|
||||
INIT_MIRROR(mVolume, 1.0),
|
||||
INIT_MIRROR(mPreservesPitch, true),
|
||||
INIT_MIRROR(mLooping, false),
|
||||
INIT_MIRROR(mStreamName, nsAutoString()),
|
||||
INIT_MIRROR(mSinkDevice, nullptr),
|
||||
INIT_MIRROR(mSecondaryVideoContainer, nullptr),
|
||||
|
@ -2777,7 +2792,10 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||
INIT_MIRROR(mOutputPrincipal, PRINCIPAL_HANDLE_NONE),
|
||||
INIT_CANONICAL(mCanonicalOutputTracks,
|
||||
nsTArray<RefPtr<ProcessedMediaTrack>>()),
|
||||
INIT_CANONICAL(mCanonicalOutputPrincipal, PRINCIPAL_HANDLE_NONE) {
|
||||
INIT_CANONICAL(mCanonicalOutputPrincipal, PRINCIPAL_HANDLE_NONE),
|
||||
INIT_CANONICAL(mDuration, NullableTimeUnit()),
|
||||
INIT_CANONICAL(mCurrentPosition, TimeUnit::Zero()),
|
||||
INIT_CANONICAL(mIsAudioDataAudible, false) {
|
||||
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
|
@ -2800,9 +2818,13 @@ void MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder) {
|
|||
MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
MediaDecoderStateMachineBase::InitializationTask(aDecoder);
|
||||
|
||||
// Connect mirrors.
|
||||
mBuffered.Connect(mReader->CanonicalBuffered());
|
||||
mPlayState.Connect(aDecoder->CanonicalPlayState());
|
||||
mVolume.Connect(aDecoder->CanonicalVolume());
|
||||
mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch());
|
||||
mLooping.Connect(aDecoder->CanonicalLooping());
|
||||
mStreamName.Connect(aDecoder->CanonicalStreamName());
|
||||
mSinkDevice.Connect(aDecoder->CanonicalSinkDevice());
|
||||
mSecondaryVideoContainer.Connect(
|
||||
aDecoder->CanonicalSecondaryVideoContainer());
|
||||
|
@ -2812,6 +2834,13 @@ void MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder) {
|
|||
mOutputPrincipal.Connect(aDecoder->CanonicalOutputPrincipal());
|
||||
|
||||
// Initialize watchers.
|
||||
mWatchManager.Watch(mBuffered,
|
||||
&MediaDecoderStateMachine::BufferedRangeUpdated);
|
||||
mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
|
||||
mWatchManager.Watch(mPreservesPitch,
|
||||
&MediaDecoderStateMachine::PreservesPitchChanged);
|
||||
mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
|
||||
mWatchManager.Watch(mLooping, &MediaDecoderStateMachine::LoopingChanged);
|
||||
mWatchManager.Watch(mStreamName,
|
||||
&MediaDecoderStateMachine::StreamNameChanged);
|
||||
mWatchManager.Watch(mSecondaryVideoContainer,
|
||||
|
@ -2977,18 +3006,27 @@ void MediaDecoderStateMachine::SetMediaNotSeekable() { mMediaSeekable = false; }
|
|||
nsresult MediaDecoderStateMachine::Init(MediaDecoder* aDecoder) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = MediaDecoderStateMachineBase::Init(aDecoder);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
// Dispatch initialization that needs to happen on that task queue.
|
||||
nsCOMPtr<nsIRunnable> r = NewRunnableMethod<RefPtr<MediaDecoder>>(
|
||||
"MediaDecoderStateMachine::InitializationTask", this,
|
||||
&MediaDecoderStateMachine::InitializationTask, aDecoder);
|
||||
mTaskQueue->DispatchStateChange(r.forget());
|
||||
|
||||
mAudioQueueListener = AudioQueue().PopFrontEvent().Connect(
|
||||
mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped);
|
||||
mVideoQueueListener = VideoQueue().PopFrontEvent().Connect(
|
||||
mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped);
|
||||
|
||||
mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
|
||||
|
||||
mOnMediaNotSeekable = mReader->OnMediaNotSeekable().Connect(
|
||||
OwnerThread(), this, &MediaDecoderStateMachine::SetMediaNotSeekable);
|
||||
|
||||
nsresult rv = mReader->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mReader->SetCanonicalDuration(&mDuration);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3247,6 +3285,12 @@ RefPtr<MediaDecoder::SeekPromise> MediaDecoderStateMachine::Seek(
|
|||
return mStateObj->HandleSeek(aTarget);
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> MediaDecoderStateMachine::InvokeSeek(
|
||||
const SeekTarget& aTarget) {
|
||||
return InvokeAsync(OwnerThread(), this, __func__,
|
||||
&MediaDecoderStateMachine::Seek, aTarget);
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::StopMediaSink() {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
if (mMediaSink->IsStarted()) {
|
||||
|
@ -3538,6 +3582,15 @@ bool MediaDecoderStateMachine::HasLowBufferedData(const TimeUnit& aThreshold) {
|
|||
return !mBuffered.Ref().Contains(interval);
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::DecodeError(const MediaResult& aError) {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
LOGE("Decode error: %s", aError.Description().get());
|
||||
PROFILER_MARKER_TEXT("MDSM::DecodeError", MEDIA_PLAYBACK, {},
|
||||
aError.Description());
|
||||
// Notify the decode error and MediaDecoder will shut down MDSM.
|
||||
mOnPlaybackErrorEvent.Notify(aError);
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent() {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
// Track value of mSentFirstFrameLoadedEvent from before updating it
|
||||
|
@ -3566,6 +3619,12 @@ void MediaDecoderStateMachine::FinishDecodeFirstFrame() {
|
|||
EnqueueFirstFrameLoadedEvent();
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise> MediaDecoderStateMachine::BeginShutdown() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return InvokeAsync(OwnerThread(), this, __func__,
|
||||
&MediaDecoderStateMachine::Shutdown);
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise> MediaDecoderStateMachine::FinishShutdown() {
|
||||
AUTO_PROFILER_LABEL("MediaDecoderStateMachine::FinishShutdown",
|
||||
MEDIA_PLAYBACK);
|
||||
|
@ -3709,6 +3768,10 @@ void MediaDecoderStateMachine::ScheduleStateMachineIn(const TimeUnit& aTime) {
|
|||
[]() { MOZ_DIAGNOSTIC_ASSERT(false); });
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::OnTaskQueue() const {
|
||||
return OwnerThread()->IsCurrentThreadIn();
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::IsStateMachineScheduled() const {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return mDispatchedStateMachine || mDelayedScheduler.IsScheduled();
|
||||
|
@ -4074,6 +4137,15 @@ size_t MediaDecoderStateMachine::SizeOfAudioQueue() const {
|
|||
return functor.mSize;
|
||||
}
|
||||
|
||||
AbstractCanonical<media::TimeIntervals>*
|
||||
MediaDecoderStateMachine::CanonicalBuffered() const {
|
||||
return mReader->CanonicalBuffered();
|
||||
}
|
||||
|
||||
MediaEventSource<void>& MediaDecoderStateMachine::OnMediaNotSeekable() const {
|
||||
return mReader->OnMediaNotSeekable();
|
||||
}
|
||||
|
||||
const char* MediaDecoderStateMachine::AudioRequestStatus() const {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
if (IsRequestingAudioData()) {
|
||||
|
|
|
@ -3,38 +3,6 @@
|
|||
/* 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/. */
|
||||
#if !defined(MediaDecoderStateMachine_h__)
|
||||
# define MediaDecoderStateMachine_h__
|
||||
|
||||
# include "AudioDeviceInfo.h"
|
||||
# include "ImageContainer.h"
|
||||
# include "MediaDecoder.h"
|
||||
# include "MediaDecoderOwner.h"
|
||||
# include "MediaDecoderStateMachineBase.h"
|
||||
# include "MediaFormatReader.h"
|
||||
# include "MediaQueue.h"
|
||||
# include "MediaSink.h"
|
||||
# include "MediaStatistics.h"
|
||||
# include "MediaTimer.h"
|
||||
# include "SeekJob.h"
|
||||
# include "mozilla/Attributes.h"
|
||||
# include "mozilla/ReentrantMonitor.h"
|
||||
# include "mozilla/StateMirroring.h"
|
||||
# include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AbstractThread;
|
||||
class AudioSegment;
|
||||
class DecodedStream;
|
||||
class DOMMediaStream;
|
||||
class ReaderProxy;
|
||||
class TaskQueue;
|
||||
|
||||
extern LazyLogModule gMediaDecoderLog;
|
||||
|
||||
DDLoggedTypeDeclName(MediaDecoderStateMachine);
|
||||
|
||||
/*
|
||||
|
||||
Each media element for a media file has one thread called the "audio thread".
|
||||
|
@ -110,19 +78,93 @@ monitor) if the audio queue is empty. Otherwise it constantly pops
|
|||
audio data off the queue and plays it with a blocking write to the audio
|
||||
hardware (via AudioStream).
|
||||
|
||||
*/
|
||||
#if !defined(MediaDecoderStateMachine_h__)
|
||||
# define MediaDecoderStateMachine_h__
|
||||
|
||||
# include "AudioDeviceInfo.h"
|
||||
# include "ImageContainer.h"
|
||||
# include "MediaDecoder.h"
|
||||
# include "MediaDecoderOwner.h"
|
||||
# include "MediaEventSource.h"
|
||||
# include "MediaFormatReader.h"
|
||||
# include "MediaMetadataManager.h"
|
||||
# include "MediaQueue.h"
|
||||
# include "MediaSink.h"
|
||||
# include "MediaStatistics.h"
|
||||
# include "MediaTimer.h"
|
||||
# include "SeekJob.h"
|
||||
# include "mozilla/Attributes.h"
|
||||
# include "mozilla/ReentrantMonitor.h"
|
||||
# include "mozilla/StateMirroring.h"
|
||||
# include "mozilla/dom/MediaDebugInfoBinding.h"
|
||||
# include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AbstractThread;
|
||||
class AudioSegment;
|
||||
class DecodedStream;
|
||||
class DOMMediaStream;
|
||||
class ReaderProxy;
|
||||
class TaskQueue;
|
||||
|
||||
extern LazyLogModule gMediaDecoderLog;
|
||||
|
||||
struct MediaPlaybackEvent {
|
||||
enum EventType {
|
||||
PlaybackStarted,
|
||||
PlaybackStopped,
|
||||
PlaybackProgressed,
|
||||
PlaybackEnded,
|
||||
SeekStarted,
|
||||
Invalidate,
|
||||
EnterVideoSuspend,
|
||||
ExitVideoSuspend,
|
||||
StartVideoSuspendTimer,
|
||||
CancelVideoSuspendTimer,
|
||||
VideoOnlySeekBegin,
|
||||
VideoOnlySeekCompleted,
|
||||
} mType;
|
||||
|
||||
using DataType = Variant<Nothing, int64_t>;
|
||||
DataType mData;
|
||||
|
||||
MOZ_IMPLICIT MediaPlaybackEvent(EventType aType)
|
||||
: mType(aType), mData(Nothing{}) {}
|
||||
|
||||
template <typename T>
|
||||
MediaPlaybackEvent(EventType aType, T&& aArg)
|
||||
: mType(aType), mData(std::forward<T>(aArg)) {}
|
||||
};
|
||||
|
||||
enum class VideoDecodeMode : uint8_t { Normal, Suspend };
|
||||
|
||||
DDLoggedTypeDeclName(MediaDecoderStateMachine);
|
||||
|
||||
/*
|
||||
The state machine class. This manages the decoding and seeking in the
|
||||
MediaDecoderReader on the decode task queue, and A/V sync on the shared
|
||||
state machine thread, and controls the audio "push" thread.
|
||||
|
||||
All internal state is synchronised via the decoder monitor. State changes
|
||||
are propagated by scheduling the state machine to run another cycle on the
|
||||
shared state machine thread.
|
||||
|
||||
See MediaDecoder.h for more details.
|
||||
*/
|
||||
class MediaDecoderStateMachine
|
||||
: public MediaDecoderStateMachineBase,
|
||||
public DecoderDoctorLifeLogger<MediaDecoderStateMachine> {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachine, override)
|
||||
: public DecoderDoctorLifeLogger<MediaDecoderStateMachine> {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachine)
|
||||
|
||||
using TrackSet = MediaFormatReader::TrackSet;
|
||||
|
||||
public:
|
||||
typedef MediaDecoderOwner::NextFrameStatus NextFrameStatus;
|
||||
typedef mozilla::layers::ImageContainer::FrameID FrameID;
|
||||
MediaDecoderStateMachine(MediaDecoder* aDecoder, MediaFormatReader* aReader);
|
||||
|
||||
nsresult Init(MediaDecoder* aDecoder) override;
|
||||
nsresult Init(MediaDecoder* aDecoder);
|
||||
|
||||
// Enumeration for the valid decoding states
|
||||
enum State {
|
||||
|
@ -140,21 +182,103 @@ class MediaDecoderStateMachine
|
|||
DECODER_STATE_SHUTDOWN
|
||||
};
|
||||
|
||||
// Returns the state machine task queue.
|
||||
TaskQueue* OwnerThread() const { return mTaskQueue; }
|
||||
|
||||
RefPtr<GenericPromise> RequestDebugInfo(
|
||||
dom::MediaDecoderStateMachineDebugInfo& aInfo) override;
|
||||
dom::MediaDecoderStateMachineDebugInfo& aInfo);
|
||||
|
||||
size_t SizeOfVideoQueue() const override;
|
||||
// Seeks to the decoder to aTarget asynchronously.
|
||||
RefPtr<MediaDecoder::SeekPromise> InvokeSeek(const SeekTarget& aTarget);
|
||||
|
||||
size_t SizeOfAudioQueue() const override;
|
||||
void DispatchSetPlaybackRate(double aPlaybackRate) {
|
||||
OwnerThread()->DispatchStateChange(NewRunnableMethod<double>(
|
||||
"MediaDecoderStateMachine::SetPlaybackRate", this,
|
||||
&MediaDecoderStateMachine::SetPlaybackRate, aPlaybackRate));
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise> BeginShutdown();
|
||||
|
||||
// Set the media fragment end time.
|
||||
void DispatchSetFragmentEndTime(const media::TimeUnit& aEndTime) {
|
||||
RefPtr<MediaDecoderStateMachine> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
"MediaDecoderStateMachine::DispatchSetFragmentEndTime",
|
||||
[self, aEndTime]() {
|
||||
// A negative number means we don't have a fragment end time at all.
|
||||
self->mFragmentEndTime = aEndTime >= media::TimeUnit::Zero()
|
||||
? aEndTime
|
||||
: media::TimeUnit::Invalid();
|
||||
});
|
||||
nsresult rv = OwnerThread()->Dispatch(r.forget());
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
|
||||
Unused << rv;
|
||||
}
|
||||
|
||||
void DispatchCanPlayThrough(bool aCanPlayThrough) {
|
||||
RefPtr<MediaDecoderStateMachine> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
"MediaDecoderStateMachine::DispatchCanPlayThrough",
|
||||
[self, aCanPlayThrough]() { self->mCanPlayThrough = aCanPlayThrough; });
|
||||
OwnerThread()->DispatchStateChange(r.forget());
|
||||
}
|
||||
|
||||
void DispatchIsLiveStream(bool aIsLiveStream) {
|
||||
RefPtr<MediaDecoderStateMachine> self = this;
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
"MediaDecoderStateMachine::DispatchIsLiveStream",
|
||||
[self, aIsLiveStream]() { self->mIsLiveStream = aIsLiveStream; });
|
||||
OwnerThread()->DispatchStateChange(r.forget());
|
||||
}
|
||||
|
||||
TimedMetadataEventSource& TimedMetadataEvent() {
|
||||
return mMetadataManager.TimedMetadataEvent();
|
||||
}
|
||||
|
||||
MediaEventSource<void>& OnMediaNotSeekable() const;
|
||||
|
||||
MediaEventSourceExc<UniquePtr<MediaInfo>, UniquePtr<MetadataTags>,
|
||||
MediaDecoderEventVisibility>&
|
||||
MetadataLoadedEvent() {
|
||||
return mMetadataLoadedEvent;
|
||||
}
|
||||
|
||||
MediaEventSourceExc<UniquePtr<MediaInfo>, MediaDecoderEventVisibility>&
|
||||
FirstFrameLoadedEvent() {
|
||||
return mFirstFrameLoadedEvent;
|
||||
}
|
||||
|
||||
MediaEventSource<MediaPlaybackEvent>& OnPlaybackEvent() {
|
||||
return mOnPlaybackEvent;
|
||||
}
|
||||
MediaEventSource<MediaResult>& OnPlaybackErrorEvent() {
|
||||
return mOnPlaybackErrorEvent;
|
||||
}
|
||||
|
||||
MediaEventSource<DecoderDoctorEvent>& OnDecoderDoctorEvent() {
|
||||
return mOnDecoderDoctorEvent;
|
||||
}
|
||||
|
||||
MediaEventSource<NextFrameStatus>& OnNextFrameStatus() {
|
||||
return mOnNextFrameStatus;
|
||||
}
|
||||
|
||||
MediaEventSourceExc<RefPtr<VideoFrameContainer>>&
|
||||
OnSecondaryVideoContainerInstalled() {
|
||||
return mOnSecondaryVideoContainerInstalled;
|
||||
}
|
||||
|
||||
size_t SizeOfVideoQueue() const;
|
||||
|
||||
size_t SizeOfAudioQueue() const;
|
||||
|
||||
// Sets the video decode mode. Used by the suspend-video-decoder feature.
|
||||
void SetVideoDecodeMode(VideoDecodeMode aMode) override;
|
||||
void SetVideoDecodeMode(VideoDecodeMode aMode);
|
||||
|
||||
RefPtr<GenericPromise> InvokeSetSink(
|
||||
const RefPtr<AudioDeviceInfo>& aSink) override;
|
||||
RefPtr<GenericPromise> InvokeSetSink(const RefPtr<AudioDeviceInfo>& aSink);
|
||||
|
||||
void InvokeSuspendMediaSink() override;
|
||||
void InvokeResumeMediaSink() override;
|
||||
void InvokeSuspendMediaSink();
|
||||
void InvokeResumeMediaSink();
|
||||
|
||||
private:
|
||||
class StateObject;
|
||||
|
@ -177,14 +301,18 @@ class MediaDecoderStateMachine
|
|||
|
||||
void GetDebugInfo(dom::MediaDecoderStateMachineDebugInfo& aInfo);
|
||||
|
||||
// Functions used by assertions to ensure we're calling things
|
||||
// on the appropriate threads.
|
||||
bool OnTaskQueue() const;
|
||||
|
||||
// Initialization that needs to happen on the task queue. This is the first
|
||||
// task that gets run on the task queue, and is dispatched from the MDSM
|
||||
// constructor immediately after the task queue is created.
|
||||
void InitializationTask(MediaDecoder* aDecoder) override;
|
||||
void InitializationTask(MediaDecoder* aDecoder);
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> Seek(const SeekTarget& aTarget) override;
|
||||
RefPtr<MediaDecoder::SeekPromise> Seek(const SeekTarget& aTarget);
|
||||
|
||||
RefPtr<ShutdownPromise> Shutdown() override;
|
||||
RefPtr<ShutdownPromise> Shutdown();
|
||||
|
||||
RefPtr<ShutdownPromise> FinishShutdown();
|
||||
|
||||
|
@ -195,6 +323,10 @@ class MediaDecoderStateMachine
|
|||
// the decode monitor held.
|
||||
void UpdatePlaybackPosition(const media::TimeUnit& aTime);
|
||||
|
||||
bool HasAudio() const { return mInfo.ref().HasAudio(); }
|
||||
bool HasVideo() const { return mInfo.ref().HasVideo(); }
|
||||
const MediaInfo& Info() const { return mInfo.ref(); }
|
||||
|
||||
// Schedules the shared state machine thread to run the state machine.
|
||||
void ScheduleStateMachine();
|
||||
|
||||
|
@ -242,11 +374,7 @@ class MediaDecoderStateMachine
|
|||
protected:
|
||||
virtual ~MediaDecoderStateMachine();
|
||||
|
||||
void BufferedRangeUpdated() override;
|
||||
void VolumeChanged() override;
|
||||
void PreservesPitchChanged() override;
|
||||
void PlayStateChanged() override;
|
||||
void LoopingChanged() override;
|
||||
void BufferedRangeUpdated();
|
||||
|
||||
void ReaderSuspendedChanged();
|
||||
|
||||
|
@ -260,20 +388,10 @@ class MediaDecoderStateMachine
|
|||
|
||||
void AudioAudibleChanged(bool aAudible);
|
||||
|
||||
void SetPlaybackRate(double aPlaybackRate) override;
|
||||
void SetIsLiveStream(bool aIsLiveStream) override {
|
||||
mIsLiveStream = aIsLiveStream;
|
||||
}
|
||||
void SetCanPlayThrough(bool aCanPlayThrough) override {
|
||||
mCanPlayThrough = aCanPlayThrough;
|
||||
}
|
||||
void SetFragmentEndTime(const media::TimeUnit& aEndTime) override {
|
||||
// A negative number means we don't have a fragment end time at all.
|
||||
mFragmentEndTime = aEndTime >= media::TimeUnit::Zero()
|
||||
? aEndTime
|
||||
: media::TimeUnit::Invalid();
|
||||
}
|
||||
|
||||
void VolumeChanged();
|
||||
void SetPlaybackRate(double aPlaybackRate);
|
||||
void PreservesPitchChanged();
|
||||
void LoopingChanged();
|
||||
void StreamNameChanged();
|
||||
void UpdateSecondaryVideoContainer();
|
||||
void UpdateOutputCaptured();
|
||||
|
@ -341,6 +459,9 @@ class MediaDecoderStateMachine
|
|||
// If start fails an NS_ERROR_FAILURE is returned.
|
||||
nsresult StartMediaSink();
|
||||
|
||||
// Notification method invoked when mPlayState changes.
|
||||
void PlayStateChanged();
|
||||
|
||||
// Notification method invoked when mIsVisible changes.
|
||||
void VisibilityChanged();
|
||||
|
||||
|
@ -353,6 +474,12 @@ class MediaDecoderStateMachine
|
|||
// Must be called with the decode monitor held.
|
||||
void MaybeStartPlayback();
|
||||
|
||||
// Moves the decoder into the shutdown state, and dispatches an error
|
||||
// event to the media element. This begins shutting down the decoder.
|
||||
// The decoder monitor must be held. This is only called on the
|
||||
// decode thread.
|
||||
void DecodeError(const MediaResult& aError);
|
||||
|
||||
void EnqueueFirstFrameLoadedEvent();
|
||||
|
||||
// Start a task to decode audio.
|
||||
|
@ -367,6 +494,11 @@ class MediaDecoderStateMachine
|
|||
|
||||
void WaitForData(MediaData::Type aType);
|
||||
|
||||
bool IsRequestingAudioData() const { return mAudioDataRequest.Exists(); }
|
||||
bool IsRequestingVideoData() const { return mVideoDataRequest.Exists(); }
|
||||
bool IsWaitingAudioData() const { return mAudioWaitRequest.Exists(); }
|
||||
bool IsWaitingVideoData() const { return mVideoWaitRequest.Exists(); }
|
||||
|
||||
// Returns the "media time". This is the absolute time which the media
|
||||
// playback has reached. i.e. this returns values in the range
|
||||
// [mStartTime, mEndTime], and mStartTime will not be 0 if the media does
|
||||
|
@ -408,6 +540,14 @@ class MediaDecoderStateMachine
|
|||
void OnMediaSinkAudioError(nsresult aResult);
|
||||
void OnMediaSinkVideoError();
|
||||
|
||||
void* const mDecoderID;
|
||||
const RefPtr<AbstractThread> mAbstractMainThread;
|
||||
const RefPtr<FrameStatistics> mFrameStats;
|
||||
const RefPtr<VideoFrameContainer> mVideoFrameContainer;
|
||||
|
||||
// Task queue for running the state machine.
|
||||
RefPtr<TaskQueue> mTaskQueue;
|
||||
|
||||
// State-watching manager.
|
||||
WatchManager<MediaDecoderStateMachine> mWatchManager;
|
||||
|
||||
|
@ -441,6 +581,8 @@ class MediaDecoderStateMachine
|
|||
// The media sink resource. Used on the state machine thread.
|
||||
RefPtr<MediaSink> mMediaSink;
|
||||
|
||||
const RefPtr<ReaderProxy> mReader;
|
||||
|
||||
// The end time of the last audio frame that's been pushed onto the media sink
|
||||
// in microseconds. This will approximately be the end time
|
||||
// of the audio stream, unless another frame is pushed to the hardware.
|
||||
|
@ -458,6 +600,9 @@ class MediaDecoderStateMachine
|
|||
// on decoded video data.
|
||||
media::TimeUnit mDecodedVideoEndTime;
|
||||
|
||||
// Playback rate. 1.0 : normal speed, 0.5 : two times slower.
|
||||
double mPlaybackRate;
|
||||
|
||||
// If we've got more than this number of decoded video frames waiting in
|
||||
// the video queue, we will not decode any more video frames until some have
|
||||
// been consumed by the play state machine thread.
|
||||
|
@ -468,6 +613,16 @@ class MediaDecoderStateMachine
|
|||
// pause decoding.
|
||||
media::TimeUnit mAmpleAudioThreshold;
|
||||
|
||||
// Only one of a given pair of ({Audio,Video}DataPromise, WaitForDataPromise)
|
||||
// should exist at any given moment.
|
||||
using AudioDataPromise = MediaFormatReader::AudioDataPromise;
|
||||
using VideoDataPromise = MediaFormatReader::VideoDataPromise;
|
||||
using WaitForDataPromise = MediaFormatReader::WaitForDataPromise;
|
||||
MozPromiseRequestHolder<AudioDataPromise> mAudioDataRequest;
|
||||
MozPromiseRequestHolder<VideoDataPromise> mVideoDataRequest;
|
||||
MozPromiseRequestHolder<WaitForDataPromise> mAudioWaitRequest;
|
||||
MozPromiseRequestHolder<WaitForDataPromise> mVideoWaitRequest;
|
||||
|
||||
const char* AudioRequestStatus() const;
|
||||
const char* VideoRequestStatus() const;
|
||||
|
||||
|
@ -486,9 +641,38 @@ class MediaDecoderStateMachine
|
|||
// True if all video frames are already rendered.
|
||||
bool mVideoCompleted = false;
|
||||
|
||||
// True if we should not decode/preroll unnecessary samples, unless we're
|
||||
// played. "Prerolling" in this context refers to when we decode and
|
||||
// buffer decoded samples in advance of when they're needed for playback.
|
||||
// This flag is set for preload=metadata media, and means we won't
|
||||
// decode more than the first video frame and first block of audio samples
|
||||
// for that media when we startup, or after a seek. When Play() is called,
|
||||
// we reset this flag, as we assume the user is playing the media, so
|
||||
// prerolling is appropriate then. This flag is used to reduce the overhead
|
||||
// of prerolling samples for media elements that may not play, both
|
||||
// memory and CPU overhead.
|
||||
bool mMinimizePreroll;
|
||||
|
||||
// Stores presentation info required for playback.
|
||||
Maybe<MediaInfo> mInfo;
|
||||
|
||||
mozilla::MediaMetadataManager mMetadataManager;
|
||||
|
||||
// True if we've decoded first frames (thus having the start time) and
|
||||
// notified the FirstFrameLoaded event. Note we can't initiate seek until the
|
||||
// start time is known which happens when the first frames are decoded or we
|
||||
// are playing an MSE stream (the start time is always assumed 0).
|
||||
bool mSentFirstFrameLoadedEvent;
|
||||
|
||||
// True if video decoding is suspended.
|
||||
bool mVideoDecodeSuspended;
|
||||
|
||||
// True if the media is seekable (i.e. supports random access).
|
||||
bool mMediaSeekable = true;
|
||||
|
||||
// True if the media is seekable only in buffered ranges.
|
||||
bool mMediaSeekableOnlyInBufferedRanges = false;
|
||||
|
||||
// Track enabling video decode suspension via timer
|
||||
DelayedScheduler mVideoDecodeSuspendTimer;
|
||||
|
||||
|
@ -504,6 +688,22 @@ class MediaDecoderStateMachine
|
|||
MediaEventListener mAudibleListener;
|
||||
MediaEventListener mOnMediaNotSeekable;
|
||||
|
||||
MediaEventProducerExc<UniquePtr<MediaInfo>, UniquePtr<MetadataTags>,
|
||||
MediaDecoderEventVisibility>
|
||||
mMetadataLoadedEvent;
|
||||
MediaEventProducerExc<UniquePtr<MediaInfo>, MediaDecoderEventVisibility>
|
||||
mFirstFrameLoadedEvent;
|
||||
|
||||
MediaEventProducer<MediaPlaybackEvent> mOnPlaybackEvent;
|
||||
MediaEventProducer<MediaResult> mOnPlaybackErrorEvent;
|
||||
|
||||
MediaEventProducer<DecoderDoctorEvent> mOnDecoderDoctorEvent;
|
||||
|
||||
MediaEventProducer<NextFrameStatus> mOnNextFrameStatus;
|
||||
|
||||
MediaEventProducerExc<RefPtr<VideoFrameContainer>>
|
||||
mOnSecondaryVideoContainerInstalled;
|
||||
|
||||
const bool mIsMSE;
|
||||
|
||||
bool mSeamlessLoopingAllowed;
|
||||
|
@ -517,6 +717,22 @@ class MediaDecoderStateMachine
|
|||
int64_t mPlaybackOffset = 0;
|
||||
|
||||
private:
|
||||
// The buffered range. Mirrored from the decoder thread.
|
||||
Mirror<media::TimeIntervals> mBuffered;
|
||||
|
||||
// The current play state, mirrored from the main thread.
|
||||
Mirror<MediaDecoder::PlayState> mPlayState;
|
||||
|
||||
// Volume of playback. 0.0 = muted. 1.0 = full volume.
|
||||
Mirror<double> mVolume;
|
||||
|
||||
// Pitch preservation for the playback rate.
|
||||
Mirror<bool> mPreservesPitch;
|
||||
|
||||
// Whether to seek back to the start of the media resource
|
||||
// upon reaching the end.
|
||||
Mirror<bool> mLooping;
|
||||
|
||||
// Audio stream name
|
||||
Mirror<nsAutoString> mStreamName;
|
||||
|
||||
|
@ -545,12 +761,26 @@ class MediaDecoderStateMachine
|
|||
Canonical<CopyableTArray<RefPtr<ProcessedMediaTrack>>> mCanonicalOutputTracks;
|
||||
Canonical<PrincipalHandle> mCanonicalOutputPrincipal;
|
||||
|
||||
// Duration of the media. This is guaranteed to be non-null after we finish
|
||||
// decoding the first frame.
|
||||
Canonical<media::NullableTimeUnit> mDuration;
|
||||
|
||||
// The time of the current frame, corresponding to the "current
|
||||
// playback position" in HTML5. This is referenced from 0, which is the
|
||||
// initial playback position.
|
||||
Canonical<media::TimeUnit> mCurrentPosition;
|
||||
|
||||
// Used to distinguish whether the audio is producing sound.
|
||||
Canonical<bool> mIsAudioDataAudible;
|
||||
|
||||
// Track when MediaSink is supsended. When that happens some actions are
|
||||
// restricted like starting the sink or changing sink id. The flag is valid
|
||||
// after Initialization. TaskQueue thread only.
|
||||
bool mIsMediaSinkSuspended = false;
|
||||
|
||||
public:
|
||||
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() const;
|
||||
|
||||
AbstractCanonical<CopyableTArray<RefPtr<ProcessedMediaTrack>>>*
|
||||
CanonicalOutputTracks() {
|
||||
return &mCanonicalOutputTracks;
|
||||
|
@ -558,6 +788,15 @@ class MediaDecoderStateMachine
|
|||
AbstractCanonical<PrincipalHandle>* CanonicalOutputPrincipal() {
|
||||
return &mCanonicalOutputPrincipal;
|
||||
}
|
||||
AbstractCanonical<media::NullableTimeUnit>* CanonicalDuration() {
|
||||
return &mDuration;
|
||||
}
|
||||
AbstractCanonical<media::TimeUnit>* CanonicalCurrentPosition() {
|
||||
return &mCurrentPosition;
|
||||
}
|
||||
AbstractCanonical<bool>* CanonicalIsAudioDataAudible() {
|
||||
return &mIsAudioDataAudible;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
/* 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 "MediaDecoderStateMachineBase.h"
|
||||
|
||||
#include "MediaDecoder.h"
|
||||
#include "mozilla/ProfilerMarkers.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define INIT_MIRROR(name, val) \
|
||||
name(mTaskQueue, val, "MediaDecoderStateMachineBase::" #name " (Mirror)")
|
||||
#define INIT_CANONICAL(name, val) \
|
||||
name(mTaskQueue, val, "MediaDecoderStateMachineBase::" #name " (Canonical)")
|
||||
#define FMT(x, ...) "Decoder=%p " x, mDecoderID, ##__VA_ARGS__
|
||||
#define LOG(x, ...) \
|
||||
DDMOZ_LOG(gMediaDecoderLog, LogLevel::Debug, "Decoder=%p " x, mDecoderID, \
|
||||
##__VA_ARGS__)
|
||||
#define LOGV(x, ...) \
|
||||
DDMOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, "Decoder=%p " x, mDecoderID, \
|
||||
##__VA_ARGS__)
|
||||
#define LOGW(x, ...) NS_WARNING(nsPrintfCString(FMT(x, ##__VA_ARGS__)).get())
|
||||
#define LOGE(x, ...) \
|
||||
NS_DebugBreak(NS_DEBUG_WARNING, \
|
||||
nsPrintfCString(FMT(x, ##__VA_ARGS__)).get(), nullptr, \
|
||||
__FILE__, __LINE__)
|
||||
|
||||
MediaDecoderStateMachineBase::MediaDecoderStateMachineBase(
|
||||
MediaDecoder* aDecoder, MediaFormatReader* aReader)
|
||||
: mDecoderID(aDecoder),
|
||||
mAbstractMainThread(aDecoder->AbstractMainThread()),
|
||||
mFrameStats(&aDecoder->GetFrameStatistics()),
|
||||
mVideoFrameContainer(aDecoder->GetVideoFrameContainer()),
|
||||
mTaskQueue(TaskQueue::Create(GetMediaThreadPool(MediaThreadType::MDSM),
|
||||
"MDSM::mTaskQueue",
|
||||
/* aSupportsTailDispatch = */ true)),
|
||||
mReader(new ReaderProxy(mTaskQueue, aReader)),
|
||||
mPlaybackRate(1.0),
|
||||
INIT_MIRROR(mBuffered, TimeIntervals()),
|
||||
INIT_MIRROR(mPlayState, MediaDecoder::PLAY_STATE_LOADING),
|
||||
INIT_MIRROR(mVolume, 1.0),
|
||||
INIT_MIRROR(mPreservesPitch, true),
|
||||
INIT_MIRROR(mLooping, false),
|
||||
INIT_CANONICAL(mDuration, NullableTimeUnit()),
|
||||
INIT_CANONICAL(mCurrentPosition, TimeUnit::Zero()),
|
||||
INIT_CANONICAL(mIsAudioDataAudible, false),
|
||||
mMinimizePreroll(aDecoder->GetMinimizePreroll()),
|
||||
mWatchManager(this, mTaskQueue) {}
|
||||
|
||||
MediaEventSource<void>& MediaDecoderStateMachineBase::OnMediaNotSeekable()
|
||||
const {
|
||||
return mReader->OnMediaNotSeekable();
|
||||
}
|
||||
|
||||
AbstractCanonical<media::TimeIntervals>*
|
||||
MediaDecoderStateMachineBase::CanonicalBuffered() const {
|
||||
return mReader->CanonicalBuffered();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachineBase::DispatchSetFragmentEndTime(
|
||||
const media::TimeUnit& aEndTime) {
|
||||
OwnerThread()->DispatchStateChange(NewRunnableMethod<media::TimeUnit>(
|
||||
"MediaDecoderStateMachineBase::SetFragmentEndTime", this,
|
||||
&MediaDecoderStateMachineBase::SetFragmentEndTime, aEndTime));
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachineBase::DispatchCanPlayThrough(
|
||||
bool aCanPlayThrough) {
|
||||
OwnerThread()->DispatchStateChange(NewRunnableMethod<bool>(
|
||||
"MediaDecoderStateMachineBase::SetCanPlayThrough", this,
|
||||
&MediaDecoderStateMachineBase::SetCanPlayThrough, aCanPlayThrough));
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachineBase::DispatchIsLiveStream(bool aIsLiveStream) {
|
||||
OwnerThread()->DispatchStateChange(NewRunnableMethod<bool>(
|
||||
"MediaDecoderStateMachineBase::SetIsLiveStream", this,
|
||||
&MediaDecoderStateMachineBase::SetIsLiveStream, aIsLiveStream));
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachineBase::DispatchSetPlaybackRate(
|
||||
double aPlaybackRate) {
|
||||
OwnerThread()->DispatchStateChange(NewRunnableMethod<double>(
|
||||
"MediaDecoderStateMachineBase::SetPlaybackRate", this,
|
||||
&MediaDecoderStateMachineBase::SetPlaybackRate, aPlaybackRate));
|
||||
}
|
||||
|
||||
nsresult MediaDecoderStateMachineBase::Init(MediaDecoder* aDecoder) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Dispatch initialization that needs to happen on that task queue.
|
||||
nsCOMPtr<nsIRunnable> r = NewRunnableMethod<RefPtr<MediaDecoder>>(
|
||||
"MediaDecoderStateMachineBase::InitializationTask", this,
|
||||
&MediaDecoderStateMachineBase::InitializationTask, aDecoder);
|
||||
mTaskQueue->DispatchStateChange(r.forget());
|
||||
;
|
||||
nsresult rv = mReader->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
|
||||
mReader->SetCanonicalDuration(&mDuration);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachineBase::InitializationTask(MediaDecoder* aDecoder) {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
||||
// Connect mirrors.
|
||||
mBuffered.Connect(mReader->CanonicalBuffered());
|
||||
mPlayState.Connect(aDecoder->CanonicalPlayState());
|
||||
mVolume.Connect(aDecoder->CanonicalVolume());
|
||||
mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch());
|
||||
mLooping.Connect(aDecoder->CanonicalLooping());
|
||||
|
||||
// Initialize watchers.
|
||||
mWatchManager.Watch(mBuffered,
|
||||
&MediaDecoderStateMachineBase::BufferedRangeUpdated);
|
||||
mWatchManager.Watch(mVolume, &MediaDecoderStateMachineBase::VolumeChanged);
|
||||
mWatchManager.Watch(mPreservesPitch,
|
||||
&MediaDecoderStateMachineBase::PreservesPitchChanged);
|
||||
mWatchManager.Watch(mPlayState,
|
||||
&MediaDecoderStateMachineBase::PlayStateChanged);
|
||||
mWatchManager.Watch(mLooping, &MediaDecoderStateMachineBase::LoopingChanged);
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise> MediaDecoderStateMachineBase::BeginShutdown() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return InvokeAsync(OwnerThread(), __func__,
|
||||
[self = RefPtr<MediaDecoderStateMachineBase>(this)]() {
|
||||
self->mWatchManager.Shutdown();
|
||||
return self->Shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> MediaDecoderStateMachineBase::InvokeSeek(
|
||||
const SeekTarget& aTarget) {
|
||||
return InvokeAsync(OwnerThread(), __func__,
|
||||
[self = RefPtr<MediaDecoderStateMachineBase>(this),
|
||||
target = aTarget]() { return self->Seek(target); });
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachineBase::OnTaskQueue() const {
|
||||
return OwnerThread()->IsCurrentThreadIn();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachineBase::DecodeError(const MediaResult& aError) {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
LOGE("Decode error: %s", aError.Description().get());
|
||||
PROFILER_MARKER_TEXT("MDSMBase::DecodeError", MEDIA_PLAYBACK, {},
|
||||
aError.Description());
|
||||
// Notify the decode error and MediaDecoder will shut down MDSM.
|
||||
mOnPlaybackErrorEvent.Notify(aError);
|
||||
}
|
||||
|
||||
#undef INIT_MIRROR
|
||||
#undef INIT_CANONICAL
|
||||
#undef FMT
|
||||
#undef LOG
|
||||
#undef LOGV
|
||||
#undef LOGW
|
||||
#undef LOGE
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,284 +0,0 @@
|
|||
/* 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 DOM_MEDIA_MEDIADECODERSTATEMACHINEBASE_H_
|
||||
#define DOM_MEDIA_MEDIADECODERSTATEMACHINEBASE_H_
|
||||
|
||||
#include "DecoderDoctorDiagnostics.h"
|
||||
#include "MediaDecoder.h"
|
||||
#include "MediaDecoderOwner.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "MediaInfo.h"
|
||||
#include "MediaMetadataManager.h"
|
||||
#include "ReaderProxy.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
#include "mozilla/dom/MediaDebugInfoBinding.h"
|
||||
#include "mozilla/Variant.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
class AudioDeviceInfo;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AbstractThread;
|
||||
class FrameStatistics;
|
||||
class MediaFormatReader;
|
||||
class TaskQueue;
|
||||
|
||||
struct MediaPlaybackEvent {
|
||||
enum EventType {
|
||||
PlaybackStarted,
|
||||
PlaybackStopped,
|
||||
PlaybackProgressed,
|
||||
PlaybackEnded,
|
||||
SeekStarted,
|
||||
Invalidate,
|
||||
EnterVideoSuspend,
|
||||
ExitVideoSuspend,
|
||||
StartVideoSuspendTimer,
|
||||
CancelVideoSuspendTimer,
|
||||
VideoOnlySeekBegin,
|
||||
VideoOnlySeekCompleted,
|
||||
} mType;
|
||||
|
||||
using DataType = Variant<Nothing, int64_t>;
|
||||
DataType mData;
|
||||
|
||||
MOZ_IMPLICIT MediaPlaybackEvent(EventType aType)
|
||||
: mType(aType), mData(Nothing{}) {}
|
||||
|
||||
template <typename T>
|
||||
MediaPlaybackEvent(EventType aType, T&& aArg)
|
||||
: mType(aType), mData(std::forward<T>(aArg)) {}
|
||||
};
|
||||
|
||||
enum class VideoDecodeMode : uint8_t { Normal, Suspend };
|
||||
|
||||
/**
|
||||
* The state machine class. This manages the decoding and seeking in the
|
||||
* MediaDecoderReader on the decode task queue, and A/V sync on the shared
|
||||
* state machine thread, and controls the audio "push" thread.
|
||||
*
|
||||
* All internal state is synchronised via the decoder monitor. State changes
|
||||
* are propagated by scheduling the state machine to run another cycle on the
|
||||
* shared state machine thread.
|
||||
*/
|
||||
class MediaDecoderStateMachineBase {
|
||||
public:
|
||||
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||
|
||||
using FirstFrameEventSourceExc =
|
||||
MediaEventSourceExc<UniquePtr<MediaInfo>, MediaDecoderEventVisibility>;
|
||||
using MetadataEventSourceExc =
|
||||
MediaEventSourceExc<UniquePtr<MediaInfo>, UniquePtr<MetadataTags>,
|
||||
MediaDecoderEventVisibility>;
|
||||
using NextFrameStatus = MediaDecoderOwner::NextFrameStatus;
|
||||
|
||||
MediaDecoderStateMachineBase(MediaDecoder* aDecoder,
|
||||
MediaFormatReader* aReader);
|
||||
|
||||
virtual nsresult Init(MediaDecoder* aDecoder);
|
||||
|
||||
RefPtr<ShutdownPromise> BeginShutdown();
|
||||
|
||||
// Seeks to the decoder to aTarget asynchronously.
|
||||
RefPtr<MediaDecoder::SeekPromise> InvokeSeek(const SeekTarget& aTarget);
|
||||
|
||||
virtual size_t SizeOfVideoQueue() const = 0;
|
||||
virtual size_t SizeOfAudioQueue() const = 0;
|
||||
|
||||
// Sets the video decode mode. Used by the suspend-video-decoder feature.
|
||||
virtual void SetVideoDecodeMode(VideoDecodeMode aMode) = 0;
|
||||
|
||||
virtual RefPtr<GenericPromise> InvokeSetSink(
|
||||
const RefPtr<AudioDeviceInfo>& aSink) = 0;
|
||||
virtual void InvokeSuspendMediaSink() = 0;
|
||||
virtual void InvokeResumeMediaSink() = 0;
|
||||
|
||||
virtual RefPtr<GenericPromise> RequestDebugInfo(
|
||||
dom::MediaDecoderStateMachineDebugInfo& aInfo) = 0;
|
||||
|
||||
// Returns the state machine task queue.
|
||||
TaskQueue* OwnerThread() const { return mTaskQueue; }
|
||||
|
||||
MetadataEventSourceExc& MetadataLoadedEvent() { return mMetadataLoadedEvent; }
|
||||
|
||||
FirstFrameEventSourceExc& FirstFrameLoadedEvent() {
|
||||
return mFirstFrameLoadedEvent;
|
||||
}
|
||||
|
||||
MediaEventSourceExc<RefPtr<VideoFrameContainer>>&
|
||||
OnSecondaryVideoContainerInstalled() {
|
||||
return mOnSecondaryVideoContainerInstalled;
|
||||
}
|
||||
|
||||
TimedMetadataEventSource& TimedMetadataEvent() {
|
||||
return mMetadataManager.TimedMetadataEvent();
|
||||
}
|
||||
|
||||
MediaEventSource<MediaPlaybackEvent>& OnPlaybackEvent() {
|
||||
return mOnPlaybackEvent;
|
||||
}
|
||||
MediaEventSource<MediaResult>& OnPlaybackErrorEvent() {
|
||||
return mOnPlaybackErrorEvent;
|
||||
}
|
||||
|
||||
MediaEventSource<DecoderDoctorEvent>& OnDecoderDoctorEvent() {
|
||||
return mOnDecoderDoctorEvent;
|
||||
}
|
||||
|
||||
MediaEventSource<NextFrameStatus>& OnNextFrameStatus() {
|
||||
return mOnNextFrameStatus;
|
||||
}
|
||||
|
||||
MediaEventSource<void>& OnMediaNotSeekable() const;
|
||||
|
||||
AbstractCanonical<media::NullableTimeUnit>* CanonicalDuration() {
|
||||
return &mDuration;
|
||||
}
|
||||
AbstractCanonical<media::TimeUnit>* CanonicalCurrentPosition() {
|
||||
return &mCurrentPosition;
|
||||
}
|
||||
AbstractCanonical<bool>* CanonicalIsAudioDataAudible() {
|
||||
return &mIsAudioDataAudible;
|
||||
}
|
||||
AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() const;
|
||||
|
||||
void DispatchSetFragmentEndTime(const media::TimeUnit& aEndTime);
|
||||
void DispatchCanPlayThrough(bool aCanPlayThrough);
|
||||
void DispatchIsLiveStream(bool aIsLiveStream);
|
||||
void DispatchSetPlaybackRate(double aPlaybackRate);
|
||||
|
||||
protected:
|
||||
virtual ~MediaDecoderStateMachineBase() = default;
|
||||
|
||||
bool HasAudio() const { return mInfo.ref().HasAudio(); }
|
||||
bool HasVideo() const { return mInfo.ref().HasVideo(); }
|
||||
const MediaInfo& Info() const { return mInfo.ref(); }
|
||||
|
||||
virtual void SetPlaybackRate(double aPlaybackRate) = 0;
|
||||
virtual void SetIsLiveStream(bool aIsLiveStream) = 0;
|
||||
virtual void SetCanPlayThrough(bool aCanPlayThrough) = 0;
|
||||
virtual void SetFragmentEndTime(const media::TimeUnit& aFragmentEndTime) = 0;
|
||||
|
||||
virtual void BufferedRangeUpdated() = 0;
|
||||
virtual void VolumeChanged() = 0;
|
||||
virtual void PreservesPitchChanged() = 0;
|
||||
virtual void PlayStateChanged() = 0;
|
||||
virtual void LoopingChanged() = 0;
|
||||
|
||||
// Init tasks which should be done on the task queue.
|
||||
virtual void InitializationTask(MediaDecoder* aDecoder);
|
||||
|
||||
virtual RefPtr<ShutdownPromise> Shutdown() = 0;
|
||||
|
||||
virtual RefPtr<MediaDecoder::SeekPromise> Seek(const SeekTarget& aTarget) = 0;
|
||||
|
||||
void DecodeError(const MediaResult& aError);
|
||||
|
||||
// Functions used by assertions to ensure we're calling things
|
||||
// on the appropriate threads.
|
||||
bool OnTaskQueue() const;
|
||||
|
||||
bool IsRequestingAudioData() const { return mAudioDataRequest.Exists(); }
|
||||
bool IsRequestingVideoData() const { return mVideoDataRequest.Exists(); }
|
||||
bool IsWaitingAudioData() const { return mAudioWaitRequest.Exists(); }
|
||||
bool IsWaitingVideoData() const { return mVideoWaitRequest.Exists(); }
|
||||
|
||||
void* const mDecoderID;
|
||||
const RefPtr<AbstractThread> mAbstractMainThread;
|
||||
const RefPtr<FrameStatistics> mFrameStats;
|
||||
const RefPtr<VideoFrameContainer> mVideoFrameContainer;
|
||||
const RefPtr<TaskQueue> mTaskQueue;
|
||||
const RefPtr<ReaderProxy> mReader;
|
||||
mozilla::MediaMetadataManager mMetadataManager;
|
||||
|
||||
// Playback rate. 1.0 : normal speed, 0.5 : two times slower.
|
||||
double mPlaybackRate;
|
||||
|
||||
// Event producers
|
||||
MediaEventProducerExc<UniquePtr<MediaInfo>, UniquePtr<MetadataTags>,
|
||||
MediaDecoderEventVisibility>
|
||||
mMetadataLoadedEvent;
|
||||
MediaEventProducerExc<UniquePtr<MediaInfo>, MediaDecoderEventVisibility>
|
||||
mFirstFrameLoadedEvent;
|
||||
MediaEventProducerExc<RefPtr<VideoFrameContainer>>
|
||||
mOnSecondaryVideoContainerInstalled;
|
||||
MediaEventProducer<MediaPlaybackEvent> mOnPlaybackEvent;
|
||||
MediaEventProducer<MediaResult> mOnPlaybackErrorEvent;
|
||||
MediaEventProducer<DecoderDoctorEvent> mOnDecoderDoctorEvent;
|
||||
MediaEventProducer<NextFrameStatus> mOnNextFrameStatus;
|
||||
|
||||
// The buffered range. Mirrored from the decoder thread.
|
||||
Mirror<media::TimeIntervals> mBuffered;
|
||||
|
||||
// The current play state, mirrored from the main thread.
|
||||
Mirror<MediaDecoder::PlayState> mPlayState;
|
||||
|
||||
// Volume of playback. 0.0 = muted. 1.0 = full volume.
|
||||
Mirror<double> mVolume;
|
||||
|
||||
// Pitch preservation for the playback rate.
|
||||
Mirror<bool> mPreservesPitch;
|
||||
|
||||
// Whether to seek back to the start of the media resource
|
||||
// upon reaching the end.
|
||||
Mirror<bool> mLooping;
|
||||
|
||||
// Duration of the media. This is guaranteed to be non-null after we finish
|
||||
// decoding the first frame.
|
||||
Canonical<media::NullableTimeUnit> mDuration;
|
||||
|
||||
// The time of the current frame, corresponding to the "current
|
||||
// playback position" in HTML5. This is referenced from 0, which is the
|
||||
// initial playback position.
|
||||
Canonical<media::TimeUnit> mCurrentPosition;
|
||||
|
||||
// Used to distinguish whether the audio is producing sound.
|
||||
Canonical<bool> mIsAudioDataAudible;
|
||||
|
||||
// Stores presentation info required for playback.
|
||||
Maybe<MediaInfo> mInfo;
|
||||
|
||||
// True if the media is seekable (i.e. supports random access).
|
||||
bool mMediaSeekable = true;
|
||||
|
||||
// True if the media is seekable only in buffered ranges.
|
||||
bool mMediaSeekableOnlyInBufferedRanges = false;
|
||||
|
||||
// True if we've decoded first frames (thus having the start time) and
|
||||
// notified the FirstFrameLoaded event. Note we can't initiate seek until the
|
||||
// start time is known which happens when the first frames are decoded or we
|
||||
// are playing an MSE stream (the start time is always assumed 0).
|
||||
bool mSentFirstFrameLoadedEvent = false;
|
||||
|
||||
// True if we should not decode/preroll unnecessary samples, unless we're
|
||||
// played. "Prerolling" in this context refers to when we decode and
|
||||
// buffer decoded samples in advance of when they're needed for playback.
|
||||
// This flag is set for preload=metadata media, and means we won't
|
||||
// decode more than the first video frame and first block of audio samples
|
||||
// for that media when we startup, or after a seek. When Play() is called,
|
||||
// we reset this flag, as we assume the user is playing the media, so
|
||||
// prerolling is appropriate then. This flag is used to reduce the overhead
|
||||
// of prerolling samples for media elements that may not play, both
|
||||
// memory and CPU overhead.
|
||||
bool mMinimizePreroll;
|
||||
|
||||
// Only one of a given pair of ({Audio,Video}DataPromise, WaitForDataPromise)
|
||||
// should exist at any given moment.
|
||||
using AudioDataPromise = MediaFormatReader::AudioDataPromise;
|
||||
using VideoDataPromise = MediaFormatReader::VideoDataPromise;
|
||||
using WaitForDataPromise = MediaFormatReader::WaitForDataPromise;
|
||||
MozPromiseRequestHolder<AudioDataPromise> mAudioDataRequest;
|
||||
MozPromiseRequestHolder<VideoDataPromise> mVideoDataRequest;
|
||||
MozPromiseRequestHolder<WaitForDataPromise> mAudioWaitRequest;
|
||||
MozPromiseRequestHolder<WaitForDataPromise> mVideoWaitRequest;
|
||||
|
||||
private:
|
||||
WatchManager<MediaDecoderStateMachineBase> mWatchManager;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_MEDIADECODERSTATEMACHINEBASE_H_
|
|
@ -376,8 +376,7 @@ void MediaFormatReader::DecoderFactory::DoCreateDecoder(Data& aData) {
|
|||
p = platform->CreateDecoder(
|
||||
{*ownerData.GetCurrentInfo()->GetAsAudioInfo(), mOwner->mCrashHelper,
|
||||
CreateDecoderParams::UseNullDecoder(ownerData.mIsNullDecode),
|
||||
TrackInfo::kAudioTrack, std::move(onWaitingForKeyEvent),
|
||||
mOwner->mMediaEngineId});
|
||||
TrackInfo::kAudioTrack, std::move(onWaitingForKeyEvent)});
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -396,8 +395,7 @@ void MediaFormatReader::DecoderFactory::DoCreateDecoder(Data& aData) {
|
|||
CreateDecoderParams::VideoFrameRate(ownerData.mMeanRate.Mean()),
|
||||
OptionSet(ownerData.mHardwareDecodingDisabled
|
||||
? Option::HardwareDecoderNotAllowed
|
||||
: Option::Default),
|
||||
mOwner->mMediaEngineId});
|
||||
: Option::Default)});
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3054,12 +3052,6 @@ void MediaFormatReader::NotifyDataArrived() {
|
|||
->Track(mNotifyDataArrivedPromise);
|
||||
}
|
||||
|
||||
void MediaFormatReader::UpdateMediaEngineId(uint64_t aMediaEngineId) {
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
LOG("Update external media engine Id %" PRIu64, aMediaEngineId);
|
||||
mMediaEngineId = Some(aMediaEngineId);
|
||||
}
|
||||
|
||||
void MediaFormatReader::UpdateBuffered() {
|
||||
AUTO_PROFILER_LABEL("MediaFormatReader::UpdateBuffered", MEDIA_PLAYBACK);
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
|
|
|
@ -133,10 +133,6 @@ class MediaFormatReader final
|
|||
// mBuffered should be recalculated and updated accordingly.
|
||||
void NotifyDataArrived();
|
||||
|
||||
// Update ID for the external playback engine. Currently it's only used on
|
||||
// Windows when the media engine playback is enabled.
|
||||
void UpdateMediaEngineId(uint64_t aMediaEngineId);
|
||||
|
||||
protected:
|
||||
// Recomputes mBuffered.
|
||||
void UpdateBuffered();
|
||||
|
@ -834,9 +830,6 @@ class MediaFormatReader final
|
|||
bool IsDecoderWaitingForCDM(TrackType aTrack);
|
||||
|
||||
void GetDebugInfo(dom::MediaFormatReaderDebugInfo& aInfo);
|
||||
|
||||
// Only be used on Windows when the media engine playback is enabled.
|
||||
Maybe<uint64_t> mMediaEngineId;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -196,14 +196,4 @@ void ReaderProxy::SetCanonicalDuration(
|
|||
Unused << rv;
|
||||
}
|
||||
|
||||
void ReaderProxy::UpdateMediaEngineId(uint64_t aMediaEngineId) {
|
||||
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
|
||||
nsCOMPtr<nsIRunnable> r = NewRunnableMethod<uint64_t>(
|
||||
"MediaFormatReader::UpdateMediaEngineId", mReader,
|
||||
&MediaFormatReader::UpdateMediaEngineId, aMediaEngineId);
|
||||
nsresult rv = mReader->OwnerThread()->Dispatch(r.forget());
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
|
||||
Unused << rv;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -84,8 +84,6 @@ class ReaderProxy {
|
|||
void SetCanonicalDuration(
|
||||
AbstractCanonical<media::NullableTimeUnit>* aCanonical);
|
||||
|
||||
void UpdateMediaEngineId(uint64_t aMediaEngineId);
|
||||
|
||||
private:
|
||||
~ReaderProxy();
|
||||
RefPtr<MetadataPromise> OnMetadataRead(MetadataHolder&& aMetadata);
|
||||
|
|
|
@ -131,7 +131,7 @@ HLSDecoder::~HLSDecoder() {
|
|||
HLS_DEBUG("HLSDecoder", "~HLSDecoder(): allocated=%zu", sAllocatedInstances);
|
||||
}
|
||||
|
||||
MediaDecoderStateMachineBase* HLSDecoder::CreateStateMachine() {
|
||||
MediaDecoderStateMachine* HLSDecoder::CreateStateMachine() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MediaFormatReaderInit init;
|
||||
|
|
|
@ -52,7 +52,7 @@ class HLSDecoder final : public MediaDecoder {
|
|||
|
||||
explicit HLSDecoder(MediaDecoderInit& aInit);
|
||||
~HLSDecoder();
|
||||
MediaDecoderStateMachineBase* CreateStateMachine();
|
||||
MediaDecoderStateMachine* CreateStateMachine();
|
||||
|
||||
bool CanPlayThroughImpl() final {
|
||||
// TODO: We don't know how to estimate 'canplaythrough' for this decoder.
|
||||
|
|
|
@ -1,243 +0,0 @@
|
|||
/* 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 "MFMediaEngineChild.h"
|
||||
|
||||
#include "MFMediaEngineUtils.h"
|
||||
#include "RemoteDecoderManagerChild.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define CLOG(msg, ...) \
|
||||
MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \
|
||||
("MFMediaEngineChild=%p, Id=%" PRId64 ", " msg, this, this->Id(), \
|
||||
##__VA_ARGS__))
|
||||
|
||||
#define WLOG(msg, ...) \
|
||||
MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \
|
||||
("MFMediaEngineWrapper=%p, Id=%" PRId64 ", " msg, this, this->Id(), \
|
||||
##__VA_ARGS__))
|
||||
|
||||
using media::TimeUnit;
|
||||
|
||||
MFMediaEngineChild::MFMediaEngineChild(MFMediaEngineWrapper* aOwner)
|
||||
: mOwner(aOwner),
|
||||
mManagerThread(RemoteDecoderManagerChild::GetManagerThread()),
|
||||
mMediaEngineId(0 /* invalid id, will be initialized later */) {}
|
||||
|
||||
RefPtr<GenericNonExclusivePromise> MFMediaEngineChild::Init(
|
||||
bool aShouldPreload) {
|
||||
if (!mManagerThread) {
|
||||
return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
|
||||
if (!IsWin8OrLater()) {
|
||||
CLOG("Media engine can only be used after Windows8");
|
||||
return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
|
||||
CLOG("Init");
|
||||
MOZ_ASSERT(mMediaEngineId == 0);
|
||||
RefPtr<GenericNonExclusivePromise> p =
|
||||
RemoteDecoderManagerChild::LaunchRDDProcessIfNeeded();
|
||||
RefPtr<MFMediaEngineChild> self = this;
|
||||
p = p->Then(
|
||||
mManagerThread, __func__,
|
||||
[self, this, aShouldPreload](bool) -> RefPtr<GenericNonExclusivePromise> {
|
||||
RefPtr<RemoteDecoderManagerChild> manager =
|
||||
RemoteDecoderManagerChild::GetSingleton(RemoteDecodeIn::RddProcess);
|
||||
if (!manager || !manager->CanSend()) {
|
||||
return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
|
||||
mIPDLSelfRef = this;
|
||||
Unused << manager->SendPMFMediaEngineConstructor(this);
|
||||
|
||||
RefPtr<GenericNonExclusivePromise::Private> promise =
|
||||
new GenericNonExclusivePromise::Private(__func__);
|
||||
MediaEngineInfoIPDL info(aShouldPreload);
|
||||
SendInitMediaEngine(info)->Then(
|
||||
mManagerThread, __func__,
|
||||
[promise, self, this](uint64_t aId) {
|
||||
// Id 0 is used to indicate error.
|
||||
if (aId == 0) {
|
||||
CLOG("Failed to initialize MFMediaEngineChild");
|
||||
promise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
mMediaEngineId = aId;
|
||||
CLOG("Initialized MFMediaEngineChild");
|
||||
promise->Resolve(true, __func__);
|
||||
},
|
||||
[promise, self,
|
||||
this](const mozilla::ipc::ResponseRejectReason& aReason) {
|
||||
CLOG("Failed to initialize MFMediaEngineChild");
|
||||
promise->Reject(NS_ERROR_FAILURE, __func__);
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
[](nsresult aResult) {
|
||||
return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineChild::RecvRequestSample(TrackType aType) {
|
||||
AssertOnManagerThread();
|
||||
if (!mOwner) {
|
||||
return IPC_OK();
|
||||
}
|
||||
if (aType == TrackType::kVideoTrack) {
|
||||
mOwner->NotifyEvent(ExternalEngineEvent::RequestForVideo);
|
||||
} else if (aType == TrackType::kAudioTrack) {
|
||||
mOwner->NotifyEvent(ExternalEngineEvent::RequestForAudio);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineChild::RecvUpdateCurrentTime(
|
||||
double aCurrentTimeInSecond) {
|
||||
AssertOnManagerThread();
|
||||
if (mOwner) {
|
||||
mOwner->UpdateCurrentTime(aCurrentTimeInSecond);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void MFMediaEngineChild::OwnerDestroyed() {
|
||||
Unused << ManagerThread()->Dispatch(NS_NewRunnableFunction(
|
||||
"MFMediaEngineChild::OwnerDestroy", [self = RefPtr{this}, this] {
|
||||
self->mOwner = nullptr;
|
||||
// Ask to destroy IPDL.
|
||||
if (CanSend()) {
|
||||
MFMediaEngineChild::Send__delete__(this);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
void MFMediaEngineChild::IPDLActorDestroyed() {
|
||||
AssertOnManagerThread();
|
||||
mIPDLSelfRef = nullptr;
|
||||
}
|
||||
|
||||
MFMediaEngineWrapper::MFMediaEngineWrapper(ExternalEngineStateMachine* aOwner)
|
||||
: ExternalPlaybackEngine(aOwner),
|
||||
mEngine(new MFMediaEngineChild(this)),
|
||||
mCurrentTimeInSecond(0.0) {}
|
||||
|
||||
RefPtr<GenericNonExclusivePromise> MFMediaEngineWrapper::Init(
|
||||
bool aShouldPreload) {
|
||||
WLOG("Init");
|
||||
return mEngine->Init(aShouldPreload);
|
||||
}
|
||||
|
||||
MFMediaEngineWrapper::~MFMediaEngineWrapper() { mEngine->OwnerDestroyed(); }
|
||||
|
||||
void MFMediaEngineWrapper::Play() {
|
||||
WLOG("Play");
|
||||
MOZ_ASSERT(IsInited());
|
||||
Unused << ManagerThread()->Dispatch(
|
||||
NS_NewRunnableFunction("MFMediaEngineWrapper::Play",
|
||||
[engine = mEngine] { engine->SendPlay(); }));
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::Pause() {
|
||||
WLOG("Pause");
|
||||
MOZ_ASSERT(IsInited());
|
||||
Unused << ManagerThread()->Dispatch(
|
||||
NS_NewRunnableFunction("MFMediaEngineWrapper::Pause",
|
||||
[engine = mEngine] { engine->SendPause(); }));
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::Seek(const TimeUnit& aTargetTime) {
|
||||
auto currentTimeInSecond = aTargetTime.ToSeconds();
|
||||
mCurrentTimeInSecond = currentTimeInSecond;
|
||||
WLOG("Seek to %f", currentTimeInSecond);
|
||||
MOZ_ASSERT(IsInited());
|
||||
Unused << ManagerThread()->Dispatch(NS_NewRunnableFunction(
|
||||
"MFMediaEngineWrapper::Seek", [engine = mEngine, currentTimeInSecond] {
|
||||
engine->SendSeek(currentTimeInSecond);
|
||||
}));
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::Shutdown() {
|
||||
WLOG("Shutdown");
|
||||
Unused << ManagerThread()->Dispatch(
|
||||
NS_NewRunnableFunction("MFMediaEngineWrapper::Shutdown",
|
||||
[engine = mEngine] { engine->SendShutdown(); }));
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::SetPlaybackRate(double aPlaybackRate) {
|
||||
WLOG("Set playback rate %f", aPlaybackRate);
|
||||
MOZ_ASSERT(IsInited());
|
||||
Unused << ManagerThread()->Dispatch(
|
||||
NS_NewRunnableFunction("MFMediaEngineWrapper::SetPlaybackRate",
|
||||
[engine = mEngine, aPlaybackRate] {
|
||||
engine->SendSetVolume(aPlaybackRate);
|
||||
}));
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::SetVolume(double aVolume) {
|
||||
WLOG("Set volume %f", aVolume);
|
||||
MOZ_ASSERT(IsInited());
|
||||
Unused << ManagerThread()->Dispatch(NS_NewRunnableFunction(
|
||||
"MFMediaEngineWrapper::SetVolume",
|
||||
[engine = mEngine, aVolume] { engine->SendSetVolume(aVolume); }));
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::SetLooping(bool aLooping) {
|
||||
WLOG("Set looping %d", aLooping);
|
||||
MOZ_ASSERT(IsInited());
|
||||
Unused << ManagerThread()->Dispatch(NS_NewRunnableFunction(
|
||||
"MFMediaEngineWrapper::SetLooping",
|
||||
[engine = mEngine, aLooping] { engine->SendSetLooping(aLooping); }));
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::SetPreservesPitch(bool aPreservesPitch) {
|
||||
// Media Engine doesn't support this.
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::NotifyEndOfStream(TrackInfo::TrackType aType) {
|
||||
WLOG("NotifyEndOfStream, type=%s", TrackTypeToStr(aType));
|
||||
MOZ_ASSERT(IsInited());
|
||||
Unused << ManagerThread()->Dispatch(NS_NewRunnableFunction(
|
||||
"MFMediaEngineWrapper::NotifyEndOfStream",
|
||||
[engine = mEngine, aType] { engine->SendNotifyEndOfStream(aType); }));
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::SetMediaInfo(const MediaInfo& aInfo) {
|
||||
WLOG("SetMediaInfo, hasAudio=%d, hasVideo=%d", aInfo.HasAudio(),
|
||||
aInfo.HasVideo());
|
||||
MOZ_ASSERT(IsInited());
|
||||
Unused << ManagerThread()->Dispatch(NS_NewRunnableFunction(
|
||||
"MFMediaEngineWrapper::SetMediaInfo", [engine = mEngine, aInfo] {
|
||||
MediaInfoIPDL info(aInfo.HasAudio() ? Some(aInfo.mAudio) : Nothing(),
|
||||
aInfo.HasVideo() ? Some(aInfo.mVideo) : Nothing());
|
||||
engine->SendNotifyMediaInfo(info);
|
||||
}));
|
||||
}
|
||||
|
||||
TimeUnit MFMediaEngineWrapper::GetCurrentPosition() {
|
||||
return TimeUnit::FromSeconds(mCurrentTimeInSecond);
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::UpdateCurrentTime(double aCurrentTimeInSecond) {
|
||||
AssertOnManagerThread();
|
||||
WLOG("Update current time %f", aCurrentTimeInSecond);
|
||||
mCurrentTimeInSecond = aCurrentTimeInSecond;
|
||||
NotifyEvent(ExternalEngineEvent::Timeupdate);
|
||||
}
|
||||
|
||||
void MFMediaEngineWrapper::NotifyEvent(ExternalEngineEvent aEvent) {
|
||||
AssertOnManagerThread();
|
||||
WLOG("Received event %s", ExternalEngineEventToStr(aEvent));
|
||||
mOwner->NotifyEvent(aEvent);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,107 +0,0 @@
|
|||
/* 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 DOM_MEDIA_IPC_MFMEDIAENGINECHILD_H_
|
||||
#define DOM_MEDIA_IPC_MFMEDIAENGINECHILD_H_
|
||||
|
||||
#include "ExternalEngineStateMachine.h"
|
||||
#include "TimeUnits.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/PMFMediaEngineChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MFMediaEngineWrapper;
|
||||
|
||||
/**
|
||||
* MFMediaEngineChild is a wrapper class for a MediaEngine in the content
|
||||
* process. It communicates with MFMediaEngineParent in the remote process by
|
||||
* using IPDL interfaces to send commands to the MediaEngine.
|
||||
* https://docs.microsoft.com/en-us/windows/win32/api/mfmediaengine/nn-mfmediaengine-imfmediaengine
|
||||
*/
|
||||
class MFMediaEngineChild final : public PMFMediaEngineChild {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MFMediaEngineChild);
|
||||
|
||||
explicit MFMediaEngineChild(MFMediaEngineWrapper* aOwner);
|
||||
|
||||
void OwnerDestroyed();
|
||||
void IPDLActorDestroyed();
|
||||
|
||||
RefPtr<GenericNonExclusivePromise> Init(bool aShouldPreload);
|
||||
|
||||
// Methods for PMFMediaEngineChild
|
||||
mozilla::ipc::IPCResult RecvRequestSample(TrackInfo::TrackType aType);
|
||||
mozilla::ipc::IPCResult RecvUpdateCurrentTime(double aCurrentTimeInSecond);
|
||||
|
||||
nsISerialEventTarget* ManagerThread() { return mManagerThread; }
|
||||
void AssertOnManagerThread() const {
|
||||
MOZ_ASSERT(mManagerThread->IsOnCurrentThread());
|
||||
}
|
||||
|
||||
uint64_t Id() const { return mMediaEngineId; }
|
||||
|
||||
private:
|
||||
~MFMediaEngineChild() = default;
|
||||
|
||||
// Only modified on the manager thread.
|
||||
MFMediaEngineWrapper* MOZ_NON_OWNING_REF mOwner;
|
||||
|
||||
const nsCOMPtr<nsISerialEventTarget> mManagerThread;
|
||||
|
||||
// This represents an unique Id to indentify the media engine in the remote
|
||||
// process. Zero is used for the status before initializaing Id from the
|
||||
// remote process.
|
||||
// Modified on the manager thread, and read on other threads.
|
||||
Atomic<uint64_t> mMediaEngineId;
|
||||
|
||||
RefPtr<MFMediaEngineChild> mIPDLSelfRef;
|
||||
};
|
||||
|
||||
/**
|
||||
* MFMediaEngineWrapper acts as an external playback engine, which is
|
||||
* implemented by using the Media Foundation Media Engine. It holds hold an
|
||||
* actor used to communicate with the engine in the remote process. Its methods
|
||||
* are all thread-safe.
|
||||
*/
|
||||
class MFMediaEngineWrapper final : public ExternalPlaybackEngine {
|
||||
public:
|
||||
explicit MFMediaEngineWrapper(ExternalEngineStateMachine* aOwner);
|
||||
~MFMediaEngineWrapper();
|
||||
|
||||
// Methods for ExternalPlaybackEngine
|
||||
RefPtr<GenericNonExclusivePromise> Init(bool aShouldPreload) override;
|
||||
void Play() override;
|
||||
void Pause() override;
|
||||
void Seek(const media::TimeUnit& aTargetTime) override;
|
||||
void Shutdown() override;
|
||||
void SetPlaybackRate(double aPlaybackRate) override;
|
||||
void SetVolume(double aVolume) override;
|
||||
void SetLooping(bool aLooping) override;
|
||||
void SetPreservesPitch(bool aPreservesPitch) override;
|
||||
media::TimeUnit GetCurrentPosition() override;
|
||||
void NotifyEndOfStream(TrackInfo::TrackType aType) override;
|
||||
uint64_t Id() const override { return mEngine->Id(); }
|
||||
void SetMediaInfo(const MediaInfo& aInfo) override;
|
||||
|
||||
nsISerialEventTarget* ManagerThread() { return mEngine->ManagerThread(); }
|
||||
void AssertOnManagerThread() const { mEngine->AssertOnManagerThread(); }
|
||||
|
||||
private:
|
||||
friend class MFMediaEngineChild;
|
||||
|
||||
bool IsInited() const { return mEngine->Id() != 0; }
|
||||
void UpdateCurrentTime(double aCurrentTimeInSecond);
|
||||
void NotifyEvent(ExternalEngineEvent aEvent);
|
||||
|
||||
const RefPtr<MFMediaEngineChild> mEngine;
|
||||
|
||||
// The current time which we receive from the MediaEngine or set by the state
|
||||
// machine when seeking.
|
||||
std::atomic<double> mCurrentTimeInSecond;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_IPC_MFMEDIAENGINECHILD_H_
|
|
@ -1,151 +0,0 @@
|
|||
/* 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 "MFMediaEngineParent.h"
|
||||
|
||||
#include "MFMediaEngineAudioStream.h"
|
||||
#include "MFMediaEngineUtils.h"
|
||||
#include "MFMediaEngineVideoStream.h"
|
||||
#include "RemoteDecoderManagerParent.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define LOG(msg, ...) \
|
||||
MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \
|
||||
("MFMediaEngineParent=%p, Id=%" PRId64 ", " msg, this, this->Id(), \
|
||||
##__VA_ARGS__))
|
||||
|
||||
using MediaEngineMap = nsTHashMap<nsUint64HashKey, MFMediaEngineParent*>;
|
||||
static StaticAutoPtr<MediaEngineMap> sMediaEngines;
|
||||
|
||||
StaticMutex sMediaEnginesLock;
|
||||
|
||||
static void RegisterMediaEngine(MFMediaEngineParent* aMediaEngine) {
|
||||
MOZ_ASSERT(aMediaEngine);
|
||||
StaticMutexAutoLock lock(sMediaEnginesLock);
|
||||
if (!sMediaEngines) {
|
||||
sMediaEngines = new MediaEngineMap();
|
||||
}
|
||||
sMediaEngines->InsertOrUpdate(aMediaEngine->Id(), aMediaEngine);
|
||||
}
|
||||
|
||||
static void UnregisterMedieEngine(MFMediaEngineParent* aMediaEngine) {
|
||||
StaticMutexAutoLock lock(sMediaEnginesLock);
|
||||
if (sMediaEngines) {
|
||||
sMediaEngines->Remove(aMediaEngine->Id());
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
MFMediaEngineParent* MFMediaEngineParent::GetMediaEngineById(uint64_t aId) {
|
||||
StaticMutexAutoLock lock(sMediaEnginesLock);
|
||||
return sMediaEngines->Get(aId);
|
||||
}
|
||||
|
||||
MFMediaEngineParent::MFMediaEngineParent(RemoteDecoderManagerParent* aManager)
|
||||
: mMediaEngineId(++sMediaEngineIdx), mManager(aManager) {
|
||||
MOZ_ASSERT(aManager);
|
||||
MOZ_ASSERT(mMediaEngineId != 0);
|
||||
MOZ_ASSERT(XRE_IsRDDProcess());
|
||||
LOG("Created MFMediaEngineParent");
|
||||
RegisterMediaEngine(this);
|
||||
mIPDLSelfRef = this;
|
||||
}
|
||||
|
||||
MFMediaEngineParent::~MFMediaEngineParent() {
|
||||
LOG("Destoryed MFMediaEngineParent");
|
||||
UnregisterMedieEngine(this);
|
||||
}
|
||||
|
||||
MFMediaEngineStream* MFMediaEngineParent::GetMediaEngineStream(
|
||||
TrackType aType, const CreateDecoderParams& aParam) {
|
||||
LOG("Create a media engine decoder for %s", TrackTypeToStr(aType));
|
||||
// TODO : make those streams associated with their media engine and source.
|
||||
if (aType == TrackType::kAudioTrack) {
|
||||
return new MFMediaEngineAudioStream(aParam);
|
||||
}
|
||||
MOZ_ASSERT(aType == TrackType::kVideoTrack);
|
||||
return new MFMediaEngineVideoStream(aParam);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvInitMediaEngine(
|
||||
const MediaEngineInfoIPDL& aInfo, InitMediaEngineResolver&& aResolver) {
|
||||
AssertOnManagerThread();
|
||||
aResolver(mMediaEngineId);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvNotifyMediaInfo(
|
||||
const MediaInfoIPDL& aInfo) {
|
||||
AssertOnManagerThread();
|
||||
// TODO : implement this by using media engine
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvPlay() {
|
||||
AssertOnManagerThread();
|
||||
// TODO : implement this by using media engine
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvPause() {
|
||||
AssertOnManagerThread();
|
||||
// TODO : implement this by using media engine
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvSeek(
|
||||
double aTargetTimeInSecond) {
|
||||
AssertOnManagerThread();
|
||||
// TODO : implement this by using media engine
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvSetVolume(double aVolume) {
|
||||
AssertOnManagerThread();
|
||||
// TODO : implement this by using media engine
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvSetPlaybackRate(
|
||||
double aPlaybackRate) {
|
||||
AssertOnManagerThread();
|
||||
// TODO : implement this by using media engine
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvSetLooping(bool aLooping) {
|
||||
AssertOnManagerThread();
|
||||
// TODO : implement this by using media engine
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvNotifyEndOfStream(
|
||||
TrackInfo::TrackType aType) {
|
||||
AssertOnManagerThread();
|
||||
// TODO : implement this by using media engine
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MFMediaEngineParent::RecvShutdown() {
|
||||
AssertOnManagerThread();
|
||||
LOG("Shutdown");
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void MFMediaEngineParent::Destroy() {
|
||||
AssertOnManagerThread();
|
||||
mIPDLSelfRef = nullptr;
|
||||
}
|
||||
|
||||
void MFMediaEngineParent::AssertOnManagerThread() const {
|
||||
MOZ_ASSERT(mManager->OnManagerThread());
|
||||
}
|
||||
|
||||
#undef LOG
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,75 +0,0 @@
|
|||
/* 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 DOM_MEDIA_IPC_MFMEDIAENGINEPARENT_H_
|
||||
#define DOM_MEDIA_IPC_MFMEDIAENGINEPARENT_H_
|
||||
|
||||
#include "MediaInfo.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/PMFMediaEngineParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MFMediaEngineStream;
|
||||
class RemoteDecoderManagerParent;
|
||||
|
||||
/**
|
||||
* MFMediaEngineParent is a wrapper class for a MediaEngine in the RDD process.
|
||||
* It's responsible to create the media engine and its related classes, such as
|
||||
* a custom media source, media engine extension, media engine notify...e.t.c
|
||||
* It communicates with MFMediaEngineChild in the content process to receive
|
||||
* commands and direct them to the media engine.
|
||||
* https://docs.microsoft.com/en-us/windows/win32/api/mfmediaengine/nn-mfmediaengine-imfmediaengine
|
||||
*/
|
||||
class MFMediaEngineParent final : public PMFMediaEngineParent {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MFMediaEngineParent);
|
||||
explicit MFMediaEngineParent(RemoteDecoderManagerParent* aManager);
|
||||
|
||||
using TrackType = TrackInfo::TrackType;
|
||||
|
||||
static MFMediaEngineParent* GetMediaEngineById(uint64_t aId);
|
||||
|
||||
MFMediaEngineStream* GetMediaEngineStream(TrackType aType,
|
||||
const CreateDecoderParams& aParam);
|
||||
|
||||
uint64_t Id() const { return mMediaEngineId; }
|
||||
|
||||
// Methods for PMFMediaEngineParent
|
||||
mozilla::ipc::IPCResult RecvInitMediaEngine(
|
||||
const MediaEngineInfoIPDL& aInfo, InitMediaEngineResolver&& aResolver);
|
||||
mozilla::ipc::IPCResult RecvNotifyMediaInfo(const MediaInfoIPDL& aInfo);
|
||||
mozilla::ipc::IPCResult RecvPlay();
|
||||
mozilla::ipc::IPCResult RecvPause();
|
||||
mozilla::ipc::IPCResult RecvSeek(double aTargetTimeInSecond);
|
||||
mozilla::ipc::IPCResult RecvSetVolume(double aVolume);
|
||||
mozilla::ipc::IPCResult RecvSetPlaybackRate(double aPlaybackRate);
|
||||
mozilla::ipc::IPCResult RecvSetLooping(bool aLooping);
|
||||
mozilla::ipc::IPCResult RecvNotifyEndOfStream(TrackInfo::TrackType aType);
|
||||
mozilla::ipc::IPCResult RecvShutdown();
|
||||
|
||||
void Destroy();
|
||||
|
||||
private:
|
||||
~MFMediaEngineParent();
|
||||
|
||||
void AssertOnManagerThread() const;
|
||||
|
||||
// This generates unique id for each MFMediaEngineParent instance, and it
|
||||
// would be increased monotonically.
|
||||
static inline uint64_t sMediaEngineIdx = 0;
|
||||
|
||||
const uint64_t mMediaEngineId;
|
||||
|
||||
// The life cycle of this class is determined by the actor in the content
|
||||
// process, we would hold a reference until the content actor asks us to
|
||||
// destroy.
|
||||
RefPtr<MFMediaEngineParent> mIPDLSelfRef;
|
||||
|
||||
const RefPtr<RemoteDecoderManagerParent> mManager;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_IPC_MFMEDIAENGINEPARENT_H_
|
|
@ -1,12 +0,0 @@
|
|||
/* 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 DOM_MEDIA_IPC_MFMEDIAENGINEUTILS_H_
|
||||
#define DOM_MEDIA_IPC_MFMEDIAENGINEUTILS_H_
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
static inline mozilla::LazyLogModule gMFMediaEngineLog("MFMediaEngine");
|
||||
|
||||
#endif // DOM_MEDIA_IPC_MFMEDIAENGINECHILD_H_
|
|
@ -1,50 +0,0 @@
|
|||
/* 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 "mozilla/dom/MediaIPCUtils.h";
|
||||
|
||||
include protocol PRemoteDecoderManager;
|
||||
|
||||
using mozilla::AudioInfo from "MediaInfo.h";
|
||||
using mozilla::VideoInfo from "MediaInfo.h";
|
||||
using mozilla::MediaResult from "MediaResult.h";
|
||||
using mozilla::TrackInfo::TrackType from "MediaInfo.h";
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct MediaEngineInfoIPDL
|
||||
{
|
||||
bool preload;
|
||||
};
|
||||
|
||||
struct MediaInfoIPDL
|
||||
{
|
||||
AudioInfo? audioInfo;
|
||||
VideoInfo? videoInfo;
|
||||
};
|
||||
|
||||
[ManualDealloc]
|
||||
async protocol PMFMediaEngine
|
||||
{
|
||||
manager PRemoteDecoderManager;
|
||||
parent:
|
||||
// Return 0 if media engine can't be created.
|
||||
async InitMediaEngine(MediaEngineInfoIPDL info) returns (uint64_t id);
|
||||
async NotifyMediaInfo(MediaInfoIPDL info);
|
||||
async Play();
|
||||
async Pause();
|
||||
async Seek(double targetTimeInSecond);
|
||||
async SetVolume(double volume);
|
||||
async SetPlaybackRate(double playbackRate);
|
||||
async SetLooping(bool looping);
|
||||
async NotifyEndOfStream(TrackType type);
|
||||
async Shutdown();
|
||||
async __delete__();
|
||||
|
||||
child:
|
||||
async UpdateCurrentTime(double currentTimeInSecond);
|
||||
async RequestSample(TrackType type);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
|
@ -3,9 +3,6 @@
|
|||
* 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/. */
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
include protocol PMFMediaEngine;
|
||||
#endif
|
||||
include protocol PTexture;
|
||||
include protocol PRemoteDecoder;
|
||||
include LayersSurfaces;
|
||||
|
@ -36,18 +33,11 @@ union RemoteDecoderInfoIPDL
|
|||
sync protocol PRemoteDecoderManager
|
||||
{
|
||||
manages PRemoteDecoder;
|
||||
#ifdef MOZ_WMF
|
||||
manages PMFMediaEngine;
|
||||
#endif
|
||||
|
||||
parent:
|
||||
#ifdef MOZ_WMF
|
||||
async PMFMediaEngine();
|
||||
#endif
|
||||
async PRemoteDecoder(RemoteDecoderInfoIPDL info,
|
||||
OptionSet options,
|
||||
TextureFactoryIdentifier? identifier,
|
||||
uint64_t? mediaEngineId);
|
||||
TextureFactoryIdentifier? identifier);
|
||||
|
||||
sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult);
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ MediaResult RemoteAudioDecoderChild::ProcessOutput(
|
|||
}
|
||||
|
||||
MediaResult RemoteAudioDecoderChild::InitIPDL(
|
||||
const AudioInfo& aAudioInfo, const CreateDecoderParams::OptionSet& aOptions,
|
||||
const Maybe<uint64_t>& aMediaEngineId) {
|
||||
const AudioInfo& aAudioInfo,
|
||||
const CreateDecoderParams::OptionSet& aOptions) {
|
||||
RefPtr<RemoteDecoderManagerChild> manager =
|
||||
RemoteDecoderManagerChild::GetSingleton(mLocation);
|
||||
|
||||
|
@ -59,24 +59,21 @@ MediaResult RemoteAudioDecoderChild::InitIPDL(
|
|||
|
||||
mIPDLSelfRef = this;
|
||||
Unused << manager->SendPRemoteDecoderConstructor(this, aAudioInfo, aOptions,
|
||||
Nothing(), aMediaEngineId);
|
||||
Nothing());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RemoteAudioDecoderParent::RemoteAudioDecoderParent(
|
||||
RemoteDecoderManagerParent* aParent, const AudioInfo& aAudioInfo,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue,
|
||||
Maybe<uint64_t> aMediaEngineId)
|
||||
: RemoteDecoderParent(aParent, aOptions, aManagerThread, aDecodeTaskQueue,
|
||||
aMediaEngineId),
|
||||
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue)
|
||||
: RemoteDecoderParent(aParent, aOptions, aManagerThread, aDecodeTaskQueue),
|
||||
mAudioInfo(aAudioInfo) {}
|
||||
|
||||
IPCResult RemoteAudioDecoderParent::RecvConstruct(
|
||||
ConstructResolver&& aResolver) {
|
||||
auto params =
|
||||
CreateDecoderParams{mAudioInfo, mOptions,
|
||||
CreateDecoderParams::NoWrapper(true), mMediaEngineId};
|
||||
auto params = CreateDecoderParams{mAudioInfo, mOptions,
|
||||
CreateDecoderParams::NoWrapper(true)};
|
||||
|
||||
mParent->EnsurePDMFactory().CreateDecoder(params)->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
|
|
|
@ -18,8 +18,7 @@ class RemoteAudioDecoderChild final : public RemoteDecoderChild {
|
|||
|
||||
MOZ_IS_CLASS_INIT
|
||||
MediaResult InitIPDL(const AudioInfo& aAudioInfo,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
const Maybe<uint64_t>& aMediaEngineId);
|
||||
const CreateDecoderParams::OptionSet& aOptions);
|
||||
|
||||
MediaResult ProcessOutput(DecodedOutputIPDL&& aDecodedData) override;
|
||||
};
|
||||
|
@ -30,8 +29,7 @@ class RemoteAudioDecoderParent final : public RemoteDecoderParent {
|
|||
const AudioInfo& aAudioInfo,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
nsISerialEventTarget* aManagerThread,
|
||||
TaskQueue* aDecodeTaskQueue,
|
||||
Maybe<uint64_t> aMediaEngineId);
|
||||
TaskQueue* aDecodeTaskQueue);
|
||||
|
||||
protected:
|
||||
IPCResult RecvConstruct(ConstructResolver&& aResolver) override;
|
||||
|
|
|
@ -23,10 +23,6 @@
|
|||
#include "nsIObserver.h"
|
||||
#include "mozilla/StaticPrefs_media.h"
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
# include "MFMediaEngineChild.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace layers;
|
||||
|
@ -269,26 +265,18 @@ RemoteDecoderManagerChild::CreateAudioDecoder(
|
|||
return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
|
||||
NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
||||
}
|
||||
|
||||
bool useUtilityAudioDecoding = StaticPrefs::media_utility_process_enabled() &&
|
||||
aLocation == RemoteDecodeIn::UtilityProcess;
|
||||
#ifdef MOZ_WMF
|
||||
// If the media engine Id is specified, using the media engine in the RDD
|
||||
// process instead.
|
||||
useUtilityAudioDecoding = useUtilityAudioDecoding &&
|
||||
!(aParams.mMediaEngineId &&
|
||||
StaticPrefs::media_wmf_media_engine_enabled());
|
||||
#endif
|
||||
RefPtr<GenericNonExclusivePromise> launchPromise =
|
||||
useUtilityAudioDecoding ? LaunchUtilityProcessIfNeeded()
|
||||
: LaunchRDDProcessIfNeeded();
|
||||
(StaticPrefs::media_utility_process_enabled() &&
|
||||
aLocation == RemoteDecodeIn::UtilityProcess)
|
||||
? LaunchUtilityProcessIfNeeded()
|
||||
: LaunchRDDProcessIfNeeded();
|
||||
|
||||
return launchPromise->Then(
|
||||
managerThread, __func__,
|
||||
[params = CreateDecoderParamsForAsync(aParams), aLocation](bool) {
|
||||
auto child = MakeRefPtr<RemoteAudioDecoderChild>();
|
||||
MediaResult result = child->InitIPDL(
|
||||
params.AudioConfig(), params.mOptions, params.mMediaEngineId);
|
||||
MediaResult result =
|
||||
child->InitIPDL(params.AudioConfig(), params.mOptions);
|
||||
if (NS_FAILED(result)) {
|
||||
return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
|
||||
result, __func__);
|
||||
|
@ -340,8 +328,7 @@ RemoteDecoderManagerChild::CreateVideoDecoder(
|
|||
params.VideoConfig(), params.mRate.mValue, params.mOptions,
|
||||
params.mKnowsCompositor
|
||||
? Some(params.mKnowsCompositor->GetTextureFactoryIdentifier())
|
||||
: Nothing(),
|
||||
params.mMediaEngineId);
|
||||
: Nothing());
|
||||
if (NS_FAILED(result)) {
|
||||
return PlatformDecoderModule::CreateDecoderPromise::CreateAndReject(
|
||||
result, __func__);
|
||||
|
@ -557,8 +544,7 @@ RemoteDecoderManagerChild::LaunchUtilityProcessIfNeeded() {
|
|||
PRemoteDecoderChild* RemoteDecoderManagerChild::AllocPRemoteDecoderChild(
|
||||
const RemoteDecoderInfoIPDL& /* not used */,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
|
||||
const Maybe<uint64_t>& aMediaEngineId) {
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier) {
|
||||
// RemoteDecoderModule is responsible for creating RemoteDecoderChild
|
||||
// classes.
|
||||
MOZ_ASSERT(false,
|
||||
|
@ -574,21 +560,6 @@ bool RemoteDecoderManagerChild::DeallocPRemoteDecoderChild(
|
|||
return true;
|
||||
}
|
||||
|
||||
PMFMediaEngineChild* RemoteDecoderManagerChild::AllocPMFMediaEngineChild() {
|
||||
MOZ_ASSERT_UNREACHABLE(
|
||||
"RemoteDecoderManagerChild cannot create MFMediaEngineChild classes");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool RemoteDecoderManagerChild::DeallocPMFMediaEngineChild(
|
||||
PMFMediaEngineChild* actor) {
|
||||
#ifdef MOZ_WMF
|
||||
MFMediaEngineChild* child = static_cast<MFMediaEngineChild*>(actor);
|
||||
child->IPDLActorDestroyed();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
RemoteDecoderManagerChild::RemoteDecoderManagerChild(RemoteDecodeIn aLocation)
|
||||
: mLocation(aLocation) {
|
||||
MOZ_ASSERT(mLocation == RemoteDecodeIn::GpuProcess ||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
class PMFMediaEngineChild;
|
||||
class RemoteDecoderChild;
|
||||
|
||||
enum class RemoteDecodeIn {
|
||||
|
@ -92,9 +91,6 @@ class RemoteDecoderManagerChild final
|
|||
RemoteDecodeIn Location() const { return mLocation; }
|
||||
layers::VideoBridgeSource GetSource() const;
|
||||
|
||||
// A thread-safe method to launch the RDD process if it hasn't launched yet.
|
||||
static RefPtr<GenericNonExclusivePromise> LaunchRDDProcessIfNeeded();
|
||||
|
||||
protected:
|
||||
void InitIPDL();
|
||||
|
||||
|
@ -105,13 +101,9 @@ class RemoteDecoderManagerChild final
|
|||
PRemoteDecoderChild* AllocPRemoteDecoderChild(
|
||||
const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
|
||||
const Maybe<uint64_t>& aMediaEngineId);
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier);
|
||||
bool DeallocPRemoteDecoderChild(PRemoteDecoderChild* actor);
|
||||
|
||||
PMFMediaEngineChild* AllocPMFMediaEngineChild();
|
||||
bool DeallocPMFMediaEngineChild(PMFMediaEngineChild* actor);
|
||||
|
||||
private:
|
||||
explicit RemoteDecoderManagerChild(RemoteDecodeIn aLocation);
|
||||
~RemoteDecoderManagerChild() = default;
|
||||
|
@ -124,6 +116,7 @@ class RemoteDecoderManagerChild final
|
|||
Endpoint<PRemoteDecoderManagerChild>&& aEndpoint);
|
||||
static void OpenForUtilityProcess(
|
||||
Endpoint<PRemoteDecoderManagerChild>&& aEndpoint);
|
||||
static RefPtr<GenericNonExclusivePromise> LaunchRDDProcessIfNeeded();
|
||||
static RefPtr<GenericNonExclusivePromise> LaunchUtilityProcessIfNeeded();
|
||||
|
||||
RefPtr<RemoteDecoderManagerChild> mIPDLSelfRef;
|
||||
|
|
|
@ -23,10 +23,6 @@
|
|||
#include "mozilla/layers/VideoBridgeChild.h"
|
||||
#include "mozilla/layers/VideoBridgeParent.h"
|
||||
|
||||
#ifdef MOZ_WMF
|
||||
# include "MFMediaEngineParent.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace ipc;
|
||||
|
@ -191,8 +187,7 @@ void RemoteDecoderManagerParent::ActorDestroy(
|
|||
PRemoteDecoderParent* RemoteDecoderManagerParent::AllocPRemoteDecoderParent(
|
||||
const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
|
||||
const Maybe<uint64_t>& aMediaEngineId) {
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier) {
|
||||
RefPtr<TaskQueue> decodeTaskQueue =
|
||||
TaskQueue::Create(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
|
||||
"RemoteVideoDecoderParent::mDecodeTaskQueue");
|
||||
|
@ -203,14 +198,13 @@ PRemoteDecoderParent* RemoteDecoderManagerParent::AllocPRemoteDecoderParent(
|
|||
aRemoteDecoderInfo.get_VideoDecoderInfoIPDL();
|
||||
return new RemoteVideoDecoderParent(
|
||||
this, decoderInfo.videoInfo(), decoderInfo.framerate(), aOptions,
|
||||
aIdentifier, sRemoteDecoderManagerParentThread, decodeTaskQueue,
|
||||
aMediaEngineId);
|
||||
aIdentifier, sRemoteDecoderManagerParentThread, decodeTaskQueue);
|
||||
}
|
||||
|
||||
if (aRemoteDecoderInfo.type() == RemoteDecoderInfoIPDL::TAudioInfo) {
|
||||
return new RemoteAudioDecoderParent(
|
||||
this, aRemoteDecoderInfo.get_AudioInfo(), aOptions,
|
||||
sRemoteDecoderManagerParentThread, decodeTaskQueue, aMediaEngineId);
|
||||
sRemoteDecoderManagerParentThread, decodeTaskQueue);
|
||||
}
|
||||
|
||||
MOZ_CRASH("unrecognized type of RemoteDecoderInfoIPDL union");
|
||||
|
@ -224,23 +218,6 @@ bool RemoteDecoderManagerParent::DeallocPRemoteDecoderParent(
|
|||
return true;
|
||||
}
|
||||
|
||||
PMFMediaEngineParent* RemoteDecoderManagerParent::AllocPMFMediaEngineParent() {
|
||||
#ifdef MOZ_WMF
|
||||
return new MFMediaEngineParent(this);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RemoteDecoderManagerParent::DeallocPMFMediaEngineParent(
|
||||
PMFMediaEngineParent* actor) {
|
||||
#ifdef MOZ_WMF
|
||||
MFMediaEngineParent* parent = static_cast<MFMediaEngineParent*>(actor);
|
||||
parent->Destroy();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoteDecoderManagerParent::Open(
|
||||
Endpoint<PRemoteDecoderManagerParent>&& aEndpoint) {
|
||||
if (!aEndpoint.Bind(this)) {
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
namespace mozilla {
|
||||
|
||||
class PDMFactory;
|
||||
class PMFMediaEngineParent;
|
||||
|
||||
class RemoteDecoderManagerParent final
|
||||
: public PRemoteDecoderManagerParent,
|
||||
|
@ -58,13 +57,9 @@ class RemoteDecoderManagerParent final
|
|||
PRemoteDecoderParent* AllocPRemoteDecoderParent(
|
||||
const RemoteDecoderInfoIPDL& aRemoteDecoderInfo,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
|
||||
const Maybe<uint64_t>& aMediaEngineId);
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier);
|
||||
bool DeallocPRemoteDecoderParent(PRemoteDecoderParent* actor);
|
||||
|
||||
PMFMediaEngineParent* AllocPMFMediaEngineParent();
|
||||
bool DeallocPMFMediaEngineParent(PMFMediaEngineParent* actor);
|
||||
|
||||
mozilla::ipc::IPCResult RecvReadback(const SurfaceDescriptorGPUVideo& aSD,
|
||||
SurfaceDescriptor* aResult);
|
||||
mozilla::ipc::IPCResult RecvDeallocateSurfaceDescriptorGPUVideo(
|
||||
|
|
|
@ -13,13 +13,11 @@ namespace mozilla {
|
|||
RemoteDecoderParent::RemoteDecoderParent(
|
||||
RemoteDecoderManagerParent* aParent,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue,
|
||||
Maybe<uint64_t> aMediaEngineId)
|
||||
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue)
|
||||
: ShmemRecycleAllocator(this),
|
||||
mParent(aParent),
|
||||
mOptions(aOptions),
|
||||
mDecodeTaskQueue(aDecodeTaskQueue),
|
||||
mMediaEngineId(aMediaEngineId),
|
||||
mManagerThread(aManagerThread) {
|
||||
MOZ_COUNT_CTOR(RemoteDecoderParent);
|
||||
MOZ_ASSERT(OnManagerThread());
|
||||
|
|
|
@ -26,8 +26,7 @@ class RemoteDecoderParent : public ShmemRecycleAllocator<RemoteDecoderParent>,
|
|||
RemoteDecoderParent(RemoteDecoderManagerParent* aParent,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
nsISerialEventTarget* aManagerThread,
|
||||
TaskQueue* aDecodeTaskQueue,
|
||||
Maybe<uint64_t> aMediaEngineId);
|
||||
TaskQueue* aDecodeTaskQueue);
|
||||
|
||||
void Destroy();
|
||||
|
||||
|
@ -56,9 +55,6 @@ class RemoteDecoderParent : public ShmemRecycleAllocator<RemoteDecoderParent>,
|
|||
const RefPtr<TaskQueue> mDecodeTaskQueue;
|
||||
RefPtr<MediaDataDecoder> mDecoder;
|
||||
|
||||
// Only be used on Windows when the media engine playback is enabled.
|
||||
const Maybe<uint64_t> mMediaEngineId;
|
||||
|
||||
private:
|
||||
void DecodeNextSample(const RefPtr<ArrayOfRemoteMediaRawData>& aData,
|
||||
size_t aIndex, MediaDataDecoder::DecodedData&& aOutput,
|
||||
|
|
|
@ -91,8 +91,7 @@ MediaResult RemoteVideoDecoderChild::ProcessOutput(
|
|||
MediaResult RemoteVideoDecoderChild::InitIPDL(
|
||||
const VideoInfo& aVideoInfo, float aFramerate,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
Maybe<layers::TextureFactoryIdentifier> aIdentifier,
|
||||
const Maybe<uint64_t>& aMediaEngineId) {
|
||||
Maybe<layers::TextureFactoryIdentifier> aIdentifier) {
|
||||
MOZ_ASSERT_IF(mLocation == RemoteDecodeIn::GpuProcess, aIdentifier);
|
||||
|
||||
RefPtr<RemoteDecoderManagerChild> manager =
|
||||
|
@ -124,7 +123,7 @@ MediaResult RemoteVideoDecoderChild::InitIPDL(
|
|||
mIPDLSelfRef = this;
|
||||
VideoDecoderInfoIPDL decoderInfo(aVideoInfo, aFramerate);
|
||||
Unused << manager->SendPRemoteDecoderConstructor(this, decoderInfo, aOptions,
|
||||
aIdentifier, aMediaEngineId);
|
||||
aIdentifier);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -133,10 +132,8 @@ RemoteVideoDecoderParent::RemoteVideoDecoderParent(
|
|||
RemoteDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
|
||||
float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
|
||||
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue,
|
||||
Maybe<uint64_t> aMediaEngineId)
|
||||
: RemoteDecoderParent(aParent, aOptions, aManagerThread, aDecodeTaskQueue,
|
||||
aMediaEngineId),
|
||||
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue)
|
||||
: RemoteDecoderParent(aParent, aOptions, aManagerThread, aDecodeTaskQueue),
|
||||
mVideoInfo(aVideoInfo),
|
||||
mFramerate(aFramerate) {
|
||||
if (aIdentifier) {
|
||||
|
@ -160,7 +157,6 @@ IPCResult RemoteVideoDecoderParent::RecvConstruct(
|
|||
mVideoInfo, mKnowsCompositor,
|
||||
imageContainer, CreateDecoderParams::VideoFrameRate(mFramerate),
|
||||
mOptions, CreateDecoderParams::NoWrapper(true),
|
||||
mMediaEngineId,
|
||||
};
|
||||
|
||||
mParent->EnsurePDMFactory().CreateDecoder(params)->Then(
|
||||
|
|
|
@ -39,8 +39,7 @@ class RemoteVideoDecoderChild : public RemoteDecoderChild {
|
|||
MOZ_IS_CLASS_INIT MediaResult
|
||||
InitIPDL(const VideoInfo& aVideoInfo, float aFramerate,
|
||||
const CreateDecoderParams::OptionSet& aOptions,
|
||||
mozilla::Maybe<layers::TextureFactoryIdentifier> aIdentifier,
|
||||
const Maybe<uint64_t>& aMediaEngineId);
|
||||
mozilla::Maybe<layers::TextureFactoryIdentifier> aIdentifier);
|
||||
|
||||
MediaResult ProcessOutput(DecodedOutputIPDL&& aDecodedData) override;
|
||||
|
||||
|
@ -54,8 +53,7 @@ class RemoteVideoDecoderParent final : public RemoteDecoderParent {
|
|||
RemoteDecoderManagerParent* aParent, const VideoInfo& aVideoInfo,
|
||||
float aFramerate, const CreateDecoderParams::OptionSet& aOptions,
|
||||
const Maybe<layers::TextureFactoryIdentifier>& aIdentifier,
|
||||
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue,
|
||||
Maybe<uint64_t> aMediaEngineId);
|
||||
nsISerialEventTarget* aManagerThread, TaskQueue* aDecodeTaskQueue);
|
||||
|
||||
protected:
|
||||
IPCResult RecvConstruct(ConstructResolver&& aResolver) override;
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
IPDL_SOURCES += [
|
||||
"PMediaDecoderParams.ipdlh",
|
||||
"PRemoteDecoder.ipdl",
|
||||
"PRemoteDecoderManager.ipdl",
|
||||
]
|
||||
|
||||
PREPROCESSED_IPDL_SOURCES += [
|
||||
"PRDD.ipdl",
|
||||
"PRemoteDecoderManager.ipdl",
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
|
@ -54,23 +54,6 @@ SOURCES += [
|
|||
"RemoteVideoDecoder.cpp",
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_WMF"]:
|
||||
IPDL_SOURCES += [
|
||||
"PMFMediaEngine.ipdl",
|
||||
]
|
||||
SOURCES += [
|
||||
"MFMediaEngineChild.cpp",
|
||||
"MFMediaEngineParent.cpp",
|
||||
]
|
||||
EXPORTS.mozilla += [
|
||||
"MFMediaEngineChild.h",
|
||||
"MFMediaEngineParent.h",
|
||||
"MFMediaEngineUtils.h",
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
"../platforms/wmf",
|
||||
]
|
||||
|
||||
# so we can include nsMacUtilsImpl.h in RDDParent.cpp for sandboxing
|
||||
LOCAL_INCLUDES += [
|
||||
"/xpcom/base",
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "MediaSourceDecoder.h"
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
#include "ExternalEngineStateMachine.h"
|
||||
#include "MediaDecoderStateMachine.h"
|
||||
#include "MediaShutdownManager.h"
|
||||
#include "MediaSource.h"
|
||||
|
@ -35,7 +34,7 @@ MediaSourceDecoder::MediaSourceDecoder(MediaDecoderInit& aInit)
|
|||
mExplicitDuration.emplace(UnspecifiedNaN<double>());
|
||||
}
|
||||
|
||||
MediaDecoderStateMachineBase* MediaSourceDecoder::CreateStateMachine() {
|
||||
MediaDecoderStateMachine* MediaSourceDecoder::CreateStateMachine() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mDemuxer = new MediaSourceDemuxer(AbstractMainThread());
|
||||
MediaFormatReaderInit init;
|
||||
|
@ -45,13 +44,6 @@ MediaDecoderStateMachineBase* MediaSourceDecoder::CreateStateMachine() {
|
|||
init.mFrameStats = mFrameStats;
|
||||
init.mMediaDecoderOwnerID = mOwner;
|
||||
mReader = new MediaFormatReader(init, mDemuxer);
|
||||
#ifdef MOZ_WMF
|
||||
// TODO : Only for testing development for now. In the future this should be
|
||||
// used for encrypted content only.
|
||||
if (StaticPrefs::media_wmf_media_engine_enabled()) {
|
||||
return new ExternalEngineStateMachine(this, mReader);
|
||||
}
|
||||
#endif
|
||||
return new MediaDecoderStateMachine(this, mReader);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaDecoderStateMachineBase;
|
||||
class MediaDecoderStateMachine;
|
||||
class MediaSourceDemuxer;
|
||||
|
||||
namespace dom {
|
||||
|
@ -73,7 +73,7 @@ class MediaSourceDecoder : public MediaDecoder,
|
|||
void NotifyDataArrived();
|
||||
|
||||
private:
|
||||
MediaDecoderStateMachineBase* CreateStateMachine();
|
||||
MediaDecoderStateMachine* CreateStateMachine();
|
||||
void DoSetMediaSourceDuration(double aDuration);
|
||||
media::TimeInterval ClampIntervalToEnd(const media::TimeInterval& aInterval);
|
||||
bool CanPlayThroughImpl() override;
|
||||
|
|
|
@ -148,7 +148,6 @@ EXPORTS += [
|
|||
"DOMMediaStream.h",
|
||||
"DriftCompensation.h",
|
||||
"DynamicResampler.h",
|
||||
"ExternalEngineStateMachine.h",
|
||||
"FileBlockCache.h",
|
||||
"ForwardedInputTrack.h",
|
||||
"FrameStatistics.h",
|
||||
|
@ -161,7 +160,6 @@ EXPORTS += [
|
|||
"MediaDecoder.h",
|
||||
"MediaDecoderOwner.h",
|
||||
"MediaDecoderStateMachine.h",
|
||||
"MediaDecoderStateMachineBase.h",
|
||||
"MediaEventSource.h",
|
||||
"MediaFormatReader.h",
|
||||
"MediaInfo.h",
|
||||
|
@ -190,7 +188,6 @@ EXPORTS += [
|
|||
"PrincipalChangeObserver.h",
|
||||
"PrincipalHandle.h",
|
||||
"QueueObject.h",
|
||||
"ReaderProxy.h",
|
||||
"SeekJob.h",
|
||||
"SeekTarget.h",
|
||||
"SelfRef.h",
|
||||
|
@ -270,7 +267,6 @@ UNIFIED_SOURCES += [
|
|||
"DeviceInputTrack.cpp",
|
||||
"DOMMediaStream.cpp",
|
||||
"DynamicResampler.cpp",
|
||||
"ExternalEngineStateMachine.cpp",
|
||||
"FileBlockCache.cpp",
|
||||
"FileMediaResource.cpp",
|
||||
"ForwardedInputTrack.cpp",
|
||||
|
@ -280,9 +276,9 @@ UNIFIED_SOURCES += [
|
|||
"ImageToI420.cpp",
|
||||
"MediaCache.cpp",
|
||||
"MediaContainerType.cpp",
|
||||
"MediaData.cpp",
|
||||
"MediaDecoder.cpp",
|
||||
"MediaDecoderStateMachine.cpp",
|
||||
"MediaDecoderStateMachineBase.cpp",
|
||||
"MediaDeviceInfo.cpp",
|
||||
"MediaDevices.cpp",
|
||||
"MediaFormatReader.cpp",
|
||||
|
@ -324,11 +320,9 @@ else:
|
|||
# CubebUtils.cpp needs to be built separately due to what appears to be some kind
|
||||
# of compiler bug on Android 4.2 x86 opt. See bug 1408459.
|
||||
# DecoderTraits.cpp needs to be built separately because of Mac OS X headers.
|
||||
# MediaData.cpp : ambiguous compilation error caused by Mac OS X headers.
|
||||
SOURCES += [
|
||||
"CubebUtils.cpp",
|
||||
"DecoderTraits.cpp",
|
||||
"MediaData.cpp",
|
||||
]
|
||||
|
||||
# Some codec-related code uses multi-character constants, which GCC and clang
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#include "nsPrintfCString.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
# include "MFMediaEngineDecoderModule.h"
|
||||
# include "WMFDecoderModule.h"
|
||||
# include "mozilla/WindowsVersion.h"
|
||||
#endif
|
||||
|
@ -88,10 +87,6 @@ class PDMInitializer final {
|
|||
if (!IsWin7AndPre2000Compatible()) {
|
||||
WMFDecoderModule::Init();
|
||||
}
|
||||
|
||||
if (IsWin8OrLater() && StaticPrefs::media_wmf_media_engine_enabled()) {
|
||||
MFMediaEngineDecoderModule::Init();
|
||||
}
|
||||
#endif
|
||||
#ifdef MOZ_APPLEMEDIA
|
||||
AppleDecoderModule::Init();
|
||||
|
@ -512,9 +507,6 @@ static DecoderDoctorDiagnostics::Flags GetFailureFlagBasedOnFFmpegStatus(
|
|||
|
||||
void PDMFactory::CreateRddPDMs() {
|
||||
#ifdef XP_WIN
|
||||
if (IsWin8OrLater() && StaticPrefs::media_wmf_media_engine_enabled()) {
|
||||
CreateAndStartupPDM<MFMediaEngineDecoderModule>();
|
||||
}
|
||||
if (StaticPrefs::media_wmf_enabled() &&
|
||||
StaticPrefs::media_rdd_wmf_enabled()) {
|
||||
CreateAndStartupPDM<WMFDecoderModule>();
|
||||
|
|
|
@ -22,8 +22,7 @@ CreateDecoderParamsForAsync::CreateDecoderParamsForAsync(
|
|||
mType(aParams.mType),
|
||||
mOnWaitingForKeyEvent(aParams.mOnWaitingForKeyEvent),
|
||||
mOptions(aParams.mOptions),
|
||||
mRate(aParams.mRate),
|
||||
mMediaEngineId(aParams.mMediaEngineId) {}
|
||||
mRate(aParams.mRate) {}
|
||||
|
||||
CreateDecoderParamsForAsync::CreateDecoderParamsForAsync(
|
||||
CreateDecoderParamsForAsync&& aParams) = default;
|
||||
|
|
|
@ -108,7 +108,6 @@ struct CreateDecoderParamsForAsync {
|
|||
mOnWaitingForKeyEvent;
|
||||
const OptionSet mOptions = OptionSet(Option::Default);
|
||||
const media::VideoFrameRate mRate;
|
||||
const Maybe<uint64_t> mMediaEngineId;
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS CreateDecoderParams final {
|
||||
|
@ -131,8 +130,7 @@ struct MOZ_STACK_CLASS CreateDecoderParams final {
|
|||
mType(aParams.mType),
|
||||
mOnWaitingForKeyEvent(aParams.mOnWaitingForKeyEvent),
|
||||
mOptions(aParams.mOptions),
|
||||
mRate(aParams.mRate),
|
||||
mMediaEngineId(aParams.mMediaEngineId) {}
|
||||
mRate(aParams.mRate) {}
|
||||
|
||||
template <typename T1, typename... Ts>
|
||||
CreateDecoderParams(const TrackInfo& aConfig, T1&& a1, Ts&&... args)
|
||||
|
@ -178,8 +176,6 @@ struct MOZ_STACK_CLASS CreateDecoderParams final {
|
|||
mOnWaitingForKeyEvent;
|
||||
OptionSet mOptions = OptionSet(Option::Default);
|
||||
media::VideoFrameRate mRate;
|
||||
// Used on Windows when the MF media engine playback is enabled.
|
||||
Maybe<uint64_t> mMediaEngineId;
|
||||
|
||||
private:
|
||||
void Set(layers::ImageContainer* aImageContainer) {
|
||||
|
@ -208,9 +204,6 @@ struct MOZ_STACK_CLASS CreateDecoderParams final {
|
|||
aOnWaitingForKey) {
|
||||
mOnWaitingForKeyEvent = aOnWaitingForKey;
|
||||
}
|
||||
void Set(const Maybe<uint64_t>& aMediaEngineId) {
|
||||
mMediaEngineId = aMediaEngineId;
|
||||
}
|
||||
void Set(const CreateDecoderParams& aParams) {
|
||||
// Set all but mTrackInfo;
|
||||
mImageContainer = aParams.mImageContainer;
|
||||
|
@ -223,7 +216,6 @@ struct MOZ_STACK_CLASS CreateDecoderParams final {
|
|||
mOnWaitingForKeyEvent = aParams.mOnWaitingForKeyEvent;
|
||||
mOptions = aParams.mOptions;
|
||||
mRate = aParams.mRate;
|
||||
mMediaEngineId = aParams.mMediaEngineId;
|
||||
}
|
||||
template <typename T1, typename T2, typename... Ts>
|
||||
void Set(T1&& a1, T2&& a2, Ts&&... args) {
|
||||
|
@ -248,8 +240,7 @@ struct MOZ_STACK_CLASS SupportDecoderParams final {
|
|||
mUseNullDecoder(aParams.mUseNullDecoder),
|
||||
mNoWrapper(aParams.mNoWrapper),
|
||||
mOptions(aParams.mOptions),
|
||||
mRate(aParams.mRate),
|
||||
mMediaEngineId(aParams.mMediaEngineId) {}
|
||||
mRate(aParams.mRate) {}
|
||||
|
||||
template <typename T1, typename... Ts>
|
||||
SupportDecoderParams(const TrackInfo& aConfig, T1&& a1, Ts&&... args)
|
||||
|
@ -267,7 +258,6 @@ struct MOZ_STACK_CLASS SupportDecoderParams final {
|
|||
NoWrapper mNoWrapper;
|
||||
OptionSet mOptions = OptionSet(Option::Default);
|
||||
VideoFrameRate mRate;
|
||||
Maybe<uint64_t> mMediaEngineId;
|
||||
|
||||
private:
|
||||
void Set(DecoderDoctorDiagnostics* aDiagnostics) {
|
||||
|
@ -286,9 +276,6 @@ struct MOZ_STACK_CLASS SupportDecoderParams final {
|
|||
MOZ_ASSERT(aKnowsCompositor->IsThreadSafe());
|
||||
}
|
||||
}
|
||||
void Set(const Maybe<uint64_t>& aMediaEngineId) {
|
||||
mMediaEngineId = aMediaEngineId;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename... Ts>
|
||||
void Set(T1&& a1, T2&& a2, Ts&&... args) {
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/* 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 "MFMediaEngineAudioStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
RefPtr<MediaDataDecoder::InitPromise> MFMediaEngineAudioStream::Init() {
|
||||
// TODO : implement this by using MediaEngine API.
|
||||
return InitPromise::CreateAndResolve(TrackType::kAudioTrack, __func__);
|
||||
}
|
||||
|
||||
nsCString MFMediaEngineAudioStream::GetDescriptionName() const {
|
||||
return "media engine audio stream"_ns;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,22 +0,0 @@
|
|||
/* 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 DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEAUDIOSTREAM_H
|
||||
#define DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEAUDIOSTREAM_H
|
||||
|
||||
#include "MFMediaEngineStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MFMediaEngineAudioStream final : public MFMediaEngineStream {
|
||||
public:
|
||||
explicit MFMediaEngineAudioStream(const CreateDecoderParams& aParam) {}
|
||||
|
||||
RefPtr<InitPromise> Init() override;
|
||||
nsCString GetDescriptionName() const override;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEAUDIOSTREAM_H
|
|
@ -1,102 +0,0 @@
|
|||
/* 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 "MFMediaEngineDecoderModule.h"
|
||||
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/MFMediaEngineParent.h"
|
||||
#include "mozilla/MFMediaEngineUtils.h"
|
||||
#include "mozilla/StaticPrefs_media.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define LOG(msg, ...) \
|
||||
MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
|
||||
|
||||
/* static */
|
||||
void MFMediaEngineDecoderModule::Init() {
|
||||
// TODO : Init any thing that media engine would need. Implement this when we
|
||||
// start implementing media engine in following patches.
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<PlatformDecoderModule> MFMediaEngineDecoderModule::Create() {
|
||||
RefPtr<MFMediaEngineDecoderModule> module = new MFMediaEngineDecoderModule();
|
||||
return module.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
MFMediaEngineDecoderModule::CreateVideoDecoder(
|
||||
const CreateDecoderParams& aParams) {
|
||||
if (!aParams.mMediaEngineId ||
|
||||
!StaticPrefs::media_wmf_media_engine_enabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<MFMediaEngineParent> mediaEngine =
|
||||
MFMediaEngineParent::GetMediaEngineById(*aParams.mMediaEngineId);
|
||||
if (!mediaEngine) {
|
||||
LOG("Can't find media engine %" PRIu64 " for video decoder",
|
||||
*aParams.mMediaEngineId);
|
||||
return nullptr;
|
||||
}
|
||||
LOG("MFMediaEngineDecoderModule, CreateVideoDecoder");
|
||||
RefPtr<MediaDataDecoder> decoder = mediaEngine->GetMediaEngineStream(
|
||||
TrackInfo::TrackType::kVideoTrack, aParams);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
MFMediaEngineDecoderModule::CreateAudioDecoder(
|
||||
const CreateDecoderParams& aParams) {
|
||||
if (!aParams.mMediaEngineId ||
|
||||
!StaticPrefs::media_wmf_media_engine_enabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<MFMediaEngineParent> mediaEngine =
|
||||
MFMediaEngineParent::GetMediaEngineById(*aParams.mMediaEngineId);
|
||||
if (!mediaEngine) {
|
||||
LOG("Can't find media engine %" PRIu64 " for audio decoder",
|
||||
*aParams.mMediaEngineId);
|
||||
return nullptr;
|
||||
}
|
||||
LOG("MFMediaEngineDecoderModule, CreateAudioDecoder");
|
||||
RefPtr<MediaDataDecoder> decoder = mediaEngine->GetMediaEngineStream(
|
||||
TrackInfo::TrackType::kAudioTrack, aParams);
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
media::DecodeSupportSet MFMediaEngineDecoderModule::SupportsMimeType(
|
||||
const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
|
||||
UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType);
|
||||
if (!trackInfo) {
|
||||
return media::DecodeSupport::Unsupported;
|
||||
}
|
||||
return SupportInternal(SupportDecoderParams(*trackInfo), aDiagnostics);
|
||||
}
|
||||
|
||||
media::DecodeSupportSet MFMediaEngineDecoderModule::Supports(
|
||||
const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const {
|
||||
if (!aParams.mMediaEngineId) {
|
||||
return media::DecodeSupport::Unsupported;
|
||||
}
|
||||
return SupportInternal(aParams, aDiagnostics);
|
||||
}
|
||||
|
||||
media::DecodeSupportSet MFMediaEngineDecoderModule::SupportInternal(
|
||||
const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const {
|
||||
if (!StaticPrefs::media_wmf_media_engine_enabled()) {
|
||||
return media::DecodeSupport::Unsupported;
|
||||
}
|
||||
// TODO : ask media engine or simply ask WMFDecoderModule? I guess the
|
||||
// internal implementation of MFMediaEngine should be using MFT, so they
|
||||
// should be the same in term of support types. Implement this when we
|
||||
// start implementing media engine in following patches.
|
||||
return media::DecodeSupport::SoftwareDecode;
|
||||
}
|
||||
|
||||
#undef LOG
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,41 +0,0 @@
|
|||
/* 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 DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEDECODERMODULE_H
|
||||
#define DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEDECODERMODULE_H
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MFMediaEngineDecoderModule final : public PlatformDecoderModule {
|
||||
public:
|
||||
static void Init();
|
||||
|
||||
static already_AddRefed<PlatformDecoderModule> Create();
|
||||
|
||||
already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
|
||||
const CreateDecoderParams& aParams) override;
|
||||
|
||||
already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
|
||||
const CreateDecoderParams& aParams) override;
|
||||
|
||||
media::DecodeSupportSet SupportsMimeType(
|
||||
const nsACString& aMimeType,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const override;
|
||||
media::DecodeSupportSet Supports(
|
||||
const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const override;
|
||||
|
||||
private:
|
||||
media::DecodeSupportSet SupportInternal(
|
||||
const SupportDecoderParams& aParams,
|
||||
DecoderDoctorDiagnostics* aDiagnostics) const;
|
||||
MFMediaEngineDecoderModule() = default;
|
||||
~MFMediaEngineDecoderModule() = default;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEDECODERMODULE_H
|
|
@ -1,34 +0,0 @@
|
|||
/* 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 "MFMediaEngineStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStream::Decode(
|
||||
MediaRawData* aSample) {
|
||||
// TODO : implement this by using MediaEngine API.
|
||||
return DecodePromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
__func__);
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise> MFMediaEngineStream::Drain() {
|
||||
// TODO : implement this by using MediaEngine API.
|
||||
return DecodePromise::CreateAndReject(NS_ERROR_DOM_MEDIA_DECODE_ERR,
|
||||
__func__);
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::FlushPromise> MFMediaEngineStream::Flush() {
|
||||
// TODO : implement this by using MediaEngine API.
|
||||
return MediaDataDecoder::FlushPromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, RESULT_DETAIL("NotImpl")),
|
||||
__func__);
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise> MFMediaEngineStream::Shutdown() {
|
||||
// TODO : implement this by using MediaEngine API.
|
||||
return ShutdownPromise::CreateAndReject(false, __func__);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,31 +0,0 @@
|
|||
/* 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 DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINESTREAM_H
|
||||
#define DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINESTREAM_H
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* MFMediaEngineStream represents a track which would be responsible to provide
|
||||
* encoded data into the media engine. The media engine can access this stream
|
||||
* by the presentation descriptor which was acquired from the custom media
|
||||
* source.
|
||||
*/
|
||||
// TODO : Inherit IMFMediaStream in following patches
|
||||
class MFMediaEngineStream : public MediaDataDecoder {
|
||||
public:
|
||||
MFMediaEngineStream() = default;
|
||||
|
||||
RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
|
||||
RefPtr<DecodePromise> Drain() override;
|
||||
RefPtr<FlushPromise> Flush() override;
|
||||
RefPtr<ShutdownPromise> Shutdown() override;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINESTREAM_H
|
|
@ -1,24 +0,0 @@
|
|||
/* 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 "MFMediaEngineVideoStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
RefPtr<MediaDataDecoder::InitPromise> MFMediaEngineVideoStream::Init() {
|
||||
// TODO : implement this by using MediaEngine API.
|
||||
return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__);
|
||||
}
|
||||
|
||||
nsCString MFMediaEngineVideoStream::GetDescriptionName() const {
|
||||
return "media engine video stream"_ns;
|
||||
}
|
||||
|
||||
MediaDataDecoder::ConversionRequired MFMediaEngineVideoStream::NeedsConversion()
|
||||
const {
|
||||
// TODO : check video type
|
||||
return ConversionRequired::kNeedAnnexB;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -1,23 +0,0 @@
|
|||
/* 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 DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEVIDEOSTREAM_H
|
||||
#define DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEVIDEOSTREAM_H
|
||||
|
||||
#include "MFMediaEngineStream.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MFMediaEngineVideoStream final : public MFMediaEngineStream {
|
||||
public:
|
||||
explicit MFMediaEngineVideoStream(const CreateDecoderParams& aParam) {}
|
||||
|
||||
RefPtr<InitPromise> Init() override;
|
||||
nsCString GetDescriptionName() const override;
|
||||
ConversionRequired NeedsConversion() const override;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_PLATFORM_WMF_MFMEDIAENGINEVIDEOSTREAM_H
|
|
@ -6,10 +6,6 @@
|
|||
|
||||
EXPORTS += [
|
||||
"DXVA2Manager.h",
|
||||
"MFMediaEngineAudioStream.h",
|
||||
"MFMediaEngineDecoderModule.h",
|
||||
"MFMediaEngineStream.h",
|
||||
"MFMediaEngineVideoStream.h",
|
||||
"MFTDecoder.h",
|
||||
"WMF.h",
|
||||
"WMFAudioMFTManager.h",
|
||||
|
@ -20,13 +16,8 @@ EXPORTS += [
|
|||
"WMFUtils.h",
|
||||
"WMFVideoMFTManager.h",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"DXVA2Manager.cpp",
|
||||
"MFMediaEngineAudioStream.cpp",
|
||||
"MFMediaEngineDecoderModule.cpp",
|
||||
"MFMediaEngineStream.cpp",
|
||||
"MFMediaEngineVideoStream.cpp",
|
||||
"MFTDecoder.cpp",
|
||||
"MFTEncoder.cpp",
|
||||
"WMFAudioMFTManager.cpp",
|
||||
|
|
|
@ -9145,12 +9145,6 @@
|
|||
value: true
|
||||
mirror: always
|
||||
|
||||
# Using Windows Media Foundation Media Engine for encrypted playback
|
||||
- name: media.wmf.media-engine.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
#endif # MOZ_WMF
|
||||
|
||||
- name: media.decoder-doctor.testing
|
||||
|
|
Загрузка…
Ссылка в новой задаче