@lumen5/beamcoder 0.0.1
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/.circleci/config.yml +41 -0
- package/.circleci/images/testbeam10-4.1/Dockerfile +12 -0
- package/.circleci/test_image/Dockerfile +14 -0
- package/.circleci/test_image/build.md +13 -0
- package/.eslintrc.js +27 -0
- package/.github/workflows/publish-npm.yml +33 -0
- package/LICENSE +674 -0
- package/README.md +1221 -0
- package/beamstreams.js +692 -0
- package/binding.gyp +103 -0
- package/examples/encode_h264.js +92 -0
- package/examples/jpeg_app.js +55 -0
- package/examples/jpeg_filter_app.js +101 -0
- package/examples/make_mp4.js +123 -0
- package/images/beamcoder_small.jpg +0 -0
- package/index.d.ts +83 -0
- package/index.js +44 -0
- package/install_ffmpeg.js +240 -0
- package/package.json +45 -0
- package/scratch/decode_aac.js +38 -0
- package/scratch/decode_avci.js +50 -0
- package/scratch/decode_hevc.js +38 -0
- package/scratch/decode_pcm.js +39 -0
- package/scratch/make_a_mux.js +68 -0
- package/scratch/muxer.js +74 -0
- package/scratch/read_wav.js +35 -0
- package/scratch/simple_mux.js +39 -0
- package/scratch/stream_avci.js +127 -0
- package/scratch/stream_mp4.js +78 -0
- package/scratch/stream_mux.js +47 -0
- package/scratch/stream_pcm.js +82 -0
- package/scratch/stream_wav.js +62 -0
- package/scripts/install_beamcoder_dependencies.sh +25 -0
- package/src/adaptor.h +202 -0
- package/src/beamcoder.cc +937 -0
- package/src/beamcoder_util.cc +1129 -0
- package/src/beamcoder_util.h +206 -0
- package/src/codec.cc +7386 -0
- package/src/codec.h +44 -0
- package/src/codec_par.cc +1818 -0
- package/src/codec_par.h +40 -0
- package/src/decode.cc +569 -0
- package/src/decode.h +75 -0
- package/src/demux.cc +584 -0
- package/src/demux.h +88 -0
- package/src/encode.cc +496 -0
- package/src/encode.h +72 -0
- package/src/filter.cc +1888 -0
- package/src/filter.h +30 -0
- package/src/format.cc +5287 -0
- package/src/format.h +77 -0
- package/src/frame.cc +2681 -0
- package/src/frame.h +52 -0
- package/src/governor.cc +286 -0
- package/src/governor.h +30 -0
- package/src/hwcontext.cc +378 -0
- package/src/hwcontext.h +35 -0
- package/src/log.cc +186 -0
- package/src/log.h +20 -0
- package/src/mux.cc +834 -0
- package/src/mux.h +106 -0
- package/src/packet.cc +762 -0
- package/src/packet.h +49 -0
- package/test/codecParamsSpec.js +148 -0
- package/test/decoderSpec.js +56 -0
- package/test/demuxerSpec.js +41 -0
- package/test/encoderSpec.js +69 -0
- package/test/filtererSpec.js +47 -0
- package/test/formatSpec.js +343 -0
- package/test/frameSpec.js +145 -0
- package/test/introspectionSpec.js +73 -0
- package/test/muxerSpec.js +34 -0
- package/test/packetSpec.js +122 -0
- package/types/Beamstreams.d.ts +98 -0
- package/types/Codec.d.ts +123 -0
- package/types/CodecContext.d.ts +555 -0
- package/types/CodecPar.d.ts +108 -0
- package/types/Decoder.d.ts +137 -0
- package/types/Demuxer.d.ts +113 -0
- package/types/Encoder.d.ts +94 -0
- package/types/Filter.d.ts +324 -0
- package/types/FormatContext.d.ts +380 -0
- package/types/Frame.d.ts +295 -0
- package/types/HWContext.d.ts +62 -0
- package/types/Muxer.d.ts +121 -0
- package/types/Packet.d.ts +82 -0
- package/types/PrivClass.d.ts +25 -0
- package/types/Stream.d.ts +165 -0
package/src/filter.cc
ADDED
|
@@ -0,0 +1,1888 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Aerostat Beam Coder - Node.js native bindings for FFmpeg.
|
|
3
|
+
Copyright (C) 2018 Streampunk Media Ltd.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
https://www.streampunk.media/ mailto:furnace@streampunk.media
|
|
19
|
+
14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
#include "filter.h"
|
|
23
|
+
#include "beamcoder_util.h"
|
|
24
|
+
#include "frame.h"
|
|
25
|
+
#include <map>
|
|
26
|
+
#include <deque>
|
|
27
|
+
|
|
28
|
+
extern "C" {
|
|
29
|
+
#include <libavfilter/avfilter.h>
|
|
30
|
+
#include <libavformat/avformat.h>
|
|
31
|
+
#include <libavcodec/avcodec.h>
|
|
32
|
+
#include <libavfilter/buffersink.h>
|
|
33
|
+
#include <libavfilter/buffersrc.h>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
napi_status toFilterPrivData(napi_env env, napi_value params, AVFilterContext *filterContext) {
|
|
37
|
+
napi_status status;
|
|
38
|
+
napi_value names, element, subel;
|
|
39
|
+
napi_valuetype type, subtype;
|
|
40
|
+
bool isArray, flag;
|
|
41
|
+
double dValue;
|
|
42
|
+
uint32_t uThirtwo;
|
|
43
|
+
char* sValue;
|
|
44
|
+
char* strProp;
|
|
45
|
+
size_t sLen;
|
|
46
|
+
const AVOption* option;
|
|
47
|
+
int64_t iValue;
|
|
48
|
+
int ret;
|
|
49
|
+
AVRational qValue = {0,0};
|
|
50
|
+
AVFilterGraph *graph = filterContext->graph;
|
|
51
|
+
void* priv_data = filterContext->priv;
|
|
52
|
+
|
|
53
|
+
if (priv_data == nullptr) {
|
|
54
|
+
return napi_invalid_arg;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
status = napi_typeof(env, params, &type);
|
|
58
|
+
PASS_STATUS;
|
|
59
|
+
status = napi_is_array(env, params, &isArray);
|
|
60
|
+
PASS_STATUS;
|
|
61
|
+
if ((isArray == false) && (type == napi_object)) {
|
|
62
|
+
status = napi_get_property_names(env, params, &names);
|
|
63
|
+
PASS_STATUS;
|
|
64
|
+
status = napi_get_array_length(env, names, &uThirtwo);
|
|
65
|
+
PASS_STATUS;
|
|
66
|
+
for ( uint32_t x = 0 ; x < uThirtwo ; x++ ) {
|
|
67
|
+
status = napi_get_element(env, names, x, &element);
|
|
68
|
+
PASS_STATUS;
|
|
69
|
+
status = napi_get_value_string_utf8(env, element, nullptr, 0, &sLen);
|
|
70
|
+
PASS_STATUS;
|
|
71
|
+
sValue = (char*) malloc(sizeof(char) * (sLen + 1));
|
|
72
|
+
status = napi_get_value_string_utf8(env, element, sValue, sLen + 1, &sLen);
|
|
73
|
+
PASS_STATUS;
|
|
74
|
+
option = av_opt_find(priv_data, sValue, nullptr, 0, 0);
|
|
75
|
+
if (option != nullptr) {
|
|
76
|
+
if (option->flags & AV_OPT_FLAG_READONLY) { continue; }
|
|
77
|
+
status = napi_get_named_property(env, params, sValue, &element);
|
|
78
|
+
PASS_STATUS;
|
|
79
|
+
status = napi_typeof(env, element, &type);
|
|
80
|
+
PASS_STATUS;
|
|
81
|
+
switch (type) {
|
|
82
|
+
case napi_boolean:
|
|
83
|
+
status = napi_get_value_bool(env, element, &flag);
|
|
84
|
+
PASS_STATUS;
|
|
85
|
+
ret = av_opt_set_int(priv_data, sValue, flag, 0);
|
|
86
|
+
if (ret < 0) printf("DEBUG: Unable to set %s with a boolean value.\n", sValue);
|
|
87
|
+
break;
|
|
88
|
+
case napi_number:
|
|
89
|
+
if ((option->type == AV_OPT_TYPE_DOUBLE) ||
|
|
90
|
+
(option->type == AV_OPT_TYPE_FLOAT)) {
|
|
91
|
+
status = napi_get_value_double(env, element, &dValue);
|
|
92
|
+
PASS_STATUS;
|
|
93
|
+
ret = av_opt_set_double(priv_data, sValue, dValue, 0);
|
|
94
|
+
if (ret < 0) printf("DEBUG: Unable to set %s with a double value %f.\n", sValue, dValue);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
status = napi_get_value_int64(env, element, &iValue);
|
|
98
|
+
PASS_STATUS;
|
|
99
|
+
ret = av_opt_set_int(priv_data, sValue, iValue, 0);
|
|
100
|
+
if (ret < 0) printf("DEBUG: Unable to set %s with an integer value %" PRId64 ": %s.\n",
|
|
101
|
+
sValue, iValue, avErrorMsg("", ret));
|
|
102
|
+
break;
|
|
103
|
+
case napi_string:
|
|
104
|
+
status = napi_get_value_string_utf8(env, element, nullptr, 0, &sLen);
|
|
105
|
+
PASS_STATUS;
|
|
106
|
+
strProp = (char*) malloc(sizeof(char) * (sLen + 1));
|
|
107
|
+
PASS_STATUS;
|
|
108
|
+
status = napi_get_value_string_utf8(env, element, strProp, sLen + 1, &sLen);
|
|
109
|
+
PASS_STATUS;
|
|
110
|
+
ret = avfilter_graph_send_command (graph, filterContext->name, sValue, strProp, nullptr, 0, 0);
|
|
111
|
+
free(strProp);
|
|
112
|
+
if (ret < 0) printf("DEBUG: Unable to set %s with a string value %s.\n", sValue, strProp);
|
|
113
|
+
break;
|
|
114
|
+
case napi_object:
|
|
115
|
+
status = napi_is_array(env, element, &isArray);
|
|
116
|
+
PASS_STATUS;
|
|
117
|
+
if (isArray && (option->type == AV_OPT_TYPE_RATIONAL)) {
|
|
118
|
+
status = napi_get_element(env, element, 0, &subel);
|
|
119
|
+
PASS_STATUS;
|
|
120
|
+
status = napi_typeof(env, subel, &subtype);
|
|
121
|
+
PASS_STATUS;
|
|
122
|
+
if (subtype != napi_number) {
|
|
123
|
+
printf("DEBUG: Non-number value for rational numerator of property %s.\n", sValue);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
status = napi_get_value_int32(env, subel, &qValue.num);
|
|
127
|
+
PASS_STATUS;
|
|
128
|
+
status = napi_get_element(env, element, 1, &subel);
|
|
129
|
+
PASS_STATUS;
|
|
130
|
+
status = napi_typeof(env, subel, &subtype);
|
|
131
|
+
PASS_STATUS;
|
|
132
|
+
if (subtype != napi_number) {
|
|
133
|
+
printf("DEBUG: Non-number value for rational denominator of property %s.\n", sValue);
|
|
134
|
+
qValue.num = 0; qValue.den = 1;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
status = napi_get_value_int32(env, subel, &qValue.den);
|
|
138
|
+
PASS_STATUS;
|
|
139
|
+
ret = av_opt_set_q(priv_data, sValue, qValue, 0);
|
|
140
|
+
if (ret < 0) {
|
|
141
|
+
printf("DEBUG: Failed to set rational property %s.\n", sValue);
|
|
142
|
+
}
|
|
143
|
+
qValue.num = 0; qValue.den = 1;
|
|
144
|
+
} else {
|
|
145
|
+
printf("DEBUG: Non-array for non-rational property %s.\n", sValue);
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
default:
|
|
149
|
+
printf("DEBUG: Failed to set a private data value %s\n", sValue);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
printf("DEBUG: Option %s not found.\n", sValue);
|
|
154
|
+
}
|
|
155
|
+
free(sValue);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
return napi_invalid_arg;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return napi_ok;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
napi_value getFilterCtxPrivData(napi_env env, napi_callback_info info) {
|
|
165
|
+
napi_status status;
|
|
166
|
+
napi_value result;
|
|
167
|
+
AVFilterContext *filterContext;
|
|
168
|
+
|
|
169
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
170
|
+
CHECK_STATUS;
|
|
171
|
+
|
|
172
|
+
if (nullptr == filterContext->priv) {
|
|
173
|
+
status = napi_get_null(env, &result);
|
|
174
|
+
CHECK_STATUS;
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
status = fromContextPrivData(env, filterContext->priv, &result);
|
|
179
|
+
CHECK_STATUS;
|
|
180
|
+
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
napi_value setFilterCtxPrivData(napi_env env, napi_callback_info info) {
|
|
185
|
+
napi_status status;
|
|
186
|
+
napi_value result, value;
|
|
187
|
+
napi_valuetype type;
|
|
188
|
+
AVFilterContext* filterContext;
|
|
189
|
+
bool isArray;
|
|
190
|
+
|
|
191
|
+
size_t argc = 1;
|
|
192
|
+
napi_value args[1];
|
|
193
|
+
status = napi_get_cb_info(env, info, &argc, args, nullptr, (void**) &filterContext);
|
|
194
|
+
CHECK_STATUS;
|
|
195
|
+
|
|
196
|
+
if (nullptr == filterContext->priv)
|
|
197
|
+
NAPI_THROW_ERROR("Filter does not have private_data.");
|
|
198
|
+
|
|
199
|
+
if (argc == 0)
|
|
200
|
+
NAPI_THROW_ERROR("A value must be provided to set private_data.");
|
|
201
|
+
|
|
202
|
+
value = args[0];
|
|
203
|
+
status = napi_typeof(env, value, &type);
|
|
204
|
+
CHECK_STATUS;
|
|
205
|
+
status = napi_is_array(env, value, &isArray);
|
|
206
|
+
CHECK_STATUS;
|
|
207
|
+
if ((isArray == false) && (type == napi_object)) {
|
|
208
|
+
status = toFilterPrivData(env, value, filterContext);
|
|
209
|
+
CHECK_STATUS;
|
|
210
|
+
} else {
|
|
211
|
+
NAPI_THROW_ERROR("An object with key/value pairs is required to set private data.");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
status = napi_get_undefined(env, &result);
|
|
215
|
+
CHECK_STATUS;
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
napi_value getFilterName(napi_env env, napi_callback_info info) {
|
|
220
|
+
napi_status status;
|
|
221
|
+
napi_value result;
|
|
222
|
+
AVFilter* filter;
|
|
223
|
+
|
|
224
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filter);
|
|
225
|
+
CHECK_STATUS;
|
|
226
|
+
|
|
227
|
+
status = napi_create_string_utf8(env, filter->name, NAPI_AUTO_LENGTH, &result);
|
|
228
|
+
CHECK_STATUS;
|
|
229
|
+
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
napi_value getFilterDesc(napi_env env, napi_callback_info info) {
|
|
234
|
+
napi_status status;
|
|
235
|
+
napi_value result;
|
|
236
|
+
AVFilter* filter;
|
|
237
|
+
|
|
238
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filter);
|
|
239
|
+
CHECK_STATUS;
|
|
240
|
+
|
|
241
|
+
status = napi_create_string_utf8(env, filter->description, NAPI_AUTO_LENGTH, &result);
|
|
242
|
+
CHECK_STATUS;
|
|
243
|
+
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
napi_value getFilterPads(napi_env env, AVFilter* filter, bool isOutput) {
|
|
248
|
+
napi_status status;
|
|
249
|
+
napi_value pad, result;
|
|
250
|
+
const AVFilterPad* filterPads;
|
|
251
|
+
int padCount;
|
|
252
|
+
|
|
253
|
+
padCount = avfilter_filter_pad_count(filter, isOutput);
|
|
254
|
+
if (0 == padCount) {
|
|
255
|
+
status = napi_get_null(env, &result);
|
|
256
|
+
CHECK_STATUS;
|
|
257
|
+
} else {
|
|
258
|
+
filterPads = isOutput ? filter->outputs : filter->inputs;
|
|
259
|
+
status = napi_create_array(env, &result);
|
|
260
|
+
CHECK_STATUS;
|
|
261
|
+
for ( int x = 0 ; x < padCount ; x++ ) {
|
|
262
|
+
status = napi_create_object(env, &pad);
|
|
263
|
+
CHECK_STATUS;
|
|
264
|
+
status = beam_set_string_utf8(env, pad, "name",
|
|
265
|
+
(char*) avfilter_pad_get_name(filterPads, x));
|
|
266
|
+
CHECK_STATUS;
|
|
267
|
+
status = beam_set_string_utf8(env, pad, "media_type",
|
|
268
|
+
(char*) av_get_media_type_string(avfilter_pad_get_type(filterPads, x)));
|
|
269
|
+
CHECK_STATUS;
|
|
270
|
+
status = napi_set_element(env, result, x, pad);
|
|
271
|
+
CHECK_STATUS;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
napi_value getFilterInputPads(napi_env env, napi_callback_info info) {
|
|
278
|
+
napi_status status;
|
|
279
|
+
AVFilter* filter;
|
|
280
|
+
|
|
281
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filter);
|
|
282
|
+
CHECK_STATUS;
|
|
283
|
+
|
|
284
|
+
return getFilterPads(env, filter, false);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
napi_value getFilterOutputPads(napi_env env, napi_callback_info info) {
|
|
288
|
+
napi_status status;
|
|
289
|
+
AVFilter* filter;
|
|
290
|
+
|
|
291
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filter);
|
|
292
|
+
CHECK_STATUS;
|
|
293
|
+
|
|
294
|
+
return getFilterPads(env, filter, true);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
napi_value getFilterPrivData(napi_env env, napi_callback_info info) {
|
|
298
|
+
napi_status status;
|
|
299
|
+
napi_value result;
|
|
300
|
+
AVFilter* filter;
|
|
301
|
+
|
|
302
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filter);
|
|
303
|
+
CHECK_STATUS;
|
|
304
|
+
|
|
305
|
+
if (filter->priv_class != nullptr) {
|
|
306
|
+
status = fromAVClass(env, (const AVClass*) filter->priv_class, &result);
|
|
307
|
+
CHECK_STATUS;
|
|
308
|
+
} else {
|
|
309
|
+
status = napi_get_null(env, &result);
|
|
310
|
+
CHECK_STATUS;
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
napi_value getFilterFlags(napi_env env, napi_callback_info info) {
|
|
316
|
+
napi_status status;
|
|
317
|
+
napi_value result;
|
|
318
|
+
AVFilter* filter;
|
|
319
|
+
|
|
320
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filter);
|
|
321
|
+
CHECK_STATUS;
|
|
322
|
+
|
|
323
|
+
status = napi_create_object(env, &result);
|
|
324
|
+
CHECK_STATUS;
|
|
325
|
+
|
|
326
|
+
status = beam_set_bool(env, result, "DYNAMIC_INPUTS",
|
|
327
|
+
(filter->flags & AVFILTER_FLAG_DYNAMIC_INPUTS) != 0);
|
|
328
|
+
CHECK_STATUS;
|
|
329
|
+
status = beam_set_bool(env, result, "DYNAMIC_OUTPUTS",
|
|
330
|
+
(filter->flags & AVFILTER_FLAG_DYNAMIC_OUTPUTS) != 0);
|
|
331
|
+
CHECK_STATUS;
|
|
332
|
+
status = beam_set_bool(env, result, "SLICE_THREADS",
|
|
333
|
+
(filter->flags & AVFILTER_FLAG_SLICE_THREADS) != 0);
|
|
334
|
+
CHECK_STATUS;
|
|
335
|
+
status = beam_set_bool(env, result, "SUPPORT_TIMELINE_GENERIC",
|
|
336
|
+
(filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC) != 0);
|
|
337
|
+
CHECK_STATUS;
|
|
338
|
+
status = beam_set_bool(env, result, "SUPPORT_TIMELINE_INTERNAL",
|
|
339
|
+
(filter->flags & AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL) != 0);
|
|
340
|
+
CHECK_STATUS;
|
|
341
|
+
status = beam_set_bool(env, result, "SUPPORT_TIMELINE",
|
|
342
|
+
(filter->flags & (AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL)) != 0);
|
|
343
|
+
CHECK_STATUS;
|
|
344
|
+
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
napi_status fromAVFilter(napi_env env, const AVFilter* filter, napi_value* result) {
|
|
349
|
+
napi_status status;
|
|
350
|
+
napi_value typeName;
|
|
351
|
+
|
|
352
|
+
status = napi_create_object(env, result);
|
|
353
|
+
PASS_STATUS;
|
|
354
|
+
status = napi_create_string_utf8(env, "Filter", NAPI_AUTO_LENGTH, &typeName);
|
|
355
|
+
PASS_STATUS;
|
|
356
|
+
|
|
357
|
+
napi_property_descriptor desc[] = {
|
|
358
|
+
{ "type", nullptr, nullptr, nullptr, nullptr, typeName, napi_enumerable, nullptr },
|
|
359
|
+
{ "name", nullptr, nullptr, getFilterName, nullptr, nullptr, napi_enumerable, (void*)filter },
|
|
360
|
+
{ "description", nullptr, nullptr, getFilterDesc, nullptr, nullptr, napi_enumerable, (void*)filter },
|
|
361
|
+
{ "inputs", nullptr, nullptr, getFilterInputPads, nullptr, nullptr, napi_enumerable, (void*)filter },
|
|
362
|
+
{ "outputs", nullptr, nullptr, getFilterOutputPads, nullptr, nullptr, napi_enumerable, (void*)filter },
|
|
363
|
+
{ "priv_class", nullptr, nullptr, getFilterPrivData, nullptr, nullptr, napi_enumerable, (void*)filter },
|
|
364
|
+
{ "flags", nullptr, nullptr, getFilterFlags, nullptr, nullptr, napi_enumerable, (void*)filter }
|
|
365
|
+
};
|
|
366
|
+
status = napi_define_properties(env, *result, 7, desc);
|
|
367
|
+
PASS_STATUS;
|
|
368
|
+
|
|
369
|
+
return napi_ok;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
napi_value getLinkSrc(napi_env env, napi_callback_info info) {
|
|
373
|
+
napi_status status;
|
|
374
|
+
AVFilterLink* filterLink;
|
|
375
|
+
|
|
376
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
377
|
+
CHECK_STATUS;
|
|
378
|
+
|
|
379
|
+
napi_value strVal;
|
|
380
|
+
status = napi_create_string_utf8(env, filterLink->src->name, NAPI_AUTO_LENGTH, &strVal);
|
|
381
|
+
CHECK_STATUS;
|
|
382
|
+
|
|
383
|
+
return strVal;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
napi_value getLinkSrcPad(napi_env env, napi_callback_info info) {
|
|
387
|
+
napi_status status;
|
|
388
|
+
AVFilterLink* filterLink;
|
|
389
|
+
|
|
390
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
391
|
+
CHECK_STATUS;
|
|
392
|
+
|
|
393
|
+
napi_value strVal;
|
|
394
|
+
status = napi_create_string_utf8(env, avfilter_pad_get_name(filterLink->srcpad, 0), NAPI_AUTO_LENGTH, &strVal);
|
|
395
|
+
CHECK_STATUS;
|
|
396
|
+
|
|
397
|
+
return strVal;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
napi_value getLinkDst(napi_env env, napi_callback_info info) {
|
|
401
|
+
napi_status status;
|
|
402
|
+
AVFilterLink* filterLink;
|
|
403
|
+
|
|
404
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
405
|
+
CHECK_STATUS;
|
|
406
|
+
|
|
407
|
+
napi_value strVal;
|
|
408
|
+
status = napi_create_string_utf8(env, filterLink->dst->name, NAPI_AUTO_LENGTH, &strVal);
|
|
409
|
+
CHECK_STATUS;
|
|
410
|
+
|
|
411
|
+
return strVal;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
napi_value getLinkDstPad(napi_env env, napi_callback_info info) {
|
|
415
|
+
napi_status status;
|
|
416
|
+
AVFilterLink* filterLink;
|
|
417
|
+
|
|
418
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
419
|
+
CHECK_STATUS;
|
|
420
|
+
|
|
421
|
+
napi_value strVal;
|
|
422
|
+
status = napi_create_string_utf8(env, avfilter_pad_get_name(filterLink->dstpad, 0), NAPI_AUTO_LENGTH, &strVal);
|
|
423
|
+
CHECK_STATUS;
|
|
424
|
+
|
|
425
|
+
return strVal;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
napi_value getLinkMediaType(napi_env env, napi_callback_info info) {
|
|
429
|
+
napi_status status;
|
|
430
|
+
AVFilterLink* filterLink;
|
|
431
|
+
|
|
432
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
433
|
+
CHECK_STATUS;
|
|
434
|
+
|
|
435
|
+
napi_value strVal;
|
|
436
|
+
status = napi_create_string_utf8(env, av_get_media_type_string(filterLink->type), NAPI_AUTO_LENGTH, &strVal);
|
|
437
|
+
CHECK_STATUS;
|
|
438
|
+
|
|
439
|
+
return strVal;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
napi_value getLinkVidWidth(napi_env env, napi_callback_info info) {
|
|
443
|
+
napi_status status;
|
|
444
|
+
AVFilterLink* filterLink;
|
|
445
|
+
|
|
446
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
447
|
+
CHECK_STATUS;
|
|
448
|
+
|
|
449
|
+
napi_value widthVal;
|
|
450
|
+
status = napi_create_int32(env, filterLink->w, &widthVal);
|
|
451
|
+
CHECK_STATUS;
|
|
452
|
+
|
|
453
|
+
return widthVal;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
napi_value getLinkVidHeight(napi_env env, napi_callback_info info) {
|
|
457
|
+
napi_status status;
|
|
458
|
+
AVFilterLink* filterLink;
|
|
459
|
+
|
|
460
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
461
|
+
CHECK_STATUS;
|
|
462
|
+
|
|
463
|
+
napi_value heightVal;
|
|
464
|
+
status = napi_create_int32(env, filterLink->h, &heightVal);
|
|
465
|
+
CHECK_STATUS;
|
|
466
|
+
|
|
467
|
+
return heightVal;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
napi_value getLinkSAR(napi_env env, napi_callback_info info) {
|
|
471
|
+
napi_status status;
|
|
472
|
+
AVFilterLink* filterLink;
|
|
473
|
+
|
|
474
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
475
|
+
CHECK_STATUS;
|
|
476
|
+
|
|
477
|
+
napi_value pair, element;
|
|
478
|
+
status = napi_create_array(env, &pair);
|
|
479
|
+
CHECK_STATUS;
|
|
480
|
+
status = napi_create_int32(env, filterLink->sample_aspect_ratio.num, &element);
|
|
481
|
+
CHECK_STATUS;
|
|
482
|
+
status = napi_set_element(env, pair, 0, element);
|
|
483
|
+
CHECK_STATUS;
|
|
484
|
+
status = napi_create_int32(env, filterLink->sample_aspect_ratio.den, &element);
|
|
485
|
+
CHECK_STATUS;
|
|
486
|
+
status = napi_set_element(env, pair, 1, element);
|
|
487
|
+
CHECK_STATUS;
|
|
488
|
+
|
|
489
|
+
return pair;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
napi_value getLinkChannelCount(napi_env env, napi_callback_info info) {
|
|
493
|
+
napi_status status;
|
|
494
|
+
AVFilterLink* filterLink;
|
|
495
|
+
|
|
496
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
497
|
+
CHECK_STATUS;
|
|
498
|
+
|
|
499
|
+
napi_value channelCountVal;
|
|
500
|
+
int channelCount = av_get_channel_layout_nb_channels(filterLink->channel_layout);
|
|
501
|
+
status = napi_create_int32(env, channelCount, &channelCountVal);
|
|
502
|
+
|
|
503
|
+
return channelCountVal;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
napi_value getLinkChannelLayout(napi_env env, napi_callback_info info) {
|
|
507
|
+
napi_status status;
|
|
508
|
+
AVFilterLink* filterLink;
|
|
509
|
+
|
|
510
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
511
|
+
CHECK_STATUS;
|
|
512
|
+
|
|
513
|
+
char channelLayoutStr[30];
|
|
514
|
+
av_get_channel_layout_string(channelLayoutStr, 30, -1, filterLink->channel_layout);
|
|
515
|
+
|
|
516
|
+
napi_value channelLayoutVal;
|
|
517
|
+
status = napi_create_string_utf8(env, channelLayoutStr, NAPI_AUTO_LENGTH, &channelLayoutVal);
|
|
518
|
+
CHECK_STATUS;
|
|
519
|
+
|
|
520
|
+
return channelLayoutVal;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
napi_value getLinkSampleRate(napi_env env, napi_callback_info info) {
|
|
524
|
+
napi_status status;
|
|
525
|
+
AVFilterLink* filterLink;
|
|
526
|
+
|
|
527
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
528
|
+
CHECK_STATUS;
|
|
529
|
+
|
|
530
|
+
napi_value sampleRateVal;
|
|
531
|
+
status = napi_create_int32(env, filterLink->sample_rate, &sampleRateVal);
|
|
532
|
+
CHECK_STATUS;
|
|
533
|
+
|
|
534
|
+
return sampleRateVal;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
napi_value getLinkFormat(napi_env env, napi_callback_info info) {
|
|
538
|
+
napi_status status;
|
|
539
|
+
AVFilterLink* filterLink;
|
|
540
|
+
|
|
541
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
542
|
+
CHECK_STATUS;
|
|
543
|
+
|
|
544
|
+
const char *formatName;
|
|
545
|
+
switch (filterLink->type) {
|
|
546
|
+
case AVMEDIA_TYPE_VIDEO:
|
|
547
|
+
formatName = av_get_pix_fmt_name((AVPixelFormat)filterLink->format);
|
|
548
|
+
break;
|
|
549
|
+
case AVMEDIA_TYPE_AUDIO:
|
|
550
|
+
formatName = av_get_sample_fmt_name((AVSampleFormat)filterLink->format);
|
|
551
|
+
break;
|
|
552
|
+
default:
|
|
553
|
+
formatName = "unrecognised";
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
napi_value formatVal;
|
|
557
|
+
status = napi_create_string_utf8(env, formatName, NAPI_AUTO_LENGTH, &formatVal);
|
|
558
|
+
CHECK_STATUS;
|
|
559
|
+
|
|
560
|
+
return formatVal;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
napi_value getLinkTimeBase(napi_env env, napi_callback_info info) {
|
|
564
|
+
napi_status status;
|
|
565
|
+
AVFilterLink* filterLink;
|
|
566
|
+
|
|
567
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterLink);
|
|
568
|
+
CHECK_STATUS;
|
|
569
|
+
|
|
570
|
+
napi_value pair, element;
|
|
571
|
+
status = napi_create_array(env, &pair);
|
|
572
|
+
CHECK_STATUS;
|
|
573
|
+
status = napi_create_int32(env, filterLink->time_base.num, &element);
|
|
574
|
+
CHECK_STATUS;
|
|
575
|
+
status = napi_set_element(env, pair, 0, element);
|
|
576
|
+
CHECK_STATUS;
|
|
577
|
+
status = napi_create_int32(env, filterLink->time_base.den, &element);
|
|
578
|
+
CHECK_STATUS;
|
|
579
|
+
status = napi_set_element(env, pair, 1, element);
|
|
580
|
+
CHECK_STATUS;
|
|
581
|
+
|
|
582
|
+
return pair;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
napi_status fromAVFilterLink(napi_env env, const AVFilterLink* link, napi_value* result) {
|
|
586
|
+
|
|
587
|
+
napi_status status = napi_create_object(env, result);
|
|
588
|
+
PASS_STATUS;
|
|
589
|
+
|
|
590
|
+
napi_property_descriptor desc[] = {
|
|
591
|
+
{ "src", nullptr, nullptr, getLinkSrc, nullptr, nullptr, napi_enumerable, (void*)link },
|
|
592
|
+
{ "srcpad", nullptr, nullptr, getLinkSrcPad, nullptr, nullptr, napi_enumerable, (void*)link },
|
|
593
|
+
{ "dst", nullptr, nullptr, getLinkDst, nullptr, nullptr, napi_enumerable, (void*)link },
|
|
594
|
+
{ "dstpad", nullptr, nullptr, getLinkDstPad, nullptr, nullptr, napi_enumerable, (void*)link },
|
|
595
|
+
{ "type", nullptr, nullptr, getLinkMediaType, nullptr, nullptr, napi_enumerable, (void*)link },
|
|
596
|
+
{ "w", nullptr, nullptr, getLinkVidWidth, nullptr, nullptr,
|
|
597
|
+
(AVMEDIA_TYPE_VIDEO == link->type) ? napi_enumerable : napi_default, (void*)link },
|
|
598
|
+
{ "h", nullptr, nullptr, getLinkVidHeight, nullptr, nullptr,
|
|
599
|
+
(AVMEDIA_TYPE_VIDEO == link->type) ? napi_enumerable : napi_default, (void*)link },
|
|
600
|
+
{ "sample_aspect_ratio", nullptr, nullptr, getLinkSAR, nullptr, nullptr,
|
|
601
|
+
(AVMEDIA_TYPE_VIDEO == link->type) ? napi_enumerable : napi_default, (void*)link },
|
|
602
|
+
{ "channel_count", nullptr, nullptr, getLinkChannelCount, nullptr, nullptr,
|
|
603
|
+
(AVMEDIA_TYPE_AUDIO == link->type) ? napi_enumerable : napi_default, (void*)link },
|
|
604
|
+
{ "channel_layout", nullptr, nullptr, getLinkChannelLayout, nullptr, nullptr,
|
|
605
|
+
(AVMEDIA_TYPE_AUDIO == link->type) ? napi_enumerable : napi_default, (void*)link },
|
|
606
|
+
{ "sample_rate", nullptr, nullptr, getLinkSampleRate, nullptr, nullptr,
|
|
607
|
+
(AVMEDIA_TYPE_AUDIO == link->type) ? napi_enumerable : napi_default, (void*)link },
|
|
608
|
+
{ "format", nullptr, nullptr, getLinkFormat, nullptr, nullptr, napi_enumerable, (void*)link },
|
|
609
|
+
{ "time_base", nullptr, nullptr, getLinkTimeBase, nullptr, nullptr, napi_enumerable, (void*)link }
|
|
610
|
+
};
|
|
611
|
+
status = napi_define_properties(env, *result, 13, desc);
|
|
612
|
+
PASS_STATUS;
|
|
613
|
+
|
|
614
|
+
return napi_ok;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
napi_value getFilter(napi_env env, napi_callback_info info) {
|
|
618
|
+
napi_status status;
|
|
619
|
+
napi_value result;
|
|
620
|
+
AVFilterContext* filterContext;
|
|
621
|
+
|
|
622
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
623
|
+
CHECK_STATUS;
|
|
624
|
+
|
|
625
|
+
status = fromAVFilter(env, filterContext->filter, &result);
|
|
626
|
+
CHECK_STATUS;
|
|
627
|
+
|
|
628
|
+
return result;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
napi_value getFilterContextName(napi_env env, napi_callback_info info) {
|
|
632
|
+
napi_status status;
|
|
633
|
+
napi_value result;
|
|
634
|
+
AVFilterContext* filterContext;
|
|
635
|
+
|
|
636
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
637
|
+
CHECK_STATUS;
|
|
638
|
+
|
|
639
|
+
status = napi_create_string_utf8(env, filterContext->name, NAPI_AUTO_LENGTH, &result);
|
|
640
|
+
CHECK_STATUS;
|
|
641
|
+
|
|
642
|
+
return result;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
napi_value getFiltCtxInputs(napi_env env, napi_callback_info info) {
|
|
646
|
+
napi_status status;
|
|
647
|
+
napi_value array, element;
|
|
648
|
+
AVFilterContext* filterContext;
|
|
649
|
+
|
|
650
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
651
|
+
CHECK_STATUS;
|
|
652
|
+
|
|
653
|
+
uint32_t numInputs = filterContext->nb_inputs;
|
|
654
|
+
if (0 == numInputs) {
|
|
655
|
+
status = napi_get_null(env, &array);
|
|
656
|
+
} else {
|
|
657
|
+
status = napi_create_array(env, &array);
|
|
658
|
+
CHECK_STATUS;
|
|
659
|
+
for (uint32_t i = 0; i < numInputs; ++i) {
|
|
660
|
+
status = fromAVFilterLink(env, filterContext->inputs[i], &element);
|
|
661
|
+
CHECK_STATUS;
|
|
662
|
+
status = napi_set_element(env, array, i, element);
|
|
663
|
+
CHECK_STATUS;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
CHECK_STATUS;
|
|
668
|
+
return array;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
napi_value getFiltCtxOutputs(napi_env env, napi_callback_info info) {
|
|
672
|
+
napi_status status;
|
|
673
|
+
napi_value array, element;
|
|
674
|
+
AVFilterContext* filterContext;
|
|
675
|
+
|
|
676
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
677
|
+
CHECK_STATUS;
|
|
678
|
+
|
|
679
|
+
uint32_t numOutputs = filterContext->nb_outputs;
|
|
680
|
+
if (0 == numOutputs) {
|
|
681
|
+
status = napi_get_null(env, &array);
|
|
682
|
+
} else {
|
|
683
|
+
status = napi_create_array(env, &array);
|
|
684
|
+
CHECK_STATUS;
|
|
685
|
+
for (uint32_t i = 0; i < numOutputs; ++i) {
|
|
686
|
+
status = fromAVFilterLink(env, filterContext->outputs[i], &element);
|
|
687
|
+
CHECK_STATUS;
|
|
688
|
+
status = napi_set_element(env, array, i, element);
|
|
689
|
+
CHECK_STATUS;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
CHECK_STATUS;
|
|
694
|
+
return array;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
napi_value getFilterCtxThreadType(napi_env env, napi_callback_info info) {
|
|
698
|
+
napi_status status;
|
|
699
|
+
napi_value result;
|
|
700
|
+
AVFilterContext* filterContext;
|
|
701
|
+
|
|
702
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
703
|
+
CHECK_STATUS;
|
|
704
|
+
|
|
705
|
+
status = napi_create_uint32(env, filterContext->thread_type, &result);
|
|
706
|
+
CHECK_STATUS;
|
|
707
|
+
|
|
708
|
+
return result;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
napi_value getNumThreads(napi_env env, napi_callback_info info) {
|
|
712
|
+
napi_status status;
|
|
713
|
+
napi_value result;
|
|
714
|
+
AVFilterContext* filterContext;
|
|
715
|
+
|
|
716
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
717
|
+
CHECK_STATUS;
|
|
718
|
+
|
|
719
|
+
status = napi_create_uint32(env, filterContext->nb_threads, &result);
|
|
720
|
+
CHECK_STATUS;
|
|
721
|
+
|
|
722
|
+
return result;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
napi_value getReady(napi_env env, napi_callback_info info) {
|
|
726
|
+
napi_status status;
|
|
727
|
+
napi_value result;
|
|
728
|
+
AVFilterContext* filterContext;
|
|
729
|
+
|
|
730
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
731
|
+
CHECK_STATUS;
|
|
732
|
+
|
|
733
|
+
status = napi_create_uint32(env, filterContext->ready, &result);
|
|
734
|
+
CHECK_STATUS;
|
|
735
|
+
|
|
736
|
+
return result;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
napi_value getExtraHwFrames(napi_env env, napi_callback_info info) {
|
|
740
|
+
napi_status status;
|
|
741
|
+
napi_value result;
|
|
742
|
+
AVFilterContext* filterContext;
|
|
743
|
+
|
|
744
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterContext);
|
|
745
|
+
CHECK_STATUS;
|
|
746
|
+
|
|
747
|
+
status = napi_create_int32(env, filterContext->extra_hw_frames, &result);
|
|
748
|
+
CHECK_STATUS;
|
|
749
|
+
|
|
750
|
+
return result;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
napi_status fromAVFilterCtx(napi_env env, AVFilterContext* filtCtx, napi_value* result) {
|
|
754
|
+
napi_status status;
|
|
755
|
+
napi_value typeName;
|
|
756
|
+
|
|
757
|
+
status = napi_create_object(env, result);
|
|
758
|
+
PASS_STATUS;
|
|
759
|
+
status = napi_create_string_utf8(env, "FilterContext", NAPI_AUTO_LENGTH, &typeName);
|
|
760
|
+
PASS_STATUS;
|
|
761
|
+
|
|
762
|
+
napi_property_descriptor desc[] = {
|
|
763
|
+
{ "type", nullptr, nullptr, nullptr, nullptr, typeName, napi_enumerable, nullptr },
|
|
764
|
+
{ "filter", nullptr, nullptr, getFilter, nullptr, nullptr, napi_enumerable, filtCtx },
|
|
765
|
+
{ "name", nullptr, nullptr, getFilterContextName, nullptr, nullptr, napi_enumerable, filtCtx },
|
|
766
|
+
{ "inputs", nullptr, nullptr, getFiltCtxInputs, nullptr, nullptr, napi_enumerable, filtCtx },
|
|
767
|
+
{ "outputs", nullptr, nullptr, getFiltCtxOutputs, nullptr, nullptr, napi_enumerable, filtCtx },
|
|
768
|
+
{ "priv", nullptr, nullptr, getFilterCtxPrivData, setFilterCtxPrivData, nullptr, napi_enumerable, filtCtx },
|
|
769
|
+
{ "thread_type", nullptr, nullptr, getFilterCtxThreadType, nullptr, nullptr, napi_enumerable, filtCtx },
|
|
770
|
+
{ "nb_threads", nullptr, nullptr, getNumThreads, nullptr, nullptr, napi_enumerable, filtCtx },
|
|
771
|
+
{ "ready", nullptr, nullptr, getReady, nullptr, nullptr, napi_enumerable, filtCtx },
|
|
772
|
+
{ "extra_hw_frames", nullptr, nullptr, getExtraHwFrames, nullptr, nullptr, napi_enumerable, filtCtx }
|
|
773
|
+
};
|
|
774
|
+
status = napi_define_properties(env, *result, 10, desc);
|
|
775
|
+
PASS_STATUS;
|
|
776
|
+
|
|
777
|
+
return napi_ok;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
napi_value getGraphFilters(napi_env env, napi_callback_info info) {
|
|
781
|
+
napi_status status;
|
|
782
|
+
napi_value result, element;
|
|
783
|
+
AVFilterGraph* filterGraph;
|
|
784
|
+
|
|
785
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterGraph);
|
|
786
|
+
CHECK_STATUS;
|
|
787
|
+
|
|
788
|
+
status = napi_create_array(env, &result);
|
|
789
|
+
CHECK_STATUS;
|
|
790
|
+
|
|
791
|
+
for ( uint32_t f = 0 ; f < filterGraph->nb_filters ; ++f ) {
|
|
792
|
+
if (filterGraph->filters[f] == nullptr) continue;
|
|
793
|
+
status = fromAVFilterCtx(env, filterGraph->filters[f], &element);
|
|
794
|
+
CHECK_STATUS;
|
|
795
|
+
status = napi_set_element(env, result, f, element);
|
|
796
|
+
CHECK_STATUS;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return result;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
napi_value getGraphScaleOpts(napi_env env, napi_callback_info info) {
|
|
803
|
+
napi_status status;
|
|
804
|
+
napi_value result;
|
|
805
|
+
AVFilterGraph* filterGraph;
|
|
806
|
+
|
|
807
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterGraph);
|
|
808
|
+
CHECK_STATUS;
|
|
809
|
+
|
|
810
|
+
// printf("sws_scale_opts: \'%s\'\n", filterGraph->scale_sws_opts);
|
|
811
|
+
if (nullptr == filterGraph->scale_sws_opts)
|
|
812
|
+
status = napi_get_null(env, &result);
|
|
813
|
+
else
|
|
814
|
+
status = napi_create_string_utf8(env, filterGraph->scale_sws_opts, NAPI_AUTO_LENGTH, &result);
|
|
815
|
+
CHECK_STATUS;
|
|
816
|
+
|
|
817
|
+
return result;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
napi_value getGraphThreads(napi_env env, napi_callback_info info) {
|
|
821
|
+
napi_status status;
|
|
822
|
+
napi_value result;
|
|
823
|
+
AVFilterGraph* filterGraph;
|
|
824
|
+
|
|
825
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterGraph);
|
|
826
|
+
CHECK_STATUS;
|
|
827
|
+
|
|
828
|
+
status = napi_create_int32(env, filterGraph->nb_threads, &result);
|
|
829
|
+
CHECK_STATUS;
|
|
830
|
+
|
|
831
|
+
return result;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
napi_value getGraphThreadType(napi_env env, napi_callback_info info) {
|
|
835
|
+
napi_status status;
|
|
836
|
+
napi_value result;
|
|
837
|
+
AVFilterGraph* filterGraph;
|
|
838
|
+
|
|
839
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterGraph);
|
|
840
|
+
CHECK_STATUS;
|
|
841
|
+
|
|
842
|
+
status = napi_create_int32(env, filterGraph->thread_type, &result);
|
|
843
|
+
CHECK_STATUS;
|
|
844
|
+
|
|
845
|
+
return result;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
napi_value dumpGraph(napi_env env, napi_callback_info info) {
|
|
849
|
+
napi_status status;
|
|
850
|
+
napi_value result;
|
|
851
|
+
AVFilterGraph* filterGraph;
|
|
852
|
+
|
|
853
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterGraph);
|
|
854
|
+
CHECK_STATUS;
|
|
855
|
+
|
|
856
|
+
status = napi_create_string_utf8(env,
|
|
857
|
+
avfilter_graph_dump(filterGraph, nullptr),
|
|
858
|
+
NAPI_AUTO_LENGTH, &result);
|
|
859
|
+
CHECK_STATUS;
|
|
860
|
+
|
|
861
|
+
return result;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
napi_value getFilterGraph(napi_env env, napi_callback_info info) {
|
|
865
|
+
napi_status status;
|
|
866
|
+
napi_value result, typeName;
|
|
867
|
+
AVFilterGraph* filterGraph;
|
|
868
|
+
|
|
869
|
+
status = napi_get_cb_info(env, info, nullptr, nullptr, nullptr, (void**) &filterGraph);
|
|
870
|
+
CHECK_STATUS;
|
|
871
|
+
|
|
872
|
+
status = napi_create_object(env, &result);
|
|
873
|
+
CHECK_STATUS;
|
|
874
|
+
status = napi_create_string_utf8(env, "FilterGraph", NAPI_AUTO_LENGTH, &typeName);
|
|
875
|
+
CHECK_STATUS;
|
|
876
|
+
|
|
877
|
+
napi_property_descriptor desc[] = {
|
|
878
|
+
{ "type", nullptr, nullptr, nullptr, nullptr, typeName, napi_enumerable, nullptr },
|
|
879
|
+
{ "filters", nullptr, nullptr, getGraphFilters, nullptr, nullptr, napi_enumerable, filterGraph },
|
|
880
|
+
{ "scale_sws_opts", nullptr, nullptr, getGraphScaleOpts, nullptr, nullptr, napi_enumerable, filterGraph },
|
|
881
|
+
{ "thread_type", nullptr, nullptr, getGraphThreadType, nullptr, nullptr, napi_enumerable, filterGraph },
|
|
882
|
+
{ "nb_threads", nullptr, nullptr, getGraphThreads, nullptr, nullptr, napi_enumerable, filterGraph },
|
|
883
|
+
{ "dump", nullptr, dumpGraph, nullptr, nullptr, nullptr, napi_enumerable, filterGraph }
|
|
884
|
+
};
|
|
885
|
+
|
|
886
|
+
status = napi_define_properties(env, result, 6, desc);
|
|
887
|
+
CHECK_STATUS;
|
|
888
|
+
|
|
889
|
+
return result;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
class filtContexts {
|
|
893
|
+
public:
|
|
894
|
+
filtContexts() {}
|
|
895
|
+
~filtContexts() {}
|
|
896
|
+
|
|
897
|
+
bool add(const std::string &name, AVFilterContext *context) {
|
|
898
|
+
mContextNames.push_back(name);
|
|
899
|
+
auto result = mFiltContexts.emplace(name, context);
|
|
900
|
+
return result.second;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
AVFilterContext *getContext(const std::string &name) const {
|
|
904
|
+
AVFilterContext *result = nullptr;
|
|
905
|
+
auto c = mFiltContexts.find(name);
|
|
906
|
+
if (c != mFiltContexts.end())
|
|
907
|
+
result = c->second;
|
|
908
|
+
return result;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
const std::vector<std::string>& getNames() const { return mContextNames; }
|
|
912
|
+
|
|
913
|
+
private:
|
|
914
|
+
std::map<std::string, AVFilterContext *> mFiltContexts;
|
|
915
|
+
std::vector<std::string> mContextNames;
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
struct filtererInData {
|
|
919
|
+
AVBufferRef *hardwareDeviceContext;
|
|
920
|
+
uint32_t width, height;
|
|
921
|
+
AVPixelFormat pixelFormat, softwarePixelFormat;
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
struct filtererCarrier : carrier {
|
|
925
|
+
std::string filterType;
|
|
926
|
+
std::vector<std::string> inNames;
|
|
927
|
+
std::vector<std::string> inParams;
|
|
928
|
+
std::vector<filtererInData> inData;
|
|
929
|
+
std::vector<std::string> outNames;
|
|
930
|
+
std::vector<std::map<std::string, std::string> > outParams;
|
|
931
|
+
std::string filterSpec;
|
|
932
|
+
|
|
933
|
+
filtContexts *srcCtxs = nullptr;
|
|
934
|
+
filtContexts *sinkCtxs = nullptr;
|
|
935
|
+
AVFilterGraph *filterGraph = nullptr;
|
|
936
|
+
~filtererCarrier() {}
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
void graphFinalizer(napi_env env, void* data, void* hint) {
|
|
940
|
+
AVFilterGraph* graph = (AVFilterGraph*)data;
|
|
941
|
+
avfilter_graph_free(&graph);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
void ctxsFinalizer(napi_env env, void* data, void* hint) {
|
|
945
|
+
filtContexts* ctxs = (filtContexts*)data;
|
|
946
|
+
delete ctxs;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
void filtererExecute(napi_env env, void* data) {
|
|
950
|
+
filtererCarrier* c = (filtererCarrier*) data;
|
|
951
|
+
int ret = 0;
|
|
952
|
+
|
|
953
|
+
c->filterGraph = avfilter_graph_alloc();
|
|
954
|
+
|
|
955
|
+
AVFilterInOut **inputs = new AVFilterInOut*[c->outNames.size()];
|
|
956
|
+
bool inAlloc = true;
|
|
957
|
+
for (size_t i = 0; i < c->outNames.size(); ++i)
|
|
958
|
+
if (!(inAlloc = (inputs[i] = avfilter_inout_alloc()) != NULL)) break;
|
|
959
|
+
|
|
960
|
+
AVFilterInOut **outputs = new AVFilterInOut*[c->inParams.size()];
|
|
961
|
+
bool opAlloc = true;
|
|
962
|
+
for (size_t i = 0; i < c->inParams.size(); ++i)
|
|
963
|
+
if (!(opAlloc = (outputs[i] = avfilter_inout_alloc()) != NULL)) break;
|
|
964
|
+
|
|
965
|
+
if (!(inAlloc && opAlloc && c->filterGraph)) {
|
|
966
|
+
c->status = BEAMCODER_ERROR_ENOMEM;
|
|
967
|
+
c->errorMsg = "Failed to allocate filter resources.";
|
|
968
|
+
goto end;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
c->srcCtxs = new filtContexts;
|
|
972
|
+
for (size_t i = 0; i < c->inParams.size(); ++i) {
|
|
973
|
+
const AVFilter *buffersrc = avfilter_get_by_name(0 == c->filterType.compare("audio")?"abuffer":"buffer");
|
|
974
|
+
AVFilterContext *srcCtx = nullptr;
|
|
975
|
+
ret = avfilter_graph_create_filter(&srcCtx, buffersrc, c->inNames[i].c_str(), c->inParams[i].c_str(), NULL, c->filterGraph);
|
|
976
|
+
if (ret < 0) {
|
|
977
|
+
c->status = BEAMCODER_ERROR_ENOMEM;
|
|
978
|
+
c->errorMsg = "Failed to allocate source filter graph.";
|
|
979
|
+
goto end;
|
|
980
|
+
}
|
|
981
|
+
outputs[i]->name = av_strdup(c->inNames[i].c_str());
|
|
982
|
+
outputs[i]->filter_ctx = srcCtx;
|
|
983
|
+
outputs[i]->pad_idx = 0;
|
|
984
|
+
outputs[i]->next = i < c->inParams.size() - 1 ? outputs[i + 1] : NULL;
|
|
985
|
+
|
|
986
|
+
if (!c->srcCtxs->add(c->inNames[i], srcCtx)) {
|
|
987
|
+
c->status = BEAMCODER_ERROR_EINVAL;
|
|
988
|
+
c->errorMsg = "Filter sources must have unique names.";
|
|
989
|
+
goto end;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
c->sinkCtxs = new filtContexts;
|
|
994
|
+
for (size_t i = 0; i < c->outParams.size(); ++i) {
|
|
995
|
+
const AVFilter *buffersink = avfilter_get_by_name(0 == c->filterType.compare("audio")?"abuffersink":"buffersink");
|
|
996
|
+
AVFilterContext *sinkCtx = nullptr;
|
|
997
|
+
ret = avfilter_graph_create_filter(&sinkCtx, buffersink, c->outNames[i].c_str(), NULL, NULL, c->filterGraph);
|
|
998
|
+
if (ret < 0) {
|
|
999
|
+
c->status = BEAMCODER_ERROR_ENOMEM;
|
|
1000
|
+
c->errorMsg = "Failed to allocate sink filter graph.";
|
|
1001
|
+
goto end;
|
|
1002
|
+
}
|
|
1003
|
+
if (0 == c->filterType.compare("audio")) {
|
|
1004
|
+
auto p = c->outParams[i].find("sample_rates");
|
|
1005
|
+
if (p != c->outParams[i].end()) {
|
|
1006
|
+
const int out_sample_rates[] = { std::stoi(p->second.c_str()), -1 };
|
|
1007
|
+
ret = av_opt_set_int_list(sinkCtx, "sample_rates", out_sample_rates, -1,
|
|
1008
|
+
AV_OPT_SEARCH_CHILDREN);
|
|
1009
|
+
if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot set output sample rate\n"); }
|
|
1010
|
+
}
|
|
1011
|
+
p = c->outParams[i].find("sample_fmts");
|
|
1012
|
+
if (p != c->outParams[i].end()) {
|
|
1013
|
+
const enum AVSampleFormat out_sample_fmts[] = { av_get_sample_fmt(p->second.c_str()), AV_SAMPLE_FMT_NONE };
|
|
1014
|
+
ret = av_opt_set_int_list(sinkCtx, "sample_fmts", out_sample_fmts, AV_SAMPLE_FMT_NONE,
|
|
1015
|
+
AV_OPT_SEARCH_CHILDREN);
|
|
1016
|
+
if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot set output sample format\n"); }
|
|
1017
|
+
}
|
|
1018
|
+
p = c->outParams[i].find("channel_layouts");
|
|
1019
|
+
if (p != c->outParams[i].end()) {
|
|
1020
|
+
const int64_t out_channel_layouts[] = { (int64_t)av_get_channel_layout(p->second.c_str()), -1 };
|
|
1021
|
+
ret = av_opt_set_int_list(sinkCtx, "channel_layouts", out_channel_layouts, -1,
|
|
1022
|
+
AV_OPT_SEARCH_CHILDREN);
|
|
1023
|
+
if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot set output channel layout\n"); }
|
|
1024
|
+
}
|
|
1025
|
+
} else {
|
|
1026
|
+
auto p = c->outParams[i].find("pix_fmts");
|
|
1027
|
+
if (p != c->outParams[i].end()) {
|
|
1028
|
+
enum AVPixelFormat pix_fmts[] = { av_get_pix_fmt(p->second.c_str()), AV_PIX_FMT_NONE };
|
|
1029
|
+
ret = av_opt_set_int_list(sinkCtx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE,
|
|
1030
|
+
AV_OPT_SEARCH_CHILDREN);
|
|
1031
|
+
if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n"); }
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
inputs[i]->name = av_strdup(c->outNames[i].c_str());
|
|
1036
|
+
inputs[i]->filter_ctx = sinkCtx;
|
|
1037
|
+
inputs[i]->pad_idx = 0;
|
|
1038
|
+
inputs[i]->next = i < c->outNames.size() - 1 ? inputs[i + 1] : NULL;
|
|
1039
|
+
|
|
1040
|
+
if (!c->sinkCtxs->add(c->outNames[i], sinkCtx)) {
|
|
1041
|
+
c->status = BEAMCODER_ERROR_EINVAL;
|
|
1042
|
+
c->errorMsg = "Filter sinks must have unique names.";
|
|
1043
|
+
goto end;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
if ((ret = avfilter_graph_parse_ptr(c->filterGraph, c->filterSpec.c_str(), inputs, outputs, NULL)) < 0) {
|
|
1048
|
+
c->status = BEAMCODER_ERROR_ENOMEM;
|
|
1049
|
+
c->errorMsg = "Failed to parse filter graph.";
|
|
1050
|
+
goto end;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
{
|
|
1054
|
+
size_t filterIndex = 0;
|
|
1055
|
+
for (filtererInData &data : c->inData)
|
|
1056
|
+
{
|
|
1057
|
+
if (data.hardwareDeviceContext)
|
|
1058
|
+
{
|
|
1059
|
+
if (filterIndex >= c->filterGraph->nb_filters ||
|
|
1060
|
+
!c->filterGraph->filters[filterIndex]->nb_outputs) {
|
|
1061
|
+
c->status = BEAMCODER_ERROR_ENOMEM;
|
|
1062
|
+
c->errorMsg = "Unexpected out of bounds when allocating hardware frame context for filter.";
|
|
1063
|
+
goto end;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
AVFilterLink *filterLink = c->filterGraph->filters[filterIndex]->outputs[0];
|
|
1067
|
+
filterLink->hw_frames_ctx = av_hwframe_ctx_alloc(data.hardwareDeviceContext);
|
|
1068
|
+
if (!filterLink->hw_frames_ctx) {
|
|
1069
|
+
c->status = BEAMCODER_ERROR_ENOMEM;
|
|
1070
|
+
c->errorMsg = "Failed to allocate hardware frame context for filter.";
|
|
1071
|
+
goto end;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
AVHWFramesContext *linkFramesContext = (AVHWFramesContext *)(filterLink->hw_frames_ctx->data);
|
|
1075
|
+
linkFramesContext->sw_format = data.softwarePixelFormat;
|
|
1076
|
+
linkFramesContext->width = data.width;
|
|
1077
|
+
linkFramesContext->height = data.height;
|
|
1078
|
+
linkFramesContext->format = data.pixelFormat;
|
|
1079
|
+
|
|
1080
|
+
int initErr = av_hwframe_ctx_init(filterLink->hw_frames_ctx);
|
|
1081
|
+
if (initErr) {
|
|
1082
|
+
c->status = BEAMCODER_ERROR_ENOMEM;
|
|
1083
|
+
c->errorMsg = "Failed to initialize hardware frame context for filter.";
|
|
1084
|
+
goto end;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
++filterIndex;
|
|
1089
|
+
av_buffer_unref(&data.hardwareDeviceContext);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
if ((ret = avfilter_graph_config(c->filterGraph, NULL)) < 0) {
|
|
1094
|
+
c->status = BEAMCODER_ERROR_ENOMEM;
|
|
1095
|
+
c->errorMsg = "Failed to configure filter graph.";
|
|
1096
|
+
goto end;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
end:
|
|
1100
|
+
avfilter_inout_free(inputs);
|
|
1101
|
+
avfilter_inout_free(outputs);
|
|
1102
|
+
delete[] outputs;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
void filtererComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
1106
|
+
filtererCarrier* c = (filtererCarrier*) data;
|
|
1107
|
+
napi_value result, typeName, filterGraphValue, srcContextsValue, sinkContextsValue;
|
|
1108
|
+
|
|
1109
|
+
if (asyncStatus != napi_ok) {
|
|
1110
|
+
c->status = asyncStatus;
|
|
1111
|
+
c->errorMsg = "Filterer allocator failed to complete.";
|
|
1112
|
+
}
|
|
1113
|
+
REJECT_STATUS;
|
|
1114
|
+
|
|
1115
|
+
c->status = napi_create_object(env, &result);
|
|
1116
|
+
REJECT_STATUS;
|
|
1117
|
+
c->status = napi_create_string_utf8(env, "Filterer", NAPI_AUTO_LENGTH, &typeName);
|
|
1118
|
+
REJECT_STATUS;
|
|
1119
|
+
|
|
1120
|
+
c->status = napi_create_external(env, c->filterGraph, graphFinalizer, nullptr, &filterGraphValue);
|
|
1121
|
+
REJECT_STATUS;
|
|
1122
|
+
c->status = napi_create_external(env, c->srcCtxs, ctxsFinalizer, nullptr, &srcContextsValue);
|
|
1123
|
+
REJECT_STATUS;
|
|
1124
|
+
c->status = napi_create_external(env, c->sinkCtxs, ctxsFinalizer, nullptr, &sinkContextsValue);
|
|
1125
|
+
REJECT_STATUS;
|
|
1126
|
+
|
|
1127
|
+
napi_property_descriptor desc[] = {
|
|
1128
|
+
{ "type", nullptr, nullptr, nullptr, nullptr, typeName, napi_enumerable, nullptr },
|
|
1129
|
+
{ "graph", nullptr, nullptr, getFilterGraph, nullptr, nullptr, napi_enumerable, c->filterGraph },
|
|
1130
|
+
{ "filter", nullptr, filter, nullptr, nullptr, nullptr, napi_enumerable, nullptr },
|
|
1131
|
+
{ "_filterGraph", nullptr, nullptr, nullptr, nullptr, filterGraphValue, napi_default, nullptr },
|
|
1132
|
+
{ "_sourceContexts", nullptr, nullptr, nullptr, nullptr, srcContextsValue, napi_default, nullptr },
|
|
1133
|
+
{ "_sinkContexts", nullptr, nullptr, nullptr, nullptr, sinkContextsValue, napi_default, nullptr }
|
|
1134
|
+
};
|
|
1135
|
+
c->status = napi_define_properties(env, result, 6, desc);
|
|
1136
|
+
REJECT_STATUS;
|
|
1137
|
+
|
|
1138
|
+
napi_status status;
|
|
1139
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
1140
|
+
FLOATING_STATUS;
|
|
1141
|
+
|
|
1142
|
+
tidyCarrier(env, c);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
napi_value filterer(napi_env env, napi_callback_info info) {
|
|
1146
|
+
napi_value resourceName, promise;
|
|
1147
|
+
napi_valuetype type;
|
|
1148
|
+
filtererCarrier* c = new filtererCarrier;
|
|
1149
|
+
bool isArray;
|
|
1150
|
+
|
|
1151
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
1152
|
+
REJECT_RETURN;
|
|
1153
|
+
|
|
1154
|
+
size_t argc = 1;
|
|
1155
|
+
napi_value args[1];
|
|
1156
|
+
|
|
1157
|
+
c->status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
|
1158
|
+
REJECT_RETURN;
|
|
1159
|
+
|
|
1160
|
+
if (argc != 1) {
|
|
1161
|
+
REJECT_ERROR_RETURN("Filterer requires a single options object.",
|
|
1162
|
+
BEAMCODER_INVALID_ARGS);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
c->status = napi_typeof(env, args[0], &type);
|
|
1166
|
+
REJECT_RETURN;
|
|
1167
|
+
c->status = napi_is_array(env, args[0], &isArray);
|
|
1168
|
+
REJECT_RETURN;
|
|
1169
|
+
if ((type != napi_object) || (isArray == true)) {
|
|
1170
|
+
REJECT_ERROR_RETURN("Filterer must be configured with a single parameter, an options object.",
|
|
1171
|
+
BEAMCODER_INVALID_ARGS);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
bool hasFilterType, hasInParams, hasOutParams, hasFilterSpec;
|
|
1175
|
+
c->status = napi_has_named_property(env, args[0], "filterType", &hasFilterType);
|
|
1176
|
+
REJECT_RETURN;
|
|
1177
|
+
c->status = napi_has_named_property(env, args[0], "inputParams", &hasInParams);
|
|
1178
|
+
REJECT_RETURN;
|
|
1179
|
+
c->status = napi_has_named_property(env, args[0], "outputParams", &hasOutParams);
|
|
1180
|
+
REJECT_RETURN;
|
|
1181
|
+
c->status = napi_has_named_property(env, args[0], "filterSpec", &hasFilterSpec);
|
|
1182
|
+
REJECT_RETURN;
|
|
1183
|
+
|
|
1184
|
+
if (!(hasFilterType && hasInParams && hasOutParams && hasFilterSpec)) {
|
|
1185
|
+
REJECT_ERROR_RETURN("Filterer parameter object requires type, inputParams, outputParams and filterSpec to be defined.",
|
|
1186
|
+
BEAMCODER_INVALID_ARGS);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
napi_value filterTypeVal;
|
|
1190
|
+
c->status = napi_get_named_property(env, args[0], "filterType", &filterTypeVal);
|
|
1191
|
+
REJECT_RETURN;
|
|
1192
|
+
size_t filterTypeLen;
|
|
1193
|
+
c->status = napi_get_value_string_utf8(env, filterTypeVal, nullptr, 0, &filterTypeLen);
|
|
1194
|
+
REJECT_RETURN;
|
|
1195
|
+
c->filterType.resize(filterTypeLen);
|
|
1196
|
+
c->status = napi_get_value_string_utf8(env, filterTypeVal, (char *)c->filterType.data(), filterTypeLen+1, nullptr);
|
|
1197
|
+
REJECT_RETURN;
|
|
1198
|
+
if ((0 != c->filterType.compare("audio")) && (0 != c->filterType.compare("video"))) {
|
|
1199
|
+
REJECT_ERROR_RETURN("Filterer expects filterType of audio or video.",
|
|
1200
|
+
BEAMCODER_INVALID_ARGS);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
napi_value paramsArrayVal;
|
|
1204
|
+
uint32_t paramsArrayLen;
|
|
1205
|
+
c->status = napi_get_named_property(env, args[0], "inputParams", ¶msArrayVal);
|
|
1206
|
+
REJECT_RETURN;
|
|
1207
|
+
c->status = napi_is_array(env, paramsArrayVal, &isArray);
|
|
1208
|
+
REJECT_RETURN;
|
|
1209
|
+
if (!isArray) {
|
|
1210
|
+
// Might be array like, as proxied by VM2 decontextify
|
|
1211
|
+
napi_value nameResult;
|
|
1212
|
+
bool hasElement;
|
|
1213
|
+
c->status = napi_get_property_names(env, paramsArrayVal, &nameResult);
|
|
1214
|
+
REJECT_RETURN;
|
|
1215
|
+
c->status = napi_get_array_length(env, nameResult, ¶msArrayLen);
|
|
1216
|
+
REJECT_RETURN;
|
|
1217
|
+
for ( uint32_t i = 0 ; i < paramsArrayLen ; ++i ) {
|
|
1218
|
+
c->status = napi_has_element(env, paramsArrayVal, i, &hasElement);
|
|
1219
|
+
REJECT_RETURN;
|
|
1220
|
+
if (!hasElement) {
|
|
1221
|
+
REJECT_ERROR_RETURN("Filterer inputParams must be an array or array-like.",
|
|
1222
|
+
BEAMCODER_INVALID_ARGS);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
} else {
|
|
1226
|
+
c->status = napi_get_array_length(env, paramsArrayVal, ¶msArrayLen);
|
|
1227
|
+
REJECT_RETURN;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
for (uint32_t i = 0; i < paramsArrayLen; ++i) {
|
|
1231
|
+
napi_value inParamsVal;
|
|
1232
|
+
c->status = napi_get_element(env, paramsArrayVal, i, &inParamsVal);
|
|
1233
|
+
REJECT_RETURN;
|
|
1234
|
+
|
|
1235
|
+
std::string name;
|
|
1236
|
+
uint32_t sampleRate;
|
|
1237
|
+
std::string sampleFormat;
|
|
1238
|
+
std::string channelLayout;
|
|
1239
|
+
uint32_t width;
|
|
1240
|
+
uint32_t height;
|
|
1241
|
+
std::string pixFmt;
|
|
1242
|
+
AVRational timeBase;
|
|
1243
|
+
AVRational pixelAspect;
|
|
1244
|
+
std::string swPixFmt;
|
|
1245
|
+
|
|
1246
|
+
bool hasNameVal;
|
|
1247
|
+
c->status = napi_has_named_property(env, inParamsVal, "name", &hasNameVal);
|
|
1248
|
+
REJECT_RETURN;
|
|
1249
|
+
if (!hasNameVal && (i > 0)) {
|
|
1250
|
+
REJECT_ERROR_RETURN("Filterer inputParams must include a name value if there is more than one input.",
|
|
1251
|
+
BEAMCODER_INVALID_ARGS);
|
|
1252
|
+
}
|
|
1253
|
+
if (hasNameVal) {
|
|
1254
|
+
napi_value nameVal;
|
|
1255
|
+
c->status = napi_get_named_property(env, inParamsVal, "name", &nameVal);
|
|
1256
|
+
REJECT_RETURN;
|
|
1257
|
+
size_t nameLen;
|
|
1258
|
+
c->status = napi_get_value_string_utf8(env, nameVal, nullptr, 0, &nameLen);
|
|
1259
|
+
REJECT_RETURN;
|
|
1260
|
+
name.resize(nameLen);
|
|
1261
|
+
c->status = napi_get_value_string_utf8(env, nameVal, (char *)name.data(), nameLen+1, nullptr);
|
|
1262
|
+
REJECT_RETURN;
|
|
1263
|
+
c->inNames.push_back(name);
|
|
1264
|
+
} else
|
|
1265
|
+
c->inNames.push_back("in");
|
|
1266
|
+
|
|
1267
|
+
uint32_t arrayLen;
|
|
1268
|
+
if (0 == c->filterType.compare("audio")) {
|
|
1269
|
+
napi_value sampleRateVal;
|
|
1270
|
+
c->status = napi_get_named_property(env, inParamsVal, "sampleRate", &sampleRateVal);
|
|
1271
|
+
REJECT_RETURN;
|
|
1272
|
+
c->status = napi_get_value_uint32(env, sampleRateVal, &sampleRate);
|
|
1273
|
+
REJECT_RETURN;
|
|
1274
|
+
|
|
1275
|
+
napi_value sampleFmtVal;
|
|
1276
|
+
c->status = napi_get_named_property(env, inParamsVal, "sampleFormat", &sampleFmtVal);
|
|
1277
|
+
REJECT_RETURN;
|
|
1278
|
+
size_t sampleFmtLen;
|
|
1279
|
+
c->status = napi_get_value_string_utf8(env, sampleFmtVal, nullptr, 0, &sampleFmtLen);
|
|
1280
|
+
REJECT_RETURN;
|
|
1281
|
+
sampleFormat.resize(sampleFmtLen);
|
|
1282
|
+
c->status = napi_get_value_string_utf8(env, sampleFmtVal, (char *)sampleFormat.data(), sampleFmtLen+1, nullptr);
|
|
1283
|
+
REJECT_RETURN;
|
|
1284
|
+
|
|
1285
|
+
napi_value channelLayoutVal;
|
|
1286
|
+
c->status = napi_get_named_property(env, inParamsVal, "channelLayout", &channelLayoutVal);
|
|
1287
|
+
REJECT_RETURN;
|
|
1288
|
+
size_t channelLayoutLen;
|
|
1289
|
+
c->status = napi_get_value_string_utf8(env, channelLayoutVal, nullptr, 0, &channelLayoutLen);
|
|
1290
|
+
REJECT_RETURN;
|
|
1291
|
+
channelLayout.resize(channelLayoutLen);
|
|
1292
|
+
c->status = napi_get_value_string_utf8(env, channelLayoutVal, (char *)channelLayout.data(), channelLayoutLen+1, nullptr);
|
|
1293
|
+
REJECT_RETURN;
|
|
1294
|
+
} else {
|
|
1295
|
+
napi_value widthVal;
|
|
1296
|
+
c->status = napi_get_named_property(env, inParamsVal, "width", &widthVal);
|
|
1297
|
+
REJECT_RETURN;
|
|
1298
|
+
c->status = napi_get_value_uint32(env, widthVal, &width);
|
|
1299
|
+
REJECT_RETURN;
|
|
1300
|
+
napi_value heightVal;
|
|
1301
|
+
c->status = napi_get_named_property(env, inParamsVal, "height", &heightVal);
|
|
1302
|
+
REJECT_RETURN;
|
|
1303
|
+
c->status = napi_get_value_uint32(env, heightVal, &height);
|
|
1304
|
+
REJECT_RETURN;
|
|
1305
|
+
|
|
1306
|
+
napi_value pixFmtVal;
|
|
1307
|
+
c->status = napi_get_named_property(env, inParamsVal, "pixelFormat", &pixFmtVal);
|
|
1308
|
+
REJECT_RETURN;
|
|
1309
|
+
size_t pixFmtLen;
|
|
1310
|
+
c->status = napi_get_value_string_utf8(env, pixFmtVal, nullptr, 0, &pixFmtLen);
|
|
1311
|
+
REJECT_RETURN;
|
|
1312
|
+
pixFmt.resize(pixFmtLen);
|
|
1313
|
+
c->status = napi_get_value_string_utf8(env, pixFmtVal, (char *)pixFmt.data(), pixFmtLen+1, nullptr);
|
|
1314
|
+
REJECT_RETURN;
|
|
1315
|
+
|
|
1316
|
+
napi_value pixelAspectVal;
|
|
1317
|
+
c->status = napi_get_named_property(env, inParamsVal, "pixelAspect", &pixelAspectVal);
|
|
1318
|
+
REJECT_RETURN;
|
|
1319
|
+
c->status = napi_is_array(env, pixelAspectVal, &isArray);
|
|
1320
|
+
REJECT_RETURN;
|
|
1321
|
+
if (isArray) {
|
|
1322
|
+
c->status = napi_get_array_length(env, pixelAspectVal, &arrayLen);
|
|
1323
|
+
REJECT_RETURN;
|
|
1324
|
+
} else {
|
|
1325
|
+
napi_value arrayLikeProps;
|
|
1326
|
+
c->status = napi_get_property_names(env, pixelAspectVal, &arrayLikeProps);
|
|
1327
|
+
REJECT_RETURN;
|
|
1328
|
+
c->status = napi_get_array_length(env, arrayLikeProps, &arrayLen);
|
|
1329
|
+
REJECT_RETURN;
|
|
1330
|
+
}
|
|
1331
|
+
if (2 != arrayLen) {
|
|
1332
|
+
REJECT_ERROR_RETURN("Filterer inputParams pixelAspect must be an array with 2 values representing a rational number.",
|
|
1333
|
+
BEAMCODER_INVALID_ARGS);
|
|
1334
|
+
}
|
|
1335
|
+
for (uint32_t i = 0; i < arrayLen; ++i) {
|
|
1336
|
+
napi_value arrayVal;
|
|
1337
|
+
c->status = napi_get_element(env, pixelAspectVal, i, &arrayVal); // Will detect if not array or array-like
|
|
1338
|
+
REJECT_RETURN;
|
|
1339
|
+
c->status = napi_get_value_int32(env, arrayVal, (0==i)?&pixelAspect.num:&pixelAspect.den);
|
|
1340
|
+
REJECT_RETURN;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
bool hasSoftwarePixelFormat;
|
|
1344
|
+
c->status = napi_has_named_property(env, inParamsVal, "swPixelFormat", &hasSoftwarePixelFormat);
|
|
1345
|
+
REJECT_RETURN;
|
|
1346
|
+
if (hasSoftwarePixelFormat) {
|
|
1347
|
+
napi_value swPixFmtVal;
|
|
1348
|
+
c->status = napi_get_named_property(env, inParamsVal, "swPixelFormat", &swPixFmtVal);
|
|
1349
|
+
REJECT_RETURN;
|
|
1350
|
+
size_t swPixFmtLen;
|
|
1351
|
+
c->status = napi_get_value_string_utf8(env, swPixFmtVal, nullptr, 0, &swPixFmtLen);
|
|
1352
|
+
REJECT_RETURN;
|
|
1353
|
+
swPixFmt.resize(swPixFmtLen);
|
|
1354
|
+
c->status = napi_get_value_string_utf8(env, swPixFmtVal, (char *)swPixFmt.data(), swPixFmtLen+1, nullptr);
|
|
1355
|
+
REJECT_RETURN;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
filtererInData inData = {};
|
|
1359
|
+
inData.width = width;
|
|
1360
|
+
inData.height = height;
|
|
1361
|
+
inData.pixelFormat = av_get_pix_fmt(pixFmt.c_str());
|
|
1362
|
+
inData.softwarePixelFormat = av_get_pix_fmt(swPixFmt.c_str());
|
|
1363
|
+
|
|
1364
|
+
bool hasDeviceCtxVal;
|
|
1365
|
+
c->status = napi_has_named_property(env, inParamsVal, "hw_device_ctx", &hasDeviceCtxVal);
|
|
1366
|
+
REJECT_RETURN;
|
|
1367
|
+
if (hasDeviceCtxVal) {
|
|
1368
|
+
napi_value hwDeviceCtxVal;
|
|
1369
|
+
c->status = napi_get_named_property(env, inParamsVal, "hw_device_ctx", &hwDeviceCtxVal);
|
|
1370
|
+
REJECT_RETURN;
|
|
1371
|
+
napi_value contextExt;
|
|
1372
|
+
c->status = napi_get_named_property(env, hwDeviceCtxVal, "_deviceContext", &contextExt);
|
|
1373
|
+
REJECT_RETURN;
|
|
1374
|
+
AVBufferRef* hwDeviceCtxRef;
|
|
1375
|
+
c->status = napi_get_value_external(env, contextExt, (void**) &hwDeviceCtxRef);
|
|
1376
|
+
REJECT_RETURN;
|
|
1377
|
+
inData.hardwareDeviceContext = av_buffer_ref(hwDeviceCtxRef);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
c->inData.push_back(inData);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
napi_value timeBaseVal;
|
|
1384
|
+
c->status = napi_get_named_property(env, inParamsVal, "timeBase", &timeBaseVal);
|
|
1385
|
+
REJECT_RETURN;
|
|
1386
|
+
c->status = napi_is_array(env, timeBaseVal, &isArray);
|
|
1387
|
+
REJECT_RETURN;
|
|
1388
|
+
if (isArray) {
|
|
1389
|
+
c->status = napi_get_array_length(env, timeBaseVal, &arrayLen);
|
|
1390
|
+
REJECT_RETURN;
|
|
1391
|
+
} else {
|
|
1392
|
+
napi_value arrayLikeProps;
|
|
1393
|
+
c->status = napi_get_property_names(env, timeBaseVal, &arrayLikeProps);
|
|
1394
|
+
REJECT_RETURN;
|
|
1395
|
+
c->status = napi_get_array_length(env, arrayLikeProps, &arrayLen);
|
|
1396
|
+
REJECT_RETURN;
|
|
1397
|
+
}
|
|
1398
|
+
if (2 != arrayLen) {
|
|
1399
|
+
REJECT_ERROR_RETURN("Filterer inputParams timeBase must be an array with 2 values representing a rational number.",
|
|
1400
|
+
BEAMCODER_INVALID_ARGS);
|
|
1401
|
+
}
|
|
1402
|
+
for (uint32_t i = 0; i < arrayLen; ++i) {
|
|
1403
|
+
napi_value arrayVal;
|
|
1404
|
+
c->status = napi_get_element(env, timeBaseVal, i, &arrayVal); // Will detect if not array or array-like
|
|
1405
|
+
REJECT_RETURN;
|
|
1406
|
+
c->status = napi_get_value_int32(env, arrayVal, (0==i)?&timeBase.num:&timeBase.den);
|
|
1407
|
+
REJECT_RETURN;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
char args[512];
|
|
1411
|
+
if (0 == c->filterType.compare("audio")) {
|
|
1412
|
+
snprintf(args, sizeof(args),
|
|
1413
|
+
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%" PRIu64 "",
|
|
1414
|
+
timeBase.num, timeBase.den, sampleRate,
|
|
1415
|
+
sampleFormat.c_str(), av_get_channel_layout(channelLayout.c_str()));
|
|
1416
|
+
} else {
|
|
1417
|
+
snprintf(args, sizeof(args),
|
|
1418
|
+
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
|
|
1419
|
+
width, height, av_get_pix_fmt(pixFmt.c_str()),
|
|
1420
|
+
timeBase.num, timeBase.den, pixelAspect.num, pixelAspect.den);
|
|
1421
|
+
}
|
|
1422
|
+
c->inParams.push_back(args);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
c->status = napi_get_named_property(env, args[0], "outputParams", ¶msArrayVal);
|
|
1426
|
+
REJECT_RETURN;
|
|
1427
|
+
c->status = napi_is_array(env, paramsArrayVal, &isArray);
|
|
1428
|
+
REJECT_RETURN;
|
|
1429
|
+
if (!isArray) { // Might be array like, as proxied by VM2 decontextify
|
|
1430
|
+
napi_value nameResult;
|
|
1431
|
+
bool hasElement;
|
|
1432
|
+
c->status = napi_get_property_names(env, paramsArrayVal, &nameResult);
|
|
1433
|
+
REJECT_RETURN;
|
|
1434
|
+
c->status = napi_get_array_length(env, nameResult, ¶msArrayLen);
|
|
1435
|
+
REJECT_RETURN;
|
|
1436
|
+
for ( uint32_t i = 0 ; i < paramsArrayLen ; ++i ) {
|
|
1437
|
+
c->status = napi_has_element(env, paramsArrayVal, i, &hasElement);
|
|
1438
|
+
REJECT_RETURN;
|
|
1439
|
+
if (!hasElement) {
|
|
1440
|
+
REJECT_ERROR_RETURN("Filterer outputParams must be an array.",
|
|
1441
|
+
BEAMCODER_INVALID_ARGS);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
} else {
|
|
1445
|
+
c->status = napi_get_array_length(env, paramsArrayVal, ¶msArrayLen);
|
|
1446
|
+
REJECT_RETURN;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
for (uint32_t i = 0; i < paramsArrayLen; ++i) {
|
|
1450
|
+
napi_value outParamsVal;
|
|
1451
|
+
c->status = napi_get_element(env, paramsArrayVal, i, &outParamsVal);
|
|
1452
|
+
REJECT_RETURN;
|
|
1453
|
+
|
|
1454
|
+
std::string name;
|
|
1455
|
+
uint32_t sampleRate;
|
|
1456
|
+
std::string sampleFormat;
|
|
1457
|
+
std::string channelLayout;
|
|
1458
|
+
std::string pixFmt;
|
|
1459
|
+
|
|
1460
|
+
bool hasNameVal;
|
|
1461
|
+
c->status = napi_has_named_property(env, outParamsVal, "name", &hasNameVal);
|
|
1462
|
+
REJECT_RETURN;
|
|
1463
|
+
if (!hasNameVal && (i > 0)) {
|
|
1464
|
+
REJECT_ERROR_RETURN("Filterer outputParams must include a name value if there is more than one output.",
|
|
1465
|
+
BEAMCODER_INVALID_ARGS);
|
|
1466
|
+
}
|
|
1467
|
+
if (hasNameVal) {
|
|
1468
|
+
napi_value nameVal;
|
|
1469
|
+
c->status = napi_get_named_property(env, outParamsVal, "name", &nameVal);
|
|
1470
|
+
REJECT_RETURN;
|
|
1471
|
+
size_t nameLen;
|
|
1472
|
+
c->status = napi_get_value_string_utf8(env, nameVal, nullptr, 0, &nameLen);
|
|
1473
|
+
REJECT_RETURN;
|
|
1474
|
+
name.resize(nameLen);
|
|
1475
|
+
c->status = napi_get_value_string_utf8(env, nameVal, (char *)name.data(), nameLen+1, nullptr);
|
|
1476
|
+
REJECT_RETURN;
|
|
1477
|
+
c->outNames.push_back(name);
|
|
1478
|
+
} else
|
|
1479
|
+
c->outNames.push_back("out");
|
|
1480
|
+
|
|
1481
|
+
if (0 == c->filterType.compare("audio")) {
|
|
1482
|
+
napi_value sampleRateVal;
|
|
1483
|
+
c->status = napi_get_named_property(env, outParamsVal, "sampleRate", &sampleRateVal);
|
|
1484
|
+
REJECT_RETURN;
|
|
1485
|
+
c->status = napi_get_value_uint32(env, sampleRateVal, &sampleRate);
|
|
1486
|
+
REJECT_RETURN;
|
|
1487
|
+
|
|
1488
|
+
napi_value sampleFmtVal;
|
|
1489
|
+
c->status = napi_get_named_property(env, outParamsVal, "sampleFormat", &sampleFmtVal);
|
|
1490
|
+
REJECT_RETURN;
|
|
1491
|
+
size_t sampleFmtLen;
|
|
1492
|
+
c->status = napi_get_value_string_utf8(env, sampleFmtVal, nullptr, 0, &sampleFmtLen);
|
|
1493
|
+
REJECT_RETURN;
|
|
1494
|
+
sampleFormat.resize(sampleFmtLen);
|
|
1495
|
+
c->status = napi_get_value_string_utf8(env, sampleFmtVal, (char *)sampleFormat.data(), sampleFmtLen+1, nullptr);
|
|
1496
|
+
REJECT_RETURN;
|
|
1497
|
+
|
|
1498
|
+
napi_value channelLayoutVal;
|
|
1499
|
+
c->status = napi_get_named_property(env, outParamsVal, "channelLayout", &channelLayoutVal);
|
|
1500
|
+
REJECT_RETURN;
|
|
1501
|
+
size_t channelLayoutLen;
|
|
1502
|
+
c->status = napi_get_value_string_utf8(env, channelLayoutVal, nullptr, 0, &channelLayoutLen);
|
|
1503
|
+
REJECT_RETURN;
|
|
1504
|
+
channelLayout.resize(channelLayoutLen);
|
|
1505
|
+
c->status = napi_get_value_string_utf8(env, channelLayoutVal, (char *)channelLayout.data(), channelLayoutLen+1, nullptr);
|
|
1506
|
+
REJECT_RETURN;
|
|
1507
|
+
|
|
1508
|
+
std::map<std::string, std::string> paramMap;
|
|
1509
|
+
paramMap.emplace("sample_rates", std::to_string(sampleRate));
|
|
1510
|
+
paramMap.emplace("sample_fmts", sampleFormat);
|
|
1511
|
+
paramMap.emplace("channel_layouts", channelLayout);
|
|
1512
|
+
c->outParams.push_back(paramMap);
|
|
1513
|
+
} else {
|
|
1514
|
+
napi_value pixFmtVal;
|
|
1515
|
+
c->status = napi_get_named_property(env, outParamsVal, "pixelFormat", &pixFmtVal);
|
|
1516
|
+
REJECT_RETURN;
|
|
1517
|
+
size_t pixFmtLen;
|
|
1518
|
+
c->status = napi_get_value_string_utf8(env, pixFmtVal, nullptr, 0, &pixFmtLen);
|
|
1519
|
+
REJECT_RETURN;
|
|
1520
|
+
pixFmt.resize(pixFmtLen);
|
|
1521
|
+
c->status = napi_get_value_string_utf8(env, pixFmtVal, (char *)pixFmt.data(), pixFmtLen+1, nullptr);
|
|
1522
|
+
REJECT_RETURN;
|
|
1523
|
+
|
|
1524
|
+
std::map<std::string, std::string> paramMap;
|
|
1525
|
+
paramMap.emplace("pix_fmts", pixFmt);
|
|
1526
|
+
c->outParams.push_back(paramMap);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
napi_value filterSpecJS;
|
|
1531
|
+
c->status = napi_get_named_property(env, args[0], "filterSpec", &filterSpecJS);
|
|
1532
|
+
REJECT_RETURN;
|
|
1533
|
+
size_t specLen;
|
|
1534
|
+
c->status = napi_get_value_string_utf8(env, filterSpecJS, nullptr, 0, &specLen);
|
|
1535
|
+
REJECT_RETURN;
|
|
1536
|
+
c->filterSpec.resize(specLen);
|
|
1537
|
+
c->status = napi_get_value_string_utf8(env, filterSpecJS, (char *)c->filterSpec.data(), specLen+1, nullptr);
|
|
1538
|
+
REJECT_RETURN;
|
|
1539
|
+
|
|
1540
|
+
c->status = napi_create_string_utf8(env, "Filterer", NAPI_AUTO_LENGTH, &resourceName);
|
|
1541
|
+
REJECT_RETURN;
|
|
1542
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, filtererExecute,
|
|
1543
|
+
filtererComplete, c, &c->_request);
|
|
1544
|
+
REJECT_RETURN;
|
|
1545
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
1546
|
+
REJECT_RETURN;
|
|
1547
|
+
|
|
1548
|
+
return promise;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
|
|
1552
|
+
struct filterCarrier : carrier {
|
|
1553
|
+
filtContexts *srcCtxs = nullptr;
|
|
1554
|
+
filtContexts *sinkCtxs = nullptr;
|
|
1555
|
+
std::unordered_map<std::string, std::deque<AVFrame *> > srcFrames;
|
|
1556
|
+
std::unordered_map<std::string, std::vector<AVFrame *> > dstFrames;
|
|
1557
|
+
std::vector<napi_ref> frameRefs;
|
|
1558
|
+
~filterCarrier() {}
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
namespace {
|
|
1562
|
+
napi_status isFrame(napi_env env, napi_value packet) {
|
|
1563
|
+
napi_status status;
|
|
1564
|
+
napi_value value;
|
|
1565
|
+
bool result;
|
|
1566
|
+
char objType[10];
|
|
1567
|
+
size_t typeLen;
|
|
1568
|
+
int cmp;
|
|
1569
|
+
napi_valuetype type;
|
|
1570
|
+
|
|
1571
|
+
status = napi_typeof(env, packet, &type);
|
|
1572
|
+
if ((status != napi_ok) || (type != napi_object)) return napi_invalid_arg;
|
|
1573
|
+
status = napi_is_array(env, packet, &result);
|
|
1574
|
+
if ((status != napi_ok) || (result == true)) return napi_invalid_arg;
|
|
1575
|
+
|
|
1576
|
+
status = napi_has_named_property(env, packet, "type", &result);
|
|
1577
|
+
if ((status != napi_ok) || (result == false)) return napi_invalid_arg;
|
|
1578
|
+
|
|
1579
|
+
status = napi_has_named_property(env, packet, "_frame", &result);
|
|
1580
|
+
if ((status != napi_ok) || (result == false)) return napi_invalid_arg;
|
|
1581
|
+
|
|
1582
|
+
status = napi_get_named_property(env, packet, "type", &value);
|
|
1583
|
+
if (status != napi_ok) return status;
|
|
1584
|
+
status = napi_get_value_string_utf8(env, value, objType, 10, &typeLen);
|
|
1585
|
+
if (status != napi_ok) return status;
|
|
1586
|
+
cmp = strcmp("Frame", objType);
|
|
1587
|
+
if (cmp != 0) return napi_invalid_arg;
|
|
1588
|
+
|
|
1589
|
+
status = napi_get_named_property(env, packet, "_frame", &value);
|
|
1590
|
+
if (status != napi_ok) return status;
|
|
1591
|
+
status = napi_typeof(env, value, &type);
|
|
1592
|
+
if (status != napi_ok) return status;
|
|
1593
|
+
if (type != napi_external) return napi_invalid_arg;
|
|
1594
|
+
|
|
1595
|
+
return napi_ok;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
AVFrame* getFrame(napi_env env, napi_value frame) {
|
|
1599
|
+
napi_status status;
|
|
1600
|
+
napi_value value;
|
|
1601
|
+
frameData* result = nullptr;
|
|
1602
|
+
status = napi_get_named_property(env, frame, "_frame", &value);
|
|
1603
|
+
if (status != napi_ok) return nullptr;
|
|
1604
|
+
status = napi_get_value_external(env, value, (void**)&result);
|
|
1605
|
+
if (status != napi_ok) return nullptr;
|
|
1606
|
+
|
|
1607
|
+
return result->frame;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
void filterExecute(napi_env env, void* data) {
|
|
1612
|
+
filterCarrier* c = (filterCarrier*) data;
|
|
1613
|
+
int ret = 0;
|
|
1614
|
+
HR_TIME_POINT filterStart = NOW;
|
|
1615
|
+
|
|
1616
|
+
for (auto it = c->srcFrames.begin(); it != c->srcFrames.end(); ++it) {
|
|
1617
|
+
AVFilterContext *srcCtx = c->srcCtxs->getContext(it->first);
|
|
1618
|
+
if (!srcCtx) {
|
|
1619
|
+
c->status = BEAMCODER_INVALID_ARGS;
|
|
1620
|
+
c->errorMsg = "Frame name not found in source contexts.";
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
std::deque<AVFrame *> frames = it->second;
|
|
1625
|
+
while (frames.size() > 0) {
|
|
1626
|
+
ret = av_buffersrc_add_frame_flags(srcCtx, frames.front(), AV_BUFFERSRC_FLAG_KEEP_REF);
|
|
1627
|
+
if (ret < 0) {
|
|
1628
|
+
c->status = BEAMCODER_ERROR_FILTER_ADD_FRAME;
|
|
1629
|
+
c->errorMsg = "Error while feeding the filtergraph.";
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
frames.pop_front();
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
std::vector<std::string> sinkNames = c->sinkCtxs->getNames();
|
|
1637
|
+
for (auto it = sinkNames.begin(); it != sinkNames.end(); ++it) {
|
|
1638
|
+
std::vector<AVFrame *> frames;
|
|
1639
|
+
while (1) {
|
|
1640
|
+
AVFrame *filtFrame = av_frame_alloc();
|
|
1641
|
+
AVFilterContext *sinkCtx = c->sinkCtxs->getContext(*it);
|
|
1642
|
+
if (!sinkCtx) {
|
|
1643
|
+
c->status = BEAMCODER_INVALID_ARGS;
|
|
1644
|
+
c->errorMsg = "Sink name not found in sink contexts.";
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
ret = av_buffersink_get_frame(sinkCtx, filtFrame);
|
|
1648
|
+
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
|
1649
|
+
break;
|
|
1650
|
+
if (ret < 0) {
|
|
1651
|
+
c->status = BEAMCODER_ERROR_FILTER_GET_FRAME;
|
|
1652
|
+
c->errorMsg = "Error while filtering.";
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
frames.push_back(filtFrame);
|
|
1656
|
+
}
|
|
1657
|
+
c->dstFrames.emplace(*it, frames);
|
|
1658
|
+
}
|
|
1659
|
+
c->totalTime = microTime(filterStart);
|
|
1660
|
+
};
|
|
1661
|
+
|
|
1662
|
+
void filterComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
1663
|
+
filterCarrier* c = (filterCarrier*) data;
|
|
1664
|
+
napi_value result, dstFrame, nameVal, frames, frame, prop;
|
|
1665
|
+
|
|
1666
|
+
for (auto it = c->frameRefs.cbegin(); it != c->frameRefs.cend(); it++) {
|
|
1667
|
+
c->status = napi_delete_reference(env, *it);
|
|
1668
|
+
REJECT_STATUS;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
if (asyncStatus != napi_ok) {
|
|
1672
|
+
c->status = asyncStatus;
|
|
1673
|
+
c->errorMsg = "Filter failed to complete.";
|
|
1674
|
+
}
|
|
1675
|
+
REJECT_STATUS;
|
|
1676
|
+
|
|
1677
|
+
c->status = napi_create_array(env, &result);
|
|
1678
|
+
REJECT_STATUS;
|
|
1679
|
+
|
|
1680
|
+
uint32_t outCount = 0;
|
|
1681
|
+
for (auto it = c->dstFrames.begin(); it != c->dstFrames.end(); it++) {
|
|
1682
|
+
c->status = napi_create_object(env, &dstFrame);
|
|
1683
|
+
REJECT_STATUS;
|
|
1684
|
+
|
|
1685
|
+
c->status = napi_create_array(env, &frames);
|
|
1686
|
+
REJECT_STATUS;
|
|
1687
|
+
|
|
1688
|
+
c->status = napi_create_string_utf8(env, it->first.c_str(), NAPI_AUTO_LENGTH, &nameVal);
|
|
1689
|
+
REJECT_STATUS;
|
|
1690
|
+
c->status = napi_set_named_property(env, dstFrame, "name", nameVal);
|
|
1691
|
+
REJECT_STATUS;
|
|
1692
|
+
|
|
1693
|
+
uint32_t frameCount = 0;
|
|
1694
|
+
for (auto fit = it->second.begin(); fit != it->second.end(); ++fit) {
|
|
1695
|
+
frameData* f = new frameData;
|
|
1696
|
+
f->frame = *fit;
|
|
1697
|
+
|
|
1698
|
+
c->status = fromAVFrame(env, f, &frame);
|
|
1699
|
+
REJECT_STATUS;
|
|
1700
|
+
|
|
1701
|
+
c->status = napi_set_element(env, frames, frameCount++, frame);
|
|
1702
|
+
REJECT_STATUS;
|
|
1703
|
+
}
|
|
1704
|
+
c->status = napi_set_named_property(env, dstFrame, "frames", frames);
|
|
1705
|
+
REJECT_STATUS;
|
|
1706
|
+
|
|
1707
|
+
c->status = napi_set_element(env, result, outCount++, dstFrame);
|
|
1708
|
+
REJECT_STATUS;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
c->status = napi_create_int64(env, c->totalTime, &prop);
|
|
1712
|
+
REJECT_STATUS;
|
|
1713
|
+
c->status = napi_set_named_property(env, result, "total_time", prop);
|
|
1714
|
+
REJECT_STATUS;
|
|
1715
|
+
|
|
1716
|
+
napi_status status;
|
|
1717
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
1718
|
+
FLOATING_STATUS;
|
|
1719
|
+
|
|
1720
|
+
tidyCarrier(env, c);
|
|
1721
|
+
};
|
|
1722
|
+
|
|
1723
|
+
napi_value filter(napi_env env, napi_callback_info info) {
|
|
1724
|
+
napi_value resourceName, promise, filtererJS, value;
|
|
1725
|
+
filterCarrier* c = new filterCarrier;
|
|
1726
|
+
napi_ref frameRef;
|
|
1727
|
+
|
|
1728
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
1729
|
+
REJECT_RETURN;
|
|
1730
|
+
|
|
1731
|
+
size_t argc = 1;
|
|
1732
|
+
napi_value args[1];
|
|
1733
|
+
|
|
1734
|
+
c->status = napi_get_cb_info(env, info, &argc, args, &filtererJS, nullptr);
|
|
1735
|
+
REJECT_RETURN;
|
|
1736
|
+
|
|
1737
|
+
napi_value srcCtxsExt, sinkCtxsExt;
|
|
1738
|
+
c->status = napi_get_named_property(env, filtererJS, "_sourceContexts", &srcCtxsExt);
|
|
1739
|
+
REJECT_RETURN;
|
|
1740
|
+
c->status = napi_get_value_external(env, srcCtxsExt, (void**)&c->srcCtxs);
|
|
1741
|
+
REJECT_RETURN;
|
|
1742
|
+
|
|
1743
|
+
c->status = napi_get_named_property(env, filtererJS, "_sinkContexts", &sinkCtxsExt);
|
|
1744
|
+
REJECT_RETURN;
|
|
1745
|
+
c->status = napi_get_value_external(env, sinkCtxsExt, (void**)&c->sinkCtxs);
|
|
1746
|
+
REJECT_RETURN;
|
|
1747
|
+
|
|
1748
|
+
if (argc != 1) {
|
|
1749
|
+
REJECT_ERROR_RETURN("Filter requires source frame array.",
|
|
1750
|
+
BEAMCODER_INVALID_ARGS);
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
bool isArray;
|
|
1754
|
+
c->status = napi_is_array(env, args[0], &isArray);
|
|
1755
|
+
// REJECT_RETURN;
|
|
1756
|
+
// if (!isArray) // Allow array-like objects
|
|
1757
|
+
// REJECT_ERROR_RETURN("Expected an array of source frame objects.",
|
|
1758
|
+
// BEAMCODER_INVALID_ARGS);
|
|
1759
|
+
|
|
1760
|
+
napi_value item;
|
|
1761
|
+
c->status = napi_get_element(env, args[0], 0, &item);
|
|
1762
|
+
if (c->status != napi_ok) {
|
|
1763
|
+
REJECT_ERROR_RETURN("Expected an array or array-like object of source frame objects.",
|
|
1764
|
+
BEAMCODER_INVALID_ARGS);
|
|
1765
|
+
}
|
|
1766
|
+
if (napi_ok == isFrame(env, item)) {
|
|
1767
|
+
// Simplest case of an array of frame objects
|
|
1768
|
+
uint32_t framesLen;
|
|
1769
|
+
if (isArray) {
|
|
1770
|
+
c->status = napi_get_array_length(env, args[0], &framesLen);
|
|
1771
|
+
REJECT_RETURN;
|
|
1772
|
+
} else {
|
|
1773
|
+
napi_value propNames;
|
|
1774
|
+
c->status = napi_get_property_names(env, args[0], &propNames);
|
|
1775
|
+
REJECT_RETURN;
|
|
1776
|
+
c->status = napi_get_array_length(env, propNames, &framesLen);
|
|
1777
|
+
REJECT_RETURN;
|
|
1778
|
+
}
|
|
1779
|
+
for (uint32_t f = 0; f < framesLen; ++f) {
|
|
1780
|
+
c->status = napi_get_element(env, args[0], f, &value);
|
|
1781
|
+
REJECT_RETURN;
|
|
1782
|
+
c->status = isFrame(env, value);
|
|
1783
|
+
if (c->status != napi_ok) {
|
|
1784
|
+
REJECT_ERROR_RETURN("Expected an array whose elements must be of type frame.",
|
|
1785
|
+
BEAMCODER_INVALID_ARGS);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
std::deque<AVFrame *> frames;
|
|
1789
|
+
for (uint32_t f = 0; f < framesLen; ++f) {
|
|
1790
|
+
c->status = napi_get_element(env, args[0], f, &value);
|
|
1791
|
+
REJECT_RETURN;
|
|
1792
|
+
c->status = napi_create_reference(env, value, 1, &frameRef);
|
|
1793
|
+
REJECT_RETURN;
|
|
1794
|
+
|
|
1795
|
+
c->frameRefs.push_back(frameRef);
|
|
1796
|
+
frames.push_back(getFrame(env, value));
|
|
1797
|
+
}
|
|
1798
|
+
auto result = c->srcFrames.emplace("in", frames);
|
|
1799
|
+
if (!result.second) {
|
|
1800
|
+
REJECT_ERROR_RETURN("Frame names must be unique.",
|
|
1801
|
+
BEAMCODER_INVALID_ARGS);
|
|
1802
|
+
}
|
|
1803
|
+
} else {
|
|
1804
|
+
// Argument is an array of filter objects with name and frames members
|
|
1805
|
+
uint32_t srcsLen;
|
|
1806
|
+
c->status = napi_get_array_length(env, args[0], &srcsLen);
|
|
1807
|
+
REJECT_RETURN;
|
|
1808
|
+
for (uint32_t i = 0; i < srcsLen; ++i) {
|
|
1809
|
+
napi_value item;
|
|
1810
|
+
c->status = napi_get_element(env, args[0], i, &item);
|
|
1811
|
+
REJECT_RETURN;
|
|
1812
|
+
std::string name;
|
|
1813
|
+
bool hasName;
|
|
1814
|
+
c->status = napi_has_named_property(env, item, "name", &hasName);
|
|
1815
|
+
REJECT_RETURN;
|
|
1816
|
+
if (hasName) {
|
|
1817
|
+
napi_value nameVal;
|
|
1818
|
+
c->status = napi_get_named_property(env, item, "name", &nameVal);
|
|
1819
|
+
REJECT_RETURN;
|
|
1820
|
+
size_t nameLen;
|
|
1821
|
+
c->status = napi_get_value_string_utf8(env, nameVal, nullptr, 0, &nameLen);
|
|
1822
|
+
REJECT_RETURN;
|
|
1823
|
+
name.resize(nameLen);
|
|
1824
|
+
c->status = napi_get_value_string_utf8(env, nameVal, (char *)name.data(), nameLen+1, nullptr);
|
|
1825
|
+
REJECT_RETURN;
|
|
1826
|
+
} else if (0 == i) {
|
|
1827
|
+
name = "in";
|
|
1828
|
+
} else {
|
|
1829
|
+
REJECT_ERROR_RETURN("Source frame object requires a name.",
|
|
1830
|
+
BEAMCODER_INVALID_ARGS);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
napi_value framesArrVal;
|
|
1834
|
+
c->status = napi_get_named_property(env, item, "frames", &framesArrVal);
|
|
1835
|
+
REJECT_RETURN;
|
|
1836
|
+
|
|
1837
|
+
bool isArray;
|
|
1838
|
+
uint32_t framesLen;
|
|
1839
|
+
c->status = napi_is_array(env, framesArrVal, &isArray);
|
|
1840
|
+
REJECT_RETURN;
|
|
1841
|
+
if (isArray) {
|
|
1842
|
+
c->status = napi_get_array_length(env, framesArrVal, &framesLen);
|
|
1843
|
+
REJECT_RETURN;
|
|
1844
|
+
} else {
|
|
1845
|
+
napi_value propNames;
|
|
1846
|
+
c->status = napi_get_property_names(env, framesArrVal, &propNames);
|
|
1847
|
+
REJECT_RETURN;
|
|
1848
|
+
c->status = napi_get_array_length(env, propNames, &framesLen);
|
|
1849
|
+
REJECT_RETURN;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
std::deque<AVFrame *> frames;
|
|
1853
|
+
for (uint32_t f = 0; f < framesLen; ++f) {
|
|
1854
|
+
c->status = napi_get_element(env, framesArrVal, f, &value);
|
|
1855
|
+
REJECT_RETURN; // Blow up here if not array or array-like
|
|
1856
|
+
c->status = isFrame(env, value);
|
|
1857
|
+
if (c->status != napi_ok) {
|
|
1858
|
+
REJECT_ERROR_RETURN("Values in array must by of type frame.",
|
|
1859
|
+
BEAMCODER_INVALID_ARGS);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
for (uint32_t f = 0; f < framesLen; ++f) {
|
|
1863
|
+
c->status = napi_get_element(env, framesArrVal, f, &value);
|
|
1864
|
+
REJECT_RETURN;
|
|
1865
|
+
c->status = napi_create_reference(env, value, 1, &frameRef);
|
|
1866
|
+
REJECT_RETURN;
|
|
1867
|
+
|
|
1868
|
+
c->frameRefs.push_back(frameRef);
|
|
1869
|
+
frames.push_back(getFrame(env, value));
|
|
1870
|
+
}
|
|
1871
|
+
auto result = c->srcFrames.emplace(name, frames);
|
|
1872
|
+
if (!result.second) {
|
|
1873
|
+
REJECT_ERROR_RETURN("Frame names must be unique.",
|
|
1874
|
+
BEAMCODER_INVALID_ARGS);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
c->status = napi_create_string_utf8(env, "Filter", NAPI_AUTO_LENGTH, &resourceName);
|
|
1880
|
+
REJECT_RETURN;
|
|
1881
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, filterExecute,
|
|
1882
|
+
filterComplete, c, &c->_request);
|
|
1883
|
+
REJECT_RETURN;
|
|
1884
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
1885
|
+
REJECT_RETURN;
|
|
1886
|
+
|
|
1887
|
+
return promise;
|
|
1888
|
+
};
|