author | Randell Jesup <rjesup@jesup.org> |
Sat, 11 Oct 2014 09:02:59 -0400 | |
changeset 233273 | 8870dc5bfa717b58bb31eff69955be035bc7d96f |
parent 233272 | 0e9a6298a2022e0d4c3a2d14ce6a4924bb0f735f |
child 233274 | cad1cbc2598f5d3b94433c79bbffd7ef106e520c |
push id | 4187 |
push user | bhearsum@mozilla.com |
push date | Fri, 28 Nov 2014 15:29:12 +0000 |
treeherder | mozilla-beta@f23cc6a30c11 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | roc |
bugs | 1070127 |
milestone | 35.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
copy from content/media/TrackUnionStream.h copy to content/media/TrackUnionStream.cpp --- a/content/media/TrackUnionStream.h +++ b/content/media/TrackUnionStream.cpp @@ -1,50 +1,77 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ /* 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 MOZILLA_TRACKUNIONSTREAM_H_ -#define MOZILLA_TRACKUNIONSTREAM_H_ +#include "MediaStreamGraphImpl.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/unused.h" -#include "MediaStreamGraph.h" +#include "AudioSegment.h" +#include "VideoSegment.h" +#include "nsContentUtils.h" +#include "nsIAppShell.h" +#include "nsIObserver.h" +#include "nsPrintfCString.h" +#include "nsServiceManagerUtils.h" +#include "nsWidgetsCID.h" +#include "prerror.h" +#include "prlog.h" +#include "mozilla/Attributes.h" +#include "TrackUnionStream.h" +#include "ImageContainer.h" +#include "AudioChannelService.h" +#include "AudioNodeEngine.h" +#include "AudioNodeStream.h" +#include "AudioNodeExternalInputStream.h" #include <algorithm> +#include "DOMMediaStream.h" +#include "GeckoProfiler.h" +#include "mozilla/unused.h" +#ifdef MOZ_WEBRTC +#include "AudioOutputObserver.h" +#endif + +using namespace mozilla::layers; +using namespace mozilla::dom; +using namespace mozilla::gfx; namespace mozilla { #ifdef PR_LOGGING -#define STREAM_LOG(type, msg) PR_LOG(gMediaStreamGraphLog, type, msg) +PRLogModuleInfo* gTrackUnionStreamLog; +#define STREAM_LOG(type, msg) PR_LOG(gTrackUnionStreamLog, type, msg) #else #define STREAM_LOG(type, msg) #endif -/** - * See MediaStreamGraph::CreateTrackUnionStream. - * This file is only included by MediaStreamGraph.cpp so it's OK to put the - * entire implementation in this header file. - */ -class TrackUnionStream : public ProcessedMediaStream { -public: - explicit TrackUnionStream(DOMMediaStream* aWrapper) : - ProcessedMediaStream(aWrapper), - mFilterCallback(nullptr) - {} +TrackUnionStream::TrackUnionStream(DOMMediaStream* aWrapper) : + ProcessedMediaStream(aWrapper), + mFilterCallback(nullptr) +{ +#ifdef PR_LOGGING + if (!gTrackUnionStreamLog) { + gTrackUnionStreamLog = PR_NewLogModule("TrackUnionStream"); + } +#endif +} - virtual void RemoveInput(MediaInputPort* aPort) MOZ_OVERRIDE + void TrackUnionStream::RemoveInput(MediaInputPort* aPort) { for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) { if (mTrackMap[i].mInputPort == aPort) { EndTrack(i); mTrackMap.RemoveElementAt(i); } } ProcessedMediaStream::RemoveInput(aPort); } - virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE + void TrackUnionStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) { if (IsFinishedOnGraphThread()) { return; } nsAutoTArray<bool,8> mappedTracksFinished; nsAutoTArray<bool,8> mappedTracksWithMatchingInputTracks; for (uint32_t i = 0; i < mTrackMap.Length(); ++i) { mappedTracksFinished.AppendElement(true); @@ -112,60 +139,35 @@ public: if (allHaveCurrentData) { // We can make progress if we're not blocked mHasCurrentData = true; } } // Consumers may specify a filtering callback to apply to every input track. // Returns true to allow the track to act as an input; false to reject it entirely. - typedef bool (*TrackIDFilterCallback)(StreamBuffer::Track*); - void SetTrackIDFilter(TrackIDFilterCallback aCallback) { + + void TrackUnionStream::SetTrackIDFilter(TrackIDFilterCallback aCallback) + { mFilterCallback = aCallback; } // Forward SetTrackEnabled(output_track_id, enabled) to the Source MediaStream, // translating the output track ID into the correct ID in the source. - virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) MOZ_OVERRIDE { + void TrackUnionStream::ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) + { for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) { if (mTrackMap[i].mOutputTrackID == aOutputID) { mTrackMap[i].mInputPort->GetSource()-> SetTrackEnabled(mTrackMap[i].mInputTrackID, aEnabled); } } } -protected: - TrackIDFilterCallback mFilterCallback; - - // Only non-ended tracks are allowed to persist in this map. - struct TrackMapEntry { - // mEndOfConsumedInputTicks is the end of the input ticks that we've consumed. - // 0 if we haven't consumed any yet. - TrackTicks mEndOfConsumedInputTicks; - // mEndOfLastInputIntervalInInputStream is the timestamp for the end of the - // previous interval which was unblocked for both the input and output - // stream, in the input stream's timeline, or -1 if there wasn't one. - StreamTime mEndOfLastInputIntervalInInputStream; - // mEndOfLastInputIntervalInOutputStream is the timestamp for the end of the - // previous interval which was unblocked for both the input and output - // stream, in the output stream's timeline, or -1 if there wasn't one. - StreamTime mEndOfLastInputIntervalInOutputStream; - MediaInputPort* mInputPort; - // We keep track IDs instead of track pointers because - // tracks can be removed without us being notified (e.g. - // when a finished track is forgotten.) When we need a Track*, - // we call StreamBuffer::FindTrack, which will return null if - // the track has been deleted. - TrackID mInputTrackID; - TrackID mOutputTrackID; - nsAutoPtr<MediaSegment> mSegment; - }; - - uint32_t AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack, + uint32_t TrackUnionStream::AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack, GraphTime aFrom) { // Use the ID of the source track if it's not already assigned to a track, // otherwise allocate a new unique ID. TrackID id = aTrack->GetID(); TrackID maxTrackID = 0; for (uint32_t i = 0; i < mTrackMap.Length(); ++i) { TrackID outID = mTrackMap[i].mOutputTrackID; @@ -208,34 +210,36 @@ protected: map->mEndOfLastInputIntervalInInputStream = -1; map->mEndOfLastInputIntervalInOutputStream = -1; map->mInputPort = aPort; map->mInputTrackID = aTrack->GetID(); map->mOutputTrackID = track->GetID(); map->mSegment = aTrack->GetSegment()->CreateEmptyClone(); return mTrackMap.Length() - 1; } - void EndTrack(uint32_t aIndex) + + void TrackUnionStream::EndTrack(uint32_t aIndex) { StreamBuffer::Track* outputTrack = mBuffer.FindTrack(mTrackMap[aIndex].mOutputTrackID); if (!outputTrack || outputTrack->IsEnded()) return; for (uint32_t j = 0; j < mListeners.Length(); ++j) { MediaStreamListener* l = mListeners[j]; TrackTicks offset = outputTrack->GetSegment()->GetDuration(); nsAutoPtr<MediaSegment> segment; segment = outputTrack->GetSegment()->CreateEmptyClone(); l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), outputTrack->GetRate(), offset, MediaStreamListener::TRACK_EVENT_ENDED, *segment); } outputTrack->SetEnded(); } - void CopyTrackData(StreamBuffer::Track* aInputTrack, + + void TrackUnionStream::CopyTrackData(StreamBuffer::Track* aInputTrack, uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo, bool* aOutputTrackFinished) { TrackMapEntry* map = &mTrackMap[aMapIndex]; StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID); MOZ_ASSERT(outputTrack && !outputTrack->IsEnded(), "Can't copy to ended track"); TrackRate rate = outputTrack->GetRate(); @@ -360,15 +364,9 @@ protected: MediaStreamListener* l = mListeners[j]; l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), outputTrack->GetRate(), startTicks, 0, *segment); } outputTrack->GetSegment()->AppendFrom(segment); } } - - nsTArray<TrackMapEntry> mTrackMap; -}; - } - -#endif /* MOZILLA_MEDIASTREAMGRAPH_H_ */
--- a/content/media/TrackUnionStream.h +++ b/content/media/TrackUnionStream.h @@ -6,137 +6,35 @@ #ifndef MOZILLA_TRACKUNIONSTREAM_H_ #define MOZILLA_TRACKUNIONSTREAM_H_ #include "MediaStreamGraph.h" #include <algorithm> namespace mozilla { -#ifdef PR_LOGGING -#define STREAM_LOG(type, msg) PR_LOG(gMediaStreamGraphLog, type, msg) -#else -#define STREAM_LOG(type, msg) -#endif - /** * See MediaStreamGraph::CreateTrackUnionStream. - * This file is only included by MediaStreamGraph.cpp so it's OK to put the - * entire implementation in this header file. */ class TrackUnionStream : public ProcessedMediaStream { public: - explicit TrackUnionStream(DOMMediaStream* aWrapper) : - ProcessedMediaStream(aWrapper), - mFilterCallback(nullptr) - {} + explicit TrackUnionStream(DOMMediaStream* aWrapper); - virtual void RemoveInput(MediaInputPort* aPort) MOZ_OVERRIDE - { - for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) { - if (mTrackMap[i].mInputPort == aPort) { - EndTrack(i); - mTrackMap.RemoveElementAt(i); - } - } - ProcessedMediaStream::RemoveInput(aPort); - } - virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE - { - if (IsFinishedOnGraphThread()) { - return; - } - nsAutoTArray<bool,8> mappedTracksFinished; - nsAutoTArray<bool,8> mappedTracksWithMatchingInputTracks; - for (uint32_t i = 0; i < mTrackMap.Length(); ++i) { - mappedTracksFinished.AppendElement(true); - mappedTracksWithMatchingInputTracks.AppendElement(false); - } - bool allFinished = true; - bool allHaveCurrentData = true; - for (uint32_t i = 0; i < mInputs.Length(); ++i) { - MediaStream* stream = mInputs[i]->GetSource(); - if (!stream->IsFinishedOnGraphThread()) { - // XXX we really should check whether 'stream' has finished within time aTo, - // not just that it's finishing when all its queued data eventually runs - // out. - allFinished = false; - } - if (!stream->HasCurrentData()) { - allHaveCurrentData = false; - } - for (StreamBuffer::TrackIter tracks(stream->GetStreamBuffer()); - !tracks.IsEnded(); tracks.Next()) { - bool found = false; - for (uint32_t j = 0; j < mTrackMap.Length(); ++j) { - TrackMapEntry* map = &mTrackMap[j]; - if (map->mInputPort == mInputs[i] && map->mInputTrackID == tracks->GetID()) { - bool trackFinished; - StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID); - if (!outputTrack || outputTrack->IsEnded()) { - trackFinished = true; - } else { - CopyTrackData(tracks.get(), j, aFrom, aTo, &trackFinished); - } - mappedTracksFinished[j] = trackFinished; - mappedTracksWithMatchingInputTracks[j] = true; - found = true; - break; - } - } - if (!found && (!mFilterCallback || mFilterCallback(tracks.get()))) { - bool trackFinished = false; - uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom); - CopyTrackData(tracks.get(), mapIndex, aFrom, aTo, &trackFinished); - mappedTracksFinished.AppendElement(trackFinished); - mappedTracksWithMatchingInputTracks.AppendElement(true); - } - } - } - for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) { - if (mappedTracksFinished[i]) { - EndTrack(i); - } else { - allFinished = false; - } - if (!mappedTracksWithMatchingInputTracks[i]) { - mTrackMap.RemoveElementAt(i); - } - } - if (allFinished && mAutofinish && (aFlags & ALLOW_FINISH)) { - // All streams have finished and won't add any more tracks, and - // all our tracks have actually finished and been removed from our map, - // so we're finished now. - FinishOnGraphThread(); - } else { - mBuffer.AdvanceKnownTracksTime(GraphTimeToStreamTime(aTo)); - } - if (allHaveCurrentData) { - // We can make progress if we're not blocked - mHasCurrentData = true; - } - } + virtual void RemoveInput(MediaInputPort* aPort) MOZ_OVERRIDE; + virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE; // Consumers may specify a filtering callback to apply to every input track. // Returns true to allow the track to act as an input; false to reject it entirely. typedef bool (*TrackIDFilterCallback)(StreamBuffer::Track*); - void SetTrackIDFilter(TrackIDFilterCallback aCallback) { - mFilterCallback = aCallback; - } + + void SetTrackIDFilter(TrackIDFilterCallback aCallback); // Forward SetTrackEnabled(output_track_id, enabled) to the Source MediaStream, // translating the output track ID into the correct ID in the source. - virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) MOZ_OVERRIDE { - for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) { - if (mTrackMap[i].mOutputTrackID == aOutputID) { - mTrackMap[i].mInputPort->GetSource()-> - SetTrackEnabled(mTrackMap[i].mInputTrackID, aEnabled); - } - } - } + virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) MOZ_OVERRIDE; protected: TrackIDFilterCallback mFilterCallback; // Only non-ended tracks are allowed to persist in this map. struct TrackMapEntry { // mEndOfConsumedInputTicks is the end of the input ticks that we've consumed. // 0 if we haven't consumed any yet. @@ -156,219 +54,20 @@ protected: // we call StreamBuffer::FindTrack, which will return null if // the track has been deleted. TrackID mInputTrackID; TrackID mOutputTrackID; nsAutoPtr<MediaSegment> mSegment; }; uint32_t AddTrack(MediaInputPort* aPort, StreamBuffer::Track* aTrack, - GraphTime aFrom) - { - // Use the ID of the source track if it's not already assigned to a track, - // otherwise allocate a new unique ID. - TrackID id = aTrack->GetID(); - TrackID maxTrackID = 0; - for (uint32_t i = 0; i < mTrackMap.Length(); ++i) { - TrackID outID = mTrackMap[i].mOutputTrackID; - maxTrackID = std::max(maxTrackID, outID); - } - // Note: we might have removed it here, but it might still be in the - // StreamBuffer if the TrackUnionStream sees its input stream flip from - // A to B, where both A and B have a track with the same ID - while (1) { - // search until we find one not in use here, and not in mBuffer - if (!mBuffer.FindTrack(id)) { - break; - } - id = ++maxTrackID; - } - - TrackRate rate = aTrack->GetRate(); - // Round up the track start time so the track, if anything, starts a - // little later than the true time. This means we'll have enough - // samples in our input stream to go just beyond the destination time. - TrackTicks outputStart = TimeToTicksRoundUp(rate, GraphTimeToStreamTime(aFrom)); - - nsAutoPtr<MediaSegment> segment; - segment = aTrack->GetSegment()->CreateEmptyClone(); - for (uint32_t j = 0; j < mListeners.Length(); ++j) { - MediaStreamListener* l = mListeners[j]; - l->NotifyQueuedTrackChanges(Graph(), id, rate, outputStart, - MediaStreamListener::TRACK_EVENT_CREATED, - *segment); - } - segment->AppendNullData(outputStart); - StreamBuffer::Track* track = - &mBuffer.AddTrack(id, rate, outputStart, segment.forget()); - STREAM_LOG(PR_LOG_DEBUG, ("TrackUnionStream %p adding track %d for input stream %p track %d, start ticks %lld", - this, id, aPort->GetSource(), aTrack->GetID(), - (long long)outputStart)); - - TrackMapEntry* map = mTrackMap.AppendElement(); - map->mEndOfConsumedInputTicks = 0; - map->mEndOfLastInputIntervalInInputStream = -1; - map->mEndOfLastInputIntervalInOutputStream = -1; - map->mInputPort = aPort; - map->mInputTrackID = aTrack->GetID(); - map->mOutputTrackID = track->GetID(); - map->mSegment = aTrack->GetSegment()->CreateEmptyClone(); - return mTrackMap.Length() - 1; - } - void EndTrack(uint32_t aIndex) - { - StreamBuffer::Track* outputTrack = mBuffer.FindTrack(mTrackMap[aIndex].mOutputTrackID); - if (!outputTrack || outputTrack->IsEnded()) - return; - for (uint32_t j = 0; j < mListeners.Length(); ++j) { - MediaStreamListener* l = mListeners[j]; - TrackTicks offset = outputTrack->GetSegment()->GetDuration(); - nsAutoPtr<MediaSegment> segment; - segment = outputTrack->GetSegment()->CreateEmptyClone(); - l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), - outputTrack->GetRate(), offset, - MediaStreamListener::TRACK_EVENT_ENDED, - *segment); - } - outputTrack->SetEnded(); - } + GraphTime aFrom); + void EndTrack(uint32_t aIndex); void CopyTrackData(StreamBuffer::Track* aInputTrack, uint32_t aMapIndex, GraphTime aFrom, GraphTime aTo, - bool* aOutputTrackFinished) - { - TrackMapEntry* map = &mTrackMap[aMapIndex]; - StreamBuffer::Track* outputTrack = mBuffer.FindTrack(map->mOutputTrackID); - MOZ_ASSERT(outputTrack && !outputTrack->IsEnded(), "Can't copy to ended track"); - - TrackRate rate = outputTrack->GetRate(); - MediaSegment* segment = map->mSegment; - MediaStream* source = map->mInputPort->GetSource(); - - GraphTime next; - *aOutputTrackFinished = false; - for (GraphTime t = aFrom; t < aTo; t = next) { - MediaInputPort::InputInterval interval = map->mInputPort->GetNextInputInterval(t); - interval.mEnd = std::min(interval.mEnd, aTo); - StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd); - TrackTicks inputTrackEndPoint = aInputTrack->GetEnd(); - - if (aInputTrack->IsEnded() && - aInputTrack->GetEndTimeRoundDown() <= inputEnd) { - *aOutputTrackFinished = true; - } - - if (interval.mStart >= interval.mEnd) - break; - next = interval.mEnd; - - // Ticks >= startTicks and < endTicks are in the interval - StreamTime outputEnd = GraphTimeToStreamTime(interval.mEnd); - TrackTicks startTicks = outputTrack->GetEnd(); - StreamTime outputStart = GraphTimeToStreamTime(interval.mStart); - MOZ_ASSERT(startTicks == TimeToTicksRoundUp(rate, outputStart), "Samples missing"); - TrackTicks endTicks = TimeToTicksRoundUp(rate, outputEnd); - TrackTicks ticks = endTicks - startTicks; - StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart); - - if (interval.mInputIsBlocked) { - // Maybe the input track ended? - segment->AppendNullData(ticks); - STREAM_LOG(PR_LOG_DEBUG+1, ("TrackUnionStream %p appending %lld ticks of null data to track %d", - this, (long long)ticks, outputTrack->GetID())); - } else { - // Figuring out which samples to use from the input stream is tricky - // because its start time and our start time may differ by a fraction - // of a tick. Assuming the input track hasn't ended, we have to ensure - // that 'ticks' samples are gathered, even though a tick boundary may - // occur between outputStart and outputEnd but not between inputStart - // and inputEnd. - // These are the properties we need to ensure: - // 1) Exactly 'ticks' ticks of output are produced, i.e. - // inputEndTicks - inputStartTicks = ticks. - // 2) inputEndTicks <= aInputTrack->GetSegment()->GetDuration(). - // 3) In any sequence of intervals where neither stream is blocked, - // the content of the input track we use is a contiguous sequence of - // ticks with no gaps or overlaps. - if (map->mEndOfLastInputIntervalInInputStream != inputStart || - map->mEndOfLastInputIntervalInOutputStream != outputStart) { - // Start of a new series of intervals where neither stream is blocked. - map->mEndOfConsumedInputTicks = TimeToTicksRoundDown(rate, inputStart) - 1; - } - TrackTicks inputStartTicks = map->mEndOfConsumedInputTicks; - TrackTicks inputEndTicks = inputStartTicks + ticks; - map->mEndOfConsumedInputTicks = inputEndTicks; - map->mEndOfLastInputIntervalInInputStream = inputEnd; - map->mEndOfLastInputIntervalInOutputStream = outputEnd; - // Now we prove that the above properties hold: - // Property #1: trivial by construction. - // Property #3: trivial by construction. Between every two - // intervals where both streams are not blocked, the above if condition - // is false and mEndOfConsumedInputTicks advances exactly to match - // the ticks that were consumed. - // Property #2: - // Let originalOutputStart be the value of outputStart and originalInputStart - // be the value of inputStart when the body of the "if" block was last - // executed. - // Let allTicks be the sum of the values of 'ticks' computed since then. - // The interval [originalInputStart/rate, inputEnd/rate) is the - // same length as the interval [originalOutputStart/rate, outputEnd/rate), - // so the latter interval can have at most one more integer in it. Thus - // TimeToTicksRoundUp(rate, outputEnd) - TimeToTicksRoundUp(rate, originalOutputStart) - // <= TimeToTicksRoundDown(rate, inputEnd) - TimeToTicksRoundDown(rate, originalInputStart) + 1 - // Then - // inputEndTicks = TimeToTicksRoundDown(rate, originalInputStart) - 1 + allTicks - // = TimeToTicksRoundDown(rate, originalInputStart) - 1 + TimeToTicksRoundUp(rate, outputEnd) - TimeToTicksRoundUp(rate, originalOutputStart) - // <= TimeToTicksRoundDown(rate, originalInputStart) - 1 + TimeToTicksRoundDown(rate, inputEnd) - TimeToTicksRoundDown(rate, originalInputStart) + 1 - // = TimeToTicksRoundDown(rate, inputEnd) - // <= inputEnd/rate - // (now using the fact that inputEnd <= track->GetEndTimeRoundDown() for a non-ended track) - // <= TicksToTimeRoundDown(rate, aInputTrack->GetSegment()->GetDuration())/rate - // <= rate*aInputTrack->GetSegment()->GetDuration()/rate - // = aInputTrack->GetSegment()->GetDuration() - // as required. - // Note that while the above proof appears to be generally right, if we are suffering - // from a lot of underrun, then in rare cases inputStartTicks >> inputTrackEndPoint. - // As such, we still need to verify the sanity of property #2 and use null data as - // appropriate. - - if (inputStartTicks < 0) { - // Data before the start of the track is just null. - // We have to add a small amount of delay to ensure that there is - // always a sample available if we see an interval that contains a - // tick boundary on the output stream's timeline but does not contain - // a tick boundary on the input stream's timeline. 1 tick delay is - // necessary and sufficient. - segment->AppendNullData(-inputStartTicks); - inputStartTicks = 0; - } - if (inputEndTicks > inputStartTicks) { - if (inputEndTicks <= inputTrackEndPoint) { - segment->AppendSlice(*aInputTrack->GetSegment(), inputStartTicks, inputEndTicks); - STREAM_LOG(PR_LOG_DEBUG+1, ("TrackUnionStream %p appending %lld ticks of input data to track %d", - this, ticks, outputTrack->GetID())); - } else { - if (inputStartTicks < inputTrackEndPoint) { - segment->AppendSlice(*aInputTrack->GetSegment(), inputStartTicks, inputTrackEndPoint); - ticks -= inputTrackEndPoint - inputStartTicks; - } - segment->AppendNullData(ticks); - STREAM_LOG(PR_LOG_DEBUG+1, ("TrackUnionStream %p appending %lld ticks of input data and %lld of null data to track %d", - this, inputTrackEndPoint - inputStartTicks, ticks, outputTrack->GetID())); - } - } - } - ApplyTrackDisabling(outputTrack->GetID(), segment); - for (uint32_t j = 0; j < mListeners.Length(); ++j) { - MediaStreamListener* l = mListeners[j]; - l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), - outputTrack->GetRate(), startTicks, 0, - *segment); - } - outputTrack->GetSegment()->AppendFrom(segment); - } - } + bool* aOutputTrackFinished); nsTArray<TrackMapEntry> mTrackMap; }; } #endif /* MOZILLA_MEDIASTREAMGRAPH_H_ */
--- a/content/media/moz.build +++ b/content/media/moz.build @@ -159,16 +159,17 @@ UNIFIED_SOURCES += [ 'RtspMediaResource.cpp', 'SharedThreadPool.cpp', 'StreamBuffer.cpp', 'TextTrack.cpp', 'TextTrackCue.cpp', 'TextTrackCueList.cpp', 'TextTrackList.cpp', 'TextTrackRegion.cpp', + 'TrackUnionStream.cpp', 'VideoFrameContainer.cpp', 'VideoPlaybackQuality.cpp', 'VideoSegment.cpp', 'VideoStreamTrack.cpp', 'VideoTrack.cpp', 'VideoTrackList.cpp', 'VideoUtils.cpp', 'WebVTTListener.cpp',