Bug 190590 - [MSE] timestampOffset can introduce floating-point rounding errors to incoming samples
Summary: [MSE] timestampOffset can introduce floating-point rounding errors to incomin...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Jer Noble
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2018-10-15 10:30 PDT by Jer Noble
Modified: 2018-10-18 16:02 PDT (History)
6 users (show)

See Also:


Attachments
Patch (12.88 KB, patch)
2018-10-15 10:42 PDT, Jer Noble
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews102 for mac-sierra (2.46 MB, application/zip)
2018-10-15 11:31 PDT, EWS Watchlist
no flags Details
Archive of layout-test-results from ews104 for mac-sierra-wk2 (3.18 MB, application/zip)
2018-10-15 11:46 PDT, EWS Watchlist
no flags Details
Archive of layout-test-results from ews112 for mac-sierra (3.06 MB, application/zip)
2018-10-15 12:46 PDT, EWS Watchlist
no flags Details
Patch (16.51 KB, patch)
2018-10-15 12:58 PDT, Jer Noble
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews100 for mac-sierra (2.44 MB, application/zip)
2018-10-15 14:07 PDT, EWS Watchlist
no flags Details
Archive of layout-test-results from ews104 for mac-sierra-wk2 (3.25 MB, application/zip)
2018-10-15 14:20 PDT, EWS Watchlist
no flags Details
Archive of layout-test-results from ews113 for mac-sierra (3.15 MB, application/zip)
2018-10-15 15:12 PDT, EWS Watchlist
no flags Details
Patch (19.39 KB, patch)
2018-10-16 13:59 PDT, Jer Noble
no flags Details | Formatted Diff | Diff
Patch (20.20 KB, patch)
2018-10-18 11:37 PDT, Jer Noble
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jer Noble 2018-10-15 10:30:25 PDT
[MSE] timestampOffset can introduce floating-point rounding errors to incoming samples
Comment 1 Radar WebKit Bug Importer 2018-10-15 10:31:57 PDT
<rdar://problem/45275626>
Comment 2 Jer Noble 2018-10-15 10:42:49 PDT
Created attachment 352342 [details]
Patch
Comment 3 EWS Watchlist 2018-10-15 11:31:44 PDT Comment hidden (obsolete)
Comment 4 EWS Watchlist 2018-10-15 11:31:45 PDT Comment hidden (obsolete)
Comment 5 EWS Watchlist 2018-10-15 11:46:16 PDT Comment hidden (obsolete)
Comment 6 EWS Watchlist 2018-10-15 11:46:17 PDT Comment hidden (obsolete)
Comment 7 EWS Watchlist 2018-10-15 12:46:40 PDT Comment hidden (obsolete)
Comment 8 EWS Watchlist 2018-10-15 12:46:42 PDT Comment hidden (obsolete)
Comment 9 Jer Noble 2018-10-15 12:58:47 PDT
Created attachment 352365 [details]
Patch
Comment 10 EWS Watchlist 2018-10-15 14:07:48 PDT Comment hidden (obsolete)
Comment 11 EWS Watchlist 2018-10-15 14:07:49 PDT Comment hidden (obsolete)
Comment 12 EWS Watchlist 2018-10-15 14:20:23 PDT Comment hidden (obsolete)
Comment 13 EWS Watchlist 2018-10-15 14:20:25 PDT Comment hidden (obsolete)
Comment 14 EWS Watchlist 2018-10-15 15:12:54 PDT Comment hidden (obsolete)
Comment 15 EWS Watchlist 2018-10-15 15:12:55 PDT Comment hidden (obsolete)
Comment 16 Alicia Boya García 2018-10-16 09:50:30 PDT
Comment on attachment 352365 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=352365&action=review

> Source/WebCore/Modules/mediasource/SourceBuffer.cpp:1485
> +                if (timeScale > std::numeric_limits<uint32_t>::max() / 2)

MediaTime::MaximumTimeScale should be used instead
Comment 17 Jer Noble 2018-10-16 13:59:35 PDT
Created attachment 352500 [details]
Patch
Comment 18 Alicia Boya García 2018-10-16 16:48:32 PDT
Comment on attachment 352500 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=352500&action=review

Nits aside, LGTM.

> Source/WebCore/Modules/mediasource/SourceBuffer.cpp:1424
> +        // NOTE: this is out-of-order, but we need the timescale from the

Is it worth it to reorder the code? We colud use `sample.duration` instead of `frameDuration`.

> Source/WebCore/Modules/mediasource/SourceBuffer.cpp:1499
> +            if (!trackBuffer.roundedTimestampOffset.isValid() || presentationTimestamp.timeScale() != trackBuffer.lastFrameTimescale) {

A TrackBuffer with samples with changing timescales is very rare. Should we add a mock test? (e.g. another one similar to our current test but where the second append has a different timescale)

> LayoutTests/media/media-source/media-source-timestampoffset-rounding-error.html:4
> +    <title>media-source-append-acb-tolerance</title>

media-source-timestampoffset-rounding-error

> LayoutTests/media/media-source/media-source-timestampoffset-rounding-error.html:50
> +        run('sourceBuffer.appendBuffer(makeVideo(2))');

makeVideo(1)
Comment 19 Alicia Boya García 2018-10-16 17:00:45 PDT
Let me check the algorithm once more...
Comment 20 Alicia Boya García 2018-10-17 23:48:44 PDT
Comment on attachment 352500 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=352500&action=review

> Source/WebCore/Modules/mediasource/SourceBuffer.cpp:1489
> +                if (timeScale > MediaTime::MaximumTimeScale / 2)

This / 2 should not be there, since otherwise in the (hypothetical) case where roundingMargin is 1/MaximumTimeScale and we only need one multiplication to get there we would not do it even if it's helpful.

Even then, this would be problematic if MaximumTimescale >= 2^31, since doubling would cause overflow. We have a patch to make it 1e9 (which happens to be slightly < 2^31), but this assumption is not obvious from reading the algorithm.

> Source/WebCore/Modules/mediasource/SourceBuffer.cpp:1492
> +            } while (abs(roundedTime - time) > roundingMargin);

I would argue that this should be >=. The idea is that if we consider microsecond precision we ignore differences smaller than 1 us: conversely 1 us is noticeable enough to make a difference.
Comment 21 Alicia Boya García 2018-10-17 23:53:31 PDT
Here is the algorithm modified to fix these problems, making no assumptions about the values of roundingMargin and MaximumTimeScale:

auto roundTowardsTimeScaleWithRoundingMargin = [] (const MediaTime& time, uint32_t timeScale, const MediaTime& roundingMargin) {
    while (true) {
        MediaTime roundedTime = time.toTimeScale(timeScale);
        if (abs(roundedTime - time) < roundingMargin || timeScale >= MediaTime::MaximumTimeScale)
            return roundedTime;

        timeScale = std::min<uint64_t>(static_cast<uint64_t>(timeScale) * 2, MediaTime::MaximumTimeScale);
    }
};
Comment 22 Jer Noble 2018-10-18 10:50:56 PDT
(In reply to Alicia Boya García from comment #21)
> Here is the algorithm modified to fix these problems, making no assumptions
> about the values of roundingMargin and MaximumTimeScale:
> 
> auto roundTowardsTimeScaleWithRoundingMargin = [] (const MediaTime& time,
> uint32_t timeScale, const MediaTime& roundingMargin) {
>     while (true) {
>         MediaTime roundedTime = time.toTimeScale(timeScale);
>         if (abs(roundedTime - time) < roundingMargin || timeScale >=
> MediaTime::MaximumTimeScale)
>             return roundedTime;
> 
>         timeScale = std::min<uint64_t>(static_cast<uint64_t>(timeScale) * 2,
> MediaTime::MaximumTimeScale);
>     }
> };

Thanks for this! We have nice convenience methods for doing checked arithmetic like this; I'll see if I can update your algorithm in terms of CheckedArithmetic.h.
Comment 23 Jer Noble 2018-10-18 11:14:57 PDT
(In reply to Alicia Boya García from comment #18)
> Comment on attachment 352500 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=352500&action=review
> 
> Nits aside, LGTM.
> 
> > Source/WebCore/Modules/mediasource/SourceBuffer.cpp:1424
> > +        // NOTE: this is out-of-order, but we need the timescale from the
> 
> Is it worth it to reorder the code? We colud use `sample.duration` instead
> of `frameDuration`.

True, but since we have to call sample.duration() already to create frameDuration, it would be a shame to have to call it twice.

> > Source/WebCore/Modules/mediasource/SourceBuffer.cpp:1499
> > +            if (!trackBuffer.roundedTimestampOffset.isValid() || presentationTimestamp.timeScale() != trackBuffer.lastFrameTimescale) {
> 
> A TrackBuffer with samples with changing timescales is very rare. Should we
> add a mock test? (e.g. another one similar to our current test but where the
> second append has a different timescale)

Sure, we could do that. Since trackBuffers are not recreated across variants, timeScale changes are definitely possible, if rare.

> > LayoutTests/media/media-source/media-source-timestampoffset-rounding-error.html:4
> > +    <title>media-source-append-acb-tolerance</title>
> 
> media-source-timestampoffset-rounding-error

Whoops.

> > LayoutTests/media/media-source/media-source-timestampoffset-rounding-error.html:50
> > +        run('sourceBuffer.appendBuffer(makeVideo(2))');
> 
> makeVideo(1)

Changed.
Comment 24 Jer Noble 2018-10-18 11:37:45 PDT
Created attachment 352712 [details]
Patch
Comment 25 Eric Carlson 2018-10-18 14:49:04 PDT
Comment on attachment 352712 [details]
Patch

rs=me based on Alicia's review.
Comment 26 WebKit Commit Bot 2018-10-18 16:02:23 PDT
Comment on attachment 352712 [details]
Patch

Clearing flags on attachment: 352712

Committed r237274: <https://trac.webkit.org/changeset/237274>
Comment 27 WebKit Commit Bot 2018-10-18 16:02:25 PDT
All reviewed patches have been landed.  Closing bug.