| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2012 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 "core/dom/Event.h" | |
| 35 #include "core/html/TimeRanges.h" | |
| 36 #include "core/platform/ContentType.h" | |
| 37 #include "core/platform/MIMETypeRegistry.h" | |
| 38 #include "core/platform/graphics/SourceBufferPrivate.h" | |
| 39 #include "wtf/Uint8Array.h" | |
| 40 | |
| 41 namespace WebCore { | |
| 42 | |
| 43 PassRefPtr<MediaSource> MediaSource::create(ScriptExecutionContext* context) | |
| 44 { | |
| 45 RefPtr<MediaSource> mediaSource(adoptRef(new MediaSource(context))); | |
| 46 mediaSource->suspendIfNeeded(); | |
| 47 return mediaSource.release(); | |
| 48 } | |
| 49 | |
| 50 MediaSource::MediaSource(ScriptExecutionContext* context) | |
| 51 : ActiveDOMObject(context) | |
| 52 , m_readyState(closedKeyword()) | |
| 53 , m_asyncEventQueue(GenericEventQueue::create(this)) | |
| 54 { | |
| 55 m_sourceBuffers = SourceBufferList::create(scriptExecutionContext(), m_async
EventQueue.get()); | |
| 56 m_activeSourceBuffers = SourceBufferList::create(scriptExecutionContext(), m
_asyncEventQueue.get()); | |
| 57 } | |
| 58 | |
| 59 const String& MediaSource::openKeyword() | |
| 60 { | |
| 61 DEFINE_STATIC_LOCAL(const String, open, (ASCIILiteral("open"))); | |
| 62 return open; | |
| 63 } | |
| 64 | |
| 65 const String& MediaSource::closedKeyword() | |
| 66 { | |
| 67 DEFINE_STATIC_LOCAL(const String, closed, (ASCIILiteral("closed"))); | |
| 68 return closed; | |
| 69 } | |
| 70 | |
| 71 const String& MediaSource::endedKeyword() | |
| 72 { | |
| 73 DEFINE_STATIC_LOCAL(const String, ended, (ASCIILiteral("ended"))); | |
| 74 return ended; | |
| 75 } | |
| 76 | |
| 77 SourceBufferList* MediaSource::sourceBuffers() | |
| 78 { | |
| 79 return m_sourceBuffers.get(); | |
| 80 } | |
| 81 | |
| 82 SourceBufferList* MediaSource::activeSourceBuffers() | |
| 83 { | |
| 84 // FIXME(91649): support track selection | |
| 85 return m_activeSourceBuffers.get(); | |
| 86 } | |
| 87 | |
| 88 double MediaSource::duration() const | |
| 89 { | |
| 90 return m_readyState == closedKeyword() ? std::numeric_limits<float>::quiet_N
aN() : m_private->duration(); | |
| 91 } | |
| 92 | |
| 93 void MediaSource::setDuration(double duration, ExceptionCode& ec) | |
| 94 { | |
| 95 if (duration < 0.0 || std::isnan(duration)) { | |
| 96 ec = INVALID_ACCESS_ERR; | |
| 97 return; | |
| 98 } | |
| 99 if (m_readyState != openKeyword()) { | |
| 100 ec = INVALID_STATE_ERR; | |
| 101 return; | |
| 102 } | |
| 103 m_private->setDuration(duration); | |
| 104 } | |
| 105 | |
| 106 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionCode& ec
) | |
| 107 { | |
| 108 // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-sour
ce.html#dom-addsourcebuffer | |
| 109 // 1. If type is null or an empty then throw an INVALID_ACCESS_ERR exception
and | |
| 110 // abort these steps. | |
| 111 if (type.isNull() || type.isEmpty()) { | |
| 112 ec = INVALID_ACCESS_ERR; | |
| 113 return 0; | |
| 114 } | |
| 115 | |
| 116 // 2. If type contains a MIME type that is not supported ..., then throw a | |
| 117 // NOT_SUPPORTED_ERR exception and abort these steps. | |
| 118 if (!isTypeSupported(type)) { | |
| 119 ec = NOT_SUPPORTED_ERR; | |
| 120 return 0; | |
| 121 } | |
| 122 | |
| 123 // 4. If the readyState attribute is not in the "open" state then throw an | |
| 124 // INVALID_STATE_ERR exception and abort these steps. | |
| 125 if (!m_private || m_readyState != openKeyword()) { | |
| 126 ec = INVALID_STATE_ERR; | |
| 127 return 0; | |
| 128 } | |
| 129 | |
| 130 // 5. Create a new SourceBuffer object and associated resources. | |
| 131 ContentType contentType(type); | |
| 132 Vector<String> codecs = contentType.codecs(); | |
| 133 OwnPtr<SourceBufferPrivate> sourceBufferPrivate; | |
| 134 switch (m_private->addSourceBuffer(contentType.type(), codecs, &sourceBuffer
Private)) { | |
| 135 case MediaSourcePrivate::Ok: { | |
| 136 ASSERT(sourceBufferPrivate); | |
| 137 RefPtr<SourceBuffer> buffer = SourceBuffer::create(sourceBufferPrivate.r
elease(), this); | |
| 138 | |
| 139 // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on
that object. | |
| 140 m_sourceBuffers->add(buffer); | |
| 141 m_activeSourceBuffers->add(buffer); | |
| 142 // 7. Return the new object to the caller. | |
| 143 return buffer.get(); | |
| 144 } | |
| 145 case MediaSourcePrivate::NotSupported: | |
| 146 // 2 (cont). If type contains a MIME type ... that is not supported with
the types | |
| 147 // specified for the other SourceBuffer objects in sourceBuffers, then t
hrow | |
| 148 // a NOT_SUPPORTED_ERR exception and abort these steps. | |
| 149 ec = NOT_SUPPORTED_ERR; | |
| 150 return 0; | |
| 151 case MediaSourcePrivate::ReachedIdLimit: | |
| 152 // 3 (cont). If the user agent can't handle any more SourceBuffer object
s then throw | |
| 153 // a QUOTA_EXCEEDED_ERR exception and abort these steps. | |
| 154 ec = QUOTA_EXCEEDED_ERR; | |
| 155 return 0; | |
| 156 } | |
| 157 | |
| 158 ASSERT_NOT_REACHED(); | |
| 159 return 0; | |
| 160 } | |
| 161 | |
| 162 void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionCode& ec) | |
| 163 { | |
| 164 // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-sour
ce.html#dom-removesourcebuffer | |
| 165 // 1. If sourceBuffer is null then throw an INVALID_ACCESS_ERR exception and | |
| 166 // abort these steps. | |
| 167 if (!buffer) { | |
| 168 ec = INVALID_ACCESS_ERR; | |
| 169 return; | |
| 170 } | |
| 171 | |
| 172 // 2. If sourceBuffers is empty then throw an INVALID_STATE_ERR exception an
d | |
| 173 // abort these steps. | |
| 174 if (!m_private || !m_sourceBuffers->length()) { | |
| 175 ec = INVALID_STATE_ERR; | |
| 176 return; | |
| 177 } | |
| 178 | |
| 179 // 3. If sourceBuffer specifies an object that is not in sourceBuffers then | |
| 180 // throw a NOT_FOUND_ERR exception and abort these steps. | |
| 181 // 6. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer e
vent | |
| 182 // on that object. | |
| 183 if (!m_sourceBuffers->remove(buffer)) { | |
| 184 ec = NOT_FOUND_ERR; | |
| 185 return; | |
| 186 } | |
| 187 | |
| 188 // 7. Destroy all resources for sourceBuffer. | |
| 189 m_activeSourceBuffers->remove(buffer); | |
| 190 | |
| 191 // 4. Remove track information from audioTracks, videoTracks, and textTracks
for all tracks | |
| 192 // associated with sourceBuffer and fire a simple event named change on the
modified lists. | |
| 193 // FIXME(91649): support track selection | |
| 194 | |
| 195 // 5. If sourceBuffer is in activeSourceBuffers, then remove it from that li
st and fire a | |
| 196 // removesourcebuffer event on that object. | |
| 197 // FIXME(91649): support track selection | |
| 198 } | |
| 199 | |
| 200 const String& MediaSource::readyState() const | |
| 201 { | |
| 202 return m_readyState; | |
| 203 } | |
| 204 | |
| 205 void MediaSource::setReadyState(const String& state) | |
| 206 { | |
| 207 ASSERT(state == openKeyword() || state == closedKeyword() || state == endedK
eyword()); | |
| 208 if (m_readyState == state) | |
| 209 return; | |
| 210 | |
| 211 String oldState = m_readyState; | |
| 212 m_readyState = state; | |
| 213 | |
| 214 if (m_readyState == closedKeyword()) { | |
| 215 m_sourceBuffers->clear(); | |
| 216 m_activeSourceBuffers->clear(); | |
| 217 m_private.clear(); | |
| 218 scheduleEvent(eventNames().webkitsourcecloseEvent); | |
| 219 return; | |
| 220 } | |
| 221 | |
| 222 if (oldState == openKeyword() && m_readyState == endedKeyword()) { | |
| 223 scheduleEvent(eventNames().webkitsourceendedEvent); | |
| 224 return; | |
| 225 } | |
| 226 | |
| 227 if (m_readyState == openKeyword()) { | |
| 228 scheduleEvent(eventNames().webkitsourceopenEvent); | |
| 229 return; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 void MediaSource::endOfStream(const String& error, ExceptionCode& ec) | |
| 234 { | |
| 235 // 3.1 http://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-sour
ce.html#dom-endofstream | |
| 236 // 1. If the readyState attribute is not in the "open" state then throw an | |
| 237 // INVALID_STATE_ERR exception and abort these steps. | |
| 238 if (!m_private || m_readyState != openKeyword()) { | |
| 239 ec = INVALID_STATE_ERR; | |
| 240 return; | |
| 241 } | |
| 242 | |
| 243 MediaSourcePrivate::EndOfStreamStatus eosStatus = MediaSourcePrivate::EosNoE
rror; | |
| 244 | |
| 245 if (error.isNull() || error.isEmpty()) | |
| 246 eosStatus = MediaSourcePrivate::EosNoError; | |
| 247 else if (error == "network") | |
| 248 eosStatus = MediaSourcePrivate::EosNetworkError; | |
| 249 else if (error == "decode") | |
| 250 eosStatus = MediaSourcePrivate::EosDecodeError; | |
| 251 else { | |
| 252 ec = INVALID_ACCESS_ERR; | |
| 253 return; | |
| 254 } | |
| 255 | |
| 256 // 2. Change the readyState attribute value to "ended". | |
| 257 setReadyState(endedKeyword()); | |
| 258 m_private->endOfStream(eosStatus); | |
| 259 } | |
| 260 | |
| 261 bool MediaSource::isTypeSupported(const String& type) | |
| 262 { | |
| 263 // Section 2.1 isTypeSupported() method steps. | |
| 264 // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.
html#widl-MediaSource-isTypeSupported-boolean-DOMString-type | |
| 265 // 1. If type is an empty string, then return false. | |
| 266 if (type.isNull() || type.isEmpty()) | |
| 267 return false; | |
| 268 | |
| 269 ContentType contentType(type); | |
| 270 String codecs = contentType.parameter("codecs"); | |
| 271 | |
| 272 // 2. If type does not contain a valid MIME type string, then return false. | |
| 273 if (contentType.type().isEmpty() || codecs.isEmpty()) | |
| 274 return false; | |
| 275 | |
| 276 // 3. If type contains a media type or media subtype that the MediaSource do
es not support, then return false. | |
| 277 // 4. If type contains at a codec that the MediaSource does not support, the
n return false. | |
| 278 // 5. If the MediaSource does not support the specified combination of media
type, media subtype, and codecs then return false. | |
| 279 // 6. Return true. | |
| 280 return MIMETypeRegistry::isSupportedMediaSourceMIMEType(contentType.type(),
codecs); | |
| 281 } | |
| 282 | |
| 283 void MediaSource::setPrivateAndOpen(PassOwnPtr<MediaSourcePrivate> mediaSourcePr
ivate) | |
| 284 { | |
| 285 ASSERT(mediaSourcePrivate); | |
| 286 ASSERT(!m_private); | |
| 287 m_private = mediaSourcePrivate; | |
| 288 setReadyState(openKeyword()); | |
| 289 } | |
| 290 | |
| 291 const AtomicString& MediaSource::interfaceName() const | |
| 292 { | |
| 293 return eventNames().interfaceForMediaSource; | |
| 294 } | |
| 295 | |
| 296 ScriptExecutionContext* MediaSource::scriptExecutionContext() const | |
| 297 { | |
| 298 return ActiveDOMObject::scriptExecutionContext(); | |
| 299 } | |
| 300 | |
| 301 bool MediaSource::hasPendingActivity() const | |
| 302 { | |
| 303 return m_private || m_asyncEventQueue->hasPendingEvents() | |
| 304 || ActiveDOMObject::hasPendingActivity(); | |
| 305 } | |
| 306 | |
| 307 void MediaSource::stop() | |
| 308 { | |
| 309 m_private.clear(); | |
| 310 m_asyncEventQueue->cancelAllEvents(); | |
| 311 } | |
| 312 | |
| 313 EventTargetData* MediaSource::eventTargetData() | |
| 314 { | |
| 315 return &m_eventTargetData; | |
| 316 } | |
| 317 | |
| 318 EventTargetData* MediaSource::ensureEventTargetData() | |
| 319 { | |
| 320 return &m_eventTargetData; | |
| 321 } | |
| 322 | |
| 323 void MediaSource::scheduleEvent(const AtomicString& eventName) | |
| 324 { | |
| 325 ASSERT(m_asyncEventQueue); | |
| 326 | |
| 327 RefPtr<Event> event = Event::create(eventName, false, false); | |
| 328 event->setTarget(this); | |
| 329 | |
| 330 m_asyncEventQueue->enqueueEvent(event.release()); | |
| 331 } | |
| 332 | |
| 333 } // namespace WebCore | |
| OLD | NEW |