grandi 1.0.0 → 1.2.0

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.
Files changed (38) hide show
  1. package/README.md +150 -77
  2. package/dist/index.d.mts +418 -33
  3. package/dist/index.mjs +188 -3
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +34 -32
  6. package/binding.gyp +0 -200
  7. package/lib/.clang-format +0 -297
  8. package/lib/grandi.cc +0 -96
  9. package/lib/grandi_find.cc +0 -365
  10. package/lib/grandi_find.h +0 -41
  11. package/lib/grandi_receive.cc +0 -1155
  12. package/lib/grandi_receive.h +0 -63
  13. package/lib/grandi_routing.cc +0 -460
  14. package/lib/grandi_routing.h +0 -37
  15. package/lib/grandi_send.cc +0 -960
  16. package/lib/grandi_send.h +0 -45
  17. package/lib/grandi_util.cc +0 -323
  18. package/lib/grandi_util.h +0 -137
  19. package/prebuilds/darwin-arm64/grandi.node +0 -0
  20. package/prebuilds/darwin-arm64/libndi.dylib +0 -0
  21. package/prebuilds/darwin-x64/grandi.node +0 -0
  22. package/prebuilds/darwin-x64/libndi.dylib +0 -0
  23. package/prebuilds/linux-arm/grandi.node +0 -0
  24. package/prebuilds/linux-arm/libndi.so.6 +0 -0
  25. package/prebuilds/linux-arm64/grandi.node +0 -0
  26. package/prebuilds/linux-arm64/libndi.so.6 +0 -0
  27. package/prebuilds/linux-ia32/grandi.node +0 -0
  28. package/prebuilds/linux-ia32/libndi.so.6 +0 -0
  29. package/prebuilds/linux-x64/grandi.node +0 -0
  30. package/prebuilds/linux-x64/libndi.so.6 +0 -0
  31. package/prebuilds/win32-ia32/Processing.NDI.Lib.x86.dll +0 -0
  32. package/prebuilds/win32-ia32/grandi.node +0 -0
  33. package/prebuilds/win32-x64/Processing.NDI.Lib.x64.dll +0 -0
  34. package/prebuilds/win32-x64/grandi.node +0 -0
  35. package/scripts/preinstall.mjs +0 -429
  36. package/src/index.ts +0 -141
  37. package/src/node-gyp-build.d.ts +0 -5
  38. package/src/types.ts +0 -228
@@ -1,960 +0,0 @@
1
- /* Copyright 2018 Streampunk Media Ltd.
2
-
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
6
-
7
- http://www.apache.org/licenses/LICENSE-2.0
8
-
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
14
- */
15
-
16
- #include <string>
17
- #include <Processing.NDI.Lib.h>
18
-
19
- #ifdef _WIN32
20
- #ifdef _WIN64
21
- #pragma comment(lib, "Processing.NDI.Lib.x64.lib")
22
- #else // _WIN64
23
- #pragma comment(lib, "Processing.NDI.Lib.x86.lib")
24
- #endif // _WIN64
25
- #endif // _WIN32
26
-
27
- #include "grandi_send.h"
28
- #include "grandi_util.h"
29
-
30
- napi_value videoSend(napi_env env, napi_callback_info info);
31
- napi_value audioSend(napi_env env, napi_callback_info info);
32
- napi_value connections(napi_env env, napi_callback_info info);
33
- napi_value metadataSend(napi_env env, napi_callback_info info);
34
- napi_value tally(napi_env env, napi_callback_info info);
35
- napi_value sourcename(napi_env env, napi_callback_info info);
36
-
37
- namespace {
38
- bool getInt64FromValue(napi_env env, napi_value value, int64_t *out, carrier *c,
39
- const char *propName) {
40
- napi_valuetype type;
41
- c->status = napi_typeof(env, value, &type);
42
- if (c->status != napi_ok)
43
- return false;
44
- if (type == napi_number) {
45
- c->status = napi_get_value_int64(env, value, out);
46
- return c->status == napi_ok;
47
- }
48
- if (type == napi_bigint) {
49
- bool lossless;
50
- c->status = napi_get_value_bigint_int64(env, value, out, &lossless);
51
- return c->status == napi_ok;
52
- }
53
- c->errorMsg =
54
- std::string(propName) +
55
- " value must be a number, bigint, or a [seconds,nanoseconds] tuple.";
56
- c->status = GRANDI_INVALID_ARGS;
57
- return false;
58
- }
59
-
60
- bool parsePtpTimestampArray(napi_env env, napi_value value, int64_t *out,
61
- carrier *c, const char *propName) {
62
- bool isArray;
63
- c->status = napi_is_array(env, value, &isArray);
64
- if (c->status != napi_ok)
65
- return false;
66
- if (!isArray) {
67
- c->errorMsg =
68
- std::string(propName) + " value must be a [seconds,nanoseconds] array.";
69
- c->status = GRANDI_INVALID_ARGS;
70
- return false;
71
- }
72
-
73
- uint32_t length;
74
- c->status = napi_get_array_length(env, value, &length);
75
- if (c->status != napi_ok)
76
- return false;
77
- if (length < 2) {
78
- c->errorMsg =
79
- std::string(propName) + " array must contain two numeric entries.";
80
- c->status = GRANDI_INVALID_ARGS;
81
- return false;
82
- }
83
-
84
- napi_value secsValue, nanosValue;
85
- c->status = napi_get_element(env, value, 0, &secsValue);
86
- if (c->status != napi_ok)
87
- return false;
88
- c->status = napi_get_element(env, value, 1, &nanosValue);
89
- if (c->status != napi_ok)
90
- return false;
91
-
92
- int64_t seconds = 0;
93
- if (!getInt64FromValue(env, secsValue, &seconds, c, propName))
94
- return false;
95
-
96
- int64_t nanos = 0;
97
- if (!getInt64FromValue(env, nanosValue, &nanos, c, propName))
98
- return false;
99
-
100
- int64_t hundredNsFromSeconds = seconds * 10000000LL;
101
- int64_t hundredNsFromNanos = nanos / 100LL;
102
- *out = hundredNsFromSeconds + hundredNsFromNanos;
103
- return true;
104
- }
105
-
106
- bool parseTimeProperty(napi_env env, napi_value object, const char *propName,
107
- int64_t *target, carrier *c) {
108
- napi_value property;
109
- c->status = napi_get_named_property(env, object, propName, &property);
110
- if (c->status != napi_ok)
111
- return false;
112
-
113
- napi_valuetype type;
114
- c->status = napi_typeof(env, property, &type);
115
- if (c->status != napi_ok)
116
- return false;
117
-
118
- if (type == napi_undefined)
119
- return true;
120
-
121
- if (type == napi_object) {
122
- bool isArray;
123
- c->status = napi_is_array(env, property, &isArray);
124
- if (c->status != napi_ok)
125
- return false;
126
- if (isArray)
127
- return parsePtpTimestampArray(env, property, target, c, propName);
128
- }
129
-
130
- return getInt64FromValue(env, property, target, c, propName);
131
- }
132
- } // namespace
133
-
134
- void sendExecute(napi_env env, void *data) {
135
- sendCarrier *c = (sendCarrier *)data;
136
-
137
- NDIlib_send_create_t NDI_send_create_desc;
138
-
139
- NDI_send_create_desc.p_ndi_name = c->name;
140
- NDI_send_create_desc.p_groups = c->groups;
141
- NDI_send_create_desc.clock_video = c->clockVideo;
142
- NDI_send_create_desc.clock_audio = c->clockAudio;
143
- c->send = NDIlib_send_create(&NDI_send_create_desc);
144
- if (!c->send) {
145
- c->status = GRANDI_SEND_CREATE_FAIL;
146
- c->errorMsg = "Failed to create NDI sender.";
147
- return;
148
- }
149
- }
150
-
151
- /* implicit destruction of NDI sender via garbage collection */
152
- void finalizeSend(napi_env env, void *data, void *hint) {
153
- /* fetch NDI sender wrapper object */
154
- napi_value obj = (napi_value)hint;
155
-
156
- /* fetch NDI sender external object */
157
- napi_value sendValue;
158
- if (napi_get_named_property(env, obj, "embedded", &sendValue) != napi_ok)
159
- return;
160
-
161
- /* ensure it was still not manually destroyed */
162
- napi_valuetype result;
163
- if (napi_typeof(env, sendValue, &result) != napi_ok)
164
- return;
165
- if (result != napi_external)
166
- return;
167
-
168
- /* fetch NDI sender native object */
169
- void *sendData;
170
- if (napi_get_value_external(env, sendValue, &sendData) != napi_ok)
171
- return;
172
- NDIlib_send_instance_t send = (NDIlib_send_instance_t)sendData;
173
-
174
- /* call the NDI API */
175
- NDIlib_send_destroy(send);
176
- }
177
-
178
- /* explicit destruction of NDI sender via "destroy" method */
179
- napi_value destroySend(napi_env env, napi_callback_info info) {
180
- bool success = false;
181
- napi_value thisValue;
182
- size_t argc = 0;
183
- if (napi_get_cb_info(env, info, &argc, nullptr, &thisValue, nullptr) !=
184
- napi_ok)
185
- goto create_result;
186
-
187
- napi_value sendValue;
188
- if (napi_get_named_property(env, thisValue, "embedded", &sendValue) !=
189
- napi_ok)
190
- goto create_result;
191
-
192
- napi_valuetype resultType;
193
- if (napi_typeof(env, sendValue, &resultType) != napi_ok)
194
- goto create_result;
195
-
196
- if (resultType == napi_external) {
197
- void *sendData;
198
- if (napi_get_value_external(env, sendValue, &sendData) != napi_ok)
199
- goto create_result;
200
-
201
- NDIlib_send_destroy((NDIlib_send_instance_t)sendData);
202
- napi_value value;
203
- if (napi_create_int32(env, 0, &value) == napi_ok)
204
- napi_set_named_property(env, thisValue, "embedded", value);
205
- success = true;
206
- }
207
-
208
- create_result:
209
- napi_value result;
210
- if (napi_get_boolean(env, success, &result) != napi_ok) {
211
- napi_get_boolean(env, false, &result);
212
- }
213
- return result;
214
- }
215
-
216
- void sendComplete(napi_env env, napi_status asyncStatus, void *data) {
217
- sendCarrier *c = (sendCarrier *)data;
218
-
219
- if (asyncStatus != napi_ok) {
220
- c->status = asyncStatus;
221
- c->errorMsg = "Async sender creation failed to complete.";
222
- }
223
- REJECT_STATUS;
224
-
225
- napi_value result;
226
- c->status = napi_create_object(env, &result);
227
- REJECT_STATUS;
228
-
229
- napi_value embedded;
230
- c->status =
231
- napi_create_external(env, c->send, finalizeSend, result, &embedded);
232
- REJECT_STATUS;
233
- c->status = napi_set_named_property(env, result, "embedded", embedded);
234
- REJECT_STATUS;
235
-
236
- napi_value destroyFn;
237
- c->status = napi_create_function(env, "destroy", NAPI_AUTO_LENGTH,
238
- destroySend, nullptr, &destroyFn);
239
- REJECT_STATUS;
240
- c->status = napi_set_named_property(env, result, "destroy", destroyFn);
241
- REJECT_STATUS;
242
-
243
- napi_value videoFn;
244
- c->status = napi_create_function(env, "video", NAPI_AUTO_LENGTH, videoSend,
245
- nullptr, &videoFn);
246
- REJECT_STATUS;
247
- c->status = napi_set_named_property(env, result, "video", videoFn);
248
- REJECT_STATUS;
249
-
250
- napi_value audioFn;
251
- c->status = napi_create_function(env, "audio", NAPI_AUTO_LENGTH, audioSend,
252
- nullptr, &audioFn);
253
- REJECT_STATUS;
254
- c->status = napi_set_named_property(env, result, "audio", audioFn);
255
- REJECT_STATUS;
256
-
257
- napi_value metadataFn;
258
- c->status = napi_create_function(env, "metadata", NAPI_AUTO_LENGTH,
259
- metadataSend, nullptr, &metadataFn);
260
- REJECT_STATUS;
261
- c->status = napi_set_named_property(env, result, "metadata", metadataFn);
262
- REJECT_STATUS;
263
-
264
- napi_value connectionsFn;
265
- c->status = napi_create_function(env, "connections", NAPI_AUTO_LENGTH,
266
- connections, nullptr, &connectionsFn);
267
- REJECT_STATUS;
268
- c->status =
269
- napi_set_named_property(env, result, "connections", connectionsFn);
270
- REJECT_STATUS;
271
-
272
- napi_value tallyFn;
273
- c->status = napi_create_function(env, "connections", NAPI_AUTO_LENGTH, tally,
274
- nullptr, &tallyFn);
275
- REJECT_STATUS;
276
- c->status = napi_set_named_property(env, result, "tally", tallyFn);
277
- REJECT_STATUS;
278
-
279
- napi_value sourcenameFn;
280
- c->status = napi_create_function(env, "sourcename", NAPI_AUTO_LENGTH,
281
- sourcename, nullptr, &sourcenameFn);
282
- REJECT_STATUS;
283
- c->status = napi_set_named_property(env, result, "sourcename", sourcenameFn);
284
- REJECT_STATUS;
285
-
286
- napi_value name, groups, clockVideo, clockAudio;
287
- c->status = napi_create_string_utf8(env, c->name, NAPI_AUTO_LENGTH, &name);
288
- REJECT_STATUS;
289
- c->status = napi_set_named_property(env, result, "name", name);
290
- REJECT_STATUS;
291
-
292
- if (c->groups != nullptr) {
293
- c->status =
294
- napi_create_string_utf8(env, c->groups, NAPI_AUTO_LENGTH, &groups);
295
- REJECT_STATUS;
296
- c->status = napi_set_named_property(env, result, "groups", groups);
297
- REJECT_STATUS;
298
- }
299
-
300
- c->status = napi_get_boolean(env, c->clockVideo, &clockVideo);
301
- REJECT_STATUS;
302
- c->status = napi_set_named_property(env, result, "clockVideo", clockVideo);
303
- REJECT_STATUS;
304
-
305
- c->status = napi_get_boolean(env, c->clockAudio, &clockAudio);
306
- REJECT_STATUS;
307
- c->status = napi_set_named_property(env, result, "clockAudio", clockAudio);
308
- REJECT_STATUS;
309
-
310
- napi_status status;
311
- status = napi_resolve_deferred(env, c->_deferred, result);
312
- FLOATING_STATUS;
313
-
314
- tidyCarrier(env, c);
315
- }
316
-
317
- napi_value send(napi_env env, napi_callback_info info) {
318
- napi_valuetype type;
319
- sendCarrier *c = new sendCarrier;
320
-
321
- napi_value promise;
322
- c->status = napi_create_promise(env, &c->_deferred, &promise);
323
- REJECT_RETURN;
324
-
325
- size_t argc = 1;
326
- napi_value args[1];
327
- c->status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
328
- REJECT_RETURN;
329
-
330
- if (argc != (size_t)1)
331
- REJECT_ERROR_RETURN("Sender must be created with an object containing at "
332
- "least a 'name' property.",
333
- GRANDI_INVALID_ARGS);
334
-
335
- c->status = napi_typeof(env, args[0], &type);
336
- REJECT_RETURN;
337
- bool isArray;
338
- c->status = napi_is_array(env, args[0], &isArray);
339
- REJECT_RETURN;
340
- if ((type != napi_object) || isArray)
341
- REJECT_ERROR_RETURN("Single argument must be an object, not an array, "
342
- "containing at least a 'name' property.",
343
- GRANDI_INVALID_ARGS);
344
-
345
- napi_value config = args[0];
346
- napi_value name, groups, clockVideo, clockAudio;
347
-
348
- c->status = napi_get_named_property(env, config, "name", &name);
349
- REJECT_RETURN;
350
- c->status = napi_typeof(env, name, &type);
351
- REJECT_RETURN;
352
- if (type != napi_string)
353
- REJECT_ERROR_RETURN("Name property must be of type string.",
354
- GRANDI_INVALID_ARGS);
355
- size_t namel;
356
- c->status = napi_get_value_string_utf8(env, name, nullptr, 0, &namel);
357
- REJECT_RETURN;
358
- c->name = (char *)malloc(namel + 1);
359
- c->status = napi_get_value_string_utf8(env, name, c->name, namel + 1, &namel);
360
- REJECT_RETURN;
361
-
362
- c->status = napi_get_named_property(env, config, "groups", &groups);
363
- REJECT_RETURN;
364
- c->status = napi_typeof(env, groups, &type);
365
- REJECT_RETURN;
366
- if (type != napi_undefined) {
367
- if (type != napi_string)
368
- REJECT_ERROR_RETURN("Groups value must be a string when provided.",
369
- GRANDI_INVALID_ARGS);
370
- size_t groupsLen;
371
- c->status = napi_get_value_string_utf8(env, groups, nullptr, 0, &groupsLen);
372
- REJECT_RETURN;
373
- c->groups = (char *)malloc(groupsLen + 1);
374
- c->status = napi_get_value_string_utf8(env, groups, c->groups,
375
- groupsLen + 1, &groupsLen);
376
- REJECT_RETURN;
377
- }
378
-
379
- c->status = napi_get_named_property(env, config, "clockVideo", &clockVideo);
380
- REJECT_RETURN;
381
- c->status = napi_typeof(env, clockVideo, &type);
382
- REJECT_RETURN;
383
- if (type != napi_undefined) {
384
- if (type != napi_boolean)
385
- REJECT_ERROR_RETURN("ClockVideo property must be of type boolean.",
386
- GRANDI_INVALID_ARGS);
387
- c->status = napi_get_value_bool(env, clockVideo, &c->clockVideo);
388
- REJECT_RETURN;
389
- }
390
-
391
- c->status = napi_get_named_property(env, config, "clockAudio", &clockAudio);
392
- REJECT_RETURN;
393
- c->status = napi_typeof(env, clockAudio, &type);
394
- REJECT_RETURN;
395
- if (type != napi_undefined) {
396
- if (type != napi_boolean)
397
- REJECT_ERROR_RETURN("ClockAudio property must be of type boolean.",
398
- GRANDI_INVALID_ARGS);
399
- c->status = napi_get_value_bool(env, clockAudio, &c->clockAudio);
400
- REJECT_RETURN;
401
- }
402
-
403
- napi_value resource_name;
404
- c->status =
405
- napi_create_string_utf8(env, "Send", NAPI_AUTO_LENGTH, &resource_name);
406
- REJECT_RETURN;
407
- c->status = napi_create_async_work(env, NULL, resource_name, sendExecute,
408
- sendComplete, c, &c->_request);
409
- REJECT_RETURN;
410
- c->status = napi_queue_async_work(env, c->_request);
411
- REJECT_RETURN;
412
-
413
- return promise;
414
- }
415
-
416
- void videoSendExecute(napi_env env, void *data) {
417
- sendDataCarrier *c = (sendDataCarrier *)data;
418
-
419
- NDIlib_send_send_video_v2(c->send, &c->videoFrame);
420
- }
421
-
422
- void videoSendComplete(napi_env env, napi_status asyncStatus, void *data) {
423
- sendDataCarrier *c = (sendDataCarrier *)data;
424
- napi_value result;
425
- napi_status status;
426
-
427
- if (c->passthru != nullptr) {
428
- c->status = napi_delete_reference(env, c->passthru);
429
- c->passthru = nullptr;
430
- REJECT_STATUS;
431
- }
432
-
433
- if (asyncStatus != napi_ok) {
434
- c->status = asyncStatus;
435
- c->errorMsg = "Async video frame send failed to complete.";
436
- }
437
- REJECT_STATUS;
438
-
439
- c->status = napi_create_object(env, &result);
440
- REJECT_STATUS;
441
- status = napi_resolve_deferred(env, c->_deferred, result);
442
- FLOATING_STATUS;
443
-
444
- tidyCarrier(env, c);
445
- }
446
-
447
- napi_value videoSend(napi_env env, napi_callback_info info) {
448
- napi_valuetype type;
449
- sendDataCarrier *c = new sendDataCarrier;
450
-
451
- napi_value promise;
452
- c->status = napi_create_promise(env, &c->_deferred, &promise);
453
- REJECT_RETURN;
454
-
455
- size_t argc = 1;
456
- napi_value args[1];
457
- napi_value thisValue;
458
- c->status = napi_get_cb_info(env, info, &argc, args, &thisValue, nullptr);
459
- REJECT_RETURN;
460
-
461
- napi_value sendValue;
462
- c->status = napi_get_named_property(env, thisValue, "embedded", &sendValue);
463
- REJECT_RETURN;
464
- void *sendData;
465
- c->status = napi_get_value_external(env, sendValue, &sendData);
466
- c->send = (NDIlib_send_instance_t)sendData;
467
- REJECT_RETURN;
468
-
469
- if (argc >= 1) {
470
- napi_value config;
471
- config = args[0];
472
- c->status = napi_typeof(env, config, &type);
473
- REJECT_RETURN;
474
- if (type != napi_object)
475
- REJECT_ERROR_RETURN("frame must be an object", GRANDI_INVALID_ARGS);
476
-
477
- bool isArray, isBuffer;
478
- c->status = napi_is_array(env, config, &isArray);
479
- REJECT_RETURN;
480
- if (isArray)
481
- REJECT_ERROR_RETURN("Argument to video send cannot be an array.",
482
- GRANDI_INVALID_ARGS);
483
-
484
- napi_value param;
485
- c->status = napi_get_named_property(env, config, "xres", &param);
486
- REJECT_RETURN;
487
- c->status = napi_typeof(env, param, &type);
488
- REJECT_RETURN;
489
- if (type != napi_number)
490
- REJECT_ERROR_RETURN("yres value must be a number", GRANDI_INVALID_ARGS);
491
- c->status = napi_get_value_int32(env, param, &c->videoFrame.xres);
492
- REJECT_RETURN;
493
-
494
- c->status = napi_get_named_property(env, config, "yres", &param);
495
- REJECT_RETURN;
496
- c->status = napi_typeof(env, param, &type);
497
- REJECT_RETURN;
498
- if (type != napi_number)
499
- REJECT_ERROR_RETURN("yres value must be a number", GRANDI_INVALID_ARGS);
500
- c->status = napi_get_value_int32(env, param, &c->videoFrame.yres);
501
- REJECT_RETURN;
502
-
503
- c->status = napi_get_named_property(env, config, "frameRateN", &param);
504
- REJECT_RETURN;
505
- c->status = napi_typeof(env, param, &type);
506
- REJECT_RETURN;
507
- if (type != napi_number)
508
- REJECT_ERROR_RETURN("frameRateN value must be a number",
509
- GRANDI_INVALID_ARGS);
510
- c->status = napi_get_value_int32(env, param, &c->videoFrame.frame_rate_N);
511
- REJECT_RETURN;
512
-
513
- c->status = napi_get_named_property(env, config, "frameRateD", &param);
514
- REJECT_RETURN;
515
- c->status = napi_typeof(env, param, &type);
516
- REJECT_RETURN;
517
- if (type != napi_number)
518
- REJECT_ERROR_RETURN("frameRateD value must be a number",
519
- GRANDI_INVALID_ARGS);
520
- c->status = napi_get_value_int32(env, param, &c->videoFrame.frame_rate_D);
521
- REJECT_RETURN;
522
-
523
- c->status =
524
- napi_get_named_property(env, config, "pictureAspectRatio", &param);
525
- REJECT_RETURN;
526
- c->status = napi_typeof(env, param, &type);
527
- REJECT_RETURN;
528
- if (type != napi_number)
529
- REJECT_ERROR_RETURN("pictureAspectRatio value must be a number",
530
- GRANDI_INVALID_ARGS);
531
- double pictureAspectRatio;
532
- c->status = napi_get_value_double(env, param, &pictureAspectRatio);
533
- REJECT_RETURN;
534
- c->videoFrame.picture_aspect_ratio = (float)pictureAspectRatio;
535
-
536
- c->videoFrame.timecode = NDIlib_send_timecode_synthesize;
537
- if (!parseTimeProperty(env, config, "timecode", &c->videoFrame.timecode, c))
538
- REJECT_RETURN;
539
-
540
- c->videoFrame.timestamp = 0;
541
- if (!parseTimeProperty(env, config, "timestamp", &c->videoFrame.timestamp,
542
- c))
543
- REJECT_RETURN;
544
-
545
- c->frameMetadata.clear();
546
- c->videoFrame.p_metadata = nullptr;
547
- c->status = napi_get_named_property(env, config, "metadata", &param);
548
- REJECT_RETURN;
549
- c->status = napi_typeof(env, param, &type);
550
- REJECT_RETURN;
551
- if (type != napi_undefined) {
552
- if (type != napi_string)
553
- REJECT_ERROR_RETURN("metadata value must be a string",
554
- GRANDI_INVALID_ARGS);
555
- size_t metadataLen;
556
- c->status =
557
- napi_get_value_string_utf8(env, param, nullptr, 0, &metadataLen);
558
- REJECT_RETURN;
559
- c->frameMetadata.resize(metadataLen + 1);
560
- c->status = napi_get_value_string_utf8(
561
- env, param, c->frameMetadata.data(), metadataLen + 1, &metadataLen);
562
- REJECT_RETURN;
563
- c->frameMetadata.resize(metadataLen);
564
- c->videoFrame.p_metadata =
565
- c->frameMetadata.empty() ? nullptr : c->frameMetadata.c_str();
566
- }
567
-
568
- c->status = napi_get_named_property(env, config, "frameFormatType", &param);
569
- REJECT_RETURN;
570
- c->status = napi_typeof(env, param, &type);
571
- REJECT_RETURN;
572
- if (type != napi_number)
573
- REJECT_ERROR_RETURN("frameFormatType value must be a number",
574
- GRANDI_INVALID_ARGS);
575
- int32_t formatType;
576
- c->status = napi_get_value_int32(env, param, &formatType);
577
- REJECT_RETURN;
578
- // TODO: checks
579
- c->videoFrame.frame_format_type = (NDIlib_frame_format_type_e)formatType;
580
-
581
- c->status = napi_get_named_property(env, config, "lineStrideBytes", &param);
582
- REJECT_RETURN;
583
- c->status = napi_typeof(env, param, &type);
584
- REJECT_RETURN;
585
- if (type != napi_number)
586
- REJECT_ERROR_RETURN("lineStrideBytes value must be a number",
587
- GRANDI_INVALID_ARGS);
588
- c->status =
589
- napi_get_value_int32(env, param, &c->videoFrame.line_stride_in_bytes);
590
- REJECT_RETURN;
591
-
592
- napi_value videoBuffer;
593
- c->status = napi_get_named_property(env, config, "data", &videoBuffer);
594
- REJECT_RETURN;
595
- c->status = napi_is_buffer(env, videoBuffer, &isBuffer);
596
- REJECT_RETURN;
597
- if (!isBuffer)
598
- REJECT_ERROR_RETURN("data must be provided as a Node Buffer",
599
- GRANDI_INVALID_ARGS);
600
- void *data;
601
- size_t length;
602
- c->status = napi_get_buffer_info(env, videoBuffer, &data, &length);
603
- REJECT_RETURN;
604
- c->videoFrame.p_data = (uint8_t *)data;
605
- napi_ref bufferRef;
606
- c->status = napi_create_reference(env, videoBuffer, 1, &bufferRef);
607
- REJECT_RETURN;
608
- c->passthru = bufferRef;
609
- // TODO: check length
610
-
611
- c->status = napi_get_named_property(env, config, "fourCC", &param);
612
- REJECT_RETURN;
613
- c->status = napi_typeof(env, param, &type);
614
- REJECT_RETURN;
615
- if (type != napi_number)
616
- REJECT_ERROR_RETURN("fourCC value must be a number", GRANDI_INVALID_ARGS);
617
- int32_t fourCC;
618
- c->status = napi_get_value_int32(env, param, &fourCC);
619
- REJECT_RETURN;
620
- // TODO: checks
621
- c->videoFrame.FourCC = (NDIlib_FourCC_video_type_e)fourCC; // TODO
622
- } else
623
- REJECT_ERROR_RETURN("frame not provided", GRANDI_INVALID_ARGS);
624
-
625
- napi_value resource_name;
626
- c->status = napi_create_string_utf8(env, "VideoSend", NAPI_AUTO_LENGTH,
627
- &resource_name);
628
- REJECT_RETURN;
629
- c->status = napi_create_async_work(env, NULL, resource_name, videoSendExecute,
630
- videoSendComplete, c, &c->_request);
631
- REJECT_RETURN;
632
- c->status = napi_queue_async_work(env, c->_request);
633
- REJECT_RETURN;
634
-
635
- return promise;
636
- }
637
-
638
- void audioSendExecute(napi_env env, void *data) {
639
- sendDataCarrier *c = (sendDataCarrier *)data;
640
-
641
- NDIlib_send_send_audio_v3(c->send, &c->audioFrame);
642
- }
643
-
644
- void audioSendComplete(napi_env env, napi_status asyncStatus, void *data) {
645
- sendDataCarrier *c = (sendDataCarrier *)data;
646
- napi_value result;
647
- napi_status status;
648
-
649
- if (c->passthru != nullptr) {
650
- c->status = napi_delete_reference(env, c->passthru);
651
- c->passthru = nullptr;
652
- REJECT_STATUS;
653
- }
654
-
655
- if (asyncStatus != napi_ok) {
656
- c->status = asyncStatus;
657
- c->errorMsg = "Async audio frame send failed to complete.";
658
- }
659
- REJECT_STATUS;
660
-
661
- c->status = napi_create_object(env, &result);
662
- REJECT_STATUS;
663
- status = napi_resolve_deferred(env, c->_deferred, result);
664
- FLOATING_STATUS;
665
-
666
- tidyCarrier(env, c);
667
- }
668
-
669
- napi_value audioSend(napi_env env, napi_callback_info info) {
670
- napi_valuetype type;
671
- sendDataCarrier *c = new sendDataCarrier;
672
-
673
- napi_value promise;
674
- c->status = napi_create_promise(env, &c->_deferred, &promise);
675
- REJECT_RETURN;
676
-
677
- size_t argc = 1;
678
- napi_value args[1];
679
- napi_value thisValue;
680
- c->status = napi_get_cb_info(env, info, &argc, args, &thisValue, nullptr);
681
- REJECT_RETURN;
682
-
683
- napi_value sendValue;
684
- c->status = napi_get_named_property(env, thisValue, "embedded", &sendValue);
685
- REJECT_RETURN;
686
- void *sendData;
687
- c->status = napi_get_value_external(env, sendValue, &sendData);
688
- c->send = (NDIlib_send_instance_t)sendData;
689
- REJECT_RETURN;
690
-
691
- if (argc >= 1) {
692
- napi_value config;
693
- config = args[0];
694
- c->status = napi_typeof(env, config, &type);
695
- REJECT_RETURN;
696
- if (type != napi_object)
697
- REJECT_ERROR_RETURN("frame must be an object", GRANDI_INVALID_ARGS);
698
-
699
- bool isArray, isBuffer;
700
- c->status = napi_is_array(env, config, &isArray);
701
- REJECT_RETURN;
702
- if (isArray)
703
- REJECT_ERROR_RETURN("Argument to audio send cannot be an array.",
704
- GRANDI_INVALID_ARGS);
705
-
706
- napi_value param;
707
- c->status = napi_get_named_property(env, config, "sampleRate", &param);
708
- REJECT_RETURN;
709
- c->status = napi_typeof(env, param, &type);
710
- REJECT_RETURN;
711
- if (type != napi_number)
712
- REJECT_ERROR_RETURN("sampleRate value must be a number",
713
- GRANDI_INVALID_ARGS);
714
- c->status = napi_get_value_int32(env, param, &c->audioFrame.sample_rate);
715
- REJECT_RETURN;
716
-
717
- c->status = napi_get_named_property(env, config, "noChannels", &param);
718
- REJECT_RETURN;
719
- c->status = napi_typeof(env, param, &type);
720
- REJECT_RETURN;
721
- if (type != napi_number)
722
- REJECT_ERROR_RETURN("noChannels value must be a number",
723
- GRANDI_INVALID_ARGS);
724
- c->status = napi_get_value_int32(env, param, &c->audioFrame.no_channels);
725
- REJECT_RETURN;
726
-
727
- c->status = napi_get_named_property(env, config, "noSamples", &param);
728
- REJECT_RETURN;
729
- c->status = napi_typeof(env, param, &type);
730
- REJECT_RETURN;
731
- if (type != napi_number)
732
- REJECT_ERROR_RETURN("Samples value must be a number",
733
- GRANDI_INVALID_ARGS);
734
- c->status = napi_get_value_int32(env, param, &c->audioFrame.no_samples);
735
- REJECT_RETURN;
736
-
737
- c->audioFrame.timecode = NDIlib_send_timecode_synthesize;
738
- if (!parseTimeProperty(env, config, "timecode", &c->audioFrame.timecode, c))
739
- REJECT_RETURN;
740
-
741
- c->audioFrame.timestamp = 0;
742
- if (!parseTimeProperty(env, config, "timestamp", &c->audioFrame.timestamp,
743
- c))
744
- REJECT_RETURN;
745
-
746
- c->frameMetadata.clear();
747
- c->audioFrame.p_metadata = nullptr;
748
- c->status = napi_get_named_property(env, config, "metadata", &param);
749
- REJECT_RETURN;
750
- c->status = napi_typeof(env, param, &type);
751
- REJECT_RETURN;
752
- if (type != napi_undefined) {
753
- if (type != napi_string)
754
- REJECT_ERROR_RETURN("metadata value must be a string",
755
- GRANDI_INVALID_ARGS);
756
- size_t metadataLen;
757
- c->status =
758
- napi_get_value_string_utf8(env, param, nullptr, 0, &metadataLen);
759
- REJECT_RETURN;
760
- c->frameMetadata.resize(metadataLen + 1);
761
- c->status = napi_get_value_string_utf8(
762
- env, param, c->frameMetadata.data(), metadataLen + 1, &metadataLen);
763
- REJECT_RETURN;
764
- c->frameMetadata.resize(metadataLen);
765
- c->audioFrame.p_metadata =
766
- c->frameMetadata.empty() ? nullptr : c->frameMetadata.c_str();
767
- }
768
-
769
- c->status =
770
- napi_get_named_property(env, config, "channelStrideBytes", &param);
771
- REJECT_RETURN;
772
- c->status = napi_typeof(env, param, &type);
773
- REJECT_RETURN;
774
- if (type != napi_number)
775
- REJECT_ERROR_RETURN("channelStrideBytes value must be a number",
776
- GRANDI_INVALID_ARGS);
777
- c->status = napi_get_value_int32(env, param,
778
- &c->audioFrame.channel_stride_in_bytes);
779
- REJECT_RETURN;
780
-
781
- napi_value audioBuffer;
782
- c->status = napi_get_named_property(env, config, "data", &audioBuffer);
783
- REJECT_RETURN;
784
- c->status = napi_is_buffer(env, audioBuffer, &isBuffer);
785
- REJECT_RETURN;
786
- if (!isBuffer)
787
- REJECT_ERROR_RETURN("data must be provided as a Node Buffer",
788
- GRANDI_INVALID_ARGS);
789
- void *data;
790
- size_t length;
791
- c->status = napi_get_buffer_info(env, audioBuffer, &data, &length);
792
- REJECT_RETURN;
793
- c->audioFrame.p_data = (uint8_t *)data;
794
- napi_ref bufferRef;
795
- c->status = napi_create_reference(env, audioBuffer, 1, &bufferRef);
796
- REJECT_RETURN;
797
- c->passthru = bufferRef;
798
-
799
- c->status = napi_get_named_property(env, config, "fourCC", &param);
800
- REJECT_RETURN;
801
- c->status = napi_typeof(env, param, &type);
802
- REJECT_RETURN;
803
- if (type != napi_number)
804
- REJECT_ERROR_RETURN("fourCC value must be a number", GRANDI_INVALID_ARGS);
805
- int32_t fourCC;
806
- c->status = napi_get_value_int32(env, param, &fourCC);
807
- REJECT_RETURN;
808
- c->audioFrame.FourCC = (NDIlib_FourCC_audio_type_e)fourCC;
809
- } else
810
- REJECT_ERROR_RETURN("frame not provided", GRANDI_INVALID_ARGS);
811
-
812
- napi_value resource_name;
813
- c->status = napi_create_string_utf8(env, "AudioSend", NAPI_AUTO_LENGTH,
814
- &resource_name);
815
- REJECT_RETURN;
816
- c->status = napi_create_async_work(env, NULL, resource_name, audioSendExecute,
817
- audioSendComplete, c, &c->_request);
818
- REJECT_RETURN;
819
- c->status = napi_queue_async_work(env, c->_request);
820
- REJECT_RETURN;
821
-
822
- return promise;
823
- }
824
-
825
- napi_value connections(napi_env env, napi_callback_info info) {
826
- napi_status status;
827
-
828
- size_t argc = 1;
829
- napi_value args[1];
830
- napi_value thisValue;
831
- status = napi_get_cb_info(env, info, &argc, args, &thisValue, nullptr);
832
- CHECK_STATUS;
833
-
834
- napi_value sendValue;
835
- status = napi_get_named_property(env, thisValue, "embedded", &sendValue);
836
- CHECK_STATUS;
837
- void *sendData;
838
- status = napi_get_value_external(env, sendValue, &sendData);
839
- CHECK_STATUS;
840
- NDIlib_send_instance_t sender = (NDIlib_send_instance_t)sendData;
841
-
842
- int conns = NDIlib_send_get_no_connections(sender, 0);
843
- napi_value result;
844
- status = napi_create_int32(env, (int32_t)conns, &result);
845
- CHECK_STATUS;
846
-
847
- return result;
848
- }
849
-
850
- napi_value tally(napi_env env, napi_callback_info info) {
851
- napi_status status;
852
-
853
- size_t argc = 1;
854
- napi_value args[1];
855
- napi_value thisValue;
856
- status = napi_get_cb_info(env, info, &argc, args, &thisValue, nullptr);
857
- CHECK_STATUS;
858
-
859
- napi_value sendValue;
860
- status = napi_get_named_property(env, thisValue, "embedded", &sendValue);
861
- CHECK_STATUS;
862
- void *sendData;
863
- status = napi_get_value_external(env, sendValue, &sendData);
864
- CHECK_STATUS;
865
- NDIlib_send_instance_t sender = (NDIlib_send_instance_t)sendData;
866
-
867
- NDIlib_tally_t tally;
868
- bool changed = NDIlib_send_get_tally(sender, &tally, 0);
869
-
870
- napi_value result;
871
- status = napi_create_object(env, &result);
872
- CHECK_STATUS;
873
-
874
- napi_value value;
875
- napi_get_boolean(env, changed, &value);
876
- status = napi_set_named_property(env, result, "changed", value);
877
- CHECK_STATUS;
878
- napi_get_boolean(env, tally.on_program, &value);
879
- status = napi_set_named_property(env, result, "on_program", value);
880
- CHECK_STATUS;
881
- napi_get_boolean(env, tally.on_preview, &value);
882
- status = napi_set_named_property(env, result, "on_preview", value);
883
- CHECK_STATUS;
884
-
885
- return result;
886
- }
887
-
888
- napi_value metadataSend(napi_env env, napi_callback_info info) {
889
- napi_status status;
890
-
891
- size_t argc = 1;
892
- napi_value args[1];
893
- napi_value thisValue;
894
- status = napi_get_cb_info(env, info, &argc, args, &thisValue, nullptr);
895
- CHECK_STATUS;
896
-
897
- if (argc != 1)
898
- NAPI_THROW_ERROR("metadata requires a single string argument.");
899
-
900
- napi_value sendValue;
901
- status = napi_get_named_property(env, thisValue, "embedded", &sendValue);
902
- CHECK_STATUS;
903
- void *sendData;
904
- status = napi_get_value_external(env, sendValue, &sendData);
905
- CHECK_STATUS;
906
- NDIlib_send_instance_t sender = (NDIlib_send_instance_t)sendData;
907
-
908
- napi_valuetype type;
909
- status = napi_typeof(env, args[0], &type);
910
- CHECK_STATUS;
911
- if (type != napi_string)
912
- NAPI_THROW_ERROR("metadata argument must be a string.");
913
-
914
- size_t length;
915
- status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &length);
916
- CHECK_STATUS;
917
- std::string metadata(length + 1, '\0');
918
- status = napi_get_value_string_utf8(env, args[0], metadata.data(), length + 1,
919
- &length);
920
- CHECK_STATUS;
921
- metadata.resize(length);
922
-
923
- NDIlib_metadata_frame_t frame;
924
- frame.length = static_cast<int>(metadata.length());
925
- frame.timecode = NDIlib_send_timecode_synthesize;
926
- frame.p_data = const_cast<char *>(metadata.c_str());
927
-
928
- NDIlib_send_send_metadata(sender, &frame);
929
-
930
- napi_value result;
931
- status = napi_get_boolean(env, true, &result);
932
- CHECK_STATUS;
933
- return result;
934
- }
935
-
936
- napi_value sourcename(napi_env env, napi_callback_info info) {
937
- napi_status status;
938
-
939
- size_t argc = 1;
940
- napi_value args[1];
941
- napi_value thisValue;
942
- status = napi_get_cb_info(env, info, &argc, args, &thisValue, nullptr);
943
- CHECK_STATUS;
944
-
945
- napi_value sendValue;
946
- status = napi_get_named_property(env, thisValue, "embedded", &sendValue);
947
- CHECK_STATUS;
948
- void *sendData;
949
- status = napi_get_value_external(env, sendValue, &sendData);
950
- CHECK_STATUS;
951
- NDIlib_send_instance_t sender = (NDIlib_send_instance_t)sendData;
952
-
953
- const NDIlib_source_t *source = NDIlib_send_get_source_name(sender);
954
- napi_value result;
955
- status = napi_create_string_utf8(env, source->p_ndi_name, NAPI_AUTO_LENGTH,
956
- &result);
957
- CHECK_STATUS;
958
-
959
- return result;
960
- }