Files
Webcam/source/webcam.cpp

482 lines
12 KiB
C++

#include "Webcam.h"
#include "log/log.h"
#include "picture/pict_io.h"
#include "platform.h"
#include "ui/ui_sprite.h"
#include "ntypes.h"
#include "opencv2\opencv.hpp"
#include <iostream>
#include <fstream>
#include <filesystem>
using namespace GS;
using namespace GS::Script;
using namespace cv;
static const double webcam_fps = 25.0;
Webcam::Webcam() {
}
Webcam::~Webcam() {
Close();
if (InputVideo && InputVideo->isOpened())
InputVideo->release();
InputVideo = nullptr;
outputVideo = nullptr;
}
ProcessWebcamThread::ProcessWebcamThread(cv::VideoCapture *_InputVideo, cv::VideoWriter *_outputVideo, int width, int height,std::string _outputVideoName,int _videoIndex) : InputVideo(_InputVideo), outputVideo(_outputVideo), not_finish(true), pause(true), record_frame(false),outputVideoName(_outputVideoName),videoIndex(_videoIndex) {
im = new Mat(width, height, CV_8UC3);
}
void ProcessWebcamThread::Execute() {
bool temp_not_finish = true;
bool temp_pause = true;
bool temp_record_frame = false;
current_frame_clock = 0.0;
//construction LUT pour synchro avec la clock squirrel
std::ofstream lutFile(outputVideoName+"_lut.bin", std::ios::binary);
__LOG_CAM__ << "NEW RECORDING THREAD "<<videoIndex<<" LAUNCHED \n";
int frame_index = 0;
while (temp_not_finish) {
mutex_not_finish.Lock();
temp_not_finish = not_finish;
mutex_not_finish.Unlock();
mutex_pause.Lock();
temp_pause = pause;
mutex_pause.Unlock();
mutex_record_frame.Lock();
temp_record_frame = record_frame;
mutex_record_frame.Unlock();
if (temp_pause || !temp_record_frame)
Platform::Get().Sleep(10);
else{
InputVideo->read(*im);
outputVideo->write(*im);
lutFile.write(reinterpret_cast<const char*>(&current_frame_clock),sizeof(float));
}
}
__LOG_CAM__ << "THREAD STOPPED "<<videoIndex<<"\n";
outputVideo->release();
im->release();
lutFile.close();
Platform::Get().Sleep(50); // Laisser le fichier se finaliser
delete im;
}
//*********************************************************
#include <windows.h>
#include <dshow.h>
#pragma comment(lib, "strmiids")
HRESULT EnumerateDevices(REFGUID category, IEnumMoniker **ppEnum)
{
// Create the System Device Enumerator.
ICreateDevEnum *pDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
if (SUCCEEDED(hr))
{
// Create an enumerator for the category.
hr = pDevEnum->CreateClassEnumerator(category, ppEnum, 0);
if (hr == S_FALSE)
{
hr = VFW_E_NOT_FOUND; // The category is empty. Treat as an error.
}
pDevEnum->Release();
}
return hr;
}
std::string BstrToString(BSTR bstr)
{
if (!bstr) return std::string();
int len = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, NULL, 0, NULL, NULL);
if (len <= 0) return std::string();
std::string result(len - 1, '\0'); // -1 pour exclure le \0 final
WideCharToMultiByte(CP_UTF8, 0, bstr, -1, &result[0], len, NULL, NULL);
return result;
}
std::vector<int> DisplayDeviceInformation(IEnumMoniker *pEnum)
{
IMoniker *pMoniker = NULL;
int return_id_device = -1;
int counter_device = 0;
std::vector<int> camera_ids;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
{
IPropertyBag *pPropBag;
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
if (FAILED(hr))
{
pMoniker->Release();
continue;
}
VARIANT var;
VariantInit(&var);
// Get description or friendly name.
hr = pPropBag->Read(L"Description", &var, 0);
if (FAILED(hr))
{
hr = pPropBag->Read(L"FriendlyName", &var, 0);
}
if (SUCCEEDED(hr))
{
std::string name = BstrToString(var.bstrVal);
printf("%S\n", var.bstrVal);
__LOG_CAM__<<"FOUND CAM ID "<<counter_device<<" NAME "<<name.c_str()<<"\n";
if (
wcsstr(var.bstrVal, L"MMP SDK") == nullptr &&
wcsstr(var.bstrVal, L"Oculus Rift") == nullptr &&
wcsstr(var.bstrVal, L"HTC Vive") == nullptr &&
wcsstr(var.bstrVal, L"RealSense") == nullptr &&
wcsstr(var.bstrVal, L"OBS") == nullptr
){
camera_ids.push_back(counter_device);
return_id_device = counter_device;
}
__LOG_CAM__<<"RETURN DEVICE ID "<<return_id_device<<"\n";
VariantClear(&var);
}
hr = pPropBag->Write(L"FriendlyName", &var);
// WaveInID applies only to audio capture devices.
hr = pPropBag->Read(L"WaveInID", &var, 0);
if (SUCCEEDED(hr))
{
printf("WaveIn ID: %d\n", var.lVal);
VariantClear(&var);
}
hr = pPropBag->Read(L"DevicePath", &var, 0);
if (SUCCEEDED(hr))
{
// The device path is not intended for display.
printf("Device path: %S\n", var.bstrVal);
VariantClear(&var);
}
pPropBag->Release();
pMoniker->Release();
++counter_device;
}
return camera_ids;
}
bool Webcam::Open(int width_, int height_) {
__LOG_CAM__ << "Open\n";
ReplayInputVideo = NULL;
index_save_video = -1;
index_replay_video = -1;
thread_process = nullptr;
InputVideo = nullptr;
outputVideo = nullptr;
width = width_;
height = height_;
std::vector<int> id_devices;
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (SUCCEEDED(hr))
{
IEnumMoniker *pEnum;
hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
if (SUCCEEDED(hr))
{
id_devices = DisplayDeviceInformation(pEnum);
pEnum->Release();
}
CoUninitialize();
}
if (id_devices.size() == 0)
return false;
InputVideo = new VideoCapture();
int device_id = -1;
for (int i = 0; i < id_devices.size(); i++)
{
InputVideo->open(id_devices[i]);
if(InputVideo->isOpened()){
device_id = id_devices[i];
break;
}
}
if(device_id == -1)
return false;
__LOG_CAM__ << "OPEN CAM ID "<<device_id <<"\n";
InputVideo->set(CV_CAP_PROP_FRAME_WIDTH, width);
InputVideo->set(CV_CAP_PROP_FRAME_HEIGHT, height);
InputVideo->set(CAP_PROP_FPS, webcam_fps);
outputVideo = new VideoWriter;
ReplayInputVideo = new VideoCapture();
ReplayIm = new Mat(width, height, CV_8UC4);
ReplayIm->setTo(cv::Scalar(255, 0, 0, 255));
ReplayImframe = new Mat;
return true;
}
void Webcam::Reset(bool _reset_video) {
__LOG_CAM__ << "RESET\n";
Close();
video_times.clear();
index_save_video = -1;
index_replay_video = -1;
if(_reset_video)
ResetVideo();
}
void Webcam::RecordFrame(float _clock) {
if (!thread_process)
return;
thread_process->mutex_record_frame.Lock();
thread_process->record_frame = true;
thread_process->current_frame_clock = _clock;
thread_process->mutex_record_frame.Unlock();
}
void Webcam::ResetVideo() {
// chargement du flux video
std::stringstream video_name;
video_name << "./webcam_record_" << index_save_video << ".avi";
// set the codec fourcc
unsigned int ex = outputVideo->fourcc('M', 'J', 'P', 'G');
outputVideo->open(video_name.str(), ex, webcam_fps, Size(width, height), true);
if (!outputVideo->isOpened())
__LOG_CAM__ << "Could not open the output video for write.\n";
//Grab a frame
thread_process = new ProcessWebcamThread(InputVideo, outputVideo, width, height,"webcam_record_"+std::to_string(index_save_video),index_save_video);
thread_process->Start();
index_save_video++;
thread_process->mutex_pause.Lock();
thread_process->pause = true;
thread_process->mutex_pause.Unlock();
}
void Webcam::setPause(bool pause) {
__LOG_CAM__ <<"setPause "<<pause<<"\n";
if (!thread_process) {
if (!pause) {
__LOG_CAM__ <<"A\n";
Close();
ResetVideo();
thread_process->mutex_pause.Lock();
thread_process->pause = pause;
thread_process->mutex_pause.Unlock();
}
return;
}
thread_process->mutex_pause.Lock();
bool thread_pause = thread_process->pause;
thread_process->mutex_pause.Unlock();
thread_process->mutex_pause.Lock();
thread_process->pause = pause;
thread_process->mutex_pause.Unlock();
if (!thread_pause && pause) {
__LOG_CAM__ <<"B\n";
Close();
Platform::Get().Sleep(50); // Laisser le fichier se finaliser
__LOG_CAM__ << "REPLAY INPUT RELEASE ./webcam_record_" << index_replay_video << ".avi\n ";
ReplayInputVideo->release();
index_replay_video = index_save_video - 1;
std::stringstream video_name;
video_name << "./webcam_record_" << index_replay_video << ".avi";
__LOG_CAM__ << "REPLAY INPUT IS HOLDING ./webcam_record_" << index_replay_video << ".avi\n";
ReplayInputVideo->open(video_name.str());
return;
} else if (thread_pause && !pause) {
__LOG_CAM__ <<"C\n";
Close();
ResetVideo();
}
thread_process->mutex_pause.Lock();
thread_process->pause = pause;
thread_process->mutex_pause.Unlock();
}
//*********************************************************
bool Webcam::IsThreadRunning() {
return thread_process != nullptr;
}
void Webcam::Close() {
__LOG_CAM__ << "Close\n";
if (thread_process) {
thread_process->mutex_not_finish.Lock();
thread_process->not_finish = false;
thread_process->mutex_not_finish.Unlock();
thread_process->Join();
delete thread_process;
thread_process = nullptr;
}
if(ReplayInputVideo)
ReplayInputVideo->release();
}
uchar *Webcam::SetFrame(float clock, GS::String record_name , int record_index) {
//create buffer from lut
bool is_the_last_record = record_index == index_save_video - 1 ;
if(video_times.size() == 0){
//charger lut dans video_times
__LOG_CAM__ << "Chargement LUT: "<<record_name<<"_"<<record_index<<"_lut.bin\n";
std::stringstream lut_path;
lut_path << record_name <<"_"<<record_index<< "_lut.bin";
std::ifstream lutFile(lut_path.str(), std::ios::binary);
if (!lutFile.is_open())
{
__LOG_CAM__ << "Cannot open LUT file: " << record_name<<"_lut.bin\n";
}
video_times.clear();
float end_time;
lutFile.seekg(0, std::ios::end);
std::streampos size = lutFile.tellg();
__LOG_CAM__ <<"size: "<<(int)size<<"\n";
if(size < 4 ) //il n'y a meme pas un seul float ecrit
return NULL;
lutFile.seekg(size - static_cast<std::streamoff>(sizeof(float)));
lutFile.read(reinterpret_cast<char*>(&end_time), sizeof(float));
__LOG_CAM__ <<"end: "<<(float)end_time<<" asked clock: "<<clock<<"\n";
//read la derniere et premiere clock
lutFile.seekg(0);
float start_time;
lutFile.read(reinterpret_cast<char*>(&start_time), sizeof(float));
if(clock < start_time){
__LOG_CAM__ << "clock < start_time\n";
lutFile.close();
return NULL;
}
else if(clock > end_time && !is_the_last_record){
__LOG_CAM__ << "clock > end_time\n";
lutFile.close();
return SetFrame(clock,record_name,record_index+1); //recursive search forward
}
if (record_index != index_replay_video) {
__LOG_CAM__ << "Changing video\n";
index_replay_video = record_index;
std::stringstream video_name;
video_name << record_name << "_" << index_replay_video << ".avi";
__LOG_CAM__ << record_name << "_" << index_replay_video << ".avi\n";
ReplayInputVideo->release();
ReplayInputVideo->open(video_name.str());
}
float t;
lutFile.seekg(0);
while (lutFile.read(reinterpret_cast<char*>(&t), sizeof(float)))
{
video_times.push_back(t);
}
lutFile.close();
}
//upper_bound -1 clock dans video_times pour recuperer index frame ou clock <=
if (video_times.empty())
return NULL;
//force reset if clock not in range
if(clock < video_times[0] || (clock > video_times[video_times.size() -1] && !is_the_last_record)){
__LOG_CAM__ << "CLEAR \n";
video_times.clear();
return SetFrame(clock, record_name , 0);
}
auto it = std::upper_bound(video_times.begin(),video_times.end(),clock);
if (it == video_times.begin())
{
__LOG_CAM__ << "clock before first frame\n";
return nullptr;
}
size_t index = std::distance(video_times.begin(), it) - 1;
if (!ReplayInputVideo || !ReplayInputVideo->isOpened())
{
__LOG_CAM__ << "ReplayInputVideo not opened\n";
return NULL;
}
int frameCount = (int)ReplayInputVideo->get(cv::CAP_PROP_FRAME_COUNT) - 1;
ReplayInputVideo->set(cv::CAP_PROP_POS_FRAMES, index);
if (!ReplayInputVideo->read(*ReplayImframe))
{
__LOG_CAM__ << "Failed to read frame at index " << index << "\n";
return NULL;
}
cv::cvtColor(*ReplayImframe, *ReplayIm, CV_BGR2RGBA, 4);
return ReplayIm->data;
}