mediasource-duration.html (21303B)
1 <!DOCTYPE html> 2 <!-- Copyright © 2016 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). --> 3 <html> 4 <head> 5 <title>MediaSource.duration & HTMLMediaElement.duration test cases.</title> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="mediasource-util.js"></script> 9 </head> 10 <body> 11 <div id="log"></div> 12 <script> 13 14 var subType = MediaSourceUtil.getSubType(MediaSourceUtil.AUDIO_ONLY_TYPE); 15 var manifestFilenameAudio = subType + "/test-a-128k-44100Hz-1ch-manifest.json"; 16 var manifestFilenameVideo = subType + "/test-v-128k-320x240-30fps-10kfr-manifest.json"; 17 18 function mediasource_truncated_duration_seek_test(testFunction, description, options) 19 { 20 return mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) 21 { 22 assert_greater_than(segmentInfo.duration, 2, 'Sufficient test media duration'); 23 24 var fullDuration = segmentInfo.duration; 25 var seekTo = fullDuration / 2.0; 26 var truncatedDuration = seekTo / 2.0; 27 28 mediaElement.play(); 29 30 // Append all the segments 31 test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer'); 32 test.expectEvent(mediaElement, 'playing', 'Playing triggered'); 33 sourceBuffer.appendBuffer(mediaData); 34 35 test.waitForExpectedEvents(function() 36 { 37 test.expectEvent(mediaElement, 'seeking', 'seeking to seekTo'); 38 test.expectEvent(mediaElement, 'timeupdate', 'timeupdate while seeking to seekTo'); 39 test.expectEvent(mediaElement, 'seeked', 'seeked to seekTo'); 40 mediaElement.currentTime = seekTo; 41 assert_true(mediaElement.seeking, 'mediaElement.seeking (to seekTo)'); 42 }); 43 44 test.waitForExpectedEvents(function() 45 { 46 assert_greater_than_equal(mediaElement.currentTime, seekTo, 'Playback time has reached seekTo'); 47 assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to seekTo'); 48 49 assert_false(sourceBuffer.updating, 'sourceBuffer.updating'); 50 51 sourceBuffer.remove(truncatedDuration, Infinity); 52 53 assert_true(sourceBuffer.updating, 'sourceBuffer.updating'); 54 test.expectEvent(sourceBuffer, 'updatestart', 'sourceBuffer'); 55 test.expectEvent(sourceBuffer, 'update', 'sourceBuffer'); 56 test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer'); 57 }); 58 59 test.waitForExpectedEvents(function() 60 { 61 assert_greater_than_equal(mediaElement.currentTime, seekTo, 'Playback time has reached seekTo'); 62 assert_false(sourceBuffer.updating, 'sourceBuffer.updating'); 63 64 // Remove will not remove partial frames, so the resulting duration is the highest end time 65 // of the track buffer ranges, and is greater than or equal to the highest coded frame 66 // presentation time across all track buffer ranges. We first obtain the intersected track buffer 67 // ranges end time and set the duration to that value. 68 truncatedDuration = sourceBuffer.buffered.end(sourceBuffer.buffered.length-1); 69 assert_less_than(truncatedDuration, seekTo, 70 'remove has removed the current playback position from at least one track buffer'); 71 72 mediaSource.duration = truncatedDuration; 73 test.expectEvent(mediaElement, 'seeking', 'Seeking to truncated duration'); 74 75 // The actual duration may be slightly higher than truncatedDuration because the 76 // duration is adjusted upwards if necessary to be the highest end time across all track buffer 77 // ranges. Allow that increase here. 78 assert_less_than_equal(truncatedDuration, mediaSource.duration, 79 'Duration should not be less than what was set'); 80 // Here, we assume no test media coded frame duration is longer than 100ms. 81 assert_less_than(mediaSource.duration - truncatedDuration, 0.1); 82 83 // Update our truncatedDuration to be the actual new duration. 84 truncatedDuration = mediaSource.duration; 85 86 assert_true(mediaElement.seeking, 'Seeking after setting truncatedDuration'); 87 }); 88 89 test.waitForExpectedEvents(function() 90 { 91 assert_equals(mediaElement.currentTime, truncatedDuration, 92 'Playback time is truncatedDuration while seeking'); 93 assert_true(mediaElement.seeking, 'mediaElement.seeking while seeking to truncatedDuration'); 94 assert_equals(mediaElement.duration, truncatedDuration, 95 'mediaElement truncatedDuration during seek to it'); 96 assert_equals(mediaSource.duration, truncatedDuration, 97 'mediaSource truncatedDuration during seek to it'); 98 99 testFunction(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData, 100 truncatedDuration); 101 }); 102 }, description, options); 103 } 104 105 mediasource_truncated_duration_seek_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, 106 mediaData, truncatedDuration) 107 { 108 // Tests that duration truncation below current playback position 109 // starts seek to new duration. 110 test.done(); 111 }, 'Test seek starts on duration truncation below currentTime'); 112 113 mediasource_truncated_duration_seek_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, 114 mediaData, truncatedDuration) 115 { 116 // The duration has been truncated at this point, and there is an 117 // outstanding seek pending. 118 test.expectEvent(sourceBuffer, 'updateend', 'updateend after appending more data'); 119 120 test.expectEvent(mediaElement, 'timeupdate', 'timeupdate while finishing seek to truncatedDuration'); 121 test.expectEvent(mediaElement, 'seeked', 'seeked to truncatedDuration'); 122 123 // Allow seek to complete by appending more data beginning at the 124 // truncated duration timestamp. 125 sourceBuffer.timestampOffset = truncatedDuration; 126 sourceBuffer.appendBuffer(mediaData); 127 128 test.waitForExpectedEvents(function() 129 { 130 assert_greater_than_equal(mediaElement.currentTime, truncatedDuration, 131 'Playback time has reached truncatedDuration'); 132 assert_approx_equals(mediaElement.duration, truncatedDuration + segmentInfo.duration, 0.05, 133 'mediaElement duration increased by new append'); 134 assert_equals(mediaSource.duration, mediaElement.duration, 135 'mediaSource duration increased by new append'); 136 assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to truncatedDuration'); 137 138 test.done(); 139 }); 140 }, 'Test appendBuffer completes previous seek to truncated duration'); 141 142 mediasource_truncated_duration_seek_test(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, 143 mediaData, truncatedDuration) 144 { 145 // The duration has been truncated at this point, and there is an 146 // outstanding seek pending. 147 test.expectEvent(mediaSource, 'sourceended', 'endOfStream acknowledged'); 148 149 test.expectEvent(mediaElement, 'timeupdate', 'timeupdate while finishing seek to truncatedDuration'); 150 test.expectEvent(mediaElement, 'seeked', 'seeked to truncatedDuration'); 151 152 // Call endOfStream() to complete the pending seek. 153 mediaSource.endOfStream(); 154 155 test.waitForExpectedEvents(function() 156 { 157 assert_greater_than_equal(mediaElement.currentTime, truncatedDuration, 158 'Playback time has reached truncatedDuration'); 159 // The mediaSource.readyState is "ended". Buffered ranges have been adjusted to the longest track. 160 truncatedDuration = sourceBuffer.buffered.end(sourceBuffer.buffered.length-1); 161 assert_equals(mediaElement.duration, truncatedDuration, 162 'mediaElement truncatedDuration after seek to it'); 163 assert_equals(mediaSource.duration, truncatedDuration, 164 'mediaSource truncatedDuration after seek to it'); 165 assert_false(mediaElement.seeking, 'mediaElement.seeking after seeked to truncatedDuration'); 166 167 test.done(); 168 }); 169 }, 'Test endOfStream completes previous seek to truncated duration'); 170 171 mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) 172 { 173 assert_greater_than(segmentInfo.duration, 2, 'Sufficient test media duration'); 174 175 var fullDuration = segmentInfo.duration; 176 var newDuration = 0.5; 177 178 var expectedDurationChangeEventCount = 1; 179 var durationchangeEventCounter = 0; 180 var durationchangeEventHandler = test.step_func(function(event) 181 { 182 assert_equals(mediaElement.duration, mediaSource.duration, 'mediaElement newDuration'); 183 // Final duration may be greater than originally set as per MSE's 2.4.6 Duration change 184 // Adjust newDuration accordingly. 185 assert_less_than_equal(newDuration, mediaSource.duration, 'mediaSource newDuration'); 186 durationchangeEventCounter++; 187 }); 188 189 mediaElement.play(); 190 191 // Append all the segments 192 test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer'); 193 test.expectEvent(mediaElement, 'playing', 'Playing triggered'); 194 sourceBuffer.appendBuffer(mediaData); 195 196 test.waitForExpectedEvents(function() 197 { 198 assert_less_than(mediaElement.currentTime, newDuration / 2, 'mediaElement currentTime'); 199 200 assert_false(sourceBuffer.updating, "updating"); 201 202 // Truncate duration. This should result in one 'durationchange' fired. 203 sourceBuffer.remove(newDuration, Infinity); 204 205 assert_true(sourceBuffer.updating, "updating"); 206 test.expectEvent(sourceBuffer, 'updatestart', 'sourceBuffer'); 207 test.expectEvent(sourceBuffer, 'update', 'sourceBuffer'); 208 test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer'); 209 }); 210 211 test.waitForExpectedEvents(function() 212 { 213 // Media load also fires 'durationchange' event, so only start counting them now. 214 mediaElement.addEventListener('durationchange', durationchangeEventHandler); 215 216 assert_false(sourceBuffer.updating, "updating"); 217 218 // Truncate duration. This should result in one 'durationchange' fired. 219 mediaSource.duration = newDuration; 220 221 // Final duration may be greater than originally set as per MSE's 2.4.6 Duration change 222 // Adjust newDuration accordingly. 223 assert_true(newDuration <= mediaSource.duration, 'adjusted duration'); 224 newDuration = mediaSource.duration; 225 226 // Set duration again to make sure it does not trigger another 'durationchange' event. 227 mediaSource.duration = newDuration; 228 229 // Mark endOfStream so that playback can reach 'ended' at the new duration. 230 test.expectEvent(mediaSource, 'sourceended', 'endOfStream acknowledged'); 231 mediaSource.endOfStream(); 232 233 // endOfStream can change duration slightly. 234 // Allow for one more 'durationchange' event only in this case. 235 var currentDuration = mediaSource.duration; 236 if (currentDuration != newDuration) { 237 newDuration = currentDuration; 238 ++expectedDurationChangeEventCount; 239 } 240 241 // Allow media to play to end while counting 'durationchange' events. 242 test.expectEvent(mediaElement, 'ended', 'Playback ended'); 243 test.waitForExpectedEvents(function() 244 { 245 mediaElement.removeEventListener('durationchange', durationchangeEventHandler); 246 assert_equals(durationchangeEventCounter, expectedDurationChangeEventCount, 'durationchanges'); 247 test.done(); 248 }); 249 }); 250 }, 'Test setting same duration multiple times does not fire duplicate durationchange'); 251 252 mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) 253 { 254 assert_greater_than(segmentInfo.duration, 2, 'Sufficient test media duration'); 255 256 var fullDuration = segmentInfo.duration; 257 var newDuration = fullDuration / 2; 258 259 // Append all the segments 260 test.expectEvent(sourceBuffer, 'updateend', 'sourceBuffer'); 261 test.expectEvent(mediaElement, 'loadedmetadata', 'mediaElement'); 262 sourceBuffer.appendBuffer(mediaData); 263 264 test.waitForExpectedEvents(function() 265 { 266 assert_false(sourceBuffer.updating, "updating"); 267 268 assert_throws_dom("InvalidStateError", function() 269 { 270 mediaSource.duration = newDuration; 271 }, "duration"); 272 273 test.done(); 274 }); 275 }, 'Test setting the duration to less than the highest starting presentation timestamp will throw'); 276 277 mediasource_test(function(test, mediaElement, mediaSource) 278 { 279 mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'")); 280 MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAudio, function(typeAudio, dataAudio) 281 { 282 MediaSourceUtil.fetchManifestAndData(test, manifestFilenameVideo, function(typeVideo, dataVideo) 283 { 284 var sourceBufferAudio = mediaSource.addSourceBuffer(typeAudio); 285 var sourceBufferVideo = mediaSource.addSourceBuffer(typeVideo); 286 var newDuration = 1.2; 287 288 sourceBufferAudio.appendWindowEnd = 2.0; 289 sourceBufferAudio.appendWindowStart = newDuration / 2.0; 290 sourceBufferAudio.appendBuffer(dataAudio); 291 292 sourceBufferVideo.appendWindowEnd = 2.0; 293 sourceBufferVideo.appendWindowStart = newDuration * 1.3; 294 sourceBufferVideo.appendBuffer(dataVideo); 295 296 test.expectEvent(sourceBufferAudio, "updateend"); 297 test.expectEvent(sourceBufferVideo, "updateend"); 298 test.waitForExpectedEvents(function() 299 { 300 assert_equals(sourceBufferAudio.buffered.length, 1); 301 assert_equals(sourceBufferVideo.buffered.length, 1); 302 assert_less_than(sourceBufferAudio.buffered.start(0), newDuration); 303 assert_greater_than(sourceBufferVideo.buffered.start(0), newDuration); 304 assert_throws_dom("InvalidStateError", function () { mediaSource.duration = newDuration; }); 305 test.done(); 306 }); 307 }); 308 }); 309 }, "Truncating the duration throws an InvalidStateError exception when new duration is less than the highest buffered range start time of one of the track buffers"); 310 311 mediasource_test(function(test, mediaElement, mediaSource) 312 { 313 mediaElement.addEventListener("error", test.unreached_func("Unexpected event 'error'")); 314 MediaSourceUtil.fetchManifestAndData(test, manifestFilenameAudio, function(typeAudio, dataAudio) 315 { 316 MediaSourceUtil.fetchManifestAndData(test, manifestFilenameVideo, function(typeVideo, dataVideo) 317 { 318 var sourceBufferAudio = mediaSource.addSourceBuffer(typeAudio); 319 var sourceBufferVideo = mediaSource.addSourceBuffer(typeVideo); 320 321 // Buffer audio [0.8,1.8) 322 sourceBufferAudio.timestampOffset = 0.8; 323 sourceBufferAudio.appendWindowEnd = 1.8; 324 sourceBufferAudio.appendBuffer(dataAudio); 325 326 // Buffer video [1.5,3) 327 sourceBufferVideo.timestampOffset = 1.5; 328 sourceBufferVideo.appendWindowEnd = 3; 329 sourceBufferVideo.appendBuffer(dataVideo); 330 331 test.expectEvent(sourceBufferAudio, "updateend"); 332 test.expectEvent(sourceBufferVideo, "updateend"); 333 test.waitForExpectedEvents(function() 334 { 335 var newDuration = 2.0; 336 337 // Verify the test setup 338 assert_equals(sourceBufferAudio.buffered.length, 1); 339 assert_equals(sourceBufferVideo.buffered.length, 1); 340 assert_greater_than(sourceBufferAudio.buffered.end(0), 1.5); 341 assert_less_than(sourceBufferAudio.buffered.end(0), newDuration); 342 assert_less_than(sourceBufferVideo.buffered.start(0), newDuration); 343 assert_greater_than(sourceBufferVideo.buffered.end(0), newDuration + 0.5); 344 345 // Verify the expected error 346 // We assume relocated test video has at least one coded 347 // frame presentation interval which fits in [>2.0,>2.5) 348 assert_throws_dom("InvalidStateError", function () { mediaSource.duration = newDuration; }); 349 test.done(); 350 }); 351 }); 352 }); 353 }, "Truncating the duration throws an InvalidStateError exception when new duration is less than a buffered coded frame presentation time"); 354 355 mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) 356 { 357 assert_less_than(segmentInfo.duration, 60, 'Sufficient test media duration'); 358 sourceBuffer.appendBuffer(mediaData); 359 test.expectEvent(sourceBuffer, 'updateend', 'Media data appended to the SourceBuffer'); 360 test.waitForExpectedEvents(function() 361 { 362 mediaSource.duration = 60; 363 assert_false(sourceBuffer.updating, 'No SourceBuffer update when duration is increased'); 364 test.done(); 365 }); 366 }, 'Increasing the duration does not trigger any SourceBuffer update'); 367 368 mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData) 369 { 370 assert_greater_than(segmentInfo.duration, 2, 'Sufficient test media duration'); 371 mediaElement.play(); 372 sourceBuffer.appendBuffer(mediaData); 373 test.expectEvent(sourceBuffer, 'updateend', 'Media data appended to the SourceBuffer'); 374 test.waitForExpectedEvents(function() 375 { 376 mediaSource.duration = 60; 377 assert_false(sourceBuffer.updating, 'No SourceBuffer update when duration is increased'); 378 test.done(); 379 }); 380 }, 'Increasing the duration during media playback does not trigger any SourceBuffer update'); 381 </script> 382 </body> 383 </html>