diff --git a/binding.gyp b/binding.gyp index 4fc3358..fe535d3 100644 --- a/binding.gyp +++ b/binding.gyp @@ -4,14 +4,20 @@ "target_name": "v4l2camera", "sources": ["capture.c", "v4l2camera.cc"], "include_dirs" : [ - "initialized = false; camera->width = 0; camera->height = 0; + camera->capabilities = 0; + camera->device_capabilities = 0; camera->buffer_count = 0; camera->buffers = NULL; camera->head.length = 0; @@ -77,12 +79,12 @@ static void free_buffers(camera_t* camera, size_t count) } static bool camera_init(camera_t* camera) { - struct v4l2_capability cap; - if (xioctl(camera->fd, VIDIOC_QUERYCAP, &cap) == -1) - return error(camera, "VIDIOC_QUERYCAP"); - if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) + if (camera->initialized) + return true; + + if (!(camera->capabilities & V4L2_CAP_VIDEO_CAPTURE)) return failure(camera, "no capture"); - if (!(cap.capabilities & V4L2_CAP_STREAMING)) + if (!(camera->capabilities & V4L2_CAP_STREAMING)) return failure(camera, "no streaming"); struct v4l2_cropcap cropcap; @@ -100,6 +102,70 @@ static bool camera_init(camera_t* camera) { return true; } +char** cap2s(unsigned cap) +{ + char **caps = calloc (sizeof(char*), 128); + int i = 0; + + if (cap & V4L2_CAP_VIDEO_CAPTURE) + caps[i++] = "Video Capture"; + if (cap & V4L2_CAP_VIDEO_CAPTURE_MPLANE) + caps[i++] = "Video Capture Multiplanar"; + if (cap & V4L2_CAP_VIDEO_OUTPUT) + caps[i++] = "Video Output"; + if (cap & V4L2_CAP_VIDEO_OUTPUT_MPLANE) + caps[i++] = "Video Output Multiplanar"; + if (cap & V4L2_CAP_VIDEO_M2M) + caps[i++] = "Video Memory-to-Memory"; + if (cap & V4L2_CAP_VIDEO_M2M_MPLANE) + caps[i++] = "Video Memory-to-Memory Multiplanar"; + if (cap & V4L2_CAP_VIDEO_OVERLAY) + caps[i++] = "Video Overlay"; + if (cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY) + caps[i++] = "Video Output Overlay"; + if (cap & V4L2_CAP_VBI_CAPTURE) + caps[i++] = "VBI Capture"; + if (cap & V4L2_CAP_VBI_OUTPUT) + caps[i++] = "VBI Output"; + if (cap & V4L2_CAP_SLICED_VBI_CAPTURE) + caps[i++] = "Sliced VBI Capture"; + if (cap & V4L2_CAP_SLICED_VBI_OUTPUT) + caps[i++] = "Sliced VBI Output"; + if (cap & V4L2_CAP_RDS_CAPTURE) + caps[i++] = "RDS Capture"; + if (cap & V4L2_CAP_RDS_OUTPUT) + caps[i++] = "RDS Output"; + if (cap & V4L2_CAP_SDR_CAPTURE) + caps[i++] = "SDR Capture"; + if (cap & V4L2_CAP_SDR_OUTPUT) + caps[i++] = "SDR Output"; + if (cap & V4L2_CAP_META_CAPTURE) + caps[i++] = "Metadata Capture"; + if (cap & V4L2_CAP_TUNER) + caps[i++] = "Tuner"; + if (cap & V4L2_CAP_TOUCH) + caps[i++] = "Touch Device"; + if (cap & V4L2_CAP_HW_FREQ_SEEK) + caps[i++] = "HW Frequency Seek"; + if (cap & V4L2_CAP_MODULATOR) + caps[i++] = "Modulator"; + if (cap & V4L2_CAP_AUDIO) + caps[i++] = "Audio"; + if (cap & V4L2_CAP_RADIO) + caps[i++] = "Radio"; + if (cap & V4L2_CAP_READWRITE) + caps[i++] = "Read/Write"; + if (cap & V4L2_CAP_ASYNCIO) + caps[i++] = "Async I/O"; + if (cap & V4L2_CAP_STREAMING) + caps[i++] = "Streaming"; + if (cap & V4L2_CAP_EXT_PIX_FORMAT) + caps[i++] = "Extended Pix Format"; + if (cap & V4L2_CAP_DEVICE_CAPS) + caps[i++] = "Device Capabilities"; + return caps; +} + static bool camera_buffer_prepare(camera_t* camera) { struct v4l2_requestbuffers req; @@ -355,6 +421,17 @@ bool camera_config_set(camera_t* camera, const camera_format_t* format) return camera_buffer_prepare(camera); } +void camera_capabilities(camera_t* camera) { + struct v4l2_capability cap; + if (xioctl(camera->fd, VIDIOC_QUERYCAP, &cap) == -1) + return error(camera, "VIDIOC_QUERYCAP"); + + camera->capabilities = cap.capabilities; + + if (cap.capabilities & V4L2_CAP_DEVICE_CAPS) + camera->device_capabilities = cap.device_caps; +} + camera_formats_t* camera_formats_new(const camera_t* camera) { camera_formats_t* ret = malloc(sizeof (camera_formats_t)); diff --git a/capture.h b/capture.h index 31188bb..3582945 100644 --- a/capture.h +++ b/capture.h @@ -1,6 +1,7 @@ #ifndef CAMERA_H #define CAMERA_H +#include #include #include #include @@ -37,6 +38,8 @@ typedef struct { uint32_t width; uint32_t height; size_t buffer_count; + uint32_t capabilities; + uint32_t device_capabilities; camera_buffer_t* buffers; camera_buffer_t head; camera_context_t context; @@ -75,6 +78,8 @@ 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); +char** cap2s(unsigned caps); +void camera_capabilities(camera_t* camera); typedef enum { CAMERA_CTRL_INTEGER = 1, diff --git a/package.json b/package.json index 93e016d..28489ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "v4l2camera", - "version": "1.0.4", + "version": "1.2.0", + "publishConfig": { + "registry": "https://bin.barco.com/artifactory/api/npm/npm-local" + }, "description": "Capturing images from USB(UVC) webcam on linux machines", "keywords": [ "linux", @@ -28,17 +31,18 @@ "jpeg-js": "*" }, "engines": { - "node": ">=4.0.0" + "node": ">=10.0.0" }, "scripts": { "test": "node test.js", "make-c-examples": "make -f c-examples.makefile" }, "os": [ - "linux" + "linux", + "android" ], "dependencies": { - "nan": ">=2.3.0" + "nan": "^2.14.0" }, "contributors": [ "Tim Cameron Ryan (https://github.com/tcr)", diff --git a/test.js b/test.js index b622dba..3988733 100644 --- a/test.js +++ b/test.js @@ -1,2 +1,13 @@ // this script for travis-ci, but its linux kernel is old 2.6 var v4l2camera = require("./"); + +try { + const camera = v4l2camera.Camera('/dev/video0'); + camera.configSet(camera.formats[6]); + console.log(camera.controlGet(9963778)); +camera.controlSet(9963778, 50) + console.log(camera.controlGet(9963778)); + debugger; +} catch (error) { +debugger; +} diff --git a/v4l2camera.cc b/v4l2camera.cc index 6beb6dd..16cc78b 100644 --- a/v4l2camera.cc +++ b/v4l2camera.cc @@ -83,7 +83,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) { @@ -212,7 +212,7 @@ namespace { auto denominator = std::uint32_t{0}; const auto finterval = getValue(format, "interval"); if (finterval->IsObject()) { - const auto interval = finterval->ToObject(); + const auto interval = finterval->ToObject(Nan::GetCurrentContext()).ToLocalChecked(); numerator = getUint(interval, "numerator"); denominator = getUint(interval, "denominator"); } @@ -246,16 +246,29 @@ 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()); - if (!inst.IsEmpty()) info.GetReturnValue().Set(inst.ToLocalChecked()); - return; + + static v8::Local convertCapabilities(unsigned flags) { + char** ccaps = cap2s(flags); + size_t len; + for (len = 0; ccaps[len] != NULL; len++); + auto caps = Nan::New(len); + for (auto i = std::size_t{0}; i < len; ++i) { + Nan::Set(caps, i, Nan::New(ccaps[i]).ToLocalChecked()); } + free(ccaps); + return caps; + } + + + 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()); + // if (!inst.IsEmpty()) info.GetReturnValue().Set(inst.ToLocalChecked()); + // return; + // } if (info.Length() < 1) { Nan::ThrowTypeError("argument required: device"); @@ -274,9 +287,15 @@ namespace { auto self = new Camera; self->camera = camera; self->Wrap(thisObj); + camera_capabilities(camera); + setValue(thisObj, "device", info[0]); setValue(thisObj, "formats", cameraFormats(camera)); setValue(thisObj, "controls", cameraControls(camera)); + setUint(thisObj, "rawCapabilities", camera->capabilities); + setUint(thisObj, "rawDeviceCapabilities", camera->device_capabilities); + setValue(thisObj, "capabilities", convertCapabilities(camera->capabilities)); + setValue(thisObj, "deviceCapabilities", convertCapabilities(camera->device_capabilities)); } NAN_METHOD(Camera::Start) { @@ -291,7 +310,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; @@ -306,6 +324,9 @@ namespace { Nan::ThrowError(cameraError(camera)); return; } + if (info.Length() < 1) { + return; + } Watch(info, StopCB); } @@ -364,7 +385,7 @@ namespace { Nan::ThrowTypeError("argument required: config"); return; } - const auto cformat = convertCFormat(info[0]->ToObject()); + const auto cformat = convertCFormat(info[0]->ToObject(Nan::GetCurrentContext()).ToLocalChecked()); auto thisObj = info.Holder(); auto camera = Nan::ObjectWrap::Unwrap(thisObj)->camera; if (!camera_config_set(camera, &cformat)) { @@ -381,7 +402,7 @@ namespace { Nan::ThrowTypeError("an argument required: id"); return; } - const auto id = info[0]->Uint32Value(); + const auto id = Nan::To(info[0]).FromJust(); auto camera = Nan::ObjectWrap::Unwrap(info.Holder())->camera; auto value = std::int32_t{0}; auto success = bool{camera_control_get(camera, id, &value)}; @@ -397,8 +418,8 @@ namespace { Nan::ThrowTypeError("arguments required: id, value"); return; } - const auto id = info[0]->Uint32Value(); - const auto value = info[1]->Int32Value(); + const auto id = Nan::To(info[0]).FromJust(); + const auto value = Nan::To(info[1]).FromJust(); auto thisObj = info.Holder(); auto camera = Nan::ObjectWrap::Unwrap(thisObj)->camera; auto success = bool{camera_control_set(camera, id, value)};