1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "MediaStreamGraph.h"
8 #include "OutputStreamManager.h"
9 
10 namespace mozilla {
11 
~OutputStreamData()12 OutputStreamData::~OutputStreamData() {
13   MOZ_ASSERT(NS_IsMainThread());
14   // Break the connection to the input stream if necessary.
15   for (RefPtr<MediaInputPort>& port : mPorts) {
16     port->Destroy();
17   }
18 }
19 
Init(OutputStreamManager * aOwner,ProcessedMediaStream * aStream,TrackID aNextAvailableTrackID)20 void OutputStreamData::Init(OutputStreamManager* aOwner,
21                             ProcessedMediaStream* aStream,
22                             TrackID aNextAvailableTrackID) {
23   mOwner = aOwner;
24   mStream = aStream;
25   mNextAvailableTrackID = aNextAvailableTrackID;
26 }
27 
Connect(MediaStream * aStream,TrackID aInputAudioTrackID,TrackID aInputVideoTrackID)28 bool OutputStreamData::Connect(MediaStream* aStream, TrackID aInputAudioTrackID,
29                                TrackID aInputVideoTrackID) {
30   MOZ_ASSERT(NS_IsMainThread());
31   MOZ_ASSERT(mPorts.IsEmpty(), "Already connected?");
32 
33   if (mStream->IsDestroyed()) {
34     return false;
35   }
36 
37   for (TrackID tid : {aInputAudioTrackID, aInputVideoTrackID}) {
38     if (tid == TRACK_NONE) {
39       continue;
40     }
41     MOZ_ASSERT(IsTrackIDExplicit(tid));
42     mPorts.AppendElement(
43         mStream->AllocateInputPort(aStream, tid, mNextAvailableTrackID++));
44   }
45   return true;
46 }
47 
Disconnect()48 bool OutputStreamData::Disconnect() {
49   MOZ_ASSERT(NS_IsMainThread());
50 
51   // During cycle collection, DOMMediaStream can be destroyed and send
52   // its Destroy message before this decoder is destroyed. So we have to
53   // be careful not to send any messages after the Destroy().
54   if (mStream->IsDestroyed()) {
55     return false;
56   }
57 
58   // Disconnect any existing port.
59   for (RefPtr<MediaInputPort>& port : mPorts) {
60     port->Destroy();
61   }
62   mPorts.Clear();
63   return true;
64 }
65 
Equals(MediaStream * aStream) const66 bool OutputStreamData::Equals(MediaStream* aStream) const {
67   return mStream == aStream;
68 }
69 
Graph() const70 MediaStreamGraph* OutputStreamData::Graph() const { return mStream->Graph(); }
71 
NextAvailableTrackID() const72 TrackID OutputStreamData::NextAvailableTrackID() const {
73   return mNextAvailableTrackID;
74 }
75 
Add(ProcessedMediaStream * aStream,TrackID aNextAvailableTrackID,bool aFinishWhenEnded)76 void OutputStreamManager::Add(ProcessedMediaStream* aStream,
77                               TrackID aNextAvailableTrackID,
78                               bool aFinishWhenEnded) {
79   MOZ_ASSERT(NS_IsMainThread());
80   // All streams must belong to the same graph.
81   MOZ_ASSERT(!Graph() || Graph() == aStream->Graph());
82 
83   // Ensure that aStream finishes the moment mDecodedStream does.
84   if (aFinishWhenEnded) {
85     aStream->QueueSetAutofinish(true);
86   }
87 
88   OutputStreamData* p = mStreams.AppendElement();
89   p->Init(this, aStream, aNextAvailableTrackID);
90 
91   // Connect to the input stream if we have one. Otherwise the output stream
92   // will be connected in Connect().
93   if (mInputStream) {
94     p->Connect(mInputStream, mInputAudioTrackID, mInputVideoTrackID);
95   }
96 }
97 
Remove(MediaStream * aStream)98 void OutputStreamManager::Remove(MediaStream* aStream) {
99   MOZ_ASSERT(NS_IsMainThread());
100   for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
101     if (mStreams[i].Equals(aStream)) {
102       mStreams.RemoveElementAt(i);
103       break;
104     }
105   }
106 }
107 
Clear()108 void OutputStreamManager::Clear() {
109   MOZ_ASSERT(NS_IsMainThread());
110   mStreams.Clear();
111 }
112 
NextAvailableTrackIDFor(MediaStream * aOutputStream) const113 TrackID OutputStreamManager::NextAvailableTrackIDFor(
114     MediaStream* aOutputStream) const {
115   MOZ_ASSERT(NS_IsMainThread());
116   for (const OutputStreamData& out : mStreams) {
117     if (out.Equals(aOutputStream)) {
118       return out.NextAvailableTrackID();
119     }
120   }
121   return TRACK_INVALID;
122 }
123 
Connect(MediaStream * aStream,TrackID aAudioTrackID,TrackID aVideoTrackID)124 void OutputStreamManager::Connect(MediaStream* aStream, TrackID aAudioTrackID,
125                                   TrackID aVideoTrackID) {
126   MOZ_ASSERT(NS_IsMainThread());
127   mInputStream = aStream;
128   mInputAudioTrackID = aAudioTrackID;
129   mInputVideoTrackID = aVideoTrackID;
130   for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
131     if (!mStreams[i].Connect(aStream, mInputAudioTrackID, mInputVideoTrackID)) {
132       // Probably the DOMMediaStream was GCed. Clean up.
133       mStreams.RemoveElementAt(i);
134     }
135   }
136 }
137 
Disconnect()138 void OutputStreamManager::Disconnect() {
139   MOZ_ASSERT(NS_IsMainThread());
140   mInputStream = nullptr;
141   mInputAudioTrackID = TRACK_INVALID;
142   mInputVideoTrackID = TRACK_INVALID;
143   for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
144     if (!mStreams[i].Disconnect()) {
145       // Probably the DOMMediaStream was GCed. Clean up.
146       mStreams.RemoveElementAt(i);
147     }
148   }
149 }
150 
151 }  // namespace mozilla
152