Vanilagy / mp4-muxer

MP4 multiplexer in pure TypeScript with support for WebCodecs API, video & audio.
https://vanilagy.github.io/mp4-muxer/demo
MIT License
419 stars 32 forks source link

Ability to set metadata in output mp4 file #68

Closed BryanChrisBrown closed 1 month ago

BryanChrisBrown commented 1 month ago

I'm not precisely sure what the correct name is for this kind of data, but for my use case I need to be able to set the track's width and height independent of the "outputWidth" and "outputHeight" variables.

Is there a way to expose this kind of data in the library?

image image
Vanilagy commented 1 month ago

Which of these do you want to override? The ones in tkhd? Why do you want to do this?

It is beyond this library's scope to allow for manual tampering with metadata as I want to keep the API minimal. You can try to patch the bytes manually though:

let patchMade = false;
function fixTkhdWidthHeight(data: Uint8Array, targetWidth: number, targetHeight: number): void {
    if (patchMade) return;

    const tkhdString = 'tkhd';
    const tkhdBytes = tkhdString.split('').map(char => char.charCodeAt(0));
    let offset = 0;

    // Simple byte-by-byte search for "tkhd"
    while (offset < data.length - 4) {
        if (
            data[offset] === tkhdBytes[0] &&
            data[offset + 1] === tkhdBytes[1] &&
            data[offset + 2] === tkhdBytes[2] &&
            data[offset + 3] === tkhdBytes[3]
        ) {
            // We found "tkhd", process the box
            const tkhdOffset = offset;
            const version = data[tkhdOffset + 4]; // Version byte
            let widthOffset: number;
            let heightOffset: number;

            if (version === 1) {
                // Version 1, 64-bit fields
                widthOffset = tkhdOffset + 84; // Width offset for version 1 tkhd
                heightOffset = tkhdOffset + 88; // Height offset for version 1 tkhd
            } else {
                // Version 0, 32-bit fields
                widthOffset = tkhdOffset + 76; // Width offset for version 0 tkhd
                heightOffset = tkhdOffset + 80; // Height offset for version 0 tkhd
            }

            const width = readFixed16_16(data, widthOffset);
            if (width !== 0) {
                // Assume it's the video track if the width is non-zero
                writeFixed16_16(data, widthOffset, targetWidth);
                writeFixed16_16(data, heightOffset, targetHeight);

                patchMade = true;
                break;
            }
        }
        offset++;
    }
}

function readUint32(data: Uint8Array, offset: number): number {
    return (
        (data[offset] << 24) |
        (data[offset + 1] << 16) |
        (data[offset + 2] << 8) |
        data[offset + 3]
    ) >>> 0;
}

function readFixed16_16(data: Uint8Array, offset: number): number {
    const value = readUint32(data, offset);
    return value / 65536;
}

function writeUint32(data: Uint8Array, offset: number, value: number): void {
    data[offset] = (value >>> 24) & 0xff;
    data[offset + 1] = (value >>> 16) & 0xff;
    data[offset + 2] = (value >>> 8) & 0xff;
    data[offset + 3] = value & 0xff;
}

function writeFixed16_16(data: Uint8Array, offset: number, value: number): void {
    const fixedValue = Math.round(value * 65536);
    writeUint32(data, offset, fixedValue);
}

I haven't tested this code, but perhaps it's something you can work with.