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