Skip to content

Commit 93c76db

Browse files
authored
Merge pull request #9 from pandres95:develop
Feature: Find API + Recv/SetTally
2 parents f69b19d + 362f68b commit 93c76db

17 files changed

+261
-15
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,25 @@ This library intends to be a Node.js wrapper for NDI v5 (and following) library.
66

77
## Features
88

9+
- Find API
10+
- [x] Listing Sources
911
- Send API
1012
- [x] Creating Send Instance
1113
- [x] Sending Video Streams
1214
- [x] Sending Audio Streams
1315
- [x] Sending Audio/Video Streams
16+
- Recv API
17+
- [x] Setting Tally
1418

1519
## Roadmap
1620

17-
- Find API
18-
- [ ] Listing Sources
1921
- Send API
2022
- [ ] Sending Metadata
2123
- [ ] Receiving Tally
2224
- Recv API
2325
- [ ] Receiving Video Streams
2426
- [ ] Receiving Audio Streams
2527
- [ ] Receiving Metadata
26-
- [ ] Setting Tally
2728

2829
## Installation
2930

binding.gyp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
"cflags_cc+": ["-fexceptions"],
99
"sources": [
1010
"src/ndi.cpp",
11+
"src/find/find_sources.cpp",
12+
"src/find/source_instance.cpp",
1113
"src/send/send_create.cpp",
1214
"src/send/send_instance.cpp",
1315
"src/structures/audio_frame.cpp",
16+
"src/structures/tally_state.cpp",
1417
"src/structures/video_frame.cpp",
1518
],
1619
"include_dirs": [

include/find/find_sources.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import type { SourceInstance } from './source_instance';
2+
3+
export function findSources (timeout?: number): Promise<SourceInstance[]>;

include/find/find_sources.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <napi.h>
2+
3+
#ifndef _SRC_FIND_FIND_SOURCES_H_
4+
#define _SRC_FIND_FIND_SOURCES_H_
5+
6+
Napi::Value FindSources(const Napi::CallbackInfo&);
7+
8+
#endif

include/find/source_instance.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { TallyState } from '../structures/tally_state';
2+
3+
export interface SourceInstance {
4+
setTally (tallyState: TallyState): void;
5+
6+
get ipAddress(): string;
7+
get name(): string;
8+
get urlAddress(): string;
9+
}

include/find/source_instance.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <napi.h>
2+
#include <Processing.NDI.Lib.h>
3+
4+
#ifndef _SRC_STRUCTURES_SOURCE_INSTANCE_H_
5+
#define _SRC_STRUCTURES_SOURCE_INSTANCE_H_
6+
7+
class SourceInstance : public Napi::ObjectWrap<SourceInstance> {
8+
public:
9+
static void Init(Napi::Env env, Napi::Object exports);
10+
static Napi::Object New(Napi::Env env, NDIlib_source_t *ndiSourceInstance);
11+
static Napi::FunctionReference constructor;
12+
13+
SourceInstance(const Napi::CallbackInfo &info);
14+
~SourceInstance();
15+
16+
void SetTally(const Napi::CallbackInfo &info);
17+
18+
Napi::Value GetIpAddress(const Napi::CallbackInfo &info);
19+
Napi::Value GetName(const Napi::CallbackInfo &info);
20+
Napi::Value GetUrlAddress(const Napi::CallbackInfo &info);
21+
22+
23+
private:
24+
void Initialize(const Napi::CallbackInfo &info);
25+
26+
NDIlib_source_t ndiSourceInstance;
27+
NDIlib_recv_instance_t ndiRecvInstance;
28+
};
29+
30+
#endif

include/ndi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include <Processing.NDI.Lib.h>
33

44
#include <send/send_instance.h>
5+
#include <find/find_sources.h>
6+
#include <find/source_instance.h>
57

68
static Napi::Object Init(Napi::Env env, Napi::Object exports);
79
static void onDestroyEnvironment(void *arg);

include/structures/tally_state.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export interface TallyState {
2+
/**
3+
* Whether a source should be marked as "onProgram", which can be understood as "LIVE", "Recording", etc.
4+
*/
5+
onProgram: boolean;
6+
7+
/**
8+
* Whether a source should be marked as "onPreview", which indicates that e.g the source is being previewed by a monitor.
9+
*/
10+
onPreview: boolean;
11+
}

include/structures/tally_state.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <napi.h>
2+
#include <Processing.NDI.Lib.h>
3+
4+
#ifndef _SRC_STRUCTURES_TALLY_STATE_H_
5+
#define _SRC_STRUCTURES_TALLY_STATE_H_
6+
7+
class TallyState {
8+
public:
9+
TallyState(const Napi::Object &);
10+
11+
bool onProgram;
12+
bool onPreview;
13+
14+
operator NDIlib_tally_t() const;
15+
};
16+
17+
#endif

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ndi.js",
3-
"version": "1.0.5",
3+
"version": "1.1.0",
44
"description": "Wrapper library for NDI",
55
"type": "module",
66
"main": "src/index.js",
@@ -42,7 +42,7 @@
4242
"dotenv": "^10.0.0",
4343
"fs-extra": "^10.0.0",
4444
"jest": "^27.3.1",
45-
"node-addon-api": "^4.2.0",
45+
"node-addon-api": "^5.0.0",
4646
"node-gyp": "^8.4.1",
4747
"zx": "^4.3.0"
4848
},

src/find/find_sources.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <find/find_sources.h>
2+
#include <find/source_instance.h>
3+
#include <iostream>
4+
5+
Napi::Value FindSources(const Napi::CallbackInfo &info) {
6+
NDIlib_find_create_t NDI_find_create_desc;
7+
NDIlib_find_instance_t pNDI_find =
8+
NDIlib_find_create_v2(&NDI_find_create_desc);
9+
10+
uint32_t timeout_in_ms = 5000;
11+
if (info.Length() == 1) {
12+
timeout_in_ms = info[0].As<Napi::Number>();
13+
}
14+
15+
if (!NDIlib_find_wait_for_sources(pNDI_find, timeout_in_ms)) {
16+
return Napi::Array::New(info.Env());
17+
} else {
18+
// Get the updated list of sources
19+
uint32_t no_sources = 0;
20+
const NDIlib_source_t *p_sources =
21+
NDIlib_find_get_current_sources(pNDI_find, &no_sources);
22+
23+
// Display all the sources.
24+
Napi::Array sourcesList = Napi::Array::New(info.Env(), (size_t)no_sources);
25+
26+
for (uint32_t i = 0; i < no_sources; i++) {
27+
NDIlib_source_t p_source = NDIlib_source_t(p_sources[i]);
28+
29+
Napi::Object sourceInstance = SourceInstance::New(info.Env(), &p_source);
30+
sourcesList[i] = sourceInstance;
31+
}
32+
33+
NDIlib_find_destroy(pNDI_find);
34+
35+
return sourcesList;
36+
}
37+
}

src/find/source_instance.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <find/source_instance.h>
2+
#include <iostream>
3+
#include <structures/tally_state.h>
4+
5+
using namespace std;
6+
7+
Napi::Object SourceInstance::New(Napi::Env env,
8+
NDIlib_source_t *ndiSourceInstance) {
9+
return SourceInstance::constructor.Value().New({
10+
Napi::External<NDIlib_source_t>::New(env, ndiSourceInstance),
11+
});
12+
}
13+
14+
SourceInstance::SourceInstance(const Napi::CallbackInfo &info)
15+
: Napi::ObjectWrap<SourceInstance>(info) {
16+
Napi::Env env = info.Env();
17+
18+
if (info.Length() < 1 || (info[0].Type() != napi_external)) {
19+
throw Napi::Error::New(
20+
env, "SourceInstances can only be constructed by native code");
21+
}
22+
23+
NDIlib_source_t ndiSourceInstance =
24+
*(info[0].As<Napi::External<NDIlib_source_t> >().Data());
25+
26+
string str_ndi_name = ndiSourceInstance.p_ndi_name;
27+
char *cstr_ndi_name = new char[str_ndi_name.length()];
28+
strcpy(cstr_ndi_name, str_ndi_name.c_str());
29+
30+
string str_url_address = ndiSourceInstance.p_url_address;
31+
char *cstr_url_address = new char[str_url_address.length()];
32+
strcpy(cstr_url_address, str_url_address.c_str());
33+
34+
this->ndiSourceInstance = NDIlib_source_t(cstr_ndi_name, cstr_url_address);
35+
36+
this->Initialize(info);
37+
}
38+
39+
void SourceInstance::Initialize(const Napi::CallbackInfo &info) {
40+
NDIlib_recv_create_v3_t NDI_recv_create_desc(this->ndiSourceInstance);
41+
42+
try {
43+
this->ndiRecvInstance = NDIlib_recv_create_v3(&NDI_recv_create_desc);
44+
45+
if (!this->ndiRecvInstance) {
46+
throw Napi::Error::New(info.Env(),
47+
"Could not initialize source receiver");
48+
}
49+
} catch (const Napi::Error &error) {
50+
error.ThrowAsJavaScriptException();
51+
}
52+
}
53+
54+
void SourceInstance::SetTally(const Napi::CallbackInfo &info) {
55+
if (!info[0].IsObject()) {
56+
Napi::TypeError::New(info.Env(),
57+
"The tallyState argument is expected to be an object")
58+
.ThrowAsJavaScriptException();
59+
return;
60+
}
61+
62+
NDIlib_tally_t tally_state = (TallyState)info[0].ToObject();
63+
NDIlib_recv_set_tally(this->ndiRecvInstance, &tally_state);
64+
}
65+
66+
Napi::Value SourceInstance::GetIpAddress(const Napi::CallbackInfo &info) {
67+
return Napi::String::New(info.Env(), this->ndiSourceInstance.p_ip_address);
68+
}
69+
70+
Napi::Value SourceInstance::GetName(const Napi::CallbackInfo &info) {
71+
return Napi::String::New(info.Env(), this->ndiSourceInstance.p_ndi_name);
72+
}
73+
74+
Napi::Value SourceInstance::GetUrlAddress(const Napi::CallbackInfo &info) {
75+
return Napi::String::New(info.Env(), this->ndiSourceInstance.p_url_address);
76+
}
77+
78+
SourceInstance::~SourceInstance() {
79+
if (!this->ndiRecvInstance) {
80+
return;
81+
}
82+
83+
NDIlib_recv_destroy(this->ndiRecvInstance);
84+
}

src/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from '../include/find/find_sources';
12
export * from '../include/structures/audio_frame';
23
export * from '../include/structures/video_frame';
34
export * from '../include/send/send_create';

src/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,10 @@ const addon = bindings('ndi');
66
*/
77
const SendInstance = addon.SendInstance;
88

9-
export { SendInstance };
9+
/**
10+
* @type {import('./index').findSources}
11+
*/
12+
const findSources = addon.findSources;
13+
14+
export { SendInstance, findSources };
1015
export * from './structures/video_frame.js';

src/ndi.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ static Napi::Object Init(Napi::Env env, Napi::Object exports) {
88
}
99

1010
SendInstance::Init(env, Napi::Value(env, exports).As<Napi::Object>());
11+
SourceInstance::Init(env, Napi::Value(env, exports).As<Napi::Object>());
12+
13+
exports.Set("findSources", Napi::Function::New(env, FindSources));
1114

1215
napi_status status =
1316
napi_add_env_cleanup_hook(env, onDestroyEnvironment, exports);
@@ -28,6 +31,24 @@ void SendInstance::Init(Napi::Env env, Napi::Object exports) {
2831
exports.Set("SendInstance", func);
2932
}
3033

34+
Napi::FunctionReference SourceInstance::constructor;
35+
void SourceInstance::Init(Napi::Env env, Napi::Object exports) {
36+
// This method is used to hook the accessor and method callbacks
37+
Napi::Function func =
38+
DefineClass(env, "SourceInstance",
39+
{
40+
InstanceMethod<&SourceInstance::SetTally>("setTally"),
41+
InstanceAccessor<&SourceInstance::GetIpAddress>("ipAddress"),
42+
InstanceAccessor<&SourceInstance::GetName>("name"),
43+
InstanceAccessor<&SourceInstance::GetUrlAddress>("urlAddress"),
44+
});
45+
46+
SourceInstance::constructor = Napi::Persistent(func);
47+
SourceInstance::constructor.SuppressDestruct();
48+
49+
exports.Set("SourceInstance", func);
50+
}
51+
3152
static void onDestroyEnvironment(void *arg) { NDIlib_destroy(); }
3253

3354
NODE_API_MODULE(ndi, Init)

src/structures/tally_state.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <structures/tally_state.h>
2+
3+
TallyState::TallyState(const Napi::Object &object)
4+
: onProgram(object.Get("onProgram").ToBoolean()),
5+
onPreview(object.Get("onPreview").ToBoolean()) {}
6+
7+
TallyState::operator NDIlib_tally_t() const {
8+
NDIlib_tally_t out;
9+
10+
out.on_preview = this->onPreview;
11+
out.on_program = this->onProgram;
12+
13+
return out;
14+
}

0 commit comments

Comments
 (0)