Skip to content

Commit

Permalink
Desktop Capture for macOS.
Browse files Browse the repository at this point in the history
[Mac] feat: Support screen capture for macOS. (#24) (#36)
fix: Get thumbnails asynchronously. (#37)
fix: Use CVPixelBuffer to build DesktopCapture Frame, fix the crash caused by non-CVPixelBuffer frame in RTCVideoEncoderH264 that cannot be cropped. (#63)
Fix the crash when setting the fps of the virtual camera. (#62)
  • Loading branch information
cloudwebrtc committed May 22, 2024
1 parent e71c396 commit 5c6d270
Show file tree
Hide file tree
Showing 17 changed files with 1,233 additions and 12 deletions.
11 changes: 1 addition & 10 deletions modules/desktop_capture/mac/screen_capturer_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,7 @@ DesktopRect GetExcludedWindowPixelBounds(CGWindowID window, float dip_to_pixel_s
ScreenConfigurationChanged();
}

// When screen is zoomed in/out, OSX only updates the part of Rects currently
// displayed on screen, with relative location to current top-left on screen.
// This will cause problems when we copy the dirty regions to the captured
// image. So we invalidate the whole screen to copy all the screen contents.
// With CGI method, the zooming will be ignored and the whole screen contents
// will be captured as before.
// With IOSurface method, the zoomed screen contents will be captured.
if (UAZoomEnabled()) {
helper_.InvalidateScreen(screen_pixel_bounds_.size());
}
helper_.InvalidateScreen(screen_pixel_bounds_.size());

DesktopRegion region;
helper_.TakeInvalidRegion(&region);
Expand Down
41 changes: 41 additions & 0 deletions sdk/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,43 @@ if (is_ios || is_mac) {
"../rtc_base/system:gcd_helpers",
]
}

rtc_library("desktopcapture_objc") {
visibility = [ "*" ]
sources = [
"objc/components/capturer/RTCDesktopCapturer+Private.h",
"objc/components/capturer/RTCDesktopCapturer.h",
"objc/components/capturer/RTCDesktopCapturer.mm",
"objc/components/capturer/RTCDesktopSource+Private.h",
"objc/components/capturer/RTCDesktopSource.h",
"objc/components/capturer/RTCDesktopSource.mm",
"objc/components/capturer/RTCDesktopMediaList+Private.h",
"objc/components/capturer/RTCDesktopMediaList.h",
"objc/components/capturer/RTCDesktopMediaList.mm",
"objc/native/src/objc_desktop_capture.h",
"objc/native/src/objc_desktop_capture.mm",
"objc/native/src/objc_desktop_media_list.h",
"objc/native/src/objc_desktop_media_list.mm",
]
frameworks = [
"AppKit.framework",
]

configs += [ "..:common_objc" ]

public_configs = [ ":common_config_objc" ]

deps = [
":base_objc",
":helpers_objc",
":videoframebuffer_objc",
"../rtc_base/system:gcd_helpers",
"../modules/desktop_capture",
]
if(is_mac) {
deps += [ "//third_party:jpeg", ]
}
}

rtc_library("videocodec_objc") {
visibility = [ "*" ]
Expand Down Expand Up @@ -1534,6 +1571,9 @@ if (is_ios || is_mac) {
"objc/base/RTCYUVPlanarBuffer.h",
"objc/components/capturer/RTCCameraVideoCapturer.h",
"objc/components/capturer/RTCFileVideoCapturer.h",
"objc/components/capturer/RTCDesktopCapturer.h",
"objc/components/capturer/RTCDesktopSource.h",
"objc/components/capturer/RTCDesktopMediaList.h",
"objc/components/renderer/metal/RTCMTLVideoView.h",
"objc/components/renderer/metal/RTCMTLNSVideoView.h",
"objc/components/renderer/opengl/RTCVideoViewShading.h",
Expand Down Expand Up @@ -1566,6 +1606,7 @@ if (is_ios || is_mac) {
":native_video",
":peerconnectionfactory_base_objc",
":videocapture_objc",
":desktopcapture_objc",
":videocodec_objc",
":videotoolbox_objc",
":darwin_privacy_info",
Expand Down
4 changes: 3 additions & 1 deletion sdk/objc/components/capturer/RTCCameraVideoCapturer.m
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,9 @@ - (void)updateDeviceCaptureFormat:(AVCaptureDeviceFormat *)format fps:(NSInteger
@"updateDeviceCaptureFormat must be called on the capture queue.");
@try {
_currentDevice.activeFormat = format;
_currentDevice.activeVideoMinFrameDuration = CMTimeMake(1, fps);
if(![NSStringFromClass([_currentDevice class]) isEqualToString:@"AVCaptureDALDevice"]) {
_currentDevice.activeVideoMinFrameDuration = CMTimeMake(1, fps);
}
} @catch (NSException *exception) {
RTCLogError(@"Failed to set active format!\n User info:%@", exception.userInfo);
return;
Expand Down
49 changes: 49 additions & 0 deletions sdk/objc/components/capturer/RTCDesktopCapturer+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2022 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/

#import "RTCDesktopCapturer.h"

#include "sdk/objc/native/src/objc_desktop_capture.h"

NS_ASSUME_NONNULL_BEGIN

RTC_OBJC_EXPORT
@protocol RTC_OBJC_TYPE
(DesktopCapturerDelegate)<NSObject>
-(void)didCaptureVideoFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *) frame;
-(void)didSourceCaptureStart;
-(void)didSourceCapturePaused;
-(void)didSourceCaptureStop;
-(void)didSourceCaptureError;
@end

@interface RTCDesktopCapturer ()

@property(nonatomic, readonly)std::shared_ptr<webrtc::ObjCDesktopCapturer> nativeCapturer;

- (void)didCaptureVideoFrame:(RTC_OBJC_TYPE(RTCVideoFrame) *)frame;

-(void)didSourceCaptureStart;

-(void)didSourceCapturePaused;

-(void)didSourceCaptureStop;

-(void)didSourceCaptureError;

@end

NS_ASSUME_NONNULL_END
61 changes: 61 additions & 0 deletions sdk/objc/components/capturer/RTCDesktopCapturer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2022 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/

#import <AVFoundation/AVFoundation.h>
#import <Foundation/Foundation.h>

#import "RTCMacros.h"
#import "RTCVideoCapturer.h"
#import "RTCDesktopSource.h"

NS_ASSUME_NONNULL_BEGIN

@class RTCDesktopCapturer;

RTC_OBJC_EXPORT
@protocol RTC_OBJC_TYPE
(RTCDesktopCapturerDelegate)<NSObject>
-(void)didSourceCaptureStart:(RTCDesktopCapturer *) capturer;

-(void)didSourceCapturePaused:(RTCDesktopCapturer *) capturer;

-(void)didSourceCaptureStop:(RTCDesktopCapturer *) capturer;

-(void)didSourceCaptureError:(RTCDesktopCapturer *) capturer;
@end

RTC_OBJC_EXPORT
// Screen capture that implements RTCVideoCapturer. Delivers frames to a
// RTCVideoCapturerDelegate (usually RTCVideoSource).
@interface RTC_OBJC_TYPE (RTCDesktopCapturer) : RTC_OBJC_TYPE(RTCVideoCapturer)

@property(nonatomic, readonly) RTCDesktopSource *source;

- (instancetype)initWithSource:(RTCDesktopSource*)source delegate:(__weak id<RTC_OBJC_TYPE(RTCDesktopCapturerDelegate)>)delegate captureDelegate:(__weak id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)captureDelegate;

- (instancetype)initWithDefaultScreen:(__weak id<RTC_OBJC_TYPE(RTCDesktopCapturerDelegate)>)delegate captureDelegate:(__weak id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)captureDelegate;

- (void)startCapture;

- (void)startCaptureWithFPS:(NSInteger)fps;

- (void)stopCapture;

- (void)stopCaptureWithCompletionHandler:(nullable void (^)(void))completionHandler;

@end

NS_ASSUME_NONNULL_END
104 changes: 104 additions & 0 deletions sdk/objc/components/capturer/RTCDesktopCapturer.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2022 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/

#import <Foundation/Foundation.h>

#import "base/RTCLogging.h"
#import "base/RTCVideoFrameBuffer.h"

#import "components/video_frame_buffer/RTCCVPixelBuffer.h"

#import "RTCDesktopCapturer.h"
#import "RTCDesktopCapturer+Private.h"
#import "RTCDesktopSource+Private.h"

@implementation RTC_OBJC_TYPE (RTCDesktopCapturer) {
__weak id<RTC_OBJC_TYPE(RTCDesktopCapturerDelegate)> _delegate;
}

@synthesize nativeCapturer = _nativeCapturer;
@synthesize source = _source;

- (instancetype)initWithSource:(RTCDesktopSource*)source delegate:(__weak id<RTC_OBJC_TYPE(RTCDesktopCapturerDelegate)>)delegate captureDelegate:(__weak id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)captureDelegate {
if (self = [super initWithDelegate:captureDelegate]) {
webrtc::DesktopType captureType = webrtc::kScreen;
if(source.sourceType == RTCDesktopSourceTypeWindow) {
captureType = webrtc::kWindow;
}
_nativeCapturer = std::make_shared<webrtc::ObjCDesktopCapturer>(captureType, source.nativeMediaSource->id(), self);
_source = source;
_delegate = delegate;
}
return self;
}

- (instancetype)initWithDefaultScreen:(__weak id<RTC_OBJC_TYPE(RTCDesktopCapturerDelegate)>)delegate captureDelegate:(__weak id<RTC_OBJC_TYPE(RTCVideoCapturerDelegate)>)captureDelegate {
if (self = [super initWithDelegate:captureDelegate]) {
_nativeCapturer = std::make_unique<webrtc::ObjCDesktopCapturer>(webrtc::kScreen, -1, self);
_source = nil;
_delegate = delegate;
}
return self;
}


-(void)dealloc {
_nativeCapturer->Stop();
_nativeCapturer = nullptr;
}

- (void)startCapture {
[self didSourceCaptureStart];
_nativeCapturer->Start(30);
}

- (void)startCaptureWithFPS:(NSInteger)fps {
_nativeCapturer->Start(fps);
}

- (void)didCaptureVideoFrame
: (RTC_OBJC_TYPE(RTCVideoFrame) *)frame {
[self.delegate capturer:self didCaptureVideoFrame:frame];
}

- (void)stopCapture {
_nativeCapturer->Stop();
}

- (void)stopCaptureWithCompletionHandler:(nullable void (^)(void))completionHandler {
[self stopCapture];
if(completionHandler != nil) {
completionHandler();
}
}

-(void)didSourceCaptureStart {
[_delegate didSourceCaptureStart:self];
}

-(void)didSourceCapturePaused {
[_delegate didSourceCapturePaused:self];
}

-(void)didSourceCaptureStop {
[_delegate didSourceCaptureStop:self];
}

-(void)didSourceCaptureError {
[_delegate didSourceCaptureError:self];
}

@end
40 changes: 40 additions & 0 deletions sdk/objc/components/capturer/RTCDesktopMediaList+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2022 LiveKit
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/

#import "RTCDesktopMediaList.h"

namespace webrtc {
class ObjCDesktopMediaList;
class MediaSource;
}

NS_ASSUME_NONNULL_BEGIN

@interface RTCDesktopMediaList ()

@property(nonatomic, readonly)std::shared_ptr<webrtc::ObjCDesktopMediaList> nativeMediaList;

-(void)mediaSourceAdded:(webrtc::MediaSource *) source;

-(void)mediaSourceRemoved:(webrtc::MediaSource *) source;

-(void)mediaSourceNameChanged:(webrtc::MediaSource *) source;

-(void)mediaSourceThumbnailChanged:(webrtc::MediaSource *) source;

@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 5c6d270

Please sign in to comment.