Last active
October 29, 2017 19:23
-
-
Save Arnold1/d6ad302384f788cba3e6c9b6b1a14fa0 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> | |
// 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(); | |
} | |
/*void enqueue(T t) | |
{ | |
{ | |
std::lock_guard<std::mutex> lock(m); | |
q.push(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; | |
} | |
}; | |
const int size = 20736000; | |
class BufferType { | |
public: | |
std::unique_ptr<unsigned char[]> buf; | |
BufferType() { | |
buf = (std::make_unique<unsigned char[]>(size)); | |
} | |
}; | |
const int NUMBUFFERS = 300; | |
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; | |
std::vector<BufferType> myBuffers; | |
public: | |
ImgProcessor(double recordTime_): myBuffers(NUMBUFFERS) { | |
onNewFramestart = std::chrono::high_resolution_clock::now(); | |
startTime = std::chrono::high_resolution_clock::now(); | |
frame_num = 0; | |
recordTime = recordTime_; | |
} | |
void work() { | |
unsigned int try_count = 4; | |
unsigned int count = 0; | |
bool can_exit = false; | |
while (true) { | |
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 += ".jpg"; | |
frame_num++; | |
tje_encode_to_file(("recording/" + s).c_str(), d.Width, d.Height, 4, (const unsigned char*)d.img.get()); | |
} | |
else { | |
count++; | |
if (count == try_count) { | |
can_exit = true; | |
} | |
} | |
if (can_exit) { | |
break; | |
} | |
} | |
std::cout << "thread finished..." << std::endl; | |
} | |
void createframegrabber() | |
{ | |
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]}; | |
memcpy(imgbuffer.get(), StartSrc(img), RowStride(img) * Height(img)); | |
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(100)); | |
framgrabber->setFrameChangeInterval(std::chrono::milliseconds(33)); | |
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); | |
std::cout << "Changing the cpature rate to 33 milli second" << std::endl; | |
processor.createframegrabber(); | |
//framgrabber->setFrameChangeInterval(std::chrono::microseconds(33000)); | |
//std::this_thread::sleep_for(std::chrono::seconds(10)); | |
//framgrabber->pause(); | |
processor.check_finished(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment