Vanilagy / webm-muxer

WebM multiplexer in pure TypeScript with support for WebCodecs API, video & audio.
https://vanilagy.github.io/webm-muxer/demo
MIT License
230 stars 17 forks source link

Create webm/audio #38

Closed weepy closed 3 months ago

weepy commented 4 months ago

Hi I am wondering if it's possible to create a webm/audio file from a PCM audio buffer using this library ? I was trying to figure it out from the demo but couldnt get it to work quite.

Vanilagy commented 4 months ago

Should work, I assume by webm/audio you just mean a WebM file with only an audio track. On this page, I was able to find these codec strings which you may have to use:

image

Note that it's technically not a WebM file if you use these codecs, but a Matroska (MKV) file instead.

What exactly have you tried?

weepy commented 4 months ago

I was able to get this working =>

import { Muxer, ArrayBufferTarget } from 'webm-muxer'

function audioBufferToAudioData(audioBuffer) {
    const numberOfChannels = audioBuffer.numberOfChannels
    const length = audioBuffer.length
    const sampleRate = audioBuffer.sampleRate

    // Create an ArrayBuffer to hold the planar audio data
    const dataBuffer = new ArrayBuffer(length * numberOfChannels * Float32Array.BYTES_PER_ELEMENT)
    const float32Array = new Float32Array(dataBuffer)

    // Store the audio data in a planar format
    for (let channel = 0; channel < numberOfChannels; channel++) {
        const channelData = audioBuffer.getChannelData(channel)
        float32Array.set(channelData, channel * length)
    }

    // Create the AudioData object
    const audioData = new AudioData({
        format: 'f32-planar', // Float32 planar format
        sampleRate: sampleRate,
        numberOfFrames: length,
        numberOfChannels: numberOfChannels,
        timestamp: 0, // You can set an appropriate timestamp here
        data: dataBuffer
    })

    return audioData
}

export async function encodeAudioBuffer(audioBuffer, bitrate = 128000) {
    let muxer = new Muxer({
        target: new ArrayBufferTarget(),
        audio: {
            codec: 'A_OPUS',
            sampleRate: audioBuffer.sampleRate,
            numberOfChannels: audioBuffer.numberOfChannels
        },
        firstTimestampBehavior: 'offset'
    })

    const audioEncoder = new AudioEncoder({
        output: (chunk, meta) => muxer.addAudioChunk(chunk, meta),
        error: (e) => console.error(e)
    })
    audioEncoder.configure({
        codec: 'opus',
        numberOfChannels: audioBuffer.numberOfChannels,
        sampleRate: audioBuffer.sampleRate,
        bitrate
    })

    const audioData = audioBufferToAudioData(audioBuffer)
    audioEncoder.encode(audioData)

    await audioEncoder.flush()

    muxer.finalize()

    const buffer = muxer.target.buffer
    const mimeType = 'audio/webm; codecs="opus"'
    const blob = new Blob([buffer], { type: mimeType })

    return blob
}

but unfort Safari doesn't support AudioEncoder :(

Vanilagy commented 4 months ago

Maybe it's time to just fuck Safari 😂

Excuse the language, but if I were you, I'd look for some WASM-based audio encoders to fall back to in place of AudioEncoder. Something like this: https://github.com/arseneyr/wasm-media-encoders

Or here for Opus, which is perfect for WebM: https://github.com/mmig/opus-encdec

weepy commented 4 months ago

Right - I would gladly fuck sarari - unfort it needs to work on iOS so ... opus-encdec looks good.

Do you know how you would mux that into a webm ? B/c otherwise it won't play on safafi LMAO!

Vanilagy commented 4 months ago

No clue, I haven't actually used those libs, but you probably wanna see if there's some API where you put PCM data in (just raw audio data) and get some encoded chunks out, and then you just pass those to the muxer using the addAudioChunkRaw method.

Vanilagy commented 3 months ago

Closing this issue for now as it's stale. If there's anything new, feel free to still write here.