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(¤tTime);
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(¤tTime);
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