Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH
"Location of ZoneMinder configuration, default system config directory")
set(ZM_CONFIG_SUBDIR "${ZM_CONFIG_DIR}/conf.d" CACHE PATH
"Location of ZoneMinder configuration subfolder, default: ZM_CONFIG_DIR/conf.d")
set(ZM_EXTRA_LIBS "" CACHE STRING
set(ZM_EXTRA_LIBS "v4l2" CACHE STRING
"A list of optional libraries, separated by semicolons, e.g. ssl;theora")
set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING
"MySQL engine to use with database, default: InnoDB")
Expand Down
25 changes: 16 additions & 9 deletions src/zm_local_camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#if ZM_HAS_V4L2
#include <libv4l2.h>
#endif // ZM_HAS_V4L2


#if ZM_HAS_V4L2

Expand All @@ -38,13 +42,15 @@
static unsigned int BigEndian;
static bool primed;

#if ZM_HAS_V4L2
static int vidioctl(int fd, int request, void *arg) {
int result = -1;
do {
result = ioctl(fd, request, arg);
result = v4l2_ioctl( fd, request, arg );
} while( result == -1 && errno == EINTR );
return result;
}
#endif // ZM_HAS_V4L2
Comment on lines 28 to +53
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#if ZM_HAS_V4L2 is currently based on finding linux/videodev2.h, but this patch unconditionally includes/uses libv4l2 (<libv4l2.h>, v4l2_open/ioctl/mmap/...) under the same macro. Systems with V4L2 headers but without libv4l2 installed will now fail to compile/link. Consider adding a separate CMake check for the V4L2::libv4l2 component and guarding the libv4l2 include/calls behind a dedicated macro, with a fallback to open/ioctl/mmap/close when libv4l2 is unavailable.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback


static _AVPIXELFORMAT getFfPixFormatFromV4lPalette(int v4l_version, int palette) {
_AVPIXELFORMAT pixFormat = AV_PIX_FMT_NONE;
Expand Down Expand Up @@ -481,7 +487,7 @@ int LocalCamera::Close() {

void LocalCamera::Initialise() {
Debug(3, "Opening video device %s", device.c_str());
if ((vid_fd = open(device.c_str(), O_RDWR, 0)) < 0)
if ( (vid_fd = v4l2_open( device.c_str(), O_RDWR, 0 )) < 0 )
Fatal("Failed to open video device %s: %s", device.c_str(), strerror(errno));

struct stat st;
Expand Down Expand Up @@ -672,7 +678,7 @@ void LocalCamera::Initialise() {
Fatal("Unable to query video buffer: %s", strerror(errno));

v4l2_data.buffers[i].length = vid_buf.length;
v4l2_data.buffers[i].start = mmap(nullptr, vid_buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, vid_buf.m.offset);
v4l2_data.buffers[i].start = v4l2_mmap(nullptr, vid_buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, vid_buf.m.offset);

if (v4l2_data.buffers[i].start == MAP_FAILED)
Fatal("Can't map video buffer %u (%u bytes) to memory: %s(%d)",
Expand Down Expand Up @@ -737,12 +743,12 @@ void LocalCamera::Terminate() {
for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) {
capturePictures[i] = nullptr;

if ( munmap(v4l2_data.buffers[i].start, v4l2_data.buffers[i].length) < 0 )
if ( v4l2_munmap(v4l2_data.buffers[i].start, v4l2_data.buffers[i].length) < 0 )
Error("Failed to munmap buffer %d: %s", i, strerror(errno));
}
}

close(vid_fd);
v4l2_close(vid_fd);
primed = false;
} // end LocalCamera::Terminate

Expand All @@ -757,7 +763,7 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) {
int enum_fd;

/* Open the device */
if ( (enum_fd = open(device.c_str(), O_RDWR, 0)) < 0 ) {
if ( (enum_fd = v4l2_open(device.c_str(), O_RDWR, 0)) < 0 ) {
Error("Automatic format selection failed to open video device %s: %s",
device.c_str(), strerror(errno));
return selected_palette;
Expand Down Expand Up @@ -841,7 +847,7 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) {
}

/* Close the device */
close(enum_fd);
v4l2_close(enum_fd);

return selected_palette;
} //uint32_t LocalCamera::AutoSelectFormat(int p_colours)
Expand All @@ -866,7 +872,7 @@ bool LocalCamera::GetCurrentSettings(
queryDevice = stringtf("/dev/video%d", devIndex);
}

if ((vid_fd = open(queryDevice.c_str(), O_RDWR)) <= 0) {
if ((vid_fd =v4l2_open(queryDevice.c_str(), O_RDWR)) <= 0) {
if (!device.empty()) {
Error("Failed to open video device %s: %s", queryDevice.c_str(), strerror(errno));
if (verbose) {
Expand Down Expand Up @@ -1149,7 +1155,7 @@ bool LocalCamera::GetCurrentSettings(
*(output_ptr-1) = '\n';
}

close(vid_fd);
v4l2_close(vid_fd);
if (!device.empty()) {
break;
}
Expand Down Expand Up @@ -1180,6 +1186,7 @@ int LocalCamera::Control(int vid_id, int newvalue) {
Warning("Given control value (%d) may be out-of-range", newvalue);
}
}
v4l2_close( vid_fd );
}
return vid_control.value;
}
Expand Down