first commit
This commit is contained in:
304
GB28181Device/media/window_capture_avf.mm
Normal file
304
GB28181Device/media/window_capture_avf.mm
Normal file
@@ -0,0 +1,304 @@
|
||||
/***************************************************************************************
|
||||
*
|
||||
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
*
|
||||
* By downloading, copying, installing or using the software you agree to this license.
|
||||
* If you do not agree to this license, do not download, install,
|
||||
* copy or use the software.
|
||||
*
|
||||
* Copyright (C) 2014-2022, Happytimesoft Corporation, all rights reserved.
|
||||
*
|
||||
* Redistribution and use in binary forms, with or without modification, are permitted.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed
|
||||
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the License.
|
||||
*
|
||||
****************************************************************************************/
|
||||
|
||||
#include "sys_inc.h"
|
||||
#include "window_capture_avf.h"
|
||||
#include <libavutil/pixfmt.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
/***************************************************************************************/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double framerate;
|
||||
int width;
|
||||
int height;
|
||||
char window_title[256];
|
||||
int window_id;
|
||||
|
||||
enum AVPixelFormat pixel_format;
|
||||
|
||||
avf_window_callback callback;
|
||||
void * userdata;
|
||||
} AVFWindowContext;
|
||||
|
||||
/***************************************************************************************/
|
||||
|
||||
#define WINDOW_NAME ((NSString *)kCGWindowName)
|
||||
#define WINDOW_NUMBER ((NSString *)kCGWindowNumber)
|
||||
#define WINDOW_LAYER ((NSString *)kCGWindowLayer)
|
||||
#define OWNER_NAME ((NSString *)kCGWindowOwnerName)
|
||||
#define OWNER_PID ((NSNumber *)kCGWindowOwnerPID)
|
||||
|
||||
static NSComparator win_info_cmp = ^(NSDictionary *o1, NSDictionary *o2)
|
||||
{
|
||||
NSComparisonResult res = [o1[OWNER_NAME] compare:o2[OWNER_NAME]];
|
||||
if (res != NSOrderedSame)
|
||||
return res;
|
||||
|
||||
res = [o1[OWNER_PID] compare:o2[OWNER_PID]];
|
||||
if (res != NSOrderedSame)
|
||||
return res;
|
||||
|
||||
res = [o1[WINDOW_NAME] compare:o2[WINDOW_NAME]];
|
||||
if (res != NSOrderedSame)
|
||||
return res;
|
||||
|
||||
return [o1[WINDOW_NUMBER] compare:o2[WINDOW_NUMBER]];
|
||||
};
|
||||
|
||||
static CGImageRef avf_window_get_image(int window_id)
|
||||
{
|
||||
NSArray *arr = (NSArray *)CGWindowListCreate(
|
||||
kCGWindowListOptionIncludingWindow, window_id);
|
||||
[arr autorelease];
|
||||
|
||||
if (!arr.count)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return CGWindowListCreateImage(CGRectNull,
|
||||
kCGWindowListOptionIncludingWindow,
|
||||
window_id, kCGWindowImageDefault);
|
||||
}
|
||||
|
||||
NSArray * avf_window_enumerate()
|
||||
{
|
||||
NSArray *arr = (NSArray *)CGWindowListCopyWindowInfo(
|
||||
kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
|
||||
|
||||
[arr autorelease];
|
||||
|
||||
return [arr sortedArrayUsingComparator:win_info_cmp];
|
||||
}
|
||||
|
||||
void avf_window_list()
|
||||
{
|
||||
NSArray *arr = avf_window_enumerate();
|
||||
|
||||
printf("\r\nAvailable window name : \r\n\r\n");
|
||||
|
||||
for (NSDictionary *dict in arr)
|
||||
{
|
||||
NSNumber *layer = (NSNumber *)dict[WINDOW_LAYER];
|
||||
if (0 != layer.intValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString *name = (NSString *)dict[WINDOW_NAME];
|
||||
|
||||
printf("%s\r\n", name.UTF8String);
|
||||
}
|
||||
}
|
||||
|
||||
void * avf_window_init(char * title, int width, int height, double framerate)
|
||||
{
|
||||
AVFWindowContext * context = (AVFWindowContext *)malloc(sizeof(AVFWindowContext));
|
||||
if (NULL == context)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(context, 0, sizeof(AVFWindowContext));
|
||||
|
||||
strncpy(context->window_title, title, sizeof(context->window_title));
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->framerate = framerate;
|
||||
|
||||
NSArray *arr = avf_window_enumerate();
|
||||
|
||||
for (NSDictionary *dict in arr)
|
||||
{
|
||||
NSNumber *layer = (NSNumber *)dict[WINDOW_LAYER];
|
||||
if (0 != layer.intValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString *name = (NSString *)dict[WINDOW_NAME];
|
||||
NSNumber *wid = (NSNumber *)dict[WINDOW_NUMBER];
|
||||
|
||||
if (strncasecmp(name.UTF8String, title, strlen(title)) == 0)
|
||||
{
|
||||
context->window_id = wid.intValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == context->window_id)
|
||||
{
|
||||
free(context);
|
||||
log_print(HT_LOG_ERR, "%s, not found window, %s\r\n", __FUNCTION__, title);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CGImageRef img = avf_window_get_image(context->window_id);
|
||||
if (!img)
|
||||
{
|
||||
free(context);
|
||||
log_print(HT_LOG_ERR, "%s, avf_window_get_image failed\r\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t img_width = CGImageGetWidth(img);
|
||||
size_t img_height = CGImageGetHeight(img);
|
||||
|
||||
if (!img_width || !img_height || CGImageGetBitsPerPixel(img) != 32 ||
|
||||
CGImageGetBitsPerComponent(img) != 8)
|
||||
{
|
||||
CGImageRelease(img);
|
||||
free(context);
|
||||
log_print(HT_LOG_ERR, "%s, invalid image format!\r\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CGImageRelease(img);
|
||||
|
||||
context->width = img_width;
|
||||
context->height = img_height;
|
||||
context->pixel_format = AV_PIX_FMT_BGR0;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void avf_window_uninit(void * ctx)
|
||||
{
|
||||
if (NULL == ctx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AVFWindowContext * context = (AVFWindowContext *)ctx;
|
||||
|
||||
free(context);
|
||||
}
|
||||
|
||||
void avf_window_set_callback(void * ctx, avf_window_callback cb, void * userdata)
|
||||
{
|
||||
if (NULL == ctx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AVFWindowContext * context = (AVFWindowContext *)ctx;
|
||||
|
||||
context->callback = cb;
|
||||
context->userdata = userdata;
|
||||
}
|
||||
|
||||
int avf_window_get_width(void * ctx)
|
||||
{
|
||||
if (NULL == ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVFWindowContext * context = (AVFWindowContext *)ctx;
|
||||
|
||||
return context->width;
|
||||
}
|
||||
|
||||
int avf_window_get_height(void * ctx)
|
||||
{
|
||||
if (NULL == ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AVFWindowContext * context = (AVFWindowContext *)ctx;
|
||||
|
||||
return context->height;
|
||||
}
|
||||
|
||||
int avf_window_get_pixfmt(void * ctx)
|
||||
{
|
||||
if (NULL == ctx)
|
||||
{
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
AVFWindowContext * context = (AVFWindowContext *)ctx;
|
||||
|
||||
return context->pixel_format;
|
||||
}
|
||||
|
||||
BOOL avf_window_capture(AVFWindowContext * context)
|
||||
{
|
||||
CGImageRef img = avf_window_get_image(context->window_id);
|
||||
if (!img)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
size_t width = CGImageGetWidth(img);
|
||||
size_t height = CGImageGetHeight(img);
|
||||
|
||||
if (!width || !height || CGImageGetBitsPerPixel(img) != 32 ||
|
||||
CGImageGetBitsPerComponent(img) != 8)
|
||||
{
|
||||
CGImageRelease(img);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CGDataProviderRef provider = CGImageGetDataProvider(img);
|
||||
CFDataRef data = CGDataProviderCopyData(provider);
|
||||
|
||||
avf_window_data frame;
|
||||
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
|
||||
frame.width = width;
|
||||
frame.height = height;
|
||||
frame.format = context->pixel_format;
|
||||
frame.data[0] = (uint8 *)CFDataGetBytePtr(data);
|
||||
frame.linesize[0] = CGImageGetBytesPerRow(img);
|
||||
|
||||
if (context->callback)
|
||||
{
|
||||
context->callback(&frame, context->userdata);
|
||||
}
|
||||
|
||||
CGImageRelease(img);
|
||||
CFRelease(data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL avf_window_read(void * ctx)
|
||||
{
|
||||
if (NULL == ctx)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL ret = FALSE;
|
||||
AVFWindowContext * context = (AVFWindowContext *)ctx;
|
||||
|
||||
@autoreleasepool {
|
||||
ret = avf_window_capture(context);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user