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
52using blink::WebSourceBuffer;
53
54namespace WebCore {
55
56PassRefPtr<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
63SourceBuffer::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
85SourceBuffer::~SourceBuffer()
86{
87    ASSERT(isRemoved());
88    ASSERT(!m_loader);
89    ASSERT(!m_stream);
90}
91
92PassRefPtr<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
106double SourceBuffer::timestampOffset() const
107{
108    return m_timestampOffset;
109}
110
111void 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
141double SourceBuffer::appendWindowStart() const
142{
143    return m_appendWindowStart;
144}
145
146void 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
178double SourceBuffer::appendWindowEnd() const
179{
180    return m_appendWindowEnd;
181}
182
183void 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
208void 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
221void 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
234void SourceBuffer::appendStream(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
235{
236    m_streamMaxSizeValid = false;
237    appendStreamInternal(stream, exceptionState);
238}
239
240void 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
248void 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
274void 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
311void 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
353void 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
366bool SourceBuffer::hasPendingActivity() const
367{
368    return m_source;
369}
370
371void SourceBuffer::suspend()
372{
373    m_appendBufferAsyncPartRunner.suspend();
374    m_removeAsyncPartRunner.suspend();
375    m_appendStreamAsyncPartRunner.suspend();
376}
377
378void SourceBuffer::resume()
379{
380    m_appendBufferAsyncPartRunner.resume();
381    m_removeAsyncPartRunner.resume();
382    m_appendStreamAsyncPartRunner.resume();
383}
384
385void SourceBuffer::stop()
386{
387    m_appendBufferAsyncPartRunner.stop();
388    m_removeAsyncPartRunner.stop();
389    m_appendStreamAsyncPartRunner.stop();
390}
391
392ExecutionContext* SourceBuffer::executionContext() const
393{
394    return ActiveDOMObject::executionContext();
395}
396
397const AtomicString& SourceBuffer::interfaceName() const
398{
399    return EventTargetNames::SourceBuffer;
400}
401
402bool SourceBuffer::isRemoved() const
403{
404    return !m_source;
405}
406
407void 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
417void 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
452void 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
484void 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
508void 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
549void 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
570void 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
608void SourceBuffer::clearAppendStreamState()
609{
610    m_streamMaxSizeValid = false;
611    m_streamMaxSize = 0;
612    m_loader.clear();
613    m_stream = 0;
614}
615
616void SourceBuffer::didStartLoading()
617{
618    WTF_LOG(Media, "SourceBuffer::didStartLoading() %p", this);
619}
620
621void 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
630void SourceBuffer::didFinishLoading()
631{
632    WTF_LOG(Media, "SourceBuffer::didFinishLoading() %p", this);
633    appendStreamDone(true);
634}
635
636void 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