#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 #include #include 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 "<read(*im); outputVideo->write(*im); lutFile.write(reinterpret_cast(¤t_frame_clock),sizeof(float)); } } __LOG_CAM__ << "THREAD STOPPED "<release(); im->release(); lutFile.close(); Platform::Get().Sleep(50); // Laisser le fichier se finaliser delete im; } //********************************************************* #include #include #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 DisplayDeviceInformation(IEnumMoniker *pEnum) { IMoniker *pMoniker = NULL; int return_id_device = -1; int counter_device = 0; std::vector 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 "<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 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 "<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 "<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: "<(sizeof(float))); lutFile.read(reinterpret_cast(&end_time), sizeof(float)); __LOG_CAM__ <<"end: "<<(float)end_time<<" asked clock: "<(&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(&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; }