Coverage Report

Created: 2018-09-25 14:53

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