From 056ca36ef4fa57ce23b4709b186370c497be9f92 Mon Sep 17 00:00:00 2001 From: Samuel Mimram Date: Sat, 19 Feb 2022 10:27:04 +0100 Subject: [PATCH 1/2] Start restoring v4l2. --- examples/Makefile | 3 + examples/dune | 6 ++ examples/webcam.ml | 25 +++++ external/deprecated/MMV4L.mli | 1 - external/dune | 14 +++ external/{deprecated/MMV4L.ml => mm_v4l2.ml} | 56 ++++------- external/mm_v4l2.mli | 9 ++ .../{deprecated/v4l_stubs.c => v4l2_stubs.c} | 95 +------------------ 8 files changed, 80 insertions(+), 129 deletions(-) create mode 100644 examples/webcam.ml delete mode 100644 external/deprecated/MMV4L.mli rename external/{deprecated/MMV4L.ml => mm_v4l2.ml} (57%) create mode 100644 external/mm_v4l2.mli rename external/{deprecated/v4l_stubs.c => v4l2_stubs.c} (68%) 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..d7d5aa01 --- /dev/null +++ b/examples/webcam.ml @@ -0,0 +1,25 @@ +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 = Mm_v4l2.grab dev in + let img = Image.RGBA32.to_int_image img in + let img = Graphics.make_image img in + Graphics.draw_image img 0 0; + Graphics.synchronize (); + Graphics.loop_at_exit [] (fun _ -> ()) 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..4216dbf4 100644 --- a/external/dune +++ b/external/dune @@ -68,3 +68,17 @@ (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)) + (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..5ba5421e 100644 --- a/external/deprecated/MMV4L.ml +++ b/external/mm_v4l2.ml @@ -31,48 +31,28 @@ * *) -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 = + { + fd : Unix.file_descr; + width : int; + height : int; + } -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 +let open_device dev width height = + let fd = open_device dev width height in + { fd; width; height } -module V4L = V4L2 +external grab : device -> data -> unit = "caml_v4l2_grab" -class reader device width height = - object (self) - val dev = V4L.opendev device width height (3 * width) +let grab dev = + let data = Bigarray.Array1.create Bigarray.int8_unsigned Bigarray.c_layout (dev.width * dev.height * 3) in + grab dev data; + Image.RGBA32.make dev.width dev.height data - 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..69ca145c --- /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 : device -> Image.RGBA32.t + +val close : device -> unit diff --git a/external/deprecated/v4l_stubs.c b/external/v4l2_stubs.c similarity index 68% rename from external/deprecated/v4l_stubs.c rename to external/v4l2_stubs.c index 08e21dd2..58174348 100644 --- a/external/deprecated/v4l_stubs.c +++ b/external/v4l2_stubs.c @@ -44,14 +44,13 @@ #include #include #include +#include #include #include #include #include #include -#include #include -#include #define CLEAR(x) memset(&(x), 0, sizeof(x)) @@ -69,13 +68,13 @@ static int xioctl(int fh, int request, void *arg) } -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); // TODO: error codes // TODO: flags - int fd = v4l2_open(String_val(device), O_RDWR | O_NONBLOCK); + int fd = open(String_val(device), O_RDWR | O_NONBLOCK); assert(fd >= 0); // TODO: different formats ? @@ -86,7 +85,6 @@ CAMLprim value caml_v4l2_open(value device, value w, value h, value stride) fmt.fmt.pix.height = Int_val(h); fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; - //fmt.fmt.pix.bytesperline = Int_val(stride); xioctl(fd, VIDIOC_S_FMT, &fmt); // TODO: check returned sizes assert(fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24); @@ -141,7 +139,7 @@ CAMLprim value caml_v4l2_grab(value _fd, value data) xioctl(fd, VIDIOC_QUERYBUF, &vbuf); mbuflen = vbuf.length; - mbuf = v4l2_mmap(NULL, mbuflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, vbuf.m.offset); + mbuf = mmap(NULL, mbuflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, vbuf.m.offset); assert(mbuf != MAP_FAILED); memset(&vbuf, 0, sizeof(vbuf)); @@ -177,7 +175,7 @@ CAMLprim value caml_v4l2_grab(value _fd, value data) type = V4L2_BUF_TYPE_VIDEO_CAPTURE; xioctl(fd, VIDIOC_STREAMOFF, &type); - v4l2_munmap(mbuf, mbuflen); + munmap(mbuf, mbuflen); caml_leave_blocking_section(); CAMLreturn(Val_unit); @@ -187,89 +185,6 @@ CAMLprim value caml_v4l2_close(value fd) { CAMLparam0(); - v4l2_close(Int_val(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); From 121160da3efcd1162a0eae3b83a69dffcec51b33 Mon Sep 17 00:00:00 2001 From: Samuel Mimram Date: Sat, 19 Feb 2022 11:44:09 +0100 Subject: [PATCH 2/2] Almost working. --- examples/webcam.ml | 15 ++++++----- external/dune | 1 + external/mm_v4l2.ml | 20 ++++---------- external/mm_v4l2.mli | 2 +- external/v4l2_stubs.c | 61 ++++++++++++++++++++++++++++--------------- 5 files changed, 56 insertions(+), 43 deletions(-) diff --git a/examples/webcam.ml b/examples/webcam.ml index d7d5aa01..23a64d86 100644 --- a/examples/webcam.ml +++ b/examples/webcam.ml @@ -17,9 +17,12 @@ let () = let dev = Mm_v4l2.open_device "/dev/video0" width height in Graphics.open_graph ""; Graphics.resize_window width height; - let img = Mm_v4l2.grab dev in - let img = Image.RGBA32.to_int_image img in - let img = Graphics.make_image img in - Graphics.draw_image img 0 0; - Graphics.synchronize (); - Graphics.loop_at_exit [] (fun _ -> ()) + 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/dune b/external/dune index 4216dbf4..2fda3649 100644 --- a/external/dune +++ b/external/dune @@ -77,6 +77,7 @@ (foreign_stubs (language c) (names v4l2_stubs)) + (c_library_flags (-lv4l2)) (enabled_if (= %{system} linux)) (optional) diff --git a/external/mm_v4l2.ml b/external/mm_v4l2.ml index 5ba5421e..2e89db12 100644 --- a/external/mm_v4l2.ml +++ b/external/mm_v4l2.ml @@ -35,24 +35,14 @@ open Mm_image type data = (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t -type device = - { - fd : Unix.file_descr; - width : int; - height : int; - } +type device = Unix.file_descr external open_device : string -> int -> int -> Unix.file_descr = "caml_v4l2_open" -let open_device dev width height = - let fd = open_device dev width height in - { fd; width; height } +external grab : Unix.file_descr -> data -> unit = "caml_v4l2_grab" -external grab : device -> data -> unit = "caml_v4l2_grab" - -let grab dev = - let data = Bigarray.Array1.create Bigarray.int8_unsigned Bigarray.c_layout (dev.width * dev.height * 3) in - grab dev data; - Image.RGBA32.make dev.width dev.height data +let grab_rgba32 dev img = + let data = Image.RGBA32.data img in + grab dev data external close : device -> unit = "caml_v4l2_close" diff --git a/external/mm_v4l2.mli b/external/mm_v4l2.mli index 69ca145c..fc241608 100644 --- a/external/mm_v4l2.mli +++ b/external/mm_v4l2.mli @@ -4,6 +4,6 @@ type device val open_device : string -> int -> int -> device -val grab : device -> Image.RGBA32.t +val grab_rgba32 : device -> Image.RGBA32.t -> unit val close : device -> unit diff --git a/external/v4l2_stubs.c b/external/v4l2_stubs.c index 58174348..435298b5 100644 --- a/external/v4l2_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 @@ -51,6 +53,7 @@ #include #include #include +#include #define CLEAR(x) memset(&(x), 0, sizeof(x)) @@ -59,10 +62,8 @@ 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; } @@ -72,9 +73,10 @@ CAMLprim value caml_v4l2_open(value device, value w, value h) { CAMLparam1(device); + int r; // TODO: error codes // TODO: flags - int fd = open(String_val(device), O_RDWR | O_NONBLOCK); + int fd = v4l2_open(String_val(device), O_RDWR | O_NONBLOCK); assert(fd >= 0); // TODO: different formats ? @@ -84,10 +86,14 @@ CAMLprim value caml_v4l2_open(value device, value w, value h) 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; - 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)); } @@ -123,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 = mmap(NULL, mbuflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, vbuf.m.offset); + 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); @@ -159,23 +177,24 @@ 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); - munmap(mbuf, mbuflen); + v4l2_munmap(mbuf, mbuflen); caml_leave_blocking_section(); CAMLreturn(Val_unit); @@ -185,7 +204,7 @@ CAMLprim value caml_v4l2_close(value fd) { CAMLparam0(); - close(Int_val(fd)); + v4l2_close(Int_val(fd)); CAMLreturn(Val_unit); }