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/v8/ExceptionState.h" 35 #include "bindings/v8/ExceptionStatePlaceholder.h" 36 #include "core/dom/ExceptionCode.h" 37 #include "core/dom/GenericEventQueue.h" 38 #include "core/html/TimeRanges.h" 39 #include "core/platform/ContentType.h" 40 #include "core/platform/Logging.h" 41 #include "core/platform/MIMETypeRegistry.h" 42 #include "core/platform/graphics/SourceBufferPrivate.h" 43 #include "modules/mediasource/MediaSourceRegistry.h" 44 #include "wtf/Uint8Array.h" 45 #include "wtf/text/CString.h" 46 47 namespace WebCore { 48 49 PassRefPtr<MediaSource> MediaSource::create(ScriptExecutionContext* context) 50 { 51 RefPtr<MediaSource> mediaSource(adoptRef(new MediaSource(context))); 52 mediaSource->suspendIfNeeded(); 53 return mediaSource.release(); 54 } 55 56 MediaSource::MediaSource(ScriptExecutionContext* context) 57 : MediaSourceBase(context) 58 { 59 LOG(Media, "MediaSource::MediaSource %p", this); 60 ScriptWrappable::init(this); 61 m_sourceBuffers = SourceBufferList::create(scriptExecutionContext(), asyncEventQueue()); 62 m_activeSourceBuffers = SourceBufferList::create(scriptExecutionContext(), asyncEventQueue()); 63 } 64 65 MediaSource::~MediaSource() 66 { 67 LOG(Media, "MediaSource::~MediaSource %p", this); 68 ASSERT(isClosed()); 69 } 70 71 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& es) 72 { 73 LOG(Media, "MediaSource::addSourceBuffer(%s) %p", type.ascii().data(), this); 74 75 // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type 76 // 1. If type is null or an empty then throw an InvalidAccessError exception and 77 // abort these steps. 78 if (type.isNull() || type.isEmpty()) { 79 es.throwDOMException(InvalidAccessError); 80 return 0; 81 } 82 83 // 2. If type contains a MIME type that is not supported ..., then throw a 84 // NotSupportedError exception and abort these steps. 85 if (!isTypeSupported(type)) { 86 es.throwDOMException(NotSupportedError); 87 return 0; 88 } 89 90 // 4. If the readyState attribute is not in the "open" state then throw an 91 // InvalidStateError exception and abort these steps. 92 if (!isOpen()) { 93 es.throwDOMException(InvalidStateError); 94 return 0; 95 } 96 97 // 5. Create a new SourceBuffer object and associated resources. 98 ContentType contentType(type); 99 Vector<String> codecs = contentType.codecs(); 100 OwnPtr<SourceBufferPrivate> sourceBufferPrivate = createSourceBufferPrivate(contentType.type(), codecs, es); 101 102 if (!sourceBufferPrivate) { 103 ASSERT(es.code() == NotSupportedError || es.code() == QuotaExceededError); 104 // 2. If type contains a MIME type that is not supported ..., then throw a NotSupportedError exception and abort these steps. 105 // 3. If the user agent can't handle any more SourceBuffer objects then throw a QuotaExceededError exception and abort these steps 106 return 0; 107 } 108 109 RefPtr<SourceBuffer> buffer = SourceBuffer::create(sourceBufferPrivate.release(), this, asyncEventQueue()); 110 // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object. 111 m_sourceBuffers->add(buffer); 112 m_activeSourceBuffers->add(buffer); 113 // 7. Return the new object to the caller. 114 return buffer.get(); 115 } 116 117 void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionState& es) 118 { 119 LOG(Media, "MediaSource::removeSourceBuffer() %p", this); 120 RefPtr<SourceBuffer> protect(buffer); 121 122 // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-removeSourceBuffer-void-SourceBuffer-sourceBuffer 123 // 1. If sourceBuffer is null then throw an InvalidAccessError exception and 124 // abort these steps. 125 if (!buffer) { 126 es.throwDOMException(InvalidAccessError); 127 return; 128 } 129 130 // 2. If sourceBuffer specifies an object that is not in sourceBuffers then 131 // throw a NotFoundError exception and abort these steps. 132 if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer)) { 133 es.throwDOMException(NotFoundError); 134 return; 135 } 136 137 // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ... 138 buffer->abortIfUpdating(); 139 140 // Steps 4-9 are related to updating audioTracks, videoTracks, and textTracks which aren't implmented yet. 141 // FIXME(91649): support track selection 142 143 // 10. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ... 144 m_activeSourceBuffers->remove(buffer); 145 146 // 11. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event 147 // on that object. 148 m_sourceBuffers->remove(buffer); 149 150 // 12. Destroy all resources for sourceBuffer. 151 buffer->removedFromMediaSource(); 152 } 153 154 void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState) 155 { 156 if (isOpen()) { 157 scheduleEvent(eventNames().sourceopenEvent); 158 return; 159 } 160 161 if (oldState == openKeyword() && newState == endedKeyword()) { 162 scheduleEvent(eventNames().sourceendedEvent); 163 return; 164 } 165 166 ASSERT(isClosed()); 167 168 m_activeSourceBuffers->clear(); 169 170 // Clear SourceBuffer references to this object. 171 for (unsigned long i = 0; i < m_sourceBuffers->length(); ++i) 172 m_sourceBuffers->item(i)->removedFromMediaSource(); 173 m_sourceBuffers->clear(); 174 175 scheduleEvent(eventNames().sourcecloseEvent); 176 } 177 178 Vector<RefPtr<TimeRanges> > MediaSource::activeRanges() const 179 { 180 Vector<RefPtr<TimeRanges> > activeRanges(m_activeSourceBuffers->length()); 181 for (size_t i = 0; i < m_activeSourceBuffers->length(); ++i) 182 activeRanges[i] = m_activeSourceBuffers->item(i)->buffered(ASSERT_NO_EXCEPTION); 183 184 return activeRanges; 185 } 186 187 bool MediaSource::isTypeSupported(const String& type) 188 { 189 LOG(Media, "MediaSource::isTypeSupported(%s)", type.ascii().data()); 190 191 // Section 2.2 isTypeSupported() method steps. 192 // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-isTypeSupported-boolean-DOMString-type 193 // 1. If type is an empty string, then return false. 194 if (type.isNull() || type.isEmpty()) 195 return false; 196 197 ContentType contentType(type); 198 String codecs = contentType.parameter("codecs"); 199 200 // 2. If type does not contain a valid MIME type string, then return false. 201 if (contentType.type().isEmpty() || codecs.isEmpty()) 202 return false; 203 204 // 3. If type contains a media type or media subtype that the MediaSource does not support, then return false. 205 // 4. If type contains at a codec that the MediaSource does not support, then return false. 206 // 5. If the MediaSource does not support the specified combination of media type, media subtype, and codecs then return false. 207 // 6. Return true. 208 return MIMETypeRegistry::isSupportedMediaSourceMIMEType(contentType.type(), codecs); 209 } 210 211 const AtomicString& MediaSource::interfaceName() const 212 { 213 return eventNames().interfaceForMediaSource; 214 } 215 216 } // namespace WebCore 217