-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[camera_windows] Fixes initializing video preview with latest webcam driver #10303
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?
Changes from all commits
9430f1d
9b76cff
ca6dbb3
6a1ebca
f651d9c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ name: camera_windows | |
| description: A Flutter plugin for getting information about and controlling the camera on Windows. | ||
| repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_windows | ||
| issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 | ||
| version: 0.2.6+2 | ||
| version: 0.2.7 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0.2.6+3, since this is a bugfix, per Dart conventions for pre-1.0 versioning. |
||
|
|
||
| environment: | ||
| sdk: ^3.7.0 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -371,7 +371,8 @@ void CaptureControllerImpl::TakePicture(const std::string& file_path) { | |
| // Check MF_CAPTURE_ENGINE_PHOTO_TAKEN event handling | ||
| // for response process. | ||
| hr = photo_handler_->TakePhoto(file_path, capture_engine_.Get(), | ||
| base_capture_media_type_.Get()); | ||
| base_capture_media_type_.Get(), | ||
| photo_source_stream_index_); | ||
| if (FAILED(hr)) { | ||
| // Destroy photo handler on error cases to make sure state is resetted. | ||
| photo_handler_ = nullptr; | ||
|
|
@@ -398,6 +399,42 @@ uint32_t CaptureControllerImpl::GetMaxPreviewHeight() const { | |
| } | ||
| } | ||
|
|
||
| enum class PlatformStreamCategory { video, photo, audio }; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Freestanding internal helpers like enums and functions should be declared at the top of the file in an anonymous namespace. |
||
|
|
||
| HRESULT GetMediaSourceStreamIndex( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs a declaration comment, per the style guide, explaining what it does and what its overall role is. |
||
| IMFCaptureSource* source, DWORD* source_stream_index, | ||
| PlatformStreamCategory target_stream_category) { | ||
| DWORD stream_count = 0; | ||
| HRESULT hr = source->GetDeviceStreamCount(&stream_count); | ||
| if (FAILED(hr)) { | ||
| return hr; | ||
| } | ||
|
|
||
| for (DWORD stream_index = 0; stream_index < stream_count; stream_index++) { | ||
| MF_CAPTURE_ENGINE_STREAM_CATEGORY stream_category; | ||
| hr = source->GetDeviceStreamCategory(stream_index, &stream_category); | ||
| if (FAILED(hr)) { | ||
| return hr; | ||
| } | ||
|
|
||
| if ((target_stream_category == PlatformStreamCategory::video && | ||
| (stream_category == MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_PREVIEW || | ||
| stream_category == | ||
| MF_CAPTURE_ENGINE_STREAM_CATEGORY_VIDEO_CAPTURE)) || | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are preview and capture streams being treated interchangeably? |
||
| (target_stream_category == PlatformStreamCategory::photo && | ||
| (stream_category == | ||
| MF_CAPTURE_ENGINE_STREAM_CATEGORY_PHOTO_DEPENDENT || | ||
| stream_category == | ||
| MF_CAPTURE_ENGINE_STREAM_CATEGORY_PHOTO_INDEPENDENT)) || | ||
| (target_stream_category == PlatformStreamCategory::audio && | ||
| stream_category == MF_CAPTURE_ENGINE_STREAM_CATEGORY_AUDIO)) { | ||
| *source_stream_index = stream_index; | ||
| return S_OK; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is unconditionally returning only the first matching stream; why is that correct? How did you determine that unconditionally using the first stream is never a regression compared to using the preferred stream? That seems counterintuitive. |
||
| } | ||
| } | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| // Finds best media type for given source stream index and max height; | ||
| bool FindBestMediaType(DWORD source_stream_index, IMFCaptureSource* source, | ||
| IMFMediaType** target_media_type, uint32_t max_height, | ||
|
|
@@ -472,23 +509,48 @@ HRESULT CaptureControllerImpl::FindBaseMediaTypes() { | |
|
|
||
| HRESULT CaptureControllerImpl::FindBaseMediaTypesForSource( | ||
| IMFCaptureSource* source) { | ||
| HRESULT hr; | ||
| hr = GetMediaSourceStreamIndex(source, &video_source_stream_index_, | ||
| PlatformStreamCategory::video); | ||
| if (FAILED(hr)) { | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| hr = GetMediaSourceStreamIndex(source, &photo_source_stream_index_, | ||
| PlatformStreamCategory::photo); | ||
| if (FAILED(hr)) { | ||
| // Use the same source stream for photo as video on fail | ||
| photo_source_stream_index_ = video_source_stream_index_; | ||
| } | ||
|
|
||
| if (media_settings_.enable_audio()) { | ||
| hr = GetMediaSourceStreamIndex(source, &audio_source_stream_index_, | ||
| PlatformStreamCategory::audio); | ||
| if (FAILED(hr)) { | ||
| return E_FAIL; | ||
| } | ||
| } | ||
|
|
||
| // Find base media type for previewing. | ||
| if (!FindBestMediaType( | ||
| (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW, | ||
| source, base_preview_media_type_.GetAddressOf(), | ||
| GetMaxPreviewHeight(), &preview_frame_width_, | ||
| &preview_frame_height_)) { | ||
| if (!FindBestMediaType(video_source_stream_index_, source, | ||
| base_preview_media_type_.GetAddressOf(), | ||
| GetMaxPreviewHeight(), &preview_frame_width_, | ||
| &preview_frame_height_)) { | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| // Find base media type for record and photo capture. | ||
| if (!FindBestMediaType( | ||
| (DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_RECORD, | ||
| source, base_capture_media_type_.GetAddressOf(), 0xffffffff, nullptr, | ||
| nullptr)) { | ||
| hr = source->SetCurrentDeviceMediaType(video_source_stream_index_, | ||
| base_preview_media_type_.Get()); | ||
| if (FAILED(hr)) { | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| // Find base media type for record and photo capture. | ||
| if (!FindBestMediaType(video_source_stream_index_, source, | ||
| base_capture_media_type_.GetAddressOf(), 0xffffffff, | ||
| nullptr, nullptr)) { | ||
| return E_FAIL; | ||
| } | ||
| return S_OK; | ||
| } | ||
|
|
||
|
|
@@ -523,8 +585,9 @@ void CaptureControllerImpl::StartRecord(const std::string& file_path) { | |
|
|
||
| // Check MF_CAPTURE_ENGINE_RECORD_STARTED event handling for response | ||
| // process. | ||
| hr = record_handler_->StartRecord(file_path, capture_engine_.Get(), | ||
| base_capture_media_type_.Get()); | ||
| hr = record_handler_->StartRecord( | ||
| file_path, capture_engine_.Get(), base_capture_media_type_.Get(), | ||
| video_source_stream_index_, audio_source_stream_index_); | ||
| if (FAILED(hr)) { | ||
| // Destroy record handler on error cases to make sure state is resetted. | ||
| record_handler_ = nullptr; | ||
|
|
@@ -610,9 +673,9 @@ void CaptureControllerImpl::StartPreview() { | |
|
|
||
| // Check MF_CAPTURE_ENGINE_PREVIEW_STARTED event handling for response | ||
| // process. | ||
| hr = preview_handler_->StartPreview(capture_engine_.Get(), | ||
| base_preview_media_type_.Get(), | ||
| capture_engine_callback_handler_.Get()); | ||
| hr = preview_handler_->StartPreview( | ||
| capture_engine_.Get(), base_preview_media_type_.Get(), | ||
| video_source_stream_index_, capture_engine_callback_handler_.Get()); | ||
|
|
||
| if (FAILED(hr)) { | ||
| // Destroy preview handler on error cases to make sure state is resetted. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,8 +52,10 @@ class PreviewHandler { | |
| // for the actual video capture media type. | ||
| // sample_callback: A pointer to capture engine listener. | ||
| // This is set as sample callback for preview sink. | ||
| // source_stream_index: Integer index of the preview source stream in | ||
| // MediaFoundation. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please update all of these to do consistent indenting.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, the order should match the parameter order. |
||
| HRESULT StartPreview(IMFCaptureEngine* capture_engine, | ||
| IMFMediaType* base_media_type, | ||
| IMFMediaType* base_media_type, DWORD source_stream_index, | ||
| CaptureEngineListener* sample_callback); | ||
|
|
||
| // Stops existing recording. | ||
|
|
@@ -90,6 +92,7 @@ class PreviewHandler { | |
| // Initializes record sink for video file capture. | ||
| HRESULT InitPreviewSink(IMFCaptureEngine* capture_engine, | ||
| IMFMediaType* base_media_type, | ||
| DWORD source_stream_index, | ||
| CaptureEngineListener* sample_callback); | ||
|
|
||
| PreviewState preview_state_ = PreviewState::kNotStarted; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,9 +48,14 @@ class RecordHandler { | |
| // the actual recording. | ||
| // base_media_type: A pointer to base media type used as a base | ||
| // for the actual video capture media type. | ||
| // video_source_stream_index: Integer index of the video source stream in | ||
| // MediaFoundation. audio_source_stream_index: Integer index of the audio | ||
| // source stream in MediaFoundation. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These need to be formatted the same as the other comments, with separate lines and proper indenting. |
||
| HRESULT StartRecord(const std::string& file_path, | ||
| IMFCaptureEngine* capture_engine, | ||
| IMFMediaType* base_media_type); | ||
| IMFMediaType* base_media_type, | ||
| DWORD video_source_stream_index, | ||
| DWORD audio_source_stream_index); | ||
|
|
||
| // Stops existing recording. | ||
| // | ||
|
|
@@ -83,7 +88,9 @@ class RecordHandler { | |
| private: | ||
| // Initializes record sink for video file capture. | ||
| HRESULT InitRecordSink(IMFCaptureEngine* capture_engine, | ||
| IMFMediaType* base_media_type); | ||
| IMFMediaType* base_media_type, | ||
| DWORD video_source_stream_index, | ||
| DWORD audio_source_stream_index); | ||
|
|
||
| const PlatformMediaSettings media_settings_; | ||
| int64_t recording_start_timestamp_us_ = -1; | ||
|
|
||
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.
This needs a period, per the linked style guide.