-
Notifications
You must be signed in to change notification settings - Fork 407
Simplify CameraView MJPEG #4213
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
base: main
Are you sure you want to change the base?
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 pull request refactors the MJPEG camera player implementation by extracting business logic into a dedicated ViewModel and reorganizing files into an MJPEG subfolder. The refactoring simplifies the architecture by replacing a UIViewControllerRepresentable wrapper with a pure SwiftUI implementation using an ObservableObject ViewModel pattern.
Changes:
- Created
CameraMJPEGPlayerViewModelto handle MJPEG streaming logic and state management - Simplified
CameraMJPEGPlayerViewto use SwiftUI with the new ViewModel instead of UIViewControllerRepresentable - Reorganized files into
Sources/App/Cameras/CameraPlayer/MJPEG/subfolder for better code organization
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| Sources/App/Cameras/CameraPlayer/MJPEG/CameraMJPEGPlayerViewModel.swift | New ViewModel containing MJPEG streaming logic, state management, and API interactions |
| Sources/App/Cameras/CameraPlayer/MJPEG/CameraMJPEGPlayerView.swift | Refactored SwiftUI view using the new ViewModel pattern instead of UIKit wrapper |
| Sources/App/Cameras/CameraPlayer/CameraMJPEGPlayerView.swift | Deleted old implementation that used UIViewControllerRepresentable |
| HomeAssistant.xcodeproj/project.pbxproj | Updated Xcode project structure to include new ViewModel and reorganized file structure |
| DispatchQueue.main.async { [weak self] in | ||
| self?.isLoading = false | ||
| self?.hasStarted = false | ||
| } |
Copilot
AI
Jan 12, 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 isLoading and hasStarted flags are being set to false in the pipe callback regardless of whether the stream successfully started or encountered an error. When the stream URL is successfully obtained and startStream is called, these flags are immediately reset before the stream actually begins. This means isLoading will be false even if the stream hasn't received its first frame yet, potentially showing incorrect loading state to the user.
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.
@copilot open a new pull request to apply changes based on this feedback
Sources/App/Cameras/CameraPlayer/MJPEG/CameraMJPEGPlayerViewModel.swift
Outdated
Show resolved
Hide resolved
| DispatchQueue.main.async { [weak self] in | ||
| self?.hasStarted = false |
Copilot
AI
Jan 12, 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 hasStarted flag is being modified from a background thread without being dispatched to the main thread, even though this class is marked @mainactor. The assignment on line 43 should be wrapped in a DispatchQueue.main.async block to maintain thread safety.
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.
@copilot open a new pull request to apply changes based on this feedback
Sources/App/Cameras/CameraPlayer/MJPEG/CameraMJPEGPlayerView.swift
Outdated
Show resolved
Hide resolved
Sources/App/Cameras/CameraPlayer/MJPEG/CameraMJPEGPlayerViewModel.swift
Outdated
Show resolved
Hide resolved
…del.swift Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
…acks (#4216) ## Summary The `CameraMJPEGPlayerViewModel` class is marked `@MainActor` but was accessing MainActor-isolated properties from background thread callbacks without proper synchronization. This creates race conditions and potential crashes. **Changes:** - Wrapped `startStream()` method call in `DispatchQueue.main.async` when invoked from background `.pipe` callback - Wrapped `errorMessage` property assignment in `DispatchQueue.main.async` in the rejected case - Removed redundant `hasStarted = false` assignment that could race with the cleanup block All MainActor-isolated members are now accessed exclusively on the main thread. ## Screenshots <!-- If this is a user-facing change not in the frontend, please include screenshots in light and dark mode. --> N/A - Internal thread safety fix with no UI changes ## Link to pull request in Documentation repository <!-- Pull requests that add, change or remove functionality must have a corresponding pull request in the Companion App Documentation repository (https://github.com/home-assistant/companion.home-assistant). Please add the number of this pull request after the "#" --> N/A - Internal implementation change ## Any other notes This PR merges into #4213 and addresses feedback from #4213 (comment) <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: bgoncal <[email protected]>
…4215) ## Summary Addresses thread safety issue where `hasStarted` flag was being modified from background threads despite `@MainActor` annotation on the class. **Changes:** - Replace direct `hasStarted = false` assignment in error handler with `stop()` call to ensure flag modification happens on main thread - All `hasStarted` modifications now properly dispatched to main thread via `DispatchQueue.main.async` or through `@MainActor` methods ## Screenshots N/A - Internal state management change with no UI impact ## Link to pull request in Documentation repository Documentation: home-assistant/companion.home-assistant# ## Any other notes This is a stacked PR that merges into #4213 to address review feedback from #4213 (comment) <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4213 +/- ##
=======================================
Coverage ? 44.85%
=======================================
Files ? 250
Lines ? 14414
Branches ? 0
=======================================
Hits ? 6466
Misses ? 7948
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Summary
Screenshots
Link to pull request in Documentation repository
Documentation: home-assistant/companion.home-assistant#
Any other notes