Home | History | Annotate | Download | only in mediasource
      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