MediaSource.cpp 11 KB

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