Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/node12 + add device caps #48

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@
"target_name": "v4l2camera",
"sources": ["capture.c", "v4l2camera.cc"],
"include_dirs" : [
"<!(node -e \"require('nan')\")"
],
"<!(node -e \"require('nan')\")"
],
"cflags": ["-Wall", "-Wextra", "-pedantic", "-O3"],
"xcode_settings": {
"OTHER_CPLUSPLUSFLAGS": ["-std=c++14"],
"OTHER_CPLUSPLUSFLAGS": ["-std=c++14"],
},
"cflags_c": ["-std=c11", "-Wunused-parameter"],
"cflags_cc": ["-std=c++14"]
"cflags_cc": ["-std=c++14"],
"conditions": [
[ "OS==\"android\"", {
"cflags": ["-Wall", "-Wextra", "-pedantic", "-O3", "-fPIC"],
"cflags!": ["-fPIE"]
}]
]
}]
}

87 changes: 82 additions & 5 deletions capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ camera_t* camera_open(const char * device)
camera->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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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));
Expand Down
5 changes: 5 additions & 0 deletions capture.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef CAMERA_H
#define CAMERA_H

#include <time.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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 <[email protected]> (https://github.com/tcr)",
Expand Down
11 changes: 11 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -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;
}
53 changes: 37 additions & 16 deletions v4l2camera.cc
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ namespace {
getUint(const v8::Local<v8::Object>& self, const char* name) {
return Nan::To<std::uint32_t>(getValue(self, name)).FromJust();
}

static inline void
setValue(const v8::Local<v8::Object>& self, const char* name,
const v8::Local<v8::Value>& value) {
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -246,16 +246,29 @@ namespace {
}
return formats;
}

NAN_METHOD(Camera::New) {
if (!info.IsConstructCall()) {
// [NOTE] generic recursive call with `new`
std::vector<v8::Local<v8::Value>> 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<v8::Object> convertCapabilities(unsigned flags) {
char** ccaps = cap2s(flags);
size_t len;
for (len = 0; ccaps[len] != NULL; len++);
auto caps = Nan::New<v8::Array>(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<v8::Local<v8::Value>> 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");
Expand All @@ -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) {
Expand All @@ -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;
Expand All @@ -306,6 +324,9 @@ namespace {
Nan::ThrowError(cameraError(camera));
return;
}
if (info.Length() < 1) {
return;
}
Watch(info, StopCB);
}

Expand Down Expand Up @@ -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<Camera>(thisObj)->camera;
if (!camera_config_set(camera, &cformat)) {
Expand All @@ -381,7 +402,7 @@ namespace {
Nan::ThrowTypeError("an argument required: id");
return;
}
const auto id = info[0]->Uint32Value();
const auto id = Nan::To<std::uint32_t>(info[0]).FromJust();
auto camera = Nan::ObjectWrap::Unwrap<Camera>(info.Holder())->camera;
auto value = std::int32_t{0};
auto success = bool{camera_control_get(camera, id, &value)};
Expand All @@ -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<std::uint32_t>(info[0]).FromJust();
const auto value = Nan::To<std::int32_t>(info[1]).FromJust();
auto thisObj = info.Holder();
auto camera = Nan::ObjectWrap::Unwrap<Camera>(thisObj)->camera;
auto success = bool{camera_control_set(camera, id, value)};
Expand Down