@scrypted/libav 1.0.172 → 1.0.173
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/package.json +1 -1
- package/.vscode/launch.json +0 -23
- package/.vscode/settings.json +0 -32
- package/darwin.md +0 -24
- package/src/bsf.cpp +0 -213
- package/src/bsf.h +0 -35
- package/src/build-ffmpeg-darwin.sh +0 -13
- package/src/build-ffmpeg-linux.sh +0 -52
- package/src/build-ffmpeg-msys.sh +0 -41
- package/src/build-ffmpeg-windows.bat +0 -8
- package/src/codeccontext.cpp +0 -602
- package/src/codeccontext.h +0 -50
- package/src/error.cpp +0 -33
- package/src/error.h +0 -19
- package/src/ffmpeg-graphparser.cpp +0 -302
- package/src/filter.cpp +0 -487
- package/src/filter.h +0 -37
- package/src/flags.ts +0 -63
- package/src/formatcontext.cpp +0 -899
- package/src/formatcontext.h +0 -47
- package/src/frame.cpp +0 -667
- package/src/frame.h +0 -55
- package/src/index.ts +0 -356
- package/src/packet.cpp +0 -182
- package/src/packet.h +0 -42
- package/src/pkgconfig/glslang.pc +0 -10
- package/src/profiles.ts +0 -108
- package/src/tsconfig.json +0 -26
- package/tsconfig.json +0 -26
package/src/formatcontext.cpp
DELETED
|
@@ -1,899 +0,0 @@
|
|
|
1
|
-
#include <napi.h>
|
|
2
|
-
extern "C"
|
|
3
|
-
{
|
|
4
|
-
#include <libavcodec/avcodec.h>
|
|
5
|
-
#include <libavformat/avformat.h>
|
|
6
|
-
#include <libavutil/hwcontext.h>
|
|
7
|
-
#include <libavutil/frame.h>
|
|
8
|
-
#include <libavfilter/buffersink.h>
|
|
9
|
-
#include <libavfilter/buffersrc.h>
|
|
10
|
-
#include <libavfilter/avfilter.h>
|
|
11
|
-
#include <libavutil/opt.h>
|
|
12
|
-
#include <libavutil/avutil.h>
|
|
13
|
-
// rtpenc.h header uses a weird restrict keyword
|
|
14
|
-
#ifndef restrict
|
|
15
|
-
#define restrict
|
|
16
|
-
#include <libavformat/rtpenc.h>
|
|
17
|
-
#undef restrict
|
|
18
|
-
#else
|
|
19
|
-
#include <libavformat/rtpenc.h>
|
|
20
|
-
#endif
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
#include <thread>
|
|
24
|
-
#include <v8.h>
|
|
25
|
-
|
|
26
|
-
#include "formatcontext.h"
|
|
27
|
-
#include "filter.h"
|
|
28
|
-
#include "codeccontext.h"
|
|
29
|
-
#include "packet.h"
|
|
30
|
-
#include "bsf.h"
|
|
31
|
-
|
|
32
|
-
static Napi::FunctionReference logCallbackRef;
|
|
33
|
-
|
|
34
|
-
Napi::Value setLogLevel(const Napi::CallbackInfo &info)
|
|
35
|
-
{
|
|
36
|
-
Napi::Env env = info.Env();
|
|
37
|
-
|
|
38
|
-
// ensure 1 param
|
|
39
|
-
if (info.Length() < 1)
|
|
40
|
-
{
|
|
41
|
-
Napi::Error::New(env, "Expected 1 argument").ThrowAsJavaScriptException();
|
|
42
|
-
return env.Undefined();
|
|
43
|
-
}
|
|
44
|
-
// get string from info
|
|
45
|
-
std::string level = info[0].As<Napi::String>().Utf8Value();
|
|
46
|
-
if (level == "debug")
|
|
47
|
-
{
|
|
48
|
-
av_log_set_level(AV_LOG_DEBUG);
|
|
49
|
-
}
|
|
50
|
-
else if (level == "verbose")
|
|
51
|
-
{
|
|
52
|
-
av_log_set_level(AV_LOG_VERBOSE);
|
|
53
|
-
}
|
|
54
|
-
else if (level == "info")
|
|
55
|
-
{
|
|
56
|
-
av_log_set_level(AV_LOG_INFO);
|
|
57
|
-
}
|
|
58
|
-
else if (level == "warning")
|
|
59
|
-
{
|
|
60
|
-
av_log_set_level(AV_LOG_WARNING);
|
|
61
|
-
}
|
|
62
|
-
else if (level == "error")
|
|
63
|
-
{
|
|
64
|
-
av_log_set_level(AV_LOG_ERROR);
|
|
65
|
-
}
|
|
66
|
-
else if (level == "fatal")
|
|
67
|
-
{
|
|
68
|
-
av_log_set_level(AV_LOG_FATAL);
|
|
69
|
-
}
|
|
70
|
-
else if (level == "panic")
|
|
71
|
-
{
|
|
72
|
-
av_log_set_level(AV_LOG_PANIC);
|
|
73
|
-
}
|
|
74
|
-
else if (level == "quiet")
|
|
75
|
-
{
|
|
76
|
-
av_log_set_level(AV_LOG_QUIET);
|
|
77
|
-
}
|
|
78
|
-
else if (level == "trace")
|
|
79
|
-
{
|
|
80
|
-
av_log_set_level(AV_LOG_TRACE);
|
|
81
|
-
}
|
|
82
|
-
else
|
|
83
|
-
{
|
|
84
|
-
Napi::Error::New(env, "Invalid log level").ThrowAsJavaScriptException();
|
|
85
|
-
return env.Undefined();
|
|
86
|
-
}
|
|
87
|
-
return env.Undefined();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
void printAVError(int errnum)
|
|
91
|
-
{
|
|
92
|
-
char errbuf[256];
|
|
93
|
-
|
|
94
|
-
// Convert the error code to a string
|
|
95
|
-
if (av_strerror(errnum, errbuf, sizeof(errbuf)) < 0)
|
|
96
|
-
{
|
|
97
|
-
printf("Unknown error code: %d", errnum);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
printf("Error: %s\n", errbuf);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
class ReadFrameWorker : public Napi::AsyncWorker
|
|
105
|
-
{
|
|
106
|
-
public:
|
|
107
|
-
napi_deferred deferred;
|
|
108
|
-
AVFormatContextObject *formatContextObject;
|
|
109
|
-
AVCodecContextObject *codecContextObject;
|
|
110
|
-
int streamIndex;
|
|
111
|
-
AVPacket *packetResult;
|
|
112
|
-
AVFrame *frameResult;
|
|
113
|
-
|
|
114
|
-
ReadFrameWorker(napi_env env, napi_deferred deferred, AVFormatContextObject *formatContextObject, AVCodecContextObject *codecContextObject, int streamIndex)
|
|
115
|
-
: Napi::AsyncWorker(env), deferred(deferred), formatContextObject(formatContextObject), codecContextObject(codecContextObject), streamIndex(streamIndex)
|
|
116
|
-
{
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
void Execute() override
|
|
120
|
-
{
|
|
121
|
-
frameResult = nullptr;
|
|
122
|
-
packetResult = nullptr;
|
|
123
|
-
|
|
124
|
-
AVFormatContext *fmt_ctx_ = formatContextObject->fmt_ctx_;
|
|
125
|
-
if (!fmt_ctx_)
|
|
126
|
-
{
|
|
127
|
-
SetError("Format context is null");
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
AVCodecContext *codecContext = nullptr;
|
|
132
|
-
AVFrame *frame;
|
|
133
|
-
if (codecContextObject)
|
|
134
|
-
{
|
|
135
|
-
codecContext = codecContextObject->codecContext;
|
|
136
|
-
if (!codecContext)
|
|
137
|
-
{
|
|
138
|
-
SetError("Codec context is null");
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
frame = av_frame_alloc();
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
AVPacket *packet = av_packet_alloc();
|
|
145
|
-
|
|
146
|
-
if (!packet)
|
|
147
|
-
{
|
|
148
|
-
SetError("Failed to allocate packet");
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
int ret;
|
|
153
|
-
while (true)
|
|
154
|
-
{
|
|
155
|
-
if (codecContext)
|
|
156
|
-
{
|
|
157
|
-
// attempt to read frames first, EAGAIN will be returned if data needs to be
|
|
158
|
-
// sent to decoder.
|
|
159
|
-
ret = avcodec_receive_frame(codecContext, frame);
|
|
160
|
-
if (!ret)
|
|
161
|
-
{
|
|
162
|
-
// printf("returning frame 0\n");
|
|
163
|
-
av_packet_free(&packet);
|
|
164
|
-
frameResult = frame;
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
else if (ret != AVERROR(EAGAIN))
|
|
168
|
-
{
|
|
169
|
-
av_packet_free(&packet);
|
|
170
|
-
SetError(AVErrorString(ret));
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
while (true)
|
|
176
|
-
{
|
|
177
|
-
ret = av_read_frame(fmt_ctx_, packet);
|
|
178
|
-
if (ret == AVERROR(EAGAIN))
|
|
179
|
-
{
|
|
180
|
-
// try reading again later
|
|
181
|
-
av_packet_free(&packet);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
else if (ret)
|
|
185
|
-
{
|
|
186
|
-
av_packet_free(&packet);
|
|
187
|
-
SetError(AVErrorString(ret));
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (!codecContext)
|
|
192
|
-
{
|
|
193
|
-
packetResult = packet;
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (packet->stream_index != streamIndex)
|
|
198
|
-
{
|
|
199
|
-
// wrong stream, return the packet
|
|
200
|
-
packetResult = packet;
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
ret = avcodec_send_packet(codecContext, packet);
|
|
205
|
-
av_packet_unref(packet);
|
|
206
|
-
|
|
207
|
-
// on decoder feed error, try again next packet.
|
|
208
|
-
// could be starting on a non keyframe or data corruption
|
|
209
|
-
// which may be recoverable.
|
|
210
|
-
if (ret)
|
|
211
|
-
{
|
|
212
|
-
av_packet_free(&packet);
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// successful decode, try reading frame.
|
|
217
|
-
break;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// loop and try reading frame again
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// This method runs in the main thread after Execute completes successfully
|
|
225
|
-
void OnOK() override
|
|
226
|
-
{
|
|
227
|
-
Napi::Env env = Env();
|
|
228
|
-
if (packetResult)
|
|
229
|
-
{
|
|
230
|
-
napi_resolve_deferred(Env(), deferred, AVPacketObject::NewInstance(env, packetResult));
|
|
231
|
-
}
|
|
232
|
-
else if (frameResult)
|
|
233
|
-
{
|
|
234
|
-
napi_resolve_deferred(Env(), deferred, AVFrameObject::NewInstance(env, frameResult));
|
|
235
|
-
}
|
|
236
|
-
else
|
|
237
|
-
{
|
|
238
|
-
napi_resolve_deferred(Env(), deferred, env.Undefined());
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// This method runs in the main thread if Execute fails
|
|
243
|
-
void OnError(const Napi::Error &e) override
|
|
244
|
-
{
|
|
245
|
-
napi_value error = e.Value();
|
|
246
|
-
|
|
247
|
-
// Reject the promise
|
|
248
|
-
napi_reject_deferred(Env(), deferred, error);
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
static enum AVPixelFormat get_hw_format(AVCodecContext *ctx,
|
|
253
|
-
const enum AVPixelFormat *pix_fmts)
|
|
254
|
-
{
|
|
255
|
-
const enum AVPixelFormat *p;
|
|
256
|
-
AVCodecContextObject *fmt = (AVCodecContextObject *)ctx->opaque;
|
|
257
|
-
|
|
258
|
-
for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++)
|
|
259
|
-
{
|
|
260
|
-
if (*p == fmt->hw_pix_fmt)
|
|
261
|
-
return *p;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// fprintf(stderr, "Failed to get HW surface format.\n");
|
|
265
|
-
return AV_PIX_FMT_NONE;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
Napi::FunctionReference AVFormatContextObject::constructor;
|
|
269
|
-
|
|
270
|
-
Napi::Object AVFormatContextObject::Init(Napi::Env env, Napi::Object exports)
|
|
271
|
-
{
|
|
272
|
-
Napi::HandleScope scope(env);
|
|
273
|
-
|
|
274
|
-
Napi::Function func = DefineClass(env, "AVFormatContext", {
|
|
275
|
-
|
|
276
|
-
AVFormatContextObject::InstanceAccessor("metadata", &AVFormatContextObject::GetMetadata, nullptr),
|
|
277
|
-
|
|
278
|
-
AVFormatContextObject::InstanceAccessor("streams", &AVFormatContextObject::GetStreams, nullptr),
|
|
279
|
-
|
|
280
|
-
InstanceMethod(Napi::Symbol::WellKnown(env, "dispose"), &AVFormatContextObject::Close),
|
|
281
|
-
|
|
282
|
-
InstanceMethod("open", &AVFormatContextObject::Open),
|
|
283
|
-
|
|
284
|
-
InstanceMethod("close", &AVFormatContextObject::Close),
|
|
285
|
-
|
|
286
|
-
InstanceMethod("createDecoder", &AVFormatContextObject::CreateDecoder),
|
|
287
|
-
|
|
288
|
-
InstanceMethod("readFrame", &AVFormatContextObject::ReadFrame),
|
|
289
|
-
|
|
290
|
-
InstanceMethod("receiveFrame", &AVFormatContextObject::ReceiveFrame),
|
|
291
|
-
|
|
292
|
-
InstanceMethod("create", &AVFormatContextObject::Create),
|
|
293
|
-
|
|
294
|
-
InstanceMethod("newStream", &AVFormatContextObject::NewStream),
|
|
295
|
-
|
|
296
|
-
InstanceMethod("writeFrame", &AVFormatContextObject::WriteFrame),
|
|
297
|
-
|
|
298
|
-
InstanceMethod("createSDP", &AVFormatContextObject::CreateSDP),
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
constructor = Napi::Persistent(func);
|
|
302
|
-
constructor.SuppressDestruct();
|
|
303
|
-
|
|
304
|
-
exports.Set("AVFormatContext", func);
|
|
305
|
-
return exports;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
AVFormatContextObject::AVFormatContextObject(const Napi::CallbackInfo &info)
|
|
309
|
-
: Napi::ObjectWrap<AVFormatContextObject>(info),
|
|
310
|
-
fmt_ctx_(nullptr), is_input(false)
|
|
311
|
-
{
|
|
312
|
-
// i don't think this constructor is called from js??
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
Napi::Value AVFormatContextObject::ReadFrame(const Napi::CallbackInfo &info)
|
|
316
|
-
{
|
|
317
|
-
Napi::Env env = info.Env();
|
|
318
|
-
// Create a new promise and get its deferred handle
|
|
319
|
-
napi_deferred deferred;
|
|
320
|
-
napi_value promise;
|
|
321
|
-
napi_create_promise(env, &deferred, &promise);
|
|
322
|
-
|
|
323
|
-
// Create and queue the AsyncWorker, passing the deferred handle
|
|
324
|
-
ReadFrameWorker *worker = new ReadFrameWorker(env, deferred, this, nullptr, 0);
|
|
325
|
-
worker->Queue();
|
|
326
|
-
|
|
327
|
-
// Return the promise to JavaScript
|
|
328
|
-
return Napi::Value(env, promise);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
Napi::Value AVFormatContextObject::ReceiveFrame(const Napi::CallbackInfo &info)
|
|
332
|
-
{
|
|
333
|
-
Napi::Env env = info.Env();
|
|
334
|
-
// Create a new promise and get its deferred handle
|
|
335
|
-
napi_deferred deferred;
|
|
336
|
-
napi_value promise;
|
|
337
|
-
napi_create_promise(env, &deferred, &promise);
|
|
338
|
-
|
|
339
|
-
if (info.Length() < 1 || !info[0].IsNumber())
|
|
340
|
-
{
|
|
341
|
-
Napi::TypeError::New(env, "Number expected for argument 1: streamIndex").ThrowAsJavaScriptException();
|
|
342
|
-
return env.Undefined();
|
|
343
|
-
}
|
|
344
|
-
int streamIndex = info[0].As<Napi::Number>().Int32Value();
|
|
345
|
-
|
|
346
|
-
if (info.Length() < 2 || !info[1].IsObject())
|
|
347
|
-
{
|
|
348
|
-
Napi::TypeError::New(env, "Object expected for argument 0: codecContext").ThrowAsJavaScriptException();
|
|
349
|
-
return env.Undefined();
|
|
350
|
-
}
|
|
351
|
-
AVCodecContextObject *codecContextObject = Napi::ObjectWrap<AVCodecContextObject>::Unwrap(info[1].As<Napi::Object>());
|
|
352
|
-
|
|
353
|
-
// Create and queue the AsyncWorker, passing the deferred handle
|
|
354
|
-
ReadFrameWorker *worker = new ReadFrameWorker(env, deferred, this, codecContextObject, streamIndex);
|
|
355
|
-
worker->Queue();
|
|
356
|
-
|
|
357
|
-
// Return the promise to JavaScript
|
|
358
|
-
return Napi::Value(env, promise);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
Napi::Value AVFormatContextObject::Open(const Napi::CallbackInfo &info)
|
|
362
|
-
{
|
|
363
|
-
Napi::Env env = info.Env();
|
|
364
|
-
if (info.Length() < 1 || !info[0].IsString())
|
|
365
|
-
{
|
|
366
|
-
Napi::TypeError::New(env, "String source expected").ThrowAsJavaScriptException();
|
|
367
|
-
return env.Undefined();
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
AVDictionary *options = nullptr;
|
|
371
|
-
std::string filename = info[0].As<Napi::String>().Utf8Value();
|
|
372
|
-
int ret = avformat_open_input(&fmt_ctx_, filename.c_str(), NULL, &options);
|
|
373
|
-
if (ret < 0)
|
|
374
|
-
{
|
|
375
|
-
Napi::Error::New(env, AVErrorString(ret)).ThrowAsJavaScriptException();
|
|
376
|
-
return env.Undefined();
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
is_input = true;
|
|
380
|
-
return env.Undefined();
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
Napi::Value AVFormatContextObject::CreateDecoder(const Napi::CallbackInfo &info)
|
|
384
|
-
{
|
|
385
|
-
if (!fmt_ctx_)
|
|
386
|
-
{
|
|
387
|
-
Napi::Error::New(info.Env(), "Format context is null").ThrowAsJavaScriptException();
|
|
388
|
-
return info.Env().Undefined();
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (info.Length() < 1 || !info[0].IsNumber())
|
|
392
|
-
{
|
|
393
|
-
Napi::TypeError::New(info.Env(), "Number expected for argument 0: streamIndex").ThrowAsJavaScriptException();
|
|
394
|
-
return info.Env().Undefined();
|
|
395
|
-
}
|
|
396
|
-
int streamIndex = info[0].As<Napi::Number>().Int32Value();
|
|
397
|
-
|
|
398
|
-
Napi::Env env = info.Env();
|
|
399
|
-
|
|
400
|
-
AVStream *stream = fmt_ctx_->streams[streamIndex];
|
|
401
|
-
AVCodecID codec_id = stream->codecpar->codec_id;
|
|
402
|
-
const struct AVCodec *codec = avcodec_find_decoder(codec_id);
|
|
403
|
-
Napi::Object codecContextReturn = AVCodecContextObject::NewInstance(env);
|
|
404
|
-
AVCodecContextObject *codecContextObject = Napi::ObjectWrap<AVCodecContextObject>::Unwrap(codecContextReturn);
|
|
405
|
-
codecContextObject->hw_device_value = AV_HWDEVICE_TYPE_NONE;
|
|
406
|
-
AVBufferRef *hw_device_ctx = nullptr;
|
|
407
|
-
int ret;
|
|
408
|
-
std::string deviceName;
|
|
409
|
-
|
|
410
|
-
// args are hardwareDeviceName (optional) hardwareDeviceDecoder (optional)
|
|
411
|
-
if (info.Length() > 1)
|
|
412
|
-
{
|
|
413
|
-
if (!info[1].IsString())
|
|
414
|
-
{
|
|
415
|
-
Napi::Error::New(env, "Unexpected type in place of hardware device name string").ThrowAsJavaScriptException();
|
|
416
|
-
return env.Undefined();
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
std::string hardwareDeviceName = info[1].As<Napi::String>().Utf8Value();
|
|
420
|
-
codecContextObject->hw_device_value = av_hwdevice_find_type_by_name(hardwareDeviceName.c_str());
|
|
421
|
-
if (codecContextObject->hw_device_value == AV_HWDEVICE_TYPE_NONE)
|
|
422
|
-
{
|
|
423
|
-
Napi::Error::New(env, "Hardware device not found").ThrowAsJavaScriptException();
|
|
424
|
-
return env.Undefined();
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// hwaccel decoder name (ie, hevc_qsv, which needs to be manually specified for some reason)
|
|
428
|
-
if (info.Length() > 2)
|
|
429
|
-
{
|
|
430
|
-
if (!info[2].IsUndefined() && !info[2].IsNull())
|
|
431
|
-
{
|
|
432
|
-
if (!info[2].IsString())
|
|
433
|
-
{
|
|
434
|
-
Napi::Error::New(env, "Unexpected type in place of decoder string").ThrowAsJavaScriptException();
|
|
435
|
-
return env.Undefined();
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
codec = avcodec_find_decoder_by_name(info[2].As<Napi::String>().Utf8Value().c_str());
|
|
439
|
-
if (!codec)
|
|
440
|
-
{
|
|
441
|
-
Napi::Error::New(env, "Decoder not found").ThrowAsJavaScriptException();
|
|
442
|
-
return env.Undefined();
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// specific device name (ie, /dev/dri/renderD128)
|
|
448
|
-
if (info.Length() > 3)
|
|
449
|
-
{
|
|
450
|
-
if (!info[3].IsUndefined() && !info[3].IsNull())
|
|
451
|
-
{
|
|
452
|
-
if (!info[3].IsString())
|
|
453
|
-
{
|
|
454
|
-
Napi::Error::New(env, "Unexpected type in place of device string").ThrowAsJavaScriptException();
|
|
455
|
-
return env.Undefined();
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
deviceName = info[3].As<Napi::String>().Utf8Value();
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
for (int i = 0;; i++)
|
|
463
|
-
{
|
|
464
|
-
const AVCodecHWConfig *config = avcodec_get_hw_config(codec, i);
|
|
465
|
-
if (!config)
|
|
466
|
-
{
|
|
467
|
-
Napi::Error::New(env, "Decoder does not support device type").ThrowAsJavaScriptException();
|
|
468
|
-
return env.Undefined();
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
|
|
472
|
-
config->device_type == codecContextObject->hw_device_value)
|
|
473
|
-
{
|
|
474
|
-
// unsure if this works for qsv?
|
|
475
|
-
codecContextObject->hw_pix_fmt = config->pix_fmt;
|
|
476
|
-
break;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
if ((ret = av_hwdevice_ctx_create(&hw_device_ctx, codecContextObject->hw_device_value,
|
|
481
|
-
deviceName.length() ? deviceName.c_str() : nullptr, NULL, 0)) < 0)
|
|
482
|
-
{
|
|
483
|
-
// throw
|
|
484
|
-
Napi::Error::New(env, AVErrorString(ret)).ThrowAsJavaScriptException();
|
|
485
|
-
return env.Undefined();
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
codecContextObject->codecContext = avcodec_alloc_context3(codec);
|
|
490
|
-
codecContextObject->codecContext->time_base = stream->time_base;
|
|
491
|
-
codecContextObject->codecContext->opaque = codecContextObject;
|
|
492
|
-
if (hw_device_ctx)
|
|
493
|
-
{
|
|
494
|
-
codecContextObject->codecContext->get_format = get_hw_format;
|
|
495
|
-
codecContextObject->codecContext->hw_device_ctx = hw_device_ctx;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if ((ret = avcodec_parameters_to_context(codecContextObject->codecContext, fmt_ctx_->streams[streamIndex]->codecpar)) < 0)
|
|
499
|
-
{
|
|
500
|
-
avcodec_free_context(&codecContextObject->codecContext);
|
|
501
|
-
codecContextObject->codecContext = nullptr;
|
|
502
|
-
Napi::Error::New(env, AVErrorString(ret)).ThrowAsJavaScriptException();
|
|
503
|
-
return env.Undefined();
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if ((ret = avcodec_open2(codecContextObject->codecContext, codec, nullptr)) < 0)
|
|
507
|
-
{
|
|
508
|
-
Napi::Error::New(env, AVErrorString(ret)).ThrowAsJavaScriptException();
|
|
509
|
-
avcodec_free_context(&codecContextObject->codecContext);
|
|
510
|
-
codecContextObject->codecContext = nullptr;
|
|
511
|
-
return env.Undefined();
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return codecContextReturn;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
AVFormatContextObject::~AVFormatContextObject()
|
|
518
|
-
{
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
Napi::Value AVFormatContextObject::Close(const Napi::CallbackInfo &info)
|
|
522
|
-
{
|
|
523
|
-
if (fmt_ctx_)
|
|
524
|
-
{
|
|
525
|
-
if (is_input) {
|
|
526
|
-
avformat_close_input(&fmt_ctx_);
|
|
527
|
-
}
|
|
528
|
-
if (callbackRef) {
|
|
529
|
-
callbackRef.Release();
|
|
530
|
-
}
|
|
531
|
-
avformat_free_context(fmt_ctx_);
|
|
532
|
-
fmt_ctx_ = nullptr;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return info.Env().Undefined();
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
Napi::Value AVFormatContextObject::GetMetadata(const Napi::CallbackInfo &info)
|
|
539
|
-
{
|
|
540
|
-
Napi::Env env = info.Env();
|
|
541
|
-
Napi::Object metadata = Napi::Object::New(env);
|
|
542
|
-
|
|
543
|
-
if (fmt_ctx_ && fmt_ctx_->metadata)
|
|
544
|
-
{
|
|
545
|
-
AVDictionaryEntry *tag = NULL;
|
|
546
|
-
while ((tag = av_dict_get(fmt_ctx_->metadata, "", tag, AV_DICT_IGNORE_SUFFIX)))
|
|
547
|
-
{
|
|
548
|
-
metadata.Set(tag->key, tag->value);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return metadata;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// Custom write function to intercept RTP packet data
|
|
556
|
-
static int write_packet(void *opaque, const uint8_t *buf, int buf_size)
|
|
557
|
-
{
|
|
558
|
-
AVFormatContextObject *formatContextObject = (AVFormatContextObject *)opaque;
|
|
559
|
-
|
|
560
|
-
// malloc and copy buf
|
|
561
|
-
uint8_t *copy = (uint8_t *)av_malloc(buf_size);
|
|
562
|
-
if (!copy)
|
|
563
|
-
{
|
|
564
|
-
return AVERROR(ENOMEM);
|
|
565
|
-
}
|
|
566
|
-
memcpy(copy, buf, buf_size);
|
|
567
|
-
|
|
568
|
-
// Call the JavaScript function on the main thread
|
|
569
|
-
formatContextObject->callbackRef.BlockingCall([copy, buf_size](Napi::Env env, Napi::Function jsCallback)
|
|
570
|
-
{
|
|
571
|
-
Napi::Buffer<uint8_t> buffer = Napi::Buffer<uint8_t>::Copy(env, copy, buf_size);
|
|
572
|
-
av_free(copy);
|
|
573
|
-
jsCallback.Call({buffer});
|
|
574
|
-
//
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
return buf_size;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// Custom write funct
|
|
581
|
-
Napi::Value AVFormatContextObject::Create(const Napi::CallbackInfo &info)
|
|
582
|
-
{
|
|
583
|
-
Napi::Env env = info.Env();
|
|
584
|
-
|
|
585
|
-
if (fmt_ctx_)
|
|
586
|
-
{
|
|
587
|
-
Napi::Error::New(env, "Format context already created").ThrowAsJavaScriptException();
|
|
588
|
-
return env.Undefined();
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
if (info.Length() < 1 || !info[0].IsString())
|
|
592
|
-
{
|
|
593
|
-
Napi::TypeError::New(env, "String expected for argument 0: format").ThrowAsJavaScriptException();
|
|
594
|
-
return env.Undefined();
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
if (info.Length() < 2 || !info[1].IsFunction())
|
|
598
|
-
{
|
|
599
|
-
Napi::TypeError::New(env, "Function expected for argument 1: callback").ThrowAsJavaScriptException();
|
|
600
|
-
return env.Undefined();
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
std::string formatName = info[0].As<Napi::String>().Utf8Value();
|
|
604
|
-
|
|
605
|
-
int ret = avformat_alloc_output_context2(&fmt_ctx_, NULL, formatName.c_str(), NULL);
|
|
606
|
-
if (ret)
|
|
607
|
-
{
|
|
608
|
-
Napi::Error::New(env, AVErrorString(ret)).ThrowAsJavaScriptException();
|
|
609
|
-
return env.Undefined();
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
// RTP doesn't initialize properly?
|
|
613
|
-
if (formatName == "rtp")
|
|
614
|
-
{
|
|
615
|
-
int MAX_RTP_PACKET_SIZE = 64000;
|
|
616
|
-
fmt_ctx_->packet_size = MAX_RTP_PACKET_SIZE;
|
|
617
|
-
RTPMuxContext *rtp_ctx = (RTPMuxContext *)fmt_ctx_->priv_data;
|
|
618
|
-
rtp_ctx->max_payload_size = MAX_RTP_PACKET_SIZE - 12;
|
|
619
|
-
rtp_ctx->buf = (uint8_t *)av_malloc(MAX_RTP_PACKET_SIZE);
|
|
620
|
-
if (!rtp_ctx->buf)
|
|
621
|
-
{
|
|
622
|
-
avformat_free_context(fmt_ctx_);
|
|
623
|
-
fmt_ctx_ = nullptr;
|
|
624
|
-
Napi::Error::New(env, "Failed to allocate RTP buffer").ThrowAsJavaScriptException();
|
|
625
|
-
return env.Undefined();
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
// Set up a custom AVIOContext to capture the RTP output
|
|
630
|
-
int MAX_MEM_SIZE = 1024 * 1024;
|
|
631
|
-
uint8_t *buffer = (uint8_t *)av_malloc(MAX_MEM_SIZE);
|
|
632
|
-
if (!buffer)
|
|
633
|
-
{
|
|
634
|
-
avformat_free_context(fmt_ctx_);
|
|
635
|
-
fmt_ctx_ = nullptr;
|
|
636
|
-
Napi::Error::New(env, "Failed to allocate AVIO context buffer").ThrowAsJavaScriptException();
|
|
637
|
-
return env.Undefined();
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
AVIOContext *avio_ctx = avio_alloc_context(buffer, MAX_MEM_SIZE, 1, this, NULL, write_packet, NULL);
|
|
641
|
-
if (!avio_ctx)
|
|
642
|
-
{
|
|
643
|
-
av_free(buffer);
|
|
644
|
-
avformat_free_context(fmt_ctx_);
|
|
645
|
-
fmt_ctx_ = nullptr;
|
|
646
|
-
Napi::Error::New(env, "Failed to create AVIOContext").ThrowAsJavaScriptException();
|
|
647
|
-
return env.Undefined();
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
callbackRef = Napi::ThreadSafeFunction::New(
|
|
651
|
-
env, // Environment
|
|
652
|
-
info[1].As<Napi::Function>(), // JavaScript function to call
|
|
653
|
-
"napi_write_packet", // Resource name for diagnostics
|
|
654
|
-
0, // Max queue size (0 = unlimited)
|
|
655
|
-
1 // Initial thread count
|
|
656
|
-
);
|
|
657
|
-
|
|
658
|
-
fmt_ctx_->pb = avio_ctx;
|
|
659
|
-
|
|
660
|
-
return env.Undefined();
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
Napi::Value AVFormatContextObject::WriteFrame(const Napi::CallbackInfo &info)
|
|
664
|
-
{
|
|
665
|
-
// arguments are index and packet
|
|
666
|
-
Napi::Env env = info.Env();
|
|
667
|
-
|
|
668
|
-
if (!fmt_ctx_)
|
|
669
|
-
{
|
|
670
|
-
Napi::Error::New(env, "Format context is null").ThrowAsJavaScriptException();
|
|
671
|
-
return env.Undefined();
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsObject())
|
|
675
|
-
{
|
|
676
|
-
Napi::TypeError::New(env, "Number expected for argument 0: index and Object expected for argument 1: packet").ThrowAsJavaScriptException();
|
|
677
|
-
return env.Undefined();
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
int streamIndex = info[0].As<Napi::Number>().Int32Value();
|
|
681
|
-
|
|
682
|
-
Napi::Object packetObject = info[1].As<Napi::Object>();
|
|
683
|
-
AVPacketObject *avPacketObject = Napi::ObjectWrap<AVPacketObject>::Unwrap(packetObject);
|
|
684
|
-
AVPacket *packet = avPacketObject->packet;
|
|
685
|
-
|
|
686
|
-
if (!packet)
|
|
687
|
-
{
|
|
688
|
-
Napi::Error::New(env, "WriteFrame received null frame").ThrowAsJavaScriptException();
|
|
689
|
-
return env.Undefined();
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
packet->stream_index = streamIndex;
|
|
693
|
-
|
|
694
|
-
av_write_frame(fmt_ctx_, packet);
|
|
695
|
-
|
|
696
|
-
return env.Undefined();
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
Napi::Value AVFormatContextObject::NewStream(const Napi::CallbackInfo &info)
|
|
700
|
-
{
|
|
701
|
-
Napi::Env env = info.Env();
|
|
702
|
-
if (!fmt_ctx_)
|
|
703
|
-
{
|
|
704
|
-
Napi::Error::New(env, "Format context is null").ThrowAsJavaScriptException();
|
|
705
|
-
return env.Undefined();
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
if (info.Length() < 1 || !info[0].IsObject())
|
|
709
|
-
{
|
|
710
|
-
Napi::TypeError::New(env, "Object expected for argument 0: codecContext").ThrowAsJavaScriptException();
|
|
711
|
-
return env.Undefined();
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
Napi::Object options = info[0].As<Napi::Object>();
|
|
715
|
-
|
|
716
|
-
Napi::Value codecContextValue = options.Get("codecContext");
|
|
717
|
-
Napi::Value formatContextValue = options.Get("formatContext");
|
|
718
|
-
Napi::Value streamIndexValue = options.Get("streamIndex");
|
|
719
|
-
AVStream *stream;
|
|
720
|
-
|
|
721
|
-
if (codecContextValue.IsObject())
|
|
722
|
-
{
|
|
723
|
-
AVCodecContextObject *codecContextObject = Napi::ObjectWrap<AVCodecContextObject>::Unwrap(codecContextValue.As<Napi::Object>());
|
|
724
|
-
AVCodecContext *codecContext = codecContextObject->codecContext;
|
|
725
|
-
|
|
726
|
-
// Create a new stream
|
|
727
|
-
stream = avformat_new_stream(fmt_ctx_, NULL);
|
|
728
|
-
if (!stream)
|
|
729
|
-
{
|
|
730
|
-
Napi::Error::New(env, "Failed to create new stream").ThrowAsJavaScriptException();
|
|
731
|
-
return env.Undefined();
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// Set the codec parameters for the new stream
|
|
735
|
-
AVCodecParameters *codecpar = stream->codecpar;
|
|
736
|
-
stream->time_base.num = codecContext->time_base.num;
|
|
737
|
-
stream->time_base.den = codecContext->time_base.den;
|
|
738
|
-
|
|
739
|
-
avcodec_parameters_from_context(codecpar, codecContext);
|
|
740
|
-
}
|
|
741
|
-
else if (formatContextValue.IsObject())
|
|
742
|
-
{
|
|
743
|
-
if (!streamIndexValue.IsNumber())
|
|
744
|
-
{
|
|
745
|
-
Napi::TypeError::New(env, "Number expected for streamIndex").ThrowAsJavaScriptException();
|
|
746
|
-
return env.Undefined();
|
|
747
|
-
}
|
|
748
|
-
unsigned int streamIndex = streamIndexValue.As<Napi::Number>().Uint32Value();
|
|
749
|
-
AVFormatContextObject *formatContextObject = Napi::ObjectWrap<AVFormatContextObject>::Unwrap(formatContextValue.As<Napi::Object>());
|
|
750
|
-
AVFormatContext *formatContext = formatContextObject->fmt_ctx_;
|
|
751
|
-
if (!formatContext)
|
|
752
|
-
{
|
|
753
|
-
Napi::Error::New(env, "Format context is null").ThrowAsJavaScriptException();
|
|
754
|
-
return env.Undefined();
|
|
755
|
-
}
|
|
756
|
-
if (streamIndex >= formatContext->nb_streams)
|
|
757
|
-
{
|
|
758
|
-
Napi::Error::New(env, "Invalid stream index").ThrowAsJavaScriptException();
|
|
759
|
-
return env.Undefined();
|
|
760
|
-
}
|
|
761
|
-
AVStream *sourceStream = formatContext->streams[streamIndex];
|
|
762
|
-
stream = avformat_new_stream(fmt_ctx_, NULL);
|
|
763
|
-
if (!stream)
|
|
764
|
-
{
|
|
765
|
-
Napi::Error::New(env, "Failed to create new stream").ThrowAsJavaScriptException();
|
|
766
|
-
return env.Undefined();
|
|
767
|
-
}
|
|
768
|
-
AVCodecParameters *codecpar = stream->codecpar;
|
|
769
|
-
if (avcodec_parameters_copy(codecpar, sourceStream->codecpar) < 0)
|
|
770
|
-
{
|
|
771
|
-
Napi::Error::New(env, "Failed to copy codec parameters").ThrowAsJavaScriptException();
|
|
772
|
-
return env.Undefined();
|
|
773
|
-
}
|
|
774
|
-
stream->time_base.num = sourceStream->time_base.num;
|
|
775
|
-
stream->time_base.den = sourceStream->time_base.den;
|
|
776
|
-
}
|
|
777
|
-
else if (streamIndexValue.IsNumber())
|
|
778
|
-
{
|
|
779
|
-
int streamIndex = streamIndexValue.As<Napi::Number>().Int32Value();
|
|
780
|
-
stream = fmt_ctx_->streams[streamIndex];
|
|
781
|
-
}
|
|
782
|
-
else {
|
|
783
|
-
// throw
|
|
784
|
-
Napi::Error::New(env, "Object expected for codecContext or formatContext").ThrowAsJavaScriptException();
|
|
785
|
-
return env.Undefined();
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
int ret;
|
|
789
|
-
if ((ret = avformat_write_header(fmt_ctx_, NULL)) < 0)
|
|
790
|
-
{
|
|
791
|
-
avformat_free_context(fmt_ctx_);
|
|
792
|
-
fmt_ctx_ = nullptr;
|
|
793
|
-
Napi::Error::New(env, AVErrorString(ret)).ThrowAsJavaScriptException();
|
|
794
|
-
return env.Undefined();
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
return Napi::Number::New(env, stream->index);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
Napi::Value AVFormatContextObject::GetStreams(const Napi::CallbackInfo &info)
|
|
801
|
-
{
|
|
802
|
-
Napi::Env env = info.Env();
|
|
803
|
-
Napi::Array streams = Napi::Array::New(env);
|
|
804
|
-
|
|
805
|
-
if (!fmt_ctx_)
|
|
806
|
-
{
|
|
807
|
-
return streams;
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
for (unsigned int i = 0; i < fmt_ctx_->nb_streams; i++)
|
|
811
|
-
{
|
|
812
|
-
AVStream *s = fmt_ctx_->streams[i];
|
|
813
|
-
Napi::Object stream = Napi::Object::New(env);
|
|
814
|
-
stream.Set("index", Napi::Number::New(env, s->index));
|
|
815
|
-
stream.Set("codec", Napi::String::New(env, avcodec_get_name(s->codecpar->codec_id)));
|
|
816
|
-
stream.Set("type", Napi::String::New(env, av_get_media_type_string(s->codecpar->codec_type)));
|
|
817
|
-
stream.Set("timeBaseNum", Napi::Number::New(env, s->time_base.num));
|
|
818
|
-
stream.Set("timeBaseDen", Napi::Number::New(env, s->time_base.den));
|
|
819
|
-
streams.Set(i, stream);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
return streams;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
Napi::Value AVFormatContextObject::CreateSDP(const Napi::CallbackInfo &info)
|
|
826
|
-
{
|
|
827
|
-
Napi::Env env = info.Env();
|
|
828
|
-
if (!fmt_ctx_)
|
|
829
|
-
{
|
|
830
|
-
Napi::Error::New(env, "Format context is null").ThrowAsJavaScriptException();
|
|
831
|
-
return env.Undefined();
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
char sdp[65536];
|
|
835
|
-
// create array of 1 AVFormatContext
|
|
836
|
-
AVFormatContext *ctx[1] = {fmt_ctx_};
|
|
837
|
-
int ret = av_sdp_create(ctx, 1, sdp, sizeof(sdp));
|
|
838
|
-
if (ret < 0)
|
|
839
|
-
{
|
|
840
|
-
Napi::Error::New(env, AVErrorString(ret)).ThrowAsJavaScriptException();
|
|
841
|
-
return env.Undefined();
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
Napi::String sdpString = Napi::String::New(env, sdp);
|
|
845
|
-
return sdpString;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
Napi::Value createSDP(const Napi::CallbackInfo &info) {
|
|
849
|
-
Napi::Env env = info.Env();
|
|
850
|
-
|
|
851
|
-
if (info.Length() < 1 || !info[0].IsArray()) {
|
|
852
|
-
Napi::TypeError::New(env, "Array of AVFormatContext expected for argument 0").ThrowAsJavaScriptException();
|
|
853
|
-
return env.Undefined();
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
Napi::Array formatContextArray = info[0].As<Napi::Array>();
|
|
857
|
-
size_t length = formatContextArray.Length();
|
|
858
|
-
AVFormatContext *ctx[100];
|
|
859
|
-
|
|
860
|
-
if (length > 100) {
|
|
861
|
-
Napi::Error::New(env, "Array length must be less than 100").ThrowAsJavaScriptException();
|
|
862
|
-
return env.Undefined();
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
for (size_t i = 0; i < length; i++) {
|
|
866
|
-
Napi::Object formatContextObject = formatContextArray.Get(i).As<Napi::Object>();
|
|
867
|
-
AVFormatContextObject *avFormatContextObject = Napi::ObjectWrap<AVFormatContextObject>::Unwrap(formatContextObject);
|
|
868
|
-
ctx[i] = avFormatContextObject->fmt_ctx_;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
char sdp[65536];
|
|
872
|
-
int ret = av_sdp_create(ctx, length, sdp, sizeof(sdp));
|
|
873
|
-
if (ret < 0) {
|
|
874
|
-
Napi::Error::New(env, AVErrorString(ret)).ThrowAsJavaScriptException();
|
|
875
|
-
return env.Undefined();
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
Napi::String sdpString = Napi::String::New(env, sdp);
|
|
879
|
-
return sdpString;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
Napi::Object Init(Napi::Env env, Napi::Object exports)
|
|
883
|
-
{
|
|
884
|
-
av_log_set_level(AV_LOG_QUIET);
|
|
885
|
-
avformat_network_init();
|
|
886
|
-
|
|
887
|
-
AVPacketObject::Init(env, exports);
|
|
888
|
-
AVFilterGraphObject::Init(env, exports);
|
|
889
|
-
AVFrameObject::Init(env, exports);
|
|
890
|
-
AVCodecContextObject::Init(env, exports);
|
|
891
|
-
AVFormatContextObject::Init(env, exports);
|
|
892
|
-
AVBitstreamFilter::Init(env, exports);
|
|
893
|
-
|
|
894
|
-
exports.Set(Napi::String::New(env, "setLogLevel"), Napi::Function::New(env, setLogLevel));
|
|
895
|
-
exports.Set(Napi::String::New(env, "createSdp"), Napi::Function::New(env, createSDP));
|
|
896
|
-
return exports;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
NODE_API_MODULE(addon, Init)
|