#include <sstream> | |
#include <Cocoa/Cocoa.h> | |
#include <CoreFoundation/CoreFoundation.h> | |
#include <screencapture/mac/ScreenCaptureDisplayStream.h> | |
namespace sc { | |
ScreenCaptureDisplayStream::ScreenCaptureDisplayStream() | |
:Base() | |
,stream_ref(NULL) | |
{ | |
dq = dispatch_queue_create("com.roxlu.screengrabber", DISPATCH_QUEUE_SERIAL); | |
} | |
int ScreenCaptureDisplayStream::init() { | |
/* Fill our cached list of displays. */ | |
CGDirectDisplayID display_ids[10]; | |
uint32_t found_displays = 0; | |
CGError err = CGGetActiveDisplayList(10, display_ids, &found_displays); | |
if (kCGErrorSuccess != err) { | |
printf("Error: failed to retrieve a list of active displays in `ScreenCaptureDisplayStream`.\n"); | |
return -1; | |
} | |
if (0 == found_displays) { | |
printf("Error: we didn't find any active display.\n"); | |
return -2; | |
} | |
for (uint32_t i = 0; i < found_displays; ++i) { | |
Display* display = new Display(); | |
ScreenCaptureDisplayStreamDisplayInfo* info = new ScreenCaptureDisplayStreamDisplayInfo(); | |
std::stringstream ss; | |
ss << "Monitor " << i; | |
info->id = display_ids[i]; | |
display->info = (void*)info; | |
display->name = ss.str(); | |
displays.push_back(display); | |
} | |
return 0; | |
} | |
int ScreenCaptureDisplayStream::shutdown() { | |
/* Release the stream. */ | |
if (NULL != stream_ref) { | |
CFRelease(stream_ref); | |
stream_ref = NULL; | |
} | |
/* Clear our cached list of displays. */ | |
for (size_t i = 0; i < displays.size(); ++i) { | |
ScreenCaptureDisplayStreamDisplayInfo* info = static_cast<ScreenCaptureDisplayStreamDisplayInfo*>(displays[i]->info); | |
if (NULL == info) { | |
printf("Error: failed to cast back to `ScreenCaptureDisplayStreamDisplayInfo` in `ScreenCaptureDisplayStream`. Not supposed to happen. Leaking memory"); | |
} | |
else { | |
delete info; | |
info = NULL; | |
} | |
delete displays[i]; | |
displays[i] = NULL; | |
} | |
displays.clear(); | |
return 0; | |
} | |
int ScreenCaptureDisplayStream::configure(Settings settings) { | |
/* Get the implementation specific display id. */ | |
ScreenCaptureDisplayStreamDisplayInfo* info = static_cast<ScreenCaptureDisplayStreamDisplayInfo*>(displays[settings.display]->info); | |
if (NULL == info) { | |
printf("Error: failed to cast the display info of stream screen capture. Failed to setup\n"); | |
return -1; | |
} | |
/* Already a stream? Reset. */ | |
if (NULL != stream_ref) { | |
if (0 == isStarted()) { | |
if (0 != stop()) { | |
printf("We're reconfiguring/setupping the display stream but we were captureing; stopping failed. Not supposed to happen; and you may leak memory here. We continue capturing though.\n"); | |
} | |
} | |
state &= ~SC_STATE_STARTED; | |
state |= SC_STATE_STOPPED; | |
CFRelease(stream_ref); | |
stream_ref = NULL; | |
} | |
uint32_t pixel_format = 0; | |
switch (settings.pixel_format) { | |
case SC_420F: { | |
pixel_format = '420f'; | |
break; | |
} | |
case SC_420V: { | |
pixel_format = '420v'; | |
break; | |
} | |
case SC_BGRA: { | |
pixel_format = 'BGRA'; | |
break; | |
} | |
case SC_L10R: { | |
pixel_format = 'l10r'; | |
break; | |
} | |
default: { | |
printf("Error: unsupported pixel format; cannot configure display stream.\n"); | |
return -2; | |
} | |
} | |
__block PixelBuffer pixel_buffer; | |
if (0 != pixel_buffer.init(settings.output_width, settings.output_height, settings.pixel_format)) { | |
printf("Error: failed to setup the the pixel format.\n"); | |
return -3; | |
} | |
/* @todo > WE DON'T WANT TO MAKE THIS THE RESPONSIBILITY OF AN IMPLEMENTATION! */ | |
pixel_buffer.user = user; | |
/* @todo make some settings available through API. */ | |
void* keys[2]; | |
void* values[2]; | |
CFDictionaryRef opts; | |
keys[0] = (void *) kCGDisplayStreamShowCursor; | |
values[0] = (void *) kCFBooleanTrue; | |
opts = CFDictionaryCreate(kCFAllocatorDefault, (const void **) keys, (const void **) values, 1, NULL, NULL); | |
/* | |
UPDATE USING THIS CLEAN CODE: https://gist.github.com/roxlu/60f2f635347863d6384d | |
TEST IF UPDATING THE CHANGED RECTS ONLY IS FASTER | |
*/ | |
stream_ref = CGDisplayStreamCreateWithDispatchQueue(info->id, | |
settings.output_width, | |
settings.output_height, | |
pixel_format, | |
opts, | |
dq, | |
^(CGDisplayStreamFrameStatus status, /* kCGDisplayStreamFrameComplete, *FrameIdle, *FrameBlank, *Stopped */ | |
uint64_t time, /* Mach absolute time when the event occurred. */ | |
IOSurfaceRef frame, /* opaque pixel buffer, can be backed by GL, CL, etc.. This may be NULL in some cases. See the docs if you want to keep access to this. */ | |
CGDisplayStreamUpdateRef ref) | |
{ | |
if (kCGDisplayStreamFrameStatusFrameComplete == status | |
&& NULL != frame) | |
{ | |
/* | |
n.b. when the user needs to handle the pixel buffer after the call, it needs to make | |
a copy. CGDisplayStream* supports a feature where you can increment the in-use-count, see | |
the documention: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/index.html#//apple_ref/doc/c_ref/CGDisplayStreamFrameAvailableHandler | |
*/ | |
IOSurfaceLock(frame, kIOSurfaceLockReadOnly, NULL); | |
size_t plane_count = IOSurfaceGetPlaneCount(frame); | |
if (2 == plane_count) { | |
pixel_buffer.plane[0] = (uint8_t*)IOSurfaceGetBaseAddressOfPlane(frame, 0); | |
pixel_buffer.stride[0] = IOSurfaceGetBytesPerRowOfPlane(frame, 0); | |
pixel_buffer.plane[1] = (uint8_t*)IOSurfaceGetBaseAddressOfPlane(frame, 1); | |
pixel_buffer.stride[1] = IOSurfaceGetBytesPerRowOfPlane(frame, 1); | |
} | |
else if (0 == plane_count) { | |
pixel_buffer.plane[0] = (uint8_t*)IOSurfaceGetBaseAddress(frame); | |
pixel_buffer.stride[0] = IOSurfaceGetBytesPerRow(frame); | |
} | |
else { | |
printf("Error: unsupported plane count in the displaystream capture. Cannot setup the pixel buffer. Exiting now.\n"); | |
exit(EXIT_FAILURE); | |
} | |
callback(pixel_buffer); | |
IOSurfaceUnlock(frame, kIOSurfaceLockReadOnly, NULL); | |
} | |
} | |
); | |
if (NULL == stream_ref) { | |
printf("Error: failed to create a display stream that we use to capture the screen.\n"); | |
return -4; | |
} | |
return 0; | |
} | |
int ScreenCaptureDisplayStream::start() { | |
CGError err = CGDisplayStreamStart(stream_ref); | |
if (kCGErrorSuccess != err) { | |
printf("Error: failed to start the display stream capturer. CGDisplayStreamStart failed: %d .\n", err); | |
return -1; | |
} | |
return 0; | |
} | |
int ScreenCaptureDisplayStream::stop() { | |
CGError err = CGDisplayStreamStop(stream_ref); | |
if (kCGErrorSuccess != err) { | |
printf("Error: failed to stop the display stream capturer. CGDisplayStreamStart failed: %d .\n", err); | |
return -1; | |
} | |
return 0; | |
} | |
int ScreenCaptureDisplayStream::getDisplays(std::vector<Display*>& result) { | |
result = displays; | |
return 0; | |
} | |
int ScreenCaptureDisplayStream::getPixelFormats(std::vector<int>& formats) { | |
formats.clear(); | |
formats.push_back(SC_420V); | |
formats.push_back(SC_420F); | |
formats.push_back(SC_BGRA); | |
formats.push_back(SC_L10R); | |
return 0; | |
} | |
}; /* namespace sc */ | |
screen capture using CGDisplayStreamCreateWithDispatchQueue
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章
聊聊Swift中的宏
原創
2024-04-18 23:26:09
Objective-C網絡請求開發的高效實現方法與技巧
原創
2024-04-12 23:25:47
萬字長文詳解如何使用Swift提高代碼質量 | 京東雲技術團隊
原創
2023-05-10 11:55:55
iOS MachineLearning 系列(12)—— 自然語言之詞句相似性分析
原創
2023-05-07 00:14:53
金蝶隨手記團隊的Protobuf應用實踐(原理篇)
原創
2023-01-28 23:02:11
White Source SAST—信息安全測試工具
原創
2022-04-30 11:47:38
iOS UIMenuController 的使用及不顯示的解決辦法
原創
2022-04-30 10:26:01
iOS 實現文字顏色漸變的方法
zh_iOS
2022-04-30 10:25:59
用項目符號格式化 UILabel? - Format UILabel with bullet points?
fyin1314
2021-12-29 09:18:02
CoreData:警告:無法加載名爲的類 - CoreData: warning: Unable to load class named
javail
2021-12-26 21:15:03
[iOS研習記]——談談靜態庫與動態庫
原創
2021-12-25 21:37:57
[iOS研習記]——聊聊野指針與殭屍對象定位
原創
2021-12-25 21:37:57
洛谷個人主頁
osc_57h7mkgj
2021-12-25 21:36:21
如何讓 UILabel 響應點擊? - How to get UILabel to respond to tap?
javail
2021-12-25 21:19:59