tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SourceBuffer.cpp (26015B)


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