SourceBuffer-appendWindowEnd-rounding.html (3866B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Test appendWindowEnd and frame end timestamp rounding</title> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="mediasource-util.js"></script> 8 </head> 9 <body> 10 </body> 11 <script> 12 const fps = 24; 13 const frames_per_keyframe = 8; 14 const default_sample_duration = 512; 15 const earliest_presentation_time = 1024; 16 const frame0_offset = earliest_presentation_time / default_sample_duration; 17 18 // Presentation positions of the 8 frames in a segment are 19 // 0, 4, 2, 1, 3, 7, 6, 5. 20 let media; 21 promise_test(async t => { 22 media = await new Promise( 23 r => MediaSourceUtil.fetchManifestAndData( 24 t, 25 `mp4/test-v-128k-320x240-${fps}fps-${frames_per_keyframe}kfr-manifest.json`, 26 (type, data) => r({type, data}))); 27 }, 'setup'); 28 29 async function append_frames({t, last_frame_pos}) { 30 assert_implements_optional(MediaSource.isTypeSupported(media.type), 31 'type supported'); 32 33 const v = document.createElement('video'); 34 const v_watcher = new EventWatcher(t, v, ['error']); 35 document.body.appendChild(v); 36 const media_source = new MediaSource(); 37 const media_source_watcher = 38 new EventWatcher(t, media_source, ['sourceopen']); 39 v.src = URL.createObjectURL(media_source); 40 await media_source_watcher.wait_for('sourceopen'); 41 42 const source_buffer = media_source.addSourceBuffer(media.type); 43 assert_equals(source_buffer.mode, 'segments', 'source_buffer.mode'); 44 const source_buffer_watcher = 45 new EventWatcher(t, source_buffer, ['updateend']); 46 47 // This is intentionally a sum of double precision representations of 48 // "presentation timestamp and frame duration" to match frame end timestamp 49 // in 50 // https://w3c.github.io/media-source/#sourcebuffer-coded-frame-processing 51 source_buffer.appendWindowEnd = last_frame_pos / fps + 1 / fps; 52 53 source_buffer.appendBuffer(media.data); 54 await source_buffer_watcher.wait_for('updateend'); 55 assert_approx_equals(source_buffer.buffered.start(0), 56 frame0_offset / fps, 57 1e-6, 58 'source_buffer.buffered.start(0) after append'); 59 assert_approx_equals( 60 source_buffer.buffered.end(source_buffer.buffered.length - 1), 61 source_buffer.appendWindowEnd, 62 1e-6, 63 'source_buffer.buffered.end() after append'); 64 65 media_source.endOfStream(); 66 assert_equals(media_source.duration, 67 source_buffer.buffered.end(source_buffer.buffered.length - 1), 68 'media_source.duration == buffered.end'); 69 assert_equals(v.duration, media_source.duration, 70 'v.duration == media_source.duration'); 71 }; 72 73 // The first frame has presentation timestamp 2 frame durations after zero. 74 // 75 // The ninth frame has presentation timestamp 10 frame durations after zero 76 // and is a keyframe. The double precision sum of double presentation 77 // representations of its timestamp and frame duration is greater than the 78 // double precision representation of the tenth frame's timestamp. 79 // i.e 10/24 + 1/24 > 11/24, which would round down if rounded to 80 // nearest microseconds. 81 // 82 // The eighteenth frame has presentation timestamp 22 frame durations after 83 // zero. (Frames are not in presentation order.) It is immediately after a 84 // keyframe and so is found before the need random access point flag is set to 85 // true on applying appendWindowEnd. The double precision sum of double 86 // presentation representations of its timestamp and frame duration is less 87 // than the double precision representation of the tenth frame's timestamp. 88 // i.e 22/24 + 1/24 < 23/24, which would round down if rounded to nearest 89 // microseconds. 90 for (const last_frame_pos of [10, 22]) { 91 promise_test( 92 t => append_frames({t, last_frame_pos}), 93 `last frame at ${last_frame_pos}`); 94 } 95 </script> 96 </html>