Title: [172778] branches/safari-600.1-branch/Source/WebCore

Diff

Modified: branches/safari-600.1-branch/Source/WebCore/ChangeLog (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/ChangeLog	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/ChangeLog	2014-08-19 23:26:50 UTC (rev 172778)
@@ -1,5 +1,95 @@
 2014-08-19  Dana Burkart  <[email protected]>
 
+        Merge r172657. <rdar://problem/17896828>
+
+    2014-08-15  Eric Carlson  <[email protected]>
+    
+            [MSE] Implement a maximum buffer size for SourceBuffer
+            https://bugs.webkit.org/show_bug.cgi?id=135614
+    
+            Reviewed by Jer Noble.
+    
+            Implement the MSE coded frame eviction algorithm: when new buffers are appended attempt
+            to keep the amount of data buffered below a maximum size (which is determined by the media
+            session manager) by freeing coded frames as follows:
+                1 - Free frames in 30 second chunks from 0 to 30 seconds before the current time.
+                2 - If there are time ranges after the range with the current time, free frames in
+                    30 second chunks from duration back to the beginning of the time range after
+                    current time.
+    
+            For now we DO NOT throw a QUOTA_EXCEEDED_ERR when we are not able to free up enough
+            space to remain below the prescribed quota, because some big name, widely deployed, 
+            code bases ignore the error and continue appending data as though the failed append
+            succeeded, leading to a corrupted bitstream and failure to play.
+    
+            * Modules/mediasource/SampleMap.cpp:
+            (WebCore::SampleMap::addSample): Drive-by performance optimization: sample->presentationTime()
+                is used more than once, stash it in a local variable.
+            (WebCore::SampleMap::removeSample): Ditto.
+    
+            * Modules/mediasource/SourceBuffer.cpp:
+            (WebCore::logRanges): Debug-only function to log TimeRanges.
+            (WebCore::SourceBuffer::SourceBuffer):
+            (WebCore::SourceBuffer::remove): Optimize logging. Buffer full and coded frame eviction logic
+                is in SourceBuffer instead of SourceBufferPrivate.
+            (WebCore::SourceBuffer::appendBufferInternal): Ditto.
+            (WebCore::SourceBuffer::sourceBufferPrivateAppendComplete): Set m_bufferFull when more data
+                has been buffered than allowed.
+            (WebCore::removeSamplesFromTrackBuffer): Add logging.
+            (WebCore::SourceBuffer::removeCodedFrames): Improve logging. Avoid debug build assert when
+                current time is after last enqueued presentation time.
+            (WebCore::SourceBuffer::evictCodedFrames): The coded frame eviction algorithm.
+            (WebCore::SourceBuffer::maximumBufferSize): Return the maximum amount of data that can 
+                be buffered.
+            (WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample): Improve logging. Don't attempt
+                to create a PlatformTimeRanges with inverted start and end to avoid an assert in debug.
+            (WebCore::SourceBuffer::hasAudio): New, return true if there are any audio tracks.
+            (WebCore::SourceBuffer::hasVideo): New, return true if there are any video tracks.
+            (WebCore::SourceBuffer::sourceBufferPrivateHasAudio): Call hasAudio.
+            (WebCore::SourceBuffer::sourceBufferPrivateHasVideo): Call hasVideo.
+            (WebCore::SourceBuffer::hasCurrentTime): Return true if currentTime is greater than duration.
+            (WebCore::SourceBuffer::hasFutureTime): Ditto.
+            (WebCore::SourceBuffer::extraMemoryCost): Return the amount of data buffered: the size of
+                the input buffer plus the size of all track samples.
+            (WebCore::SourceBuffer::reportExtraMemoryCost): Move buffered size calculation to extraMemoryCost.
+            (WebCore::SourceBuffer::document): Document accessor.
+            * Modules/mediasource/SourceBuffer.h: Drive-by size optimization by moving all bool member
+                variables to the end of class.
+    
+            * html/HTMLMediaElement.cpp:
+            (WebCore::HTMLMediaElement::refreshCachedTime): Drive-by removal of overly chatty logging.
+            (WebCore::HTMLMediaElement::maximumSourceBufferSize): New, maximum source buffer size.
+            * html/HTMLMediaElement.h:
+    
+            * html/HTMLMediaSession.cpp:
+            (WebCore::HTMLMediaSession::maximumMediaSourceBufferSize): New, get maximum source buffer 
+                size from settings, return full amount for an SourceBuffer with video and audio, return 5%
+                of the maximum for an audio-only SourceBuffer.
+            * html/HTMLMediaSession.h:
+    
+            * page/Settings.in: Add maximumSourceBufferSize. Default value is enough for approximately
+                five minutes of 1080p video and stereo audio.
+    
+            * platform/graphics/PlatformTimeRanges.cpp:
+            (WebCore::PlatformTimeRanges::totalDuration): Drive-by optimization.
+            (WebCore::PlatformTimeRanges::dump): New, allow a PlatformTimeRanges to be printed.
+            * platform/graphics/PlatformTimeRanges.h:
+    
+            * platform/graphics/SourceBufferPrivate.h: Delete evictCodedFrames and isFull, that logic
+                is not in SourceBuffer.
+            * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
+            * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
+            (WebCore::SourceBufferPrivateAVFObjC::processCodedFrame): Drive-by logging fix.
+            (WebCore::SourceBufferPrivateAVFObjC::evictCodedFrames): Deleted.
+            (WebCore::SourceBufferPrivateAVFObjC::isFull): Deleted.
+    
+            * platform/mock/mediasource/MockSourceBufferPrivate.cpp:
+            (WebCore::MockSourceBufferPrivate::evictCodedFrames): Deleted.
+            (WebCore::MockSourceBufferPrivate::isFull): Deleted.
+            * platform/mock/mediasource/MockSourceBufferPrivate.h:
+    
+2014-08-19  Dana Burkart  <[email protected]>
+
         Merge r172656. <rdar://problem/17961698>
 
     2014-08-15  Zalan Bujtas  <[email protected]>

Modified: branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SampleMap.cpp (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SampleMap.cpp	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SampleMap.cpp	2014-08-19 23:26:50 UTC (rev 172778)
@@ -113,9 +113,11 @@
     RefPtr<MediaSample> sample = prpSample;
     ASSERT(sample);
 
-    presentationOrder().m_samples.insert(PresentationOrderSampleMap::MapType::value_type(sample->presentationTime(), sample));
+    MediaTime presentationTime = sample->presentationTime();
 
-    auto decodeKey = DecodeOrderSampleMap::KeyType(sample->decodeTime(), sample->presentationTime());
+    presentationOrder().m_samples.insert(PresentationOrderSampleMap::MapType::value_type(presentationTime, sample));
+
+    auto decodeKey = DecodeOrderSampleMap::KeyType(sample->decodeTime(), presentationTime);
     decodeOrder().m_samples.insert(DecodeOrderSampleMap::MapType::value_type(decodeKey, sample));
 
     m_totalSize += sample->sizeInBytes();
@@ -124,9 +126,11 @@
 void SampleMap::removeSample(MediaSample* sample)
 {
     ASSERT(sample);
-    presentationOrder().m_samples.erase(sample->presentationTime());
+    MediaTime presentationTime = sample->presentationTime();
 
-    auto decodeKey = DecodeOrderSampleMap::KeyType(sample->decodeTime(), sample->presentationTime());
+    presentationOrder().m_samples.erase(presentationTime);
+
+    auto decodeKey = DecodeOrderSampleMap::KeyType(sample->decodeTime(), presentationTime);
     decodeOrder().m_samples.erase(decodeKey);
 
     m_totalSize -= sample->sizeInBytes();

Modified: branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2014-08-19 23:26:50 UTC (rev 172778)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -48,12 +49,16 @@
 #include "TextTrackList.h"
 #include "TimeRanges.h"
 #include "VideoTrackList.h"
+#include <limits>
 #include <map>
 #include <runtime/JSCInlines.h>
 #include <runtime/JSLock.h>
 #include <runtime/VM.h>
 #include <wtf/CurrentTime.h>
 #include <wtf/NeverDestroyed.h>
+#if !LOG_DISABLED
+#include <wtf/text/StringBuilder.h>
+#endif
 
 namespace WebCore {
 
@@ -104,12 +109,9 @@
     , m_private(WTF::move(sourceBufferPrivate))
     , m_source(source)
     , m_asyncEventQueue(*this)
-    , m_updating(false)
     , m_appendBufferTimer(this, &SourceBuffer::appendBufferTimerFired)
     , m_highestPresentationEndTimestamp(MediaTime::invalidTime())
-    , m_receivedFirstInitializationSegment(false)
     , m_buffered(TimeRanges::create())
-    , m_active(false)
     , m_appendState(WaitingForSegment)
     , m_timeOfBufferingMonitor(monotonicallyIncreasingTime())
     , m_bufferedSinceLastMonitor(0)
@@ -118,6 +120,10 @@
     , m_pendingRemoveStart(MediaTime::invalidTime())
     , m_pendingRemoveEnd(MediaTime::invalidTime())
     , m_removeTimer(this, &SourceBuffer::removeTimerFired)
+    , m_updating(false)
+    , m_receivedFirstInitializationSegment(false)
+    , m_active(false)
+    , m_bufferFull(false)
 {
     ASSERT(m_source);
 
@@ -235,7 +241,8 @@
 
 void SourceBuffer::remove(double start, double end, ExceptionCode& ec)
 {
-    LOG(MediaSource, "SourceBuffer::remove(%p) - start(%s), end(%s)", this, toString(start).utf8().data(), toString(end).utf8().data());
+    LOG(MediaSource, "SourceBuffer::remove(%p) - start(%lf), end(%lf)", this, start, end);
+
     // Section 3.2 remove() method steps.
     // 1. If start is negative or greater than duration, then throw an InvalidAccessError exception and abort these steps.
     // 2. If end is less than or equal to start, then throw an InvalidAccessError exception and abort these steps.
@@ -407,13 +414,17 @@
     m_source->openIfInEndedState();
 
     // 4. Run the coded frame eviction algorithm.
-    m_private->evictCodedFrames();
+    evictCodedFrames(size);
 
+    // FIXME: enable this code when MSE libraries have been updated to support it.
+#if 0
     // 5. If the buffer full flag equals true, then throw a QUOTA_EXCEEDED_ERR exception and abort these step.
-    if (m_private->isFull()) {
+    if (m_bufferFull) {
+        LOG(MediaSource, "SourceBuffer::appendBufferInternal(%p) -  buffer full, failing with QUOTA_EXCEEDED_ERR error", this);
         ec = QUOTA_EXCEEDED_ERR;
         return;
     }
+#endif
 
     // NOTE: Return to 3.2 appendBuffer()
     // 3. Add data to the end of the input buffer.
@@ -514,6 +525,10 @@
     }
 
     reportExtraMemoryCost();
+    if (extraMemoryCost() > this->maximumBufferSize())
+        m_bufferFull = true;
+
+    LOG(Media, "SourceBuffer::sourceBufferPrivateAppendComplete(%p) - buffered = %s", this, toString(m_buffered->ranges()).utf8().data());
 }
 
 void SourceBuffer::sourceBufferPrivateDidReceiveRenderingError(SourceBufferPrivate*, int)
@@ -527,13 +542,27 @@
     return a.second->decodeTime() < b.second->decodeTime();
 }
 
-static PassRefPtr<TimeRanges> removeSamplesFromTrackBuffer(const DecodeOrderSampleMap::MapType& samples, SourceBuffer::TrackBuffer& trackBuffer)
+static PassRefPtr<TimeRanges> removeSamplesFromTrackBuffer(const DecodeOrderSampleMap::MapType& samples, SourceBuffer::TrackBuffer& trackBuffer, const SourceBuffer* buffer, const char* logPrefix)
 {
+#if !LOG_DISABLED
+    double earliestSample = std::numeric_limits<double>::infinity();
+    double latestSample = 0;
+    size_t bytesRemoved = 0;
+#else
+    UNUSED_PARAM(logPrefix);
+    UNUSED_PARAM(buffer);
+#endif
+
     RefPtr<TimeRanges> erasedRanges = TimeRanges::create();
     MediaTime microsecond(1, 1000000);
     for (auto sampleIt : samples) {
         const DecodeOrderSampleMap::KeyType& decodeKey = sampleIt.first;
+#if !LOG_DISABLED
+        size_t startBufferSize = trackBuffer.samples.sizeInBytes();
+#endif
+
         RefPtr<MediaSample>& sample = sampleIt.second;
+        LOG(MediaSource, "SourceBuffer::%s(%p) - removing sample(%s)", logPrefix, buffer, toString(*sampleIt.second).utf8().data());
 
         // Remove the erased samples from the TrackBuffer sample map.
         trackBuffer.samples.removeSample(sample.get());
@@ -544,7 +573,21 @@
         double startTime = sample->presentationTime().toDouble();
         double endTime = startTime + (sample->duration() + microsecond).toDouble();
         erasedRanges->add(startTime, endTime);
+
+#if !LOG_DISABLED
+        bytesRemoved += startBufferSize - trackBuffer.samples.sizeInBytes();
+        if (startTime < earliestSample)
+            earliestSample = startTime;
+        if (endTime > latestSample)
+            latestSample = endTime;
+#endif
     }
+
+#if !LOG_DISABLED
+    if (bytesRemoved)
+        LOG(MediaSource, "SourceBuffer::%s(%p) removed %zu bytes, start(%lf), end(%lf)", logPrefix, buffer, bytesRemoved, earliestSample, latestSample);
+#endif
+
     return erasedRanges.release();
 }
 
@@ -590,14 +633,16 @@
         DecodeOrderSampleMap::iterator removeDecodeStart = trackBuffer.samples.decodeOrder().findSampleWithDecodeKey(decodeKey);
 
         DecodeOrderSampleMap::MapType erasedSamples(removeDecodeStart, removeDecodeEnd);
-        RefPtr<TimeRanges> erasedRanges = removeSamplesFromTrackBuffer(erasedSamples, trackBuffer);
+        RefPtr<TimeRanges> erasedRanges = removeSamplesFromTrackBuffer(erasedSamples, trackBuffer, this, "removeCodedFrames");
 
         // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly
         // not yet displayed samples.
-        PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
-        possiblyEnqueuedRanges.intersectWith(erasedRanges->ranges());
-        if (possiblyEnqueuedRanges.length())
-            trackBuffer.needsReenqueueing = true;
+        if (currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) {
+            PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
+            possiblyEnqueuedRanges.intersectWith(erasedRanges->ranges());
+            if (possiblyEnqueuedRanges.length())
+                trackBuffer.needsReenqueueing = true;
+        }
 
         erasedRanges->invert();
         m_buffered->intersectWith(*erasedRanges);
@@ -611,6 +656,8 @@
 
     // 4. If buffer full flag equals true and this object is ready to accept more bytes, then set the buffer full flag to false.
     // No-op
+
+    LOG(Media, "SourceBuffer::removeCodedFrames(%p) - buffered = %s", this, toString(m_buffered->ranges()).utf8().data());
 }
 
 void SourceBuffer::removeTimerFired(Timer<SourceBuffer>*)
@@ -637,6 +684,109 @@
     scheduleEvent(eventNames().updateendEvent);
 }
 
+void SourceBuffer::evictCodedFrames(size_t newDataSize)
+{
+    // 3.5.13 Coded Frame Eviction Algorithm
+    // http://www.w3.org/TR/media-source/#sourcebuffer-coded-frame-eviction
+
+    if (isRemoved())
+        return;
+
+    // This algorithm is run to free up space in this source buffer when new data is appended.
+    // 1. Let new data equal the data that is about to be appended to this SourceBuffer.
+    // 2. If the buffer full flag equals false, then abort these steps.
+    if (!m_bufferFull)
+        return;
+
+    size_t maximumBufferSize = this->maximumBufferSize();
+
+    // 3. Let removal ranges equal a list of presentation time ranges that can be evicted from
+    // the presentation to make room for the new data.
+
+    // NOTE: begin by removing data from the beginning of the buffered ranges, 30 seconds at
+    // a time, up to 30 seconds before currentTime.
+    MediaTime thirtySeconds = MediaTime(30, 1);
+    MediaTime currentTime = MediaTime::createWithDouble(m_source->currentTime());
+    MediaTime maximumRangeEnd = currentTime - thirtySeconds;
+
+#if !LOG_DISABLED
+    LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - currentTime = %lf, require %zu bytes, maximum buffer size is %zu", this, m_source->currentTime(), extraMemoryCost() + newDataSize, maximumBufferSize);
+    size_t initialBufferedSize = extraMemoryCost();
+#endif
+
+    MediaTime rangeStart = MediaTime::zeroTime();
+    MediaTime rangeEnd = rangeStart + thirtySeconds;
+    while (rangeStart < maximumRangeEnd) {
+        // 4. For each range in removal ranges, run the coded frame removal algorithm with start and
+        // end equal to the removal range start and end timestamp respectively.
+        removeCodedFrames(rangeStart, std::min(rangeEnd, maximumRangeEnd));
+        if (extraMemoryCost() + newDataSize < maximumBufferSize) {
+            m_bufferFull = false;
+            break;
+        }
+
+        rangeStart += thirtySeconds;
+        rangeEnd += thirtySeconds;
+    }
+
+    if (!m_bufferFull) {
+        LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes", this, initialBufferedSize - extraMemoryCost());
+        return;
+    }
+
+    // If there still isn't enough free space and there buffers in time ranges after the current range (ie. there is a gap after
+    // the current buffered range), delete 30 seconds at a time from duration back to the current time range or 30 seconds after
+    // currenTime whichever we hit first.
+    auto buffered = m_buffered->ranges();
+    size_t currentTimeRange = buffered.find(currentTime);
+    if (currentTimeRange == notFound || currentTimeRange == buffered.length() - 1) {
+        LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes but FAILED to free enough", this, initialBufferedSize - extraMemoryCost());
+        return;
+    }
+
+    MediaTime minimumRangeStart = currentTime + thirtySeconds;
+
+    rangeEnd = MediaTime::createWithDouble(m_source->duration());
+    rangeStart = rangeEnd - thirtySeconds;
+    while (rangeStart > minimumRangeStart) {
+
+        // Do not evict data from the time range that contains currentTime.
+        size_t startTimeRange = buffered.find(rangeStart);
+        if (startTimeRange == currentTimeRange) {
+            size_t endTimeRange = buffered.find(rangeEnd);
+            if (endTimeRange == currentTimeRange)
+                break;
+
+            rangeEnd = buffered.start(endTimeRange);
+        }
+
+        // 4. For each range in removal ranges, run the coded frame removal algorithm with start and
+        // end equal to the removal range start and end timestamp respectively.
+        removeCodedFrames(std::max(minimumRangeStart, rangeStart), rangeEnd);
+        if (extraMemoryCost() + newDataSize < maximumBufferSize) {
+            m_bufferFull = false;
+            break;
+        }
+
+        rangeStart -= thirtySeconds;
+        rangeEnd -= thirtySeconds;
+    }
+
+    LOG(MediaSource, "SourceBuffer::evictCodedFrames(%p) - evicted %zu bytes%s", this, initialBufferedSize - extraMemoryCost(), m_bufferFull ? "" : " but FAILED to free enough");
+}
+
+size_t SourceBuffer::maximumBufferSize() const
+{
+    if (isRemoved())
+        return 0;
+
+    HTMLMediaElement* element = m_source->mediaElement();
+    if (!element)
+        return 0;
+
+    return element->maximumSourceBufferSize(*this);
+}
+
 const AtomicString& SourceBuffer::decodeError()
 {
     static NeverDestroyed<AtomicString> decode("decode", AtomicString::ConstructFromLiteral);
@@ -1198,19 +1348,17 @@
             auto nextSyncIter = trackBuffer.samples.decodeOrder().findSyncSampleAfterDecodeIterator(lastDecodeIter);
             dependentSamples.insert(firstDecodeIter, nextSyncIter);
 
-            RefPtr<TimeRanges> erasedRanges = removeSamplesFromTrackBuffer(dependentSamples, trackBuffer);
-#if !LOG_DISABLED
-            for (auto& samplePair : dependentSamples)
-                LOG(MediaSource, "SourceBuffer::sourceBufferPrivateDidReceiveSample(%p) - removing sample(%s)", this, toString(*samplePair.second).utf8().data());
-#endif
+            RefPtr<TimeRanges> erasedRanges = removeSamplesFromTrackBuffer(dependentSamples, trackBuffer, this, "sourceBufferPrivateDidReceiveSample");
 
             // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly
             // not yet displayed samples.
             MediaTime currentMediaTime = MediaTime::createWithDouble(m_source->currentTime());
-            PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
-            possiblyEnqueuedRanges.intersectWith(erasedRanges->ranges());
-            if (possiblyEnqueuedRanges.length())
-                trackBuffer.needsReenqueueing = true;
+            if (currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) {
+                PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
+                possiblyEnqueuedRanges.intersectWith(erasedRanges->ranges());
+                if (possiblyEnqueuedRanges.length())
+                    trackBuffer.needsReenqueueing = true;
+            }
 
             erasedRanges->invert();
             m_buffered->intersectWith(*erasedRanges.get());
@@ -1262,16 +1410,26 @@
         m_source->setDurationInternal(highestPresentationEndTimestamp().toDouble());
 }
 
-bool SourceBuffer::sourceBufferPrivateHasAudio(const SourceBufferPrivate*) const
+bool SourceBuffer::hasAudio() const
 {
     return m_audioTracks && m_audioTracks->length();
 }
 
-bool SourceBuffer::sourceBufferPrivateHasVideo(const SourceBufferPrivate*) const
+bool SourceBuffer::hasVideo() const
 {
     return m_videoTracks && m_videoTracks->length();
 }
 
+bool SourceBuffer::sourceBufferPrivateHasAudio(const SourceBufferPrivate*) const
+{
+    return hasAudio();
+}
+
+bool SourceBuffer::sourceBufferPrivateHasVideo(const SourceBufferPrivate*) const
+{
+    return hasVideo();
+}
+
 void SourceBuffer::videoTrackSelectedChanged(VideoTrack* track)
 {
     // 2.4.5 Changes to selected/enabled track state
@@ -1515,6 +1673,10 @@
         return false;
 
     MediaTime currentTime = MediaTime::createWithDouble(m_source->currentTime());
+    MediaTime duration = MediaTime::createWithDouble(m_source->duration());
+    if (currentTime >= duration)
+        return true;
+
     std::unique_ptr<PlatformTimeRanges> ranges = bufferedAccountingForEndOfStream();
     return abs(ranges->nearest(currentTime) - currentTime) <= currentTimeFudgeFactor();
 }
@@ -1529,6 +1691,10 @@
         return false;
 
     MediaTime currentTime = MediaTime::createWithDouble(m_source->currentTime());
+    MediaTime duration = MediaTime::createWithDouble(m_source->duration());
+    if (currentTime >= duration)
+        return true;
+
     MediaTime nearest = ranges->nearest(currentTime);
     if (abs(nearest - currentTime) > currentTimeFudgeFactor())
         return false;
@@ -1538,7 +1704,6 @@
         return false;
 
     MediaTime localEnd = ranges->end(found);
-    MediaTime duration = MediaTime::createWithDouble(m_source->duration());
     if (localEnd == duration)
         return true;
 
@@ -1572,12 +1737,18 @@
     return unbufferedTime.toDouble() / m_averageBufferRate < timeRemaining.toDouble();
 }
 
-void SourceBuffer::reportExtraMemoryCost()
+size_t SourceBuffer::extraMemoryCost() const
 {
     size_t extraMemoryCost = m_pendingAppendData.capacity();
     for (auto& trackBuffer : m_trackBufferMap.values())
         extraMemoryCost += trackBuffer.samples.sizeInBytes();
 
+    return extraMemoryCost;
+}
+
+void SourceBuffer::reportExtraMemoryCost()
+{
+    size_t extraMemoryCost = this->extraMemoryCost();
     if (extraMemoryCost < m_reportedExtraMemoryCost)
         return;
 
@@ -1603,6 +1774,12 @@
     return sampleDescriptions;
 }
 
+Document& SourceBuffer::document() const
+{
+    ASSERT(scriptExecutionContext()->isDocument());
+    return *static_cast<Document*>(scriptExecutionContext());
+}
+
 } // namespace WebCore
 
 #endif

Modified: branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SourceBuffer.h (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SourceBuffer.h	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/Modules/mediasource/SourceBuffer.h	2014-08-19 23:26:50 UTC (rev 172778)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -90,6 +91,9 @@
     bool hasFutureTime() const;
     bool canPlayThrough();
 
+    bool hasVideo() const;
+    bool hasAudio() const;
+
     bool active() const { return m_active; }
 
     // ActiveDOMObject interface
@@ -105,6 +109,8 @@
 
     struct TrackBuffer;
 
+    Document& document() const;
+
 protected:
     // EventTarget interface
     virtual void refEventTarget() override { ref(); }
@@ -154,12 +160,15 @@
     void reenqueueMediaForTime(TrackBuffer&, AtomicString trackID, const MediaTime&);
     void provideMediaData(TrackBuffer&, AtomicString trackID);
     void didDropSample();
+    void evictCodedFrames(size_t newDataSize);
+    size_t maximumBufferSize() const;
 
     void monitorBufferingRate();
 
     void removeTimerFired(Timer<SourceBuffer>*);
     void removeCodedFrames(const MediaTime& start, const MediaTime& end);
 
+    size_t extraMemoryCost() const;
     void reportExtraMemoryCost();
 
     std::unique_ptr<PlatformTimeRanges> bufferedAccountingForEndOfStream() const;
@@ -172,8 +181,6 @@
     MediaSource* m_source;
     GenericEventQueue m_asyncEventQueue;
 
-    bool m_updating;
-
     Vector<unsigned char> m_pendingAppendData;
     Timer<SourceBuffer> m_appendBufferTimer;
 
@@ -189,9 +196,7 @@
     MediaTime m_highestPresentationEndTimestamp;
 
     HashMap<AtomicString, TrackBuffer> m_trackBufferMap;
-    bool m_receivedFirstInitializationSegment;
     RefPtr<TimeRanges> m_buffered;
-    bool m_active;
 
     enum AppendStateType { WaitingForSegment, ParsingInitSegment, ParsingMediaSegment };
     AppendStateType m_appendState;
@@ -205,6 +210,11 @@
     MediaTime m_pendingRemoveStart;
     MediaTime m_pendingRemoveEnd;
     Timer<SourceBuffer> m_removeTimer;
+
+    bool m_updating;
+    bool m_receivedFirstInitializationSegment;
+    bool m_active;
+    bool m_bufferFull;
 };
 
 } // namespace WebCore

Modified: branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaElement.cpp (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaElement.cpp	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaElement.cpp	2014-08-19 23:26:50 UTC (rev 172778)
@@ -2518,7 +2518,6 @@
         return; 
     } 
 
-    LOG(Media, "HTMLMediaElement::refreshCachedTime - caching time %f", m_cachedTime); 
     m_clockTimeAtLastCachedTimeUpdate = monotonicallyIncreasingTime();
 }
 
@@ -5972,6 +5971,13 @@
     return MediaSession::Audio;
 }
 
+#if ENABLE(MEDIA_SOURCE)
+size_t HTMLMediaElement::maximumSourceBufferSize(const SourceBuffer& buffer) const
+{
+    return m_mediaSession->maximumMediaSourceBufferSize(buffer);
+}
+#endif
+
 void HTMLMediaElement::pausePlayback()
 {
     LOG(Media, "HTMLMediaElement::pausePlayback - paused = %s", boolString(paused()));

Modified: branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaElement.h (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaElement.h	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaElement.h	2014-08-19 23:26:50 UTC (rev 172778)
@@ -72,6 +72,7 @@
 #endif
 #if ENABLE(MEDIA_SOURCE)
 class MediaSource;
+class SourceBuffer;
 class VideoPlaybackQuality;
 #endif
 
@@ -208,6 +209,7 @@
 //  Media Source.
     void closeMediaSource();
     void incrementDroppedFrameCount() { ++m_droppedVideoFrames; }
+    size_t maximumSourceBufferSize(const SourceBuffer&) const;
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA)

Modified: branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaSession.cpp (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaSession.cpp	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaSession.cpp	2014-08-19 23:26:50 UTC (rev 172778)
@@ -38,6 +38,7 @@
 #include "MediaSessionManager.h"
 #include "Page.h"
 #include "ScriptController.h"
+#include "SourceBuffer.h"
 
 #if PLATFORM(IOS)
 #include "AudioSession.h"
@@ -301,6 +302,38 @@
     
 }
 
+#if ENABLE(MEDIA_SOURCE)
+const unsigned fiveMinutesOf1080PVideo = 290 * 1024 * 1024; // 290 MB is approximately 5 minutes of 8Mbps (1080p) content.
+const unsigned fiveMinutesStereoAudio = 14 * 1024 * 1024; // 14 MB is approximately 5 minutes of 384kbps content.
+
+size_t HTMLMediaSession::maximumMediaSourceBufferSize(const SourceBuffer& buffer) const
+{
+    // A good quality 1080p video uses 8,000 kbps and stereo audio uses 384 kbps, so assume 95% for video and 5% for audio.
+    const float bufferBudgetPercentageForVideo = .95;
+    const float bufferBudgetPercentageForAudio = .05;
+
+    size_t maximum;
+    Settings* settings = buffer.document().settings();
+    if (settings)
+        maximum = settings->maximumSourceBufferSize();
+    else
+        maximum = fiveMinutesOf1080PVideo + fiveMinutesStereoAudio;
+
+    // Allow a SourceBuffer to buffer as though it is audio-only even if it doesn't have any active tracks (yet).
+    size_t bufferSize = static_cast<size_t>(maximum * bufferBudgetPercentageForAudio);
+    if (buffer.hasVideo())
+        bufferSize += static_cast<size_t>(maximum * bufferBudgetPercentageForVideo);
+
+    // FIXME: we might want to modify this algorithm to:
+    // - decrease the maximum size for background tabs
+    // - decrease the maximum size allowed for inactive elements when a process has more than one
+    //   element, eg. so a page with many elements which are played one at a time doesn't keep
+    //   everything buffered after an element has finished playing.
+
+    return bufferSize;
 }
+#endif
 
+}
+
 #endif // ENABLE(VIDEO)

Modified: branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaSession.h (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaSession.h	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/html/HTMLMediaSession.h	2014-08-19 23:26:50 UTC (rev 172778)
@@ -34,6 +34,7 @@
 namespace WebCore {
 
 class HTMLMediaElement;
+class SourceBuffer;
 
 class HTMLMediaSession : public MediaSession {
 public:
@@ -82,6 +83,10 @@
     void addBehaviorRestriction(BehaviorRestrictions);
     void removeBehaviorRestriction(BehaviorRestrictions);
 
+#if ENABLE(MEDIA_SOURCE)
+    size_t maximumMediaSourceBufferSize(const SourceBuffer&) const;
+#endif
+
 private:
     BehaviorRestrictions m_restrictions;
 };

Modified: branches/safari-600.1-branch/Source/WebCore/page/Settings.in (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/page/Settings.in	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/page/Settings.in	2014-08-19 23:26:50 UTC (rev 172778)
@@ -228,3 +228,7 @@
 # Allow clients to permit navigation to an invalid URL. Some apps may use invalid URLs
 # as a means to pass data from the web-portion of their app to the native portion.
 allowNavigationToInvalidURL initial=false
+
+# Allow SourceBuffers to store up to 304MB each, enough for approximately five minutes
+# of 1080p video and stereo audio.
+maximumSourceBufferSize type=int, initial=318767104, conditional=MEDIA_SOURCE

Modified: branches/safari-600.1-branch/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/platform/graphics/PlatformTimeRanges.cpp	2014-08-19 23:26:50 UTC (rev 172778)
@@ -27,6 +27,7 @@
 #include "PlatformTimeRanges.h"
 
 #include <math.h>
+#include <wtf/PrintStream.h>
 
 namespace WebCore {
 
@@ -252,11 +253,19 @@
 MediaTime PlatformTimeRanges::totalDuration() const
 {
     MediaTime total = MediaTime::zeroTime();
-    bool ignoreInvalid;
 
     for (unsigned n = 0; n < length(); n++)
-        total += abs(end(n, ignoreInvalid) - start(n, ignoreInvalid));
+        total += abs(end(n) - start(n));
     return total;
 }
 
+void PlatformTimeRanges::dump(PrintStream& out) const
+{
+    if (!length())
+        return;
+
+    for (size_t i = 0; i < length(); ++i)
+        out.print("[", start(i), "..", end(i), "] ");
 }
+
+}

Modified: branches/safari-600.1-branch/Source/WebCore/platform/graphics/PlatformTimeRanges.h (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/platform/graphics/PlatformTimeRanges.h	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/platform/graphics/PlatformTimeRanges.h	2014-08-19 23:26:50 UTC (rev 172778)
@@ -32,6 +32,10 @@
 #include <wtf/RefCounted.h>
 #include <wtf/Vector.h>
 
+namespace WTF {
+class PrintStream;
+}
+
 namespace WebCore {
 
 class PlatformTimeRanges {
@@ -69,6 +73,8 @@
 
     MediaTime totalDuration() const;
 
+    void dump(WTF::PrintStream&) const;
+
 private:
     PlatformTimeRanges& copy(const PlatformTimeRanges&);
 

Modified: branches/safari-600.1-branch/Source/WebCore/platform/graphics/SourceBufferPrivate.h (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/platform/graphics/SourceBufferPrivate.h	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/platform/graphics/SourceBufferPrivate.h	2014-08-19 23:26:50 UTC (rev 172778)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -55,8 +56,6 @@
 
     virtual MediaPlayer::ReadyState readyState() const = 0;
     virtual void setReadyState(MediaPlayer::ReadyState) = 0;
-    virtual void evictCodedFrames() = 0;
-    virtual bool isFull() = 0;
 
     virtual void flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>>, AtomicString) { }
     virtual void enqueueSample(PassRefPtr<MediaSample>, AtomicString) { }

Modified: branches/safari-600.1-branch/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h	2014-08-19 23:26:50 UTC (rev 172778)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -112,8 +112,6 @@
     virtual void removedFromMediaSource() override;
     virtual MediaPlayer::ReadyState readyState() const override;
     virtual void setReadyState(MediaPlayer::ReadyState) override;
-    virtual void evictCodedFrames() override;
-    virtual bool isFull() override;
     virtual void flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>>, AtomicString trackID) override;
     virtual void enqueueSample(PassRefPtr<MediaSample>, AtomicString trackID) override;
     virtual bool isReadyForMoreSamples(AtomicString trackID) override;

Modified: branches/safari-600.1-branch/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm	2014-08-19 23:26:50 UTC (rev 172778)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -632,7 +632,7 @@
         CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
         FloatSize formatSize = FloatSize(CMVideoFormatDescriptionGetPresentationDimensions(formatDescription, true, true));
         if (formatSize != m_cachedSize) {
-            LOG(MediaSource, "SourceBufferPrivateAVFObjC::processCodedFrame(%p) - size change detected: {width=%lf, height=%lf", formatSize.width(), formatSize.height());
+            LOG(MediaSource, "SourceBufferPrivateAVFObjC::processCodedFrame(%p) - size change detected: {width=%lf, height=%lf}", formatSize.width(), formatSize.height());
             m_cachedSize = formatSize;
             if (m_mediaSource)
                 m_mediaSource->player()->sizeChanged();
@@ -779,18 +779,6 @@
         m_mediaSource->player()->setReadyState(readyState);
 }
 
-void SourceBufferPrivateAVFObjC::evictCodedFrames()
-{
-    notImplemented();
-}
-
-bool SourceBufferPrivateAVFObjC::isFull()
-{
-    notImplemented();
-    return false;
-}
-
-
 bool SourceBufferPrivateAVFObjC::hasVideo() const
 {
     if (!m_client)

Modified: branches/safari-600.1-branch/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.cpp	2014-08-19 23:26:50 UTC (rev 172778)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -213,16 +213,6 @@
         m_mediaSource->player()->setReadyState(readyState);
 }
 
-void MockSourceBufferPrivate::evictCodedFrames()
-{
-    // No-op.
-}
-
-bool MockSourceBufferPrivate::isFull()
-{
-    return false;
-}
-
 void MockSourceBufferPrivate::setActive(bool isActive)
 {
     if (m_mediaSource)

Modified: branches/safari-600.1-branch/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.h (172777 => 172778)


--- branches/safari-600.1-branch/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.h	2014-08-19 23:25:37 UTC (rev 172777)
+++ branches/safari-600.1-branch/Source/WebCore/platform/mock/mediasource/MockSourceBufferPrivate.h	2014-08-19 23:26:50 UTC (rev 172778)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -68,8 +68,6 @@
     virtual void removedFromMediaSource() override;
     virtual MediaPlayer::ReadyState readyState() const override;
     virtual void setReadyState(MediaPlayer::ReadyState) override;
-    virtual void evictCodedFrames() override;
-    virtual bool isFull() override;
 
     virtual void flushAndEnqueueNonDisplayingSamples(Vector<RefPtr<MediaSample>>, AtomicString) override { }
     virtual void enqueueSample(PassRefPtr<MediaSample>, AtomicString) override;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to