Skip to content

Commit

Permalink
Update v4l2loopback handling further
Browse files Browse the repository at this point in the history
Set the 'keep_format' control value, like the
'v4l2loopback-ctl set-caps' tool, which results
in more consistent behaviour.

Extra note: there also seem to be some differences
between the current main branch of v4l2loopback
and the current public release v0.12.7.
Both seem to work as expected now.
  • Loading branch information
aramg committed Dec 20, 2023
1 parent a2305da commit bfb9ddb
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 17 deletions.
4 changes: 2 additions & 2 deletions src/decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ int decoder_init(const char* v4l2_device, unsigned v4l2_width, unsigned v4l2_hei
set_v4l2_device(v4l2_device);
droidcam_device_fd = open_v4l2_device();
} else {
droidcam_device_fd = find_v4l2_device("platform:v4l2loopback_dc");
droidcam_device_fd = find_v4l2_device(V4L2_PLATFORM_DC);
if (droidcam_device_fd < 0) {
// check for generic v4l2loopback device
droidcam_device_fd = find_v4l2_device("platform:v4l2loopback");
droidcam_device_fd = find_v4l2_device(V4L2_PLATFORM);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ void decoder_show_test_image();
#define VIDEO_FMT_DROIDCAM 3
#define VIDEO_FMT_DROIDCAMX 18

#define V4L2_PLATFORM "platform:v4l2loopback"
#define V4L2_PLATFORM_DC "platform:v4l2loopback_dc"

void set_v4l2_device(const char* device);
int open_v4l2_device(void);
int find_v4l2_device(const char* bus_info);
Expand Down
97 changes: 83 additions & 14 deletions src/decoder_v4l2.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,60 @@ static int xioctl(int fd, int request, void *arg){
return r;
}

static unsigned int _get_control_id(int fd, const char *control){
const size_t length = strnlen(control, 1024);
const unsigned next = V4L2_CTRL_FLAG_NEXT_CTRL;
struct v4l2_queryctrl qctrl;
int id;

memset(&qctrl, 0, sizeof(qctrl));
while (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) {
if (!strncmp((const char*)qctrl.name, control, length))
return qctrl.id;
qctrl.id |= next;
}
for (id = V4L2_CID_USER_BASE; id < V4L2_CID_LASTP1; id++) {
qctrl.id = id;
if (ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0) {
if (!strncmp((const char*)qctrl.name, control, length))
return qctrl.id;
}
}
for (qctrl.id = V4L2_CID_PRIVATE_BASE;
ioctl(fd, VIDIOC_QUERYCTRL, &qctrl) == 0; qctrl.id++) {
if (!strncmp((const char*)qctrl.name, control, length)) {
unsigned int id = qctrl.id;
return id;
}
}
return 0;
}

static int set_control_i(int fd, const char *control, int value){
struct v4l2_control ctrl;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = _get_control_id(fd, control);
ctrl.value = value;
if (ctrl.id && ioctl(fd, VIDIOC_S_CTRL, &ctrl) == 0) {
int value = ctrl.value;
return value;
}
return 0;
}
#if 0
static int get_control_i(int fd, const char *control){
struct v4l2_control ctrl;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.id = _get_control_id(fd, control);

if (ctrl.id && ioctl(fd, VIDIOC_G_CTRL, &ctrl) == 0) {
int value = ctrl.value;
return value;
}
return 0;
}
#endif

int open_v4l2_device(void) {
int fd;
struct stat st;
Expand Down Expand Up @@ -79,7 +133,8 @@ void set_v4l2_device(const char* device) {
v4l2_device[sizeof(v4l2_device) - 1] = '\0';
}

void query_v4l_device(int droidcam_device_fd, unsigned *WEBCAM_W, unsigned *WEBCAM_H) {
void query_v4l_device(int fd, unsigned *WEBCAM_W, unsigned *WEBCAM_H) {
struct v4l2_capability v4l2cap = {0};
struct v4l2_format vid_format = {0};
vid_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

Expand All @@ -88,22 +143,35 @@ void query_v4l_device(int droidcam_device_fd, unsigned *WEBCAM_W, unsigned *WEBC
*WEBCAM_W = 0;
*WEBCAM_H = 0;

dbgprint("Trying to set format YU12:%dx%d\n", in_width, in_height);
vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
vid_format.fmt.pix.width = in_width;
vid_format.fmt.pix.height = in_height;
vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
vid_format.fmt.pix.field = V4L2_FIELD_NONE;
xioctl(droidcam_device_fd, VIDIOC_S_FMT, &vid_format);
if (xioctl(fd, VIDIOC_QUERYCAP, &v4l2cap) < 0) {
errprint("Error: Unable to query video device. dev=%s errno=%d\n",
v4l2_device, errno);
return;
}

dbgprint("using '%s' (%s)\n", v4l2cap.card, v4l2cap.bus_info);

const char* bus_info_dc = V4L2_PLATFORM_DC;
if (0 != strncmp(bus_info_dc, (const char*) v4l2cap.bus_info, strlen(bus_info_dc))) {
vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
xioctl(fd, VIDIOC_G_FMT, &vid_format);

dbgprint("set fmt YU12:%dx%d\n", in_width, in_height);
vid_format.fmt.pix.width = in_width;
vid_format.fmt.pix.height = in_height;
vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
vid_format.fmt.pix.field = V4L2_FIELD_NONE;
if (xioctl(fd, VIDIOC_S_FMT, &vid_format) >= 0) {
set_control_i(fd, "keep_format", 1);
goto early_out;
}

#ifdef __linux__
vid_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(droidcam_device_fd, VIDIOC_S_FMT, &vid_format);
#endif
dbgprint("set fmt failed, trying to query fmt\n");
}

int ret = xioctl(droidcam_device_fd, VIDIOC_G_FMT, &vid_format);
int ret = xioctl(fd, VIDIOC_G_FMT, &vid_format);
if (ret < 0) {
errprint("Fatal: Unable to query video device. dev=%s errno=%d\n",
errprint("Error: Unable to determine video device fmt. dev=%s errno=%d\n",
v4l2_device, errno);
return;
}
Expand Down Expand Up @@ -134,6 +202,7 @@ void query_v4l_device(int droidcam_device_fd, unsigned *WEBCAM_W, unsigned *WEBC
return;
}

early_out:
*WEBCAM_W = vid_format.fmt.pix.width;
*WEBCAM_H = vid_format.fmt.pix.height;
}
2 changes: 1 addition & 1 deletion src/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void LoadSettings(struct settings* settings) {
if (1 == sscanf(buf, "audio=%d\n", &settings->audio)) continue;
if (1 == sscanf(buf, "video=%d\n", &settings->video)) continue;

if (2 == sscanf(buf, "size=%dx%d\n", &arg1, &arg2)) {
if (2 == sscanf(buf, "size=%dx%d\n", &arg1, &arg2) && arg1 > 0 && arg2 > 0) {
settings->v4l2_width = arg1;
settings->v4l2_height = arg2;
continue;
Expand Down

0 comments on commit bfb9ddb

Please sign in to comment.