e5049ed74df80dedfa579c3293c2a6d1c6c25ebf
[WebKit-https.git] / Source / WebCore / Modules / mediasource / SourceBuffer.cpp
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "SourceBuffer.h"
33
34 #if ENABLE(MEDIA_SOURCE)
35
36 #include "AudioTrackList.h"
37 #include "Event.h"
38 #include "ExceptionCodePlaceholder.h"
39 #include "GenericEventQueue.h"
40 #include "HTMLMediaElement.h"
41 #include "InbandTextTrack.h"
42 #include "Logging.h"
43 #include "MediaDescription.h"
44 #include "MediaSample.h"
45 #include "MediaSource.h"
46 #include "SampleMap.h"
47 #include "SourceBufferPrivate.h"
48 #include "TextTrackList.h"
49 #include "TimeRanges.h"
50 #include "VideoTrackList.h"
51 #include <map>
52 #include <wtf/NeverDestroyed.h>
53
54 namespace WebCore {
55
56 struct SourceBuffer::TrackBuffer {
57     MediaTime lastDecodeTimestamp;
58     MediaTime lastFrameDuration;
59     MediaTime highestPresentationTimestamp;
60     MediaTime lastEnqueuedPresentationTime;
61     bool needRandomAccessFlag;
62     bool enabled;
63     SampleMap samples;
64     SampleMap::MapType decodeQueue;
65     RefPtr<MediaDescription> description;
66
67     TrackBuffer()
68         : lastDecodeTimestamp(MediaTime::invalidTime())
69         , lastFrameDuration(MediaTime::invalidTime())
70         , highestPresentationTimestamp(MediaTime::invalidTime())
71         , lastEnqueuedPresentationTime(MediaTime::invalidTime())
72         , needRandomAccessFlag(true)
73         , enabled(false)
74     {
75     }
76 };
77
78 PassRef<SourceBuffer> SourceBuffer::create(PassRef<SourceBufferPrivate> sourceBufferPrivate, MediaSource* source)
79 {
80     RefPtr<SourceBuffer> sourceBuffer(adoptRef(new SourceBuffer(std::move(sourceBufferPrivate), source)));
81     sourceBuffer->suspendIfNeeded();
82     return sourceBuffer.releaseNonNull();
83 }
84
85 SourceBuffer::SourceBuffer(PassRef<SourceBufferPrivate> sourceBufferPrivate, MediaSource* source)
86     : ActiveDOMObject(source->scriptExecutionContext())
87     , m_private(std::move(sourceBufferPrivate))
88     , m_source(source)
89     , m_asyncEventQueue(*this)
90     , m_updating(false)
91     , m_appendBufferTimer(this, &SourceBuffer::appendBufferTimerFired)
92     , m_highestPresentationEndTimestamp(MediaTime::invalidTime())
93     , m_receivedFirstInitializationSegment(false)
94     , m_buffered(TimeRanges::create())
95     , m_active(false)
96     , m_appendState(WaitingForSegment)
97 {
98     ASSERT(m_private);
99     ASSERT(m_source);
100
101     m_private->setClient(this);
102 }
103
104 SourceBuffer::~SourceBuffer()
105 {
106     ASSERT(isRemoved());
107
108     m_private->setClient(0);
109 }
110
111 PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionCode& ec) const
112 {
113     // Section 3.1 buffered attribute steps.
114     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#attributes-1
115     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
116     //    INVALID_STATE_ERR exception and abort these steps.
117     if (isRemoved()) {
118         ec = INVALID_STATE_ERR;
119         return nullptr;
120     }
121
122     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
123     return m_buffered->copy();
124 }
125
126 const RefPtr<TimeRanges>& SourceBuffer::buffered() const
127 {
128     return m_buffered;
129 }
130
131 double SourceBuffer::timestampOffset() const
132 {
133     return m_timestampOffset.toDouble();
134 }
135
136 void SourceBuffer::setTimestampOffset(double offset, ExceptionCode& ec)
137 {
138     // Section 3.1 timestampOffset attribute setter steps.
139     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#attributes-1
140     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
141     //    INVALID_STATE_ERR exception and abort these steps.
142     if (isRemoved()) {
143         ec = INVALID_STATE_ERR;
144         return;
145     }
146
147     // 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
148     if (m_updating) {
149         ec = INVALID_STATE_ERR;
150         return;
151     }
152
153     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
154     // 4.1 Set the readyState attribute of the parent media source to "open"
155     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
156     m_source->openIfInEndedState();
157
158     // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
159     if (m_appendState == ParsingMediaSegment) {
160         ec = INVALID_STATE_ERR;
161         return;
162     }
163
164     // 6. Update the attribute to the new value.
165     m_timestampOffset = MediaTime::createWithDouble(offset);
166 }
167
168 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionCode& ec)
169 {
170     // Section 3.2 appendBuffer()
171     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
172     // 1. If data is null then throw an INVALID_ACCESS_ERR exception and abort these steps.
173     if (!data) {
174         ec = INVALID_ACCESS_ERR;
175         return;
176     }
177
178     appendBufferInternal(static_cast<unsigned char*>(data->data()), data->byteLength(), ec);
179 }
180
181 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionCode& ec)
182 {
183     // Section 3.2 appendBuffer()
184     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
185     // 1. If data is null then throw an INVALID_ACCESS_ERR exception and abort these steps.
186     if (!data) {
187         ec = INVALID_ACCESS_ERR;
188         return;
189     }
190
191     appendBufferInternal(static_cast<unsigned char*>(data->baseAddress()), data->byteLength(), ec);
192 }
193
194 void SourceBuffer::abort(ExceptionCode& ec)
195 {
196     // Section 3.2 abort() method steps.
197     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
198     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
199     //    then throw an INVALID_STATE_ERR exception and abort these steps.
200     // 2. If the readyState attribute of the parent media source is not in the "open" state
201     //    then throw an INVALID_STATE_ERR exception and abort these steps.
202     if (isRemoved() || !m_source->isOpen()) {
203         ec = INVALID_STATE_ERR;
204         return;
205     }
206
207     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
208     abortIfUpdating();
209
210     // 4. Run the reset parser state algorithm.
211     m_private->abort();
212
213     // FIXME(229408) Add steps 5-6 update appendWindowStart & appendWindowEnd.
214 }
215
216
217 void SourceBuffer::abortIfUpdating()
218 {
219     // Section 3.2 abort() method step 3 substeps.
220     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
221
222     if (!m_updating)
223         return;
224
225     // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
226     m_appendBufferTimer.stop();
227     m_pendingAppendData.clear();
228
229     // 3.2. Set the updating attribute to false.
230     m_updating = false;
231
232     // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
233     scheduleEvent(eventNames().abortEvent);
234
235     // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
236     scheduleEvent(eventNames().updateendEvent);
237 }
238
239 void SourceBuffer::removedFromMediaSource()
240 {
241     if (isRemoved())
242         return;
243
244     m_private->removedFromMediaSource();
245     m_source = 0;
246     m_asyncEventQueue.close();
247 }
248
249 void SourceBuffer::sourceBufferPrivateSeekToTime(SourceBufferPrivate*, const MediaTime& time)
250 {
251     LOG(Media, "SourceBuffer::sourceBufferPrivateSeekToTime(%p)", this);
252
253     for (auto trackBufferIterator = m_trackBufferMap.begin(); trackBufferIterator != m_trackBufferMap.end(); ++trackBufferIterator) {
254         TrackBuffer& trackBuffer = trackBufferIterator->value;
255         AtomicString trackID = trackBufferIterator->key;
256
257         // Find the sample which contains the current presentation time.
258         auto currentSamplePTSIterator = trackBuffer.samples.findSampleContainingPresentationTime(time);
259
260         if (currentSamplePTSIterator == trackBuffer.samples.presentationEnd()) {
261             trackBuffer.decodeQueue.clear();
262             m_private->flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>>(), trackID);
263             continue;
264         }
265
266         // Seach backward for the previous sync sample.
267         MediaTime currentSampleDecodeTime = currentSamplePTSIterator->second->decodeTime();
268         auto currentSampleDTSIterator = trackBuffer.samples.findSampleWithDecodeTime(currentSampleDecodeTime);
269         ASSERT(currentSampleDTSIterator != trackBuffer.samples.decodeEnd());
270
271         auto reverseCurrentSampleIter = --SampleMap::reverse_iterator(currentSampleDTSIterator);
272         auto reverseLastSyncSampleIter = trackBuffer.samples.findSyncSamplePriorToDecodeIterator(reverseCurrentSampleIter);
273         if (reverseLastSyncSampleIter == trackBuffer.samples.reverseDecodeEnd()) {
274             trackBuffer.decodeQueue.clear();
275             m_private->flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>>(), trackID);
276             continue;
277         }
278
279         Vector<RefPtr<MediaSample>> nonDisplayingSamples;
280         for (auto iter = reverseLastSyncSampleIter; iter != reverseCurrentSampleIter; --iter)
281             nonDisplayingSamples.append(iter->second);
282
283         m_private->flushAndEnqueueNonDisplayingSamples(nonDisplayingSamples, trackID);
284
285         // Fill the decode queue with the remaining samples.
286         trackBuffer.decodeQueue.clear();
287         for (auto iter = currentSampleDTSIterator; iter != trackBuffer.samples.decodeEnd(); ++iter)
288             trackBuffer.decodeQueue.insert(*iter);
289
290         provideMediaData(trackBuffer, trackID);
291     }
292 }
293
294 MediaTime SourceBuffer::sourceBufferPrivateFastSeekTimeForMediaTime(SourceBufferPrivate*, const MediaTime& targetTime, const MediaTime& negativeThreshold, const MediaTime& positiveThreshold)
295 {
296     MediaTime seekTime = targetTime;
297     MediaTime lowerBoundTime = targetTime - negativeThreshold;
298     MediaTime upperBoundTime = targetTime + positiveThreshold;
299
300     for (auto trackBufferIterator = m_trackBufferMap.begin(); trackBufferIterator != m_trackBufferMap.end(); ++trackBufferIterator) {
301         TrackBuffer& trackBuffer = trackBufferIterator->value;
302
303         // Find the sample which contains the target time time.
304         auto futureSyncSampleIterator = trackBuffer.samples.findSyncSampleAfterPresentationTime(targetTime, positiveThreshold);
305         auto pastSyncSampleIterator = trackBuffer.samples.findSyncSamplePriorToPresentationTime(targetTime, negativeThreshold);
306         auto upperBound = trackBuffer.samples.decodeEnd();
307         auto lowerBound = trackBuffer.samples.reverseDecodeEnd();
308
309         if (futureSyncSampleIterator == upperBound && pastSyncSampleIterator == lowerBound)
310             continue;
311
312         MediaTime futureSeekTime = MediaTime::positiveInfiniteTime();
313         if (futureSyncSampleIterator != upperBound) {
314             RefPtr<MediaSample>& sample = futureSyncSampleIterator->second;
315             futureSeekTime = sample->presentationTime();
316         }
317
318         MediaTime pastSeekTime = MediaTime::negativeInfiniteTime();
319         if (pastSyncSampleIterator != lowerBound) {
320             RefPtr<MediaSample>& sample = pastSyncSampleIterator->second;
321             pastSeekTime = sample->presentationTime();
322         }
323
324         MediaTime trackSeekTime = abs(targetTime - futureSeekTime) < abs(targetTime - pastSeekTime) ? futureSeekTime : pastSeekTime;
325         if (abs(targetTime - trackSeekTime) > abs(targetTime - seekTime))
326             seekTime = trackSeekTime;
327     }
328
329     return seekTime;
330 }
331
332 bool SourceBuffer::hasPendingActivity() const
333 {
334     return m_source;
335 }
336
337 void SourceBuffer::stop()
338 {
339     m_appendBufferTimer.stop();
340 }
341
342 bool SourceBuffer::isRemoved() const
343 {
344     return !m_source;
345 }
346
347 void SourceBuffer::scheduleEvent(const AtomicString& eventName)
348 {
349     RefPtr<Event> event = Event::create(eventName, false, false);
350     event->setTarget(this);
351
352     m_asyncEventQueue.enqueueEvent(event.release());
353 }
354
355 void SourceBuffer::appendBufferInternal(unsigned char* data, unsigned size, ExceptionCode& ec)
356 {
357     // Section 3.2 appendBuffer()
358     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
359
360     // Step 1 is enforced by the caller.
361     // 2. Run the prepare append algorithm.
362     // Section 3.5.4 Prepare AppendAlgorithm
363
364     // 1. If the SourceBuffer has been removed from the sourceBuffers attribute of the parent media source
365     // then throw an INVALID_STATE_ERR exception and abort these steps.
366     // 2. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
367     if (isRemoved() || m_updating) {
368         ec = INVALID_STATE_ERR;
369         return;
370     }
371
372     // 3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
373     // 3.1. Set the readyState attribute of the parent media source to "open"
374     // 3.2. Queue a task to fire a simple event named sourceopen at the parent media source .
375     m_source->openIfInEndedState();
376
377     // 4. Run the coded frame eviction algorithm.
378     m_private->evictCodedFrames();
379
380     // 5. If the buffer full flag equals true, then throw a QUOTA_EXCEEDED_ERR exception and abort these step.
381     if (m_private->isFull()) {
382         ec = QUOTA_EXCEEDED_ERR;
383         return;
384     }
385
386     // NOTE: Return to 3.2 appendBuffer()
387     // 3. Add data to the end of the input buffer.
388     m_pendingAppendData.append(data, size);
389
390     // 4. Set the updating attribute to true.
391     m_updating = true;
392
393     // 5. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
394     scheduleEvent(eventNames().updatestartEvent);
395
396     // 6. Asynchronously run the buffer append algorithm.
397     m_appendBufferTimer.startOneShot(0);
398 }
399
400 void SourceBuffer::appendBufferTimerFired(Timer<SourceBuffer>*)
401 {
402     ASSERT(m_updating);
403
404     // Section 3.5.5 Buffer Append Algorithm
405     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
406
407     // 1. Run the segment parser loop algorithm.
408     size_t appendSize = m_pendingAppendData.size();
409     if (!appendSize) {
410         // Resize buffer for 0 byte appends so we always have a valid pointer.
411         // We need to convey all appends, even 0 byte ones to |m_private| so
412         // that it can clear its end of stream state if necessary.
413         m_pendingAppendData.resize(1);
414     }
415
416     // Section 3.5.1 Segment Parser Loop
417     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#sourcebuffer-segment-parser-loop
418     // When the segment parser loop algorithm is invoked, run the following steps:
419
420     SourceBufferPrivate::AppendResult result = SourceBufferPrivate::AppendSucceeded;
421     do {
422         // 1. Loop Top: If the input buffer is empty, then jump to the need more data step below.
423         if (!m_pendingAppendData.size())
424             break;
425
426         result = m_private->append(m_pendingAppendData.data(), appendSize);
427         m_pendingAppendData.clear();
428
429         // 2. If the input buffer contains bytes that violate the SourceBuffer byte stream format specification,
430         // then run the end of stream algorithm with the error parameter set to "decode" and abort this algorithm.
431         if (result == SourceBufferPrivate::ParsingFailed) {
432             m_source->streamEndedWithError(decodeError(), IgnorableExceptionCode());
433             break;
434         }
435
436         // NOTE: Steps 3 - 6 enforced by sourceBufferPrivateDidReceiveInitializationSegment() and
437         // sourceBufferPrivateDidReceiveSample below.
438
439         // 7. Need more data: Return control to the calling algorithm.
440     } while (0);
441
442     // NOTE: return to Section 3.5.5
443     // 2.If the segment parser loop algorithm in the previous step was aborted, then abort this algorithm.
444     if (result != SourceBufferPrivate::AppendSucceeded)
445         return;
446
447     // 3. Set the updating attribute to false.
448     m_updating = false;
449
450     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
451     scheduleEvent(eventNames().updateEvent);
452
453     // 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
454     scheduleEvent(eventNames().updateendEvent);
455
456     m_source->monitorSourceBuffers();
457     for (auto iter = m_trackBufferMap.begin(), end = m_trackBufferMap.end(); iter != end; ++iter)
458         provideMediaData(iter->value, iter->key);
459 }
460
461 const AtomicString& SourceBuffer::decodeError()
462 {
463     static NeverDestroyed<AtomicString> decode("decode", AtomicString::ConstructFromLiteral);
464     return decode;
465 }
466
467 const AtomicString& SourceBuffer::networkError()
468 {
469     static NeverDestroyed<AtomicString> network("network", AtomicString::ConstructFromLiteral);
470     return network;
471 }
472
473 VideoTrackList* SourceBuffer::videoTracks()
474 {
475     if (!m_source->mediaElement())
476         return nullptr;
477
478     if (!m_videoTracks)
479         m_videoTracks = VideoTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
480
481     return m_videoTracks.get();
482 }
483
484 AudioTrackList* SourceBuffer::audioTracks()
485 {
486     if (!m_source->mediaElement())
487         return nullptr;
488
489     if (!m_audioTracks)
490         m_audioTracks = AudioTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
491
492     return m_audioTracks.get();
493 }
494
495 TextTrackList* SourceBuffer::textTracks()
496 {
497     if (!m_source->mediaElement())
498         return nullptr;
499
500     if (!m_textTracks)
501         m_textTracks = TextTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
502
503     return m_textTracks.get();
504 }
505
506 void SourceBuffer::setActive(bool active)
507 {
508     if (m_active == active)
509         return;
510
511     m_active = active;
512     m_private->setActive(active);
513     m_source->sourceBufferDidChangeAcitveState(this, active);
514 }
515
516 void SourceBuffer::sourceBufferPrivateDidEndStream(SourceBufferPrivate*, const WTF::AtomicString& error)
517 {
518     m_source->endOfStream(error, IgnorableExceptionCode());
519 }
520
521 void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBufferPrivate*, const InitializationSegment& segment)
522 {
523     // 3.5.7 Initialization Segment Received
524     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-init-segment-received
525     // 1. Update the duration attribute if it currently equals NaN:
526     if (std::isnan(m_source->duration())) {
527         // ↳ If the initialization segment contains a duration:
528         //   Run the duration change algorithm with new duration set to the duration in the initialization segment.
529         // ↳ Otherwise:
530         //   Run the duration change algorithm with new duration set to positive Infinity.
531         MediaTime newDuration = segment.duration.isValid() ? segment.duration : MediaTime::positiveInfiniteTime();
532         m_source->setDuration(newDuration.toDouble(), IGNORE_EXCEPTION);
533     }
534
535     // 2. If the initialization segment has no audio, video, or text tracks, then run the end of stream
536     // algorithm with the error parameter set to "decode" and abort these steps.
537     if (!segment.audioTracks.size() && !segment.videoTracks.size() && !segment.textTracks.size())
538         m_source->endOfStream(decodeError(), IgnorableExceptionCode());
539
540
541     // 3. If the first initialization segment flag is true, then run the following steps:
542     if (m_receivedFirstInitializationSegment) {
543         if (!validateInitializationSegment(segment)) {
544             m_source->endOfStream(decodeError(), IgnorableExceptionCode());
545             return;
546         }
547         // 3.2 Add the appropriate track descriptions from this initialization segment to each of the track buffers.
548         // NOTE: No changes to make
549     }
550
551     // 4. Let active track flag equal false.
552     bool activeTrackFlag = false;
553
554     // 5. If the first initialization segment flag is false, then run the following steps:
555     if (!m_receivedFirstInitializationSegment) {
556         // 5.1 If the initialization segment contains tracks with codecs the user agent does not support,
557         // then run the end of stream algorithm with the error parameter set to "decode" and abort these steps.
558         // NOTE: This check is the responsibility of the SourceBufferPrivate.
559
560         // 5.2 For each audio track in the initialization segment, run following steps:
561         for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
562             AudioTrackPrivate* audioTrackPrivate = it->track.get();
563
564             // 5.2.1 Let new audio track be a new AudioTrack object.
565             // 5.2.2 Generate a unique ID and assign it to the id property on new video track.
566             RefPtr<AudioTrack> newAudioTrack = AudioTrack::create(this, audioTrackPrivate);
567             newAudioTrack->setSourceBuffer(this);
568
569             // 5.2.3 If audioTracks.length equals 0, then run the following steps:
570             if (!audioTracks()->length()) {
571                 // 5.2.3.1 Set the enabled property on new audio track to true.
572                 newAudioTrack->setEnabled(true);
573
574                 // 5.2.3.2 Set active track flag to true.
575                 activeTrackFlag = true;
576             }
577
578             // 5.2.4 Add new audio track to the audioTracks attribute on this SourceBuffer object.
579             // 5.2.5 Queue a task to fire a trusted event named addtrack, that does not bubble and is
580             // not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object
581             // referenced by the audioTracks attribute on this SourceBuffer object.
582             audioTracks()->append(newAudioTrack);
583
584             // 5.2.6 Add new audio track to the audioTracks attribute on the HTMLMediaElement.
585             // 5.2.7 Queue a task to fire a trusted event named addtrack, that does not bubble and is
586             // not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object
587             // referenced by the audioTracks attribute on the HTMLMediaElement.
588             m_source->mediaElement()->audioTracks()->append(newAudioTrack);
589
590             // 5.2.8 Create a new track buffer to store coded frames for this track.
591             ASSERT(!m_trackBufferMap.contains(newAudioTrack->id()));
592             TrackBuffer& trackBuffer = m_trackBufferMap.add(newAudioTrack->id(), TrackBuffer()).iterator->value;
593
594             // 5.2.9 Add the track description for this track to the track buffer.
595             trackBuffer.description = it->description;
596         }
597
598         // 5.3 For each video track in the initialization segment, run following steps:
599         for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
600             VideoTrackPrivate* videoTrackPrivate = it->track.get();
601
602             // 5.3.1 Let new video track be a new VideoTrack object.
603             // 5.3.2 Generate a unique ID and assign it to the id property on new video track.
604             RefPtr<VideoTrack> newVideoTrack = VideoTrack::create(this, videoTrackPrivate);
605             newVideoTrack->setSourceBuffer(this);
606
607             // 5.3.3 If videoTracks.length equals 0, then run the following steps:
608             if (!videoTracks()->length()) {
609                 // 5.3.3.1 Set the selected property on new video track to true.
610                 newVideoTrack->setSelected(true);
611
612                 // 5.3.3.2 Set active track flag to true.
613                 activeTrackFlag = true;
614             }
615
616             // 5.3.4 Add new video track to the videoTracks attribute on this SourceBuffer object.
617             // 5.3.5 Queue a task to fire a trusted event named addtrack, that does not bubble and is
618             // not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object
619             // referenced by the videoTracks attribute on this SourceBuffer object.
620             videoTracks()->append(newVideoTrack);
621
622             // 5.3.6 Add new video track to the videoTracks attribute on the HTMLMediaElement.
623             // 5.3.7 Queue a task to fire a trusted event named addtrack, that does not bubble and is
624             // not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object
625             // referenced by the videoTracks attribute on the HTMLMediaElement.
626             m_source->mediaElement()->videoTracks()->append(newVideoTrack);
627
628             // 5.3.8 Create a new track buffer to store coded frames for this track.
629             ASSERT(!m_trackBufferMap.contains(newVideoTrack->id()));
630             TrackBuffer& trackBuffer = m_trackBufferMap.add(newVideoTrack->id(), TrackBuffer()).iterator->value;
631
632             // 5.3.9 Add the track description for this track to the track buffer.
633             trackBuffer.description = it->description;
634         }
635
636         // 5.4 For each text track in the initialization segment, run following steps:
637         for (auto it = segment.textTracks.begin(); it != segment.textTracks.end(); ++it) {
638             InbandTextTrackPrivate* textTrackPrivate = it->track.get();
639
640             // 5.4.1 Let new text track be a new TextTrack object with its properties populated with the
641             // appropriate information from the initialization segment.
642             RefPtr<InbandTextTrack> newTextTrack = InbandTextTrack::create(scriptExecutionContext(), this, textTrackPrivate);
643
644             // 5.4.2 If the mode property on new text track equals "showing" or "hidden", then set active
645             // track flag to true.
646             if (textTrackPrivate->mode() != InbandTextTrackPrivate::Disabled)
647                 activeTrackFlag = true;
648
649             // 5.4.3 Add new text track to the textTracks attribute on this SourceBuffer object.
650             // 5.4.4 Queue a task to fire a trusted event named addtrack, that does not bubble and is
651             // not cancelable, and that uses the TrackEvent interface, at textTracks attribute on this
652             // SourceBuffer object.
653             textTracks()->append(newTextTrack);
654
655             // 5.4.5 Add new text track to the textTracks attribute on the HTMLMediaElement.
656             // 5.4.6 Queue a task to fire a trusted event named addtrack, that does not bubble and is
657             // not cancelable, and that uses the TrackEvent interface, at the TextTrackList object
658             // referenced by the textTracks attribute on the HTMLMediaElement.
659             m_source->mediaElement()->textTracks()->append(newTextTrack);
660
661             // 5.4.7 Create a new track buffer to store coded frames for this track.
662             ASSERT(!m_trackBufferMap.contains(textTrackPrivate->id()));
663             TrackBuffer& trackBuffer = m_trackBufferMap.add(textTrackPrivate->id(), TrackBuffer()).iterator->value;
664
665             // 5.4.8 Add the track description for this track to the track buffer.
666             trackBuffer.description = it->description;
667         }
668
669         // 5.5 If active track flag equals true, then run the following steps:
670         if (activeTrackFlag) {
671             // 5.5.1 Add this SourceBuffer to activeSourceBuffers.
672             setActive(true);
673         }
674
675         // 5.6 Set first initialization segment flag to true.
676         m_receivedFirstInitializationSegment = true;
677     }
678
679     // 6. If the HTMLMediaElement.readyState attribute is HAVE_NOTHING, then run the following steps:
680     if (m_private->readyState() == MediaPlayer::HaveNothing) {
681         // 6.1 If one or more objects in sourceBuffers have first initialization segment flag set to false, then abort these steps.
682         for (unsigned long i = 0; i < m_source->sourceBuffers()->length(); ++i) {
683             if (!m_source->sourceBuffers()->item(i)->m_receivedFirstInitializationSegment)
684                 return;
685         }
686
687         // 6.2 Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
688         // 6.3 Queue a task to fire a simple event named loadedmetadata at the media element.
689         m_private->setReadyState(MediaPlayer::HaveMetadata);
690     }
691
692     // 7. If the active track flag equals true and the HTMLMediaElement.readyState
693     // attribute is greater than HAVE_CURRENT_DATA, then set the HTMLMediaElement.readyState
694     // attribute to HAVE_METADATA.
695     if (activeTrackFlag && m_private->readyState() > MediaPlayer::HaveCurrentData)
696         m_private->setReadyState(MediaPlayer::HaveMetadata);
697 }
698
699 bool SourceBuffer::validateInitializationSegment(const InitializationSegment& segment)
700 {
701     // 3.5.7 Initialization Segment Received (ctd)
702     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-init-segment-received
703
704     // 3.1. Verify the following properties. If any of the checks fail then run the end of stream
705     // algorithm with the error parameter set to "decode" and abort these steps.
706     //   * The number of audio, video, and text tracks match what was in the first initialization segment.
707     if (segment.audioTracks.size() != audioTracks()->length()
708         || segment.videoTracks.size() != videoTracks()->length()
709         || segment.textTracks.size() != textTracks()->length())
710         return false;
711
712     //   * The codecs for each track, match what was specified in the first initialization segment.
713     for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
714         if (!m_videoCodecs.contains(it->description->codec()))
715             return false;
716     }
717
718     for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
719         if (!m_audioCodecs.contains(it->description->codec()))
720             return false;
721     }
722
723     for (auto it = segment.textTracks.begin(); it != segment.textTracks.end(); ++it) {
724         if (!m_textCodecs.contains(it->description->codec()))
725             return false;
726     }
727
728     //   * If more than one track for a single type are present (ie 2 audio tracks), then the Track
729     //   IDs match the ones in the first initialization segment.
730     if (segment.audioTracks.size() >= 2) {
731         for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
732             if (!m_trackBufferMap.contains(it->track->id()))
733                 return false;
734         }
735     }
736
737     if (segment.videoTracks.size() >= 2) {
738         for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
739             if (!m_trackBufferMap.contains(it->track->id()))
740                 return false;
741         }
742     }
743
744     if (segment.textTracks.size() >= 2) {
745         for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
746             if (!m_trackBufferMap.contains(it->track->id()))
747                 return false;
748         }
749     }
750
751     return true;
752 }
753
754 class SampleLessThanComparator {
755 public:
756     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value1, std::pair<MediaTime, RefPtr<MediaSample>> value2)
757     {
758         return value1.first < value2.first;
759     }
760
761     bool operator()(MediaTime value1, std::pair<MediaTime, RefPtr<MediaSample>> value2)
762     {
763         return value1 < value2.first;
764     }
765
766     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value1, MediaTime value2)
767     {
768         return value1.first < value2;
769     }
770 };
771
772 void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, PassRefPtr<MediaSample> prpSample)
773 {
774     RefPtr<MediaSample> sample = prpSample;
775
776     // 3.5.8 Coded Frame Processing
777     // When complete coded frames have been parsed by the segment parser loop then the following steps
778     // are run:
779     // 1. For each coded frame in the media segment run the following steps:
780     // 1.1. Loop Top
781     do {
782         // 1.1 (ctd) Let presentation timestamp be a double precision floating point representation of
783         // the coded frame's presentation timestamp in seconds.
784         MediaTime presentationTimestamp = sample->presentationTime();
785
786         // 1.2 Let decode timestamp be a double precision floating point representation of the coded frame's
787         // decode timestamp in seconds.
788         MediaTime decodeTimestamp = sample->decodeTime();
789
790         // 1.3 Let frame duration be a double precision floating point representation of the coded frame's
791         // duration in seconds.
792         MediaTime frameDuration = sample->duration();
793
794         // 1.4 If mode equals "sequence" and group start timestamp is set, then run the following steps:
795         // FIXME: add support for "sequence" mode
796
797         // 1.5 If timestampOffset is not 0, then run the following steps:
798         if (m_timestampOffset != MediaTime::zeroTime()) {
799             // 1.5.1 Add timestampOffset to the presentation timestamp.
800             presentationTimestamp += m_timestampOffset;
801
802             // 1.5.2 Add timestampOffset to the decode timestamp.
803             decodeTimestamp += m_timestampOffset;
804
805             // 1.5.3 If the presentation timestamp or decode timestamp is less than the presentation start
806             // time, then run the end of stream algorithm with the error parameter set to "decode", and
807             // abort these steps.
808             MediaTime presentationStartTime = MediaTime::zeroTime();
809             if (presentationTimestamp < presentationStartTime || decodeTimestamp < presentationStartTime) {
810                 m_source->streamEndedWithError(decodeError(), IgnorableExceptionCode());
811                 return;
812             }
813         }
814
815         // 1.6 Let track buffer equal the track buffer that the coded frame will be added to.
816         AtomicString trackID = sample->trackID();
817         auto it = m_trackBufferMap.find(trackID);
818         if (it == m_trackBufferMap.end())
819             it = m_trackBufferMap.add(trackID, TrackBuffer()).iterator;
820         TrackBuffer& trackBuffer = it->value;
821
822         // 1.7 If last decode timestamp for track buffer is set and decode timestamp is less than last
823         // decode timestamp:
824         // OR
825         // If last decode timestamp for track buffer is set and the difference between decode timestamp and
826         // last decode timestamp is greater than 2 times last frame duration:
827         if (trackBuffer.lastDecodeTimestamp.isValid() && (decodeTimestamp < trackBuffer.lastDecodeTimestamp
828             || abs(decodeTimestamp - trackBuffer.lastDecodeTimestamp) > (trackBuffer.lastFrameDuration * 2))) {
829             // 1.7.1 If mode equals "segments":
830             // Set highest presentation end timestamp to presentation timestamp.
831             m_highestPresentationEndTimestamp = presentationTimestamp;
832
833             // If mode equals "sequence":
834             // Set group start timestamp equal to the highest presentation end timestamp.
835             // FIXME: Add support for "sequence" mode.
836
837             for (auto i = m_trackBufferMap.values().begin(); i != m_trackBufferMap.values().end(); ++i) {
838                 // 1.7.2 Unset the last decode timestamp on all track buffers.
839                 i->lastDecodeTimestamp = MediaTime::invalidTime();
840                 // 1.7.3 Unset the last frame duration on all track buffers.
841                 i->lastFrameDuration = MediaTime::invalidTime();
842                 // 1.7.4 Unset the highest presentation timestamp on all track buffers.
843                 i->highestPresentationTimestamp = MediaTime::invalidTime();
844                 // 1.7.5 Set the need random access point flag on all track buffers to true.
845                 i->needRandomAccessFlag = true;
846             }
847
848             // 1.7.6 Jump to the Loop Top step above to restart processing of the current coded frame.
849             continue;
850         }
851
852         // 1.8 Let frame end timestamp equal the sum of presentation timestamp and frame duration.
853         MediaTime frameEndTimestamp = presentationTimestamp + frameDuration;
854
855         // 1.9 If presentation timestamp is less than appendWindowStart, then set the need random access
856         // point flag to true, drop the coded frame, and jump to the top of the loop to start processing
857         // the next coded frame.
858         // 1.10 If frame end timestamp is greater than appendWindowEnd, then set the need random access
859         // point flag to true, drop the coded frame, and jump to the top of the loop to start processing
860         // the next coded frame.
861         // FIXME: implement append windows
862
863         // 1.11 If the need random access point flag on track buffer equals true, then run the following steps:
864         if (trackBuffer.needRandomAccessFlag) {
865             // 1.11.1 If the coded frame is not a random access point, then drop the coded frame and jump
866             // to the top of the loop to start processing the next coded frame.
867             if (!sample->isSync()) {
868                 didDropSample();
869                 return;
870             }
871
872             // 1.11.2 Set the need random access point flag on track buffer to false.
873             trackBuffer.needRandomAccessFlag = false;
874         }
875
876         // 1.12 Let spliced audio frame be an unset variable for holding audio splice information
877         // 1.13 Let spliced timed text frame be an unset variable for holding timed text splice information
878         // FIXME: Add support for sample splicing.
879
880         SampleMap::MapType erasedSamples;
881         MediaTime microsecond(1, 1000000);
882
883         // 1.14 If last decode timestamp for track buffer is unset and there is a coded frame in
884         // track buffer with a presentation timestamp less than or equal to presentation timestamp
885         // and presentation timestamp is less than this coded frame's presentation timestamp plus
886         // its frame duration, then run the following steps:
887         if (trackBuffer.lastDecodeTimestamp.isInvalid()) {
888             auto iter = trackBuffer.samples.findSampleContainingPresentationTime(presentationTimestamp);
889             if (iter != trackBuffer.samples.presentationEnd()) {
890                 // 1.14.1 Let overlapped frame be the coded frame in track buffer that matches the condition above.
891                 RefPtr<MediaSample> overlappedFrame = iter->second;
892
893                 // 1.14.2 If track buffer contains audio coded frames:
894                 // Run the audio splice frame algorithm and if a splice frame is returned, assign it to
895                 // spliced audio frame.
896                 // FIXME: Add support for sample splicing.
897
898                 // If track buffer contains video coded frames:
899                 if (trackBuffer.description->isVideo()) {
900                     // 1.14.2.1 Let overlapped frame presentation timestamp equal the presentation timestamp
901                     // of overlapped frame.
902                     MediaTime overlappedFramePresentationTimestamp = overlappedFrame->presentationTime();
903
904                     // 1.14.2.2 Let remove window timestamp equal overlapped frame presentation timestamp
905                     // plus 1 microsecond.
906                     MediaTime removeWindowTimestamp = overlappedFramePresentationTimestamp + microsecond;
907
908                     // 1.14.2.3 If the presentation timestamp is less than the remove window timestamp,
909                     // then remove overlapped frame and any coded frames that depend on it from track buffer.
910                     if (presentationTimestamp < removeWindowTimestamp)
911                         erasedSamples.insert(*iter);
912                 }
913
914                 // If track buffer contains timed text coded frames:
915                 // Run the text splice frame algorithm and if a splice frame is returned, assign it to spliced timed text frame.
916                 // FIXME: Add support for sample splicing.
917             }
918         }
919
920         // 1.15 Remove existing coded frames in track buffer:
921         // If highest presentation timestamp for track buffer is not set:
922         if (trackBuffer.highestPresentationTimestamp.isInvalid()) {
923             // Remove all coded frames from track buffer that have a presentation timestamp greater than or
924             // equal to presentation timestamp and less than frame end timestamp.
925             auto iter_pair = trackBuffer.samples.findSamplesBetweenPresentationTimes(presentationTimestamp, frameEndTimestamp);
926             if (iter_pair.first != trackBuffer.samples.presentationEnd())
927                 erasedSamples.insert(iter_pair.first, iter_pair.second);
928         }
929
930         // If highest presentation timestamp for track buffer is set and less than presentation timestamp
931         if (trackBuffer.highestPresentationTimestamp.isValid() && trackBuffer.highestPresentationTimestamp < presentationTimestamp) {
932             // Remove all coded frames from track buffer that have a presentation timestamp greater than highest
933             // presentation timestamp and less than or equal to frame end timestamp.
934             auto iter_pair = trackBuffer.samples.findSamplesBetweenPresentationTimes(trackBuffer.highestPresentationTimestamp, frameEndTimestamp);
935             if (iter_pair.first != trackBuffer.samples.presentationEnd())
936                 erasedSamples.insert(iter_pair.first, iter_pair.second);
937         }
938
939         // 1.16 Remove decoding dependencies of the coded frames removed in the previous step:
940         SampleMap::MapType dependentSamples;
941         if (!erasedSamples.empty()) {
942             // If detailed information about decoding dependencies is available:
943             // FIXME: Add support for detailed dependency information
944
945             // Otherwise: Remove all coded frames between the coded frames removed in the previous step
946             // and the next random access point after those removed frames.
947             for (auto erasedIt = erasedSamples.begin(), end = erasedSamples.end(); erasedIt != end; ++erasedIt) {
948                 auto currentDecodeIter = trackBuffer.samples.findSampleWithDecodeTime(erasedIt->second->decodeTime());
949                 auto nextSyncIter = trackBuffer.samples.findSyncSampleAfterDecodeIterator(currentDecodeIter);
950                 dependentSamples.insert(currentDecodeIter, nextSyncIter);
951             }
952
953
954             RefPtr<TimeRanges> erasedRanges = TimeRanges::create();
955             for (auto erasedIt = erasedSamples.begin(), end = erasedSamples.end(); erasedIt != end; ++erasedIt) {
956                 double startTime = erasedIt->first.toDouble();
957                 double endTime = ((erasedIt->first + erasedIt->second->duration()) + microsecond).toDouble();
958                 erasedRanges->add(startTime, endTime);
959                 trackBuffer.samples.removeSample(erasedIt->second.get());
960             }
961
962             for (auto dependentIt = dependentSamples.begin(), end = dependentSamples.end(); dependentIt != end; ++dependentIt) {
963                 double startTime = dependentIt->first.toDouble();
964                 double endTime = ((dependentIt->first + dependentIt->second->duration()) + microsecond).toDouble();
965                 erasedRanges->add(startTime, endTime);
966                 trackBuffer.samples.removeSample(dependentIt->second.get());
967             }
968
969             erasedRanges->invert();
970             m_buffered->intersectWith(erasedRanges.get());
971         }
972
973         // 1.17 If spliced audio frame is set:
974         // Add spliced audio frame to the track buffer.
975         // If spliced timed text frame is set:
976         // Add spliced timed text frame to the track buffer.
977         // FIXME: Add support for sample splicing.
978
979         // Otherwise:
980         // Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
981         trackBuffer.samples.addSample(sample);
982         trackBuffer.decodeQueue.insert(SampleMap::MapType::value_type(decodeTimestamp, sample));
983
984         // 1.18 Set last decode timestamp for track buffer to decode timestamp.
985         trackBuffer.lastDecodeTimestamp = decodeTimestamp;
986
987         // 1.19 Set last frame duration for track buffer to frame duration.
988         trackBuffer.lastFrameDuration = frameDuration;
989
990         // 1.20 If highest presentation timestamp for track buffer is unset or frame end timestamp is greater
991         // than highest presentation timestamp, then set highest presentation timestamp for track buffer
992         // to frame end timestamp.
993         if (trackBuffer.highestPresentationTimestamp.isInvalid() || frameEndTimestamp > trackBuffer.highestPresentationTimestamp)
994             trackBuffer.highestPresentationTimestamp = frameEndTimestamp;
995
996         // 1.21 If highest presentation end timestamp is unset or frame end timestamp is greater than highest
997         // presentation end timestamp, then set highest presentation end timestamp equal to frame end timestamp.
998         if (m_highestPresentationEndTimestamp.isInvalid() || frameEndTimestamp > m_highestPresentationEndTimestamp)
999             m_highestPresentationEndTimestamp = frameEndTimestamp;
1000
1001         m_buffered->add(presentationTimestamp.toDouble(), (presentationTimestamp + frameDuration + microsecond).toDouble());
1002
1003         break;
1004     } while (1);
1005
1006     // Steps 2-4 will be handled by MediaSource::monitorSourceBuffers()
1007
1008     // 5. If the media segment contains data beyond the current duration, then run the duration change algorithm with new
1009     // duration set to the maximum of the current duration and the highest end timestamp reported by HTMLMediaElement.buffered.
1010     if (highestPresentationEndTimestamp().toDouble() > m_source->duration())
1011         m_source->setDuration(highestPresentationEndTimestamp().toDouble(), IgnorableExceptionCode());
1012 }
1013
1014 bool SourceBuffer::sourceBufferPrivateHasAudio(const SourceBufferPrivate*) const
1015 {
1016     return m_audioTracks && m_audioTracks->length();
1017 }
1018
1019 bool SourceBuffer::sourceBufferPrivateHasVideo(const SourceBufferPrivate*) const
1020 {
1021     return m_videoTracks && m_videoTracks->length();
1022 }
1023
1024 void SourceBuffer::videoTrackSelectedChanged(VideoTrack* track)
1025 {
1026     // 2.4.5 Changes to selected/enabled track state
1027     // If the selected video track changes, then run the following steps:
1028     // 1. If the SourceBuffer associated with the previously selected video track is not associated with
1029     // any other enabled tracks, run the following steps:
1030     if (track->selected()
1031         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
1032         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
1033         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
1034         // 1.1 Remove the SourceBuffer from activeSourceBuffers.
1035         // 1.2 Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
1036         setActive(false);
1037     } else if (!track->selected()) {
1038         // 2. If the SourceBuffer associated with the newly selected video track is not already in activeSourceBuffers,
1039         // run the following steps:
1040         // 2.1 Add the SourceBuffer to activeSourceBuffers.
1041         // 2.2 Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
1042         setActive(true);
1043     }
1044
1045     if (!isRemoved())
1046         m_source->mediaElement()->videoTrackSelectedChanged(track);
1047 }
1048
1049 void SourceBuffer::audioTrackEnabledChanged(AudioTrack* track)
1050 {
1051     // 2.4.5 Changes to selected/enabled track state
1052     // If an audio track becomes disabled and the SourceBuffer associated with this track is not
1053     // associated with any other enabled or selected track, then run the following steps:
1054     if (track->enabled()
1055         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
1056         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
1057         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
1058         // 1. Remove the SourceBuffer associated with the audio track from activeSourceBuffers
1059         // 2. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
1060         setActive(false);
1061     } else if (!track->enabled()) {
1062         // If an audio track becomes enabled and the SourceBuffer associated with this track is
1063         // not already in activeSourceBuffers, then run the following steps:
1064         // 1. Add the SourceBuffer associated with the audio track to activeSourceBuffers
1065         // 2. Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
1066         setActive(true);
1067     }
1068
1069     if (!isRemoved())
1070         m_source->mediaElement()->audioTrackEnabledChanged(track);
1071 }
1072
1073 void SourceBuffer::textTrackModeChanged(TextTrack* track)
1074 {
1075     // 2.4.5 Changes to selected/enabled track state
1076     // If a text track mode becomes "disabled" and the SourceBuffer associated with this track is not
1077     // associated with any other enabled or selected track, then run the following steps:
1078     if (track->mode() == TextTrack::disabledKeyword()
1079         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
1080         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
1081         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
1082         // 1. Remove the SourceBuffer associated with the audio track from activeSourceBuffers
1083         // 2. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
1084         setActive(false);
1085     } else {
1086         // If a text track mode becomes "showing" or "hidden" and the SourceBuffer associated with this
1087         // track is not already in activeSourceBuffers, then run the following steps:
1088         // 1. Add the SourceBuffer associated with the text track to activeSourceBuffers
1089         // 2. Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
1090         setActive(true);
1091     }
1092
1093     if (!isRemoved())
1094         m_source->mediaElement()->textTrackModeChanged(track);
1095 }
1096
1097 void SourceBuffer::textTrackAddCue(TextTrack* track, WTF::PassRefPtr<TextTrackCue> cue)
1098 {
1099     if (!isRemoved())
1100         m_source->mediaElement()->textTrackAddCue(track, cue);
1101 }
1102
1103 void SourceBuffer::textTrackAddCues(TextTrack* track, TextTrackCueList const* cueList)
1104 {
1105     if (!isRemoved())
1106         m_source->mediaElement()->textTrackAddCues(track, cueList);
1107 }
1108
1109 void SourceBuffer::textTrackRemoveCue(TextTrack* track, WTF::PassRefPtr<TextTrackCue> cue)
1110 {
1111     if (!isRemoved())
1112         m_source->mediaElement()->textTrackRemoveCue(track, cue);
1113 }
1114
1115 void SourceBuffer::textTrackRemoveCues(TextTrack* track, TextTrackCueList const* cueList)
1116 {
1117     if (!isRemoved())
1118         m_source->mediaElement()->textTrackRemoveCues(track, cueList);
1119 }
1120
1121 void SourceBuffer::textTrackKindChanged(TextTrack* track)
1122 {
1123     if (!isRemoved())
1124         m_source->mediaElement()->textTrackKindChanged(track);
1125 }
1126
1127 void SourceBuffer::sourceBufferPrivateDidBecomeReadyForMoreSamples(SourceBufferPrivate*, AtomicString trackID)
1128 {
1129     LOG(Media, "SourceBuffer::sourceBufferPrivateDidBecomeReadyForMoreSamples(%p)", this);
1130     auto it = m_trackBufferMap.find(trackID);
1131     if (it == m_trackBufferMap.end())
1132         return;
1133
1134     provideMediaData(it->value, trackID);
1135 }
1136
1137 void SourceBuffer::provideMediaData(TrackBuffer& trackBuffer, AtomicString trackID)
1138 {
1139 #if !LOG_DISABLED
1140     unsigned enqueuedSamples = 0;
1141 #endif
1142
1143     auto sampleIt = trackBuffer.decodeQueue.begin();
1144     for (auto sampleEnd = trackBuffer.decodeQueue.end(); sampleIt != sampleEnd; ++sampleIt) {
1145         if (!m_private->isReadyForMoreSamples(trackID)) {
1146             m_private->notifyClientWhenReadyForMoreSamples(trackID);
1147             break;
1148         }
1149
1150         RefPtr<MediaSample> sample = sampleIt->second;
1151         trackBuffer.lastEnqueuedPresentationTime = sample->presentationTime();
1152         m_private->enqueueSample(sample.release(), trackID);
1153 #if !LOG_DISABLED
1154         ++enqueuedSamples;
1155 #endif
1156
1157     }
1158     trackBuffer.decodeQueue.erase(trackBuffer.decodeQueue.begin(), sampleIt);
1159
1160     LOG(Media, "SourceBuffer::provideMediaData(%p) - Enqueued %u samples", this, enqueuedSamples);
1161 }
1162
1163 void SourceBuffer::didDropSample()
1164 {
1165     if (!isRemoved())
1166         m_source->mediaElement()->incrementDroppedFrameCount();
1167 }
1168
1169 } // namespace WebCore
1170
1171 #endif