Browse Source

Implement MPLANE buffers

The qualcomm driver (on MSM8916 at least) uses an MPLANE buffer
for the video capture with a single plane in it. This detects
such drivers and requests MPLANE buffers from the driver and then
always uses the first plane from that through the normal pipeline.
Martijn Braam 4 years ago
parent
commit
f108fe7d06
1 changed files with 105 additions and 49 deletions
  1. 105 49
      main.c

+ 105 - 49
main.c

@@ -57,6 +57,7 @@ struct camerainfo {
 	int rotate;
 	int fmt;
 	int mbus;
+	enum v4l2_buf_type type;
 	int fd;
 
 	char media_dev_name[260];
@@ -95,6 +96,7 @@ static float colormatrix_srgb[] = {
 };
 
 struct buffer *buffers;
+struct v4l2_plane buf_planes[1];
 static unsigned int n_buffers;
 
 struct camerainfo current;
@@ -175,22 +177,24 @@ remap(int value, int input_min, int input_max, int output_min, int output_max)
 static void
 start_capturing(int fd)
 {
-	enum v4l2_buf_type type;
-
 	for (int i = 0; i < n_buffers; ++i) {
 		struct v4l2_buffer buf = {
-			.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			.type = current.type,
 			.memory = V4L2_MEMORY_MMAP,
 			.index = i,
 		};
 
+		if(current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			buf.m.planes = buf_planes;
+			buf.length = 1;
+		}
+
 		if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
 			errno_exit("VIDIOC_QBUF");
 		}
 	}
 
-	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	if (xioctl(fd, VIDIOC_STREAMON, &type) == -1) {
+	if (xioctl(fd, VIDIOC_STREAMON, &current.type) == -1) {
 		errno_exit("VIDIOC_STREAMON");
 	}
 
@@ -204,8 +208,7 @@ stop_capturing(int fd)
 	ready = 0;
 	printf("Stopping capture\n");
 
-	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	if (xioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
+	if (xioctl(fd, VIDIOC_STREAMOFF, &current.type) == -1) {
 		errno_exit("VIDIOC_STREAMOFF");
 	}
 
@@ -218,10 +221,11 @@ stop_capturing(int fd)
 static void
 init_mmap(int fd)
 {
-	struct v4l2_requestbuffers req = {0};
-	req.count = 4;
-	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	req.memory = V4L2_MEMORY_MMAP;
+	struct v4l2_requestbuffers req = {
+		.count = 4,
+		.type = current.type,
+		.memory = V4L2_MEMORY_MMAP,
+	};
 
 	if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
 		if (errno == EINVAL) {
@@ -248,21 +252,35 @@ init_mmap(int fd)
 
 	for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
 		struct v4l2_buffer buf = {
-			.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			.type = current.type,
 			.memory = V4L2_MEMORY_MMAP,
 			.index = n_buffers,
 		};
 
+		if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			buf.m.planes = buf_planes;
+			buf.length = 1;
+		}
+
 		if (xioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
 			errno_exit("VIDIOC_QUERYBUF");
 		}
 
-		buffers[n_buffers].length = buf.length;
-		buffers[n_buffers].start = mmap(NULL /* start anywhere */,
-			buf.length,
-			PROT_READ | PROT_WRITE /* required */,
-			MAP_SHARED /* recommended */,
-			fd, buf.m.offset);
+		if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			buffers[n_buffers].length = buf.m.planes[0].length;
+			buffers[n_buffers].start = mmap(NULL /* start anywhere */,
+					buf.m.planes[0].length,
+					PROT_READ | PROT_WRITE /* required */,
+					MAP_SHARED /* recommended */,
+					fd, buf.m.planes[0].m.mem_offset);
+		} else {
+			buffers[n_buffers].length = buf.length;
+			buffers[n_buffers].start = mmap(NULL /* start anywhere */,
+					buf.length,
+					PROT_READ | PROT_WRITE /* required */,
+					MAP_SHARED /* recommended */,
+					fd, buf.m.offset);
+		}
 
 		if (MAP_FAILED == buffers[n_buffers].start) {
 			errno_exit("mmap");
@@ -497,7 +515,13 @@ init_device(int fd)
 		}
 	}
 
-	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+	// Detect buffer format for the interface node, preferring normal video capture
+	if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
+		current.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	} else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
+		printf("[%s] Using the MPLANE buffer format\n", current.cfg_name);
+		current.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	} else {
 		fprintf(stderr, "%s is no video capture device\n",
 			current.dev_name);
 		exit(EXIT_FAILURE);
@@ -511,12 +535,12 @@ init_device(int fd)
 
 	/* Select video input, video standard and tune here. */
 	struct v4l2_cropcap cropcap = {
-		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		.type = current.type,
 	};
 
 	struct v4l2_crop crop = {0};
 	if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) {
-		crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		crop.type = current.type;
 		crop.c = cropcap.defrect; /* reset to default */
 
 		if (xioctl(fd, VIDIOC_S_CROP, &crop) == -1) {
@@ -533,26 +557,43 @@ init_device(int fd)
 		/* Errors ignored. */
 	}
 
-
+	// Request a video format
 	struct v4l2_format fmt = {
-		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		.type = current.type,
 	};
 	if (current.width > 0) {
 		g_print("Setting camera to %dx%d fmt %d\n",
 			current.width, current.height, current.fmt);
-		fmt.fmt.pix.width = current.width;
-		fmt.fmt.pix.height = current.height;
-		fmt.fmt.pix.pixelformat = current.fmt;
-		fmt.fmt.pix.field = V4L2_FIELD_ANY;
+
+		if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			fmt.fmt.pix_mp.width = current.width;
+			fmt.fmt.pix_mp.height = current.height;
+			fmt.fmt.pix_mp.pixelformat = current.fmt;
+			fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
+		} else {
+			fmt.fmt.pix.width = current.width;
+			fmt.fmt.pix.height = current.height;
+			fmt.fmt.pix.pixelformat = current.fmt;
+			fmt.fmt.pix.field = V4L2_FIELD_ANY;
+		}
 
 		if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
 			g_printerr("VIDIOC_S_FMT failed");
 			show_error("Could not set camera mode");
 			return -1;
 		}
-		if (fmt.fmt.pix.width != current.width ||
+
+		if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
+			&& (fmt.fmt.pix_mp.width != current.width ||
+			fmt.fmt.pix_mp.height != current.height ||
+			fmt.fmt.pix_mp.pixelformat != current.fmt))
+			g_printerr("Driver returned %dx%d fmt %d\n",
+				fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
+				fmt.fmt.pix_mp.pixelformat);
+		if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
+			&& (fmt.fmt.pix.width != current.width ||
 			fmt.fmt.pix.height != current.height ||
-			fmt.fmt.pix.pixelformat != current.fmt)
+			fmt.fmt.pix.pixelformat != current.fmt))
 			g_printerr("Driver returned %dx%d fmt %d\n",
 				fmt.fmt.pix.width, fmt.fmt.pix.height,
 				fmt.fmt.pix.pixelformat);
@@ -563,22 +604,24 @@ init_device(int fd)
 		if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {
 			errno_exit("VIDIOC_G_FMT");
 		}
-		g_print("Got %dx%d fmt %d from the driver\n",
-			fmt.fmt.pix.width, fmt.fmt.pix.height,
-			fmt.fmt.pix.pixelformat);
-		current.width = fmt.fmt.pix.width;
-		current.height = fmt.fmt.pix.height;
-	}
-	current.fmt = fmt.fmt.pix.pixelformat;
-
-	/* Buggy driver paranoia. */
-	unsigned int min = fmt.fmt.pix.width * 2;
-	if (fmt.fmt.pix.bytesperline < min) {
-		fmt.fmt.pix.bytesperline = min;
+		if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			g_print("Got %dx%d fmt %d from the driver\n",
+				fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
+				fmt.fmt.pix_mp.pixelformat);
+			current.width = fmt.fmt.pix.width;
+			current.height = fmt.fmt.pix.height;
+		} else {
+			g_print("Got %dx%d fmt %d from the driver\n",
+				fmt.fmt.pix.width, fmt.fmt.pix.height,
+				fmt.fmt.pix.pixelformat);
+			current.width = fmt.fmt.pix_mp.width;
+			current.height = fmt.fmt.pix_mp.height;
+		}
 	}
-	min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
-	if (fmt.fmt.pix.sizeimage < min) {
-		fmt.fmt.pix.sizeimage = min;
+	if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		current.fmt = fmt.fmt.pix_mp.pixelformat;
+	} else {
+		current.fmt = fmt.fmt.pix.pixelformat;
 	}
 
 	init_mmap(fd);
@@ -897,10 +940,19 @@ preview_configure(GtkWidget *widget, GdkEventConfigure *event)
 static int
 read_frame(int fd)
 {
-	struct v4l2_buffer buf = {0};
+	int bytesused;
+
+	struct v4l2_buffer buf = {
+		.type = current.type,
+		.memory = V4L2_MEMORY_MMAP,
+		.index = n_buffers,
+	};
+
+	if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		buf.m.planes = buf_planes;
+		buf.length = 1;
+	}
 
-	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	buf.memory = V4L2_MEMORY_MMAP;
 	if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
 		switch (errno) {
 			case EAGAIN:
@@ -914,9 +966,13 @@ read_frame(int fd)
 		}
 	}
 
-	//assert(buf.index < n_buffers);
+	if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		bytesused = buf.m.planes[0].bytesused;
+	} else {
+		bytesused = buf.bytesused;
+	}
 
-	process_image(buffers[buf.index].start, buf.bytesused);
+	process_image(buffers[buf.index].start, bytesused);
 
 	if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
 		errno_exit("VIDIOC_QBUF");