@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/demux.cc
ADDED
|
@@ -0,0 +1,584 @@
|
|
|
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 "demux.h"
|
|
23
|
+
|
|
24
|
+
int read_packet(void *opaque, uint8_t *buf, int buf_size)
|
|
25
|
+
{
|
|
26
|
+
Adaptor *adaptor = (Adaptor *)opaque;
|
|
27
|
+
int numBytes = adaptor->read(buf, buf_size);
|
|
28
|
+
if (0 == numBytes)
|
|
29
|
+
return AVERROR_EOF;
|
|
30
|
+
|
|
31
|
+
return numBytes;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void demuxerExecute(napi_env env, void* data) {
|
|
35
|
+
demuxerCarrier* c = (demuxerCarrier*) data;
|
|
36
|
+
|
|
37
|
+
int ret;
|
|
38
|
+
|
|
39
|
+
if (!(c->format = avformat_alloc_context())) {
|
|
40
|
+
c->status = BEAMCODER_ERROR_START;
|
|
41
|
+
c->errorMsg = avErrorMsg("Problem allocating demuxer: ", AVERROR(ENOMEM));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (c->adaptor) {
|
|
46
|
+
AVIOContext* avio_ctx = avio_alloc_context(nullptr, 0, 0, c->adaptor, &read_packet, nullptr, nullptr);
|
|
47
|
+
if (!avio_ctx) {
|
|
48
|
+
c->status = BEAMCODER_ERROR_START;
|
|
49
|
+
c->errorMsg = avErrorMsg("Problem allocating demuxer context: ", AVERROR(ENOMEM));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
c->format->pb = avio_ctx;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if ((ret = avformat_open_input(&c->format, c->filename, c->iformat, &c->options))) {
|
|
56
|
+
c->status = BEAMCODER_ERROR_START;
|
|
57
|
+
c->errorMsg = avErrorMsg("Problem opening input format: ", ret);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if ((ret = avformat_find_stream_info(c->format, nullptr))) {
|
|
62
|
+
printf("DEBUG: Could not find stream info for file %s, return value %i.",
|
|
63
|
+
c->filename, ret);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void demuxerComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
68
|
+
demuxerCarrier* c = (demuxerCarrier*) data;
|
|
69
|
+
napi_value result, prop;
|
|
70
|
+
AVDictionaryEntry* tag = nullptr;
|
|
71
|
+
|
|
72
|
+
if (asyncStatus != napi_ok) {
|
|
73
|
+
c->status = asyncStatus;
|
|
74
|
+
c->errorMsg = "Demuxer creator failed to complete.";
|
|
75
|
+
}
|
|
76
|
+
REJECT_STATUS;
|
|
77
|
+
|
|
78
|
+
// tidy up adaptor chunks if required
|
|
79
|
+
if (c->adaptor) {
|
|
80
|
+
c->status = c->adaptor->finaliseBufs(env);
|
|
81
|
+
REJECT_STATUS;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
while ((tag = av_dict_get(c->options, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
|
85
|
+
printf("DEBUG: On creating demuxer '%s', failed to set option %s.\n",
|
|
86
|
+
c->format->iformat->name, tag->key);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
c->status = fromAVFormatContext(env, c->format, c->adaptor, &result);
|
|
90
|
+
c->format = nullptr;
|
|
91
|
+
REJECT_STATUS;
|
|
92
|
+
|
|
93
|
+
c->status = napi_create_function(env, "readFrame", NAPI_AUTO_LENGTH, readFrame,
|
|
94
|
+
nullptr, &prop);
|
|
95
|
+
REJECT_STATUS;
|
|
96
|
+
c->status = napi_set_named_property(env, result, "read", prop);
|
|
97
|
+
REJECT_STATUS;
|
|
98
|
+
|
|
99
|
+
c->status = napi_create_function(env, "seekFrame", NAPI_AUTO_LENGTH, seekFrame,
|
|
100
|
+
nullptr, &prop);
|
|
101
|
+
REJECT_STATUS;
|
|
102
|
+
c->status = napi_set_named_property(env, result, "seek", prop);
|
|
103
|
+
REJECT_STATUS;
|
|
104
|
+
|
|
105
|
+
c->status = napi_create_function(env, "forceClose", NAPI_AUTO_LENGTH, forceCloseInput,
|
|
106
|
+
nullptr, &prop);
|
|
107
|
+
REJECT_STATUS;
|
|
108
|
+
c->status = napi_set_named_property(env, result, "forceClose", prop);
|
|
109
|
+
REJECT_STATUS;
|
|
110
|
+
|
|
111
|
+
napi_status status;
|
|
112
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
113
|
+
FLOATING_STATUS;
|
|
114
|
+
|
|
115
|
+
tidyCarrier(env, c);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
napi_value demuxer(napi_env env, napi_callback_info info) {
|
|
119
|
+
napi_value resourceName, promise, value, subValue;
|
|
120
|
+
napi_valuetype type;
|
|
121
|
+
size_t strLen;
|
|
122
|
+
bool isArray;
|
|
123
|
+
demuxerCarrier* c = new demuxerCarrier;
|
|
124
|
+
|
|
125
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
126
|
+
REJECT_RETURN;
|
|
127
|
+
|
|
128
|
+
size_t argc = 1;
|
|
129
|
+
napi_value args[1];
|
|
130
|
+
|
|
131
|
+
c->status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
|
132
|
+
REJECT_RETURN;
|
|
133
|
+
|
|
134
|
+
if (argc < 1) {
|
|
135
|
+
REJECT_ERROR_RETURN("Format requires a filename, URL or buffer.",
|
|
136
|
+
BEAMCODER_INVALID_ARGS);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
c->status = napi_typeof(env, args[0], &type);
|
|
140
|
+
REJECT_RETURN;
|
|
141
|
+
c->status = napi_is_array(env, args[0], &isArray);
|
|
142
|
+
REJECT_RETURN;
|
|
143
|
+
|
|
144
|
+
if (type == napi_string) {
|
|
145
|
+
c->status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &strLen);
|
|
146
|
+
REJECT_RETURN;
|
|
147
|
+
c->filename = (const char *) malloc((strLen + 1) * sizeof(char));
|
|
148
|
+
c->status = napi_get_value_string_utf8(env, args[0], (char *) c->filename, strLen + 1, &strLen);
|
|
149
|
+
REJECT_RETURN;
|
|
150
|
+
} else if ((isArray == false) && (type == napi_object)) {
|
|
151
|
+
napi_value governorValue;
|
|
152
|
+
c->status = napi_get_named_property(env, args[0], "governor", &governorValue);
|
|
153
|
+
REJECT_RETURN;
|
|
154
|
+
c->status = napi_typeof(env, governorValue, &type);
|
|
155
|
+
REJECT_RETURN;
|
|
156
|
+
if (type == napi_object) {
|
|
157
|
+
napi_value adaptorValue;
|
|
158
|
+
c->status = napi_get_named_property(env, governorValue, "_adaptor", &adaptorValue);
|
|
159
|
+
REJECT_RETURN;
|
|
160
|
+
c->status = napi_typeof(env, adaptorValue, &type);
|
|
161
|
+
REJECT_RETURN;
|
|
162
|
+
if (type == napi_external) {
|
|
163
|
+
c->status = napi_get_value_external(env, adaptorValue, (void**)&c->adaptor);
|
|
164
|
+
REJECT_RETURN;
|
|
165
|
+
} else if (type != napi_undefined) {
|
|
166
|
+
REJECT_ERROR_RETURN("Adaptor must be of external type when specified.",
|
|
167
|
+
BEAMCODER_INVALID_ARGS);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
c->status = napi_get_named_property(env, args[0], "url", &value);
|
|
172
|
+
REJECT_RETURN;
|
|
173
|
+
c->status = napi_typeof(env, value, &type);
|
|
174
|
+
REJECT_RETURN;
|
|
175
|
+
if (type == napi_string) {
|
|
176
|
+
c->status = napi_get_value_string_utf8(env, value, nullptr, 0, &strLen);
|
|
177
|
+
REJECT_RETURN;
|
|
178
|
+
c->filename = (const char *) malloc((strLen + 1) * sizeof(char));
|
|
179
|
+
c->status = napi_get_value_string_utf8(env, value, (char *) c->filename, strLen + 1, &strLen);
|
|
180
|
+
REJECT_RETURN;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
c->status = napi_get_named_property(env, args[0], "iformat", &value);
|
|
184
|
+
REJECT_RETURN;
|
|
185
|
+
c->status = napi_typeof(env, value, &type);
|
|
186
|
+
REJECT_RETURN;
|
|
187
|
+
c->status = napi_is_array(env, value, &isArray);
|
|
188
|
+
REJECT_RETURN;
|
|
189
|
+
if (!isArray && (type == napi_object)) {
|
|
190
|
+
c->status = napi_get_named_property(env, value, "_iformat", &subValue);
|
|
191
|
+
REJECT_RETURN;
|
|
192
|
+
c->status = napi_typeof(env, subValue, &type);
|
|
193
|
+
REJECT_RETURN;
|
|
194
|
+
if (type == napi_external) {
|
|
195
|
+
c->status = napi_get_value_external(env, subValue, (void**) &c->iformat);
|
|
196
|
+
REJECT_RETURN;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
c->status = napi_get_named_property(env, args[0], "options", &value);
|
|
201
|
+
REJECT_RETURN;
|
|
202
|
+
c->status = napi_typeof(env, value, &type);
|
|
203
|
+
REJECT_RETURN;
|
|
204
|
+
c->status = napi_is_array(env, value, &isArray);
|
|
205
|
+
REJECT_RETURN;
|
|
206
|
+
if ((isArray == false) && (type == napi_object)) {
|
|
207
|
+
c->status = makeAVDictionary(env, value, &c->options);
|
|
208
|
+
REJECT_RETURN;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if ((c->filename == nullptr) && (c->adaptor == nullptr)) {
|
|
213
|
+
REJECT_ERROR_RETURN("Neither a filename nor an adaptor have been provided.",
|
|
214
|
+
BEAMCODER_INVALID_ARGS);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
c->status = napi_create_string_utf8(env, "Format", NAPI_AUTO_LENGTH, &resourceName);
|
|
218
|
+
REJECT_RETURN;
|
|
219
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, demuxerExecute,
|
|
220
|
+
demuxerComplete, c, &c->_request);
|
|
221
|
+
REJECT_RETURN;
|
|
222
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
223
|
+
REJECT_RETURN;
|
|
224
|
+
|
|
225
|
+
return promise;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
void demuxerFinalizer(napi_env env, void* data, void* hint) {
|
|
229
|
+
AVFormatContext *fmtCtx = (AVFormatContext*) data;
|
|
230
|
+
avformat_close_input(&fmtCtx);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
void readFrameExecute(napi_env env, void* data) {
|
|
234
|
+
readFrameCarrier* c = (readFrameCarrier*) data;
|
|
235
|
+
int ret;
|
|
236
|
+
|
|
237
|
+
AVFormatContext* fmtCtx = c->formatRef->fmtCtx;
|
|
238
|
+
if (fmtCtx == nullptr) {
|
|
239
|
+
c->status = BEAMCODER_ERROR_READ_FRAME;
|
|
240
|
+
c->errorMsg = "Format context has been deleted.";
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
ret = av_read_frame(fmtCtx, c->packet);
|
|
245
|
+
if (ret == AVERROR_EOF) {
|
|
246
|
+
av_packet_free(&c->packet);
|
|
247
|
+
} else if (ret < 0) {
|
|
248
|
+
c->status = BEAMCODER_ERROR_READ_FRAME;
|
|
249
|
+
c->errorMsg = avErrorMsg("Problem reading frame: ", ret);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
void readFrameComplete(napi_env env, napi_status asyncStatus, void* data) {
|
|
255
|
+
readFrameCarrier* c = (readFrameCarrier*) data;
|
|
256
|
+
napi_value result;
|
|
257
|
+
packetData* p;
|
|
258
|
+
|
|
259
|
+
if (asyncStatus != napi_ok) {
|
|
260
|
+
c->status = asyncStatus;
|
|
261
|
+
c->errorMsg = "Read frame failed to complete.";
|
|
262
|
+
}
|
|
263
|
+
REJECT_STATUS;
|
|
264
|
+
|
|
265
|
+
// tidy up adaptor chunks if required
|
|
266
|
+
if (c->adaptor) {
|
|
267
|
+
c->status = c->adaptor->finaliseBufs(env);
|
|
268
|
+
REJECT_STATUS;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (c->packet != nullptr) {
|
|
272
|
+
p = new packetData;
|
|
273
|
+
p->packet = c->packet;
|
|
274
|
+
c->status = fromAVPacket(env, p, &result);
|
|
275
|
+
c->packet = nullptr;
|
|
276
|
+
REJECT_STATUS;
|
|
277
|
+
} else {
|
|
278
|
+
c->status = napi_get_null(env, &result);
|
|
279
|
+
REJECT_STATUS;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
napi_status status;
|
|
283
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
284
|
+
FLOATING_STATUS;
|
|
285
|
+
|
|
286
|
+
tidyCarrier(env, c);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
napi_value readFrame(napi_env env, napi_callback_info info) {
|
|
290
|
+
napi_value resourceName, promise, formatJS, formatRefExt, adaptorExt;
|
|
291
|
+
readFrameCarrier* c = new readFrameCarrier;
|
|
292
|
+
|
|
293
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
294
|
+
REJECT_RETURN;
|
|
295
|
+
|
|
296
|
+
size_t argc = 0;
|
|
297
|
+
c->status = napi_get_cb_info(env, info, &argc, nullptr, &formatJS, nullptr);
|
|
298
|
+
REJECT_RETURN;
|
|
299
|
+
c->status = napi_get_named_property(env, formatJS, "_formatContextRef", &formatRefExt);
|
|
300
|
+
REJECT_RETURN;
|
|
301
|
+
c->status = napi_get_value_external(env, formatRefExt, (void**) &c->formatRef);
|
|
302
|
+
REJECT_RETURN;
|
|
303
|
+
|
|
304
|
+
c->status = napi_get_named_property(env, formatJS, "_adaptor", &adaptorExt);
|
|
305
|
+
REJECT_RETURN;
|
|
306
|
+
c->status = napi_get_value_external(env, adaptorExt, (void**)&c->adaptor);
|
|
307
|
+
REJECT_RETURN;
|
|
308
|
+
|
|
309
|
+
c->status = napi_create_reference(env, formatJS, 1, &c->passthru);
|
|
310
|
+
REJECT_RETURN;
|
|
311
|
+
|
|
312
|
+
c->status = napi_create_string_utf8(env, "ReadFrame", NAPI_AUTO_LENGTH, &resourceName);
|
|
313
|
+
REJECT_RETURN;
|
|
314
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, readFrameExecute,
|
|
315
|
+
readFrameComplete, c, &c->_request);
|
|
316
|
+
REJECT_RETURN;
|
|
317
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
318
|
+
REJECT_RETURN;
|
|
319
|
+
|
|
320
|
+
return promise;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
void readBufferFinalizer(napi_env env, void* data, void* hint) {
|
|
324
|
+
AVBufferRef* hintRef = (AVBufferRef*) hint;
|
|
325
|
+
napi_status status;
|
|
326
|
+
int64_t externalMemory;
|
|
327
|
+
status = napi_adjust_external_memory(env, -(int64_t)hintRef->size, &externalMemory);
|
|
328
|
+
if (status != napi_ok) {
|
|
329
|
+
printf("DEBUG: Napi failure to adjust external memory. In beamcoder format.cc readBufferFinalizer.");
|
|
330
|
+
}
|
|
331
|
+
av_buffer_unref(&hintRef);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
void seekFrameExecute(napi_env env, void *data) {
|
|
335
|
+
seekFrameCarrier* c = (seekFrameCarrier*) data;
|
|
336
|
+
int ret;
|
|
337
|
+
|
|
338
|
+
AVFormatContext* fmtCtx = c->formatRef->fmtCtx;
|
|
339
|
+
if (fmtCtx == nullptr) {
|
|
340
|
+
c->status = BEAMCODER_ERROR_READ_FRAME;
|
|
341
|
+
c->errorMsg = "Format context has been deleted.";
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
ret = av_seek_frame(fmtCtx, c->streamIndex, c->timestamp, c->flags);
|
|
346
|
+
// printf("Seek and ye shall %i, streamIndex = %i, timestamp = %i, flags = %i\n",
|
|
347
|
+
// ret, c->streamIndex, c->timestamp, c->flags );
|
|
348
|
+
if (ret < 0) {
|
|
349
|
+
c->status = BEAMCODER_ERROR_SEEK_FRAME;
|
|
350
|
+
c->errorMsg = avErrorMsg("Problem seeking frame: ", ret);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
void seekFrameComplete(napi_env env, napi_status asyncStatus, void *data) {
|
|
356
|
+
seekFrameCarrier* c = (seekFrameCarrier*) data;
|
|
357
|
+
napi_value result;
|
|
358
|
+
if (asyncStatus != napi_ok) {
|
|
359
|
+
c->status = asyncStatus;
|
|
360
|
+
c->errorMsg = "Seek frame failed to complete.";
|
|
361
|
+
}
|
|
362
|
+
REJECT_STATUS;
|
|
363
|
+
|
|
364
|
+
c->status = napi_get_null(env, &result);
|
|
365
|
+
REJECT_STATUS;
|
|
366
|
+
|
|
367
|
+
napi_status status;
|
|
368
|
+
status = napi_resolve_deferred(env, c->_deferred, result);
|
|
369
|
+
FLOATING_STATUS;
|
|
370
|
+
|
|
371
|
+
tidyCarrier(env, c);
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
/*
|
|
375
|
+
let frame = await format.seek({
|
|
376
|
+
streamIndex: 0, // Default is -1 - use primary stream, seek in seconds
|
|
377
|
+
timestamp: 12345, // Timestamp - default units are stream timeBase
|
|
378
|
+
backward: false, // Seek backwards
|
|
379
|
+
byte: false, // Timestamp is a byte position
|
|
380
|
+
any: false, // Select any frame, not just key frames
|
|
381
|
+
frame: false // Timestamp is frame number
|
|
382
|
+
});
|
|
383
|
+
*/
|
|
384
|
+
|
|
385
|
+
napi_value seekFrame(napi_env env, napi_callback_info info) {
|
|
386
|
+
napi_value resourceName, promise, formatJS, formatRefExt, value;
|
|
387
|
+
napi_valuetype type;
|
|
388
|
+
seekFrameCarrier* c = new seekFrameCarrier;
|
|
389
|
+
bool isArray, bValue;
|
|
390
|
+
double fracTime = 0.0;
|
|
391
|
+
|
|
392
|
+
c->status = napi_create_promise(env, &c->_deferred, &promise);
|
|
393
|
+
REJECT_RETURN;
|
|
394
|
+
|
|
395
|
+
size_t argc = 1;
|
|
396
|
+
napi_value argv[1];
|
|
397
|
+
|
|
398
|
+
c->status = napi_get_cb_info(env, info, &argc, argv, &formatJS, nullptr);
|
|
399
|
+
REJECT_RETURN;
|
|
400
|
+
c->status = napi_get_named_property(env, formatJS, "_formatContextRef", &formatRefExt);
|
|
401
|
+
REJECT_RETURN;
|
|
402
|
+
c->status = napi_get_value_external(env, formatRefExt, (void**) &c->formatRef);
|
|
403
|
+
REJECT_RETURN;
|
|
404
|
+
|
|
405
|
+
c->status = napi_create_reference(env, formatJS, 1, &c->passthru);
|
|
406
|
+
REJECT_RETURN;
|
|
407
|
+
|
|
408
|
+
if ((argc < 1) || (argc > 1)) {
|
|
409
|
+
REJECT_ERROR_RETURN("Seek must have exactly one options object argument.",
|
|
410
|
+
BEAMCODER_INVALID_ARGS);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
c->status = napi_typeof(env, argv[0], &type);
|
|
414
|
+
REJECT_RETURN;
|
|
415
|
+
c->status = napi_is_array(env, argv[0], &isArray);
|
|
416
|
+
REJECT_RETURN;
|
|
417
|
+
if ((type != napi_object) || (isArray == true)) {
|
|
418
|
+
REJECT_ERROR_RETURN("Single argument options object must be an object and not an array.",
|
|
419
|
+
BEAMCODER_INVALID_ARGS);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
c->status = napi_get_named_property(env, argv[0], "stream_index", &value);
|
|
423
|
+
REJECT_RETURN;
|
|
424
|
+
c->status = napi_typeof(env, value, &type);
|
|
425
|
+
REJECT_RETURN;
|
|
426
|
+
if (type == napi_number) {
|
|
427
|
+
c->status = napi_get_value_int32(env, value, &c->streamIndex);
|
|
428
|
+
REJECT_RETURN;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
c->status = napi_get_named_property(env, argv[0], "timestamp", &value);
|
|
432
|
+
REJECT_RETURN;
|
|
433
|
+
c->status = napi_typeof(env, value, &type);
|
|
434
|
+
REJECT_RETURN;
|
|
435
|
+
if (type == napi_number) {
|
|
436
|
+
if (c->streamIndex < 0) {
|
|
437
|
+
REJECT_ERROR_RETURN("Stream index must be provided when seeking by timestamp.",
|
|
438
|
+
BEAMCODER_INVALID_ARGS);
|
|
439
|
+
}
|
|
440
|
+
c->status = napi_get_value_int64(env, value, &c->timestamp);
|
|
441
|
+
REJECT_RETURN;
|
|
442
|
+
goto flags;
|
|
443
|
+
} else {
|
|
444
|
+
if (type != napi_undefined) {
|
|
445
|
+
REJECT_ERROR_RETURN("Timestamp must by specified with a number.",
|
|
446
|
+
BEAMCODER_INVALID_ARGS);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
c->status = napi_get_named_property(env, argv[0], "time", &value);
|
|
451
|
+
REJECT_RETURN;
|
|
452
|
+
c->status = napi_typeof(env, value, &type);
|
|
453
|
+
REJECT_RETURN;
|
|
454
|
+
if (type == napi_number) {
|
|
455
|
+
if (c->streamIndex >= 0) {
|
|
456
|
+
REJECT_ERROR_RETURN("Cannot seek by time value on a specific stream. Try to unset 'stream_index'?",
|
|
457
|
+
BEAMCODER_INVALID_ARGS);
|
|
458
|
+
}
|
|
459
|
+
c->status = napi_get_value_double(env, value, &fracTime);
|
|
460
|
+
REJECT_RETURN;
|
|
461
|
+
c->timestamp = (int64_t) (fracTime * AV_TIME_BASE);
|
|
462
|
+
c->streamIndex = -1;
|
|
463
|
+
goto flags;
|
|
464
|
+
} else {
|
|
465
|
+
if (type != napi_undefined) {
|
|
466
|
+
REJECT_ERROR_RETURN("Time value must by specified with a number.",
|
|
467
|
+
BEAMCODER_INVALID_ARGS);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
c->status = napi_get_named_property(env, argv[0], "pos", &value);
|
|
472
|
+
REJECT_RETURN;
|
|
473
|
+
c->status = napi_typeof(env, value, &type);
|
|
474
|
+
REJECT_RETURN;
|
|
475
|
+
if (type == napi_number) {
|
|
476
|
+
if (c->streamIndex >= 0) {
|
|
477
|
+
REJECT_ERROR_RETURN("Cannot seek by byte position on a specific stream. Try to unset 'stream_index'?",
|
|
478
|
+
BEAMCODER_INVALID_ARGS);
|
|
479
|
+
}
|
|
480
|
+
c->status = napi_get_value_int64(env, value, &c->timestamp);
|
|
481
|
+
REJECT_RETURN;
|
|
482
|
+
c->flags = c->flags | AVSEEK_FLAG_BYTE;
|
|
483
|
+
goto flags;
|
|
484
|
+
} else {
|
|
485
|
+
if (type != napi_undefined) {
|
|
486
|
+
REJECT_ERROR_RETURN("Position value must by specified with a number.",
|
|
487
|
+
BEAMCODER_INVALID_ARGS);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
c->status = napi_get_named_property(env, argv[0], "frame", &value);
|
|
492
|
+
REJECT_RETURN;
|
|
493
|
+
c->status = napi_typeof(env, value, &type);
|
|
494
|
+
REJECT_RETURN;
|
|
495
|
+
if (type == napi_number) {
|
|
496
|
+
if (c->streamIndex < 0) {
|
|
497
|
+
REJECT_ERROR_RETURN("Cannot seek by frame number unless a 'stream_index' is provided.",
|
|
498
|
+
BEAMCODER_INVALID_ARGS);
|
|
499
|
+
}
|
|
500
|
+
c->status = napi_get_value_int64(env, value, &c->timestamp);
|
|
501
|
+
REJECT_RETURN;
|
|
502
|
+
c->flags = c->flags | AVSEEK_FLAG_FRAME;
|
|
503
|
+
goto flags;
|
|
504
|
+
} else {
|
|
505
|
+
if (type != napi_undefined) {
|
|
506
|
+
REJECT_ERROR_RETURN("Frame number must by specified with a number.",
|
|
507
|
+
BEAMCODER_INVALID_ARGS);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
flags:
|
|
512
|
+
c->status = napi_get_named_property(env, argv[0], "any", &value);
|
|
513
|
+
REJECT_RETURN;
|
|
514
|
+
c->status = napi_typeof(env, value, &type);
|
|
515
|
+
REJECT_RETURN;
|
|
516
|
+
if (type == napi_boolean) {
|
|
517
|
+
c->status = napi_get_value_bool(env, value, &bValue);
|
|
518
|
+
REJECT_RETURN;
|
|
519
|
+
c->flags = (bValue) ? c->flags | AVSEEK_FLAG_ANY : c->flags;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
c->status = napi_get_named_property(env, argv[0], "backward", &value);
|
|
523
|
+
REJECT_RETURN;
|
|
524
|
+
c->status = napi_typeof(env, value, &type);
|
|
525
|
+
REJECT_RETURN;
|
|
526
|
+
if (type == napi_boolean) {
|
|
527
|
+
c->status = napi_get_value_bool(env, value, &bValue);
|
|
528
|
+
REJECT_RETURN;
|
|
529
|
+
c->flags = (bValue) ?
|
|
530
|
+
c->flags | AVSEEK_FLAG_BACKWARD : c->flags & ~AVSEEK_FLAG_BACKWARD;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
c->status = napi_create_string_utf8(env, "SeekFrame", NAPI_AUTO_LENGTH, &resourceName);
|
|
534
|
+
REJECT_RETURN;
|
|
535
|
+
c->status = napi_create_async_work(env, nullptr, resourceName, seekFrameExecute,
|
|
536
|
+
seekFrameComplete, c, &c->_request);
|
|
537
|
+
REJECT_RETURN;
|
|
538
|
+
c->status = napi_queue_async_work(env, c->_request);
|
|
539
|
+
REJECT_RETURN;
|
|
540
|
+
|
|
541
|
+
return promise;
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
napi_value forceCloseInput(napi_env env, napi_callback_info info) {
|
|
545
|
+
napi_status status;
|
|
546
|
+
napi_value result, formatJS, formatRefExt, adaptorExt;
|
|
547
|
+
fmtCtxRef* fmtRef;
|
|
548
|
+
AVFormatContext* fc;
|
|
549
|
+
Adaptor *adaptor;
|
|
550
|
+
int ret;
|
|
551
|
+
|
|
552
|
+
size_t argc = 0;
|
|
553
|
+
status = napi_get_cb_info(env, info, &argc, nullptr, &formatJS, nullptr);
|
|
554
|
+
CHECK_STATUS;
|
|
555
|
+
status = napi_get_named_property(env, formatJS, "_formatContextRef", &formatRefExt);
|
|
556
|
+
CHECK_STATUS;
|
|
557
|
+
status = napi_get_value_external(env, formatRefExt, (void**) &fmtRef);
|
|
558
|
+
CHECK_STATUS;
|
|
559
|
+
status = napi_get_named_property(env, formatJS, "_adaptor", &adaptorExt);
|
|
560
|
+
CHECK_STATUS;
|
|
561
|
+
status = napi_get_value_external(env, adaptorExt, (void**) &adaptor);
|
|
562
|
+
CHECK_STATUS;
|
|
563
|
+
|
|
564
|
+
if (fmtRef->fmtCtx != nullptr) {
|
|
565
|
+
fc = fmtRef->fmtCtx;
|
|
566
|
+
if (fc->pb != nullptr) {
|
|
567
|
+
if (adaptor)
|
|
568
|
+
avio_context_free(&fc->pb);
|
|
569
|
+
else {
|
|
570
|
+
ret = avio_closep(&fc->pb);
|
|
571
|
+
if (ret < 0) {
|
|
572
|
+
printf("DEBUG: For url '%s', %s", (fc->url != nullptr) ? fc->url : "unknown",
|
|
573
|
+
avErrorMsg("error closing IO: ", ret));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
avformat_close_input(&fmtRef->fmtCtx);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
status = napi_get_undefined(env, &result);
|
|
582
|
+
CHECK_STATUS;
|
|
583
|
+
return result;
|
|
584
|
+
}
|
package/src/demux.h
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
#ifndef DEMUX_H
|
|
23
|
+
#define DEMUX_H
|
|
24
|
+
|
|
25
|
+
extern "C" {
|
|
26
|
+
#include <libavformat/avformat.h>
|
|
27
|
+
#include <libavutil/opt.h>
|
|
28
|
+
#include <libavutil/dict.h>
|
|
29
|
+
#include <libavutil/pixfmt.h>
|
|
30
|
+
#include <libavutil/attributes.h>
|
|
31
|
+
#include <libavutil/pixdesc.h>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#include "beamcoder_util.h"
|
|
35
|
+
#include "packet.h"
|
|
36
|
+
#include "format.h"
|
|
37
|
+
#include "node_api.h"
|
|
38
|
+
#include "adaptor.h"
|
|
39
|
+
|
|
40
|
+
void demuxerExecute(napi_env env, void* data);
|
|
41
|
+
void demuxerComplete(napi_env env, napi_status asyncStatus, void* data);
|
|
42
|
+
napi_value demuxer(napi_env env, napi_callback_info info);
|
|
43
|
+
|
|
44
|
+
void readFrameExecute(napi_env env, void* data);
|
|
45
|
+
void readFrameComplete(napi_env env, napi_status asyncStatus, void* data);
|
|
46
|
+
napi_value readFrame(napi_env env, napi_callback_info info);
|
|
47
|
+
|
|
48
|
+
void seekFrameExecute(napi_env env, void *data);
|
|
49
|
+
void seekFrameComplete(napi_env env, napi_status asyncStatus, void *data);
|
|
50
|
+
napi_value seekFrame(napi_env env, napi_callback_info info);
|
|
51
|
+
|
|
52
|
+
void demuxerFinalizer(napi_env env, void* data, void* hint);
|
|
53
|
+
void readBufferFinalizer(napi_env env, void* data, void* hint);
|
|
54
|
+
|
|
55
|
+
napi_value forceCloseInput(napi_env env, napi_callback_info info);
|
|
56
|
+
|
|
57
|
+
struct demuxerCarrier : carrier {
|
|
58
|
+
const char* filename = nullptr;
|
|
59
|
+
Adaptor *adaptor = nullptr;
|
|
60
|
+
AVFormatContext* format = nullptr;
|
|
61
|
+
AVInputFormat* iformat = nullptr;
|
|
62
|
+
AVDictionary* options = nullptr;
|
|
63
|
+
~demuxerCarrier() {
|
|
64
|
+
if (format != nullptr) { avformat_close_input(&format); }
|
|
65
|
+
if (options != nullptr) { av_dict_free(&options); }
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
struct readFrameCarrier : carrier {
|
|
70
|
+
fmtCtxRef* formatRef = nullptr;
|
|
71
|
+
Adaptor *adaptor = nullptr;
|
|
72
|
+
AVPacket* packet = av_packet_alloc();
|
|
73
|
+
~readFrameCarrier() {
|
|
74
|
+
if (packet != nullptr) av_packet_free(&packet);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
struct seekFrameCarrier : carrier {
|
|
79
|
+
fmtCtxRef* formatRef = nullptr;
|
|
80
|
+
int streamIndex = -1;
|
|
81
|
+
int64_t timestamp = 0;
|
|
82
|
+
// weird semantic - backward actually means 'find nearest key frame before timestamp'
|
|
83
|
+
// setting as default as more natural
|
|
84
|
+
int flags = AVSEEK_FLAG_BACKWARD;
|
|
85
|
+
~seekFrameCarrier() { }
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
#endif // DEMUX_H
|