Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / mediasource / SourceBuffer.cpp
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "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
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
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_mode(segmentsKeyword())
69     , m_updating(false)
70     , m_timestampOffset(0)
71     , m_appendWindowStart(0)
72     , m_appendWindowEnd(std::numeric_limits<double>::infinity())
73     , m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart)
74     , m_pendingRemoveStart(-1)
75     , m_pendingRemoveEnd(-1)
76     , m_removeAsyncPartRunner(this, &SourceBuffer::removeAsyncPart)
77     , m_streamMaxSizeValid(false)
78     , m_streamMaxSize(0)
79     , m_appendStreamAsyncPartRunner(this, &SourceBuffer::appendStreamAsyncPart)
80 {
81     ASSERT(m_webSourceBuffer);
82     ASSERT(m_source);
83     ScriptWrappable::init(this);
84 }
85
86 SourceBuffer::~SourceBuffer()
87 {
88     ASSERT(isRemoved());
89     ASSERT(!m_loader);
90     ASSERT(!m_stream);
91 }
92
93 const AtomicString& SourceBuffer::segmentsKeyword()
94 {
95     DEFINE_STATIC_LOCAL(const AtomicString, segments, ("segments", AtomicString::ConstructFromLiteral));
96     return segments;
97 }
98
99 const AtomicString& SourceBuffer::sequenceKeyword()
100 {
101     DEFINE_STATIC_LOCAL(const AtomicString, sequence, ("sequence", AtomicString::ConstructFromLiteral));
102     return sequence;
103 }
104
105 void SourceBuffer::setMode(const AtomicString& newMode, ExceptionState& exceptionState)
106 {
107     // Section 3.1 On setting mode attribute steps.
108     // 1. Let new mode equal the new value being assigned to this attribute.
109     // 2. If new mode does not equal "segments" or "sequence", then throw an INVALID_ACCESS_ERR exception and abort
110     //    these steps.
111     if (newMode != segmentsKeyword() && newMode != sequenceKeyword()) {
112         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
113         return;
114     }
115
116     // 3. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw
117     //    an INVALID_STATE_ERR exception and abort these steps.
118     // 4. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
119     if (isRemoved() || m_updating) {
120         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
121         return;
122     }
123
124     // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
125     // 5.1 Set the readyState attribute of the parent media source to "open"
126     // 5.2 Queue a task to fire a simple event named sourceopen at the parent media source.
127     m_source->openIfInEndedState();
128
129     // 6. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
130     // 7. If the new mode equals "sequence", then set the group start timestamp to the highest presentation end timestamp.
131     WebSourceBuffer::AppendMode appendMode = WebSourceBuffer::AppendModeSegments;
132     if (newMode == sequenceKeyword())
133         appendMode = WebSourceBuffer::AppendModeSequence;
134     if (!m_webSourceBuffer->setMode(appendMode)) {
135         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
136         return;
137     }
138
139     // 8. Update the attribute to new mode.
140     m_mode = newMode;
141 }
142
143 PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionState& exceptionState) const
144 {
145     // Section 3.1 buffered attribute steps.
146     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
147     //    InvalidStateError exception and abort these steps.
148     if (isRemoved()) {
149         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
150         return 0;
151     }
152
153     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
154     return TimeRanges::create(m_webSourceBuffer->buffered());
155 }
156
157 double SourceBuffer::timestampOffset() const
158 {
159     return m_timestampOffset;
160 }
161
162 void SourceBuffer::setTimestampOffset(double offset, ExceptionState& exceptionState)
163 {
164     // Section 3.1 timestampOffset attribute setter steps.
165     // 1. Let new timestamp offset equal the new value being assigned to this attribute.
166     // 2. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw an
167     //    InvalidStateError exception and abort these steps.
168     // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
169     if (isRemoved() || m_updating) {
170         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
171         return;
172     }
173
174     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
175     // 4.1 Set the readyState attribute of the parent media source to "open"
176     // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
177     m_source->openIfInEndedState();
178
179     // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
180     // 6. If the mode attribute equals "sequence", then set the group start timestamp to new timestamp offset.
181     if (!m_webSourceBuffer->setTimestampOffset(offset)) {
182         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
183         return;
184     }
185
186     // 7. Update the attribute to new timestamp offset.
187     m_timestampOffset = offset;
188 }
189
190 double SourceBuffer::appendWindowStart() const
191 {
192     return m_appendWindowStart;
193 }
194
195 void SourceBuffer::setAppendWindowStart(double start, ExceptionState& exceptionState)
196 {
197     // Enforce throwing an exception on restricted double values.
198     if (std::isnan(start)
199         || start == std::numeric_limits<double>::infinity()
200         || start == -std::numeric_limits<double>::infinity()) {
201         exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
202         return;
203     }
204
205     // Section 3.1 appendWindowStart attribute setter steps.
206     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
207     //    InvalidStateError exception and abort these steps.
208     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
209     if (isRemoved() || m_updating) {
210         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
211         return;
212     }
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.throwUninformativeAndGenericDOMException(InvalidAccessError);
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
227 double SourceBuffer::appendWindowEnd() const
228 {
229     return m_appendWindowEnd;
230 }
231
232 void SourceBuffer::setAppendWindowEnd(double end, ExceptionState& exceptionState)
233 {
234     // Section 3.1 appendWindowEnd attribute setter steps.
235     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
236     //    InvalidStateError exception and abort these steps.
237     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
238     if (isRemoved() || m_updating) {
239         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
240         return;
241     }
242
243     // 3. If the new value equals NaN, then throw an InvalidAccessError and abort these steps.
244     // 4. If the new value is less than or equal to appendWindowStart then throw an InvalidAccessError
245     //    exception and abort these steps.
246     if (std::isnan(end) || end <= m_appendWindowStart) {
247         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
248         return;
249     }
250
251     m_webSourceBuffer->setAppendWindowEnd(end);
252
253     // 5. Update the attribute to the new value.
254     m_appendWindowEnd = end;
255 }
256
257 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionState& exceptionState)
258 {
259     // Section 3.2 appendBuffer()
260     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
261     // 1. If data is null then throw an InvalidAccessError exception and abort these steps.
262     if (!data) {
263         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
264         return;
265     }
266
267     appendBufferInternal(static_cast<const unsigned char*>(data->data()), data->byteLength(), exceptionState);
268 }
269
270 void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState)
271 {
272     // Section 3.2 appendBuffer()
273     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
274     // 1. If data is null then throw an InvalidAccessError exception and abort these steps.
275     if (!data) {
276         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
277         return;
278     }
279
280     appendBufferInternal(static_cast<const unsigned char*>(data->baseAddress()), data->byteLength(), exceptionState);
281 }
282
283 void SourceBuffer::appendStream(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
284 {
285     m_streamMaxSizeValid = false;
286     appendStreamInternal(stream, exceptionState);
287 }
288
289 void SourceBuffer::appendStream(PassRefPtr<Stream> stream, unsigned long long maxSize, ExceptionState& exceptionState)
290 {
291     m_streamMaxSizeValid = maxSize > 0;
292     if (m_streamMaxSizeValid)
293         m_streamMaxSize = maxSize;
294     appendStreamInternal(stream, exceptionState);
295 }
296
297 void SourceBuffer::abort(ExceptionState& exceptionState)
298 {
299     // Section 3.2 abort() method steps.
300     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
301     // 1. If this object has been removed from the sourceBuffers attribute of the parent media source
302     //    then throw an InvalidStateError exception and abort these steps.
303     // 2. If the readyState attribute of the parent media source is not in the "open" state
304     //    then throw an InvalidStateError exception and abort these steps.
305     if (isRemoved() || !m_source->isOpen()) {
306         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
307         return;
308     }
309
310     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
311     abortIfUpdating();
312
313     // 4. Run the reset parser state algorithm.
314     m_webSourceBuffer->abort();
315
316     // 5. Set appendWindowStart to 0.
317     setAppendWindowStart(0, exceptionState);
318
319     // 6. Set appendWindowEnd to positive Infinity.
320     setAppendWindowEnd(std::numeric_limits<double>::infinity(), exceptionState);
321 }
322
323 void SourceBuffer::remove(double start, double end, ExceptionState& exceptionState)
324 {
325     // Section 3.2 remove() method steps.
326     // 1. If start is negative or greater than duration, then throw an InvalidAccessError exception and abort these steps.
327     // 2. If end is less than or equal to start, then throw an InvalidAccessError exception and abort these steps.
328     if (start < 0 || (m_source && (std::isnan(m_source->duration()) || start > m_source->duration())) || end <= start) {
329         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
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 (isRemoved() || m_updating) {
337         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
338         return;
339     }
340
341     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::remove", this);
342
343     // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
344     // 5.1. Set the readyState attribute of the parent media source to "open"
345     // 5.2. Queue a task to fire a simple event named sourceopen at the parent media source .
346     m_source->openIfInEndedState();
347
348     // 6. Set the updating attribute to true.
349     m_updating = true;
350
351     // 7. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
352     scheduleEvent(EventTypeNames::updatestart);
353
354     // 8. Return control to the caller and run the rest of the steps asynchronously.
355     m_pendingRemoveStart = start;
356     m_pendingRemoveEnd = end;
357     m_removeAsyncPartRunner.runAsync();
358 }
359
360 void SourceBuffer::abortIfUpdating()
361 {
362     // Section 3.2 abort() method step 3 substeps.
363     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-abort-void
364
365     if (!m_updating)
366         return;
367
368     const char* traceEventName = 0;
369     if (!m_pendingAppendData.isEmpty()) {
370         traceEventName = "SourceBuffer::appendBuffer";
371     } else if (m_stream) {
372         traceEventName = "SourceBuffer::appendStream";
373     } else if (m_pendingRemoveStart != -1) {
374         traceEventName = "SourceBuffer::remove";
375     } else {
376         ASSERT_NOT_REACHED();
377     }
378
379     // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
380     m_appendBufferAsyncPartRunner.stop();
381     m_pendingAppendData.clear();
382
383     m_removeAsyncPartRunner.stop();
384     m_pendingRemoveStart = -1;
385     m_pendingRemoveEnd = -1;
386
387     m_appendStreamAsyncPartRunner.stop();
388     clearAppendStreamState();
389
390     // 3.2. Set the updating attribute to false.
391     m_updating = false;
392
393     // 3.3. Queue a task to fire a simple event named abort at this SourceBuffer object.
394     scheduleEvent(EventTypeNames::abort);
395
396     // 3.4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
397     scheduleEvent(EventTypeNames::updateend);
398
399     TRACE_EVENT_ASYNC_END0("media", traceEventName, this);
400 }
401
402 void SourceBuffer::removedFromMediaSource()
403 {
404     if (isRemoved())
405         return;
406
407     abortIfUpdating();
408
409     m_webSourceBuffer->removedFromMediaSource();
410     m_webSourceBuffer.clear();
411     m_source = 0;
412     m_asyncEventQueue = 0;
413 }
414
415 bool SourceBuffer::hasPendingActivity() const
416 {
417     return m_source;
418 }
419
420 void SourceBuffer::suspend()
421 {
422     m_appendBufferAsyncPartRunner.suspend();
423     m_removeAsyncPartRunner.suspend();
424     m_appendStreamAsyncPartRunner.suspend();
425 }
426
427 void SourceBuffer::resume()
428 {
429     m_appendBufferAsyncPartRunner.resume();
430     m_removeAsyncPartRunner.resume();
431     m_appendStreamAsyncPartRunner.resume();
432 }
433
434 void SourceBuffer::stop()
435 {
436     m_appendBufferAsyncPartRunner.stop();
437     m_removeAsyncPartRunner.stop();
438     m_appendStreamAsyncPartRunner.stop();
439 }
440
441 ExecutionContext* SourceBuffer::executionContext() const
442 {
443     return ActiveDOMObject::executionContext();
444 }
445
446 const AtomicString& SourceBuffer::interfaceName() const
447 {
448     return EventTargetNames::SourceBuffer;
449 }
450
451 bool SourceBuffer::isRemoved() const
452 {
453     return !m_source;
454 }
455
456 void SourceBuffer::scheduleEvent(const AtomicString& eventName)
457 {
458     ASSERT(m_asyncEventQueue);
459
460     RefPtr<Event> event = Event::create(eventName);
461     event->setTarget(this);
462
463     m_asyncEventQueue->enqueueEvent(event.release());
464 }
465
466 void SourceBuffer::appendBufferInternal(const unsigned char* data, unsigned size, ExceptionState& exceptionState)
467 {
468     // Section 3.2 appendBuffer()
469     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
470
471     // Step 1 is enforced by the caller.
472     // 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.
473     // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
474     if (isRemoved() || m_updating) {
475         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
476         return;
477     }
478
479     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendBuffer", this);
480
481     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
482     m_source->openIfInEndedState();
483
484     // Steps 5-6
485
486     // 7. Add data to the end of the input buffer.
487     m_pendingAppendData.append(data, size);
488
489     // 8. Set the updating attribute to true.
490     m_updating = true;
491
492     // 9. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
493     scheduleEvent(EventTypeNames::updatestart);
494
495     // 10. Asynchronously run the buffer append algorithm.
496     m_appendBufferAsyncPartRunner.runAsync();
497
498     TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "waiting");
499 }
500
501 void SourceBuffer::appendBufferAsyncPart()
502 {
503     ASSERT(m_updating);
504
505     TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "appending");
506
507     // Section 3.5.4 Buffer Append Algorithm
508     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
509
510     // 1. Run the segment parser loop algorithm.
511     // Step 2 doesn't apply since we run Step 1 synchronously here.
512     size_t appendSize = m_pendingAppendData.size();
513     if (!appendSize) {
514         // Resize buffer for 0 byte appends so we always have a valid pointer.
515         // We need to convey all appends, even 0 byte ones to |m_webSourceBuffer|
516         // so that it can clear its end of stream state if necessary.
517         m_pendingAppendData.resize(1);
518     }
519     m_webSourceBuffer->append(m_pendingAppendData.data(), appendSize);
520
521     // 3. Set the updating attribute to false.
522     m_updating = false;
523     m_pendingAppendData.clear();
524
525     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
526     scheduleEvent(EventTypeNames::update);
527
528     // 5. Queue a task to fire a simple event named updateend at this SourceBuffer object.
529     scheduleEvent(EventTypeNames::updateend);
530     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendBuffer", this);
531 }
532
533 void SourceBuffer::removeAsyncPart()
534 {
535     ASSERT(m_updating);
536     ASSERT(m_pendingRemoveStart >= 0);
537     ASSERT(m_pendingRemoveStart < m_pendingRemoveEnd);
538
539     // Section 3.2 remove() method steps
540     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-remove-void-double-start-double-end
541
542     // 9. Run the coded frame removal algorithm with start and end as the start and end of the removal range.
543     m_webSourceBuffer->remove(m_pendingRemoveStart, m_pendingRemoveEnd);
544
545     // 10. Set the updating attribute to false.
546     m_updating = false;
547     m_pendingRemoveStart = -1;
548     m_pendingRemoveEnd = -1;
549
550     // 11. Queue a task to fire a simple event named update at this SourceBuffer object.
551     scheduleEvent(EventTypeNames::update);
552
553     // 12. Queue a task to fire a simple event named updateend at this SourceBuffer object.
554     scheduleEvent(EventTypeNames::updateend);
555 }
556
557 void SourceBuffer::appendStreamInternal(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
558 {
559     // Section 3.2 appendStream()
560     // 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
561     // 1. If stream is null then throw an InvalidAccessError exception and abort these steps.
562     if (!stream || stream->isNeutered()) {
563         exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
564         return;
565     }
566
567     // 2. Run the prepare append algorithm.
568     //  Section 3.5.4 Prepare Append Algorithm.
569     //  https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-prepare-append
570     //  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.
571     //  2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
572     if (isRemoved() || m_updating) {
573         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
574         return;
575     }
576
577     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendStream", this);
578
579     //  3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
580     m_source->openIfInEndedState();
581
582     // Steps 4-5 of the prepare append algorithm are handled by m_webSourceBuffer.
583
584     // 3. Set the updating attribute to true.
585     m_updating = true;
586
587     // 4. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
588     scheduleEvent(EventTypeNames::updatestart);
589
590     // 5. Asynchronously run the stream append loop algorithm with stream and maxSize.
591
592     stream->neuter();
593     m_loader = adoptPtr(new FileReaderLoader(FileReaderLoader::ReadByClient, this));
594     m_stream = stream;
595     m_appendStreamAsyncPartRunner.runAsync();
596 }
597
598 void SourceBuffer::appendStreamAsyncPart()
599 {
600     ASSERT(m_updating);
601     ASSERT(m_loader);
602     ASSERT(m_stream);
603
604     // Section 3.5.6 Stream Append Loop
605     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-stream-append-loop
606
607     // 1. If maxSize is set, then let bytesLeft equal maxSize.
608     // 2. Loop Top: If maxSize is set and bytesLeft equals 0, then jump to the loop done step below.
609     if (m_streamMaxSizeValid && !m_streamMaxSize) {
610         appendStreamDone(true);
611         return;
612     }
613
614     // Steps 3-11 are handled by m_loader.
615     // Note: Passing 0 here signals that maxSize was not set. (i.e. Read all the data in the stream).
616     m_loader->start(executionContext(), *m_stream, m_streamMaxSizeValid ? m_streamMaxSize : 0);
617 }
618
619 void SourceBuffer::appendStreamDone(bool success)
620 {
621     ASSERT(m_updating);
622     ASSERT(m_loader);
623     ASSERT(m_stream);
624
625     clearAppendStreamState();
626
627     if (!success) {
628         // Section 3.5.3 Append Error Algorithm
629         // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-append-error
630         //
631         // 1. Run the reset parser state algorithm. (Handled by caller)
632         // 2. Set the updating attribute to false.
633         m_updating = false;
634
635         // 3. Queue a task to fire a simple event named error at this SourceBuffer object.
636         scheduleEvent(EventTypeNames::error);
637
638         // 4. Queue a task to fire a simple event named updateend at this SourceBuffer object.
639         scheduleEvent(EventTypeNames::updateend);
640         TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
641         return;
642     }
643
644     // Section 3.5.6 Stream Append Loop
645     // Steps 1-11 are handled by appendStreamAsyncPart(), |m_loader|, and |m_webSourceBuffer|.
646     // 12. Loop Done: Set the updating attribute to false.
647     m_updating = false;
648
649     // 13. Queue a task to fire a simple event named update at this SourceBuffer object.
650     scheduleEvent(EventTypeNames::update);
651
652     // 14. Queue a task to fire a simple event named updateend at this SourceBuffer object.
653     scheduleEvent(EventTypeNames::updateend);
654     TRACE_EVENT_ASYNC_END0("media", "SourceBuffer::appendStream", this);
655 }
656
657 void SourceBuffer::clearAppendStreamState()
658 {
659     m_streamMaxSizeValid = false;
660     m_streamMaxSize = 0;
661     m_loader.clear();
662     m_stream = 0;
663 }
664
665 void SourceBuffer::didStartLoading()
666 {
667     WTF_LOG(Media, "SourceBuffer::didStartLoading() %p", this);
668 }
669
670 void SourceBuffer::didReceiveDataForClient(const char* data, unsigned dataLength)
671 {
672     WTF_LOG(Media, "SourceBuffer::didReceiveDataForClient(%d) %p", dataLength, this);
673     ASSERT(m_updating);
674     ASSERT(m_loader);
675
676     m_webSourceBuffer->append(reinterpret_cast<const unsigned char*>(data), dataLength);
677 }
678
679 void SourceBuffer::didFinishLoading()
680 {
681     WTF_LOG(Media, "SourceBuffer::didFinishLoading() %p", this);
682     appendStreamDone(true);
683 }
684
685 void SourceBuffer::didFail(FileError::ErrorCode errorCode)
686 {
687     WTF_LOG(Media, "SourceBuffer::didFail(%d) %p", errorCode, this);
688     appendStreamDone(false);
689 }
690
691 } // namespace WebCore