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