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