// // DesktopCaptureSourceView.m // TgVoipWebrtc // // Created by Mikhail Filimonov on 28.12.2020. // Copyright © 2020 Mikhail Filimonov. All rights reserved. // #import #import "DesktopCaptureSourceViewMac.h" #import "platform/darwin/VideoMetalViewMac.h" #import "tgcalls/desktop_capturer/DesktopCaptureSource.h" #import "tgcalls/desktop_capturer/DesktopCaptureSourceHelper.h" #import "tgcalls/desktop_capturer/DesktopCaptureSourceManager.h" #import "platform/darwin/VideoMetalViewMac.h" @interface DesktopCaptureSourceViewMetal : VideoMetalView @end @implementation DesktopCaptureSourceViewMetal -(id)initWithHelper:(tgcalls::DesktopCaptureSourceHelper)helper { if (self = [super initWithFrame:CGRectZero]) { std::shared_ptr> sink = [self getSink]; helper.setOutput(sink); [self setVideoContentMode:kCAGravityResizeAspectFill]; } return self; } @end @implementation DesktopCaptureSourceDataMac -(id)initWithSize:(CGSize)size fps:(double)fps captureMouse:(bool)captureMouse { if (self = [super init]) { self.aspectSize = size; self.fps = fps; self.captureMouse = captureMouse; } return self; } -(NSString *)cachedKey { return [[NSString alloc] initWithFormat:@"%@:%f:%d", NSStringFromSize(self.aspectSize), self.fps, self.captureMouse]; } @end @interface DesktopCaptureSourceMac () { absl::optional _source; BOOL _isWindow; } -(id)initWithSource:(tgcalls::DesktopCaptureSource)source isWindow:(BOOL)isWindow; -(tgcalls::DesktopCaptureSource)getSource; @end @implementation DesktopCaptureSourceMac -(tgcalls::DesktopCaptureSource)getSource { return _source.value(); } -(NSString *)title { if (_isWindow) { const tgcalls::DesktopCaptureSource source = _source.value(); return [[NSString alloc] initWithCString:source.title().c_str() encoding:NSUTF8StringEncoding]; } else return [[NSString alloc] initWithFormat:@"Screen"]; } -(long)uniqueId { return _source.value().uniqueId(); } -(BOOL)isWindow { return _isWindow; } -(NSString *)uniqueKey { return [[NSString alloc] initWithFormat:@"%ld:%@", self.uniqueId, _isWindow ? @"Window" : @"Screen"]; } -(NSString *)deviceIdKey { return [[NSString alloc] initWithFormat:@"desktop_capturer_%@_%ld", _isWindow ? @"window" : @"screen", self.uniqueId]; } -(BOOL)isEqual:(id)object { return [[((DesktopCaptureSourceMac *)object) uniqueKey] isEqualToString:[self uniqueKey]]; } - (BOOL)isEqualTo:(id)object { return [[((DesktopCaptureSourceMac *)object) uniqueKey] isEqualToString:[self uniqueKey]]; } -(id)initWithSource:(tgcalls::DesktopCaptureSource)source isWindow:(BOOL)isWindow { if (self = [super init]) { _source = source; _isWindow = isWindow; } return self; } @end @interface DesktopCaptureSourceScopeMac () -(tgcalls::DesktopCaptureSourceData)getData; -(tgcalls::DesktopCaptureSource)getSource; @end @implementation DesktopCaptureSourceScopeMac -(id)initWithSource:(DesktopCaptureSourceMac *)source data:(DesktopCaptureSourceDataMac *)data { if (self = [super init]) { _data = data; _source = source; } return self; } -(NSString *)cachedKey { return [[NSString alloc] initWithFormat:@"%@:%@", _source.uniqueKey, _data.cachedKey]; } -(tgcalls::DesktopCaptureSourceData)getData { tgcalls::DesktopCaptureSourceData data{ /*.aspectSize = */{ (int)_data.aspectSize.width, (int)_data.aspectSize.height}, /*.fps = */_data.fps, /*.captureMouse = */_data.captureMouse, }; return data; } -(tgcalls::DesktopCaptureSource)getSource { return [_source getSource]; } @end @implementation DesktopCaptureSourceManagerMac { std::map _cached; std::unique_ptr _manager; BOOL _isWindow; } -(instancetype)init_s { if (self = [super init]) { _manager = std::make_unique(tgcalls::DesktopCaptureType::Screen); _isWindow = NO; } return self; } -(instancetype)init_w { if (self = [super init]) { _manager = std::make_unique(tgcalls::DesktopCaptureType::Window); _isWindow = YES; } return self; } -(NSArray *)list { std::vector sources = _manager->sources(); NSMutableArray *macSources = [[NSMutableArray alloc] init]; for (auto i = sources.begin(); i != sources.end(); ++i) { [macSources addObject:[[DesktopCaptureSourceMac alloc] initWithSource:*i isWindow:_isWindow]]; } return macSources; } -(NSView *)createForScope:(DesktopCaptureSourceScopeMac*)scope { auto i = _cached.find(std::string([scope.cachedKey UTF8String])); if (i == _cached.end()) { i = _cached.emplace( std::string([scope.cachedKey UTF8String]), tgcalls::DesktopCaptureSourceHelper([scope getSource], [scope getData])).first; } DesktopCaptureSourceViewMetal *view = [[DesktopCaptureSourceViewMetal alloc] initWithHelper:i->second]; if (scope.data.captureMouse) { [view setVideoContentMode:kCAGravityResizeAspect]; } return view; } -(void)start:(DesktopCaptureSourceScopeMac *)scope { const auto i = _cached.find(std::string([scope.cachedKey UTF8String])); if (i != _cached.end()) { i->second.start(); } } -(void)stop:(DesktopCaptureSourceScopeMac *)scope { const auto i = _cached.find(std::string([scope.cachedKey UTF8String])); if (i != _cached.end()) { i->second.stop(); } } -(void)dealloc { for (auto &[key, helper] : _cached) { helper.stop(); } _manager.reset(); } @end