From a797651db4e636bd00cba3715686dc35057eb9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Mon, 26 Jun 2023 20:52:32 +0200 Subject: [PATCH] feat: Parses a PRFT Box, with a loss of precision beyond 53 bits (#5354) --- lib/media/streaming_engine.js | 38 +++++++++------------------ lib/util/mp4_box_parsers.js | 40 +++++++++++++++++++++++++++++ test/media/streaming_engine_unit.js | 16 ------------ 3 files changed, 52 insertions(+), 42 deletions(-) diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js index 5cb0381239..db7f2ed2b1 100644 --- a/lib/media/streaming_engine.js +++ b/lib/media/streaming_engine.js @@ -2038,30 +2038,16 @@ shaka.media.StreamingEngine = class { !reference.initSegmentReference.timescale) { return; } - box.reader.readUint32(); // Ignore referenceTrackId - const ntpTimestampSec = box.reader.readUint32(); - const ntpTimestampFrac = box.reader.readUint32(); - const ntpTimestamp = ntpTimestampSec * 1000 + - ntpTimestampFrac / 2**32 * 1000; - - let mediaTime; - if (box.version === 0) { - mediaTime = box.reader.readUint32(); - } else { - try { - mediaTime = box.reader.readUint64(); - } catch (e) { - shaka.log.warning('parsePrft_: parsing mediatime resulted in a '+ - 'MEDIA.JS_INTEGER_OVERFLOW exception'); - this.parsedPrftEventRaised_ = true; - return; - } - } + goog.asserts.assert( + box.version == 0 || box.version == 1, + 'PRFT version can only be 0 or 1'); + const parsed = shaka.util.Mp4BoxParsers.parsePRFTInaccurate( + box.reader, box.version); const timescale = reference.initSegmentReference.timescale; - const wallClockTime = this.convertNtp(ntpTimestamp); + const wallClockTime = this.convertNtp(parsed.ntpTimestamp); const programStartDate = new Date(wallClockTime - - (mediaTime / timescale) * 1000); + (parsed.mediaTime / timescale) * 1000); const prftInfo = { wallClockTime, programStartDate, @@ -2077,11 +2063,11 @@ shaka.media.StreamingEngine = class { /** - * Convert Ntp ntpTimeStamp to UTC Time - * - * @param {number} ntpTimeStamp - * @return {number} utcTime - */ + * Convert Ntp ntpTimeStamp to UTC Time + * + * @param {number} ntpTimeStamp + * @return {number} utcTime + */ convertNtp(ntpTimeStamp) { const start = new Date(Date.UTC(1900, 0, 1, 0, 0, 0)); return new Date(start.getTime() + ntpTimeStamp).getTime(); diff --git a/lib/util/mp4_box_parsers.js b/lib/util/mp4_box_parsers.js index 4658e1d495..d14c5b781b 100644 --- a/lib/util/mp4_box_parsers.js +++ b/lib/util/mp4_box_parsers.js @@ -89,6 +89,36 @@ shaka.util.Mp4BoxParsers = class { } } + /** + * Parses a PRFT Box, with a loss of precision beyond 53 bits. + * Use only when exact integers are not required, e.g. when + * dividing by the timescale. + * + * @param {!shaka.util.DataViewReader} reader + * @param {number} version + * @return {!shaka.util.ParsedPRFTBox} + */ + static parsePRFTInaccurate(reader, version) { + reader.readUint32(); // Ignore referenceTrackId + const ntpTimestampSec = reader.readUint32(); + const ntpTimestampFrac = reader.readUint32(); + const ntpTimestamp = ntpTimestampSec * 1000 + + ntpTimestampFrac / 2**32 * 1000; + + let mediaTime; + if (version === 0) { + mediaTime = reader.readUint32(); + } else { + const high = reader.readUint32(); + const low = reader.readUint32(); + mediaTime = (high * Math.pow(2, 32)) + low; + } + return { + mediaTime, + ntpTimestamp, + }; + } + /** * Parses a MDHD Box. * @param {!shaka.util.DataViewReader} reader @@ -332,6 +362,16 @@ shaka.util.ParsedTFHDBox; */ shaka.util.ParsedTFDTBox; +/** + * @typedef {{ + * mediaTime: number, + * ntpTimestamp: number + * }} + * + * @exportDoc + */ +shaka.util.ParsedPRFTBox; + /** * @typedef {{ * timescale: number, diff --git a/test/media/streaming_engine_unit.js b/test/media/streaming_engine_unit.js index 352a314784..492b6c55e3 100644 --- a/test/media/streaming_engine_unit.js +++ b/test/media/streaming_engine_unit.js @@ -3226,22 +3226,6 @@ describe('StreamingEngine', () => { expectedStartDate.toUTCString()); }); - it('does not raise event if mediatime exceeds Number.MAX_VALUE', - async () => { - const prftSegment = Uint8ArrayUtils.fromHex( - '00000020707266740100000000000001E683B62E8E63CC58'+ - 'FFFFFFFFFFFFFFFF'); - segmentData[ContentType.VIDEO].segments[0] = prftSegment; - segmentData[ContentType.VIDEO].initSegments[0] = mdhdSegment; - - streamingEngine.switchVariant(variant); - streamingEngine.switchTextStream(textStream); - await streamingEngine.start(); - playing = true; - await runTest(); - expect(onEvent).not.toHaveBeenCalled(); - }); - it('raises an event once only', async () => { segmentData[ContentType.VIDEO].segments[0] = shaka.util.Uint8ArrayUtils.concat(prftSegment, prftSegment);