[MSE] Add MediaSource extensions to AudioTrack, VideoTrack, and TextTrack.
[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 "GenericEventQueue.h"
39 #include "HTMLMediaElement.h"
40 #include "InbandTextTrack.h"
41 #include "Logging.h"
42 #include "MediaDescription.h"
43 #include "MediaSample.h"
44 #include "MediaSource.h"
45 #include "SourceBufferPrivate.h"
46 #include "TextTrackList.h"
47 #include "TimeRanges.h"
48 #include "VideoTrackList.h"
49 #include <map>
50 #include <wtf/NeverDestroyed.h>
51
52 namespace WebCore {
53
54 struct SourceBuffer::TrackBuffer {
55     MediaTime lastDecodeTimestamp;
56     MediaTime lastFrameDuration;
57     MediaTime highestPresentationTimestamp;
58     MediaTime lastEnqueuedPresentationTime;
59     bool needRandomAccessFlag;
60     typedef std::multimap<MediaTime, RefPtr<MediaSample>> SampleMap;
61     SampleMap samples;
62     SampleMap decodeQueue;
63     RefPtr<MediaDescription> description;
64
65     TrackBuffer()
66         : lastDecodeTimestamp(MediaTime::invalidTime())
67         , lastFrameDuration(MediaTime::invalidTime())
68         , highestPresentationTimestamp(MediaTime::invalidTime())
69         , lastEnqueuedPresentationTime(MediaTime::invalidTime())
70         , needRandomAccessFlag(false)
71     {
72     }
73 };
74
75 PassRef<SourceBuffer> SourceBuffer::create(PassRef<SourceBufferPrivate> sourceBufferPrivate, MediaSource* source)
76 {
77     RefPtr<SourceBuffer> sourceBuffer(adoptRef(new SourceBuffer(std::move(sourceBufferPrivate), source)));
78     sourceBuffer->suspendIfNeeded();
79     return sourceBuffer.releaseNonNull();
80 }
81
82 SourceBuffer::SourceBuffer(PassRef<SourceBufferPrivate> sourceBufferPrivate, MediaSource* source)
83     : ActiveDOMObject(source->scriptExecutionContext())
84     , m_private(std::move(sourceBufferPrivate))
85     , m_source(source)
86     , m_asyncEventQueue(*this)
87     , m_updating(false)
88     , m_appendBufferTimer(this, &SourceBuffer::appendBufferTimerFired)
89     , m_highestPresentationEndTimestamp(MediaTime::invalidTime())
90     , m_receivedFirstInitializationSegment(false)
91     , m_buffered(TimeRanges::create())
92     , m_active(false)
93     , m_appendState(WaitingForSegment)
94 {
95     ASSERT(m_private);
96     ASSERT(m_source);
97
98     m_private->setClient(this);
99 }
100
101 SourceBuffer::~SourceBuffer()
102 {
103     ASSERT(isRemoved());
104
105     m_private->setClient(0);
106 }
107
108 PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionCode& ec) const
109 {
110     // Section 3.1 buffered attribute steps.
111     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
112     //    INVALID_STATE_ERR exception and abort these steps.
113     if (isRemoved()) {
114         ec = INVALID_STATE_ERR;
115         return 0;
116     }
117
118     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
119     return m_buffered->copy();
120 }
121
122 const RefPtr<TimeRanges>& SourceBuffer::buffered() const
123 {
124     return m_buffered;
125 }
126
127 double SourceBuffer::timestampOffset() const
128 {
129     return m_timestampOffset.toDouble();
130 }
131
132 void SourceBuffer::setTimestampOffset(double offset, ExceptionCode& ec)
133 {
134     // Section 3.1 timestampOffset attribute setter steps.
135     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
136     //    INVALID_STATE_ERR exception and abort these steps.
137     if (isRemoved()) {
138         ec = INVALID_STATE_ERR;
139         return;
140     }
141
142     // 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
143     if (m_updating) {
144         ec = INVALID_STATE_ERR;
145         return;
146     }
147
148     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
149     // 4.1 Set the readyState attribute of the parent media source to "open"
150     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
151     m_source->openIfInEndedState();
152
153     // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
154     if (m_appendState == ParsingMediaSegment) {
155         ec = INVALID_STATE_ERR;
156         return;
157     }
158
159     // 6. Update the attribute to the new value.
160     m_timestampOffset = offset;
161 }
162
163 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionCode& ec)
164 {
165     // Section 3.2 appendBuffer()
166     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
167     // 1. If data is null then throw an INVALID_ACCESS_ERR exception and abort these steps.
168     if (!data) {
169         ec = INVALID_ACCESS_ERR;
170         return;
171     }
172
173     appendBufferInternal(static_cast<unsigned char*>(data->data()), data->byteLength(), ec);
174 }
175
176 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionCode& ec)
177 {
178     // Section 3.2 appendBuffer()
179     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
180     // 1. If data is null then throw an INVALID_ACCESS_ERR exception and abort these steps.
181     if (!data) {
182         ec = INVALID_ACCESS_ERR;
183         return;
184     }
185
186     appendBufferInternal(static_cast<unsigned char*>(data->baseAddress()), data->byteLength(), ec);
187 }
188
189 void SourceBuffer::abort(ExceptionCode& ec)
190 {
191     // Section 3.2 abort() method steps.
192     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
193     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
194     //    then throw an INVALID_STATE_ERR exception and abort these steps.
195     // 2. If the readyState attribute of the parent media source is not in the "open" state
196     //    then throw an INVALID_STATE_ERR exception and abort these steps.
197     if (isRemoved() || !m_source->isOpen()) {
198         ec = INVALID_STATE_ERR;
199         return;
200     }
201
202     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
203     abortIfUpdating();
204
205     // 4. Run the reset parser state algorithm.
206     m_private->abort();
207
208     // FIXME(229408) Add steps 5-6 update appendWindowStart & appendWindowEnd.
209 }
210
211
212 void SourceBuffer::abortIfUpdating()
213 {
214     // Section 3.2 abort() method step 3 substeps.
215     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
216
217     if (!m_updating)
218         return;
219
220     // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
221     m_appendBufferTimer.stop();
222     m_pendingAppendData.clear();
223
224     // 3.2. Set the updating attribute to false.
225     m_updating = false;
226
227     // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
228     scheduleEvent(eventNames().abortEvent);
229
230     // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
231     scheduleEvent(eventNames().updateendEvent);
232 }
233
234 void SourceBuffer::removedFromMediaSource()
235 {
236     if (isRemoved())
237         return;
238
239     m_private->removedFromMediaSource();
240     m_source = 0;
241     m_asyncEventQueue.close();
242 }
243
244 bool SourceBuffer::hasPendingActivity() const
245 {
246     return m_source;
247 }
248
249 void SourceBuffer::stop()
250 {
251     m_appendBufferTimer.stop();
252 }
253
254 bool SourceBuffer::isRemoved() const
255 {
256     return !m_source;
257 }
258
259 void SourceBuffer::scheduleEvent(const AtomicString& eventName)
260 {
261     RefPtr<Event> event = Event::create(eventName, false, false);
262     event->setTarget(this);
263
264     m_asyncEventQueue.enqueueEvent(event.release());
265 }
266
267 void SourceBuffer::appendBufferInternal(unsigned char* data, unsigned size, ExceptionCode& ec)
268 {
269     // Section 3.2 appendBuffer()
270     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
271
272     // Step 1 is enforced by the caller.
273     // 2. 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.
274     // 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
275     if (isRemoved() || m_updating) {
276         ec = INVALID_STATE_ERR;
277         return;
278     }
279
280     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
281     m_source->openIfInEndedState();
282
283     // Steps 5-6
284
285     // 7. Add data to the end of the input buffer.
286     m_pendingAppendData.append(data, size);
287
288     // 8. Set the updating attribute to true.
289     m_updating = true;
290
291     // 9. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
292     scheduleEvent(eventNames().updatestartEvent);
293
294     // 10. Asynchronously run the buffer append algorithm.
295     m_appendBufferTimer.startOneShot(0);
296 }
297
298 void SourceBuffer::appendBufferTimerFired(Timer<SourceBuffer>*)
299 {
300     ASSERT(m_updating);
301
302     // Section 3.5.4 Buffer Append Algorithm
303     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
304
305     // 1. Run the segment parser loop algorithm.
306     // Step 2 doesn't apply since we run Step 1 synchronously here.
307     size_t appendSize = m_pendingAppendData.size();
308     if (!appendSize) {
309         // Resize buffer for 0 byte appends so we always have a valid pointer.
310         // We need to convey all appends, even 0 byte ones to |m_private| so
311         // that it can clear its end of stream state if necessary.
312         m_pendingAppendData.resize(1);
313     }
314     m_private->append(m_pendingAppendData.data(), appendSize);
315
316     // 3. Set the updating attribute to false.
317     m_updating = false;
318     m_pendingAppendData.clear();
319
320     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
321     scheduleEvent(eventNames().updateEvent);
322
323     // 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
324     scheduleEvent(eventNames().updateendEvent);
325
326     m_source->monitorSourceBuffers();
327 }
328
329 const AtomicString& SourceBuffer::decodeError()
330 {
331     static NeverDestroyed<AtomicString> decode("decode", AtomicString::ConstructFromLiteral);
332     return decode;
333 }
334
335 const AtomicString& SourceBuffer::networkError()
336 {
337     static NeverDestroyed<AtomicString> network("network", AtomicString::ConstructFromLiteral);
338     return network;
339 }
340
341 VideoTrackList* SourceBuffer::videoTracks()
342 {
343     if (!m_source->mediaElement())
344         return 0;
345
346     if (!m_videoTracks)
347         m_videoTracks = VideoTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
348
349     return m_videoTracks.get();
350 }
351
352 AudioTrackList* SourceBuffer::audioTracks()
353 {
354     if (!m_source->mediaElement())
355         return 0;
356
357     if (!m_audioTracks)
358         m_audioTracks = AudioTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
359
360     return m_audioTracks.get();
361 }
362
363 TextTrackList* SourceBuffer::textTracks()
364 {
365     if (!m_source->mediaElement())
366         return 0;
367
368     if (!m_textTracks)
369         m_textTracks = TextTrackList::create(m_source->mediaElement(), ActiveDOMObject::scriptExecutionContext());
370
371     return m_textTracks.get();
372 }
373
374 void SourceBuffer::setActive(bool active)
375 {
376     if (m_active == active)
377         return;
378
379     m_active = active;
380     m_source->sourceBufferDidChangeAcitveState(this, active);
381 }
382
383 void SourceBuffer::sourceBufferPrivateDidEndStream(SourceBufferPrivate*, const WTF::AtomicString& error)
384 {
385     m_source->endOfStream(error, IgnorableExceptionCode());
386 }
387
388 void SourceBuffer::sourceBufferPrivateDidReceiveInitializationSegment(SourceBufferPrivate*, const InitializationSegment& segment)
389 {
390     m_appendState = ParsingInitSegment;
391
392     // 3.5.7 Initialization Segment Received
393     // 1. Update the duration attribute if it currently equals NaN:
394     if (std::isnan(m_source->duration()) && segment.duration.isValid())
395         m_source->setDuration(segment.duration.toDouble(), IGNORE_EXCEPTION);
396
397     // 2. If the initialization segment has no audio, video, or text tracks, then run the end of stream
398     // algorithm with the error parameter set to "decode" and abort these steps.
399     if (!segment.audioTracks.size() && !segment.videoTracks.size() && !segment.textTracks.size())
400         m_source->endOfStream(decodeError(), IgnorableExceptionCode());
401
402
403     // 3. If the first initialization segment flag is true, then run the following steps:
404     if (m_receivedFirstInitializationSegment) {
405         if (!validateInitializationSegment(segment)) {
406             m_source->endOfStream(decodeError(), IgnorableExceptionCode());
407             return;
408         }
409         // 3.2 Add the appropriate track descriptions from this initialization segment to each of the track buffers.
410         // NOTE: No changes to make
411     }
412
413     // 4. Let active track flag equal false.
414     bool activeTrackFlag = false;
415
416     // 5. If the first initialization segment flag is false, then run the following steps:
417     if (!m_receivedFirstInitializationSegment) {
418         // 5.1 If the initialization segment contains tracks with codecs the user agent does not support,
419         // then run the end of stream algorithm with the error parameter set to "decode" and abort these steps.
420         // NOTE: This check is the responsibility of the SourceBufferPrivate.
421
422         // 5.2 For each audio track in the initialization segment, run following steps:
423         for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
424             AudioTrackPrivate* audioTrackPrivate = it->track.get();
425
426             // 5.2.1 Let new audio track be a new AudioTrack object.
427             // 5.2.2 Generate a unique ID and assign it to the id property on new video track.
428             RefPtr<AudioTrack> newAudioTrack = AudioTrack::create(this, audioTrackPrivate);
429             newAudioTrack->setSourceBuffer(this);
430
431             // 5.2.3 If audioTracks.length equals 0, then run the following steps:
432             if (!audioTracks()->length()) {
433                 // 5.2.3.1 Set the enabled property on new audio track to true.
434                 newAudioTrack->setEnabled(true);
435
436                 // 5.2.3.2 Set active track flag to true.
437                 activeTrackFlag = true;
438             }
439
440             // 5.2.4 Add new audio track to the audioTracks attribute on this SourceBuffer object.
441             // 5.2.5 Queue a task to fire a trusted event named addtrack, that does not bubble and is
442             // not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object
443             // referenced by the audioTracks attribute on this SourceBuffer object.
444             audioTracks()->append(newAudioTrack);
445
446             // 5.2.6 Add new audio track to the audioTracks attribute on the HTMLMediaElement.
447             // 5.2.7 Queue a task to fire a trusted event named addtrack, that does not bubble and is
448             // not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object
449             // referenced by the audioTracks attribute on the HTMLMediaElement.
450             m_source->mediaElement()->audioTracks()->append(newAudioTrack);
451
452             // 5.2.8 Create a new track buffer to store coded frames for this track.
453             ASSERT(!m_trackBufferMap.contains(newAudioTrack->id()));
454             TrackBuffer& trackBuffer = m_trackBufferMap.add(newAudioTrack->id(), TrackBuffer()).iterator->value;
455
456             // 5.2.9 Add the track description for this track to the track buffer.
457             trackBuffer.description = it->description;
458         }
459
460         // 5.3 For each video track in the initialization segment, run following steps:
461         for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
462             VideoTrackPrivate* videoTrackPrivate = it->track.get();
463
464             // 5.3.1 Let new video track be a new VideoTrack object.
465             // 5.3.2 Generate a unique ID and assign it to the id property on new video track.
466             RefPtr<VideoTrack> newVideoTrack = VideoTrack::create(this, videoTrackPrivate);
467             newVideoTrack->setSourceBuffer(this);
468
469             // 5.3.3 If videoTracks.length equals 0, then run the following steps:
470             if (!videoTracks()->length()) {
471                 // 5.3.3.1 Set the selected property on new video track to true.
472                 newVideoTrack->setSelected(true);
473
474                 // 5.3.3.2 Set active track flag to true.
475                 activeTrackFlag = true;
476             }
477
478             // 5.3.4 Add new video track to the videoTracks attribute on this SourceBuffer object.
479             // 5.3.5 Queue a task to fire a trusted event named addtrack, that does not bubble and is
480             // not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object
481             // referenced by the videoTracks attribute on this SourceBuffer object.
482             videoTracks()->append(newVideoTrack);
483
484             // 5.3.6 Add new video track to the videoTracks attribute on the HTMLMediaElement.
485             // 5.3.7 Queue a task to fire a trusted event named addtrack, that does not bubble and is
486             // not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object
487             // referenced by the videoTracks attribute on the HTMLMediaElement.
488             m_source->mediaElement()->videoTracks()->append(newVideoTrack);
489
490             // 5.3.8 Create a new track buffer to store coded frames for this track.
491             ASSERT(!m_trackBufferMap.contains(newVideoTrack->id()));
492             TrackBuffer& trackBuffer = m_trackBufferMap.add(newVideoTrack->id(), TrackBuffer()).iterator->value;
493
494             // 5.3.9 Add the track description for this track to the track buffer.
495             trackBuffer.description = it->description;
496         }
497
498         // 5.4 For each text track in the initialization segment, run following steps:
499         for (auto it = segment.textTracks.begin(); it != segment.textTracks.end(); ++it) {
500             InbandTextTrackPrivate* textTrackPrivate = it->track.get();
501
502             // 5.4.1 Let new text track be a new TextTrack object with its properties populated with the
503             // appropriate information from the initialization segment.
504             RefPtr<InbandTextTrack> newTextTrack = InbandTextTrack::create(scriptExecutionContext(), this, textTrackPrivate);
505
506             // 5.4.2 If the mode property on new text track equals "showing" or "hidden", then set active
507             // track flag to true.
508             if (textTrackPrivate->mode() != InbandTextTrackPrivate::Disabled)
509                 activeTrackFlag = true;
510
511             // 5.4.3 Add new text track to the textTracks attribute on this SourceBuffer object.
512             // 5.4.4 Queue a task to fire a trusted event named addtrack, that does not bubble and is
513             // not cancelable, and that uses the TrackEvent interface, at textTracks attribute on this
514             // SourceBuffer object.
515             textTracks()->append(newTextTrack);
516
517             // 5.4.5 Add new text track to the textTracks attribute on the HTMLMediaElement.
518             // 5.4.6 Queue a task to fire a trusted event named addtrack, that does not bubble and is
519             // not cancelable, and that uses the TrackEvent interface, at the TextTrackList object
520             // referenced by the textTracks attribute on the HTMLMediaElement.
521             m_source->mediaElement()->textTracks()->append(newTextTrack);
522
523             // 5.4.7 Create a new track buffer to store coded frames for this track.
524             ASSERT(!m_trackBufferMap.contains(textTrackPrivate->id()));
525             TrackBuffer& trackBuffer = m_trackBufferMap.add(textTrackPrivate->id(), TrackBuffer()).iterator->value;
526
527             // 5.4.8 Add the track description for this track to the track buffer.
528             trackBuffer.description = it->description;
529         }
530
531         // 5.5 If active track flag equals true, then run the following steps:
532         if (activeTrackFlag) {
533             // 5.5.1 Add this SourceBuffer to activeSourceBuffers.
534             setActive(true);
535         }
536
537         // 5.6 Set first initialization segment flag to true.
538         m_receivedFirstInitializationSegment = true;
539     }
540
541     // 6. If the HTMLMediaElement.readyState attribute is HAVE_NOTHING, then run the following steps:
542     if (m_private->readyState() == MediaPlayer::HaveNothing) {
543         // 6.1 If one or more objects in sourceBuffers have first initialization segment flag set to false, then abort these steps.
544         for (unsigned long i = 0; i < m_source->sourceBuffers()->length(); ++i) {
545             if (!m_source->sourceBuffers()->item(i)->m_receivedFirstInitializationSegment)
546                 return;
547         }
548
549         // 6.2 Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
550         // 6.3 Queue a task to fire a simple event named loadedmetadata at the media element.
551         m_private->setReadyState(MediaPlayer::HaveMetadata);
552     }
553
554     // 7. If the active track flag equals true and the HTMLMediaElement.readyState
555     // attribute is greater than HAVE_CURRENT_DATA, then set the HTMLMediaElement.readyState
556     // attribute to HAVE_METADATA.
557     if (activeTrackFlag && m_private->readyState() > MediaPlayer::HaveCurrentData)
558         m_private->setReadyState(MediaPlayer::HaveMetadata);
559 }
560
561 bool SourceBuffer::validateInitializationSegment(const InitializationSegment& segment)
562 {
563     // 3.1. Verify the following properties. If any of the checks fail then run the end of stream
564     // algorithm with the error parameter set to "decode" and abort these steps.
565     //   * The number of audio, video, and text tracks match what was in the first initialization segment.
566     if (segment.audioTracks.size() != audioTracks()->length()
567         || segment.videoTracks.size() != videoTracks()->length()
568         || segment.textTracks.size() != textTracks()->length())
569         return false;
570
571     //   * The codecs for each track, match what was specified in the first initialization segment.
572     for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
573         if (!m_videoCodecs.contains(it->description->codec()))
574             return false;
575     }
576
577     for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
578         if (!m_audioCodecs.contains(it->description->codec()))
579             return false;
580     }
581
582     for (auto it = segment.textTracks.begin(); it != segment.textTracks.end(); ++it) {
583         if (!m_textCodecs.contains(it->description->codec()))
584             return false;
585     }
586
587     //   * If more than one track for a single type are present (ie 2 audio tracks), then the Track
588     //   IDs match the ones in the first initialization segment.
589     if (segment.audioTracks.size() >= 2) {
590         for (auto it = segment.audioTracks.begin(); it != segment.audioTracks.end(); ++it) {
591             if (!m_trackBufferMap.contains(it->track->id()))
592                 return false;
593         }
594     }
595
596     if (segment.videoTracks.size() >= 2) {
597         for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
598             if (!m_trackBufferMap.contains(it->track->id()))
599                 return false;
600         }
601     }
602
603     if (segment.textTracks.size() >= 2) {
604         for (auto it = segment.videoTracks.begin(); it != segment.videoTracks.end(); ++it) {
605             if (!m_trackBufferMap.contains(it->track->id()))
606                 return false;
607         }
608     }
609
610     return true;
611 }
612
613 class SampleLessThanComparator {
614 public:
615     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value1, std::pair<MediaTime, RefPtr<MediaSample>> value2)
616     {
617         return value1.first < value2.first;
618     }
619
620     bool operator()(MediaTime value1, std::pair<MediaTime, RefPtr<MediaSample>> value2)
621     {
622         return value1 < value2.first;
623     }
624
625     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value1, MediaTime value2)
626     {
627         return value1.first < value2;
628     }
629 };
630
631 class SampleContainsMediaTimeComparator {
632 public:
633     bool contains(MediaSample* sample, MediaTime time)
634     {
635         MediaTime presentationStartTime = sample->presentationTime();
636         MediaTime presentationEndTime = presentationStartTime + sample->duration();
637         return presentationStartTime <= time && presentationEndTime > time;
638     }
639     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value, MediaTime time)
640     {
641         MediaTime presentationStartTime = value.second->presentationTime();
642         MediaTime presentationEndTime = presentationStartTime + value.second->duration();
643         return presentationStartTime < time && presentationEndTime <= time;
644     }
645 };
646
647 class SamplePresentationTimeIsWithinRangeComparator {
648 public:
649     bool operator()(std::pair<MediaTime, MediaTime> range, std::pair<MediaTime, RefPtr<MediaSample>> value)
650     {
651         return range.second < value.first;
652     }
653     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value, std::pair<MediaTime, MediaTime> range)
654     {
655         return value.first < range.first;
656     }
657 };
658
659 class SampleIsRandomAccessPredicate {
660 public:
661     bool operator()(std::pair<MediaTime, RefPtr<MediaSample>> value)
662     {
663         return value.second->flags() == MediaSample::IsSync;
664     }
665 };
666
667 void SourceBuffer::sourceBufferPrivateDidReceiveSample(SourceBufferPrivate*, PassRefPtr<MediaSample> sample)
668 {
669     // 3.5.8 Coded Frame Processing
670     // When complete coded frames have been parsed by the segment parser loop then the following steps
671     // are run:
672     // 1. For each coded frame in the media segment run the following steps:
673     // 1.1. Loop Top
674     do {
675         // 1.1 (ctd) Let presentation timestamp be a double precision floating point representation of
676         // the coded frame's presentation timestamp in seconds.
677         MediaTime presentationTimestamp = sample->presentationTime();
678
679         // 1.2 Let decode timestamp be a double precision floating point representation of the coded frame's
680         // decode timestamp in seconds.
681         MediaTime decodeTimestamp = sample->decodeTime();
682
683         // 1.3 Let frame duration be a double precision floating point representation of the coded frame's
684         // duration in seconds.
685         MediaTime frameDuration = sample->duration();
686
687         // 1.4 If mode equals "sequence" and group start timestamp is set, then run the following steps:
688         // FIXME: add support for "sequence" mode
689
690         // 1.5 If timestampOffset is not 0, then run the following steps:
691         if (m_timestampOffset != MediaTime::zeroTime()) {
692             // 1.5.1 Add timestampOffset to the presentation timestamp.
693             presentationTimestamp += m_timestampOffset;
694
695             // 1.5.2 Add timestampOffset to the decode timestamp.
696             decodeTimestamp += m_timestampOffset;
697
698             // 1.5.3 If the presentation timestamp or decode timestamp is less than the presentation start
699             // time, then run the end of stream algorithm with the error parameter set to "decode", and
700             // abort these steps.
701             MediaTime presentationStartTime = MediaTime::zeroTime();
702             if (presentationTimestamp < presentationStartTime || decodeTimestamp < presentationStartTime) {
703                 m_source->endOfStream(decodeError(), IgnorableExceptionCode());
704                 return;
705             }
706         }
707
708         // 1.6 Let track buffer equal the track buffer that the coded frame will be added to.
709         AtomicString trackID = sample->trackID();
710         auto it = m_trackBufferMap.find(trackID);
711         if (it == m_trackBufferMap.end())
712             it = m_trackBufferMap.add(trackID, TrackBuffer()).iterator;
713         TrackBuffer& trackBuffer = it->value;
714
715         // 1.7 If last decode timestamp for track buffer is set and decode timestamp is less than last
716         // decode timestamp:
717         // OR
718         // If last decode timestamp for track buffer is set and the difference between decode timestamp and
719         // last decode timestamp is greater than 2 times last frame duration:
720         if (trackBuffer.lastDecodeTimestamp.isValid() && (decodeTimestamp < trackBuffer.lastDecodeTimestamp
721             || abs(decodeTimestamp - trackBuffer.lastDecodeTimestamp) > (trackBuffer.lastFrameDuration * 2))) {
722             // 1.7.1 If mode equals "segments":
723             // Set highest presentation end timestamp to presentation timestamp.
724             m_highestPresentationEndTimestamp = presentationTimestamp;
725
726             // If mode equals "sequence":
727             // Set group start timestamp equal to the highest presentation end timestamp.
728             // FIXME: Add support for "sequence" mode.
729
730             for (auto i = m_trackBufferMap.values().begin(); i != m_trackBufferMap.values().end(); ++i) {
731                 // 1.7.2 Unset the last decode timestamp on all track buffers.
732                 i->lastDecodeTimestamp = MediaTime::invalidTime();
733                 // 1.7.3 Unset the last frame duration on all track buffers.
734                 i->lastFrameDuration = MediaTime::invalidTime();
735                 // 1.7.4 Unset the highest presentation timestamp on all track buffers.
736                 i->highestPresentationTimestamp = MediaTime::invalidTime();
737                 // 1.7.5 Set the need random access point flag on all track buffers to true.
738                 i->needRandomAccessFlag = true;
739             }
740
741             // 1.7.6 Jump to the Loop Top step above to restart processing of the current coded frame.
742             continue;
743         }
744
745         // 1.8 Let frame end timestamp equal the sum of presentation timestamp and frame duration.
746         MediaTime frameEndTimestamp = presentationTimestamp + frameDuration;
747
748         // 1.9 If presentation timestamp is less than appendWindowStart, then set the need random access
749         // point flag to true, drop the coded frame, and jump to the top of the loop to start processing
750         // the next coded frame.
751         // 1.10 If frame end timestamp is greater than appendWindowEnd, then set the need random access
752         // point flag to true, drop the coded frame, and jump to the top of the loop to start processing
753         // the next coded frame.
754         // FIXME: implement append windows
755
756         // 1.11 If the need random access point flag on track buffer equals true, then run the following steps:
757         // 1.11.1 If the coded frame is not a random access point, then drop the coded frame and jump
758         // to the top of the loop to start processing the next coded frame.
759         // 1.11.2 Set the need random access point flag on track buffer to false.
760         // NOTE: MockSampleBoxes are not decodable.
761
762         // 1.12 Let spliced audio frame be an unset variable for holding audio splice information
763         // 1.13 Let spliced timed text frame be an unset variable for holding timed text splice information
764         // FIXME: Add support for sample splicing.
765
766         TrackBuffer::SampleMap erasedSamples;
767         MediaTime microsecond(1, 1000000);
768
769         // 1.14 If last decode timestamp for track buffer is unset and there is a coded frame in
770         // track buffer with a presentation timestamp less than or equal to presentation timestamp
771         // and presentation timestamp is less than this coded frame's presentation timestamp plus
772         // its frame duration, then run the following steps:
773         if (trackBuffer.lastDecodeTimestamp.isInvalid()) {
774             SampleContainsMediaTimeComparator comparator;
775             auto iter = std::lower_bound(trackBuffer.samples.begin(), trackBuffer.samples.end(), presentationTimestamp, comparator);
776             if (iter != trackBuffer.samples.end() && comparator.contains(iter->second.get(), presentationTimestamp)) {
777                 // 1.14.1 Let overlapped frame be the coded frame in track buffer that matches the condition above.
778                 RefPtr<MediaSample> overlappedFrame = iter->second;
779
780                 // 1.14.2 If track buffer contains audio coded frames:
781                 // Run the audio splice frame algorithm and if a splice frame is returned, assign it to
782                 // spliced audio frame.
783                 // FIXME: Add support for sample splicing.
784
785                 // If track buffer contains video coded frames:
786                 if (trackBuffer.description->isVideo()) {
787                     // 1.14.2.1 Let overlapped frame presentation timestamp equal the presentation timestamp
788                     // of overlapped frame.
789                     MediaTime overlappedFramePresentationTimestamp = overlappedFrame->presentationTime();
790
791                     // 1.14.2.2 Let remove window timestamp equal overlapped frame presentation timestamp
792                     // plus 1 microsecond.
793                     MediaTime removeWindowTimestamp = overlappedFramePresentationTimestamp + microsecond;
794
795                     // 1.14.2.3 If the presentation timestamp is less than the remove window timestamp,
796                     // then remove overlapped frame and any coded frames that depend on it from track buffer.
797                     if (presentationTimestamp < removeWindowTimestamp) {
798                         // NOTE: MockSampleBoxes don't have dependencies
799                         erasedSamples.insert(*iter);
800                         trackBuffer.samples.erase(iter);
801                     }
802                 }
803
804                 // If track buffer contains timed text coded frames:
805                 // Run the text splice frame algorithm and if a splice frame is returned, assign it to spliced timed text frame.
806                 // FIXME: Add support for sample splicing.
807             }
808         }
809
810         // 1.15 Remove existing coded frames in track buffer:
811         // If highest presentation timestamp for track buffer is not set:
812         if (trackBuffer.highestPresentationTimestamp.isInvalid()) {
813             // Remove all coded frames from track buffer that have a presentation timestamp greater than or
814             // equal to presentation timestamp and less than frame end timestamp.
815             SamplePresentationTimeIsWithinRangeComparator comparator;
816             std::pair<MediaTime, MediaTime> range(presentationTimestamp, frameEndTimestamp);
817             auto iter_pair = std::equal_range(trackBuffer.samples.begin(), trackBuffer.samples.end(), range, comparator);
818             if (iter_pair.first != trackBuffer.samples.end()) {
819                 erasedSamples.insert(iter_pair.first, iter_pair.second);
820                 trackBuffer.samples.erase(iter_pair.first, iter_pair.second);
821             }
822         }
823
824         // If highest presentation timestamp for track buffer is set and less than presentation timestamp
825         if (trackBuffer.highestPresentationTimestamp.isValid() && trackBuffer.highestPresentationTimestamp < presentationTimestamp) {
826             // Remove all coded frames from track buffer that have a presentation timestamp greater than highest
827             // presentation timestamp and less than or equal to frame end timestamp.
828             SamplePresentationTimeIsWithinRangeComparator comparator;
829             std::pair<MediaTime, MediaTime> range(trackBuffer.highestPresentationTimestamp, frameEndTimestamp);
830             auto iter_pair = std::equal_range(trackBuffer.samples.begin(), trackBuffer.samples.end(), range, comparator);
831             if (iter_pair.first != trackBuffer.samples.end()) {
832                 erasedSamples.insert(iter_pair.first, iter_pair.second);
833                 trackBuffer.samples.erase(iter_pair.first, iter_pair.second);
834             }
835         }
836
837         // 1.16 Remove decoding dependencies of the coded frames removed in the previous step:
838         if (!erasedSamples.empty()) {
839             // If detailed information about decoding dependencies is available:
840             // FIXME: Add support for detailed dependency information
841
842             // Otherwise: Remove all coded frames between the coded frames removed in the previous step
843             // and the next random access point after those removed frames.
844             auto first_iter = std::upper_bound(trackBuffer.samples.begin(), trackBuffer.samples.end(), *erasedSamples.begin(), SampleLessThanComparator());
845             auto second_iter = std::find_if(first_iter, trackBuffer.samples.end(), SampleIsRandomAccessPredicate());
846             if (first_iter != trackBuffer.samples.end()) {
847                 trackBuffer.samples.erase(first_iter, second_iter);
848                 erasedSamples.insert(first_iter, second_iter);
849             }
850         }
851
852         // 1.17 If spliced audio frame is set:
853         // Add spliced audio frame to the track buffer.
854         // If spliced timed text frame is set:
855         // Add spliced timed text frame to the track buffer.
856         // FIXME: Add support for sample splicing.
857
858         // Otherwise:
859         // Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
860         trackBuffer.samples.insert(TrackBuffer::SampleMap::value_type(presentationTimestamp, sample));
861         trackBuffer.decodeQueue.insert(TrackBuffer::SampleMap::value_type(decodeTimestamp, sample));
862
863         // 1.18 Set last decode timestamp for track buffer to decode timestamp.
864         trackBuffer.lastDecodeTimestamp = decodeTimestamp;
865
866         // 1.19 Set last frame duration for track buffer to frame duration.
867         trackBuffer.lastFrameDuration = frameDuration;
868
869         // 1.20 If highest presentation timestamp for track buffer is unset or frame end timestamp is greater
870         // than highest presentation timestamp, then set highest presentation timestamp for track buffer
871         // to frame end timestamp.
872         if (trackBuffer.highestPresentationTimestamp.isInvalid() || frameEndTimestamp > trackBuffer.highestPresentationTimestamp)
873             trackBuffer.highestPresentationTimestamp = frameEndTimestamp;
874
875         if (erasedSamples.size()) {
876             RefPtr<TimeRanges> erasedRanges = TimeRanges::create();
877             for (auto iter = erasedSamples.begin(); iter != erasedSamples.end(); ++iter) {
878                 double start = iter->first.toDouble();
879                 double end = ((iter->first + iter->second->duration()) + microsecond).toDouble();
880                 erasedRanges->add(start, end);
881             }
882             erasedRanges->invert();
883             m_buffered->intersectWith(erasedRanges.get());
884         }
885
886         m_buffered->add(presentationTimestamp.toDouble(), (presentationTimestamp + frameDuration + microsecond).toDouble());
887
888         break;
889     } while (1);
890 }
891
892 bool SourceBuffer::sourceBufferPrivateHasAudio(const SourceBufferPrivate*) const
893 {
894     return m_audioTracks->length();
895 }
896
897 bool SourceBuffer::sourceBufferPrivateHasVideo(const SourceBufferPrivate*) const
898 {
899     return m_videoTracks->length();
900 }
901
902 void SourceBuffer::videoTrackSelectedChanged(VideoTrack* track)
903 {
904     // 2.4.5 Changes to selected/enabled track state
905     // If the selected video track changes, then run the following steps:
906     // 1. If the SourceBuffer associated with the previously selected video track is not associated with
907     // any other enabled tracks, run the following steps:
908     if (track->selected()
909         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
910         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
911         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
912         // 1.1 Remove the SourceBuffer from activeSourceBuffers.
913         // 1.2 Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
914         setActive(false);
915     } else if (!track->selected()) {
916         // 2. If the SourceBuffer associated with the newly selected video track is not already in activeSourceBuffers,
917         // run the following steps:
918         // 2.1 Add the SourceBuffer to activeSourceBuffers.
919         // 2.2 Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
920         setActive(true);
921     }
922
923     if (!isRemoved())
924         m_source->mediaElement()->videoTrackSelectedChanged(track);
925 }
926
927 void SourceBuffer::audioTrackEnabledChanged(AudioTrack* track)
928 {
929     // 2.4.5 Changes to selected/enabled track state
930     // If an audio track becomes disabled and the SourceBuffer associated with this track is not
931     // associated with any other enabled or selected track, then run the following steps:
932     if (track->enabled()
933         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
934         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
935         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
936         // 1. Remove the SourceBuffer associated with the audio track from activeSourceBuffers
937         // 2. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
938         setActive(false);
939     } else if (!track->enabled()) {
940         // If an audio track becomes enabled and the SourceBuffer associated with this track is
941         // not already in activeSourceBuffers, then run the following steps:
942         // 1. Add the SourceBuffer associated with the audio track to activeSourceBuffers
943         // 2. Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
944         setActive(true);
945     }
946
947     if (!isRemoved())
948         m_source->mediaElement()->audioTrackEnabledChanged(track);
949 }
950
951 void SourceBuffer::textTrackModeChanged(TextTrack* track)
952 {
953     // 2.4.5 Changes to selected/enabled track state
954     // If a text track mode becomes "disabled" and the SourceBuffer associated with this track is not
955     // associated with any other enabled or selected track, then run the following steps:
956     if (track->mode() == TextTrack::disabledKeyword()
957         && (!m_videoTracks || !m_videoTracks->isAnyTrackEnabled())
958         && (!m_audioTracks || !m_audioTracks->isAnyTrackEnabled())
959         && (!m_textTracks || !m_textTracks->isAnyTrackEnabled())) {
960         // 1. Remove the SourceBuffer associated with the audio track from activeSourceBuffers
961         // 2. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers
962         setActive(false);
963     } else {
964         // If a text track mode becomes "showing" or "hidden" and the SourceBuffer associated with this
965         // track is not already in activeSourceBuffers, then run the following steps:
966         // 1. Add the SourceBuffer associated with the text track to activeSourceBuffers
967         // 2. Queue a task to fire a simple event named addsourcebuffer at activeSourceBuffers
968         setActive(true);
969     }
970
971     if (!isRemoved())
972         m_source->mediaElement()->textTrackModeChanged(track);
973 }
974
975 void SourceBuffer::textTrackAddCue(TextTrack* track, WTF::PassRefPtr<TextTrackCue> cue)
976 {
977     if (!isRemoved())
978         m_source->mediaElement()->textTrackAddCue(track, cue);
979 }
980
981 void SourceBuffer::textTrackAddCues(TextTrack* track, TextTrackCueList const* cueList)
982 {
983     if (!isRemoved())
984         m_source->mediaElement()->textTrackAddCues(track, cueList);
985 }
986
987 void SourceBuffer::textTrackRemoveCue(TextTrack* track, WTF::PassRefPtr<TextTrackCue> cue)
988 {
989     if (!isRemoved())
990         m_source->mediaElement()->textTrackRemoveCue(track, cue);
991 }
992
993 void SourceBuffer::textTrackRemoveCues(TextTrack* track, TextTrackCueList const* cueList)
994 {
995     if (!isRemoved())
996         m_source->mediaElement()->textTrackRemoveCues(track, cueList);
997 }
998
999 void SourceBuffer::textTrackKindChanged(TextTrack* track)
1000 {
1001     if (!isRemoved())
1002         m_source->mediaElement()->textTrackKindChanged(track);
1003 }
1004
1005 } // namespace WebCore
1006
1007 #endif