Files
LifeProject/LifeCore/media/WebCamera.cpp
T

225 lines
5.8 KiB
C++
Raw Normal View History

2025-05-13 02:46:21 +03:00
#include "WebCamera.h"
#include "../Utils.h"
#include "../log/NLog.h"
#include "../Com.h"
namespace LifeCore {
class WinWebCameraDescription : public WebCameraDescription {
public:
friend class WebCamera;
WinWebCameraDescription(IMFActivate* act, int id)
: activate(act), deviceId(id)
{
if(activate != nullptr)
activate->AddRef();
}
~WinWebCameraDescription() {
SafeRelease(&activate);
}
std::wstring GetName() override{
if(activate == nullptr)
return std::wstring(L"");
WCHAR *szFriendlyName = NULL;
UINT32 cchName;
auto hr = activate->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &szFriendlyName, &cchName);
if(hr != S_OK)
return std::wstring(L"");
std::wstring result(szFriendlyName);
CoTaskMemFree(szFriendlyName);
return result;
}
int deviceId;
protected:
IMFActivate* activate;
};
struct ChooseDeviceParam {
ChooseDeviceParam() : ppDevices(NULL), count(0) {}
~ChooseDeviceParam() {
for(DWORD i = 0; i < count; i++) {
SafeRelease(&ppDevices[i]);
}
CoTaskMemFree(ppDevices);
}
IMFActivate **ppDevices;
UINT32 count;
UINT32 selection;
};
class WinWebCamCallback : IMFCaptureEngineOnEventCallback {
public:
WinWebCamCallback(WebCamera* cam)
: m_cRef(1), camera(cam), sleeping(false)
{
}
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
static const QITAB qit[] = {
QITABENT(WinWebCamCallback, IMFCaptureEngineOnEventCallback), { 0 }
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) AddRef() {
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) Release() {
LONG cRef = InterlockedDecrement(&m_cRef);
if(cRef == 0) {
delete this;
}
return cRef;
}
// IMFCaptureEngineOnEventCallback
STDMETHODIMP OnEvent(_In_ IMFMediaEvent* pEvent) {
if(sleeping && camera != nullptr) {
// We're about to fall asleep, that means we've just asked the CE to stop the preview
// and record. We need to handle it here since our message pump may be gone.
GUID guidType;
HRESULT hrStatus;
HRESULT hr = pEvent->GetStatus(&hrStatus);
if(FAILED(hr)) {
hrStatus = hr;
}
hr = pEvent->GetExtendedType(&guidType);
if(SUCCEEDED(hr)) {
if(guidType == MF_CAPTURE_ENGINE_PREVIEW_STOPPED) {
LogInfo() << "Preview stopped";
//m_pManager->OnPreviewStopped(hrStatus);
// SetEvent(m_pManager->m_hEvent);
} else if(guidType == MF_CAPTURE_ENGINE_RECORD_STOPPED) {
LogInfo() << "Engine stopped";
//m_pManager->OnRecordStopped(hrStatus);
//SetEvent(m_pManager->m_hEvent);
} else {
// This is an event we don't know about, we don't really care and there's
// no clean way to report the error so just set the event and fall through.
//SetEvent(m_pManager->m_hEvent);
}
}
return S_OK;
} else {
LogInfo() << "Capture event";
pEvent->AddRef(); // The application will release the pointer when it handles the message.
camera->OnCaptureEvent(pEvent);
//PostMessage(m_hwnd, WM_APP_CAPTURE_EVENT, (WPARAM)pEvent, 0L);
}
return S_OK;
}
protected:
bool sleeping;
long m_cRef;
WebCamera* camera;
};
WebCamera::WebCamera(webcam_desc_ptr desc)
: description(desc), FrameNumber(0), pSource(nullptr), grabber(nullptr)
{
callback = new WinWebCamCallback(this);
}
void WebCamera::OnCaptureEvent(IMFMediaEvent* pEvent) {
HRESULT hrStatus;
GUID eventType;
pEvent->GetStatus(&hrStatus);
pEvent->GetExtendedType(&eventType);
}
RawBuffer<uint8>* WebCamera::GetImage() {
return grabber->GetImage();
}
void WebCamera::OnImageGrabbed(WinMfImageGrabber* grabber, RawBuffer<uint8>* image) {
FrameNumber++;
Event_ImageCapture.Call(this, image);
}
bool WebCamera::Open() {
IMFActivate* pActivate = dynamic_cast<WinWebCameraDescription*>(description.get())->activate;
HRESULT hr = pActivate->ActivateObject(__uuidof(IMFMediaSource), (void**)&pSource);
if(FAILED(hr)) {
LogInfo() << "Failed to activate camera!";
return false;
}
grabber = new WinMfImageGrabber(this);
if(FAILED(grabber->initImageGrabber(pSource, MFVideoFormat_RGB24)) || FAILED(grabber->startGrabbing())) {
LogInfo() << "Failed to init camera grabber";
pActivate->ShutdownObject();
FINAL_RELEASE(grabber);
FINAL_RELEASE(pSource);
return false;
}
return true;
}
void WebCamera::Close() {
IMFActivate* pActivate = dynamic_cast<WinWebCameraDescription*>(description.get())->activate;
if(grabber != nullptr)
grabber->stopGrabbing();
FINAL_RELEASE(grabber);
FINAL_RELEASE(pSource);
pActivate->ShutdownObject();
pActivate->AddRef();
auto val = pActivate->Release();
LogInfo() << "Initial 2 pActivate refCount = " << val;
FrameNumber = 0;
}
int2 WebCamera::GetSize() {
return int2();
}
int32 WebCamera::GetBpp() {
return -1;
}
bool WebCamera::Read(int2 size, uint8* buffer) {
return false;
}
void WebCamera::Update() {
grabber->processGrabbing();
}
std::vector<webcam_desc_ptr> WebCamera::GetConnectedDevices() {
std::vector<webcam_desc_ptr> result;
IMFAttributes *pAttributes = NULL;
ChooseDeviceParam param;
HRESULT hr = MFCreateAttributes(&pAttributes, 1);
if(FAILED(hr)) {
printf("Failed\n");
goto Exit;
}
// Ask for source type = video capture devices
hr = pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
if(FAILED(hr)) {
printf("Failed\n");
goto Exit;
}
// Enumerate devices.
hr = MFEnumDeviceSources(pAttributes, &param.ppDevices, &param.count);
if(FAILED(hr)) {
printf("Failed\n");
goto Exit;
}
for(UINT32 i = 0; i < param.count; ++i) {
result.push_back(webcam_desc_ptr(new WinWebCameraDescription(param.ppDevices[i], i)));
}
Exit:
if(NULL != pAttributes) {
pAttributes->Release();
pAttributes = NULL;
}
return result;
}
}