I am trying to convert an MJPEG stream to some kind of RGB format and I am stuck with an error, that I don't understand why it is happening.
When checking device capabilities then /dev/video10 should be able to do what I need.
The problem is that when trying to request an OUTPUT buffer, I get an error that it cannot allocate memory.
I am running on a Raspberry Pi 4, but this code needs to work on a Pi3 as well.
Source code below and sample image attached:
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
//#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <linux/videodev2.h>
void get_supported_formats(int videofd) {
int i, x;
struct v4l2_fmtdesc fmtdesc;
/* Get supported Input Formats */
//memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
i = 0;
do {
fmtdesc.index = i;
if ((x=ioctl(videofd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0) {
printf("OUT (%d): %s\n", i, fmtdesc.description);
}
i++;
} while (x == 0);
/* Get supported Output Formats */
//memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
i = 0;
do {
fmtdesc.index = i;
if ((x=ioctl(videofd, VIDIOC_ENUM_FMT, &fmtdesc)) == 0) {
printf("CAP (%d): %s\n", i, fmtdesc.description);
}
i++;
} while (x == 0);
}
void decodeJPEG(const void* data, size_t size, size_t width, size_t height) {
//int fd = open("/dev/video31", O_RDWR);
int fd = open("/dev/video10", O_RDWR);
if (fd == -1) {
perror("Failed to open V4L2 device");
exit(EXIT_FAILURE);
}
get_supported_formats(fd);
//return;
// setting IO formats
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
fmt.fmt.pix.width = width;
fmt.fmt.pix.height = height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
perror("Failed to set V4L2 OUT format");
exit(EXIT_FAILURE);
}
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
perror("Failed to set V4L2 CAP format");
exit(EXIT_FAILURE);
}
// requesting and setting up IO buffers
struct v4l2_requestbuffers reqbuf;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 1;
reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
perror("Failed to request OUTPUT buffer");
exit(EXIT_FAILURE);
}
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
perror("Failed to request CAPTURE buffer");
exit(EXIT_FAILURE);
}
struct v4l2_plane outPlane;
struct v4l2_buffer outBuffer;
outBuffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
outBuffer.memory = V4L2_MEMORY_MMAP;
outBuffer.index = 0;
outBuffer.length = 1;
outBuffer.m.planes = &outPlane;
if (ioctl(fd, VIDIOC_QUERYBUF, &outBuffer) == -1) {
perror("Failed to query OUTPUT buffer");
exit(EXIT_FAILURE);
}
void* outBufferStart = mmap(NULL,
outPlane.length,
PROT_READ | PROT_WRITE, MAP_SHARED,
fd,
outPlane.m.mem_offset
);
struct v4l2_plane capPlane;
struct v4l2_buffer capBuffer;
capBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
capBuffer.memory = V4L2_MEMORY_MMAP;
capBuffer.index = 0;
capBuffer.length = 1;
capBuffer.m.planes = &capPlane;
if (ioctl(fd, VIDIOC_QUERYBUF, &capBuffer) == -1) {
perror("Failed to query CAPTURE buffer");
exit(EXIT_FAILURE);
}
void* capBufferStart = mmap(NULL,
capPlane.length,
PROT_READ | PROT_WRITE, MAP_SHARED,
fd,
capPlane.m.mem_offset
);
{
int type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
perror("Failed to STEAMON OUT buffer");
exit(EXIT_FAILURE);
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
perror("Failed to STEAMON CAP buffer");
exit(EXIT_FAILURE);
}
}
printf("starting loop \n");
int bytes;
do {
printf("Q CAP buffer \n");
if (ioctl(fd, VIDIOC_QBUF, &capBuffer) == -1) {
perror("Q CAP failed");
exit(EXIT_FAILURE);
}
printf("copy data \n");
bytes = MIN(size, outPlane.length);
memcpy(outBufferStart, data, bytes);
outPlane.bytesused = bytes;
printf("plane size: %d, image size: %d \n", outPlane.length, size);
printf("Q OUT buffer \n");
if (ioctl(fd, VIDIOC_QBUF, &outBuffer) == -1) {
perror("Q OUT failed");
exit(EXIT_FAILURE);
}
printf("DQ CAP buffer \n");
if (ioctl(fd, VIDIOC_DQBUF, &capBuffer) == -1) {
perror("DQ CAP failed");
exit(EXIT_FAILURE);
}
int encodedSize = capPlane.bytesused;
printf("plane size: %d, encoded size: %d \n", outPlane.length, encodedSize);
// FIXME this is just for testing
FILE* fp = fopen("out2.raw", "wb");
fwrite(capBufferStart, encodedSize, 1, fp);
fclose(fp);
printf("DQ OUT buffer \n");
if (ioctl(fd, VIDIOC_DQBUF, &outBuffer) == -1) {
perror("DQ OUT failed");
exit(EXIT_FAILURE);
}
// FIXME just for testing
bytes = 0;
} while (bytes != 0);
printf("exiting loop \n");
close(fd);
return;
{
int type = V4L2_CAP_VIDEO_OUTPUT_MPLANE;
if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
perror("Failed to STEAMOFF OUT buffer");
exit(EXIT_FAILURE);
}
type = V4L2_CAP_VIDEO_CAPTURE_MPLANE;
if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
perror("Failed to STEAMOFF CAP buffer");
exit(EXIT_FAILURE);
}
}
}
int main() {
FILE* image;
size_t imageSize;
image = fopen("sample.jpeg", "rb");
if (!image) {
perror("failed to open image file");
exit(EXIT_FAILURE);
}
fseek(image, 0, SEEK_END);
imageSize = ftell(image);
fseek(image, 0, SEEK_SET);
void* imageData = malloc(imageSize);
fread(imageData, 1, imageSize, image);
fclose(image);
//decodeJPEG(imageData, imageSize, 800, 450);
decodeJPEG(imageData, imageSize, 160, 384);
free(imageData);
return 0;
}
The output I am getting:
Code: Select all
$ gcc v4ltest.c && ./a.out
OUT (0): H.264
OUT (1): Motion-JPEG
CAP (0): Planar YUV 4:2:0
CAP (1): Planar YVU 4:2:0
CAP (2): Y/UV 4:2:0
CAP (3): Y/VU 4:2:0
CAP (4): Y/CbCr 4:2:0 (128b cols)
CAP (5): 16-bit RGB 5-6-5
----
Failed to request OUTPUT buffer: Cannot allocate memory