Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/camera/camera_windows/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 0.2.7

* Fixes initializing video preview with latest webcam driver [#140014](https://github.com/flutter/flutter/issues/140014)
* Updates minimum supported SDK version to Flutter 3.29/Dart 3.7.

## 0.2.6+2
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera_windows/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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

environment:
sdk: ^3.7.0
Expand Down
95 changes: 79 additions & 16 deletions packages/camera/camera_windows/windows/capture_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -398,6 +399,42 @@ uint32_t CaptureControllerImpl::GetMaxPreviewHeight() const {
}
}

enum class PlatformStreamCategory { video, photo, audio };

HRESULT GetMediaSourceStreamIndex(
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)) ||
(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;
}
}
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,
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions packages/camera/camera_windows/windows/capture_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ class CaptureControllerImpl : public CaptureController,

uint32_t preview_frame_width_ = 0;
uint32_t preview_frame_height_ = 0;
DWORD video_source_stream_index_ = (DWORD)MF_CAPTURE_ENGINE_MEDIASOURCE;
DWORD photo_source_stream_index_ = (DWORD)MF_CAPTURE_ENGINE_MEDIASOURCE;
DWORD audio_source_stream_index_ = (DWORD)MF_CAPTURE_ENGINE_MEDIASOURCE;
UINT dx_device_reset_token_ = 0;
std::unique_ptr<RecordHandler> record_handler_;
std::unique_ptr<PreviewHandler> preview_handler_;
Expand Down
14 changes: 8 additions & 6 deletions packages/camera/camera_windows/windows/photo_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ HRESULT BuildMediaTypeForPhotoCapture(IMFMediaType* src_media_type,
}

HRESULT PhotoHandler::InitPhotoSink(IMFCaptureEngine* capture_engine,
IMFMediaType* base_media_type) {
IMFMediaType* base_media_type,
DWORD source_stream_index) {
assert(capture_engine);
assert(base_media_type);

Expand Down Expand Up @@ -99,9 +100,8 @@ HRESULT PhotoHandler::InitPhotoSink(IMFCaptureEngine* capture_engine,
}

DWORD photo_sink_stream_index;
hr = photo_sink_->AddStream(
(DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO,
photo_media_type.Get(), nullptr, &photo_sink_stream_index);
hr = photo_sink_->AddStream(source_stream_index, photo_media_type.Get(),
nullptr, &photo_sink_stream_index);
if (FAILED(hr)) {
photo_sink_ = nullptr;
return hr;
Expand All @@ -118,14 +118,16 @@ HRESULT PhotoHandler::InitPhotoSink(IMFCaptureEngine* capture_engine,

HRESULT PhotoHandler::TakePhoto(const std::string& file_path,
IMFCaptureEngine* capture_engine,
IMFMediaType* base_media_type) {
IMFMediaType* base_media_type,
DWORD source_stream_index) {
assert(!file_path.empty());
assert(capture_engine);
assert(base_media_type);

file_path_ = file_path;

HRESULT hr = InitPhotoSink(capture_engine, base_media_type);
HRESULT hr =
InitPhotoSink(capture_engine, base_media_type, source_stream_index);
if (FAILED(hr)) {
return hr;
}
Expand Down
6 changes: 4 additions & 2 deletions packages/camera/camera_windows/windows/photo_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ class PhotoHandler {
// base_media_type: A pointer to base media type used as a base
// for the actual photo capture media type.
// file_path: A string that hold file path for photo capture.
// source_stream_index: Integer index of the source stream in MediaFoundation.
HRESULT TakePhoto(const std::string& file_path,
IMFCaptureEngine* capture_engine,
IMFMediaType* base_media_type);
IMFMediaType* base_media_type, DWORD source_stream_index);

// Set the photo handler recording state to: kIdle.
void OnPhotoTaken();
Expand All @@ -68,7 +69,8 @@ class PhotoHandler {
private:
// Initializes record sink for video file capture.
HRESULT InitPhotoSink(IMFCaptureEngine* capture_engine,
IMFMediaType* base_media_type);
IMFMediaType* base_media_type,
DWORD source_stream_index);

std::string file_path_;
PhotoState photo_state_ = PhotoState::kNotStarted;
Expand Down
12 changes: 6 additions & 6 deletions packages/camera/camera_windows/windows/preview_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ HRESULT BuildMediaTypeForVideoPreview(IMFMediaType* src_media_type,

HRESULT PreviewHandler::InitPreviewSink(
IMFCaptureEngine* capture_engine, IMFMediaType* base_media_type,
CaptureEngineListener* sample_callback) {
DWORD source_stream_index, CaptureEngineListener* sample_callback) {
assert(capture_engine);
assert(base_media_type);
assert(sample_callback);
Expand Down Expand Up @@ -94,9 +94,8 @@ HRESULT PreviewHandler::InitPreviewSink(
}

DWORD preview_sink_stream_index;
hr = preview_sink_->AddStream(
(DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW,
preview_media_type.Get(), nullptr, &preview_sink_stream_index);
hr = preview_sink_->AddStream(source_stream_index, preview_media_type.Get(),
nullptr, &preview_sink_stream_index);

if (FAILED(hr)) {
return hr;
Expand All @@ -115,12 +114,13 @@ HRESULT PreviewHandler::InitPreviewSink(

HRESULT PreviewHandler::StartPreview(IMFCaptureEngine* capture_engine,
IMFMediaType* base_media_type,
DWORD source_stream_index,
CaptureEngineListener* sample_callback) {
assert(capture_engine);
assert(base_media_type);

HRESULT hr =
InitPreviewSink(capture_engine, base_media_type, sample_callback);
HRESULT hr = InitPreviewSink(capture_engine, base_media_type,
source_stream_index, sample_callback);

if (FAILED(hr)) {
return hr;
Expand Down
5 changes: 4 additions & 1 deletion packages/camera/camera_windows/windows/preview_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
HRESULT StartPreview(IMFCaptureEngine* capture_engine,
IMFMediaType* base_media_type,
IMFMediaType* base_media_type, DWORD source_stream_index,
CaptureEngineListener* sample_callback);

// Stops existing recording.
Expand Down Expand Up @@ -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;
Expand Down
25 changes: 15 additions & 10 deletions packages/camera/camera_windows/windows/record_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ inline HRESULT SetAudioBitrate(IMFMediaType* pType, UINT32 bitrate) {
}

HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine,
IMFMediaType* base_media_type) {
IMFMediaType* base_media_type,
DWORD video_source_stream_index,
DWORD audio_source_stream_index) {
assert(!file_path_.empty());
assert(capture_engine);
assert(base_media_type);
Expand Down Expand Up @@ -189,9 +191,9 @@ HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine,
}

DWORD video_record_sink_stream_index;
hr = record_sink_->AddStream(
(DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_RECORD,
video_record_media_type.Get(), nullptr, &video_record_sink_stream_index);
hr = record_sink_->AddStream(video_source_stream_index,
video_record_media_type.Get(), nullptr,
&video_record_sink_stream_index);
if (FAILED(hr)) {
return hr;
}
Expand All @@ -210,10 +212,9 @@ HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine,
}

DWORD audio_record_sink_stream_index;
hr = record_sink_->AddStream(
(DWORD)MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_AUDIO,
audio_record_media_type.Get(), nullptr,
&audio_record_sink_stream_index);
hr = record_sink_->AddStream(audio_source_stream_index,
audio_record_media_type.Get(), nullptr,
&audio_record_sink_stream_index);
}

if (FAILED(hr)) {
Expand All @@ -228,7 +229,9 @@ HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine,

HRESULT RecordHandler::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) {
assert(!file_path.empty());
assert(capture_engine);
assert(base_media_type);
Expand All @@ -237,7 +240,9 @@ HRESULT RecordHandler::StartRecord(const std::string& file_path,
recording_start_timestamp_us_ = -1;
recording_duration_us_ = 0;

HRESULT hr = InitRecordSink(capture_engine, base_media_type);
HRESULT hr =
InitRecordSink(capture_engine, base_media_type, video_source_stream_index,
audio_source_stream_index);
if (FAILED(hr)) {
return hr;
}
Expand Down
11 changes: 9 additions & 2 deletions packages/camera/camera_windows/windows/record_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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.
//
Expand Down Expand Up @@ -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;
Expand Down
Loading