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