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

Add back v4l2 support. #14

Open
wants to merge 2 commits into
base: main
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
3 changes: 3 additions & 0 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ sine_wav:
graphics:
@dune exec ./graphics_test.exe

webcam:
@dune exec ./webcam.exe

test:
@dune exec ./test.exe

Expand Down
6 changes: 6 additions & 0 deletions examples/dune
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@
(optional)
(libraries graphics mm))

(executable
(name webcam)
(modules webcam)
(optional)
(libraries graphics mm mm.v4l2))

(executable
(name test)
(modules test)
Expand Down
28 changes: 28 additions & 0 deletions examples/webcam.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
open Mm

let show img =
let width = Image.YUV420.width img in
let height = Image.YUV420.height img in
let img = Image.YUV420.to_int_image img in
Graphics.open_graph "";
Graphics.resize_window width height;
let img = Graphics.make_image img in
Graphics.draw_image img 0 0;
Graphics.synchronize ();
Graphics.loop_at_exit [] (fun _ -> ())

let () =
let width = 640 in
let height = 480 in
let dev = Mm_v4l2.open_device "/dev/video0" width height in
Graphics.open_graph "";
Graphics.resize_window width height;
let img = Image.RGBA32.create width height in
while true do
Mm_v4l2.grab_rgba32 dev img;
let img = Image.RGBA32.to_int_image img in
let img = Graphics.make_image img in
Graphics.draw_image img 0 0;
Graphics.synchronize ();
Unix.sleepf 0.5
done
1 change: 0 additions & 1 deletion external/deprecated/MMV4L.mli

This file was deleted.

15 changes: 15 additions & 0 deletions external/dune
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,18 @@
(optional)
(synopsis
"High-level APIs to create and manipulate multimedia streams -- optional oss module"))

(library
(name mm_v4l2)
(public_name mm.v4l2)
(modules mm_v4l2)
(libraries mm.base mm.image)
(foreign_stubs
(language c)
(names v4l2_stubs))
(c_library_flags (-lv4l2))
(enabled_if
(= %{system} linux))
(optional)
(synopsis
"High-level APIs to create and manipulate multimedia streams -- optional v4l2 module"))
48 changes: 9 additions & 39 deletions external/deprecated/MMV4L.ml → external/mm_v4l2.ml
Original file line number Diff line number Diff line change
Expand Up @@ -31,48 +31,18 @@
*
*)

module G = Image.Generic
open Mm_image

module V4L1 = struct
type device = Unix.file_descr
type data = (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t

external opendev : string -> int -> int -> int -> device = "caml_v4l1_open"
external grab : device -> G.data -> unit = "caml_v4l1_grab"
external close : device -> unit = "caml_v4l1_close"
end
type device = Unix.file_descr

module V4L2 = struct
type device = Unix.file_descr
external open_device : string -> int -> int -> Unix.file_descr = "caml_v4l2_open"

external opendev : string -> int -> int -> int -> device = "caml_v4l2_open"
external grab : device -> G.data -> unit = "caml_v4l2_grab"
external close : device -> unit = "caml_v4l2_close"
end
external grab : Unix.file_descr -> data -> unit = "caml_v4l2_grab"

module V4L = V4L2
let grab_rgba32 dev img =
let data = Image.RGBA32.data img in
grab dev data

class reader device width height =
object (self)
val dev = V4L.opendev device width height (3 * width)

val img =
let data =
Bigarray.Array1.create Bigarray.int8_unsigned Bigarray.c_layout
(width * height * 3)
in
G.make_rgb G.Pixel.RGB24 width height data

method frame_rate = 12.
method width = width
method height = height

method read buf ofs len =
V4L.grab dev (fst (G.rgb_data img));
for i = ofs to ofs + len - 1 do
buf.(i) <- Image.RGBA32.create width height;
G.convert ~copy:true ~proportional:true img (G.of_RGBA32 buf.(i))
done;
len

method close = V4L.close dev
end
external close : device -> unit = "caml_v4l2_close"
9 changes: 9 additions & 0 deletions external/mm_v4l2.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
open Mm_image

type device

val open_device : string -> int -> int -> device

val grab_rgba32 : device -> Image.RGBA32.t -> unit

val close : device -> unit
140 changes: 37 additions & 103 deletions external/deprecated/v4l_stubs.c → external/v4l2_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
*
*/

/* See https://www.kernel.org/doc/html/v4.12/media/uapi/v4l/v4l2grab.c.html */

#include <caml/alloc.h>
#include <caml/bigarray.h>
#include <caml/fail.h>
Expand All @@ -44,12 +46,12 @@
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/videodev.h>
#include <linux/videodev2.h>
#include <libv4l2.h>

Expand All @@ -60,19 +62,18 @@ static int xioctl(int fh, int request, void *arg)
int r;

do {
r = ioctl(fh, request, arg);
} while (r == -1 && ((errno == EINTR) || (errno == EAGAIN)));

assert(r != -1);
r = v4l2_ioctl(fh, request, arg);
} while (r == -1 && (errno == EINTR || errno == EAGAIN));

return r;
}


CAMLprim value caml_v4l2_open(value device, value w, value h, value stride)
CAMLprim value caml_v4l2_open(value device, value w, value h)
{
CAMLparam1(device);

int r;
// TODO: error codes
// TODO: flags
int fd = v4l2_open(String_val(device), O_RDWR | O_NONBLOCK);
Expand All @@ -85,11 +86,14 @@ CAMLprim value caml_v4l2_open(value device, value w, value h, value stride)
fmt.fmt.pix.width = Int_val(w);
fmt.fmt.pix.height = Int_val(h);
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
/* fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; */
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
//fmt.fmt.pix.bytesperline = Int_val(stride);
xioctl(fd, VIDIOC_S_FMT, &fmt);
r = xioctl(fd, VIDIOC_S_FMT, &fmt);
assert (r != -1);
// TODO: check returned sizes
assert(fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24);
assert(fmt.fmt.pix.width == Int_val(w));
assert(fmt.fmt.pix.height == Int_val(h));

CAMLreturn(Val_int(fd));
}
Expand Down Expand Up @@ -125,34 +129,46 @@ CAMLprim value caml_v4l2_grab(value _fd, value data)
enum v4l2_buf_type type;
struct timeval tv;
fd_set fds;
int ret;
int r;

caml_enter_blocking_section();

// Initiate memory mapping
// TODO: support more than 1
CLEAR(req);
req.count = 1;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
xioctl(fd, VIDIOC_REQBUFS, &req);
r = xioctl(fd, VIDIOC_REQBUFS, &req);

if (r == -1) caml_failwith(strerror(errno));

// Mmap buffer
memset(&vbuf, 0, sizeof(vbuf));
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vbuf.memory = V4L2_MEMORY_MMAP;
vbuf.index = 0;
xioctl(fd, VIDIOC_QUERYBUF, &vbuf);
r = xioctl(fd, VIDIOC_QUERYBUF, &vbuf);
assert (r != -1);

mbuflen = vbuf.length;
mbuf = v4l2_mmap(NULL, mbuflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, vbuf.m.offset);
assert(mbuf != MAP_FAILED);

// Enqueue buffer
memset(&vbuf, 0, sizeof(vbuf));
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vbuf.memory = V4L2_MEMORY_MMAP;
vbuf.index = 0;
xioctl(fd, VIDIOC_QBUF, &vbuf);
r = xioctl(fd, VIDIOC_QBUF, &vbuf);
assert (r != -1);

// Start streaming
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(fd, VIDIOC_STREAMON, &type);
r = xioctl(fd, VIDIOC_STREAMON, &type);
assert (r != -1);

// Capture image
do {
FD_ZERO(&fds);
FD_SET(fd, &fds);
Expand All @@ -161,21 +177,22 @@ CAMLprim value caml_v4l2_grab(value _fd, value data)
tv.tv_sec = 2;
tv.tv_usec = 0;

ret = select(fd + 1, &fds, NULL, NULL, &tv);
} while ((ret == -1 && (errno == EINTR)));
assert(ret != -1);
r = select(fd + 1, &fds, NULL, NULL, &tv);
} while (r == -1 && errno == EINTR);
assert(r != -1);

memset(&vbuf, 0, sizeof(vbuf));
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vbuf.memory = V4L2_MEMORY_MMAP;
xioctl(fd, VIDIOC_DQBUF, &vbuf);
r = xioctl(fd, VIDIOC_DQBUF, &vbuf);
assert (r != -1);

memcpy(buf, mbuf, vbuf.bytesused);

xioctl(fd, VIDIOC_QBUF, &vbuf);

// Stop capture
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(fd, VIDIOC_STREAMOFF, &type);
r = xioctl(fd, VIDIOC_STREAMOFF, &type);
assert (r != -1);

v4l2_munmap(mbuf, mbuflen);
caml_leave_blocking_section();
Expand All @@ -191,86 +208,3 @@ CAMLprim value caml_v4l2_close(value fd)

CAMLreturn(Val_unit);
}

CAMLprim value caml_v4l1_open(value device, value w, value h, value stride)
{
CAMLparam1(device);
int fd;
struct video_capability cap;
struct video_window win;
struct video_picture vpic;

fd = open(String_val(device), O_RDONLY);
assert(fd >= 0);
assert(ioctl(fd, VIDIOCGCAP, &cap) >= 0);
assert(ioctl(fd, VIDIOCGWIN, &win) >= 0);
assert(ioctl(fd, VIDIOCGPICT, &vpic) >= 0);

if (cap.type & VID_TYPE_MONOCHROME) {
vpic.depth=8;
vpic.palette=VIDEO_PALETTE_GREY; /* 8bit grey */
if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
vpic.depth=6;
if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
vpic.depth=4;
if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
//fprintf(stderr, "Unable to find a supported capture format.\n");
close(fd);
assert(0);
}
}
}
}
else {
vpic.depth=24;
vpic.palette=VIDEO_PALETTE_RGB24;

if(ioctl(fd, VIDIOCSPICT, &vpic) < 0) {
vpic.palette=VIDEO_PALETTE_RGB565;
vpic.depth=16;

if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
vpic.palette=VIDEO_PALETTE_RGB555;
vpic.depth=15;

if(ioctl(fd, VIDIOCSPICT, &vpic)==-1) {
//fprintf(stderr, "Unable to find a supported capture format.\n");
//return -1;
close(fd);
assert(0);
}
}
}
}
assert(!(cap.type & VID_TYPE_MONOCHROME));
assert(vpic.depth == 24);
assert(vpic.palette == VIDEO_PALETTE_RGB24);

CAMLreturn(Val_int(fd));
}

CAMLprim value caml_v4l1_grab(value fd, value data)
{
CAMLparam1(data);
int len = caml_ba_byte_size(Caml_ba_array_val(data));
int ret;

caml_enter_blocking_section();
ret = read(fd, Caml_ba_data_val(data), len);
caml_leave_blocking_section();

if (ret < 0)
printf("error: %d\n", errno);
assert(ret == len);

CAMLreturn(Val_unit);
}

CAMLprim value caml_v4l1_close(value fd)
{
CAMLparam0();

close(Int_val(fd));

CAMLreturn(Val_unit);
}