Improve use of NeverDestroyed
[WebKit-https.git] / Source / WebCore / Modules / mediasource / SourceBuffer.cpp
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "SourceBuffer.h"
34
35 #if ENABLE(MEDIA_SOURCE)
36
37 #include "AudioTrackList.h"
38 #include "BufferSource.h"
39 #include "Event.h"
40 #include "EventNames.h"
41 #include "ExceptionCode.h"
42 #include "GenericEventQueue.h"
43 #include "HTMLMediaElement.h"
44 #include "InbandTextTrack.h"
45 #include "Logging.h"
46 #include "MediaDescription.h"
47 #include "MediaSample.h"
48 #include "MediaSource.h"
49 #include "SampleMap.h"
50 #include "SourceBufferList.h"
51 #include "SourceBufferPrivate.h"
52 #include "TextTrackList.h"
53 #include "TimeRanges.h"
54 #include "VideoTrackList.h"
55 #include <limits>
56 #include <map>
57 #include <runtime/JSCInlines.h>
58 #include <runtime/JSLock.h>
59 #include <runtime/VM.h>
60 #include <wtf/CurrentTime.h>
61
62 namespace WebCore {
63
64 static const double ExponentialMovingAverageCoefficient = 0.1;
65
66 struct SourceBuffer::TrackBuffer {
67     MediaTime lastDecodeTimestamp;
68     MediaTime lastFrameDuration;
69     MediaTime highestPresentationTimestamp;
70     MediaTime lastEnqueuedPresentationTime;
71     MediaTime lastEnqueuedDecodeEndTime;
72     bool needRandomAccessFlag { true };
73     bool enabled { false };
74     bool needsReenqueueing { false };
75     SampleMap samples;
76     DecodeOrderSampleMap::MapType decodeQueue;
77     RefPtr<MediaDescription> description;
78     PlatformTimeRanges buffered;
79
80     TrackBuffer()
81         : lastDecodeTimestamp(MediaTime::invalidTime())
82         , lastFrameDuration(MediaTime::invalidTime())
83         , highestPresentationTimestamp(MediaTime::invalidTime())
84         , lastEnqueuedPresentationTime(MediaTime::invalidTime())
85         , lastEnqueuedDecodeEndTime(MediaTime::invalidTime())
86     {
87     }
88 };
89
90 Ref<SourceBuffer> SourceBuffer::create(Ref<SourceBufferPrivate>&& sourceBufferPrivate, MediaSource* source)
91 {
92     auto sourceBuffer = adoptRef(*new SourceBuffer(WTFMove(sourceBufferPrivate), source));
93     sourceBuffer->suspendIfNeeded();
94     return sourceBuffer;
95 }
96
97 SourceBuffer::SourceBuffer(Ref<SourceBufferPrivate>&& sourceBufferPrivate, MediaSource* source)
98     : ActiveDOMObject(source->scriptExecutionContext())
99     , m_private(WTFMove(sourceBufferPrivate))
100     , m_source(source)
101     , m_asyncEventQueue(*this)
102     , m_appendBufferTimer(*this, &SourceBuffer::appendBufferTimerFired)
103     , m_appendWindowStart(MediaTime::zeroTime())
104     , m_appendWindowEnd(MediaTime::positiveInfiniteTime())
105     , m_groupStartTimestamp(MediaTime::invalidTime())
106     , m_groupEndTimestamp(MediaTime::zeroTime())
107     , m_buffered(TimeRanges::create())
108     , m_appendState(WaitingForSegment)
109     , m_timeOfBufferingMonitor(monotonicallyIncreasingTime())
110     , m_pendingRemoveStart(MediaTime::invalidTime())
111     , m_pendingRemoveEnd(MediaTime::invalidTime())
112     , m_removeTimer(*this, &SourceBuffer::removeTimerFired)
113 {
114     ASSERT(m_source);
115
116     m_private->setClient(this);
117 }
118
119 SourceBuffer::~SourceBuffer()
120 {
121     ASSERT(isRemoved());
122
123     m_private->setClient(nullptr);
124 }
125
126 ExceptionOr<Ref<TimeRanges>> SourceBuffer::buffered() const
127 {
128     // Section 3.1 buffered attribute steps.
129     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#attributes-1
130     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
131     //    INVALID_STATE_ERR exception and abort these steps.
132     if (isRemoved())
133         return Exception { INVALID_STATE_ERR };
134
135     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
136     return m_buffered->copy();
137 }
138
139 double SourceBuffer::timestampOffset() const
140 {
141     return m_timestampOffset.toDouble();
142 }
143
144 ExceptionOr<void> SourceBuffer::setTimestampOffset(double offset)
145 {
146     // Section 3.1 timestampOffset attribute setter steps.
147     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#attributes-1
148     // 1. Let new timestamp offset equal the new value being assigned to this attribute.
149     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw an
150     //    INVALID_STATE_ERR exception and abort these steps.
151     // 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
152     if (isRemoved() || m_updating)
153         return Exception { INVALID_STATE_ERR };
154
155     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
156     // 4.1 Set the readyState attribute of the parent media source to "open"
157     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
158     m_source->openIfInEndedState();
159
160     // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
161     if (m_appendState == ParsingMediaSegment)
162         return Exception { INVALID_STATE_ERR };
163
164     MediaTime newTimestampOffset = MediaTime::createWithDouble(offset);
165
166     // 6. If the mode attribute equals "sequence", then set the group start timestamp to new timestamp offset.
167     if (m_mode == AppendMode::Sequence)
168         m_groupStartTimestamp = newTimestampOffset;
169
170     // 7. Update the attribute to the new value.
171     m_timestampOffset = newTimestampOffset;
172
173     return { };
174 }
175
176 double SourceBuffer::appendWindowStart() const
177 {
178     return m_appendWindowStart.toDouble();
179 }
180
181 ExceptionOr<void> SourceBuffer::setAppendWindowStart(double newValue)
182 {
183     // Section 3.1 appendWindowStart attribute setter steps.
184     // W3C Editor's Draft 16 September 2016
185     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-appendwindowstart
186     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source,
187     //    then throw an InvalidStateError  exception and abort these steps.
188     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
189     if (isRemoved() || m_updating)
190         return Exception { INVALID_STATE_ERR };
191
192     // 3. If the new value is less than 0 or greater than or equal to appendWindowEnd then
193     //    throw an TypeError exception and abort these steps.
194     if (newValue < 0 || newValue >= m_appendWindowEnd.toDouble())
195         return Exception { TypeError };
196
197     // 4. Update the attribute to the new value.
198     m_appendWindowStart = MediaTime::createWithDouble(newValue);
199
200     return { };
201 }
202
203 double SourceBuffer::appendWindowEnd() const
204 {
205     return m_appendWindowEnd.toDouble();
206 }
207
208 ExceptionOr<void> SourceBuffer::setAppendWindowEnd(double newValue)
209 {
210     // Section 3.1 appendWindowEnd attribute setter steps.
211     // W3C Editor's Draft 16 September 2016
212     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-appendwindowend
213     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source,
214     //    then throw an InvalidStateError exception and abort these steps.
215     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
216     if (isRemoved() || m_updating)
217         return Exception { INVALID_STATE_ERR };
218
219     // 3. If the new value equals NaN, then throw an TypeError and abort these steps.
220     // 4. If the new value is less than or equal to appendWindowStart then throw an TypeError exception
221     //    and abort these steps.
222     if (std::isnan(newValue) || newValue <= m_appendWindowStart.toDouble())
223         return Exception { TypeError };
224
225     // 5.. Update the attribute to the new value.
226     m_appendWindowEnd = MediaTime::createWithDouble(newValue);
227
228     return { };
229 }
230
231 ExceptionOr<void> SourceBuffer::appendBuffer(const BufferSource& data)
232 {
233     return appendBufferInternal(static_cast<const unsigned char*>(data.data()), data.length());
234 }
235
236 void SourceBuffer::resetParserState()
237 {
238     // Section 3.5.2 Reset Parser State algorithm steps.
239     // http://www.w3.org/TR/2014/CR-media-source-20140717/#sourcebuffer-reset-parser-state
240     // 1. If the append state equals PARSING_MEDIA_SEGMENT and the input buffer contains some complete coded frames,
241     //    then run the coded frame processing algorithm until all of these complete coded frames have been processed.
242     // FIXME: If any implementation will work in pulling mode (instead of async push to SourceBufferPrivate, and forget)
243     //     this should be handled somehow either here, or in m_private->abort();
244
245     // 2. Unset the last decode timestamp on all track buffers.
246     // 3. Unset the last frame duration on all track buffers.
247     // 4. Unset the highest presentation timestamp on all track buffers.
248     // 5. Set the need random access point flag on all track buffers to true.
249     for (auto& trackBufferPair : m_trackBufferMap.values()) {
250         trackBufferPair.lastDecodeTimestamp = MediaTime::invalidTime();
251         trackBufferPair.lastFrameDuration = MediaTime::invalidTime();
252         trackBufferPair.highestPresentationTimestamp = MediaTime::invalidTime();
253         trackBufferPair.needRandomAccessFlag = true;
254     }
255     // 6. Remove all bytes from the input buffer.
256     // Note: this is handled by abortIfUpdating()
257     // 7. Set append state to WAITING_FOR_SEGMENT.
258     m_appendState = WaitingForSegment;
259
260     m_private->resetParserState();
261 }
262
263 ExceptionOr<void> SourceBuffer::abort()
264 {
265     // Section 3.2 abort() method steps.
266     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-abort
267     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
268     //    then throw an INVALID_STATE_ERR exception and abort these steps.
269     // 2. If the readyState attribute of the parent media source is not in the "open" state
270     //    then throw an INVALID_STATE_ERR exception and abort these steps.
271     if (isRemoved() || !m_source->isOpen())
272         return Exception { INVALID_STATE_ERR };
273
274     // 3. If the range removal algorithm is running, then throw an InvalidStateError exception and abort these steps.
275     if (m_removeTimer.isActive())
276         return Exception { INVALID_STATE_ERR };
277
278     // 4. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
279     abortIfUpdating();
280
281     // 5. Run the reset parser state algorithm.
282     resetParserState();
283
284     // 6. Set appendWindowStart to the presentation start time.
285     m_appendWindowStart = MediaTime::zeroTime();
286
287     // 7. Set appendWindowEnd to positive Infinity.
288     m_appendWindowEnd = MediaTime::positiveInfiniteTime();
289
290     return { };
291 }
292
293 ExceptionOr<void> SourceBuffer::remove(double start, double end)
294 {
295     return remove(MediaTime::createWithDouble(start), MediaTime::createWithDouble(end));
296 }
297
298 ExceptionOr<void> SourceBuffer::remove(const MediaTime& start, const MediaTime& end)
299 {
300     LOG(MediaSource, "SourceBuffer::remove(%p) - start(%lf), end(%lf)", this, start.toDouble(), end.toDouble());
301
302     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-remove
303     // Section 3.2 remove() method steps.
304     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw
305     //    an InvalidStateError exception and abort these steps.
306     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
307     if (isRemoved() || m_updating)
308         return Exception { INVALID_STATE_ERR };
309
310     // 3. If duration equals NaN, then throw a TypeError exception and abort these steps.
311     // 4. If start is negative or greater than duration, then throw a TypeError exception and abort these steps.
312     // 5. If end is less than or equal to start or end equals NaN, then throw a TypeError exception and abort these steps.
313     if (m_source->duration().isInvalid()
314         || end.isInvalid()
315         || start.isInvalid()
316         || start < MediaTime::zeroTime()
317         || start > m_source->duration()
318         || end <= start) {
319         return Exception { TypeError };
320     }
321
322     // 6. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
323     // 6.1. Set the readyState attribute of the parent media source to "open"
324     // 6.2. Queue a task to fire a simple event named sourceopen at the parent media source .
325     m_source->openIfInEndedState();
326
327     // 7. Run the range removal algorithm with start and end as the start and end of the removal range.
328     rangeRemoval(start, end);
329
330     return { };
331 }
332
333 void SourceBuffer::rangeRemoval(const MediaTime& start, const MediaTime& end)
334 {
335     // 3.5.7 Range Removal
336     // https://rawgit.com/w3c/media-source/7bbe4aa33c61ec025bc7acbd80354110f6a000f9/media-source.html#sourcebuffer-range-removal
337     // 1. Let start equal the starting presentation timestamp for the removal range.
338     // 2. Let end equal the end presentation timestamp for the removal range.
339     // 3. Set the updating attribute to true.
340     m_updating = true;
341
342     // 4. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
343     scheduleEvent(eventNames().updatestartEvent);
344
345     // 5. Return control to the caller and run the rest of the steps asynchronously.
346     m_pendingRemoveStart = start;
347     m_pendingRemoveEnd = end;
348     m_removeTimer.startOneShot(0_s);
349 }
350
351 void SourceBuffer::abortIfUpdating()
352 {
353     // Section 3.2 abort() method step 4 substeps.
354     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-abort
355
356     if (!m_updating)
357         return;
358
359     // 4.1. Abort the buffer append algorithm if it is running.
360     m_appendBufferTimer.stop();
361     m_pendingAppendData.clear();
362     m_private->abort();
363
364     // 4.2. Set the updating attribute to false.
365     m_updating = false;
366
367     // 4.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
368     scheduleEvent(eventNames().abortEvent);
369
370     // 4.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
371     scheduleEvent(eventNames().updateendEvent);
372 }
373
374 MediaTime SourceBuffer::highestPresentationTimestamp() const
375 {
376     MediaTime highestTime;
377     for (auto& trackBuffer : m_trackBufferMap.values()) {
378         auto lastSampleIter = trackBuffer.samples.presentationOrder().rbegin();
379         if (lastSampleIter == trackBuffer.samples.presentationOrder().rend())
380             continue;
381         highestTime = std::max(highestTime, lastSampleIter->first);
382     }
383     return highestTime;
384 }
385
386 void SourceBuffer::readyStateChanged()
387 {
388     updateBufferedFromTrackBuffers();
389 }
390
391 void SourceBuffer::removedFromMediaSource()
392 {
393     if (isRemoved())
394         return;
395
396     abortIfUpdating();
397
398     for (auto& trackBufferPair : m_trackBufferMap.values()) {
399         trackBufferPair.samples.clear();
400         trackBufferPair.decodeQueue.clear();
401     }
402
403     m_private->removedFromMediaSource();
404     m_source = nullptr;
405 }
406
407 void SourceBuffer::seekToTime(const MediaTime& time)
408 {
409     LOG(MediaSource, "SourceBuffer::seekToTime(%p) - time(%s)", this, toString(time).utf8().data());
410
411     for (auto& trackBufferPair : m_trackBufferMap) {
412         TrackBuffer& trackBuffer = trackBufferPair.value;
413         const AtomicString& trackID = trackBufferPair.key;
414
415         trackBuffer.needsReenqueueing = true;
416         reenqueueMediaForTime(trackBuffer, trackID, time);
417     }
418 }
419
420 MediaTime SourceBuffer::sourceBufferPrivateFastSeekTimeForMediaTime(const MediaTime& targetTime, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold)
421 {
422     MediaTime seekTime = targetTime;
423     MediaTime lowerBoundTime = targetTime - negativeThreshold;
424     MediaTime upperBoundTime = targetTime + positiveThreshold;
425
426     for (auto& trackBuffer : m_trackBufferMap.values()) {
427         // Find the sample which contains the target time time.
428         auto futureSyncSampleIterator = trackBuffer.samples.decodeOrder().findSyncSampleAfterPresentationTime(targetTime, positiveThreshold);
429         auto pastSyncSampleIterator = trackBuffer.samples.decodeOrder().findSyncSamplePriorToPresentationTime(targetTime, negativeThreshold);
430         auto upperBound = trackBuffer.samples.decodeOrder().end();
431         auto lowerBound = trackBuffer.samples.decodeOrder().rend();
432
433         if (futureSyncSampleIterator == upperBound && pastSyncSampleIterator == lowerBound)
434             continue;
435
436         MediaTime futureSeekTime = MediaTime::positiveInfiniteTime();
437         if (futureSyncSampleIterator != upperBound) {
438             RefPtr<MediaSample>& sample = futureSyncSampleIterator->second;
439             futureSeekTime = sample->presentationTime();
440         }
441
442         MediaTime pastSeekTime = MediaTime::negativeInfiniteTime();
443         if (pastSyncSampleIterator != lowerBound) {
444             RefPtr<MediaSample>& sample = pastSyncSampleIterator->second;
445             pastSeekTime = sample->presentationTime();
446         }
447
448         MediaTime trackSeekTime = abs(targetTime - futureSeekTime) < abs(targetTime - pastSeekTime) ? futureSeekTime : pastSeekTime;
449         if (abs(targetTime - trackSeekTime) > abs(targetTime - seekTime))
450             seekTime = trackSeekTime;
451     }
452
453     return seekTime;
454 }
455
456 bool SourceBuffer::hasPendingActivity() const
457 {
458     return m_source || m_asyncEventQueue.hasPendingEvents();
459 }
460
461 void SourceBuffer::stop()
462 {
463     m_appendBufferTimer.stop();
464     m_removeTimer.stop();
465 }
466
467 bool SourceBuffer::canSuspendForDocumentSuspension() const
468 {
469     return !hasPendingActivity();
470 }
471
472 const char* SourceBuffer::activeDOMObjectName() const
473 {
474     return "SourceBuffer";
475 }
476
477 bool SourceBuffer::isRemoved() const
478 {
479     return !m_source;
480 }
481
482 void SourceBuffer::scheduleEvent(const AtomicString& eventName)
483 {
484     auto event = Event::create(eventName, false, false);
485     event->setTarget(this);
486
487     m_asyncEventQueue.enqueueEvent(WTFMove(event));
488 }
489
490 ExceptionOr<void> SourceBuffer::appendBufferInternal(const unsigned char* data, unsigned size)
491 {
492     // Section 3.2 appendBuffer()
493     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
494
495     // Step 1 is enforced by the caller.
496     // 2. Run the prepare append algorithm.
497     // Section 3.5.4 Prepare AppendAlgorithm
498
499     // 1. If the SourceBuffer has been removed from the sourceBuffers attribute of the parent media source
500     // then throw an INVALID_STATE_ERR exception and abort these steps.
501     // 2. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
502     if (isRemoved() || m_updating)
503         return Exception { INVALID_STATE_ERR };
504
505     // 3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
506     // 3.1. Set the readyState attribute of the parent media source to "open"
507     // 3.2. Queue a task to fire a simple event named sourceopen at the parent media source .
508     m_source->openIfInEndedState();
509
510     // 4. Run the coded frame eviction algorithm.
511     evictCodedFrames(size);
512
513     // FIXME: enable this code when MSE libraries have been updated to support it.
514 #if USE(GSTREAMER)
515     // 5. If the buffer full flag equals true, then throw a QUOTA_EXCEEDED_ERR exception and abort these step.
516     if (m_bufferFull) {
517         LOG(MediaSource, "SourceBuffer::appendBufferInternal(%p) -  buffer full, failing with QUOTA_EXCEEDED_ERR error", this);
518         return Exception { QUOTA_EXCEEDED_ERR };
519     }
520 #endif
521
522     // NOTE: Return to 3.2 appendBuffer()
523     // 3. Add data to the end of the input buffer.
524     m_pendingAppendData.append(data, size);
525
526     // 4. Set the updating attribute to true.
527     m_updating = true;
528
529     // 5. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
530     scheduleEvent(eventNames().updatestartEvent);
531
532     // 6. Asynchronously run the buffer append algorithm.
533     m_appendBufferTimer.startOneShot(0_s);
534
535     reportExtraMemoryAllocated();
536
537     return { };
538 }
539
540 void SourceBuffer::appendBufferTimerFired()
541 {
542     if (isRemoved())
543         return;
544
545     ASSERT(m_updating);
546
547     // Section 3.5.5 Buffer Append Algorithm
548     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
549
550     // 1. Run the segment parser loop algorithm.
551     size_t appendSize = m_pendingAppendData.size();
552     if (!appendSize) {
553         // Resize buffer for 0 byte appends so we always have a valid pointer.
554         // We need to convey all appends, even 0 byte ones to |m_private| so
555         // that it can clear its end of stream state if necessary.
556         m_pendingAppendData.resize(1);
557     }
558
559     // Section 3.5.1 Segment Parser Loop
560     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#sourcebuffer-segment-parser-loop
561     // When the segment parser loop algorithm is invoked, run the following steps:
562
563     // 1. Loop Top: If the input buffer is empty, then jump to the need more data step below.
564     if (!m_pendingAppendData.size()) {
565         sourceBufferPrivateAppendComplete(AppendSucceeded);
566         return;
567     }
568
569     m_private->append(m_pendingAppendData.data(), appendSize);
570     m_pendingAppendData.clear();
571 }
572
573 void SourceBuffer::sourceBufferPrivateAppendComplete(AppendResult result)
574 {
575     if (isRemoved())
576         return;
577
578     // Resolve the changes it TrackBuffers' buffered ranges
579     // into the SourceBuffer's buffered ranges
580     updateBufferedFromTrackBuffers();
581
582     // Section 3.5.5 Buffer Append Algorithm, ctd.
583     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
584
585     // 2. If the input buffer contains bytes that violate the SourceBuffer byte stream format specification,
586     // then run the append error algorithm with the decode error parameter set to true and abort this algorithm.
587     if (result == ParsingFailed) {
588         LOG(MediaSource, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - result = ParsingFailed", this);
589         appendError(true);
590         return;
591     }
592
593     // NOTE: Steps 3 - 6 enforced by sourceBufferPrivateDidReceiveInitializationSegment() and
594     // sourceBufferPrivateDidReceiveSample below.
595
596     // 7. Need more data: Return control to the calling algorithm.
597
598     // NOTE: return to Section 3.5.5
599     // 2.If the segment parser loop algorithm in the previous step was aborted, then abort this algorithm.
600     if (result != AppendSucceeded)
601         return;
602
603     // 3. Set the updating attribute to false.
604     m_updating = false;
605
606     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
607     scheduleEvent(eventNames().updateEvent);
608
609     // 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
610     scheduleEvent(eventNames().updateendEvent);
611
612     if (m_source)
613         m_source->monitorSourceBuffers();
614
615     MediaTime currentMediaTime = m_source->currentTime();
616     for (auto& trackBufferPair : m_trackBufferMap) {
617         TrackBuffer& trackBuffer = trackBufferPair.value;
618         const AtomicString& trackID = trackBufferPair.key;
619
620         if (trackBuffer.needsReenqueueing) {
621             LOG(MediaSource, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - reenqueuing at time (%s)", this, toString(currentMediaTime).utf8().data());
622             reenqueueMediaForTime(trackBuffer, trackID, currentMediaTime);
623         } else
624             provideMediaData(trackBuffer, trackID);
625     }
626
627     reportExtraMemoryAllocated();
628     if (extraMemoryCost() > this->maximumBufferSize())
629         m_bufferFull = true;
630
631     LOG(Media, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - buffered = %s", this, toString(m_buffered->ranges()).utf8().data());
632 }
633
634 void SourceBuffer::sourceBufferPrivateDidReceiveRenderingError(int error)
635 {
636 #if LOG_DISABLED
637     UNUSED_PARAM(error);
638 #endif
639
640     LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidReceiveRenderingError(%p) - result = %i", this, error);
641
642     if (!isRemoved())
643         m_source->streamEndedWithError(MediaSource::EndOfStreamError::Decode);
644 }
645
646 static bool decodeTimeComparator(const PresentationOrderSampleMap::MapType::value_type& a, const PresentationOrderSampleMap::MapType::value_type& b)
647 {
648     return a.second->decodeTime() < b.second->decodeTime();
649 }
650
651 static PlatformTimeRanges removeSamplesFromTrackBuffer(const DecodeOrderSampleMap::MapType& samples, SourceBuffer::TrackBuffer& trackBuffer, const SourceBuffer* buffer, const char* logPrefix)
652 {
653 #if !LOG_DISABLED
654     MediaTime earliestSample = MediaTime::positiveInfiniteTime();
655     MediaTime latestSample = MediaTime::zeroTime();
656     size_t bytesRemoved = 0;
657 #else
658     UNUSED_PARAM(logPrefix);
659     UNUSED_PARAM(buffer);
660 #endif
661
662     PlatformTimeRanges erasedRanges;
663     for (auto sampleIt : samples) {
664         const DecodeOrderSampleMap::KeyType& decodeKey = sampleIt.first;
665 #if !LOG_DISABLED
666         size_t startBufferSize = trackBuffer.samples.sizeInBytes();
667 #endif
668
669         RefPtr<MediaSample>& sample = sampleIt.second;
670         LOG(MediaSource, "SourceBuffer::%s(%p) - removing sample(%s)", logPrefix, buffer, toString(*sampleIt.second).utf8().data());
671
672         // Remove the erased samples from the TrackBuffer sample map.
673         trackBuffer.samples.removeSample(sample.get());
674
675         // Also remove the erased samples from the TrackBuffer decodeQueue.
676         trackBuffer.decodeQueue.erase(decodeKey);
677
678         auto startTime = sample->presentationTime();
679         auto endTime = startTime + sample->duration();
680         erasedRanges.add(startTime, endTime);
681
682 #if !LOG_DISABLED
683         bytesRemoved += startBufferSize - trackBuffer.samples.sizeInBytes();
684         if (startTime < earliestSample)
685             earliestSample = startTime;
686         if (endTime > latestSample)
687             latestSample = endTime;
688 #endif
689     }
690
691     // Because we may have added artificial padding in the buffered ranges when adding samples, we may
692     // need to remove that padding when removing those same samples. Walk over the erased ranges looking
693     // for unbuffered areas and expand erasedRanges to encompass those areas.
694     PlatformTimeRanges additionalErasedRanges;
695     for (unsigned i = 0; i < erasedRanges.length(); ++i) {
696         auto erasedStart = erasedRanges.start(i);
697         auto erasedEnd = erasedRanges.end(i);
698         auto startIterator = trackBuffer.samples.presentationOrder().reverseFindSampleBeforePresentationTime(erasedStart);
699         if (startIterator == trackBuffer.samples.presentationOrder().rend())
700             additionalErasedRanges.add(MediaTime::zeroTime(), erasedStart);
701         else {
702             auto& previousSample = *startIterator->second;
703             if (previousSample.presentationTime() + previousSample.duration() < erasedStart)
704                 additionalErasedRanges.add(previousSample.presentationTime() + previousSample.duration(), erasedStart);
705         }
706
707         auto endIterator = trackBuffer.samples.presentationOrder().findSampleStartingOnOrAfterPresentationTime(erasedEnd);
708         if (endIterator == trackBuffer.samples.presentationOrder().end())
709             additionalErasedRanges.add(erasedEnd, MediaTime::positiveInfiniteTime());
710         else {
711             auto& nextSample = *endIterator->second;
712             if (nextSample.presentationTime() > erasedEnd)
713                 additionalErasedRanges.add(erasedEnd, nextSample.presentationTime());
714         }
715     }
716     if (additionalErasedRanges.length())
717         erasedRanges.unionWith(additionalErasedRanges);
718
719 #if !LOG_DISABLED
720     if (bytesRemoved)
721         LOG(MediaSource, "SourceBuffer::%s(%p) removed %zu bytes, start(%lf), end(%lf)", logPrefix, buffer, bytesRemoved, earliestSample.toDouble(), latestSample.toDouble());
722 #endif
723
724     return erasedRanges;
725 }
726
727 void SourceBuffer::removeCodedFrames(const MediaTime& start, const MediaTime& end)
728 {
729     LOG(MediaSource, "SourceBuffer::removeCodedFrames(%p) - start(%s), end(%s)", this, toString(start).utf8().data(), toString(end).utf8().data());
730
731     // 3.5.9 Coded Frame Removal Algorithm
732     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#sourcebuffer-coded-frame-removal
733
734     // 1. Let start be the starting presentation timestamp for the removal range.
735     MediaTime durationMediaTime = m_source->duration();
736     MediaTime currentMediaTime = m_source->currentTime();
737
738     // 2. Let end be the end presentation timestamp for the removal range.
739     // 3. For each track buffer in this source buffer, run the following steps:
740     for (auto& trackBuffer : m_trackBufferMap.values()) {
741         // 3.1. Let remove end timestamp be the current value of duration
742         // 3.2 If this track buffer has a random access point timestamp that is greater than or equal to end, then update
743         // remove end timestamp to that random access point timestamp.
744         // NOTE: Step 3.2 will be incorrect for any random access point timestamp whose decode time is later than the sample at end,
745         // but whose presentation time is less than the sample at end. Skip this step until step 3.3 below.
746
747         // NOTE: To handle MediaSamples which may be an amalgamation of multiple shorter samples, find samples whose presentation
748         // interval straddles the start and end times, and divide them if possible:
749         auto divideSampleIfPossibleAtPresentationTime = [&] (const MediaTime& time) {
750             auto sampleIterator = trackBuffer.samples.presentationOrder().findSampleContainingPresentationTime(time);
751             if (sampleIterator == trackBuffer.samples.presentationOrder().end())
752                 return;
753             RefPtr<MediaSample> sample = sampleIterator->second;
754             if (!sample->isDivisable())
755                 return;
756             std::pair<RefPtr<MediaSample>, RefPtr<MediaSample>> replacementSamples = sample->divide(time);
757             if (!replacementSamples.first || !replacementSamples.second)
758                 return;
759             LOG(MediaSource, "SourceBuffer::removeCodedFrames(%p) - splitting sample (%s) into\n\t(%s)\n\t(%s)", this,
760                 toString(sample).utf8().data(),
761                 toString(replacementSamples.first).utf8().data(),
762                 toString(replacementSamples.second).utf8().data());
763             trackBuffer.samples.removeSample(sample.get());
764             trackBuffer.samples.addSample(*replacementSamples.first);
765             trackBuffer.samples.addSample(*replacementSamples.second);
766         };
767         divideSampleIfPossibleAtPresentationTime(start);
768         divideSampleIfPossibleAtPresentationTime(end);
769
770         auto removePresentationStart = trackBuffer.samples.presentationOrder().findSampleContainingOrAfterPresentationTime(start);
771         auto removePresentationEnd = trackBuffer.samples.presentationOrder().findSampleStartingAfterPresentationTime(end);
772         if (start == end)
773             continue;
774
775         // 3.3 Remove all media data, from this track buffer, that contain starting timestamps greater than or equal to
776         // start and less than the remove end timestamp.
777         // NOTE: frames must be removed in decode order, so that all dependant frames between the frame to be removed
778         // and the next sync sample frame are removed. But we must start from the first sample in decode order, not
779         // presentation order.
780         auto minmaxDecodeTimeIterPair = std::minmax_element(removePresentationStart, removePresentationEnd, decodeTimeComparator);
781         auto& firstSample = *minmaxDecodeTimeIterPair.first->second;
782         auto& lastSample = *minmaxDecodeTimeIterPair.second->second;
783         auto removeDecodeStart = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey({firstSample.decodeTime(), firstSample.presentationTime()});
784         auto removeDecodeLast = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey({lastSample.decodeTime(), lastSample.presentationTime()});
785         auto removeDecodeEnd = trackBuffer.samples.decodeOrder().findSyncSampleAfterDecodeIterator(removeDecodeLast);
786
787         DecodeOrderSampleMap::MapType erasedSamples(removeDecodeStart, removeDecodeEnd);
788         PlatformTimeRanges erasedRanges = removeSamplesFromTrackBuffer(erasedSamples, trackBuffer, this, "removeCodedFrames");
789
790         // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly
791         // not yet displayed samples.
792         if (trackBuffer.lastEnqueuedPresentationTime.isValid() && currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) {
793             PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
794             possiblyEnqueuedRanges.intersectWith(erasedRanges);
795             if (possiblyEnqueuedRanges.length())
796                 trackBuffer.needsReenqueueing = true;
797         }
798
799         erasedRanges.invert();
800         trackBuffer.buffered.intersectWith(erasedRanges);
801         setBufferedDirty(true);
802
803         // 3.4 If this object is in activeSourceBuffers, the current playback position is greater than or equal to start
804         // and less than the remove end timestamp, and HTMLMediaElement.readyState is greater than HAVE_METADATA, then set
805         // the HTMLMediaElement.readyState attribute to HAVE_METADATA and stall playback.
806         if (m_active && currentMediaTime >= start && currentMediaTime < end && m_private->readyState() > MediaPlayer::HaveMetadata)
807             m_private->setReadyState(MediaPlayer::HaveMetadata);
808     }
809     
810     updateBufferedFromTrackBuffers();
811
812     // 4. If buffer full flag equals true and this object is ready to accept more bytes, then set the buffer full flag to false.
813     // No-op
814
815     LOG(Media, "SourceBuffer::removeCodedFrames(%p) - buffered = %s", this, toString(m_buffered->ranges()).utf8().data());
816 }
817
818 void SourceBuffer::removeTimerFired()
819 {
820     if (isRemoved())
821         return;
822
823     ASSERT(m_updating);
824     ASSERT(m_pendingRemoveStart.isValid());
825     ASSERT(m_pendingRemoveStart < m_pendingRemoveEnd);
826
827     // Section 3.5.7 Range Removal
828     // http://w3c.github.io/media-source/#sourcebuffer-range-removal
829
830     // 6. Run the coded frame removal algorithm with start and end as the start and end of the removal range.
831     removeCodedFrames(m_pendingRemoveStart, m_pendingRemoveEnd);
832
833     // 7. Set the updating attribute to false.
834     m_updating = false;
835     m_pendingRemoveStart = MediaTime::invalidTime();
836     m_pendingRemoveEnd = MediaTime::invalidTime();
837
838     // 8. Queue a task to fire a simple event named update at this SourceBuffer object.
839     scheduleEvent(eventNames().updateEvent);
840
841     // 9. Queue a task to fire a simple event named updateend at this SourceBuffer object.
842     scheduleEvent(eventNames().updateendEvent);
843 }
844
845 void SourceBuffer::evictCodedFrames(size_t newDataSize)
846 {
847     // 3.5.13 Coded Frame Eviction Algorithm
848     // http://www.w3.org/TR/media-source/#sourcebuffer-coded-frame-eviction
849
850     if (isRemoved())
851         return;
852
853     // This algorithm is run to free up space in this source buffer when new data is appended.
854     // 1. Let new data equal the data that is about to be appended to this SourceBuffer.
855     // 2. If the buffer full flag equals false, then abort these steps.
856     if (!m_bufferFull)
857         return;
858
859     size_t maximumBufferSize = this->maximumBufferSize();
860
861     // 3. Let removal ranges equal a list of presentation time ranges that can be evicted from
862     // the presentation to make room for the new data.
863
864     // NOTE: begin by removing data from the beginning of the buffered ranges, 30 seconds at
865     // a time, up to 30 seconds before currentTime.
866     MediaTime thirtySeconds = MediaTime(30, 1);
867     MediaTime currentTime = m_source->currentTime();
868     MediaTime maximumRangeEnd = currentTime - thirtySeconds;
869
870 #if !LOG_DISABLED
871     LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - currentTime = %lf, require %zu bytes, maximum buffer size is %zu", this, m_source->currentTime().toDouble(), extraMemoryCost() + newDataSize, maximumBufferSize);
872     size_t initialBufferedSize = extraMemoryCost();
873 #endif
874
875     MediaTime rangeStart = MediaTime::zeroTime();
876     MediaTime rangeEnd = rangeStart + thirtySeconds;
877     while (rangeStart < maximumRangeEnd) {
878         // 4. For each range in removal ranges, run the coded frame removal algorithm with start and
879         // end equal to the removal range start and end timestamp respectively.
880         removeCodedFrames(rangeStart, std::min(rangeEnd, maximumRangeEnd));
881         if (extraMemoryCost() + newDataSize < maximumBufferSize) {
882             m_bufferFull = false;
883             break;
884         }
885
886         rangeStart += thirtySeconds;
887         rangeEnd += thirtySeconds;
888     }
889
890     if (!m_bufferFull) {
891         LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes", this, initialBufferedSize - extraMemoryCost());
892         return;
893     }
894
895     // If there still isn't enough free space and there buffers in time ranges after the current range (ie. there is a gap after
896     // the current buffered range), delete 30 seconds at a time from duration back to the current time range or 30 seconds after
897     // currenTime whichever we hit first.
898     auto buffered = m_buffered->ranges();
899     size_t currentTimeRange = buffered.find(currentTime);
900     if (currentTimeRange == notFound || currentTimeRange == buffered.length() - 1) {
901         LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes but FAILED to free enough", this, initialBufferedSize - extraMemoryCost());
902         return;
903     }
904
905     MediaTime minimumRangeStart = currentTime + thirtySeconds;
906
907     rangeEnd = m_source->duration();
908     rangeStart = rangeEnd - thirtySeconds;
909     while (rangeStart > minimumRangeStart) {
910
911         // Do not evict data from the time range that contains currentTime.
912         size_t startTimeRange = buffered.find(rangeStart);
913         if (startTimeRange == currentTimeRange) {
914             size_t endTimeRange = buffered.find(rangeEnd);
915             if (endTimeRange == currentTimeRange)
916                 break;
917
918             rangeEnd = buffered.start(endTimeRange);
919         }
920
921         // 4. For each range in removal ranges, run the coded frame removal algorithm with start and
922         // end equal to the removal range start and end timestamp respectively.
923         removeCodedFrames(std::max(minimumRangeStart, rangeStart), rangeEnd);
924         if (extraMemoryCost() + newDataSize < maximumBufferSize) {
925             m_bufferFull = false;
926             break;
927         }
928
929         rangeStart -= thirtySeconds;
930         rangeEnd -= thirtySeconds;
931     }
932
933     LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes%s", this, initialBufferedSize - extraMemoryCost(), m_bufferFull ? "" : " but FAILED to free enough");
934 }
935
936 size_t SourceBuffer::maximumBufferSize() const
937 {
938     if (isRemoved())
939         return 0;
940
941     auto* element = m_source->mediaElement();
942     if (!element)
943         return 0;
944
945     return element->maximumSourceBufferSize(*this);
946 }
947
948 VideoTrackList& SourceBuffer::videoTracks()
949 {
950     if (!m_videoTracks)
951         m_videoTracks = VideoTrackList::create(m_source->mediaElement(), scriptExecutionContext());
952     return *m_videoTracks;
953 }
954
955 AudioTrackList& SourceBuffer::audioTracks()
956 {
957     if (!m_audioTracks)
958         m_audioTracks = AudioTrackList::create(m_source->mediaElement(), scriptExecutionContext());
959     return *m_audioTracks;
960 }
961
962 TextTrackList& SourceBuffer::textTracks()
963 {
964     if (!m_textTracks)
965         m_textTracks = TextTrackList::create(m_source->mediaElement(), scriptExecutionContext());
966     return *m_textTracks;
967 }
968
969 void SourceBuffer::setActive(bool active)
970 {
971     if (m_active == active)
972         return;
973
974     m_active = active;
975     m_private->setActive(active);
976     if (!isRemoved())
977         m_source->sourceBufferDidChangeActiveState(*this, active);
978 }
979
980 void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(const InitializationSegment& segment)
981 {
982     if (isRemoved())
983         return;
984
985     LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(%p)", this);
986
987     // 3.5.8 Initialization Segment Received (ctd)
988     // https://rawgit.com/w3c/media-source/c3ad59c7a370d04430969ba73d18dc9bcde57a33/index.html#sourcebuffer-init-segment-received [Editor's Draft 09 January 2015]
989
990     // 1. Update the duration attribute if it currently equals NaN:
991     if (m_source->duration().isInvalid()) {
992         // ↳ If the initialization segment contains a duration:
993         //   Run the duration change algorithm with new duration set to the duration in the initialization segment.
994         // ↳ Otherwise:
995         //   Run the duration change algorithm with new duration set to positive Infinity.
996         MediaTime newDuration = segment.duration.isValid() ? segment.duration : MediaTime::positiveInfiniteTime();
997         m_source->setDurationInternal(newDuration);
998     }
999
1000     // 2. If the initialization segment has no audio, video, or text tracks, then run the append error algorithm
1001     // with the decode error parameter set to true and abort these steps.
1002     if (segment.audioTracks.isEmpty() && segment.videoTracks.isEmpty() && segment.textTracks.isEmpty()) {
1003         appendError(true);
1004         return;
1005     }
1006
1007     // 3. If the first initialization segment flag is true, then run the following steps:
1008     if (m_receivedFirstInitializationSegment) {
1009
1010         // 3.1. Verify the following properties. If any of the checks fail then run the append error algorithm
1011         // with the decode error parameter set to true and abort these steps.
1012         if (!validateInitializationSegment(segment)) {
1013             appendError(true);
1014             return;
1015         }
1016         // 3.2 Add the appropriate track descriptions from this initialization segment to each of the track buffers.
1017         ASSERT(segment.audioTracks.size() == audioTracks().length());
1018         for (auto& audioTrackInfo : segment.audioTracks) {
1019             if (audioTracks().length() == 1) {
1020                 audioTracks().item(0)->setPrivate(*audioTrackInfo.track);
1021                 break;
1022             }
1023
1024             auto audioTrack = audioTracks().getTrackById(audioTrackInfo.track->id());
1025             ASSERT(audioTrack);
1026             audioTrack->setPrivate(*audioTrackInfo.track);
1027         }
1028
1029         ASSERT(segment.videoTracks.size() == videoTracks().length());
1030         for (auto& videoTrackInfo : segment.videoTracks) {
1031             if (videoTracks().length() == 1) {
1032                 videoTracks().item(0)->setPrivate(*videoTrackInfo.track);
1033                 break;
1034             }
1035
1036             auto videoTrack = videoTracks().getTrackById(videoTrackInfo.track->id());
1037             ASSERT(videoTrack);
1038             videoTrack->setPrivate(*videoTrackInfo.track);
1039         }
1040
1041         ASSERT(segment.textTracks.size() == textTracks().length());
1042         for (auto& textTrackInfo : segment.textTracks) {
1043             if (textTracks().length() == 1) {
1044                 downcast<InbandTextTrack>(*textTracks().item(0)).setPrivate(*textTrackInfo.track);
1045                 break;
1046             }
1047
1048             auto textTrack = textTracks().getTrackById(textTrackInfo.track->id());
1049             ASSERT(textTrack);
1050             downcast<InbandTextTrack>(*textTrack).setPrivate(*textTrackInfo.track);
1051         }
1052
1053         // 3.3 Set the need random access point flag on all track buffers to true.
1054         for (auto& trackBuffer : m_trackBufferMap.values())
1055             trackBuffer.needRandomAccessFlag = true;
1056     }
1057
1058     // 4. Let active track flag equal false.
1059     bool activeTrackFlag = false;
1060
1061     // 5. If the first initialization segment flag is false, then run the following steps:
1062     if (!m_receivedFirstInitializationSegment) {
1063         // 5.1 If the initialization segment contains tracks with codecs the user agent does not support,
1064         // then run the append error algorithm with the decode error parameter set to true and abort these steps.
1065         // NOTE: This check is the responsibility of the SourceBufferPrivate.
1066
1067         // 5.2 For each audio track in the initialization segment, run following steps:
1068         for (auto& audioTrackInfo : segment.audioTracks) {
1069             // FIXME: Implement steps 5.2.1-5.2.8.1 as per Editor's Draft 09 January 2015, and reorder this
1070             // 5.2.1 Let new audio track be a new AudioTrack object.
1071             // 5.2.2 Generate a unique ID and assign it to the id property on new video track.
1072             auto newAudioTrack = AudioTrack::create(*this, *audioTrackInfo.track);
1073             newAudioTrack->setSourceBuffer(this);
1074
1075             // 5.2.3 If audioTracks.length equals 0, then run the following steps:
1076             if (!audioTracks().length()) {
1077                 // 5.2.3.1 Set the enabled property on new audio track to true.
1078                 newAudioTrack->setEnabled(true);
1079
1080                 // 5.2.3.2 Set active track flag to true.
1081                 activeTrackFlag = true;
1082             }
1083
1084             // 5.2.4 Add new audio track to the audioTracks attribute on this SourceBuffer object.
1085             // 5.2.5 Queue a task to fire a trusted event named addtrack, that does not bubble and is
1086             // not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object
1087             // referenced by the audioTracks attribute on this SourceBuffer object.
1088             audioTracks().append(newAudioTrack.copyRef());
1089
1090             // 5.2.6 Add new audio track to the audioTracks attribute on the HTMLMediaElement.
1091             // 5.2.7 Queue a task to fire a trusted event named addtrack, that does not bubble and is
1092             // not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object
1093             // referenced by the audioTracks attribute on the HTMLMediaElement.
1094             m_source->mediaElement()->audioTracks().append(newAudioTrack.copyRef());
1095
1096             // 5.2.8 Create a new track buffer to store coded frames for this track.
1097             ASSERT(!m_trackBufferMap.contains(newAudioTrack->id()));
1098             auto& trackBuffer = m_trackBufferMap.add(newAudioTrack->id(), TrackBuffer()).iterator->value;
1099
1100             // 5.2.9 Add the track description for this track to the track buffer.
1101             trackBuffer.description = audioTrackInfo.description;
1102
1103             m_audioCodecs.append(trackBuffer.description->codec());
1104         }
1105
1106         // 5.3 For each video track in the initialization segment, run following steps:
1107         for (auto& videoTrackInfo : segment.videoTracks) {
1108             // FIXME: Implement steps 5.3.1-5.3.8.1 as per Editor's Draft 09 January 2015, and reorder this
1109             // 5.3.1 Let new video track be a new VideoTrack object.
1110             // 5.3.2 Generate a unique ID and assign it to the id property on new video track.
1111             auto newVideoTrack = VideoTrack::create(*this, *videoTrackInfo.track);
1112             newVideoTrack->setSourceBuffer(this);
1113
1114             // 5.3.3 If videoTracks.length equals 0, then run the following steps:
1115             if (!videoTracks().length()) {
1116                 // 5.3.3.1 Set the selected property on new video track to true.
1117                 newVideoTrack->setSelected(true);
1118
1119                 // 5.3.3.2 Set active track flag to true.
1120                 activeTrackFlag = true;
1121             }
1122
1123             // 5.3.4 Add new video track to the videoTracks attribute on this SourceBuffer object.
1124             // 5.3.5 Queue a task to fire a trusted event named addtrack, that does not bubble and is
1125             // not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object
1126             // referenced by the videoTracks attribute on this SourceBuffer object.
1127             videoTracks().append(newVideoTrack.copyRef());
1128
1129             // 5.3.6 Add new video track to the videoTracks attribute on the HTMLMediaElement.
1130             // 5.3.7 Queue a task to fire a trusted event named addtrack, that does not bubble and is
1131             // not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object
1132             // referenced by the videoTracks attribute on the HTMLMediaElement.
1133             m_source->mediaElement()->videoTracks().append(newVideoTrack.copyRef());
1134
1135             // 5.3.8 Create a new track buffer to store coded frames for this track.
1136             ASSERT(!m_trackBufferMap.contains(newVideoTrack->id()));
1137             auto& trackBuffer = m_trackBufferMap.add(newVideoTrack->id(), TrackBuffer()).iterator->value;
1138
1139             // 5.3.9 Add the track description for this track to the track buffer.
1140             trackBuffer.description = videoTrackInfo.description;
1141
1142             m_videoCodecs.append(trackBuffer.description->codec());
1143         }
1144
1145         // 5.4 For each text track in the initialization segment, run following steps:
1146         for (auto& textTrackInfo : segment.textTracks) {
1147             auto& textTrackPrivate = *textTrackInfo.track;
1148
1149             // FIXME: Implement steps 5.4.1-5.4.8.1 as per Editor's Draft 09 January 2015, and reorder this
1150             // 5.4.1 Let new text track be a new TextTrack object with its properties populated with the
1151             // appropriate information from the initialization segment.
1152             auto newTextTrack = InbandTextTrack::create(*scriptExecutionContext(), *this, textTrackPrivate);
1153
1154             // 5.4.2 If the mode property on new text track equals "showing" or "hidden", then set active
1155             // track flag to true.
1156             if (textTrackPrivate.mode() != InbandTextTrackPrivate::Disabled)
1157                 activeTrackFlag = true;
1158
1159             // 5.4.3 Add new text track to the textTracks attribute on this SourceBuffer object.
1160             // 5.4.4 Queue a task to fire a trusted event named addtrack, that does not bubble and is
1161             // not cancelable, and that uses the TrackEvent interface, at textTracks attribute on this
1162             // SourceBuffer object.
1163             textTracks().append(newTextTrack.get());
1164
1165             // 5.4.5 Add new text track to the textTracks attribute on the HTMLMediaElement.
1166             // 5.4.6 Queue a task to fire a trusted event named addtrack, that does not bubble and is
1167             // not cancelable, and that uses the TrackEvent interface, at the TextTrackList object
1168             // referenced by the textTracks attribute on the HTMLMediaElement.
1169             m_source->mediaElement()->textTracks().append(WTFMove(newTextTrack));
1170
1171             // 5.4.7 Create a new track buffer to store coded frames for this track.
1172             ASSERT(!m_trackBufferMap.contains(textTrackPrivate.id()));
1173             auto& trackBuffer = m_trackBufferMap.add(textTrackPrivate.id(), TrackBuffer()).iterator->value;
1174
1175             // 5.4.8 Add the track description for this track to the track buffer.
1176             trackBuffer.description = textTrackInfo.description;
1177
1178             m_textCodecs.append(trackBuffer.description->codec());
1179         }
1180
1181         // 5.5 If active track flag equals true, then run the following steps:
1182         if (activeTrackFlag) {
1183             // 5.5.1 Add this SourceBuffer to activeSourceBuffers.
1184             // 5.5.2 Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
1185             setActive(true);
1186         }
1187
1188         // 5.6 Set first initialization segment flag to true.
1189         m_receivedFirstInitializationSegment = true;
1190     }
1191
1192     // 6. If the HTMLMediaElement.readyState attribute is HAVE_NOTHING, then run the following steps:
1193     if (m_private->readyState() == MediaPlayer::HaveNothing) {
1194         // 6.1 If one or more objects in sourceBuffers have first initialization segment flag set to false, then abort these steps.
1195         for (auto& sourceBuffer : *m_source->sourceBuffers()) {
1196             if (!sourceBuffer->m_receivedFirstInitializationSegment)
1197                 return;
1198         }
1199
1200         // 6.2 Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
1201         // 6.3 Queue a task to fire a simple event named loadedmetadata at the media element.
1202         m_private->setReadyState(MediaPlayer::HaveMetadata);
1203     }
1204
1205     // 7. If the active track flag equals true and the HTMLMediaElement.readyState
1206     // attribute is greater than HAVE_CURRENT_DATA, then set the HTMLMediaElement.readyState
1207     // attribute to HAVE_METADATA.
1208     if (activeTrackFlag && m_private->readyState() > MediaPlayer::HaveCurrentData)
1209         m_private->setReadyState(MediaPlayer::HaveMetadata);
1210 }
1211
1212 bool SourceBuffer::validateInitializationSegment(const InitializationSegment& segment)
1213 {
1214     // FIXME: ordering of all 3.5.X (X>=7) functions needs to be updated to post-[24 July 2014 Editor's Draft] version
1215     // 3.5.8 Initialization Segment Received (ctd)
1216     // https://rawgit.com/w3c/media-source/c3ad59c7a370d04430969ba73d18dc9bcde57a33/index.html#sourcebuffer-init-segment-received [Editor's Draft 09 January 2015]
1217
1218     // Note: those are checks from step 3.1
1219     //   * The number of audio, video, and text tracks match what was in the first initialization segment.
1220     if (segment.audioTracks.size() != audioTracks().length()
1221         || segment.videoTracks.size() != videoTracks().length()
1222         || segment.textTracks.size() != textTracks().length())
1223         return false;
1224
1225     //   * The codecs for each track, match what was specified in the first initialization segment.
1226     for (auto& audioTrackInfo : segment.audioTracks) {
1227         if (!m_audioCodecs.contains(audioTrackInfo.description->codec()))
1228             return false;
1229     }
1230
1231     for (auto& videoTrackInfo : segment.videoTracks) {
1232         if (!m_videoCodecs.contains(videoTrackInfo.description->codec()))
1233             return false;
1234     }
1235
1236     for (auto& textTrackInfo : segment.textTracks) {
1237         if (!m_textCodecs.contains(textTrackInfo.description->codec()))
1238             return false;
1239     }
1240
1241     //   * If more than one track for a single type are present (ie 2 audio tracks), then the Track
1242     //   IDs match the ones in the first initialization segment.
1243     if (segment.audioTracks.size() >= 2) {
1244         for (auto& audioTrackInfo : segment.audioTracks) {
1245             if (!m_trackBufferMap.contains(audioTrackInfo.track->id()))
1246                 return false;
1247         }
1248     }
1249
1250     if (segment.videoTracks.size() >= 2) {
1251         for (auto& videoTrackInfo : segment.videoTracks) {
1252             if (!m_trackBufferMap.contains(videoTrackInfo.track->id()))
1253                 return false;
1254         }
1255     }
1256
1257     if (segment.textTracks.size() >= 2) {
1258         for (auto& textTrackInfo : segment.videoTracks) {
1259             if (!m_trackBufferMap.contains(textTrackInfo.track->id()))
1260                 return false;
1261         }
1262     }
1263
1264     return true;
1265 }
1266
1267 class SampleLessThanComparator {
1268 public:
1269     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value1, std::pair<MediaTime, RefPtr<MediaSample>> value2)
1270     {
1271         return value1.first < value2.first;
1272     }
1273
1274     bool operator()(MediaTime value1, std::pair<MediaTime, RefPtr<MediaSample>> value2)
1275     {
1276         return value1 < value2.first;
1277     }
1278
1279     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value1, MediaTime value2)
1280     {
1281         return value1.first < value2;
1282     }
1283 };
1284
1285 void SourceBuffer::appendError(bool decodeErrorParam)
1286 {
1287     // 3.5.3 Append Error Algorithm
1288     // https://rawgit.com/w3c/media-source/c3ad59c7a370d04430969ba73d18dc9bcde57a33/index.html#sourcebuffer-append-error [Editor's Draft 09 January 2015]
1289
1290     ASSERT(m_updating);
1291     // 1. Run the reset parser state algorithm.
1292     resetParserState();
1293
1294     // 2. Set the updating attribute to false.
1295     m_updating = false;
1296
1297     // 3. Queue a task to fire a simple event named error at this SourceBuffer object.
1298     scheduleEvent(eventNames().errorEvent);
1299
1300     // 4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
1301     scheduleEvent(eventNames().updateendEvent);
1302
1303     // 5. If decode error is true, then run the end of stream algorithm with the error parameter set to "decode".
1304     if (decodeErrorParam)
1305         m_source->streamEndedWithError(MediaSource::EndOfStreamError::Decode);
1306 }
1307
1308 void SourceBuffer::sourceBufferPrivateDidReceiveSample(MediaSample& sample)
1309 {
1310     if (isRemoved())
1311         return;
1312
1313     // 3.5.1 Segment Parser Loop
1314     // 6.1 If the first initialization segment received flag is false, then run the append error algorithm
1315     //     with the decode error parameter set to true and abort this algorithm.
1316     // Note: current design makes SourceBuffer somehow ignorant of append state - it's more a thing
1317     //  of SourceBufferPrivate. That's why this check can't really be done in appendInternal.
1318     //  unless we force some kind of design with state machine switching.
1319     if (!m_receivedFirstInitializationSegment) {
1320         appendError(true);
1321         return;
1322     }
1323
1324     // 3.5.8 Coded Frame Processing
1325     // http://www.w3.org/TR/media-source/#sourcebuffer-coded-frame-processing
1326
1327     // When complete coded frames have been parsed by the segment parser loop then the following steps
1328     // are run:
1329     // 1. For each coded frame in the media segment run the following steps:
1330     // 1.1. Loop Top
1331     do {
1332         MediaTime presentationTimestamp;
1333         MediaTime decodeTimestamp;
1334
1335         if (m_shouldGenerateTimestamps) {
1336             // ↳ If generate timestamps flag equals true:
1337             // 1. Let presentation timestamp equal 0.
1338             presentationTimestamp = MediaTime::zeroTime();
1339
1340             // 2. Let decode timestamp equal 0.
1341             decodeTimestamp = MediaTime::zeroTime();
1342         } else {
1343             // ↳ Otherwise:
1344             // 1. Let presentation timestamp be a double precision floating point representation of
1345             // the coded frame's presentation timestamp in seconds.
1346             presentationTimestamp = sample.presentationTime();
1347
1348             // 2. Let decode timestamp be a double precision floating point representation of the coded frame's
1349             // decode timestamp in seconds.
1350             decodeTimestamp = sample.decodeTime();
1351         }
1352
1353         // 1.2 Let frame duration be a double precision floating point representation of the coded frame's
1354         // duration in seconds.
1355         MediaTime frameDuration = sample.duration();
1356
1357         // 1.3 If mode equals "sequence" and group start timestamp is set, then run the following steps:
1358         if (m_mode == AppendMode::Sequence && m_groupStartTimestamp.isValid()) {
1359             // 1.3.1 Set timestampOffset equal to group start timestamp - presentation timestamp.
1360             m_timestampOffset = m_groupStartTimestamp;
1361
1362             // 1.3.2 Set group end timestamp equal to group start timestamp.
1363             m_groupEndTimestamp = m_groupStartTimestamp;
1364
1365             // 1.3.3 Set the need random access point flag on all track buffers to true.
1366             for (auto& trackBuffer : m_trackBufferMap.values())
1367                 trackBuffer.needRandomAccessFlag = true;
1368
1369             // 1.3.4 Unset group start timestamp.
1370             m_groupStartTimestamp = MediaTime::invalidTime();
1371         }
1372
1373         // 1.4 If timestampOffset is not 0, then run the following steps:
1374         if (m_timestampOffset) {
1375             // 1.4.1 Add timestampOffset to the presentation timestamp.
1376             presentationTimestamp += m_timestampOffset;
1377
1378             // 1.4.2 Add timestampOffset to the decode timestamp.
1379             decodeTimestamp += m_timestampOffset;
1380         }
1381
1382         // 1.5 Let track buffer equal the track buffer that the coded frame will be added to.
1383         AtomicString trackID = sample.trackID();
1384         auto it = m_trackBufferMap.find(trackID);
1385         if (it == m_trackBufferMap.end()) {
1386             // The client managed to append a sample with a trackID not present in the initialization
1387             // segment. This would be a good place to post an message to the developer console.
1388             didDropSample();
1389             return;
1390         }
1391         TrackBuffer& trackBuffer = it->value;
1392
1393         // 1.6 ↳ If last decode timestamp for track buffer is set and decode timestamp is less than last
1394         // decode timestamp:
1395         // OR
1396         // ↳ If last decode timestamp for track buffer is set and the difference between decode timestamp and
1397         // last decode timestamp is greater than 2 times last frame duration:
1398         if (trackBuffer.lastDecodeTimestamp.isValid() && (decodeTimestamp < trackBuffer.lastDecodeTimestamp
1399             || abs(decodeTimestamp - trackBuffer.lastDecodeTimestamp) > (trackBuffer.lastFrameDuration * 2))) {
1400
1401             // 1.6.1:
1402             if (m_mode == AppendMode::Segments) {
1403                 // ↳ If mode equals "segments":
1404                 // Set group end timestamp to presentation timestamp.
1405                 m_groupEndTimestamp = presentationTimestamp;
1406             } else {
1407                 // ↳ If mode equals "sequence":
1408                 // Set group start timestamp equal to the group end timestamp.
1409                 m_groupStartTimestamp = m_groupEndTimestamp;
1410             }
1411
1412             for (auto& trackBuffer : m_trackBufferMap.values()) {
1413                 // 1.6.2 Unset the last decode timestamp on all track buffers.
1414                 trackBuffer.lastDecodeTimestamp = MediaTime::invalidTime();
1415                 // 1.6.3 Unset the last frame duration on all track buffers.
1416                 trackBuffer.lastFrameDuration = MediaTime::invalidTime();
1417                 // 1.6.4 Unset the highest presentation timestamp on all track buffers.
1418                 trackBuffer.highestPresentationTimestamp = MediaTime::invalidTime();
1419                 // 1.6.5 Set the need random access point flag on all track buffers to true.
1420                 trackBuffer.needRandomAccessFlag = true;
1421             }
1422
1423             // 1.6.6 Jump to the Loop Top step above to restart processing of the current coded frame.
1424             continue;
1425         }
1426
1427         if (m_mode == AppendMode::Sequence) {
1428             // Use the generated timestamps instead of the sample's timestamps.
1429             sample.setTimestamps(presentationTimestamp, decodeTimestamp);
1430         } else if (m_timestampOffset) {
1431             // Reflect the timestamp offset into the sample.
1432             sample.offsetTimestampsBy(m_timestampOffset);
1433         }
1434
1435         // 1.7 Let frame end timestamp equal the sum of presentation timestamp and frame duration.
1436         MediaTime frameEndTimestamp = presentationTimestamp + frameDuration;
1437
1438         // 1.8 If presentation timestamp is less than appendWindowStart, then set the need random access
1439         // point flag to true, drop the coded frame, and jump to the top of the loop to start processing
1440         // the next coded frame.
1441         // 1.9 If frame end timestamp is greater than appendWindowEnd, then set the need random access
1442         // point flag to true, drop the coded frame, and jump to the top of the loop to start processing
1443         // the next coded frame.
1444         if (presentationTimestamp < m_appendWindowStart || frameEndTimestamp > m_appendWindowEnd) {
1445             trackBuffer.needRandomAccessFlag = true;
1446             didDropSample();
1447             return;
1448         }
1449
1450
1451         // 1.10 If the decode timestamp is less than the presentation start time, then run the end of stream
1452         // algorithm with the error parameter set to "decode", and abort these steps.
1453         // NOTE: Until <https://www.w3.org/Bugs/Public/show_bug.cgi?id=27487> is resolved, we will only check
1454         // the presentation timestamp.
1455         MediaTime presentationStartTime = MediaTime::zeroTime();
1456         if (presentationTimestamp < presentationStartTime) {
1457             LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidReceiveSample(%p) - failing because presentationTimestamp < presentationStartTime", this);
1458             m_source->streamEndedWithError(MediaSource::EndOfStreamError::Decode);
1459             return;
1460         }
1461
1462         // 1.11 If the need random access point flag on track buffer equals true, then run the following steps:
1463         if (trackBuffer.needRandomAccessFlag) {
1464             // 1.11.1 If the coded frame is not a random access point, then drop the coded frame and jump
1465             // to the top of the loop to start processing the next coded frame.
1466             if (!sample.isSync()) {
1467                 didDropSample();
1468                 return;
1469             }
1470
1471             // 1.11.2 Set the need random access point flag on track buffer to false.
1472             trackBuffer.needRandomAccessFlag = false;
1473         }
1474
1475         // 1.12 Let spliced audio frame be an unset variable for holding audio splice information
1476         // 1.13 Let spliced timed text frame be an unset variable for holding timed text splice information
1477         // FIXME: Add support for sample splicing.
1478
1479         SampleMap erasedSamples;
1480         MediaTime microsecond(1, 1000000);
1481
1482         // 1.14 If last decode timestamp for track buffer is unset and presentation timestamp falls
1483         // falls within the presentation interval of a coded frame in track buffer, then run the
1484         // following steps:
1485         if (trackBuffer.lastDecodeTimestamp.isInvalid()) {
1486             auto iter = trackBuffer.samples.presentationOrder().findSampleContainingPresentationTime(presentationTimestamp);
1487             if (iter != trackBuffer.samples.presentationOrder().end()) {
1488                 // 1.14.1 Let overlapped frame be the coded frame in track buffer that matches the condition above.
1489                 RefPtr<MediaSample> overlappedFrame = iter->second;
1490
1491                 // 1.14.2 If track buffer contains audio coded frames:
1492                 // Run the audio splice frame algorithm and if a splice frame is returned, assign it to
1493                 // spliced audio frame.
1494                 // FIXME: Add support for sample splicing.
1495
1496                 // If track buffer contains video coded frames:
1497                 if (trackBuffer.description && trackBuffer.description->isVideo()) {
1498                     // 1.14.2.1 Let overlapped frame presentation timestamp equal the presentation timestamp
1499                     // of overlapped frame.
1500                     MediaTime overlappedFramePresentationTimestamp = overlappedFrame->presentationTime();
1501
1502                     // 1.14.2.2 Let remove window timestamp equal overlapped frame presentation timestamp
1503                     // plus 1 microsecond.
1504                     MediaTime removeWindowTimestamp = overlappedFramePresentationTimestamp + microsecond;
1505
1506                     // 1.14.2.3 If the presentation timestamp is less than the remove window timestamp,
1507                     // then remove overlapped frame and any coded frames that depend on it from track buffer.
1508                     if (presentationTimestamp < removeWindowTimestamp)
1509                         erasedSamples.addSample(*iter->second);
1510                 }
1511
1512                 // If track buffer contains timed text coded frames:
1513                 // Run the text splice frame algorithm and if a splice frame is returned, assign it to spliced timed text frame.
1514                 // FIXME: Add support for sample splicing.
1515             }
1516         }
1517
1518         // 1.15 Remove existing coded frames in track buffer:
1519         // If highest presentation timestamp for track buffer is not set:
1520         if (trackBuffer.highestPresentationTimestamp.isInvalid()) {
1521             // Remove all coded frames from track buffer that have a presentation timestamp greater than or
1522             // equal to presentation timestamp and less than frame end timestamp.
1523             auto iter_pair = trackBuffer.samples.presentationOrder().findSamplesBetweenPresentationTimes(presentationTimestamp, frameEndTimestamp);
1524             if (iter_pair.first != trackBuffer.samples.presentationOrder().end())
1525                 erasedSamples.addRange(iter_pair.first, iter_pair.second);
1526         }
1527
1528         // If highest presentation timestamp for track buffer is set and less than or equal to presentation timestamp
1529         if (trackBuffer.highestPresentationTimestamp.isValid() && trackBuffer.highestPresentationTimestamp <= presentationTimestamp) {
1530             // Remove all coded frames from track buffer that have a presentation timestamp greater than highest
1531             // presentation timestamp and less than or equal to frame end timestamp.
1532             do {
1533                 // NOTE: Searching from the end of the trackBuffer will be vastly more efficient if the search range is
1534                 // near the end of the buffered range. Use a linear-backwards search if the search range is within one
1535                 // frame duration of the end:
1536                 unsigned bufferedLength = trackBuffer.buffered.length();
1537                 if (!bufferedLength)
1538                     break;
1539
1540                 MediaTime highestBufferedTime = trackBuffer.buffered.maximumBufferedTime();
1541
1542                 PresentationOrderSampleMap::iterator_range range;
1543                 if (highestBufferedTime - trackBuffer.highestPresentationTimestamp < trackBuffer.lastFrameDuration)
1544                     range = trackBuffer.samples.presentationOrder().findSamplesWithinPresentationRangeFromEnd(trackBuffer.highestPresentationTimestamp, frameEndTimestamp);
1545                 else
1546                     range = trackBuffer.samples.presentationOrder().findSamplesWithinPresentationRange(trackBuffer.highestPresentationTimestamp, frameEndTimestamp);
1547
1548                 if (range.first != trackBuffer.samples.presentationOrder().end())
1549                     erasedSamples.addRange(range.first, range.second);
1550             } while(false);
1551         }
1552
1553         // 1.16 Remove decoding dependencies of the coded frames removed in the previous step:
1554         DecodeOrderSampleMap::MapType dependentSamples;
1555         if (!erasedSamples.empty()) {
1556             // If detailed information about decoding dependencies is available:
1557             // FIXME: Add support for detailed dependency information
1558
1559             // Otherwise: Remove all coded frames between the coded frames removed in the previous step
1560             // and the next random access point after those removed frames.
1561             auto firstDecodeIter = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(erasedSamples.decodeOrder().begin()->first);
1562             auto lastDecodeIter = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(erasedSamples.decodeOrder().rbegin()->first);
1563             auto nextSyncIter = trackBuffer.samples.decodeOrder().findSyncSampleAfterDecodeIterator(lastDecodeIter);
1564             dependentSamples.insert(firstDecodeIter, nextSyncIter);
1565
1566             PlatformTimeRanges erasedRanges = removeSamplesFromTrackBuffer(dependentSamples, trackBuffer, this, "sourceBufferPrivateDidReceiveSample");
1567
1568             // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly
1569             // not yet displayed samples.
1570             MediaTime currentMediaTime = m_source->currentTime();
1571             if (currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) {
1572                 PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
1573                 possiblyEnqueuedRanges.intersectWith(erasedRanges);
1574                 if (possiblyEnqueuedRanges.length())
1575                     trackBuffer.needsReenqueueing = true;
1576             }
1577
1578             erasedRanges.invert();
1579             trackBuffer.buffered.intersectWith(erasedRanges);
1580             setBufferedDirty(true);
1581         }
1582
1583         // 1.17 If spliced audio frame is set:
1584         // Add spliced audio frame to the track buffer.
1585         // If spliced timed text frame is set:
1586         // Add spliced timed text frame to the track buffer.
1587         // FIXME: Add support for sample splicing.
1588
1589         // Otherwise:
1590         // Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
1591         trackBuffer.samples.addSample(sample);
1592
1593         if (trackBuffer.lastEnqueuedDecodeEndTime.isInvalid() || decodeTimestamp >= trackBuffer.lastEnqueuedDecodeEndTime) {
1594             DecodeOrderSampleMap::KeyType decodeKey(sample.decodeTime(), sample.presentationTime());
1595             trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, &sample));
1596         }
1597
1598         // 1.18 Set last decode timestamp for track buffer to decode timestamp.
1599         trackBuffer.lastDecodeTimestamp = decodeTimestamp;
1600
1601         // 1.19 Set last frame duration for track buffer to frame duration.
1602         trackBuffer.lastFrameDuration = frameDuration;
1603
1604         // 1.20 If highest presentation timestamp for track buffer is unset or frame end timestamp is greater
1605         // than highest presentation timestamp, then set highest presentation timestamp for track buffer
1606         // to frame end timestamp.
1607         if (trackBuffer.highestPresentationTimestamp.isInvalid() || frameEndTimestamp > trackBuffer.highestPresentationTimestamp)
1608             trackBuffer.highestPresentationTimestamp = frameEndTimestamp;
1609
1610         // 1.21 If frame end timestamp is greater than group end timestamp, then set group end timestamp equal
1611         // to frame end timestamp.
1612         if (m_groupEndTimestamp.isInvalid() || frameEndTimestamp > m_groupEndTimestamp)
1613             m_groupEndTimestamp = frameEndTimestamp;
1614
1615         // 1.22 If generate timestamps flag equals true, then set timestampOffset equal to frame end timestamp.
1616         if (m_shouldGenerateTimestamps)
1617             m_timestampOffset = frameEndTimestamp;
1618
1619         // Eliminate small gaps between buffered ranges by coalescing
1620         // disjoint ranges separated by less than a "fudge factor".
1621         auto presentationEndTime = presentationTimestamp + frameDuration;
1622         auto nearestToPresentationStartTime = trackBuffer.buffered.nearest(presentationTimestamp);
1623         if (nearestToPresentationStartTime.isValid() && (presentationTimestamp - nearestToPresentationStartTime).isBetween(MediaTime::zeroTime(), MediaSource::currentTimeFudgeFactor()))
1624             presentationTimestamp = nearestToPresentationStartTime;
1625
1626         auto nearestToPresentationEndTime = trackBuffer.buffered.nearest(presentationEndTime);
1627         if (nearestToPresentationEndTime.isValid() && (nearestToPresentationEndTime - presentationEndTime).isBetween(MediaTime::zeroTime(), MediaSource::currentTimeFudgeFactor()))
1628             presentationEndTime = nearestToPresentationEndTime;
1629
1630         trackBuffer.buffered.add(presentationTimestamp, presentationEndTime);
1631         m_bufferedSinceLastMonitor += frameDuration.toDouble();
1632         setBufferedDirty(true);
1633
1634         break;
1635     } while (1);
1636
1637     // Steps 2-4 will be handled by MediaSource::monitorSourceBuffers()
1638
1639     // 5. If the media segment contains data beyond the current duration, then run the duration change algorithm with new
1640     // duration set to the maximum of the current duration and the group end timestamp.
1641     if (m_groupEndTimestamp > m_source->duration())
1642         m_source->setDurationInternal(m_groupEndTimestamp);
1643 }
1644
1645 bool SourceBuffer::hasAudio() const
1646 {
1647     return m_audioTracks && m_audioTracks->length();
1648 }
1649
1650 bool SourceBuffer::hasVideo() const
1651 {
1652     return m_videoTracks && m_videoTracks->length();
1653 }
1654
1655 bool SourceBuffer::sourceBufferPrivateHasAudio() const
1656 {
1657     return hasAudio();
1658 }
1659
1660 bool SourceBuffer::sourceBufferPrivateHasVideo() const
1661 {
1662     return hasVideo();
1663 }
1664
1665 void SourceBuffer::videoTrackSelectedChanged(VideoTrack& track)
1666 {
1667     // 2.4.5 Changes to selected/enabled track state
1668     // If the selected video track changes, then run the following steps:
1669     // 1. If the SourceBuffer associated with the previously selected video track is not associated with
1670     // any other enabled tracks, run the following steps:
1671     if (!track.selected()
1672         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
1673         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
1674         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
1675         // 1.1 Remove the SourceBuffer from activeSourceBuffers.
1676         // 1.2 Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
1677         setActive(false);
1678     } else if (track.selected()) {
1679         // 2. If the SourceBuffer associated with the newly selected video track is not already in activeSourceBuffers,
1680         // run the following steps:
1681         // 2.1 Add the SourceBuffer to activeSourceBuffers.
1682         // 2.2 Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
1683         setActive(true);
1684     }
1685
1686     if (m_videoTracks && m_videoTracks->contains(track))
1687         m_videoTracks->scheduleChangeEvent();
1688
1689     if (!isRemoved())
1690         m_source->mediaElement()->videoTrackSelectedChanged(track);
1691 }
1692
1693 void SourceBuffer::audioTrackEnabledChanged(AudioTrack& track)
1694 {
1695     // 2.4.5 Changes to selected/enabled track state
1696     // If an audio track becomes disabled and the SourceBuffer associated with this track is not
1697     // associated with any other enabled or selected track, then run the following steps:
1698     if (!track.enabled()
1699         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
1700         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
1701         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
1702         // 1. Remove the SourceBuffer associated with the audio track from activeSourceBuffers
1703         // 2. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
1704         setActive(false);
1705     } else if (track.enabled()) {
1706         // If an audio track becomes enabled and the SourceBuffer associated with this track is
1707         // not already in activeSourceBuffers, then run the following steps:
1708         // 1. Add the SourceBuffer associated with the audio track to activeSourceBuffers
1709         // 2. Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
1710         setActive(true);
1711     }
1712
1713     if (m_audioTracks && m_audioTracks->contains(track))
1714         m_audioTracks->scheduleChangeEvent();
1715
1716     if (!isRemoved())
1717         m_source->mediaElement()->audioTrackEnabledChanged(track);
1718 }
1719
1720 void SourceBuffer::textTrackModeChanged(TextTrack& track)
1721 {
1722     // 2.4.5 Changes to selected/enabled track state
1723     // If a text track mode becomes "disabled" and the SourceBuffer associated with this track is not
1724     // associated with any other enabled or selected track, then run the following steps:
1725     if (track.mode() == TextTrack::Mode::Disabled
1726         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
1727         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
1728         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
1729         // 1. Remove the SourceBuffer associated with the audio track from activeSourceBuffers
1730         // 2. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
1731         setActive(false);
1732     } else {
1733         // If a text track mode becomes "showing" or "hidden" and the SourceBuffer associated with this
1734         // track is not already in activeSourceBuffers, then run the following steps:
1735         // 1. Add the SourceBuffer associated with the text track to activeSourceBuffers
1736         // 2. Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
1737         setActive(true);
1738     }
1739
1740     if (m_textTracks && m_textTracks->contains(track))
1741         m_textTracks->scheduleChangeEvent();
1742
1743     if (!isRemoved())
1744         m_source->mediaElement()->textTrackModeChanged(track);
1745 }
1746
1747 void SourceBuffer::textTrackAddCue(TextTrack& track, TextTrackCue& cue)
1748 {
1749     if (!isRemoved())
1750         m_source->mediaElement()->textTrackAddCue(track, cue);
1751 }
1752
1753 void SourceBuffer::textTrackAddCues(TextTrack& track, const TextTrackCueList& cueList)
1754 {
1755     if (!isRemoved())
1756         m_source->mediaElement()->textTrackAddCues(track, cueList);
1757 }
1758
1759 void SourceBuffer::textTrackRemoveCue(TextTrack& track, TextTrackCue& cue)
1760 {
1761     if (!isRemoved())
1762         m_source->mediaElement()->textTrackRemoveCue(track, cue);
1763 }
1764
1765 void SourceBuffer::textTrackRemoveCues(TextTrack& track, const TextTrackCueList& cueList)
1766 {
1767     if (!isRemoved())
1768         m_source->mediaElement()->textTrackRemoveCues(track, cueList);
1769 }
1770
1771 void SourceBuffer::textTrackKindChanged(TextTrack& track)
1772 {
1773     if (!isRemoved())
1774         m_source->mediaElement()->textTrackKindChanged(track);
1775 }
1776
1777 void SourceBuffer::sourceBufferPrivateReenqueSamples(const AtomicString& trackID)
1778 {
1779     if (isRemoved())
1780         return;
1781
1782     LOG(MediaSource, "SourceBuffer::sourceBufferPrivateReenqueSamples(%p)", this);
1783     auto it = m_trackBufferMap.find(trackID);
1784     if (it == m_trackBufferMap.end())
1785         return;
1786
1787     auto& trackBuffer = it->value;
1788     trackBuffer.needsReenqueueing = true;
1789     reenqueueMediaForTime(trackBuffer, trackID, m_source->currentTime());
1790 }
1791
1792 void SourceBuffer::sourceBufferPrivateDidBecomeReadyForMoreSamples(const AtomicString& trackID)
1793 {
1794     if (isRemoved())
1795         return;
1796
1797     LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidBecomeReadyForMoreSamples(%p)", this);
1798     auto it = m_trackBufferMap.find(trackID);
1799     if (it == m_trackBufferMap.end())
1800         return;
1801
1802     auto& trackBuffer = it->value;
1803     if (!trackBuffer.needsReenqueueing && !m_source->isSeeking())
1804         provideMediaData(trackBuffer, trackID);
1805 }
1806
1807 void SourceBuffer::provideMediaData(TrackBuffer& trackBuffer, const AtomicString& trackID)
1808 {
1809     if (m_source->isSeeking())
1810         return;
1811
1812 #if !LOG_DISABLED
1813     unsigned enqueuedSamples = 0;
1814 #endif
1815
1816     while (!trackBuffer.decodeQueue.empty()) {
1817         if (!m_private->isReadyForMoreSamples(trackID)) {
1818             m_private->notifyClientWhenReadyForMoreSamples(trackID);
1819             break;
1820         }
1821
1822         // FIXME(rdar://problem/20635969): Remove this re-entrancy protection when the aforementioned radar is resolved; protecting
1823         // against re-entrancy introduces a small inefficency when removing appended samples from the decode queue one at a time
1824         // rather than when all samples have been enqueued.
1825         auto sample = trackBuffer.decodeQueue.begin()->second;
1826         trackBuffer.decodeQueue.erase(trackBuffer.decodeQueue.begin());
1827
1828         // Do not enqueue samples spanning a significant unbuffered gap.
1829         // NOTE: one second is somewhat arbitrary. MediaSource::monitorSourceBuffers() is run
1830         // on the playbackTimer, which is effectively every 350ms. Allowing > 350ms gap between
1831         // enqueued samples allows for situations where we overrun the end of a buffered range
1832         // but don't notice for 350s of playback time, and the client can enqueue data for the
1833         // new current time without triggering this early return.
1834         // FIXME(135867): Make this gap detection logic less arbitrary.
1835         MediaTime oneSecond(1, 1);
1836         if (trackBuffer.lastEnqueuedDecodeEndTime.isValid() && sample->decodeTime() - trackBuffer.lastEnqueuedDecodeEndTime > oneSecond)
1837             break;
1838
1839         trackBuffer.lastEnqueuedPresentationTime = sample->presentationTime();
1840         trackBuffer.lastEnqueuedDecodeEndTime = sample->decodeTime() + sample->duration();
1841         m_private->enqueueSample(sample.releaseNonNull(), trackID);
1842 #if !LOG_DISABLED
1843         ++enqueuedSamples;
1844 #endif
1845     }
1846
1847     LOG(MediaSource, "SourceBuffer::provideMediaData(%p) - Enqueued %u samples", this, enqueuedSamples);
1848 }
1849
1850 void SourceBuffer::reenqueueMediaForTime(TrackBuffer& trackBuffer, const AtomicString& trackID, const MediaTime& time)
1851 {
1852     m_private->flush(trackID);
1853     trackBuffer.decodeQueue.clear();
1854
1855     // Find the sample which contains the current presentation time.
1856     auto currentSamplePTSIterator = trackBuffer.samples.presentationOrder().findSampleContainingPresentationTime(time);
1857
1858     if (currentSamplePTSIterator == trackBuffer.samples.presentationOrder().end())
1859         currentSamplePTSIterator = trackBuffer.samples.presentationOrder().findSampleStartingOnOrAfterPresentationTime(time);
1860
1861     if (currentSamplePTSIterator == trackBuffer.samples.presentationOrder().end()
1862         || (currentSamplePTSIterator->first - time) > MediaSource::currentTimeFudgeFactor())
1863         return;
1864
1865     // Seach backward for the previous sync sample.
1866     DecodeOrderSampleMap::KeyType decodeKey(currentSamplePTSIterator->second->decodeTime(), currentSamplePTSIterator->second->presentationTime());
1867     auto currentSampleDTSIterator = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(decodeKey);
1868     ASSERT(currentSampleDTSIterator != trackBuffer.samples.decodeOrder().end());
1869
1870     auto reverseCurrentSampleIter = --DecodeOrderSampleMap::reverse_iterator(currentSampleDTSIterator);
1871     auto reverseLastSyncSampleIter = trackBuffer.samples.decodeOrder().findSyncSamplePriorToDecodeIterator(reverseCurrentSampleIter);
1872     if (reverseLastSyncSampleIter == trackBuffer.samples.decodeOrder().rend())
1873         return;
1874
1875     // Fill the decode queue with the non-displaying samples.
1876     for (auto iter = reverseLastSyncSampleIter; iter != reverseCurrentSampleIter; --iter) {
1877         auto copy = iter->second->createNonDisplayingCopy();
1878         DecodeOrderSampleMap::KeyType decodeKey(copy->decodeTime(), copy->presentationTime());
1879         trackBuffer.decodeQueue.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, WTFMove(copy)));
1880     }
1881
1882     if (!trackBuffer.decodeQueue.empty()) {
1883         auto& lastSample = trackBuffer.decodeQueue.rbegin()->second;
1884         trackBuffer.lastEnqueuedPresentationTime = lastSample->presentationTime();
1885         trackBuffer.lastEnqueuedDecodeEndTime = lastSample->decodeTime();
1886     } else {
1887         trackBuffer.lastEnqueuedPresentationTime = MediaTime::invalidTime();
1888         trackBuffer.lastEnqueuedDecodeEndTime = MediaTime::invalidTime();
1889     }
1890
1891     // Fill the decode queue with the remaining samples.
1892     for (auto iter = currentSampleDTSIterator; iter != trackBuffer.samples.decodeOrder().end(); ++iter)
1893         trackBuffer.decodeQueue.insert(*iter);
1894     provideMediaData(trackBuffer, trackID);
1895
1896     trackBuffer.needsReenqueueing = false;
1897 }
1898
1899
1900 void SourceBuffer::didDropSample()
1901 {
1902     if (!isRemoved())
1903         m_source->mediaElement()->incrementDroppedFrameCount();
1904 }
1905
1906 void SourceBuffer::monitorBufferingRate()
1907 {
1908     double now = monotonicallyIncreasingTime();
1909     double interval = now - m_timeOfBufferingMonitor;
1910     double rateSinceLastMonitor = m_bufferedSinceLastMonitor / interval;
1911
1912     m_timeOfBufferingMonitor = now;
1913     m_bufferedSinceLastMonitor = 0;
1914
1915     m_averageBufferRate += (interval * ExponentialMovingAverageCoefficient) * (rateSinceLastMonitor - m_averageBufferRate);
1916
1917     LOG(MediaSource, "SourceBuffer::monitorBufferingRate(%p) - m_avegareBufferRate: %lf", this, m_averageBufferRate);
1918 }
1919
1920 void SourceBuffer::updateBufferedFromTrackBuffers()
1921 {
1922     // 3.1 Attributes, buffered
1923     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-sourcebuffer-buffered
1924
1925     // 2. Let highest end time be the largest track buffer ranges end time across all the track buffers managed by this SourceBuffer object.
1926     MediaTime highestEndTime = MediaTime::negativeInfiniteTime();
1927     for (auto& trackBuffer : m_trackBufferMap.values()) {
1928         if (!trackBuffer.buffered.length())
1929             continue;
1930         highestEndTime = std::max(highestEndTime, trackBuffer.buffered.maximumBufferedTime());
1931     }
1932
1933     // NOTE: Short circuit the following if none of the TrackBuffers have buffered ranges to avoid generating
1934     // a single range of {0, 0}.
1935     if (highestEndTime.isNegativeInfinite()) {
1936         m_buffered->ranges() = PlatformTimeRanges();
1937         return;
1938     }
1939
1940     // 3. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
1941     PlatformTimeRanges intersectionRanges { MediaTime::zeroTime(), highestEndTime };
1942
1943     // 4. For each audio and video track buffer managed by this SourceBuffer, run the following steps:
1944     for (auto& trackBuffer : m_trackBufferMap.values()) {
1945         // 4.1 Let track ranges equal the track buffer ranges for the current track buffer.
1946         PlatformTimeRanges trackRanges = trackBuffer.buffered;
1947         if (!trackRanges.length())
1948             continue;
1949
1950         // 4.2 If readyState is "ended", then set the end time on the last range in track ranges to highest end time.
1951         if (m_source->isEnded())
1952             trackRanges.add(trackRanges.maximumBufferedTime(), highestEndTime);
1953
1954         // 4.3 Let new intersection ranges equal the intersection between the intersection ranges and the track ranges.
1955         // 4.4 Replace the ranges in intersection ranges with the new intersection ranges.
1956         intersectionRanges.intersectWith(trackRanges);
1957     }
1958
1959     // 5. If intersection ranges does not contain the exact same range information as the current value of this attribute,
1960     //    then update the current value of this attribute to intersection ranges.
1961     m_buffered->ranges() = intersectionRanges;
1962     setBufferedDirty(true);
1963 }
1964
1965 bool SourceBuffer::canPlayThroughRange(PlatformTimeRanges& ranges)
1966 {
1967     if (isRemoved())
1968         return false;
1969
1970     monitorBufferingRate();
1971
1972     // Assuming no fluctuations in the buffering rate, loading 1 second per second or greater
1973     // means indefinite playback. This could be improved by taking jitter into account.
1974     if (m_averageBufferRate > 1)
1975         return true;
1976
1977     // Add up all the time yet to be buffered.
1978     MediaTime currentTime = m_source->currentTime();
1979     MediaTime duration = m_source->duration();
1980
1981     PlatformTimeRanges unbufferedRanges = ranges;
1982     unbufferedRanges.invert();
1983     unbufferedRanges.intersectWith(PlatformTimeRanges(currentTime, std::max(currentTime, duration)));
1984     MediaTime unbufferedTime = unbufferedRanges.totalDuration();
1985     if (!unbufferedTime.isValid())
1986         return true;
1987
1988     MediaTime timeRemaining = duration - currentTime;
1989     return unbufferedTime.toDouble() / m_averageBufferRate < timeRemaining.toDouble();
1990 }
1991
1992 size_t SourceBuffer::extraMemoryCost() const
1993 {
1994     size_t extraMemoryCost = m_pendingAppendData.capacity();
1995     for (auto& trackBuffer : m_trackBufferMap.values())
1996         extraMemoryCost += trackBuffer.samples.sizeInBytes();
1997
1998     return extraMemoryCost;
1999 }
2000
2001 void SourceBuffer::reportExtraMemoryAllocated()
2002 {
2003     size_t extraMemoryCost = this->extraMemoryCost();
2004     if (extraMemoryCost <= m_reportedExtraMemoryCost)
2005         return;
2006
2007     size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
2008     m_reportedExtraMemoryCost = extraMemoryCost;
2009
2010     JSC::JSLockHolder lock(scriptExecutionContext()->vm());
2011     // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
2012     // https://bugs.webkit.org/show_bug.cgi?id=142595
2013     scriptExecutionContext()->vm().heap.deprecatedReportExtraMemory(extraMemoryCostDelta);
2014 }
2015
2016 Vector<String> SourceBuffer::bufferedSamplesForTrackID(const AtomicString& trackID)
2017 {
2018     auto it = m_trackBufferMap.find(trackID);
2019     if (it == m_trackBufferMap.end())
2020         return Vector<String>();
2021
2022     TrackBuffer& trackBuffer = it->value;
2023     Vector<String> sampleDescriptions;
2024     for (auto& pair : trackBuffer.samples.decodeOrder())
2025         sampleDescriptions.append(toString(*pair.second));
2026
2027     return sampleDescriptions;
2028 }
2029
2030 Vector<String> SourceBuffer::enqueuedSamplesForTrackID(const AtomicString& trackID)
2031 {
2032     return m_private->enqueuedSamplesForTrackID(trackID);
2033 }
2034
2035 Document& SourceBuffer::document() const
2036 {
2037     ASSERT(scriptExecutionContext());
2038     return downcast<Document>(*scriptExecutionContext());
2039 }
2040
2041 ExceptionOr<void> SourceBuffer::setMode(AppendMode newMode)
2042 {
2043     // 3.1 Attributes - mode
2044     // http://www.w3.org/TR/media-source/#widl-SourceBuffer-mode
2045
2046     // On setting, run the following steps:
2047
2048     // 1. Let new mode equal the new value being assigned to this attribute.
2049     // 2. If generate timestamps flag equals true and new mode equals "segments", then throw an INVALID_ACCESS_ERR exception and abort these steps.
2050     if (m_shouldGenerateTimestamps && newMode == AppendMode::Segments)
2051         return Exception { INVALID_ACCESS_ERR };
2052
2053     // 3. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw an INVALID_STATE_ERR exception and abort these steps.
2054     // 4. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
2055     if (isRemoved() || m_updating)
2056         return Exception { INVALID_STATE_ERR };
2057
2058     // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
2059     if (m_source->isEnded()) {
2060         // 5.1. Set the readyState attribute of the parent media source to "open"
2061         // 5.2. Queue a task to fire a simple event named sourceopen at the parent media source.
2062         m_source->openIfInEndedState();
2063     }
2064
2065     // 6. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
2066     if (m_appendState == ParsingMediaSegment)
2067         return Exception { INVALID_STATE_ERR };
2068
2069     // 7. If the new mode equals "sequence", then set the group start timestamp to the group end timestamp.
2070     if (newMode == AppendMode::Sequence)
2071         m_groupStartTimestamp = m_groupEndTimestamp;
2072
2073     // 8. Update the attribute to new mode.
2074     m_mode = newMode;
2075
2076     return { };
2077 }
2078
2079 } // namespace WebCore
2080
2081 #endif