TrackUnionStream.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  4. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "MediaStreamGraphImpl.h"
  6. #include "MediaStreamListener.h"
  7. #include "mozilla/MathAlgorithms.h"
  8. #include "mozilla/Unused.h"
  9. #include "AudioSegment.h"
  10. #include "VideoSegment.h"
  11. #include "nsContentUtils.h"
  12. #include "nsIAppShell.h"
  13. #include "nsIObserver.h"
  14. #include "nsPrintfCString.h"
  15. #include "nsServiceManagerUtils.h"
  16. #include "nsWidgetsCID.h"
  17. #include "prerror.h"
  18. #include "mozilla/Logging.h"
  19. #include "mozilla/Attributes.h"
  20. #include "TrackUnionStream.h"
  21. #include "ImageContainer.h"
  22. #include "AudioChannelService.h"
  23. #include "AudioNodeEngine.h"
  24. #include "AudioNodeStream.h"
  25. #include "AudioNodeExternalInputStream.h"
  26. #include "webaudio/MediaStreamAudioDestinationNode.h"
  27. #include <algorithm>
  28. #include "DOMMediaStream.h"
  29. #include "GeckoProfiler.h"
  30. #ifdef MOZ_WEBRTC
  31. #include "AudioOutputObserver.h"
  32. #endif
  33. using namespace mozilla::layers;
  34. using namespace mozilla::dom;
  35. using namespace mozilla::gfx;
  36. namespace mozilla {
  37. #ifdef STREAM_LOG
  38. #undef STREAM_LOG
  39. #endif
  40. LazyLogModule gTrackUnionStreamLog("TrackUnionStream");
  41. #define STREAM_LOG(type, msg) MOZ_LOG(gTrackUnionStreamLog, type, msg)
  42. TrackUnionStream::TrackUnionStream() :
  43. ProcessedMediaStream(), mNextAvailableTrackID(1)
  44. {
  45. }
  46. void TrackUnionStream::RemoveInput(MediaInputPort* aPort)
  47. {
  48. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing input %p", this, aPort));
  49. for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
  50. if (mTrackMap[i].mInputPort == aPort) {
  51. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing trackmap entry %d", this, i));
  52. EndTrack(i);
  53. nsTArray<RefPtr<DirectMediaStreamTrackListener>> listeners(
  54. mTrackMap[i].mOwnedDirectListeners);
  55. for (auto listener : listeners) {
  56. // Remove listeners while the entry still exists.
  57. RemoveDirectTrackListenerImpl(listener, mTrackMap[i].mOutputTrackID);
  58. }
  59. mTrackMap.RemoveElementAt(i);
  60. }
  61. }
  62. ProcessedMediaStream::RemoveInput(aPort);
  63. }
  64. void TrackUnionStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
  65. {
  66. if (IsFinishedOnGraphThread()) {
  67. return;
  68. }
  69. AutoTArray<bool,8> mappedTracksFinished;
  70. AutoTArray<bool,8> mappedTracksWithMatchingInputTracks;
  71. for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
  72. mappedTracksFinished.AppendElement(true);
  73. mappedTracksWithMatchingInputTracks.AppendElement(false);
  74. }
  75. bool allFinished = !mInputs.IsEmpty();
  76. bool allHaveCurrentData = !mInputs.IsEmpty();
  77. for (uint32_t i = 0; i < mInputs.Length(); ++i) {
  78. MediaStream* stream = mInputs[i]->GetSource();
  79. if (!stream->IsFinishedOnGraphThread()) {
  80. // XXX we really should check whether 'stream' has finished within time aTo,
  81. // not just that it's finishing when all its queued data eventually runs
  82. // out.
  83. allFinished = false;
  84. }
  85. if (!stream->HasCurrentData()) {
  86. allHaveCurrentData = false;
  87. }
  88. bool trackAdded = false;
  89. for (StreamTracks::TrackIter tracks(stream->GetStreamTracks());
  90. !tracks.IsEnded(); tracks.Next()) {
  91. bool found = false;
  92. for (uint32_t j = 0; j < mTrackMap.Length(); ++j) {
  93. TrackMapEntry* map = &mTrackMap[j];
  94. if (map->mInputPort == mInputs[i] && map->mInputTrackID == tracks->GetID()) {
  95. bool trackFinished = false;
  96. StreamTracks::Track* outputTrack = mTracks.FindTrack(map->mOutputTrackID);
  97. found = true;
  98. if (!outputTrack || outputTrack->IsEnded() ||
  99. !mInputs[i]->PassTrackThrough(tracks->GetID())) {
  100. trackFinished = true;
  101. } else {
  102. CopyTrackData(tracks.get(), j, aFrom, aTo, &trackFinished);
  103. }
  104. mappedTracksFinished[j] = trackFinished;
  105. mappedTracksWithMatchingInputTracks[j] = true;
  106. break;
  107. }
  108. }
  109. if (!found && mInputs[i]->AllowCreationOf(tracks->GetID())) {
  110. bool trackFinished = false;
  111. trackAdded = true;
  112. uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom);
  113. CopyTrackData(tracks.get(), mapIndex, aFrom, aTo, &trackFinished);
  114. mappedTracksFinished.AppendElement(trackFinished);
  115. mappedTracksWithMatchingInputTracks.AppendElement(true);
  116. }
  117. }
  118. if (trackAdded) {
  119. for (MediaStreamListener* l : mListeners) {
  120. l->NotifyFinishedTrackCreation(Graph());
  121. }
  122. }
  123. }
  124. for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
  125. if (mappedTracksFinished[i]) {
  126. EndTrack(i);
  127. } else {
  128. allFinished = false;
  129. }
  130. if (!mappedTracksWithMatchingInputTracks[i]) {
  131. for (auto listener : mTrackMap[i].mOwnedDirectListeners) {
  132. // Remove listeners while the entry still exists.
  133. RemoveDirectTrackListenerImpl(listener, mTrackMap[i].mOutputTrackID);
  134. }
  135. mTrackMap.RemoveElementAt(i);
  136. }
  137. }
  138. if (allFinished && mAutofinish && (aFlags & ALLOW_FINISH)) {
  139. // All streams have finished and won't add any more tracks, and
  140. // all our tracks have actually finished and been removed from our map,
  141. // so we're finished now.
  142. FinishOnGraphThread();
  143. } else {
  144. mTracks.AdvanceKnownTracksTime(GraphTimeToStreamTimeWithBlocking(aTo));
  145. }
  146. if (allHaveCurrentData) {
  147. // We can make progress if we're not blocked
  148. mHasCurrentData = true;
  149. }
  150. }
  151. uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamTracks::Track* aTrack,
  152. GraphTime aFrom)
  153. {
  154. STREAM_LOG(LogLevel::Verbose, ("TrackUnionStream %p adding track %d for "
  155. "input stream %p track %d, desired id %d",
  156. this, aTrack->GetID(), aPort->GetSource(),
  157. aTrack->GetID(),
  158. aPort->GetDestinationTrackId()));
  159. TrackID id;
  160. if (IsTrackIDExplicit(id = aPort->GetDestinationTrackId())) {
  161. MOZ_ASSERT(id >= mNextAvailableTrackID &&
  162. mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex,
  163. "Desired destination id taken. Only provide a destination ID "
  164. "if you can assure its availability, or we may not be able "
  165. "to bind to the correct DOM-side track.");
  166. #ifdef DEBUG
  167. for (size_t i = 0; mInputs[i] != aPort; ++i) {
  168. MOZ_ASSERT(mInputs[i]->GetSourceTrackId() != TRACK_ANY,
  169. "You are adding a MediaInputPort with a track mapping "
  170. "while there already exist generic MediaInputPorts for this "
  171. "destination stream. This can lead to TrackID collisions!");
  172. }
  173. #endif
  174. mUsedTracks.InsertElementSorted(id);
  175. } else if ((id = aTrack->GetID()) &&
  176. id > mNextAvailableTrackID &&
  177. mUsedTracks.BinaryIndexOf(id) == mUsedTracks.NoIndex) {
  178. // Input id available. Mark it used in mUsedTracks.
  179. mUsedTracks.InsertElementSorted(id);
  180. } else {
  181. // No desired destination id and Input id taken, allocate a new one.
  182. id = mNextAvailableTrackID;
  183. // Update mNextAvailableTrackID and prune any mUsedTracks members it now
  184. // covers.
  185. while (1) {
  186. if (!mUsedTracks.RemoveElementSorted(++mNextAvailableTrackID)) {
  187. // Not in use. We're done.
  188. break;
  189. }
  190. }
  191. }
  192. // Round up the track start time so the track, if anything, starts a
  193. // little later than the true time. This means we'll have enough
  194. // samples in our input stream to go just beyond the destination time.
  195. StreamTime outputStart = GraphTimeToStreamTimeWithBlocking(aFrom);
  196. nsAutoPtr<MediaSegment> segment;
  197. segment = aTrack->GetSegment()->CreateEmptyClone();
  198. for (uint32_t j = 0; j < mListeners.Length(); ++j) {
  199. MediaStreamListener* l = mListeners[j];
  200. l->NotifyQueuedTrackChanges(Graph(), id, outputStart,
  201. TrackEventCommand::TRACK_EVENT_CREATED,
  202. *segment,
  203. aPort->GetSource(), aTrack->GetID());
  204. }
  205. segment->AppendNullData(outputStart);
  206. StreamTracks::Track* track =
  207. &mTracks.AddTrack(id, outputStart, segment.forget());
  208. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p added track %d for input stream %p track %d, start ticks %lld",
  209. this, track->GetID(), aPort->GetSource(), aTrack->GetID(),
  210. (long long)outputStart));
  211. TrackMapEntry* map = mTrackMap.AppendElement();
  212. map->mEndOfConsumedInputTicks = 0;
  213. map->mEndOfLastInputIntervalInInputStream = -1;
  214. map->mEndOfLastInputIntervalInOutputStream = -1;
  215. map->mInputPort = aPort;
  216. map->mInputTrackID = aTrack->GetID();
  217. map->mOutputTrackID = track->GetID();
  218. map->mSegment = aTrack->GetSegment()->CreateEmptyClone();
  219. for (int32_t i = mPendingDirectTrackListeners.Length() - 1; i >= 0; --i) {
  220. TrackBound<DirectMediaStreamTrackListener>& bound =
  221. mPendingDirectTrackListeners[i];
  222. if (bound.mTrackID != map->mOutputTrackID) {
  223. continue;
  224. }
  225. MediaStream* source = map->mInputPort->GetSource();
  226. map->mOwnedDirectListeners.AppendElement(bound.mListener);
  227. DisabledTrackMode currentMode = GetDisabledTrackMode(bound.mTrackID);
  228. if (currentMode != DisabledTrackMode::ENABLED) {
  229. bound.mListener->IncreaseDisabled(currentMode);
  230. }
  231. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener "
  232. "%p for track %d. Forwarding to input "
  233. "stream %p track %d.",
  234. this, bound.mListener.get(), bound.mTrackID,
  235. source, map->mInputTrackID));
  236. source->AddDirectTrackListenerImpl(bound.mListener.forget(),
  237. map->mInputTrackID);
  238. mPendingDirectTrackListeners.RemoveElementAt(i);
  239. }
  240. return mTrackMap.Length() - 1;
  241. }
  242. void TrackUnionStream::EndTrack(uint32_t aIndex)
  243. {
  244. StreamTracks::Track* outputTrack = mTracks.FindTrack(mTrackMap[aIndex].mOutputTrackID);
  245. if (!outputTrack || outputTrack->IsEnded())
  246. return;
  247. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p ending track %d", this, outputTrack->GetID()));
  248. for (uint32_t j = 0; j < mListeners.Length(); ++j) {
  249. MediaStreamListener* l = mListeners[j];
  250. StreamTime offset = outputTrack->GetSegment()->GetDuration();
  251. nsAutoPtr<MediaSegment> segment;
  252. segment = outputTrack->GetSegment()->CreateEmptyClone();
  253. l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), offset,
  254. TrackEventCommand::TRACK_EVENT_ENDED,
  255. *segment,
  256. mTrackMap[aIndex].mInputPort->GetSource(),
  257. mTrackMap[aIndex].mInputTrackID);
  258. }
  259. for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
  260. if (b.mTrackID == outputTrack->GetID()) {
  261. b.mListener->NotifyEnded();
  262. }
  263. }
  264. outputTrack->SetEnded();
  265. }
  266. void TrackUnionStream::CopyTrackData(StreamTracks::Track* aInputTrack,
  267. uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo,
  268. bool* aOutputTrackFinished)
  269. {
  270. TrackMapEntry* map = &mTrackMap[aMapIndex];
  271. StreamTracks::Track* outputTrack = mTracks.FindTrack(map->mOutputTrackID);
  272. MOZ_ASSERT(outputTrack && !outputTrack->IsEnded(), "Can't copy to ended track");
  273. MediaSegment* segment = map->mSegment;
  274. MediaStream* source = map->mInputPort->GetSource();
  275. GraphTime next;
  276. *aOutputTrackFinished = false;
  277. for (GraphTime t = aFrom; t < aTo; t = next) {
  278. MediaInputPort::InputInterval interval = map->mInputPort->GetNextInputInterval(t);
  279. interval.mEnd = std::min(interval.mEnd, aTo);
  280. StreamTime inputEnd = source->GraphTimeToStreamTimeWithBlocking(interval.mEnd);
  281. StreamTime inputTrackEndPoint = STREAM_TIME_MAX;
  282. if (aInputTrack->IsEnded() &&
  283. aInputTrack->GetEnd() <= inputEnd) {
  284. inputTrackEndPoint = aInputTrack->GetEnd();
  285. *aOutputTrackFinished = true;
  286. }
  287. if (interval.mStart >= interval.mEnd) {
  288. break;
  289. }
  290. StreamTime ticks = interval.mEnd - interval.mStart;
  291. next = interval.mEnd;
  292. StreamTime outputStart = outputTrack->GetEnd();
  293. if (interval.mInputIsBlocked) {
  294. // Maybe the input track ended?
  295. segment->AppendNullData(ticks);
  296. STREAM_LOG(LogLevel::Verbose, ("TrackUnionStream %p appending %lld ticks of null data to track %d",
  297. this, (long long)ticks, outputTrack->GetID()));
  298. } else if (InMutedCycle()) {
  299. segment->AppendNullData(ticks);
  300. } else {
  301. if (source->IsSuspended()) {
  302. segment->AppendNullData(aTo - aFrom);
  303. } else {
  304. MOZ_ASSERT(outputTrack->GetEnd() == GraphTimeToStreamTimeWithBlocking(interval.mStart),
  305. "Samples missing");
  306. StreamTime inputStart = source->GraphTimeToStreamTimeWithBlocking(interval.mStart);
  307. segment->AppendSlice(*aInputTrack->GetSegment(),
  308. std::min(inputTrackEndPoint, inputStart),
  309. std::min(inputTrackEndPoint, inputEnd));
  310. }
  311. }
  312. ApplyTrackDisabling(outputTrack->GetID(), segment);
  313. for (uint32_t j = 0; j < mListeners.Length(); ++j) {
  314. MediaStreamListener* l = mListeners[j];
  315. // Separate Audio and Video.
  316. if (segment->GetType() == MediaSegment::AUDIO) {
  317. l->NotifyQueuedAudioData(Graph(), outputTrack->GetID(),
  318. outputStart,
  319. *static_cast<AudioSegment*>(segment),
  320. map->mInputPort->GetSource(),
  321. map->mInputTrackID);
  322. }
  323. }
  324. for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
  325. if (b.mTrackID != outputTrack->GetID()) {
  326. continue;
  327. }
  328. b.mListener->NotifyQueuedChanges(Graph(), outputStart, *segment);
  329. }
  330. outputTrack->GetSegment()->AppendFrom(segment);
  331. }
  332. }
  333. void
  334. TrackUnionStream::SetTrackEnabledImpl(TrackID aTrackID, DisabledTrackMode aMode) {
  335. bool enabled = aMode == DisabledTrackMode::ENABLED;
  336. for (TrackMapEntry& entry : mTrackMap) {
  337. if (entry.mOutputTrackID == aTrackID) {
  338. STREAM_LOG(LogLevel::Info, ("TrackUnionStream %p track %d was explicitly %s",
  339. this, aTrackID, enabled ? "enabled" : "disabled"));
  340. for (DirectMediaStreamTrackListener* listener : entry.mOwnedDirectListeners) {
  341. DisabledTrackMode oldMode = GetDisabledTrackMode(aTrackID);
  342. bool oldEnabled = oldMode == DisabledTrackMode::ENABLED;
  343. if (!oldEnabled && enabled) {
  344. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting "
  345. "direct listener enabled",
  346. this, aTrackID));
  347. listener->DecreaseDisabled(oldMode);
  348. } else if (oldEnabled && !enabled) {
  349. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p track %d setting "
  350. "direct listener disabled",
  351. this, aTrackID));
  352. listener->IncreaseDisabled(aMode);
  353. }
  354. }
  355. }
  356. }
  357. MediaStream::SetTrackEnabledImpl(aTrackID, aMode);
  358. }
  359. MediaStream*
  360. TrackUnionStream::GetInputStreamFor(TrackID aTrackID)
  361. {
  362. for (TrackMapEntry& entry : mTrackMap) {
  363. if (entry.mOutputTrackID == aTrackID && entry.mInputPort) {
  364. return entry.mInputPort->GetSource();
  365. }
  366. }
  367. return nullptr;
  368. }
  369. TrackID
  370. TrackUnionStream::GetInputTrackIDFor(TrackID aTrackID)
  371. {
  372. for (TrackMapEntry& entry : mTrackMap) {
  373. if (entry.mOutputTrackID == aTrackID) {
  374. return entry.mInputTrackID;
  375. }
  376. }
  377. return TRACK_NONE;
  378. }
  379. void
  380. TrackUnionStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
  381. TrackID aTrackID)
  382. {
  383. RefPtr<DirectMediaStreamTrackListener> listener = aListener;
  384. for (TrackMapEntry& entry : mTrackMap) {
  385. if (entry.mOutputTrackID == aTrackID) {
  386. MediaStream* source = entry.mInputPort->GetSource();
  387. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p adding direct listener "
  388. "%p for track %d. Forwarding to input "
  389. "stream %p track %d.",
  390. this, listener.get(), aTrackID, source,
  391. entry.mInputTrackID));
  392. entry.mOwnedDirectListeners.AppendElement(listener);
  393. DisabledTrackMode currentMode = GetDisabledTrackMode(aTrackID);
  394. if (currentMode != DisabledTrackMode::ENABLED) {
  395. listener->IncreaseDisabled(currentMode);
  396. }
  397. source->AddDirectTrackListenerImpl(listener.forget(),
  398. entry.mInputTrackID);
  399. return;
  400. }
  401. }
  402. TrackBound<DirectMediaStreamTrackListener>* bound =
  403. mPendingDirectTrackListeners.AppendElement();
  404. bound->mListener = listener.forget();
  405. bound->mTrackID = aTrackID;
  406. }
  407. void
  408. TrackUnionStream::RemoveDirectTrackListenerImpl(DirectMediaStreamTrackListener* aListener,
  409. TrackID aTrackID)
  410. {
  411. for (TrackMapEntry& entry : mTrackMap) {
  412. // OutputTrackID is unique to this stream so we only need to do this once.
  413. if (entry.mOutputTrackID != aTrackID) {
  414. continue;
  415. }
  416. for (size_t i = 0; i < entry.mOwnedDirectListeners.Length(); ++i) {
  417. if (entry.mOwnedDirectListeners[i] == aListener) {
  418. STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p removing direct "
  419. "listener %p for track %d, forwarding "
  420. "to input stream %p track %d",
  421. this, aListener, aTrackID,
  422. entry.mInputPort->GetSource(),
  423. entry.mInputTrackID));
  424. DisabledTrackMode currentMode = GetDisabledTrackMode(aTrackID);
  425. if (currentMode != DisabledTrackMode::ENABLED) {
  426. // Reset the listener's state.
  427. aListener->DecreaseDisabled(currentMode);
  428. }
  429. entry.mOwnedDirectListeners.RemoveElementAt(i);
  430. break;
  431. }
  432. }
  433. // Forward to the input
  434. MediaStream* source = entry.mInputPort->GetSource();
  435. source->RemoveDirectTrackListenerImpl(aListener, entry.mInputTrackID);
  436. return;
  437. }
  438. for (size_t i = 0; i < mPendingDirectTrackListeners.Length(); ++i) {
  439. TrackBound<DirectMediaStreamTrackListener>& bound =
  440. mPendingDirectTrackListeners[i];
  441. if (bound.mListener == aListener && bound.mTrackID == aTrackID) {
  442. mPendingDirectTrackListeners.RemoveElementAt(i);
  443. return;
  444. }
  445. }
  446. }
  447. } // namespace mozilla