Skip to content

Commit 37548ed

Browse files
committed
fix(Windows): Send messages on main thread (workaround).
fixes #379
1 parent 4c4ba96 commit 37548ed

6 files changed

+104
-33
lines changed

record_windows/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 1.0.5
2+
* fix: Send messages on main thread (workaround).
3+
14
## 1.0.4
25
* fix: Process path as wide string (UTF-16).
36

record_windows/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: record_windows
22
description: Windows specific implementation for record package called by record_platform_interface.
3-
version: 1.0.4
3+
version: 1.0.5
44
homepage: https://github.com/llfbandit/record/tree/master/record_windows
55

66
environment:

record_windows/windows/record.cpp

+4-26
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,5 @@
1-
/*
2-
*
3-
* https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/codec-query
4-
* https://learn.microsoft.com/en-us/uwp/api/windows.media.core.codecsubtypes?view=winrt-22621
5-
*
6-
* https://github.com/microsoft/Windows-universal-samples/blob/main/Samples/CameraStarterKit/cpp/MainPage.xaml.h
7-
* https://github.com/microsoft/Windows-universal-samples/blob/main/Samples/CameraStarterKit/cpp/MainPage.xaml.cpp
8-
*
9-
* https://learn.microsoft.com/en-us/windows/win32/medfound/audio-video-capture
10-
* WMF FLAC https://stackoverflow.com/questions/48930499/how-do-i-encode-raw-48khz-32bits-pcm-to-flac-using-microsoft-media-foundation
11-
* WMF AAC https://learn.microsoft.com/en-us/windows/win32/medfound/aac-encoder
12-
* WMF https://learn.microsoft.com/en-us/windows/win32/medfound/audio-video-capture-in-media-foundation
13-
* https://learn.microsoft.com/en-us/windows/win32/medfound/tutorial--using-the-sink-writer-to-encode-video
14-
* https://learn.microsoft.com/en-us/windows/win32/medfound/audio-subtype-guids
15-
* https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/multimedia/mediafoundation/wavsink
16-
* https://learn.microsoft.com/fr-fr/windows/win32/api/_mf/
17-
* https://stackoverflow.com/questions/12917256/windows-media-foundation-recording-audio
18-
* https://github.com/sipsorcery/mediafoundationsamples/blob/master/MFAudioCaptureToSAR/MFAudioCaptureToSAR.cpp
19-
* https://chromium-review.googlesource.com/c/chromium/src/+/3293969
20-
*
21-
* https://learn.microsoft.com/en-us/windows/win32/medfound/uncompressed-audio-media-types
22-
*
23-
* https://learn.microsoft.com/en-us/windows/win32/medfound/tutorial--encoding-an-mp4-file-
24-
*/
25-
261
#include "record.h"
2+
#include "record_windows_plugin.h"
273

284
namespace record_windows
295
{
@@ -308,7 +284,9 @@ namespace record_windows
308284
m_recordState = state;
309285

310286
if (m_stateEventHandler) {
311-
m_stateEventHandler->Success(std::make_unique<flutter::EncodableValue>(state));
287+
RecordWindowsPlugin::RunOnMainThread([this, state]() -> void {
288+
m_stateEventHandler->Success(std::make_unique<flutter::EncodableValue>(state));
289+
});
312290
}
313291
}
314292

record_windows/windows/record_readercallback.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "record.h"
2+
#include "record_windows_plugin.h"
23

34
namespace record_windows
45
{
@@ -68,7 +69,10 @@ namespace record_windows
6869
// Send data to stream when there's no writer
6970
if (m_recordEventHandler && !m_pWriter) {
7071
std::vector<uint8_t> bytes(pChunk, pChunk + size);
71-
m_recordEventHandler->Success(std::make_unique<flutter::EncodableValue>(bytes));
72+
73+
RecordWindowsPlugin::RunOnMainThread([this, bytes]() -> void {
74+
m_recordEventHandler->Success(std::make_unique<flutter::EncodableValue>(bytes));
75+
});
7276
}
7377

7478
GetAmplitude(pChunk, size, 2);

record_windows/windows/record_windows_plugin.cpp

+59-4
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,29 @@ namespace record_windows {
2121
return hr;
2222
}
2323

24-
void ErrorFromHR(HRESULT hr, MethodResult<EncodableValue>& result)
24+
static void ErrorFromHR(HRESULT hr, MethodResult<EncodableValue>& result)
2525
{
2626
_com_error err(hr);
2727
std::string errorText = Utf8FromUtf16(err.ErrorMessage());
2828

2929
result.Error("Record", "", EncodableValue(errorText));
3030
}
3131

32+
static HWND GetRootWindow(flutter::FlutterView* view) {
33+
return ::GetAncestor(view->GetNativeWindow(), GA_ROOT);
34+
}
35+
3236
// static, Register the plugin
3337
void RecordWindowsPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar) {
34-
auto plugin = std::make_unique<RecordWindowsPlugin>();
38+
auto plugin = std::make_unique<RecordWindowsPlugin>(
39+
[registrar](auto delegate) {
40+
return registrar->RegisterTopLevelWindowProcDelegate(delegate);
41+
},
42+
[registrar](auto proc_id) {
43+
registrar->UnregisterTopLevelWindowProcDelegate(proc_id);
44+
},
45+
[registrar] { return GetRootWindow(registrar->GetView()); }
46+
);
3547

3648
m_binaryMessenger = registrar->messenger();
3749

@@ -48,14 +60,56 @@ namespace record_windows {
4860
registrar->AddPlugin(std::move(plugin));
4961
}
5062

51-
RecordWindowsPlugin::RecordWindowsPlugin() {
63+
// static
64+
std::queue<std::function<void()>> RecordWindowsPlugin::callbacks{};
65+
66+
// static
67+
FlutterRootWindowProvider RecordWindowsPlugin::get_root_window{};
68+
69+
// static
70+
void RecordWindowsPlugin::RunOnMainThread(std::function<void()> callback) {
71+
callbacks.push(callback);
72+
PostMessage(get_root_window(), WM_RUN_DELEGATE, 0, 0);
73+
}
74+
75+
RecordWindowsPlugin::RecordWindowsPlugin(
76+
WindowProcDelegateRegistrator registrator,
77+
WindowProcDelegateUnregistrator unregistrator,
78+
FlutterRootWindowProvider window_provider
79+
): m_win_proc_delegate_registrator(registrator),
80+
m_win_proc_delegate_unregistrator(unregistrator) {
81+
82+
get_root_window = std::move(window_provider);
83+
84+
m_window_proc_id = m_win_proc_delegate_registrator(
85+
[this](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
86+
return HandleWindowProc(hwnd, message, wparam, lparam);
87+
}
88+
);
5289
}
5390

5491
RecordWindowsPlugin::~RecordWindowsPlugin() {
5592
for (const auto& [recorderId, recorder] : m_recorders)
5693
{
5794
recorder->Dispose();
5895
}
96+
97+
m_win_proc_delegate_unregistrator(m_window_proc_id);
98+
}
99+
100+
std::optional<LRESULT> RecordWindowsPlugin::HandleWindowProc(HWND hwnd,
101+
UINT message,
102+
WPARAM wparam,
103+
LPARAM lparam) {
104+
std::optional<LRESULT> result;
105+
switch (message) {
106+
case WM_RUN_DELEGATE:
107+
callbacks.front()();
108+
callbacks.pop();
109+
result = 0;
110+
break;
111+
}
112+
return result;
59113
}
60114

61115
// Called when a method is called on this plugin's channel from Dart.
@@ -190,7 +244,8 @@ namespace record_windows {
190244
EncodableMap({
191245
{EncodableValue("current"), EncodableValue(amp["current"])},
192246
{EncodableValue("max"), EncodableValue(amp["max"])}
193-
}))
247+
}
248+
))
194249
);
195250
}
196251
else if (method_call.method_name().compare("isEncoderSupported") == 0)

record_windows/windows/record_windows_plugin.h

+32-1
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,45 @@
1818

1919
#include "utils.h"
2020
#include "record.h"
21+
#include <queue>
2122

2223
using namespace flutter;
2324

25+
#define WM_RUN_DELEGATE (WM_USER + 101)
26+
2427
namespace record_windows {
2528
typedef flutter::EventSink<flutter::EncodableValue> FlEventSink;
2629
typedef flutter::StreamHandlerError<flutter::EncodableValue> FlStreamHandlerError;
2730

31+
using FlutterRootWindowProvider = std::function<HWND()>;
32+
using WindowProcDelegate = std::function<std::optional<LRESULT>(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)>;
33+
using WindowProcDelegateRegistrator = std::function<int(WindowProcDelegate delegate)>;
34+
using WindowProcDelegateUnregistrator = std::function<void(int proc_id)>;
35+
2836
class RecordWindowsPlugin : public flutter::Plugin {
2937
public:
3038
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar);
3139

32-
RecordWindowsPlugin();
40+
RecordWindowsPlugin(
41+
WindowProcDelegateRegistrator registrator,
42+
WindowProcDelegateUnregistrator unregistrator,
43+
FlutterRootWindowProvider window_provider
44+
);
3345
virtual ~RecordWindowsPlugin();
3446

3547
// Disallow copy and assign.
3648
RecordWindowsPlugin(const RecordWindowsPlugin&) = delete;
3749
RecordWindowsPlugin& operator=(const RecordWindowsPlugin&) = delete;
3850

51+
// The function to call to get the root window.
52+
static FlutterRootWindowProvider get_root_window;
53+
54+
// A queue of callbacks to run on the main thread.
55+
static std::queue<std::function<void()>> callbacks;
56+
57+
// Runs the given callback on the main thread.
58+
static void RunOnMainThread(std::function<void()> callback);
59+
3960
private:
4061
static inline BinaryMessenger* m_binaryMessenger;
4162

@@ -50,6 +71,16 @@ namespace record_windows {
5071
std::unique_ptr<RecordConfig> InitRecordConfig(const EncodableMap* args);
5172

5273
std::map<std::string, std::unique_ptr<Recorder>> m_recorders{};
74+
75+
// Called for top-level WindowProc delegation.
76+
std::optional<LRESULT> HandleWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
77+
78+
// The registrar for this plugin, for registering top-level WindowProc delegates.
79+
WindowProcDelegateRegistrator m_win_proc_delegate_registrator;
80+
WindowProcDelegateUnregistrator m_win_proc_delegate_unregistrator;
81+
82+
// The ID of the WindowProc delegate registration.
83+
int m_window_proc_id = -1;
5384
};
5485

5586
} // namespace record_windows

0 commit comments

Comments
 (0)