Remove NodeAv FinaliziationRegistry logic (#392)

This commit is contained in:
Vanilagy
2026-06-02 10:24:33 +02:00
parent 96d0d9fb22
commit e0ced9901e
4 changed files with 212 additions and 293 deletions
+34 -54
View File
@@ -12,28 +12,10 @@ import { CODEC_TO_CODEC_ID, getHardwareDecoderCodec } from './misc';
import { assert, binarySearchLessOrEqual, simplifyRational, toUint8Array } from '../../../src/misc';
import { AvFrameVideoSampleResource } from './video-sample';
type NodeAvState = {
frame: NodeAv.Frame;
packet: NodeAv.Packet;
codecContext: NodeAv.CodecContext | null;
};
const freeState = (state: NodeAvState) => {
state.codecContext?.freeContext();
state.frame.free();
state.packet.free();
};
// Needed for proper freeing if close isn't called
let finalizationRegistry: FinalizationRegistry<NodeAvState> | null = null;
if (typeof FinalizationRegistry !== 'undefined') {
finalizationRegistry = new FinalizationRegistry<NodeAvState>((state) => {
freeState(state);
});
}
export class NodeAvVideoDecoder extends CustomVideoDecoder {
state!: NodeAvState;
frame!: NodeAv.Frame;
packet!: NodeAv.Packet;
codecContext: NodeAv.CodecContext | null = null;
pixelAspectRatio!: Rational;
// Bookkeeping to restore the original timing information
@@ -51,17 +33,14 @@ export class NodeAvVideoDecoder extends CustomVideoDecoder {
}
async init() {
const frame = new NodeAv.Frame();
frame.alloc();
const packet = new NodeAv.Packet();
packet.alloc();
this.state = { frame, packet, codecContext: null };
finalizationRegistry?.register(this, this.state, this);
this.frame = new NodeAv.Frame();
this.frame.alloc();
this.packet = new NodeAv.Packet();
this.packet.alloc();
}
async initCodecContext(packet: EncodedPacket) {
assert(this.state.codecContext === null);
assert(this.codecContext === null);
const codecId = CODEC_TO_CODEC_ID[this.codec];
assert(codecId !== undefined);
@@ -106,29 +85,29 @@ export class NodeAvVideoDecoder extends CustomVideoDecoder {
const ret = await codecContext.open2();
NodeAv.FFmpegError.throwIfError(ret, 'Open codec context');
this.state.codecContext = codecContext;
this.codecContext = codecContext;
}
async decode(packet: EncodedPacket) {
if (this.state.codecContext === null) {
if (this.codecContext === null) {
await this.initCodecContext(packet);
}
assert(this.state.codecContext);
assert(this.codecContext);
this.state.packet.isKeyframe = packet.type === 'key';
this.state.packet.data = Buffer.from(packet.data);
this.state.packet.timeBase = { num: 1, den: 1e6 };
this.state.packet.pts = BigInt(packet.microsecondTimestamp);
this.state.packet.dts = NodeAv.AV_NOPTS_VALUE;
this.state.packet.duration = BigInt(packet.microsecondDuration);
this.packet.isKeyframe = packet.type === 'key';
this.packet.data = Buffer.from(packet.data);
this.packet.timeBase = { num: 1, den: 1e6 };
this.packet.pts = BigInt(packet.microsecondTimestamp);
this.packet.dts = NodeAv.AV_NOPTS_VALUE;
this.packet.duration = BigInt(packet.microsecondDuration);
if (packet.sideData.alpha) {
const matroskaBlockAdditional = Buffer.alloc(8 + packet.sideData.alpha.byteLength);
matroskaBlockAdditional[7] = 1; // BlockAddId
matroskaBlockAdditional.set(packet.sideData.alpha, 8);
this.state.packet.addSideData(NodeAv.AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, matroskaBlockAdditional);
this.packet.addSideData(NodeAv.AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, matroskaBlockAdditional);
}
const preciseTimingIndex = binarySearchLessOrEqual(
@@ -163,13 +142,13 @@ export class NodeAvVideoDecoder extends CustomVideoDecoder {
}
}
const ret = await this.state.codecContext.sendPacket(this.state.packet);
const ret = await this.codecContext.sendPacket(this.packet);
NodeAv.FFmpegError.throwIfError(ret, 'Send packet');
this.state.packet.unref(); // Don't need the data again, so just unref it
this.packet.unref(); // Don't need the data again, so just unref it
while (true) {
const receiveRet = await this.state.codecContext.receiveFrame(this.state.frame);
const receiveRet = await this.codecContext.receiveFrame(this.frame);
if (receiveRet === NodeAv.AVERROR_EAGAIN || receiveRet === NodeAv.AVERROR_EOF) {
break;
}
@@ -181,14 +160,14 @@ export class NodeAvVideoDecoder extends CustomVideoDecoder {
receiveFrame(ret: number) {
NodeAv.FFmpegError.throwIfError(ret, 'Receive frame');
this.state.frame.sampleAspectRatio = new NodeAv.Rational(this.pixelAspectRatio.num, this.pixelAspectRatio.den);
this.frame.sampleAspectRatio = new NodeAv.Rational(this.pixelAspectRatio.num, this.pixelAspectRatio.den);
let timestamp = Number(this.state.frame.pts) / 1e6;
let duration = Number(this.state.frame.duration) / 1e6;
let timestamp = Number(this.frame.pts) / 1e6;
let duration = Number(this.frame.duration) / 1e6;
const preciseTimingIndex = binarySearchLessOrEqual(
this.preciseTimings,
Number(this.state.frame.pts),
Number(this.frame.pts),
x => x.microsecondTimestamp,
);
const entry = preciseTimingIndex !== -1
@@ -197,7 +176,7 @@ export class NodeAvVideoDecoder extends CustomVideoDecoder {
// If there's a relevant timing entry, refine the frame's timing data to get better accuracy than
// microseconds
if (entry && entry.microsecondTimestamp === Number(this.state.frame.pts)) {
if (entry && entry.microsecondTimestamp === Number(this.frame.pts)) {
if (entry.timestampIsValid) {
timestamp = entry.timestamp;
}
@@ -206,7 +185,7 @@ export class NodeAvVideoDecoder extends CustomVideoDecoder {
}
}
const clone = this.state.frame.clone();
const clone = this.frame.clone();
if (!clone) {
throw new Error('Frame clone allocation failed.');
}
@@ -218,17 +197,17 @@ export class NodeAvVideoDecoder extends CustomVideoDecoder {
}
async flush() {
if (!this.state.codecContext) {
if (!this.codecContext) {
return;
}
// Send null packet to signal flush
const ret = await this.state.codecContext.sendPacket(null);
const ret = await this.codecContext.sendPacket(null);
NodeAv.FFmpegError.throwIfError(ret, 'Flush decoder');
// Keep receiving frames until no more are available
while (true) {
const receiveRet = await this.state.codecContext.receiveFrame(this.state.frame);
const receiveRet = await this.codecContext.receiveFrame(this.frame);
if (receiveRet === NodeAv.AVERROR_EAGAIN || receiveRet === NodeAv.AVERROR_EOF) {
// No more frames available
break;
@@ -237,11 +216,12 @@ export class NodeAvVideoDecoder extends CustomVideoDecoder {
this.receiveFrame(receiveRet);
}
this.state.codecContext.flushBuffers();
this.codecContext.flushBuffers();
}
close(): MaybePromise<void> {
finalizationRegistry?.unregister(this);
freeState(this.state);
this.codecContext?.freeContext();
this.frame.free();
this.packet.free();
}
}