这是indexloc提供的服务,不要输入任何密码
Skip to content

Live fMP4 HLS with WebVTT does not display due to timestamp adjustments overflowing int64 #1763

@wmho24

Description

@wmho24

Version

Media3 1.3.1

More version details

commit sha1 d833d59

Devices that reproduce the issue

Android Studio Koala Feature Drop 2024.1.2 Patch 1 Medium Phone API 35 emulator

Devices that do not reproduce the issue

No response

Reproducible in the demo app?

Yes

Reproduction steps

Given the following WebVTT content

WEBVTT
X-TIMESTAMP-MAP=LOCAL:479819:32:36.732,MPEGTS:895859864

479819:32:36.732 --> 479819:32:36.920
LANE. JADEN IVEY HANDS IT OFF
INSIDE TO JAYLENOTHER END. ISH G

479819:32:36.920 --> 479819:32:36.954
LANE. JADEN IVEY HANDS IT OFF
INSIDE TO JAYLENOTHER END. ISH G

479819:32:36.954 --> 479819:32:36.987
LANE. JADEN IVEY HANDS IT OFF
INSIDE TO JAYLENOTHER END. ISH G

479819:32:36.987 --> 479819:32:37.120
LANE. JADEN IVEY HANDS IT OFF
INSIDE TO JAYLENOTHER END. ISH G

Loading this WebVTT fragment will result in a calls to androidx.media3.exoplayer.hls.WebVttExtractor::processSample(). Debugging the following to evaluate sampleTimeUs will reveal an int64 overflow on the timestamp passed to usToWrappedPts()

  long sampleTimeUs =
        timestampAdjuster.adjustTsTimestamp(
            TimestampAdjuster.usToWrappedPts(firstCueTimeUs + tsTimestampUs - vttTimestampUs));

When reading fMP4 media, TimestampAdjuster's lastUnadjustedTimestampUs member has a value of 1727354212517266 at the time of the first call to processSample(). This value is in excess of 51-bits, meaning that the call to usToUnwrappedPts() will overflow due to a multiplication with 90000

public synchronized long adjustTsTimestamp(long pts90Khz) {
    if (pts90Khz == C.TIME_UNSET) {
      return C.TIME_UNSET;
    }
    if (lastUnadjustedTimestampUs != C.TIME_UNSET) {
      // The wrap count for the current PTS may be closestWrapCount or (closestWrapCount - 1),
      // and we need to snap to the one closest to lastSampleTimestampUs.
      long lastPts = usToNonWrappedPts(lastUnadjustedTimestampUs);
      long closestWrapCount = (lastPts + (MAX_PTS_PLUS_ONE / 2)) / MAX_PTS_PLUS_ONE;

Note that when the same code executes with HLS TS segments, the value of TimestampAdjuster's lastUnadjustedTimestampUs is a lot smaller: 14405760488 because the first PES header timestamp stayed within 33-bits. It will take many PCR wraparounds before reaching the magnitude seen for fMP4 TFDT 64-bit EPOCH time values above

It is possible to prevent this overflow by multiplying refactored scaling factors 9/100 and 100/9 instead of 90000/1000000 and 1000000/90000

The live HLS playout URL is not available on a permanent basis so an example segment is attached (video + webVtt) that should be enough to initialize exoplayer's TimestampAdjuster and reproduce the issue in debug mode.

A similar issue was opened in April 2023 that relates strongly to this bug report:

google/ExoPlayer#10969

The original issue highlighted a problem with adjustTsTimestamp() but did not dwell on it any further. The workaround cited in the cited issue worked by no longer calling the functions that overflow.

In our case, using scaling factors 9/100 for usToUnwrappedPts and 100/9 for ptsToUs fixes the issue

Expected result

WebVTT content displays when playing fMP4 HLS live feed (as in shakaPlayer and akamai hls.js)

image

Actual result

Nothing is displayed.

Media

segment_287604122.zip

`

Bug Report

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions