Skip to content

Commit 78ec196

Browse files
Camera device driver supports streaming now
1 parent c19860b commit 78ec196

4 files changed

Lines changed: 169 additions & 14 deletions

‎Drivers/Camera/Implementation/libmcdriver_camera_driver_camera_windows.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ IVideoDevice* CDriver_Camera_Windows::OpenVideoDevice(const std::string& sIdenti
283283
if (iIter != m_VideoDeviceIdentifierMap.end())
284284
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_CAMERAIDENTIFIERALREADYREGISTERED, "camera identifier already registered: " + sIdentifier);
285285

286-
auto pDeviceInstance = std::make_shared<CVideoDeviceInstance_Win32> (sIdentifier, sOSName, sFriendlyName);
286+
auto pDeviceInstance = std::make_shared<CVideoDeviceInstance_Win32> (sIdentifier, sOSName, sFriendlyName, m_pDriverEnvironment);
287287

288288
m_VideoDeviceIdentifierMap.insert(std::make_pair (sIdentifier, pDeviceInstance));
289289
m_VideoDeviceOSNameMap.insert(std::make_pair (sOSName, pDeviceInstance));

‎Drivers/Camera/Implementation/libmcdriver_camera_videodevice.cpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,21 +173,21 @@ std::string CVideoDevice_Win32::GetSourceFormatDescription(const LibMCDriver_Cam
173173

174174
void CVideoDevice_Win32::StartStreamCapture(LibMCEnv::PVideoStream pStreamInstance)
175175
{
176-
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_NOTIMPLEMENTED);
176+
m_pDeviceInstance->startStreamCapture(pStreamInstance);
177177
}
178178

179179
void CVideoDevice_Win32::StopStreamCapture()
180180
{
181-
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_NOTIMPLEMENTED);
181+
m_pDeviceInstance->stopStreamCapture();
182182
}
183183

184184
bool CVideoDevice_Win32::StreamCaptureIsActive()
185185
{
186-
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_NOTIMPLEMENTED);
186+
return m_pDeviceInstance->streamCaptureIsActive();
187187
}
188188

189189
void CVideoDevice_Win32::GetStreamCaptureStatistics(LibMCDriver_Camera_double & dDesiredFramerate, LibMCDriver_Camera_double & dMinFramerate, LibMCDriver_Camera_double & dMaxFramerate, LibMCDriver_Camera_double & dMeanFramerate, LibMCDriver_Camera_double & dStdDevFramerate)
190190
{
191-
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_NOTIMPLEMENTED);
191+
m_pDeviceInstance->getStreamCaptureStatistics(dDesiredFramerate, dMinFramerate, dMaxFramerate, dMeanFramerate, dStdDevFramerate);
192192
}
193193

‎Drivers/Camera/Implementation/libmcdriver_camera_videodeviceinstance_win32.cpp‎

Lines changed: 139 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,18 @@ extern std::string wstring_to_utf8(const std::wstring& wstr);
5555
Class definition of CVideoDevice
5656
**************************************************************************************************************************/
5757

58-
CVideoDeviceInstance_Win32::CVideoDeviceInstance_Win32(const std::string& sIdentifier, const std::string& sOSName, const std::string& sFriendlyName)
58+
CVideoDeviceInstance_Win32::CVideoDeviceInstance_Win32(const std::string& sIdentifier, const std::string& sOSName, const std::string& sFriendlyName, LibMCEnv::PDriverEnvironment pDriverEnvironment)
5959
: m_sIdentifier (sIdentifier),
6060
m_nCurrentResolutionX (0),
6161
m_nCurrentResolutionY (0),
6262
m_nCurrentFrameRate (0),
63-
m_SourceFormat (eVideoSourceFormat::Invalid)
63+
m_SourceFormat (eVideoSourceFormat::Invalid),
64+
m_pDriverEnvironment (pDriverEnvironment),
65+
m_bCapturing (false),
66+
m_dDesiredFramerateInFPS (0.0)
6467
{
68+
if (pDriverEnvironment.get() == nullptr)
69+
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_INVALIDPARAM);
6570

6671
#ifdef _WIN32
6772

@@ -184,12 +189,19 @@ CVideoDeviceInstance_Win32::CVideoDeviceInstance_Win32(const std::string& sIdent
184189

185190
std::string GUIDToString(const GUID& guid) {
186191
wchar_t guidString[39] = { 0 }; // GUID string format is 38 characters long plus null terminator
187-
StringFromGUID2(guid, guidString, 39);
192+
int nCharsWritten = StringFromGUID2(guid, guidString, 39);
193+
if (nCharsWritten == 0)
194+
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_INVALIDPARAM, "could not convert GUID to string");
188195
guidString[38] = 0;
189-
std::wstring sResultStringW (guidString);
190-
std::string sResultString(sResultStringW.begin(), sResultStringW.end());
191196

192-
return sResultString;
197+
// GUID strings are pure ASCII, so a simple narrowing loop is safe
198+
std::string sResult;
199+
sResult.reserve(38);
200+
for (int i = 0; i < 38 && guidString[i] != 0; i++) {
201+
sResult.push_back(static_cast<char>(guidString[i]));
202+
}
203+
204+
return sResult;
193205
}
194206
#endif
195207

@@ -256,7 +268,7 @@ void CVideoDeviceInstance_Win32::refreshSupportedResolutions()
256268

257269
CVideoDeviceInstance_Win32::~CVideoDeviceInstance_Win32()
258270
{
259-
271+
stopStreamCapture();
260272
}
261273

262274
std::string CVideoDeviceInstance_Win32::getFriendlyName()
@@ -557,21 +569,140 @@ void CVideoDeviceInstance_Win32::setResolution(uint32_t nWidth, uint32_t nHeight
557569
#endif //_WIN32
558570
}
559571

572+
void CVideoDeviceInstance_Win32::captureThreadFunction()
573+
{
574+
while (m_bCapturing.load()) {
575+
try {
576+
auto pImage = m_pDriverEnvironment->CreateEmptyImage(
577+
m_nCurrentResolutionX, m_nCurrentResolutionY,
578+
72.0, 72.0, LibMCEnv::eImagePixelFormat::RGB24bit);
579+
580+
auto startTime = std::chrono::high_resolution_clock::now();
581+
582+
if (captureRawImage(pImage)) {
583+
m_pActiveStream->PushFrame(pImage.get());
584+
}
585+
586+
auto endTime = std::chrono::high_resolution_clock::now();
587+
double dFrameDurationInSeconds = std::chrono::duration<double>(endTime - startTime).count();
588+
589+
{
590+
std::lock_guard<std::mutex> lock(m_StatisticsMutex);
591+
m_FrameDurationsInSeconds.push_back(dFrameDurationInSeconds);
592+
}
593+
594+
// Sleep for the remaining frame time if capture was faster than desired
595+
if (m_nCurrentFrameRate > 0) {
596+
double dDesiredDurationInSeconds = 1.0 / (double)m_nCurrentFrameRate;
597+
double dRemainingInSeconds = dDesiredDurationInSeconds - dFrameDurationInSeconds;
598+
if (dRemainingInSeconds > 0.0) {
599+
auto nSleepMicroseconds = (uint64_t)(dRemainingInSeconds * STREAMCAPTURE_MICROSECONDS_PER_SECOND);
600+
std::this_thread::sleep_for(std::chrono::microseconds(nSleepMicroseconds));
601+
}
602+
}
603+
}
604+
catch (...) {
605+
// If a single frame fails, continue capturing rather than killing the thread.
606+
// A brief sleep avoids busy-looping on persistent errors.
607+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
608+
}
609+
}
610+
}
611+
560612
void CVideoDeviceInstance_Win32::startStreamCapture(LibMCEnv::PVideoStream pStreamInstance)
561613
{
614+
if (pStreamInstance.get() == nullptr)
615+
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_INVALIDPARAM);
616+
617+
if ((m_nCurrentResolutionX == 0) || (m_nCurrentResolutionY == 0) || (m_nCurrentFrameRate == 0))
618+
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_CAMERARESOLUTIONNOTSET);
619+
620+
// Validate that camera resolution matches the video stream dimensions
621+
uint32_t nStreamWidth = pStreamInstance->GetWidth();
622+
uint32_t nStreamHeight = pStreamInstance->GetHeight();
623+
if (nStreamWidth != m_nCurrentResolutionX || nStreamHeight != m_nCurrentResolutionY)
624+
throw ELibMCDriver_CameraInterfaceException(LIBMCDRIVER_CAMERA_ERROR_INVALIDPARAM,
625+
"video stream dimensions (" + std::to_string(nStreamWidth) + "x" + std::to_string(nStreamHeight) +
626+
") do not match camera resolution (" + std::to_string(m_nCurrentResolutionX) + "x" + std::to_string(m_nCurrentResolutionY) + ")");
627+
628+
// Stop any existing capture before starting a new one
629+
stopStreamCapture();
630+
631+
m_pActiveStream = pStreamInstance;
632+
m_dDesiredFramerateInFPS = (double)m_nCurrentFrameRate;
633+
634+
{
635+
std::lock_guard<std::mutex> lock(m_StatisticsMutex);
636+
m_FrameDurationsInSeconds.clear();
637+
}
638+
639+
m_bCapturing.store(true);
640+
m_pCaptureThread = std::make_unique<std::thread>(&CVideoDeviceInstance_Win32::captureThreadFunction, this);
562641
}
563642

564643
void CVideoDeviceInstance_Win32::stopStreamCapture()
565644
{
645+
m_bCapturing.store(false);
646+
647+
if (m_pCaptureThread && m_pCaptureThread->joinable()) {
648+
m_pCaptureThread->join();
649+
}
650+
651+
m_pCaptureThread.reset();
652+
m_pActiveStream = nullptr;
566653
}
567654

568655
bool CVideoDeviceInstance_Win32::streamCaptureIsActive()
569656
{
570-
return false;
657+
return m_bCapturing.load();
571658
}
572659

573660
void CVideoDeviceInstance_Win32::getStreamCaptureStatistics(LibMCDriver_Camera_double & dDesiredFramerate, LibMCDriver_Camera_double & dMinFramerate, LibMCDriver_Camera_double & dMaxFramerate, LibMCDriver_Camera_double & dMeanFramerate, LibMCDriver_Camera_double & dStdDevFramerate)
574661
{
662+
std::lock_guard<std::mutex> lock(m_StatisticsMutex);
663+
664+
dDesiredFramerate = m_dDesiredFramerateInFPS;
665+
dMinFramerate = 0.0;
666+
dMaxFramerate = 0.0;
667+
dMeanFramerate = 0.0;
668+
dStdDevFramerate = 0.0;
669+
670+
if (m_FrameDurationsInSeconds.size() < STREAMCAPTURE_STATISTICS_MIN_FRAME_COUNT)
671+
return;
672+
673+
// Compute min/max/mean of actual frame durations, then convert to FPS
674+
double dMinDuration = m_FrameDurationsInSeconds[0];
675+
double dMaxDuration = m_FrameDurationsInSeconds[0];
676+
double dSumDuration = 0.0;
677+
678+
for (const auto& dDuration : m_FrameDurationsInSeconds) {
679+
if (dDuration < dMinDuration)
680+
dMinDuration = dDuration;
681+
if (dDuration > dMaxDuration)
682+
dMaxDuration = dDuration;
683+
dSumDuration += dDuration;
684+
}
685+
686+
double dMeanDuration = dSumDuration / (double)m_FrameDurationsInSeconds.size();
687+
688+
// Standard deviation of durations
689+
double dVarianceSum = 0.0;
690+
for (const auto& dDuration : m_FrameDurationsInSeconds) {
691+
double dDiff = dDuration - dMeanDuration;
692+
dVarianceSum += dDiff * dDiff;
693+
}
694+
double dStdDevDuration = std::sqrt(dVarianceSum / (double)m_FrameDurationsInSeconds.size());
695+
696+
// Convert durations to framerates (shorter duration = higher framerate)
697+
if (dMinDuration > 0.0)
698+
dMaxFramerate = 1.0 / dMinDuration;
699+
if (dMaxDuration > 0.0)
700+
dMinFramerate = 1.0 / dMaxDuration;
701+
if (dMeanDuration > 0.0) {
702+
dMeanFramerate = 1.0 / dMeanDuration;
703+
// Propagate std dev from duration to framerate via |df/dt| = 1/t^2
704+
dStdDevFramerate = dStdDevDuration / (dMeanDuration * dMeanDuration);
705+
}
575706
}
576707

577708

‎Drivers/Camera/Implementation/libmcdriver_camera_videodeviceinstance_win32.hpp‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ Abstract: This is the class declaration of CVideoDevice
3838
#include "libmcdriver_camera_interfaces.hpp"
3939
#include "libmcdriver_camera_videoresolution.hpp"
4040

41+
#include <thread>
42+
#include <atomic>
43+
#include <mutex>
44+
#include <vector>
45+
4146
#ifdef _WIN32
4247
#define NOMINMAX
4348
#include <windows.h>
@@ -49,6 +54,10 @@ Abstract: This is the class declaration of CVideoDevice
4954
#include <wrl/client.h>
5055
#endif
5156

57+
#define STREAMCAPTURE_MICROSECONDS_PER_SECOND 1000000.0
58+
#define STREAMCAPTURE_STATISTICS_MIN_FRAME_COUNT 2
59+
#define STREAMCAPTURE_THREAD_SHUTDOWN_TIMEOUT_MS 5000
60+
5261
namespace LibMCDriver_Camera {
5362
namespace Impl {
5463

@@ -69,6 +78,21 @@ class CVideoDeviceInstance_Win32 {
6978

7079
eVideoSourceFormat m_SourceFormat;
7180

81+
// Driver environment for creating images during stream capture
82+
LibMCEnv::PDriverEnvironment m_pDriverEnvironment;
83+
84+
// Stream capture state
85+
LibMCEnv::PVideoStream m_pActiveStream;
86+
std::unique_ptr<std::thread> m_pCaptureThread;
87+
std::atomic<bool> m_bCapturing;
88+
89+
// Stream capture statistics (protected by m_StatisticsMutex)
90+
mutable std::mutex m_StatisticsMutex;
91+
std::vector<double> m_FrameDurationsInSeconds;
92+
double m_dDesiredFramerateInFPS;
93+
94+
void captureThreadFunction();
95+
7296
#ifdef _WIN32
7397
Microsoft::WRL::ComPtr<IMFSourceReader> m_pSourceReader;
7498
Microsoft::WRL::ComPtr<IMFMediaType> m_pMediaType;
@@ -88,7 +112,7 @@ class CVideoDeviceInstance_Win32 {
88112

89113
public:
90114

91-
CVideoDeviceInstance_Win32(const std::string & sIdentifier, const std::string & sOSName, const std::string & sFriendlyName);
115+
CVideoDeviceInstance_Win32(const std::string & sIdentifier, const std::string & sOSName, const std::string & sFriendlyName, LibMCEnv::PDriverEnvironment pDriverEnvironment);
92116

93117
virtual ~CVideoDeviceInstance_Win32();
94118

0 commit comments

Comments
 (0)