- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在开发一个 Windows 应用程序,它能够显示高质量的视频源、录制它或从中拍摄照片,并在以后进行编辑(最高 4K,在不久的将来可能是 8K)。我目前有一个工作产品,使用 WPF (C#)。为了捕获和显示视频,我使用了 AForge.NET 库。
我的问题是应用程序真的很慢,主要的性能损失来自视频渲染。显然,做到这一点的唯一方法是从 AForge 库中进行回调,在每次可用时提供一个新框架。然后将该帧作为图像放置在 Image
元素中。我相信您可以看到性能受到影响的原因,尤其是对于高分辨率图像。
我使用 WPF 和这些庞大的库的经验让我重新思考我想要如何进行一般的编程;我不想制作糟糕的软件,因为速度慢而占用每个人的时间(我引用 Handmade 网络 了解更多关于“为什么?”的信息。
问题是,摄像头捕获和显示在 WPF C# 中非常糟糕,但我似乎并没有比其他任何地方(在 Windows 上)更好。我可以选择主要使用 C++ 和 DirectShow。这是一个不错的解决方案,但在性能方面感觉已经过时,并且是建立在微软的 COM 系统之上的,我宁愿避免这种情况。有一些选项可以使用 Direct3D 使用硬件进行渲染,但 DirectShow 和 Direct3D 不能很好地协同工作。
我研究了其他应用程序是如何实现这一目标的。VLC 使用 DirectShow,但这只能说明 DirectShow 存在较大延迟。我认为这是因为 VLC 并非用于实时目的。OBS studio 使用 QT 使用的任何东西,但我无法找到他们是如何做到的。OpenCV 抓取帧并将它们 blits 到屏幕上,效率不高,但这对 OpenCV 观众来说已经足够了。最后,来自 Windows 的集成网络摄像头应用程序。出于某种原因,该应用程序能够实时录制和回放,而不会对性能造成太大影响。我无法弄清楚他们是如何做到这一点的,也没有找到任何其他解决方案可以达到与该工具相当的结果。
TLDR; 所以我的问题是:我将如何有效地捕获和渲染相机流,最好是硬件加速;是否可以在不通过 Directshow 的情况下在 Windows 上执行此操作?最后,当我希望它们实时处理 4K 素材时,我是否需要很多商品设备?
我还没有发现任何人以满足我需求的方式来做这件事;这让我同时感到绝望和内疚。我宁愿不为这个问题打扰 StackOverflow。
非常感谢您就此主题的一般性回答或建议。
最佳答案
这是一个完整的可重现示例代码,它使用 GDI+ 进行渲染并使用 MediaFoundation 捕获视频。它应该在 visual studio 上开箱即用,并且不应由于使用 unique_ptr 和 CComPtr 的自动内存管理而出现任何类型的内存泄漏。此外,您的相机将使用此代码输出其默认视频格式。如果需要,您始终可以使用以下设置视频格式:https://learn.microsoft.com/en-us/windows/win32/medfound/how-to-set-the-video-capture-format
#include <windows.h>
#include <mfapi.h>
#include <iostream>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <dshow.h>
#include <dvdmedia.h>
#include <gdiplus.h>
#include <atlbase.h>
#include <thread>
#include <vector>
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "gdiplus")
void BackgroundRecording(HWND hWnd, CComPtr<IMFSourceReader> pReader, int videoWidth, int videoHeight) {
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
Gdiplus::PixelFormat pixelFormat = PixelFormat24bppRGB;
Gdiplus::Graphics* g = Gdiplus::Graphics::FromHWND(hWnd, FALSE);
while (true) {
CComPtr<IMFSample> pSample;
HRESULT hr = pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &streamIndex, &flags, &llTimeStamp, &pSample);
if (!FAILED(hr)) {
if (pSample != NULL) {
CComPtr<IMFMediaBuffer> pBuffer;
hr = pSample->ConvertToContiguousBuffer(&pBuffer);
if (!FAILED(hr)) {
DWORD length;
hr = pBuffer->GetCurrentLength(&length);
if (!FAILED(hr)) {
unsigned char* data;
hr = pBuffer->Lock(&data, NULL, &length);
if (!FAILED(hr)) {
std::unique_ptr<unsigned char[]> reversedData(new unsigned char[length]);
int counter = length - 1;
for (int i = 0; i < length; i += 3) {
reversedData[i] = data[counter - 2];
reversedData[i + 1] = data[counter - 1];
reversedData[i + 2] = data[counter];
counter -= 3;
}
std::unique_ptr<Gdiplus::Bitmap> bitmap(new Gdiplus::Bitmap(videoWidth, videoHeight, 3 * videoWidth, pixelFormat, reversedData.get()));
g->DrawImage(bitmap.get(), 0, 0);
}
}
}
}
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
break;
case WM_CLOSE:
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
HRESULT hr = MFStartup(MF_VERSION);
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
CComPtr<IMFSourceReader> pReader = NULL;
CComPtr<IMFMediaSource> pSource = NULL;
CComPtr<IMFAttributes> pConfig = NULL;
IMFActivate** ppDevices = NULL;
hr = MFCreateAttributes(&pConfig, 1);
if (FAILED(hr)) {
std::cout << "Failed to create attribute store" << std::endl;
}
hr = pConfig->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if (FAILED(hr)) {
std::cout << "Failed to request capture devices" << std::endl;
}
UINT32 count = 0;
hr = MFEnumDeviceSources(pConfig, &ppDevices, &count);
if (FAILED(hr)) {
std::cout << "Failed to enumerate capture devices" << std::endl;
}
hr = ppDevices[0]->ActivateObject(IID_PPV_ARGS(&pSource));
if (FAILED(hr)) {
std::cout << "Failed to connect camera to source" << std::endl;
}
hr = MFCreateSourceReaderFromMediaSource(pSource, pConfig, &pReader);
if (FAILED(hr)) {
std::cout << "Failed to create source reader" << std::endl;
}
for (unsigned int i = 0; i < count; i++) {
ppDevices[i]->Release();
}
CoTaskMemFree(ppDevices);
CComPtr<IMFMediaType> pType = NULL;
DWORD dwMediaTypeIndex = 0;
DWORD dwStreamIndex = 0;
hr = pReader->GetNativeMediaType(dwStreamIndex, dwMediaTypeIndex, &pType);
LPVOID representation;
pType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, &representation);
GUID subType = ((AM_MEDIA_TYPE*)representation)->subtype;
BYTE* pbFormat = ((AM_MEDIA_TYPE*)representation)->pbFormat;
GUID formatType = ((AM_MEDIA_TYPE*)representation)->formattype;
int videoWidth = ((VIDEOINFOHEADER2*)pbFormat)->bmiHeader.biWidth;
int videoHeight = ((VIDEOINFOHEADER2*)pbFormat)->bmiHeader.biHeight;
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Window";
RegisterClass(&wc);
HWND hWnd = CreateWindowExW(NULL, L"Window", L"Window", WS_OVERLAPPEDWINDOW, 0, 0, videoWidth, videoHeight, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
std::thread th(BackgroundRecording, hWnd, pReader, videoWidth, videoHeight);
th.detach();
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
pSource->Shutdown();
Gdiplus::GdiplusShutdown(gdiplusToken);
return 0;
}
关于windows - 在 Windows 上捕获和显示实时摄像头内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66140642/
我正在为我的按钮使用 onClick 功能。我的按钮代码如下所示: Button 1 Button 2 我的 JS 函数如下所示: function fadeNext(selectedId, spee
首先,我想提一下,我理解每个人在不提供至少一些试验或错误的情况下提出问题的感受,但这纯粹是一种知识需求,话虽如此,我会去提前问。 我一直无法弄清楚如何将保存在 MySQL 表中的 600-1000 个
我想做的事情有点令人困惑,而且我英语不太好,所以我先把代码贴在这里,这样你就可以很容易地理解: 以下是表单内容: Testing for Stackoverflow Option1
我学习 SDL 二维编程已有一段时间了,现在我想创建一个结合使用 SDL 和 OpenGL 的程序。我是这样设置的: SDL_Init(SDL_INIT_VIDEO); window = SDL_Cr
我创建了 2 个 data-* 标签。数据类别和数据标签。单击 href 标签后,我想复制该数据类别和数据标签以形成输入。我的代码是:
我想用 CSS 换行。我正在使用内容。 td:before { content: "Test\A Test2"; } 它不工作。如何正确
这个问题已经有答案了: Java Class that implements Map and keeps insertion order? (8 个回答) 已关闭 6 年前。 我有一个 HashMap
我正在尝试使用 JMeter 执行端到端测试。测试涉及写入SFTP文件夹并从另一个SFTP文件夹读取写入操作生成的文件。 我能够使用 JMeter SSH SFTP 插件连接到 SFTP 文件夹,并能
您好,我有带有标准服务器端 Servlet 的 GWT 客户端。 我可以从 GWT 客户端上传文件并在服务器端读取其内容 我可以将其作为字符串发送回客户端 但是 我有 GWT FormPanel与操作
我在 Plone 4.3.9 中创建了一个自定义类型的灵巧性,称为 PersonalPage,必须只允许在特定文件夹中使用 成员文件夹/用户文件夹 . 在他的 FTI 中,默认情况下 False .
在新(更新)版本的应用程序中更改小部件布局的最佳做法是什么?当新版本提供更新、更好的小部件时,如何处理现有小部件? 最佳答案 我认为您必须向用户显示一个弹出窗口,说明“此版本中的新功能”并要求他们重新
在我的应用程序中,我使用支持 View 寻呼机和 PagerTabStrip。进入查看寻呼机我有一些 fragment ,进入其中一个我正在使用支持卡片 View 。运行应用程序后,所有卡片 View
我有以下布局文件。基本上我有谷歌地图,在左上角我有一个 TextView,我需要在其中每 15 秒保持一次计数器以刷新 map 。布局很好。
我使用如下结构: HashMap > > OverallMap 如果我这样做: OverallMap . clear ( ) clear() 丢弃的所有内容(HashMap 对象、Integer 对
我在数据库中有 1000 张图像。在页面加载时,我随机显示 60 张图片,当用户滚动时,我通过 AJAX 请求添加 20 张图片。 第一种方法 我所做的是将所有图像加载到一个容器中,然后隐藏所有图像并
我正在使用 woocommerce 创建一个网上商店。 我想在每个产品上添加一个包含产品信息的表格,例如颜色、交货时间等等。 但是当我添加这张表时。本产品消失后的所有内容。 我的表的代码: td {
This question already has an answer here: What does an empty value for the CSS property content do?
因此,我正在与我的 friend 一起为 Google Chrome 开发一个扩展程序,对于大多数功能(即日历、设置等),我们打开一个模式,这样我们就不必重定向到另一个页面。当您在内容之外单击时,我们
我将可变高度的 CSS 框设置为在更大的 div 中向左浮动。现在我想添加一个标题,其中文本在框的左侧垂直显示(旋转 90 度),如下面的链接所示(抱歉还不能发布图片)。 http://imagesh
相关页面位于 www.codykrauskopf.com/circus 如果您查看我页面的右侧,在半透明容器和浏览器窗口边缘之间有一个间隙。我看了看,出于某种原因,wrap、main、content
我是一名优秀的程序员,十分优秀!