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