| <!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 |
| // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-processing |
| 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> |