Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
- /media-source/SourceBuffer-appendWindowEnd-rounding.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<html>
<head>
<title>Test appendWindowEnd and frame end timestamp rounding</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="mediasource-util.js"></script>
</head>
<body>
</body>
<script>
const fps = 24;
const frames_per_keyframe = 8;
const default_sample_duration = 512;
const earliest_presentation_time = 1024;
const frame0_offset = earliest_presentation_time / default_sample_duration;
// Presentation positions of the 8 frames in a segment are
// 0, 4, 2, 1, 3, 7, 6, 5.
let media;
promise_test(async t => {
media = await new Promise(
r => MediaSourceUtil.fetchManifestAndData(
t,
`mp4/test-v-128k-320x240-${fps}fps-${frames_per_keyframe}kfr-manifest.json`,
(type, data) => r({type, data})));
}, 'setup');
async function append_frames({t, last_frame_pos}) {
assert_implements_optional(MediaSource.isTypeSupported(media.type),
'type supported');
const v = document.createElement('video');
const v_watcher = new EventWatcher(t, v, ['error']);
document.body.appendChild(v);
const media_source = new MediaSource();
const media_source_watcher =
new EventWatcher(t, media_source, ['sourceopen']);
v.src = URL.createObjectURL(media_source);
await media_source_watcher.wait_for('sourceopen');
const source_buffer = media_source.addSourceBuffer(media.type);
assert_equals(source_buffer.mode, 'segments', 'source_buffer.mode');
const source_buffer_watcher =
new EventWatcher(t, source_buffer, ['updateend']);
// This is intentionally a sum of double precision representations of
// "presentation timestamp and frame duration" to match frame end timestamp
// in
source_buffer.appendWindowEnd = last_frame_pos / fps + 1 / fps;
source_buffer.appendBuffer(media.data);
await source_buffer_watcher.wait_for('updateend');
assert_approx_equals(source_buffer.buffered.start(0),
frame0_offset / fps,
1e-6,
'source_buffer.buffered.start(0) after append');
assert_approx_equals(
source_buffer.buffered.end(source_buffer.buffered.length - 1),
source_buffer.appendWindowEnd,
1e-6,
'source_buffer.buffered.end() after append');
media_source.endOfStream();
assert_equals(media_source.duration,
source_buffer.buffered.end(source_buffer.buffered.length - 1),
'media_source.duration == buffered.end');
assert_equals(v.duration, media_source.duration,
'v.duration == media_source.duration');
};
// The first frame has presentation timestamp 2 frame durations after zero.
//
// The ninth frame has presentation timestamp 10 frame durations after zero
// and is a keyframe. The double precision sum of double presentation
// representations of its timestamp and frame duration is greater than the
// double precision representation of the tenth frame's timestamp.
// i.e 10/24 + 1/24 > 11/24, which would round down if rounded to
// nearest microseconds.
//
// The eighteenth frame has presentation timestamp 22 frame durations after
// zero. (Frames are not in presentation order.) It is immediately after a
// keyframe and so is found before the need random access point flag is set to
// true on applying appendWindowEnd. The double precision sum of double
// presentation representations of its timestamp and frame duration is less
// than the double precision representation of the tenth frame's timestamp.
// i.e 22/24 + 1/24 < 23/24, which would round down if rounded to nearest
// microseconds.
for (const last_frame_pos of [10, 22]) {
promise_test(
t => append_frames({t, last_frame_pos}),
`last frame at ${last_frame_pos}`);
}
</script>
</html>