Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JingleCallStack] Use WebRTC framework included in Jitsi Meet SDK #654

Merged
merged 7 commits into from
Apr 24, 2019
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changes in Matrix iOS SDK in 0.12.5 (2019-03-)
Improvements:
* Crypto: Handle partially-shared sessions better (vector-im/riot-ios/issues/2320).
* MXSession: add a global notification posted when the account data are updated from the homeserver.
* VoIP: Use WebRTC framework included in Jitsi Meet SDK (vector-im/riot-ios/issues/1483).

Bug Fix:
* MXRoomSummaryUpdater: Fix `MXRoomSummary.hiddenFromUser` property not being saved when associated room become tombstoned (vector-im/riot-ios/issues/2148).
Expand Down
7 changes: 4 additions & 3 deletions MatrixSDK.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Pod::Spec.new do |s|
end

s.subspec 'JingleCallStack' do |ss|
ss.ios.deployment_target = "9.0"
ss.ios.deployment_target = "10.0"

ss.source_files = "MatrixSDKExtensions/VoIP/Jingle/**/*.{h,m}"

Expand All @@ -52,8 +52,9 @@ Pod::Spec.new do |s|
# prevents us from submitting the MatrixSDK pod
#ss.ios.dependency 'GoogleWebRTC', '~>1.1.21820'

# Use WebRTC build at https://github.com/Anakros/WebRTC instead
ss.ios.dependency 'WebRTC', '63.11.20455'
# Use WebRTC framework included in Jitsi Meet SDK
ss.ios.dependency 'JitsiMeetSDK', '~> 2.1.0'

end

s.subspec 'SwiftSupport' do |ss|
Expand Down
32 changes: 10 additions & 22 deletions MatrixSDK/VoIP/CallKit/MXCallKitAdapter.m
Original file line number Diff line number Diff line change
Expand Up @@ -224,57 +224,45 @@ - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSessio
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action
{
MXCall *call = self.calls[action.callUUID];
if (!call)
if (call)
{
[action fail];
return;
[self.audioSessionConfigurator configureAudioSessionForVideoCall:call.isVideoCall];
}

[self.audioSessionConfigurator configureAudioSessionForVideoCall:call.isVideoCall];

[action fulfill];
}

- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action
{
MXCall *call = self.calls[action.callUUID];
if (!call)
if (call)
{
[action fail];
return;
[call answer];
}

[call answer];

[action fulfill];
}

- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action
{
MXCall *call = self.calls[action.callUUID];
if (!call)
if (call)
{
[action fail];
return;
[call hangup];
[self.calls removeObjectForKey:action.UUID];
}

[call hangup];
[self.calls removeObjectForKey:action.UUID];

[action fulfill];
}

- (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action
{
MXCall *call = self.calls[action.callUUID];
if (!call)
if (call)
{
[action fail];
return;
[call setAudioMuted:action.isMuted];
}

[call setAudioMuted:action.isMuted];


[action fulfill];
}

Expand Down
1 change: 1 addition & 0 deletions MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

@see https://developers.google.com/talk/libjingle/developer_guide
*/
NS_EXTENSION_UNAVAILABLE_IOS("Camera capture and video rendering are not available in app extensions.")
@interface MXJingleCallStack : NSObject <MXCallStack>

@end
1 change: 1 addition & 0 deletions MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN

@see https://developers.google.com/talk/libjingle/developer_guide
*/
NS_EXTENSION_UNAVAILABLE_IOS("Camera capture and video rendering are not available in app extensions.")
@interface MXJingleCallStackCall : NSObject <MXCallStackCall>

- (instancetype)initWithFactory:(RTCPeerConnectionFactory *)factory NS_DESIGNATED_INITIALIZER;
Expand Down
109 changes: 68 additions & 41 deletions MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#import "MXTools.h"

#import "MXJingleVideoView.h"
#import "MXJingleCameraCaptureController.h"
#import <WebRTC/WebRTC.h>

@interface MXJingleCallStackCall () <RTCPeerConnectionDelegate>
Expand Down Expand Up @@ -60,6 +61,9 @@ @interface MXJingleCallStackCall () <RTCPeerConnectionDelegate>
void (^onStartCapturingMediaWithVideoSuccess)(void);
}

@property (nonatomic, strong) RTCVideoCapturer *videoCapturer;
@property (nonatomic, strong) MXJingleCameraCaptureController *captureController;

@end

@implementation MXJingleCallStackCall
Expand Down Expand Up @@ -99,6 +103,10 @@ - (void)startCapturingMediaWithVideo:(BOOL)video success:(void (^)(void))success

- (void)end
{
self.videoCapturer = nil;
[self.captureController stopCapture];
self.captureController = nil;

[peerConnection close];
peerConnection = nil;

Expand Down Expand Up @@ -463,21 +471,17 @@ - (void)setAudioToSpeaker:(BOOL)theAudioToSpeaker
- (void)setCameraPosition:(AVCaptureDevicePosition)theCameraPosition
{
cameraPosition = theCameraPosition;

if (localVideoTrack)
{
RTCVideoSource* source = localVideoTrack.source;
if ([source isKindOfClass:[RTCAVFoundationVideoSource class]])
{
RTCAVFoundationVideoSource* avSource = (RTCAVFoundationVideoSource*)source;
avSource.useBackCamera = (cameraPosition == AVCaptureDevicePositionBack) ? YES : NO;

[self fixMirrorOnSelfVideoView];
}
[self fixMirrorOnSelfVideoView];
}

self.captureController.cameraPosition = theCameraPosition;
}



#pragma mark - Private methods
- (RTCMediaConstraints *)mediaConstraints
{
Expand All @@ -490,55 +494,78 @@ - (RTCMediaConstraints *)mediaConstraints

- (void)createLocalMediaStream
{
RTCMediaStream *localStream = [peerConnectionFactory mediaStreamWithStreamId:@"ARDAMS"];

// Set up audio
localAudioTrack = [peerConnectionFactory audioTrackWithTrackId:@"ARDAMSa0"];
[localStream addAudioTrack:localAudioTrack];

localAudioTrack = [self createLocalAudioTrack];

[peerConnection addTrack:localAudioTrack streamIds:@[@"ARDAMS"]];

// And video
if (isVideoCall)
{
// Find the device that corresponds to self.cameraPosition
AVCaptureDevice *device;
for (AVCaptureDevice *captureDevice in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo])
{
if (captureDevice.position == cameraPosition)
{
device = captureDevice;
break;
}
}

localVideoTrack = [self createLocalVideoTrack];
// Create a video track and add it to the media stream
if (device)
if (localVideoTrack)
{
// Use RTCAVFoundationVideoSource to be able to switch the camera
RTCAVFoundationVideoSource *localVideoSource = [peerConnectionFactory avFoundationVideoSourceWithConstraints:nil];

localVideoTrack = [peerConnectionFactory videoTrackWithSource:localVideoSource trackId:@"ARDAMSv0"];
[localStream addVideoTrack:localVideoTrack];

[peerConnection addTrack:localVideoTrack streamIds:@[@"ARDAMS"]];

// Display the self view
// Use selfVideoView as a container of a RTCEAGLVideoView
MXJingleVideoView *renderView = [[MXJingleVideoView alloc] initWithContainerView:self.selfVideoView];
[localVideoTrack addRenderer:renderView];

[self fixMirrorOnSelfVideoView];
[self startVideoCaptureWithRenderer:renderView];
}
}

// Set the audio route
self.audioToSpeaker = audioToSpeaker;

if (onStartCapturingMediaWithVideoSuccess)
{
onStartCapturingMediaWithVideoSuccess();
onStartCapturingMediaWithVideoSuccess = nil;
}
}

// Wire the streams to the call
[peerConnection addStream:localStream];
- (RTCAudioTrack*)createLocalAudioTrack
{
RTCMediaConstraints *mediaConstraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil optionalConstraints:nil];
RTCAudioSource *localAudioSource = [peerConnectionFactory audioSourceWithConstraints:mediaConstraints];
return [peerConnectionFactory audioTrackWithSource:localAudioSource trackId:@"ARDAMSa0"];
}

if (onStartCapturingMediaWithVideoSuccess)
- (RTCVideoTrack*)createLocalVideoTrack
{
RTCVideoSource *localVideoSource = [peerConnectionFactory videoSource];

self.videoCapturer = [self createVideoCapturerWithVideoSource:localVideoSource];

return [peerConnectionFactory videoTrackWithSource:localVideoSource trackId:@"ARDAMSv0"];
}

- (RTCVideoCapturer*)createVideoCapturerWithVideoSource:(RTCVideoSource*)videoSource
{
RTCVideoCapturer *videoCapturer;

#if !TARGET_OS_SIMULATOR
videoCapturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:videoSource];
#endif

return videoCapturer;
}

- (void)startVideoCaptureWithRenderer:(id<RTCVideoRenderer>)videoRenderer
{
if (!self.videoCapturer || ![self.videoCapturer isKindOfClass:[RTCCameraVideoCapturer class]])
{
onStartCapturingMediaWithVideoSuccess();
onStartCapturingMediaWithVideoSuccess = nil;
return;
}

RTCCameraVideoCapturer *cameraVideoCapturer = (RTCCameraVideoCapturer*)self.videoCapturer;

self.captureController = [[MXJingleCameraCaptureController alloc] initWithCapturer:cameraVideoCapturer];

[localVideoTrack addRenderer:videoRenderer];

[self.captureController startCapture];
}

- (void)checkStartGetCaptureSourcesForVideo
Expand Down
54 changes: 54 additions & 0 deletions MatrixSDKExtensions/VoIP/Jingle/MXJingleCameraCaptureController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Copyright 2019 New Vector Ltd

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 <WebRTC/RTCCameraVideoCapturer.h>

/**
`MXJingleCameraCaptureController` controls the WebRTC camera capture.

Based on `ARDCaptureController` from iOS WebRTC sample app.
@see https://github.com/WebKit/webkit/blob/master/Source/ThirdParty/libwebrtc/Source/webrtc/examples/objc/AppRTCMobile/ARDCaptureController.h
*/
NS_EXTENSION_UNAVAILABLE_IOS("Camera not available in app extensions.")
@interface MXJingleCameraCaptureController : NSObject

/**
Change camera position.
*/
@property (nonatomic) AVCaptureDevicePosition cameraPosition;


/**
Initialize with a camera video capturer.

@param capturer WebRTC camera video capturer.
@return A `MXJingleCameraCaptureController` instance.
*/
- (instancetype)initWithCapturer:(RTCCameraVideoCapturer *)capturer;

- (instancetype)init NS_UNAVAILABLE;

/**
Start camera capture.
*/
- (void)startCapture;

/**
Stop camera capture.
*/
- (void)stopCapture;

@end
Loading