Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/mediasource/SourceBuffer.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
7
#include "SourceBuffer.h"
8
9
#include "AsyncEventRunner.h"
10
#include "MediaData.h"
11
#include "MediaSourceDemuxer.h"
12
#include "MediaSourceUtils.h"
13
#include "mozilla/ErrorResult.h"
14
#include "mozilla/FloatingPoint.h"
15
#include "mozilla/Preferences.h"
16
#include "mozilla/dom/MediaSourceBinding.h"
17
#include "mozilla/dom/TimeRanges.h"
18
#include "nsError.h"
19
#include "nsIEventTarget.h"
20
#include "nsIRunnable.h"
21
#include "nsThreadUtils.h"
22
#include "mozilla/Logging.h"
23
#include <time.h>
24
#include "TimeUnits.h"
25
26
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
27
// GetTickCount() and conflicts with MediaDecoder::GetCurrentTime implementation.
28
#ifdef GetCurrentTime
29
#undef GetCurrentTime
30
#endif
31
32
struct JSContext;
33
class JSObject;
34
35
extern mozilla::LogModule* GetMediaSourceLog();
36
extern mozilla::LogModule* GetMediaSourceAPILog();
37
38
#define MSE_DEBUG(arg, ...)                                                    \
39
0
  DDMOZ_LOG(GetMediaSourceLog(),                                               \
40
0
            mozilla::LogLevel::Debug,                                          \
41
0
            "(%s)::%s: " arg,                                                  \
42
0
            mType.OriginalString().Data(),                                     \
43
0
            __func__,                                                          \
44
0
            ##__VA_ARGS__)
45
#define MSE_DEBUGV(arg, ...)                                                   \
46
0
  DDMOZ_LOG(GetMediaSourceLog(),                                               \
47
0
            mozilla::LogLevel::Verbose,                                        \
48
0
            "(%s)::%s: " arg,                                                  \
49
0
            mType.OriginalString().Data(),                                     \
50
0
            __func__,                                                          \
51
0
            ##__VA_ARGS__)
52
#define MSE_API(arg, ...)                                                      \
53
0
  DDMOZ_LOG(GetMediaSourceAPILog(),                                            \
54
0
            mozilla::LogLevel::Debug,                                          \
55
0
            "(%s)::%s: " arg,                                                  \
56
0
            mType.OriginalString().Data(),                                     \
57
0
            __func__,                                                          \
58
0
            ##__VA_ARGS__)
59
60
namespace mozilla {
61
62
using media::TimeUnit;
63
typedef SourceBufferAttributes::AppendState AppendState;
64
65
namespace dom {
66
67
void
68
SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
69
0
{
70
0
  MOZ_ASSERT(NS_IsMainThread());
71
0
  MSE_API("SetMode(aMode=%" PRIu32 ")", static_cast<uint32_t>(aMode));
72
0
  if (!IsAttached() || mUpdating) {
73
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
74
0
    return;
75
0
  }
76
0
  if (mCurrentAttributes.mGenerateTimestamps &&
77
0
      aMode == SourceBufferAppendMode::Segments) {
78
0
    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
79
0
    return;
80
0
  }
81
0
  MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
82
0
  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
83
0
    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
84
0
  }
85
0
  if (mCurrentAttributes.GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
86
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
87
0
    return;
88
0
  }
89
0
90
0
  if (aMode == SourceBufferAppendMode::Sequence) {
91
0
    // Will set GroupStartTimestamp to GroupEndTimestamp.
92
0
    mCurrentAttributes.RestartGroupStartTimestamp();
93
0
  }
94
0
95
0
  mCurrentAttributes.SetAppendMode(aMode);
96
0
}
97
98
void
99
SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
100
0
{
101
0
  MOZ_ASSERT(NS_IsMainThread());
102
0
  MSE_API("SetTimestampOffset(aTimestampOffset=%f)", aTimestampOffset);
103
0
  if (!IsAttached() || mUpdating) {
104
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
105
0
    return;
106
0
  }
107
0
  MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
108
0
  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
109
0
    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
110
0
  }
111
0
  if (mCurrentAttributes.GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
112
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
113
0
    return;
114
0
  }
115
0
  mCurrentAttributes.SetApparentTimestampOffset(aTimestampOffset);
116
0
  if (mCurrentAttributes.GetAppendMode() == SourceBufferAppendMode::Sequence) {
117
0
    mCurrentAttributes.SetGroupStartTimestamp(mCurrentAttributes.GetTimestampOffset());
118
0
  }
119
0
}
120
121
TimeRanges*
122
SourceBuffer::GetBuffered(ErrorResult& aRv)
123
0
{
124
0
  MOZ_ASSERT(NS_IsMainThread());
125
0
  // http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
126
0
  // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an InvalidStateError exception and abort these steps.
127
0
  if (!IsAttached()) {
128
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
129
0
    return nullptr;
130
0
  }
131
0
  bool rangeChanged = true;
132
0
  media::TimeIntervals intersection = mTrackBuffersManager->Buffered();
133
0
  MSE_DEBUGV("intersection=%s", DumpTimeRanges(intersection).get());
134
0
  if (mBuffered) {
135
0
    media::TimeIntervals currentValue(mBuffered->ToTimeIntervals());
136
0
    rangeChanged = (intersection != currentValue);
137
0
    MSE_DEBUGV("currentValue=%s", DumpTimeRanges(currentValue).get());
138
0
  }
139
0
  // 5. If intersection ranges does not contain the exact same range information as the current value of this attribute, then update the current value of this attribute to intersection ranges.
140
0
  if (rangeChanged) {
141
0
    mBuffered = new TimeRanges(ToSupports(this), intersection);
142
0
  }
143
0
  // 6. Return the current value of this attribute.
144
0
  return mBuffered;
145
0
}
146
147
media::TimeIntervals
148
SourceBuffer::GetTimeIntervals()
149
0
{
150
0
  return mTrackBuffersManager->Buffered();
151
0
}
152
153
void
154
SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
155
0
{
156
0
  MOZ_ASSERT(NS_IsMainThread());
157
0
  MSE_API("SetAppendWindowStart(aAppendWindowStart=%f)", aAppendWindowStart);
158
0
  DDLOG(DDLogCategory::API, "SetAppendWindowStart", aAppendWindowStart);
159
0
  if (!IsAttached() || mUpdating) {
160
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
161
0
    return;
162
0
  }
163
0
  if (aAppendWindowStart < 0 ||
164
0
      aAppendWindowStart >= mCurrentAttributes.GetAppendWindowEnd()) {
165
0
    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
166
0
    return;
167
0
  }
168
0
  mCurrentAttributes.SetAppendWindowStart(aAppendWindowStart);
169
0
}
170
171
void
172
SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
173
0
{
174
0
  MOZ_ASSERT(NS_IsMainThread());
175
0
  MSE_API("SetAppendWindowEnd(aAppendWindowEnd=%f)", aAppendWindowEnd);
176
0
  DDLOG(DDLogCategory::API, "SetAppendWindowEnd", aAppendWindowEnd);
177
0
  if (!IsAttached() || mUpdating) {
178
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
179
0
    return;
180
0
  }
181
0
  if (IsNaN(aAppendWindowEnd) ||
182
0
      aAppendWindowEnd <= mCurrentAttributes.GetAppendWindowStart()) {
183
0
    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
184
0
    return;
185
0
  }
186
0
  mCurrentAttributes.SetAppendWindowEnd(aAppendWindowEnd);
187
0
}
188
189
void
190
SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
191
0
{
192
0
  MOZ_ASSERT(NS_IsMainThread());
193
0
  MSE_API("AppendBuffer(ArrayBuffer)");
194
0
  aData.ComputeLengthAndData();
195
0
  DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
196
0
  AppendData(aData.Data(), aData.Length(), aRv);
197
0
}
198
199
void
200
SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
201
0
{
202
0
  MOZ_ASSERT(NS_IsMainThread());
203
0
  MSE_API("AppendBuffer(ArrayBufferView)");
204
0
  aData.ComputeLengthAndData();
205
0
  DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
206
0
  AppendData(aData.Data(), aData.Length(), aRv);
207
0
}
208
209
already_AddRefed<Promise>
210
SourceBuffer::AppendBufferAsync(const ArrayBuffer& aData,
211
                                ErrorResult& aRv)
212
0
{
213
0
  MOZ_ASSERT(NS_IsMainThread());
214
0
215
0
  MSE_API("AppendBufferAsync(ArrayBuffer)");
216
0
  aData.ComputeLengthAndData();
217
0
  DDLOG(DDLogCategory::API, "AppendBufferAsync", aData.Length());
218
0
219
0
  return AppendDataAsync(aData.Data(), aData.Length(), aRv);
220
0
}
221
222
already_AddRefed<Promise>
223
SourceBuffer::AppendBufferAsync(const ArrayBufferView& aData,
224
                                ErrorResult& aRv)
225
0
{
226
0
  MOZ_ASSERT(NS_IsMainThread());
227
0
228
0
  MSE_API("AppendBufferAsync(ArrayBufferView)");
229
0
  aData.ComputeLengthAndData();
230
0
  DDLOG(DDLogCategory::API, "AppendBufferAsync", aData.Length());
231
0
232
0
  return AppendDataAsync(aData.Data(), aData.Length(), aRv);
233
0
}
234
235
void
236
SourceBuffer::Abort(ErrorResult& aRv)
237
0
{
238
0
  MOZ_ASSERT(NS_IsMainThread());
239
0
  MSE_API("Abort()");
240
0
  if (!IsAttached()) {
241
0
    DDLOG(DDLogCategory::API, "Abort", NS_ERROR_DOM_INVALID_STATE_ERR);
242
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
243
0
    return;
244
0
  }
245
0
  if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
246
0
    DDLOG(DDLogCategory::API, "Abort", NS_ERROR_DOM_INVALID_STATE_ERR);
247
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
248
0
    return;
249
0
  }
250
0
  if (mPendingRemoval.Exists()) {
251
0
    DDLOG(DDLogCategory::API, "Abort", NS_ERROR_DOM_INVALID_STATE_ERR);
252
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
253
0
    return;
254
0
  }
255
0
  DDLOG(DDLogCategory::API, "Abort", NS_OK);
256
0
  AbortBufferAppend();
257
0
  ResetParserState();
258
0
  mCurrentAttributes.SetAppendWindowStart(0);
259
0
  mCurrentAttributes.SetAppendWindowEnd(PositiveInfinity<double>());
260
0
}
261
262
void
263
SourceBuffer::AbortBufferAppend()
264
0
{
265
0
  if (mUpdating) {
266
0
    mCompletionPromise.DisconnectIfExists();
267
0
    if (mPendingAppend.Exists()) {
268
0
      mPendingAppend.Disconnect();
269
0
      mTrackBuffersManager->AbortAppendData();
270
0
    }
271
0
    AbortUpdating();
272
0
  }
273
0
}
274
275
void
276
SourceBuffer::ResetParserState()
277
0
{
278
0
  mTrackBuffersManager->ResetParserState(mCurrentAttributes);
279
0
}
280
281
void
282
SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
283
0
{
284
0
  MOZ_ASSERT(NS_IsMainThread());
285
0
  MSE_API("Remove(aStart=%f, aEnd=%f)", aStart, aEnd);
286
0
  DDLOG(DDLogCategory::API, "Remove-from", aStart);
287
0
  DDLOG(DDLogCategory::API, "Remove-until", aEnd);
288
0
289
0
  PrepareRemove(aStart, aEnd, aRv);
290
0
  if (aRv.Failed()) {
291
0
    return;
292
0
  }
293
0
  RangeRemoval(aStart, aEnd);
294
0
}
295
296
already_AddRefed<Promise>
297
SourceBuffer::RemoveAsync(double aStart, double aEnd, ErrorResult& aRv)
298
0
{
299
0
  MOZ_ASSERT(NS_IsMainThread());
300
0
  MSE_API("RemoveAsync(aStart=%f, aEnd=%f)", aStart, aEnd);
301
0
  DDLOG(DDLogCategory::API, "Remove-from", aStart);
302
0
  DDLOG(DDLogCategory::API, "Remove-until", aEnd);
303
0
304
0
  if (!IsAttached()) {
305
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
306
0
    return nullptr;
307
0
  }
308
0
309
0
  nsCOMPtr<nsIGlobalObject> parentObject =
310
0
    do_QueryInterface(mMediaSource->GetParentObject());
311
0
  if (!parentObject) {
312
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
313
0
    return nullptr;
314
0
  }
315
0
316
0
  RefPtr<Promise> promise = Promise::Create(parentObject, aRv);
317
0
  if (aRv.Failed()) {
318
0
    return nullptr;
319
0
  }
320
0
321
0
  PrepareRemove(aStart, aEnd, aRv);
322
0
323
0
  if (aRv.Failed()) {
324
0
    // The bindings will automatically return a rejected promise.
325
0
    return nullptr;
326
0
  }
327
0
  MOZ_ASSERT(!mDOMPromise, "Can't have a pending operation going");
328
0
  mDOMPromise = promise;
329
0
  RangeRemoval(aStart, aEnd);
330
0
331
0
  return promise.forget();
332
0
}
333
334
void
335
SourceBuffer::PrepareRemove(double aStart, double aEnd, ErrorResult& aRv)
336
0
{
337
0
  if (!IsAttached()) {
338
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
339
0
    return;
340
0
  }
341
0
  if (mUpdating) {
342
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
343
0
    return;
344
0
  }
345
0
  if (IsNaN(mMediaSource->Duration()) ||
346
0
      aStart < 0 || aStart > mMediaSource->Duration() ||
347
0
      aEnd <= aStart || IsNaN(aEnd)) {
348
0
    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
349
0
    return;
350
0
  }
351
0
  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
352
0
    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
353
0
  }
354
0
}
355
356
void
357
SourceBuffer::RangeRemoval(double aStart, double aEnd)
358
0
{
359
0
  StartUpdating();
360
0
361
0
  RefPtr<SourceBuffer> self = this;
362
0
    mTrackBuffersManager->RangeRemoval(TimeUnit::FromSeconds(aStart),
363
0
                                       TimeUnit::FromSeconds(aEnd))
364
0
      ->Then(mAbstractMainThread, __func__,
365
0
             [self] (bool) {
366
0
               self->mPendingRemoval.Complete();
367
0
               self->StopUpdating();
368
0
             },
369
0
             []() { MOZ_ASSERT(false); })
370
0
      ->Track(mPendingRemoval);
371
0
}
372
373
void
374
SourceBuffer::ChangeType(const nsAString& aType, ErrorResult& aRv)
375
0
{
376
0
  MOZ_ASSERT(NS_IsMainThread());
377
0
378
0
  // 1. If type is an empty string then throw a TypeError exception and abort
379
0
  //    these steps.
380
0
  if (aType.IsEmpty()) {
381
0
    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
382
0
    return;
383
0
  }
384
0
385
0
  // 2. If this object has been removed from the sourceBuffers attribute of the
386
0
  //    parent media source , then throw an InvalidStateError exception and
387
0
  //    abort these steps.
388
0
  // 3. If the updating attribute equals true, then throw an InvalidStateError
389
0
  //    exception and abort these steps.
390
0
  if (!IsAttached() || mUpdating) {
391
0
    DDLOG(DDLogCategory::API, "ChangeType", NS_ERROR_DOM_INVALID_STATE_ERR);
392
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
393
0
    return;
394
0
  }
395
0
396
0
  // 4. If type contains a MIME type that is not supported or contains a MIME
397
0
  //    type that is not supported with the types specified (currently or
398
0
  //    previously) of SourceBuffer objects in the sourceBuffers attribute of
399
0
  //    the parent media source , then throw a NotSupportedError exception and
400
0
  //    abort these steps.
401
0
  DecoderDoctorDiagnostics diagnostics;
402
0
  nsresult rv = MediaSource::IsTypeSupported(aType, &diagnostics);
403
0
  diagnostics.StoreFormatDiagnostics(mMediaSource->GetOwner()
404
0
                                     ? mMediaSource->GetOwner()->GetExtantDoc()
405
0
                                     : nullptr,
406
0
                                     aType, NS_SUCCEEDED(rv), __func__);
407
0
  MSE_API("ChangeType(aType=%s)%s",
408
0
          NS_ConvertUTF16toUTF8(aType).get(),
409
0
          rv == NS_OK ? "" : " [not supported]");
410
0
  if (NS_FAILED(rv)) {
411
0
    DDLOG(DDLogCategory::API, "ChangeType", rv);
412
0
    aRv.Throw(rv);
413
0
    return;
414
0
  }
415
0
416
0
  // 5. If the readyState attribute of the parent media source is in the "ended"
417
0
  //    state then run the following steps:
418
0
  //    1. Set the readyState attribute of the parent media source to "open"
419
0
  //    2.   Queue a task to fire a simple event named sourceopen at the parent
420
0
  //         media source .
421
0
  MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
422
0
  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
423
0
    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
424
0
  }
425
0
  Maybe<MediaContainerType> containerType = MakeMediaContainerType(aType);
426
0
  MOZ_ASSERT(containerType);
427
0
  mType = *containerType;
428
0
  // 6. Run the reset parser state algorithm .
429
0
  ResetParserState();
430
0
431
0
  // 7. Update the generate timestamps flag on this SourceBuffer object to the
432
0
  //    value in the "Generate Timestamps Flag" column of the byte stream format
433
0
  //    registry [ MSE-REGISTRY ] entry that is associated with type .
434
0
  if (mType.Type() == MEDIAMIMETYPE("audio/mpeg") ||
435
0
      mType.Type() == MEDIAMIMETYPE("audio/aac")) {
436
0
    mCurrentAttributes.mGenerateTimestamps = true;
437
0
    // 8. If the generate timestamps flag equals true:
438
0
    //    Set the mode attribute on this SourceBuffer object to "sequence" ,
439
0
    //    including running the associated steps for that attribute being set.
440
0
    ErrorResult dummy;
441
0
    SetMode(SourceBufferAppendMode::Sequence, dummy);
442
0
  } else {
443
0
    mCurrentAttributes.mGenerateTimestamps = false;
444
0
    //    Otherwise: Keep the previous value of the mode attribute on this
445
0
    //    SourceBuffer object, without running any associated steps for that
446
0
    //    attribute being set.
447
0
  }
448
0
449
0
  // 9. Set pending initialization segment for changeType flag to true.
450
0
  mTrackBuffersManager->ChangeType(mType);
451
0
}
452
453
void
454
SourceBuffer::Detach()
455
0
{
456
0
  MOZ_ASSERT(NS_IsMainThread());
457
0
  MSE_DEBUG("Detach");
458
0
  if (!mMediaSource) {
459
0
    MSE_DEBUG("Already detached");
460
0
    return;
461
0
  }
462
0
  AbortBufferAppend();
463
0
  if (mTrackBuffersManager) {
464
0
    mMediaSource->GetDecoder()->GetDemuxer()->DetachSourceBuffer(
465
0
      mTrackBuffersManager);
466
0
    mTrackBuffersManager->Detach();
467
0
  }
468
0
  mTrackBuffersManager = nullptr;
469
0
  mMediaSource = nullptr;
470
0
}
471
472
void
473
SourceBuffer::Ended()
474
0
{
475
0
  MOZ_ASSERT(NS_IsMainThread());
476
0
  MOZ_ASSERT(IsAttached());
477
0
  MSE_DEBUG("Ended");
478
0
  mTrackBuffersManager->Ended();
479
0
}
480
481
SourceBuffer::SourceBuffer(MediaSource* aMediaSource,
482
                           const MediaContainerType& aType)
483
  : DOMEventTargetHelper(aMediaSource->GetParentObject())
484
  , mMediaSource(aMediaSource)
485
  , mAbstractMainThread(aMediaSource->AbstractMainThread())
486
  , mCurrentAttributes(aType.Type() == MEDIAMIMETYPE("audio/mpeg") ||
487
                       aType.Type() == MEDIAMIMETYPE("audio/aac"))
488
  , mUpdating(false)
489
  , mActive(false)
490
  , mType(aType)
491
0
{
492
0
  MOZ_ASSERT(NS_IsMainThread());
493
0
  MOZ_ASSERT(aMediaSource);
494
0
495
0
  mTrackBuffersManager =
496
0
    new TrackBuffersManager(aMediaSource->GetDecoder(), aType);
497
0
  DDLINKCHILD("track buffers manager", mTrackBuffersManager.get());
498
0
499
0
  MSE_DEBUG("Create mTrackBuffersManager=%p",
500
0
            mTrackBuffersManager.get());
501
0
502
0
  ErrorResult dummy;
503
0
  if (mCurrentAttributes.mGenerateTimestamps) {
504
0
    SetMode(SourceBufferAppendMode::Sequence, dummy);
505
0
  } else {
506
0
    SetMode(SourceBufferAppendMode::Segments, dummy);
507
0
  }
508
0
  mMediaSource->GetDecoder()->GetDemuxer()->AttachSourceBuffer(
509
0
    mTrackBuffersManager);
510
0
}
511
512
SourceBuffer::~SourceBuffer()
513
0
{
514
0
  MOZ_ASSERT(NS_IsMainThread());
515
0
  MOZ_ASSERT(!mMediaSource);
516
0
  MSE_DEBUG("");
517
0
}
518
519
MediaSource*
520
SourceBuffer::GetParentObject() const
521
0
{
522
0
  return mMediaSource;
523
0
}
524
525
JSObject*
526
SourceBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
527
0
{
528
0
  return SourceBuffer_Binding::Wrap(aCx, this, aGivenProto);
529
0
}
530
531
void
532
SourceBuffer::DispatchSimpleEvent(const char* aName)
533
0
{
534
0
  MOZ_ASSERT(NS_IsMainThread());
535
0
  MSE_API("Dispatch event '%s'", aName);
536
0
  DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
537
0
}
538
539
void
540
SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
541
0
{
542
0
  MSE_DEBUG("Queuing event '%s'", aName);
543
0
  nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
544
0
  mAbstractMainThread->Dispatch(event.forget());
545
0
}
546
547
void
548
SourceBuffer::StartUpdating()
549
0
{
550
0
  MOZ_ASSERT(NS_IsMainThread());
551
0
  MOZ_ASSERT(!mUpdating);
552
0
  mUpdating = true;
553
0
  QueueAsyncSimpleEvent("updatestart");
554
0
}
555
556
void
557
SourceBuffer::StopUpdating()
558
0
{
559
0
  MOZ_ASSERT(NS_IsMainThread());
560
0
  if (!mUpdating) {
561
0
    // The buffer append or range removal algorithm  has been interrupted by
562
0
    // abort().
563
0
    return;
564
0
  }
565
0
  mUpdating = false;
566
0
  QueueAsyncSimpleEvent("update");
567
0
  QueueAsyncSimpleEvent("updateend");
568
0
  if (mDOMPromise) {
569
0
    mDOMPromise->MaybeResolveWithUndefined();
570
0
    mDOMPromise = nullptr;
571
0
  }
572
0
}
573
574
void
575
SourceBuffer::AbortUpdating()
576
0
{
577
0
  MOZ_ASSERT(NS_IsMainThread());
578
0
  mUpdating = false;
579
0
  QueueAsyncSimpleEvent("abort");
580
0
  QueueAsyncSimpleEvent("updateend");
581
0
  if (mDOMPromise) {
582
0
    mDOMPromise->MaybeReject(NS_ERROR_DOM_MEDIA_ABORT_ERR);
583
0
    mDOMPromise = nullptr;
584
0
  }
585
0
}
586
587
void
588
SourceBuffer::CheckEndTime()
589
0
{
590
0
  MOZ_ASSERT(NS_IsMainThread());
591
0
  // Check if we need to update mMediaSource duration
592
0
  double endTime = mCurrentAttributes.GetGroupEndTimestamp().ToSeconds();
593
0
  double duration = mMediaSource->Duration();
594
0
  if (endTime > duration) {
595
0
    mMediaSource->SetDuration(endTime);
596
0
  }
597
0
}
598
599
void
600
SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
601
0
{
602
0
  MOZ_ASSERT(NS_IsMainThread());
603
0
  MSE_DEBUG("AppendData(aLength=%u)", aLength);
604
0
605
0
  RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aLength, aRv);
606
0
  if (!data) {
607
0
    return;
608
0
  }
609
0
  StartUpdating();
610
0
611
0
  mTrackBuffersManager->AppendData(data.forget(), mCurrentAttributes)
612
0
    ->Then(mAbstractMainThread, __func__, this,
613
0
           &SourceBuffer::AppendDataCompletedWithSuccess,
614
0
           &SourceBuffer::AppendDataErrored)
615
0
    ->Track(mPendingAppend);
616
0
}
617
618
already_AddRefed<Promise>
619
SourceBuffer::AppendDataAsync(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
620
0
{
621
0
  MOZ_ASSERT(NS_IsMainThread());
622
0
623
0
  if (!IsAttached()) {
624
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
625
0
    return nullptr;
626
0
  }
627
0
628
0
  nsCOMPtr<nsIGlobalObject> parentObject =
629
0
    do_QueryInterface(mMediaSource->GetParentObject());
630
0
  if (!parentObject) {
631
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
632
0
    return nullptr;
633
0
  }
634
0
635
0
  RefPtr<Promise> promise = Promise::Create(parentObject, aRv);
636
0
  if (aRv.Failed()) {
637
0
    return nullptr;
638
0
  }
639
0
640
0
  AppendData(aData, aLength, aRv);
641
0
642
0
  if (aRv.Failed()) {
643
0
    return nullptr;
644
0
  }
645
0
646
0
  MOZ_ASSERT(!mDOMPromise, "Can't have a pending operation going");
647
0
  mDOMPromise = promise;
648
0
649
0
  return promise.forget();
650
0
}
651
652
void
653
SourceBuffer::AppendDataCompletedWithSuccess(
654
  const SourceBufferTask::AppendBufferResult& aResult)
655
0
{
656
0
  MOZ_ASSERT(mUpdating);
657
0
  mPendingAppend.Complete();
658
0
  DDLOG(DDLogCategory::API, "AppendBuffer-completed", NS_OK);
659
0
660
0
  if (aResult.first()) {
661
0
    if (!mActive) {
662
0
      mActive = true;
663
0
      MSE_DEBUG("Init segment received");
664
0
      RefPtr<SourceBuffer> self = this;
665
0
      mMediaSource->SourceBufferIsActive(this)
666
0
        ->Then(mAbstractMainThread, __func__,
667
0
               [self, this]() {
668
0
                 MSE_DEBUG("Complete AppendBuffer operation");
669
0
                 mCompletionPromise.Complete();
670
0
                 StopUpdating();
671
0
               })
672
0
        ->Track(mCompletionPromise);
673
0
    }
674
0
  }
675
0
  if (mActive) {
676
0
    // Tell our parent decoder that we have received new data
677
0
    // and send progress event.
678
0
    mMediaSource->GetDecoder()->NotifyDataArrived();
679
0
  }
680
0
681
0
  mCurrentAttributes = aResult.second();
682
0
683
0
  CheckEndTime();
684
0
685
0
  if (!mCompletionPromise.Exists()) {
686
0
    StopUpdating();
687
0
  }
688
0
}
689
690
void
691
SourceBuffer::AppendDataErrored(const MediaResult& aError)
692
0
{
693
0
  MOZ_ASSERT(mUpdating);
694
0
  mPendingAppend.Complete();
695
0
  DDLOG(DDLogCategory::API, "AppendBuffer-error", aError);
696
0
697
0
  switch (aError.Code()) {
698
0
    case NS_ERROR_DOM_MEDIA_CANCELED:
699
0
      // Nothing further to do as the trackbuffer has been shutdown.
700
0
      // or append was aborted and abort() has handled all the events.
701
0
      break;
702
0
    default:
703
0
      AppendError(aError);
704
0
      break;
705
0
  }
706
0
}
707
708
void
709
SourceBuffer::AppendError(const MediaResult& aDecodeError)
710
0
{
711
0
  MOZ_ASSERT(NS_IsMainThread());
712
0
713
0
  ResetParserState();
714
0
715
0
  mUpdating = false;
716
0
717
0
  QueueAsyncSimpleEvent("error");
718
0
  QueueAsyncSimpleEvent("updateend");
719
0
720
0
  MOZ_ASSERT(NS_FAILED(aDecodeError));
721
0
722
0
  mMediaSource->EndOfStream(aDecodeError);
723
0
724
0
  if (mDOMPromise) {
725
0
    mDOMPromise->MaybeReject(aDecodeError);
726
0
    mDOMPromise = nullptr;
727
0
  }
728
0
}
729
730
already_AddRefed<MediaByteBuffer>
731
SourceBuffer::PrepareAppend(const uint8_t* aData,
732
                            uint32_t aLength,
733
                            ErrorResult& aRv)
734
0
{
735
0
  typedef TrackBuffersManager::EvictDataResult Result;
736
0
737
0
  if (!IsAttached() || mUpdating) {
738
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
739
0
    return nullptr;
740
0
  }
741
0
742
0
  // If the HTMLMediaElement.error attribute is not null, then throw an
743
0
  // InvalidStateError exception and abort these steps.
744
0
  if (!mMediaSource->GetDecoder() ||
745
0
      mMediaSource->GetDecoder()->OwnerHasError()) {
746
0
    MSE_DEBUG("HTMLMediaElement.error is not null");
747
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
748
0
    return nullptr;
749
0
  }
750
0
751
0
  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
752
0
    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
753
0
  }
754
0
755
0
  // Eviction uses a byte threshold. If the buffer is greater than the
756
0
  // number of bytes then data is evicted.
757
0
  // TODO: Drive evictions off memory pressure notifications.
758
0
  // TODO: Consider a global eviction threshold  rather than per TrackBuffer.
759
0
  // Give a chance to the TrackBuffersManager to evict some data if needed.
760
0
  Result evicted =
761
0
    mTrackBuffersManager->EvictData(TimeUnit::FromSeconds(mMediaSource->GetDecoder()->GetCurrentTime()),
762
0
                                    aLength);
763
0
764
0
  // See if we have enough free space to append our new data.
765
0
  if (evicted == Result::BUFFER_FULL) {
766
0
    aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
767
0
    return nullptr;
768
0
  }
769
0
770
0
  RefPtr<MediaByteBuffer> data = new MediaByteBuffer();
771
0
  if (!data->AppendElements(aData, aLength, fallible)) {
772
0
    aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
773
0
    return nullptr;
774
0
  }
775
0
  return data.forget();
776
0
}
777
778
double
779
SourceBuffer::GetBufferedStart()
780
0
{
781
0
  MOZ_ASSERT(NS_IsMainThread());
782
0
  ErrorResult dummy;
783
0
  RefPtr<TimeRanges> ranges = GetBuffered(dummy);
784
0
  return ranges->Length() > 0 ? ranges->GetStartTime() : 0;
785
0
}
786
787
double
788
SourceBuffer::GetBufferedEnd()
789
0
{
790
0
  MOZ_ASSERT(NS_IsMainThread());
791
0
  ErrorResult dummy;
792
0
  RefPtr<TimeRanges> ranges = GetBuffered(dummy);
793
0
  return ranges->Length() > 0 ? ranges->GetEndTime() : 0;
794
0
}
795
796
double
797
SourceBuffer::HighestStartTime()
798
0
{
799
0
  MOZ_ASSERT(NS_IsMainThread());
800
0
  return mTrackBuffersManager
801
0
         ? mTrackBuffersManager->HighestStartTime().ToSeconds()
802
0
         : 0.0;
803
0
}
804
805
double
806
SourceBuffer::HighestEndTime()
807
0
{
808
0
  MOZ_ASSERT(NS_IsMainThread());
809
0
  return mTrackBuffersManager
810
0
         ? mTrackBuffersManager->HighestEndTime().ToSeconds()
811
0
         : 0.0;
812
0
}
813
814
NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
815
816
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)
817
0
  tmp->Detach();
818
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
819
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mBuffered)
820
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMPromise)
821
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper)
822
823
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SourceBuffer,
824
0
                                                  DOMEventTargetHelper)
825
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
826
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBuffered)
827
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMPromise)
828
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
829
830
NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
831
NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
832
833
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SourceBuffer)
834
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
835
836
#undef MSE_DEBUG
837
#undef MSE_DEBUGV
838
#undef MSE_API
839
840
} // namespace dom
841
842
} // namespace mozilla