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