[GStreamer] Fix silly bug in GStreamerVideoFrameLibWebRTC.
[WebKit-https.git] / Source / WebCore / Modules / mediasource / MediaSource.cpp
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "MediaSource.h"
34
35 #if ENABLE(MEDIA_SOURCE)
36
37 #include "AudioTrackList.h"
38 #include "ContentType.h"
39 #include "Event.h"
40 #include "EventNames.h"
41 #include "HTMLMediaElement.h"
42 #include "Logging.h"
43 #include "MediaSourcePrivate.h"
44 #include "MediaSourceRegistry.h"
45 #include "SourceBuffer.h"
46 #include "SourceBufferList.h"
47 #include "SourceBufferPrivate.h"
48 #include "TextTrackList.h"
49 #include "TimeRanges.h"
50 #include "VideoTrackList.h"
51 #include <wtf/IsoMallocInlines.h>
52
53 namespace WebCore {
54
55 WTF_MAKE_ISO_ALLOCATED_IMPL(MediaSource);
56
57 String convertEnumerationToString(MediaSourcePrivate::AddStatus enumerationValue)
58 {
59     static const NeverDestroyed<String> values[] = {
60         MAKE_STATIC_STRING_IMPL("Ok"),
61         MAKE_STATIC_STRING_IMPL("NotSupported"),
62         MAKE_STATIC_STRING_IMPL("ReachedIdLimit"),
63     };
64     static_assert(static_cast<size_t>(MediaSourcePrivate::AddStatus::Ok) == 0, "MediaSourcePrivate::AddStatus::Ok is not 0 as expected");
65     static_assert(static_cast<size_t>(MediaSourcePrivate::AddStatus::NotSupported) == 1, "MediaSourcePrivate::AddStatus::NotSupported is not 1 as expected");
66     static_assert(static_cast<size_t>(MediaSourcePrivate::AddStatus::ReachedIdLimit) == 2, "MediaSourcePrivate::AddStatus::ReachedIdLimit is not 2 as expected");
67     ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));
68     return values[static_cast<size_t>(enumerationValue)];
69 }
70
71 String convertEnumerationToString(MediaSourcePrivate::EndOfStreamStatus enumerationValue)
72 {
73     static const NeverDestroyed<String> values[] = {
74         MAKE_STATIC_STRING_IMPL("EosNoError"),
75         MAKE_STATIC_STRING_IMPL("EosNetworkError"),
76         MAKE_STATIC_STRING_IMPL("EosDecodeError"),
77     };
78     static_assert(static_cast<size_t>(MediaSourcePrivate::EndOfStreamStatus::EosNoError) == 0, "MediaSourcePrivate::EndOfStreamStatus::EosNoError is not 0 as expected");
79     static_assert(static_cast<size_t>(MediaSourcePrivate::EndOfStreamStatus::EosNetworkError) == 1, "MediaSourcePrivate::EndOfStreamStatus::EosNetworkError is not 1 as expected");
80     static_assert(static_cast<size_t>(MediaSourcePrivate::EndOfStreamStatus::EosDecodeError) == 2, "MediaSourcePrivate::EndOfStreamStatus::EosDecodeError is not 2 as expected");
81     ASSERT(static_cast<size_t>(enumerationValue) < WTF_ARRAY_LENGTH(values));
82     return values[static_cast<size_t>(enumerationValue)];
83 }
84
85 URLRegistry* MediaSource::s_registry;
86
87 void MediaSource::setRegistry(URLRegistry* registry)
88 {
89     ASSERT(!s_registry);
90     s_registry = registry;
91 }
92
93 Ref<MediaSource> MediaSource::create(ScriptExecutionContext& context)
94 {
95     auto mediaSource = adoptRef(*new MediaSource(context));
96     mediaSource->suspendIfNeeded();
97     return mediaSource;
98 }
99
100 MediaSource::MediaSource(ScriptExecutionContext& context)
101     : ActiveDOMObject(&context)
102     , m_duration(MediaTime::invalidTime())
103     , m_pendingSeekTime(MediaTime::invalidTime())
104     , m_asyncEventQueue(MainThreadGenericEventQueue::create(*this))
105 #if !RELEASE_LOG_DISABLED
106     , m_logger(downcast<Document>(context).logger())
107 #endif
108 {
109     m_sourceBuffers = SourceBufferList::create(scriptExecutionContext());
110     m_activeSourceBuffers = SourceBufferList::create(scriptExecutionContext());
111 }
112
113 MediaSource::~MediaSource()
114 {
115     ALWAYS_LOG(LOGIDENTIFIER);
116     ASSERT(isClosed());
117 }
118
119 void MediaSource::setPrivateAndOpen(Ref<MediaSourcePrivate>&& mediaSourcePrivate)
120 {
121     DEBUG_LOG(LOGIDENTIFIER);
122     ASSERT(!m_private);
123     ASSERT(m_mediaElement);
124     m_private = WTFMove(mediaSourcePrivate);
125
126     // 2.4.1 Attaching to a media element
127     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#mediasource-attach
128
129     // ↳ If readyState is NOT set to "closed"
130     //    Run the "If the media data cannot be fetched at all, due to network errors, causing the user agent to give up trying
131     //    to fetch the resource" steps of the resource fetch algorithm's media data processing steps list.
132     if (!isClosed()) {
133         m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError);
134         return;
135     }
136
137     // ↳ Otherwise
138     // 1. Set the media element's delaying-the-load-event-flag to false.
139     m_mediaElement->setShouldDelayLoadEvent(false);
140
141     // 2. Set the readyState attribute to "open".
142     // 3. Queue a task to fire a simple event named sourceopen at the MediaSource.
143     setReadyState(ReadyState::Open);
144
145     // 4. Continue the resource fetch algorithm by running the remaining "Otherwise (mode is local)" steps,
146     // with these clarifications:
147     // NOTE: This is handled in HTMLMediaElement.
148 }
149
150 void MediaSource::addedToRegistry()
151 {
152     DEBUG_LOG(LOGIDENTIFIER);
153     setPendingActivity(*this);
154 }
155
156 void MediaSource::removedFromRegistry()
157 {
158     DEBUG_LOG(LOGIDENTIFIER);
159     unsetPendingActivity(*this);
160 }
161
162 MediaTime MediaSource::duration() const
163 {
164     return m_duration;
165 }
166
167 void MediaSource::durationChanged(const MediaTime& duration)
168 {
169     ALWAYS_LOG(LOGIDENTIFIER, duration);
170     m_duration = duration;
171 }
172
173 MediaTime MediaSource::currentTime() const
174 {
175     return m_mediaElement ? m_mediaElement->currentMediaTime() : MediaTime::zeroTime();
176 }
177
178 std::unique_ptr<PlatformTimeRanges> MediaSource::buffered() const
179 {
180     if (m_buffered && m_activeSourceBuffers->length() && std::all_of(m_activeSourceBuffers->begin(), m_activeSourceBuffers->end(), [](auto& buffer) { return !buffer->isBufferedDirty(); }))
181         return makeUnique<PlatformTimeRanges>(*m_buffered);
182
183     m_buffered = makeUnique<PlatformTimeRanges>();
184     for (auto& sourceBuffer : *m_activeSourceBuffers)
185         sourceBuffer->setBufferedDirty(false);
186
187     // Implements MediaSource algorithm for HTMLMediaElement.buffered.
188     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#htmlmediaelement-extensions
189     Vector<PlatformTimeRanges> activeRanges = this->activeRanges();
190
191     // 1. If activeSourceBuffers.length equals 0 then return an empty TimeRanges object and abort these steps.
192     if (activeRanges.isEmpty())
193         return makeUnique<PlatformTimeRanges>(*m_buffered);
194
195     // 2. Let active ranges be the ranges returned by buffered for each SourceBuffer object in activeSourceBuffers.
196     // 3. Let highest end time be the largest range end time in the active ranges.
197     MediaTime highestEndTime = MediaTime::zeroTime();
198     for (auto& ranges : activeRanges) {
199         unsigned length = ranges.length();
200         if (length)
201             highestEndTime = std::max(highestEndTime, ranges.end(length - 1));
202     }
203
204     // Return an empty range if all ranges are empty.
205     if (!highestEndTime)
206         return makeUnique<PlatformTimeRanges>(*m_buffered);
207
208     // 4. Let intersection ranges equal a TimeRange object containing a single range from 0 to highest end time.
209     m_buffered->add(MediaTime::zeroTime(), highestEndTime);
210
211     // 5. For each SourceBuffer object in activeSourceBuffers run the following steps:
212     bool ended = readyState() == ReadyState::Ended;
213     for (auto& sourceRanges : activeRanges) {
214         // 5.1 Let source ranges equal the ranges returned by the buffered attribute on the current SourceBuffer.
215         // 5.2 If readyState is "ended", then set the end time on the last range in source ranges to highest end time.
216         if (ended && sourceRanges.length())
217             sourceRanges.add(sourceRanges.start(sourceRanges.length() - 1), highestEndTime);
218
219         // 5.3 Let new intersection ranges equal the intersection between the intersection ranges and the source ranges.
220         // 5.4 Replace the ranges in intersection ranges with the new intersection ranges.
221         m_buffered->intersectWith(sourceRanges);
222     }
223
224     return makeUnique<PlatformTimeRanges>(*m_buffered);
225 }
226
227 void MediaSource::seekToTime(const MediaTime& time)
228 {
229     if (isClosed())
230         return;
231
232     ALWAYS_LOG(LOGIDENTIFIER, time);
233
234     // 2.4.3 Seeking
235     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#mediasource-seeking
236
237     m_pendingSeekTime = time;
238
239     // Run the following steps as part of the "Wait until the user agent has established whether or not the
240     // media data for the new playback position is available, and, if it is, until it has decoded enough data
241     // to play back that position" step of the seek algorithm:
242     // ↳ If new playback position is not in any TimeRange of HTMLMediaElement.buffered
243     if (!hasBufferedTime(time)) {
244         // 1. If the HTMLMediaElement.readyState attribute is greater than HAVE_METADATA,
245         // then set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
246         m_private->setReadyState(MediaPlayer::HaveMetadata);
247
248         // 2. The media element waits until an appendBuffer() or an appendStream() call causes the coded
249         // frame processing algorithm to set the HTMLMediaElement.readyState attribute to a value greater
250         // than HAVE_METADATA.
251         m_private->waitForSeekCompleted();
252         return;
253     }
254     // ↳ Otherwise
255     // Continue
256
257 // https://bugs.webkit.org/show_bug.cgi?id=125157 broke seek on MediaPlayerPrivateGStreamerMSE
258 #if !USE(GSTREAMER)
259     m_private->waitForSeekCompleted();
260 #endif
261     completeSeek();
262 }
263
264 void MediaSource::completeSeek()
265 {
266     if (isClosed())
267         return;
268
269     // 2.4.3 Seeking, ctd.
270     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#mediasource-seeking
271
272     ASSERT(m_pendingSeekTime.isValid());
273
274     ALWAYS_LOG(LOGIDENTIFIER, m_pendingSeekTime);
275
276     // 2. The media element resets all decoders and initializes each one with data from the appropriate
277     // initialization segment.
278     // 3. The media element feeds coded frames from the active track buffers into the decoders starting
279     // with the closest random access point before the new playback position.
280     MediaTime pendingSeekTime = m_pendingSeekTime;
281     m_pendingSeekTime = MediaTime::invalidTime();
282     for (auto& sourceBuffer : *m_activeSourceBuffers)
283         sourceBuffer->seekToTime(pendingSeekTime);
284
285     // 4. Resume the seek algorithm at the "Await a stable state" step.
286     m_private->seekCompleted();
287
288     monitorSourceBuffers();
289 }
290
291 Ref<TimeRanges> MediaSource::seekable()
292 {
293     // 6. HTMLMediaElement Extensions, seekable
294     // W3C Editor's Draft 16 September 2016
295     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#htmlmediaelement-extensions
296
297     // ↳ If duration equals NaN:
298     // Return an empty TimeRanges object.
299     if (m_duration.isInvalid())
300         return TimeRanges::create();
301
302     // ↳ If duration equals positive Infinity:
303     if (m_duration.isPositiveInfinite()) {
304         auto buffered = this->buffered();
305         // If live seekable range is not empty:
306         if (m_liveSeekable && m_liveSeekable->length()) {
307             // Let union ranges be the union of live seekable range and the HTMLMediaElement.buffered attribute.
308             buffered->unionWith(*m_liveSeekable);
309             // Return a single range with a start time equal to the earliest start time in union ranges
310             // and an end time equal to the highest end time in union ranges and abort these steps.
311             buffered->add(buffered->start(0), buffered->maximumBufferedTime());
312             return TimeRanges::create(*buffered);
313         }
314
315         // If the HTMLMediaElement.buffered attribute returns an empty TimeRanges object, then return
316         // an empty TimeRanges object and abort these steps.
317         if (!buffered->length())
318             return TimeRanges::create();
319
320         // Return a single range with a start time of 0 and an end time equal to the highest end time
321         // reported by the HTMLMediaElement.buffered attribute.
322         return TimeRanges::create({MediaTime::zeroTime(), buffered->maximumBufferedTime()});
323     }
324
325     // ↳ Otherwise:
326     // Return a single range with a start time of 0 and an end time equal to duration.
327     return TimeRanges::create({MediaTime::zeroTime(), m_duration});
328 }
329
330 ExceptionOr<void> MediaSource::setLiveSeekableRange(double start, double end)
331 {
332     // W3C Editor's Draft 16 September 2016
333     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-mediasource-setliveseekablerange
334
335     ALWAYS_LOG(LOGIDENTIFIER, "start = ", start, ", end = ", end);
336
337     // If the readyState attribute is not "open" then throw an InvalidStateError exception and abort these steps.
338     if (!isOpen())
339         return Exception { InvalidStateError };
340
341     // If start is negative or greater than end, then throw a TypeError exception and abort these steps.
342     if (start < 0 || start > end)
343         return Exception { TypeError };
344
345     // Set live seekable range to be a new normalized TimeRanges object containing a single range
346     // whose start position is start and end position is end.
347     m_liveSeekable = makeUnique<PlatformTimeRanges>(MediaTime::createWithDouble(start), MediaTime::createWithDouble(end));
348
349     return { };
350 }
351
352 ExceptionOr<void> MediaSource::clearLiveSeekableRange()
353 {
354     // W3C Editor's Draft 16 September 2016
355     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#dom-mediasource-clearliveseekablerange
356
357     ALWAYS_LOG(LOGIDENTIFIER);
358
359     // If the readyState attribute is not "open" then throw an InvalidStateError exception and abort these steps.
360     if (!isOpen())
361         return Exception { InvalidStateError };
362     m_liveSeekable = nullptr;
363     return { };
364 }
365
366 const MediaTime& MediaSource::currentTimeFudgeFactor()
367 {
368     // Allow hasCurrentTime() to be off by as much as the length of two 24fps video frames
369     static NeverDestroyed<MediaTime> fudgeFactor(2002, 24000);
370     return fudgeFactor;
371 }
372
373 bool MediaSource::contentTypeShouldGenerateTimestamps(const ContentType& contentType)
374 {
375     return contentType.containerType() == "audio/aac" || contentType.containerType() == "audio/mpeg";
376 }
377
378 bool MediaSource::hasBufferedTime(const MediaTime& time)
379 {
380     if (time > duration())
381         return false;
382
383     auto ranges = buffered();
384     if (!ranges->length())
385         return false;
386
387     return abs(ranges->nearest(time) - time) <= currentTimeFudgeFactor();
388 }
389
390 bool MediaSource::hasCurrentTime()
391 {
392     return hasBufferedTime(currentTime());
393 }
394
395 bool MediaSource::hasFutureTime()
396 {
397     MediaTime currentTime = this->currentTime();
398     MediaTime duration = this->duration();
399
400     if (currentTime >= duration)
401         return true;
402
403     auto ranges = buffered();
404     MediaTime nearest = ranges->nearest(currentTime);
405     if (abs(nearest - currentTime) > currentTimeFudgeFactor())
406         return false;
407
408     size_t found = ranges->find(nearest);
409     if (found == notFound)
410         return false;
411
412     MediaTime localEnd = ranges->end(found);
413     if (localEnd == duration)
414         return true;
415
416     return localEnd - currentTime > currentTimeFudgeFactor();
417 }
418
419 void MediaSource::monitorSourceBuffers()
420 {
421     if (isClosed())
422         return;
423
424     // 2.4.4 SourceBuffer Monitoring
425     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#buffer-monitoring
426
427     // Note, the behavior if activeSourceBuffers is empty is undefined.
428     if (!m_activeSourceBuffers) {
429         m_private->setReadyState(MediaPlayer::HaveNothing);
430         return;
431     }
432
433     // ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING:
434     if (mediaElement()->readyState() == HTMLMediaElement::HAVE_NOTHING) {
435         // 1. Abort these steps.
436         return;
437     }
438
439     // ↳ If HTMLMediaElement.buffered does not contain a TimeRange for the current playback position:
440     if (!hasCurrentTime()) {
441         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_METADATA.
442         // 2. If this is the first transition to HAVE_METADATA, then queue a task to fire a simple event
443         // named loadedmetadata at the media element.
444         m_private->setReadyState(MediaPlayer::HaveMetadata);
445
446         // 3. Abort these steps.
447         return;
448     }
449
450     // ↳ If HTMLMediaElement.buffered contains a TimeRange that includes the current
451     //  playback position and enough data to ensure uninterrupted playback:
452     auto ranges = buffered();
453     if (std::all_of(m_activeSourceBuffers->begin(), m_activeSourceBuffers->end(), [&](auto& sourceBuffer) {
454         return sourceBuffer->canPlayThroughRange(*ranges);
455     })) {
456         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_ENOUGH_DATA.
457         // 2. Queue a task to fire a simple event named canplaythrough at the media element.
458         // 3. Playback may resume at this point if it was previously suspended by a transition to HAVE_CURRENT_DATA.
459         m_private->setReadyState(MediaPlayer::HaveEnoughData);
460
461         if (m_pendingSeekTime.isValid())
462             completeSeek();
463
464         // 4. Abort these steps.
465         return;
466     }
467
468     // ↳ If HTMLMediaElement.buffered contains a TimeRange that includes the current playback
469     //  position and some time beyond the current playback position, then run the following steps:
470     if (hasFutureTime()) {
471         // 1. Set the HTMLMediaElement.readyState attribute to HAVE_FUTURE_DATA.
472         // 2. If the previous value of HTMLMediaElement.readyState was less than HAVE_FUTURE_DATA, then queue a task to fire a simple event named canplay at the media element.
473         // 3. Playback may resume at this point if it was previously suspended by a transition to HAVE_CURRENT_DATA.
474         m_private->setReadyState(MediaPlayer::HaveFutureData);
475
476         if (m_pendingSeekTime.isValid())
477             completeSeek();
478
479         // 4. Abort these steps.
480         return;
481     }
482
483     // ↳ If HTMLMediaElement.buffered contains a TimeRange that ends at the current playback position and does not have a range covering the time immediately after the current position:
484     // NOTE: Logically, !(all objects do not contain currentTime) == (some objects contain current time)
485
486     // 1. Set the HTMLMediaElement.readyState attribute to HAVE_CURRENT_DATA.
487     // 2. If this is the first transition to HAVE_CURRENT_DATA, then queue a task to fire a simple
488     // event named loadeddata at the media element.
489     // 3. Playback is suspended at this point since the media element doesn't have enough data to
490     // advance the media timeline.
491     m_private->setReadyState(MediaPlayer::HaveCurrentData);
492
493     if (m_pendingSeekTime.isValid())
494         completeSeek();
495
496     // 4. Abort these steps.
497 }
498
499 ExceptionOr<void> MediaSource::setDuration(double duration)
500 {
501     // 2.1 Attributes - Duration
502     // https://www.w3.org/TR/2016/REC-media-source-20161117/#attributes
503
504     ALWAYS_LOG(LOGIDENTIFIER, duration);
505
506     // On setting, run the following steps:
507     // 1. If the value being set is negative or NaN then throw a TypeError exception and abort these steps.
508     if (duration < 0.0 || std::isnan(duration))
509         return Exception { TypeError };
510
511     // 2. If the readyState attribute is not "open" then throw an InvalidStateError exception and abort these steps.
512     if (!isOpen())
513         return Exception { InvalidStateError };
514
515     // 3. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an InvalidStateError
516     // exception and abort these steps.
517     for (auto& sourceBuffer : *m_sourceBuffers) {
518         if (sourceBuffer->updating())
519             return Exception { InvalidStateError };
520     }
521
522     // 4. Run the duration change algorithm with new duration set to the value being assigned to this attribute.
523     return setDurationInternal(MediaTime::createWithDouble(duration));
524 }
525
526 ExceptionOr<void> MediaSource::setDurationInternal(const MediaTime& duration)
527 {
528     // 2.4.6 Duration Change
529     // https://www.w3.org/TR/2016/REC-media-source-20161117/#duration-change-algorithm
530
531     MediaTime newDuration = duration;
532
533     // 1. If the current value of duration is equal to new duration, then return.
534     if (newDuration == m_duration)
535         return { };
536
537     // 2. If new duration is less than the highest presentation timestamp of any buffered coded frames
538     // for all SourceBuffer objects in sourceBuffers, then throw an InvalidStateError exception and
539     // abort these steps.
540     // 3. Let highest end time be the largest track buffer ranges end time across all the track buffers
541     // across all SourceBuffer objects in sourceBuffers.
542     MediaTime highestPresentationTimestamp;
543     MediaTime highestEndTime;
544     for (auto& sourceBuffer : *m_sourceBuffers) {
545         highestPresentationTimestamp = std::max(highestPresentationTimestamp, sourceBuffer->highestPresentationTimestamp());
546         highestEndTime = std::max(highestEndTime, sourceBuffer->bufferedInternal().ranges().maximumBufferedTime());
547     }
548     if (highestPresentationTimestamp.isValid() && newDuration < highestPresentationTimestamp)
549         return Exception { InvalidStateError };
550
551     // 4. If new duration is less than highest end time, then
552     // 4.1. Update new duration to equal highest end time.
553     if (highestEndTime.isValid() && newDuration < highestEndTime)
554         newDuration = highestEndTime;
555
556     // 5. Update duration to new duration.
557     m_duration = newDuration;
558     ALWAYS_LOG(LOGIDENTIFIER, duration);
559
560     // 6. Update the media duration to new duration and run the HTMLMediaElement duration change algorithm.
561     m_private->durationChanged();
562
563     return { };
564 }
565
566 void MediaSource::setReadyState(ReadyState state)
567 {
568     auto oldState = readyState();
569     if (oldState == state)
570         return;
571
572     m_readyState = state;
573
574     onReadyStateChange(oldState, state);
575 }
576
577 ExceptionOr<void> MediaSource::endOfStream(Optional<EndOfStreamError> error)
578 {
579     ALWAYS_LOG(LOGIDENTIFIER);
580
581     // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-endOfStream-void-EndOfStreamError-error
582     // 1. If the readyState attribute is not in the "open" state then throw an
583     // InvalidStateError exception and abort these steps.
584     if (!isOpen())
585         return Exception { InvalidStateError };
586
587     // 2. If the updating attribute equals true on any SourceBuffer in sourceBuffers, then throw an
588     // InvalidStateError exception and abort these steps.
589     if (std::any_of(m_sourceBuffers->begin(), m_sourceBuffers->end(), [](auto& sourceBuffer) { return sourceBuffer->updating(); }))
590         return Exception { InvalidStateError };
591
592     // 3. Run the end of stream algorithm with the error parameter set to error.
593     streamEndedWithError(error);
594
595     return { };
596 }
597
598 void MediaSource::streamEndedWithError(Optional<EndOfStreamError> error)
599 {
600 #if !RELEASE_LOG_DISABLED
601     if (error)
602         ALWAYS_LOG(LOGIDENTIFIER, error.value());
603     else
604         ALWAYS_LOG(LOGIDENTIFIER);
605 #endif
606
607     if (isClosed())
608         return;
609
610     // 2.4.7 https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#end-of-stream-algorithm
611
612     // 1. Change the readyState attribute value to "ended".
613     // 2. Queue a task to fire a simple event named sourceended at the MediaSource.
614     setReadyState(ReadyState::Ended);
615
616     // 3.
617     if (!error) {
618         // ↳ If error is not set, is null, or is an empty string
619         // 1. Run the duration change algorithm with new duration set to the highest end time reported by
620         // the buffered attribute across all SourceBuffer objects in sourceBuffers.
621         MediaTime maxEndTime;
622         for (auto& sourceBuffer : *m_sourceBuffers) {
623             if (auto length = sourceBuffer->bufferedInternal().length())
624                 maxEndTime = std::max(sourceBuffer->bufferedInternal().ranges().end(length - 1), maxEndTime);
625         }
626         setDurationInternal(maxEndTime);
627
628         // 2. Notify the media element that it now has all of the media data.
629         for (auto& sourceBuffer : *m_sourceBuffers)
630             sourceBuffer->trySignalAllSamplesEnqueued();
631         m_private->markEndOfStream(MediaSourcePrivate::EosNoError);
632     } else if (error == EndOfStreamError::Network) {
633         // ↳ If error is set to "network"
634         ASSERT(m_mediaElement);
635         if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) {
636             //  ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING
637             //    Run the "If the media data cannot be fetched at all, due to network errors, causing
638             //    the user agent to give up trying to fetch the resource" steps of the resource fetch algorithm.
639             //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed().
640             m_mediaElement->mediaLoadingFailed(MediaPlayer::NetworkError);
641         } else {
642             //  ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING
643             //    Run the "If the connection is interrupted after some media data has been received, causing the
644             //    user agent to give up trying to fetch the resource" steps of the resource fetch algorithm.
645             //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally().
646             m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::NetworkError);
647         }
648     } else {
649         // ↳ If error is set to "decode"
650         ASSERT(error == EndOfStreamError::Decode);
651         ASSERT(m_mediaElement);
652         if (m_mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING) {
653             //  ↳ If the HTMLMediaElement.readyState attribute equals HAVE_NOTHING
654             //    Run the "If the media data can be fetched but is found by inspection to be in an unsupported
655             //    format, or can otherwise not be rendered at all" steps of the resource fetch algorithm.
656             //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailed().
657             m_mediaElement->mediaLoadingFailed(MediaPlayer::FormatError);
658         } else {
659             //  ↳ If the HTMLMediaElement.readyState attribute is greater than HAVE_NOTHING
660             //    Run the media data is corrupted steps of the resource fetch algorithm.
661             //    NOTE: This step is handled by HTMLMediaElement::mediaLoadingFailedFatally().
662             m_mediaElement->mediaLoadingFailedFatally(MediaPlayer::DecodeError);
663         }
664     }
665 }
666
667 ExceptionOr<Ref<SourceBuffer>> MediaSource::addSourceBuffer(const String& type)
668 {
669     DEBUG_LOG(LOGIDENTIFIER, type);
670
671     // 2.2 http://www.w3.org/TR/media-source/#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
672     // When this method is invoked, the user agent must run the following steps:
673
674     // 1. If type is an empty string then throw a TypeError exception and abort these steps.
675     if (type.isEmpty())
676         return Exception { TypeError };
677
678     // 2. If type contains a MIME type that is not supported ..., then throw a
679     // NotSupportedError exception and abort these steps.
680     if (!isTypeSupported(type))
681         return Exception { NotSupportedError };
682
683     // 4. If the readyState attribute is not in the "open" state then throw an
684     // InvalidStateError exception and abort these steps.
685     if (!isOpen())
686         return Exception { InvalidStateError };
687
688     // 5. Create a new SourceBuffer object and associated resources.
689     ContentType contentType(type);
690     auto sourceBufferPrivate = createSourceBufferPrivate(contentType);
691
692     if (sourceBufferPrivate.hasException()) {
693         // 2. If type contains a MIME type that is not supported ..., then throw a NotSupportedError exception and abort these steps.
694         // 3. If the user agent can't handle any more SourceBuffer objects then throw a QuotaExceededError exception and abort these steps
695         return sourceBufferPrivate.releaseException();
696     }
697
698     auto buffer = SourceBuffer::create(sourceBufferPrivate.releaseReturnValue(), this);
699     DEBUG_LOG(LOGIDENTIFIER, "created SourceBuffer");
700
701     // 6. Set the generate timestamps flag on the new object to the value in the "Generate Timestamps Flag"
702     // column of the byte stream format registry [MSE-REGISTRY] entry that is associated with type.
703     // NOTE: In the current byte stream format registry <http://www.w3.org/2013/12/byte-stream-format-registry/>
704     // only the "MPEG Audio Byte Stream Format" has the "Generate Timestamps Flag" value set.
705     bool shouldGenerateTimestamps = contentTypeShouldGenerateTimestamps(contentType);
706     buffer->setShouldGenerateTimestamps(shouldGenerateTimestamps);
707
708     // 7. If the generate timestamps flag equals true:
709     // ↳ Set the mode attribute on the new object to "sequence".
710     // Otherwise:
711     // ↳ Set the mode attribute on the new object to "segments".
712     buffer->setMode(shouldGenerateTimestamps ? SourceBuffer::AppendMode::Sequence : SourceBuffer::AppendMode::Segments);
713
714     // 8. Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
715     m_sourceBuffers->add(buffer.copyRef());
716     regenerateActiveSourceBuffers();
717
718     // 9. Return the new object to the caller.
719     return buffer;
720 }
721
722 ExceptionOr<void> MediaSource::removeSourceBuffer(SourceBuffer& buffer)
723 {
724     DEBUG_LOG(LOGIDENTIFIER);
725
726     Ref<SourceBuffer> protect(buffer);
727
728     // 2. If sourceBuffer specifies an object that is not in sourceBuffers then
729     // throw a NotFoundError exception and abort these steps.
730     if (!m_sourceBuffers->length() || !m_sourceBuffers->contains(buffer))
731         return Exception { NotFoundError };
732
733     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
734     buffer.abortIfUpdating();
735
736     ASSERT(scriptExecutionContext());
737     if (!scriptExecutionContext()->activeDOMObjectsAreStopped()) {
738         // 4. Let SourceBuffer audioTracks list equal the AudioTrackList object returned by sourceBuffer.audioTracks.
739         auto* audioTracks = buffer.audioTracksIfExists();
740
741         // 5. If the SourceBuffer audioTracks list is not empty, then run the following steps:
742         if (audioTracks && audioTracks->length()) {
743             // 5.1 Let HTMLMediaElement audioTracks list equal the AudioTrackList object returned by the audioTracks
744             // attribute on the HTMLMediaElement.
745             // 5.2 Let the removed enabled audio track flag equal false.
746             bool removedEnabledAudioTrack = false;
747
748             // 5.3 For each AudioTrack object in the SourceBuffer audioTracks list, run the following steps:
749             while (audioTracks->length()) {
750                 auto& track = *audioTracks->lastItem();
751
752                 // 5.3.1 Set the sourceBuffer attribute on the AudioTrack object to null.
753                 track.setSourceBuffer(nullptr);
754
755                 // 5.3.2 If the enabled attribute on the AudioTrack object is true, then set the removed enabled
756                 // audio track flag to true.
757                 if (track.enabled())
758                     removedEnabledAudioTrack = true;
759
760                 // 5.3.3 Remove the AudioTrack object from the HTMLMediaElement audioTracks list.
761                 // 5.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
762                 // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement audioTracks list.
763                 if (mediaElement())
764                     mediaElement()->removeAudioTrack(track);
765
766                 // 5.3.5 Remove the AudioTrack object from the SourceBuffer audioTracks list.
767                 // 5.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
768                 // cancelable, and that uses the TrackEvent interface, at the SourceBuffer audioTracks list.
769                 audioTracks->remove(track);
770             }
771
772             // 5.4 If the removed enabled audio track flag equals true, then queue a task to fire a simple event
773             // named change at the HTMLMediaElement audioTracks list.
774             if (removedEnabledAudioTrack)
775                 mediaElement()->ensureAudioTracks().scheduleChangeEvent();
776         }
777
778         // 6. Let SourceBuffer videoTracks list equal the VideoTrackList object returned by sourceBuffer.videoTracks.
779         auto* videoTracks = buffer.videoTracksIfExists();
780
781         // 7. If the SourceBuffer videoTracks list is not empty, then run the following steps:
782         if (videoTracks && videoTracks->length()) {
783             // 7.1 Let HTMLMediaElement videoTracks list equal the VideoTrackList object returned by the videoTracks
784             // attribute on the HTMLMediaElement.
785             // 7.2 Let the removed selected video track flag equal false.
786             bool removedSelectedVideoTrack = false;
787
788             // 7.3 For each VideoTrack object in the SourceBuffer videoTracks list, run the following steps:
789             while (videoTracks->length()) {
790                 auto& track = *videoTracks->lastItem();
791
792                 // 7.3.1 Set the sourceBuffer attribute on the VideoTrack object to null.
793                 track.setSourceBuffer(nullptr);
794
795                 // 7.3.2 If the selected attribute on the VideoTrack object is true, then set the removed selected
796                 // video track flag to true.
797                 if (track.selected())
798                     removedSelectedVideoTrack = true;
799
800                 // 7.3.3 Remove the VideoTrack object from the HTMLMediaElement videoTracks list.
801                 // 7.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
802                 // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement videoTracks list.
803                 if (mediaElement())
804                     mediaElement()->removeVideoTrack(track);
805
806                 // 7.3.5 Remove the VideoTrack object from the SourceBuffer videoTracks list.
807                 // 7.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
808                 // cancelable, and that uses the TrackEvent interface, at the SourceBuffer videoTracks list.
809                 videoTracks->remove(track);
810             }
811
812             // 7.4 If the removed selected video track flag equals true, then queue a task to fire a simple event
813             // named change at the HTMLMediaElement videoTracks list.
814             if (removedSelectedVideoTrack)
815                 mediaElement()->ensureVideoTracks().scheduleChangeEvent();
816         }
817
818         // 8. Let SourceBuffer textTracks list equal the TextTrackList object returned by sourceBuffer.textTracks.
819         auto* textTracks = buffer.textTracksIfExists();
820
821         // 9. If the SourceBuffer textTracks list is not empty, then run the following steps:
822         if (textTracks && textTracks->length()) {
823             // 9.1 Let HTMLMediaElement textTracks list equal the TextTrackList object returned by the textTracks
824             // attribute on the HTMLMediaElement.
825             // 9.2 Let the removed enabled text track flag equal false.
826             bool removedEnabledTextTrack = false;
827
828             // 9.3 For each TextTrack object in the SourceBuffer textTracks list, run the following steps:
829             while (textTracks->length()) {
830                 auto& track = *textTracks->lastItem();
831
832                 // 9.3.1 Set the sourceBuffer attribute on the TextTrack object to null.
833                 track.setSourceBuffer(nullptr);
834
835                 // 9.3.2 If the mode attribute on the TextTrack object is set to "showing" or "hidden", then
836                 // set the removed enabled text track flag to true.
837                 if (track.mode() == TextTrack::Mode::Showing || track.mode() == TextTrack::Mode::Hidden)
838                     removedEnabledTextTrack = true;
839
840                 // 9.3.3 Remove the TextTrack object from the HTMLMediaElement textTracks list.
841                 // 9.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
842                 // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement textTracks list.
843                 if (mediaElement())
844                     mediaElement()->removeTextTrack(track);
845
846                 // 9.3.5 Remove the TextTrack object from the SourceBuffer textTracks list.
847                 // 9.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
848                 // cancelable, and that uses the TrackEvent interface, at the SourceBuffer textTracks list.
849                 textTracks->remove(track);
850             }
851
852             // 9.4 If the removed enabled text track flag equals true, then queue a task to fire a simple event
853             // named change at the HTMLMediaElement textTracks list.
854             if (removedEnabledTextTrack)
855                 mediaElement()->ensureTextTracks().scheduleChangeEvent();
856         }
857     }
858
859     // 10. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ...
860     m_activeSourceBuffers->remove(buffer);
861
862     // 11. Remove sourceBuffer from sourceBuffers and fire a removesourcebuffer event
863     // on that object.
864     m_sourceBuffers->remove(buffer);
865
866     // 12. Destroy all resources for sourceBuffer.
867     buffer.removedFromMediaSource();
868
869     return { };
870 }
871
872 bool MediaSource::isTypeSupported(const String& type)
873 {
874     // Section 2.2 isTypeSupported() method steps.
875     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-MediaSource-isTypeSupported-boolean-DOMString-type
876     // 1. If type is an empty string, then return false.
877     if (type.isNull() || type.isEmpty())
878         return false;
879
880     ContentType contentType(type);
881     String codecs = contentType.parameter("codecs");
882
883     // 2. If type does not contain a valid MIME type string, then return false.
884     if (contentType.containerType().isEmpty())
885         return false;
886
887     // 3. If type contains a media type or media subtype that the MediaSource does not support, then return false.
888     // 4. If type contains at a codec that the MediaSource does not support, then return false.
889     // 5. If the MediaSource does not support the specified combination of media type, media subtype, and codecs then return false.
890     // 6. Return true.
891     MediaEngineSupportParameters parameters;
892     parameters.type = contentType;
893     parameters.isMediaSource = true;
894     MediaPlayer::SupportsType supported = MediaPlayer::supportsType(parameters);
895
896     if (codecs.isEmpty())
897         return supported != MediaPlayer::IsNotSupported;
898
899     return supported == MediaPlayer::IsSupported;
900 }
901
902 bool MediaSource::isOpen() const
903 {
904     return readyState() == ReadyState::Open;
905 }
906
907 bool MediaSource::isClosed() const
908 {
909     return readyState() == ReadyState::Closed;
910 }
911
912 bool MediaSource::isEnded() const
913 {
914     return readyState() == ReadyState::Ended;
915 }
916
917 void MediaSource::detachFromElement(HTMLMediaElement& element)
918 {
919     ALWAYS_LOG(LOGIDENTIFIER);
920
921     ASSERT_UNUSED(element, m_mediaElement == &element);
922
923     // 2.4.2 Detaching from a media element
924     // https://rawgit.com/w3c/media-source/45627646344eea0170dd1cbc5a3d508ca751abb8/media-source-respec.html#mediasource-detach
925
926     // 1. Set the readyState attribute to "closed".
927     // 7. Queue a task to fire a simple event named sourceclose at the MediaSource.
928     setReadyState(ReadyState::Closed);
929
930     // 2. Update duration to NaN.
931     m_duration = MediaTime::invalidTime();
932
933     // 3. Remove all the SourceBuffer objects from activeSourceBuffers.
934     // 4. Queue a task to fire a simple event named removesourcebuffer at activeSourceBuffers.
935     while (m_activeSourceBuffers->length())
936         removeSourceBuffer(*m_activeSourceBuffers->item(0));
937
938     // 5. Remove all the SourceBuffer objects from sourceBuffers.
939     // 6. Queue a task to fire a simple event named removesourcebuffer at sourceBuffers.
940     while (m_sourceBuffers->length())
941         removeSourceBuffer(*m_sourceBuffers->item(0));
942
943     m_private = nullptr;
944     m_mediaElement = nullptr;
945 }
946
947 void MediaSource::sourceBufferDidChangeActiveState(SourceBuffer&, bool)
948 {
949     regenerateActiveSourceBuffers();
950 }
951
952 bool MediaSource::attachToElement(HTMLMediaElement& element)
953 {
954     if (m_mediaElement)
955         return false;
956
957     ASSERT(isClosed());
958
959     m_mediaElement = &element;
960     return true;
961 }
962
963 void MediaSource::openIfInEndedState()
964 {
965     if (m_readyState != ReadyState::Ended)
966         return;
967
968     ALWAYS_LOG(LOGIDENTIFIER);
969
970     setReadyState(ReadyState::Open);
971     m_private->unmarkEndOfStream();
972 }
973
974 bool MediaSource::hasPendingActivity() const
975 {
976     return m_private || m_asyncEventQueue->hasPendingEvents()
977         || ActiveDOMObject::hasPendingActivity();
978 }
979
980 void MediaSource::stop()
981 {
982     ALWAYS_LOG(LOGIDENTIFIER);
983
984     if (m_mediaElement)
985         m_mediaElement->detachMediaSource();
986     m_readyState = ReadyState::Closed;
987     m_private = nullptr;
988 }
989
990 const char* MediaSource::activeDOMObjectName() const
991 {
992     return "MediaSource";
993 }
994
995 void MediaSource::onReadyStateChange(ReadyState oldState, ReadyState newState)
996 {
997     ALWAYS_LOG(LOGIDENTIFIER, "old state = ", oldState, ", new state = ", newState);
998
999     for (auto& buffer : *m_sourceBuffers)
1000         buffer->readyStateChanged();
1001
1002     if (isOpen()) {
1003         scheduleEvent(eventNames().sourceopenEvent);
1004         return;
1005     }
1006
1007     if (oldState == ReadyState::Open && newState == ReadyState::Ended) {
1008         scheduleEvent(eventNames().sourceendedEvent);
1009         return;
1010     }
1011
1012     ASSERT(isClosed());
1013     scheduleEvent(eventNames().sourcecloseEvent);
1014 }
1015
1016 Vector<PlatformTimeRanges> MediaSource::activeRanges() const
1017 {
1018     Vector<PlatformTimeRanges> activeRanges;
1019     for (auto& sourceBuffer : *m_activeSourceBuffers)
1020         activeRanges.append(sourceBuffer->bufferedInternal().ranges());
1021     return activeRanges;
1022 }
1023
1024 ExceptionOr<Ref<SourceBufferPrivate>> MediaSource::createSourceBufferPrivate(const ContentType& type)
1025 {
1026     RefPtr<SourceBufferPrivate> sourceBufferPrivate;
1027     switch (m_private->addSourceBuffer(type, sourceBufferPrivate)) {
1028     case MediaSourcePrivate::Ok:
1029         return sourceBufferPrivate.releaseNonNull();
1030     case MediaSourcePrivate::NotSupported:
1031         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
1032         // Step 2: If type contains a MIME type ... that is not supported with the types
1033         // specified for the other SourceBuffer objects in sourceBuffers, then throw
1034         // a NotSupportedError exception and abort these steps.
1035         return Exception { NotSupportedError };
1036     case MediaSourcePrivate::ReachedIdLimit:
1037         // 2.2 https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-MediaSource-addSourceBuffer-SourceBuffer-DOMString-type
1038         // Step 3: If the user agent can't handle any more SourceBuffer objects then throw
1039         // a QuotaExceededError exception and abort these steps.
1040         return Exception { QuotaExceededError };
1041     }
1042
1043     ASSERT_NOT_REACHED();
1044     return Exception { QuotaExceededError };
1045 }
1046
1047 void MediaSource::scheduleEvent(const AtomString& eventName)
1048 {
1049     DEBUG_LOG(LOGIDENTIFIER, "scheduling '", eventName, "'");
1050
1051     auto event = Event::create(eventName, Event::CanBubble::No, Event::IsCancelable::No);
1052     event->setTarget(this);
1053
1054     m_asyncEventQueue->enqueueEvent(WTFMove(event));
1055 }
1056
1057 ScriptExecutionContext* MediaSource::scriptExecutionContext() const
1058 {
1059     return ActiveDOMObject::scriptExecutionContext();
1060 }
1061
1062 EventTargetInterface MediaSource::eventTargetInterface() const
1063 {
1064     return MediaSourceEventTargetInterfaceType;
1065 }
1066
1067 URLRegistry& MediaSource::registry() const
1068 {
1069     return MediaSourceRegistry::registry();
1070 }
1071
1072 void MediaSource::regenerateActiveSourceBuffers()
1073 {
1074     Vector<RefPtr<SourceBuffer>> newList;
1075     for (auto& sourceBuffer : *m_sourceBuffers) {
1076         if (sourceBuffer->active())
1077             newList.append(sourceBuffer);
1078     }
1079     m_activeSourceBuffers->swap(newList);
1080     for (auto& sourceBuffer : *m_activeSourceBuffers)
1081         sourceBuffer->setBufferedDirty(true);
1082 }
1083
1084 #if !RELEASE_LOG_DISABLED
1085 void MediaSource::setLogIdentifier(const void* identifier)
1086 {
1087     m_logIdentifier = identifier;
1088     ALWAYS_LOG(LOGIDENTIFIER);
1089 }
1090
1091 WTFLogChannel& MediaSource::logChannel() const
1092 {
1093     return LogMediaSource;
1094 }
1095 #endif
1096
1097 }
1098
1099 #endif