Skip to content

Conversation

@vshcryabets
Copy link
Owner

No description provided.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the Linux UVC camera implementation with major API changes and modernizations:

  • Renames UvcCamera to UvcSource for consistency
  • Migrates to std::expected for better error handling (C++23)
  • Updates all implementations to return ExpectedFrame/ExpectedResolutions
  • Adds command-line argument support to GTK preview app
  • Improves error messages and exception handling

Key Changes

  1. Adopted C++23 and std::expected for improved error handling across all source implementations
  2. Refactored UvcSource with std::string/std::vector instead of raw pointers for better memory safety
  3. Enhanced GTK preview app with runtime source selection via command-line arguments

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
linux/apps/sdl_preview/uvccapture.cpp Updated to use renamed UvcSource class
linux/apps/gtk_preview/gtk_preview.cpp Major refactoring: added command-line argument parsing for source selection, removed preprocessor conditionals, made RGBA conversion dynamic
linux/CMakeLists.txt Updated C++ standard to C++23, enabled V4L2 module by default, updated comments
libausbc/src/main/jni/modules/sources/test/*.cpp Updated tests to handle ExpectedFrame/ExpectedResolutions return types
libausbc/src/main/jni/modules/sources/src/jni/*.cpp Updated JNI wrappers to handle std::expected and convert errors to Java exceptions
libausbc/src/main/jni/modules/sources/src/UvcSource.cpp New implementation replacing UvcCamera with improved C++ memory management
libausbc/src/main/jni/modules/sources/src/UvcCamera.cpp Deleted old implementation
libausbc/src/main/jni/modules/sources/src/*.cpp Updated all source implementations to return std::expected types
libausbc/src/main/jni/modules/sources/include/*.h Updated headers with new API signatures using ExpectedFrame/ExpectedResolutions
libausbc/src/main/jni/modules/images/src/ImageUseCases.cpp Added buffer size validation for YUYV conversion
libausbc/src/main/jni/modules/encoders/*.cpp Updated encoder to use new ExpectedResolutions type
libausbc/src/main/jni/modules/sources/CMakeLists.txt Updated source file from UvcCamera.cpp to UvcSource.cpp
Comments suppressed due to low confidence (1)

libausbc/src/main/jni/modules/sources/include/UvcSource.h:22

  • The header now uses std::string (line 22, 31) and std::vector (line 42, 47) but relies on transitive includes from Source.h. For better header independence and to avoid potential issues if Source.h changes its includes, consider explicitly including <string> and <vector> in this header file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 219 to 222
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf(stderr, "%s is no video capture device\n",
this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Using exit(EXIT_FAILURE) in library code prevents proper error handling and resource cleanup. Consider throwing a UvcException instead, which allows callers to handle the error appropriately and ensures that destructors are called for stack-allocated objects.

Copilot uses AI. Check for mistakes.
Comment on lines 212 to 238
fprintf(stderr, "%s is no V4L2 device\n", this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
} else {
throw UvcException(UvcException::Type::IoCtlError, "VIDIOC_QUERYCAP");
}
}

if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf(stderr, "%s is no video capture device\n",
this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
}
switch (io) {
case IO_METHOD_READ:
if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
fprintf(stderr, "%s does not support read i/o\n",
this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
}
break;

case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
fprintf(stderr, "%s does not support streaming i/o\n",
this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Using exit(EXIT_FAILURE) in library code prevents proper error handling and resource cleanup. Consider throwing a UvcException instead, which allows callers to handle the error appropriately and ensures that destructors are called for stack-allocated objects.

Suggested change
fprintf(stderr, "%s is no V4L2 device\n", this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
} else {
throw UvcException(UvcException::Type::IoCtlError, "VIDIOC_QUERYCAP");
}
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf(stderr, "%s is no video capture device\n",
this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
}
switch (io) {
case IO_METHOD_READ:
if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
fprintf(stderr, "%s does not support read i/o\n",
this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
}
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
fprintf(stderr, "%s does not support streaming i/o\n",
this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
throw UvcException(
UvcException::Type::WrongDevice,
std::string(this->uvcConfig.dev_name) + " is no V4L2 device");
} else {
throw UvcException(UvcException::Type::IoCtlError, "VIDIOC_QUERYCAP");
}
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
throw UvcException(
UvcException::Type::WrongDevice,
std::string(this->uvcConfig.dev_name) + " is no video capture device");
}
switch (io) {
case IO_METHOD_READ:
if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
throw UvcException(
UvcException::Type::WrongDevice,
std::string(this->uvcConfig.dev_name) + " does not support read i/o");
}
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
throw UvcException(
UvcException::Type::WrongDevice,
std::string(this->uvcConfig.dev_name) + " does not support streaming i/o");

Copilot uses AI. Check for mistakes.

std::future<void> UvcSource::startProducing(const Source::ProducingConfiguration &config)
{
return std::async(std::launch::async, [this,&config]() {
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Capturing 'config' by reference in an asynchronous lambda is dangerous because the config parameter may go out of scope before the lambda executes. The lambda should capture 'config' by value instead to avoid potential use-after-free bugs.

Suggested change
return std::async(std::launch::async, [this,&config]() {
return std::async(std::launch::async, [this, config]() {

Copilot uses AI. Check for mistakes.

if (req.count < 2) {
fprintf(stderr, "Insufficient buffer memory on %s\n",
this->uvcConfig.dev_name);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Using std::string directly with fprintf's %s format specifier is undefined behavior. Since dev_name is now a std::string instead of const char*, you must call .c_str() on it when passing to fprintf.

Suggested change
this->uvcConfig.dev_name);
this->uvcConfig.dev_name.c_str());

Copilot uses AI. Check for mistakes.
Comment on lines 227 to 228
fprintf(stderr, "%s does not support read i/o\n",
this->uvcConfig.dev_name);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Using std::string directly with fprintf's %s format specifier is undefined behavior. Since dev_name is now a std::string instead of const char*, you must call .c_str() on it when passing to fprintf.

Copilot uses AI. Check for mistakes.
Comment on lines 226 to 229
if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
fprintf(stderr, "%s does not support read i/o\n",
this->uvcConfig.dev_name);
exit(EXIT_FAILURE);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Using exit(EXIT_FAILURE) in library code prevents proper error handling and resource cleanup. Consider throwing a UvcException instead, which allows callers to handle the error appropriately and ensures that destructors are called for stack-allocated objects.

Copilot uses AI. Check for mistakes.
Comment on lines 133 to 134
fprintf(stderr, "%s does not support "
"user pointer i/o\n", this->uvcConfig.dev_name);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Using std::string directly with fprintf's %s format specifier is undefined behavior. Since dev_name is now a std::string instead of const char*, you must call .c_str() on it when passing to fprintf.

Copilot uses AI. Check for mistakes.

if (-1 == xioctl(deviceFd, VIDIOC_QUERYCAP, &cap)) {
if (EINVAL == errno) {
fprintf(stderr, "%s is no V4L2 device\n", this->uvcConfig.dev_name);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Using std::string directly with fprintf's %s format specifier is undefined behavior. Since dev_name is now a std::string instead of const char*, you must call .c_str() on it when passing to fprintf.

Copilot uses AI. Check for mistakes.

// Filter out our custom options before passing to GTK
int gtk_argc = 1;
char *gtk_argv[argc];
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Variable-length arrays (VLAs) are not standard C++ and may not be supported by all compilers. The size 'argc' is not a compile-time constant. Consider using std::vector instead to ensure portability and standard compliance.

Copilot uses AI. Check for mistakes.
.height = 480};
}
if (testSource == nullptr) {
std::cerr << "No test source specified. Use --testSourceYUV420 or --testSourceRGB, --uvcSource" << std::endl;
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The error message format is inconsistent. Consider changing to: "No test source specified. Use --testSourceYUV420, --testSourceRGB, or --uvcSource" for better clarity and consistency with the option names.

Suggested change
std::cerr << "No test source specified. Use --testSourceYUV420 or --testSourceRGB, --uvcSource" << std::endl;
std::cerr << "No test source specified. Use --testSourceYUV420, --testSourceRGB, or --uvcSource" << std::endl;

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 11 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const char *videodevice = "/dev/video0";

UvcCamera::Configuration deviceConfig = {
UvcSource::Configuration deviceConfig = {
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The type name should be UvcSource::OpenConfiguration instead of UvcSource::Configuration. The UvcSource class defines an OpenConfiguration struct (as seen in UvcSource.h line 30), not a Configuration struct.

Copilot uses AI. Check for mistakes.
Comment on lines 213 to 214
gtk_argv.push_back(argv[i]);
}
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Inconsistent indentation: the closing brace and the gtk_argv.push_back statement should be indented at the same level as the if statement. Currently the gtk_argv.push_back is indented incorrectly.

Copilot uses AI. Check for mistakes.
Comment on lines 220 to 227
throw UvcException(UvcException::Type::ReadError, "Doesn't support read i/o" + this->uvcConfig.dev_name);
}
break;

case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
throw UvcException(UvcException::Type::MmapError, "Doesn't support streaming i/o" + this->uvcConfig.dev_name);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Missing space in error message concatenation. The error message will read "Doesn't support streaming i/o/dev/video0" instead of "Doesn't support streaming i/o /dev/video0". Add a space before concatenating the device name.

Suggested change
throw UvcException(UvcException::Type::ReadError, "Doesn't support read i/o" + this->uvcConfig.dev_name);
}
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
throw UvcException(UvcException::Type::MmapError, "Doesn't support streaming i/o" + this->uvcConfig.dev_name);
throw UvcException(UvcException::Type::ReadError, "Doesn't support read i/o " + this->uvcConfig.dev_name);
}
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
throw UvcException(UvcException::Type::MmapError, "Doesn't support streaming i/o " + this->uvcConfig.dev_name);

Copilot uses AI. Check for mistakes.
}

UvcSource::~UvcSource() {
close().get();
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Calling .get() on a future in a destructor can be problematic. If the async operation throws an exception, .get() will rethrow it in the destructor, which will call std::terminate() if the destructor itself is called during stack unwinding. Consider handling exceptions or using a non-throwing cleanup approach in the destructor.

Suggested change
close().get();
try {
close().get();
} catch (...) {
// Suppress all exceptions in destructor to avoid std::terminate during stack unwinding.
}

Copilot uses AI. Check for mistakes.

buffers.clear();

if (deviceFd > 0) {
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The condition deviceFd > 0 is incorrect for file descriptor validation. File descriptor 0 (stdin) is valid. The check should be deviceFd >= 0 to properly handle all valid file descriptors, or compare against -1 which is the error value returned by open().

Suggested change
if (deviceFd > 0) {
if (deviceFd >= 0) {

Copilot uses AI. Check for mistakes.
Comment on lines 524 to 528
resolutions.emplace_back(frmsize.discrete.width, frmsize.discrete.height);
} else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
for (uint32_t w = frmsize.stepwise.min_width; w <= frmsize.stepwise.max_width; w += frmsize.stepwise.step_width) {
for (uint32_t h = frmsize.stepwise.min_height; h <= frmsize.stepwise.max_height; h += frmsize.stepwise.step_height) {
resolutions.emplace_back(w, h);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Incomplete initialization of Resolution struct. The Resolution struct has 4 fields (id, width, height, fps), but only width and height are being provided to emplace_back. The id and fps fields will be default-initialized (id=0, fps=empty vector), which may not be the intended behavior. Consider explicitly initializing all fields or documenting the expected default values.

Suggested change
resolutions.emplace_back(frmsize.discrete.width, frmsize.discrete.height);
} else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
for (uint32_t w = frmsize.stepwise.min_width; w <= frmsize.stepwise.max_width; w += frmsize.stepwise.step_width) {
for (uint32_t h = frmsize.stepwise.min_height; h <= frmsize.stepwise.max_height; h += frmsize.stepwise.step_height) {
resolutions.emplace_back(w, h);
resolutions.emplace_back(auvc::Resolution{0, frmsize.discrete.width, frmsize.discrete.height, {}});
} else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
for (uint32_t w = frmsize.stepwise.min_width; w <= frmsize.stepwise.max_width; w += frmsize.stepwise.step_width) {
for (uint32_t h = frmsize.stepwise.min_height; h <= frmsize.stepwise.max_height; h += frmsize.stepwise.step_height) {
resolutions.emplace_back(auvc::Resolution{0, w, h, {}});

Copilot uses AI. Check for mistakes.
Comment on lines 206 to 216
int gtk_argc = 1;
std::vector<char*> gtk_argv;
gtk_argv.push_back(argv[0]);
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--testSourceYUV420") != 0 &&
strcmp(argv[i], "--testSourceRGB") != 0 &&
strcmp(argv[i], "--uvcSource") != 0) {
gtk_argv.push_back(argv[i]);
}
}
status = g_application_run(G_APPLICATION(app), gtk_argc, gtk_argv.data());
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

The gtk_argc variable is incremented incorrectly. It's initialized to 1 (for argv[0]) but never incremented when custom arguments are filtered out. This will cause gtk_argc to not match the actual size of gtk_argv. You should either increment gtk_argc when pushing arguments to gtk_argv, or use gtk_argv.size() instead of gtk_argc.

Copilot uses AI. Check for mistakes.
Comment on lines 220 to 227
throw UvcException(UvcException::Type::ReadError, "Doesn't support read i/o" + this->uvcConfig.dev_name);
}
break;

case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
throw UvcException(UvcException::Type::MmapError, "Doesn't support streaming i/o" + this->uvcConfig.dev_name);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Missing space in error message concatenation. The error message will read "Doesn't support read i/o/dev/video0" instead of "Doesn't support read i/o /dev/video0". Add a space before concatenating the device name.

Suggested change
throw UvcException(UvcException::Type::ReadError, "Doesn't support read i/o" + this->uvcConfig.dev_name);
}
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
throw UvcException(UvcException::Type::MmapError, "Doesn't support streaming i/o" + this->uvcConfig.dev_name);
throw UvcException(UvcException::Type::ReadError, "Doesn't support read i/o " + this->uvcConfig.dev_name);
}
break;
case IO_METHOD_MMAP:
case IO_METHOD_USERPTR:
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
throw UvcException(UvcException::Type::MmapError, "Doesn't support streaming i/o " + this->uvcConfig.dev_name);

Copilot uses AI. Check for mistakes.

while (ioctl(deviceFd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) {
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
resolutions.emplace_back(frmsize.discrete.width, frmsize.discrete.height);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Incomplete initialization of Resolution struct. The Resolution struct has 4 fields (id, width, height, fps), but only width and height are being provided to emplace_back. The id and fps fields will be default-initialized (id=0, fps=empty vector), which may not be the intended behavior. Consider explicitly initializing all fields or documenting the expected default values.

Copilot uses AI. Check for mistakes.
Comment on lines 526 to 528
for (uint32_t w = frmsize.stepwise.min_width; w <= frmsize.stepwise.max_width; w += frmsize.stepwise.step_width) {
for (uint32_t h = frmsize.stepwise.min_height; h <= frmsize.stepwise.max_height; h += frmsize.stepwise.step_height) {
resolutions.emplace_back(w, h);
Copy link

Copilot AI Dec 30, 2025

Choose a reason for hiding this comment

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

Potential infinite loop if step_width or step_height is zero. The V4L2 API might return zero for step values in some cases, which would cause an infinite loop here. Add validation to ensure step values are greater than zero before entering the loop, or handle the zero case appropriately.

Suggested change
for (uint32_t w = frmsize.stepwise.min_width; w <= frmsize.stepwise.max_width; w += frmsize.stepwise.step_width) {
for (uint32_t h = frmsize.stepwise.min_height; h <= frmsize.stepwise.max_height; h += frmsize.stepwise.step_height) {
resolutions.emplace_back(w, h);
// Guard against zero step values to avoid potential infinite loops.
if (frmsize.stepwise.step_width == 0 || frmsize.stepwise.step_height == 0) {
// Fallback: at least record the minimum resolution rather than looping indefinitely.
resolutions.emplace_back(frmsize.stepwise.min_width, frmsize.stepwise.min_height);
} else {
for (uint32_t w = frmsize.stepwise.min_width; w <= frmsize.stepwise.max_width; w += frmsize.stepwise.step_width) {
for (uint32_t h = frmsize.stepwise.min_height; h <= frmsize.stepwise.max_height; h += frmsize.stepwise.step_height) {
resolutions.emplace_back(w, h);
}

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 35 out of 35 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +539 to +547
if (frmsize.stepwise.step_width == 0 || frmsize.stepwise.step_height == 0) {
resolutions.emplace_back(
auvc::Resolution {
.id = frmsize.index,
.width = static_cast<uint16_t>(frmsize.stepwise.min_width),
.height = static_cast<uint16_t>(frmsize.stepwise.min_height),
.fps = {}
});
continue;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

In the STEPWISE frame size handling, when step_width or step_height is 0, the code adds only the minimum resolution and then continues to the next iteration. However, according to V4L2 documentation, a step of 0 typically means any value is supported between min and max. The current implementation might miss valid resolutions. Consider handling this case more comprehensively or adding a comment explaining why only the minimum is added.

Copilot uses AI. Check for mistakes.

class Source {
public:
struct OpenConfiguration {
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

A 'tag' field was added to OpenConfiguration but it's not clear what this field is used for. Consider adding documentation to explain the purpose and expected usage of this field, especially since it's part of the public API.

Suggested change
struct OpenConfiguration {
struct OpenConfiguration {
/**
* Optional user-defined identifier for this source instance.
*
* The library does not interpret this value; it is provided so callers
* can attach an opaque tag (for example, for logging, debugging, or
* distinguishing between multiple open sources).
*/

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +80
[[nodiscard]] virtual std::future<void> stopProducing() {
this->captureConfiguration = ProducingConfiguration();
return auvc::completed();
};
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

The stopProducing method was changed from pure virtual to have a default implementation that resets captureConfiguration. This is a good improvement that reduces boilerplate in derived classes, but ensure all existing implementations are reviewed to verify they don't rely on being forced to override this method.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +26
class UvcException : public std::exception {
public:
enum Type {
Other,
WrongDevice,
CantOpenDevice,
CantCloseDevice,
IoCtlError,
MmapError,
ReadError,
FrameTimeout,
DeviceNotOpened
};
private:
Type errorType;
std::string errorMessage;
public:
UvcException(Type type, const std::string& message);
const char* what() const noexcept override;
};
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

UvcException is defined outside the SUPPORT_V4L2 conditional compilation guard, but it's only used within UvcSource which is inside the guard. Consider moving the UvcException class definition inside the #ifdef SUPPORT_V4L2 block to avoid defining it when it won't be used.

Copilot uses AI. Check for mistakes.
Comment on lines +211 to +221
int gtk_argc = 1;
std::vector<char*> gtk_argv;
gtk_argv.push_back(argv[0]);
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--testSourceYUV420") != 0 &&
strcmp(argv[i], "--testSourceRGB") != 0 &&
strcmp(argv[i], "--uvcSource") != 0) {
gtk_argv.push_back(argv[i]);
}
}
status = g_application_run(G_APPLICATION(app), gtk_argc, gtk_argv.data());
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

The gtk_argc variable is hardcoded to 1 but should reflect the actual size of the gtk_argv vector. After filtering out custom options, gtk_argc should be set to gtk_argv.size() instead of being fixed at 1. This would cause GTK to not receive any of its own command-line arguments that might be passed.

Copilot uses AI. Check for mistakes.
include(CTest)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 23)
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

The upgrade to C++23 is necessary because std::expected was introduced in C++23. However, note that this requires compiler support for C++23, which may not be available on all platforms. Consider documenting this requirement or checking if C++23 is widely supported in your target environments.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +33
uint8_t id;
uint16_t width;
uint16_t height;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

The const qualifiers were removed from the Resolution struct members (id, width, height). While this makes the struct more flexible for assignments, it may break existing code that relied on these members being immutable. Consider whether this breaking change is intentional and necessary.

Suggested change
uint8_t id;
uint16_t width;
uint16_t height;
const uint8_t id{0};
const uint16_t width{0};
const uint16_t height{0};

Copilot uses AI. Check for mistakes.
}
if (consumer == nullptr) {
throw SourceError(SourceError::SOURCE_ERROR_WRONG_CONFIG, "No consumer or frame callback set");
throw auvc::SourceError(auvc::SourceErrorCode::SOURCE_ERROR_WRONG_CONFIG, "No consumer");
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

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

The error message was changed from "No consumer or frame callback set" to just "No consumer". If there was previously support for both consumer and frame callback options, this message change might be misleading. Verify that frame callback support has been removed or clarify the message.

Suggested change
throw auvc::SourceError(auvc::SourceErrorCode::SOURCE_ERROR_WRONG_CONFIG, "No consumer");
throw auvc::SourceError(auvc::SourceErrorCode::SOURCE_ERROR_WRONG_CONFIG, "No consumer or frame callback set");

Copilot uses AI. Check for mistakes.
@vshcryabets vshcryabets merged commit c14b345 into master Jan 3, 2026
7 checks passed
@vshcryabets vshcryabets deleted the fix/linux-uvc-source branch January 3, 2026 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants