diff --git a/submodules/TgVoipWebrtcCustom/BUILD b/submodules/TgVoipWebrtcCustom/BUILD index 623ed59927..44ec63828d 100644 --- a/submodules/TgVoipWebrtcCustom/BUILD +++ b/submodules/TgVoipWebrtcCustom/BUILD @@ -20,6 +20,7 @@ objc_library( "-DWEBRTC_IOS", "-DWEBRTC_MAC", "-DWEBRTC_POSIX", + "-std=c++14", ], includes = [ "PublicHeaders", diff --git a/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm index 4336df0ff1..77f7cccc6d 100644 --- a/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm @@ -25,6 +25,8 @@ #import "components/renderer/metal/RTCMTLVideoView.h" #import "components/renderer/opengl/RTCEAGLVideoView.h" +#import "RtcConnection.h" + static void (*InternalVoipLoggingFunction)(NSString *) = NULL; static void voipLog(NSString* format, ...) { @@ -38,82 +40,6 @@ static void voipLog(NSString* format, ...) { } } -@interface NativePeerConnectionDelegate : NSObject { - id _queue; - void (^_didGenerateIceCandidate)(RTCIceCandidate *); - void (^_didChangeIceState)(OngoingCallStateWebrtcCustom); -} - -@end - -@implementation NativePeerConnectionDelegate - -- (instancetype)initWithQueue:(id)queue didGenerateIceCandidate:(void (^)(RTCIceCandidate *))didGenerateIceCandidate didChangeIceState:(void (^)(OngoingCallStateWebrtcCustom))didChangeIceState { - self = [super init]; - if (self != nil) { - _queue = queue; - _didGenerateIceCandidate = [didGenerateIceCandidate copy]; - _didChangeIceState = [didChangeIceState copy]; - } - return self; -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeSignalingState:(RTCSignalingState)stateChanged { - switch (stateChanged) { - case RTCSignalingStateStable: - _didChangeIceState(OngoingCallStateConnected); - break; - case RTCSignalingStateHaveLocalOffer: - _didChangeIceState(OngoingCallStateInitializing); - break; - case RTCSignalingStateHaveLocalPrAnswer: - _didChangeIceState(OngoingCallStateInitializing); - break; - case RTCSignalingStateHaveRemoteOffer: - _didChangeIceState(OngoingCallStateInitializing); - break; - case RTCSignalingStateHaveRemotePrAnswer: - _didChangeIceState(OngoingCallStateInitializing); - break; - default: - break; - } - voipLog(@"didChangeSignalingState: %d", stateChanged); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection didAddStream:(RTCMediaStream *)stream { - voipLog(@"Added stream: %@", stream.streamId); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection didRemoveStream:(RTCMediaStream *)stream { -} - -- (void)peerConnectionShouldNegotiate:(RTCPeerConnection *)peerConnection { -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeIceConnectionState:(RTCIceConnectionState)newState { - voipLog(@"IceConnectionState: %d", newState); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection didChangeIceGatheringState:(RTCIceGatheringState)newState { - voipLog(@"didChangeIceGatheringState: %d", newState); -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection didGenerateIceCandidate:(RTCIceCandidate *)candidate { - [_queue dispatch:^{ - _didGenerateIceCandidate(candidate); - }]; -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection didRemoveIceCandidates:(NSArray *)candidates { -} - -- (void)peerConnection:(RTCPeerConnection *)peerConnection - didOpenDataChannel:(RTCDataChannel *)dataChannel { -} - -@end - @implementation OngoingCallConnectionDescriptionWebrtcCustom - (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag { @@ -136,8 +62,6 @@ static void voipLog(NSString* format, ...) { bool _isOutgoing; void (^_sendSignalingData)(NSData * _Nonnull); - - NativePeerConnectionDelegate *_peerConnectionDelegate; OngoingCallNetworkTypeWebrtcCustom _networkType; NSTimeInterval _callReceiveTimeout; @@ -148,13 +72,11 @@ static void voipLog(NSString* format, ...) { OngoingCallStateWebrtcCustom _state; int32_t _signalBars; - RTCPeerConnectionFactory *_peerConnectionFactory; + RtcConnection *_connection; - RTCPeerConnection *_peerConnection; - - RTCVideoCapturer *_videoCapturer; - RTCVideoTrack *_localVideoTrack; - RTCVideoTrack *_remoteVideoTrack; + //RTCVideoCapturer *_videoCapturer; + //RTCVideoTrack *_localVideoTrack; + //RTCVideoTrack *_remoteVideoTrack; bool _receivedRemoteDescription; @@ -222,54 +144,31 @@ static void voipLog(NSString* format, ...) { [RTCAudioSession sharedInstance].useManualAudio = true; [RTCAudioSession sharedInstance].isAudioEnabled = true; - RTCDefaultVideoDecoderFactory *decoderFactory = [[RTCDefaultVideoDecoderFactory alloc] init]; - RTCDefaultVideoEncoderFactory *encoderFactory = [[RTCDefaultVideoEncoderFactory alloc] init]; - - _peerConnectionFactory = [[RTCPeerConnectionFactory alloc] initWithEncoderFactory:encoderFactory decoderFactory:decoderFactory]; - - NSArray *iceServers = @[ - @"stun:stun.l.google.com:19302" - ]; - - RTCConfiguration *config = [[RTCConfiguration alloc] init]; - config.iceServers = @[ - [[RTCIceServer alloc] initWithURLStrings:iceServers] - ]; - config.sdpSemantics = RTCSdpSemanticsUnifiedPlan; - config.continualGatheringPolicy = RTCContinualGatheringPolicyGatherContinually; - - RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil optionalConstraints:@{ @"DtlsSrtpKeyAgreement": kRTCMediaConstraintsValueTrue }]; - __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; - _peerConnectionDelegate = [[NativePeerConnectionDelegate alloc] initWithQueue:_queue didGenerateIceCandidate:^(RTCIceCandidate *iceCandidate) { - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - [strongSelf sendCandidate:iceCandidate]; - } didChangeIceState: ^(OngoingCallStateWebrtcCustom state) { - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - if (strongSelf.stateChanged) { - strongSelf.stateChanged(state); - } + _connection = [[RtcConnection alloc] initWithDiscoveredIceCandidate:^(NSString *sdp, int mLineIndex, NSString *sdpMid) { + [queue dispatch:^{ + __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + [strongSelf sendCandidateWithSdp:sdp mLineIndex:mLineIndex sdpMid:sdpMid]; + }]; + } connectionStateChanged:^(bool isConnected) { + [queue dispatch:^{ + __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + if (strongSelf.stateChanged) { + strongSelf.stateChanged(isConnected ? OngoingCallStateConnected : OngoingCallStateInitializing); + } + }]; }]; - _peerConnection = [_peerConnectionFactory peerConnectionWithConfiguration:config constraints:constraints delegate:_peerConnectionDelegate]; + //RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil optionalConstraints:@{ @"DtlsSrtpKeyAgreement": kRTCMediaConstraintsValueTrue }]; - NSString *streamId = @"stream"; - - RTCMediaConstraints *audioConstrains = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil optionalConstraints:nil]; - - RTCAudioSource *audioSource = [_peerConnectionFactory audioSourceWithConstraints:audioConstrains]; - RTCAudioTrack * _Nonnull audioTrack = [_peerConnectionFactory audioTrackWithSource:audioSource trackId:@"audio0"]; - - [_peerConnection addTrack:audioTrack streamIds:@[streamId]]; - - RTCVideoSource *videoSource = [_peerConnectionFactory videoSource]; + /*RTCVideoSource *videoSource = [_peerConnectionFactory videoSource]; #if TARGET_OS_SIMULATOR _videoCapturer = [[RTCFileVideoCapturer alloc] initWithDelegate:videoSource]; @@ -278,31 +177,25 @@ static void voipLog(NSString* format, ...) { #endif _localVideoTrack = [_peerConnectionFactory videoTrackWithSource:videoSource trackId:@"video0"]; - [_peerConnection addTrack:_localVideoTrack streamIds:@[streamId]]; + [_peerConnection addTrack:_localVideoTrack streamIds:@[streamId]];*/ if (isOutgoing) { id queue = _queue; - NSDictionary *mediaConstraints = @{ - kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueTrue, - kRTCMediaConstraintsOfferToReceiveVideo: kRTCMediaConstraintsValueTrue - }; - RTCMediaConstraints *connectionConstraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mediaConstraints optionalConstraints:nil]; - __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; - [_peerConnection offerForConstraints:connectionConstraints completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) { + [_connection getOffer:^(NSString *sdp, NSString *type) { [queue dispatch:^{ __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; if (strongSelf == nil) { return; } - [strongSelf->_peerConnection setLocalDescription:sdp completionHandler:^(__unused NSError * _Nullable error) { + [strongSelf->_connection setLocalDescription:sdp type:type completion:^{ [queue dispatch:^{ __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; if (strongSelf == nil) { return; } - [strongSelf tryAdvertising:sdp]; + [strongSelf tryAdvertising:sdp type:type]; }]; }]; }]; @@ -318,24 +211,24 @@ static void voipLog(NSString* format, ...) { assert([_queue isCurrent]); } -- (void)tryAdvertising:(RTCSessionDescription *)sessionDescription { +- (void)tryAdvertising:(NSString *)sdp type:(NSString *)type { if (_receivedRemoteDescription) { return; } - [self sendSdp:sessionDescription]; + [self sendSdp:sdp type:type]; __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; [_queue dispatchAfter:1.0 block:^{ __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; if (strongSelf == nil) { return; } - [strongSelf tryAdvertising:sessionDescription]; + [strongSelf tryAdvertising:sdp type:type]; }]; } - (void)startLocalVideo { - if (_videoCapturer == nil || ![_videoCapturer isKindOfClass:[RTCCameraVideoCapturer class]]) { + /*if (_videoCapturer == nil || ![_videoCapturer isKindOfClass:[RTCCameraVideoCapturer class]]) { return; } RTCCameraVideoCapturer *cameraCapturer = (RTCCameraVideoCapturer *)_videoCapturer; @@ -383,7 +276,7 @@ static void voipLog(NSString* format, ...) { } [cameraCapturer startCaptureWithDevice:frontCamera format:bestFormat fps:27 completionHandler:^(NSError * _Nonnull error) { - }]; + }];*/ } - (bool)needRate { @@ -391,11 +284,11 @@ static void voipLog(NSString* format, ...) { } - (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion { - if ([_videoCapturer isKindOfClass:[RTCCameraVideoCapturer class]]) { + /*if ([_videoCapturer isKindOfClass:[RTCCameraVideoCapturer class]]) { RTCCameraVideoCapturer *cameraCapturer = (RTCCameraVideoCapturer *)_videoCapturer; [cameraCapturer stopCapture]; - } - [_peerConnection close]; + }*/ + [_connection close]; if (completion) { completion(@"", 0, 0, 0, 0); } @@ -414,30 +307,24 @@ static void voipLog(NSString* format, ...) { return [NSData data]; } -- (void)sendSdp:(RTCSessionDescription *)rtcSdp { +- (void)sendSdp:(NSString *)sdp type:(NSString *)type { NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; json[@"messageType"] = @"sessionDescription"; - json[@"sdp"] = rtcSdp.sdp; - if (rtcSdp.type == RTCSdpTypeOffer) { - json[@"type"] = @"offer"; - } else if (rtcSdp.type == RTCSdpTypePrAnswer) { - json[@"type"] = @"prAnswer"; - } else if (rtcSdp.type == RTCSdpTypeAnswer) { - json[@"type"] = @"answer"; - } + json[@"sdp"] = sdp; + json[@"type"] = type; NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; if (data != nil) { _sendSignalingData(data); } } -- (void)sendCandidate:(RTCIceCandidate *)rtcIceCandidate { +- (void)sendCandidateWithSdp:(NSString *)sdp mLineIndex:(int)mLineIndex sdpMid:(NSString *)sdpMid { NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; json[@"messageType"] = @"iceCandidate"; - json[@"sdp"] = rtcIceCandidate.sdp; - json[@"mLineIndex"] = @(rtcIceCandidate.sdpMLineIndex); - if (rtcIceCandidate.sdpMid != nil) { - json[@"sdpMid"] = rtcIceCandidate.sdpMid; + json[@"sdp"] = sdp; + json[@"mLineIndex"] = @(mLineIndex); + if (sdpMid != nil) { + json[@"sdpMid"] = sdpMid; } NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; if (data != nil) { @@ -466,50 +353,31 @@ static void voipLog(NSString* format, ...) { return; } - RTCSdpType type; - if ([typeString isEqualToString:@"offer"]) { - type = RTCSdpTypeOffer; - } else if ([typeString isEqualToString:@"prAnswer"]) { - type = RTCSdpTypePrAnswer; - } else if ([typeString isEqualToString:@"answer"]) { - type = RTCSdpTypeAnswer; - } else { - return; - } - if (_receivedRemoteDescription) { return; } _receivedRemoteDescription = true; - RTCSessionDescription *sessionDescription = [[RTCSessionDescription alloc] initWithType:type sdp:sdp]; - - NSDictionary *mediaConstraints = @{ - kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueTrue, - kRTCMediaConstraintsOfferToReceiveVideo: kRTCMediaConstraintsValueTrue - }; - - RTCMediaConstraints *connectionConstraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mediaConstraints optionalConstraints:nil]; - - [_peerConnection setRemoteDescription:sessionDescription completionHandler:^(__unused NSError * _Nullable error) { + [_connection setRemoteDescription:sdp type:typeString completion:^{ }]; if (!_isOutgoing) { __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; - [_peerConnection answerForConstraints:connectionConstraints completionHandler:^(RTCSessionDescription * _Nullable sdp, NSError * _Nullable error) { - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - - id queue = strongSelf->_queue; - [strongSelf->_peerConnection setLocalDescription:sdp completionHandler:^(__unused NSError * _Nullable error) { - [queue dispatch:^{ - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - [strongSelf sendSdp:sdp]; + id queue = _queue; + [_connection getAnswer:^(NSString *sdp, NSString *type) { + [queue dispatch:^{ + __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + [strongSelf->_connection setLocalDescription:sdp type:type completion:^{ + [queue dispatch:^{ + __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + [strongSelf sendSdp:sdp type:type]; + }]; }]; }]; }]; @@ -531,26 +399,24 @@ static void voipLog(NSString* format, ...) { sdpMid = sdpMidString; } - RTCIceCandidate *iceCandidate = [[RTCIceCandidate alloc] initWithSdp:sdp sdpMLineIndex:[mLineIndex intValue] sdpMid:sdpMid]; - voipLog(@"didReceiveIceCandidate: %@", iceCandidate); - [_peerConnection addIceCandidate:iceCandidate]; + [_connection addIceCandidateWithSdp:sdp sdpMLineIndex:[mLineIndex intValue] sdpMid:sdpMid]; } } - (void)setIsMuted:(bool)isMuted { - for (RTCRtpTransceiver *transceiver in _peerConnection.transceivers) { + /*for (RTCRtpTransceiver *transceiver in _peerConnection.transceivers) { if ([transceiver isKindOfClass:[RTCAudioTrack class]]) { RTCAudioTrack *audioTrack = (RTCAudioTrack *)transceiver; [audioTrack setIsEnabled:!isMuted]; } - } + }*/ } - (void)setNetworkType:(OngoingCallNetworkTypeWebrtcCustom)networkType { } - (void)getRemoteCameraView:(void (^_Nonnull)(UIView * _Nullable))completion { - if (_remoteVideoTrack == nil) { + /*if (_remoteVideoTrack == nil) { for (RTCRtpTransceiver *transceiver in _peerConnection.transceivers) { if (transceiver.mediaType == RTCRtpMediaTypeVideo && [transceiver.receiver.track isKindOfClass:[RTCVideoTrack class]]) { _remoteVideoTrack = (RTCVideoTrack *)transceiver.receiver.track; @@ -571,7 +437,7 @@ static void voipLog(NSString* format, ...) { [remoteVideoTrack addRenderer:remoteRenderer]; completion(remoteRenderer); #endif - }); + });*/ } @end diff --git a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.h b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.h new file mode 100644 index 0000000000..f76bdf90e4 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.h @@ -0,0 +1,20 @@ +#ifndef RTCCONNECTION_H +#define RTCCONNECTION_H + +#import + +@interface RtcConnection : NSObject + +- (instancetype)initWithDiscoveredIceCandidate:(void (^)(NSString *, int, NSString *))discoveredIceCandidate connectionStateChanged:(void (^)(bool))connectionStateChanged; + +- (void)close; + +- (void)getOffer:(void (^)(NSString *, NSString *))completion; +- (void)getAnswer:(void (^)(NSString *, NSString *))completion; +- (void)setLocalDescription:(NSString *)serializedDescription type:(NSString *)type completion:(void (^)())completion; +- (void)setRemoteDescription:(NSString *)serializedDescription type:(NSString *)type completion:(void (^)())completion; +- (void)addIceCandidateWithSdp:(NSString *)sdp sdpMLineIndex:(int)sdpMLineIndex sdpMid:(NSString *)sdpMid; + +@end + +#endif diff --git a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm new file mode 100644 index 0000000000..886f7d2d19 --- /dev/null +++ b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm @@ -0,0 +1,298 @@ +#import "RtcConnection.h" + +#include +#include "api/scoped_refptr.h" +#include "rtc_base/thread.h" +#include "api/peer_connection_interface.h" +#include "api/task_queue/default_task_queue_factory.h" +#include "media/engine/webrtc_media_engine.h" +#include "sdk/objc/native/api/audio_device_module.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "sdk/objc/components/video_codec/RTCVideoEncoderFactoryH264.h" +#include "sdk/objc/components/video_codec/RTCVideoDecoderFactoryH264.h" +#include "sdk/objc/native/api/video_encoder_factory.h" +#include "sdk/objc/native/api/video_decoder_factory.h" +#include "api/rtc_event_log/rtc_event_log_factory.h" +#include "sdk/media_constraints.h" +#include "api/peer_connection_interface.h" + +class PeerConnectionObserverImpl : public webrtc::PeerConnectionObserver { +private: + void (^_discoveredIceCandidate)(NSString *, int, NSString *); + void (^_connectionStateChanged)(bool); + +public: + PeerConnectionObserverImpl(void (^discoveredIceCandidate)(NSString *, int, NSString *), void (^connectionStateChanged)(bool)) { + _discoveredIceCandidate = [discoveredIceCandidate copy]; + _connectionStateChanged = [connectionStateChanged copy]; + } + + virtual ~PeerConnectionObserverImpl() { + _discoveredIceCandidate = nil; + _connectionStateChanged = nil; + } + + virtual void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) { + bool isConnected = false; + if (new_state == webrtc::PeerConnectionInterface::SignalingState::kStable) { + isConnected = true; + } + _connectionStateChanged(isConnected); + } + + virtual void OnAddStream(rtc::scoped_refptr stream) { + } + + virtual void OnRemoveStream(rtc::scoped_refptr stream) { + } + + virtual void OnDataChannel( + rtc::scoped_refptr data_channel) { + } + + virtual void OnRenegotiationNeeded() { + } + + virtual void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) { + } + + virtual void OnStandardizedIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) { + } + + virtual void OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState new_state) { + } + + virtual void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) { + } + + virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { + std::string sdp; + candidate->ToString(&sdp); + NSString *sdpString = [NSString stringWithUTF8String:sdp.c_str()]; + NSString *sdpMidString = [NSString stringWithUTF8String:candidate->sdp_mid().c_str()]; + _discoveredIceCandidate(sdpString, candidate->sdp_mline_index(), sdpMidString); + } + + virtual void OnIceCandidateError(const std::string& host_candidate, const std::string& url, int error_code, const std::string& error_text) { + } + + virtual void OnIceCandidateError(const std::string& address, + int port, + const std::string& url, + int error_code, + const std::string& error_text) { + } + + virtual void OnIceCandidatesRemoved(const std::vector& candidates) { + } + + virtual void OnIceConnectionReceivingChange(bool receiving) { + } + + virtual void OnIceSelectedCandidatePairChanged(const cricket::CandidatePairChangeEvent& event) { + } + + virtual void OnAddTrack(rtc::scoped_refptr receiver, const std::vector>& streams) { + } + + virtual void OnTrack(rtc::scoped_refptr transceiver) { + } + + virtual void OnRemoveTrack(rtc::scoped_refptr receiver) { + } + + virtual void OnInterestingUsage(int usage_pattern) { + } +}; + +class CreateSessionDescriptionObserverImpl : public webrtc::CreateSessionDescriptionObserver { +private: + void (^_completion)(NSString *, NSString *); + +public: + CreateSessionDescriptionObserverImpl(void (^completion)(NSString *, NSString *)) { + _completion = [completion copy]; + } + + ~CreateSessionDescriptionObserverImpl() override { + _completion = nil; + } + + virtual void OnSuccess(webrtc::SessionDescriptionInterface* desc) override { + if (desc) { + NSString *typeString = [NSString stringWithUTF8String:desc->type().c_str()]; + std::string sdp; + desc->ToString(&sdp); + NSString *serializedString = [NSString stringWithUTF8String:sdp.c_str()]; + if (_completion && typeString && serializedString) { + _completion(serializedString, typeString); + } + } + _completion = nil; + } + + virtual void OnFailure(webrtc::RTCError error) override { + _completion = nil; + } +}; + +class SetSessionDescriptionObserverImpl : public webrtc::SetSessionDescriptionObserver { +private: + void (^_completion)(); + +public: + SetSessionDescriptionObserverImpl(void (^completion)()) { + _completion = [completion copy]; + } + + ~SetSessionDescriptionObserverImpl() override { + _completion = nil; + } + + virtual void OnSuccess() override { + if (_completion) { + _completion(); + } + _completion = nil; + } + + virtual void OnFailure(webrtc::RTCError error) override { + _completion = nil; + } +}; + +@interface RtcConnection () { + void (^_discoveredIceCandidate)(NSString *, int, NSString *); + void (^_connectionStateChanged)(bool); + + std::unique_ptr _networkThread; + std::unique_ptr _workerThread; + std::unique_ptr _signalingThread; + rtc::scoped_refptr _nativeFactory; + + std::unique_ptr _observer; + rtc::scoped_refptr _peerConnection; + std::unique_ptr _nativeConstraints; + bool _hasStartedRtcEventLog; +} + +@end + +@implementation RtcConnection + +- (instancetype)initWithDiscoveredIceCandidate:(void (^)(NSString *, int, NSString *))discoveredIceCandidate connectionStateChanged:(void (^)(bool))connectionStateChanged { + self = [super init]; + if (self != nil) { + _discoveredIceCandidate = [discoveredIceCandidate copy]; + _connectionStateChanged = [connectionStateChanged copy]; + + _networkThread = rtc::Thread::CreateWithSocketServer(); + _networkThread->SetName("network_thread", _networkThread.get()); + BOOL result = _networkThread->Start(); + assert(result); + + _workerThread = rtc::Thread::Create(); + _workerThread->SetName("worker_thread", _workerThread.get()); + result = _workerThread->Start(); + assert(result); + + _signalingThread = rtc::Thread::Create(); + _signalingThread->SetName("signaling_thread", _signalingThread.get()); + result = _signalingThread->Start(); + assert(result); + + webrtc::PeerConnectionFactoryDependencies dependencies; + dependencies.network_thread = _networkThread.get(); + dependencies.worker_thread = _workerThread.get(); + dependencies.signaling_thread = _signalingThread.get(); + dependencies.task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); + cricket::MediaEngineDependencies media_deps; + media_deps.adm = webrtc::CreateAudioDeviceModule(); + media_deps.task_queue_factory = dependencies.task_queue_factory.get(); + media_deps.audio_encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory(); + media_deps.audio_decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory(); + media_deps.video_encoder_factory = webrtc::ObjCToNativeVideoEncoderFactory([[RTCVideoEncoderFactoryH264 alloc] init]); + media_deps.video_decoder_factory = webrtc::ObjCToNativeVideoDecoderFactory([[RTCVideoDecoderFactoryH264 alloc] init]); + media_deps.audio_processing = webrtc::AudioProcessingBuilder().Create(); + dependencies.media_engine = cricket::CreateMediaEngine(std::move(media_deps)); + dependencies.call_factory = webrtc::CreateCallFactory(); + dependencies.event_log_factory = + std::make_unique(dependencies.task_queue_factory.get()); + dependencies.network_controller_factory = nil; + dependencies.media_transport_factory = nil; + _nativeFactory = webrtc::CreateModularPeerConnectionFactory(std::move(dependencies)); + + webrtc::PeerConnectionInterface::RTCConfiguration config; + config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; + config.continual_gathering_policy = webrtc::PeerConnectionInterface::ContinualGatheringPolicy::GATHER_CONTINUALLY; + webrtc::PeerConnectionInterface::IceServer iceServer; + iceServer.uri = "stun:stun.l.google.com:19302"; + config.servers.push_back(iceServer); + + _observer.reset(new PeerConnectionObserverImpl(_discoveredIceCandidate, _connectionStateChanged)); + _peerConnection = _nativeFactory->CreatePeerConnection(config, nullptr, nullptr, _observer.get()); + + cricket::AudioOptions options; + rtc::scoped_refptr audioSource = _nativeFactory->CreateAudioSource(options); + rtc::scoped_refptr track = _nativeFactory->CreateAudioTrack("audio0", audioSource); + + std::vector streamIds; + streamIds.push_back("stream"); + + _peerConnection->AddTrack(track, streamIds); + } + return self; +} + +- (void)close { + _peerConnection->Close(); +} + +- (void)getOffer:(void (^)(NSString *, NSString *))completion { + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_audio = 1; + options.offer_to_receive_video = 1; + + rtc::scoped_refptr observer(new rtc::RefCountedObject(completion)); + _peerConnection->CreateOffer(observer, options); +} + +- (void)getAnswer:(void (^)(NSString *, NSString *))completion { + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_audio = 1; + options.offer_to_receive_video = 1; + + rtc::scoped_refptr observer(new rtc::RefCountedObject(completion)); + _peerConnection->CreateAnswer(observer, options); +} + +- (void)setLocalDescription:(NSString *)serializedDescription type:(NSString *)type completion:(void (^)())completion { + webrtc::SdpParseError error; + webrtc::SessionDescriptionInterface *sessionDescription = webrtc::CreateSessionDescription(type.UTF8String, serializedDescription.UTF8String, &error); + if (sessionDescription != nullptr) { + rtc::scoped_refptr observer(new rtc::RefCountedObject(completion)); + _peerConnection->SetLocalDescription(observer, sessionDescription); + } +} + +- (void)setRemoteDescription:(NSString *)serializedDescription type:(NSString *)type completion:(void (^)())completion { + webrtc::SdpParseError error; + webrtc::SessionDescriptionInterface *sessionDescription = webrtc::CreateSessionDescription(type.UTF8String, serializedDescription.UTF8String, &error); + if (sessionDescription != nullptr) { + rtc::scoped_refptr observer(new rtc::RefCountedObject(completion)); + _peerConnection->SetRemoteDescription(observer, sessionDescription); + } +} + +- (void)addIceCandidateWithSdp:(NSString *)sdp sdpMLineIndex:(int)sdpMLineIndex sdpMid:(NSString *)sdpMid { + webrtc::SdpParseError error; + webrtc::IceCandidateInterface *iceCandidate = webrtc::CreateIceCandidate(sdpMid == nil ? "" : sdpMid.UTF8String, sdpMLineIndex, sdp.UTF8String, &error); + if (iceCandidate != nullptr) { + std::unique_ptr nativeCandidate = std::unique_ptr(iceCandidate); + _peerConnection->AddIceCandidate(std::move(nativeCandidate), [](auto error) { + }); + } +} + +@end