Skip to content

Instantly share code, notes, and snippets.

@brccabral
Forked from mik30s/webcam_capture.cpp
Last active May 12, 2023 22:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brccabral/638b320a8b2c62d3dd86d3814340805c to your computer and use it in GitHub Desktop.
Save brccabral/638b320a8b2c62d3dd86d3814340805c to your computer and use it in GitHub Desktop.
Simple C++ program to capture a webcam frame in Linux
#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