@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.
@@ -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)