diff --git a/index.html b/index.html index d3f1108..00782e9 100644 --- a/index.html +++ b/index.html @@ -254,7 +254,7 @@ for (let i = 0; i < 6; i++) { let noWorker = false; try { - renderWorkers.push(new Worker('js/render-worker.js?v=$Format:1.%cs.%ct.%h$')); + renderWorkers.push(new Worker('js/render-worker.js?v=$Format:1/%cs/%ct/%h$')); } catch { if (i !== 0) break; noWorker = true; @@ -268,7 +268,7 @@ }; const renderScript = document.createElement('script'); - renderScript.src = 'js/render-worker.js'; + renderScript.src = 'js/render-worker.js?v=$Format:1/%cs/%ct/%h$'; document.head.appendChild(renderScript); renderWorkers.push(window.renderWorkerCompat.proxy); } @@ -337,7 +337,7 @@ renderBusy[i] = true; resolve([renderCb[0].then(v => { - const [blockImage, renderFrame] = v; + const [blockImage, renderFrame, , rot] = v; // let graphCtx; // const outputDim = config.outputDim; // const tsX = outputDim / 2 / videoDurationUs; @@ -351,6 +351,7 @@ // graphCtx.fillRect(frame.timestamp * tsX, 128-v/2, Math.max(1,frame.duration*tsX), 1); // } blockImage.close(); + renderFrame['rotationNum'] = rot; return renderFrame; })]); return; @@ -797,28 +798,22 @@ document.getElementById('status').textContent = `Analyzing ${(frame.timestamp/1000000).toFixed(2)}/${(videoDurationUs/1000000).toFixed(2)}`; fbCtx.drawImage(frame, config.cropL, config.cropT, frameBuffer.width, frameBuffer.height, 0, 0, frameBuffer.width, frameBuffer.height); - const data = fbCtx.getImageData(2, 2, 4, 4); + const data = fbCtx.getImageData(2, 2, 3, 3); r = Array.from({length: data.height * data.width}, (_, i) => data.data[i * 4]).reduce((a, b) => a + b) / data.height / data.width; g = Array.from({length: data.height * data.width}, (_, i) => data.data[i * 4 + 1]).reduce((a, b) => a + b) / data.height / data.width; b = Array.from({length: data.height * data.width}, (_, i) => data.data[i * 4 + 2]).reduce((a, b) => a + b) / data.height / data.width; + frame.close(); if (r > 127 && g > 127) { await vDec.flush(); - if (videoFrames.length) { - frame.close(); - } else { - videoFrames.unshift(frame); - } break; } - - frame.close(); } if (r > 127 && g > 127) break; } - if (r < 128 && g < 128 && b < 128) return; + if (r < 128 && g < 128) return; for(; blockSize <= lim; blockSize++) { const data = fbCtx.getImageData(1, 1, blockSize - 1, blockSize - 1); r = Array.from({length: data.height * data.width}, (_, i) => data.data[i*4]).reduce((a,b) => a+b) / data.height / data.width; @@ -835,6 +830,68 @@ document.getElementById('blockSize').value = blockSize; } + const guessOrientation = async function () { + config = getParameters(); + const seekLoHi = libav.f64toi64(videoDurationUs / 3); + let rotationNum = []; + await libav.avformat_seek_file_max(inCtx, -1, seekLoHi[0], seekLoHi[1], 0); + while ((await libav.av_read_frame(inCtx, avpPkt)) === 0) { + const avPkt = libav.ff_copyout_packet_sync(avpPkt); + if (avPkt.stream_index !== ivStream.index) continue; + let evc = LibAVWebCodecsBridge.packetToEncodedVideoChunk(avPkt, ivStream); + vDec.decode(evc); + libav.av_packet_unref_sync(avpPkt); + if (vDec.decodeQueueSize > 2) + await delay(evc.duration * vDec.decodeQueueSize / 2000); + + const processTasks = []; + while (videoFrames.length) { + const frame = videoFrames.shift(); + document.getElementById('status').textContent = `Analyzing ${(frame.timestamp/1000000).toFixed(2)}/${(videoDurationUs/1000000).toFixed(2)}`; + processTasks.push((await processFrame(frame))[0].then(frame => { + if (rotationNum.length || frame.rotationNum) + rotationNum.push(frame.rotationNum); + + frame.close(); + })); + } + + await Promise.all(processTasks); + if (rotationNum.length > 127) + break; + } + + const votes = [0,0,0,0]; + rotationNum = rotationNum.map(x => [ + Math.floor(x / 512) % 8, + Math.floor(x / 64) % 8, + Math.floor(x / 8) % 8, + Math.floor(x / 1) % 8, + ]); + + for(let d = 0; d < 3; d++) { + const vote = Array.from({length: 4}, (_, i) => { + const series = rotationNum.map(x => x[i]); + let e = Array.from({length: 8}).map(() => 0); + series.forEach(x => e[x]++); + e = e.map(x => x ? Math.log2(series.length / x) : 0); + return series.reduce((a, b) => a + e[b], 0) / series.length; + }); + + vote.forEach((v, i) => votes[i] += v * (d ? 2 : 1)); + + rotationNum = rotationNum.map((x, i, a) => { + if (!i) return [0,0,0,0]; + return x.map((v, n) => v-a[i-1][n]).map(v => v < 0 ? v + 8: v) + }); + rotationNum.shift(); + } + + const votedOption = votes.indexOf(Math.max(...votes)); + const options = document.getElementById('orientation').querySelectorAll('option'); + options.forEach((e, i) => e.selected = i === votedOption); + } + document.getElementById('inputFile').onchange = async function () { document.getElementById('status').textContent = 'Loading'; @@ -966,6 +1023,7 @@ document.getElementById('status').textContent = 'Analyzing'; await estimateCrop(ivConfig.codedWidth, ivConfig.codedHeight); await estimateBlockSize(Math.floor(Math.max(ivConfig.codedWidth, ivConfig.codedHeight) / 100)); + await guessOrientation(); while (videoFrames.length) videoFrames.pop().close(); @@ -1313,8 +1371,8 @@ diff --git a/js/render-worker.js b/js/render-worker.js index 01f1e8b..dd86dd0 100644 --- a/js/render-worker.js +++ b/js/render-worker.js @@ -53,7 +53,7 @@ if (typeof prevRot === 'number') { rot = prevRot; } else { - messaging.postMessage([renderId, 0, frame, thr, scale, rotPx], [frame]); + messaging.postMessage([renderId, 0, frame, thr, scale], [frame]); return; } } else { @@ -87,7 +87,7 @@ const renderFrame = await createImageBitmap(renderCtx.canvas); const blockImage = await createImageBitmap(blockCanvasCtx.canvas); - messaging.postMessage([renderId, blockImage, renderFrame, thr, rot, rotPx], [blockImage, renderFrame]); + messaging.postMessage([renderId, blockImage, renderFrame, thr, rot], [blockImage, renderFrame]); frame.close(); } })(self.renderWorkerCompat ?? self);