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 #include "MediaSourceDecoder.h"
7 
8 #include "mozilla/Logging.h"
9 #include "MediaDecoderStateMachine.h"
10 #include "MediaShutdownManager.h"
11 #include "MediaSource.h"
12 #include "MediaSourceDemuxer.h"
13 #include "MediaSourceUtils.h"
14 #include "SourceBuffer.h"
15 #include "SourceBufferList.h"
16 #include "VideoUtils.h"
17 #include <algorithm>
18 
19 extern mozilla::LogModule* GetMediaSourceLog();
20 
21 #define MSE_DEBUG(arg, ...)                                              \
22   DDMOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, "::%s: " arg, \
23             __func__, ##__VA_ARGS__)
24 #define MSE_DEBUGV(arg, ...)                                               \
25   DDMOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Verbose, "::%s: " arg, \
26             __func__, ##__VA_ARGS__)
27 
28 using namespace mozilla::media;
29 
30 namespace mozilla {
31 
MediaSourceDecoder(MediaDecoderInit & aInit)32 MediaSourceDecoder::MediaSourceDecoder(MediaDecoderInit& aInit)
33     : MediaDecoder(aInit), mMediaSource(nullptr), mEnded(false) {
34   mExplicitDuration.emplace(UnspecifiedNaN<double>());
35 }
36 
CreateStateMachine()37 MediaDecoderStateMachine* MediaSourceDecoder::CreateStateMachine() {
38   MOZ_ASSERT(NS_IsMainThread());
39   mDemuxer = new MediaSourceDemuxer(AbstractMainThread());
40   MediaFormatReaderInit init;
41   init.mVideoFrameContainer = GetVideoFrameContainer();
42   init.mKnowsCompositor = GetCompositor();
43   init.mCrashHelper = GetOwner()->CreateGMPCrashHelper();
44   init.mFrameStats = mFrameStats;
45   init.mMediaDecoderOwnerID = mOwner;
46   mReader = new MediaFormatReader(init, mDemuxer);
47   return new MediaDecoderStateMachine(this, mReader);
48 }
49 
Load(nsIPrincipal * aPrincipal)50 nsresult MediaSourceDecoder::Load(nsIPrincipal* aPrincipal) {
51   MOZ_ASSERT(NS_IsMainThread());
52   MOZ_ASSERT(!GetStateMachine());
53 
54   mPrincipal = aPrincipal;
55 
56   nsresult rv = MediaShutdownManager::Instance().Register(this);
57   if (NS_WARN_IF(NS_FAILED(rv))) {
58     return rv;
59   }
60 
61   SetStateMachine(CreateStateMachine());
62   if (!GetStateMachine()) {
63     NS_WARNING("Failed to create state machine!");
64     return NS_ERROR_FAILURE;
65   }
66 
67   rv = GetStateMachine()->Init(this);
68   NS_ENSURE_SUCCESS(rv, rv);
69 
70   GetStateMachine()->DispatchIsLiveStream(!mEnded);
71   SetStateMachineParameters();
72   return NS_OK;
73 }
74 
GetSeekable()75 media::TimeIntervals MediaSourceDecoder::GetSeekable() {
76   MOZ_ASSERT(NS_IsMainThread());
77   if (!mMediaSource) {
78     NS_WARNING("MediaSource element isn't attached");
79     return media::TimeIntervals::Invalid();
80   }
81 
82   media::TimeIntervals seekable;
83   double duration = mMediaSource->Duration();
84   if (IsNaN(duration)) {
85     // Return empty range.
86   } else if (duration > 0 && mozilla::IsInfinite(duration)) {
87     media::TimeIntervals buffered = GetBuffered();
88 
89     // 1. If live seekable range is not empty:
90     if (mMediaSource->HasLiveSeekableRange()) {
91       // 1. Let union ranges be the union of live seekable range and the
92       // HTMLMediaElement.buffered attribute.
93       media::TimeIntervals unionRanges =
94           buffered + mMediaSource->LiveSeekableRange();
95       // 2. Return a single range with a start time equal to the earliest start
96       // time in union ranges and an end time equal to the highest end time in
97       // union ranges and abort these steps.
98       seekable +=
99           media::TimeInterval(unionRanges.GetStart(), unionRanges.GetEnd());
100       return seekable;
101     }
102 
103     if (!buffered.IsEmpty()) {
104       seekable += media::TimeInterval(TimeUnit::Zero(), buffered.GetEnd());
105     }
106   } else {
107     seekable +=
108         media::TimeInterval(TimeUnit::Zero(), TimeUnit::FromSeconds(duration));
109   }
110   MSE_DEBUG("ranges=%s", DumpTimeRanges(seekable).get());
111   return seekable;
112 }
113 
GetBuffered()114 media::TimeIntervals MediaSourceDecoder::GetBuffered() {
115   MOZ_ASSERT(NS_IsMainThread());
116 
117   if (!mMediaSource) {
118     NS_WARNING("MediaSource element isn't attached");
119     return media::TimeIntervals::Invalid();
120   }
121   dom::SourceBufferList* sourceBuffers = mMediaSource->ActiveSourceBuffers();
122   if (!sourceBuffers) {
123     // Media source object is shutting down.
124     return TimeIntervals();
125   }
126   TimeUnit highestEndTime;
127   nsTArray<media::TimeIntervals> activeRanges;
128   media::TimeIntervals buffered;
129 
130   for (uint32_t i = 0; i < sourceBuffers->Length(); i++) {
131     bool found;
132     dom::SourceBuffer* sb = sourceBuffers->IndexedGetter(i, found);
133     MOZ_ASSERT(found);
134 
135     activeRanges.AppendElement(sb->GetTimeIntervals());
136     highestEndTime =
137         std::max(highestEndTime, activeRanges.LastElement().GetEnd());
138   }
139 
140   buffered += media::TimeInterval(TimeUnit::Zero(), highestEndTime);
141 
142   for (auto& range : activeRanges) {
143     if (mEnded && !range.IsEmpty()) {
144       // Set the end time on the last range to highestEndTime by adding a
145       // new range spanning the current end time to highestEndTime, which
146       // Normalize() will then merge with the old last range.
147       range += media::TimeInterval(range.GetEnd(), highestEndTime);
148     }
149     buffered.Intersection(range);
150   }
151 
152   MSE_DEBUG("ranges=%s", DumpTimeRanges(buffered).get());
153   return buffered;
154 }
155 
Shutdown()156 void MediaSourceDecoder::Shutdown() {
157   MOZ_ASSERT(NS_IsMainThread());
158   MSE_DEBUG("Shutdown");
159   // Detach first so that TrackBuffers are unused on the main thread when
160   // shut down on the decode task queue.
161   if (mMediaSource) {
162     mMediaSource->Detach();
163   }
164   mDemuxer = nullptr;
165 
166   MediaDecoder::Shutdown();
167 }
168 
AttachMediaSource(dom::MediaSource * aMediaSource)169 void MediaSourceDecoder::AttachMediaSource(dom::MediaSource* aMediaSource) {
170   MOZ_ASSERT(!mMediaSource && !GetStateMachine() && NS_IsMainThread());
171   mMediaSource = aMediaSource;
172   DDLINKCHILD("mediasource", aMediaSource);
173 }
174 
DetachMediaSource()175 void MediaSourceDecoder::DetachMediaSource() {
176   MOZ_ASSERT(mMediaSource && NS_IsMainThread());
177   DDUNLINKCHILD(mMediaSource);
178   mMediaSource = nullptr;
179 }
180 
Ended(bool aEnded)181 void MediaSourceDecoder::Ended(bool aEnded) {
182   MOZ_ASSERT(NS_IsMainThread());
183   if (aEnded) {
184     // We want the MediaSourceReader to refresh its buffered range as it may
185     // have been modified (end lined up).
186     NotifyDataArrived();
187   }
188   mEnded = aEnded;
189   GetStateMachine()->DispatchIsLiveStream(!mEnded);
190 }
191 
AddSizeOfResources(ResourceSizes * aSizes)192 void MediaSourceDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
193   MOZ_ASSERT(NS_IsMainThread());
194   if (GetDemuxer()) {
195     GetDemuxer()->AddSizeOfResources(aSizes);
196   }
197 }
198 
SetInitialDuration(int64_t aDuration)199 void MediaSourceDecoder::SetInitialDuration(int64_t aDuration) {
200   MOZ_ASSERT(NS_IsMainThread());
201   // Only use the decoded duration if one wasn't already
202   // set.
203   if (!mMediaSource || !IsNaN(ExplicitDuration())) {
204     return;
205   }
206   double duration = aDuration;
207   // A duration of -1 is +Infinity.
208   if (aDuration >= 0) {
209     duration /= USECS_PER_S;
210   }
211   SetMediaSourceDuration(duration);
212 }
213 
SetMediaSourceDuration(double aDuration)214 void MediaSourceDecoder::SetMediaSourceDuration(double aDuration) {
215   MOZ_ASSERT(NS_IsMainThread());
216   MOZ_ASSERT(!IsShutdown());
217   if (aDuration >= 0) {
218     int64_t checkedDuration;
219     if (NS_FAILED(SecondsToUsecs(aDuration, checkedDuration))) {
220       // INT64_MAX is used as infinity by the state machine.
221       // We want a very bigger number, but not infinity.
222       checkedDuration = INT64_MAX - 1;
223     }
224     SetExplicitDuration(aDuration);
225   } else {
226     SetExplicitDuration(PositiveInfinity<double>());
227   }
228 }
229 
GetDebugInfo(dom::MediaSourceDecoderDebugInfo & aInfo)230 void MediaSourceDecoder::GetDebugInfo(dom::MediaSourceDecoderDebugInfo& aInfo) {
231   if (mReader && mDemuxer) {
232     mReader->GetDebugInfo(aInfo.mReader);
233     mDemuxer->GetDebugInfo(aInfo.mDemuxer);
234   }
235 }
236 
GetDuration()237 double MediaSourceDecoder::GetDuration() {
238   MOZ_ASSERT(NS_IsMainThread());
239   return ExplicitDuration();
240 }
241 
242 MediaDecoderOwner::NextFrameStatus
NextFrameBufferedStatus()243 MediaSourceDecoder::NextFrameBufferedStatus() {
244   MOZ_ASSERT(NS_IsMainThread());
245 
246   if (!mMediaSource ||
247       mMediaSource->ReadyState() == dom::MediaSourceReadyState::Closed) {
248     return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
249   }
250 
251   // Next frame hasn't been decoded yet.
252   // Use the buffered range to consider if we have the next frame available.
253   auto currentPosition = CurrentPosition();
254   TimeIntervals buffered = GetBuffered();
255   buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
256   TimeInterval interval(
257       currentPosition, currentPosition + DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED);
258   return buffered.ContainsWithStrictEnd(ClampIntervalToEnd(interval))
259              ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
260              : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
261 }
262 
CanPlayThroughImpl()263 bool MediaSourceDecoder::CanPlayThroughImpl() {
264   MOZ_ASSERT(NS_IsMainThread());
265 
266   if (NextFrameBufferedStatus() == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE) {
267     return false;
268   }
269 
270   if (IsNaN(mMediaSource->Duration())) {
271     // Don't have any data yet.
272     return false;
273   }
274   TimeUnit duration = TimeUnit::FromSeconds(mMediaSource->Duration());
275   auto currentPosition = CurrentPosition();
276   if (duration <= currentPosition) {
277     return true;
278   }
279   // If we have data up to the mediasource's duration or 3s ahead, we can
280   // assume that we can play without interruption.
281   TimeIntervals buffered = GetBuffered();
282   buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2);
283   TimeUnit timeAhead =
284       std::min(duration, currentPosition + TimeUnit::FromSeconds(3));
285   TimeInterval interval(currentPosition, timeAhead);
286   return buffered.ContainsWithStrictEnd(ClampIntervalToEnd(interval));
287 }
288 
ClampIntervalToEnd(const TimeInterval & aInterval)289 TimeInterval MediaSourceDecoder::ClampIntervalToEnd(
290     const TimeInterval& aInterval) {
291   MOZ_ASSERT(NS_IsMainThread());
292 
293   if (!mEnded) {
294     return aInterval;
295   }
296   TimeUnit duration = TimeUnit::FromSeconds(GetDuration());
297   if (duration < aInterval.mStart) {
298     return aInterval;
299   }
300   return TimeInterval(aInterval.mStart, std::min(aInterval.mEnd, duration),
301                       aInterval.mFuzz);
302 }
303 
NotifyInitDataArrived()304 void MediaSourceDecoder::NotifyInitDataArrived() {
305   MOZ_ASSERT(NS_IsMainThread());
306 
307   if (mDemuxer) {
308     mDemuxer->NotifyInitDataArrived();
309   }
310 }
311 
NotifyDataArrived()312 void MediaSourceDecoder::NotifyDataArrived() {
313   MOZ_ASSERT(NS_IsMainThread());
314   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
315   NotifyReaderDataArrived();
316   GetOwner()->DownloadProgressed();
317 }
318 
GetCurrentPrincipal()319 already_AddRefed<nsIPrincipal> MediaSourceDecoder::GetCurrentPrincipal() {
320   MOZ_ASSERT(NS_IsMainThread());
321   return do_AddRef(mPrincipal);
322 }
323 
HadCrossOriginRedirects()324 bool MediaSourceDecoder::HadCrossOriginRedirects() {
325   MOZ_ASSERT(NS_IsMainThread());
326   return false;
327 }
328 
329 #undef MSE_DEBUG
330 #undef MSE_DEBUGV
331 
332 }  // namespace mozilla
333