github.com/0chain/gosdk@v1.17.11/wasmsdk/demo/player.js (about) 1 async function stopPlay({goWasm, videoElement}){ 2 3 if (!videoElement) { 4 throw new Error('video element is required'); 5 } 6 7 await goWasm.sdk.stop() 8 9 videoElement.pause() 10 URL.revokeObjectURL(videoElement.src); 11 } 12 13 async function startPlay({ 14 goWasm, 15 allocationId, 16 containerElement, 17 videoElement, 18 remotePath, 19 authTicket = '', 20 lookupHash = '', 21 mimeType = '', 22 isLive = false, 23 }) { 24 if (!videoElement) { 25 throw new Error('video element is required'); 26 } 27 28 videoElement.addEventListener('error', function () { 29 switch (videoElement.error.code) { 30 case videoElement.error.MEDIA_ERR_ABORTED: 31 console.error("Fetch aborted by the user."); 32 break; 33 case videoElement.error.MEDIA_ERR_NETWORK: 34 console.error("Network error occurred."); 35 break; 36 case videoElement.error.MEDIA_ERR_DECODE: 37 console.error("Media decoding failed."); 38 break; 39 case videoElement.error.MEDIA_ERR_SRC_NOT_SUPPORTED: 40 console.error("Media not supported."); 41 break; 42 default: 43 console.error("An unknown error occurred."); 44 break; 45 } 46 }); 47 48 49 await goWasm.sdk.play( 50 allocationId, remotePath, authTicket, lookupHash, isLive); 51 52 if (isLive) { 53 return playStream({goWasm, videoElement,allocationId,remotePath,authTicket, lookupHash}); 54 } 55 56 // first segment 57 const buf = await goWasm.sdk.getNextSegment(); 58 59 const {isFragmented, mimeCodecs} = getMimeCodecs({mimeType, buf}); 60 61 // const mimeCodecs = `${mimeType}; 62 // codecs="${muxjs.mp4.probe.tracks(buf).map(t => t.codec).join(",")}"`; 63 console.log( 64 `playlist: isFragmented:${isFragmented} mimeCodecs:${mimeCodecs}`); 65 66 if (isFragmented && MediaSource.isTypeSupported(mimeCodecs)) { 67 return playChunks({goWasm, videoElement, buf, mimeCodecs}); 68 } 69 70 goWasm.sdk.stop(); 71 // allocationID, remotePath, authTicket, lookupHash string, 72 // downloadThumbnailOnly, autoCommit bool 73 const {url} = await goWasm.sdk.download( 74 allocationId, remotePath, authTicket, lookupHash, false,10,""); 75 videoElement.crossOrigin = 'anonymous'; 76 videoElement.src = url 77 78 var promise = videoElement.play(); 79 if (promise !== undefined) { 80 promise.catch(err => { 81 // Auto-play was prevented 82 while (videoElement.lastElementChild) { 83 videoElement.removeChild(videoElement.lastElementChild); 84 } 85 videoElement.removeAttribute("src") 86 87 const source = document.createElement("source") 88 source.setAttribute("src", url) 89 source.setAttribute("type", mimeType) 90 videoElement.appendChild(source) 91 92 videoElement.setAttribute("muted",true) 93 videoElement.setAttribute("autoplay",true) 94 videoElement.setAttribute("playsinline",true) 95 videoElement.setAttribute("loop",true) 96 setTimeout(function() { 97 // weird fix for safari 98 containerElement.innerHTML = videoElement.outerHTML; 99 }, 100); 100 }).then(() => { 101 // Auto-play started 102 }); 103 } 104 } 105 106 107 async function playStream({ 108 goWasm, 109 videoElement, 110 allocationId, 111 remotePath, 112 authTicket, 113 lookupHash 114 }) { 115 await goWasm.sdk.play( 116 allocationId, remotePath, authTicket, lookupHash, true); 117 118 const mimeCodecs = 'video/mp4; codecs="mp4a.40.2,avc1.64001f"'; 119 120 if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodecs)) { 121 let sourceBuffer; 122 123 const transmuxer = new muxjs.mp4.Transmuxer(); 124 const mediaSource = new MediaSource(); 125 126 const getNextSegment = async () => { 127 try { 128 const buf = await goWasm.sdk.getNextSegment() 129 130 if (buf?.length > 0) { 131 transmuxer.push(new Uint8Array(buf)) 132 transmuxer.flush() 133 } 134 } catch (err) { 135 getNextSegment() 136 } 137 }; 138 139 transmuxer.on('data', segment => { 140 const data = new Uint8Array( 141 segment.initSegment.byteLength + segment.data.byteLength); 142 data.set(segment.initSegment, 0); 143 data.set(segment.data, segment.initSegment.byteLength); 144 // To inspect data use => 145 // console.log(muxjs.mp4.tools.inspect(data)); 146 sourceBuffer.appendBuffer(data); 147 }) 148 149 mediaSource.addEventListener('sourceopen', async () => { 150 sourceBuffer = mediaSource.addSourceBuffer(mimeCodecs); 151 sourceBuffer.mode = 'sequence'; 152 sourceBuffer.addEventListener('updateend', getNextSegment); 153 154 await getNextSegment(); 155 156 videoElement.play(); 157 158 URL.revokeObjectURL(videoElement.src); 159 }) 160 161 videoElement.src = URL.createObjectURL(mediaSource); 162 videoElement.crossOrigin = 'anonymous'; 163 } else { 164 throw new Error('Unsupported MIME type or codec: ', mimeCodecs); 165 } 166 } 167 168 169 async function playChunks({goWasm, videoElement, buf, mimeCodecs}) { 170 let sourceBuffer; 171 172 const mediaSource = new MediaSource(); 173 174 videoElement.src = URL.createObjectURL(mediaSource); 175 videoElement.crossOrigin = 'anonymous'; 176 177 const getNextSegment = async () => { 178 try { 179 const buffer = await goWasm.sdk.getNextSegment() 180 181 if (buffer?.length > 0) { 182 sourceBuffer.appendBuffer(new Uint8Array(buffer)) 183 } 184 else { 185 if (!sourceBuffer.updating && mediaSource.readyState === 'open') { 186 mediaSource.endOfStream() 187 } 188 } 189 } catch (err) { 190 getNextSegment() 191 } 192 }; 193 194 mediaSource.addEventListener('sourceopen', async () => { 195 sourceBuffer = mediaSource.addSourceBuffer(mimeCodecs); 196 sourceBuffer.mode = 'sequence' 197 sourceBuffer.addEventListener('updateend', getNextSegment) 198 sourceBuffer.appendBuffer(buf) 199 videoElement.play() 200 }) 201 } 202 203 204 function detectMp4({mimeType, buf}) { 205 const isFragmented = 206 muxjs.mp4.probe.findBox(buf, ['moov', 'mvex']).length > 0 ? true : false; 207 208 return { 209 isFragmented, 210 mimeCodecs: `${mimeType}; codecs="${ 211 muxjs.mp4.probe.tracks(buf).map(t => t.codec).join(',')}"`, 212 } 213 } 214 215 function detectWebm({mimeType, buf}) { 216 const decoder = new EBML.Decoder(); 217 const codecs = 218 decoder.decode(buf) 219 .filter(it => it.name == 'CodecID') 220 .map(it => it.value.replace(/^(V_)|(A_)/, '').toLowerCase()) 221 .join(',') 222 return { 223 isFragmented: true, mimeCodecs: `${mimeType}; codecs="${codecs}"` 224 } 225 } 226 227 const detectors = { 228 'video/mp4': detectMp4, 229 'audio/mp4': detectMp4, 230 'video/webm': detectWebm, 231 'audio/webm': detectWebm, 232 } 233 234 function getMimeCodecs({mimeType, buf}) { 235 const detect = detectors[mimeType]; 236 if (detect) { 237 return detect({mimeType, buf}) 238 } 239 return mimeType 240 }