gpt4 book ai didi

windows - 在 Windows 上捕获和显示实时摄像头内容

转载 作者:行者123 更新时间:2023-12-05 09:06:22 25 4
gpt4 key购买 nike

我正在开发一个 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com