Add WTF::move()
[WebKit-https.git] / Source / WebCore / Modules / mediasource / MediaSource.cpp
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 "MediaSource.h"
33
34 #if ENABLE(MEDIA_SOURCE)
35
36 #include "AudioTrack.h"
37 #include "AudioTrackList.h"
38 #include "ContentType.h"
39 #include "Event.h"
40 #include "ExceptionCode.h"
41 #include "ExceptionCodePlaceholder.h"
42 #include "GenericEventQueue.h"
43 #include "HTMLMediaElement.h"
44 #include "Logging.h"
45 #include "MIMETypeRegistry.h"
46 #include "MediaError.h"
47 #include "MediaPlayer.h"
48 #include "MediaSourceRegistry.h"
49 #include "SourceBufferPrivate.h"
50 #include "TextTrack.h"
51 #include "TextTrackList.h"
52 #include "TimeRanges.h"
53 #include "VideoTrack.h"
54 #include "VideoTrackList.h"
55 #include <runtime/Uint8Array.h>
56 #include <wtf/text/CString.h>
57 #include <wtf/text/WTFString.h>
58
59 namespace WebCore {
60
61 URLRegistry* MediaSource::s_registry = 0;
62
63 void MediaSource::setRegistry(URLRegistry* registry)
64 {
65     ASSERT(!s_registry);
66     s_registry = registry;
67 }
68
69 PassRefPtr<MediaSource> MediaSource::create(ScriptExecutionContext& context)
70 {
71     RefPtr<MediaSource> mediaSource(adoptRef(new MediaSource(context)));
72     mediaSource->suspendIfNeeded();
73     return mediaSource.release();
74 }
75
76 MediaSource::MediaSource(ScriptExecutionContext& context)
77     : ActiveDOMObject(&context)
78     , m_mediaElement(0)
79     , m_readyState(closedKeyword())
80     , m_asyncEventQueue(*this)
81 {
82     LOG(Media, "MediaSource::MediaSource %p", this);
83     m_sourceBuffers = SourceBufferList::create(scriptExecutionContext());
84     m_activeSourceBuffers = SourceBufferList::create(scriptExecutionContext());
85 }
86
87 MediaSource::~MediaSource()
88 {
89     LOG(Media, "MediaSource::~MediaSource %p", this);
90     ASSERT(isClosed());
91 }
92
93 const AtomicString& MediaSource::openKeyword()
94 {
95     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, open, ("open", AtomicString::ConstructFromLiteral));
96     return open;
97 }
98
99 const AtomicString& MediaSource::closedKeyword()
100 {
101     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, closed, ("closed", AtomicString::ConstructFromLiteral));
102     return closed;
103 }
104
105 const AtomicString& MediaSource::endedKeyword()
106 {
107     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, ended, ("ended", AtomicString::ConstructFromLiteral));
108     return ended;
109 }
110
111 void MediaSource::setPrivateAndOpen(PassRef<MediaSourcePrivate> mediaSourcePrivate)
112 {
113     ASSERT(!m_private);
114     ASSERT(m_mediaElement);
115     m_private = WTF::move(mediaSourcePrivate);
116     setReadyState(openKeyword());
117 }
118
119 void MediaSource::addedToRegistry()
120 {
121     setPendingActivity(this);
122 }
123
124 void MediaSource::removedFromRegistry()
125 {
126     unsetPendingActivity(this);
127 }
128
129 double MediaSource::duration() const
130 {
131     return isClosed() ? std::numeric_limits<float>::quiet_NaN() : m_private->duration().toDouble();
132 }
133
134 double MediaSource::currentTime() const
135 {
136     return m_mediaElement ? m_mediaElement->currentTime() : 0;
137 }
138
139 std::unique_ptr<PlatformTimeRanges> MediaSource::buffered() const
140 {
141     // Implements MediaSource algorithm for HTMLMediaElement.buffered.
142     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions
143     Vector<RefPtr<TimeRanges>> ranges = activeRanges();
144
145     // 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges object and abort these steps.
146     if (ranges.isEmpty())
147         return PlatformTimeRanges::create();
148
149     // 2. Let active ranges be the ranges returned by buffered for each SourceBuffer object in activeSourceBuffers.
150     // 3. Let highest end time be the largest range end time in the active ranges.
151     double highestEndTime = -1;
152     for (size_t i = 0; i < ranges.size(); ++i) {
153         unsigned length = ranges[i]->length();
154         if (length)
155             highestEndTime = std::max(highestEndTime, ranges[i]->end(length - 1, ASSERT_NO_EXCEPTION));
156     }
157
158     // Return an empty range if all ranges are empty.
159     if (highestEndTime < 0)
160         return PlatformTimeRanges::create();
161
162     // 4. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
163     RefPtr<TimeRanges> intersectionRanges = TimeRanges::create(0, highestEndTime);
164
165     // 5. For each SourceBuffer object in activeSourceBuffers run the following steps:
166     bool ended = readyState() == endedKeyword();
167     for (size_t i = 0; i < ranges.size(); ++i) {
168         // 5.1 Let source ranges equal the ranges returned by the buffered attribute on the current SourceBuffer.
169         TimeRanges* sourceRanges = ranges[i].get();
170
171         // 5.2 If readyState is "ended", then set the end time on the last range in source ranges to highest end time.
172         if (ended && sourceRanges->length())
173             sourceRanges->add(sourceRanges->start(sourceRanges->length() - 1, ASSERT_NO_EXCEPTION), highestEndTime);
174
175         // 5.3 Let new intersection ranges equal the the intersection between the intersection ranges and the source ranges.
176         // 5.4 Replace the ranges in intersection ranges with the new intersection ranges.
177         intersectionRanges->intersectWith(*sourceRanges);
178     }
179
180     return PlatformTimeRanges::create(intersectionRanges->ranges());
181 }
182
183 void MediaSource::monitorSourceBuffers()
184 {
185     // 2.4.4 SourceBuffer Monitoring
186     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#buffer-monitoring
187
188     // Note, the behavior if activeSourceBuffers is empty is undefined.
189     if (!m_activeSourceBuffers) {
190         m_private->setReadyState(MediaPlayer::HaveNothing);
191         return;
192     }
193
194     // ↳ If buffered for all objects in activeSourceBuffers do not contain TimeRanges for the current
195     // playback position:
196     auto begin = m_activeSourceBuffers->begin();
197     auto end = m_activeSourceBuffers->end();
198     if (std::all_of(begin, end, [](RefPtr<SourceBuffer>& sourceBuffer) {
199         return !sourceBuffer->hasCurrentTime();
200     })) {
201         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
202         // 2. If this is the first transition to HAVE_METADATA, then queue a task to fire a simple event
203         // named loadedmetadata at the media element.
204         m_private->setReadyState(MediaPlayer::HaveMetadata);
205
206         // 3. Abort these steps.
207         return;
208     }
209
210     // ↳ If buffered for all objects in activeSourceBuffers contain TimeRanges that include the current
211     // playback position and enough data to ensure uninterrupted playback:
212     if (std::all_of(begin, end, [](RefPtr<SourceBuffer>& sourceBuffer) {
213         return sourceBuffer->hasFutureTime() && sourceBuffer->canPlayThrough();
214     })) {
215         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_ENOUGH_DATA.
216         // 2. Queue a task to fire a simple event named canplaythrough at the media element.
217         // 3. Playback may resume at this point if it was previously suspended by a transition to HAVE_CURRENT_DATA.
218         m_private->setReadyState(MediaPlayer::HaveEnoughData);
219
220         // 4. Abort these steps.
221         return;
222     }
223
224     // ↳ If buffered for all objects in activeSourceBuffers contain a TimeRange that includes
225     // the current playback position and some time beyond the current playback position, then run the following steps:
226     if (std::all_of(begin, end, [](RefPtr<SourceBuffer>& sourceBuffer) {
227         return sourceBuffer->hasFutureTime();
228     })) {
229         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_FUTURE_DATA.
230         // 2. If the previous value of HTMLMediaElement.readyState was less than HAVE_FUTURE_DATA, then queue a task to fire a simple event named canplay at the media element.
231         // 3. Playback may resume at this point if it was previously suspended by a transition to HAVE_CURRENT_DATA.
232         m_private->setReadyState(MediaPlayer::HaveFutureData);
233
234         // 4. Abort these steps.
235         return;
236     }
237
238     // ↳ If buffered for at least one object in activeSourceBuffers contains a TimeRange that ends
239     // at the current playback position and does not have a range covering the time immediately
240     // after the current position:
241     // NOTE: Logically, !(all objects do not contain currentTime) == (some objects contain current time)
242
243     // 1. Set the HTMLMediaElement.readyState attribute to HAVE_CURRENT_DATA.
244     // 2. If this is the first transition to HAVE_CURRENT_DATA, then queue a task to fire a simple
245     // event named loadeddata at the media element.
246     // 3. Playback is suspended at this point since the media element doesn't have enough data to
247     // advance the media timeline.
248     m_private->setReadyState(MediaPlayer::HaveCurrentData);
249     
250     // 4. Abort these steps.
251 }
252
253 void MediaSource::setDuration(double duration, ExceptionCode& ec)
254 {
255     // 2.1 Attributes - Duration
256     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#attributes
257
258     // On setting, run the following steps:
259     // 1. If the value being set is negative or NaN then throw an INVALID_ACCESS_ERR exception and abort these steps.
260     if (duration < 0.0 || std::isnan(duration)) {
261         ec = INVALID_ACCESS_ERR;
262         return;
263     }
264
265     // 2. If the readyState attribute is not "open" then throw an INVALID_STATE_ERR exception and abort these steps.
266     if (!isOpen()) {
267         ec = INVALID_STATE_ERR;
268         return;
269     }
270
271     // 3. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an INVALID_STATE_ERR
272     // exception and abort these steps.
273     for (auto& sourceBuffer : *m_sourceBuffers) {
274         if (sourceBuffer->updating()) {
275             ec = INVALID_STATE_ERR;
276             return;
277         }
278     }
279
280     // 4. Run the duration change algorithm with new duration set to the value being assigned to this attribute.
281     m_private->setDuration(MediaTime::createWithDouble(duration));
282 }
283
284
285 void MediaSource::setReadyState(const AtomicString& state)
286 {
287     ASSERT(state == openKeyword() || state == closedKeyword() || state == endedKeyword());
288
289     AtomicString oldState = readyState();
290     LOG(Media, "MediaSource::setReadyState() %p : %s -> %s", this, oldState.string().ascii().data(), state.string().ascii().data());
291
292     if (state == closedKeyword()) {
293         m_private.clear();
294         m_mediaElement = 0;
295     }
296
297     if (oldState == state)
298         return;
299
300     m_readyState = state;
301
302     onReadyStateChange(oldState, state);
303 }
304
305 static bool SourceBufferIsUpdating(RefPtr<SourceBuffer>& sourceBuffer)
306 {
307     return sourceBuffer->updating();
308 }
309
310 void MediaSource::endOfStream(const AtomicString& error, ExceptionCode& ec)
311 {
312     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-endOfStream-void-EndOfStreamError-error
313     // 1. If the readyState attribute is not in the "open" state then throw an
314     // INVALID_STATE_ERR exception and abort these steps.
315     if (!isOpen()) {
316         ec = INVALID_STATE_ERR;
317         return;
318     }
319
320     // 2. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an
321     // INVALID_STATE_ERR exception and abort these steps.
322     if (std::any_of(m_sourceBuffers->begin(), m_sourceBuffers->end(), SourceBufferIsUpdating)) {
323         ec = INVALID_STATE_ERR;
324         return;
325     }
326
327     // 3. Run the end of stream algorithm with the error parameter set to error.
328     streamEndedWithError(error, ec);
329 }
330
331 void MediaSource::streamEndedWithError(const AtomicString& error, ExceptionCode& ec)
332 {
333     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, network, ("network", AtomicString::ConstructFromLiteral));
334     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, decode, ("decode", AtomicString::ConstructFromLiteral));
335
336     // 2.4.7 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#end-of-stream-algorithm
337     // 1. Change the readyState attribute value to "ended".
338     // 2. Queue a task to fire a simple event named sourceended at the MediaSource.
339     setReadyState(endedKeyword());
340
341     // 3.
342     if (error.isEmpty()) {
343         // ↳ If error is not set, is null, or is an empty string
344         // 1. Run the duration change algorithm with new duration set to the highest end timestamp
345         // across all SourceBuffer objects in sourceBuffers.
346         MediaTime maxEndTimestamp;
347         for (auto it = m_sourceBuffers->begin(), end = m_sourceBuffers->end(); it != end; ++it)
348             maxEndTimestamp = std::max((*it)->highestPresentationEndTimestamp(), maxEndTimestamp);
349         m_private->setDuration(maxEndTimestamp);
350
351         // 2. Notify the media element that it now has all of the media data.
352         m_private->markEndOfStream(MediaSourcePrivate::EosNoError);
353     } else if (error == network) {
354         // ↳ If error is set to "network"
355         ASSERT(m_mediaElement);
356         if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) {
357             //  ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING
358             //    Run the "If the media data cannot be fetched at all, due to network errors, causing
359             //    the user agent to give up trying to fetch the resource" steps of the resource fetch algorithm.
360             //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed().
361             m_mediaElement->mediaLoadingFailed(MediaPlayer::NetworkError);
362         } else {
363             //  ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING
364             //    Run the "If the connection is interrupted after some media data has been received, causing the
365             //    user agent to give up trying to fetch the resource" steps of the resource fetch algorithm.
366             //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally().
367             m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError);
368         }
369     } else if (error == decode) {
370         // ↳ If error is set to "decode"
371         ASSERT(m_mediaElement);
372         if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) {
373             //  ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING
374             //    Run the "If the media data can be fetched but is found by inspection to be in an unsupported
375             //    format, or can otherwise not be rendered at all" steps of the resource fetch algorithm.
376             //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed().
377             m_mediaElement->mediaLoadingFailed(MediaPlayer::FormatError);
378         } else {
379             //  ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING
380             //    Run the media data is corrupted steps of the resource fetch algorithm.
381             //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally().
382             m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::DecodeError);
383         }
384     } else {
385         // ↳ Otherwise
386         //   Throw an INVALID_ACCESS_ERR exception.
387         ec = INVALID_ACCESS_ERR;
388     }
389 }
390
391 SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionCode& ec)
392 {
393     LOG(Media, "MediaSource::addSourceBuffer(%s) %p", type.ascii().data(), this);
394
395     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
396     // 1. If type is null or an empty then throw an INVALID_ACCESS_ERR exception and
397     // abort these steps.
398     if (type.isNull() || type.isEmpty()) {
399         ec = INVALID_ACCESS_ERR;
400         return nullptr;
401     }
402
403     // 2. If type contains a MIME type that is not supported ..., then throw a
404     // NOT_SUPPORTED_ERR exception and abort these steps.
405     if (!isTypeSupported(type)) {
406         ec = NOT_SUPPORTED_ERR;
407         return nullptr;
408     }
409
410     // 4. If the readyState attribute is not in the "open" state then throw an
411     // INVALID_STATE_ERR exception and abort these steps.
412     if (!isOpen()) {
413         ec = INVALID_STATE_ERR;
414         return nullptr;
415     }
416
417     // 5. Create a new SourceBuffer object and associated resources.
418     ContentType contentType(type);
419     RefPtr<SourceBufferPrivate> sourceBufferPrivate = createSourceBufferPrivate(contentType, ec);
420
421     if (!sourceBufferPrivate) {
422         ASSERT(ec == NOT_SUPPORTED_ERR || ec == QUOTA_EXCEEDED_ERR);
423         // 2. If type contains a MIME type that is not supported ..., then throw a NOT_SUPPORTED_ERR exception and abort these steps.
424         // 3. If the user agent can't handle any more SourceBuffer objects then throw a QUOTA_EXCEEDED_ERR exception and abort these steps
425         return nullptr;
426     }
427
428     RefPtr<SourceBuffer> buffer = SourceBuffer::create(sourceBufferPrivate.releaseNonNull(), this);
429     // 6. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
430     m_sourceBuffers->add(buffer);
431
432     if (buffer->active())
433         m_activeSourceBuffers->add(buffer);
434     // 7. Return the new object to the caller.
435     return buffer.get();
436 }
437
438 void MediaSource::removeSourceBuffer(SourceBuffer* buffer, ExceptionCode& ec)
439 {
440     LOG(Media, "MediaSource::removeSourceBuffer() %p", this);
441     RefPtr<SourceBuffer> protect(buffer);
442
443     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-removeSourceBuffer-void-SourceBuffer-sourceBuffer
444     // 1. If sourceBuffer is null then throw an INVALID_ACCESS_ERR exception and
445     // abort these steps.
446     if (!buffer) {
447         ec = INVALID_ACCESS_ERR;
448         return;
449     }
450
451     // 2. If sourceBuffer specifies an object that is not in sourceBuffers then
452     // throw a NOT_FOUND_ERR exception and abort these steps.
453     if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer)) {
454         ec = NOT_FOUND_ERR;
455         return;
456     }
457
458     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
459     buffer->abortIfUpdating();
460
461     // 4. Let SourceBuffer audioTracks list equal the AudioTrackList object returned by sourceBuffer.audioTracks.
462     RefPtr<AudioTrackList> audioTracks = buffer->audioTracks();
463
464     // 5. If the SourceBuffer audioTracks list is not empty, then run the following steps:
465     if (audioTracks->length()) {
466         // 5.1 Let HTMLMediaElement audioTracks list equal the AudioTrackList object returned by the audioTracks
467         // attribute on the HTMLMediaElement.
468         // 5.2 Let the removed enabled audio track flag equal false.
469         bool removedEnabledAudioTrack = false;
470
471         // 5.3 For each AudioTrack object in the SourceBuffer audioTracks list, run the following steps:
472         while (audioTracks->length()) {
473             AudioTrack* track = audioTracks->lastItem();
474
475             // 5.3.1 Set the sourceBuffer attribute on the AudioTrack object to null.
476             track->setSourceBuffer(0);
477
478             // 5.3.2 If the enabled attribute on the AudioTrack object is true, then set the removed enabled
479             // audio track flag to true.
480             if (track->enabled())
481                 removedEnabledAudioTrack = true;
482
483             // 5.3.3 Remove the AudioTrack object from the HTMLMediaElement audioTracks list.
484             // 5.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
485             // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement audioTracks list.
486             if (mediaElement())
487                 mediaElement()->removeAudioTrack(track);
488
489             // 5.3.5 Remove the AudioTrack object from the SourceBuffer audioTracks list.
490             // 5.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
491             // cancelable, and that uses the TrackEvent interface, at the SourceBuffer audioTracks list.
492             audioTracks->remove(track);
493         }
494
495         // 5.4 If the removed enabled audio track flag equals true, then queue a task to fire a simple event
496         // named change at the HTMLMediaElement audioTracks list.
497         if (removedEnabledAudioTrack)
498             mediaElement()->audioTracks()->scheduleChangeEvent();
499     }
500
501     // 6. Let SourceBuffer videoTracks list equal the VideoTrackList object returned by sourceBuffer.videoTracks.
502     RefPtr<VideoTrackList> videoTracks = buffer->videoTracks();
503
504     // 7. If the SourceBuffer videoTracks list is not empty, then run the following steps:
505     if (videoTracks->length()) {
506         // 7.1 Let HTMLMediaElement videoTracks list equal the VideoTrackList object returned by the videoTracks
507         // attribute on the HTMLMediaElement.
508         // 7.2 Let the removed selected video track flag equal false.
509         bool removedSelectedVideoTrack = false;
510
511         // 7.3 For each VideoTrack object in the SourceBuffer videoTracks list, run the following steps:
512         while (videoTracks->length()) {
513             VideoTrack* track = videoTracks->lastItem();
514
515             // 7.3.1 Set the sourceBuffer attribute on the VideoTrack object to null.
516             track->setSourceBuffer(0);
517
518             // 7.3.2 If the selected attribute on the VideoTrack object is true, then set the removed selected
519             // video track flag to true.
520             if (track->selected())
521                 removedSelectedVideoTrack = true;
522
523             // 7.3.3 Remove the VideoTrack object from the HTMLMediaElement videoTracks list.
524             // 7.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
525             // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement videoTracks list.
526             if (mediaElement())
527                 mediaElement()->removeVideoTrack(track);
528
529             // 7.3.5 Remove the VideoTrack object from the SourceBuffer videoTracks list.
530             // 7.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
531             // cancelable, and that uses the TrackEvent interface, at the SourceBuffer videoTracks list.
532             videoTracks->remove(track);
533         }
534
535         // 7.4 If the removed selected video track flag equals true, then queue a task to fire a simple event
536         // named change at the HTMLMediaElement videoTracks list.
537         if (removedSelectedVideoTrack)
538             mediaElement()->videoTracks()->scheduleChangeEvent();
539     }
540
541     // 8. Let SourceBuffer textTracks list equal the TextTrackList object returned by sourceBuffer.textTracks.
542     RefPtr<TextTrackList> textTracks = buffer->textTracks();
543
544     // 9. If the SourceBuffer textTracks list is not empty, then run the following steps:
545     if (textTracks->length()) {
546         // 9.1 Let HTMLMediaElement textTracks list equal the TextTrackList object returned by the textTracks
547         // attribute on the HTMLMediaElement.
548         // 9.2 Let the removed enabled text track flag equal false.
549         bool removedEnabledTextTrack = false;
550
551         // 9.3 For each TextTrack object in the SourceBuffer textTracks list, run the following steps:
552         while (textTracks->length()) {
553             TextTrack* track = textTracks->lastItem();
554
555             // 9.3.1 Set the sourceBuffer attribute on the TextTrack object to null.
556             track->setSourceBuffer(0);
557
558             // 9.3.2 If the mode attribute on the TextTrack object is set to "showing" or "hidden", then
559             // set the removed enabled text track flag to true.
560             if (track->mode() == TextTrack::showingKeyword() || track->mode() == TextTrack::hiddenKeyword())
561                 removedEnabledTextTrack = true;
562
563             // 9.3.3 Remove the TextTrack object from the HTMLMediaElement textTracks list.
564             // 9.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
565             // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement textTracks list.
566             if (mediaElement())
567                 mediaElement()->removeTextTrack(track);
568
569             // 9.3.5 Remove the TextTrack object from the SourceBuffer textTracks list.
570             // 9.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
571             // cancelable, and that uses the TrackEvent interface, at the SourceBuffer textTracks list.
572             textTracks->remove(track);
573         }
574         
575         // 9.4 If the removed enabled text track flag equals true, then queue a task to fire a simple event
576         // named change at the HTMLMediaElement textTracks list.
577         if (removedEnabledTextTrack)
578             mediaElement()->textTracks()->scheduleChangeEvent();
579     }
580     
581     
582     // 10. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ...
583     m_activeSourceBuffers->remove(buffer);
584     
585     // 11. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event
586     // on that object.
587     m_sourceBuffers->remove(buffer);
588     
589     // 12. Destroy all resources for sourceBuffer.
590     buffer->removedFromMediaSource();
591 }
592
593 bool MediaSource::isTypeSupported(const String& type)
594 {
595     LOG(Media, "MediaSource::isTypeSupported(%s)", type.ascii().data());
596
597     // Section 2.2 isTypeSupported() method steps.
598     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-isTypeSupported-boolean-DOMString-type
599     // 1. If type is an empty string, then return false.
600     if (type.isNull() || type.isEmpty())
601         return false;
602
603     ContentType contentType(type);
604     String codecs = contentType.parameter("codecs");
605
606     // 2. If type does not contain a valid MIME type string, then return false.
607     if (contentType.type().isEmpty() || codecs.isEmpty())
608         return false;
609
610     // 3. If type contains a media type or media subtype that the MediaSource does not support, then return false.
611     // 4. If type contains at a codec that the MediaSource does not support, then return false.
612     // 5. If the MediaSource does not support the specified combination of media type, media subtype, and codecs then return false.
613     // 6. Return true.
614     MediaEngineSupportParameters parameters;
615     parameters.type = contentType.type();
616     parameters.codecs = codecs;
617     parameters.isMediaSource = true;
618     return MediaPlayer::supportsType(parameters, 0) != MediaPlayer::IsNotSupported;
619 }
620
621 bool MediaSource::isOpen() const
622 {
623     return readyState() == openKeyword();
624 }
625
626 bool MediaSource::isClosed() const
627 {
628     return readyState() == closedKeyword();
629 }
630
631 void MediaSource::close()
632 {
633     setReadyState(closedKeyword());
634 }
635
636 void MediaSource::sourceBufferDidChangeAcitveState(SourceBuffer* sourceBuffer, bool active)
637 {
638     if (active && !m_activeSourceBuffers->contains(sourceBuffer))
639         m_activeSourceBuffers->add(sourceBuffer);
640     else if (!active && m_activeSourceBuffers->contains(sourceBuffer))
641         m_activeSourceBuffers->remove(sourceBuffer);
642 }
643
644 bool MediaSource::attachToElement(HTMLMediaElement* element)
645 {
646     if (m_mediaElement)
647         return false;
648
649     ASSERT(isClosed());
650
651     m_mediaElement = element;
652     return true;
653 }
654
655 void MediaSource::openIfInEndedState()
656 {
657     if (m_readyState != endedKeyword())
658         return;
659
660     setReadyState(openKeyword());
661     m_private->unmarkEndOfStream();
662 }
663
664 bool MediaSource::hasPendingActivity() const
665 {
666     return m_private || m_asyncEventQueue.hasPendingEvents()
667         || ActiveDOMObject::hasPendingActivity();
668 }
669
670 void MediaSource::stop()
671 {
672     m_asyncEventQueue.close();
673     if (!isClosed())
674         setReadyState(closedKeyword());
675     m_private.clear();
676 }
677
678 void MediaSource::onReadyStateChange(const AtomicString& oldState, const AtomicString& newState)
679 {
680     if (isOpen()) {
681         scheduleEvent(eventNames().sourceopenEvent);
682         return;
683     }
684
685     if (oldState == openKeyword() && newState == endedKeyword()) {
686         scheduleEvent(eventNames().sourceendedEvent);
687         return;
688     }
689
690     ASSERT(isClosed());
691
692     m_activeSourceBuffers->clear();
693
694     // Clear SourceBuffer references to this object.
695     for (unsigned long i = 0, length =  m_sourceBuffers->length(); i < length; ++i)
696         m_sourceBuffers->item(i)->removedFromMediaSource();
697     m_sourceBuffers->clear();
698     
699     scheduleEvent(eventNames().sourcecloseEvent);
700 }
701
702 Vector<RefPtr<TimeRanges>> MediaSource::activeRanges() const
703 {
704     Vector<RefPtr<TimeRanges>> activeRanges(m_activeSourceBuffers->length());
705     for (size_t i = 0, length = m_activeSourceBuffers->length(); i < length; ++i)
706         activeRanges[i] = m_activeSourceBuffers->item(i)->buffered(ASSERT_NO_EXCEPTION);
707
708     return activeRanges;
709 }
710
711 RefPtr<SourceBufferPrivate> MediaSource::createSourceBufferPrivate(const ContentType& type, ExceptionCode& ec)
712 {
713     RefPtr<SourceBufferPrivate> sourceBufferPrivate;
714     switch (m_private->addSourceBuffer(type, sourceBufferPrivate)) {
715     case MediaSourcePrivate::Ok: {
716         return sourceBufferPrivate;
717     }
718     case MediaSourcePrivate::NotSupported:
719         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
720         // Step 2: If type contains a MIME type ... that is not supported with the types
721         // specified for the other SourceBuffer objects in sourceBuffers, then throw
722         // a NOT_SUPPORTED_ERR exception and abort these steps.
723         ec = NOT_SUPPORTED_ERR;
724         return nullptr;
725     case MediaSourcePrivate::ReachedIdLimit:
726         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
727         // Step 3: If the user agent can't handle any more SourceBuffer objects then throw
728         // a QUOTA_EXCEEDED_ERR exception and abort these steps.
729         ec = QUOTA_EXCEEDED_ERR;
730         return nullptr;
731     }
732
733     ASSERT_NOT_REACHED();
734     return nullptr;
735 }
736
737 void MediaSource::scheduleEvent(const AtomicString& eventName)
738 {
739     RefPtr<Event> event = Event::create(eventName, false, false);
740     event->setTarget(this);
741
742     m_asyncEventQueue.enqueueEvent(event.release());
743 }
744
745 ScriptExecutionContext* MediaSource::scriptExecutionContext() const
746 {
747     return ActiveDOMObject::scriptExecutionContext();
748 }
749
750 EventTargetInterface MediaSource::eventTargetInterface() const
751 {
752     return MediaSourceEventTargetInterfaceType;
753 }
754
755 URLRegistry& MediaSource::registry() const
756 {
757     return MediaSourceRegistry::registry();
758 }
759
760 }
761
762 #endif