Skip to content

Commit 7cde5e7

Browse files
committed
Address review fixes for Windows MSMF backend
1 parent a95eded commit 7cde5e7

9 files changed

Lines changed: 267 additions & 106 deletions

File tree

CMakeLists.txt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,19 @@ elseif (WIN32)
253253

254254
# MSVC: Delay load Media Foundation DLLs to preserve legacy DirectShow-only startup paths.
255255
if (MSVC)
256-
target_link_options(ccap PRIVATE
257-
/DELAYLOAD:mf.dll
258-
/DELAYLOAD:mfplat.dll
259-
/DELAYLOAD:mfreadwrite.dll
260-
)
256+
if (CCAP_BUILD_SHARED)
257+
target_link_options(ccap PRIVATE
258+
/DELAYLOAD:mf.dll
259+
/DELAYLOAD:mfplat.dll
260+
/DELAYLOAD:mfreadwrite.dll
261+
)
262+
else ()
263+
target_link_options(ccap INTERFACE
264+
/DELAYLOAD:mf.dll
265+
/DELAYLOAD:mfplat.dll
266+
/DELAYLOAD:mfreadwrite.dll
267+
)
268+
endif ()
261269
target_link_libraries(ccap PUBLIC delayimp.lib)
262270
endif ()
263271
else ()

README.zh-CN.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,17 @@ Windows 上默认优先使用 Media Foundation,并在需要时自动回退到
182182
- 设置环境变量 `CCAP_WINDOWS_BACKEND=auto|msmf|dshow`,对整个进程生效,包括 CLI 和 Rust 绑定。
183183
184184
```cpp
185-
ccap::Provider msmfProvider("", "msmf");
186-
ccap::Provider dshowProvider("", "dshow");
185+
// 下面两段是互斥示例;同一时刻不要对同一设备同时创建两个 Provider。
186+
187+
// Force MSMF
188+
{
189+
ccap::Provider provider("", "msmf");
190+
}
191+
192+
// Force DirectShow
193+
{
194+
ccap::Provider provider("", "dshow");
195+
}
187196
```
188197
189198
### Rust 绑定

bindings/rust/build.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ Please vendor the sources into bindings/rust/native/, or set CCAP_SOURCE_DIR to
196196
{
197197
build
198198
.file(ccap_root.join("src/ccap_imp_windows.cpp"))
199+
.file(ccap_root.join("src/ccap_imp_windows_msmf.cpp"))
199200
.file(ccap_root.join("src/ccap_file_reader_windows.cpp"));
200201
}
201202

@@ -368,10 +369,11 @@ Please vendor the sources into bindings/rust/native/, or set CCAP_SOURCE_DIR to
368369

369370
#[cfg(target_os = "windows")]
370371
{
372+
println!("cargo:rustc-link-lib=mf");
371373
println!("cargo:rustc-link-lib=strmiids");
372374
println!("cargo:rustc-link-lib=ole32");
373375
println!("cargo:rustc-link-lib=oleaut32");
374-
// Media Foundation libraries for video file playback
376+
// Media Foundation libraries for the MSMF camera backend and video file playback
375377
println!("cargo:rustc-link-lib=mfplat");
376378
println!("cargo:rustc-link-lib=mfreadwrite");
377379
println!("cargo:rustc-link-lib=mfuuid");

bindings/rust/src/provider.rs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,14 @@ impl Provider {
9696
///
9797
/// On Windows, `extra_info` can be used to force backend selection with values like
9898
/// `"auto"`, `"msmf"`, `"dshow"`, or `"backend=<value>"`.
99-
pub fn with_device_and_extra_info(
100-
device_index: i32,
101-
extra_info: Option<&str>,
102-
) -> Result<Self> {
99+
pub fn with_device_and_extra_info(device_index: i32, extra_info: Option<&str>) -> Result<Self> {
103100
let extra_info = optional_c_string(extra_info, "extra info")?;
104101
let handle = unsafe {
105102
sys::ccap_provider_create_with_index(
106103
device_index,
107-
extra_info.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
104+
extra_info
105+
.as_ref()
106+
.map_or(ptr::null(), |value| value.as_ptr()),
108107
)
109108
};
110109
if handle.is_null() {
@@ -144,7 +143,9 @@ impl Provider {
144143
let handle = unsafe {
145144
sys::ccap_provider_create_with_device(
146145
c_name.as_ptr(),
147-
extra_info.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
146+
extra_info
147+
.as_ref()
148+
.map_or(ptr::null(), |value| value.as_ptr()),
148149
)
149150
};
150151
if handle.is_null() {
@@ -271,6 +272,11 @@ impl Provider {
271272
auto_start: bool,
272273
) -> Result<()> {
273274
if let Some(name) = device_name {
275+
let c_name = CString::new(name).map_err(|_| {
276+
CcapError::InvalidParameter("device name contains null byte".to_string())
277+
})?;
278+
let extra_info = optional_c_string(extra_info, "extra info")?;
279+
274280
// Recreate provider with specific device
275281
if !self.handle.is_null() {
276282
// If the previous provider was running, stop it and detach callbacks
@@ -281,21 +287,27 @@ impl Provider {
281287
unsafe {
282288
sys::ccap_provider_destroy(self.handle);
283289
}
290+
self.handle = ptr::null_mut();
291+
self.is_opened = false;
292+
} else {
293+
self.cleanup_callback();
284294
}
285-
let c_name = CString::new(name).map_err(|_| {
286-
CcapError::InvalidParameter("device name contains null byte".to_string())
287-
})?;
288-
let extra_info = optional_c_string(extra_info, "extra info")?;
295+
289296
self.handle = unsafe {
290297
sys::ccap_provider_create_with_device(
291298
c_name.as_ptr(),
292-
extra_info.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
299+
extra_info
300+
.as_ref()
301+
.map_or(ptr::null(), |value| value.as_ptr()),
293302
)
294303
};
295304
if self.handle.is_null() {
296305
return Err(CcapError::InvalidDevice(name.to_string()));
297306
}
298307
self.is_opened = true;
308+
if !auto_start {
309+
self.stop_capture()?;
310+
}
299311
} else if extra_info.is_some() {
300312
return self.open_with_index_and_extra_info(-1, extra_info, auto_start);
301313
} else {
@@ -581,6 +593,8 @@ impl Provider {
581593
extra_info: Option<&str>,
582594
auto_start: bool,
583595
) -> Result<()> {
596+
let extra_info = optional_c_string(extra_info, "extra info")?;
597+
584598
// If the previous provider was running, stop it and detach callbacks
585599
// before destroying the underlying handle.
586600
if !self.handle.is_null() {
@@ -590,18 +604,20 @@ impl Provider {
590604
unsafe {
591605
sys::ccap_provider_destroy(self.handle);
592606
}
607+
self.handle = ptr::null_mut();
608+
self.is_opened = false;
593609
} else {
594610
// Clean up any stale callback allocation even if handle is null.
595611
self.cleanup_callback();
596612
}
597613

598-
let extra_info = optional_c_string(extra_info, "extra info")?;
599-
600614
// Create a new provider with the specified device index
601615
self.handle = unsafe {
602616
sys::ccap_provider_create_with_index(
603617
device_index,
604-
extra_info.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
618+
extra_info
619+
.as_ref()
620+
.map_or(ptr::null(), |value| value.as_ptr()),
605621
)
606622
};
607623

@@ -614,6 +630,9 @@ impl Provider {
614630

615631
// ccap C API contract: create_with_index opens the device.
616632
self.is_opened = true;
633+
if !auto_start {
634+
self.stop_capture()?;
635+
}
617636
if auto_start {
618637
self.start_capture()?;
619638
}

docs/content/implementation-details.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ Or use the script:
218218
**Media Foundation Backend:**
219219
- Preferred on modern Windows systems
220220
- Uses Source Reader for frame delivery and format negotiation
221-
- Automatically falls back to DirectShow when Media Foundation is unavailable or device open fails
221+
- When backend selection is `auto`, `Provider::open()` falls back to DirectShow if Media Foundation is unavailable or device open fails
222+
- When callers explicitly request `msmf` via `extraInfo` or `CCAP_WINDOWS_BACKEND`, `Provider::open()` returns an error instead of falling back
222223

223224
**DirectShow Backend:**
224225
- Mature, stable API

include/ccap_core.h

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -229,34 +229,16 @@ class CCAP_EXPORT Provider final {
229229

230230

231231
// ↓ This part is not relevant to the user ↓
232-
Provider(Provider&&) = default;
233-
Provider& operator=(Provider&&) = default;
232+
Provider(Provider&&) noexcept;
233+
Provider& operator=(Provider&&) noexcept;
234234
~Provider();
235235

236236
private:
237237
void applyCachedState(ProviderImp* imp) const;
238238
bool tryOpenWithImplementation(ProviderImp* imp, std::string_view deviceName, bool autoStart) const;
239239

240240
private:
241-
std::string m_extraInfo;
242-
std::function<bool(const std::shared_ptr<VideoFrame>&)> m_frameCallback;
243-
std::function<std::shared_ptr<Allocator>()> m_allocatorFactory;
244-
uint32_t m_maxAvailableFrameSize = DEFAULT_MAX_AVAILABLE_FRAME_SIZE;
245-
uint32_t m_maxCacheFrameSize = DEFAULT_MAX_CACHE_FRAME_SIZE;
246-
int m_requestedWidth = 640;
247-
int m_requestedHeight = 480;
248-
double m_requestedFrameRate = 0.0;
249-
PixelFormat m_requestedInternalFormat = PixelFormat::Unknown;
250-
PixelFormat m_requestedOutputFormat{
251-
#ifdef __APPLE__
252-
PixelFormat::BGRA32
253-
#else
254-
PixelFormat::BGR24
255-
#endif
256-
};
257-
bool m_hasFrameOrientationOverride = false;
258-
FrameOrientation m_requestedFrameOrientation = FrameOrientation::Default;
259-
ProviderImp* m_imp;
241+
ProviderImp* m_imp = nullptr;
260242
};
261243

262244
/**

0 commit comments

Comments
 (0)