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