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 53using blink::WebSourceBuffer; 54 55namespace blink { 56 57namespace { 58 59static 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 75SourceBuffer* 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 82SourceBuffer::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 107SourceBuffer::~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 120const AtomicString& SourceBuffer::segmentsKeyword() 121{ 122 DEFINE_STATIC_LOCAL(const AtomicString, segments, ("segments", AtomicString::ConstructFromLiteral)); 123 return segments; 124} 125 126const AtomicString& SourceBuffer::sequenceKeyword() 127{ 128 DEFINE_STATIC_LOCAL(const AtomicString, sequence, ("sequence", AtomicString::ConstructFromLiteral)); 129 return sequence; 130} 131 132void 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 161PassRefPtrWillBeRawPtr<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 175double SourceBuffer::timestampOffset() const 176{ 177 return m_timestampOffset; 178} 179 180void 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 207double SourceBuffer::appendWindowStart() const 208{ 209 return m_appendWindowStart; 210} 211 212void 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 235double SourceBuffer::appendWindowEnd() const 236{ 237 return m_appendWindowEnd; 238} 239 240void 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 268void 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 275void 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 282void SourceBuffer::appendStream(PassRefPtrWillBeRawPtr<Stream> stream, ExceptionState& exceptionState) 283{ 284 m_streamMaxSizeValid = false; 285 appendStreamInternal(stream, exceptionState); 286} 287 288void 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 296void 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 326void 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 369void 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 412void 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 425void 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 448bool SourceBuffer::hasPendingActivity() const 449{ 450 return m_source; 451} 452 453void SourceBuffer::suspend() 454{ 455 m_appendBufferAsyncPartRunner.suspend(); 456 m_removeAsyncPartRunner.suspend(); 457 m_appendStreamAsyncPartRunner.suspend(); 458} 459 460void SourceBuffer::resume() 461{ 462 m_appendBufferAsyncPartRunner.resume(); 463 m_removeAsyncPartRunner.resume(); 464 m_appendStreamAsyncPartRunner.resume(); 465} 466 467void SourceBuffer::stop() 468{ 469 m_appendBufferAsyncPartRunner.stop(); 470 m_removeAsyncPartRunner.stop(); 471 m_appendStreamAsyncPartRunner.stop(); 472} 473 474ExecutionContext* SourceBuffer::executionContext() const 475{ 476 return ActiveDOMObject::executionContext(); 477} 478 479const AtomicString& SourceBuffer::interfaceName() const 480{ 481 return EventTargetNames::SourceBuffer; 482} 483 484bool SourceBuffer::isRemoved() const 485{ 486 return !m_source; 487} 488 489void 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 499void 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 536void 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 590void 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 614void 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 653void 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 674void 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 712void SourceBuffer::clearAppendStreamState() 713{ 714 m_streamMaxSizeValid = false; 715 m_streamMaxSize = 0; 716 m_loader.clear(); 717 m_stream = nullptr; 718} 719 720void SourceBuffer::didStartLoading() 721{ 722 WTF_LOG(Media, "SourceBuffer::didStartLoading() %p", this); 723} 724 725void 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 733void SourceBuffer::didFinishLoading() 734{ 735 WTF_LOG(Media, "SourceBuffer::didFinishLoading() %p", this); 736 appendStreamDone(true); 737} 738 739void SourceBuffer::didFail(FileError::ErrorCode errorCode) 740{ 741 WTF_LOG(Media, "SourceBuffer::didFail(%d) %p", errorCode, this); 742 appendStreamDone(false); 743} 744 745void 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