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