@mediabunny/server 1.45.4 → 1.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/bundles/mediabunny-server.cjs +67 -12
- package/dist/bundles/mediabunny-server.min.cjs +2 -2
- package/dist/bundles/mediabunny-server.min.mjs +2 -2
- package/dist/bundles/mediabunny-server.mjs +74 -13
- package/dist/mediabunny-server.d.ts +20 -1
- package/dist/modules/src/audio-decoder.d.ts +1 -1
- package/dist/modules/src/audio-decoder.d.ts.map +1 -1
- package/dist/modules/src/audio-decoder.js +8 -1
- package/dist/modules/src/audio-encoder.d.ts +3 -3
- package/dist/modules/src/audio-encoder.d.ts.map +1 -1
- package/dist/modules/src/audio-encoder.js +6 -2
- package/dist/modules/src/audio-sample.d.ts.map +1 -1
- package/dist/modules/src/audio-sample.js +3 -0
- package/dist/modules/src/index.d.ts +21 -2
- package/dist/modules/src/index.d.ts.map +1 -1
- package/dist/modules/src/index.js +24 -2
- package/dist/modules/src/misc.d.ts +2 -2
- package/dist/modules/src/misc.d.ts.map +1 -1
- package/dist/modules/src/misc.js +23 -2
- package/dist/modules/src/video-decoder.d.ts.map +1 -1
- package/dist/modules/src/video-decoder.js +3 -2
- package/dist/modules/src/video-encoder.d.ts +3 -3
- package/dist/modules/src/video-encoder.d.ts.map +1 -1
- package/dist/modules/src/video-encoder.js +8 -4
- package/dist/modules/src/video-sample.d.ts.map +1 -1
- package/dist/modules/src/video-sample.js +3 -0
- package/dist/modules/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/audio-decoder.ts +8 -2
- package/src/audio-encoder.ts +7 -3
- package/src/audio-sample.ts +3 -0
- package/src/index.ts +63 -2
- package/src/misc.ts +31 -2
- package/src/video-decoder.ts +5 -2
- package/src/video-encoder.ts +12 -11
- package/src/video-sample.ts +3 -0
|
@@ -47,6 +47,10 @@ const misc_1 = require("./misc");
|
|
|
47
47
|
const misc_2 = require("../../../src/misc");
|
|
48
48
|
const audio_sample_1 = require("./audio-sample");
|
|
49
49
|
class NodeAvAudioDecoder extends mediabunny_1.CustomAudioDecoder {
|
|
50
|
+
constructor() {
|
|
51
|
+
super(...arguments);
|
|
52
|
+
this.codecContext = null;
|
|
53
|
+
}
|
|
50
54
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
51
55
|
static supports(codec, config) {
|
|
52
56
|
return codec === 'aac'
|
|
@@ -83,6 +87,7 @@ class NodeAvAudioDecoder extends mediabunny_1.CustomAudioDecoder {
|
|
|
83
87
|
this.codecContext = codecContext;
|
|
84
88
|
}
|
|
85
89
|
async decode(packet) {
|
|
90
|
+
(0, misc_2.assert)(this.codecContext);
|
|
86
91
|
this.packet.isKeyframe = packet.type === 'key';
|
|
87
92
|
this.packet.data = Buffer.from(packet.data);
|
|
88
93
|
this.packet.timeBase = { num: 1, den: this.config.sampleRate };
|
|
@@ -91,6 +96,7 @@ class NodeAvAudioDecoder extends mediabunny_1.CustomAudioDecoder {
|
|
|
91
96
|
this.packet.duration = BigInt(Math.round(packet.duration * this.config.sampleRate));
|
|
92
97
|
const ret = await this.codecContext.sendPacket(this.packet);
|
|
93
98
|
NodeAv.FFmpegError.throwIfError(ret, 'Send packet');
|
|
99
|
+
this.packet.unref(); // Don't need the data again, so just unref it
|
|
94
100
|
while (true) {
|
|
95
101
|
const receiveRet = await this.codecContext.receiveFrame(this.frame);
|
|
96
102
|
if (receiveRet === NodeAv.AVERROR_EAGAIN || receiveRet === NodeAv.AVERROR_EOF) {
|
|
@@ -109,6 +115,7 @@ class NodeAvAudioDecoder extends mediabunny_1.CustomAudioDecoder {
|
|
|
109
115
|
this.onSample(new mediabunny_1.AudioSample(new audio_sample_1.AvFrameAudioSampleResource(clone)));
|
|
110
116
|
}
|
|
111
117
|
async flush() {
|
|
118
|
+
(0, misc_2.assert)(this.codecContext);
|
|
112
119
|
// Send null packet to signal flush
|
|
113
120
|
const ret = await this.codecContext.sendPacket(null);
|
|
114
121
|
NodeAv.FFmpegError.throwIfError(ret, 'Flush decoder');
|
|
@@ -124,7 +131,7 @@ class NodeAvAudioDecoder extends mediabunny_1.CustomAudioDecoder {
|
|
|
124
131
|
this.codecContext.flushBuffers();
|
|
125
132
|
}
|
|
126
133
|
close() {
|
|
127
|
-
this.codecContext
|
|
134
|
+
this.codecContext?.freeContext();
|
|
128
135
|
this.frame.free();
|
|
129
136
|
this.packet.free();
|
|
130
137
|
}
|
|
@@ -11,15 +11,15 @@ import { AdtsHeaderTemplate } from '../../../shared/aac-misc';
|
|
|
11
11
|
export declare class NodeAvAudioEncoder extends CustomAudioEncoder {
|
|
12
12
|
frame: NodeAv.Frame;
|
|
13
13
|
packet: NodeAv.Packet;
|
|
14
|
-
avCodec: NodeAv.Codec;
|
|
15
14
|
codecContext: NodeAv.CodecContext | null;
|
|
15
|
+
resampler: NodeAv.SoftwareResampleContext | null;
|
|
16
|
+
dstFrame: NodeAv.Frame | null;
|
|
17
|
+
avCodec: NodeAv.Codec;
|
|
16
18
|
firstExpectedTimestamp: number | null;
|
|
17
19
|
outputTimestampOffset: number;
|
|
18
|
-
resampler: NodeAv.SoftwareResampleContext | null;
|
|
19
20
|
inputParametersKey: string | null;
|
|
20
21
|
resamplerInputSampleRate: number | null;
|
|
21
22
|
nextResamplerPts: bigint | null;
|
|
22
|
-
dstFrame: NodeAv.Frame | null;
|
|
23
23
|
packetEmitted: boolean;
|
|
24
24
|
adtsHeaderTemplate: AdtsHeaderTemplate | null;
|
|
25
25
|
static supports(codec: AudioCodec, config: AudioEncoderConfig): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-encoder.d.ts","sourceRoot":"","sources":["../../../src/audio-encoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,KAAK,YAAY,EAGjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAIlC,OAAO,EACN,kBAAkB,EAGlB,MAAM,0BAA0B,CAAC;AAUlC,qBAAa,kBAAmB,SAAQ,kBAAkB;IACzD,KAAK,EAAG,MAAM,CAAC,KAAK,CAAC;IACrB,MAAM,EAAG,MAAM,CAAC,MAAM,CAAC;IACvB,
|
|
1
|
+
{"version":3,"file":"audio-encoder.d.ts","sourceRoot":"","sources":["../../../src/audio-encoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,KAAK,YAAY,EAGjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAIlC,OAAO,EACN,kBAAkB,EAGlB,MAAM,0BAA0B,CAAC;AAUlC,qBAAa,kBAAmB,SAAQ,kBAAkB;IACzD,KAAK,EAAG,MAAM,CAAC,KAAK,CAAC;IACrB,MAAM,EAAG,MAAM,CAAC,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAQ;IAChD,SAAS,EAAE,MAAM,CAAC,uBAAuB,GAAG,IAAI,CAAQ;IACxD,QAAQ,EAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAQ;IACrC,OAAO,EAAG,MAAM,CAAC,KAAK,CAAC;IACvB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC7C,qBAAqB,SAAK;IAE1B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAQ;IACzC,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC/C,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAQ;IACvC,aAAa,UAAS;IACtB,kBAAkB,EAAE,kBAAkB,GAAG,IAAI,CAAQ;WAErC,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO;IA2B1E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBrB,kBAAkB;IA+BlB,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiF/C,mBAAmB;IAuBnB,0BAA0B,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,IAAI;IAgB3D,aAAa,CAAC,GAAG,EAAE,MAAM;IAsGnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2C5B,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC;CAO3B"}
|
|
@@ -56,13 +56,13 @@ class NodeAvAudioEncoder extends mediabunny_1.CustomAudioEncoder {
|
|
|
56
56
|
constructor() {
|
|
57
57
|
super(...arguments);
|
|
58
58
|
this.codecContext = null;
|
|
59
|
+
this.resampler = null;
|
|
60
|
+
this.dstFrame = null;
|
|
59
61
|
this.firstExpectedTimestamp = null;
|
|
60
62
|
this.outputTimestampOffset = 0;
|
|
61
|
-
this.resampler = null;
|
|
62
63
|
this.inputParametersKey = null;
|
|
63
64
|
this.resamplerInputSampleRate = null;
|
|
64
65
|
this.nextResamplerPts = null;
|
|
65
|
-
this.dstFrame = null;
|
|
66
66
|
this.packetEmitted = false;
|
|
67
67
|
this.adtsHeaderTemplate = null;
|
|
68
68
|
}
|
|
@@ -122,6 +122,10 @@ class NodeAvAudioEncoder extends mediabunny_1.CustomAudioEncoder {
|
|
|
122
122
|
}
|
|
123
123
|
this.firstExpectedTimestamp ??= audioSample.timestamp;
|
|
124
124
|
if (audioSample._data instanceof audio_sample_1.AvFrameAudioSampleResource) {
|
|
125
|
+
// Release any buffers still referenced from the previous encode before reffing the new frame, otherwise
|
|
126
|
+
// av_frame_ref leaks them
|
|
127
|
+
// https://github.com/Vanilagy/mediabunny/issues/392
|
|
128
|
+
this.frame.unref();
|
|
125
129
|
this.frame.ref(audioSample._data.frame);
|
|
126
130
|
}
|
|
127
131
|
else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-sample.d.ts","sourceRoot":"","sources":["../../../src/audio-sample.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAIlC;;;;;;;;;;;GAWG;AACH,qBAAa,0BAA2B,SAAQ,mBAAmB;IAIlE;;;OAGG;IACH,IAAI,KAAK,iBAMR;gBAEW,KAAK,EAAE,MAAM,CAAC,KAAK;
|
|
1
|
+
{"version":3,"file":"audio-sample.d.ts","sourceRoot":"","sources":["../../../src/audio-sample.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAIlC;;;;;;;;;;;GAWG;AACH,qBAAa,0BAA2B,SAAQ,mBAAmB;IAIlE;;;OAGG;IACH,IAAI,KAAK,iBAMR;gBAEW,KAAK,EAAE,MAAM,CAAC,KAAK;IAa/B,SAAS,IAAI,iBAAiB;IAU9B,aAAa,IAAI,MAAM;IAIvB,mBAAmB,IAAI,MAAM;IAI7B,iBAAiB,IAAI,MAAM;IAI3B,YAAY,IAAI,MAAM;IAItB,KAAK,IAAI,IAAI;IAKb,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU;CAI5C;AAED,eAAO,MAAM,wBAAwB,GAAI,QAAQ,WAAW,EAAE,OAAO,MAAM,CAAC,KAAK,SAYhF,CAAC"}
|
|
@@ -70,6 +70,9 @@ class AvFrameAudioSampleResource extends mediabunny_1.AudioSampleResource {
|
|
|
70
70
|
}
|
|
71
71
|
constructor(frame) {
|
|
72
72
|
super();
|
|
73
|
+
if (!(frame instanceof NodeAv.Frame)) {
|
|
74
|
+
throw new TypeError('frame must be a NodeAv.Frame.');
|
|
75
|
+
}
|
|
73
76
|
if (frame.getMediaType() !== NodeAv.AVMEDIA_TYPE_AUDIO) {
|
|
74
77
|
throw new Error('AvFrameAudioSampleResource must be initialized with an audio frame.');
|
|
75
78
|
}
|
|
@@ -5,8 +5,24 @@
|
|
|
5
5
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
6
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
7
|
*/
|
|
8
|
-
import { AudioSample, VideoSample } from 'mediabunny';
|
|
8
|
+
import { AudioSample, MaybePromise, VideoSample } from 'mediabunny';
|
|
9
9
|
import * as NodeAv from 'node-av';
|
|
10
|
+
/**
|
|
11
|
+
* Options for configuring Mediabunny's server-side polyfills.
|
|
12
|
+
* @group \@mediabunny/server
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
type MediabunnyServerOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* The hardware context to use for hardware-accelerated decoding and encoding. When set to a
|
|
18
|
+
* `NodeAv.HardwareContext`, that context is used directly. When set to a function, the function is invoked (with
|
|
19
|
+
* no caching) every time a hardware decoder or encoder codec is resolved, receiving the codec ID and returning the
|
|
20
|
+
* context to use for it (or `null` for no hardware acceleration).
|
|
21
|
+
*
|
|
22
|
+
* When not set, a context is automatically detected on first use and then cached.
|
|
23
|
+
*/
|
|
24
|
+
hardwareContext?: NodeAv.HardwareContext | null | ((codecId: NodeAv.AVCodecID) => MaybePromise<NodeAv.HardwareContext | null>);
|
|
25
|
+
};
|
|
10
26
|
/**
|
|
11
27
|
* Registers video and audio decoders and encoders for all codecs, using FFmpeg's libavcodec under the hood.
|
|
12
28
|
* Additionally, a custom `VideoSample` transformer based on libavfilter is registered to enable resizing, rotation and
|
|
@@ -17,10 +33,13 @@ import * as NodeAv from 'node-av';
|
|
|
17
33
|
* The decoders and encoders will automatically detect hardware acceleration support for each codec and platform and
|
|
18
34
|
* make use of it if applicable.
|
|
19
35
|
*
|
|
36
|
+
* You can pass optional {@link MediabunnyServerOptions} for additional configuration.
|
|
37
|
+
*
|
|
20
38
|
* @group \@mediabunny/server
|
|
21
39
|
* @public
|
|
22
40
|
*/
|
|
23
|
-
export declare const registerMediabunnyServer: () => void;
|
|
41
|
+
export declare const registerMediabunnyServer: (options?: MediabunnyServerOptions) => void;
|
|
42
|
+
export type { MediabunnyServerOptions };
|
|
24
43
|
export { AvFrameVideoSampleResource } from './video-sample';
|
|
25
44
|
export { AvFrameAudioSampleResource } from './audio-sample';
|
|
26
45
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,WAAW,EACX,YAAY,EAIZ,WAAW,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAqBlC;;;;GAIG;AACH,KAAK,uBAAuB,GAAG;IAC9B;;;;;;;OAOG;IACH,eAAe,CAAC,EACb,MAAM,CAAC,eAAe,GACtB,IAAI,GACJ,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,KAAK,YAAY,CAAC,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;CAChF,CAAC;AAKF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,GAAI,UAAS,uBAA4B,SAiC7E,CAAC;AAEF,YAAY,EAAE,uBAAuB,EAAE,CAAC;AAExC,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAU,QAAQ,WAAW,GAAG,WAAW,EAAE,OAAO,MAAM,CAAC,KAAK,kBAqCrF,CAAC"}
|
|
@@ -40,7 +40,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
40
40
|
};
|
|
41
41
|
})();
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
exports.toAvFrame = exports.AvFrameAudioSampleResource = exports.AvFrameVideoSampleResource = exports.registerMediabunnyServer = void 0;
|
|
43
|
+
exports.toAvFrame = exports.AvFrameAudioSampleResource = exports.AvFrameVideoSampleResource = exports.registerMediabunnyServer = exports._serverOptions = void 0;
|
|
44
44
|
const mediabunny_1 = require("mediabunny");
|
|
45
45
|
const NodeAv = __importStar(require("node-av"));
|
|
46
46
|
const video_decoder_1 = require("./video-decoder");
|
|
@@ -58,6 +58,8 @@ if (globalThis[SERVER_LOADED_SYMBOL]) {
|
|
|
58
58
|
}
|
|
59
59
|
globalThis[SERVER_LOADED_SYMBOL] = true;
|
|
60
60
|
let registered = false;
|
|
61
|
+
/** @internal */
|
|
62
|
+
exports._serverOptions = {};
|
|
61
63
|
/**
|
|
62
64
|
* Registers video and audio decoders and encoders for all codecs, using FFmpeg's libavcodec under the hood.
|
|
63
65
|
* Additionally, a custom `VideoSample` transformer based on libavfilter is registered to enable resizing, rotation and
|
|
@@ -68,14 +70,24 @@ let registered = false;
|
|
|
68
70
|
* The decoders and encoders will automatically detect hardware acceleration support for each codec and platform and
|
|
69
71
|
* make use of it if applicable.
|
|
70
72
|
*
|
|
73
|
+
* You can pass optional {@link MediabunnyServerOptions} for additional configuration.
|
|
74
|
+
*
|
|
71
75
|
* @group \@mediabunny/server
|
|
72
76
|
* @public
|
|
73
77
|
*/
|
|
74
|
-
const registerMediabunnyServer = () => {
|
|
78
|
+
const registerMediabunnyServer = (options = {}) => {
|
|
79
|
+
if (typeof options !== 'object' || !options) {
|
|
80
|
+
throw new TypeError('options must be an object.');
|
|
81
|
+
}
|
|
82
|
+
if (options.hardwareContext != null
|
|
83
|
+
&& !(options.hardwareContext instanceof NodeAv.HardwareContext || typeof options.hardwareContext === 'function')) {
|
|
84
|
+
throw new TypeError('options.hardwareContext, when provided, must be a NodeAv.HardwareContext, a function, or null.');
|
|
85
|
+
}
|
|
75
86
|
if (registered) {
|
|
76
87
|
return;
|
|
77
88
|
}
|
|
78
89
|
registered = true;
|
|
90
|
+
exports._serverOptions = options;
|
|
79
91
|
NodeAv.Log.setLevel(NodeAv.AV_LOG_ERROR);
|
|
80
92
|
// Video
|
|
81
93
|
(0, mediabunny_1.registerDecoder)(video_decoder_1.NodeAvVideoDecoder);
|
|
@@ -103,8 +115,16 @@ Object.defineProperty(exports, "AvFrameAudioSampleResource", { enumerable: true,
|
|
|
103
115
|
* @public
|
|
104
116
|
*/
|
|
105
117
|
const toAvFrame = async (sample, frame) => {
|
|
118
|
+
if (!(sample instanceof mediabunny_1.VideoSample) && !(sample instanceof mediabunny_1.AudioSample)) {
|
|
119
|
+
throw new TypeError('sample must be a VideoSample or an AudioSample.');
|
|
120
|
+
}
|
|
121
|
+
if (!(frame instanceof NodeAv.Frame)) {
|
|
122
|
+
throw new TypeError('frame must be a NodeAv.Frame.');
|
|
123
|
+
}
|
|
106
124
|
if (sample instanceof mediabunny_1.VideoSample) {
|
|
107
125
|
if (sample._data instanceof video_sample_1.AvFrameVideoSampleResource) {
|
|
126
|
+
// We're overriding the frame, so release whatever it referenced before, otherwise av_frame_ref leaks it
|
|
127
|
+
frame.unref();
|
|
108
128
|
frame.ref(sample._data.frame);
|
|
109
129
|
}
|
|
110
130
|
else {
|
|
@@ -119,6 +139,8 @@ const toAvFrame = async (sample, frame) => {
|
|
|
119
139
|
}
|
|
120
140
|
else {
|
|
121
141
|
if (sample._data instanceof audio_sample_1.AvFrameAudioSampleResource) {
|
|
142
|
+
// We're overriding the frame, so release whatever it referenced before, otherwise av_frame_ref leaks it
|
|
143
|
+
frame.unref();
|
|
122
144
|
frame.ref(sample._data.frame);
|
|
123
145
|
}
|
|
124
146
|
else {
|
|
@@ -9,8 +9,8 @@ import { VideoSamplePixelFormat, MediaCodec } from 'mediabunny';
|
|
|
9
9
|
import * as NodeAv from 'node-av';
|
|
10
10
|
export declare const CODEC_TO_CODEC_ID: Partial<Record<MediaCodec, NodeAv.AVCodecID>>;
|
|
11
11
|
export declare const getHardwareContext: () => NodeAv.HardwareContext | null;
|
|
12
|
-
export declare const getHardwareDecoderCodec: (codecId: NodeAv.AVCodecID) => NodeAv.Codec | null
|
|
13
|
-
export declare const getHardwareEncoderCodec: (codecId: NodeAv.AVCodecID) => NodeAv.Codec | null
|
|
12
|
+
export declare const getHardwareDecoderCodec: (codecId: NodeAv.AVCodecID) => Promise<NodeAv.Codec | null>;
|
|
13
|
+
export declare const getHardwareEncoderCodec: (codecId: NodeAv.AVCodecID) => Promise<NodeAv.Codec | null>;
|
|
14
14
|
export declare const mapColorPrimaries: (primaries: string) => NodeAv.AVColorPrimaries | null;
|
|
15
15
|
export declare const unmapColorPrimaries: (primaries: number) => "bt709" | "bt470bg" | "smpte170m" | "bt2020" | "smpte432" | null;
|
|
16
16
|
export declare const mapTransferCharacteristics: (transfer: string) => NodeAv.AVColorTransferCharacteristic | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../src/misc.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../src/misc.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAGlC,eAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,CAc3E,CAAC;AAGF,eAAO,MAAM,kBAAkB,QAAO,MAAM,CAAC,eAAe,GAAG,IAU9D,CAAC;AAYF,eAAO,MAAM,uBAAuB,GAAU,SAAS,MAAM,CAAC,SAAS,KAAG,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAapG,CAAC;AAGF,eAAO,MAAM,uBAAuB,GAAU,SAAS,MAAM,CAAC,SAAS,KAAG,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAapG,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,mCAUlD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,qEAUpD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,UAAU,MAAM,gDAW1D,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,4EAW5D,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,+BAUnD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,MAAM,oEAUrD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,mBAAmB,MAAM,CAAC,aAAa,KAAG,sBAAsB,GAAG,IAqChG,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,aAAa,sBAAsB,yBA+BlE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,oBAAoB,MAAM,CAAC,cAAc,KAAG,iBAAiB,GAAG,IAanG,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,cAAc,iBAAiB,KAAG,MAAM,CAAC,cAa9E,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,KAAG,MAAM,CAAC,aAS7D,CAAC"}
|
package/dist/modules/src/misc.js
CHANGED
|
@@ -42,6 +42,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
43
|
exports.getChannelLayout = exports.fromAudioSampleFormat = exports.toAudioSampleFormat = exports.fromPixelFormat = exports.toPixelFormat = exports.unmapMatrixCoefficients = exports.mapMatrixCoefficients = exports.unmapTransferCharacteristics = exports.mapTransferCharacteristics = exports.unmapColorPrimaries = exports.mapColorPrimaries = exports.getHardwareEncoderCodec = exports.getHardwareDecoderCodec = exports.getHardwareContext = exports.CODEC_TO_CODEC_ID = void 0;
|
|
44
44
|
const NodeAv = __importStar(require("node-av"));
|
|
45
|
+
const index_1 = require("./index");
|
|
45
46
|
exports.CODEC_TO_CODEC_ID = {
|
|
46
47
|
avc: NodeAv.AV_CODEC_ID_H264,
|
|
47
48
|
hevc: NodeAv.AV_CODEC_ID_HEVC,
|
|
@@ -58,14 +59,29 @@ exports.CODEC_TO_CODEC_ID = {
|
|
|
58
59
|
};
|
|
59
60
|
let cachedHardwareContext = undefined;
|
|
60
61
|
const getHardwareContext = () => {
|
|
62
|
+
if (index_1._serverOptions.hardwareContext !== undefined && typeof index_1._serverOptions.hardwareContext !== 'function') {
|
|
63
|
+
// Return the user-provided one
|
|
64
|
+
return index_1._serverOptions.hardwareContext;
|
|
65
|
+
}
|
|
61
66
|
if (cachedHardwareContext === undefined) {
|
|
62
67
|
cachedHardwareContext = NodeAv.HardwareContext.auto();
|
|
63
68
|
}
|
|
64
69
|
return cachedHardwareContext;
|
|
65
70
|
};
|
|
66
71
|
exports.getHardwareContext = getHardwareContext;
|
|
72
|
+
const validateHwContext = (hw) => {
|
|
73
|
+
if (hw !== null && !(hw instanceof NodeAv.HardwareContext)) {
|
|
74
|
+
throw new TypeError('When serverOptions.hardwareContext is a function, it must return or resolve to a'
|
|
75
|
+
+ ' NodeAv.HardwareContext or null.');
|
|
76
|
+
}
|
|
77
|
+
};
|
|
67
78
|
const hardwareDecoderCodecCache = new Map();
|
|
68
|
-
const getHardwareDecoderCodec = (codecId) => {
|
|
79
|
+
const getHardwareDecoderCodec = async (codecId) => {
|
|
80
|
+
if (typeof index_1._serverOptions.hardwareContext === 'function') {
|
|
81
|
+
const hw = await index_1._serverOptions.hardwareContext(codecId);
|
|
82
|
+
validateHwContext(hw);
|
|
83
|
+
return hw?.getDecoderCodec(codecId) ?? null;
|
|
84
|
+
}
|
|
69
85
|
if (!hardwareDecoderCodecCache.has(codecId)) {
|
|
70
86
|
const hw = (0, exports.getHardwareContext)();
|
|
71
87
|
hardwareDecoderCodecCache.set(codecId, hw?.getDecoderCodec(codecId) ?? null);
|
|
@@ -74,7 +90,12 @@ const getHardwareDecoderCodec = (codecId) => {
|
|
|
74
90
|
};
|
|
75
91
|
exports.getHardwareDecoderCodec = getHardwareDecoderCodec;
|
|
76
92
|
const hardwareEncoderCodecCache = new Map();
|
|
77
|
-
const getHardwareEncoderCodec = (codecId) => {
|
|
93
|
+
const getHardwareEncoderCodec = async (codecId) => {
|
|
94
|
+
if (typeof index_1._serverOptions.hardwareContext === 'function') {
|
|
95
|
+
const hw = await index_1._serverOptions.hardwareContext(codecId);
|
|
96
|
+
validateHwContext(hw);
|
|
97
|
+
return hw?.getEncoderCodec(codecId) ?? null;
|
|
98
|
+
}
|
|
78
99
|
if (!hardwareEncoderCodecCache.has(codecId)) {
|
|
79
100
|
const hw = (0, exports.getHardwareContext)();
|
|
80
101
|
hardwareEncoderCodecCache.set(codecId, hw?.getEncoderCodec(codecId) ?? null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-decoder.d.ts","sourceRoot":"","sources":["../../../src/video-decoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,aAAa,EAAe,KAAK,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACrH,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAKlC,qBAAa,kBAAmB,SAAQ,kBAAkB;IACzD,KAAK,EAAG,MAAM,CAAC,KAAK,CAAC;IACrB,MAAM,EAAG,MAAM,CAAC,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAQ;IAChD,gBAAgB,EAAG,QAAQ,CAAC;IAG5B,cAAc,EAAE;QACf,oBAAoB,EAAE,MAAM,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;KACzB,EAAE,CAAM;WAGO,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO;IAI1E,IAAI;IAOJ,gBAAgB,CAAC,MAAM,EAAE,aAAa;IAiDtC,MAAM,CAAC,MAAM,EAAE,aAAa;
|
|
1
|
+
{"version":3,"file":"video-decoder.d.ts","sourceRoot":"","sources":["../../../src/video-decoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,aAAa,EAAe,KAAK,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACrH,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAKlC,qBAAa,kBAAmB,SAAQ,kBAAkB;IACzD,KAAK,EAAG,MAAM,CAAC,KAAK,CAAC;IACrB,MAAM,EAAG,MAAM,CAAC,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAQ;IAChD,gBAAgB,EAAG,QAAQ,CAAC;IAG5B,cAAc,EAAE;QACf,oBAAoB,EAAE,MAAM,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;KACzB,EAAE,CAAM;WAGO,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO;IAI1E,IAAI;IAOJ,gBAAgB,CAAC,MAAM,EAAE,aAAa;IAiDtC,MAAM,CAAC,MAAM,EAAE,aAAa;IAqElC,YAAY,CAAC,GAAG,EAAE,MAAM;IAuClB,KAAK;IAuBX,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC;CAK3B"}
|
|
@@ -82,7 +82,7 @@ class NodeAvVideoDecoder extends mediabunny_1.CustomVideoDecoder {
|
|
|
82
82
|
codec = NodeAv.Codec.findDecoder(codecId);
|
|
83
83
|
}
|
|
84
84
|
else {
|
|
85
|
-
codec = (0, misc_1.getHardwareDecoderCodec)(codecId) ?? NodeAv.Codec.findDecoder(codecId);
|
|
85
|
+
codec = (await (0, misc_1.getHardwareDecoderCodec)(codecId)) ?? NodeAv.Codec.findDecoder(codecId);
|
|
86
86
|
}
|
|
87
87
|
if (!codec) {
|
|
88
88
|
throw new Error(`Unable to obtain libav codec for '${this.codec}'.`);
|
|
@@ -108,8 +108,8 @@ class NodeAvVideoDecoder extends mediabunny_1.CustomVideoDecoder {
|
|
|
108
108
|
async decode(packet) {
|
|
109
109
|
if (this.codecContext === null) {
|
|
110
110
|
await this.initCodecContext(packet);
|
|
111
|
-
(0, misc_2.assert)(this.codecContext);
|
|
112
111
|
}
|
|
112
|
+
(0, misc_2.assert)(this.codecContext);
|
|
113
113
|
this.packet.isKeyframe = packet.type === 'key';
|
|
114
114
|
this.packet.data = Buffer.from(packet.data);
|
|
115
115
|
this.packet.timeBase = { num: 1, den: 1e6 };
|
|
@@ -151,6 +151,7 @@ class NodeAvVideoDecoder extends mediabunny_1.CustomVideoDecoder {
|
|
|
151
151
|
}
|
|
152
152
|
const ret = await this.codecContext.sendPacket(this.packet);
|
|
153
153
|
NodeAv.FFmpegError.throwIfError(ret, 'Send packet');
|
|
154
|
+
this.packet.unref(); // Don't need the data again, so just unref it
|
|
154
155
|
while (true) {
|
|
155
156
|
const receiveRet = await this.codecContext.receiveFrame(this.frame);
|
|
156
157
|
if (receiveRet === NodeAv.AVERROR_EAGAIN || receiveRet === NodeAv.AVERROR_EOF) {
|
|
@@ -10,13 +10,13 @@ import * as NodeAv from 'node-av';
|
|
|
10
10
|
export declare class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
11
11
|
frame: NodeAv.Frame;
|
|
12
12
|
packet: NodeAv.Packet;
|
|
13
|
-
avCodec: NodeAv.Codec;
|
|
14
13
|
codecContext: NodeAv.CodecContext | null;
|
|
14
|
+
scaler: NodeAv.SoftwareScaleContext | null;
|
|
15
|
+
dstFrame: NodeAv.Frame | null;
|
|
16
|
+
avCodec: NodeAv.Codec;
|
|
15
17
|
lastBuffer: Buffer | null;
|
|
16
18
|
packetEmitted: boolean;
|
|
17
|
-
scaler: NodeAv.SoftwareScaleContext | null;
|
|
18
19
|
lastScalerKey: string | null;
|
|
19
|
-
dstFrame: NodeAv.Frame | null;
|
|
20
20
|
preciseTimings: {
|
|
21
21
|
microsecondTimestamp: number;
|
|
22
22
|
timestamp: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-encoder.d.ts","sourceRoot":"","sources":["../../../src/video-encoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,kBAAkB,EAClB,KAAK,YAAY,EAEjB,UAAU,EACV,WAAW,EAGX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AA0BlC,qBAAa,kBAAmB,SAAQ,kBAAkB;IACzD,KAAK,EAAG,MAAM,CAAC,KAAK,CAAC;IACrB,MAAM,EAAG,MAAM,CAAC,MAAM,CAAC;IACvB,
|
|
1
|
+
{"version":3,"file":"video-encoder.d.ts","sourceRoot":"","sources":["../../../src/video-encoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,kBAAkB,EAClB,KAAK,YAAY,EAEjB,UAAU,EACV,WAAW,EAGX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AA0BlC,qBAAa,kBAAmB,SAAQ,kBAAkB;IACzD,KAAK,EAAG,MAAM,CAAC,KAAK,CAAC;IACrB,MAAM,EAAG,MAAM,CAAC,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAQ;IAChD,MAAM,EAAE,MAAM,CAAC,oBAAoB,GAAG,IAAI,CAAQ;IAClD,QAAQ,EAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAQ;IACrC,OAAO,EAAG,MAAM,CAAC,KAAK,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC,aAAa,UAAS;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAGpC,cAAc,EAAE;QACf,oBAAoB,EAAE,MAAM,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;KACzB,EAAE,CAAM;WAEO,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO;IAK1E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BrB,kBAAkB;IAoFlB,MAAM,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwHzF,aAAa,CAAC,GAAG,EAAE,MAAM;IA0OnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC;CAO3B"}
|
|
@@ -52,11 +52,11 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
|
|
|
52
52
|
constructor() {
|
|
53
53
|
super(...arguments);
|
|
54
54
|
this.codecContext = null;
|
|
55
|
+
this.scaler = null;
|
|
56
|
+
this.dstFrame = null;
|
|
55
57
|
this.lastBuffer = null;
|
|
56
58
|
this.packetEmitted = false;
|
|
57
|
-
this.scaler = null;
|
|
58
59
|
this.lastScalerKey = null;
|
|
59
|
-
this.dstFrame = null;
|
|
60
60
|
// Bookkeeping to restore the original timing information
|
|
61
61
|
this.preciseTimings = [];
|
|
62
62
|
}
|
|
@@ -80,7 +80,7 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
|
|
|
80
80
|
codec = NodeAv.Codec.findEncoder(codecId);
|
|
81
81
|
}
|
|
82
82
|
else {
|
|
83
|
-
codec = (0, misc_1.getHardwareEncoderCodec)(codecId) ?? NodeAv.Codec.findEncoder(codecId);
|
|
83
|
+
codec = (await (0, misc_1.getHardwareEncoderCodec)(codecId)) ?? NodeAv.Codec.findEncoder(codecId);
|
|
84
84
|
}
|
|
85
85
|
if (!codec) {
|
|
86
86
|
throw new Error(`Unable to obtain libav codec for '${this.codec}'.`);
|
|
@@ -164,9 +164,13 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
|
|
|
164
164
|
async encode(videoSample, options) {
|
|
165
165
|
if (this.codecContext === null) {
|
|
166
166
|
await this.createCodecContext();
|
|
167
|
-
(0, misc_2.assert)(this.codecContext);
|
|
168
167
|
}
|
|
168
|
+
(0, misc_2.assert)(this.codecContext);
|
|
169
169
|
if (videoSample._data instanceof video_sample_1.AvFrameVideoSampleResource) {
|
|
170
|
+
// Release any buffers still referenced from the previous encode before reffing the new frame, otherwise
|
|
171
|
+
// av_frame_ref leaks them
|
|
172
|
+
// https://github.com/Vanilagy/mediabunny/issues/392
|
|
173
|
+
this.frame.unref();
|
|
170
174
|
this.frame.ref(videoSample._data.frame);
|
|
171
175
|
}
|
|
172
176
|
else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-sample.d.ts","sourceRoot":"","sources":["../../../src/video-sample.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,KAAK,YAAY,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,WAAW,EACX,cAAc,EACd,oCAAoC,EACpC,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAqBlC;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,0BAA2B,SAAQ,mBAAmB;IAIlE;;;OAGG;IACH,IAAI,KAAK,iBAMR;gBAEW,KAAK,EAAE,MAAM,CAAC,KAAK;
|
|
1
|
+
{"version":3,"file":"video-sample.d.ts","sourceRoot":"","sources":["../../../src/video-sample.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,KAAK,YAAY,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,eAAe,EACf,WAAW,EACX,cAAc,EACd,oCAAoC,EACpC,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAqBlC;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,0BAA2B,SAAQ,mBAAmB;IAIlE;;;OAGG;IACH,IAAI,KAAK,iBAMR;gBAEW,KAAK,EAAE,MAAM,CAAC,KAAK;IAa/B,SAAS,IAAI,sBAAsB,GAAG,IAAI;IAI1C,aAAa,IAAI,MAAM;IAIvB,cAAc,IAAI,MAAM;IAIxB,mBAAmB,IAAI,MAAM;IAQ7B,oBAAoB,IAAI,MAAM;IAQ9B,aAAa,IAAI,qBAAqB;IActC,KAAK,IAAI,IAAI;IAKb,aAAa,IAAI,YAAY,CAAC,cAAc,EAAE,CAAC;IASzC,WAAW,CAChB,IAAI,EAAE,WAAW,CAAC,eAAe,EAAE,WAAW,CAAC,EAG/C,UAAU,EAAE,oBAAoB,GAC9B,OAAO,CAAC,WAAW,CAAC;CAiCvB;AAED,eAAO,MAAM,wBAAwB,GAAU,QAAQ,WAAW,EAAE,OAAO,MAAM,CAAC,KAAK,EAAE,YAAY,MAAM,GAAG,IAAI,qCA+BjH,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAChC,QAAQ,WAAW,EACnB,aAAa,oCAAoC,KAC/C,OAAO,CAAC,WAAW,GAAG,IAAI,CAkG5B,CAAC"}
|
|
@@ -81,6 +81,9 @@ class AvFrameVideoSampleResource extends mediabunny_1.VideoSampleResource {
|
|
|
81
81
|
}
|
|
82
82
|
constructor(frame) {
|
|
83
83
|
super();
|
|
84
|
+
if (!(frame instanceof NodeAv.Frame)) {
|
|
85
|
+
throw new TypeError('frame must be a NodeAv.Frame.');
|
|
86
|
+
}
|
|
84
87
|
if (frame.getMediaType() !== NodeAv.AVMEDIA_TYPE_VIDEO) {
|
|
85
88
|
throw new Error('AvFrameVideoSampleResource must be initialized with a video frame.');
|
|
86
89
|
}
|