Skip to content

Commit

Permalink
Merge pull request #9 from pandres95:develop
Browse files Browse the repository at this point in the history
Feature: Find API + Recv/SetTally
  • Loading branch information
pandres95 committed Sep 19, 2022
2 parents f69b19d + 362f68b commit 93c76db
Show file tree
Hide file tree
Showing 17 changed files with 261 additions and 15 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,25 @@ This library intends to be a Node.js wrapper for NDI v5 (and following) library.

## Features

- Find API
- [x] Listing Sources
- Send API
- [x] Creating Send Instance
- [x] Sending Video Streams
- [x] Sending Audio Streams
- [x] Sending Audio/Video Streams
- Recv API
- [x] Setting Tally

## Roadmap

- Find API
- [ ] Listing Sources
- Send API
- [ ] Sending Metadata
- [ ] Receiving Tally
- Recv API
- [ ] Receiving Video Streams
- [ ] Receiving Audio Streams
- [ ] Receiving Metadata
- [ ] Setting Tally

## Installation

Expand Down
3 changes: 3 additions & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
"cflags_cc+": ["-fexceptions"],
"sources": [
"src/ndi.cpp",
"src/find/find_sources.cpp",
"src/find/source_instance.cpp",
"src/send/send_create.cpp",
"src/send/send_instance.cpp",
"src/structures/audio_frame.cpp",
"src/structures/tally_state.cpp",
"src/structures/video_frame.cpp",
],
"include_dirs": [
Expand Down
3 changes: 3 additions & 0 deletions include/find/find_sources.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { SourceInstance } from './source_instance';

export function findSources (timeout?: number): Promise<SourceInstance[]>;
8 changes: 8 additions & 0 deletions include/find/find_sources.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <napi.h>

#ifndef _SRC_FIND_FIND_SOURCES_H_
#define _SRC_FIND_FIND_SOURCES_H_

Napi::Value FindSources(const Napi::CallbackInfo&);

#endif
9 changes: 9 additions & 0 deletions include/find/source_instance.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { TallyState } from '../structures/tally_state';

export interface SourceInstance {
setTally (tallyState: TallyState): void;

get ipAddress(): string;
get name(): string;
get urlAddress(): string;
}
30 changes: 30 additions & 0 deletions include/find/source_instance.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include <napi.h>
#include <Processing.NDI.Lib.h>

#ifndef _SRC_STRUCTURES_SOURCE_INSTANCE_H_
#define _SRC_STRUCTURES_SOURCE_INSTANCE_H_

class SourceInstance : public Napi::ObjectWrap<SourceInstance> {
public:
static void Init(Napi::Env env, Napi::Object exports);
static Napi::Object New(Napi::Env env, NDIlib_source_t *ndiSourceInstance);
static Napi::FunctionReference constructor;

SourceInstance(const Napi::CallbackInfo &info);
~SourceInstance();

void SetTally(const Napi::CallbackInfo &info);

Napi::Value GetIpAddress(const Napi::CallbackInfo &info);
Napi::Value GetName(const Napi::CallbackInfo &info);
Napi::Value GetUrlAddress(const Napi::CallbackInfo &info);


private:
void Initialize(const Napi::CallbackInfo &info);

NDIlib_source_t ndiSourceInstance;
NDIlib_recv_instance_t ndiRecvInstance;
};

#endif
2 changes: 2 additions & 0 deletions include/ndi.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include <Processing.NDI.Lib.h>

#include <send/send_instance.h>
#include <find/find_sources.h>
#include <find/source_instance.h>

static Napi::Object Init(Napi::Env env, Napi::Object exports);
static void onDestroyEnvironment(void *arg);
11 changes: 11 additions & 0 deletions include/structures/tally_state.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface TallyState {
/**
* Whether a source should be marked as "onProgram", which can be understood as "LIVE", "Recording", etc.
*/
onProgram: boolean;

/**
* Whether a source should be marked as "onPreview", which indicates that e.g the source is being previewed by a monitor.
*/
onPreview: boolean;
}
17 changes: 17 additions & 0 deletions include/structures/tally_state.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <napi.h>
#include <Processing.NDI.Lib.h>

#ifndef _SRC_STRUCTURES_TALLY_STATE_H_
#define _SRC_STRUCTURES_TALLY_STATE_H_

class TallyState {
public:
TallyState(const Napi::Object &);

bool onProgram;
bool onPreview;

operator NDIlib_tally_t() const;
};

#endif
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ndi.js",
"version": "1.0.5",
"version": "1.1.0",
"description": "Wrapper library for NDI",
"type": "module",
"main": "src/index.js",
Expand Down Expand Up @@ -42,7 +42,7 @@
"dotenv": "^10.0.0",
"fs-extra": "^10.0.0",
"jest": "^27.3.1",
"node-addon-api": "^4.2.0",
"node-addon-api": "^5.0.0",
"node-gyp": "^8.4.1",
"zx": "^4.3.0"
},
Expand Down
37 changes: 37 additions & 0 deletions src/find/find_sources.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include <find/find_sources.h>
#include <find/source_instance.h>
#include <iostream>

Napi::Value FindSources(const Napi::CallbackInfo &info) {
NDIlib_find_create_t NDI_find_create_desc;
NDIlib_find_instance_t pNDI_find =
NDIlib_find_create_v2(&NDI_find_create_desc);

uint32_t timeout_in_ms = 5000;
if (info.Length() == 1) {
timeout_in_ms = info[0].As<Napi::Number>();
}

if (!NDIlib_find_wait_for_sources(pNDI_find, timeout_in_ms)) {
return Napi::Array::New(info.Env());
} else {
// Get the updated list of sources
uint32_t no_sources = 0;
const NDIlib_source_t *p_sources =
NDIlib_find_get_current_sources(pNDI_find, &no_sources);

// Display all the sources.
Napi::Array sourcesList = Napi::Array::New(info.Env(), (size_t)no_sources);

for (uint32_t i = 0; i < no_sources; i++) {
NDIlib_source_t p_source = NDIlib_source_t(p_sources[i]);

Napi::Object sourceInstance = SourceInstance::New(info.Env(), &p_source);
sourcesList[i] = sourceInstance;
}

NDIlib_find_destroy(pNDI_find);

return sourcesList;
}
}
84 changes: 84 additions & 0 deletions src/find/source_instance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#include <find/source_instance.h>
#include <iostream>
#include <structures/tally_state.h>

using namespace std;

Napi::Object SourceInstance::New(Napi::Env env,
NDIlib_source_t *ndiSourceInstance) {
return SourceInstance::constructor.Value().New({
Napi::External<NDIlib_source_t>::New(env, ndiSourceInstance),
});
}

SourceInstance::SourceInstance(const Napi::CallbackInfo &info)
: Napi::ObjectWrap<SourceInstance>(info) {
Napi::Env env = info.Env();

if (info.Length() < 1 || (info[0].Type() != napi_external)) {
throw Napi::Error::New(
env, "SourceInstances can only be constructed by native code");
}

NDIlib_source_t ndiSourceInstance =
*(info[0].As<Napi::External<NDIlib_source_t> >().Data());

string str_ndi_name = ndiSourceInstance.p_ndi_name;
char *cstr_ndi_name = new char[str_ndi_name.length()];
strcpy(cstr_ndi_name, str_ndi_name.c_str());

string str_url_address = ndiSourceInstance.p_url_address;
char *cstr_url_address = new char[str_url_address.length()];
strcpy(cstr_url_address, str_url_address.c_str());

this->ndiSourceInstance = NDIlib_source_t(cstr_ndi_name, cstr_url_address);

this->Initialize(info);
}

void SourceInstance::Initialize(const Napi::CallbackInfo &info) {
NDIlib_recv_create_v3_t NDI_recv_create_desc(this->ndiSourceInstance);

try {
this->ndiRecvInstance = NDIlib_recv_create_v3(&NDI_recv_create_desc);

if (!this->ndiRecvInstance) {
throw Napi::Error::New(info.Env(),
"Could not initialize source receiver");
}
} catch (const Napi::Error &error) {
error.ThrowAsJavaScriptException();
}
}

void SourceInstance::SetTally(const Napi::CallbackInfo &info) {
if (!info[0].IsObject()) {
Napi::TypeError::New(info.Env(),
"The tallyState argument is expected to be an object")
.ThrowAsJavaScriptException();
return;
}

NDIlib_tally_t tally_state = (TallyState)info[0].ToObject();
NDIlib_recv_set_tally(this->ndiRecvInstance, &tally_state);
}

Napi::Value SourceInstance::GetIpAddress(const Napi::CallbackInfo &info) {
return Napi::String::New(info.Env(), this->ndiSourceInstance.p_ip_address);
}

Napi::Value SourceInstance::GetName(const Napi::CallbackInfo &info) {
return Napi::String::New(info.Env(), this->ndiSourceInstance.p_ndi_name);
}

Napi::Value SourceInstance::GetUrlAddress(const Napi::CallbackInfo &info) {
return Napi::String::New(info.Env(), this->ndiSourceInstance.p_url_address);
}

SourceInstance::~SourceInstance() {
if (!this->ndiRecvInstance) {
return;
}

NDIlib_recv_destroy(this->ndiRecvInstance);
}
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from '../include/find/find_sources';
export * from '../include/structures/audio_frame';
export * from '../include/structures/video_frame';
export * from '../include/send/send_create';
Expand Down
7 changes: 6 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ const addon = bindings('ndi');
*/
const SendInstance = addon.SendInstance;

export { SendInstance };
/**
* @type {import('./index').findSources}
*/
const findSources = addon.findSources;

export { SendInstance, findSources };
export * from './structures/video_frame.js';
21 changes: 21 additions & 0 deletions src/ndi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ static Napi::Object Init(Napi::Env env, Napi::Object exports) {
}

SendInstance::Init(env, Napi::Value(env, exports).As<Napi::Object>());
SourceInstance::Init(env, Napi::Value(env, exports).As<Napi::Object>());

exports.Set("findSources", Napi::Function::New(env, FindSources));

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

Napi::FunctionReference SourceInstance::constructor;
void SourceInstance::Init(Napi::Env env, Napi::Object exports) {
// This method is used to hook the accessor and method callbacks
Napi::Function func =
DefineClass(env, "SourceInstance",
{
InstanceMethod<&SourceInstance::SetTally>("setTally"),
InstanceAccessor<&SourceInstance::GetIpAddress>("ipAddress"),
InstanceAccessor<&SourceInstance::GetName>("name"),
InstanceAccessor<&SourceInstance::GetUrlAddress>("urlAddress"),
});

SourceInstance::constructor = Napi::Persistent(func);
SourceInstance::constructor.SuppressDestruct();

exports.Set("SourceInstance", func);
}

static void onDestroyEnvironment(void *arg) { NDIlib_destroy(); }

NODE_API_MODULE(ndi, Init)
14 changes: 14 additions & 0 deletions src/structures/tally_state.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <structures/tally_state.h>

TallyState::TallyState(const Napi::Object &object)
: onProgram(object.Get("onProgram").ToBoolean()),
onPreview(object.Get("onPreview").ToBoolean()) {}

TallyState::operator NDIlib_tally_t() const {
NDIlib_tally_t out;

out.on_preview = this->onPreview;
out.on_program = this->onProgram;

return out;
}

0 comments on commit 93c76db

Please sign in to comment.