Bug 1544650 - Always pre-create MediaStreamTracks for DecodedStream in MediaDecoder. r=padenot

This moves the responsibility for creating MediaStreamTracks from
DecodedStream::Start to MediaDecoder. This let's MediaDecoder create them as
soon as metadata is known. This gives the application guarantees on when tracks
can be expected to exist, and aligns with the spec that says they should be
created when metadata is known.

Differential Revision: https://phabricator.services.mozilla.com/D28473

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andreas Pehrson 2019-04-23 16:46:30 +00:00
Родитель eaf1537b05
Коммит 1e4382f9fb
6 изменённых файлов: 98 добавлений и 83 удалений

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

@ -248,7 +248,10 @@ void MediaDecoder::AddOutputStream(DOMMediaStream* aStream) {
MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load()."); MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
AbstractThread::AutoEnter context(AbstractMainThread()); AbstractThread::AutoEnter context(AbstractMainThread());
mDecoderStateMachine->EnsureOutputStreamManager( mDecoderStateMachine->EnsureOutputStreamManager(
aStream->GetInputStream()->Graph(), ToMaybe(mInfo.get())); aStream->GetInputStream()->Graph());
if (mInfo) {
mDecoderStateMachine->EnsureOutputStreamManagerHasTracks(*mInfo);
}
mDecoderStateMachine->AddOutputStream(aStream); mDecoderStateMachine->AddOutputStream(aStream);
} }
@ -656,6 +659,7 @@ void MediaDecoder::MetadataLoaded(
aInfo->mMediaSeekableOnlyInBufferedRanges; aInfo->mMediaSeekableOnlyInBufferedRanges;
mInfo = aInfo.release(); mInfo = aInfo.release();
GetOwner()->ConstructMediaTracks(mInfo); GetOwner()->ConstructMediaTracks(mInfo);
mDecoderStateMachine->EnsureOutputStreamManagerHasTracks(*mInfo);
// Make sure the element and the frame (if any) are told about // Make sure the element and the frame (if any) are told about
// our new size. // our new size.

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

@ -3338,8 +3338,8 @@ void MediaDecoderStateMachine::FinishDecodeFirstFrame() {
RefPtr<ShutdownPromise> MediaDecoderStateMachine::BeginShutdown() { RefPtr<ShutdownPromise> MediaDecoderStateMachine::BeginShutdown() {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
if (mOutputStreamManager) { if (mOutputStreamManager) {
mNextOutputStreamTrackID = mOutputStreamManager->NextTrackID();
mOutputStreamManager->Disconnect(); mOutputStreamManager->Disconnect();
mNextOutputStreamTrackID = mOutputStreamManager->NextTrackID();
} }
return InvokeAsync(OwnerThread(), this, __func__, return InvokeAsync(OwnerThread(), this, __func__,
&MediaDecoderStateMachine::Shutdown); &MediaDecoderStateMachine::Shutdown);
@ -3790,28 +3790,39 @@ void MediaDecoderStateMachine::RemoveOutputStream(DOMMediaStream* aStream) {
} }
void MediaDecoderStateMachine::EnsureOutputStreamManager( void MediaDecoderStateMachine::EnsureOutputStreamManager(
MediaStreamGraph* aGraph, const Maybe<MediaInfo>& aLoadedInfo) { MediaStreamGraph* aGraph) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
if (mOutputStreamManager) { if (mOutputStreamManager) {
return; return;
} }
LOG("Starting output track allocations at id %d", mNextOutputStreamTrackID);
mOutputStreamManager = new OutputStreamManager( mOutputStreamManager = new OutputStreamManager(
aGraph->CreateSourceStream(), mNextOutputStreamTrackID, aGraph->CreateSourceStream(), mNextOutputStreamTrackID,
mOutputStreamPrincipal, mOutputStreamCORSMode, mAbstractMainThread); mOutputStreamPrincipal, mOutputStreamCORSMode, mAbstractMainThread);
if (!aLoadedInfo) { }
void MediaDecoderStateMachine::EnsureOutputStreamManagerHasTracks(
const MediaInfo& aLoadedInfo) {
MOZ_ASSERT(NS_IsMainThread());
if (!mOutputStreamManager) {
return; return;
} }
TrackID mirroredTrackIDAllocation = mNextOutputStreamTrackID; if ((!aLoadedInfo.HasAudio() ||
if (aLoadedInfo->HasAudio()) { mOutputStreamManager->HasTrackType(MediaSegment::AUDIO)) &&
mOutputStreamManager->AddTrack(mirroredTrackIDAllocation++, (!aLoadedInfo.HasVideo() ||
MediaSegment::AUDIO); mOutputStreamManager->HasTrackType(MediaSegment::VIDEO))) {
LOG("Pre-created audio track with id %d", mirroredTrackIDAllocation - 1); return;
} }
if (aLoadedInfo->HasVideo()) { if (aLoadedInfo.HasAudio()) {
mOutputStreamManager->AddTrack(mirroredTrackIDAllocation++, MOZ_ASSERT(!mOutputStreamManager->HasTrackType(MediaSegment::AUDIO));
MediaSegment::VIDEO); mOutputStreamManager->AddTrack(MediaSegment::AUDIO);
LOG("Pre-created video track with id %d", mirroredTrackIDAllocation - 1); LOG("Pre-created audio track with id %d",
mOutputStreamManager->GetLiveTrackIDFor(MediaSegment::AUDIO));
}
if (aLoadedInfo.HasVideo()) {
MOZ_ASSERT(!mOutputStreamManager->HasTrackType(MediaSegment::VIDEO));
mOutputStreamManager->AddTrack(MediaSegment::VIDEO);
LOG("Pre-created video track with id %d",
mOutputStreamManager->GetLiveTrackIDFor(MediaSegment::VIDEO));
} }
} }

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

@ -186,11 +186,11 @@ class MediaDecoderStateMachine
void SetOutputStreamPrincipal(const nsCOMPtr<nsIPrincipal>& aPrincipal); void SetOutputStreamPrincipal(const nsCOMPtr<nsIPrincipal>& aPrincipal);
void SetOutputStreamCORSMode(CORSMode aCORSMode); void SetOutputStreamCORSMode(CORSMode aCORSMode);
// If an OutputStreamManager does not exist, one will be created and tracks // If an OutputStreamManager does not exist, one will be created.
// matching aLoadedInfo will be created ahead of being created by the void EnsureOutputStreamManager(MediaStreamGraph* aGraph);
// DecodedStream sink. // If an OutputStreamManager exists, tracks matching aLoadedInfo will be
void EnsureOutputStreamManager(MediaStreamGraph* aGraph, // created unless they already exist in the manager.
const Maybe<MediaInfo>& aLoadedInfo); void EnsureOutputStreamManagerHasTracks(const MediaInfo& aLoadedInfo);
// Add an output stream to the output stream manager. The manager must have // Add an output stream to the output stream manager. The manager must have
// been created through EnsureOutputStreamManager() before this. // been created through EnsureOutputStreamManager() before this.
void AddOutputStream(DOMMediaStream* aStream); void AddOutputStream(DOMMediaStream* aStream);

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

@ -317,32 +317,14 @@ DecodedStreamData::DecodedStreamData(
mOutputStreamManager(aOutputStreamManager), mOutputStreamManager(aOutputStreamManager),
mAbstractMainThread(aMainThread) { mAbstractMainThread(aMainThread) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
// Initialize tracks on main thread and in the MediaStreamGraph. MOZ_DIAGNOSTIC_ASSERT(
// Tracks on main thread may have been created early in OutputStreamManager mOutputStreamManager->HasTracks(aInit.mAudioTrackID, aInit.mVideoTrackID),
// by the state machine, since creating them here is async from the js call. "Tracks must be pre-created on main thread");
// If they were pre-created in OutputStreamManager and the MediaInfo has
// changed since then, we end them and create new tracks.
if (!mOutputStreamManager->HasTracks(aInit.mAudioTrackID,
aInit.mVideoTrackID)) {
// Because these tracks were pre-allocated, we also have to increment the
// internal track allocator by the same number of tracks, so we don't risk
// a TrackID collision.
for (size_t i = 0; i < mOutputStreamManager->NumberOfTracks(); ++i) {
Unused << mOutputStreamManager->AllocateNextTrackID();
}
mOutputStreamManager->RemoveTracks();
}
if (IsTrackIDExplicit(aInit.mAudioTrackID)) { if (IsTrackIDExplicit(aInit.mAudioTrackID)) {
if (!mOutputStreamManager->HasTrack(aInit.mAudioTrackID)) {
mOutputStreamManager->AddTrack(aInit.mAudioTrackID, MediaSegment::AUDIO);
}
mStream->AddAudioTrack(aInit.mAudioTrackID, aInit.mInfo.mAudio.mRate, mStream->AddAudioTrack(aInit.mAudioTrackID, aInit.mInfo.mAudio.mRate,
new AudioSegment()); new AudioSegment());
} }
if (IsTrackIDExplicit(aInit.mVideoTrackID)) { if (IsTrackIDExplicit(aInit.mVideoTrackID)) {
if (!mOutputStreamManager->HasTrack(aInit.mVideoTrackID)) {
mOutputStreamManager->AddTrack(aInit.mVideoTrackID, MediaSegment::VIDEO);
}
mStream->AddTrack(aInit.mVideoTrackID, new VideoSegment()); mStream->AddTrack(aInit.mVideoTrackID, new VideoSegment());
} }
} }
@ -457,12 +439,18 @@ nsresult DecodedStream::Start(const TimeUnit& aStartTime,
mVideoEndedPromise.Resolve(true, __func__); mVideoEndedPromise.Resolve(true, __func__);
return NS_OK; return NS_OK;
} }
mInit.mAudioTrackID = mInit.mInfo.HasAudio() if (mInit.mInfo.HasAudio() &&
? mOutputStreamManager->AllocateNextTrackID() !mOutputStreamManager->HasTrackType(MediaSegment::AUDIO)) {
: TRACK_NONE; mOutputStreamManager->AddTrack(MediaSegment::AUDIO);
mInit.mVideoTrackID = mInit.mInfo.HasVideo() }
? mOutputStreamManager->AllocateNextTrackID() if (mInit.mInfo.HasVideo() &&
: TRACK_NONE; !mOutputStreamManager->HasTrackType(MediaSegment::VIDEO)) {
mOutputStreamManager->AddTrack(MediaSegment::VIDEO);
}
mInit.mAudioTrackID =
mOutputStreamManager->GetLiveTrackIDFor(MediaSegment::AUDIO);
mInit.mVideoTrackID =
mOutputStreamManager->GetLiveTrackIDFor(MediaSegment::VIDEO);
mData = MakeUnique<DecodedStreamData>( mData = MakeUnique<DecodedStreamData>(
mOutputStreamManager, std::move(mInit), std::move(mAudioEndedPromise), mOutputStreamManager, std::move(mInit), std::move(mAudioEndedPromise),
std::move(mVideoEndedPromise), mAbstractMainThread); std::move(mVideoEndedPromise), mAbstractMainThread);
@ -542,9 +530,12 @@ void DecodedStream::DestroyData(UniquePtr<DecodedStreamData>&& aData) {
mOutputListener.Disconnect(); mOutputListener.Disconnect();
NS_DispatchToMainThread( NS_DispatchToMainThread(NS_NewRunnableFunction(
NS_NewRunnableFunction("DecodedStream::DestroyData", "DecodedStream::DestroyData",
[data = std::move(aData)]() { data->Forget(); })); [data = std::move(aData), manager = mOutputStreamManager]() {
data->Forget();
manager->RemoveTracks();
}));
} }
void DecodedStream::SetPlaying(bool aPlaying) { void DecodedStream::SetPlaying(bool aPlaying) {

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

@ -201,10 +201,10 @@ void OutputStreamManager::Remove(DOMMediaStream* aDOMStream) {
MOZ_ASSERT(rv); MOZ_ASSERT(rv);
} }
bool OutputStreamManager::HasTrack(TrackID aTrackID) { bool OutputStreamManager::HasTrackType(MediaSegment::Type aType) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
return mLiveTracks.Contains(aTrackID, TrackIDComparator()); return mLiveTracks.Contains(aType, TrackTypeComparator());
} }
bool OutputStreamManager::HasTracks(TrackID aAudioTrack, TrackID aVideoTrack) { bool OutputStreamManager::HasTracks(TrackID aAudioTrack, TrackID aVideoTrack) {
@ -216,13 +216,13 @@ bool OutputStreamManager::HasTracks(TrackID aAudioTrack, TrackID aVideoTrack) {
Unused << ++nrExpectedTracks; Unused << ++nrExpectedTracks;
asExpected = asExpected && mLiveTracks.Contains( asExpected = asExpected && mLiveTracks.Contains(
MakePair(aAudioTrack, MediaSegment::AUDIO), MakePair(aAudioTrack, MediaSegment::AUDIO),
TrackTypeComparator()); TrackComparator());
} }
if (IsTrackIDExplicit(aVideoTrack)) { if (IsTrackIDExplicit(aVideoTrack)) {
Unused << ++nrExpectedTracks; Unused << ++nrExpectedTracks;
asExpected = asExpected && mLiveTracks.Contains( asExpected = asExpected && mLiveTracks.Contains(
MakePair(aVideoTrack, MediaSegment::VIDEO), MakePair(aVideoTrack, MediaSegment::VIDEO),
TrackTypeComparator()); TrackComparator());
} }
asExpected = asExpected && mLiveTracks.Length() == nrExpectedTracks; asExpected = asExpected && mLiveTracks.Length() == nrExpectedTracks;
return asExpected; return asExpected;
@ -233,17 +233,20 @@ size_t OutputStreamManager::NumberOfTracks() {
return mLiveTracks.Length(); return mLiveTracks.Length();
} }
void OutputStreamManager::AddTrack(TrackID aTrackID, MediaSegment::Type aType) { void OutputStreamManager::AddTrack(MediaSegment::Type aType) {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mSourceStream->IsDestroyed()); MOZ_ASSERT(!mSourceStream->IsDestroyed());
MOZ_ASSERT(!HasTrack(aTrackID)); MOZ_ASSERT(!HasTrackType(aType),
"Cannot have two tracks of the same type at the same time");
TrackID id = mNextTrackID++;
LOG(LogLevel::Info, "Adding %s track with id %d", LOG(LogLevel::Info, "Adding %s track with id %d",
aType == MediaSegment::AUDIO ? "audio" : "video", aTrackID); aType == MediaSegment::AUDIO ? "audio" : "video", id);
mLiveTracks.AppendElement(MakePair(aTrackID, aType)); mLiveTracks.AppendElement(MakePair(id, aType));
for (const auto& data : mStreams) { for (const auto& data : mStreams) {
data->AddTrack(aTrackID, aType, mPrincipal, mCORSMode, true); data->AddTrack(id, aType, mPrincipal, mCORSMode, true);
} }
} }
@ -259,22 +262,16 @@ void OutputStreamManager::RemoveTrack(TrackID aTrackID) {
} }
void OutputStreamManager::RemoveTracks() { void OutputStreamManager::RemoveTracks() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mSourceStream->IsDestroyed());
for (const Pair<TrackID, MediaSegment::Type>& pair : mLiveTracks) {
for (const auto& data : mStreams) {
data->RemoveTrack(pair.first());
}
}
mLiveTracks.Clear();
}
void OutputStreamManager::Disconnect() {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
nsTArray<Pair<TrackID, MediaSegment::Type>> liveTracks(mLiveTracks); nsTArray<Pair<TrackID, MediaSegment::Type>> liveTracks(mLiveTracks);
for (const auto& pair : liveTracks) { for (const auto& pair : liveTracks) {
RemoveTrack(pair.first()); RemoveTrack(pair.first());
} }
}
void OutputStreamManager::Disconnect() {
MOZ_ASSERT(NS_IsMainThread());
RemoveTracks();
MOZ_ASSERT(mLiveTracks.IsEmpty()); MOZ_ASSERT(mLiveTracks.IsEmpty());
nsTArray<RefPtr<DOMMediaStream>> domStreams(mStreams.Length()); nsTArray<RefPtr<DOMMediaStream>> domStreams(mStreams.Length());
for (const auto& data : mStreams) { for (const auto& data : mStreams) {
@ -311,10 +308,14 @@ TrackID OutputStreamManager::NextTrackID() const {
return mNextTrackID; return mNextTrackID;
} }
TrackID OutputStreamManager::AllocateNextTrackID() { TrackID OutputStreamManager::GetLiveTrackIDFor(MediaSegment::Type aType) const {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(IsTrackIDExplicit(mNextTrackID)); for (const auto& pair : mLiveTracks) {
return mNextTrackID++; if (pair.second() == aType) {
return pair.first();
}
}
return TRACK_NONE;
} }
void OutputStreamManager::SetPlaying(bool aPlaying) { void OutputStreamManager::SetPlaying(bool aPlaying) {

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

@ -76,18 +76,16 @@ class OutputStreamManager {
void Add(DOMMediaStream* aDOMStream); void Add(DOMMediaStream* aDOMStream);
// Remove the output stream from the collection. // Remove the output stream from the collection.
void Remove(DOMMediaStream* aDOMStream); void Remove(DOMMediaStream* aDOMStream);
// Returns true if aTrackID has been added to all output streams. // Returns true if there's a live track of the given type.
bool HasTrack(TrackID aTrackID); bool HasTrackType(MediaSegment::Type aType);
// Returns true if the given tracks and no others are currently live. // Returns true if the given tracks and no others are currently live.
// Use a non-explicit TrackID to make it ignored for that type. // Use a non-explicit TrackID to make it ignored for that type.
bool HasTracks(TrackID aAudioTrack, TrackID aVideoTrack); bool HasTracks(TrackID aAudioTrack, TrackID aVideoTrack);
// Returns the number of live tracks. // Returns the number of live tracks.
size_t NumberOfTracks(); size_t NumberOfTracks();
// Add aTrackID to all output streams. // Add a track to all output streams.
void AddTrack(TrackID aTrackID, MediaSegment::Type aType); void AddTrack(MediaSegment::Type aType);
// Remove aTrackID from all output streams. // Remove all currently live tracks from all output streams.
void RemoveTrack(TrackID aTrackID);
// Remove all added tracks from all output streams.
void RemoveTracks(); void RemoveTracks();
// Disconnect mSourceStream from all output streams. // Disconnect mSourceStream from all output streams.
void Disconnect(); void Disconnect();
@ -100,11 +98,11 @@ class OutputStreamManager {
// Called when the CORSMode for the media element owning the decoder has // Called when the CORSMode for the media element owning the decoder has
// changed. // changed.
void SetCORSMode(CORSMode aCORSMode); void SetCORSMode(CORSMode aCORSMode);
// Returns the track id that would be used the next time a track is allocated. // Returns the track id that would be used the next time a track is added.
TrackID NextTrackID() const; TrackID NextTrackID() const;
// Like NextTrackID() but advances internal state, so the next call returns a // Returns the TrackID for the currently live track of the given type, or
// new unique TrackID. // TRACK_NONE otherwise.
TrackID AllocateNextTrackID(); TrackID GetLiveTrackIDFor(MediaSegment::Type aType) const;
// Called by DecodedStream when its playing state changes. While not playing // Called by DecodedStream when its playing state changes. While not playing
// we suspend mSourceStream. // we suspend mSourceStream.
void SetPlaying(bool aPlaying); void SetPlaying(bool aPlaying);
@ -134,12 +132,22 @@ class OutputStreamManager {
} }
}; };
struct TrackTypeComparator { struct TrackTypeComparator {
static bool Equals(const Pair<TrackID, MediaSegment::Type>& aLiveTrack,
MediaSegment::Type aType) {
return aLiveTrack.second() == aType;
}
};
struct TrackComparator {
static bool Equals(const Pair<TrackID, MediaSegment::Type>& aLiveTrack, static bool Equals(const Pair<TrackID, MediaSegment::Type>& aLiveTrack,
const Pair<TrackID, MediaSegment::Type>& aOther) { const Pair<TrackID, MediaSegment::Type>& aOther) {
return aLiveTrack.first() == aOther.first() && return aLiveTrack.first() == aOther.first() &&
aLiveTrack.second() == aOther.second(); aLiveTrack.second() == aOther.second();
} }
}; };
// Remove aTrackID from all output streams.
void RemoveTrack(TrackID aTrackID);
nsTArray<UniquePtr<OutputStreamData>> mStreams; nsTArray<UniquePtr<OutputStreamData>> mStreams;
nsTArray<Pair<TrackID, MediaSegment::Type>> mLiveTracks; nsTArray<Pair<TrackID, MediaSegment::Type>> mLiveTracks;
Canonical<PrincipalHandle> mPrincipalHandle; Canonical<PrincipalHandle> mPrincipalHandle;