Last active
October 29, 2017 22:55
-
-
Save Arnold1/57508cf245cf77be458049ca3fdf10d4 to your computer and use it in GitHub Desktop.
screen capture
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ScreenCapture.h" | |
#include <algorithm> | |
#include <atomic> | |
#include <chrono> | |
#include <iostream> | |
#include <locale> | |
#include <string> | |
#include <thread> | |
#include <vector> | |
#include <queue> | |
#include "opencv2/opencv.hpp" | |
// THESE LIBRARIES ARE HERE FOR CONVINIENCE!! They are SLOW and ONLY USED FOR | |
// HOW THE LIBRARY WORKS! | |
#define TJE_IMPLEMENTATION | |
#include "tiny_jpeg.h" | |
#define LODEPNG_COMPILE_PNG | |
#define LODEPNG_COMPILE_DISK | |
#include "lodepng.h" | |
///////////////////////////////////////////////////////////////////////// | |
using namespace std::chrono_literals; | |
std::shared_ptr<SL::Screen_Capture::IScreenCaptureManager> framgrabber; | |
std::atomic<int> realcounter; | |
std::atomic<int> onNewFramecounter; | |
inline std::ostream &operator<<(std::ostream &os, | |
const SL::Screen_Capture::ImageRect &p) { | |
return os << "left=" << p.left << " top=" << p.top << " right=" << p.right | |
<< " bottom=" << p.bottom; | |
} | |
inline std::ostream &operator<<(std::ostream &os, | |
const SL::Screen_Capture::Monitor &p) { | |
return os << "Id=" << p.Id << " Index=" << p.Index << " Height=" << p.Height | |
<< " Width=" << p.Width << " OffsetX=" << p.OffsetX | |
<< " OffsetY=" << p.OffsetY << " Name=" << p.Name; | |
} | |
template <class T> | |
class SafeQueue { | |
public: | |
SafeQueue(void) : q(), m(), c() {} | |
~SafeQueue(void) {} | |
void enqueue(T &&t) { | |
{ | |
std::lock_guard<std::mutex> lock(m); | |
q.push(std::move(t)); | |
} | |
c.notify_one(); | |
} | |
T dequeue(void) { | |
std::unique_lock<std::mutex> lock(m); | |
while (q.empty()) { | |
c.wait(lock); | |
} | |
T val = q.front(); | |
q.pop(); | |
return val; | |
} | |
bool try_dequeue(T &d, std::chrono::milliseconds timeout) { | |
std::unique_lock<std::mutex> lock(m); | |
if (!c.wait_for(lock, timeout, [this] { return !q.empty(); })) { | |
return false; | |
} | |
d = std::move(q.front()); | |
q.pop(); | |
return true; | |
} | |
private: | |
std::queue<T> q; | |
mutable std::mutex m; | |
std::condition_variable c; | |
}; | |
class data { | |
public: | |
int size; | |
int Width; | |
int Height; | |
std::unique_ptr<unsigned char[]> img; | |
data() {} | |
data(int size_, int Width_, int Height_, | |
std::unique_ptr<unsigned char[]> img_) { | |
size = size_; | |
Width = Width_; | |
Height = Height_; | |
img = std::move(img_); | |
} | |
data(const data &) = delete; | |
data(data &&other) { | |
size = other.size; | |
Width = other.Width; | |
Height = other.Height; | |
img = std::move(other.img); | |
} | |
data &operator=(data &&other) { | |
if (this != &other) { | |
size = other.size; | |
Width = other.Width; | |
Height = other.Height; | |
img = std::move(other.img); | |
} | |
return *this; | |
} | |
}; | |
class ImgProcessor { | |
private: | |
SafeQueue<data> queue; | |
std::chrono::time_point<std::chrono::high_resolution_clock> onNewFramestart; | |
std::chrono::time_point<std::chrono::high_resolution_clock> startTime; | |
double recordTime; // ms | |
int frame_num; | |
std::thread t1; | |
public: | |
ImgProcessor(double recordTime_) { | |
onNewFramestart = std::chrono::high_resolution_clock::now(); | |
startTime = std::chrono::high_resolution_clock::now(); | |
frame_num = 0; | |
recordTime = recordTime_; | |
} | |
void saveBmp(const std::string filename, int Width, int Height, | |
std::unique_ptr<unsigned char[]> img) { | |
struct RGB { | |
uint8_t r, g, b; | |
}; | |
#define BI_RGB 0L | |
typedef struct __attribute__((__packed__)) { | |
uint16_t Signature; | |
uint32_t Size; | |
uint32_t Reserved; | |
uint32_t BitsOffset; | |
} BITMAPFILEHEADER; | |
#define BITMAP_FILEHEADER_SIZE 14 | |
typedef struct __attribute__((__packed__)) { | |
uint32_t HeaderSize; | |
int32_t Width; | |
int32_t Height; | |
uint16_t Planes; | |
uint16_t BitCount; | |
uint32_t Compression; | |
uint32_t SizeImage; | |
int32_t PelsPerMeterX; | |
int32_t PelsPerMeterY; | |
uint32_t ClrUsed; | |
uint32_t ClrImportant; | |
} BITMAPINFOHEADER; | |
BITMAPINFOHEADER BMIH; | |
BITMAPFILEHEADER bmfh; | |
FILE *pFile = fopen(filename.c_str(), "wb"); | |
if (pFile == NULL) { | |
return; | |
} | |
BMIH.HeaderSize = sizeof(BITMAPINFOHEADER); | |
BMIH.Width = Width; | |
BMIH.Height = Height; | |
BMIH.Planes = 1; | |
BMIH.BitCount = 24; | |
BMIH.Compression = BI_RGB; | |
BMIH.SizeImage = Width * Height * 3; | |
int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.HeaderSize; | |
long lImageSize = BMIH.SizeImage; | |
long lFileSize = nBitsOffset + lImageSize; | |
bmfh.Signature = 'B' + ('M' << 8); | |
bmfh.BitsOffset = nBitsOffset; | |
bmfh.Size = lFileSize; | |
bmfh.Reserved = 0; | |
unsigned int nWrittenFileHeaderSize = | |
fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), pFile); | |
unsigned int nWrittenInfoHeaderSize = | |
fwrite(&BMIH, 1, sizeof(BITMAPINFOHEADER), pFile); | |
unsigned int nWrittenDIBDataSize = fwrite(img.get(), 1, lImageSize, pFile); | |
fclose(pFile); | |
} | |
void work() { | |
unsigned int try_count = 4; | |
unsigned int count = 0; | |
bool can_exit = false; | |
cv::VideoWriter out_capture("recording/video.avi", | |
CV_FOURCC('H', '2', '6', '4'), 22, // i put 22 here because thats i see as output in avg: onNewFrame fps22 | |
cv::Size(2880, 1800)); // this is hardcoded... not good | |
while (true) { | |
// auto data = queue.dequeue(); | |
data d; | |
auto success = queue.try_dequeue(d, std::chrono::milliseconds(100)); | |
if (success) { | |
count = 0; | |
//std::string s = std::to_string(frame_num); | |
//s += ".bmp"; | |
frame_num++; | |
// Support for writing JPG | |
// std::vector<int> compression_params; | |
// compression_params.push_back( CV_IMWRITE_JPEG_QUALITY ); | |
// compression_params.push_back( 100 ); | |
out_capture.write(cv::Mat(d.Height, d.Width, CV_8UC3, d.img.get())); | |
// cv::imwrite(("recording/" + s).c_str(), cv::Mat(d.Height, d.Width, | |
// CV_8UC3, d.img.get()));//, compression_params); | |
// saveBmp("recording/" + s, d.Width, d.Height, std::move(d.img)); | |
} else { | |
count++; | |
if (count == try_count) { | |
can_exit = true; | |
} | |
} | |
if (can_exit) { | |
break; | |
} | |
} | |
std::cout << "thread finished..." << std::endl; | |
} | |
void createframegrabber(int frame_change_interval) { | |
realcounter = 0; | |
onNewFramecounter = 0; | |
framgrabber = | |
SL::Screen_Capture::CreateCaptureConfiguration([]() { | |
auto mons = SL::Screen_Capture::GetMonitors(); | |
std::cout << "Library is requesting the list of monitors to capture!" | |
<< std::endl; | |
for (auto &m : mons) { | |
// capture just a 512x512 square... USERS SHOULD MAKE SURE bounds | |
// are | |
// valid!!!! | |
/* | |
m.OffsetX += 512; | |
m.OffsetY += 512; | |
m.Height = 512; | |
m.Width = 512; | |
*/ | |
std::cout << m << std::endl; | |
} | |
return mons; | |
}) | |
->onNewFrame([&](const SL::Screen_Capture::Image &img, | |
const SL::Screen_Capture::Monitor &monitor) { | |
if (std::chrono::duration_cast<std::chrono::milliseconds>( | |
std::chrono::high_resolution_clock::now() - startTime) | |
.count() >= recordTime) { | |
framgrabber->pause(); | |
return; | |
} | |
// auto r = realcounter.fetch_add(1); | |
// auto s = std::to_string(r) + std::string("MONITORNEW_") + | |
// std::string(".jpg"); | |
auto size = RowStride(img) * Height(img); | |
// auto start = std::chrono::high_resolution_clock::now(); | |
std::unique_ptr<unsigned char[]> imgbuffer{ | |
new unsigned char[size]}; | |
SL::Screen_Capture::ExtractAndConvertToBGR(img, imgbuffer.get(), | |
size); | |
queue.enqueue( | |
data(size, Width(img), Height(img), std::move(imgbuffer))); | |
// std::cout << | |
// std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() | |
// - start).count() << std::endl; | |
if (std::chrono::duration_cast<std::chrono::milliseconds>( | |
std::chrono::high_resolution_clock::now() - | |
onNewFramestart) | |
.count() >= 1000) { | |
std::cout << "onNewFrame fps" << onNewFramecounter << std::endl; | |
onNewFramecounter = 0; | |
onNewFramestart = std::chrono::high_resolution_clock::now(); | |
} | |
onNewFramecounter += 1; | |
}) | |
/*->onMouseChanged([&](const SL::Screen_Capture::Image *img, const | |
SL::Screen_Capture::Point &point) { | |
auto r = realcounter.fetch_add(1); | |
auto s = std::to_string(r) + std::string(" M") + | |
std::string(".png"); | |
if (img) { | |
// std::cout << "New mouse coordinates AND NEW Image | |
received." << " x= " << point.x << " y= " << | |
// point.y << std::endl; | |
// lodepng::encode(s,StartSrc(*img), Width(*img), | |
Height(*img)); | |
} | |
else { | |
// std::cout << "New mouse coordinates received." << " x= " | |
<< point.x << " y= " << point.y << " The | |
// mouse image is still the same | |
// as the last" << std::endl; | |
} | |
})*/ | |
->start_capturing(); | |
framgrabber->setFrameChangeInterval( | |
std::chrono::milliseconds(frame_change_interval)); | |
framgrabber->setMouseChangeInterval(std::chrono::milliseconds(100)); | |
t1 = std::thread(&ImgProcessor::work, this); | |
} | |
void check_finished() { t1.join(); } | |
}; | |
int main() { | |
std::cout << "Starting Capture Demo/Test" << std::endl; | |
std::cout << "Testing captured monitor bounds check" << std::endl; | |
auto goodmonitors = SL::Screen_Capture::GetMonitors(); | |
for (auto &m : goodmonitors) { | |
std::cout << m << std::endl; | |
assert(isMonitorInsideBounds(goodmonitors, m)); | |
} | |
auto badmonitors = SL::Screen_Capture::GetMonitors(); | |
for (auto m : badmonitors) { | |
m.Height += 1; | |
std::cout << m << std::endl; | |
assert(!isMonitorInsideBounds(goodmonitors, m)); | |
} | |
for (auto m : badmonitors) { | |
m.Width += 1; | |
std::cout << m << std::endl; | |
assert(!isMonitorInsideBounds(goodmonitors, m)); | |
} | |
ImgProcessor processor(10000, 33); | |
std::cout << "Changing the cpature rate to 33 milli second" << std::endl; | |
processor.createframegrabber(); | |
processor.check_finished(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment