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 "DecodedStream.h"
8 
9 #include "AudioDecoderInputTrack.h"
10 #include "AudioSegment.h"
11 #include "MediaData.h"
12 #include "MediaDecoderStateMachine.h"
13 #include "MediaQueue.h"
14 #include "MediaTrackGraph.h"
15 #include "MediaTrackListener.h"
16 #include "SharedBuffer.h"
17 #include "Tracing.h"
18 #include "VideoSegment.h"
19 #include "VideoUtils.h"
20 #include "mozilla/AbstractThread.h"
21 #include "mozilla/CheckedInt.h"
22 #include "mozilla/ProfilerLabels.h"
23 #include "mozilla/ProfilerMarkerTypes.h"
24 #include "mozilla/SyncRunnable.h"
25 #include "mozilla/gfx/Point.h"
26 #include "mozilla/StaticPrefs_dom.h"
27 #include "nsProxyRelease.h"
28 
29 namespace mozilla {
30 
31 using media::NullableTimeUnit;
32 using media::TimeUnit;
33 
34 extern LazyLogModule gMediaDecoderLog;
35 
36 #define LOG_DS(type, fmt, ...)    \
37   MOZ_LOG(gMediaDecoderLog, type, \
38           ("DecodedStream=%p " fmt, this, ##__VA_ARGS__))
39 
40 #define PLAYBACK_PROFILER_MARKER(markerString) \
41   PROFILER_MARKER_TEXT(FUNCTION_SIGNATURE, MEDIA_PLAYBACK, {}, markerString)
42 
43 /*
44  * A container class to make it easier to pass the playback info all the
45  * way to DecodedStreamGraphListener from DecodedStream.
46  */
47 struct PlaybackInfoInit {
48   TimeUnit mStartTime;
49   MediaInfo mInfo;
50 };
51 
52 class DecodedStreamGraphListener;
53 
54 class SourceVideoTrackListener : public MediaTrackListener {
55  public:
56   SourceVideoTrackListener(DecodedStreamGraphListener* aGraphListener,
57                            SourceMediaTrack* aVideoTrack,
58                            MediaTrack* aAudioTrack,
59                            nsISerialEventTarget* aDecoderThread);
60 
61   void NotifyOutput(MediaTrackGraph* aGraph,
62                     TrackTime aCurrentTrackTime) override;
63   void NotifyEnded(MediaTrackGraph* aGraph) override;
64 
65  private:
66   const RefPtr<DecodedStreamGraphListener> mGraphListener;
67   const RefPtr<SourceMediaTrack> mVideoTrack;
68   const RefPtr<const MediaTrack> mAudioTrack;
69   const RefPtr<nsISerialEventTarget> mDecoderThread;
70   TrackTime mLastVideoOutputTime = 0;
71 };
72 
73 class DecodedStreamGraphListener {
74   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStreamGraphListener)
75  public:
DecodedStreamGraphListener(nsISerialEventTarget * aDecoderThread,AudioDecoderInputTrack * aAudioTrack,MozPromiseHolder<DecodedStream::EndedPromise> && aAudioEndedHolder,SourceMediaTrack * aVideoTrack,MozPromiseHolder<DecodedStream::EndedPromise> && aVideoEndedHolder)76   DecodedStreamGraphListener(
77       nsISerialEventTarget* aDecoderThread, AudioDecoderInputTrack* aAudioTrack,
78       MozPromiseHolder<DecodedStream::EndedPromise>&& aAudioEndedHolder,
79       SourceMediaTrack* aVideoTrack,
80       MozPromiseHolder<DecodedStream::EndedPromise>&& aVideoEndedHolder)
81       : mDecoderThread(aDecoderThread),
82         mVideoTrackListener(
83             aVideoTrack ? MakeRefPtr<SourceVideoTrackListener>(
84                               this, aVideoTrack, aAudioTrack, aDecoderThread)
85                         : nullptr),
86         mAudioEndedHolder(std::move(aAudioEndedHolder)),
87         mVideoEndedHolder(std::move(aVideoEndedHolder)),
88         mAudioTrack(aAudioTrack),
89         mVideoTrack(aVideoTrack) {
90     MOZ_ASSERT(NS_IsMainThread());
91     MOZ_ASSERT(mDecoderThread);
92 
93     if (mAudioTrack) {
94       mOnAudioOutput = mAudioTrack->OnOutput().Connect(
95           mDecoderThread,
96           [self = RefPtr<DecodedStreamGraphListener>(this)](TrackTime aTime) {
97             self->NotifyOutput(MediaSegment::AUDIO, aTime);
98           });
99       mOnAudioEnd = mAudioTrack->OnEnd().Connect(
100           mDecoderThread, [self = RefPtr<DecodedStreamGraphListener>(this)]() {
101             self->NotifyEnded(MediaSegment::AUDIO);
102           });
103     } else {
104       mAudioEnded = true;
105       mAudioEndedHolder.ResolveIfExists(true, __func__);
106     }
107 
108     if (mVideoTrackListener) {
109       mVideoTrack->AddListener(mVideoTrackListener);
110     } else {
111       mVideoEnded = true;
112       mVideoEndedHolder.ResolveIfExists(true, __func__);
113     }
114   }
115 
Close()116   void Close() {
117     AssertOnDecoderThread();
118     if (mAudioTrack) {
119       mAudioTrack->Close();
120     }
121     if (mVideoTrack) {
122       mVideoTrack->End();
123     }
124     mAudioEndedHolder.ResolveIfExists(false, __func__);
125     mVideoEndedHolder.ResolveIfExists(false, __func__);
126     mOnAudioOutput.DisconnectIfExists();
127     mOnAudioEnd.DisconnectIfExists();
128   }
129 
NotifyOutput(MediaSegment::Type aType,TrackTime aCurrentTrackTime)130   void NotifyOutput(MediaSegment::Type aType, TrackTime aCurrentTrackTime) {
131     AssertOnDecoderThread();
132     if (aType == MediaSegment::AUDIO) {
133       mAudioOutputFrames = aCurrentTrackTime;
134     } else if (aType == MediaSegment::VIDEO) {
135       if (aCurrentTrackTime >= mVideoEndTime) {
136         mVideoTrack->End();
137       }
138     } else {
139       MOZ_CRASH("Unexpected track type");
140     }
141 
142     MOZ_ASSERT_IF(aType == MediaSegment::AUDIO, !mAudioEnded);
143     MOZ_ASSERT_IF(aType == MediaSegment::VIDEO, !mVideoEnded);
144     // This situation would happen when playing audio in >1x playback rate,
145     // because the audio output clock isn't align the graph time and would go
146     // forward faster. Eg. playback rate=2, when the graph time passes 10s, the
147     // audio clock time actually already goes forward 20s. After audio track
148     // ended, video track would tirgger the clock, but the video time still
149     // follows the graph time, which is smaller than the preivous audio clock
150     // time and should be ignored.
151     if (aCurrentTrackTime <= mLastOutputTime) {
152       MOZ_ASSERT(aType == MediaSegment::VIDEO);
153       return;
154     }
155     MOZ_ASSERT(aCurrentTrackTime > mLastOutputTime);
156     mLastOutputTime = aCurrentTrackTime;
157 
158     // Only when audio track doesn't exists or has reached the end, video
159     // track should drive the clock.
160     MOZ_ASSERT_IF(aType == MediaSegment::VIDEO, mAudioEnded);
161     const MediaTrack* track = aType == MediaSegment::VIDEO
162                                   ? static_cast<MediaTrack*>(mVideoTrack)
163                                   : static_cast<MediaTrack*>(mAudioTrack);
164     mOnOutput.Notify(track->TrackTimeToMicroseconds(aCurrentTrackTime));
165   }
166 
NotifyEnded(MediaSegment::Type aType)167   void NotifyEnded(MediaSegment::Type aType) {
168     AssertOnDecoderThread();
169     if (aType == MediaSegment::AUDIO) {
170       MOZ_ASSERT(!mAudioEnded);
171       mAudioEnded = true;
172       mAudioEndedHolder.ResolveIfExists(true, __func__);
173     } else if (aType == MediaSegment::VIDEO) {
174       MOZ_ASSERT(!mVideoEnded);
175       mVideoEnded = true;
176       mVideoEndedHolder.ResolveIfExists(true, __func__);
177     } else {
178       MOZ_CRASH("Unexpected track type");
179     }
180   }
181 
182   /**
183    * Tell the graph listener to end the track sourced by the given track after
184    * it has seen at least aEnd worth of output reported as processed by the
185    * graph.
186    *
187    * A TrackTime of TRACK_TIME_MAX indicates that the track has no end and is
188    * the default.
189    *
190    * This method of ending tracks is needed because the MediaTrackGraph
191    * processes ended tracks (through SourceMediaTrack::EndTrack) at the
192    * beginning of an iteration, but waits until the end of the iteration to
193    * process any ControlMessages. When such a ControlMessage is a listener that
194    * is to be added to a track that has ended in its very first iteration, the
195    * track ends before the listener tracking this ending is added. This can lead
196    * to a MediaStreamTrack ending on main thread (it uses another listener)
197    * before the listeners to render the track get added, potentially meaning a
198    * media element doesn't progress before reaching the end although data was
199    * available.
200    */
EndVideoTrackAt(MediaTrack * aTrack,TrackTime aEnd)201   void EndVideoTrackAt(MediaTrack* aTrack, TrackTime aEnd) {
202     AssertOnDecoderThread();
203     MOZ_DIAGNOSTIC_ASSERT(aTrack == mVideoTrack);
204     mVideoEndTime = aEnd;
205   }
206 
Forget()207   void Forget() {
208     MOZ_ASSERT(NS_IsMainThread());
209     if (mVideoTrackListener && !mVideoTrack->IsDestroyed()) {
210       mVideoTrack->RemoveListener(mVideoTrackListener);
211     }
212     mVideoTrackListener = nullptr;
213   }
214 
GetAudioFramesPlayed()215   TrackTime GetAudioFramesPlayed() {
216     AssertOnDecoderThread();
217     return mAudioOutputFrames;
218   }
219 
OnOutput()220   MediaEventSource<int64_t>& OnOutput() { return mOnOutput; }
221 
222  private:
~DecodedStreamGraphListener()223   ~DecodedStreamGraphListener() {
224     MOZ_ASSERT(mAudioEndedHolder.IsEmpty());
225     MOZ_ASSERT(mVideoEndedHolder.IsEmpty());
226   }
227 
AssertOnDecoderThread() const228   inline void AssertOnDecoderThread() const {
229     MOZ_ASSERT(mDecoderThread->IsOnCurrentThread());
230   }
231 
232   const RefPtr<nsISerialEventTarget> mDecoderThread;
233 
234   // Accessible on any thread, but only notify on the decoder thread.
235   MediaEventProducer<int64_t> mOnOutput;
236 
237   RefPtr<SourceVideoTrackListener> mVideoTrackListener;
238 
239   // These can be resolved on the main thread on creation if there is no
240   // corresponding track, otherwise they are resolved on the decoder thread.
241   MozPromiseHolder<DecodedStream::EndedPromise> mAudioEndedHolder;
242   MozPromiseHolder<DecodedStream::EndedPromise> mVideoEndedHolder;
243 
244   // Decoder thread only.
245   TrackTime mAudioOutputFrames = 0;
246   TrackTime mLastOutputTime = 0;
247   bool mAudioEnded = false;
248   bool mVideoEnded = false;
249 
250   // Any thread.
251   const RefPtr<AudioDecoderInputTrack> mAudioTrack;
252   const RefPtr<SourceMediaTrack> mVideoTrack;
253   MediaEventListener mOnAudioOutput;
254   MediaEventListener mOnAudioEnd;
255   Atomic<TrackTime> mVideoEndTime{TRACK_TIME_MAX};
256 };
257 
SourceVideoTrackListener(DecodedStreamGraphListener * aGraphListener,SourceMediaTrack * aVideoTrack,MediaTrack * aAudioTrack,nsISerialEventTarget * aDecoderThread)258 SourceVideoTrackListener::SourceVideoTrackListener(
259     DecodedStreamGraphListener* aGraphListener, SourceMediaTrack* aVideoTrack,
260     MediaTrack* aAudioTrack, nsISerialEventTarget* aDecoderThread)
261     : mGraphListener(aGraphListener),
262       mVideoTrack(aVideoTrack),
263       mAudioTrack(aAudioTrack),
264       mDecoderThread(aDecoderThread) {}
265 
NotifyOutput(MediaTrackGraph * aGraph,TrackTime aCurrentTrackTime)266 void SourceVideoTrackListener::NotifyOutput(MediaTrackGraph* aGraph,
267                                             TrackTime aCurrentTrackTime) {
268   aGraph->AssertOnGraphThreadOrNotRunning();
269   if (mAudioTrack && !mAudioTrack->Ended()) {
270     // Only audio playout drives the clock forward, if present and live.
271     return;
272   }
273   // The graph can iterate without time advancing, but the invariant is that
274   // time can never go backwards.
275   if (aCurrentTrackTime <= mLastVideoOutputTime) {
276     MOZ_ASSERT(aCurrentTrackTime == mLastVideoOutputTime);
277     return;
278   }
279   mLastVideoOutputTime = aCurrentTrackTime;
280   mDecoderThread->Dispatch(NS_NewRunnableFunction(
281       "SourceVideoTrackListener::NotifyOutput",
282       [self = RefPtr<SourceVideoTrackListener>(this), aCurrentTrackTime]() {
283         self->mGraphListener->NotifyOutput(MediaSegment::VIDEO,
284                                            aCurrentTrackTime);
285       }));
286 }
287 
NotifyEnded(MediaTrackGraph * aGraph)288 void SourceVideoTrackListener::NotifyEnded(MediaTrackGraph* aGraph) {
289   aGraph->AssertOnGraphThreadOrNotRunning();
290   mDecoderThread->Dispatch(NS_NewRunnableFunction(
291       "SourceVideoTrackListener::NotifyEnded",
292       [self = RefPtr<SourceVideoTrackListener>(this)]() {
293         self->mGraphListener->NotifyEnded(MediaSegment::VIDEO);
294       }));
295 }
296 
297 /**
298  * All MediaStream-related data is protected by the decoder's monitor. We have
299  * at most one DecodedStreamData per MediaDecoder. XXX Its tracks are used as
300  * inputs for all output tracks created by OutputStreamManager after calls to
301  * captureStream/UntilEnded. Seeking creates new source tracks, as does
302  * replaying after the input as ended. In the latter case, the new sources are
303  * not connected to tracks created by captureStreamUntilEnded.
304  */
305 class DecodedStreamData final {
306  public:
307   DecodedStreamData(
308       PlaybackInfoInit&& aInit, MediaTrackGraph* aGraph,
309       RefPtr<ProcessedMediaTrack> aAudioOutputTrack,
310       RefPtr<ProcessedMediaTrack> aVideoOutputTrack,
311       MozPromiseHolder<DecodedStream::EndedPromise>&& aAudioEndedPromise,
312       MozPromiseHolder<DecodedStream::EndedPromise>&& aVideoEndedPromise,
313       float aPlaybackRate, float aVolume, bool aPreservesPitch,
314       nsISerialEventTarget* aDecoderThread);
315   ~DecodedStreamData();
316   MediaEventSource<int64_t>& OnOutput();
317   // This is used to mark track as closed and should be called before Forget().
318   // Decoder thread only.
319   void Close();
320   // After calling this function, the DecodedStreamData would be destroyed.
321   // Main thread only.
322   void Forget();
323   void GetDebugInfo(dom::DecodedStreamDataDebugInfo& aInfo);
324 
325   void WriteVideoToSegment(layers::Image* aImage, const TimeUnit& aStart,
326                            const TimeUnit& aEnd,
327                            const gfx::IntSize& aIntrinsicSize,
328                            const TimeStamp& aTimeStamp, VideoSegment* aOutput,
329                            const PrincipalHandle& aPrincipalHandle,
330                            double aPlaybackRate);
331 
332   /* The following group of fields are protected by the decoder's monitor
333    * and can be read or written on any thread.
334    */
335   // Count of audio frames written to the track
336   int64_t mAudioFramesWritten;
337   // Count of video frames written to the track in the track's rate
338   TrackTime mVideoTrackWritten;
339   // mNextAudioTime is the end timestamp for the last packet sent to the track.
340   // Therefore audio packets starting at or after this time need to be copied
341   // to the output track.
342   TimeUnit mNextAudioTime;
343   // mLastVideoStartTime is the start timestamp for the last packet sent to the
344   // track. Therefore video packets starting after this time need to be copied
345   // to the output track.
346   NullableTimeUnit mLastVideoStartTime;
347   // mLastVideoEndTime is the end timestamp for the last packet sent to the
348   // track. It is used to adjust durations of chunks sent to the output track
349   // when there are overlaps in VideoData.
350   NullableTimeUnit mLastVideoEndTime;
351   // The timestamp of the last frame, so we can ensure time never goes
352   // backwards.
353   TimeStamp mLastVideoTimeStamp;
354   // The last video image sent to the track. Useful if we need to replicate
355   // the image.
356   RefPtr<layers::Image> mLastVideoImage;
357   gfx::IntSize mLastVideoImageDisplaySize;
358   bool mHaveSentFinishAudio;
359   bool mHaveSentFinishVideo;
360 
361   const RefPtr<AudioDecoderInputTrack> mAudioTrack;
362   const RefPtr<SourceMediaTrack> mVideoTrack;
363   const RefPtr<ProcessedMediaTrack> mAudioOutputTrack;
364   const RefPtr<ProcessedMediaTrack> mVideoOutputTrack;
365   const RefPtr<MediaInputPort> mAudioPort;
366   const RefPtr<MediaInputPort> mVideoPort;
367   const RefPtr<DecodedStream::EndedPromise> mAudioEndedPromise;
368   const RefPtr<DecodedStream::EndedPromise> mVideoEndedPromise;
369   const RefPtr<DecodedStreamGraphListener> mListener;
370 };
371 
DecodedStreamData(PlaybackInfoInit && aInit,MediaTrackGraph * aGraph,RefPtr<ProcessedMediaTrack> aAudioOutputTrack,RefPtr<ProcessedMediaTrack> aVideoOutputTrack,MozPromiseHolder<DecodedStream::EndedPromise> && aAudioEndedPromise,MozPromiseHolder<DecodedStream::EndedPromise> && aVideoEndedPromise,float aPlaybackRate,float aVolume,bool aPreservesPitch,nsISerialEventTarget * aDecoderThread)372 DecodedStreamData::DecodedStreamData(
373     PlaybackInfoInit&& aInit, MediaTrackGraph* aGraph,
374     RefPtr<ProcessedMediaTrack> aAudioOutputTrack,
375     RefPtr<ProcessedMediaTrack> aVideoOutputTrack,
376     MozPromiseHolder<DecodedStream::EndedPromise>&& aAudioEndedPromise,
377     MozPromiseHolder<DecodedStream::EndedPromise>&& aVideoEndedPromise,
378     float aPlaybackRate, float aVolume, bool aPreservesPitch,
379     nsISerialEventTarget* aDecoderThread)
380     : mAudioFramesWritten(0),
381       mVideoTrackWritten(0),
382       mNextAudioTime(aInit.mStartTime),
383       mHaveSentFinishAudio(false),
384       mHaveSentFinishVideo(false),
385       mAudioTrack(aInit.mInfo.HasAudio()
386                       ? AudioDecoderInputTrack::Create(
387                             aGraph, aDecoderThread, aInit.mInfo.mAudio,
388                             aPlaybackRate, aVolume, aPreservesPitch)
389                       : nullptr),
390       mVideoTrack(aInit.mInfo.HasVideo()
391                       ? aGraph->CreateSourceTrack(MediaSegment::VIDEO)
392                       : nullptr),
393       mAudioOutputTrack(std::move(aAudioOutputTrack)),
394       mVideoOutputTrack(std::move(aVideoOutputTrack)),
395       mAudioPort((mAudioOutputTrack && mAudioTrack)
396                      ? mAudioOutputTrack->AllocateInputPort(mAudioTrack)
397                      : nullptr),
398       mVideoPort((mVideoOutputTrack && mVideoTrack)
399                      ? mVideoOutputTrack->AllocateInputPort(mVideoTrack)
400                      : nullptr),
401       mAudioEndedPromise(aAudioEndedPromise.Ensure(__func__)),
402       mVideoEndedPromise(aVideoEndedPromise.Ensure(__func__)),
403       // DecodedStreamGraphListener will resolve these promises.
404       mListener(MakeRefPtr<DecodedStreamGraphListener>(
405           aDecoderThread, mAudioTrack, std::move(aAudioEndedPromise),
406           mVideoTrack, std::move(aVideoEndedPromise))) {
407   MOZ_ASSERT(NS_IsMainThread());
408 }
409 
~DecodedStreamData()410 DecodedStreamData::~DecodedStreamData() {
411   MOZ_ASSERT(NS_IsMainThread());
412   if (mAudioTrack) {
413     mAudioTrack->Destroy();
414   }
415   if (mVideoTrack) {
416     mVideoTrack->Destroy();
417   }
418   if (mAudioPort) {
419     mAudioPort->Destroy();
420   }
421   if (mVideoPort) {
422     mVideoPort->Destroy();
423   }
424 }
425 
OnOutput()426 MediaEventSource<int64_t>& DecodedStreamData::OnOutput() {
427   return mListener->OnOutput();
428 }
429 
Close()430 void DecodedStreamData::Close() { mListener->Close(); }
431 
Forget()432 void DecodedStreamData::Forget() { mListener->Forget(); }
433 
GetDebugInfo(dom::DecodedStreamDataDebugInfo & aInfo)434 void DecodedStreamData::GetDebugInfo(dom::DecodedStreamDataDebugInfo& aInfo) {
435   CopyUTF8toUTF16(nsPrintfCString("%p", this), aInfo.mInstance);
436   aInfo.mAudioFramesWritten = mAudioFramesWritten;
437   aInfo.mStreamAudioWritten = mListener->GetAudioFramesPlayed();
438   aInfo.mNextAudioTime = mNextAudioTime.ToMicroseconds();
439   aInfo.mLastVideoStartTime =
440       mLastVideoStartTime.valueOr(TimeUnit::FromMicroseconds(-1))
441           .ToMicroseconds();
442   aInfo.mLastVideoEndTime =
443       mLastVideoEndTime.valueOr(TimeUnit::FromMicroseconds(-1))
444           .ToMicroseconds();
445   aInfo.mHaveSentFinishAudio = mHaveSentFinishAudio;
446   aInfo.mHaveSentFinishVideo = mHaveSentFinishVideo;
447 }
448 
DecodedStream(MediaDecoderStateMachine * aStateMachine,nsMainThreadPtrHandle<SharedDummyTrack> aDummyTrack,CopyableTArray<RefPtr<ProcessedMediaTrack>> aOutputTracks,double aVolume,double aPlaybackRate,bool aPreservesPitch,MediaQueue<AudioData> & aAudioQueue,MediaQueue<VideoData> & aVideoQueue)449 DecodedStream::DecodedStream(
450     MediaDecoderStateMachine* aStateMachine,
451     nsMainThreadPtrHandle<SharedDummyTrack> aDummyTrack,
452     CopyableTArray<RefPtr<ProcessedMediaTrack>> aOutputTracks, double aVolume,
453     double aPlaybackRate, bool aPreservesPitch,
454     MediaQueue<AudioData>& aAudioQueue, MediaQueue<VideoData>& aVideoQueue)
455     : mOwnerThread(aStateMachine->OwnerThread()),
456       mDummyTrack(std::move(aDummyTrack)),
457       mWatchManager(this, mOwnerThread),
458       mPlaying(false, "DecodedStream::mPlaying"),
459       mPrincipalHandle(aStateMachine->OwnerThread(), PRINCIPAL_HANDLE_NONE,
460                        "DecodedStream::mPrincipalHandle (Mirror)"),
461       mCanonicalOutputPrincipal(aStateMachine->CanonicalOutputPrincipal()),
462       mOutputTracks(std::move(aOutputTracks)),
463       mVolume(aVolume),
464       mPlaybackRate(aPlaybackRate),
465       mPreservesPitch(aPreservesPitch),
466       mAudioQueue(aAudioQueue),
467       mVideoQueue(aVideoQueue) {}
468 
~DecodedStream()469 DecodedStream::~DecodedStream() {
470   MOZ_ASSERT(mStartTime.isNothing(), "playback should've ended.");
471 }
472 
OnEnded(TrackType aType)473 RefPtr<DecodedStream::EndedPromise> DecodedStream::OnEnded(TrackType aType) {
474   AssertOwnerThread();
475   MOZ_ASSERT(mStartTime.isSome());
476 
477   if (aType == TrackInfo::kAudioTrack && mInfo.HasAudio()) {
478     return mAudioEndedPromise;
479   } else if (aType == TrackInfo::kVideoTrack && mInfo.HasVideo()) {
480     return mVideoEndedPromise;
481   }
482   return nullptr;
483 }
484 
Start(const TimeUnit & aStartTime,const MediaInfo & aInfo)485 nsresult DecodedStream::Start(const TimeUnit& aStartTime,
486                               const MediaInfo& aInfo) {
487   AssertOwnerThread();
488   MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
489 
490   AUTO_PROFILER_LABEL(FUNCTION_SIGNATURE, MEDIA_PLAYBACK);
491   if (profiler_thread_is_being_profiled_for_markers()) {
492     nsPrintfCString markerString("StartTime=%" PRId64,
493                                  aStartTime.ToMicroseconds());
494     PLAYBACK_PROFILER_MARKER(markerString);
495   }
496   LOG_DS(LogLevel::Debug, "Start() mStartTime=%" PRId64,
497          aStartTime.ToMicroseconds());
498 
499   mStartTime.emplace(aStartTime);
500   mLastOutputTime = TimeUnit::Zero();
501   mInfo = aInfo;
502   mPlaying = true;
503   mPrincipalHandle.Connect(mCanonicalOutputPrincipal);
504   mWatchManager.Watch(mPlaying, &DecodedStream::PlayingChanged);
505   mAudibilityMonitor.emplace(
506       mInfo.mAudio.mRate,
507       StaticPrefs::dom_media_silence_duration_for_audibility());
508   ConnectListener();
509 
510   class R : public Runnable {
511    public:
512     R(PlaybackInfoInit&& aInit,
513       nsMainThreadPtrHandle<SharedDummyTrack> aDummyTrack,
514       nsTArray<RefPtr<ProcessedMediaTrack>> aOutputTracks,
515       MozPromiseHolder<MediaSink::EndedPromise>&& aAudioEndedPromise,
516       MozPromiseHolder<MediaSink::EndedPromise>&& aVideoEndedPromise,
517       float aPlaybackRate, float aVolume, bool aPreservesPitch,
518       nsISerialEventTarget* aDecoderThread)
519         : Runnable("CreateDecodedStreamData"),
520           mInit(std::move(aInit)),
521           mDummyTrack(std::move(aDummyTrack)),
522           mOutputTracks(std::move(aOutputTracks)),
523           mAudioEndedPromise(std::move(aAudioEndedPromise)),
524           mVideoEndedPromise(std::move(aVideoEndedPromise)),
525           mPlaybackRate(aPlaybackRate),
526           mVolume(aVolume),
527           mPreservesPitch(aPreservesPitch),
528           mDecoderThread(aDecoderThread) {}
529     NS_IMETHOD Run() override {
530       MOZ_ASSERT(NS_IsMainThread());
531       RefPtr<ProcessedMediaTrack> audioOutputTrack;
532       RefPtr<ProcessedMediaTrack> videoOutputTrack;
533       for (const auto& track : mOutputTracks) {
534         if (track->mType == MediaSegment::AUDIO) {
535           MOZ_DIAGNOSTIC_ASSERT(
536               !audioOutputTrack,
537               "We only support capturing to one output track per kind");
538           audioOutputTrack = track;
539         } else if (track->mType == MediaSegment::VIDEO) {
540           MOZ_DIAGNOSTIC_ASSERT(
541               !videoOutputTrack,
542               "We only support capturing to one output track per kind");
543           videoOutputTrack = track;
544         } else {
545           MOZ_CRASH("Unknown media type");
546         }
547       }
548       if (!mDummyTrack) {
549         // No dummy track - no graph. This could be intentional as the owning
550         // media element needs access to the tracks on main thread to set up
551         // forwarding of them before playback starts. MDSM will re-create
552         // DecodedStream once a dummy track is available. This effectively halts
553         // playback for this DecodedStream.
554         return NS_OK;
555       }
556       if ((audioOutputTrack && audioOutputTrack->IsDestroyed()) ||
557           (videoOutputTrack && videoOutputTrack->IsDestroyed())) {
558         // A track has been destroyed and we'll soon get re-created with a
559         // proper one. This effectively halts playback for this DecodedStream.
560         return NS_OK;
561       }
562       mData = MakeUnique<DecodedStreamData>(
563           std::move(mInit), mDummyTrack->mTrack->Graph(),
564           std::move(audioOutputTrack), std::move(videoOutputTrack),
565           std::move(mAudioEndedPromise), std::move(mVideoEndedPromise),
566           mPlaybackRate, mVolume, mPreservesPitch, mDecoderThread);
567       return NS_OK;
568     }
569     UniquePtr<DecodedStreamData> ReleaseData() { return std::move(mData); }
570 
571    private:
572     PlaybackInfoInit mInit;
573     nsMainThreadPtrHandle<SharedDummyTrack> mDummyTrack;
574     const nsTArray<RefPtr<ProcessedMediaTrack>> mOutputTracks;
575     MozPromiseHolder<MediaSink::EndedPromise> mAudioEndedPromise;
576     MozPromiseHolder<MediaSink::EndedPromise> mVideoEndedPromise;
577     UniquePtr<DecodedStreamData> mData;
578     const float mPlaybackRate;
579     const float mVolume;
580     const bool mPreservesPitch;
581     const RefPtr<nsISerialEventTarget> mDecoderThread;
582   };
583 
584   MozPromiseHolder<DecodedStream::EndedPromise> audioEndedHolder;
585   MozPromiseHolder<DecodedStream::EndedPromise> videoEndedHolder;
586   PlaybackInfoInit init{aStartTime, aInfo};
587   nsCOMPtr<nsIRunnable> r =
588       new R(std::move(init), mDummyTrack, mOutputTracks.Clone(),
589             std::move(audioEndedHolder), std::move(videoEndedHolder),
590             static_cast<float>(mPlaybackRate), static_cast<float>(mVolume),
591             mPreservesPitch, mOwnerThread);
592   SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(), r);
593   mData = static_cast<R*>(r.get())->ReleaseData();
594 
595   if (mData) {
596     mAudioEndedPromise = mData->mAudioEndedPromise;
597     mVideoEndedPromise = mData->mVideoEndedPromise;
598     mOutputListener = mData->OnOutput().Connect(mOwnerThread, this,
599                                                 &DecodedStream::NotifyOutput);
600     SendData();
601   }
602   return NS_OK;
603 }
604 
Stop()605 void DecodedStream::Stop() {
606   AssertOwnerThread();
607   MOZ_ASSERT(mStartTime.isSome(), "playback not started.");
608 
609   TRACE("DecodedStream::Stop");
610   LOG_DS(LogLevel::Debug, "Stop()");
611 
612   DisconnectListener();
613   ResetVideo(mPrincipalHandle);
614   ResetAudio();
615   mStartTime.reset();
616   mAudioEndedPromise = nullptr;
617   mVideoEndedPromise = nullptr;
618 
619   // Clear mData immediately when this playback session ends so we won't
620   // send data to the wrong track in SendData() in next playback session.
621   DestroyData(std::move(mData));
622 
623   mPrincipalHandle.DisconnectIfConnected();
624   mWatchManager.Unwatch(mPlaying, &DecodedStream::PlayingChanged);
625   mAudibilityMonitor.reset();
626 }
627 
IsStarted() const628 bool DecodedStream::IsStarted() const {
629   AssertOwnerThread();
630   return mStartTime.isSome();
631 }
632 
IsPlaying() const633 bool DecodedStream::IsPlaying() const {
634   AssertOwnerThread();
635   return IsStarted() && mPlaying;
636 }
637 
Shutdown()638 void DecodedStream::Shutdown() {
639   AssertOwnerThread();
640   mPrincipalHandle.DisconnectIfConnected();
641   mWatchManager.Shutdown();
642 }
643 
DestroyData(UniquePtr<DecodedStreamData> && aData)644 void DecodedStream::DestroyData(UniquePtr<DecodedStreamData>&& aData) {
645   AssertOwnerThread();
646 
647   if (!aData) {
648     return;
649   }
650 
651   TRACE("DecodedStream::DestroyData");
652   mOutputListener.Disconnect();
653 
654   aData->Close();
655   NS_DispatchToMainThread(
656       NS_NewRunnableFunction("DecodedStream::DestroyData",
657                              [data = std::move(aData)]() { data->Forget(); }));
658 }
659 
SetPlaying(bool aPlaying)660 void DecodedStream::SetPlaying(bool aPlaying) {
661   AssertOwnerThread();
662 
663   // Resume/pause matters only when playback started.
664   if (mStartTime.isNothing()) {
665     return;
666   }
667 
668   if (profiler_thread_is_being_profiled_for_markers()) {
669     nsPrintfCString markerString("Playing=%s", aPlaying ? "true" : "false");
670     PLAYBACK_PROFILER_MARKER(markerString);
671   }
672   LOG_DS(LogLevel::Debug, "playing (%d) -> (%d)", mPlaying.Ref(), aPlaying);
673   mPlaying = aPlaying;
674 }
675 
SetVolume(double aVolume)676 void DecodedStream::SetVolume(double aVolume) {
677   AssertOwnerThread();
678   if (profiler_thread_is_being_profiled_for_markers()) {
679     nsPrintfCString markerString("Volume=%f", aVolume);
680     PLAYBACK_PROFILER_MARKER(markerString);
681   }
682   if (mVolume == aVolume) {
683     return;
684   }
685   mVolume = aVolume;
686   if (mData && mData->mAudioTrack) {
687     mData->mAudioTrack->SetVolume(static_cast<float>(aVolume));
688   }
689 }
690 
SetPlaybackRate(double aPlaybackRate)691 void DecodedStream::SetPlaybackRate(double aPlaybackRate) {
692   AssertOwnerThread();
693   if (profiler_thread_is_being_profiled_for_markers()) {
694     nsPrintfCString markerString("PlaybackRate=%f", aPlaybackRate);
695     PLAYBACK_PROFILER_MARKER(markerString);
696   }
697   if (mPlaybackRate == aPlaybackRate) {
698     return;
699   }
700   mPlaybackRate = aPlaybackRate;
701   if (mData && mData->mAudioTrack) {
702     mData->mAudioTrack->SetPlaybackRate(static_cast<float>(aPlaybackRate));
703   }
704 }
705 
SetPreservesPitch(bool aPreservesPitch)706 void DecodedStream::SetPreservesPitch(bool aPreservesPitch) {
707   AssertOwnerThread();
708   if (profiler_thread_is_being_profiled_for_markers()) {
709     nsPrintfCString markerString("PreservesPitch=%s",
710                                  aPreservesPitch ? "true" : "false");
711     PLAYBACK_PROFILER_MARKER(markerString);
712   }
713   if (mPreservesPitch == aPreservesPitch) {
714     return;
715   }
716   mPreservesPitch = aPreservesPitch;
717   if (mData && mData->mAudioTrack) {
718     mData->mAudioTrack->SetPreservesPitch(aPreservesPitch);
719   }
720 }
721 
PlaybackRate() const722 double DecodedStream::PlaybackRate() const {
723   AssertOwnerThread();
724   return mPlaybackRate;
725 }
726 
SendAudio(const PrincipalHandle & aPrincipalHandle)727 void DecodedStream::SendAudio(const PrincipalHandle& aPrincipalHandle) {
728   AssertOwnerThread();
729 
730   if (!mInfo.HasAudio()) {
731     return;
732   }
733 
734   if (mData->mHaveSentFinishAudio) {
735     return;
736   }
737 
738   TRACE("DecodedStream::SendAudio");
739   // It's OK to hold references to the AudioData because AudioData
740   // is ref-counted.
741   AutoTArray<RefPtr<AudioData>, 10> audio;
742   mAudioQueue.GetElementsAfter(mData->mNextAudioTime, &audio);
743 
744   // This will happen everytime when the media sink switches from `AudioSink` to
745   // `DecodedStream`. If we don't insert the silence then the A/V will be out of
746   // sync.
747   RefPtr<AudioData> nextAudio = audio.IsEmpty() ? nullptr : audio[0];
748   if (RefPtr<AudioData> silence = CreateSilenceDataIfGapExists(nextAudio)) {
749     LOG_DS(LogLevel::Verbose, "Detect a gap in audio, insert silence=%u",
750            silence->Frames());
751     audio.InsertElementAt(0, silence);
752   }
753 
754   // Append data which hasn't been sent to audio track before.
755   mData->mAudioTrack->AppendData(audio, aPrincipalHandle);
756   for (uint32_t i = 0; i < audio.Length(); ++i) {
757     CheckIsDataAudible(audio[i]);
758     mData->mNextAudioTime = audio[i]->GetEndTime();
759     mData->mAudioFramesWritten += audio[i]->Frames();
760   }
761 
762   if (mAudioQueue.IsFinished() && !mData->mHaveSentFinishAudio) {
763     mData->mAudioTrack->NotifyEndOfStream();
764     mData->mHaveSentFinishAudio = true;
765   }
766 }
767 
CreateSilenceDataIfGapExists(RefPtr<AudioData> & aNextAudio)768 already_AddRefed<AudioData> DecodedStream::CreateSilenceDataIfGapExists(
769     RefPtr<AudioData>& aNextAudio) {
770   AssertOwnerThread();
771   if (!aNextAudio) {
772     return nullptr;
773   }
774   CheckedInt64 audioWrittenOffset =
775       mData->mAudioFramesWritten +
776       TimeUnitToFrames(*mStartTime, aNextAudio->mRate);
777   CheckedInt64 frameOffset =
778       TimeUnitToFrames(aNextAudio->mTime, aNextAudio->mRate);
779   if (audioWrittenOffset.value() >= frameOffset.value()) {
780     return nullptr;
781   }
782   // We've written less audio than our frame offset, return a silence data so we
783   // have enough audio to be at the correct offset for our current frames.
784   CheckedInt64 missingFrames = frameOffset - audioWrittenOffset;
785   AlignedAudioBuffer silenceBuffer(missingFrames.value() *
786                                    aNextAudio->mChannels);
787   if (!silenceBuffer) {
788     NS_WARNING("OOM in DecodedStream::CreateSilenceDataIfGapExists");
789     return nullptr;
790   }
791   auto duration = FramesToTimeUnit(missingFrames.value(), aNextAudio->mRate);
792   if (!duration.IsValid()) {
793     NS_WARNING("Int overflow in DecodedStream::CreateSilenceDataIfGapExists");
794     return nullptr;
795   }
796   RefPtr<AudioData> silenceData = new AudioData(
797       aNextAudio->mOffset, aNextAudio->mTime, std::move(silenceBuffer),
798       aNextAudio->mChannels, aNextAudio->mRate);
799   MOZ_DIAGNOSTIC_ASSERT(duration == silenceData->mDuration, "must be equal");
800   return silenceData.forget();
801 }
802 
CheckIsDataAudible(const AudioData * aData)803 void DecodedStream::CheckIsDataAudible(const AudioData* aData) {
804   MOZ_ASSERT(aData);
805 
806   mAudibilityMonitor->Process(aData);
807   bool isAudible = mAudibilityMonitor->RecentlyAudible();
808 
809   if (isAudible != mIsAudioDataAudible) {
810     mIsAudioDataAudible = isAudible;
811     mAudibleEvent.Notify(mIsAudioDataAudible);
812   }
813 }
814 
WriteVideoToSegment(layers::Image * aImage,const TimeUnit & aStart,const TimeUnit & aEnd,const gfx::IntSize & aIntrinsicSize,const TimeStamp & aTimeStamp,VideoSegment * aOutput,const PrincipalHandle & aPrincipalHandle,double aPlaybackRate)815 void DecodedStreamData::WriteVideoToSegment(
816     layers::Image* aImage, const TimeUnit& aStart, const TimeUnit& aEnd,
817     const gfx::IntSize& aIntrinsicSize, const TimeStamp& aTimeStamp,
818     VideoSegment* aOutput, const PrincipalHandle& aPrincipalHandle,
819     double aPlaybackRate) {
820   RefPtr<layers::Image> image = aImage;
821   auto end =
822       mVideoTrack->MicrosecondsToTrackTimeRoundDown(aEnd.ToMicroseconds());
823   auto start =
824       mVideoTrack->MicrosecondsToTrackTimeRoundDown(aStart.ToMicroseconds());
825   aOutput->AppendFrame(image.forget(), aIntrinsicSize, aPrincipalHandle, false,
826                        aTimeStamp);
827   // Extend this so we get accurate durations for all frames.
828   // Because this track is pushed, we need durations so the graph can track
829   // when playout of the track has finished.
830   MOZ_ASSERT(aPlaybackRate > 0);
831   aOutput->ExtendLastFrameBy(
832       static_cast<TrackTime>((float)(end - start) / aPlaybackRate));
833 
834   mLastVideoStartTime = Some(aStart);
835   mLastVideoEndTime = Some(aEnd);
836   mLastVideoTimeStamp = aTimeStamp;
837 }
838 
ZeroDurationAtLastChunk(VideoSegment & aInput)839 static bool ZeroDurationAtLastChunk(VideoSegment& aInput) {
840   // Get the last video frame's start time in VideoSegment aInput.
841   // If the start time is equal to the duration of aInput, means the last video
842   // frame's duration is zero.
843   TrackTime lastVideoStratTime;
844   aInput.GetLastFrame(&lastVideoStratTime);
845   return lastVideoStratTime == aInput.GetDuration();
846 }
847 
ResetAudio()848 void DecodedStream::ResetAudio() {
849   AssertOwnerThread();
850 
851   if (!mData) {
852     return;
853   }
854 
855   if (!mInfo.HasAudio()) {
856     return;
857   }
858 
859   TRACE("DecodedStream::ResetAudio");
860   mData->mAudioTrack->ClearFutureData();
861   if (const RefPtr<AudioData>& v = mAudioQueue.PeekFront()) {
862     mData->mNextAudioTime = v->mTime;
863     mData->mHaveSentFinishAudio = false;
864   }
865 }
866 
ResetVideo(const PrincipalHandle & aPrincipalHandle)867 void DecodedStream::ResetVideo(const PrincipalHandle& aPrincipalHandle) {
868   AssertOwnerThread();
869 
870   if (!mData) {
871     return;
872   }
873 
874   if (!mInfo.HasVideo()) {
875     return;
876   }
877 
878   TRACE("DecodedStream::ResetVideo");
879   TrackTime cleared = mData->mVideoTrack->ClearFutureData();
880   mData->mVideoTrackWritten -= cleared;
881   if (mData->mHaveSentFinishVideo && cleared > 0) {
882     mData->mHaveSentFinishVideo = false;
883     mData->mListener->EndVideoTrackAt(mData->mVideoTrack, TRACK_TIME_MAX);
884   }
885 
886   VideoSegment resetter;
887   TimeStamp currentTime;
888   TimeUnit currentPosition = GetPosition(&currentTime);
889 
890   // Giving direct consumers a frame (really *any* frame, so in this case:
891   // nullptr) at an earlier time than the previous, will signal to that consumer
892   // to discard any frames ahead in time of the new frame. To be honest, this is
893   // an ugly hack because the direct listeners of the MediaTrackGraph do not
894   // have an API that supports clearing the future frames. ImageContainer and
895   // VideoFrameContainer do though, and we will need to move to a similar API
896   // for video tracks as part of bug 1493618.
897   resetter.AppendFrame(nullptr, mData->mLastVideoImageDisplaySize,
898                        aPrincipalHandle, false, currentTime);
899   mData->mVideoTrack->AppendData(&resetter);
900 
901   // Consumer buffers have been reset. We now set the next time to the start
902   // time of the current frame, so that it can be displayed again on resuming.
903   if (RefPtr<VideoData> v = mVideoQueue.PeekFront()) {
904     mData->mLastVideoStartTime = Some(v->mTime - TimeUnit::FromMicroseconds(1));
905     mData->mLastVideoEndTime = Some(v->mTime);
906   } else {
907     // There was no current frame in the queue. We set the next time to the
908     // current time, so we at least don't resume starting in the future.
909     mData->mLastVideoStartTime =
910         Some(currentPosition - TimeUnit::FromMicroseconds(1));
911     mData->mLastVideoEndTime = Some(currentPosition);
912   }
913 
914   mData->mLastVideoTimeStamp = currentTime;
915 }
916 
SendVideo(const PrincipalHandle & aPrincipalHandle)917 void DecodedStream::SendVideo(const PrincipalHandle& aPrincipalHandle) {
918   AssertOwnerThread();
919 
920   if (!mInfo.HasVideo()) {
921     return;
922   }
923 
924   if (mData->mHaveSentFinishVideo) {
925     return;
926   }
927 
928   TRACE("DecodedStream::SendVideo");
929   VideoSegment output;
930   AutoTArray<RefPtr<VideoData>, 10> video;
931 
932   // It's OK to hold references to the VideoData because VideoData
933   // is ref-counted.
934   mVideoQueue.GetElementsAfter(
935       mData->mLastVideoStartTime.valueOr(mStartTime.ref()), &video);
936 
937   TimeStamp currentTime;
938   TimeUnit currentPosition = GetPosition(&currentTime);
939 
940   if (mData->mLastVideoTimeStamp.IsNull()) {
941     mData->mLastVideoTimeStamp = currentTime;
942   }
943 
944   for (uint32_t i = 0; i < video.Length(); ++i) {
945     VideoData* v = video[i];
946     TimeUnit lastStart = mData->mLastVideoStartTime.valueOr(
947         mStartTime.ref() - TimeUnit::FromMicroseconds(1));
948     TimeUnit lastEnd = mData->mLastVideoEndTime.valueOr(mStartTime.ref());
949 
950     if (lastEnd < v->mTime) {
951       // Write last video frame to catch up. mLastVideoImage can be null here
952       // which is fine, it just means there's no video.
953 
954       // TODO: |mLastVideoImage| should come from the last image rendered
955       // by the state machine. This will avoid the black frame when capture
956       // happens in the middle of playback (especially in th middle of a
957       // video frame). E.g. if we have a video frame that is 30 sec long
958       // and capture happens at 15 sec, we'll have to append a black frame
959       // that is 15 sec long.
960       TimeStamp t =
961           std::max(mData->mLastVideoTimeStamp,
962                    currentTime + (lastEnd - currentPosition).ToTimeDuration());
963       mData->WriteVideoToSegment(mData->mLastVideoImage, lastEnd, v->mTime,
964                                  mData->mLastVideoImageDisplaySize, t, &output,
965                                  aPrincipalHandle, mPlaybackRate);
966       lastEnd = v->mTime;
967     }
968 
969     if (lastStart < v->mTime) {
970       // This frame starts after the last frame's start. Note that this could be
971       // before the last frame's end time for some videos. This only matters for
972       // the track's lifetime in the MTG, as rendering is based on timestamps,
973       // aka frame start times.
974       TimeStamp t =
975           std::max(mData->mLastVideoTimeStamp,
976                    currentTime + (lastEnd - currentPosition).ToTimeDuration());
977       TimeUnit end = std::max(
978           v->GetEndTime(),
979           lastEnd + TimeUnit::FromMicroseconds(
980                         mData->mVideoTrack->TrackTimeToMicroseconds(1) + 1));
981       mData->mLastVideoImage = v->mImage;
982       mData->mLastVideoImageDisplaySize = v->mDisplay;
983       mData->WriteVideoToSegment(v->mImage, lastEnd, end, v->mDisplay, t,
984                                  &output, aPrincipalHandle, mPlaybackRate);
985     }
986   }
987 
988   // Check the output is not empty.
989   bool compensateEOS = false;
990   bool forceBlack = false;
991   if (output.GetLastFrame()) {
992     compensateEOS = ZeroDurationAtLastChunk(output);
993   }
994 
995   if (output.GetDuration() > 0) {
996     mData->mVideoTrackWritten += mData->mVideoTrack->AppendData(&output);
997   }
998 
999   if (mVideoQueue.IsFinished() && !mData->mHaveSentFinishVideo) {
1000     if (!mData->mLastVideoImage) {
1001       // We have video, but the video queue finished before we received any
1002       // frame. We insert a black frame to progress any consuming
1003       // HTMLMediaElement. This mirrors the behavior of VideoSink.
1004 
1005       // Force a frame - can be null
1006       compensateEOS = true;
1007       // Force frame to be black
1008       forceBlack = true;
1009       // Override the frame's size (will be 0x0 otherwise)
1010       mData->mLastVideoImageDisplaySize = mInfo.mVideo.mDisplay;
1011     }
1012     if (compensateEOS) {
1013       VideoSegment endSegment;
1014       // Calculate the deviation clock time from DecodedStream.
1015       // We round the nr of microseconds up, because WriteVideoToSegment
1016       // will round the conversion from microseconds to TrackTime down.
1017       auto deviation = TimeUnit::FromMicroseconds(
1018           mData->mVideoTrack->TrackTimeToMicroseconds(1) + 1);
1019       auto start = mData->mLastVideoEndTime.valueOr(mStartTime.ref());
1020       mData->WriteVideoToSegment(
1021           mData->mLastVideoImage, start, start + deviation,
1022           mData->mLastVideoImageDisplaySize,
1023           currentTime + (start + deviation - currentPosition).ToTimeDuration(),
1024           &endSegment, aPrincipalHandle, mPlaybackRate);
1025       MOZ_ASSERT(endSegment.GetDuration() > 0);
1026       if (forceBlack) {
1027         endSegment.ReplaceWithDisabled();
1028       }
1029       mData->mVideoTrackWritten += mData->mVideoTrack->AppendData(&endSegment);
1030     }
1031     mData->mListener->EndVideoTrackAt(mData->mVideoTrack,
1032                                       mData->mVideoTrackWritten);
1033     mData->mHaveSentFinishVideo = true;
1034   }
1035 }
1036 
SendData()1037 void DecodedStream::SendData() {
1038   AssertOwnerThread();
1039 
1040   // Not yet created on the main thread. MDSM will try again later.
1041   if (!mData) {
1042     return;
1043   }
1044 
1045   if (!mPlaying) {
1046     return;
1047   }
1048 
1049   LOG_DS(LogLevel::Verbose, "SendData()");
1050   SendAudio(mPrincipalHandle);
1051   SendVideo(mPrincipalHandle);
1052 }
1053 
GetEndTime(TrackType aType) const1054 TimeUnit DecodedStream::GetEndTime(TrackType aType) const {
1055   AssertOwnerThread();
1056   TRACE("DecodedStream::GetEndTime");
1057   if (aType == TrackInfo::kAudioTrack && mInfo.HasAudio() && mData) {
1058     auto t = mStartTime.ref() +
1059              FramesToTimeUnit(mData->mAudioFramesWritten, mInfo.mAudio.mRate);
1060     if (t.IsValid()) {
1061       return t;
1062     }
1063   } else if (aType == TrackInfo::kVideoTrack && mData) {
1064     return mData->mLastVideoEndTime.valueOr(mStartTime.ref());
1065   }
1066   return TimeUnit::Zero();
1067 }
1068 
GetPosition(TimeStamp * aTimeStamp) const1069 TimeUnit DecodedStream::GetPosition(TimeStamp* aTimeStamp) const {
1070   AssertOwnerThread();
1071   TRACE("DecodedStream::GetPosition");
1072   // This is only called after MDSM starts playback. So mStartTime is
1073   // guaranteed to be something.
1074   MOZ_ASSERT(mStartTime.isSome());
1075   if (aTimeStamp) {
1076     *aTimeStamp = TimeStamp::Now();
1077   }
1078   return mStartTime.ref() + mLastOutputTime;
1079 }
1080 
NotifyOutput(int64_t aTime)1081 void DecodedStream::NotifyOutput(int64_t aTime) {
1082   AssertOwnerThread();
1083   TimeUnit time = TimeUnit::FromMicroseconds(aTime);
1084   if (time == mLastOutputTime) {
1085     return;
1086   }
1087   MOZ_ASSERT(mLastOutputTime < time);
1088   mLastOutputTime = time;
1089   auto currentTime = GetPosition();
1090 
1091   if (profiler_thread_is_being_profiled_for_markers()) {
1092     nsPrintfCString markerString("OutputTime=%" PRId64,
1093                                  currentTime.ToMicroseconds());
1094     PLAYBACK_PROFILER_MARKER(markerString);
1095   }
1096   LOG_DS(LogLevel::Verbose, "time is now %" PRId64,
1097          currentTime.ToMicroseconds());
1098 
1099   // Remove audio samples that have been played by MTG from the queue.
1100   RefPtr<AudioData> a = mAudioQueue.PeekFront();
1101   for (; a && a->GetEndTime() <= currentTime;) {
1102     LOG_DS(LogLevel::Debug, "Dropping audio [%" PRId64 ",%" PRId64 "]",
1103            a->mTime.ToMicroseconds(), a->GetEndTime().ToMicroseconds());
1104     RefPtr<AudioData> releaseMe = mAudioQueue.PopFront();
1105     a = mAudioQueue.PeekFront();
1106   }
1107 }
1108 
PlayingChanged()1109 void DecodedStream::PlayingChanged() {
1110   AssertOwnerThread();
1111   TRACE("DecodedStream::PlayingChanged");
1112 
1113   if (!mPlaying) {
1114     // On seek or pause we discard future frames.
1115     ResetVideo(mPrincipalHandle);
1116     ResetAudio();
1117   }
1118 }
1119 
ConnectListener()1120 void DecodedStream::ConnectListener() {
1121   AssertOwnerThread();
1122 
1123   mAudioPushListener = mAudioQueue.PushEvent().Connect(
1124       mOwnerThread, this, &DecodedStream::SendData);
1125   mAudioFinishListener = mAudioQueue.FinishEvent().Connect(
1126       mOwnerThread, this, &DecodedStream::SendData);
1127   mVideoPushListener = mVideoQueue.PushEvent().Connect(
1128       mOwnerThread, this, &DecodedStream::SendData);
1129   mVideoFinishListener = mVideoQueue.FinishEvent().Connect(
1130       mOwnerThread, this, &DecodedStream::SendData);
1131   mWatchManager.Watch(mPlaying, &DecodedStream::SendData);
1132 }
1133 
DisconnectListener()1134 void DecodedStream::DisconnectListener() {
1135   AssertOwnerThread();
1136 
1137   mAudioPushListener.Disconnect();
1138   mVideoPushListener.Disconnect();
1139   mAudioFinishListener.Disconnect();
1140   mVideoFinishListener.Disconnect();
1141   mWatchManager.Unwatch(mPlaying, &DecodedStream::SendData);
1142 }
1143 
GetDebugInfo(dom::MediaSinkDebugInfo & aInfo)1144 void DecodedStream::GetDebugInfo(dom::MediaSinkDebugInfo& aInfo) {
1145   AssertOwnerThread();
1146   int64_t startTime = mStartTime.isSome() ? mStartTime->ToMicroseconds() : -1;
1147   aInfo.mDecodedStream.mInstance =
1148       NS_ConvertUTF8toUTF16(nsPrintfCString("%p", this));
1149   aInfo.mDecodedStream.mStartTime = startTime;
1150   aInfo.mDecodedStream.mLastOutputTime = mLastOutputTime.ToMicroseconds();
1151   aInfo.mDecodedStream.mPlaying = mPlaying.Ref();
1152   auto lastAudio = mAudioQueue.PeekBack();
1153   aInfo.mDecodedStream.mLastAudio =
1154       lastAudio ? lastAudio->GetEndTime().ToMicroseconds() : -1;
1155   aInfo.mDecodedStream.mAudioQueueFinished = mAudioQueue.IsFinished();
1156   aInfo.mDecodedStream.mAudioQueueSize = mAudioQueue.GetSize();
1157   if (mData) {
1158     mData->GetDebugInfo(aInfo.mDecodedStream.mData);
1159   }
1160 }
1161 
1162 #undef LOG_DS
1163 
1164 }  // namespace mozilla
1165