#include "config.h"
#include "MediaSource.h"
#if ENABLE(MEDIA_SOURCE)
#include "ContentType.h"
#include "Event.h"
#include "MIMETypeRegistry.h"
#include "SourceBufferPrivate.h"
#include "TimeRanges.h"
#include <wtf/Uint8Array.h>
namespace WebCore {
PassRefPtr<MediaSource> MediaSource::create(ScriptExecutionContext* context)
{
RefPtr<MediaSource> mediaSource(adoptRef(new MediaSource(context)));
mediaSource->suspendIfNeeded();
return mediaSource.release();
}
MediaSource::MediaSource(ScriptExecutionContext* context)
: ActiveDOMObject(context)
, m_readyState(closedKeyword())
, m_asyncEventQueue(GenericEventQueue::create(this))
{
m_sourceBuffers = SourceBufferList::create(scriptExecutionContext(), m_asyncEventQueue.get());
m_activeSourceBuffers = SourceBufferList::create(scriptExecutionContext(), m_asyncEventQueue.get());
}
const String& MediaSource::openKeyword()
{
DEFINE_STATIC_LOCAL(const String, open, (ASCIILiteral("open")));
return open;
}
const String& MediaSource::closedKeyword()
{
DEFINE_STATIC_LOCAL(const String, closed, (ASCIILiteral("closed")));
return closed;
}
const String& MediaSource::endedKeyword()
{
DEFINE_STATIC_LOCAL(const String, ended, (ASCIILiteral("ended")));
return ended;
}
SourceBufferList* MediaSource::sourceBuffers()
{
return m_sourceBuffers.get();
}
SourceBufferList* MediaSource::activeSourceBuffers()
{
return m_activeSourceBuffers.get();
}
double MediaSource::duration() const
{
return m_readyState == closedKeyword() ? std::numeric_limits<float>::quiet_NaN() : m_private->duration();
}
void MediaSource::setDuration(double duration, ExceptionCode& ec)
{
if (duration < 0.0 || std::isnan(duration)) {
ec = INVALID_ACCESS_ERR;
return;
}
if (m_readyState != openKeyword()) {
ec = INVALID_STATE_ERR;
return;
}
m_private->setDuration(duration);
}
SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionCode& ec)
{
if (type.isNull() || type.isEmpty()) {
ec = INVALID_ACCESS_ERR;
return 0;
}
if (!isTypeSupported(type)) {
ec = NOT_SUPPORTED_ERR;
return 0;
}
if (!m_private || m_readyState != openKeyword()) {
ec = INVALID_STATE_ERR;
return 0;
}
ContentType contentType(type);
Vector<String> codecs = contentType.codecs();
OwnPtr<SourceBufferPrivate> sourceBufferPrivate;
switch (m_private->addSourceBuffer(contentType.type(), codecs, &sourceBufferPrivate)) {
case MediaSourcePrivate::Ok: {
ASSERT(sourceBufferPrivate);
RefPtr<SourceBuffer> buffer = SourceBuffer::create(sourceBufferPrivate.release(), this);
m_sourceBuffers->add(buffer);
m_activeSourceBuffers->add(buffer);
return buffer.get();
}
case MediaSourcePrivate::NotSupported:
ec = NOT_SUPPORTED_ERR;
return 0;
case MediaSourcePrivate::ReachedIdLimit:
ec = QUOTA_EXCEEDED_ERR;
return 0;
}
ASSERT_NOT_REACHED();
return 0;
}
void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionCode& ec)
{
if (!buffer) {
ec = INVALID_ACCESS_ERR;
return;
}
if (!m_private || !m_sourceBuffers->length()) {
ec = INVALID_STATE_ERR;
return;
}
if (!m_sourceBuffers->remove(buffer)) {
ec = NOT_FOUND_ERR;
return;
}
m_activeSourceBuffers->remove(buffer);
}
const String& MediaSource::readyState() const
{
return m_readyState;
}
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_private.clear();
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)
{
if (!m_private || m_readyState != openKeyword()) {
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;
}
setReadyState(endedKeyword());
m_private->endOfStream(eosStatus);
}
bool MediaSource::isTypeSupported(const String& type)
{
if (type.isNull() || type.isEmpty())
return false;
ContentType contentType(type);
String codecs = contentType.parameter("codecs");
if (contentType.type().isEmpty() || codecs.isEmpty())
return false;
return MIMETypeRegistry::isSupportedMediaSourceMIMEType(contentType.type(), codecs);
}
void MediaSource::setPrivateAndOpen(PassOwnPtr<MediaSourcePrivate> mediaSourcePrivate)
{
ASSERT(mediaSourcePrivate);
ASSERT(!m_private);
m_private = mediaSourcePrivate;
setReadyState(openKeyword());
}
const AtomicString& MediaSource::interfaceName() const
{
return eventNames().interfaceForMediaSource;
}
ScriptExecutionContext* MediaSource::scriptExecutionContext() const
{
return ActiveDOMObject::scriptExecutionContext();
}
bool MediaSource::hasPendingActivity() const
{
return m_private || m_asyncEventQueue->hasPendingEvents()
|| ActiveDOMObject::hasPendingActivity();
}
void MediaSource::stop()
{
m_private.clear();
m_asyncEventQueue->cancelAllEvents();
}
EventTargetData* MediaSource::eventTargetData()
{
return &m_eventTargetData;
}
EventTargetData* MediaSource::ensureEventTargetData()
{
return &m_eventTargetData;
}
void MediaSource::scheduleEvent(const AtomicString& eventName)
{
ASSERT(m_asyncEventQueue);
RefPtr<Event> event = Event::create(eventName, false, false);
event->setTarget(this);
m_asyncEventQueue->enqueueEvent(event.release());
}
}
#endif