diff --git a/examples/Makefile b/examples/Makefile index f308aa59..745ac390 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,6 +30,9 @@ sine_wav: graphics: @dune exec ./graphics_test.exe +webcam: + @dune exec ./webcam.exe + test: @dune exec ./test.exe diff --git a/examples/dune b/examples/dune index 01f25e15..288f4694 100644 --- a/examples/dune +++ b/examples/dune @@ -58,6 +58,12 @@ (optional) (libraries graphics mm)) +(executable + (name webcam) + (modules webcam) + (optional) + (libraries graphics mm mm.v4l2)) + (executable (name test) (modules test) diff --git a/examples/webcam.ml b/examples/webcam.ml new file mode 100644 index 00000000..23a64d86 --- /dev/null +++ b/examples/webcam.ml @@ -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 diff --git a/external/deprecated/MMV4L.mli b/external/deprecated/MMV4L.mli deleted file mode 100644 index 8df39940..00000000 --- a/external/deprecated/MMV4L.mli +++ /dev/null @@ -1 +0,0 @@ -class reader : string -> int -> int -> Video.IO.Reader.t diff --git a/external/dune b/external/dune index 15fb8216..2fda3649 100644 --- a/external/dune +++ b/external/dune @@ -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")) diff --git a/external/deprecated/MMV4L.ml b/external/mm_v4l2.ml similarity index 57% rename from external/deprecated/MMV4L.ml rename to external/mm_v4l2.ml index 742a9111..2e89db12 100644 --- a/external/deprecated/MMV4L.ml +++ b/external/mm_v4l2.ml @@ -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" diff --git a/external/mm_v4l2.mli b/external/mm_v4l2.mli new file mode 100644 index 00000000..fc241608 --- /dev/null +++ b/external/mm_v4l2.mli @@ -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 diff --git a/external/deprecated/v4l_stubs.c b/external/v4l2_stubs.c similarity index 64% rename from external/deprecated/v4l_stubs.c rename to external/v4l2_stubs.c index 08e21dd2..435298b5 100644 --- a/external/deprecated/v4l_stubs.c +++ b/external/v4l2_stubs.c @@ -31,6 +31,8 @@ * */ +/* See https://www.kernel.org/doc/html/v4.12/media/uapi/v4l/v4l2grab.c.html */ + #include #include #include @@ -44,12 +46,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include @@ -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); @@ -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)); } @@ -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); @@ -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(); @@ -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); -}