Add license headers, add @mediabunny/server to build process, add doc blocks, small fixes

This commit is contained in:
Vanilagy
2026-05-12 15:04:32 +02:00
parent 633d1bc235
commit af43f67835
15 changed files with 197 additions and 27 deletions
+4 -2
View File
@@ -2,7 +2,9 @@
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "dist/modules/src/index.d.ts",
"bundledPackages": [],
"compiler": {},
"compiler": {
"tsconfigFilePath": "<projectFolder>/tsconfig.api-extractor.json"
},
"apiReport": {
"enabled": false
},
@@ -11,7 +13,7 @@
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "dist/mediabunny-flac-encoder.d.ts"
"untrimmedFilePath": "dist/mediabunny-server.d.ts"
},
"tsdocMetadata": {
"enabled": false
+16 -3
View File
@@ -1,4 +1,12 @@
import { AudioCodec, AudioSample, CustomAudioDecoder, EncodedPacket, MaybePromise } from 'mediabunny';
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { AudioCodec, AudioSample, CustomAudioDecoder, EncodedPacket, type MaybePromise } from 'mediabunny';
import * as NodeAv from 'node-av';
import { CODEC_TO_CODEC_ID, getChannelLayout } from './misc';
import { assert, toUint8Array } from '../../../src/misc';
@@ -76,8 +84,13 @@ export class NodeAvAudioDecoder extends CustomAudioDecoder {
receiveFrame(ret: number) {
NodeAv.FFmpegError.throwIfError(ret, 'Receive frame');
const timestamp = Number(this.frame.pts) / this.config.sampleRate;
this.onSample(new AudioSample(new NodeAvFrameAudioSampleResource(this.frame, timestamp)));
const clone = this.frame.clone();
if (!clone) {
throw new Error('Allocation failure during frame clone.');
}
clone.timeBase = new NodeAv.Rational(1, this.config.sampleRate);
this.onSample(new AudioSample(new NodeAvFrameAudioSampleResource(clone)));
}
async flush(): Promise<void> {
+16 -1
View File
@@ -1,4 +1,19 @@
import { AudioCodec, AudioSample, CustomAudioEncoder, MaybePromise, QUALITY_MEDIUM, EncodedPacket } from 'mediabunny';
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import {
AudioCodec,
AudioSample,
CustomAudioEncoder,
type MaybePromise,
QUALITY_MEDIUM,
EncodedPacket,
} from 'mediabunny';
import * as NodeAv from 'node-av';
import { CODEC_TO_CODEC_ID, fromAudioSampleFormat, getChannelLayout } from './misc';
import { assert, toUint8Array } from '../../../src/misc';
+39 -11
View File
@@ -1,22 +1,49 @@
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { AudioSampleResource } from 'mediabunny';
import * as NodeAv from 'node-av';
import { toAudioSampleFormat } from './misc';
import { assert, toUint8Array } from '../../../src/misc';
/**
* A custom `AudioSampleResource` backed by NodeAV's
* [`Frame`](https://seydx.github.io/node-av/api/lib/classes/Frame.html), which in turn is backed by FFmpeg's
* [`AVFrame`](https://ffmpeg.org/doxygen/2.7/structAVFrame.html). You can use this resource to create `AudioSample`
* instances that are directly backed by FFmpeg's `AVFrame` without data having to be copied.
*
* @group \@mediabunny/server
* @public
*/
export class NodeAvFrameAudioSampleResource extends AudioSampleResource {
frame: NodeAv.Frame;
timestamp: number;
/** @internal */
_frame: NodeAv.Frame | null;
constructor(frame: NodeAv.Frame, timestamp: number) {
super();
const clone = frame.clone();
if (!clone) {
throw new Error('Allocation failure during frame clone.');
/**
* The NodeAV [`Frame`](https://seydx.github.io/node-av/api/lib/classes/Frame.html) instance backing this resource.
* Access throws if the resource has already been closed.
*/
get frame() {
if (!this._frame) {
throw new Error('NodeAvFrameAudioSampleResource has been closed.');
}
this.frame = clone;
this.timestamp = timestamp;
return this._frame;
}
constructor(frame: NodeAv.Frame) {
super();
if (frame.getMediaType() !== NodeAv.AVMEDIA_TYPE_AUDIO) {
throw new Error('NodeAvFrameAudioSampleResource must be initialized with an audio frame.');
}
this._frame = frame;
}
getFormat(): AudioSampleFormat {
@@ -41,11 +68,12 @@ export class NodeAvFrameAudioSampleResource extends AudioSampleResource {
}
getTimestamp(): number {
return this.timestamp;
return Number(this.frame.pts) / this.frame.timeBase.den;
}
close(): void {
this.frame.free();
this._frame = null;
}
getDataPlane(planeIndex: number): Uint8Array {
+25
View File
@@ -1,3 +1,11 @@
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { registerDecoder, registerEncoder, registerVideoSampleTransformer } from 'mediabunny';
import * as NodeAv from 'node-av';
import { NodeAvVideoDecoder } from './video-decoder';
@@ -18,6 +26,20 @@ if ((globalThis as Record<symbol, unknown>)[SERVER_LOADED_SYMBOL]) {
(globalThis as Record<symbol, unknown>)[SERVER_LOADED_SYMBOL] = true;
let registered = false;
/**
* Registers video and audio decoders and encoders for all codecs, using FFmpeg's libavcodec under the hood.
* Additionally, a custom `VideoSample` transformer based on libavfilter is registered to enable resizing, rotation and
* cropping of video frames.
*
* Make sure to call this function before interacting with Mediabunny.
*
* The decoders and encoders will automatically detect hardware acceleration support for each codec and platform and
* make use of it if applicable.
*
* @group \@mediabunny/server
* @public
*/
export const registerMediabunnyServer = () => {
if (registered) {
return;
@@ -36,3 +58,6 @@ export const registerMediabunnyServer = () => {
registerVideoSampleTransformer(transformVideoSample);
};
export { NodeAvFrameVideoSampleResource } from './video-sample';
export { NodeAvFrameAudioSampleResource } from './audio-sample';
+8
View File
@@ -1,3 +1,11 @@
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { VideoSamplePixelFormat, MediaCodec } from 'mediabunny';
import * as NodeAv from 'node-av';
+9 -1
View File
@@ -1,4 +1,12 @@
import { CustomVideoDecoder, VideoCodec, EncodedPacket, VideoSample, MaybePromise, Rational } from 'mediabunny';
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { CustomVideoDecoder, VideoCodec, EncodedPacket, VideoSample, type MaybePromise, Rational } from 'mediabunny';
import * as NodeAv from 'node-av';
import { CODEC_TO_CODEC_ID, getHardwareDecoderCodec, LIBVPX_VP9 } from './misc';
import { assert, binarySearchLessOrEqual, simplifyRational, toUint8Array } from '../../../src/misc';
+9 -1
View File
@@ -1,6 +1,14 @@
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import {
CustomVideoEncoder,
MaybePromise,
type MaybePromise,
QUALITY_MEDIUM,
VideoCodec,
VideoSample,
+43 -3
View File
@@ -1,4 +1,13 @@
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import {
type MaybePromise,
VideoSamplePixelFormat,
VideoSampleResource,
VideoSampleColorSpace,
@@ -9,7 +18,7 @@ import {
VideoSampleTransformationDescription,
} from 'mediabunny';
import * as NodeAv from 'node-av';
import { assert, MaybePromise, toUint8Array } from '../../../src/misc';
import { assert, toUint8Array } from '../../../src/misc';
import {
toPixelFormat,
unmapColorPrimaries,
@@ -29,13 +38,43 @@ const JPEG_RANGE_PIX_FORMATS = new Set([
NodeAv.AV_PIX_FMT_YUVJ444P,
]);
/**
* A custom `VideoSampleResource` backed by NodeAV's
* [`Frame`](https://seydx.github.io/node-av/api/lib/classes/Frame.html), which in turn is backed by FFmpeg's
* [`AVFrame`](https://ffmpeg.org/doxygen/2.7/structAVFrame.html). You can use this resource to create `VideoSample`
* instances that are directly backed by FFmpeg's `AVFrame` without data having to be copied. Since `AVFrame`s can
* themselves be backed by data on the GPU, this enables zero-copy hardware-accelerated decode and encode paths.
*
* When using Electron, you can directly create `Frame` instances without the data having to leave the GPU. For more,
* see [NodeAV's docs](https://seydx.github.io/node-av/api/lib/classes/Frame.html).
*
* @group \@mediabunny/server
* @public
*/
export class NodeAvFrameVideoSampleResource extends VideoSampleResource {
frame: NodeAv.Frame;
/** @internal */
_frame: NodeAv.Frame | null;
/**
* The NodeAV [`Frame`](https://seydx.github.io/node-av/api/lib/classes/Frame.html) instance backing this resource.
* Access throws if the resource has already been closed.
*/
get frame() {
if (!this._frame) {
throw new Error('NodeAvFrameVideoSampleResource has been closed.');
}
return this._frame;
}
constructor(frame: NodeAv.Frame) {
super();
this.frame = frame;
if (frame.getMediaType() !== NodeAv.AVMEDIA_TYPE_VIDEO) {
throw new Error('NodeAvFrameVideoSampleResource must be initialized with a video frame.');
}
this._frame = frame;
}
getFormat(): VideoSamplePixelFormat | null {
@@ -82,6 +121,7 @@ export class NodeAvFrameVideoSampleResource extends VideoSampleResource {
close(): void {
this.frame.free();
this._frame = null;
}
getDataPlanes(): MaybePromise<VideoDataPlane[]> {
@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"paths": {} // Fails if we don't override it due to the Mediabunny imports
}
}