h264-profile-levels.https.html (4325B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <title>RTCPeerConnection H.264 profile levels</title> 4 <meta name="timeout" content="long"> 5 <script src="../third_party/sdp/sdp.js"></script> 6 <script src="simulcast.js"></script> 7 <script src="../RTCPeerConnection-helper.js"></script> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script> 11 12 function mungeLevel(sdp, level) { 13 level_hex = Math.round(level * 10).toString(16).padStart(2, '0'); 14 return { 15 type: sdp.type, 16 sdp: sdp.sdp.replace(/(profile-level-id=....)(..)/g, 17 "$1" + level_hex) 18 } 19 } 20 21 // Numbers taken from 22 // https://en.wikipedia.org/wiki/Advanced_Video_Coding#Levels 23 let levelTable = { 24 1: {mbs: 1485, fs: 99}, 25 1.1: {mbs: 3000, fs: 396}, 26 1.2: {mbs: 6000, fs: 396}, 27 1.3: {mbs: 11880, fs: 396}, 28 2: {mbs: 11880, fs: 396}, 29 2.1: {mbs: 19800, fs: 792}, 30 2.2: {mbs: 20250, fs: 1620}, 31 3: {mbs: 40500, fs: 1620}, 32 3.1: {mbs: 108000, fs: 3600}, 33 3.2: {mbs: 216000, fs: 5120}, 34 4: {mbs: 245760, fs: 8192}, 35 4.1: {mbs: 245760, fs: 8192}, 36 4.2: {mbs: 522240, fs: 8704}, 37 5: {mbs: 589824, fs: 22800}, 38 5.1: {mbs: 983040, fs: 36864}, 39 5.2: {mbs: 2073600, fs: 36864}, 40 6: {mbs: 4177920, fs: 139264}, 41 6.1: {mbs: 8355840, fs: 139264}, 42 6.2: {mbs: 16711680, fs: 139264}, 43 }; 44 45 function sizeFitsLevel(width, height, fps, level) { 46 const frameSizeMacroblocks = width * height / 256; 47 const macroblocksPerSecond = frameSizeMacroblocks * fps; 48 assert_less_than_equal(frameSizeMacroblocks, 49 levelTable[level].fs, 'frame size'); 50 assert_less_than_equal(macroblocksPerSecond, 51 levelTable[level].mbs, 'macroblocks/second'); 52 } 53 54 // Constant for now, may be variable later. 55 const framesPerSecond = 30; 56 57 for (let level of Object.keys(levelTable)) { 58 promise_test(async t => { 59 assert_implements('getCapabilities' in RTCRtpSender, 'RTCRtpSender.getCapabilities not supported'); 60 assert_implements(RTCRtpSender.getCapabilities('video').codecs.find(c => c.mimeType === 'video/H264'), 'H264 not supported'); 61 62 const pc1 = new RTCPeerConnection(); 63 t.add_cleanup(() => pc1.close()); 64 const pc2 = new RTCPeerConnection(); 65 t.add_cleanup(() => pc2.close()); 66 const v = document.createElement('video'); 67 68 // Generate the largest video we can get from the attached device. 69 // This means platform inconsistency. 70 // The fake video in Chrome WPT tests is 3840x2160. 71 const stream = await navigator.mediaDevices.getUserMedia( 72 {video: {width: 12800, height: 7200, frameRate: framesPerSecond}}); 73 t.add_cleanup(() => stream.getTracks().forEach(track => track.stop())); 74 const transceiver = pc1.addTransceiver(stream.getVideoTracks()[0], { 75 streams: [stream], 76 }); 77 preferCodec(transceiver, 'video/H264'); 78 79 exchangeIceCandidates(pc1, pc2); 80 const trackEvent = new Promise(r => pc2.ontrack = r); 81 82 const offer = await pc1.createOffer(); 83 await pc1.setLocalDescription(offer), 84 await pc2.setRemoteDescription(offer); 85 const answer = await pc2.createAnswer(); 86 await pc2.setLocalDescription(answer); 87 await pc1.setRemoteDescription(mungeLevel(answer, level)); 88 89 v.srcObject = new MediaStream([(await trackEvent).track]); 90 let metadataLoaded = new Promise((resolve) => { 91 v.autoplay = true; 92 v.id = stream.id 93 v.addEventListener('loadedmetadata', () => { 94 resolve(); 95 }); 96 }); 97 await metadataLoaded; 98 // Ensure that H.264 is in fact used. 99 const statsReport = await transceiver.sender.getStats(); 100 for (const stats of statsReport.values()) { 101 if (stats.type === 'outbound-rtp') { 102 const activeCodec = stats.codecId; 103 const codecStats = statsReport.get(activeCodec); 104 assert_implements_optional(codecStats.mimeType ==='video/H264', 105 'Level ' + level + ' H264 video is not supported'); 106 } 107 } 108 // TODO(hta): This will not catch situations where the initial size is 109 // within the permitted bounds, but resolution or framerate changes to 110 // outside the permitted bounds after a while. Should be addressed. 111 sizeFitsLevel(v.videoWidth, v.videoHeight, framesPerSecond, level); 112 }, 'Level ' + level + ' H264 video is appropriately constrained'); 113 114 } 115 </script>