mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-27 10:53:50 +02:00
536 lines
13 KiB
HTML
536 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<script src="../dist/metamuxer.js"></script>
|
|
|
|
<script type="module">
|
|
const fileInput = document.createElement('input');
|
|
fileInput.type = 'file';
|
|
document.body.append(fileInput);
|
|
|
|
fileInput.addEventListener('change', async () => {
|
|
const file = fileInput.files[0];
|
|
const source = new Metamuxer.BlobSource(file);
|
|
|
|
const input = new Metamuxer.Input({
|
|
formats: Metamuxer.ALL_FORMATS,
|
|
source
|
|
});
|
|
|
|
const output = new Metamuxer.Output({
|
|
format: new Metamuxer.Mp3OutputFormat(),
|
|
target: new Metamuxer.BufferTarget()
|
|
});
|
|
|
|
const audioTrack = await input.getPrimaryAudioTrack();
|
|
const sink = new Metamuxer.AudioDataSink(audioTrack);
|
|
|
|
const mediaSource = new Metamuxer.AudioDataSource({
|
|
codec: 'mp3',
|
|
bitrate: 192000
|
|
});
|
|
output.addAudioTrack(mediaSource);
|
|
|
|
output.start();
|
|
|
|
for await (const { data } of sink.data()) {
|
|
await mediaSource.digest(data);
|
|
}
|
|
|
|
await output.finalize();
|
|
|
|
function download(blob, filename) {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
console.log(output.target.buffer)
|
|
download(new Blob([output.target.buffer]), 'converted.mp3');
|
|
|
|
|
|
/*
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
const sink = new Metamuxer.EncodedVideoSampleSink(videoTrack);
|
|
|
|
console.log(await sink.getSample(16));
|
|
console.log(await sink.getFirstSample());
|
|
*/
|
|
|
|
/*
|
|
const source = new Metamuxer.BufferSource(await file.arrayBuffer()) ?? new Metamuxer.BlobSource(file);
|
|
|
|
const start = performance.now();
|
|
const input = new Metamuxer.Input({
|
|
formats: Metamuxer.ALL_FORMATS,
|
|
source
|
|
});
|
|
|
|
const output = new Metamuxer.Output({
|
|
format: new Metamuxer.WaveOutputFormat(),
|
|
target: new Metamuxer.BufferTarget()
|
|
});
|
|
output.start();
|
|
*/
|
|
|
|
/*
|
|
const audioTrack = await input.getPrimaryAudioTrack();
|
|
const sink = new Metamuxer.EncodedAudioSampleSink(audioTrack);
|
|
|
|
let total = 0;
|
|
for await (const sample of sink.samples(undefined, undefined, { metadataOnly: true })) {
|
|
total++;
|
|
if (performance.now() > 10000) {
|
|
break;
|
|
}
|
|
}
|
|
console.log("Done", total);
|
|
*/
|
|
|
|
/*
|
|
const sink = new Metamuxer.VideoFrameSink(videoTrack);
|
|
|
|
console.log(await videoTrack.getLanguageCode());
|
|
|
|
for await (const thing of sink.framesAtTimestamps([0.2, 1, 2, 423])) {
|
|
console.log(thing)
|
|
}
|
|
console.log("Done")
|
|
*/
|
|
|
|
/*
|
|
//const videoTrack = await input.getPrimaryVideoTrack();
|
|
//console.log(await videoTrack.computeSampleStats());
|
|
//return;
|
|
|
|
const target = new Metamuxer.BufferTarget();
|
|
const output = new Metamuxer.Output({
|
|
format: new Metamuxer.WaveOutputFormat(),
|
|
target
|
|
});
|
|
|
|
console.log(await input.getFormat());
|
|
|
|
const audioTrack = await input.getPrimaryAudioTrack();
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
|
|
const sink = new Metamuxer.EncodedAudioSampleSink(audioTrack);
|
|
|
|
const decoderConfig = await audioTrack.getDecoderConfig();
|
|
const sampleSource = new Metamuxer.EncodedAudioSampleSource(await audioTrack.getCodec());
|
|
const audioDataSource = new Metamuxer.AudioDataSource({ codec: 'pcm-s16', bitrate: 128e3 });
|
|
const videoSampleSource = new Metamuxer.EncodedVideoSampleSource(await videoTrack.getCodec());
|
|
output.addAudioTrack(audioDataSource ?? sampleSource);
|
|
//output.addVideoTrack(videoSampleSource);
|
|
|
|
output.start();
|
|
|
|
const videoDecoderConfig = await videoTrack.getDecoderConfig();
|
|
|
|
|
|
|
|
|
|
//for await (const sample of sink.samples()) {
|
|
// //console.log(sample.timestamp);
|
|
// //sample.timestamp *= 2;
|
|
// //console.log(sample);
|
|
// //console.log(sample)
|
|
// await sampleSource.digest(sample, { decoderConfig });
|
|
//}
|
|
for await (const { data, timestamp } of new Metamuxer.AudioDataSink(audioTrack).data()) {
|
|
//console.log("wow", data.timestamp);
|
|
//console.log(sample)
|
|
await audioDataSource.digest(data);
|
|
}
|
|
//for await (const sample of new Metamuxer.EncodedVideoSampleSink(videoTrack).samples()) {
|
|
// //console.log(sample)
|
|
// await videoSampleSource.digest(sample, { decoderConfig: videoDecoderConfig });
|
|
//}
|
|
|
|
await output.finalize();
|
|
|
|
console.log(target)
|
|
|
|
function download(blob, filename) {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
download(new Blob([target.buffer]), 'converted.wav');
|
|
|
|
document.body.textContent = performance.now() - start;
|
|
*/
|
|
|
|
/*
|
|
const input2 = new Metamuxer.Input({
|
|
formats: Metamuxer.ALL_FORMATS,
|
|
source: new Metamuxer.BufferSource(target.buffer)
|
|
});
|
|
|
|
const videoTrack2 = await input2.getPrimaryVideoTrack();
|
|
const drain2 = new Metamuxer.EncodedVideoSampleDrain(videoTrack2);
|
|
|
|
let i = 0;
|
|
for await (const sample of drain2.samples(undefined, undefined)) {
|
|
console.log(sample);
|
|
if (i++ > 10) {
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
|
|
async function* timestamps() {
|
|
const fromTime = 0;
|
|
const toTime = await videoTrack.computeDuration();
|
|
const extractDuration = toTime - fromTime;
|
|
const drain = new Metamuxer.EncodedVideoSampleDrain(videoTrack);
|
|
const thumbnailsNeeded = 16;
|
|
|
|
for (let i = 0; i < thumbnailsNeeded; i++) {
|
|
const startTime = (extractDuration * i) / thumbnailsNeeded + fromTime;
|
|
const endTime = Math.min(
|
|
(extractDuration * (i + 1)) / thumbnailsNeeded + fromTime,
|
|
toTime + 1,
|
|
);
|
|
|
|
const sample = await drain.getSample(startTime, { onlyMetadata: true });
|
|
|
|
let bestTimestamp = sample.timestamp;
|
|
if (sample.type !== 'key') {
|
|
const nextKeySample = await drain.getNextKeySample(sample, { onlyMetadata: true });
|
|
if (nextKeySample.timestamp < endTime) {
|
|
bestTimestamp = nextKeySample.timestamp;
|
|
}
|
|
}
|
|
|
|
yield bestTimestamp;
|
|
}
|
|
}
|
|
|
|
for await (const { frame, timestamp } of drain.framesAtTimestamps(timestamps())) {
|
|
console.log(frame.timestamp, timestamp)
|
|
frame.close();
|
|
}
|
|
console.log("done")
|
|
*/
|
|
|
|
/*
|
|
const audioTrack = await input.getPrimaryVideoTrack();
|
|
const drain = new Metamuxer.EncodedVideoChunkDrain(audioTrack);
|
|
|
|
for await (const chunk of drain.chunks()) {
|
|
//console.log(chunk);
|
|
}
|
|
|
|
document.body.textContent = performance.now() - start;
|
|
*/
|
|
|
|
|
|
/*
|
|
const drain = new Metamuxer.AudioBufferDrain(audioTrack);
|
|
|
|
const chunks = drain.buffers()
|
|
const a = chunks.next();
|
|
*/
|
|
|
|
//chunks.return();
|
|
|
|
//console.log("Done")
|
|
|
|
/*
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
|
|
const target = new Metamuxer.ArrayBufferTarget();
|
|
const format = new Metamuxer.Mp4OutputFormat();
|
|
const output = new Metamuxer.Output({ target, format });
|
|
|
|
const mediaSource = new Metamuxer.VideoFrameSource({
|
|
codec: 'av1',
|
|
bitrate: 1e6
|
|
});
|
|
output.addVideoTrack(mediaSource);
|
|
|
|
output.start();
|
|
|
|
for await (const frame of drain.frames(0, 10)) {
|
|
await mediaSource.digest(frame);
|
|
frame.close();
|
|
}
|
|
|
|
await output.finalize();
|
|
function download(blob, filename) {
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
download(new Blob([target.buffer]), 'converted.mp4');
|
|
*/
|
|
|
|
/*
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
|
|
async function* timestamps() {
|
|
const fromTime = 0;
|
|
const toTime = await videoTrack.computeDuration();
|
|
const extractDuration = toTime - fromTime;
|
|
const drain = new Metamuxer.EncodedVideoChunkDrain(videoTrack);
|
|
const thumbnailsNeeded = 16;
|
|
|
|
for (let i = 0; i < thumbnailsNeeded; i++) {
|
|
const startTime = (extractDuration * i) / thumbnailsNeeded + fromTime;
|
|
const endTime = Math.min(
|
|
(extractDuration * (i + 1)) / thumbnailsNeeded + fromTime,
|
|
toTime + 1,
|
|
);
|
|
|
|
const chunk = await drain.getChunk(startTime, { onlyMetadata: true });
|
|
|
|
let bestTimestamp = chunk.timestamp / 1e6;
|
|
if (chunk.type !== 'key') {
|
|
const nextKeyChunk = await drain.getNextKeyChunk(chunk, { onlyMetadata: true });
|
|
if (nextKeyChunk.timestamp / 1e6 < endTime) {
|
|
bestTimestamp = nextKeyChunk.timestamp / 1e6;
|
|
}
|
|
}
|
|
|
|
yield bestTimestamp;
|
|
}
|
|
}
|
|
|
|
for await (const frame of drain.framesAtTimestamps(timestamps())) {
|
|
const clone = structuredClone(frame);
|
|
frame.close();
|
|
console.log(clone)
|
|
}
|
|
console.log("done")
|
|
*/
|
|
|
|
/*
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
const width = await videoTrack.getWidth();
|
|
const height = await videoTrack.getHeight();
|
|
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
document.body.append(canvas);
|
|
|
|
const context = canvas.getContext('2d');
|
|
|
|
// Top-level for await loop inside the event listener
|
|
for await (const frame of drain.frames()) {
|
|
context.drawImage(frame, 0, 0, width, height);
|
|
frame.close();
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 1000 / 24));
|
|
}
|
|
*/
|
|
|
|
/*
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
const audioTrack = await input.getPrimaryAudioTrack();;
|
|
console.log(videoTrack.computeDuration());
|
|
console.log(await audioTrack.computeDuration());
|
|
*/
|
|
|
|
/*
|
|
const audioTrack = await input.getPrimaryAudioTrack();
|
|
const drain = new Metamuxer.AudioBufferDrain(audioTrack);
|
|
|
|
const context = new AudioContext();
|
|
const startTime = context.currentTime;
|
|
|
|
for await (const { buffer, timestamp } of drain.buffers()) {
|
|
const node = context.createBufferSource();
|
|
node.buffer = buffer;
|
|
node.connect(context.destination);
|
|
const start = startTime + timestamp
|
|
node.start(start);
|
|
|
|
if (start > context.currentTime + 5) {
|
|
await new Promise(resolve => {
|
|
const id = setInterval(() => {
|
|
if (start < context.currentTime + 5) {
|
|
clearInterval(id);
|
|
resolve();
|
|
}
|
|
}, 100);
|
|
});
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
const drain = new Metamuxer.AudioDataDrain(audioTrack);
|
|
|
|
console.time()
|
|
for await (const data of drain.data()) {
|
|
data.close();
|
|
}
|
|
console.timeEnd()
|
|
*/
|
|
|
|
|
|
/*
|
|
const drain = new Metamuxer.EncodedAudioChunkDrain(audioTrack);
|
|
|
|
for await (const chunk of drain.chunks()) {
|
|
console.log(chunk);
|
|
|
|
if (chunk.timestamp > 1e6) {
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
const width = await videoTrack.getWidth();
|
|
const height = await videoTrack.getHeight();
|
|
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
document.body.append(canvas);
|
|
|
|
const context = canvas.getContext('2d');
|
|
|
|
// Top-level for await loop inside the event listener
|
|
for await (const frame of drain.frames()) {
|
|
context.drawImage(frame, 0, 0, width, height);
|
|
frame.close();
|
|
}
|
|
*/
|
|
|
|
/*
|
|
const drain = new Metamuxer.EncodedVideoChunkDrain(videoTrack);
|
|
|
|
const startChunk = await drain.getFirstChunk();
|
|
for await (const chunk of drain.chunks(startChunk)) {
|
|
console.log(chunk.timestamp);
|
|
}
|
|
*/
|
|
|
|
|
|
/*
|
|
for (let i = 0; i < 2; i += 0.1) {
|
|
console.log(await drain.getChunk(i));
|
|
}
|
|
*/
|
|
|
|
/*
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
|
|
for await (const frame of drain.frames()) {
|
|
// ...
|
|
}
|
|
|
|
console.log(await videoTrack.getDuration());
|
|
*/
|
|
|
|
/*
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
|
|
|
|
const width = await videoTrack.getWidth();
|
|
const height = await videoTrack.getHeight();
|
|
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
document.body.append(canvas);
|
|
|
|
const context = canvas.getContext('2d');
|
|
|
|
// Top-level for await loop inside the event listener
|
|
for await (const frame of drain.frames()) {
|
|
context.drawImage(frame, 0, 0, width, height);
|
|
frame.close();
|
|
}
|
|
*/
|
|
|
|
/*
|
|
const videoTrack = await input.getPrimaryVideoTrack();
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
|
|
for await (const frame of drain.frames(0.01)) {
|
|
console.log(frame)
|
|
frame.close()
|
|
|
|
if (frame.timestamp > 2e6) {
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
const drain = new Metamuxer.EncodedVideoChunkDrain(videoTrack);
|
|
|
|
const decoder = new VideoDecoder({
|
|
output: console.log,
|
|
error: console.error
|
|
});
|
|
decoder.configure({
|
|
...await videoTrack.getDecoderConfig()
|
|
});
|
|
|
|
const firstChunk = await drain.getFirstChunk();
|
|
const secondChunk = await drain.getNextChunk(firstChunk);
|
|
const thirdChunk = await drain.getNextChunk(secondChunk);
|
|
|
|
console.log(firstChunk, secondChunk, thirdChunk);
|
|
|
|
decoder.decode(firstChunk);
|
|
decoder.decode(secondChunk);
|
|
decoder.decode(thirdChunk);
|
|
|
|
decoder.flush();
|
|
*/
|
|
|
|
|
|
/*
|
|
const drain = new Metamuxer.VideoFrameDrain(videoTrack);
|
|
|
|
const frame = await drain.getKeyFrame(69);
|
|
|
|
const canvas = document.createElement('canvas')
|
|
canvas.width = await videoTrack.getWidth();
|
|
canvas.height = await videoTrack.getHeight();
|
|
const context = canvas.getContext('2d');
|
|
context.drawImage(frame, 0, 0);
|
|
|
|
document.body.append(canvas);
|
|
*/
|
|
|
|
/*
|
|
const drain = new Metamuxer.EncodedVideoChunkDrain(videoTrack);
|
|
|
|
for await (const chunk of drain.chunks()) {
|
|
console.log(chunk);
|
|
|
|
if (chunk.timestamp > 1e6) {
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
});
|
|
</script> |