当捕获设备被其他应用程序使用时,如何捕获UWP MediaCapture异常?
我正在为UWP应用程序添加一个简单的网络摄像头预览。启动并运行预览效果良好,因此我正在正确处理由当捕获设备被其他应用程序使用时,如何捕获UWP MediaCapture异常?,uwp,c++-winrt,mediacapture,Uwp,C++ Winrt,Mediacapture,我正在为UWP应用程序添加一个简单的网络摄像头预览。启动并运行预览效果良好,因此我正在正确处理由InitializeAsync()和startPreviewSync()引发的异常,并且无法正确捕获第二个异常 我的代码基于,它说如果一个应用程序不能访问捕获设备,它将在调用InitializeAsync()时抛出未经授权的访问异常。对于C++/WinRT,它似乎能够捕获hresult\u错误code ofE\u ACCESSDENIED,如下所示。我通过在应用程序选项中关闭对网络摄像头的访问来测试这
InitializeAsync()
和startPreviewSync()
引发的异常,并且无法正确捕获第二个异常
我的代码基于,它说如果一个应用程序不能访问捕获设备,它将在调用InitializeAsync()
时抛出未经授权的访问异常。对于C++/WinRT,它似乎能够捕获hresult\u错误
code ofE\u ACCESSDENIED
,如下所示。我通过在应用程序选项中关闭对网络摄像头的访问来测试这一点,而try/catch块的工作原理与您预期的一样,它会弹出内容对话框并向用户解释问题
参考资料还指出,如果另一个应用程序独占控制捕获设备,则startPreviewSync()
应引发FileLoadException
。首先,我不知道该异常的C++/WinRT等价物是什么。其次,我似乎根本看不到任何异常。我尝试使用与我在InitializeAsync()
中使用的相同类型的catch块,因为这是中所描述的,但当这不起作用时,我尝试使用下面的块捕获任何内容。文档中说,当您捕获异常时,您可以注册一个CaptureDeviceExclusiveControlStatusChanged
事件,但由于我无法捕获异常,因此我不确定在何处执行该操作,或者如果在另一个应用已经控制了捕获设备之后启动我的应用,事件是否会触发。我从未在catch块中看到来自OutputDebugString()
的任何文本,但在调试输出窗口中确实收到以下消息(两次):
在中的0x00007FFC7114A839(KernelBase.dll)处引发异常
DBRacing.exe:WinRT原始错误-0xC00D3704:“硬件MFT”
由于缺少硬件资源,无法启动流媒体。“
似乎正在生成异常,出于某种原因,我似乎无法捕获它
在下面的代码中,my ViewModel()使用的方法只是提供对本地设置的访问,在本地设置中,我存储上次使用的设备ID,当我的应用程序独占控制网络摄像头时,一切正常
所以,我的问题是:当另一个应用程序独占控制捕获设备时,我如何正确识别
我有一个MediaCapture对象的私有页面类变量:
private:
Windows::Media::Capture::MediaCapture m_mediaCapture;
导航到以下页面时,相机预览开始:
void VideoPage::OnNavigatedTo(NavigationEventArgs /*e*/) {
StartPreviewAsync();
}
StartPreviewSync()的定义如下:
Windows::Foundation::IAsyncAction VideoPage::StartPreviewAsync() {
// if we have a previously used device, then we should check if it's valid and use it
if (!ViewModel().hasDeviceID()) {
// device picker to choose camera
DevicePicker picker;
// create a filter that only looks for video capture devices
picker.Filter().SupportedDeviceClasses().Append(DeviceClass::VideoCapture);
// show the picker below the button that opens it and get the chosen device
DeviceInformation device = co_await picker.PickSingleDeviceAsync({ 0,0,100,100 });
// the user can cancel the dialog before picking something
if (!device)
return;
// store the device ID
ViewModel().deviceID(device.Id());
// store the device name
ViewModel().deviceName(device.Name());
}
// settings for the media capture object (such as which camera to use)
MediaCaptureInitializationSettings settings;
// add the chosen device to the settings so we initialize on that camera
settings.VideoDeviceId(ViewModel().deviceID());
try {
// initialize the mediacapture object using the chosen camera
co_await m_mediaCapture.InitializeAsync(settings);
// dont let the screen go to sleep while the preview is active
m_displayRequest.RequestActive();
}
// an exception is thrown if the user does not allow access to the camera
catch (winrt::hresult_error const& ex) {
winrt::hresult hr = ex.to_abi();
if (hr == E_ACCESSDENIED) {
ContentDialog msg;
// set all the options for the dialog
msg.Title(box_value(L"Access Denied"));
msg.Content(box_value(L"This App has not been given permission to use the Camera and/or Microphone.\nPlease go to the settings in Windows for this App to allow access."));
msg.CloseButtonText(L"OK");
// Show the message dialog.
msg.ShowAsync();
}
return;
}
try {
// assign the source to the Capture Element on the XAML page
capturePreview().Source(m_mediaCapture);
co_await m_mediaCapture.StartPreviewAsync();
}
// This method should throw a FileLoadException (0x80070020) if another app has exclusive control of the capture device
catch(...) {
OutputDebugString(L"Exception Message\n");
return;
}
}
根据我的测试,当我在捕获异常之前首次注册CaptureDeviceExclusiveControlStatusChanged事件时,其中一个应用程序使用了摄像头。之后,我运行另一个应用程序,它也将使用相同的摄像头,它可以捕捉异常。您可以尝试先添加事件进行测试,如下所示,mediaCapture.Failed
事件具有相同的效果
try
{
DisplayRequest displayRequest = DisplayRequest();
m_mediaCapture = MediaCapture();
// initialize the mediacapture object using the chosen camera
co_await m_mediaCapture.InitializeAsync();
//Register
m_mediaCapture.CaptureDeviceExclusiveControlStatusChanged({ this, &MainPage::MediaCapture_CaptureDeviceExclusiveControlStatusChanged });
displayRequest.RequestActive();
}
catch (winrt::hresult_error const& ex)
{
winrt::hresult hr = ex.to_abi();
if (hr == E_ACCESSDENIED) {
}
return;
}
try
{
PreviewControl().Source(m_mediaCapture);
co_await m_mediaCapture.StartPreviewAsync();
}
catch (winrt::hresult_error const& ex)
{
winrt::hresult hr = ex.to_abi(); // HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND).
winrt::hstring message = ex.message(); // The system cannot find the file specified.
}
//event
void MainPage::MediaCapture_CaptureDeviceExclusiveControlStatusChanged(MediaCapture const&, MediaCaptureDeviceExclusiveControlStatusChangedEventArgs const&)
{
throw hresult_not_implemented();
}
根据我的测试,当我在捕获异常之前首次注册CaptureDeviceExclusiveControlStatusChanged事件时,其中一个应用程序使用了摄像头。之后,我运行另一个应用程序,它也将使用相同的摄像头,它可以捕捉异常。您可以尝试先添加事件进行测试,如下所示,mediaCapture.Failed
事件具有相同的效果
try
{
DisplayRequest displayRequest = DisplayRequest();
m_mediaCapture = MediaCapture();
// initialize the mediacapture object using the chosen camera
co_await m_mediaCapture.InitializeAsync();
//Register
m_mediaCapture.CaptureDeviceExclusiveControlStatusChanged({ this, &MainPage::MediaCapture_CaptureDeviceExclusiveControlStatusChanged });
displayRequest.RequestActive();
}
catch (winrt::hresult_error const& ex)
{
winrt::hresult hr = ex.to_abi();
if (hr == E_ACCESSDENIED) {
}
return;
}
try
{
PreviewControl().Source(m_mediaCapture);
co_await m_mediaCapture.StartPreviewAsync();
}
catch (winrt::hresult_error const& ex)
{
winrt::hresult hr = ex.to_abi(); // HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND).
winrt::hstring message = ex.message(); // The system cannot find the file specified.
}
//event
void MainPage::MediaCapture_CaptureDeviceExclusiveControlStatusChanged(MediaCapture const&, MediaCaptureDeviceExclusiveControlStatusChangedEventArgs const&)
{
throw hresult_not_implemented();
}
这起作用了。出于某种原因,通过移动流中先前更改的CaptureDeviceExclusiveControlStatus的注册,它不仅将抛出的异常更改为正确的异常,而且还允许catch块实际捕获它。我也尝试了mediaCapture.Failed事件,它也可以工作,但对于同一问题,它会得到不同的错误代码。这很有效。出于某种原因,通过移动流中先前更改的CaptureDeviceExclusiveControlStatus的注册,它不仅将抛出的异常更改为正确的异常,而且还允许catch块实际捕获它。我也尝试了mediaCapture.Failed事件,它也可以工作,但它会为同一问题得到不同的错误代码。