Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / mediasource / SourceBuffer.cpp
index 7eed238..2211b11 100644 (file)
@@ -31,6 +31,7 @@
 #include "config.h"
 #include "modules/mediasource/SourceBuffer.h"
 
+#include "bindings/v8/ExceptionMessages.h"
 #include "bindings/v8/ExceptionState.h"
 #include "core/dom/ExceptionCode.h"
 #include "core/dom/ExecutionContext.h"
@@ -53,11 +54,29 @@ using blink::WebSourceBuffer;
 
 namespace WebCore {
 
-PassRefPtr<SourceBuffer> SourceBuffer::create(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
+namespace {
+
+static bool throwExceptionIfRemovedOrUpdating(bool isRemoved, bool isUpdating, ExceptionState& exceptionState)
+{
+    if (isRemoved) {
+        exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
+        return true;
+    }
+    if (isUpdating) {
+        exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer is still processing an 'appendBuffer', 'appendStream', or 'remove' operation.");
+        return true;
+    }
+
+    return false;
+}
+
+} // namespace
+
+SourceBuffer* SourceBuffer::create(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
 {
-    RefPtr<SourceBuffer> sourceBuffer(adoptRef(new SourceBuffer(webSourceBuffer, source, asyncEventQueue)));
+    SourceBuffer* sourceBuffer(adoptRefCountedGarbageCollected(new SourceBuffer(webSourceBuffer, source, asyncEventQueue)));
     sourceBuffer->suspendIfNeeded();
-    return sourceBuffer.release();
+    return sourceBuffer;
 }
 
 SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSource* source, GenericEventQueue* asyncEventQueue)
@@ -70,6 +89,7 @@ SourceBuffer::SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer, MediaSou
     , m_timestampOffset(0)
     , m_appendWindowStart(0)
     , m_appendWindowEnd(std::numeric_limits<double>::infinity())
+    , m_pendingAppendDataOffset(0)
     , m_appendBufferAsyncPartRunner(this, &SourceBuffer::appendBufferAsyncPart)
     , m_pendingRemoveStart(-1)
     , m_pendingRemoveEnd(-1)
@@ -106,37 +126,28 @@ void SourceBuffer::setMode(const AtomicString& newMode, ExceptionState& exceptio
 {
     // Section 3.1 On setting mode attribute steps.
     // 1. Let new mode equal the new value being assigned to this attribute.
-    // 2. If new mode does not equal "segments" or "sequence", then throw an INVALID_ACCESS_ERR exception and abort
-    //    these steps.
-    if (newMode != segmentsKeyword() && newMode != sequenceKeyword()) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
-        return;
-    }
-
-    // 3. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw
+    // 2. If this object has been removed from the sourceBuffers attribute of the parent media source, then throw
     //    an INVALID_STATE_ERR exception and abort these steps.
-    // 4. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
-    if (isRemoved() || m_updating) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+    // 3. If the updating attribute equals true, then throw an INVALID_STATE_ERR exception and abort these steps.
+    if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
         return;
-    }
 
-    // 5. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
-    // 5.1 Set the readyState attribute of the parent media source to "open"
-    // 5.2 Queue a task to fire a simple event named sourceopen at the parent media source.
+    // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
+    // 4.1 Set the readyState attribute of the parent media source to "open"
+    // 4.2 Queue a task to fire a simple event named sourceopen at the parent media source.
     m_source->openIfInEndedState();
 
-    // 6. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
-    // 7. If the new mode equals "sequence", then set the group start timestamp to the highest presentation end timestamp.
+    // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
+    // 6. If the new mode equals "sequence", then set the group start timestamp to the highest presentation end timestamp.
     WebSourceBuffer::AppendMode appendMode = WebSourceBuffer::AppendModeSegments;
     if (newMode == sequenceKeyword())
         appendMode = WebSourceBuffer::AppendModeSequence;
     if (!m_webSourceBuffer->setMode(appendMode)) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+        exceptionState.throwDOMException(InvalidStateError, "The mode may not be set while the SourceBuffer's append state is 'PARSING_MEDIA_SEGMENT'.");
         return;
     }
 
-    // 8. Update the attribute to new mode.
+    // 7. Update the attribute to new mode.
     m_mode = newMode;
 }
 
@@ -146,8 +157,8 @@ PassRefPtr<TimeRanges> SourceBuffer::buffered(ExceptionState& exceptionState) co
     // 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.
     if (isRemoved()) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
-        return 0;
+        exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
+        return nullptr;
     }
 
     // 2. Return a new static normalized TimeRanges object for the media segments buffered.
@@ -162,14 +173,13 @@ double SourceBuffer::timestampOffset() const
 void SourceBuffer::setTimestampOffset(double offset, ExceptionState& exceptionState)
 {
     // Section 3.1 timestampOffset attribute setter steps.
+    // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-SourceBuffer-timestampOffset
     // 1. Let new timestamp offset equal the new value being assigned to this attribute.
     // 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.
     // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
-    if (isRemoved() || m_updating) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+    if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
         return;
-    }
 
     // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps:
     // 4.1 Set the readyState attribute of the parent media source to "open"
@@ -179,7 +189,7 @@ void SourceBuffer::setTimestampOffset(double offset, ExceptionState& exceptionSt
     // 5. If the append state equals PARSING_MEDIA_SEGMENT, then throw an INVALID_STATE_ERR and abort these steps.
     // 6. If the mode attribute equals "sequence", then set the group start timestamp to new timestamp offset.
     if (!m_webSourceBuffer->setTimestampOffset(offset)) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+        exceptionState.throwDOMException(InvalidStateError, "The timestamp offset may not be set while the SourceBuffer's append state is 'PARSING_MEDIA_SEGMENT'.");
         return;
     }
 
@@ -194,27 +204,18 @@ double SourceBuffer::appendWindowStart() const
 
 void SourceBuffer::setAppendWindowStart(double start, ExceptionState& exceptionState)
 {
-    // Enforce throwing an exception on restricted double values.
-    if (std::isnan(start)
-        || start == std::numeric_limits<double>::infinity()
-        || start == -std::numeric_limits<double>::infinity()) {
-        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
-        return;
-    }
-
     // Section 3.1 appendWindowStart attribute setter steps.
+    // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-SourceBuffer-appendWindowStart
     // 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.
     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
-    if (isRemoved() || m_updating) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+    if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
         return;
-    }
 
     // 3. If the new value is less than 0 or greater than or equal to appendWindowEnd then throw an InvalidAccessError
     //    exception and abort these steps.
     if (start < 0 || start >= m_appendWindowEnd) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
+        exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexOutsideRange("value", start, 0.0, ExceptionMessages::ExclusiveBound, m_appendWindowEnd, ExceptionMessages::InclusiveBound));
         return;
     }
 
@@ -232,19 +233,23 @@ double SourceBuffer::appendWindowEnd() const
 void SourceBuffer::setAppendWindowEnd(double end, ExceptionState& exceptionState)
 {
     // Section 3.1 appendWindowEnd attribute setter steps.
+    // https://dvcs.w3.org/hg/html-media/raw-file/tip/media-source/media-source.html#widl-SourceBuffer-appendWindowEnd
     // 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.
     // 2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
-    if (isRemoved() || m_updating) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+    if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
         return;
-    }
 
     // 3. If the new value equals NaN, then throw an InvalidAccessError and abort these steps.
+    if (std::isnan(end)) {
+        exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::notAFiniteNumber(end));
+        return;
+    }
     // 4. If the new value is less than or equal to appendWindowStart then throw an InvalidAccessError
     //    exception and abort these steps.
-    if (std::isnan(end) || end <= m_appendWindowStart) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
+    if (end <= m_appendWindowStart) {
+        // FIXME: Use ExceptionState::indexExceedsMinimumBound() once it lands.
+        exceptionState.throwDOMException(InvalidAccessError, "The value provided ('" + String::number(end) + "') is less than or equal to the minimum value (" + String::number(m_appendWindowStart) + ").");
         return;
     }
 
@@ -258,12 +263,6 @@ void SourceBuffer::appendBuffer(PassRefPtr<ArrayBuffer> data, ExceptionState& ex
 {
     // Section 3.2 appendBuffer()
     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
-    // 1. If data is null then throw an InvalidAccessError exception and abort these steps.
-    if (!data) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
-        return;
-    }
-
     appendBufferInternal(static_cast<const unsigned char*>(data->data()), data->byteLength(), exceptionState);
 }
 
@@ -271,22 +270,16 @@ void SourceBuffer::appendBuffer(PassRefPtr<ArrayBufferView> data, ExceptionState
 {
     // Section 3.2 appendBuffer()
     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
-    // 1. If data is null then throw an InvalidAccessError exception and abort these steps.
-    if (!data) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
-        return;
-    }
-
     appendBufferInternal(static_cast<const unsigned char*>(data->baseAddress()), data->byteLength(), exceptionState);
 }
 
-void SourceBuffer::appendStream(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
+void SourceBuffer::appendStream(PassRefPtrWillBeRawPtr<Stream> stream, ExceptionState& exceptionState)
 {
     m_streamMaxSizeValid = false;
     appendStreamInternal(stream, exceptionState);
 }
 
-void SourceBuffer::appendStream(PassRefPtr<Stream> stream, unsigned long long maxSize, ExceptionState& exceptionState)
+void SourceBuffer::appendStream(PassRefPtrWillBeRawPtr<Stream> stream, unsigned long long maxSize, ExceptionState& exceptionState)
 {
     m_streamMaxSizeValid = maxSize > 0;
     if (m_streamMaxSizeValid)
@@ -302,8 +295,12 @@ void SourceBuffer::abort(ExceptionState& exceptionState)
     //    then throw an InvalidStateError exception and abort these steps.
     // 2. If the readyState attribute of the parent media source is not in the "open" state
     //    then throw an InvalidStateError exception and abort these steps.
-    if (isRemoved() || !m_source->isOpen()) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+    if (isRemoved()) {
+        exceptionState.throwDOMException(InvalidStateError, "This SourceBuffer has been removed from the parent media source.");
+        return;
+    }
+    if (!m_source->isOpen()) {
+        exceptionState.throwDOMException(InvalidStateError, "The parent media source's readyState is not 'open'.");
         return;
     }
 
@@ -325,18 +322,21 @@ void SourceBuffer::remove(double start, double end, ExceptionState& exceptionSta
     // Section 3.2 remove() method steps.
     // 1. If start is negative or greater than duration, then throw an InvalidAccessError exception and abort these steps.
     // 2. If end is less than or equal to start, then throw an InvalidAccessError exception and abort these steps.
-    if (start < 0 || (m_source && (std::isnan(m_source->duration()) || start > m_source->duration())) || end <= start) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
+
+    if (start < 0 || (m_source && (std::isnan(m_source->duration()) || start > m_source->duration()))) {
+        exceptionState.throwDOMException(InvalidAccessError, ExceptionMessages::indexOutsideRange("start", start, 0.0, ExceptionMessages::ExclusiveBound, !m_source || std::isnan(m_source->duration()) ? 0 : m_source->duration(), ExceptionMessages::ExclusiveBound));
+        return;
+    }
+    if (end <= start) {
+        exceptionState.throwDOMException(InvalidAccessError, "The end value provided (" + String::number(end) + ") must be greater than the start value provided (" + String::number(start) + ").");
         return;
     }
 
     // 3. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an
     //    InvalidStateError exception and abort these steps.
     // 4. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
-    if (isRemoved() || m_updating) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+    if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
         return;
-    }
 
     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::remove", this);
 
@@ -379,6 +379,7 @@ void SourceBuffer::abortIfUpdating()
     // 3.1. Abort the buffer append and stream append loop algorithms if they are running.
     m_appendBufferAsyncPartRunner.stop();
     m_pendingAppendData.clear();
+    m_pendingAppendDataOffset = 0;
 
     m_removeAsyncPartRunner.stop();
     m_pendingRemoveStart = -1;
@@ -408,7 +409,7 @@ void SourceBuffer::removedFromMediaSource()
 
     m_webSourceBuffer->removedFromMediaSource();
     m_webSourceBuffer.clear();
-    m_source = 0;
+    m_source = nullptr;
     m_asyncEventQueue = 0;
 }
 
@@ -457,7 +458,7 @@ void SourceBuffer::scheduleEvent(const AtomicString& eventName)
 {
     ASSERT(m_asyncEventQueue);
 
-    RefPtr<Event> event = Event::create(eventName);
+    RefPtrWillBeRawPtr<Event> event = Event::create(eventName);
     event->setTarget(this);
 
     m_asyncEventQueue->enqueueEvent(event.release());
@@ -468,59 +469,83 @@ void SourceBuffer::appendBufferInternal(const unsigned char* data, unsigned size
     // Section 3.2 appendBuffer()
     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data
 
-    // Step 1 is enforced by the caller.
-    // 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.
-    // 3. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
-    if (isRemoved() || m_updating) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+    // 1. Run the prepare append algorithm.
+    // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-prepare-append
+    //  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.
+    //  2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
+    if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
         return;
-    }
 
-    TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendBuffer", this);
+    TRACE_EVENT_ASYNC_BEGIN1("media", "SourceBuffer::appendBuffer", this, "size", size);
 
-    // 4. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
+    //  3. If the readyState attribute of the parent media source is in the "ended" state then run the following steps: ...
     m_source->openIfInEndedState();
 
-    // Steps 5-6
+    //  Steps 4-5 - end "prepare append" algorithm.
 
-    // 7. Add data to the end of the input buffer.
-    m_pendingAppendData.append(data, size);
+    // 2. Add data to the end of the input buffer.
+    ASSERT(data || size == 0);
+    if (data)
+        m_pendingAppendData.append(data, size);
+    m_pendingAppendDataOffset = 0;
 
-    // 8. Set the updating attribute to true.
+    // 3. Set the updating attribute to true.
     m_updating = true;
 
-    // 9. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
+    // 4. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
     scheduleEvent(EventTypeNames::updatestart);
 
-    // 10. Asynchronously run the buffer append algorithm.
+    // 5. Asynchronously run the buffer append algorithm.
     m_appendBufferAsyncPartRunner.runAsync();
 
-    TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "waiting");
+    TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "initialDelay");
 }
 
 void SourceBuffer::appendBufferAsyncPart()
 {
     ASSERT(m_updating);
 
-    TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "appending");
-
     // Section 3.5.4 Buffer Append Algorithm
     // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append
 
     // 1. Run the segment parser loop algorithm.
     // Step 2 doesn't apply since we run Step 1 synchronously here.
-    size_t appendSize = m_pendingAppendData.size();
-    if (!appendSize) {
-        // Resize buffer for 0 byte appends so we always have a valid pointer.
-        // We need to convey all appends, even 0 byte ones to |m_webSourceBuffer|
-        // so that it can clear its end of stream state if necessary.
-        m_pendingAppendData.resize(1);
+    ASSERT(m_pendingAppendData.size() >= m_pendingAppendDataOffset);
+    size_t appendSize = m_pendingAppendData.size() - m_pendingAppendDataOffset;
+
+    // Impose an arbitrary max size for a single append() call so that an append
+    // doesn't block the renderer event loop very long. This value was selected
+    // by looking at YouTube SourceBuffer usage across a variety of bitrates.
+    // This value allows relatively large appends while keeping append() call
+    // duration in the  ~5-15ms range.
+    const size_t MaxAppendSize = 128 * 1024;
+    if (appendSize > MaxAppendSize)
+        appendSize = MaxAppendSize;
+
+    TRACE_EVENT_ASYNC_STEP_INTO1("media", "SourceBuffer::appendBuffer", this, "appending", "appendSize", static_cast<unsigned>(appendSize));
+
+    // |zero| is used for 0 byte appends so we always have a valid pointer.
+    // We need to convey all appends, even 0 byte ones to |m_webSourceBuffer|
+    // so that it can clear its end of stream state if necessary.
+    unsigned char zero = 0;
+    unsigned char* appendData = &zero;
+    if (appendSize)
+        appendData = m_pendingAppendData.data() + m_pendingAppendDataOffset;
+
+    m_webSourceBuffer->append(appendData, appendSize, &m_timestampOffset);
+
+    m_pendingAppendDataOffset += appendSize;
+
+    if (m_pendingAppendDataOffset < m_pendingAppendData.size()) {
+        m_appendBufferAsyncPartRunner.runAsync();
+        TRACE_EVENT_ASYNC_STEP_INTO0("media", "SourceBuffer::appendBuffer", this, "nextPieceDelay");
+        return;
     }
-    m_webSourceBuffer->append(m_pendingAppendData.data(), appendSize);
 
     // 3. Set the updating attribute to false.
     m_updating = false;
     m_pendingAppendData.clear();
+    m_pendingAppendDataOffset = 0;
 
     // 4. Queue a task to fire a simple event named update at this SourceBuffer object.
     scheduleEvent(EventTypeNames::update);
@@ -554,25 +579,23 @@ void SourceBuffer::removeAsyncPart()
     scheduleEvent(EventTypeNames::updateend);
 }
 
-void SourceBuffer::appendStreamInternal(PassRefPtr<Stream> stream, ExceptionState& exceptionState)
+void SourceBuffer::appendStreamInternal(PassRefPtrWillBeRawPtr<Stream> stream, ExceptionState& exceptionState)
 {
     // Section 3.2 appendStream()
     // 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
-    // 1. If stream is null then throw an InvalidAccessError exception and abort these steps.
-    if (!stream || stream->isNeutered()) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidAccessError);
+    // (0. If the stream has been neutered, then throw an InvalidAccessError exception and abort these steps.)
+    if (stream->isNeutered()) {
+        exceptionState.throwDOMException(InvalidAccessError, "The stream provided has been neutered.");
         return;
     }
 
-    // 2. Run the prepare append algorithm.
+    // 1. Run the prepare append algorithm.
     //  Section 3.5.4 Prepare Append Algorithm.
     //  https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-prepare-append
     //  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.
     //  2. If the updating attribute equals true, then throw an InvalidStateError exception and abort these steps.
-    if (isRemoved() || m_updating) {
-        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
+    if (throwExceptionIfRemovedOrUpdating(isRemoved(), m_updating, exceptionState))
         return;
-    }
 
     TRACE_EVENT_ASYNC_BEGIN0("media", "SourceBuffer::appendStream", this);
 
@@ -581,13 +604,13 @@ void SourceBuffer::appendStreamInternal(PassRefPtr<Stream> stream, ExceptionStat
 
     // Steps 4-5 of the prepare append algorithm are handled by m_webSourceBuffer.
 
-    // 3. Set the updating attribute to true.
+    // 2. Set the updating attribute to true.
     m_updating = true;
 
-    // 4. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
+    // 3. Queue a task to fire a simple event named updatestart at this SourceBuffer object.
     scheduleEvent(EventTypeNames::updatestart);
 
-    // 5. Asynchronously run the stream append loop algorithm with stream and maxSize.
+    // 4. Asynchronously run the stream append loop algorithm with stream and maxSize.
 
     stream->neuter();
     m_loader = adoptPtr(new FileReaderLoader(FileReaderLoader::ReadByClient, this));
@@ -659,7 +682,7 @@ void SourceBuffer::clearAppendStreamState()
     m_streamMaxSizeValid = false;
     m_streamMaxSize = 0;
     m_loader.clear();
-    m_stream = 0;
+    m_stream = nullptr;
 }
 
 void SourceBuffer::didStartLoading()
@@ -672,8 +695,7 @@ void SourceBuffer::didReceiveDataForClient(const char* data, unsigned dataLength
     WTF_LOG(Media, "SourceBuffer::didReceiveDataForClient(%d) %p", dataLength, this);
     ASSERT(m_updating);
     ASSERT(m_loader);
-
-    m_webSourceBuffer->append(reinterpret_cast<const unsigned char*>(data), dataLength);
+    m_webSourceBuffer->append(reinterpret_cast<const unsigned char*>(data), dataLength, &m_timestampOffset);
 }
 
 void SourceBuffer::didFinishLoading()
@@ -688,4 +710,10 @@ void SourceBuffer::didFail(FileError::ErrorCode errorCode)
     appendStreamDone(false);
 }
 
+void SourceBuffer::trace(Visitor* visitor)
+{
+    visitor->trace(m_source);
+    visitor->trace(m_stream);
+}
+
 } // namespace WebCore