• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
throwExceptionIfRemovedOrUpdating(bool isRemoved,bool isUpdating,ExceptionState & exceptionState)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 
create(PassOwnPtr<WebSourceBuffer> webSourceBuffer,MediaSource * source,GenericEventQueue * asyncEventQueue)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 
SourceBuffer(PassOwnPtr<WebSourceBuffer> webSourceBuffer,MediaSource * source,GenericEventQueue * asyncEventQueue)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 
~SourceBuffer()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 
segmentsKeyword()120 const AtomicString& SourceBuffer::segmentsKeyword()
121 {
122     DEFINE_STATIC_LOCAL(const AtomicString, segments, ("segments", AtomicString::ConstructFromLiteral));
123     return segments;
124 }
125 
sequenceKeyword()126 const AtomicString& SourceBuffer::sequenceKeyword()
127 {
128     DEFINE_STATIC_LOCAL(const AtomicString, sequence, ("sequence", AtomicString::ConstructFromLiteral));
129     return sequence;
130 }
131 
setMode(const AtomicString & newMode,ExceptionState & exceptionState)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 
buffered(ExceptionState & exceptionState) const161 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 
timestampOffset() const175 double SourceBuffer::timestampOffset() const
176 {
177     return m_timestampOffset;
178 }
179 
setTimestampOffset(double offset,ExceptionState & exceptionState)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 
appendWindowStart() const207 double SourceBuffer::appendWindowStart() const
208 {
209     return m_appendWindowStart;
210 }
211 
setAppendWindowStart(double start,ExceptionState & exceptionState)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 
appendWindowEnd() const235 double SourceBuffer::appendWindowEnd() const
236 {
237     return m_appendWindowEnd;
238 }
239 
setAppendWindowEnd(double end,ExceptionState & exceptionState)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 
appendBuffer(PassRefPtr<ArrayBuffer> data,ExceptionState & exceptionState)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 
appendBuffer(PassRefPtr<ArrayBufferView> data,ExceptionState & exceptionState)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 
appendStream(PassRefPtrWillBeRawPtr<Stream> stream,ExceptionState & exceptionState)282 void SourceBuffer::appendStream(PassRefPtrWillBeRawPtr<Stream> stream, ExceptionState& exceptionState)
283 {
284     m_streamMaxSizeValid = false;
285     appendStreamInternal(stream, exceptionState);
286 }
287 
appendStream(PassRefPtrWillBeRawPtr<Stream> stream,unsigned long long maxSize,ExceptionState & exceptionState)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 
abort(ExceptionState & exceptionState)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 
remove(double start,double end,ExceptionState & exceptionState)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 
abortIfUpdating()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 
removedFromMediaSource()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 
initializationSegmentReceived()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 
hasPendingActivity() const448 bool SourceBuffer::hasPendingActivity() const
449 {
450     return m_source;
451 }
452 
suspend()453 void SourceBuffer::suspend()
454 {
455     m_appendBufferAsyncPartRunner.suspend();
456     m_removeAsyncPartRunner.suspend();
457     m_appendStreamAsyncPartRunner.suspend();
458 }
459 
resume()460 void SourceBuffer::resume()
461 {
462     m_appendBufferAsyncPartRunner.resume();
463     m_removeAsyncPartRunner.resume();
464     m_appendStreamAsyncPartRunner.resume();
465 }
466 
stop()467 void SourceBuffer::stop()
468 {
469     m_appendBufferAsyncPartRunner.stop();
470     m_removeAsyncPartRunner.stop();
471     m_appendStreamAsyncPartRunner.stop();
472 }
473 
executionContext() const474 ExecutionContext* SourceBuffer::executionContext() const
475 {
476     return ActiveDOMObject::executionContext();
477 }
478 
interfaceName() const479 const AtomicString& SourceBuffer::interfaceName() const
480 {
481     return EventTargetNames::SourceBuffer;
482 }
483 
isRemoved() const484 bool SourceBuffer::isRemoved() const
485 {
486     return !m_source;
487 }
488 
scheduleEvent(const AtomicString & eventName)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 
appendBufferInternal(const unsigned char * data,unsigned size,ExceptionState & exceptionState)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 
appendBufferAsyncPart()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 
removeAsyncPart()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 
appendStreamInternal(PassRefPtrWillBeRawPtr<Stream> stream,ExceptionState & exceptionState)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 
appendStreamAsyncPart()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 
appendStreamDone(bool success)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 
clearAppendStreamState()712 void SourceBuffer::clearAppendStreamState()
713 {
714     m_streamMaxSizeValid = false;
715     m_streamMaxSize = 0;
716     m_loader.clear();
717     m_stream = nullptr;
718 }
719 
didStartLoading()720 void SourceBuffer::didStartLoading()
721 {
722     WTF_LOG(Media, "SourceBuffer::didStartLoading() %p", this);
723 }
724 
didReceiveDataForClient(const char * data,unsigned dataLength)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 
didFinishLoading()733 void SourceBuffer::didFinishLoading()
734 {
735     WTF_LOG(Media, "SourceBuffer::didFinishLoading() %p", this);
736     appendStreamDone(true);
737 }
738 
didFail(FileError::ErrorCode errorCode)739 void SourceBuffer::didFail(FileError::ErrorCode errorCode)
740 {
741     WTF_LOG(Media, "SourceBuffer::didFail(%d) %p", errorCode, this);
742     appendStreamDone(false);
743 }
744 
trace(Visitor * visitor)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