diff --git a/binding.gyp b/binding.gyp index 4fc3358..dccb229 100644 --- a/binding.gyp +++ b/binding.gyp @@ -10,6 +10,9 @@ "xcode_settings": { "OTHER_CPLUSPLUSFLAGS": ["-std=c++14"], }, + "libraries": [ + "-lv4l2" + ], "cflags_c": ["-std=c11", "-Wunused-parameter"], "cflags_cc": ["-std=c++14"] }] diff --git a/c-examples.makefile b/c-examples.makefile index 3dad1c7..f751548 100644 --- a/c-examples.makefile +++ b/c-examples.makefile @@ -2,7 +2,7 @@ CC = gcc CFLAGS = -std=c11 -Wall -Wextra -Wunused-parameter -pedantic -LDLIBS = -ljpeg +LDLIBS = -ljpeg -lv4l2 capturesrc := capture.h capture.c srcdir := c-examples diff --git a/capture.c b/capture.c index d80be75..2383777 100644 --- a/capture.c +++ b/capture.c @@ -5,10 +5,39 @@ #include #include -#include #include #include - +#include + +typedef struct { + uint16_t width; + uint16_t height; +} frame_size_t; + +frame_size_t frame_sizes[] = { + { 640, 480 } + ,{ 800, 600 } + ,{ 1024, 768 } + ,{ 1440, 1080 } + //,{ 1920, 1440 } + ,{ 1024, 576 } + ,{ 1280, 720 } + ,{ 1920, 1080 } + //,{ 2560, 1440 } + //,{ 3840, 2160 } +}; + +uint8_t frame_intervals[] = { + 5, + 10, + 15, + 20, + 30, + //45, + //60, + //90, + //120 +}; static void log_stderr(camera_log_t type, const char* msg, void* pointer) { (void) pointer; @@ -25,12 +54,12 @@ static void log_stderr(camera_log_t type, const char* msg, void* pointer) { } } -static bool error(camera_t* camera, const char * msg) +static bool error(const camera_t* camera, const char * msg) { camera->context.log(CAMERA_ERROR, msg, camera->context.pointer); return false; } -static bool failure(camera_t* camera, const char * msg) +static bool failure(const camera_t* camera, const char * msg) { camera->context.log(CAMERA_FAIL, msg, camera->context.pointer); return false; @@ -39,7 +68,7 @@ static bool failure(camera_t* camera, const char * msg) static int xioctl(int fd, unsigned long int request, void* arg) { for (int i = 0; i < 100; i++) { - int r = ioctl(fd, request, arg); + int r = v4l2_ioctl(fd, request, arg); if (r != -1 || errno != EINTR) return r; } return -1; @@ -49,7 +78,7 @@ static int xioctl(int fd, unsigned long int request, void* arg) camera_t* camera_open(const char * device) { - int fd = open(device, O_RDWR | O_NONBLOCK, 0); + int fd = v4l2_open(device, O_RDWR | O_NONBLOCK, 0); if (fd == -1) return NULL; camera_t* camera = malloc(sizeof (camera_t)); @@ -69,7 +98,7 @@ camera_t* camera_open(const char * device) static void free_buffers(camera_t* camera, size_t count) { for (size_t i = 0; i < count; i++) { - munmap(camera->buffers[i].start, camera->buffers[i].length); + v4l2_munmap(camera->buffers[i].start, camera->buffers[i].length); } free(camera->buffers); camera->buffers = NULL; @@ -126,7 +155,7 @@ static bool camera_buffer_prepare(camera_t* camera) if (buf.length > buf_max) buf_max = buf.length; camera->buffers[i].length = buf.length; camera->buffers[i].start = - mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, + v4l2_mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camera->fd, buf.m.offset); if (camera->buffers[i].start == MAP_FAILED) { free_buffers(camera, i); @@ -213,7 +242,7 @@ bool camera_close(camera_t* camera) camera_stop(camera); } for (int i = 0; i < 10; i++) { - if (close(camera->fd) != -1) break; + if (v4l2_close(camera->fd) != -1) break; } free(camera); return true; @@ -365,59 +394,93 @@ camera_formats_t* camera_formats_new(const camera_t* camera) memset(&fmt, 0, sizeof fmt); fmt.index = i; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(camera->fd, VIDIOC_ENUM_FMT, &fmt) == -1) break; + if (v4l2_ioctl(camera->fd, VIDIOC_ENUM_FMT, &fmt) == -1) break; //printf("[%s]\n", fmt.description); for (uint32_t j = 0; ; j++) { struct v4l2_frmsizeenum frmsize; memset(&frmsize, 0, sizeof frmsize); frmsize.index = j; frmsize.pixel_format = fmt.pixelformat; - if (ioctl(camera->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == -1) break; - if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { - //printf("- w: %d, h: %d\n", - // frmsize.discrete.width, frmsize.discrete.height); - for (uint32_t k = 0; ; k++) { + if (v4l2_ioctl(camera->fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == -1) break; + if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { // discrete framesizes + for (uint32_t k = 0; ; k++) { // discrete frame sizes struct v4l2_frmivalenum frmival; memset(&frmival, 0, sizeof frmival); frmival.index = k; frmival.pixel_format = fmt.pixelformat; frmival.width = frmsize.discrete.width; frmival.height = frmsize.discrete.height; - if (ioctl(camera->fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == -1) + //printf("checking frameintervals for discrete size: %dx%dpx\n", frmival.width, frmival.height); + if (v4l2_ioctl(camera->fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == -1) { + //printf("frameintervals ioctl failed\n"); break; - if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { - //printf(" - fps: %d/%d\n", - // frmival.discrete.denominator,frmival.discrete.numerator); - ret->head = - realloc(ret->head, (ret->length + 1) * sizeof (camera_format_t)); - ret->head[ret->length].format = fmt.pixelformat; - ret->head[ret->length].width = frmsize.discrete.width; - ret->head[ret->length].height = frmsize.discrete.height; - ret->head[ret->length].interval.numerator = - frmival.discrete.numerator; - ret->head[ret->length].interval.denominator = - frmival.discrete.denominator; - ret->length++; - } else { - //printf(" - fps: %d/%d-%d/%d\n", - // frmival.stepwise.min.denominator, - // frmival.stepwise.min.numerator, - // frmival.stepwise.max.denominator, - // frmival.stepwise.max.numerator); - // TBD: when stepwize + } + if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { // discrete interval + //printf("\t- fps: %d/%d\n", + // frmival.discrete.numerator,frmival.discrete.denominator); + add_format(ret, fmt.pixelformat, frmival.width, frmival.height, frmival.discrete.numerator, frmival.discrete.denominator); + } else { // stepwise intervals + uint8_t max = frmival.stepwise.max.numerator/frmival.stepwise.max.denominator; + uint8_t min = frmival.stepwise.min.numerator/frmival.stepwise.min.denominator; + for (uint8_t i = 0; ; i++) { + if (frame_intervals[i] >= min && frame_intervals[i] <= max) { + //printf("\t- fps: %d\n",frame_intervals[i]); + add_format(ret, fmt.pixelformat, frmsize.discrete.width, frmsize.discrete.height, frame_intervals[i], 1 /* denominator */); + } + } } } } else { - //printf("- w: %d-%d, h: %d-%d\n", - // frmsize.stepwise.min_width, frmsize.stepwise.max_width, - // frmsize.stepwise.min_height, frmsize.stepwise.max_height); - // TBD: when stepwize + for (uint8_t l = 0; l < sizeof(frame_sizes) / sizeof(frame_size_t); l++) { // step wise frame sizes + //printf("checking frameintervals for stepwise %dx%dpx\n", frame_sizes[l].width, frame_sizes[l].height); + for (uint32_t k = 0; ; k++) { // step wise frame intervals + struct v4l2_frmivalenum frmival; + memset(&frmival, 0, sizeof frmival); + frmival.index = k; + frmival.pixel_format = fmt.pixelformat; + frmival.width = frame_sizes[l].width; + frmival.height = frame_sizes[l].height; + if (v4l2_ioctl(camera->fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) != -1) { + if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { + //printf("\t- fps: %d/%d\n", + // frmival.discrete.numerator,frmival.discrete.denominator); + add_format(ret, fmt.pixelformat, frmival.width, frmival.height, frmival.discrete.numerator, frmival.discrete.denominator); + } else { + uint8_t min = frmival.stepwise.max.denominator/frmival.stepwise.max.numerator; + uint8_t max = frmival.stepwise.min.denominator/frmival.stepwise.min.numerator; + for (uint8_t m = 0; m < sizeof(frame_intervals) / sizeof(uint8_t); m++) { + if (frame_intervals[m] >= min && frame_intervals[m] <= max) { + //printf("\t- fps: %d\n",frame_intervals[i]); + add_format(ret, fmt.pixelformat, frmival.width, frmival.height, 1, frame_intervals[m] /* denominator */); + } + } + break; + } + } else { + //printf("frameintervals ioctl failed\n"); + break; + } + } + } } } - } + return ret; } + +void add_format(camera_formats_t* ret, uint32_t format, uint32_t width, uint32_t height, uint32_t numerator, uint32_t denominator) +{ + //printf("\tadding format: %d w:%d h:%d n:%d d:%d\n", format, width, height, numerator, denominator); + ret->head = realloc(ret->head, (ret->length + 1) * sizeof (camera_format_t)); + ret->head[ret->length].format = format; + ret->head[ret->length].width = width; + ret->head[ret->length].height = height; + ret->head[ret->length].interval.numerator = numerator; + ret->head[ret->length].interval.denominator = denominator; + ret->length++; +} + void camera_formats_delete(camera_formats_t* formats) { free(formats->head); @@ -462,8 +525,10 @@ camera_controls_menus(const camera_t* camera, camera_control_t* control) memset(&qmenu, 0, sizeof qmenu); qmenu.id = control->id; qmenu.index = mindex; - if (ioctl(camera->fd, VIDIOC_QUERYMENU, &qmenu) == 0) { + if (v4l2_ioctl(camera->fd, VIDIOC_QUERYMENU, &qmenu) == 0) { copy(&control->menus.head[mindex], &qmenu); + } else { + error(camera, "VIDIOC_QUERYMENU"); } } } @@ -471,12 +536,11 @@ static camera_control_t* camera_controls_query(const camera_t* camera, camera_control_t* control_list) { camera_control_t* control_list_last = control_list; + struct v4l2_queryctrl qctrl; - for (uint32_t cid = V4L2_CID_USER_BASE; cid < V4L2_CID_LASTP1; cid++) { - struct v4l2_queryctrl qctrl; - memset(&qctrl, 0, sizeof qctrl); - qctrl.id = cid; - if (ioctl(camera->fd, VIDIOC_QUERYCTRL, &qctrl) == -1) continue; + for (qctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL; + v4l2_ioctl(camera->fd, VIDIOC_QUERYCTRL, &qctrl) == 0; + qctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL) { camera_control_t* control = control_list_last++; control->id = qctrl.id; memcpy(control->name, qctrl.name, sizeof qctrl.name); @@ -500,7 +564,7 @@ camera_controls_query(const camera_t* camera, camera_control_t* control_list) } camera_controls_t* camera_controls_new(const camera_t* camera) { - camera_control_t control_list[V4L2_CID_LASTP1 - V4L2_CID_USER_BASE]; + camera_control_t control_list[V4L2_CID_MAX_CTRLS]; // It can't be a higher qty :P camera_control_t* control_list_last = camera_controls_query(camera, control_list); camera_controls_t* controls = malloc(sizeof (camera_controls_t)); @@ -522,11 +586,22 @@ void camera_controls_delete(camera_controls_t* controls) bool camera_control_get(camera_t* camera, uint32_t id, int32_t* value) { + int ioctl_result; struct v4l2_control ctrl; ctrl.id = id; ctrl.value = 0; - if (ioctl(camera->fd, VIDIOC_G_CTRL, &ctrl) == -1) + + for(int i = 0; i < 100; i++) { + ioctl_result = v4l2_ioctl(camera->fd, VIDIOC_G_CTRL, &ctrl); + if (ioctl_result != -1) + break; + else if(errno != EIO) + return error(camera, "VIDIOC_G_CTRL"); + } + + if(ioctl_result == -1) return error(camera, "VIDIOC_G_CTRL"); + *value = ctrl.value; return true; } @@ -536,7 +611,7 @@ bool camera_control_set(camera_t* camera, uint32_t id, int32_t value) struct v4l2_control ctrl; ctrl.id = id; ctrl.value = value; - if (ioctl(camera->fd, VIDIOC_S_CTRL, &ctrl) == -1) + if (v4l2_ioctl(camera->fd, VIDIOC_S_CTRL, &ctrl) == -1) return error(camera, "VIDIOC_S_CTRL"); return true; } diff --git a/capture.h b/capture.h index 31188bb..deaf79b 100644 --- a/capture.h +++ b/capture.h @@ -1,6 +1,7 @@ #ifndef CAMERA_H #define CAMERA_H +#include #include #include #include @@ -74,6 +75,7 @@ camera_formats_t* camera_formats_new(const camera_t* camera); void camera_formats_delete(camera_formats_t* formats); bool camera_config_get(camera_t* camera, camera_format_t* format); bool camera_config_set(camera_t* camera, const camera_format_t* format); +void add_format(camera_formats_t* ret, uint32_t format, uint32_t width, uint32_t height, uint32_t numerator, uint32_t denominator); typedef enum { diff --git a/readme.md b/readme.md index 8aaa5f2..9767d60 100644 --- a/readme.md +++ b/readme.md @@ -71,10 +71,8 @@ Capturing API (frame access) - `cam.frameRaw()`: Get the cached raw frame as `Uint8Array` (YUYU frame is array of YUYV..., MJPG frame is single JPEG compressed data) -- `cam.toYUYV()`: Get the cached frame as `Uint8Array` of pixels YUYVYUYV... - (will be deprecated method) -- `cam.toRGB()`: Get the cached frame as `Uint8Array` of pixels RGBRGB... - (will be deprecated method) +- `cam.reloadControls()`: Reload the `cam.controls` array + (use if you need to update the flags) Capturing API (camera frame info) diff --git a/v4l2camera.cc b/v4l2camera.cc index 6beb6dd..6bdf062 100644 --- a/v4l2camera.cc +++ b/v4l2camera.cc @@ -10,12 +10,12 @@ namespace { - + struct CallbackData { Nan::Persistent thisObj; std::unique_ptr callback; }; - + class Camera : public Nan::ObjectWrap { public: static NAN_MODULE_INIT(Init); @@ -30,20 +30,21 @@ namespace { static NAN_METHOD(ConfigSet); static NAN_METHOD(ControlGet); static NAN_METHOD(ControlSet); - + static NAN_METHOD(ReloadControls); + static void StopCB(uv_poll_t* handle, int status, int events); static void CaptureCB(uv_poll_t* handle, int status, int events); - + static void WatchCB(uv_poll_t* handle, void (*callbackCall)(CallbackData* data)); static void Watch(const Nan::FunctionCallbackInfo& info, uv_poll_cb cb); - + Camera(); ~Camera(); camera_t* camera; }; - + //[error message handling] struct LogContext { std::string msg; @@ -63,13 +64,13 @@ namespace { } static_cast(pointer)->msg = ss.str(); } - + static inline v8::Local cameraError(const camera_t* camera) { const auto ctx = static_cast(camera->context.pointer); return Nan::Error(ctx->msg.c_str()); } - - + + //[helpers] static inline v8::Local getValue(const v8::Local& self, const char* name) { @@ -83,7 +84,7 @@ namespace { getUint(const v8::Local& self, const char* name) { return Nan::To(getValue(self, name)).FromJust(); } - + static inline void setValue(const v8::Local& self, const char* name, const v8::Local& value) { @@ -108,7 +109,7 @@ namespace { setBool(const v8::Local& self, const char* name, bool value) { setValue(self, name, Nan::New(value)); } - + //[callback helpers] void Camera::WatchCB(uv_poll_t* handle, void (*callbackCall)(CallbackData* data)) { @@ -117,7 +118,7 @@ namespace { uv_poll_stop(handle); uv_close(reinterpret_cast(handle), [](uv_handle_t* handle) -> void {delete handle;}); - + callbackCall(data); data->thisObj.Reset(); delete data; @@ -128,27 +129,28 @@ namespace { data->thisObj.Reset(info.Holder()); data->callback.reset(new Nan::Callback(info[0].As())); auto camera = Nan::ObjectWrap::Unwrap(info.Holder())->camera; - + auto handle = new uv_poll_t; handle->data = data; uv_poll_init(uv_default_loop(), handle, camera->fd); uv_poll_start(handle, UV_READABLE, cb); } - + //[methods] - + static const char* control_type_names[] = { "invalid", "int", "bool", "menu", + "button", "int64", - "class" + "class", "string", "bitmask", "int_menu", }; - + static v8::Local cameraControls(const camera_t* camera) { auto ccontrols = camera_controls_new(camera); auto controls = Nan::New(ccontrols->length); @@ -166,7 +168,7 @@ namespace { setInt(control, "max", ccontrol->max); setInt(control, "step", ccontrol->step); setInt(control, "default", ccontrol->default_value); - + auto flags = Nan::New(); setValue(control, "flags", flags); setBool(flags, "disabled", ccontrol->flags.disabled); @@ -177,7 +179,7 @@ namespace { setBool(flags, "slider", ccontrol->flags.slider); setBool(flags, "writeOnly", ccontrol->flags.write_only); setBool(flags, "volatile", ccontrol->flags.volatile_value); - + auto menu = Nan::New(ccontrol->menus.length); setValue(control, "menu", menu); switch (ccontrol->type) { @@ -220,7 +222,7 @@ namespace { pixformat, width, height, {numerator, denominator} }; } - + static v8::Local convertFormat(const camera_format_t* cformat) { char name[5]; camera_format_name(cformat->format, name); @@ -235,7 +237,7 @@ namespace { setUint(interval, "denominator", cformat->interval.denominator); return format; } - + static v8::Local cameraFormats(const camera_t* camera) { auto cformats = camera_formats_new(camera); auto formats = Nan::New(cformats->length); @@ -246,13 +248,13 @@ namespace { } return formats; } - + NAN_METHOD(Camera::New) { if (!info.IsConstructCall()) { // [NOTE] generic recursive call with `new` std::vector> args(info.Length()); for (auto i = std::size_t{0}; i < args.size(); ++i) args[i] = info[i]; - auto inst = Nan::NewInstance(info.Callee(), args.size(), args.data()); + auto inst = Nan::NewInstance(v8::Local::Cast(info.Data()), args.size(), args.data()); if (!inst.IsEmpty()) info.GetReturnValue().Set(inst.ToLocalChecked()); return; } @@ -269,7 +271,7 @@ namespace { } camera->context.pointer = new LogContext; camera->context.log = &logRecord; - + auto thisObj = info.This(); auto self = new Camera; self->camera = camera; @@ -278,7 +280,7 @@ namespace { setValue(thisObj, "formats", cameraFormats(camera)); setValue(thisObj, "controls", cameraControls(camera)); } - + NAN_METHOD(Camera::Start) { auto thisObj = info.Holder(); auto camera = Nan::ObjectWrap::Unwrap(thisObj)->camera; @@ -291,7 +293,6 @@ namespace { info.GetReturnValue().Set(thisObj); } - void Camera::StopCB(uv_poll_t* handle, int /*status*/, int /*events*/) { auto callCallback = [](CallbackData* data) -> void { Nan::HandleScope scope; @@ -300,6 +301,7 @@ namespace { }; WatchCB(handle, callCallback); } + NAN_METHOD(Camera::Stop) { auto camera = Nan::ObjectWrap::Unwrap(info.Holder())->camera; if (!camera_stop(camera)) { @@ -308,7 +310,7 @@ namespace { } Watch(info, StopCB); } - + void Camera::CaptureCB(uv_poll_t* handle, int /*status*/, int /*events*/) { auto callCallback = [](CallbackData* data) -> void { Nan::HandleScope scope; @@ -324,30 +326,15 @@ namespace { Watch(info, CaptureCB); } - NAN_METHOD(Camera::FrameRaw) { const auto camera = Nan::ObjectWrap::Unwrap(info.Holder())->camera; const auto size = camera->head.length; - auto data = new uint8_t[size]; - std::copy(camera->head.start, camera->head.start + size, data); - const auto flag = v8::ArrayBufferCreationMode::kInternalized; - auto buf = v8::ArrayBuffer::New(info.GetIsolate(), data, size, flag); + auto buf = v8::ArrayBuffer::New(info.GetIsolate(), size); + std::memmove(buf->GetContents().Data(), camera->head.start, size); auto array = v8::Uint8Array::New(buf, 0, size); info.GetReturnValue().Set(array); } - - NAN_METHOD(Camera::FrameYUYVToRGB) { - // TBD: check the current format as YUYV - const auto camera = Nan::ObjectWrap::Unwrap(info.Holder())->camera; - auto rgb = yuyv2rgb(camera->head.start, camera->width, camera->height); - const auto size = camera->width * camera->height * 3; - const auto flag = v8::ArrayBufferCreationMode::kInternalized; - auto buf = v8::ArrayBuffer::New(info.GetIsolate(), rgb, size, flag); - auto array = v8::Uint8Array::New(buf, 0, size); - info.GetReturnValue().Set(array); - } - - + NAN_METHOD(Camera::ConfigGet) { const auto camera = Nan::ObjectWrap::Unwrap(info.Holder())->camera; camera_format_t cformat; @@ -358,7 +345,7 @@ namespace { auto format = convertFormat(&cformat); info.GetReturnValue().Set(format); } - + NAN_METHOD(Camera::ConfigSet) { if (info.Length() < 1) { Nan::ThrowTypeError("argument required: config"); @@ -375,7 +362,7 @@ namespace { setUint(thisObj, "height", camera->height); info.GetReturnValue().Set(thisObj); } - + NAN_METHOD(Camera::ControlGet) { if (info.Length() < 1) { Nan::ThrowTypeError("an argument required: id"); @@ -391,7 +378,7 @@ namespace { } info.GetReturnValue().Set(Nan::New(value)); } - + NAN_METHOD(Camera::ControlSet) { if (info.Length() < 2) { Nan::ThrowTypeError("arguments required: id, value"); @@ -408,8 +395,16 @@ namespace { } info.GetReturnValue().Set(thisObj); } - - + + NAN_METHOD(Camera::ReloadControls) { + auto thisObj = info.Holder(); + auto camera = Nan::ObjectWrap::Unwrap(thisObj)->camera; + auto newControls = cameraControls(camera); + setValue(thisObj, "controls", newControls); + + info.GetReturnValue().Set(newControls); + } + Camera::Camera() : camera(nullptr) {} Camera::~Camera() { if (camera) { @@ -418,8 +413,7 @@ namespace { delete ctx; } } - - + //[module init] NAN_MODULE_INIT(Camera::Init) { const auto name = Nan::New("Camera").ToLocalChecked(); @@ -427,17 +421,16 @@ namespace { auto ctorInst = ctor->InstanceTemplate(); ctor->SetClassName(name); ctorInst->SetInternalFieldCount(1); - + Nan::SetPrototypeMethod(ctor, "start", Start); Nan::SetPrototypeMethod(ctor, "stop", Stop); Nan::SetPrototypeMethod(ctor, "capture", Capture); Nan::SetPrototypeMethod(ctor, "frameRaw", FrameRaw); - Nan::SetPrototypeMethod(ctor, "toYUYV", FrameRaw); - Nan::SetPrototypeMethod(ctor, "toRGB", FrameYUYVToRGB); Nan::SetPrototypeMethod(ctor, "configGet", ConfigGet); Nan::SetPrototypeMethod(ctor, "configSet", ConfigSet); Nan::SetPrototypeMethod(ctor, "controlGet", ControlGet); Nan::SetPrototypeMethod(ctor, "controlSet", ControlSet); + Nan::SetPrototypeMethod(ctor, "reloadControls", ReloadControls); Nan::Set(target, name, Nan::GetFunction(ctor).ToLocalChecked()); } }