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