-
-
Save brccabral/638b320a8b2c62d3dd86d3814340805c to your computer and use it in GitHub Desktop.
Simple C++ program to capture a webcam frame in Linux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <linux/ioctl.h> | |
#include <linux/types.h> | |
#include <linux/v4l2-common.h> | |
#include <linux/v4l2-controls.h> | |
#include <linux/videodev2.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <sys/ioctl.h> | |
#include <sys/mman.h> | |
#include <string.h> | |
#include <fstream> | |
#include <string> | |
#include <chrono> | |
#include <thread> | |
using namespace std; | |
int main() | |
{ | |
// 1. Open the device | |
int fd; // A file descriptor to the video device | |
fd = open("/dev/video0", O_RDWR); | |
if (fd < 0) | |
{ | |
perror("Failed to open device, OPEN"); | |
return 1; | |
} | |
// 2. Ask the device if it can capture frames | |
v4l2_capability capability; | |
if (ioctl(fd, VIDIOC_QUERYCAP, &capability) < 0) | |
{ | |
// something went wrong... exit | |
perror("Failed to get device capabilities, VIDIOC_QUERYCAP"); | |
return 1; | |
} | |
// 3. Set Image format | |
v4l2_format imageFormat; | |
imageFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
imageFormat.fmt.pix.width = 1024; | |
imageFormat.fmt.pix.height = 1024; | |
imageFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; | |
imageFormat.fmt.pix.field = V4L2_FIELD_NONE; | |
// tell the device you are using this format | |
if (ioctl(fd, VIDIOC_S_FMT, &imageFormat) < 0) | |
{ | |
perror("Device could not set format, VIDIOC_S_FMT"); | |
return 1; | |
} | |
// 4. Request Buffers from the device | |
v4l2_requestbuffers requestBuffer = {0}; | |
requestBuffer.count = 1; // one request buffer | |
requestBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // request a buffer wich we an use for capturing frames | |
requestBuffer.memory = V4L2_MEMORY_MMAP; | |
if (ioctl(fd, VIDIOC_REQBUFS, &requestBuffer) < 0) | |
{ | |
perror("Could not request buffer from device, VIDIOC_REQBUFS"); | |
return 1; | |
} | |
// 5. Quety the buffer to get raw data ie. ask for the you requested buffer | |
// and allocate memory for it | |
v4l2_buffer queryBuffer = {0}; | |
queryBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
queryBuffer.memory = V4L2_MEMORY_MMAP; | |
queryBuffer.index = 0; | |
if (ioctl(fd, VIDIOC_QUERYBUF, &queryBuffer) < 0) | |
{ | |
perror("Device did not return the buffer information, VIDIOC_QUERYBUF"); | |
return 1; | |
} | |
// use a pointer to point to the newly created buffer | |
// mmap() will map the memory address of the device to | |
// an address in memory | |
char *buffer = (char *)mmap(NULL, queryBuffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, | |
fd, queryBuffer.m.offset); | |
memset(buffer, 0, queryBuffer.length); | |
// 6. Get a frame | |
// Create a new buffer type so the device knows whichbuffer we are talking about | |
v4l2_buffer bufferinfo; | |
memset(&bufferinfo, 0, sizeof(bufferinfo)); | |
bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
bufferinfo.memory = V4L2_MEMORY_MMAP; | |
bufferinfo.index = 0; | |
// Activate streaming | |
int type = bufferinfo.type; | |
if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) | |
{ | |
perror("Could not start streaming, VIDIOC_STREAMON"); | |
return 1; | |
} | |
this_thread::sleep_for(chrono::milliseconds(50)); | |
/***************************** Begin looping here *********************/ | |
// Queue the buffer | |
if (ioctl(fd, VIDIOC_QBUF, &bufferinfo) < 0) | |
{ | |
perror("Could not queue buffer, VIDIOC_QBUF"); | |
return 1; | |
} | |
// Dequeue the buffer | |
if (ioctl(fd, VIDIOC_DQBUF, &bufferinfo) < 0) | |
{ | |
perror("Could not dequeue the buffer, VIDIOC_DQBUF"); | |
return 1; | |
} | |
// Frames get written after dequeuing the buffer | |
cout << "Buffer has: " << (double)bufferinfo.bytesused / 1024 | |
<< " KBytes of data" << endl; | |
// Open the file | |
ofstream outFile; | |
if (!outFile) | |
{ | |
string error_msg = "Could not open ofstream"; | |
perror(error_msg.c_str()); | |
return 1; | |
} | |
string fileName = "webcam_output_" + to_string(time(0)) + ".jpeg"; | |
outFile.open(fileName.c_str(), ios::binary); | |
if (!outFile.is_open()) | |
{ | |
string error_msg = "Could not open " + fileName; | |
perror(error_msg.c_str()); | |
return 1; | |
} | |
// Write the data out to file | |
outFile.write(buffer, (double)bufferinfo.bytesused); | |
// Close the file | |
outFile.close(); | |
/******************************** end looping here **********************/ | |
// end streaming | |
if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0) | |
{ | |
perror("Could not end streaming, VIDIOC_STREAMOFF"); | |
return 1; | |
} | |
close(fd); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment