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