Skip to content

Instantly share code, notes, and snippets.

@Arnold1
Last active October 29, 2017 22:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Arnold1/57508cf245cf77be458049ca3fdf10d4 to your computer and use it in GitHub Desktop.
Save Arnold1/57508cf245cf77be458049ca3fdf10d4 to your computer and use it in GitHub Desktop.
screen capture
#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