@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/mux.cc
ADDED
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Aerostat Beam Coder - Node.js native bindings for FFmpeg.
|
|
3
|
+
Copyright (C) 2019 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 "mux.h"
|
|
23
|
+
|
|
24
|
+
int write_packet(void *opaque, uint8_t *buf, int buf_size)
|
|
25
|
+
{
|
|
26
|
+
Adaptor *adaptor = (Adaptor *)opaque;
|
|
27
|
+
return adaptor->write(buf, buf_size);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
napi_value muxer(napi_env env, napi_callback_info info) {
|
|
31
|
+
napi_status status;
|
|
32
|
+
napi_value result, prop, subprop;
|
|
33
|
+
int ret;
|
|
34
|
+
AVOutputFormat* oformat = nullptr;
|
|
35
|
+
char* formatName = nullptr;
|
|
36
|
+
char* filename = nullptr;
|
|
37
|
+
size_t strLen;
|
|
38
|
+
napi_valuetype type;
|
|
39
|
+
bool isArray;
|
|
40
|
+
AVFormatContext* fmtCtx = nullptr;
|
|
41
|
+
Adaptor *adaptor = nullptr;
|
|
42
|
+
size_t argc = 1;
|
|
43
|
+
napi_value args[1];
|
|
44
|
+
|
|
45
|
+
status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
|
46
|
+
CHECK_STATUS;
|
|
47
|
+
if (argc != 1) {
|
|
48
|
+
NAPI_THROW_ERROR("Muxer can only be allocated with a single options object.");
|
|
49
|
+
}
|
|
50
|
+
status = napi_typeof(env, args[0], &type);
|
|
51
|
+
CHECK_STATUS;
|
|
52
|
+
status = napi_is_array(env, args[0], &isArray);
|
|
53
|
+
CHECK_STATUS;
|
|
54
|
+
if (isArray || (type != napi_object)) {
|
|
55
|
+
NAPI_THROW_ERROR("Muxer must be allocated be a single options object.");
|
|
56
|
+
}
|
|
57
|
+
status = napi_get_named_property(env, args[0], "format_name", &prop);
|
|
58
|
+
CHECK_STATUS;
|
|
59
|
+
status = napi_typeof(env, prop, &type);
|
|
60
|
+
CHECK_STATUS;
|
|
61
|
+
if (type == napi_undefined) {
|
|
62
|
+
status = napi_get_named_property(env, args[0], "name", &prop);
|
|
63
|
+
CHECK_STATUS;
|
|
64
|
+
status = napi_typeof(env, prop, &type);
|
|
65
|
+
CHECK_STATUS;
|
|
66
|
+
}
|
|
67
|
+
if (type == napi_string) { // Found a name property
|
|
68
|
+
status = napi_get_value_string_utf8(env, prop, nullptr, 0, &strLen);
|
|
69
|
+
CHECK_STATUS;
|
|
70
|
+
formatName = (char*) malloc(sizeof(char) * (strLen + 1));
|
|
71
|
+
status = napi_get_value_string_utf8(env, prop, formatName, strLen + 1, &strLen);
|
|
72
|
+
CHECK_STATUS;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
status = napi_get_named_property(env, args[0], "filename", &prop);
|
|
76
|
+
CHECK_STATUS;
|
|
77
|
+
status = napi_typeof(env, prop, &type);
|
|
78
|
+
CHECK_STATUS;
|
|
79
|
+
if (type == napi_string) {
|
|
80
|
+
status = napi_get_value_string_utf8(env, prop, nullptr, 0, &strLen);
|
|
81
|
+
CHECK_STATUS;
|
|
82
|
+
filename = (char*) malloc(sizeof(char) * (strLen + 1));
|
|
83
|
+
status = napi_get_value_string_utf8(env, prop, filename, strLen + 1, &strLen);
|
|
84
|
+
CHECK_STATUS;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
status = napi_get_named_property(env, args[0], "oformat", &prop);
|
|
88
|
+
CHECK_STATUS;
|
|
89
|
+
status = napi_typeof(env, prop, &type);
|
|
90
|
+
CHECK_STATUS;
|
|
91
|
+
status = napi_is_array(env, prop, &isArray);
|
|
92
|
+
CHECK_STATUS;
|
|
93
|
+
if (!isArray && (type == napi_object)) {
|
|
94
|
+
status = napi_get_named_property(env, prop, "_oformat", &subprop);
|
|
95
|
+
CHECK_STATUS;
|
|
96
|
+
status = napi_typeof(env, subprop, &type);
|
|
97
|
+
CHECK_STATUS;
|
|
98
|
+
if (type == napi_external) {
|
|
99
|
+
status = napi_get_value_external(env, subprop, (void**) &oformat);
|
|
100
|
+
CHECK_STATUS;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
status = napi_get_named_property(env, args[0], "governor", &prop);
|
|
105
|
+
CHECK_STATUS;
|
|
106
|
+
status = napi_typeof(env, prop, &type);
|
|
107
|
+
CHECK_STATUS;
|
|
108
|
+
if (type == napi_object) {
|
|
109
|
+
napi_value adaptorValue;
|
|
110
|
+
status = napi_get_named_property(env, prop, "_adaptor", &adaptorValue);
|
|
111
|
+
CHECK_STATUS;
|
|
112
|
+
status = napi_typeof(env, adaptorValue, &type);
|
|
113
|
+
CHECK_STATUS;
|
|
114
|
+
if (type == napi_external) {
|
|
115
|
+
status = napi_get_value_external(env, adaptorValue, (void**)&adaptor);
|
|
116
|
+
CHECK_STATUS;
|
|
117
|
+
} else if (type != napi_undefined) {
|
|
118
|
+
NAPI_THROW_ERROR("Adaptor must be of external type when specified.");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
AVIOContext* avio_ctx = nullptr;
|
|
123
|
+
if (adaptor) {
|
|
124
|
+
avio_ctx = avio_alloc_context(adaptor->buf(), adaptor->bufLen(), 1, adaptor, nullptr, &write_packet, nullptr);
|
|
125
|
+
if (!avio_ctx) {
|
|
126
|
+
NAPI_THROW_ERROR("Problem allocating muxer stream output context.");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
ret = avformat_alloc_output_context2(&fmtCtx, oformat, formatName, filename);
|
|
130
|
+
|
|
131
|
+
free(formatName);
|
|
132
|
+
free(filename);
|
|
133
|
+
|
|
134
|
+
if (ret < 0) {
|
|
135
|
+
NAPI_THROW_ERROR(avErrorMsg("Error allocating muxer context: ", ret));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fmtCtx->pb = avio_ctx;
|
|
139
|
+
|
|
140
|
+
status = fromAVFormatContext(env, fmtCtx, adaptor, &result);
|
|
141
|
+
CHECK_STATUS;
|
|
142
|
+
|
|
143
|
+
status = napi_create_function(env, "openIO", NAPI_AUTO_LENGTH,
|
|
144
|
+
openIO, nullptr, &prop);
|
|
145
|
+
CHECK_STATUS;
|
|
146
|
+
status = napi_set_named_property(env, result, "openIO", prop);
|
|
147
|
+
CHECK_STATUS;
|
|
148
|
+
|
|
149
|
+
status = napi_create_function(env, "writeHeader", NAPI_AUTO_LENGTH,
|
|
150
|
+
writeHeader, nullptr, &prop);
|
|
151
|
+
CHECK_STATUS;
|
|
152
|
+
status = napi_set_named_property(env, result, "writeHeader", prop);
|
|
153
|
+
CHECK_STATUS;
|
|
154
|
+
|
|
155
|
+
status = napi_create_function(env, "initOutput", NAPI_AUTO_LENGTH,
|
|
156
|
+
initOutput, nullptr, &prop);
|
|
157
|
+
CHECK_STATUS;
|
|
158
|
+
status = napi_set_named_property(env, result, "initOutput", prop);
|
|
159
|
+
CHECK_STATUS;
|
|
160
|
+
|
|
161
|
+
status = napi_create_function(env, "writeFrame", NAPI_AUTO_LENGTH,
|
|
162
|
+
writeFrame, nullptr, &prop);
|
|
163
|
+
CHECK_STATUS;
|
|
164
|
+
status = napi_set_named_property(env, result, "writeFrame", prop);
|
|
165
|
+
CHECK_STATUS;
|
|
166
|
+
|
|
167
|
+
status = napi_create_function(env, "writeTrailer", NAPI_AUTO_LENGTH,
|
|
168
|
+
writeTrailer, nullptr, &prop);
|
|
169
|
+
CHECK_STATUS;
|
|
170
|
+
status = napi_set_named_property(env, result, "writeTrailer", prop);
|
|
171
|
+
CHECK_STATUS;
|
|
172
|
+
|
|
173
|
+
status = napi_create_function(env, "forceClose", NAPI_AUTO_LENGTH,
|
|
174
|
+
forceClose, nullptr, &prop);
|
|
175
|
+
CHECK_STATUS;
|
|
176
|
+
status = napi_set_named_property(env, result, "forceClose", prop);
|
|
177
|
+
CHECK_STATUS;
|
|
178
|
+
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
void openIOExecute(napi_env env, void* data) {
|
|
183
|
+
openIOCarrier* c = (openIOCarrier*) data;
|
|
184
|
+
int ret;
|
|
185
|
+
if (c->format->pb == nullptr) {
|
|
186
|
+
ret = avio_open2(&c->format->pb, c->format->url, c->flags, nullptr, &c->options);
|
|
187
|
+
if (ret < 0) {
|
|
188
|
+
c->status = BEAMCODER_ERROR_OPENIO;
|
|
189
|
+
c->errorMsg = avErrorMsg("Problem opening IO context: ", ret);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
void openIOComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
195
|
+
napi_value result, unset;
|
|
196
|
+
openIOCarrier* c = (openIOCarrier*) data;
|
|
197
|
+
AVDictionaryEntry* tag = nullptr;
|
|
198
|
+
|
|
199
|
+
if (asyncStatus != napi_ok) {
|
|
200
|
+
c->status = asyncStatus;
|
|
201
|
+
c->errorMsg = "Open IO failed to complete.";
|
|
202
|
+
}
|
|
203
|
+
REJECT_STATUS;
|
|
204
|
+
|
|
205
|
+
if (c->options != nullptr) {
|
|
206
|
+
c->status = napi_create_object(env, &result);
|
|
207
|
+
REJECT_STATUS;
|
|
208
|
+
c->status = napi_create_object(env, &unset);
|
|
209
|
+
REJECT_STATUS;
|
|
210
|
+
c->status = napi_set_named_property(env, result, "unset", unset);
|
|
211
|
+
REJECT_STATUS;
|
|
212
|
+
while ((tag = av_dict_get(c->options, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
|
213
|
+
c->status = beam_set_string_utf8(env, unset, tag->key, tag->value);
|
|
214
|
+
REJECT_STATUS;
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
c->status = napi_get_undefined(env, &result);
|
|
218
|
+
REJECT_STATUS;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
napi_status status;
|
|
222
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
223
|
+
FLOATING_STATUS;
|
|
224
|
+
|
|
225
|
+
tidyCarrier(env, c);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
napi_value openIO(napi_env env, napi_callback_info info) {
|
|
229
|
+
napi_value promise, formatJS, formatExt, resourceName, prop;
|
|
230
|
+
napi_valuetype type;
|
|
231
|
+
bool isArray, present, flag;
|
|
232
|
+
size_t strLen;
|
|
233
|
+
openIOCarrier* c = new openIOCarrier;
|
|
234
|
+
|
|
235
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
236
|
+
REJECT_RETURN;
|
|
237
|
+
|
|
238
|
+
size_t argc = 0;
|
|
239
|
+
c->status = napi_get_cb_info(env, info, &argc, nullptr, &formatJS, nullptr);
|
|
240
|
+
REJECT_RETURN;
|
|
241
|
+
c->status = napi_get_named_property(env, formatJS, "_formatContext", &formatExt);
|
|
242
|
+
REJECT_RETURN;
|
|
243
|
+
c->status = napi_get_value_external(env, formatExt, (void**) &c->format);
|
|
244
|
+
REJECT_RETURN;
|
|
245
|
+
|
|
246
|
+
if (argc > 0) { // Possible options, url and flags
|
|
247
|
+
napi_value args[1];
|
|
248
|
+
argc = 1;
|
|
249
|
+
c->status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
|
250
|
+
REJECT_RETURN;
|
|
251
|
+
c->status = napi_typeof(env, args[0], &type);
|
|
252
|
+
REJECT_RETURN;
|
|
253
|
+
c->status = napi_is_array(env, args[0], &isArray);
|
|
254
|
+
REJECT_RETURN;
|
|
255
|
+
if (isArray || (type != napi_object)) {
|
|
256
|
+
REJECT_ERROR_RETURN("OpenIO can only be provided with a single options object.",
|
|
257
|
+
BEAMCODER_INVALID_ARGS);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
c->status = napi_get_named_property(env, args[0], "url", &prop);
|
|
261
|
+
REJECT_RETURN;
|
|
262
|
+
c->status = napi_typeof(env, prop, &type);
|
|
263
|
+
REJECT_RETURN;
|
|
264
|
+
if (type == napi_string) {
|
|
265
|
+
c->status = napi_get_value_string_utf8(env, prop, nullptr, 0, &strLen);
|
|
266
|
+
REJECT_RETURN;
|
|
267
|
+
c->format->url = (char*) av_malloc(sizeof(char) * (strLen + 1));
|
|
268
|
+
c->status = napi_get_value_string_utf8(env, prop, c->format->url, strLen + 1, &strLen);
|
|
269
|
+
REJECT_RETURN;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
c->status = napi_get_named_property(env, args[0], "filename", &prop);
|
|
273
|
+
REJECT_RETURN;
|
|
274
|
+
c->status = napi_typeof(env, prop, &type);
|
|
275
|
+
REJECT_RETURN;
|
|
276
|
+
if (type == napi_string) {
|
|
277
|
+
c->status = napi_get_value_string_utf8(env, prop, nullptr, 0, &strLen);
|
|
278
|
+
REJECT_RETURN;
|
|
279
|
+
c->format->url = (char*) av_malloc(sizeof(char) * (strLen + 1));
|
|
280
|
+
c->status = napi_get_value_string_utf8(env, prop, c->format->url, strLen + 1, &strLen);
|
|
281
|
+
REJECT_RETURN;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
c->status = napi_get_named_property(env, args[0], "options", &prop);
|
|
285
|
+
REJECT_RETURN;
|
|
286
|
+
c->status = napi_typeof(env, prop, &type);
|
|
287
|
+
REJECT_RETURN;
|
|
288
|
+
c->status = napi_is_array(env, prop, &isArray);
|
|
289
|
+
REJECT_RETURN;
|
|
290
|
+
if (!isArray && (type == napi_object)) {
|
|
291
|
+
c->status = makeAVDictionary(env, prop, &c->options);
|
|
292
|
+
REJECT_RETURN;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
c->status = napi_get_named_property(env, args[0], "flags", &prop);
|
|
296
|
+
REJECT_RETURN
|
|
297
|
+
c->status = napi_typeof(env, prop, &type);
|
|
298
|
+
REJECT_RETURN;
|
|
299
|
+
c->status = napi_is_array(env, prop, &isArray);
|
|
300
|
+
REJECT_RETURN;
|
|
301
|
+
if (!isArray && (type == napi_object)) {
|
|
302
|
+
c->status = beam_get_bool(env, prop, "READ", &present, &flag);
|
|
303
|
+
REJECT_RETURN;
|
|
304
|
+
if (present) { c->flags = (flag) ?
|
|
305
|
+
c->flags | AVIO_FLAG_READ :
|
|
306
|
+
c->flags & ~AVIO_FLAG_READ; }
|
|
307
|
+
c->status = beam_get_bool(env, prop, "WRITE", &present, &flag);
|
|
308
|
+
REJECT_RETURN;
|
|
309
|
+
if (present) { c->flags = (flag) ?
|
|
310
|
+
c->flags | AVIO_FLAG_WRITE :
|
|
311
|
+
c->flags & ~AVIO_FLAG_WRITE; }
|
|
312
|
+
c->status = beam_get_bool(env, prop, "NONBLOCK", &present, &flag);
|
|
313
|
+
REJECT_RETURN;
|
|
314
|
+
if (present) { c->flags = (flag) ?
|
|
315
|
+
c->flags | AVIO_FLAG_NONBLOCK :
|
|
316
|
+
c->flags & ~AVIO_FLAG_NONBLOCK; }
|
|
317
|
+
c->status = beam_get_bool(env, prop, "DIRECT", &present, &flag);
|
|
318
|
+
REJECT_RETURN;
|
|
319
|
+
if (present) { c->flags = (flag) ?
|
|
320
|
+
c->flags | AVIO_FLAG_DIRECT :
|
|
321
|
+
c->flags & ~AVIO_FLAG_DIRECT; }
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if ((c->format->url == nullptr) && (c->format->pb == nullptr)) {
|
|
326
|
+
REJECT_ERROR_RETURN("Cannot open muxer IO without a URL or filename.",
|
|
327
|
+
BEAMCODER_INVALID_ARGS);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
c->status = napi_create_string_utf8(env, "OpenIO", NAPI_AUTO_LENGTH, &resourceName);
|
|
331
|
+
REJECT_RETURN;
|
|
332
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, openIOExecute,
|
|
333
|
+
openIOComplete, c, &c->_request);
|
|
334
|
+
REJECT_RETURN;
|
|
335
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
336
|
+
REJECT_RETURN;
|
|
337
|
+
|
|
338
|
+
return promise;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
void writeHeaderExecute(napi_env env, void* data) {
|
|
342
|
+
writeHeaderCarrier* c = (writeHeaderCarrier*) data;
|
|
343
|
+
|
|
344
|
+
c->result = avformat_write_header(c->format, &c->options);
|
|
345
|
+
if (c->result < 0) {
|
|
346
|
+
c->status = BEAMCODER_ERROR_WRITE_HEADER;
|
|
347
|
+
c->errorMsg = avErrorMsg("Failed to write header: ", c->result);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
void writeHeaderComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
353
|
+
napi_value result, unset, initIn;
|
|
354
|
+
writeHeaderCarrier* c = (writeHeaderCarrier*) data;
|
|
355
|
+
AVDictionaryEntry* tag = nullptr;
|
|
356
|
+
|
|
357
|
+
if (asyncStatus != napi_ok) {
|
|
358
|
+
c->status = asyncStatus;
|
|
359
|
+
c->errorMsg = "Write header failed to complete.";
|
|
360
|
+
}
|
|
361
|
+
REJECT_STATUS;
|
|
362
|
+
|
|
363
|
+
c->status = napi_create_object(env, &result);
|
|
364
|
+
REJECT_STATUS;
|
|
365
|
+
|
|
366
|
+
c->status = napi_create_string_utf8(env,
|
|
367
|
+
(c->result == AVSTREAM_INIT_IN_WRITE_HEADER) ? "WRITE_HEADER" : "INIT_OUTPUT",
|
|
368
|
+
NAPI_AUTO_LENGTH, &initIn);
|
|
369
|
+
REJECT_STATUS;
|
|
370
|
+
c->status = napi_set_named_property(env, result, "INIT_IN", initIn);
|
|
371
|
+
REJECT_STATUS;
|
|
372
|
+
|
|
373
|
+
if (c->options != nullptr) {
|
|
374
|
+
c->status = napi_create_object(env, &unset);
|
|
375
|
+
REJECT_STATUS;
|
|
376
|
+
c->status = napi_set_named_property(env, result, "unset", unset);
|
|
377
|
+
REJECT_STATUS;
|
|
378
|
+
while ((tag = av_dict_get(c->options, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
|
379
|
+
c->status = beam_set_string_utf8(env, unset, tag->key, tag->value);
|
|
380
|
+
REJECT_STATUS;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
napi_status status;
|
|
385
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
386
|
+
FLOATING_STATUS;
|
|
387
|
+
|
|
388
|
+
tidyCarrier(env, c);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
napi_value writeHeader(napi_env env, napi_callback_info info) {
|
|
392
|
+
napi_value promise, formatJS, formatExt, resourceName, prop;
|
|
393
|
+
napi_valuetype type;
|
|
394
|
+
bool isArray;
|
|
395
|
+
writeHeaderCarrier* c = new writeHeaderCarrier;
|
|
396
|
+
|
|
397
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
398
|
+
REJECT_RETURN;
|
|
399
|
+
|
|
400
|
+
size_t argc = 0;
|
|
401
|
+
c->status = napi_get_cb_info(env, info, &argc, nullptr, &formatJS, nullptr);
|
|
402
|
+
REJECT_RETURN;
|
|
403
|
+
c->status = napi_get_named_property(env, formatJS, "_formatContext", &formatExt);
|
|
404
|
+
REJECT_RETURN;
|
|
405
|
+
c->status = napi_get_value_external(env, formatExt, (void**) &c->format);
|
|
406
|
+
REJECT_RETURN;
|
|
407
|
+
|
|
408
|
+
if (argc > 0) { // Possible options
|
|
409
|
+
napi_value args[1];
|
|
410
|
+
argc = 1;
|
|
411
|
+
c->status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
|
412
|
+
REJECT_RETURN;
|
|
413
|
+
c->status = napi_typeof(env, args[0], &type);
|
|
414
|
+
REJECT_RETURN;
|
|
415
|
+
c->status = napi_is_array(env, args[0], &isArray);
|
|
416
|
+
REJECT_RETURN;
|
|
417
|
+
if (isArray || (type != napi_object)) {
|
|
418
|
+
REJECT_ERROR_RETURN("Write header can only be provided with a single options object.",
|
|
419
|
+
BEAMCODER_INVALID_ARGS);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
c->status = napi_get_named_property(env, args[0], "options", &prop);
|
|
423
|
+
REJECT_RETURN;
|
|
424
|
+
c->status = napi_typeof(env, prop, &type);
|
|
425
|
+
REJECT_RETURN;
|
|
426
|
+
c->status = napi_is_array(env, prop, &isArray);
|
|
427
|
+
REJECT_RETURN;
|
|
428
|
+
if (isArray || (type != napi_object)) { // Allow flat or nested options
|
|
429
|
+
prop = args[0];
|
|
430
|
+
}
|
|
431
|
+
c->status = makeAVDictionary(env, prop, &c->options);
|
|
432
|
+
REJECT_RETURN;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
c->status = napi_create_string_utf8(env, "WriteHeader", NAPI_AUTO_LENGTH, &resourceName);
|
|
436
|
+
REJECT_RETURN;
|
|
437
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, writeHeaderExecute,
|
|
438
|
+
writeHeaderComplete, c, &c->_request);
|
|
439
|
+
REJECT_RETURN;
|
|
440
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
441
|
+
REJECT_RETURN;
|
|
442
|
+
|
|
443
|
+
return promise;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
void initOutputExecute(napi_env env, void* data) {
|
|
447
|
+
initOutputCarrier* c = (initOutputCarrier*) data;
|
|
448
|
+
|
|
449
|
+
c->result = avformat_init_output(c->format, &c->options);
|
|
450
|
+
if (c->result < 0) {
|
|
451
|
+
c->status = BEAMCODER_ERROR_INIT_OUTPUT;
|
|
452
|
+
c->errorMsg = avErrorMsg("Failed to initialize output: ", c->result);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
void initOutputComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
458
|
+
napi_value result, initIn, unset;
|
|
459
|
+
AVDictionaryEntry* tag = nullptr;
|
|
460
|
+
initOutputCarrier* c = (initOutputCarrier*) data;
|
|
461
|
+
|
|
462
|
+
if (asyncStatus != napi_ok) {
|
|
463
|
+
c->status = asyncStatus;
|
|
464
|
+
c->errorMsg = "Initialize output failed to complete.";
|
|
465
|
+
}
|
|
466
|
+
REJECT_STATUS;
|
|
467
|
+
|
|
468
|
+
c->status = napi_create_object(env, &result);
|
|
469
|
+
REJECT_STATUS;
|
|
470
|
+
|
|
471
|
+
c->status = napi_create_string_utf8(env,
|
|
472
|
+
(c->result == AVSTREAM_INIT_IN_WRITE_HEADER) ? "WRITE_HEADER" : "INIT_OUTPUT",
|
|
473
|
+
NAPI_AUTO_LENGTH, &initIn);
|
|
474
|
+
REJECT_STATUS;
|
|
475
|
+
c->status = napi_set_named_property(env, result, "INIT_IN", initIn);
|
|
476
|
+
REJECT_STATUS;
|
|
477
|
+
|
|
478
|
+
if (c->options != nullptr) {
|
|
479
|
+
c->status = napi_create_object(env, &unset);
|
|
480
|
+
REJECT_STATUS;
|
|
481
|
+
c->status = napi_set_named_property(env, result, "unset", unset);
|
|
482
|
+
REJECT_STATUS;
|
|
483
|
+
while ((tag = av_dict_get(c->options, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
|
484
|
+
c->status = beam_set_string_utf8(env, unset, tag->key, tag->value);
|
|
485
|
+
REJECT_STATUS;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
napi_status status;
|
|
490
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
491
|
+
FLOATING_STATUS;
|
|
492
|
+
|
|
493
|
+
tidyCarrier(env, c);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
napi_value initOutput(napi_env env, napi_callback_info info) {
|
|
497
|
+
napi_value promise, formatJS, formatExt, resourceName, prop;
|
|
498
|
+
napi_valuetype type;
|
|
499
|
+
bool isArray;
|
|
500
|
+
initOutputCarrier* c = new initOutputCarrier;
|
|
501
|
+
|
|
502
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
503
|
+
REJECT_RETURN;
|
|
504
|
+
|
|
505
|
+
size_t argc = 0;
|
|
506
|
+
c->status = napi_get_cb_info(env, info, &argc, nullptr, &formatJS, nullptr);
|
|
507
|
+
REJECT_RETURN;
|
|
508
|
+
c->status = napi_get_named_property(env, formatJS, "_formatContext", &formatExt);
|
|
509
|
+
REJECT_RETURN;
|
|
510
|
+
c->status = napi_get_value_external(env, formatExt, (void**) &c->format);
|
|
511
|
+
REJECT_RETURN;
|
|
512
|
+
|
|
513
|
+
if (argc > 0) { // Possible options
|
|
514
|
+
napi_value args[1];
|
|
515
|
+
argc = 1;
|
|
516
|
+
c->status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
|
517
|
+
REJECT_RETURN;
|
|
518
|
+
c->status = napi_typeof(env, args[0], &type);
|
|
519
|
+
REJECT_RETURN;
|
|
520
|
+
c->status = napi_is_array(env, args[0], &isArray);
|
|
521
|
+
REJECT_RETURN;
|
|
522
|
+
if (isArray || (type != napi_object)) {
|
|
523
|
+
REJECT_ERROR_RETURN("Initialize output can only be provided with a single options object.",
|
|
524
|
+
BEAMCODER_INVALID_ARGS);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
c->status = napi_get_named_property(env, args[0], "options", &prop);
|
|
528
|
+
REJECT_RETURN;
|
|
529
|
+
c->status = napi_typeof(env, prop, &type);
|
|
530
|
+
REJECT_RETURN;
|
|
531
|
+
c->status = napi_is_array(env, prop, &isArray);
|
|
532
|
+
REJECT_RETURN;
|
|
533
|
+
if (isArray || (type != napi_object)) { // Allow flat or nested options
|
|
534
|
+
prop = args[0];
|
|
535
|
+
}
|
|
536
|
+
c->status = makeAVDictionary(env, prop, &c->options);
|
|
537
|
+
REJECT_RETURN;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
c->status = napi_create_string_utf8(env, "InitOutput", NAPI_AUTO_LENGTH, &resourceName);
|
|
541
|
+
REJECT_RETURN;
|
|
542
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, initOutputExecute,
|
|
543
|
+
initOutputComplete, c, &c->_request);
|
|
544
|
+
REJECT_RETURN;
|
|
545
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
546
|
+
REJECT_RETURN;
|
|
547
|
+
|
|
548
|
+
return promise;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
void writeFrameExecute(napi_env env, void* data) {
|
|
552
|
+
writeFrameCarrier* c = (writeFrameCarrier*) data;
|
|
553
|
+
int ret;
|
|
554
|
+
|
|
555
|
+
if (c->interleaved) {
|
|
556
|
+
if (c->packet != nullptr) {
|
|
557
|
+
ret = av_interleaved_write_frame(c->format, c->packet);
|
|
558
|
+
} else if (c->frame != nullptr) {
|
|
559
|
+
ret = av_interleaved_write_uncoded_frame(c->format, c->streamIndex, c->frame);
|
|
560
|
+
} else {
|
|
561
|
+
ret = av_interleaved_write_frame(c->format, nullptr); // flush
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
if (c->packet != nullptr) {
|
|
565
|
+
ret = av_write_frame(c->format, c->packet);
|
|
566
|
+
} else if (c->frame != nullptr) {
|
|
567
|
+
ret = av_write_uncoded_frame(c->format, c->streamIndex, c->frame);
|
|
568
|
+
} else {
|
|
569
|
+
ret = av_write_frame(c->format, nullptr); // flush
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (ret < 0) {
|
|
574
|
+
c->status = BEAMCODER_ERROR_WRITE_FRAME;
|
|
575
|
+
c->errorMsg = avErrorMsg("Error writing frame: ", ret);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
void writeFrameComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
581
|
+
napi_value result;
|
|
582
|
+
writeFrameCarrier* c = (writeFrameCarrier*) data;
|
|
583
|
+
|
|
584
|
+
if (asyncStatus != napi_ok) {
|
|
585
|
+
c->status = asyncStatus;
|
|
586
|
+
c->errorMsg = "Write frame failed to complete.";
|
|
587
|
+
}
|
|
588
|
+
REJECT_STATUS;
|
|
589
|
+
|
|
590
|
+
// tidy up adaptor chunks if required
|
|
591
|
+
if (c->adaptor) {
|
|
592
|
+
c->status = c->adaptor->finaliseBufs(env);
|
|
593
|
+
REJECT_STATUS;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
c->status = napi_get_undefined(env, &result);
|
|
597
|
+
REJECT_STATUS;
|
|
598
|
+
|
|
599
|
+
napi_status status;
|
|
600
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
601
|
+
FLOATING_STATUS;
|
|
602
|
+
|
|
603
|
+
tidyCarrier(env, c);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
napi_value writeFrame(napi_env env, napi_callback_info info) {
|
|
607
|
+
napi_value promise, formatJS, formatExt, adaptorExt, interleavedJS, resourceName, options, prop;
|
|
608
|
+
napi_valuetype type;
|
|
609
|
+
bool isArray;
|
|
610
|
+
bool hasOptions = false;
|
|
611
|
+
writeFrameCarrier* c = new writeFrameCarrier;
|
|
612
|
+
packetData* packetData;
|
|
613
|
+
frameData* frameData;
|
|
614
|
+
int ret;
|
|
615
|
+
|
|
616
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
617
|
+
REJECT_RETURN;
|
|
618
|
+
|
|
619
|
+
size_t argc = 1;
|
|
620
|
+
napi_value args[1];
|
|
621
|
+
c->status = napi_get_cb_info(env, info, &argc, args, &formatJS, nullptr);
|
|
622
|
+
REJECT_RETURN;
|
|
623
|
+
c->status = napi_get_named_property(env, formatJS, "_formatContext", &formatExt);
|
|
624
|
+
REJECT_RETURN;
|
|
625
|
+
c->status = napi_get_value_external(env, formatExt, (void**) &c->format);
|
|
626
|
+
REJECT_RETURN;
|
|
627
|
+
|
|
628
|
+
c->status = napi_get_named_property(env, formatJS, "_adaptor", &adaptorExt);
|
|
629
|
+
REJECT_RETURN;
|
|
630
|
+
c->status = napi_get_value_external(env, adaptorExt, (void**)&c->adaptor);
|
|
631
|
+
REJECT_RETURN;
|
|
632
|
+
|
|
633
|
+
c->status = napi_get_named_property(env, formatJS, "interleaved", &interleavedJS);
|
|
634
|
+
REJECT_RETURN;
|
|
635
|
+
c->status = napi_get_value_bool(env, interleavedJS, &c->interleaved);
|
|
636
|
+
REJECT_RETURN;
|
|
637
|
+
|
|
638
|
+
if (argc > 0) { // is it a packet, a frame, an options object
|
|
639
|
+
c->status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
|
640
|
+
REJECT_RETURN;
|
|
641
|
+
c->status = napi_typeof(env, args[0], &type);
|
|
642
|
+
REJECT_RETURN;
|
|
643
|
+
c->status = napi_is_array(env, args[0], &isArray);
|
|
644
|
+
REJECT_RETURN;
|
|
645
|
+
if (isArray || (type != napi_object)) {
|
|
646
|
+
REJECT_ERROR_RETURN("Write frame requires a frame, a packet or an options object.",
|
|
647
|
+
BEAMCODER_INVALID_ARGS);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
c->status = napi_get_named_property(env, args[0], "packet", &options);
|
|
651
|
+
REJECT_RETURN;
|
|
652
|
+
c->status = napi_typeof(env, options, &type);
|
|
653
|
+
REJECT_RETURN;
|
|
654
|
+
hasOptions = (type == napi_object);
|
|
655
|
+
c->status = napi_get_named_property(env,
|
|
656
|
+
hasOptions ? options : args[0], "_packet", &prop);
|
|
657
|
+
REJECT_RETURN;
|
|
658
|
+
c->status = napi_typeof(env, prop, &type);
|
|
659
|
+
REJECT_RETURN;
|
|
660
|
+
if (type == napi_external) {
|
|
661
|
+
c->status = napi_get_value_external(env, prop, (void**) &packetData);
|
|
662
|
+
REJECT_RETURN;
|
|
663
|
+
c->packet = av_packet_alloc();
|
|
664
|
+
if ((ret = av_packet_ref(c->packet, const_cast<AVPacket*>(packetData->packet)))) {
|
|
665
|
+
REJECT_ERROR_RETURN(avErrorMsg("Failed to reference packet: ", ret),
|
|
666
|
+
BEAMCODER_ERROR_ENOMEM);
|
|
667
|
+
};
|
|
668
|
+
goto work;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
c->status = napi_get_named_property(env, args[0], "frame", &options);
|
|
672
|
+
REJECT_RETURN;
|
|
673
|
+
c->status = napi_typeof(env, options, &type);
|
|
674
|
+
REJECT_RETURN;
|
|
675
|
+
hasOptions = (type == napi_object);
|
|
676
|
+
c->status = napi_get_named_property(env,
|
|
677
|
+
hasOptions ? options : args[0], "_frame", &prop);
|
|
678
|
+
REJECT_RETURN;
|
|
679
|
+
c->status = napi_typeof(env, prop, &type);
|
|
680
|
+
REJECT_RETURN;
|
|
681
|
+
if (type == napi_external) {
|
|
682
|
+
c->status = napi_get_value_external(env, prop, (void**) &frameData);
|
|
683
|
+
REJECT_RETURN;
|
|
684
|
+
c->frame = av_frame_alloc();
|
|
685
|
+
if ((ret = av_frame_ref(c->frame, const_cast<AVFrame*>(frameData->frame)))) {
|
|
686
|
+
REJECT_ERROR_RETURN(avErrorMsg("Failed to reference frame: ", ret),
|
|
687
|
+
BEAMCODER_ERROR_ENOMEM);
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
c->status = napi_get_named_property(env,
|
|
692
|
+
hasOptions ? options : args[0], "stream_index", &prop);
|
|
693
|
+
REJECT_RETURN;
|
|
694
|
+
c->status = napi_typeof(env, prop, &type);
|
|
695
|
+
REJECT_RETURN;
|
|
696
|
+
if (type == napi_number) {
|
|
697
|
+
c->status = napi_get_value_int32(env, prop, &c->streamIndex);
|
|
698
|
+
REJECT_RETURN;
|
|
699
|
+
}
|
|
700
|
+
if (c->streamIndex < 0) {
|
|
701
|
+
REJECT_ERROR_RETURN("Frame for writing must be provided with a stream_index, either as an internal property or in an options object.",
|
|
702
|
+
BEAMCODER_INVALID_ARGS);
|
|
703
|
+
}
|
|
704
|
+
} else {
|
|
705
|
+
REJECT_ERROR_RETURN("A packet or frame must be provided.",
|
|
706
|
+
BEAMCODER_INVALID_ARGS);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
work:
|
|
710
|
+
if ((c->packet != nullptr) || (c->frame != nullptr)) {
|
|
711
|
+
c->status = napi_create_reference(env, hasOptions ? options : args[0], 1,
|
|
712
|
+
&c->passthru);
|
|
713
|
+
REJECT_RETURN;
|
|
714
|
+
} // Don't garbage collect during write
|
|
715
|
+
|
|
716
|
+
c->status = napi_create_string_utf8(env, "WriteFrame", NAPI_AUTO_LENGTH, &resourceName);
|
|
717
|
+
REJECT_RETURN;
|
|
718
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, writeFrameExecute,
|
|
719
|
+
writeFrameComplete, c, &c->_request);
|
|
720
|
+
REJECT_RETURN;
|
|
721
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
722
|
+
REJECT_RETURN;
|
|
723
|
+
|
|
724
|
+
return promise;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
void writeTrailerExecute(napi_env env, void* data) {
|
|
728
|
+
writeTrailerCarrier* c = (writeTrailerCarrier*) data;
|
|
729
|
+
int retWrite = 0, retClose = 0;
|
|
730
|
+
|
|
731
|
+
retWrite = av_write_trailer(c->format);
|
|
732
|
+
if (c->format->pb != nullptr) {
|
|
733
|
+
if (c->adaptor) {
|
|
734
|
+
c->adaptor->finish();
|
|
735
|
+
avio_context_free(&c->format->pb);
|
|
736
|
+
}
|
|
737
|
+
else
|
|
738
|
+
retClose = avio_closep(&c->format->pb);
|
|
739
|
+
}
|
|
740
|
+
if ((retWrite < 0) && (retClose < 0)) {
|
|
741
|
+
c->status = BEAMCODER_ERROR_WRITE_TRAILER;
|
|
742
|
+
c->errorMsg = "Errors writing trailer and closing IO.";
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
if (retWrite < 0) {
|
|
746
|
+
c->status = BEAMCODER_ERROR_WRITE_TRAILER;
|
|
747
|
+
c->errorMsg = avErrorMsg("Error writing trailer: ", retWrite);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
if (retClose < 0) {
|
|
751
|
+
c->status = BEAMCODER_ERROR_WRITE_TRAILER;
|
|
752
|
+
c->errorMsg = avErrorMsg("Error closing IO: ", retClose);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
void writeTrailerComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
758
|
+
napi_value result;
|
|
759
|
+
writeTrailerCarrier* c = (writeTrailerCarrier*) data;
|
|
760
|
+
|
|
761
|
+
if (asyncStatus != napi_ok) {
|
|
762
|
+
c->status = asyncStatus;
|
|
763
|
+
c->errorMsg = "Write trailer failed to complete.";
|
|
764
|
+
}
|
|
765
|
+
REJECT_STATUS;
|
|
766
|
+
|
|
767
|
+
c->status = napi_get_undefined(env, &result);
|
|
768
|
+
REJECT_STATUS;
|
|
769
|
+
|
|
770
|
+
napi_status status;
|
|
771
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
772
|
+
FLOATING_STATUS;
|
|
773
|
+
|
|
774
|
+
tidyCarrier(env, c);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
napi_value writeTrailer(napi_env env, napi_callback_info info) {
|
|
778
|
+
napi_value promise, formatJS, formatExt, adaptorExt, resourceName;
|
|
779
|
+
writeTrailerCarrier* c = new writeTrailerCarrier;
|
|
780
|
+
|
|
781
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
782
|
+
REJECT_RETURN;
|
|
783
|
+
|
|
784
|
+
size_t argc = 0;
|
|
785
|
+
c->status = napi_get_cb_info(env, info, &argc, nullptr, &formatJS, nullptr);
|
|
786
|
+
REJECT_RETURN;
|
|
787
|
+
c->status = napi_get_named_property(env, formatJS, "_formatContext", &formatExt);
|
|
788
|
+
REJECT_RETURN;
|
|
789
|
+
c->status = napi_get_value_external(env, formatExt, (void**) &c->format);
|
|
790
|
+
REJECT_RETURN;
|
|
791
|
+
c->status = napi_get_named_property(env, formatJS, "_adaptor", &adaptorExt);
|
|
792
|
+
REJECT_RETURN;
|
|
793
|
+
c->status = napi_get_value_external(env, adaptorExt, (void**) &c->adaptor);
|
|
794
|
+
REJECT_RETURN;
|
|
795
|
+
|
|
796
|
+
c->status = napi_create_string_utf8(env, "WriteTrailer", NAPI_AUTO_LENGTH, &resourceName);
|
|
797
|
+
REJECT_RETURN;
|
|
798
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, writeTrailerExecute,
|
|
799
|
+
writeTrailerComplete, c, &c->_request);
|
|
800
|
+
REJECT_RETURN;
|
|
801
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
802
|
+
REJECT_RETURN;
|
|
803
|
+
|
|
804
|
+
return promise;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
napi_value forceClose(napi_env env, napi_callback_info info) {
|
|
808
|
+
napi_status status;
|
|
809
|
+
napi_value result, formatJS, formatExt;
|
|
810
|
+
AVFormatContext* format;
|
|
811
|
+
int ret;
|
|
812
|
+
|
|
813
|
+
size_t argc = 0;
|
|
814
|
+
status = napi_get_cb_info(env, info, &argc, nullptr, &formatJS, nullptr);
|
|
815
|
+
CHECK_STATUS;
|
|
816
|
+
status = napi_get_named_property(env, formatJS, "_formatContext", &formatExt);
|
|
817
|
+
CHECK_STATUS;
|
|
818
|
+
status = napi_get_value_external(env, formatExt, (void**) &format);
|
|
819
|
+
CHECK_STATUS;
|
|
820
|
+
|
|
821
|
+
if (format->pb != nullptr) {
|
|
822
|
+
ret = avio_closep(&format->pb);
|
|
823
|
+
if (ret < 0) {
|
|
824
|
+
NAPI_THROW_ERROR(avErrorMsg("Failed to force close muxer resource: ", ret));
|
|
825
|
+
}
|
|
826
|
+
} else {
|
|
827
|
+
printf("DEBUG: Muxer IO resource '%s' already closed or not managed by AVIO.\n",
|
|
828
|
+
(format->url != nullptr) ? format->url : "unknown");
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
status = napi_get_undefined(env, &result);
|
|
832
|
+
CHECK_STATUS;
|
|
833
|
+
return result;
|
|
834
|
+
}
|