• 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/ExceptionState.h"
35 #include "core/dom/ExceptionCode.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/events/Event.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 
create(PassOwnPtr<WebSourceBuffer> webSourceBuffer,MediaSource * source,GenericEventQueue * asyncEventQueue)56 PassRefPtr<SourceBuffer> SourceBuffer::create(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
57 {
58     RefPtr<SourceBuffer> sourceBuffer(adoptRef(new SourceBuffer(webSourceBuffer, source, asyncEventQueue)));
59     sourceBuffer->suspendIfNeeded();
60     return sourceBuffer.release();
61 }
62 
SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer,MediaSource * source,GenericEventQueue * asyncEventQueue)63 SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
64     : ActiveDOMObject(source->executionContext())
65     , m_webSourceBuffer(webSourceBuffer)
66     , m_source(source)
67     , m_asyncEventQueue(asyncEventQueue)
68     , m_updating(false)
69     , m_timestampOffset(0)
70     , m_appendWindowStart(0)
71     , m_appendWindowEnd(std::numeric_limits<double>::infinity())
72     , m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart)
73     , m_pendingRemoveStart(-1)
74     , m_pendingRemoveEnd(-1)
75     , m_removeAsyncPartRunner(this, &SourceBuffer::removeAsyncPart)
76     , m_streamMaxSizeValid(false)
77     , m_streamMaxSize(0)
78     , m_appendStreamAsyncPartRunner(this, &SourceBuffer::appendStreamAsyncPart)
79 {
80     ASSERT(m_webSourceBuffer);
81     ASSERT(m_source);
82     ScriptWrappable::init(this);
83 }
84 
~SourceBuffer()85 SourceBuffer::~SourceBuffer()
86 {
87     ASSERT(isRemoved());
88     ASSERT(!m_loader);
89     ASSERT(!m_stream);
90 }
91 
buffered(ExceptionState & exceptionState) const92 PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionState& exceptionState) const
93 {
94     // Section 3.1 buffered attribute steps.
95     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
96     //    InvalidStateError exception and abort these steps.
97     if (isRemoved()) {
98         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
99         return 0;
100     }
101 
102     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
103     return TimeRanges::create(m_webSourceBuffer->buffered());
104 }
105 
timestampOffset() const106 double SourceBuffer::timestampOffset() const
107 {
108     return m_timestampOffset;
109 }
110 
setTimestampOffset(double offset,ExceptionState & exceptionState)111 void SourceBuffer::setTimestampOffset(double offset, ExceptionState& exceptionState)
112 {
113     // Section 3.1 timestampOffset attribute setter steps.
114     // 1. Let new timestamp offset equal the new value being assigned to this attribute.
115     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw an
116     //    InvalidStateError exception and abort these steps.
117     // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
118     if (isRemoved() || m_updating) {
119         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
120         return;
121     }
122 
123     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
124     // 4.1 Set the readyState attribute of the parent media source to "open"
125     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
126     m_source->openIfInEndedState();
127 
128     // 5. If this object is waiting for the end of a media segment to be appended, then throw an InvalidStateError
129     // and abort these steps.
130     //
131     // FIXME: Add step 6 text when mode attribute is implemented.
132     if (!m_webSourceBuffer->setTimestampOffset(offset)) {
133         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
134         return;
135     }
136 
137     // 7. Update the attribute to new timestamp offset.
138     m_timestampOffset = offset;
139 }
140 
appendWindowStart() const141 double SourceBuffer::appendWindowStart() const
142 {
143     return m_appendWindowStart;
144 }
145 
setAppendWindowStart(double start,ExceptionState & exceptionState)146 void SourceBuffer::setAppendWindowStart(double start, ExceptionState& exceptionState)
147 {
148     // Enforce throwing an exception on restricted double values.
149     if (std::isnan(start)
150         || start == std::numeric_limits<double>::infinity()
151         || start == -std::numeric_limits<double>::infinity()) {
152         exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
153         return;
154     }
155 
156     // Section 3.1 appendWindowStart attribute setter steps.
157     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
158     //    InvalidStateError exception and abort these steps.
159     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
160     if (isRemoved() || m_updating) {
161         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
162         return;
163     }
164 
165     // 3. If the new value is less than 0 or greater than or equal to appendWindowEnd then throw an InvalidAccessError
166     //    exception and abort these steps.
167     if (start < 0 || start >= m_appendWindowEnd) {
168         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
169         return;
170     }
171 
172     m_webSourceBuffer->setAppendWindowStart(start);
173 
174     // 4. Update the attribute to the new value.
175     m_appendWindowStart = start;
176 }
177 
appendWindowEnd() const178 double SourceBuffer::appendWindowEnd() const
179 {
180     return m_appendWindowEnd;
181 }
182 
setAppendWindowEnd(double end,ExceptionState & exceptionState)183 void SourceBuffer::setAppendWindowEnd(double end, ExceptionState& exceptionState)
184 {
185     // Section 3.1 appendWindowEnd attribute setter steps.
186     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
187     //    InvalidStateError exception and abort these steps.
188     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
189     if (isRemoved() || m_updating) {
190         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
191         return;
192     }
193 
194     // 3. If the new value equals NaN, then throw an InvalidAccessError and abort these steps.
195     // 4. If the new value is less than or equal to appendWindowStart then throw an InvalidAccessError
196     //    exception and abort these steps.
197     if (std::isnan(end) || end <= m_appendWindowStart) {
198         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
199         return;
200     }
201 
202     m_webSourceBuffer->setAppendWindowEnd(end);
203 
204     // 5. Update the attribute to the new value.
205     m_appendWindowEnd = end;
206 }
207 
appendBuffer(PassRefPtr<ArrayBuffer> data,ExceptionState & exceptionState)208 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionState& exceptionState)
209 {
210     // Section 3.2 appendBuffer()
211     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
212     // 1. If data is null then throw an InvalidAccessError exception and abort these steps.
213     if (!data) {
214         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
215         return;
216     }
217 
218     appendBufferInternal(static_cast<const unsigned char*>(data->data()), data->byteLength(), exceptionState);
219 }
220 
appendBuffer(PassRefPtr<ArrayBufferView> data,ExceptionState & exceptionState)221 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState)
222 {
223     // Section 3.2 appendBuffer()
224     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
225     // 1. If data is null then throw an InvalidAccessError exception and abort these steps.
226     if (!data) {
227         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
228         return;
229     }
230 
231     appendBufferInternal(static_cast<const unsigned char*>(data->baseAddress()), data->byteLength(), exceptionState);
232 }
233 
appendStream(PassRefPtr<Stream> stream,ExceptionState & exceptionState)234 void SourceBuffer::appendStream(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
235 {
236     m_streamMaxSizeValid = false;
237     appendStreamInternal(stream, exceptionState);
238 }
239 
appendStream(PassRefPtr<Stream> stream,unsigned long long maxSize,ExceptionState & exceptionState)240 void SourceBuffer::appendStream(PassRefPtr<Stream> stream, unsigned long long maxSize, ExceptionState& exceptionState)
241 {
242     m_streamMaxSizeValid = maxSize > 0;
243     if (m_streamMaxSizeValid)
244         m_streamMaxSize = maxSize;
245     appendStreamInternal(stream, exceptionState);
246 }
247 
abort(ExceptionState & exceptionState)248 void SourceBuffer::abort(ExceptionState& exceptionState)
249 {
250     // Section 3.2 abort() method steps.
251     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
252     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
253     //    then throw an InvalidStateError exception and abort these steps.
254     // 2. If the readyState attribute of the parent media source is not in the "open" state
255     //    then throw an InvalidStateError exception and abort these steps.
256     if (isRemoved() || !m_source->isOpen()) {
257         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
258         return;
259     }
260 
261     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
262     abortIfUpdating();
263 
264     // 4. Run the reset parser state algorithm.
265     m_webSourceBuffer->abort();
266 
267     // 5. Set appendWindowStart to 0.
268     setAppendWindowStart(0, exceptionState);
269 
270     // 6. Set appendWindowEnd to positive Infinity.
271     setAppendWindowEnd(std::numeric_limits<double>::infinity(), exceptionState);
272 }
273 
remove(double start,double end,ExceptionState & exceptionState)274 void SourceBuffer::remove(double start, double end, ExceptionState& exceptionState)
275 {
276     // Section 3.2 remove() method steps.
277     // 1. If start is negative or greater than duration, then throw an InvalidAccessError exception and abort these steps.
278     // 2. If end is less than or equal to start, then throw an InvalidAccessError exception and abort these steps.
279     if (start < 0 || (m_source && (std::isnan(m_source->duration()) || start > m_source->duration())) || end <= start) {
280         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
281         return;
282     }
283 
284     // 3. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
285     //    InvalidStateError exception and abort these steps.
286     // 4. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
287     if (isRemoved() || m_updating) {
288         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
289         return;
290     }
291 
292     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::remove", this);
293 
294     // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
295     // 5.1. Set the readyState attribute of the parent media source to "open"
296     // 5.2. Queue a task to fire a simple event named sourceopen at the parent media source .
297     m_source->openIfInEndedState();
298 
299     // 6. Set the updating attribute to true.
300     m_updating = true;
301 
302     // 7. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
303     scheduleEvent(EventTypeNames::updatestart);
304 
305     // 8. Return control to the caller and run the rest of the steps asynchronously.
306     m_pendingRemoveStart = start;
307     m_pendingRemoveEnd = end;
308     m_removeAsyncPartRunner.runAsync();
309 }
310 
abortIfUpdating()311 void SourceBuffer::abortIfUpdating()
312 {
313     // Section 3.2 abort() method step 3 substeps.
314     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
315 
316     if (!m_updating)
317         return;
318 
319     const char* traceEventName = 0;
320     if (!m_pendingAppendData.isEmpty()) {
321         traceEventName = "SourceBuffer::appendBuffer";
322     } else if (m_stream) {
323         traceEventName = "SourceBuffer::appendStream";
324     } else if (m_pendingRemoveStart != -1) {
325         traceEventName = "SourceBuffer::remove";
326     } else {
327         ASSERT_NOT_REACHED();
328     }
329 
330     // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
331     m_appendBufferAsyncPartRunner.stop();
332     m_pendingAppendData.clear();
333 
334     m_removeAsyncPartRunner.stop();
335     m_pendingRemoveStart = -1;
336     m_pendingRemoveEnd = -1;
337 
338     m_appendStreamAsyncPartRunner.stop();
339     clearAppendStreamState();
340 
341     // 3.2. Set the updating attribute to false.
342     m_updating = false;
343 
344     // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
345     scheduleEvent(EventTypeNames::abort);
346 
347     // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
348     scheduleEvent(EventTypeNames::updateend);
349 
350     TRACE_EVENT_ASYNC_END0("media", traceEventName, this);
351 }
352 
removedFromMediaSource()353 void SourceBuffer::removedFromMediaSource()
354 {
355     if (isRemoved())
356         return;
357 
358     abortIfUpdating();
359 
360     m_webSourceBuffer->removedFromMediaSource();
361     m_webSourceBuffer.clear();
362     m_source = 0;
363     m_asyncEventQueue = 0;
364 }
365 
hasPendingActivity() const366 bool SourceBuffer::hasPendingActivity() const
367 {
368     return m_source;
369 }
370 
suspend()371 void SourceBuffer::suspend()
372 {
373     m_appendBufferAsyncPartRunner.suspend();
374     m_removeAsyncPartRunner.suspend();
375     m_appendStreamAsyncPartRunner.suspend();
376 }
377 
resume()378 void SourceBuffer::resume()
379 {
380     m_appendBufferAsyncPartRunner.resume();
381     m_removeAsyncPartRunner.resume();
382     m_appendStreamAsyncPartRunner.resume();
383 }
384 
stop()385 void SourceBuffer::stop()
386 {
387     m_appendBufferAsyncPartRunner.stop();
388     m_removeAsyncPartRunner.stop();
389     m_appendStreamAsyncPartRunner.stop();
390 }
391 
executionContext() const392 ExecutionContext* SourceBuffer::executionContext() const
393 {
394     return ActiveDOMObject::executionContext();
395 }
396 
interfaceName() const397 const AtomicString& SourceBuffer::interfaceName() const
398 {
399     return EventTargetNames::SourceBuffer;
400 }
401 
isRemoved() const402 bool SourceBuffer::isRemoved() const
403 {
404     return !m_source;
405 }
406 
scheduleEvent(const AtomicString & eventName)407 void SourceBuffer::scheduleEvent(const AtomicString& eventName)
408 {
409     ASSERT(m_asyncEventQueue);
410 
411     RefPtr<Event> event = Event::create(eventName);
412     event->setTarget(this);
413 
414     m_asyncEventQueue->enqueueEvent(event.release());
415 }
416 
appendBufferInternal(const unsigned char * data,unsigned size,ExceptionState & exceptionState)417 void SourceBuffer::appendBufferInternal(const unsigned char* data, unsigned size, ExceptionState& exceptionState)
418 {
419     // Section 3.2 appendBuffer()
420     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
421 
422     // Step 1 is enforced by the caller.
423     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an InvalidStateError exception and abort these steps.
424     // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
425     if (isRemoved() || m_updating) {
426         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
427         return;
428     }
429 
430     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendBuffer", this);
431 
432     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
433     m_source->openIfInEndedState();
434 
435     // Steps 5-6
436 
437     // 7. Add data to the end of the input buffer.
438     m_pendingAppendData.append(data, size);
439 
440     // 8. Set the updating attribute to true.
441     m_updating = true;
442 
443     // 9. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
444     scheduleEvent(EventTypeNames::updatestart);
445 
446     // 10. Asynchronously run the buffer append algorithm.
447     m_appendBufferAsyncPartRunner.runAsync();
448 
449     TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "waiting");
450 }
451 
appendBufferAsyncPart()452 void SourceBuffer::appendBufferAsyncPart()
453 {
454     ASSERT(m_updating);
455 
456     TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "appending");
457 
458     // Section 3.5.4 Buffer Append Algorithm
459     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
460 
461     // 1. Run the segment parser loop algorithm.
462     // Step 2 doesn't apply since we run Step 1 synchronously here.
463     size_t appendSize = m_pendingAppendData.size();
464     if (!appendSize) {
465         // Resize buffer for 0 byte appends so we always have a valid pointer.
466         // We need to convey all appends, even 0 byte ones to |m_webSourceBuffer|
467         // so that it can clear its end of stream state if necessary.
468         m_pendingAppendData.resize(1);
469     }
470     m_webSourceBuffer->append(m_pendingAppendData.data(), appendSize);
471 
472     // 3. Set the updating attribute to false.
473     m_updating = false;
474     m_pendingAppendData.clear();
475 
476     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
477     scheduleEvent(EventTypeNames::update);
478 
479     // 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
480     scheduleEvent(EventTypeNames::updateend);
481     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendBuffer", this);
482 }
483 
removeAsyncPart()484 void SourceBuffer::removeAsyncPart()
485 {
486     ASSERT(m_updating);
487     ASSERT(m_pendingRemoveStart >= 0);
488     ASSERT(m_pendingRemoveStart < m_pendingRemoveEnd);
489 
490     // Section 3.2 remove() method steps
491     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-remove-void-double-start-double-end
492 
493     // 9. Run the coded frame removal algorithm with start and end as the start and end of the removal range.
494     m_webSourceBuffer->remove(m_pendingRemoveStart, m_pendingRemoveEnd);
495 
496     // 10. Set the updating attribute to false.
497     m_updating = false;
498     m_pendingRemoveStart = -1;
499     m_pendingRemoveEnd = -1;
500 
501     // 11. Queue a task to fire a simple event named update at this SourceBuffer object.
502     scheduleEvent(EventTypeNames::update);
503 
504     // 12. Queue a task to fire a simple event named updateend at this SourceBuffer object.
505     scheduleEvent(EventTypeNames::updateend);
506 }
507 
appendStreamInternal(PassRefPtr<Stream> stream,ExceptionState & exceptionState)508 void SourceBuffer::appendStreamInternal(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
509 {
510     // Section 3.2 appendStream()
511     // 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
512     // 1. If stream is null then throw an InvalidAccessError exception and abort these steps.
513     if (!stream || stream->isNeutered()) {
514         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
515         return;
516     }
517 
518     // 2. Run the prepare append algorithm.
519     //  Section 3.5.4 Prepare Append Algorithm.
520     //  https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-prepare-append
521     //  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.
522     //  2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
523     if (isRemoved() || m_updating) {
524         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
525         return;
526     }
527 
528     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendStream", this);
529 
530     //  3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
531     m_source->openIfInEndedState();
532 
533     // Steps 4-5 of the prepare append algorithm are handled by m_webSourceBuffer.
534 
535     // 3. Set the updating attribute to true.
536     m_updating = true;
537 
538     // 4. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
539     scheduleEvent(EventTypeNames::updatestart);
540 
541     // 5. Asynchronously run the stream append loop algorithm with stream and maxSize.
542 
543     stream->neuter();
544     m_loader = adoptPtr(new FileReaderLoader(FileReaderLoader::ReadByClient, this));
545     m_stream = stream;
546     m_appendStreamAsyncPartRunner.runAsync();
547 }
548 
appendStreamAsyncPart()549 void SourceBuffer::appendStreamAsyncPart()
550 {
551     ASSERT(m_updating);
552     ASSERT(m_loader);
553     ASSERT(m_stream);
554 
555     // Section 3.5.6 Stream Append Loop
556     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-stream-append-loop
557 
558     // 1. If maxSize is set, then let bytesLeft equal maxSize.
559     // 2. Loop Top: If maxSize is set and bytesLeft equals 0, then jump to the loop done step below.
560     if (m_streamMaxSizeValid && !m_streamMaxSize) {
561         appendStreamDone(true);
562         return;
563     }
564 
565     // Steps 3-11 are handled by m_loader.
566     // Note: Passing 0 here signals that maxSize was not set. (i.e. Read all the data in the stream).
567     m_loader->start(executionContext(), *m_stream, m_streamMaxSizeValid ? m_streamMaxSize : 0);
568 }
569 
appendStreamDone(bool success)570 void SourceBuffer::appendStreamDone(bool success)
571 {
572     ASSERT(m_updating);
573     ASSERT(m_loader);
574     ASSERT(m_stream);
575 
576     clearAppendStreamState();
577 
578     if (!success) {
579         // Section 3.5.3 Append Error Algorithm
580         // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-append-error
581         //
582         // 1. Run the reset parser state algorithm. (Handled by caller)
583         // 2. Set the updating attribute to false.
584         m_updating = false;
585 
586         // 3. Queue a task to fire a simple event named error at this SourceBuffer object.
587         scheduleEvent(EventTypeNames::error);
588 
589         // 4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
590         scheduleEvent(EventTypeNames::updateend);
591         TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
592         return;
593     }
594 
595     // Section 3.5.6 Stream Append Loop
596     // Steps 1-11 are handled by appendStreamAsyncPart(), |m_loader|, and |m_webSourceBuffer|.
597     // 12. Loop Done: Set the updating attribute to false.
598     m_updating = false;
599 
600     // 13. Queue a task to fire a simple event named update at this SourceBuffer object.
601     scheduleEvent(EventTypeNames::update);
602 
603     // 14. Queue a task to fire a simple event named updateend at this SourceBuffer object.
604     scheduleEvent(EventTypeNames::updateend);
605     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
606 }
607 
clearAppendStreamState()608 void SourceBuffer::clearAppendStreamState()
609 {
610     m_streamMaxSizeValid = false;
611     m_streamMaxSize = 0;
612     m_loader.clear();
613     m_stream = 0;
614 }
615 
didStartLoading()616 void SourceBuffer::didStartLoading()
617 {
618     WTF_LOG(Media, "SourceBuffer::didStartLoading() %p", this);
619 }
620 
didReceiveDataForClient(const char * data,unsigned dataLength)621 void SourceBuffer::didReceiveDataForClient(const char* data, unsigned dataLength)
622 {
623     WTF_LOG(Media, "SourceBuffer::didReceiveDataForClient(%d) %p", dataLength, this);
624     ASSERT(m_updating);
625     ASSERT(m_loader);
626 
627     m_webSourceBuffer->append(reinterpret_cast<const unsigned char*>(data), dataLength);
628 }
629 
didFinishLoading()630 void SourceBuffer::didFinishLoading()
631 {
632     WTF_LOG(Media, "SourceBuffer::didFinishLoading() %p", this);
633     appendStreamDone(true);
634 }
635 
didFail(FileError::ErrorCode errorCode)636 void SourceBuffer::didFail(FileError::ErrorCode errorCode)
637 {
638     WTF_LOG(Media, "SourceBuffer::didFail(%d) %p", errorCode, this);
639     appendStreamDone(false);
640 }
641 
642 } // namespace WebCore
643