• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "modules/mediasource/SourceBuffer.h"
33 
34 #include "bindings/v8/ExceptionMessages.h"
35 #include "bindings/v8/ExceptionState.h"
36 #include "core/dom/ExceptionCode.h"
37 #include "core/dom/ExecutionContext.h"
38 #include "core/events/GenericEventQueue.h"
39 #include "core/fileapi/FileReaderLoader.h"
40 #include "core/fileapi/Stream.h"
41 #include "core/html/TimeRanges.h"
42 #include "modules/mediasource/MediaSource.h"
43 #include "platform/Logging.h"
44 #include "platform/TraceEvent.h"
45 #include "public/platform/WebSourceBuffer.h"
46 #include "wtf/ArrayBuffer.h"
47 #include "wtf/ArrayBufferView.h"
48 #include "wtf/MathExtras.h"
49 
50 #include <limits>
51 
52 using blink::WebSourceBuffer;
53 
54 namespace WebCore {
55 
56 namespace {
57 
throwExceptionIfRemovedOrUpdating(bool isRemoved,bool isUpdating,ExceptionState & exceptionState)58 static bool throwExceptionIfRemovedOrUpdating(bool isRemoved, bool isUpdating, ExceptionState& exceptionState)
59 {
60     if (isRemoved) {
61         exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
62         return true;
63     }
64     if (isUpdating) {
65         exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer is still processing an 'appendBuffer', 'appendStream', or 'remove' operation.");
66         return true;
67     }
68 
69     return false;
70 }
71 
72 } // namespace
73 
create(PassOwnPtr<WebSourceBuffer> webSourceBuffer,MediaSource * source,GenericEventQueue * asyncEventQueue)74 PassRefPtrWillBeRawPtr<SourceBuffer> SourceBuffer::create(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
75 {
76     RefPtrWillBeRawPtr<SourceBuffer> sourceBuffer(adoptRefWillBeRefCountedGarbageCollected(new SourceBuffer(webSourceBuffer, source, asyncEventQueue)));
77     sourceBuffer->suspendIfNeeded();
78     return sourceBuffer.release();
79 }
80 
SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer,MediaSource * source,GenericEventQueue * asyncEventQueue)81 SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
82     : ActiveDOMObject(source->executionContext())
83     , m_webSourceBuffer(webSourceBuffer)
84     , m_source(source)
85     , m_asyncEventQueue(asyncEventQueue)
86     , m_mode(segmentsKeyword())
87     , m_updating(false)
88     , m_timestampOffset(0)
89     , m_appendWindowStart(0)
90     , m_appendWindowEnd(std::numeric_limits<double>::infinity())
91     , m_pendingAppendDataOffset(0)
92     , m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart)
93     , m_pendingRemoveStart(-1)
94     , m_pendingRemoveEnd(-1)
95     , m_removeAsyncPartRunner(this, &SourceBuffer::removeAsyncPart)
96     , m_streamMaxSizeValid(false)
97     , m_streamMaxSize(0)
98     , m_appendStreamAsyncPartRunner(this, &SourceBuffer::appendStreamAsyncPart)
99 {
100     ASSERT(m_webSourceBuffer);
101     ASSERT(m_source);
102     ScriptWrappable::init(this);
103 }
104 
~SourceBuffer()105 SourceBuffer::~SourceBuffer()
106 {
107     ASSERT(isRemoved());
108     ASSERT(!m_loader);
109     ASSERT(!m_stream);
110 }
111 
segmentsKeyword()112 const AtomicString& SourceBuffer::segmentsKeyword()
113 {
114     DEFINE_STATIC_LOCAL(const AtomicString, segments, ("segments", AtomicString::ConstructFromLiteral));
115     return segments;
116 }
117 
sequenceKeyword()118 const AtomicString& SourceBuffer::sequenceKeyword()
119 {
120     DEFINE_STATIC_LOCAL(const AtomicString, sequence, ("sequence", AtomicString::ConstructFromLiteral));
121     return sequence;
122 }
123 
setMode(const AtomicString & newMode,ExceptionState & exceptionState)124 void SourceBuffer::setMode(const AtomicString& newMode, ExceptionState& exceptionState)
125 {
126     // Section 3.1 On setting mode attribute steps.
127     // 1. Let new mode equal the new value being assigned to this attribute.
128     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw
129     //    an INVALID_STATE_ERR exception and abort these steps.
130     // 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
131     if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
132         return;
133 
134     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
135     // 4.1 Set the readyState attribute of the parent media source to "open"
136     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
137     m_source->openIfInEndedState();
138 
139     // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
140     // 6. If the new mode equals "sequence", then set the group start timestamp to the highest presentation end timestamp.
141     WebSourceBuffer::AppendMode appendMode = WebSourceBuffer::AppendModeSegments;
142     if (newMode == sequenceKeyword())
143         appendMode = WebSourceBuffer::AppendModeSequence;
144     if (!m_webSourceBuffer->setMode(appendMode)) {
145         exceptionState.throwDOMException(InvalidStateError, "The mode may not be set while the SourceBuffer's append state is 'PARSING_MEDIA_SEGMENT'.");
146         return;
147     }
148 
149     // 7. Update the attribute to new mode.
150     m_mode = newMode;
151 }
152 
buffered(ExceptionState & exceptionState) const153 PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionState& exceptionState) const
154 {
155     // Section 3.1 buffered attribute steps.
156     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
157     //    InvalidStateError exception and abort these steps.
158     if (isRemoved()) {
159         exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
160         return nullptr;
161     }
162 
163     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
164     return TimeRanges::create(m_webSourceBuffer->buffered());
165 }
166 
timestampOffset() const167 double SourceBuffer::timestampOffset() const
168 {
169     return m_timestampOffset;
170 }
171 
setTimestampOffset(double offset,ExceptionState & exceptionState)172 void SourceBuffer::setTimestampOffset(double offset, ExceptionState& exceptionState)
173 {
174     // Section 3.1 timestampOffset attribute setter steps.
175     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-SourceBuffer-timestampOffset
176     // 1. Let new timestamp offset equal the new value being assigned to this attribute.
177     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw an
178     //    InvalidStateError exception and abort these steps.
179     // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
180     if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
181         return;
182 
183     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
184     // 4.1 Set the readyState attribute of the parent media source to "open"
185     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
186     m_source->openIfInEndedState();
187 
188     // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
189     // 6. If the mode attribute equals "sequence", then set the group start timestamp to new timestamp offset.
190     if (!m_webSourceBuffer->setTimestampOffset(offset)) {
191         exceptionState.throwDOMException(InvalidStateError, "The timestamp offset may not be set while the SourceBuffer's append state is 'PARSING_MEDIA_SEGMENT'.");
192         return;
193     }
194 
195     // 7. Update the attribute to new timestamp offset.
196     m_timestampOffset = offset;
197 }
198 
appendWindowStart() const199 double SourceBuffer::appendWindowStart() const
200 {
201     return m_appendWindowStart;
202 }
203 
setAppendWindowStart(double start,ExceptionState & exceptionState)204 void SourceBuffer::setAppendWindowStart(double start, ExceptionState& exceptionState)
205 {
206     // Section 3.1 appendWindowStart attribute setter steps.
207     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-SourceBuffer-appendWindowStart
208     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
209     //    InvalidStateError exception and abort these steps.
210     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
211     if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
212         return;
213 
214     // 3. If the new value is less than 0 or greater than or equal to appendWindowEnd then throw an InvalidAccessError
215     //    exception and abort these steps.
216     if (start < 0 || start >= m_appendWindowEnd) {
217         exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexOutsideRange("value", start, 0.0, ExceptionMessages::ExclusiveBound, m_appendWindowEnd, ExceptionMessages::InclusiveBound));
218         return;
219     }
220 
221     m_webSourceBuffer->setAppendWindowStart(start);
222 
223     // 4. Update the attribute to the new value.
224     m_appendWindowStart = start;
225 }
226 
appendWindowEnd() const227 double SourceBuffer::appendWindowEnd() const
228 {
229     return m_appendWindowEnd;
230 }
231 
setAppendWindowEnd(double end,ExceptionState & exceptionState)232 void SourceBuffer::setAppendWindowEnd(double end, ExceptionState& exceptionState)
233 {
234     // Section 3.1 appendWindowEnd attribute setter steps.
235     // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-SourceBuffer-appendWindowEnd
236     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
237     //    InvalidStateError exception and abort these steps.
238     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
239     if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
240         return;
241 
242     // 3. If the new value equals NaN, then throw an InvalidAccessError and abort these steps.
243     if (std::isnan(end)) {
244         exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::notAFiniteNumber(end));
245         return;
246     }
247     // 4. If the new value is less than or equal to appendWindowStart then throw an InvalidAccessError
248     //    exception and abort these steps.
249     if (end <= m_appendWindowStart) {
250         exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexExceedsMinimumBound("value", end, m_appendWindowStart));
251         return;
252     }
253 
254     m_webSourceBuffer->setAppendWindowEnd(end);
255 
256     // 5. Update the attribute to the new value.
257     m_appendWindowEnd = end;
258 }
259 
appendBuffer(PassRefPtr<ArrayBuffer> data,ExceptionState & exceptionState)260 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionState& exceptionState)
261 {
262     // Section 3.2 appendBuffer()
263     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
264     appendBufferInternal(static_cast<const unsigned char*>(data->data()), data->byteLength(), exceptionState);
265 }
266 
appendBuffer(PassRefPtr<ArrayBufferView> data,ExceptionState & exceptionState)267 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState)
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     appendBufferInternal(static_cast<const unsigned char*>(data->baseAddress()), data->byteLength(), exceptionState);
272 }
273 
appendStream(PassRefPtrWillBeRawPtr<Stream> stream,ExceptionState & exceptionState)274 void SourceBuffer::appendStream(PassRefPtrWillBeRawPtr<Stream> stream, ExceptionState& exceptionState)
275 {
276     m_streamMaxSizeValid = false;
277     appendStreamInternal(stream, exceptionState);
278 }
279 
appendStream(PassRefPtrWillBeRawPtr<Stream> stream,unsigned long long maxSize,ExceptionState & exceptionState)280 void SourceBuffer::appendStream(PassRefPtrWillBeRawPtr<Stream> stream, unsigned long long maxSize, ExceptionState& exceptionState)
281 {
282     m_streamMaxSizeValid = maxSize > 0;
283     if (m_streamMaxSizeValid)
284         m_streamMaxSize = maxSize;
285     appendStreamInternal(stream, exceptionState);
286 }
287 
abort(ExceptionState & exceptionState)288 void SourceBuffer::abort(ExceptionState& exceptionState)
289 {
290     // Section 3.2 abort() method steps.
291     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
292     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
293     //    then throw an InvalidStateError exception and abort these steps.
294     // 2. If the readyState attribute of the parent media source is not in the "open" state
295     //    then throw an InvalidStateError exception and abort these steps.
296     if (isRemoved()) {
297         exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
298         return;
299     }
300     if (!m_source->isOpen()) {
301         exceptionState.throwDOMException(InvalidStateError, "The parent media source's readyState is not 'open'.");
302         return;
303     }
304 
305     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
306     abortIfUpdating();
307 
308     // 4. Run the reset parser state algorithm.
309     m_webSourceBuffer->abort();
310 
311     // 5. Set appendWindowStart to 0.
312     setAppendWindowStart(0, exceptionState);
313 
314     // 6. Set appendWindowEnd to positive Infinity.
315     setAppendWindowEnd(std::numeric_limits<double>::infinity(), exceptionState);
316 }
317 
remove(double start,double end,ExceptionState & exceptionState)318 void SourceBuffer::remove(double start, double end, ExceptionState& exceptionState)
319 {
320     // Section 3.2 remove() method steps.
321     // 1. If start is negative or greater than duration, then throw an InvalidAccessError exception and abort these steps.
322     // 2. If end is less than or equal to start, then throw an InvalidAccessError exception and abort these steps.
323 
324     if (start < 0 || (m_source && (std::isnan(m_source->duration()) || start > m_source->duration()))) {
325         exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexOutsideRange("start", start, 0.0, ExceptionMessages::ExclusiveBound, !m_source || std::isnan(m_source->duration()) ? 0 : m_source->duration(), ExceptionMessages::ExclusiveBound));
326         return;
327     }
328     if (end <= start) {
329         exceptionState.throwDOMException(InvalidAccessError, "The end value provided (" + String::number(end) + ") must be greater than the start value provided (" + String::number(start) + ").");
330         return;
331     }
332 
333     // 3. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
334     //    InvalidStateError exception and abort these steps.
335     // 4. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
336     if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
337         return;
338 
339     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::remove", this);
340 
341     // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
342     // 5.1. Set the readyState attribute of the parent media source to "open"
343     // 5.2. Queue a task to fire a simple event named sourceopen at the parent media source .
344     m_source->openIfInEndedState();
345 
346     // 6. Set the updating attribute to true.
347     m_updating = true;
348 
349     // 7. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
350     scheduleEvent(EventTypeNames::updatestart);
351 
352     // 8. Return control to the caller and run the rest of the steps asynchronously.
353     m_pendingRemoveStart = start;
354     m_pendingRemoveEnd = end;
355     m_removeAsyncPartRunner.runAsync();
356 }
357 
abortIfUpdating()358 void SourceBuffer::abortIfUpdating()
359 {
360     // Section 3.2 abort() method step 3 substeps.
361     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
362 
363     if (!m_updating)
364         return;
365 
366     const char* traceEventName = 0;
367     if (!m_pendingAppendData.isEmpty()) {
368         traceEventName = "SourceBuffer::appendBuffer";
369     } else if (m_stream) {
370         traceEventName = "SourceBuffer::appendStream";
371     } else if (m_pendingRemoveStart != -1) {
372         traceEventName = "SourceBuffer::remove";
373     } else {
374         ASSERT_NOT_REACHED();
375     }
376 
377     // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
378     m_appendBufferAsyncPartRunner.stop();
379     m_pendingAppendData.clear();
380     m_pendingAppendDataOffset = 0;
381 
382     m_removeAsyncPartRunner.stop();
383     m_pendingRemoveStart = -1;
384     m_pendingRemoveEnd = -1;
385 
386     m_appendStreamAsyncPartRunner.stop();
387     clearAppendStreamState();
388 
389     // 3.2. Set the updating attribute to false.
390     m_updating = false;
391 
392     // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
393     scheduleEvent(EventTypeNames::abort);
394 
395     // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
396     scheduleEvent(EventTypeNames::updateend);
397 
398     TRACE_EVENT_ASYNC_END0("media", traceEventName, this);
399 }
400 
removedFromMediaSource()401 void SourceBuffer::removedFromMediaSource()
402 {
403     if (isRemoved())
404         return;
405 
406     abortIfUpdating();
407 
408     m_webSourceBuffer->removedFromMediaSource();
409     m_webSourceBuffer.clear();
410     m_source = nullptr;
411     m_asyncEventQueue = 0;
412 }
413 
hasPendingActivity() const414 bool SourceBuffer::hasPendingActivity() const
415 {
416     return m_source;
417 }
418 
suspend()419 void SourceBuffer::suspend()
420 {
421     m_appendBufferAsyncPartRunner.suspend();
422     m_removeAsyncPartRunner.suspend();
423     m_appendStreamAsyncPartRunner.suspend();
424 }
425 
resume()426 void SourceBuffer::resume()
427 {
428     m_appendBufferAsyncPartRunner.resume();
429     m_removeAsyncPartRunner.resume();
430     m_appendStreamAsyncPartRunner.resume();
431 }
432 
stop()433 void SourceBuffer::stop()
434 {
435     m_appendBufferAsyncPartRunner.stop();
436     m_removeAsyncPartRunner.stop();
437     m_appendStreamAsyncPartRunner.stop();
438 }
439 
executionContext() const440 ExecutionContext* SourceBuffer::executionContext() const
441 {
442     return ActiveDOMObject::executionContext();
443 }
444 
interfaceName() const445 const AtomicString& SourceBuffer::interfaceName() const
446 {
447     return EventTargetNames::SourceBuffer;
448 }
449 
isRemoved() const450 bool SourceBuffer::isRemoved() const
451 {
452     return !m_source;
453 }
454 
scheduleEvent(const AtomicString & eventName)455 void SourceBuffer::scheduleEvent(const AtomicString& eventName)
456 {
457     ASSERT(m_asyncEventQueue);
458 
459     RefPtrWillBeRawPtr<Event> event = Event::create(eventName);
460     event->setTarget(this);
461 
462     m_asyncEventQueue->enqueueEvent(event.release());
463 }
464 
appendBufferInternal(const unsigned char * data,unsigned size,ExceptionState & exceptionState)465 void SourceBuffer::appendBufferInternal(const unsigned char* data, unsigned size, ExceptionState& exceptionState)
466 {
467     // Section 3.2 appendBuffer()
468     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
469 
470     // 1. Run the prepare append algorithm.
471     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-prepare-append
472     //  1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an InvalidStateError exception and abort these steps.
473     //  2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
474     if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
475         return;
476 
477     TRACE_EVENT_ASYNC_BEGIN1("media", "SourceBuffer::appendBuffer", this, "size", size);
478 
479     //  3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
480     m_source->openIfInEndedState();
481 
482     //  Steps 4-5 - end "prepare append" algorithm.
483 
484     // 2. Add data to the end of the input buffer.
485     ASSERT(data || size == 0);
486     if (data)
487         m_pendingAppendData.append(data, size);
488     m_pendingAppendDataOffset = 0;
489 
490     // 3. Set the updating attribute to true.
491     m_updating = true;
492 
493     // 4. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
494     scheduleEvent(EventTypeNames::updatestart);
495 
496     // 5. Asynchronously run the buffer append algorithm.
497     m_appendBufferAsyncPartRunner.runAsync();
498 
499     TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "initialDelay");
500 }
501 
appendBufferAsyncPart()502 void SourceBuffer::appendBufferAsyncPart()
503 {
504     ASSERT(m_updating);
505 
506     // Section 3.5.4 Buffer Append Algorithm
507     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
508 
509     // 1. Run the segment parser loop algorithm.
510     // Step 2 doesn't apply since we run Step 1 synchronously here.
511     ASSERT(m_pendingAppendData.size() >= m_pendingAppendDataOffset);
512     size_t appendSize = m_pendingAppendData.size() - m_pendingAppendDataOffset;
513 
514     // Impose an arbitrary max size for a single append() call so that an append
515     // doesn't block the renderer event loop very long. This value was selected
516     // by looking at YouTube SourceBuffer usage across a variety of bitrates.
517     // This value allows relatively large appends while keeping append() call
518     // duration in the  ~5-15ms range.
519     const size_t MaxAppendSize = 128 * 1024;
520     if (appendSize > MaxAppendSize)
521         appendSize = MaxAppendSize;
522 
523     TRACE_EVENT_ASYNC_STEP_INTO1("media", "SourceBuffer::appendBuffer", this, "appending", "appendSize", static_cast<unsigned>(appendSize));
524 
525     // |zero| is used for 0 byte appends so we always have a valid pointer.
526     // We need to convey all appends, even 0 byte ones to |m_webSourceBuffer|
527     // so that it can clear its end of stream state if necessary.
528     unsigned char zero = 0;
529     unsigned char* appendData = &zero;
530     if (appendSize)
531         appendData = m_pendingAppendData.data() + m_pendingAppendDataOffset;
532 
533     m_webSourceBuffer->append(appendData, appendSize, &m_timestampOffset);
534 
535     m_pendingAppendDataOffset += appendSize;
536 
537     if (m_pendingAppendDataOffset < m_pendingAppendData.size()) {
538         m_appendBufferAsyncPartRunner.runAsync();
539         TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "nextPieceDelay");
540         return;
541     }
542 
543     // 3. Set the updating attribute to false.
544     m_updating = false;
545     m_pendingAppendData.clear();
546     m_pendingAppendDataOffset = 0;
547 
548     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
549     scheduleEvent(EventTypeNames::update);
550 
551     // 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
552     scheduleEvent(EventTypeNames::updateend);
553     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendBuffer", this);
554 }
555 
removeAsyncPart()556 void SourceBuffer::removeAsyncPart()
557 {
558     ASSERT(m_updating);
559     ASSERT(m_pendingRemoveStart >= 0);
560     ASSERT(m_pendingRemoveStart < m_pendingRemoveEnd);
561 
562     // Section 3.2 remove() method steps
563     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-remove-void-double-start-double-end
564 
565     // 9. Run the coded frame removal algorithm with start and end as the start and end of the removal range.
566     m_webSourceBuffer->remove(m_pendingRemoveStart, m_pendingRemoveEnd);
567 
568     // 10. Set the updating attribute to false.
569     m_updating = false;
570     m_pendingRemoveStart = -1;
571     m_pendingRemoveEnd = -1;
572 
573     // 11. Queue a task to fire a simple event named update at this SourceBuffer object.
574     scheduleEvent(EventTypeNames::update);
575 
576     // 12. Queue a task to fire a simple event named updateend at this SourceBuffer object.
577     scheduleEvent(EventTypeNames::updateend);
578 }
579 
appendStreamInternal(PassRefPtrWillBeRawPtr<Stream> stream,ExceptionState & exceptionState)580 void SourceBuffer::appendStreamInternal(PassRefPtrWillBeRawPtr<Stream> stream, ExceptionState& exceptionState)
581 {
582     // Section 3.2 appendStream()
583     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendStream-void-Stream-stream-unsigned-long-long-maxSize
584     // (0. If the stream has been neutered, then throw an InvalidAccessError exception and abort these steps.)
585     if (stream->isNeutered()) {
586         exceptionState.throwDOMException(InvalidAccessError, "The stream provided has been neutered.");
587         return;
588     }
589 
590     // 1. Run the prepare append algorithm.
591     //  Section 3.5.4 Prepare Append Algorithm.
592     //  https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-prepare-append
593     //  1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an InvalidStateError exception and abort these steps.
594     //  2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
595     if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
596         return;
597 
598     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendStream", this);
599 
600     //  3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
601     m_source->openIfInEndedState();
602 
603     // Steps 4-5 of the prepare append algorithm are handled by m_webSourceBuffer.
604 
605     // 2. Set the updating attribute to true.
606     m_updating = true;
607 
608     // 3. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
609     scheduleEvent(EventTypeNames::updatestart);
610 
611     // 4. Asynchronously run the stream append loop algorithm with stream and maxSize.
612 
613     stream->neuter();
614     m_loader = adoptPtr(new FileReaderLoader(FileReaderLoader::ReadByClient, this));
615     m_stream = stream;
616     m_appendStreamAsyncPartRunner.runAsync();
617 }
618 
appendStreamAsyncPart()619 void SourceBuffer::appendStreamAsyncPart()
620 {
621     ASSERT(m_updating);
622     ASSERT(m_loader);
623     ASSERT(m_stream);
624 
625     // Section 3.5.6 Stream Append Loop
626     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-stream-append-loop
627 
628     // 1. If maxSize is set, then let bytesLeft equal maxSize.
629     // 2. Loop Top: If maxSize is set and bytesLeft equals 0, then jump to the loop done step below.
630     if (m_streamMaxSizeValid && !m_streamMaxSize) {
631         appendStreamDone(true);
632         return;
633     }
634 
635     // Steps 3-11 are handled by m_loader.
636     // Note: Passing 0 here signals that maxSize was not set. (i.e. Read all the data in the stream).
637     m_loader->start(executionContext(), *m_stream, m_streamMaxSizeValid ? m_streamMaxSize : 0);
638 }
639 
appendStreamDone(bool success)640 void SourceBuffer::appendStreamDone(bool success)
641 {
642     ASSERT(m_updating);
643     ASSERT(m_loader);
644     ASSERT(m_stream);
645 
646     clearAppendStreamState();
647 
648     if (!success) {
649         // Section 3.5.3 Append Error Algorithm
650         // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-append-error
651         //
652         // 1. Run the reset parser state algorithm. (Handled by caller)
653         // 2. Set the updating attribute to false.
654         m_updating = false;
655 
656         // 3. Queue a task to fire a simple event named error at this SourceBuffer object.
657         scheduleEvent(EventTypeNames::error);
658 
659         // 4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
660         scheduleEvent(EventTypeNames::updateend);
661         TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
662         return;
663     }
664 
665     // Section 3.5.6 Stream Append Loop
666     // Steps 1-11 are handled by appendStreamAsyncPart(), |m_loader|, and |m_webSourceBuffer|.
667     // 12. Loop Done: Set the updating attribute to false.
668     m_updating = false;
669 
670     // 13. Queue a task to fire a simple event named update at this SourceBuffer object.
671     scheduleEvent(EventTypeNames::update);
672 
673     // 14. Queue a task to fire a simple event named updateend at this SourceBuffer object.
674     scheduleEvent(EventTypeNames::updateend);
675     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
676 }
677 
clearAppendStreamState()678 void SourceBuffer::clearAppendStreamState()
679 {
680     m_streamMaxSizeValid = false;
681     m_streamMaxSize = 0;
682     m_loader.clear();
683     m_stream = nullptr;
684 }
685 
didStartLoading()686 void SourceBuffer::didStartLoading()
687 {
688     WTF_LOG(Media, "SourceBuffer::didStartLoading() %p", this);
689 }
690 
didReceiveDataForClient(const char * data,unsigned dataLength)691 void SourceBuffer::didReceiveDataForClient(const char* data, unsigned dataLength)
692 {
693     WTF_LOG(Media, "SourceBuffer::didReceiveDataForClient(%d) %p", dataLength, this);
694     ASSERT(m_updating);
695     ASSERT(m_loader);
696     m_webSourceBuffer->append(reinterpret_cast<const unsigned char*>(data), dataLength, &m_timestampOffset);
697 }
698 
didFinishLoading()699 void SourceBuffer::didFinishLoading()
700 {
701     WTF_LOG(Media, "SourceBuffer::didFinishLoading() %p", this);
702     appendStreamDone(true);
703 }
704 
didFail(FileError::ErrorCode errorCode)705 void SourceBuffer::didFail(FileError::ErrorCode errorCode)
706 {
707     WTF_LOG(Media, "SourceBuffer::didFail(%d) %p", errorCode, this);
708     appendStreamDone(false);
709 }
710 
trace(Visitor * visitor)711 void SourceBuffer::trace(Visitor* visitor)
712 {
713     visitor->trace(m_source);
714     visitor->trace(m_stream);
715     EventTargetWithInlineData::trace(visitor);
716 }
717 
718 } // namespace WebCore
719