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