Media: harden TrackID handling.

This commit is contained in:
wolfbeast 2018-06-07 16:04:56 +02:00 committed by Roy Tam
commit 0bdecfc6ea
9 changed files with 183 additions and 37 deletions

View file

@ -327,11 +327,13 @@ MediaDecoder::SetVolume(double aVolume)
void
MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
TrackID aNextAvailableTrackID,
bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
mDecoderStateMachine->AddOutputStream(aStream, aFinishWhenEnded);
mDecoderStateMachine->AddOutputStream(
aStream, aNextAvailableTrackID, aFinishWhenEnded);
}
void
@ -342,6 +344,14 @@ MediaDecoder::RemoveOutputStream(MediaStream* aStream)
mDecoderStateMachine->RemoveOutputStream(aStream);
}
TrackID
MediaDecoder::NextAvailableTrackIDFor(MediaStream* aOutputStream) const
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
return mDecoderStateMachine->NextAvailableTrackIDFor(aOutputStream);
}
double
MediaDecoder::GetDuration()
{
@ -1736,6 +1746,8 @@ MediaDecoder::RemoveMediaTracks()
videoList->RemoveTracks();
}
element->EndPreCreatedCapturedDecoderTracks();
mMediaTracksConstructed = false;
}

View file

@ -210,9 +210,13 @@ public:
// Add an output stream. All decoder output will be sent to the stream.
// The stream is initially blocked. The decoder is responsible for unblocking
// it while it is playing back.
virtual void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
virtual void AddOutputStream(ProcessedMediaStream* aStream,
TrackID aNextAvailableTrackID,
bool aFinishWhenEnded);
// Remove an output stream added with AddOutputStream.
virtual void RemoveOutputStream(MediaStream* aStream);
// The next TrackID that can be used without risk of a collision.
virtual TrackID NextAvailableTrackIDFor(MediaStream* aOutputStream) const;
// Return the duration of the video in seconds.
virtual double GetDuration();

View file

@ -2835,6 +2835,10 @@ MediaDecoderStateMachine::FinishDecodeFirstFrame()
RefPtr<ShutdownPromise>
MediaDecoderStateMachine::BeginShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
if (mOutputStreamManager) {
mOutputStreamManager->Clear();
}
return InvokeAsync(OwnerThread(), this, __func__,
&MediaDecoderStateMachine::Shutdown);
}
@ -3250,11 +3254,12 @@ MediaDecoderStateMachine::DumpDebugInfo()
}
void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
TrackID aNextAvailableTrackID,
bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG("AddOutputStream aStream=%p!", aStream);
mOutputStreamManager->Add(aStream, aFinishWhenEnded);
mOutputStreamManager->Add(aStream, aNextAvailableTrackID, aFinishWhenEnded);
nsCOMPtr<nsIRunnable> r = NewRunnableMethod<bool>(
this, &MediaDecoderStateMachine::SetAudioCaptured, true);
OwnerThread()->Dispatch(r.forget());
@ -3269,9 +3274,17 @@ void MediaDecoderStateMachine::RemoveOutputStream(MediaStream* aStream)
nsCOMPtr<nsIRunnable> r = NewRunnableMethod<bool>(
this, &MediaDecoderStateMachine::SetAudioCaptured, false);
OwnerThread()->Dispatch(r.forget());
}
}
TrackID
MediaDecoderStateMachine::NextAvailableTrackIDFor(MediaStream* aOutputStream) const
{
MOZ_ASSERT(NS_IsMainThread());
return mOutputStreamManager->NextAvailableTrackIDFor(aOutputStream);
}
size_t
MediaDecoderStateMachine::SizeOfVideoQueue() const
{

View file

@ -168,9 +168,12 @@ public:
void DumpDebugInfo();
void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
void AddOutputStream(ProcessedMediaStream* aStream,
TrackID aNextAvailableTrackID,
bool aFinishWhenEnded);
// Remove an output stream added with AddOutputStream.
void RemoveOutputStream(MediaStream* aStream);
TrackID NextAvailableTrackIDFor(MediaStream* aOutputStream) const;
// Seeks to the decoder to aTarget asynchronously.
RefPtr<MediaDecoder::SeekPromise> InvokeSeek(SeekTarget aTarget);

View file

@ -181,17 +181,22 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager* aOutputStreamManager,
, mOutputStreamManager(aOutputStreamManager)
{
mStream->AddListener(mListener);
mOutputStreamManager->Connect(mStream);
TrackID audioTrack = TRACK_NONE;
TrackID videoTrack = TRACK_NONE;
// Initialize tracks.
if (aInit.mInfo.HasAudio()) {
mStream->AddAudioTrack(aInit.mInfo.mAudio.mTrackId,
audioTrack = aInit.mInfo.mAudio.mTrackId;
mStream->AddAudioTrack(audioTrack,
aInit.mInfo.mAudio.mRate,
0, new AudioSegment());
}
if (aInit.mInfo.HasVideo()) {
mStream->AddTrack(aInit.mInfo.mVideo.mTrackId, 0, new VideoSegment());
videoTrack = aInit.mInfo.mVideo.mTrackId;
mStream->AddTrack(videoTrack, 0, new VideoSegment());
}
mOutputStreamManager->Connect(mStream, audioTrack, videoTrack);
}
DecodedStreamData::~DecodedStreamData()

View file

@ -13,29 +13,41 @@ OutputStreamData::~OutputStreamData()
{
MOZ_ASSERT(NS_IsMainThread());
// Break the connection to the input stream if necessary.
if (mPort) {
mPort->Destroy();
for (RefPtr<MediaInputPort>& port : mPorts) {
port->Destroy();
}
}
void
OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream)
OutputStreamData::Init(OutputStreamManager* aOwner,
ProcessedMediaStream* aStream,
TrackID aNextAvailableTrackID)
{
mOwner = aOwner;
mStream = aStream;
mNextAvailableTrackID = aNextAvailableTrackID;
}
bool
OutputStreamData::Connect(MediaStream* aStream)
OutputStreamData::Connect(MediaStream* aStream,
TrackID aInputAudioTrackID,
TrackID aInputVideoTrackID)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mPort, "Already connected?");
MOZ_ASSERT(mPorts.IsEmpty(), "Already connected?");
if (mStream->IsDestroyed()) {
return false;
}
mPort = mStream->AllocateInputPort(aStream);
for (TrackID tid : {aInputAudioTrackID, aInputVideoTrackID}) {
if (tid == TRACK_NONE) {
continue;
}
MOZ_ASSERT(IsTrackIDExplicit(tid));
mPorts.AppendElement(mStream->AllocateInputPort(
aStream, tid, mNextAvailableTrackID++));
}
return true;
}
@ -51,11 +63,11 @@ OutputStreamData::Disconnect()
return false;
}
// Disconnect the existing port if necessary.
if (mPort) {
mPort->Destroy();
mPort = nullptr;
// Disconnect any existing port.
for (RefPtr<MediaInputPort>& port : mPorts) {
port->Destroy();
}
mPorts.Clear();
return true;
}
@ -71,8 +83,16 @@ OutputStreamData::Graph() const
return mStream->Graph();
}
TrackID
OutputStreamData::NextAvailableTrackID() const
{
return mNextAvailableTrackID;
}
void
OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
OutputStreamManager::Add(ProcessedMediaStream* aStream,
TrackID aNextAvailableTrackID,
bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
// All streams must belong to the same graph.
@ -84,12 +104,12 @@ OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
}
OutputStreamData* p = mStreams.AppendElement();
p->Init(this, aStream);
p->Init(this, aStream, aNextAvailableTrackID);
// Connect to the input stream if we have one. Otherwise the output stream
// will be connected in Connect().
if (mInputStream) {
p->Connect(mInputStream);
p->Connect(mInputStream, mInputAudioTrackID, mInputVideoTrackID);
}
}
@ -106,12 +126,35 @@ OutputStreamManager::Remove(MediaStream* aStream)
}
void
OutputStreamManager::Connect(MediaStream* aStream)
OutputStreamManager::Clear()
{
MOZ_ASSERT(NS_IsMainThread());
mStreams.Clear();
}
TrackID
OutputStreamManager::NextAvailableTrackIDFor(MediaStream* aOutputStream) const
{
MOZ_ASSERT(NS_IsMainThread());
for (const OutputStreamData& out : mStreams) {
if (out.Equals(aOutputStream)) {
return out.NextAvailableTrackID();
}
}
return TRACK_INVALID;
}
void
OutputStreamManager::Connect(MediaStream* aStream,
TrackID aAudioTrackID,
TrackID aVideoTrackID)
{
MOZ_ASSERT(NS_IsMainThread());
mInputStream = aStream;
mInputAudioTrackID = aAudioTrackID;
mInputVideoTrackID = aVideoTrackID;
for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
if (!mStreams[i].Connect(aStream)) {
if (!mStreams[i].Connect(aStream, mInputAudioTrackID, mInputVideoTrackID)) {
// Probably the DOMMediaStream was GCed. Clean up.
mStreams.RemoveElementAt(i);
}
@ -123,6 +166,8 @@ OutputStreamManager::Disconnect()
{
MOZ_ASSERT(NS_IsMainThread());
mInputStream = nullptr;
mInputAudioTrackID = TRACK_INVALID;
mInputVideoTrackID = TRACK_INVALID;
for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
if (!mStreams[i].Disconnect()) {
// Probably the DOMMediaStream was GCed. Clean up.

View file

@ -9,6 +9,7 @@
#include "mozilla/RefPtr.h"
#include "nsTArray.h"
#include "MediaSegment.h"
namespace mozilla {
@ -21,11 +22,13 @@ class ProcessedMediaStream;
class OutputStreamData {
public:
~OutputStreamData();
void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
void Init(OutputStreamManager* aOwner,
ProcessedMediaStream* aStream,
TrackID aNextAvailableTrackID);
// Connect mStream to the input stream.
// Connect the given input stream's audio and video tracks to mStream.
// Return false is mStream is already destroyed, otherwise true.
bool Connect(MediaStream* aStream);
bool Connect(MediaStream* aStream, TrackID aAudioTrackID, TrackID aVideoTrackID);
// Disconnect mStream from its input stream.
// Return false is mStream is already destroyed, otherwise true.
bool Disconnect();
@ -34,12 +37,16 @@ public:
bool Equals(MediaStream* aStream) const;
// Return the graph mStream belongs to.
MediaStreamGraph* Graph() const;
// The next TrackID that will not cause a collision in mStream.
TrackID NextAvailableTrackID() const;
private:
OutputStreamManager* mOwner;
RefPtr<ProcessedMediaStream> mStream;
// mPort connects our mStream to an input stream.
RefPtr<MediaInputPort> mPort;
// mPort connects an input stream to our mStream.
nsTArray<RefPtr<MediaInputPort>> mPorts;
// For guaranteeing TrackID uniqueness in our mStream.
TrackID mNextAvailableTrackID = TRACK_INVALID;
};
class OutputStreamManager {
@ -47,18 +54,26 @@ class OutputStreamManager {
public:
// Add the output stream to the collection.
void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
void Add(ProcessedMediaStream* aStream,
TrackID aNextAvailableTrackID,
bool aFinishWhenEnded);
// Remove the output stream from the collection.
void Remove(MediaStream* aStream);
// Clear all output streams from the collection.
void Clear();
// The next TrackID that will not cause a collision in aOutputStream.
TrackID NextAvailableTrackIDFor(MediaStream* aOutputStream) const;
// Return true if the collection empty.
bool IsEmpty() const
{
MOZ_ASSERT(NS_IsMainThread());
return mStreams.IsEmpty();
}
// Connect all output streams in the collection to the input stream.
void Connect(MediaStream* aStream);
// Disconnect all output streams from the input stream.
// Connect the given input stream's tracks to all output streams.
void Connect(MediaStream* aStream,
TrackID aAudioTrackID,
TrackID aVideoTrackID);
// Disconnect the input stream to all output streams.
void Disconnect();
// Return the graph these streams belong to or null if empty.
MediaStreamGraph* Graph() const
@ -72,6 +87,8 @@ private:
// Keep the input stream so we can connect the output streams that
// are added after Connect().
RefPtr<MediaStream> mInputStream;
TrackID mInputAudioTrackID = TRACK_INVALID;
TrackID mInputVideoTrackID = TRACK_INVALID;
nsTArray<OutputStreamData> mStreams;
};