-
Notifications
You must be signed in to change notification settings - Fork 7
Fix linux uvc source #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this 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
UvcCameratoUvcSourcefor 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
- Adopted C++23 and std::expected for improved error handling across all source implementations
- Refactored UvcSource with std::string/std::vector instead of raw pointers for better memory safety
- 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.
| if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { | ||
| fprintf(stderr, "%s is no video capture device\n", | ||
| this->uvcConfig.dev_name); | ||
| exit(EXIT_FAILURE); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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"); |
|
|
||
| std::future<void> UvcSource::startProducing(const Source::ProducingConfiguration &config) | ||
| { | ||
| return std::async(std::launch::async, [this,&config]() { |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| return std::async(std::launch::async, [this,&config]() { | |
| return std::async(std::launch::async, [this, config]() { |
|
|
||
| if (req.count < 2) { | ||
| fprintf(stderr, "Insufficient buffer memory on %s\n", | ||
| this->uvcConfig.dev_name); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| this->uvcConfig.dev_name); | |
| this->uvcConfig.dev_name.c_str()); |
| fprintf(stderr, "%s does not support read i/o\n", | ||
| this->uvcConfig.dev_name); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| if (!(cap.capabilities & V4L2_CAP_READWRITE)) { | ||
| fprintf(stderr, "%s does not support read i/o\n", | ||
| this->uvcConfig.dev_name); | ||
| exit(EXIT_FAILURE); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| fprintf(stderr, "%s does not support " | ||
| "user pointer i/o\n", this->uvcConfig.dev_name); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
|
|
||
| if (-1 == xioctl(deviceFd, VIDIOC_QUERYCAP, &cap)) { | ||
| if (EINVAL == errno) { | ||
| fprintf(stderr, "%s is no V4L2 device\n", this->uvcConfig.dev_name); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
|
|
||
| // Filter out our custom options before passing to GTK | ||
| int gtk_argc = 1; | ||
| char *gtk_argv[argc]; |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| .height = 480}; | ||
| } | ||
| if (testSource == nullptr) { | ||
| std::cerr << "No test source specified. Use --testSourceYUV420 or --testSourceRGB, --uvcSource" << std::endl; |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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; |
…d improve error handling
There was a problem hiding this 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 = { |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| gtk_argv.push_back(argv[i]); | ||
| } |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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); |
| } | ||
|
|
||
| UvcSource::~UvcSource() { | ||
| close().get(); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| close().get(); | |
| try { | |
| close().get(); | |
| } catch (...) { | |
| // Suppress all exceptions in destructor to avoid std::terminate during stack unwinding. | |
| } |
|
|
||
| buffers.clear(); | ||
|
|
||
| if (deviceFd > 0) { |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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().
| if (deviceFd > 0) { | |
| if (deviceFd >= 0) { |
| 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); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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, {}}); |
| 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()); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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); |
|
|
||
| while (ioctl(deviceFd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) { | ||
| if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { | ||
| resolutions.emplace_back(frmsize.discrete.width, frmsize.discrete.height); |
Copilot
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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
AI
Dec 30, 2025
There was a problem hiding this comment.
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.
| 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); | |
| } |
There was a problem hiding this 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.
| 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; |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
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.
|
|
||
| class Source { | ||
| public: | ||
| struct OpenConfiguration { |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
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.
| 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). | |
| */ |
| [[nodiscard]] virtual std::future<void> stopProducing() { | ||
| this->captureConfiguration = ProducingConfiguration(); | ||
| return auvc::completed(); | ||
| }; |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
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.
| 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; | ||
| }; |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
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.
| 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()); |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
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.
| include(CTest) | ||
|
|
||
| set(CMAKE_CXX_STANDARD 20) | ||
| set(CMAKE_CXX_STANDARD 23) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
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.
| uint8_t id; | ||
| uint16_t width; | ||
| uint16_t height; |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
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.
| uint8_t id; | |
| uint16_t width; | |
| uint16_t height; | |
| const uint8_t id{0}; | |
| const uint16_t width{0}; | |
| const uint16_t height{0}; |
| } | ||
| 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"); |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
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.
| 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"); |
No description provided.