This source file includes following definitions.
- throwExceptionIfRemovedOrUpdating
- create
- m_appendStreamAsyncPartRunner
- segmentsKeyword
- sequenceKeyword
- setMode
- buffered
- timestampOffset
- setTimestampOffset
- appendWindowStart
- setAppendWindowStart
- appendWindowEnd
- setAppendWindowEnd
- appendBuffer
- appendBuffer
- appendStream
- appendStream
- abort
- remove
- abortIfUpdating
- removedFromMediaSource
- hasPendingActivity
- suspend
- resume
- stop
- executionContext
- interfaceName
- isRemoved
- scheduleEvent
- appendBufferInternal
- appendBufferAsyncPart
- removeAsyncPart
- appendStreamInternal
- appendStreamAsyncPart
- appendStreamDone
- clearAppendStreamState
- didStartLoading
- didReceiveDataForClient
- didFinishLoading
- didFail
- trace
#include "config.h"
#include "modules/mediasource/SourceBuffer.h"
#include "bindings/v8/ExceptionMessages.h"
#include "bindings/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/ExecutionContext.h"
#include "core/events/Event.h"
#include "core/events/GenericEventQueue.h"
#include "core/fileapi/FileReaderLoader.h"
#include "core/fileapi/Stream.h"
#include "core/html/TimeRanges.h"
#include "modules/mediasource/MediaSource.h"
#include "platform/Logging.h"
#include "platform/TraceEvent.h"
#include "public/platform/WebSourceBuffer.h"
#include "wtf/ArrayBuffer.h"
#include "wtf/ArrayBufferView.h"
#include "wtf/MathExtras.h"
#include <limits>
using blink::WebSourceBuffer;
namespace WebCore {
namespace {
static bool throwExceptionIfRemovedOrUpdating(bool isRemoved, bool isUpdating, ExceptionState& exceptionState)
{
if (isRemoved) {
exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
return true;
}
if (isUpdating) {
exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer is still processing an 'appendBuffer', 'appendStream', or 'remove' operation.");
return true;
}
return false;
}
}
PassRefPtrWillBeRawPtr<SourceBuffer> SourceBuffer::create(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
{
RefPtrWillBeRawPtr<SourceBuffer> sourceBuffer(adoptRefWillBeRefCountedGarbageCollected(new SourceBuffer(webSourceBuffer, source, asyncEventQueue)));
sourceBuffer->suspendIfNeeded();
return sourceBuffer.release();
}
SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
: ActiveDOMObject(source->executionContext())
, m_webSourceBuffer(webSourceBuffer)
, m_source(source)
, m_asyncEventQueue(asyncEventQueue)
, m_mode(segmentsKeyword())
, m_updating(false)
, m_timestampOffset(0)
, m_appendWindowStart(0)
, m_appendWindowEnd(std::numeric_limits<double>::infinity())
, m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart)
, m_pendingRemoveStart(-1)
, m_pendingRemoveEnd(-1)
, m_removeAsyncPartRunner(this, &SourceBuffer::removeAsyncPart)
, m_streamMaxSizeValid(false)
, m_streamMaxSize(0)
, m_appendStreamAsyncPartRunner(this, &SourceBuffer::appendStreamAsyncPart)
{
ASSERT(m_webSourceBuffer);
ASSERT(m_source);
ScriptWrappable::init(this);
}
SourceBuffer::~SourceBuffer()
{
ASSERT(isRemoved());
ASSERT(!m_loader);
ASSERT(!m_stream);
}
const AtomicString& SourceBuffer::segmentsKeyword()
{
DEFINE_STATIC_LOCAL(const AtomicString, segments, ("segments", AtomicString::ConstructFromLiteral));
return segments;
}
const AtomicString& SourceBuffer::sequenceKeyword()
{
DEFINE_STATIC_LOCAL(const AtomicString, sequence, ("sequence", AtomicString::ConstructFromLiteral));
return sequence;
}
void SourceBuffer::setMode(const AtomicString& newMode, ExceptionState& exceptionState)
{
if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
return;
m_source->openIfInEndedState();
WebSourceBuffer::AppendMode appendMode = WebSourceBuffer::AppendModeSegments;
if (newMode == sequenceKeyword())
appendMode = WebSourceBuffer::AppendModeSequence;
if (!m_webSourceBuffer->setMode(appendMode)) {
exceptionState.throwDOMException(InvalidStateError, "The mode may not be set while the SourceBuffer's append state is 'PARSING_MEDIA_SEGMENT'.");
return;
}
m_mode = newMode;
}
PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionState& exceptionState) const
{
if (isRemoved()) {
exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
return nullptr;
}
return TimeRanges::create(m_webSourceBuffer->buffered());
}
double SourceBuffer::timestampOffset() const
{
return m_timestampOffset;
}
void SourceBuffer::setTimestampOffset(double offset, ExceptionState& exceptionState)
{
if (!std::isfinite(offset)) {
exceptionState.throwTypeError(ExceptionMessages::notAFiniteNumber(offset));
return;
}
if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
return;
m_source->openIfInEndedState();
if (!m_webSourceBuffer->setTimestampOffset(offset)) {
exceptionState.throwDOMException(InvalidStateError, "The timestamp offset may not be set while the SourceBuffer's append state is 'PARSING_MEDIA_SEGMENT'.");
return;
}
m_timestampOffset = offset;
}
double SourceBuffer::appendWindowStart() const
{
return m_appendWindowStart;
}
void SourceBuffer::setAppendWindowStart(double start, ExceptionState& exceptionState)
{
if (!std::isfinite(start)) {
exceptionState.throwTypeError(ExceptionMessages::notAFiniteNumber(start));
return;
}
if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
return;
if (start < 0 || start >= m_appendWindowEnd) {
exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexOutsideRange("value", start, 0.0, ExceptionMessages::ExclusiveBound, m_appendWindowEnd, ExceptionMessages::InclusiveBound));
return;
}
m_webSourceBuffer->setAppendWindowStart(start);
m_appendWindowStart = start;
}
double SourceBuffer::appendWindowEnd() const
{
return m_appendWindowEnd;
}
void SourceBuffer::setAppendWindowEnd(double end, ExceptionState& exceptionState)
{
if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
return;
if (std::isnan(end)) {
exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::notAFiniteNumber(end));
return;
}
if (end <= m_appendWindowStart) {
exceptionState.throwDOMException(InvalidAccessError, "The value provided ('" + String::number(end) + "') is less than or equal to the minimum value (" + String::number(m_appendWindowStart) + ").");
return;
}
m_webSourceBuffer->setAppendWindowEnd(end);
m_appendWindowEnd = end;
}
void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionState& exceptionState)
{
if (!data) {
exceptionState.throwDOMException(InvalidAccessError, "The ArrayBuffer provided is invalid.");
return;
}
appendBufferInternal(static_cast<const unsigned char*>(data->data()), data->byteLength(), exceptionState);
}
void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState)
{
if (!data) {
exceptionState.throwDOMException(InvalidAccessError, "The ArrayBuffer provided is invalid.");
return;
}
appendBufferInternal(static_cast<const unsigned char*>(data->baseAddress()), data->byteLength(), exceptionState);
}
void SourceBuffer::appendStream(PassRefPtrWillBeRawPtr<Stream> stream, ExceptionState& exceptionState)
{
m_streamMaxSizeValid = false;
appendStreamInternal(stream, exceptionState);
}
void SourceBuffer::appendStream(PassRefPtrWillBeRawPtr<Stream> stream, unsigned long long maxSize, ExceptionState& exceptionState)
{
m_streamMaxSizeValid = maxSize > 0;
if (m_streamMaxSizeValid)
m_streamMaxSize = maxSize;
appendStreamInternal(stream, exceptionState);
}
void SourceBuffer::abort(ExceptionState& exceptionState)
{
if (isRemoved()) {
exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
return;
}
if (!m_source->isOpen()) {
exceptionState.throwDOMException(InvalidStateError, "The parent media source's readyState is not 'open'.");
return;
}
abortIfUpdating();
m_webSourceBuffer->abort();
setAppendWindowStart(0, exceptionState);
setAppendWindowEnd(std::numeric_limits<double>::infinity(), exceptionState);
}
void SourceBuffer::remove(double start, double end, ExceptionState& exceptionState)
{
if (start < 0 || (m_source && (std::isnan(m_source->duration()) || start > m_source->duration()))) {
exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexOutsideRange("start", start, 0.0, ExceptionMessages::ExclusiveBound, !m_source || std::isnan(m_source->duration()) ? 0 : m_source->duration(), ExceptionMessages::ExclusiveBound));
return;
}
if (end <= start) {
exceptionState.throwDOMException(InvalidAccessError, "The end value provided (" + String::number(end) + ") must be greater than the start value provided (" + String::number(start) + ").");
return;
}
if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
return;
TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::remove", this);
m_source->openIfInEndedState();
m_updating = true;
scheduleEvent(EventTypeNames::updatestart);
m_pendingRemoveStart = start;
m_pendingRemoveEnd = end;
m_removeAsyncPartRunner.runAsync();
}
void SourceBuffer::abortIfUpdating()
{
if (!m_updating)
return;
const char* traceEventName = 0;
if (!m_pendingAppendData.isEmpty()) {
traceEventName = "SourceBuffer::appendBuffer";
} else if (m_stream) {
traceEventName = "SourceBuffer::appendStream";
} else if (m_pendingRemoveStart != -1) {
traceEventName = "SourceBuffer::remove";
} else {
ASSERT_NOT_REACHED();
}
m_appendBufferAsyncPartRunner.stop();
m_pendingAppendData.clear();
m_removeAsyncPartRunner.stop();
m_pendingRemoveStart = -1;
m_pendingRemoveEnd = -1;
m_appendStreamAsyncPartRunner.stop();
clearAppendStreamState();
m_updating = false;
scheduleEvent(EventTypeNames::abort);
scheduleEvent(EventTypeNames::updateend);
TRACE_EVENT_ASYNC_END0("media", traceEventName, this);
}
void SourceBuffer::removedFromMediaSource()
{
if (isRemoved())
return;
abortIfUpdating();
m_webSourceBuffer->removedFromMediaSource();
m_webSourceBuffer.clear();
m_source = nullptr;
m_asyncEventQueue = 0;
}
bool SourceBuffer::hasPendingActivity() const
{
return m_source;
}
void SourceBuffer::suspend()
{
m_appendBufferAsyncPartRunner.suspend();
m_removeAsyncPartRunner.suspend();
m_appendStreamAsyncPartRunner.suspend();
}
void SourceBuffer::resume()
{
m_appendBufferAsyncPartRunner.resume();
m_removeAsyncPartRunner.resume();
m_appendStreamAsyncPartRunner.resume();
}
void SourceBuffer::stop()
{
m_appendBufferAsyncPartRunner.stop();
m_removeAsyncPartRunner.stop();
m_appendStreamAsyncPartRunner.stop();
}
ExecutionContext* SourceBuffer::executionContext() const
{
return ActiveDOMObject::executionContext();
}
const AtomicString& SourceBuffer::interfaceName() const
{
return EventTargetNames::SourceBuffer;
}
bool SourceBuffer::isRemoved() const
{
return !m_source;
}
void SourceBuffer::scheduleEvent(const AtomicString& eventName)
{
ASSERT(m_asyncEventQueue);
RefPtrWillBeRawPtr<Event> event = Event::create(eventName);
event->setTarget(this);
m_asyncEventQueue->enqueueEvent(event.release());
}
void SourceBuffer::appendBufferInternal(const unsigned char* data, unsigned size, ExceptionState& exceptionState)
{
if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
return;
TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendBuffer", this);
m_source->openIfInEndedState();
m_pendingAppendData.append(data, size);
m_updating = true;
scheduleEvent(EventTypeNames::updatestart);
m_appendBufferAsyncPartRunner.runAsync();
TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "waiting");
}
void SourceBuffer::appendBufferAsyncPart()
{
ASSERT(m_updating);
TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "appending");
size_t appendSize = m_pendingAppendData.size();
if (!appendSize) {
m_pendingAppendData.resize(1);
}
m_webSourceBuffer->append(m_pendingAppendData.data(), appendSize, &m_timestampOffset);
m_updating = false;
m_pendingAppendData.clear();
scheduleEvent(EventTypeNames::update);
scheduleEvent(EventTypeNames::updateend);
TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendBuffer", this);
}
void SourceBuffer::removeAsyncPart()
{
ASSERT(m_updating);
ASSERT(m_pendingRemoveStart >= 0);
ASSERT(m_pendingRemoveStart < m_pendingRemoveEnd);
m_webSourceBuffer->remove(m_pendingRemoveStart, m_pendingRemoveEnd);
m_updating = false;
m_pendingRemoveStart = -1;
m_pendingRemoveEnd = -1;
scheduleEvent(EventTypeNames::update);
scheduleEvent(EventTypeNames::updateend);
}
void SourceBuffer::appendStreamInternal(PassRefPtrWillBeRawPtr<Stream> stream, ExceptionState& exceptionState)
{
if (!stream || stream->isNeutered()) {
exceptionState.throwDOMException(InvalidAccessError, stream ? "The stream provided has been neutered." : "The stream provided is invalid.");
return;
}
if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
return;
TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendStream", this);
m_source->openIfInEndedState();
m_updating = true;
scheduleEvent(EventTypeNames::updatestart);
stream->neuter();
m_loader = adoptPtr(new FileReaderLoader(FileReaderLoader::ReadByClient, this));
m_stream = stream;
m_appendStreamAsyncPartRunner.runAsync();
}
void SourceBuffer::appendStreamAsyncPart()
{
ASSERT(m_updating);
ASSERT(m_loader);
ASSERT(m_stream);
if (m_streamMaxSizeValid && !m_streamMaxSize) {
appendStreamDone(true);
return;
}
m_loader->start(executionContext(), *m_stream, m_streamMaxSizeValid ? m_streamMaxSize : 0);
}
void SourceBuffer::appendStreamDone(bool success)
{
ASSERT(m_updating);
ASSERT(m_loader);
ASSERT(m_stream);
clearAppendStreamState();
if (!success) {
m_updating = false;
scheduleEvent(EventTypeNames::error);
scheduleEvent(EventTypeNames::updateend);
TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
return;
}
m_updating = false;
scheduleEvent(EventTypeNames::update);
scheduleEvent(EventTypeNames::updateend);
TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
}
void SourceBuffer::clearAppendStreamState()
{
m_streamMaxSizeValid = false;
m_streamMaxSize = 0;
m_loader.clear();
m_stream = nullptr;
}
void SourceBuffer::didStartLoading()
{
WTF_LOG(Media, "SourceBuffer::didStartLoading() %p", this);
}
void SourceBuffer::didReceiveDataForClient(const char* data, unsigned dataLength)
{
WTF_LOG(Media, "SourceBuffer::didReceiveDataForClient(%d) %p", dataLength, this);
ASSERT(m_updating);
ASSERT(m_loader);
m_webSourceBuffer->append(reinterpret_cast<const unsigned char*>(data), dataLength, &m_timestampOffset);
}
void SourceBuffer::didFinishLoading()
{
WTF_LOG(Media, "SourceBuffer::didFinishLoading() %p", this);
appendStreamDone(true);
}
void SourceBuffer::didFail(FileError::ErrorCode errorCode)
{
WTF_LOG(Media, "SourceBuffer::didFail(%d) %p", errorCode, this);
appendStreamDone(false);
}
void SourceBuffer::trace(Visitor* visitor)
{
visitor->trace(m_source);
visitor->trace(m_stream);
}
}