void MediaSource::setReadyState(const String& state) { ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword()); if (m_readyState == state) return; String oldState = m_readyState; m_readyState = state; if (m_readyState == closedKeyword()) { m_sourceBuffers->clear(); m_activeSourceBuffers->clear(); m_player = 0; scheduleEvent(eventNames().webkitsourcecloseEvent); return; } if (oldState == openKeyword() && m_readyState == endedKeyword()) { scheduleEvent(eventNames().webkitsourceendedEvent); return; } if (m_readyState == openKeyword()) { scheduleEvent(eventNames().webkitsourceopenEvent); return; } }
void MediaSource::endOfStream(const String& error, ExceptionCode& ec) { // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#dom-endofstream // 1. If the readyState attribute is not in the "open" state then throw an // INVALID_STATE_ERR exception and abort these steps. if (!m_player || m_readyState != openKeyword()) { ec = INVALID_STATE_ERR; return; } MediaPlayer::EndOfStreamStatus eosStatus = MediaPlayer::EosNoError; if (error.isNull() || error.isEmpty()) eosStatus = MediaPlayer::EosNoError; else if (error == "network") eosStatus = MediaPlayer::EosNetworkError; else if (error == "decode") eosStatus = MediaPlayer::EosDecodeError; else { ec = INVALID_ACCESS_ERR; return; } // 2. Change the readyState attribute value to "ended". setReadyState(endedKeyword()); m_player->sourceEndOfStream(eosStatus); }
void MediaSourceBase::endOfStream(const AtomicString& error, ExceptionCode& ec) { DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral)); DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral)); // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#dom-endofstream // 1. If the readyState attribute is not in the "open" state then throw an // INVALID_STATE_ERR exception and abort these steps. if (!isOpen()) { ec = INVALID_STATE_ERR; return; } MediaSourcePrivate::EndOfStreamStatus eosStatus = MediaSourcePrivate::EosNoError; if (error.isNull() || error.isEmpty()) eosStatus = MediaSourcePrivate::EosNoError; else if (error == network) eosStatus = MediaSourcePrivate::EosNetworkError; else if (error == decode) eosStatus = MediaSourcePrivate::EosDecodeError; else { ec = INVALID_ACCESS_ERR; return; } // 2. Change the readyState attribute value to "ended". setReadyState(endedKeyword()); m_private->markEndOfStream(eosStatus); }
void MediaSource::streamEndedWithError(const AtomicString& error, ExceptionCode& ec) { DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral)); DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral)); // 2.4.7 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#end-of-stream-algorithm // 1. Change the readyState attribute value to "ended". // 2. Queue a task to fire a simple event named sourceended at the MediaSource. setReadyState(endedKeyword()); // 3. if (error.isEmpty()) { // ↳ If error is not set, is null, or is an empty string // 1. Run the duration change algorithm with new duration set to the highest end timestamp // across all SourceBuffer objects in sourceBuffers. MediaTime maxEndTimestamp; for (auto it = m_sourceBuffers->begin(), end = m_sourceBuffers->end(); it != end; ++it) maxEndTimestamp = std::max((*it)->highestPresentationEndTimestamp(), maxEndTimestamp); m_private->setDuration(maxEndTimestamp); // 2. Notify the media element that it now has all of the media data. m_private->markEndOfStream(MediaSourcePrivate::EosNoError); } else if (error == network) { // ↳ If error is set to "network" ASSERT(m_mediaElement); if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) { // ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING // Run the "If the media data cannot be fetched at all, due to network errors, causing // the user agent to give up trying to fetch the resource" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed(). m_mediaElement->mediaLoadingFailed(MediaPlayer::NetworkError); } else { // ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING // Run the "If the connection is interrupted after some media data has been received, causing the // user agent to give up trying to fetch the resource" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally(). m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError); } } else if (error == decode) { // ↳ If error is set to "decode" ASSERT(m_mediaElement); if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) { // ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING // Run the "If the media data can be fetched but is found by inspection to be in an unsupported // format, or can otherwise not be rendered at all" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed(). m_mediaElement->mediaLoadingFailed(MediaPlayer::FormatError); } else { // ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING // Run the media data is corrupted steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally(). m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::DecodeError); } } else { // ↳ Otherwise // Throw an INVALID_ACCESS_ERR exception. ec = INVALID_ACCESS_ERR; } }
void MediaSource::openIfInEndedState() { if (m_readyState != endedKeyword()) return; setReadyState(openKeyword()); m_private->unmarkEndOfStream(); }
void MediaSource::streamEndedWithError(Optional<EndOfStreamError> error) { LOG(MediaSource, "MediaSource::streamEndedWithError(%p)", this); // 2.4.7 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#end-of-stream-algorithm // 1. Change the readyState attribute value to "ended". // 2. Queue a task to fire a simple event named sourceended at the MediaSource. setReadyState(endedKeyword()); // 3. if (!error) { // ↳ If error is not set, is null, or is an empty string // 1. Run the duration change algorithm with new duration set to the highest end time reported by // the buffered attribute across all SourceBuffer objects in sourceBuffers. MediaTime maxEndTime; for (auto& sourceBuffer : *m_sourceBuffers) { if (auto length = sourceBuffer->buffered()->length()) maxEndTime = std::max(sourceBuffer->buffered()->ranges().end(length - 1), maxEndTime); } setDurationInternal(maxEndTime); // 2. Notify the media element that it now has all of the media data. m_private->markEndOfStream(MediaSourcePrivate::EosNoError); } else if (error == EndOfStreamError::Network) { // ↳ If error is set to "network" ASSERT(m_mediaElement); if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) { // ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING // Run the "If the media data cannot be fetched at all, due to network errors, causing // the user agent to give up trying to fetch the resource" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed(). m_mediaElement->mediaLoadingFailed(MediaPlayer::NetworkError); } else { // ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING // Run the "If the connection is interrupted after some media data has been received, causing the // user agent to give up trying to fetch the resource" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally(). m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError); } } else { // ↳ If error is set to "decode" ASSERT(error == EndOfStreamError::Decode); ASSERT(m_mediaElement); if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) { // ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING // Run the "If the media data can be fetched but is found by inspection to be in an unsupported // format, or can otherwise not be rendered at all" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed(). m_mediaElement->mediaLoadingFailed(MediaPlayer::FormatError); } else { // ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING // Run the media data is corrupted steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally(). m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::DecodeError); } } }
std::unique_ptr<PlatformTimeRanges> MediaSource::buffered() const { if (m_buffered && m_activeSourceBuffers->length() && std::all_of(m_activeSourceBuffers->begin(), m_activeSourceBuffers->end(), [] (RefPtr<SourceBuffer>& buffer) { return !buffer->isBufferedDirty(); })) return std::make_unique<PlatformTimeRanges>(*m_buffered); m_buffered = std::make_unique<PlatformTimeRanges>(); for (auto& sourceBuffer : *m_activeSourceBuffers) sourceBuffer->setBufferedDirty(false); // Implements MediaSource algorithm for HTMLMediaElement.buffered. // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions Vector<PlatformTimeRanges> activeRanges = this->activeRanges(); // 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges object and abort these steps. if (activeRanges.isEmpty()) return std::make_unique<PlatformTimeRanges>(*m_buffered); // 2. Let active ranges be the ranges returned by buffered for each SourceBuffer object in activeSourceBuffers. // 3. Let highest end time be the largest range end time in the active ranges. MediaTime highestEndTime = MediaTime::zeroTime(); for (auto& ranges : activeRanges) { unsigned length = ranges.length(); if (length) highestEndTime = std::max(highestEndTime, ranges.end(length - 1)); } // Return an empty range if all ranges are empty. if (!highestEndTime) return std::make_unique<PlatformTimeRanges>(*m_buffered); // 4. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time. m_buffered->add(MediaTime::zeroTime(), highestEndTime); // 5. For each SourceBuffer object in activeSourceBuffers run the following steps: bool ended = readyState() == endedKeyword(); for (auto& sourceRanges : activeRanges) { // 5.1 Let source ranges equal the ranges returned by the buffered attribute on the current SourceBuffer. // 5.2 If readyState is "ended", then set the end time on the last range in source ranges to highest end time. if (ended && sourceRanges.length()) sourceRanges.add(sourceRanges.start(sourceRanges.length() - 1), highestEndTime); // 5.3 Let new intersection ranges equal the the intersection between the intersection ranges and the source ranges. // 5.4 Replace the ranges in intersection ranges with the new intersection ranges. m_buffered->intersectWith(sourceRanges); } return std::make_unique<PlatformTimeRanges>(*m_buffered); }
void MediaSource::setReadyState(const AtomicString& state) { ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword()); AtomicString oldState = readyState(); LOG(Media, "MediaSource::setReadyState() %p : %s -> %s", this, oldState.string().ascii().data(), state.string().ascii().data()); if (state == closedKeyword()) { m_private.clear(); m_mediaElement = 0; } if (oldState == state) return; m_readyState = state; onReadyStateChange(oldState, state); }
std::unique_ptr<PlatformTimeRanges> MediaSource::buffered() const { // Implements MediaSource algorithm for HTMLMediaElement.buffered. // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions Vector<RefPtr<TimeRanges>> ranges = activeRanges(); // 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges object and abort these steps. if (ranges.isEmpty()) return PlatformTimeRanges::create(); // 2. Let active ranges be the ranges returned by buffered for each SourceBuffer object in activeSourceBuffers. // 3. Let highest end time be the largest range end time in the active ranges. double highestEndTime = -1; for (size_t i = 0; i < ranges.size(); ++i) { unsigned length = ranges[i]->length(); if (length) highestEndTime = std::max(highestEndTime, ranges[i]->end(length - 1, ASSERT_NO_EXCEPTION)); } // Return an empty range if all ranges are empty. if (highestEndTime < 0) return PlatformTimeRanges::create(); // 4. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time. RefPtr<TimeRanges> intersectionRanges = TimeRanges::create(0, highestEndTime); // 5. For each SourceBuffer object in activeSourceBuffers run the following steps: bool ended = readyState() == endedKeyword(); for (size_t i = 0; i < ranges.size(); ++i) { // 5.1 Let source ranges equal the ranges returned by the buffered attribute on the current SourceBuffer. TimeRanges* sourceRanges = ranges[i].get(); // 5.2 If readyState is "ended", then set the end time on the last range in source ranges to highest end time. if (ended && sourceRanges->length()) sourceRanges->add(sourceRanges->start(sourceRanges->length() - 1, ASSERT_NO_EXCEPTION), highestEndTime); // 5.3 Let new intersection ranges equal the the intersection between the intersection ranges and the source ranges. // 5.4 Replace the ranges in intersection ranges with the new intersection ranges. intersectionRanges->intersectWith(*sourceRanges); } return PlatformTimeRanges::create(intersectionRanges->ranges()); }
void WebKitMediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState) { if (isClosed()) { m_sourceBuffers->clear(); m_activeSourceBuffers->clear(); scheduleEvent(eventNames().webkitsourcecloseEvent); return; } if (oldState == openKeyword() && newState == endedKeyword()) { scheduleEvent(eventNames().webkitsourceendedEvent); return; } if (isOpen()) { scheduleEvent(eventNames().webkitsourceopenEvent); return; } }
void MediaSource::setReadyState(const AtomicString& state) { ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword()); AtomicString oldState = readyState(); LOG(MediaSource, "MediaSource::setReadyState(%p) : %s -> %s", this, oldState.string().ascii().data(), state.string().ascii().data()); if (state == closedKeyword()) { m_private = nullptr; m_mediaElement = nullptr; m_duration = MediaTime::invalidTime(); } if (oldState == state) return; m_readyState = state; onReadyStateChange(oldState, state); }
void MediaSource::append(const String& id, PassRefPtr<Uint8Array> data, ExceptionCode& ec) { if (!data.get()) { ec = INVALID_ACCESS_ERR; return; } if (!m_player || m_readyState == closedKeyword()) { ec = INVALID_STATE_ERR; return; } if (m_readyState == endedKeyword()) setReadyState(openKeyword()); if (!m_player->sourceAppend(id, data->data(), data->length())) { ec = SYNTAX_ERR; return; } }
void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState) { if (isOpen()) { scheduleEvent(eventNames().sourceopenEvent); return; } if (oldState == openKeyword() && newState == endedKeyword()) { scheduleEvent(eventNames().sourceendedEvent); return; } ASSERT(isClosed()); m_activeSourceBuffers->clear(); // Clear SourceBuffer references to this object. for (auto& buffer : *m_sourceBuffers) buffer->removedFromMediaSource(); m_sourceBuffers->clear(); scheduleEvent(eventNames().sourcecloseEvent); }
void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState) { if (isOpen()) { scheduleEvent(eventNames().sourceopenEvent); return; } if (oldState == openKeyword() && newState == endedKeyword()) { scheduleEvent(eventNames().sourceendedEvent); return; } ASSERT(isClosed()); m_activeSourceBuffers->clear(); // Clear SourceBuffer references to this object. for (unsigned long i = 0, length = m_sourceBuffers->length(); i < length; ++i) m_sourceBuffers->item(i)->removedFromMediaSource(); m_sourceBuffers->clear(); scheduleEvent(eventNames().sourcecloseEvent); }
bool MediaSource::isEnded() const { return readyState() == endedKeyword(); }
void MediaSource::streamEndedWithError(const AtomicString& error, ExceptionCode& ec) { DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral)); DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral)); LOG(MediaSource, "MediaSource::streamEndedWithError(%p) : %s", this, error.string().ascii().data()); // 2.4.7 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#end-of-stream-algorithm // 3. if (error.isEmpty()) { // ↳ If error is not set, is null, or is an empty string // 1. Run the duration change algorithm with new duration set to the highest end time reported by // the buffered attribute across all SourceBuffer objects in sourceBuffers. MediaTime maxEndTime; for (auto& sourceBuffer : *m_sourceBuffers) { if (auto length = sourceBuffer->buffered()->length()) maxEndTime = std::max(sourceBuffer->buffered()->ranges().end(length - 1), maxEndTime); } setDurationInternal(maxEndTime); // 2. Notify the media element that it now has all of the media data. m_private->markEndOfStream(MediaSourcePrivate::EosNoError); } // NOTE: Do steps 1 & 2 after step 3 (with an empty error) to avoid the MediaSource's readyState being re-opened by a // remove() operation resulting from a duration change. // FIXME: Re-number or update this section once <https://www.w3.org/Bugs/Public/show_bug.cgi?id=26316> is resolved. // 1. Change the readyState attribute value to "ended". // 2. Queue a task to fire a simple event named sourceended at the MediaSource. setReadyState(endedKeyword()); if (error == network) { // ↳ If error is set to "network" ASSERT(m_mediaElement); if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) { // ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING // Run the "If the media data cannot be fetched at all, due to network errors, causing // the user agent to give up trying to fetch the resource" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed(). m_mediaElement->mediaLoadingFailed(MediaPlayer::NetworkError); } else { // ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING // Run the "If the connection is interrupted after some media data has been received, causing the // user agent to give up trying to fetch the resource" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally(). m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError); } } else if (error == decode) { // ↳ If error is set to "decode" ASSERT(m_mediaElement); if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) { // ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING // Run the "If the media data can be fetched but is found by inspection to be in an unsupported // format, or can otherwise not be rendered at all" steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed(). m_mediaElement->mediaLoadingFailed(MediaPlayer::FormatError); } else { // ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING // Run the media data is corrupted steps of the resource fetch algorithm. // NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally(). m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::DecodeError); } } else if (!error.isEmpty()) { // ↳ Otherwise // Throw an INVALID_ACCESS_ERR exception. ec = INVALID_ACCESS_ERR; } }