-
Notifications
You must be signed in to change notification settings - Fork 3.3k
[camera_windows] Restore image streaming support #8234
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.
Thanks for the contribution!
I haven't reviewed the entire PR yet, but one high-level note about the overall PR structure, carried over from my comment on the original PR.
…#8307) Add API support query, supportsImageStreaming for checking if the camera platform supports image streaming. As requested on this comment: #8234 (comment) Step 3 from the [changing federated plugins guide](https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins), split off from #8250 with a new Dart test added.
Add API support query, `supportsImageStreaming` for checking if the camera platform supports image streaming. As requested on this comment: #8234 (comment) Attempting to follow the [contribution guide wrt. changing federated plugins](https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins). There is no issue to link to, but should I create one?
From triage: This was waiting on the streaming support query API, so was blocked on that. I believe it's now unblocked. |
1bdbe66
to
46f38a6
Compare
Rebased on main and added the support query. |
@@ -93,6 +95,7 @@ class CameraPlugin : public flutter::Plugin, | |||
flutter::TextureRegistrar* texture_registrar_; | |||
flutter::BinaryMessenger* messenger_; | |||
std::vector<std::unique_ptr<Camera>> cameras_; | |||
std::shared_ptr<TaskRunner> task_runner_; |
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.
Why is this a shared_ptr
rather than a unique_ptr
. This class controls Camera
lifetimes; in what scenario would the task runner need to outlive it?
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 instance is shared with the cameras and ultimately with the CaptureController
.
In terms of lifetime, I guess there might be a small window where IMF is still running the callback when the plugin is being disposed, though I’m not really sure if that’s actually possible.
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 instance is shared with the cameras
Whose lifetime is, as I mentioned in my comment defined by CameraPlugin
.
and ultimately with the
CaptureController
.
Whose lifetime is controlled by Camera
(and thus also CameraPlugin
).
I'm still not seeing where any of the instances of this shared_ptr
can outlive CameraPlugin
.
In terms of lifetime, I guess there might be a small window where IMF is still running the callback when the plugin is being disposed, though I’m not really sure if that’s actually possible.
If the callback you linked to can be called on a deallocated capture controller, then this PR is introducing undefined behavior that is extremely likely to cause racy crashes. Please re-check the logic and make sure the threading is demonstrably correct; if you don't know whether that can happen, this this PR's threading isn't demonstrably correct yet.
{EncodableValue("width"), | ||
EncodableValue(static_cast<int64_t>(preview_frame_width_))}, | ||
{EncodableValue("length"), | ||
EncodableValue(static_cast<int64_t>(data_length))}}; |
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.
Rather than using raw method channels, can you use CameraApi::GetCodec
as the codec, and use a Pigeon-defined class here, to get strong typing?
(We've done something very similar with plugins that use platform view arguments, where we define a class in Pigeon that's not used in the Pigeon-managed APIs, and then use that class in other places where things are serialized via channel codecs.)
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.
Can you point to an example where you’ve done this? It seems like I’d have to switch to using a BasicMessageChannel
to take advantage of CameraApi::GetCodec
.
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.
Can you point to an example where you’ve done this?
There aren't any C++ examples, so I don't think they would be very helpful.
It seems like I’d have to switch to using a
BasicMessageChannel
to take advantage ofCameraApi::GetCodec
.
Why? The codec is passed when creating the event channel.
|
||
TaskRunnerWindow::~TaskRunnerWindow() { | ||
if (window_handle_) { | ||
DestroyWindow(window_handle_); |
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.
Can this use a unique_ptr
with a custom destructor, instead of managing the reference manually?
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.
Added a UniqueHWND
typedef for dealing with this.
void TaskRunnerWindow::ProcessTasks() { | ||
// Even though it would usually be sufficient to process only a single task | ||
// whenever we receive the message, if the message queue happens to be full, | ||
// we might not receive a message for each individual task. |
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.
Please don't use "we" in comments, as it's often ambiguous who "we" is (e.g., here, "we" is very vague... it seems to be a personification of the code, but which part?)
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.
Updated the comment.
if (tasks_.empty()) break; | ||
TaskClosure task = tasks_.front(); | ||
tasks_.pop(); | ||
task(); |
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.
Why is the queue staying locked for the entire execution of the task? Isn't it only the draining of the queue that needs the lock?
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 lock scope extended too far indeed, fixed, thanks.
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.
It doesn't look like any of the new capture controller logic has tests.
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.
Added test cases for the start/stop logic.
ef2acaa
to
653b574
Compare
Attempted to address some of the review issues, will continue with the remaining stuff later. |
653b574
to
68ad6b0
Compare
…tform (flutter#7951)" This reverts commit b3a5e33.
The logic is synchronous, so there is no need to have the `@async` annotation here. Addresses: flutter#7067 (comment)
When image stream frames are being received from Media Foundation, it appears to use a separate thread when calling the `OnSample` callback, which results in this error from: The 'plugins.flutter.io/camera_windows/imageStream' channel sent a message from native to Flutter on a non-platform thread. Platform channel messages must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must be fixed in the plugin or application code creating that channel. In order to ensure that we are only ever using the `EventChannel` from a platform thread, create a `TaskRunner` that is backed by a hidden window to submit the frames to the image stream. Based on the suggestion in this comment: flutter/flutter#134346 (comment)
Instead of a global `EventSink` that is re-used for each stream, create a dedicated `EventChannel` for each capture stream. The channel gets an unique name like `plugins.flutter.io/camera_android/imageStream/<cameraId>` for each camera instance. Addresses: flutter#7067 (comment)
We don’t really need to keep track of the subscription and controller in instance variables.
Given that subscribing to the `EventChannel` tells the capture controller to start the image stream and cancelling the subscription tells it to stop, we don’t actually need a separate method in `CameraPlatform` for stopping the stream.
68ad6b0
to
ef16b4a
Compare
ef16b4a
to
9326ab2
Compare
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.
(Updating my review state to correctly reflect the open questions from the last round of reviews.)
From triage: @liff Are you still planning on updating this PR based on the review feedback? |
Yes—though I don’t know when, I’ve been preoccupied with other stuff lately. |
…flutter#8307) Add API support query, supportsImageStreaming for checking if the camera platform supports image streaming. As requested on this comment: flutter#8234 (comment) Step 3 from the [changing federated plugins guide](https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins), split off from flutter#8250 with a new Dart test added.
Add API support query, `supportsImageStreaming` for checking if the camera platform supports image streaming. As requested on this comment: flutter#8234 (comment) Attempting to follow the [contribution guide wrt. changing federated plugins](https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins). There is no issue to link to, but should I create one?
…flutter#8307) Add API support query, supportsImageStreaming for checking if the camera platform supports image streaming. As requested on this comment: flutter#8234 (comment) Step 3 from the [changing federated plugins guide](https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins), split off from flutter#8250 with a new Dart test added.
Add API support query, `supportsImageStreaming` for checking if the camera platform supports image streaming. As requested on this comment: flutter#8234 (comment) Attempting to follow the [contribution guide wrt. changing federated plugins](https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changing-federated-plugins). There is no issue to link to, but should I create one?
Restores support for streaming images from the camera(s) in Windows.
I reverted #7951 as a starting point and attempted to resolve the issues that were raised:
startImageStream
andstopImageStream
methods are no longerasync
.EventSink
for the images. Each camera gets a dedicatedEventChannel
for the image data.In addition, the images are submitted to the
EventChannel
s from a dedicated hidden window -based task runner, based on flutter/engine#24232.Implemented with assistance from @jokerttu.
Fixes flutter/flutter#97542.
Pre-launch Checklist
dart format
.)[shared_preferences]
pubspec.yaml
with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.CHANGELOG.md
to add a description of the change, following repository CHANGELOG style, or this PR is exempt from CHANGELOG changes.///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.