1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "modules/mediasource/MediaSource.h" 33 34 #include "bindings/core/v8/ExceptionMessages.h" 35 #include "bindings/core/v8/ExceptionState.h" 36 #include "bindings/core/v8/ExceptionStatePlaceholder.h" 37 #include "core/dom/ExceptionCode.h" 38 #include "core/events/Event.h" 39 #include "core/events/GenericEventQueue.h" 40 #include "core/html/HTMLMediaElement.h" 41 #include "core/html/TimeRanges.h" 42 #include "modules/mediasource/MediaSourceRegistry.h" 43 #include "platform/ContentType.h" 44 #include "platform/Logging.h" 45 #include "platform/MIMETypeRegistry.h" 46 #include "platform/RuntimeEnabledFeatures.h" 47 #include "platform/TraceEvent.h" 48 #include "public/platform/WebMediaSource.h" 49 #include "public/platform/WebSourceBuffer.h" 50 #include "wtf/Uint8Array.h" 51 #include "wtf/text/CString.h" 52 53 using blink::WebMediaSource; 54 using blink::WebSourceBuffer; 55 56 namespace blink { 57 58 static bool throwExceptionIfClosedOrUpdating(bool isOpen, bool isUpdating, ExceptionState& exceptionState) 59 { 60 if (!isOpen) { 61 exceptionState.throwDOMException(InvalidStateError, "The MediaSource's readyState is not 'open'."); 62 return true; 63 } 64 if (isUpdating) { 65 exceptionState.throwDOMException(InvalidStateError, "The 'updating' attribute is true on one or more of this MediaSource's SourceBuffers."); 66 return true; 67 } 68 69 return false; 70 } 71 72 const AtomicString& MediaSource::openKeyword() 73 { 74 DEFINE_STATIC_LOCAL(const AtomicString, open, ("open", AtomicString::ConstructFromLiteral)); 75 return open; 76 } 77 78 const AtomicString& MediaSource::closedKeyword() 79 { 80 DEFINE_STATIC_LOCAL(const AtomicString, closed, ("closed", AtomicString::ConstructFromLiteral)); 81 return closed; 82 } 83 84 const AtomicString& MediaSource::endedKeyword() 85 { 86 DEFINE_STATIC_LOCAL(const AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral)); 87 return ended; 88 } 89 90 MediaSource* MediaSource::create(ExecutionContext* context) 91 { 92 MediaSource* mediaSource(adoptRefCountedGarbageCollectedWillBeNoop(new MediaSource(context))); 93 mediaSource->suspendIfNeeded(); 94 return mediaSource; 95 } 96 97 MediaSource::MediaSource(ExecutionContext* context) 98 : ActiveDOMObject(context) 99 , m_readyState(closedKeyword()) 100 , m_asyncEventQueue(GenericEventQueue::create(this)) 101 , m_attachedElement(nullptr) 102 , m_sourceBuffers(SourceBufferList::create(executionContext(), m_asyncEventQueue.get())) 103 , m_activeSourceBuffers(SourceBufferList::create(executionContext(), m_asyncEventQueue.get())) 104 , m_isAddedToRegistry(false) 105 { 106 WTF_LOG(Media, "MediaSource::MediaSource %p", this); 107 } 108 109 MediaSource::~MediaSource() 110 { 111 WTF_LOG(Media, "MediaSource::~MediaSource %p", this); 112 #if !ENABLE(OILPAN) 113 ASSERT(isClosed()); 114 #endif 115 } 116 117 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& exceptionState) 118 { 119 WTF_LOG(Media, "MediaSource::addSourceBuffer(%s) %p", type.ascii().data(), this); 120 121 // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type 122 // 1. If type is an empty string then throw an InvalidAccessError exception 123 // and abort these steps. 124 if (type.isEmpty()) { 125 exceptionState.throwDOMException(InvalidAccessError, "The type provided is empty."); 126 return 0; 127 } 128 129 // 2. If type contains a MIME type that is not supported ..., then throw a 130 // NotSupportedError exception and abort these steps. 131 if (!isTypeSupported(type)) { 132 exceptionState.throwDOMException(NotSupportedError, "The type provided ('" + type + "') is unsupported."); 133 return 0; 134 } 135 136 // 4. If the readyState attribute is not in the "open" state then throw an 137 // InvalidStateError exception and abort these steps. 138 if (!isOpen()) { 139 exceptionState.throwDOMException(InvalidStateError, "The MediaSource's readyState is not 'open'."); 140 return 0; 141 } 142 143 // 5. Create a new SourceBuffer object and associated resources. 144 ContentType contentType(type); 145 Vector<String> codecs = contentType.codecs(); 146 OwnPtr<WebSourceBuffer> webSourceBuffer = createWebSourceBuffer(contentType.type(), codecs, exceptionState); 147 148 if (!webSourceBuffer) { 149 ASSERT(exceptionState.code() == NotSupportedError || exceptionState.code() == QuotaExceededError); 150 // 2. If type contains a MIME type that is not supported ..., then throw a NotSupportedError exception and abort these steps. 151 // 3. If the user agent can't handle any more SourceBuffer objects then throw a QuotaExceededError exception and abort these steps 152 return 0; 153 } 154 155 SourceBuffer* buffer = SourceBuffer::create(webSourceBuffer.release(), this, m_asyncEventQueue.get()); 156 // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object. 157 m_sourceBuffers->add(buffer); 158 159 // 7. Return the new object to the caller. 160 return buffer; 161 } 162 163 void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionState& exceptionState) 164 { 165 WTF_LOG(Media, "MediaSource::removeSourceBuffer() %p", this); 166 167 // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-removeSourceBuffer-void-SourceBuffer-sourceBuffer 168 169 // 1. If sourceBuffer specifies an object that is not in sourceBuffers then 170 // throw a NotFoundError exception and abort these steps. 171 if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer)) { 172 exceptionState.throwDOMException(NotFoundError, "The SourceBuffer provided is not contained in this MediaSource."); 173 return; 174 } 175 176 // 2. If the sourceBuffer.updating attribute equals true, then run the following steps: ... 177 buffer->abortIfUpdating(); 178 179 // Steps 3-8 are related to updating audioTracks, videoTracks, and textTracks which aren't implmented yet. 180 // FIXME(91649): support track selection 181 182 // 9. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ... 183 m_activeSourceBuffers->remove(buffer); 184 185 // 10. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event 186 // on that object. 187 m_sourceBuffers->remove(buffer); 188 189 // 11. Destroy all resources for sourceBuffer. 190 buffer->removedFromMediaSource(); 191 } 192 193 void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState) 194 { 195 if (isOpen()) { 196 scheduleEvent(EventTypeNames::sourceopen); 197 return; 198 } 199 200 if (oldState == openKeyword() && newState == endedKeyword()) { 201 scheduleEvent(EventTypeNames::sourceended); 202 return; 203 } 204 205 ASSERT(isClosed()); 206 207 m_activeSourceBuffers->clear(); 208 209 // Clear SourceBuffer references to this object. 210 for (unsigned long i = 0; i < m_sourceBuffers->length(); ++i) 211 m_sourceBuffers->item(i)->removedFromMediaSource(); 212 m_sourceBuffers->clear(); 213 214 scheduleEvent(EventTypeNames::sourceclose); 215 } 216 217 bool MediaSource::isUpdating() const 218 { 219 // Return true if any member of |m_sourceBuffers| is updating. 220 for (unsigned long i = 0; i < m_sourceBuffers->length(); ++i) { 221 if (m_sourceBuffers->item(i)->updating()) 222 return true; 223 } 224 225 return false; 226 } 227 228 bool MediaSource::isTypeSupported(const String& type) 229 { 230 WTF_LOG(Media, "MediaSource::isTypeSupported(%s)", type.ascii().data()); 231 232 // Section 2.2 isTypeSupported() method steps. 233 // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-isTypeSupported-boolean-DOMString-type 234 // 1. If type is an empty string, then return false. 235 if (type.isNull() || type.isEmpty()) 236 return false; 237 238 ContentType contentType(type); 239 String codecs = contentType.parameter("codecs"); 240 241 // 2. If type does not contain a valid MIME type string, then return false. 242 if (contentType.type().isEmpty()) 243 return false; 244 245 // Note: MediaSource.isTypeSupported() returning true implies that HTMLMediaElement.canPlayType() will return "maybe" or "probably" 246 // since it does not make sense for a MediaSource to support a type the HTMLMediaElement knows it cannot play. 247 if (HTMLMediaElement::supportsType(contentType, String()) == WebMimeRegistry::IsNotSupported) 248 return false; 249 250 // 3. If type contains a media type or media subtype that the MediaSource does not support, then return false. 251 // 4. If type contains at a codec that the MediaSource does not support, then return false. 252 // 5. If the MediaSource does not support the specified combination of media type, media subtype, and codecs then return false. 253 // 6. Return true. 254 return MIMETypeRegistry::isSupportedMediaSourceMIMEType(contentType.type(), codecs); 255 } 256 257 const AtomicString& MediaSource::interfaceName() const 258 { 259 return EventTargetNames::MediaSource; 260 } 261 262 ExecutionContext* MediaSource::executionContext() const 263 { 264 return ActiveDOMObject::executionContext(); 265 } 266 267 void MediaSource::clearWeakMembers(Visitor* visitor) 268 { 269 #if ENABLE(OILPAN) 270 // Oilpan: If the MediaSource survived, but its attached media 271 // element did not, signal the element that it can safely 272 // notify its MediaSource during finalization by calling close(). 273 if (m_attachedElement && !visitor->isAlive(m_attachedElement)) { 274 m_attachedElement->setCloseMediaSourceWhenFinalizing(); 275 m_attachedElement.clear(); 276 } 277 #endif 278 } 279 280 void MediaSource::trace(Visitor* visitor) 281 { 282 #if ENABLE(OILPAN) 283 visitor->trace(m_asyncEventQueue); 284 #endif 285 visitor->trace(m_sourceBuffers); 286 visitor->trace(m_activeSourceBuffers); 287 visitor->registerWeakMembers<MediaSource, &MediaSource::clearWeakMembers>(this); 288 EventTargetWithInlineData::trace(visitor); 289 } 290 291 void MediaSource::setWebMediaSourceAndOpen(PassOwnPtr<WebMediaSource> webMediaSource) 292 { 293 TRACE_EVENT_ASYNC_END0("media", "MediaSource::attachToElement", this); 294 ASSERT(webMediaSource); 295 ASSERT(!m_webMediaSource); 296 ASSERT(m_attachedElement); 297 m_webMediaSource = webMediaSource; 298 setReadyState(openKeyword()); 299 } 300 301 void MediaSource::addedToRegistry() 302 { 303 ASSERT(!m_isAddedToRegistry); 304 m_isAddedToRegistry = true; 305 } 306 307 void MediaSource::removedFromRegistry() 308 { 309 ASSERT(m_isAddedToRegistry); 310 m_isAddedToRegistry = false; 311 } 312 313 double MediaSource::duration() const 314 { 315 return isClosed() ? std::numeric_limits<float>::quiet_NaN() : m_webMediaSource->duration(); 316 } 317 318 PassRefPtrWillBeRawPtr<TimeRanges> MediaSource::buffered() const 319 { 320 // Implements MediaSource algorithm for HTMLMediaElement.buffered. 321 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions 322 WillBeHeapVector<RefPtrWillBeMember<TimeRanges> > ranges(m_activeSourceBuffers->length()); 323 for (size_t i = 0; i < m_activeSourceBuffers->length(); ++i) 324 ranges[i] = m_activeSourceBuffers->item(i)->buffered(ASSERT_NO_EXCEPTION); 325 326 // 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges object and abort these steps. 327 if (ranges.isEmpty()) 328 return TimeRanges::create(); 329 330 // 2. Let active ranges be the ranges returned by buffered for each SourceBuffer object in activeSourceBuffers. 331 // 3. Let highest end time be the largest range end time in the active ranges. 332 double highestEndTime = -1; 333 for (size_t i = 0; i < ranges.size(); ++i) { 334 unsigned length = ranges[i]->length(); 335 if (length) 336 highestEndTime = std::max(highestEndTime, ranges[i]->end(length - 1, ASSERT_NO_EXCEPTION)); 337 } 338 339 // Return an empty range if all ranges are empty. 340 if (highestEndTime < 0) 341 return TimeRanges::create(); 342 343 // 4. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time. 344 RefPtrWillBeRawPtr<TimeRanges> intersectionRanges = TimeRanges::create(0, highestEndTime); 345 346 // 5. For each SourceBuffer object in activeSourceBuffers run the following steps: 347 bool ended = readyState() == endedKeyword(); 348 for (size_t i = 0; i < ranges.size(); ++i) { 349 // 5.1 Let source ranges equal the ranges returned by the buffered attribute on the current SourceBuffer. 350 TimeRanges* sourceRanges = ranges[i].get(); 351 352 // 5.2 If readyState is "ended", then set the end time on the last range in source ranges to highest end time. 353 if (ended && sourceRanges->length()) 354 sourceRanges->add(sourceRanges->start(sourceRanges->length() - 1, ASSERT_NO_EXCEPTION), highestEndTime); 355 356 // 5.3 Let new intersection ranges equal the the intersection between the intersection ranges and the source ranges. 357 // 5.4 Replace the ranges in intersection ranges with the new intersection ranges. 358 intersectionRanges->intersectWith(sourceRanges); 359 } 360 361 return intersectionRanges.release(); 362 } 363 364 void MediaSource::setDuration(double duration, ExceptionState& exceptionState) 365 { 366 // 2.1 http://www.w3.org/TR/media-source/#widl-MediaSource-duration 367 // 1. If the value being set is negative or NaN then throw an InvalidAccessError 368 // exception and abort these steps. 369 if (std::isnan(duration)) { 370 exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::notAFiniteNumber(duration, "duration")); 371 return; 372 } 373 if (duration < 0.0) { 374 exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexExceedsMinimumBound("duration", duration, 0.0)); 375 return; 376 } 377 378 // 2. If the readyState attribute is not "open" then throw an InvalidStateError 379 // exception and abort these steps. 380 // 3. If the updating attribute equals true on any SourceBuffer in sourceBuffers, 381 // then throw an InvalidStateError exception and abort these steps. 382 if (throwExceptionIfClosedOrUpdating(isOpen(), isUpdating(), exceptionState)) 383 return; 384 385 // 4. Run the duration change algorithm with new duration set to the value being 386 // assigned to this attribute. 387 durationChangeAlgorithm(duration); 388 } 389 390 void MediaSource::durationChangeAlgorithm(double newDuration) 391 { 392 // Section 2.6.4 Duration change 393 // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#duration-change-algorithm 394 // 1. If the current value of duration is equal to new duration, then return. 395 if (newDuration == duration()) 396 return; 397 398 // 2. Set old duration to the current value of duration. 399 double oldDuration = duration(); 400 401 bool requestSeek = m_attachedElement->currentTime() > newDuration; 402 403 // 3. Update duration to new duration. 404 m_webMediaSource->setDuration(newDuration); 405 406 // 4. If the new duration is less than old duration, then call remove(new duration, old duration) on all all objects in sourceBuffers. 407 if (newDuration < oldDuration) { 408 for (size_t i = 0; i < m_sourceBuffers->length(); ++i) 409 m_sourceBuffers->item(i)->remove(newDuration, oldDuration, ASSERT_NO_EXCEPTION); 410 } 411 412 // 5. If a user agent is unable to partially render audio frames or text cues that start before and end after the duration, then run the following steps: 413 // NOTE: Currently we assume that the media engine is able to render partial frames/cues. If a media 414 // engine gets added that doesn't support this, then we'll need to add logic to handle the substeps. 415 416 // 6. Update the media controller duration to new duration and run the HTMLMediaElement duration change algorithm. 417 m_attachedElement->durationChanged(newDuration, requestSeek); 418 } 419 420 void MediaSource::setReadyState(const AtomicString& state) 421 { 422 ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword()); 423 424 AtomicString oldState = readyState(); 425 WTF_LOG(Media, "MediaSource::setReadyState() %p : %s -> %s", this, oldState.ascii().data(), state.ascii().data()); 426 427 if (state == closedKeyword()) { 428 m_webMediaSource.clear(); 429 m_attachedElement.clear(); 430 } 431 432 if (oldState == state) 433 return; 434 435 m_readyState = state; 436 437 onReadyStateChange(oldState, state); 438 } 439 440 void MediaSource::endOfStream(const AtomicString& error, ExceptionState& exceptionState) 441 { 442 DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral)); 443 DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral)); 444 445 if (error == network) { 446 endOfStreamInternal(WebMediaSource::EndOfStreamStatusNetworkError, exceptionState); 447 } else if (error == decode) { 448 endOfStreamInternal(WebMediaSource::EndOfStreamStatusDecodeError, exceptionState); 449 } else { 450 ASSERT_NOT_REACHED(); // IDL enforcement should prevent this case. 451 } 452 } 453 454 void MediaSource::endOfStream(ExceptionState& exceptionState) 455 { 456 endOfStreamInternal(WebMediaSource::EndOfStreamStatusNoError, exceptionState); 457 } 458 459 void MediaSource::endOfStreamInternal(const WebMediaSource::EndOfStreamStatus eosStatus, ExceptionState& exceptionState) 460 { 461 // 2.2 http://www.w3.org/TR/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error 462 // 1. If the readyState attribute is not in the "open" state then throw an 463 // InvalidStateError exception and abort these steps. 464 // 2. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an 465 // InvalidStateError exception and abort these steps. 466 if (throwExceptionIfClosedOrUpdating(isOpen(), isUpdating(), exceptionState)) 467 return; 468 469 // 3. Run the end of stream algorithm with the error parameter set to error. 470 // 1. Change the readyState attribute value to "ended". 471 // 2. Queue a task to fire a simple event named sourceended at the MediaSource. 472 setReadyState(endedKeyword()); 473 474 // 3. Do various steps based on |eosStatus|. 475 m_webMediaSource->markEndOfStream(eosStatus); 476 } 477 478 bool MediaSource::isOpen() const 479 { 480 return readyState() == openKeyword(); 481 } 482 483 void MediaSource::setSourceBufferActive(SourceBuffer* sourceBuffer) 484 { 485 ASSERT(!m_activeSourceBuffers->contains(sourceBuffer)); 486 487 // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-activeSourceBuffers 488 // SourceBuffer objects in SourceBuffer.activeSourceBuffers must appear in 489 // the same order as they appear in SourceBuffer.sourceBuffers. 490 // SourceBuffer transitions to active are not guaranteed to occur in the 491 // same order as buffers in |m_sourceBuffers|, so this method needs to 492 // insert |sourceBuffer| into |m_activeSourceBuffers|. 493 size_t indexInSourceBuffers = m_sourceBuffers->find(sourceBuffer); 494 ASSERT(indexInSourceBuffers != kNotFound); 495 496 size_t insertPosition = 0; 497 while (insertPosition < m_activeSourceBuffers->length() 498 && m_sourceBuffers->find(m_activeSourceBuffers->item(insertPosition)) < indexInSourceBuffers) { 499 ++insertPosition; 500 } 501 502 m_activeSourceBuffers->insert(insertPosition, sourceBuffer); 503 } 504 505 bool MediaSource::isClosed() const 506 { 507 return readyState() == closedKeyword(); 508 } 509 510 void MediaSource::close() 511 { 512 setReadyState(closedKeyword()); 513 } 514 515 bool MediaSource::attachToElement(HTMLMediaElement* element) 516 { 517 if (m_attachedElement) 518 return false; 519 520 ASSERT(isClosed()); 521 522 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSource::attachToElement", this); 523 m_attachedElement = element; 524 return true; 525 } 526 527 void MediaSource::openIfInEndedState() 528 { 529 if (m_readyState != endedKeyword()) 530 return; 531 532 setReadyState(openKeyword()); 533 m_webMediaSource->unmarkEndOfStream(); 534 } 535 536 bool MediaSource::hasPendingActivity() const 537 { 538 return m_attachedElement || m_webMediaSource 539 || m_asyncEventQueue->hasPendingEvents() 540 || m_isAddedToRegistry; 541 } 542 543 void MediaSource::stop() 544 { 545 m_asyncEventQueue->close(); 546 if (!isClosed()) 547 setReadyState(closedKeyword()); 548 m_webMediaSource.clear(); 549 } 550 551 PassOwnPtr<WebSourceBuffer> MediaSource::createWebSourceBuffer(const String& type, const Vector<String>& codecs, ExceptionState& exceptionState) 552 { 553 WebSourceBuffer* webSourceBuffer = 0; 554 555 switch (m_webMediaSource->addSourceBuffer(type, codecs, &webSourceBuffer)) { 556 case WebMediaSource::AddStatusOk: 557 return adoptPtr(webSourceBuffer); 558 case WebMediaSource::AddStatusNotSupported: 559 ASSERT(!webSourceBuffer); 560 // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type 561 // Step 2: If type contains a MIME type ... that is not supported with the types 562 // specified for the other SourceBuffer objects in sourceBuffers, then throw 563 // a NotSupportedError exception and abort these steps. 564 exceptionState.throwDOMException(NotSupportedError, "The type provided ('" + type + "') is not supported."); 565 return nullptr; 566 case WebMediaSource::AddStatusReachedIdLimit: 567 ASSERT(!webSourceBuffer); 568 // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type 569 // Step 3: If the user agent can't handle any more SourceBuffer objects then throw 570 // a QuotaExceededError exception and abort these steps. 571 exceptionState.throwDOMException(QuotaExceededError, "This MediaSource has reached the limit of SourceBuffer objects it can handle. No additional SourceBuffer objects may be added."); 572 return nullptr; 573 } 574 575 ASSERT_NOT_REACHED(); 576 return nullptr; 577 } 578 579 void MediaSource::scheduleEvent(const AtomicString& eventName) 580 { 581 ASSERT(m_asyncEventQueue); 582 583 RefPtrWillBeRawPtr<Event> event = Event::create(eventName); 584 event->setTarget(this); 585 586 m_asyncEventQueue->enqueueEvent(event.release()); 587 } 588 589 URLRegistry& MediaSource::registry() const 590 { 591 return MediaSourceRegistry::registry(); 592 } 593 594 } // namespace blink 595