Changeset 202829 in webkit


Ignore:
Timestamp:
Jul 5, 2016 1:22:58 PM (8 years ago)
Author:
jer.noble@apple.com
Message:

REGRESSION (r202641): Netflix playback stalls after a few seconds
https://bugs.webkit.org/show_bug.cgi?id=159365

Reviewed by Eric Carlson.

Source/WebCore:

Test: LayoutTests/media/media-source/media-source-small-gap.html

In r202641, we removed a "fudge factor" of 1 millisecond added onto the duration
of every sample for the purposes of calculating a SourceBuffer's buffered ranges.
Netflix (and likely other providers) have streams that have 1 "timeScale" gaps
between segments (e.g., 1/9000s, 1/3003s, etc.). Fill those gaps by looking for
the previous and next samples and extending the buffered range to cover the gaps
if they're short enough. We have to ensure that we correctly remove those extended
durations when we remove samples from the SourceBuffer as well.

  • Modules/mediasource/SourceBuffer.cpp:

(WebCore::removeSamplesFromTrackBuffer):
(WebCore::SourceBuffer::removeCodedFrames):
(WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample):

Source/WTF:

Add a isBetween() convenience method.

  • wtf/MediaTime.cpp:

(WTF::MediaTime::isBetween):

  • wtf/MediaTime.h:

LayoutTests:

  • media/media-source/media-source-small-gap-expected.txt: Added.
  • media/media-source/media-source-small-gap.html: Added.
Location:
trunk
Files:
2 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r202826 r202829  
     12016-07-05  Jer Noble  <jer.noble@apple.com>
     2
     3        REGRESSION (r202641): Netflix playback stalls after a few seconds
     4        https://bugs.webkit.org/show_bug.cgi?id=159365
     5
     6        Reviewed by Eric Carlson.
     7
     8        * media/media-source/media-source-small-gap-expected.txt: Added.
     9        * media/media-source/media-source-small-gap.html: Added.
     10
    1112016-07-05  Myles C. Maxfield  <mmaxfield@apple.com>
    212
  • trunk/Source/WTF/ChangeLog

    r202799 r202829  
     12016-07-01  Jer Noble  <jer.noble@apple.com>
     2
     3        REGRESSION (r202641): Netflix playback stalls after a few seconds
     4        https://bugs.webkit.org/show_bug.cgi?id=159365
     5
     6        Reviewed by Eric Carlson.
     7
     8        Add a isBetween() convenience method.
     9
     10        * wtf/MediaTime.cpp:
     11        (WTF::MediaTime::isBetween):
     12        * wtf/MediaTime.h:
     13
    1142016-07-03  Per Arne Vollan  <pvollan@apple.com>
    215
  • trunk/Source/WTF/wtf/MediaTime.cpp

    r176863 r202829  
    434434}
    435435
     436bool MediaTime::isBetween(const MediaTime& a, const MediaTime& b) const
     437{
     438    if (a > b)
     439        return *this > b && *this < a;
     440    return *this > a && *this < b;
     441}
     442
    436443const MediaTime& MediaTime::zeroTime()
    437444{
  • trunk/Source/WTF/wtf/MediaTime.h

    r176863 r202829  
    8989
    9090    ComparisonFlags compare(const MediaTime& rhs) const;
     91    bool isBetween(const MediaTime&, const MediaTime&) const;
    9192
    9293    bool isValid() const { return m_timeFlags & Valid; }
  • trunk/Source/WebCore/ChangeLog

    r202822 r202829  
     12016-07-01  Jer Noble  <jer.noble@apple.com>
     2
     3        REGRESSION (r202641): Netflix playback stalls after a few seconds
     4        https://bugs.webkit.org/show_bug.cgi?id=159365
     5
     6        Reviewed by Eric Carlson.
     7
     8        Test: LayoutTests/media/media-source/media-source-small-gap.html
     9
     10        In r202641, we removed a "fudge factor" of 1 millisecond added onto the duration
     11        of every sample for the purposes of calculating a SourceBuffer's buffered ranges.
     12        Netflix (and likely other providers) have streams that have 1 "timeScale" gaps
     13        between segments (e.g., 1/9000s, 1/3003s, etc.). Fill those gaps by looking for
     14        the previous and next samples and extending the buffered range to cover the gaps
     15        if they're short enough. We have to ensure that we correctly remove those extended
     16        durations when we remove samples from the SourceBuffer as well.
     17
     18        * Modules/mediasource/SourceBuffer.cpp:
     19        (WebCore::removeSamplesFromTrackBuffer):
     20        (WebCore::SourceBuffer::removeCodedFrames):
     21        (WebCore::SourceBuffer::sourceBufferPrivateDidReceiveSample):
     22
    1232016-07-05  Brady Eidson  <beidson@apple.com>
    224
  • trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp

    r202641 r202829  
    653653}
    654654
    655 static PassRefPtr<TimeRanges> removeSamplesFromTrackBuffer(const DecodeOrderSampleMap::MapType& samples, SourceBuffer::TrackBuffer& trackBuffer, const SourceBuffer* buffer, const char* logPrefix)
     655static PlatformTimeRanges removeSamplesFromTrackBuffer(const DecodeOrderSampleMap::MapType& samples, SourceBuffer::TrackBuffer& trackBuffer, const SourceBuffer* buffer, const char* logPrefix)
    656656{
    657657#if !LOG_DISABLED
     
    664664#endif
    665665
    666     auto erasedRanges = TimeRanges::create();
     666    PlatformTimeRanges erasedRanges;
    667667    for (auto sampleIt : samples) {
    668668        const DecodeOrderSampleMap::KeyType& decodeKey = sampleIt.first;
     
    682682        auto startTime = sample->presentationTime();
    683683        auto endTime = startTime + sample->duration();
    684         erasedRanges->ranges().add(startTime, endTime);
     684        erasedRanges.add(startTime, endTime);
    685685
    686686#if !LOG_DISABLED
     
    693693    }
    694694
     695    // Because we may have added artificial padding in the buffered ranges when adding samples, we may
     696    // need to remove that padding when removing those same samples. Walk over the erased ranges looking
     697    // for unbuffered areas and expand erasedRanges to encompass those areas.
     698    PlatformTimeRanges additionalErasedRanges;
     699    for (unsigned i = 0; i < erasedRanges.length(); ++i) {
     700        auto erasedStart = erasedRanges.start(i);
     701        auto erasedEnd = erasedRanges.end(i);
     702        auto startIterator = trackBuffer.samples.presentationOrder().reverseFindSampleBeforePresentationTime(erasedStart);
     703        if (startIterator == trackBuffer.samples.presentationOrder().rend())
     704            additionalErasedRanges.add(MediaTime::zeroTime(), erasedStart);
     705        else {
     706            auto& previousSample = *startIterator->second;
     707            if (previousSample.presentationTime() + previousSample.duration() < erasedStart)
     708                additionalErasedRanges.add(previousSample.presentationTime() + previousSample.duration(), erasedStart);
     709        }
     710
     711        auto endIterator = trackBuffer.samples.presentationOrder().findSampleOnOrAfterPresentationTime(erasedEnd);
     712        if (endIterator == trackBuffer.samples.presentationOrder().end())
     713            additionalErasedRanges.add(erasedEnd, MediaTime::positiveInfiniteTime());
     714        else {
     715            auto& nextSample = *endIterator->second;
     716            if (nextSample.presentationTime() > erasedEnd)
     717                additionalErasedRanges.add(erasedEnd, nextSample.presentationTime());
     718        }
     719    }
     720    if (additionalErasedRanges.length())
     721        erasedRanges.unionWith(additionalErasedRanges);
     722
    695723#if !LOG_DISABLED
    696724    if (bytesRemoved)
     
    698726#endif
    699727
    700     return WTFMove(erasedRanges);
     728    return erasedRanges;
    701729}
    702730
     
    743771
    744772        DecodeOrderSampleMap::MapType erasedSamples(removeDecodeStart, removeDecodeEnd);
    745         RefPtr<TimeRanges> erasedRanges = removeSamplesFromTrackBuffer(erasedSamples, trackBuffer, this, "removeCodedFrames");
     773        PlatformTimeRanges erasedRanges = removeSamplesFromTrackBuffer(erasedSamples, trackBuffer, this, "removeCodedFrames");
    746774
    747775        // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly
     
    749777        if (currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) {
    750778            PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
    751             possiblyEnqueuedRanges.intersectWith(erasedRanges->ranges());
     779            possiblyEnqueuedRanges.intersectWith(erasedRanges);
    752780            if (possiblyEnqueuedRanges.length())
    753781                trackBuffer.needsReenqueueing = true;
    754782        }
    755783
    756         erasedRanges->invert();
    757         m_buffered->intersectWith(*erasedRanges);
     784        erasedRanges.invert();
     785        m_buffered->ranges().intersectWith(erasedRanges);
    758786        setBufferedDirty(true);
    759787
     
    15331561            dependentSamples.insert(firstDecodeIter, nextSyncIter);
    15341562
    1535             RefPtr<TimeRanges> erasedRanges = removeSamplesFromTrackBuffer(dependentSamples, trackBuffer, this, "sourceBufferPrivateDidReceiveSample");
     1563            PlatformTimeRanges erasedRanges = removeSamplesFromTrackBuffer(dependentSamples, trackBuffer, this, "sourceBufferPrivateDidReceiveSample");
    15361564
    15371565            // Only force the TrackBuffer to re-enqueue if the removed ranges overlap with enqueued and possibly
     
    15401568            if (currentMediaTime < trackBuffer.lastEnqueuedPresentationTime) {
    15411569                PlatformTimeRanges possiblyEnqueuedRanges(currentMediaTime, trackBuffer.lastEnqueuedPresentationTime);
    1542                 possiblyEnqueuedRanges.intersectWith(erasedRanges->ranges());
     1570                possiblyEnqueuedRanges.intersectWith(erasedRanges);
    15431571                if (possiblyEnqueuedRanges.length())
    15441572                    trackBuffer.needsReenqueueing = true;
    15451573            }
    15461574
    1547             erasedRanges->invert();
    1548             m_buffered->intersectWith(*erasedRanges);
     1575            erasedRanges.invert();
     1576            m_buffered->ranges().intersectWith(erasedRanges);
    15491577            setBufferedDirty(true);
    15501578        }
     
    15861614            m_timestampOffset = frameEndTimestamp;
    15871615
    1588         m_buffered->ranges().add(presentationTimestamp, presentationTimestamp + frameDuration);
     1616        // Eliminate small gaps between buffered ranges by coalescing
     1617        // disjoint ranges separated by less than a "fudge factor".
     1618        auto presentationEndTime = presentationTimestamp + frameDuration;
     1619        auto nearestToPresentationStartTime = m_buffered->ranges().nearest(presentationTimestamp);
     1620        if ((presentationTimestamp - nearestToPresentationStartTime).isBetween(MediaTime::zeroTime(), currentTimeFudgeFactor()))
     1621            presentationTimestamp = nearestToPresentationStartTime;
     1622
     1623        auto nearestToPresentationEndTime = m_buffered->ranges().nearest(presentationEndTime);
     1624        if ((nearestToPresentationEndTime - presentationEndTime).isBetween(MediaTime::zeroTime(), currentTimeFudgeFactor()))
     1625            presentationEndTime = nearestToPresentationEndTime;
     1626
     1627        m_buffered->ranges().add(presentationTimestamp, presentationEndTime);
    15891628        m_bufferedSinceLastMonitor += frameDuration.toDouble();
    15901629        setBufferedDirty(true);
Note: See TracChangeset for help on using the changeset viewer.