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