From eee4036987f3e585bf91e5853755e43caabeb93f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 10 Jul 2020 19:57:40 +0400 Subject: [PATCH] Video call improvements --- .../Sources/PresentationCallManager.swift | 8 +- .../SettingsUI/Sources/DebugController.swift | 2 +- .../Sources/CallController.swift | 2 +- .../Sources/CallControllerButtonsNode.swift | 65 +- .../Sources/CallControllerNode.swift | 283 +- .../LegacyCallControllerButtonsNode.swift | 48 +- .../Sources/LegacyCallControllerNode.swift | 51 +- .../Sources/PresentationCall.swift | 28 +- .../Sources/PresentationCallManager.swift | 13 +- .../Sources/ChatControllerNode.swift | 100 +- .../Sources/OverlayUniversalVideoNode.swift | 4 + .../Sources/OngoingCallContext.swift | 103 +- submodules/TgVoipWebrtc/Impl/Manager.cpp | 44 +- submodules/TgVoipWebrtc/Impl/Manager.h | 19 +- submodules/TgVoipWebrtc/Impl/TgVoip.h | 12 +- submodules/TgVoipWebrtc/Impl/TgVoip.mm | 73 +- .../TgVoip/OngoingCallThreadLocalContext.h | 7 +- .../Sources/OngoingCallThreadLocalContext.mm | 67 +- submodules/TgVoipWebrtcCustom/BUCK | 47 - submodules/TgVoipWebrtcCustom/BUILD | 47 - .../TgVoip/OngoingCallThreadLocalContext.h | 87 - .../Sources/OngoingCallThreadLocalContext.mm | 353 - .../Sources/RtcConnection.h | 25 - .../Sources/RtcConnection.mm | 438 -- .../Sources/VideoCameraCapturer.h | 23 - .../Sources/VideoCameraCapturer.mm | 459 -- .../Sources/VideoFileCapturer.h | 0 .../Sources/VideoFileCapturer.mm | 0 .../Sources/VideoMetalView.h | 24 - .../Sources/VideoMetalView.mm | 274 - .../Sources/VideoRendererAdapter.h | 0 .../Sources/VideoRendererAdapter.mm | 0 .../Sources/tg_dtls_transport.cpp | 811 -- .../Sources/tg_dtls_transport.h | 229 - .../Sources/tg_jsep_transport.cpp | 868 --- .../Sources/tg_jsep_transport.h | 417 -- .../Sources/tg_jsep_transport_controller.cpp | 1699 ----- .../Sources/tg_jsep_transport_controller.h | 478 -- .../Sources/tg_peer_connection.cpp | 6629 ----------------- .../Sources/tg_peer_connection.h | 1735 ----- .../Sources/tg_peer_connection_factory.cpp | 516 -- .../Sources/tg_peer_connection_factory.h | 261 - .../Sources/tg_rtp_data_engine.cpp | 330 - .../Sources/tg_rtp_data_engine.h | 109 - .../Sources/tg_rtp_sender.cpp | 357 - .../Sources/tg_rtp_sender.h | 176 - .../Sources/tg_rtp_transport.cpp | 292 - .../Sources/tg_rtp_transport.h | 133 - .../tg_webrtc_session_description_factory.cpp | 501 -- .../tg_webrtc_session_description_factory.h | 167 - 50 files changed, 454 insertions(+), 17960 deletions(-) delete mode 100644 submodules/TgVoipWebrtcCustom/BUCK delete mode 100644 submodules/TgVoipWebrtcCustom/BUILD delete mode 100644 submodules/TgVoipWebrtcCustom/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/RtcConnection.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/VideoCameraCapturer.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/VideoCameraCapturer.mm delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/VideoFileCapturer.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/VideoFileCapturer.mm delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/VideoMetalView.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/VideoMetalView.mm delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/VideoRendererAdapter.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/VideoRendererAdapter.mm delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.h delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.cpp delete mode 100644 submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.h diff --git a/submodules/AccountContext/Sources/PresentationCallManager.swift b/submodules/AccountContext/Sources/PresentationCallManager.swift index 0d40d5ad6c..54a6456f19 100644 --- a/submodules/AccountContext/Sources/PresentationCallManager.swift +++ b/submodules/AccountContext/Sources/PresentationCallManager.swift @@ -46,9 +46,10 @@ public struct PresentationCallState: Equatable { public enum VideoState: Equatable { case notAvailable - case available(Bool) + case possible + case outgoingRequested + case incomingRequested case active - case activeOutgoing } public enum RemoteVideoState: Equatable { @@ -87,6 +88,7 @@ public protocol PresentationCall: class { var peerId: PeerId { get } var isOutgoing: Bool { get } var isVideo: Bool { get } + var isVideoPossible: Bool { get } var peer: Peer? { get } var state: Signal { get } @@ -103,7 +105,7 @@ public protocol PresentationCall: class { func toggleIsMuted() func setIsMuted(_ value: Bool) - func setEnableVideo(_ value: Bool) + func requestVideo() func setOutgoingVideoIsPaused(_ isPaused: Bool) func switchVideoCamera() func setCurrentAudioOutput(_ output: AudioSessionOutput) diff --git a/submodules/SettingsUI/Sources/DebugController.swift b/submodules/SettingsUI/Sources/DebugController.swift index 0c93c46908..d06d8a8810 100644 --- a/submodules/SettingsUI/Sources/DebugController.swift +++ b/submodules/SettingsUI/Sources/DebugController.swift @@ -629,7 +629,7 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper)) entries.append(.alternativeFolderTabs(experimentalSettings.foldersTabAtBottom)) entries.append(.playerEmbedding(experimentalSettings.playerEmbedding)) - //entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) + entries.append(.playlistPlayback(experimentalSettings.playlistPlayback)) entries.append(.videoCalls(experimentalSettings.videoCalls)) entries.append(.videoCallsInfo(presentationData.theme, "Enables experimental transmission of electromagnetic radiation synchronized with pressure waves. Needs to be enabled on both sides.")) diff --git a/submodules/TelegramCallsUI/Sources/CallController.swift b/submodules/TelegramCallsUI/Sources/CallController.swift index bbac1d893f..45891d7192 100644 --- a/submodules/TelegramCallsUI/Sources/CallController.swift +++ b/submodules/TelegramCallsUI/Sources/CallController.swift @@ -134,7 +134,7 @@ public final class CallController: ViewController { } override public func loadDisplayNode() { - if self.call.isVideo { + if self.call.isVideoPossible { self.displayNode = CallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call) } else { self.displayNode = LegacyCallControllerNode(sharedContext: self.sharedContext, account: self.account, presentationData: self.presentationData, statusBar: self.statusBar, debugInfo: self.call.debugInfo(), shouldStayHiddenUntilConnection: !self.call.isOutgoing && self.call.isIntegratedWithCallKit, easyDebugAccess: self.easyDebugAccess, call: self.call) diff --git a/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift index 93714216a5..daeb7b2909 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerButtonsNode.swift @@ -17,7 +17,9 @@ enum CallControllerButtonsSpeakerMode { enum CallControllerButtonsMode: Equatable { enum VideoState: Equatable { case notAvailable - case available(Bool) + case possible + case outgoingRequested + case incomingRequested case active } @@ -147,9 +149,42 @@ final class CallControllerButtonsNode: ASDisplayNode { let height: CGFloat - var buttons: [PlacedButton] = [] + let speakerMode: CallControllerButtonsSpeakerMode + var videoState: CallControllerButtonsMode.VideoState switch mode { - case .incoming(let speakerMode, let videoState), .outgoingRinging(let speakerMode, let videoState): + case .incoming(let speakerModeValue, let videoStateValue), .outgoingRinging(let speakerModeValue, let videoStateValue), .active(let speakerModeValue, let videoStateValue): + speakerMode = speakerModeValue + videoState = videoStateValue + } + + enum MappedState { + case incomingRinging + case outgoingRinging + case active + } + + let mappedState: MappedState + switch mode { + case .incoming: + mappedState = .incomingRinging + case .outgoingRinging: + mappedState = .outgoingRinging + case let .active(_, videoStateValue): + switch videoStateValue { + case .incomingRequested: + mappedState = .incomingRinging + videoState = .outgoingRequested + case .outgoingRequested: + mappedState = .outgoingRinging + videoState = .outgoingRequested + case .active, .possible, .notAvailable: + mappedState = .active + } + } + + var buttons: [PlacedButton] = [] + switch mappedState { + case .incomingRinging, .outgoingRinging: var topButtons: [ButtonDescription] = [] var bottomButtons: [ButtonDescription] = [] @@ -166,8 +201,14 @@ final class CallControllerButtonsNode: ASDisplayNode { } switch videoState { - case .active, .available: - topButtons.append(.enableCamera(!self.isCameraPaused)) + case .active, .possible, .incomingRequested, .outgoingRequested: + let isCameraActive: Bool + if case .possible = videoState { + isCameraActive = false + } else { + isCameraActive = !self.isCameraPaused + } + topButtons.append(.enableCamera(isCameraActive)) topButtons.append(.mute(self.isMuted)) topButtons.append(.switchCamera) case .notAvailable: @@ -185,7 +226,7 @@ final class CallControllerButtonsNode: ASDisplayNode { topButtonsLeftOffset += smallButtonSize + topButtonsSpacing } - if case .incoming = mode { + if case .incomingRinging = mappedState { bottomButtons.append(.end(.decline)) bottomButtons.append(.accept) } else { @@ -203,7 +244,7 @@ final class CallControllerButtonsNode: ASDisplayNode { } height = smallButtonSize + topBottomSpacing + largeButtonSize + max(bottomInset + 32.0, 46.0) - case let .active(speakerMode, videoState): + case .active: var topButtons: [ButtonDescription] = [] let soundOutput: ButtonDescription.SoundOutput @@ -219,8 +260,14 @@ final class CallControllerButtonsNode: ASDisplayNode { } switch videoState { - case .active, .available: - topButtons.append(.enableCamera(!self.isCameraPaused)) + case .active, .incomingRequested, .outgoingRequested, .possible: + let isCameraActive: Bool + if case .possible = videoState { + isCameraActive = false + } else { + isCameraActive = !self.isCameraPaused + } + topButtons.append(.enableCamera(isCameraActive)) topButtons.append(.mute(isMuted)) topButtons.append(.switchCamera) case .notAvailable: diff --git a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift index 9b461f3614..57f26ab781 100644 --- a/submodules/TelegramCallsUI/Sources/CallControllerNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallControllerNode.swift @@ -22,22 +22,41 @@ private func interpolate(from: CGFloat, to: CGFloat, value: CGFloat) -> CGFloat return (1.0 - value) * from + value * to } -private final class IncomingVideoNode: ASDisplayNode { +private final class CallVideoNode: ASDisplayNode { + private let videoTransformContainer: ASDisplayNode private let videoView: PresentationCallVideoView + private var effectView: UIVisualEffectView? private var isBlurred: Bool = false + private var currentCornerRadius: CGFloat = 0.0 private let isReadyUpdated: () -> Void private(set) var isReady: Bool = false private var isReadyTimer: SwiftSignalKit.Timer? init(videoView: PresentationCallVideoView, isReadyUpdated: @escaping () -> Void) { - self.videoView = videoView self.isReadyUpdated = isReadyUpdated + self.videoTransformContainer = ASDisplayNode() + self.videoTransformContainer.clipsToBounds = true + self.videoView = videoView + self.videoView.view.layer.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0) + super.init() - self.view.addSubview(self.videoView.view) + self.videoTransformContainer.view.addSubview(self.videoView.view) + self.addSubnode(self.videoTransformContainer) + + self.videoView.setOnFirstFrameReceived { [weak self] in + guard let strongSelf = self else { + return + } + if !strongSelf.isReady { + strongSelf.isReady = true + strongSelf.isReadyTimer?.invalidate() + strongSelf.isReadyUpdated() + } + } self.isReadyTimer = SwiftSignalKit.Timer(timeout: 3.0, repeat: false, completion: { [weak self] in guard let strongSelf = self else { @@ -49,89 +68,14 @@ private final class IncomingVideoNode: ASDisplayNode { } }, queue: .mainQueue()) self.isReadyTimer?.start() - - videoView.setOnFirstFrameReceived { [weak self] in - Queue.mainQueue().async { - guard let strongSelf = self else { - return - } - - if !strongSelf.isReady { - strongSelf.isReady = true - strongSelf.isReadyTimer?.invalidate() - strongSelf.isReadyUpdated() - } - } - } } deinit { self.isReadyTimer?.invalidate() } - func updateLayout(size: CGSize) { - self.videoView.view.frame = CGRect(origin: CGPoint(), size: size) - } - - func updateIsBlurred(isBlurred: Bool) { - if self.isBlurred == isBlurred { - return - } - self.isBlurred = isBlurred - - if isBlurred { - if self.effectView == nil { - let effectView = UIVisualEffectView() - self.effectView = effectView - effectView.frame = self.videoView.view.frame - self.view.addSubview(effectView) - } - UIView.animate(withDuration: 0.3, animations: { - self.effectView?.effect = UIBlurEffect(style: .dark) - }) - } else if let effectView = self.effectView { - UIView.animate(withDuration: 0.3, animations: { - effectView.effect = nil - }) - } - } -} - -private final class OutgoingVideoNode: ASDisplayNode { - private let videoTransformContainer: ASDisplayNode - private let videoView: PresentationCallVideoView - private let buttonNode: HighlightTrackingButtonNode - - private var effectView: UIVisualEffectView? - private var isBlurred: Bool = false - private var currentCornerRadius: CGFloat = 0.0 - - var tapped: (() -> Void)? - - init(videoView: PresentationCallVideoView) { - self.videoTransformContainer = ASDisplayNode() - self.videoTransformContainer.clipsToBounds = true - self.videoView = videoView - self.videoView.view.layer.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0) - - self.buttonNode = HighlightTrackingButtonNode() - - super.init() - - self.videoTransformContainer.view.addSubview(self.videoView.view) - self.addSubnode(self.videoTransformContainer) - //self.addSubnode(self.buttonNode) - - self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) - } - - @objc func buttonPressed() { - self.tapped?() - } - func updateLayout(size: CGSize, cornerRadius: CGFloat, transition: ContainedViewLayoutTransition) { let videoFrame = CGRect(origin: CGPoint(), size: size) - self.buttonNode.frame = videoFrame self.currentCornerRadius = cornerRadius let previousVideoFrame = self.videoTransformContainer.frame @@ -168,8 +112,11 @@ private final class OutgoingVideoNode: ASDisplayNode { self.effectView?.effect = UIBlurEffect(style: .dark) }) } else if let effectView = self.effectView { + self.effectView = nil UIView.animate(withDuration: 0.3, animations: { effectView.effect = nil + }, completion: { [weak effectView] _ in + effectView?.removeFromSuperview() }) } } @@ -200,11 +147,16 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro private let imageNode: TransformImageNode private let dimNode: ASDisplayNode - private var incomingVideoNode: IncomingVideoNode? + + private var incomingVideoNodeValue: CallVideoNode? private var incomingVideoViewRequested: Bool = false - private var outgoingVideoNode: OutgoingVideoNode? + private var outgoingVideoNodeValue: CallVideoNode? private var outgoingVideoViewRequested: Bool = false - private var outgoingVideoExplicitelyFullscreen: Bool = false + + private var expandedVideoNode: CallVideoNode? + private var minimizedVideoNode: CallVideoNode? + private var disableAnimationForExpandedVideoOnce: Bool = false + private var outgoingVideoNodeCorner: VideoNodeCorner = .bottomRight private let backButtonArrowNode: ASImageNode private let backButtonNode: HighlightableButtonNode @@ -352,13 +304,17 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro guard let strongSelf = self else { return } - strongSelf.isVideoPaused = !strongSelf.isVideoPaused - strongSelf.outgoingVideoNode?.updateIsBlurred(isBlurred: strongSelf.isVideoPaused) - strongSelf.buttonsNode.isCameraPaused = strongSelf.isVideoPaused - strongSelf.setIsVideoPaused?(strongSelf.isVideoPaused) - - if let (layout, navigationBarHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut)) + if strongSelf.outgoingVideoNodeValue == nil { + strongSelf.call.requestVideo() + } else { + strongSelf.isVideoPaused = !strongSelf.isVideoPaused + strongSelf.outgoingVideoNodeValue?.updateIsBlurred(isBlurred: strongSelf.isVideoPaused) + strongSelf.buttonsNode.isCameraPaused = strongSelf.isVideoPaused + strongSelf.setIsVideoPaused?(strongSelf.isVideoPaused) + + if let (layout, navigationBarHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut)) + } } } @@ -432,7 +388,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro return } if let incomingVideoView = incomingVideoView { - let incomingVideoNode = IncomingVideoNode(videoView: incomingVideoView, isReadyUpdated: { + let incomingVideoNode = CallVideoNode(videoView: incomingVideoView, isReadyUpdated: { guard let strongSelf = self else { return } @@ -440,7 +396,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.5, curve: .spring)) } }) - strongSelf.incomingVideoNode = incomingVideoNode + strongSelf.incomingVideoNodeValue = incomingVideoNode + strongSelf.expandedVideoNode = incomingVideoNode strongSelf.containerNode.insertSubnode(incomingVideoNode, aboveSubnode: strongSelf.dimNode) if let (layout, navigationBarHeight) = strongSelf.validLayout { strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.5, curve: .spring)) @@ -453,7 +410,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro } switch callState.videoState { - case .active, .activeOutgoing: + case .active, .outgoingRequested, .incomingRequested: if !self.outgoingVideoViewRequested { self.outgoingVideoViewRequested = true self.call.makeOutgoingVideoView(completion: { [weak self] outgoingVideoView in @@ -471,25 +428,17 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro strongSelf.setCurrentAudioOutput?(.speaker) } } - let outgoingVideoNode = OutgoingVideoNode(videoView: outgoingVideoView) - strongSelf.outgoingVideoNode = outgoingVideoNode - if let incomingVideoNode = strongSelf.incomingVideoNode { - strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: incomingVideoNode) + let outgoingVideoNode = CallVideoNode(videoView: outgoingVideoView, isReadyUpdated: {}) + strongSelf.outgoingVideoNodeValue = outgoingVideoNode + strongSelf.minimizedVideoNode = outgoingVideoNode + if let expandedVideoNode = strongSelf.expandedVideoNode { + strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: expandedVideoNode) } else { strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: strongSelf.dimNode) } if let (layout, navigationBarHeight) = strongSelf.validLayout { strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) } - /*outgoingVideoNode.tapped = { - guard let strongSelf = self else { - return - } - strongSelf.outgoingVideoExplicitelyFullscreen = !strongSelf.outgoingVideoExplicitelyFullscreen - if let (layout, navigationBarHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring)) - } - }*/ } }) } @@ -497,8 +446,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro break } - if let incomingVideoNode = self.incomingVideoNode { - incomingVideoNode.isHidden = !incomingVideoNode.isReady + if let incomingVideoNode = self.incomingVideoNodeValue { let isActive: Bool switch callState.remoteVideoState { case .inactive: @@ -643,12 +591,14 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro switch callState.videoState { case .notAvailable: mappedVideoState = .notAvailable - case .available: - mappedVideoState = .available(true) + case .possible: + mappedVideoState = .possible + case .outgoingRequested: + mappedVideoState = .outgoingRequested + case .incomingRequested: + mappedVideoState = .incomingRequested case .active: mappedVideoState = .active - case .activeOutgoing: - mappedVideoState = .active } switch callState.state { @@ -717,16 +667,24 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro let buttonsHeight: CGFloat = self.buttonsNode.bounds.height - var insets = layout.insets(options: .statusBar) - insets.top += 44.0 + 8.0 - insets.bottom = buttonsHeight + 27.0 - insets.left = 20.0 - insets.right = 20.0 + var fullInsets = layout.insets(options: .statusBar) + + var cleanInsets = fullInsets + cleanInsets.bottom = layout.intrinsicInsets.bottom + cleanInsets.left = 20.0 + cleanInsets.right = 20.0 + + fullInsets.top += 44.0 + 8.0 + fullInsets.bottom = buttonsHeight + 27.0 + fullInsets.left = 20.0 + fullInsets.right = 20.0 + + var insets: UIEdgeInsets = self.isUIHidden ? cleanInsets : fullInsets let expandedInset: CGFloat = 16.0 - insets.top = interpolate(from: expandedInset, to: insets.top, value: uiDisplayTransition) - insets.bottom = interpolate(from: expandedInset, to: insets.bottom, value: uiDisplayTransition) + insets.top = interpolate(from: expandedInset, to: insets.top, value: 1.0 - self.pictureInPictureTransitionFraction) + insets.bottom = interpolate(from: expandedInset, to: insets.bottom, value: 1.0 - self.pictureInPictureTransitionFraction) insets.left = interpolate(from: expandedInset, to: insets.left, value: 1.0 - self.pictureInPictureTransitionFraction) insets.right = interpolate(from: expandedInset, to: insets.right, value: 1.0 - self.pictureInPictureTransitionFraction) @@ -860,38 +818,30 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro transition.updateAlpha(node: self.buttonsNode, alpha: overlayAlpha) let fullscreenVideoFrame = CGRect(origin: CGPoint(), size: layout.size) - let previewVideoFrame = self.calculatePreviewVideoRect(layout: layout, navigationHeight: navigationBarHeight) - if let incomingVideoNode = self.incomingVideoNode { - var incomingVideoTransition = transition - if incomingVideoNode.frame.isEmpty { - incomingVideoTransition = .immediate + if let expandedVideoNode = self.expandedVideoNode { + var expandedVideoTransition = transition + if expandedVideoNode.frame.isEmpty || self.disableAnimationForExpandedVideoOnce { + expandedVideoTransition = .immediate + self.disableAnimationForExpandedVideoOnce = false } - if self.outgoingVideoExplicitelyFullscreen { - incomingVideoTransition.updateFrame(node: incomingVideoNode, frame: previewVideoFrame) - } else { - incomingVideoTransition.updateFrame(node: incomingVideoNode, frame: fullscreenVideoFrame) - } - incomingVideoNode.updateLayout(size: incomingVideoNode.frame.size) + expandedVideoTransition.updateFrame(node: expandedVideoNode, frame: fullscreenVideoFrame) + expandedVideoNode.updateLayout(size: expandedVideoNode.frame.size, cornerRadius: 0.0, transition: expandedVideoTransition) } - if let outgoingVideoNode = self.outgoingVideoNode { - var outgoingVideoTransition = transition - if outgoingVideoNode.frame.isEmpty { - outgoingVideoTransition = .immediate + if let minimizedVideoNode = self.minimizedVideoNode { + var minimizedVideoTransition = transition + if minimizedVideoNode.frame.isEmpty { + minimizedVideoTransition = .immediate } - if let incomingVideoNode = self.incomingVideoNode, incomingVideoNode.isReady { + if let expandedVideoNode = self.expandedVideoNode, expandedVideoNode.isReady { if self.minimizedVideoDraggingPosition == nil { - if self.outgoingVideoExplicitelyFullscreen { - outgoingVideoTransition.updateFrame(node: outgoingVideoNode, frame: fullscreenVideoFrame) - } else { - outgoingVideoTransition.updateFrame(node: outgoingVideoNode, frame: previewVideoFrame) - } - outgoingVideoNode.updateLayout(size: outgoingVideoNode.frame.size, cornerRadius: interpolate(from: self.outgoingVideoExplicitelyFullscreen ? 0.0 : 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), transition: outgoingVideoTransition) + minimizedVideoTransition.updateFrame(node: minimizedVideoNode, frame: previewVideoFrame) + minimizedVideoNode.updateLayout(size: minimizedVideoNode.frame.size, cornerRadius: interpolate(from: 14.0, to: 24.0, value: self.pictureInPictureTransitionFraction), transition: minimizedVideoTransition) } } else { - outgoingVideoNode.frame = fullscreenVideoFrame - outgoingVideoNode.updateLayout(size: layout.size, cornerRadius: 0.0, transition: outgoingVideoTransition) + minimizedVideoNode.frame = fullscreenVideoFrame + minimizedVideoNode.updateLayout(size: layout.size, cornerRadius: 0.0, transition: minimizedVideoTransition) } } @@ -949,19 +899,32 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro } else if let _ = self.keyPreviewNode { self.backPressed() } else { - if self.incomingVideoNode != nil || self.outgoingVideoNode != nil { - var updated = false - if let callState = self.callState { - switch callState.state { - case .active, .connecting, .reconnecting: - self.isUIHidden = !self.isUIHidden - updated = true - default: - break + if let expandedVideoNode = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode { + let point = recognizer.location(in: recognizer.view) + if minimizedVideoNode.frame.contains(point) { + self.expandedVideoNode = minimizedVideoNode + self.minimizedVideoNode = expandedVideoNode + if let supernode = expandedVideoNode.supernode { + supernode.insertSubnode(expandedVideoNode, aboveSubnode: minimizedVideoNode) + } + if let (layout, navigationBarHeight) = self.validLayout { + self.disableAnimationForExpandedVideoOnce = true + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut)) + } + } else { + var updated = false + if let callState = self.callState { + switch callState.state { + case .active, .connecting, .reconnecting: + self.isUIHidden = !self.isUIHidden + updated = true + default: + break + } + } + if updated, let (layout, navigationBarHeight) = self.validLayout { + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut)) } - } - if updated, let (layout, navigationBarHeight) = self.validLayout { - self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.3, curve: .easeInOut)) } } else { let point = recognizer.location(in: recognizer.view) @@ -1128,8 +1091,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro switch recognizer.state { case .began: let location = recognizer.location(in: self.view) - if self.self.pictureInPictureTransitionFraction.isZero, let _ = self.incomingVideoNode, let outgoingVideoNode = self.outgoingVideoNode, outgoingVideoNode.frame.contains(location) { - self.minimizedVideoInitialPosition = outgoingVideoNode.position + if self.self.pictureInPictureTransitionFraction.isZero, let _ = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode, minimizedVideoNode.frame.contains(location) { + self.minimizedVideoInitialPosition = minimizedVideoNode.position } else { self.minimizedVideoInitialPosition = nil if !self.pictureInPictureTransitionFraction.isZero { @@ -1139,11 +1102,11 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro } } case .changed: - if let outgoingVideoNode = self.outgoingVideoNode, let minimizedVideoInitialPosition = self.minimizedVideoInitialPosition { + if let minimizedVideoNode = self.minimizedVideoNode, let minimizedVideoInitialPosition = self.minimizedVideoInitialPosition { let translation = recognizer.translation(in: self.view) let minimizedVideoDraggingPosition = CGPoint(x: minimizedVideoInitialPosition.x + translation.x, y: minimizedVideoInitialPosition.y + translation.y) self.minimizedVideoDraggingPosition = minimizedVideoDraggingPosition - outgoingVideoNode.position = minimizedVideoDraggingPosition + minimizedVideoNode.position = minimizedVideoDraggingPosition } else { switch self.pictureInPictureGestureState { case .none: @@ -1184,7 +1147,7 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro } } case .cancelled, .ended: - if let outgoingVideoNode = self.outgoingVideoNode, let _ = self.minimizedVideoInitialPosition, let minimizedVideoDraggingPosition = self.minimizedVideoDraggingPosition { + if let minimizedVideoNode = self.minimizedVideoNode, let _ = self.minimizedVideoInitialPosition, let minimizedVideoDraggingPosition = self.minimizedVideoDraggingPosition { self.minimizedVideoInitialPosition = nil self.minimizedVideoDraggingPosition = nil @@ -1192,8 +1155,8 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro self.outgoingVideoNodeCorner = self.nodeLocationForPosition(layout: layout, position: minimizedVideoDraggingPosition, velocity: recognizer.velocity(in: self.view)) let videoFrame = self.calculatePreviewVideoRect(layout: layout, navigationHeight: navigationHeight) - outgoingVideoNode.frame = videoFrame - outgoingVideoNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: minimizedVideoDraggingPosition.x - videoFrame.midX, y: minimizedVideoDraggingPosition.y - videoFrame.midY)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.5, delay: 0.0, initialVelocity: 0.0, damping: 110.0, removeOnCompletion: true, additive: true, completion: nil) + minimizedVideoNode.frame = videoFrame + minimizedVideoNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: minimizedVideoDraggingPosition.x - videoFrame.midX, y: minimizedVideoDraggingPosition.y - videoFrame.midY)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.5, delay: 0.0, initialVelocity: 0.0, damping: 110.0, removeOnCompletion: true, additive: true, completion: nil) } } else { switch self.pictureInPictureGestureState { diff --git a/submodules/TelegramCallsUI/Sources/LegacyCallControllerButtonsNode.swift b/submodules/TelegramCallsUI/Sources/LegacyCallControllerButtonsNode.swift index 59e14b0f1a..6cc2c829d1 100644 --- a/submodules/TelegramCallsUI/Sources/LegacyCallControllerButtonsNode.swift +++ b/submodules/TelegramCallsUI/Sources/LegacyCallControllerButtonsNode.swift @@ -15,13 +15,7 @@ enum LegacyCallControllerButtonsSpeakerMode { } enum LegacyCallControllerButtonsMode: Equatable { - enum VideoState: Equatable { - case notAvailable - case available(Bool) - case active - } - - case active(speakerMode: LegacyCallControllerButtonsSpeakerMode, videoState: VideoState) + case active(speakerMode: LegacyCallControllerButtonsSpeakerMode) case incoming } @@ -142,41 +136,27 @@ final class LegacyCallControllerButtonsNode: ASDisplayNode { for button in [self.muteButton, self.endButton, self.speakerButton, self.swichCameraButton] { button.alpha = 0.0 } - case let .active(speakerMode, videoState): + case let .active(speakerMode): for button in [self.muteButton] { if animated && button.alpha.isZero { button.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) } button.alpha = 1.0 } - switch videoState { - case .active, .available: - for button in [self.speakerButton] { - if animated && !button.alpha.isZero { - button.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) - } - button.alpha = 0.0 - } - for button in [self.swichCameraButton] { - if animated && button.alpha.isZero { - button.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - } - button.alpha = 1.0 - } - case .notAvailable: - for button in [self.swichCameraButton] { - if animated && !button.alpha.isZero { - button.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) - } - button.alpha = 0.0 - } - for button in [self.speakerButton] { - if animated && button.alpha.isZero { - button.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - } - button.alpha = 1.0 + + for button in [self.swichCameraButton] { + if animated && !button.alpha.isZero { + button.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) } + button.alpha = 0.0 } + for button in [self.speakerButton] { + if animated && button.alpha.isZero { + button.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + } + button.alpha = 1.0 + } + var animatingAcceptButton = false if self.endButton.alpha.isZero { if animated { diff --git a/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift b/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift index b2e640ddf0..37209ee233 100644 --- a/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift +++ b/submodules/TelegramCallsUI/Sources/LegacyCallControllerNode.swift @@ -229,10 +229,6 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol self?.acceptCall?() } - self.buttonsNode.toggleVideo = { [weak self] in - self?.toggleVideo?() - } - self.buttonsNode.rotateCamera = { [weak self] in self?.call.switchVideoCamera() } @@ -314,35 +310,11 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol } }) } - if !self.outgoingVideoViewRequested { - self.outgoingVideoViewRequested = true - self.call.makeOutgoingVideoView(completion: { [weak self] outgoingVideoView in - guard let strongSelf = self else { - return - } - if let outgoingVideoView = outgoingVideoView?.view { - outgoingVideoView.backgroundColor = .black - outgoingVideoView.clipsToBounds = true - strongSelf.setCurrentAudioOutput?(.speaker) - let outgoingVideoNode = OutgoingVideoNode(videoView: outgoingVideoView, switchCamera: { - guard let strongSelf = self else { - return - } - strongSelf.call.switchVideoCamera() - }) - strongSelf.outgoingVideoNode = outgoingVideoNode - if let incomingVideoNode = strongSelf.incomingVideoNode { - strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: incomingVideoNode) - } else { - strongSelf.containerNode.insertSubnode(outgoingVideoNode, aboveSubnode: strongSelf.dimNode) - } - if let (layout, navigationBarHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) - } - } - }) - } - case .activeOutgoing: + default: + break + } + switch callState.videoState { + case .active, .outgoingRequested: if !self.outgoingVideoViewRequested { self.outgoingVideoViewRequested = true self.call.makeOutgoingVideoView(completion: { [weak self] outgoingVideoView in @@ -527,18 +499,7 @@ final class LegacyCallControllerNode: ASDisplayNode, CallControllerNodeProtocol mode = .none } } - let mappedVideoState: LegacyCallControllerButtonsMode.VideoState - switch callState.videoState { - case .notAvailable: - mappedVideoState = .notAvailable - case .available: - mappedVideoState = .available(true) - case .active: - mappedVideoState = .active - case .activeOutgoing: - mappedVideoState = .active - } - self.buttonsNode.updateMode(.active(speakerMode: mode, videoState: mappedVideoState)) + self.buttonsNode.updateMode(.active(speakerMode: mode)) } } diff --git a/submodules/TelegramCallsUI/Sources/PresentationCall.swift b/submodules/TelegramCallsUI/Sources/PresentationCall.swift index f7c52fa11b..56cbeffbc3 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCall.swift @@ -168,6 +168,7 @@ public final class PresentationCallImpl: PresentationCall { public let peerId: PeerId public let isOutgoing: Bool public var isVideo: Bool + public var isVideoPossible: Bool public let peer: Peer? private let serializedData: String? @@ -236,7 +237,7 @@ public final class PresentationCallImpl: PresentationCall { private var videoCapturer: OngoingCallVideoCapturer? - init(account: Account, audioSession: ManagedAudioSession, callSessionManager: CallSessionManager, callKitIntegration: CallKitIntegration?, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), initialState: CallSession?, internalId: CallSessionInternalId, peerId: PeerId, isOutgoing: Bool, peer: Peer?, proxyServer: ProxyServerSettings?, auxiliaryServers: [CallAuxiliaryServer], currentNetworkType: NetworkType, updatedNetworkType: Signal, startWithVideo: Bool) { + init(account: Account, audioSession: ManagedAudioSession, callSessionManager: CallSessionManager, callKitIntegration: CallKitIntegration?, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, getDeviceAccessData: @escaping () -> (presentationData: PresentationData, present: (ViewController, Any?) -> Void, openSettings: () -> Void), initialState: CallSession?, internalId: CallSessionInternalId, peerId: PeerId, isOutgoing: Bool, peer: Peer?, proxyServer: ProxyServerSettings?, auxiliaryServers: [CallAuxiliaryServer], currentNetworkType: NetworkType, updatedNetworkType: Signal, startWithVideo: Bool, isVideoPossible: Bool) { self.account = account self.audioSession = audioSession self.callSessionManager = callSessionManager @@ -261,11 +262,12 @@ public final class PresentationCallImpl: PresentationCall { self.peerId = peerId self.isOutgoing = isOutgoing self.isVideo = initialState?.type == .video + self.isVideoPossible = isVideoPossible self.peer = peer self.isVideo = startWithVideo if self.isVideo { self.videoCapturer = OngoingCallVideoCapturer() - self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .activeOutgoing, remoteVideoState: .inactive)) + self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .outgoingRequested, remoteVideoState: .inactive)) } else { self.statePromise.set(PresentationCallState(state: isOutgoing ? .waiting : .ringing, videoState: .notAvailable, remoteVideoState: .inactive)) } @@ -436,12 +438,14 @@ public final class PresentationCallImpl: PresentationCall { switch callContextState.videoState { case .notAvailable: mappedVideoState = .notAvailable - case let .available(enabled): - mappedVideoState = .available(enabled) + case .possible: + mappedVideoState = .possible + case .outgoingRequested: + mappedVideoState = .outgoingRequested + case .incomingRequested: + mappedVideoState = .incomingRequested case .active: mappedVideoState = .active - case .activeOutgoing: - mappedVideoState = .activeOutgoing } switch callContextState.remoteVideoState { case .inactive: @@ -451,7 +455,9 @@ public final class PresentationCallImpl: PresentationCall { } } else { if self.isVideo { - mappedVideoState = .activeOutgoing + mappedVideoState = .outgoingRequested + } else if self.isVideoPossible { + mappedVideoState = .possible } else { mappedVideoState = .notAvailable } @@ -729,8 +735,12 @@ public final class PresentationCallImpl: PresentationCall { self.ongoingContext?.setIsMuted(self.isMutedValue) } - public func setEnableVideo(_ value: Bool) { - self.ongoingContext?.setEnableVideo(value) + public func requestVideo() { + if self.videoCapturer == nil { + let videoCapturer = OngoingCallVideoCapturer() + self.videoCapturer = videoCapturer + self.ongoingContext?.requestVideo(videoCapturer) + } } public func setOutgoingVideoIsPaused(_ isPaused: Bool) { diff --git a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift index c85864c2aa..ffa70806a0 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationCallManager.swift @@ -82,6 +82,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { private let accountManager: AccountManager private let audioSession: ManagedAudioSession private let callKitIntegration: CallKitIntegration? + private var isVideoPossible: Bool private var currentCallValue: PresentationCallImpl? private var currentCall: PresentationCallImpl? { @@ -124,6 +125,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { self.getDeviceAccessData = getDeviceAccessData self.accountManager = accountManager self.audioSession = audioSession + self.isVideoPossible = enableVideoCalls self.isMediaPlaying = isMediaPlaying self.resumeMediaPlayback = resumeMediaPlayback @@ -212,7 +214,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { startCallImpl = { [weak self] account, uuid, handle, isVideo in if let strongSelf = self, let userId = Int32(handle) { - return strongSelf.startCall(account: account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), isVideo: isVideo, internalId: uuid) + return strongSelf.startCall(account: account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), isVideo: isVideo, isVideoPossible: strongSelf.isVideoPossible, internalId: uuid) |> take(1) |> map { result -> Bool in return result @@ -292,7 +294,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue - let call = PresentationCallImpl(account: firstState.0, audioSession: strongSelf.audioSession, callSessionManager: firstState.0.callSessionManager, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings) : nil, serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: strongSelf.getDeviceAccessData, initialState: nil, internalId: firstState.2.id, peerId: firstState.2.peerId, isOutgoing: false, peer: firstState.1, proxyServer: strongSelf.proxyServer, auxiliaryServers: auxiliaryServers(appConfiguration: appConfiguration), currentNetworkType: firstState.4, updatedNetworkType: firstState.0.networkType, startWithVideo: firstState.2.isVideo) + let call = PresentationCallImpl(account: firstState.0, audioSession: strongSelf.audioSession, callSessionManager: firstState.0.callSessionManager, callKitIntegration: enableCallKit ? callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings) : nil, serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: strongSelf.getDeviceAccessData, initialState: nil, internalId: firstState.2.id, peerId: firstState.2.peerId, isOutgoing: false, peer: firstState.1, proxyServer: strongSelf.proxyServer, auxiliaryServers: auxiliaryServers(appConfiguration: appConfiguration), currentNetworkType: firstState.4, updatedNetworkType: firstState.0.networkType, startWithVideo: firstState.2.isVideo, isVideoPossible: strongSelf.isVideoPossible) strongSelf.updateCurrentCall(call) strongSelf.currentCallPromise.set(.single(call)) strongSelf.hasActiveCallsPromise.set(true) @@ -318,6 +320,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { } public func requestCall(account: Account, peerId: PeerId, isVideo: Bool, endCurrentIfAny: Bool) -> RequestCallResult { + let isVideoPossible = self.isVideoPossible if let call = self.currentCall, !endCurrentIfAny { return .alreadyInProgress(call.peerId) } @@ -382,7 +385,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { guard let strongSelf = self else { return } - let _ = strongSelf.startCall(account: account, peerId: peerId, isVideo: isVideo).start() + let _ = strongSelf.startCall(account: account, peerId: peerId, isVideo: isVideo, isVideoPossible: isVideoPossible).start() } if let currentCall = self.currentCall { self.startCallDisposable.set((currentCall.hangUp() @@ -396,7 +399,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { return .requested } - private func startCall(account: Account, peerId: PeerId, isVideo: Bool, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal { + private func startCall(account: Account, peerId: PeerId, isVideo: Bool, isVideoPossible: Bool, internalId: CallSessionInternalId = CallSessionInternalId()) -> Signal { let (presentationData, present, openSettings) = self.getDeviceAccessData() let accessEnabledSignal: Signal = Signal { subscriber in @@ -445,7 +448,7 @@ public final class PresentationCallManagerImpl: PresentationCallManager { let autodownloadSettings = sharedData.entries[SharedDataKeys.autodownloadSettings] as? AutodownloadSettings ?? .defaultSettings let appConfiguration = preferences.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? AppConfiguration.defaultValue - let call = PresentationCallImpl(account: account, audioSession: strongSelf.audioSession, callSessionManager: account.callSessionManager, callKitIntegration: callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings), serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: strongSelf.getDeviceAccessData, initialState: nil, internalId: internalId, peerId: peerId, isOutgoing: true, peer: nil, proxyServer: strongSelf.proxyServer, auxiliaryServers: auxiliaryServers(appConfiguration: appConfiguration), currentNetworkType: currentNetworkType, updatedNetworkType: account.networkType, startWithVideo: isVideo) + let call = PresentationCallImpl(account: account, audioSession: strongSelf.audioSession, callSessionManager: account.callSessionManager, callKitIntegration: callKitIntegrationIfEnabled(strongSelf.callKitIntegration, settings: strongSelf.callSettings), serializedData: configuration.serializedData, dataSaving: effectiveDataSaving(for: strongSelf.callSettings, autodownloadSettings: autodownloadSettings), derivedState: derivedState, getDeviceAccessData: strongSelf.getDeviceAccessData, initialState: nil, internalId: internalId, peerId: peerId, isOutgoing: true, peer: nil, proxyServer: strongSelf.proxyServer, auxiliaryServers: auxiliaryServers(appConfiguration: appConfiguration), currentNetworkType: currentNetworkType, updatedNetworkType: account.networkType, startWithVideo: isVideo, isVideoPossible: isVideoPossible) strongSelf.updateCurrentCall(call) strongSelf.currentCallPromise.set(.single(call)) strongSelf.hasActiveCallsPromise.set(true) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 83b6a76e08..344eb3e35c 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -68,12 +68,13 @@ private struct ChatControllerNodeDerivedLayoutState { private final class ChatEmbeddedTitleContentNode: ASDisplayNode { private let context: AccountContext private let backgroundNode: ASDisplayNode + private let statusBarBackgroundNode: ASDisplayNode private let videoNode: OverlayUniversalVideoNode private let disableInternalAnimationIn: Bool private let isUIHiddenUpdated: () -> Void private let unembedWhenPortrait: (OverlayMediaItemNode) -> Bool - private var validLayout: (CGSize, CGFloat, CGFloat)? + private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat)? private let dismissed: () -> Void private let interactiveExtensionUpdated: (ContainedViewLayoutTransition) -> Void @@ -83,6 +84,8 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode { private(set) var isUIHidden: Bool = false + var unembedOnLeave: Bool = true + init(context: AccountContext, videoNode: OverlayUniversalVideoNode, disableInternalAnimationIn: Bool, interactiveExtensionUpdated: @escaping (ContainedViewLayoutTransition) -> Void, dismissed: @escaping () -> Void, isUIHiddenUpdated: @escaping () -> Void, unembedWhenPortrait: @escaping (OverlayMediaItemNode) -> Bool) { self.dismissed = dismissed self.interactiveExtensionUpdated = interactiveExtensionUpdated @@ -95,6 +98,9 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode { self.backgroundNode = ASDisplayNode() self.backgroundNode.backgroundColor = .black + self.statusBarBackgroundNode = ASDisplayNode() + self.statusBarBackgroundNode.backgroundColor = .black + self.videoNode = videoNode super.init() @@ -102,6 +108,7 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode { self.clipsToBounds = true self.addSubnode(self.backgroundNode) + self.addSubnode(self.statusBarBackgroundNode) self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))) @@ -151,12 +158,13 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode { return self.videoNode.content.dimensions.aspectFilled(CGSize(width: width, height: 16.0)).height } - func updateLayout(size: CGSize, topInset: CGFloat, interactiveExtension: CGFloat, transition: ContainedViewLayoutTransition, transitionSurface: ASDisplayNode?, navigationBar: NavigationBar?) { + func updateLayout(size: CGSize, actualHeight: CGFloat, topInset: CGFloat, interactiveExtension: CGFloat, transition: ContainedViewLayoutTransition, transitionSurface: ASDisplayNode?, navigationBar: NavigationBar?) { let isFirstTime = self.validLayout == nil - self.validLayout = (size, topInset, interactiveExtension) + self.validLayout = (size, actualHeight, topInset, interactiveExtension) + let videoSize = CGSize(width: size.width, height: actualHeight) - let videoFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset + interactiveExtension), size: CGSize(width: size.width, height: size.height - topInset - interactiveExtension)) + let videoFrame = CGRect(origin: CGPoint(x: 0.0, y: topInset + interactiveExtension + floor((size.height - actualHeight) / 2.0)), size: CGSize(width: videoSize.width, height: videoSize.height - topInset - interactiveExtension)) if isFirstTime, let transitionSurface = transitionSurface { let sourceFrame = self.videoNode.view.convert(self.videoNode.bounds, to: transitionSurface.view) @@ -204,16 +212,16 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode { self.videoNode.updateLayout(targetFrame.size, transition: nodeTransition) self.videoNode.frame = targetFrame if self.disableInternalAnimationIn { - self.addSubnode(self.videoNode) + self.insertSubnode(self.videoNode, belowSubnode: self.statusBarBackgroundNode) } else { self.videoNode.layer.animateFrame(from: sourceFrame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak self] _ in guard let strongSelf = self else { return } navigationBarContainer?.removeFromSuperview() - strongSelf.addSubnode(strongSelf.videoNode) - if let (size, topInset, interactiveExtension) = strongSelf.validLayout { - strongSelf.updateLayout(size: size, topInset: topInset, interactiveExtension: interactiveExtension, transition: .immediate, transitionSurface: nil, navigationBar: nil) + strongSelf.insertSubnode(strongSelf.videoNode, belowSubnode: strongSelf.statusBarBackgroundNode) + if let (size, actualHeight, topInset, interactiveExtension) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, actualHeight: actualHeight, topInset: topInset, interactiveExtension: interactiveExtension, transition: .immediate, transitionSurface: nil, navigationBar: nil) } }) self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) @@ -228,6 +236,7 @@ private final class ChatEmbeddedTitleContentNode: ASDisplayNode { } } transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size)) + transition.updateFrame(node: self.statusBarBackgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: topInset))) if self.videoNode.supernode == self { self.videoNode.layer.transform = CATransform3DIdentity @@ -430,6 +439,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var hasEmbeddedTitleContent: Bool { return self.embeddedTitleContentNode != nil } + private var didProcessExperimentalEmbedUrl: String? init(context: AccountContext, chatLocation: ChatLocation, subject: ChatControllerSubject?, controllerInteraction: ChatControllerInteraction, chatPresentationInterfaceState: ChatPresentationInterfaceState, automaticMediaDownloadSettings: MediaAutoDownloadSettings, navigationBar: NavigationBar?, controller: ChatControllerImpl?) { self.context = context @@ -953,6 +963,64 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let statusBarHeight = layout.insets(options: [.statusBar]).top + func extractExperimentalPlaylistUrl(_ text: String) -> String? { + let prefix = "stream: " + if text.hasPrefix(prefix) { + if let url = URL(string: String(text[text.index(text.startIndex, offsetBy: prefix.count)...])), url.absoluteString.hasSuffix(".m3u8") { + return url.absoluteString + } else { + return nil + } + } else { + return nil + } + } + + if let pinnedMessage = self.chatPresentationInterfaceState.pinnedMessage, self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding, self.context.sharedContext.immediateExperimentalUISettings.playlistPlayback, self.embeddedTitleContentNode == nil, let url = extractExperimentalPlaylistUrl(pinnedMessage.text), self.didProcessExperimentalEmbedUrl != url { + self.didProcessExperimentalEmbedUrl = url + let context = self.context + let baseNavigationController = self.controller?.navigationController as? NavigationController + let mediaManager = self.context.sharedContext.mediaManager + var expandImpl: (() -> Void)? + let content = PlatformVideoContent(id: .instantPage(MediaId(namespace: 0, id: 0), MediaId(namespace: 0, id: 0)), content: .url(url), streamVideo: true, loopVideo: false) + let overlayNode = OverlayUniversalVideoNode(postbox: self.context.account.postbox, audioSession: self.context.sharedContext.mediaManager.audioSession, manager: self.context.sharedContext.mediaManager.universalVideoManager, content: content, expand: { + expandImpl?() + }, close: { [weak mediaManager] in + mediaManager?.setOverlayVideoNode(nil) + }) + self.embeddedTitleContentNode = ChatEmbeddedTitleContentNode(context: self.context, videoNode: overlayNode, disableInternalAnimationIn: true, interactiveExtensionUpdated: { [weak self] transition in + guard let strongSelf = self else { + return + } + strongSelf.requestLayout(transition) + }, dismissed: { [weak self] in + guard let strongSelf = self else { + return + } + if let embeddedTitleContentNode = strongSelf.embeddedTitleContentNode { + strongSelf.embeddedTitleContentNode = nil + strongSelf.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode + strongSelf.requestLayout(.animated(duration: 0.25, curve: .spring)) + strongSelf.updateHasEmbeddedTitleContent?() + } + }, isUIHiddenUpdated: { [weak self] in + self?.updateHasEmbeddedTitleContent?() + }, unembedWhenPortrait: { [weak self] itemNode in + guard let strongSelf = self, let itemNode = itemNode as? OverlayUniversalVideoNode else { + return false + } + strongSelf.unembedWhenPortrait(contentNode: itemNode) + return true + }) + self.embeddedTitleContentNode?.unembedOnLeave = false + self.updateHasEmbeddedTitleContent?() + overlayNode.controlPlay() + } + + if self.chatPresentationInterfaceState.pinnedMessage == nil { + self.didProcessExperimentalEmbedUrl = nil + } + if let embeddedTitleContentNode = self.embeddedTitleContentNode, embeddedTitleContentNode.supernode != nil { if layout.size.width > layout.size.height { self.embeddedTitleContentNode = nil @@ -963,7 +1031,15 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } if let embeddedTitleContentNode = self.embeddedTitleContentNode { - let embeddedSize = CGSize(width: layout.size.width, height: min(400.0, embeddedTitleContentNode.calculateHeight(width: layout.size.width)) + statusBarHeight + embeddedTitleContentNode.interactiveExtension) + let defaultEmbeddedSize = CGSize(width: layout.size.width, height: min(400.0, embeddedTitleContentNode.calculateHeight(width: layout.size.width)) + statusBarHeight + embeddedTitleContentNode.interactiveExtension) + + let embeddedSize: CGSize + if let inputHeight = layout.inputHeight, inputHeight > 100.0 { + embeddedSize = CGSize(width: defaultEmbeddedSize.width, height: floor(defaultEmbeddedSize.height * 0.6)) + } else { + embeddedSize = defaultEmbeddedSize + } + if embeddedTitleContentNode.supernode == nil { self.insertSubnode(embeddedTitleContentNode, aboveSubnode: self.navigationBarBackroundNode) @@ -980,10 +1056,10 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { embeddedTitleContentNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: previousTopInset)) transition.updateFrame(node: embeddedTitleContentNode, frame: CGRect(origin: CGPoint(), size: embeddedSize)) - embeddedTitleContentNode.updateLayout(size: embeddedSize, topInset: statusBarHeight, interactiveExtension: embeddedTitleContentNode.interactiveExtension, transition: .immediate, transitionSurface: self, navigationBar: self.navigationBar) + embeddedTitleContentNode.updateLayout(size: embeddedSize, actualHeight: defaultEmbeddedSize.height, topInset: statusBarHeight, interactiveExtension: embeddedTitleContentNode.interactiveExtension, transition: .immediate, transitionSurface: self, navigationBar: self.navigationBar) } else { transition.updateFrame(node: embeddedTitleContentNode, frame: CGRect(origin: CGPoint(), size: embeddedSize)) - embeddedTitleContentNode.updateLayout(size: embeddedSize, topInset: statusBarHeight, interactiveExtension: embeddedTitleContentNode.interactiveExtension, transition: transition, transitionSurface: self, navigationBar: self.navigationBar) + embeddedTitleContentNode.updateLayout(size: embeddedSize, actualHeight: defaultEmbeddedSize.height, topInset: statusBarHeight, interactiveExtension: embeddedTitleContentNode.interactiveExtension, transition: transition, transitionSurface: self, navigationBar: self.navigationBar) } insets.top += embeddedSize.height @@ -2817,7 +2893,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } func willNavigateAway() { - if let embeddedTitleContentNode = self.embeddedTitleContentNode { + if let embeddedTitleContentNode = self.embeddedTitleContentNode, embeddedTitleContentNode.unembedOnLeave { self.embeddedTitleContentNode = nil self.dismissedEmbeddedTitleContentNode = embeddedTitleContentNode embeddedTitleContentNode.expandIntoPiP() diff --git a/submodules/TelegramUniversalVideoContent/Sources/OverlayUniversalVideoNode.swift b/submodules/TelegramUniversalVideoContent/Sources/OverlayUniversalVideoNode.swift index ff10e06372..2068dadf88 100644 --- a/submodules/TelegramUniversalVideoContent/Sources/OverlayUniversalVideoNode.swift +++ b/submodules/TelegramUniversalVideoContent/Sources/OverlayUniversalVideoNode.swift @@ -164,4 +164,8 @@ public final class OverlayUniversalVideoNode: OverlayMediaItemNode { self.defaultExpand() } } + + public func controlPlay() { + self.videoNode.play() + } } diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 19dbd52b58..a447de7644 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -103,9 +103,10 @@ public struct OngoingCallContextState: Equatable { public enum VideoState: Equatable { case notAvailable - case available(Bool) + case possible + case outgoingRequested + case incomingRequested case active - case activeOutgoing } public enum RemoteVideoState: Equatable { @@ -244,7 +245,7 @@ private func ongoingDataSavingForTypeWebrtc(_ type: VoiceCallDataSaving) -> Ongo private protocol OngoingCallThreadLocalContextProtocol: class { func nativeSetNetworkType(_ type: NetworkType) func nativeSetIsMuted(_ value: Bool) - func nativeSetVideoEnabled(_ value: Bool) + func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void) func nativeDebugInfo() -> String func nativeVersion() -> String @@ -272,7 +273,7 @@ extension OngoingCallThreadLocalContext: OngoingCallThreadLocalContextProtocol { self.setIsMuted(value) } - func nativeSetVideoEnabled(_ value: Bool) { + func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) { } func nativeSwitchVideoCamera() { @@ -324,8 +325,8 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt self.setIsMuted(value) } - func nativeSetVideoEnabled(_ value: Bool) { - self.setVideoEnabled(value) + func nativeRequestVideo(_ capturer: OngoingCallVideoCapturer) { + self.requestVideo(capturer.impl) } func nativeDebugInfo() -> String { @@ -341,32 +342,6 @@ extension OngoingCallThreadLocalContextWebrtc: OngoingCallThreadLocalContextProt } } -/*extension OngoingCallThreadLocalContextWebrtcCustom: OngoingCallThreadLocalContextProtocol { - func nativeSetNetworkType(_ type: NetworkType) { - self.setNetworkType(ongoingNetworkTypeForTypeWebrtcCustom(type)) - } - - func nativeStop(_ completion: @escaping (String?, Int64, Int64, Int64, Int64) -> Void) { - self.stop(completion) - } - - func nativeSetIsMuted(_ value: Bool) { - self.setIsMuted(value) - } - - func nativeDebugInfo() -> String { - return self.debugInfo() ?? "" - } - - func nativeVersion() -> String { - return self.version() ?? "" - } - - func nativeGetDerivedState() -> Data { - return self.getDerivedState() - } -}*/ - private extension OngoingCallContextState.State { init(_ state: OngoingCallState) { switch state { @@ -401,23 +376,6 @@ private extension OngoingCallContextState.State { } } -/*private extension OngoingCallContextState { - init(_ state: OngoingCallStateWebrtcCustom) { - switch state { - case .initializing: - self = .initializing - case .connected: - self = .connected - case .failed: - self = .failed - case .reconnecting: - self = .reconnecting - default: - self = .failed - } - } -}*/ - public protocol OngoingCallContextPresentationCallVideoView: UIView { func setOnFirstFrameReceived(_ onFirstFrameReceived: (() -> Void)?) } @@ -485,7 +443,6 @@ public final class OngoingCallContext { public init(account: Account, callSessionManager: CallSessionManager, internalId: CallSessionInternalId, proxyServer: ProxyServerSettings?, auxiliaryServers: [AuxiliaryServer], initialNetworkType: NetworkType, updatedNetworkType: Signal, serializedData: String?, dataSaving: VoiceCallDataSaving, derivedState: VoipDerivedState, key: Data, isOutgoing: Bool, video: OngoingCallVideoCapturer?, connections: CallSessionConnectionSet, maxLayer: Int32, version: String, allowP2P: Bool, audioSessionActive: Signal, logName: String) { let _ = setupLogs OngoingCallThreadLocalContext.applyServerConfig(serializedData) - //OngoingCallThreadLocalContextWebrtc.applyServerConfig(serializedData) self.internalId = internalId self.account = account @@ -502,35 +459,7 @@ public final class OngoingCallContext { |> take(1) |> deliverOn(queue)).start(next: { [weak self] _ in if let strongSelf = self { - /*if version == OngoingCallThreadLocalContextWebrtcCustom.version() { - var voipProxyServer: VoipProxyServerWebrtcCustom? - if let proxyServer = proxyServer { - switch proxyServer.connection { - case let .socks5(username, password): - voipProxyServer = VoipProxyServerWebrtcCustom(host: proxyServer.host, port: proxyServer.port, username: username, password: password) - case .mtp: - break - } - } - let context = OngoingCallThreadLocalContextWebrtcCustom(queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtcCustom(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtcCustom(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, primaryConnection: callConnectionDescriptionWebrtcCustom(connections.primary), alternativeConnections: connections.alternatives.map(callConnectionDescriptionWebrtcCustom), maxLayer: maxLayer, allowP2P: allowP2P, logPath: logPath, sendSignalingData: { [weak callSessionManager] data in - callSessionManager?.sendSignalingData(internalId: internalId, data: data) - }) - - strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) - context.stateChanged = { state in - self?.contextState.set(.single(OngoingCallContextState(state))) - } - context.signalBarsChanged = { signalBars in - self?.receptionPromise.set(.single(signalBars)) - } - - strongSelf.networkTypeDisposable = (updatedNetworkType - |> deliverOn(queue)).start(next: { networkType in - self?.withContext { context in - context.nativeSetNetworkType(networkType) - } - }) - } else */if version == OngoingCallThreadLocalContextWebrtc.version() { + if version == OngoingCallThreadLocalContextWebrtc.version() { var voipProxyServer: VoipProxyServerWebrtc? if let proxyServer = proxyServer { switch proxyServer.connection { @@ -574,14 +503,16 @@ public final class OngoingCallContext { let mappedState = OngoingCallContextState.State(state) let mappedVideoState: OngoingCallContextState.VideoState switch videoState { - case .inactive: - mappedVideoState = .available(true) + case .possible: + mappedVideoState = .possible + case .incomingRequested: + mappedVideoState = .incomingRequested + case .outgoingRequested: + mappedVideoState = .outgoingRequested case .active: mappedVideoState = .active - case .activeOutgoing: - mappedVideoState = .activeOutgoing @unknown default: - mappedVideoState = .available(false) + mappedVideoState = .notAvailable } let mappedRemoteVideoState: OngoingCallContextState.RemoteVideoState switch remoteVideoState { @@ -709,9 +640,9 @@ public final class OngoingCallContext { } } - public func setEnableVideo(_ value: Bool) { + public func requestVideo(_ capturer: OngoingCallVideoCapturer) { self.withContext { context in - context.nativeSetVideoEnabled(value) + context.nativeRequestVideo(capturer) } } diff --git a/submodules/TgVoipWebrtc/Impl/Manager.cpp b/submodules/TgVoipWebrtc/Impl/Manager.cpp index 1e7cbe0a5f..a748596497 100644 --- a/submodules/TgVoipWebrtc/Impl/Manager.cpp +++ b/submodules/TgVoipWebrtc/Impl/Manager.cpp @@ -37,8 +37,7 @@ Manager::Manager( bool enableP2P, std::vector const &rtcServers, std::shared_ptr videoCapture, - std::function stateUpdated, - std::function videoStateUpdated, + std::function stateUpdated, std::function remoteVideoIsActiveUpdated, std::function &)> signalingDataEmitted ) : @@ -48,11 +47,14 @@ _enableP2P(enableP2P), _rtcServers(rtcServers), _videoCapture(videoCapture), _stateUpdated(stateUpdated), -_videoStateUpdated(videoStateUpdated), _remoteVideoIsActiveUpdated(remoteVideoIsActiveUpdated), _signalingDataEmitted(signalingDataEmitted), -_isVideoRequested(false) { +_state(TgVoipState::Reconnecting), +_videoState(VideoState::possible) { assert(_thread->IsCurrent()); + if (videoCapture != nullptr) { + _videoState = VideoState::outgoingRequested; + } } Manager::~Manager() { @@ -60,6 +62,9 @@ Manager::~Manager() { } void Manager::start() { + if (_videoCapture != nullptr) { + _videoState = VideoState::active; + } auto weakThis = std::weak_ptr(shared_from_this()); _networkManager.reset(new ThreadLocalObject(getNetworkThread(), [encryptionKey = _encryptionKey, enableP2P = _enableP2P, rtcServers = _rtcServers, thread = _thread, weakThis, signalingDataEmitted = _signalingDataEmitted]() { return new NetworkManager( @@ -76,10 +81,17 @@ void Manager::start() { TgVoipState mappedState; if (state.isReadyToSendData) { mappedState = TgVoipState::Estabilished; + if (!strongThis->_didConnectOnce) { + strongThis->_didConnectOnce = true; + if (strongThis->_videoState == VideoState::outgoingRequested) { + strongThis->_videoState = VideoState::active; + } + } } else { mappedState = TgVoipState::Reconnecting; } - strongThis->_stateUpdated(mappedState); + strongThis->_state = mappedState; + strongThis->_stateUpdated(mappedState, strongThis->_videoState); strongThis->_mediaManager->perform([state](MediaManager *mediaManager) { mediaManager->setIsConnected(state.isReadyToSendData); @@ -154,10 +166,10 @@ void Manager::receiveSignalingData(const std::vector &data) { } if (mode == 1) { - _mediaManager->perform([](MediaManager *mediaManager) { - mediaManager->setSendVideo(true); - }); - _videoStateUpdated(true); + if (_videoState == VideoState::possible) { + _videoState = VideoState::incomingRequested; + _stateUpdated(_state, _videoState); + } } else if (mode == 2) { } else if (mode == 3) { auto candidatesData = buffer.Slice(1, buffer.size() - 1); @@ -172,10 +184,10 @@ void Manager::receiveSignalingData(const std::vector &data) { } } -void Manager::setSendVideo(bool sendVideo) { - if (sendVideo) { - if (!_isVideoRequested) { - _isVideoRequested = true; +void Manager::requestVideo(std::shared_ptr videoCapture) { + if (videoCapture != nullptr) { + if (_videoState == VideoState::possible) { + _videoState = VideoState::outgoingRequested; rtc::CopyOnWriteBuffer buffer; uint8_t mode = 1; @@ -187,11 +199,11 @@ void Manager::setSendVideo(bool sendVideo) { _signalingDataEmitted(data); - _mediaManager->perform([](MediaManager *mediaManager) { + /*_mediaManager->perform([](MediaManager *mediaManager) { mediaManager->setSendVideo(true); - }); + });*/ - _videoStateUpdated(true); + _stateUpdated(_state, _videoState); } } } diff --git a/submodules/TgVoipWebrtc/Impl/Manager.h b/submodules/TgVoipWebrtc/Impl/Manager.h index ff113c4175..0428761eb6 100644 --- a/submodules/TgVoipWebrtc/Impl/Manager.h +++ b/submodules/TgVoipWebrtc/Impl/Manager.h @@ -12,6 +12,13 @@ namespace TGVOIP_NAMESPACE { class Manager : public std::enable_shared_from_this { public: + enum class VideoState { + possible, + outgoingRequested, + incomingRequested, + active + }; + static rtc::Thread *getMediaThread(); Manager( @@ -20,8 +27,7 @@ public: bool enableP2P, std::vector const &rtcServers, std::shared_ptr videoCapture, - std::function stateUpdated, - std::function videoStateUpdated, + std::function stateUpdated, std::function remoteVideoIsActiveUpdated, std::function &)> signalingDataEmitted ); @@ -29,7 +35,7 @@ public: void start(); void receiveSignalingData(const std::vector &data); - void setSendVideo(bool sendVideo); + void requestVideo(std::shared_ptr videoCapture); void setMuteOutgoingAudio(bool mute); void notifyIsLocalVideoActive(bool isActive); void setIncomingVideoOutput(std::shared_ptr> sink); @@ -40,13 +46,14 @@ private: bool _enableP2P; std::vector _rtcServers; std::shared_ptr _videoCapture; - std::function _stateUpdated; - std::function _videoStateUpdated; + std::function _stateUpdated; std::function _remoteVideoIsActiveUpdated; std::function &)> _signalingDataEmitted; std::unique_ptr> _networkManager; std::unique_ptr> _mediaManager; - bool _isVideoRequested; + TgVoipState _state; + VideoState _videoState; + bool _didConnectOnce; private: }; diff --git a/submodules/TgVoipWebrtc/Impl/TgVoip.h b/submodules/TgVoipWebrtc/Impl/TgVoip.h index ccd7a485d2..da59fb09c1 100644 --- a/submodules/TgVoipWebrtc/Impl/TgVoip.h +++ b/submodules/TgVoipWebrtc/Impl/TgVoip.h @@ -147,6 +147,13 @@ protected: TgVoip() = default; public: + enum class VideoState { + possible, + outgoingRequested, + incomingRequested, + active + }; + static void setLoggingFunction(std::function loggingFunction); static void setGlobalServerConfig(std::string const &serverConfig); static int getConnectionMaxLayer(); @@ -160,8 +167,7 @@ public: TgVoipNetworkType initialNetworkType, TgVoipEncryptionKey const &encryptionKey, std::shared_ptr videoCapture, - std::function stateUpdated, - std::function videoStateUpdated, + std::function stateUpdated, std::function remoteVideoIsActiveUpdated, std::function &)> signalingDataEmitted ); @@ -182,7 +188,7 @@ public: virtual TgVoipPersistentState getPersistentState() = 0; virtual void receiveSignalingData(const std::vector &data) = 0; - virtual void setSendVideo(bool sendVideo) = 0; + virtual void requestVideo(std::shared_ptr videoCapture) = 0; virtual TgVoipFinalState stop() = 0; }; diff --git a/submodules/TgVoipWebrtc/Impl/TgVoip.mm b/submodules/TgVoipWebrtc/Impl/TgVoip.mm index 3e1fd2bb15..75598ddef7 100644 --- a/submodules/TgVoipWebrtc/Impl/TgVoip.mm +++ b/submodules/TgVoipWebrtc/Impl/TgVoip.mm @@ -155,8 +155,7 @@ public: TgVoipEncryptionKey const &encryptionKey, std::shared_ptr videoCapture, TgVoipNetworkType initialNetworkType, - std::function stateUpdated, - std::function videoStateUpdated, + std::function stateUpdated, std::function remoteVideoIsActiveUpdated, std::function &)> signalingDataEmitted ) : @@ -171,18 +170,30 @@ public: bool enableP2P = config.enableP2P; - _manager.reset(new ThreadLocalObject(getManagerThread(), [encryptionKey = encryptionKey, enableP2P = enableP2P, stateUpdated, videoStateUpdated, remoteVideoIsActiveUpdated, signalingDataEmitted, rtcServers, videoCapture](){ + _manager.reset(new ThreadLocalObject(getManagerThread(), [encryptionKey = encryptionKey, enableP2P = enableP2P, stateUpdated, remoteVideoIsActiveUpdated, signalingDataEmitted, rtcServers, videoCapture](){ return new Manager( getManagerThread(), encryptionKey, enableP2P, rtcServers, videoCapture, - [stateUpdated](const TgVoipState &state) { - stateUpdated(state); - }, - [videoStateUpdated](bool isActive) { - videoStateUpdated(isActive); + [stateUpdated](const TgVoipState &state, Manager::VideoState videoState) { + TgVoip::VideoState mappedVideoState; + switch (videoState) { + case Manager::VideoState::possible: + mappedVideoState = TgVoip::VideoState::possible; + break; + case Manager::VideoState::outgoingRequested: + mappedVideoState = TgVoip::VideoState::outgoingRequested; + break; + case Manager::VideoState::incomingRequested: + mappedVideoState = TgVoip::VideoState::incomingRequested; + break; + case Manager::VideoState::active: + mappedVideoState = TgVoip::VideoState::active; + break; + } + stateUpdated(state, mappedVideoState); }, [remoteVideoIsActiveUpdated](bool isActive) { remoteVideoIsActiveUpdated(isActive); @@ -207,11 +218,11 @@ public: }); }; - void setSendVideo(bool sendVideo) override { - _manager->perform([sendVideo](Manager *manager) { - manager->setSendVideo(sendVideo); + virtual void requestVideo(std::shared_ptr videoCapture) override { + _manager->perform([videoCapture](Manager *manager) { + manager->requestVideo(videoCapture); }); - }; + } void setNetworkType(TgVoipNetworkType networkType) override { /*message::NetworkType mappedType; @@ -307,37 +318,9 @@ public: return finalState; } - /*void controllerStateCallback(Controller::State state) { - if (onStateUpdated_) { - TgVoipState mappedState; - switch (state) { - case Controller::State::WaitInit: - mappedState = TgVoipState::WaitInit; - break; - case Controller::State::WaitInitAck: - mappedState = TgVoipState::WaitInitAck; - break; - case Controller::State::Established: - mappedState = TgVoipState::Estabilished; - break; - case Controller::State::Failed: - mappedState = TgVoipState::Failed; - break; - case Controller::State::Reconnecting: - mappedState = TgVoipState::Reconnecting; - break; - default: - mappedState = TgVoipState::Estabilished; - break; - } - - onStateUpdated_(mappedState); - } - }*/ - private: std::unique_ptr> _manager; - std::function _stateUpdated; + std::function _stateUpdated; std::function &)> _signalingDataEmitted; LogSinkImpl _logSink; @@ -371,11 +354,11 @@ void TgVoip::setGlobalServerConfig(const std::string &serverConfig) { } int TgVoip::getConnectionMaxLayer() { - return 92; // TODO: retrieve from LayerBase + return 92; } std::string TgVoip::getVersion() { - return ""; // TODO: version not known while not released + return ""; } TgVoip *TgVoip::makeInstance( @@ -387,8 +370,7 @@ TgVoip *TgVoip::makeInstance( TgVoipNetworkType initialNetworkType, TgVoipEncryptionKey const &encryptionKey, std::shared_ptr videoCapture, - std::function stateUpdated, - std::function videoStateUpdated, + std::function stateUpdated, std::function remoteVideoIsActiveUpdated, std::function &)> signalingDataEmitted ) { @@ -402,7 +384,6 @@ TgVoip *TgVoip::makeInstance( videoCapture, initialNetworkType, stateUpdated, - videoStateUpdated, remoteVideoIsActiveUpdated, signalingDataEmitted ); diff --git a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h index f082bcce12..10339cbc4f 100644 --- a/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h +++ b/submodules/TgVoipWebrtc/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h @@ -24,8 +24,9 @@ typedef NS_ENUM(int32_t, OngoingCallStateWebrtc) { }; typedef NS_ENUM(int32_t, OngoingCallVideoStateWebrtc) { - OngoingCallVideoStateInactive, - OngoingCallVideoStateActiveOutgoing, + OngoingCallVideoStatePossible, + OngoingCallVideoStateOutgoingRequested, + OngoingCallVideoStateIncomingRequested, OngoingCallVideoStateActive }; @@ -115,9 +116,9 @@ typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtc) { - (NSData * _Nonnull)getDerivedState; - (void)setIsMuted:(bool)isMuted; -- (void)setVideoEnabled:(bool)videoEnabled; - (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType; - (void)makeIncomingVideoView:(void (^_Nonnull)(OngoingCallThreadLocalContextWebrtcVideoView * _Nullable))completion; +- (void)requestVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer; - (void)addSignalingData:(NSData * _Nonnull)data; @end diff --git a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm index f21d5ddd02..0d19965d1d 100644 --- a/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm +++ b/submodules/TgVoipWebrtc/Sources/OngoingCallThreadLocalContext.mm @@ -78,6 +78,7 @@ using namespace TGVOIP_NAMESPACE; OngoingCallStateWebrtc _state; OngoingCallVideoStateWebrtc _videoState; + bool _connectedOnce; OngoingCallRemoteVideoStateWebrtc _remoteVideoState; OngoingCallThreadLocalContextVideoCapturer *_videoCapturer; @@ -87,7 +88,7 @@ using namespace TGVOIP_NAMESPACE; void (^_sendSignalingData)(NSData *); } -- (void)controllerStateChanged:(TgVoipState)state; +- (void)controllerStateChanged:(TgVoipState)state videoState:(OngoingCallVideoStateWebrtc)videoState; - (void)signalBarsChanged:(int32_t)signalBars; @end @@ -192,10 +193,10 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; _sendSignalingData = [sendSignalingData copy]; _videoCapturer = videoCapturer; if (videoCapturer != nil) { - _videoState = OngoingCallVideoStateActiveOutgoing; + _videoState = OngoingCallVideoStateOutgoingRequested; _remoteVideoState = OngoingCallRemoteVideoStateActive; } else { - _videoState = OngoingCallVideoStateInactive; + _videoState = OngoingCallVideoStatePossible; _remoteVideoState = OngoingCallRemoteVideoStateInactive; } @@ -282,30 +283,27 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; callControllerNetworkTypeForType(networkType), encryptionKey, [_videoCapturer getInterface], - [weakSelf, queue](TgVoipState state) { + [weakSelf, queue](TgVoipState state, TgVoip::VideoState videoState) { [queue dispatch:^{ __strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf; if (strongSelf) { - [strongSelf controllerStateChanged:state]; - } - }]; - }, - [weakSelf, queue](bool isActive) { - [queue dispatch:^{ - __strong OngoingCallThreadLocalContextWebrtc *strongSelf = weakSelf; - if (strongSelf) { - OngoingCallVideoStateWebrtc videoState; - if (isActive) { - videoState = OngoingCallVideoStateActive; - } else { - videoState = OngoingCallVideoStateInactive; - } - if (strongSelf->_videoState != videoState) { - strongSelf->_videoState = videoState; - if (strongSelf->_stateChanged) { - strongSelf->_stateChanged(strongSelf->_state, strongSelf->_videoState, strongSelf->_remoteVideoState); - } + OngoingCallVideoStateWebrtc mappedVideoState; + switch (videoState) { + case TgVoip::VideoState::possible: + mappedVideoState = OngoingCallVideoStatePossible; + break; + case TgVoip::VideoState::outgoingRequested: + mappedVideoState = OngoingCallVideoStateOutgoingRequested; + break; + case TgVoip::VideoState::incomingRequested: + mappedVideoState = OngoingCallVideoStateIncomingRequested; + break; + case TgVoip::VideoState::active: + mappedVideoState = OngoingCallVideoStateActive; + break; } + + [strongSelf controllerStateChanged:state videoState:mappedVideoState]; } }]; }, @@ -402,7 +400,7 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } } -- (void)controllerStateChanged:(TgVoipState)state { +- (void)controllerStateChanged:(TgVoipState)state videoState:(OngoingCallVideoStateWebrtc)videoState { OngoingCallStateWebrtc callState = OngoingCallStateInitializing; switch (state) { case TgVoipState::Estabilished: @@ -418,15 +416,11 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; break; } - if (callState != _state) { + if (_state != callState || _videoState != videoState) { _state = callState; + _videoState = videoState; if (_stateChanged) { - if (_videoState == OngoingCallVideoStateActiveOutgoing) { - if (_state == OngoingCallStateConnected) { - _videoState = OngoingCallVideoStateActive; - } - } _stateChanged(_state, _videoState, _remoteVideoState); } } @@ -463,12 +457,6 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } } -- (void)setVideoEnabled:(bool)videoEnabled { - if (_tgVoip) { - _tgVoip->setSendVideo(videoEnabled); - } -} - - (void)setNetworkType:(OngoingCallNetworkTypeWebrtc)networkType { if (_networkType != networkType) { _networkType = networkType; @@ -496,6 +484,13 @@ static void (*InternalVoipLoggingFunction)(NSString *) = NULL; } } +- (void)requestVideo:(OngoingCallThreadLocalContextVideoCapturer * _Nullable)videoCapturer { + if (_tgVoip && _videoCapturer == nil) { + _videoCapturer = videoCapturer; + _tgVoip->requestVideo([_videoCapturer getInterface]); + } +} + @end @implementation OngoingCallThreadLocalContextWebrtcVideoView : UIView diff --git a/submodules/TgVoipWebrtcCustom/BUCK b/submodules/TgVoipWebrtcCustom/BUCK deleted file mode 100644 index 1f11dc6ede..0000000000 --- a/submodules/TgVoipWebrtcCustom/BUCK +++ /dev/null @@ -1,47 +0,0 @@ -load("//Config:buck_rule_macros.bzl", "static_library", "glob_map", "glob_sub_map", "merge_maps") - -static_library( - name = "TgVoipWebrtcCustom", - srcs = glob([ - "Sources/**/*.m", - "Sources/**/*.mm", - "Impl/*.cpp", - ]), - has_cpp = True, - headers = merge_maps([ - glob_sub_map("PublicHeaders/", [ - "PublicHeaders/**/*.h", - ]), - glob_sub_map("Impl/", [ - "Impl/*.h", - ]), - ]), - exported_headers = glob([ - "PublicHeaders/**/*.h", - ]), - compiler_flags = [ - "-Ithird-party/submodules/TgVoipWebrtcCustom/PublicHeaders", - "-Ithird-party/webrtc/webrtc-ios/src", - "-Ithird-party/webrtc/webrtc-ios/src/third_party/abseil-cpp", - "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc", - "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/base", - "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/components/renderer/metal", - "-DWEBRTC_IOS", - "-DWEBRTC_MAC", - "-DWEBRTC_POSIX", - "-std=c++14", - ], - deps = [ - "//third-party/webrtc:webrtc_lib", - ], - frameworks = [ - "$SDKROOT/System/Library/Frameworks/Foundation.framework", - "$SDKROOT/System/Library/Frameworks/UIKit.framework", - "$SDKROOT/System/Library/Frameworks/AudioToolbox.framework", - "$SDKROOT/System/Library/Frameworks/VideoToolbox.framework", - "$SDKROOT/System/Library/Frameworks/CoreTelephony.framework", - "$SDKROOT/System/Library/Frameworks/CoreMedia.framework", - "$SDKROOT/System/Library/Frameworks/AVFoundation.framework", - "$SDKROOT/System/Library/Frameworks/Metal.framework", - ], -) diff --git a/submodules/TgVoipWebrtcCustom/BUILD b/submodules/TgVoipWebrtcCustom/BUILD deleted file mode 100644 index 5bbfd2e05f..0000000000 --- a/submodules/TgVoipWebrtcCustom/BUILD +++ /dev/null @@ -1,47 +0,0 @@ - -objc_library( - name = "TgVoipWebrtcCustom", - enable_modules = True, - module_name = "TgVoipWebrtcCustom", - srcs = glob([ - "Sources/**/*.m", - "Sources/**/*.mm", - "Sources/**/*.h", - "Sources/**/*.cpp", - "Sources/**/*.h", - ]), - hdrs = glob([ - "PublicHeaders/**/*.h", - ]), - copts = [ - "-I{}/Impl".format(package_name()), - "-Ithird-party/webrtc/webrtc-ios/src", - "-Ithird-party/webrtc/webrtc-ios/src/third_party/abseil-cpp", - "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc", - "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/base", - "-Ithird-party/webrtc/webrtc-ios/src/sdk/objc/components/renderer/metal", - "-DWEBRTC_IOS", - "-DWEBRTC_MAC", - "-DWEBRTC_POSIX", - "-std=c++14", - ], - includes = [ - "PublicHeaders", - ], - deps = [ - "//third-party/webrtc:webrtc_lib", - "//submodules/MtProtoKit:MtProtoKit", - ], - sdk_frameworks = [ - "Foundation", - "UIKit", - "AudioToolbox", - "VideoToolbox", - "CoreTelephony", - "CoreMedia", - "AVFoundation", - ], - visibility = [ - "//visibility:public", - ], -) diff --git a/submodules/TgVoipWebrtcCustom/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h b/submodules/TgVoipWebrtcCustom/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h deleted file mode 100644 index 4f486fdc99..0000000000 --- a/submodules/TgVoipWebrtcCustom/PublicHeaders/TgVoip/OngoingCallThreadLocalContext.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef OngoingCallContext_h -#define OngoingCallContext_h - -#import -#import - -@interface OngoingCallConnectionDescriptionWebrtcCustom : NSObject - -@property (nonatomic, readonly) int64_t connectionId; -@property (nonatomic, strong, readonly) NSString * _Nonnull ip; -@property (nonatomic, strong, readonly) NSString * _Nonnull ipv6; -@property (nonatomic, readonly) int32_t port; -@property (nonatomic, strong, readonly) NSData * _Nonnull peerTag; - -- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag; - -@end - -typedef NS_ENUM(int32_t, OngoingCallStateWebrtcCustom) { - OngoingCallStateInitializing, - OngoingCallStateConnected, - OngoingCallStateFailed, - OngoingCallStateReconnecting -}; - -typedef NS_ENUM(int32_t, OngoingCallNetworkTypeWebrtcCustom) { - OngoingCallNetworkTypeWifi, - OngoingCallNetworkTypeCellularGprs, - OngoingCallNetworkTypeCellularEdge, - OngoingCallNetworkTypeCellular3g, - OngoingCallNetworkTypeCellularLte -}; - -typedef NS_ENUM(int32_t, OngoingCallDataSavingWebrtcCustom) { - OngoingCallDataSavingNever, - OngoingCallDataSavingCellular, - OngoingCallDataSavingAlways -}; - -@protocol OngoingCallThreadLocalContextQueueWebrtcCustom - -- (void)dispatch:(void (^ _Nonnull)())f; -- (void)dispatchAfter:(double)seconds block:(void (^ _Nonnull)())f; -- (bool)isCurrent; - -@end - -@interface VoipProxyServerWebrtcCustom : NSObject - -@property (nonatomic, strong, readonly) NSString * _Nonnull host; -@property (nonatomic, readonly) int32_t port; -@property (nonatomic, strong, readonly) NSString * _Nullable username; -@property (nonatomic, strong, readonly) NSString * _Nullable password; - -- (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password; - -@end - -@interface OngoingCallThreadLocalContextWebrtcCustom : NSObject - -+ (void)setupLoggingFunction:(void (* _Nullable)(NSString * _Nullable))loggingFunction; -+ (void)applyServerConfig:(NSString * _Nullable)data; -+ (int32_t)maxLayer; -+ (NSString * _Nonnull)version; - -@property (nonatomic, copy) void (^ _Nullable stateChanged)(OngoingCallStateWebrtcCustom); -@property (nonatomic, copy) void (^ _Nullable signalBarsChanged)(int32_t); - -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtcCustom * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtcCustom)networkType dataSaving:(OngoingCallDataSavingWebrtcCustom)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtcCustom * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData; -- (void)stop:(void (^_Nullable)(NSString * _Nullable debugLog, int64_t bytesSentWifi, int64_t bytesReceivedWifi, int64_t bytesSentMobile, int64_t bytesReceivedMobile))completion; - -- (bool)needRate; - -- (NSString * _Nullable)debugInfo; -- (NSString * _Nullable)version; -- (NSData * _Nonnull)getDerivedState; - - -- (void)receiveSignalingData:(NSData * _Nonnull)data; - -- (void)setIsMuted:(bool)isMuted; -- (void)setNetworkType:(OngoingCallNetworkTypeWebrtcCustom)networkType; -- (void)getRemoteCameraView:(void (^_Nonnull)(UIView * _Nullable))completion; - -@end - -#endif diff --git a/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm b/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm deleted file mode 100644 index 2bbe477718..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/OngoingCallThreadLocalContext.mm +++ /dev/null @@ -1,353 +0,0 @@ -#import - -#import - -#import "api/peerconnection/RTCPeerConnectionFactory.h" -#import "api/peerconnection/RTCSSLAdapter.h" -#import "api/peerconnection/RTCConfiguration.h" -#import "api/peerconnection/RTCIceServer.h" -#import "api/peerconnection/RTCPeerConnection.h" -#import "api/peerconnection/RTCMediaConstraints.h" -#import "api/peerconnection/RTCMediaStreamTrack.h" -#import "api/peerconnection/RTCAudioTrack.h" -#import "api/peerconnection/RTCVideoTrack.h" -#import "api/peerconnection/RTCRtpTransceiver.h" -#import "api/peerconnection/RTCSessionDescription.h" -#import "api/peerconnection/RTCIceCandidate.h" -#import "api/peerconnection/RTCMediaStream.h" -#import "components/video_codec/RTCDefaultVideoDecoderFactory.h" -#import "components/video_codec/RTCDefaultVideoEncoderFactory.h" -#import "components/audio/RTCAudioSession.h" -#import "base/RTCVideoCapturer.h" -#import "api/peerconnection/RTCVideoSource.h" -#import "components/capturer/RTCFileVideoCapturer.h" -#import "components/capturer/RTCCameraVideoCapturer.h" -#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, ...) { - va_list args; - va_start(args, format); - NSString *string = [[NSString alloc] initWithFormat:format arguments:args]; - va_end(args); - - if (InternalVoipLoggingFunction) { - InternalVoipLoggingFunction(string); - } -} - -@implementation OngoingCallConnectionDescriptionWebrtcCustom - -- (instancetype _Nonnull)initWithConnectionId:(int64_t)connectionId ip:(NSString * _Nonnull)ip ipv6:(NSString * _Nonnull)ipv6 port:(int32_t)port peerTag:(NSData * _Nonnull)peerTag { - self = [super init]; - if (self != nil) { - _connectionId = connectionId; - _ip = ip; - _ipv6 = ipv6; - _port = port; - _peerTag = peerTag; - } - return self; -} - -@end - -@interface OngoingCallThreadLocalContextWebrtcCustom () { - id _queue; - int32_t _contextId; - - bool _isOutgoing; - void (^_sendSignalingData)(NSData * _Nonnull); - - OngoingCallNetworkTypeWebrtcCustom _networkType; - NSTimeInterval _callReceiveTimeout; - NSTimeInterval _callRingTimeout; - NSTimeInterval _callConnectTimeout; - NSTimeInterval _callPacketTimeout; - - OngoingCallStateWebrtcCustom _state; - int32_t _signalBars; - - RtcConnection *_connection; - - bool _receivedRemoteDescription; -} - -@end - -@implementation VoipProxyServerWebrtcCustom - -- (instancetype _Nonnull)initWithHost:(NSString * _Nonnull)host port:(int32_t)port username:(NSString * _Nullable)username password:(NSString * _Nullable)password { - self = [super init]; - if (self != nil) { - _host = host; - _port = port; - _username = username; - _password = password; - } - return self; -} - -@end - -@implementation OngoingCallThreadLocalContextWebrtcCustom - -+ (NSString *)version { - return @"2.8.8"; -} - -+ (void)setupLoggingFunction:(void (*)(NSString *))loggingFunction { - InternalVoipLoggingFunction = loggingFunction; -} - -+ (void)applyServerConfig:(NSString * _Nullable)__unused data { - -} - -+ (int32_t)maxLayer { - return 80; -} - -- (instancetype _Nonnull)initWithQueue:(id _Nonnull)queue proxy:(VoipProxyServerWebrtcCustom * _Nullable)proxy networkType:(OngoingCallNetworkTypeWebrtcCustom)networkType dataSaving:(OngoingCallDataSavingWebrtcCustom)dataSaving derivedState:(NSData * _Nonnull)derivedState key:(NSData * _Nonnull)key isOutgoing:(bool)isOutgoing primaryConnection:(OngoingCallConnectionDescriptionWebrtcCustom * _Nonnull)primaryConnection alternativeConnections:(NSArray * _Nonnull)alternativeConnections maxLayer:(int32_t)maxLayer allowP2P:(BOOL)allowP2P logPath:(NSString * _Nonnull)logPath sendSignalingData:(void (^)(NSData * _Nonnull))sendSignalingData { - self = [super init]; - if (self != nil) { - _queue = queue; - assert([queue isCurrent]); - - _isOutgoing = isOutgoing; - _sendSignalingData = [sendSignalingData copy]; - - _callReceiveTimeout = 20.0; - _callRingTimeout = 90.0; - _callConnectTimeout = 30.0; - _callPacketTimeout = 10.0; - _networkType = networkType; - - _state = OngoingCallStateInitializing; - _signalBars = -1; - - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - RTCInitializeSSL(); - }); - - [RTCAudioSession sharedInstance].useManualAudio = true; - [RTCAudioSession sharedInstance].isAudioEnabled = true; - - __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; - - _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); - } - }]; - }]; - - //RTCMediaConstraints *constraints = [[RTCMediaConstraints alloc] initWithMandatoryConstraints:nil optionalConstraints:@{ @"DtlsSrtpKeyAgreement": kRTCMediaConstraintsValueTrue }]; - - /*RTCVideoSource *videoSource = [_peerConnectionFactory videoSource]; - - #if TARGET_OS_SIMULATOR - _videoCapturer = [[RTCFileVideoCapturer alloc] initWithDelegate:videoSource]; - #else - _videoCapturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:videoSource]; - #endif - - _localVideoTrack = [_peerConnectionFactory videoTrackWithSource:videoSource trackId:@"video0"]; - [_peerConnection addTrack:_localVideoTrack streamIds:@[streamId]];*/ - - if (isOutgoing) { - id queue = _queue; - - [_connection getOffer:^(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 tryAdvertising:sdp type:type]; - }]; - }]; - }]; - }]; - } - } - return self; -} - -- (void)dealloc { - assert([_queue isCurrent]); -} - -- (void)tryAdvertising:(NSString *)sdp type:(NSString *)type { - if (_receivedRemoteDescription) { - return; - } - - [self sendSdp:sdp type:type]; - __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; - [_queue dispatchAfter:1.0 block:^{ - __strong OngoingCallThreadLocalContextWebrtcCustom *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - [strongSelf tryAdvertising:sdp type:type]; - }]; -} - -- (bool)needRate { - return false; -} - -- (void)stop:(void (^)(NSString *, int64_t, int64_t, int64_t, int64_t))completion { - [_connection close]; - if (completion) { - completion(@"", 0, 0, 0, 0); - } -} - -- (NSString *)debugInfo { - NSString *version = [self version]; - return [NSString stringWithFormat:@"WebRTC, Version: %@", version]; -} - -- (NSString *)version { - return [OngoingCallThreadLocalContextWebrtcCustom version]; -} - -- (NSData * _Nonnull)getDerivedState { - return [NSData data]; -} - -- (void)sendSdp:(NSString *)sdp type:(NSString *)type { - NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; - json[@"messageType"] = @"sessionDescription"; - json[@"sdp"] = sdp; - json[@"type"] = type; - NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; - if (data != nil) { - _sendSignalingData(data); - } -} - -- (void)sendCandidateWithSdp:(NSString *)sdp mLineIndex:(int)mLineIndex sdpMid:(NSString *)sdpMid { - NSMutableDictionary *json = [[NSMutableDictionary alloc] init]; - json[@"messageType"] = @"iceCandidate"; - json[@"sdp"] = sdp; - json[@"mLineIndex"] = @(mLineIndex); - if (sdpMid != nil) { - json[@"sdpMid"] = sdpMid; - } - NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; - if (data != nil) { - _sendSignalingData(data); - } -} - -- (void)receiveSignalingData:(NSData *)data { - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; - if (![json isKindOfClass:[NSDictionary class]]) { - return; - } - NSString *messageType = json[@"messageType"]; - if (![messageType isKindOfClass:[NSString class]]) { - return; - } - - if ([messageType isEqualToString:@"sessionDescription"]) { - NSString *sdp = json[@"sdp"]; - if (![sdp isKindOfClass:[NSString class]]) { - return; - } - - NSString *typeString = json[@"type"]; - if (![typeString isKindOfClass:[NSString class]]) { - return; - } - - if (_receivedRemoteDescription) { - return; - } - _receivedRemoteDescription = true; - - [_connection setRemoteDescription:sdp type:typeString completion:^{ - }]; - - if (!_isOutgoing) { - __weak OngoingCallThreadLocalContextWebrtcCustom *weakSelf = self; - 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]; - }]; - }]; - }]; - }]; - } - } else if ([messageType isEqualToString:@"iceCandidate"]) { - NSString *sdp = json[@"sdp"]; - if (![sdp isKindOfClass:[NSString class]]) { - return; - } - - NSNumber *mLineIndex = json[@"mLineIndex"]; - if (![mLineIndex isKindOfClass:[NSNumber class]]) { - return; - } - - NSString *sdpMidString = json[@"sdpMid"]; - NSString *sdpMid = nil; - if ([sdpMidString isKindOfClass:[NSString class]]) { - sdpMid = sdpMidString; - } - - [_connection addIceCandidateWithSdp:sdp sdpMLineIndex:[mLineIndex intValue] sdpMid:sdpMid]; - } -} - -- (void)setIsMuted:(bool)isMuted { - [_connection setIsMuted:isMuted]; -} - -- (void)setNetworkType:(OngoingCallNetworkTypeWebrtcCustom)networkType { -} - -- (void)getRemoteCameraView:(void (^_Nonnull)(UIView * _Nullable))completion { - [_connection getRemoteCameraView:completion]; -} - -@end diff --git a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.h b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.h deleted file mode 100644 index 166f434242..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef RTCCONNECTION_H -#define RTCCONNECTION_H - -#import -#import - -@interface RtcConnection : NSObject - -- (instancetype _Nonnull)initWithDiscoveredIceCandidate:(void (^_Nonnull)(NSString *, int, NSString * _Nonnull))discoveredIceCandidate connectionStateChanged:(void (^_Nonnull)(bool))connectionStateChanged; - -- (void)close; - -- (void)setIsMuted:(bool)isMuted; - -- (void)getOffer:(void (^_Nonnull)(NSString * _Nonnull, NSString * _Nonnull))completion; -- (void)getAnswer:(void (^_Nonnull)(NSString * _Nonnull, NSString * _Nonnull))completion; -- (void)setLocalDescription:(NSString * _Nonnull)serializedDescription type:(NSString * _Nonnull)type completion:(void (^_Nonnull)())completion; -- (void)setRemoteDescription:(NSString * _Nonnull)serializedDescription type:(NSString * _Nonnull)type completion:(void (^_Nonnull)())completion; -- (void)addIceCandidateWithSdp:(NSString * _Nonnull)sdp sdpMLineIndex:(int)sdpMLineIndex sdpMid:(NSString * _Nullable)sdpMid; - -- (void)getRemoteCameraView:(void (^_Nonnull)(UIView * _Nullable))completion; - -@end - -#endif diff --git a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm b/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm deleted file mode 100644 index 4e72b848d9..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/RtcConnection.mm +++ /dev/null @@ -1,438 +0,0 @@ -#import "RtcConnection.h" - -#import - -#include -#include "api/scoped_refptr.h" -#include "api/proxy.h" -#include "api/peer_connection_factory_proxy.h" -#include "rtc_base/thread.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" -#include "sdk/objc/native/src/objc_video_track_source.h" -#include "api/video_track_source_proxy.h" -#include "sdk/objc/api/RTCVideoRendererAdapter.h" -#include "sdk/objc/native/api/video_frame.h" - -#include "tg_peer_connection.h" -#include "tg_peer_connection_factory.h" - -#include "VideoCameraCapturer.h" - -#import "VideoMetalView.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; - - rtc::scoped_refptr _localAudioTrack; - - rtc::scoped_refptr _nativeVideoSource; - rtc::scoped_refptr _localVideoTrack; - VideoCameraCapturer *_videoCapturer; - - rtc::scoped_refptr _remoteVideoTrack; -} - -@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; - - rtc::scoped_refptr pc_factory( - new rtc::RefCountedObject( - std::move(dependencies))); - // Call Initialize synchronously but make sure it is executed on - // |signaling_thread|. - webrtc::MethodCall call(pc_factory.get(), &webrtc::TgPeerConnectionFactory::Initialize); - result = call.Marshal(RTC_FROM_HERE, pc_factory->signaling_thread()); - - if (!result) { - return nil; - } - _nativeFactory = webrtc::TgPeerConnectionFactoryProxy::Create(pc_factory->signaling_thread(), pc_factory); - - 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"; - - /*iceServer.uri = "stun:rrrtest.uksouth.cloudapp.azure.com:3478"; - iceServer.username = "user"; - iceServer.password = "root";*/ - - config.servers.push_back(iceServer); - - /*webrtc::PeerConnectionInterface::IceServer turnServer; - turnServer.uri = "turn:rrrtest.uksouth.cloudapp.azure.com:3478"; - turnServer.username = "user"; - turnServer.password = "root"; - - config.servers.push_back(turnServer);*/ - - //config.type = webrtc::PeerConnectionInterface::kRelay; - - _observer.reset(new PeerConnectionObserverImpl(_discoveredIceCandidate, _connectionStateChanged)); - - _peerConnection = _nativeFactory->CreatePeerConnection(config, nullptr, nullptr, _observer.get()); - assert(_peerConnection != nullptr); - - std::vector streamIds; - streamIds.push_back("stream"); - - cricket::AudioOptions options; - rtc::scoped_refptr audioSource = _nativeFactory->CreateAudioSource(options); - _localAudioTrack = _nativeFactory->CreateAudioTrack("audio0", audioSource); - _peerConnection->AddTrack(_localAudioTrack, streamIds); - - rtc::scoped_refptr objCVideoTrackSource(new rtc::RefCountedObject()); - _nativeVideoSource = webrtc::VideoTrackSourceProxy::Create(_signalingThread.get(), _workerThread.get(), objCVideoTrackSource); - - _localVideoTrack = _nativeFactory->CreateVideoTrack("video0", _nativeVideoSource); - _peerConnection->AddTrack(_localVideoTrack, streamIds); - - [self startLocalVideo]; - } - return self; -} - -- (void)close { - if (_videoCapturer != nil) { - [_videoCapturer stopCapture]; - } - - _peerConnection->Close(); -} - -- (void)startLocalVideo { -#if TARGET_OS_SIMULATOR - return; -#endif - _videoCapturer = [[VideoCameraCapturer alloc] initWithSource:_nativeVideoSource]; - - AVCaptureDevice *frontCamera = nil; - for (AVCaptureDevice *device in [VideoCameraCapturer captureDevices]) { - if (device.position == AVCaptureDevicePositionFront) { - frontCamera = device; - break; - } - } - - if (frontCamera == nil) { - return; - } - - NSArray *sortedFormats = [[VideoCameraCapturer supportedFormatsForDevice:frontCamera] sortedArrayUsingComparator:^NSComparisonResult(AVCaptureDeviceFormat* lhs, AVCaptureDeviceFormat *rhs) { - int32_t width1 = CMVideoFormatDescriptionGetDimensions(lhs.formatDescription).width; - int32_t width2 = CMVideoFormatDescriptionGetDimensions(rhs.formatDescription).width; - return width1 < width2 ? NSOrderedAscending : NSOrderedDescending; - }]; - - AVCaptureDeviceFormat *bestFormat = nil; - for (AVCaptureDeviceFormat *format in sortedFormats) { - CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription); - if (dimensions.width >= 1000 || dimensions.height >= 1000) { - bestFormat = format; - break; - } - } - - if (bestFormat == nil) { - return; - } - - AVFrameRateRange *frameRateRange = [[bestFormat.videoSupportedFrameRateRanges sortedArrayUsingComparator:^NSComparisonResult(AVFrameRateRange *lhs, AVFrameRateRange *rhs) { - if (lhs.maxFrameRate < rhs.maxFrameRate) { - return NSOrderedAscending; - } else { - return NSOrderedDescending; - } - }] lastObject]; - - if (frameRateRange == nil) { - return; - } - - [_videoCapturer startCaptureWithDevice:frontCamera format:bestFormat fps:27]; -} - -- (void)setIsMuted:(bool)isMuted { - _localAudioTrack->set_enabled(!isMuted); -} - -- (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) { - }); - } -} - -- (void)getRemoteCameraView:(void (^_Nonnull)(UIView * _Nullable))completion { - if (_remoteVideoTrack == nullptr) { - for (auto &it : _peerConnection->GetTransceivers()) { - if (it->media_type() == cricket::MediaType::MEDIA_TYPE_VIDEO) { - _remoteVideoTrack = static_cast(it->receiver()->track().get()); - break; - } - } - } - - rtc::scoped_refptr remoteVideoTrack = _remoteVideoTrack; - - dispatch_async(dispatch_get_main_queue(), ^{ - if (remoteVideoTrack != nullptr) { - VideoMetalView *remoteRenderer = [[VideoMetalView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 240.0f)]; - remoteRenderer.videoContentMode = UIViewContentModeScaleAspectFill; - [remoteRenderer addToTrack:remoteVideoTrack]; - - completion(remoteRenderer); - } - }); -} - -@end diff --git a/submodules/TgVoipWebrtcCustom/Sources/VideoCameraCapturer.h b/submodules/TgVoipWebrtcCustom/Sources/VideoCameraCapturer.h deleted file mode 100644 index a19023032d..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/VideoCameraCapturer.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef VIDEOCAMERACAPTURER_H -#define VIDEOCAMERACAPTURER_H - -#import -#import - -#include -#include "api/scoped_refptr.h" -#include "api/media_stream_interface.h" - -@interface VideoCameraCapturer : NSObject - -+ (NSArray *)captureDevices; -+ (NSArray *)supportedFormatsForDevice:(AVCaptureDevice *)device; - -- (instancetype)initWithSource:(rtc::scoped_refptr)source; - -- (void)startCaptureWithDevice:(AVCaptureDevice *)device format:(AVCaptureDeviceFormat *)format fps:(NSInteger)fps; -- (void)stopCapture; - -@end - -#endif diff --git a/submodules/TgVoipWebrtcCustom/Sources/VideoCameraCapturer.mm b/submodules/TgVoipWebrtcCustom/Sources/VideoCameraCapturer.mm deleted file mode 100644 index ec2ed05650..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/VideoCameraCapturer.mm +++ /dev/null @@ -1,459 +0,0 @@ -#include "VideoCameraCapturer.h" - -#import - -#import "base/RTCLogging.h" -#import "base/RTCVideoFrameBuffer.h" -#import "components/video_frame_buffer/RTCCVPixelBuffer.h" -#import "sdk/objc/native/src/objc_video_track_source.h" -#import "api/video_track_source_proxy.h" - -#import "helpers/UIDevice+RTCDevice.h" - -#import "helpers/AVCaptureSession+DevicePosition.h" -#import "helpers/RTCDispatcher+Private.h" -#import "base/RTCVideoFrame.h" - -static const int64_t kNanosecondsPerSecond = 1000000000; - -static webrtc::ObjCVideoTrackSource *getObjCVideoSource(const rtc::scoped_refptr nativeSource) { - webrtc::VideoTrackSourceProxy *proxy_source = - static_cast(nativeSource.get()); - return static_cast(proxy_source->internal()); -} - -@interface VideoCameraCapturer () { - rtc::scoped_refptr _source; - - dispatch_queue_t _frameQueue; - AVCaptureDevice *_currentDevice; - BOOL _hasRetriedOnFatalError; - BOOL _isRunning; - BOOL _willBeRunning; - - AVCaptureVideoDataOutput *_videoDataOutput; - AVCaptureSession *_captureSession; - FourCharCode _preferredOutputPixelFormat; - FourCharCode _outputPixelFormat; - RTCVideoRotation _rotation; - UIDeviceOrientation _orientation; -} - -@end - -@implementation VideoCameraCapturer - -- (instancetype)initWithSource:(rtc::scoped_refptr)source { - self = [super init]; - if (self != nil) { - _source = source; - if (![self setupCaptureSession:[[AVCaptureSession alloc] init]]) { - return nil; - } - - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - _orientation = UIDeviceOrientationPortrait; - _rotation = RTCVideoRotation_90; - [center addObserver:self - selector:@selector(deviceOrientationDidChange:) - name:UIDeviceOrientationDidChangeNotification - object:nil]; - [center addObserver:self - selector:@selector(handleCaptureSessionInterruption:) - name:AVCaptureSessionWasInterruptedNotification - object:_captureSession]; - [center addObserver:self - selector:@selector(handleCaptureSessionInterruptionEnded:) - name:AVCaptureSessionInterruptionEndedNotification - object:_captureSession]; - [center addObserver:self - selector:@selector(handleApplicationDidBecomeActive:) - name:UIApplicationDidBecomeActiveNotification - object:[UIApplication sharedApplication]]; - [center addObserver:self - selector:@selector(handleCaptureSessionRuntimeError:) - name:AVCaptureSessionRuntimeErrorNotification - object:_captureSession]; - [center addObserver:self - selector:@selector(handleCaptureSessionDidStartRunning:) - name:AVCaptureSessionDidStartRunningNotification - object:_captureSession]; - [center addObserver:self - selector:@selector(handleCaptureSessionDidStopRunning:) - name:AVCaptureSessionDidStopRunningNotification - object:_captureSession]; - } - return self; -} - -- (void)dealloc { - NSAssert(!_willBeRunning, @"Session was still running in RTCCameraVideoCapturer dealloc. Forgot to call stopCapture?"); - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -+ (NSArray *)captureDevices { - AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession - discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInWideAngleCamera ] - mediaType:AVMediaTypeVideo - position:AVCaptureDevicePositionUnspecified]; - return session.devices; -} - -+ (NSArray *)supportedFormatsForDevice:(AVCaptureDevice *)device { - // Support opening the device in any format. We make sure it's converted to a format we - // can handle, if needed, in the method `-setupVideoDataOutput`. - return device.formats; -} - -- (FourCharCode)preferredOutputPixelFormat { - return _preferredOutputPixelFormat; -} - -- (void)startCaptureWithDevice:(AVCaptureDevice *)device - format:(AVCaptureDeviceFormat *)format - fps:(NSInteger)fps { - [self startCaptureWithDevice:device format:format fps:fps completionHandler:nil]; -} - -- (void)stopCapture { - [self stopCaptureWithCompletionHandler:nil]; -} - -- (void)startCaptureWithDevice:(AVCaptureDevice *)device - format:(AVCaptureDeviceFormat *)format - fps:(NSInteger)fps - completionHandler:(nullable void (^)(NSError *))completionHandler { - _willBeRunning = YES; - [RTCDispatcher - dispatchAsyncOnType:RTCDispatcherTypeCaptureSession - block:^{ - RTCLogInfo("startCaptureWithDevice %@ @ %ld fps", format, (long)fps); - - dispatch_async(dispatch_get_main_queue(), ^{ - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; - }); - - _currentDevice = device; - - NSError *error = nil; - if (![_currentDevice lockForConfiguration:&error]) { - RTCLogError(@"Failed to lock device %@. Error: %@", - _currentDevice, - error.userInfo); - if (completionHandler) { - completionHandler(error); - } - _willBeRunning = NO; - return; - } - [self reconfigureCaptureSessionInput]; - [self updateOrientation]; - [self updateDeviceCaptureFormat:format fps:fps]; - [self updateVideoDataOutputPixelFormat:format]; - [_captureSession startRunning]; - [_currentDevice unlockForConfiguration]; - _isRunning = YES; - if (completionHandler) { - completionHandler(nil); - } - }]; -} - -- (void)stopCaptureWithCompletionHandler:(nullable void (^)(void))completionHandler { - _willBeRunning = NO; - [RTCDispatcher - dispatchAsyncOnType:RTCDispatcherTypeCaptureSession - block:^{ - RTCLogInfo("Stop"); - _currentDevice = nil; - for (AVCaptureDeviceInput *oldInput in [_captureSession.inputs copy]) { - [_captureSession removeInput:oldInput]; - } - [_captureSession stopRunning]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; - }); - _isRunning = NO; - if (completionHandler) { - completionHandler(); - } - }]; -} - -#pragma mark iOS notifications - -#if TARGET_OS_IPHONE -- (void)deviceOrientationDidChange:(NSNotification *)notification { - [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession - block:^{ - [self updateOrientation]; - }]; -} -#endif - -#pragma mark AVCaptureVideoDataOutputSampleBufferDelegate - -- (void)captureOutput:(AVCaptureOutput *)captureOutput - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *)connection { - NSParameterAssert(captureOutput == _videoDataOutput); - - if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || !CMSampleBufferIsValid(sampleBuffer) || - !CMSampleBufferDataIsReady(sampleBuffer)) { - return; - } - - CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - if (pixelBuffer == nil) { - return; - } - - // Default to portrait orientation on iPhone. - BOOL usingFrontCamera = NO; - // Check the image's EXIF for the camera the image came from as the image could have been - // delayed as we set alwaysDiscardsLateVideoFrames to NO. - AVCaptureDevicePosition cameraPosition = - [AVCaptureSession devicePositionForSampleBuffer:sampleBuffer]; - if (cameraPosition != AVCaptureDevicePositionUnspecified) { - usingFrontCamera = AVCaptureDevicePositionFront == cameraPosition; - } else { - AVCaptureDeviceInput *deviceInput = - (AVCaptureDeviceInput *)((AVCaptureInputPort *)connection.inputPorts.firstObject).input; - usingFrontCamera = AVCaptureDevicePositionFront == deviceInput.device.position; - } - switch (_orientation) { - case UIDeviceOrientationPortrait: - _rotation = RTCVideoRotation_90; - break; - case UIDeviceOrientationPortraitUpsideDown: - _rotation = RTCVideoRotation_270; - break; - case UIDeviceOrientationLandscapeLeft: - _rotation = usingFrontCamera ? RTCVideoRotation_180 : RTCVideoRotation_0; - break; - case UIDeviceOrientationLandscapeRight: - _rotation = usingFrontCamera ? RTCVideoRotation_0 : RTCVideoRotation_180; - break; - case UIDeviceOrientationFaceUp: - case UIDeviceOrientationFaceDown: - case UIDeviceOrientationUnknown: - // Ignore. - break; - } - - RTCCVPixelBuffer *rtcPixelBuffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:pixelBuffer]; - int64_t timeStampNs = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) * - kNanosecondsPerSecond; - RTCVideoFrame *videoFrame = [[RTCVideoFrame alloc] initWithBuffer:rtcPixelBuffer - rotation:_rotation - timeStampNs:timeStampNs]; - getObjCVideoSource(_source)->OnCapturedFrame(videoFrame); -} - -- (void)captureOutput:(AVCaptureOutput *)captureOutput - didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *)connection { - NSString *droppedReason = - (__bridge NSString *)CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_DroppedFrameReason, nil); - RTCLogError(@"Dropped sample buffer. Reason: %@", droppedReason); -} - -#pragma mark - AVCaptureSession notifications - -- (void)handleCaptureSessionInterruption:(NSNotification *)notification { - NSString *reasonString = nil; - NSNumber *reason = notification.userInfo[AVCaptureSessionInterruptionReasonKey]; - if (reason) { - switch (reason.intValue) { - case AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableInBackground: - reasonString = @"VideoDeviceNotAvailableInBackground"; - break; - case AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient: - reasonString = @"AudioDeviceInUseByAnotherClient"; - break; - case AVCaptureSessionInterruptionReasonVideoDeviceInUseByAnotherClient: - reasonString = @"VideoDeviceInUseByAnotherClient"; - break; - case AVCaptureSessionInterruptionReasonVideoDeviceNotAvailableWithMultipleForegroundApps: - reasonString = @"VideoDeviceNotAvailableWithMultipleForegroundApps"; - break; - } - } - RTCLog(@"Capture session interrupted: %@", reasonString); -} - -- (void)handleCaptureSessionInterruptionEnded:(NSNotification *)notification { - RTCLog(@"Capture session interruption ended."); -} - -- (void)handleCaptureSessionRuntimeError:(NSNotification *)notification { - NSError *error = [notification.userInfo objectForKey:AVCaptureSessionErrorKey]; - RTCLogError(@"Capture session runtime error: %@", error); - - [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession - block:^{ - if (error.code == AVErrorMediaServicesWereReset) { - [self handleNonFatalError]; - } else { - [self handleFatalError]; - } - }]; -} - -- (void)handleCaptureSessionDidStartRunning:(NSNotification *)notification { - RTCLog(@"Capture session started."); - - [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession - block:^{ - // If we successfully restarted after an unknown error, - // allow future retries on fatal errors. - _hasRetriedOnFatalError = NO; - }]; -} - -- (void)handleCaptureSessionDidStopRunning:(NSNotification *)notification { - RTCLog(@"Capture session stopped."); -} - -- (void)handleFatalError { - [RTCDispatcher - dispatchAsyncOnType:RTCDispatcherTypeCaptureSession - block:^{ - if (!_hasRetriedOnFatalError) { - RTCLogWarning(@"Attempting to recover from fatal capture error."); - [self handleNonFatalError]; - _hasRetriedOnFatalError = YES; - } else { - RTCLogError(@"Previous fatal error recovery failed."); - } - }]; -} - -- (void)handleNonFatalError { - [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession - block:^{ - RTCLog(@"Restarting capture session after error."); - if (_isRunning) { - [_captureSession startRunning]; - } - }]; -} - -#pragma mark - UIApplication notifications - -- (void)handleApplicationDidBecomeActive:(NSNotification *)notification { - [RTCDispatcher dispatchAsyncOnType:RTCDispatcherTypeCaptureSession - block:^{ - if (_isRunning && !_captureSession.isRunning) { - RTCLog(@"Restarting capture session on active."); - [_captureSession startRunning]; - } - }]; -} - -#pragma mark - Private - -- (dispatch_queue_t)frameQueue { - if (!_frameQueue) { - _frameQueue = - dispatch_queue_create("org.webrtc.cameravideocapturer.video", DISPATCH_QUEUE_SERIAL); - dispatch_set_target_queue(_frameQueue, - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); - } - return _frameQueue; -} - -- (BOOL)setupCaptureSession:(AVCaptureSession *)captureSession { - NSAssert(_captureSession == nil, @"Setup capture session called twice."); - _captureSession = captureSession; - _captureSession.sessionPreset = AVCaptureSessionPresetInputPriority; - _captureSession.usesApplicationAudioSession = NO; - [self setupVideoDataOutput]; - // Add the output. - if (![_captureSession canAddOutput:_videoDataOutput]) { - RTCLogError(@"Video data output unsupported."); - return NO; - } - [_captureSession addOutput:_videoDataOutput]; - - return YES; -} - -- (void)setupVideoDataOutput { - NSAssert(_videoDataOutput == nil, @"Setup video data output called twice."); - AVCaptureVideoDataOutput *videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; - - // `videoDataOutput.availableVideoCVPixelFormatTypes` returns the pixel formats supported by the - // device with the most efficient output format first. Find the first format that we support. - NSSet *supportedPixelFormats = [RTCCVPixelBuffer supportedPixelFormats]; - NSMutableOrderedSet *availablePixelFormats = - [NSMutableOrderedSet orderedSetWithArray:videoDataOutput.availableVideoCVPixelFormatTypes]; - [availablePixelFormats intersectSet:supportedPixelFormats]; - NSNumber *pixelFormat = availablePixelFormats.firstObject; - NSAssert(pixelFormat, @"Output device has no supported formats."); - - _preferredOutputPixelFormat = [pixelFormat unsignedIntValue]; - _outputPixelFormat = _preferredOutputPixelFormat; - videoDataOutput.videoSettings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey : pixelFormat}; - videoDataOutput.alwaysDiscardsLateVideoFrames = NO; - [videoDataOutput setSampleBufferDelegate:self queue:self.frameQueue]; - _videoDataOutput = videoDataOutput; -} - -- (void)updateVideoDataOutputPixelFormat:(AVCaptureDeviceFormat *)format { - FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(format.formatDescription); - if (![[RTCCVPixelBuffer supportedPixelFormats] containsObject:@(mediaSubType)]) { - mediaSubType = _preferredOutputPixelFormat; - } - - if (mediaSubType != _outputPixelFormat) { - _outputPixelFormat = mediaSubType; - _videoDataOutput.videoSettings = - @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(mediaSubType) }; - } -} - -#pragma mark - Private, called inside capture queue - -- (void)updateDeviceCaptureFormat:(AVCaptureDeviceFormat *)format fps:(NSInteger)fps { - NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], - @"updateDeviceCaptureFormat must be called on the capture queue."); - @try { - _currentDevice.activeFormat = format; - _currentDevice.activeVideoMinFrameDuration = CMTimeMake(1, (int32_t)fps); - } @catch (NSException *exception) { - RTCLogError(@"Failed to set active format!\n User info:%@", exception.userInfo); - return; - } -} - -- (void)reconfigureCaptureSessionInput { - NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], - @"reconfigureCaptureSessionInput must be called on the capture queue."); - NSError *error = nil; - AVCaptureDeviceInput *input = - [AVCaptureDeviceInput deviceInputWithDevice:_currentDevice error:&error]; - if (!input) { - RTCLogError(@"Failed to create front camera input: %@", error.localizedDescription); - return; - } - [_captureSession beginConfiguration]; - for (AVCaptureDeviceInput *oldInput in [_captureSession.inputs copy]) { - [_captureSession removeInput:oldInput]; - } - if ([_captureSession canAddInput:input]) { - [_captureSession addInput:input]; - } else { - RTCLogError(@"Cannot add camera as an input to the session."); - } - [_captureSession commitConfiguration]; -} - -- (void)updateOrientation { - NSAssert([RTCDispatcher isOnQueueForType:RTCDispatcherTypeCaptureSession], - @"updateOrientation must be called on the capture queue."); - _orientation = [UIDevice currentDevice].orientation; -} - -@end diff --git a/submodules/TgVoipWebrtcCustom/Sources/VideoFileCapturer.h b/submodules/TgVoipWebrtcCustom/Sources/VideoFileCapturer.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/submodules/TgVoipWebrtcCustom/Sources/VideoFileCapturer.mm b/submodules/TgVoipWebrtcCustom/Sources/VideoFileCapturer.mm deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/submodules/TgVoipWebrtcCustom/Sources/VideoMetalView.h b/submodules/TgVoipWebrtcCustom/Sources/VideoMetalView.h deleted file mode 100644 index ed390dc5fd..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/VideoMetalView.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef VIDEOMETALVIEW_H -#define VIDEOMETALVIEW_H - -#import -#import - -#import "api/media_stream_interface.h" - -@class RTCVideoFrame; - -@interface VideoMetalView : UIView - -@property(nonatomic) UIViewContentMode videoContentMode; -@property(nonatomic, getter=isEnabled) BOOL enabled; -@property(nonatomic, nullable) NSValue* rotationOverride; - -- (void)setSize:(CGSize)size; -- (void)renderFrame:(nullable RTCVideoFrame *)frame; - -- (void)addToTrack:(rtc::scoped_refptr)track; - -@end - -#endif diff --git a/submodules/TgVoipWebrtcCustom/Sources/VideoMetalView.mm b/submodules/TgVoipWebrtcCustom/Sources/VideoMetalView.mm deleted file mode 100644 index 077d265ed8..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/VideoMetalView.mm +++ /dev/null @@ -1,274 +0,0 @@ -#import "VideoMetalView.h" - -#import -#import - -#import "base/RTCLogging.h" -#import "base/RTCVideoFrame.h" -#import "base/RTCVideoFrameBuffer.h" -#import "components/video_frame_buffer/RTCCVPixelBuffer.h" -#include "sdk/objc/native/api/video_frame.h" - -#import "api/video/video_sink_interface.h" -#import "api/media_stream_interface.h" - -#import "RTCMTLI420Renderer.h" -#import "RTCMTLNV12Renderer.h" -#import "RTCMTLRGBRenderer.h" - -#define MTKViewClass NSClassFromString(@"MTKView") -#define RTCMTLNV12RendererClass NSClassFromString(@"RTCMTLNV12Renderer") -#define RTCMTLI420RendererClass NSClassFromString(@"RTCMTLI420Renderer") -#define RTCMTLRGBRendererClass NSClassFromString(@"RTCMTLRGBRenderer") - -class VideoRendererAdapterImpl : public rtc::VideoSinkInterface { - public: - VideoRendererAdapterImpl(VideoMetalView *adapter) { - adapter_ = adapter; - size_ = CGSizeZero; - } - - void OnFrame(const webrtc::VideoFrame& nativeVideoFrame) override { - RTCVideoFrame* videoFrame = NativeToObjCVideoFrame(nativeVideoFrame); - - CGSize current_size = (videoFrame.rotation % 180 == 0) ? CGSizeMake(videoFrame.width, videoFrame.height) : CGSizeMake(videoFrame.height, videoFrame.width); - - if (!CGSizeEqualToSize(size_, current_size)) { - size_ = current_size; - [adapter_ setSize:size_]; - } - [adapter_ renderFrame:videoFrame]; - } - -private: - __weak VideoMetalView *adapter_; - CGSize size_; -}; - -@interface VideoMetalView () { - RTCMTLI420Renderer *_rendererI420; - RTCMTLNV12Renderer *_rendererNV12; - RTCMTLRGBRenderer *_rendererRGB; - MTKView *_metalView; - RTCVideoFrame *_videoFrame; - CGSize _videoFrameSize; - int64_t _lastFrameTimeNs; - - std::unique_ptr _sink; -} - -@end - -@implementation VideoMetalView - -- (instancetype)initWithFrame:(CGRect)frameRect { - self = [super initWithFrame:frameRect]; - if (self) { - [self configure]; - - _sink.reset(new VideoRendererAdapterImpl(self)); - } - return self; -} - -- (BOOL)isEnabled { - return !_metalView.paused; -} - -- (void)setEnabled:(BOOL)enabled { - _metalView.paused = !enabled; -} - -- (UIViewContentMode)videoContentMode { - return _metalView.contentMode; -} - -- (void)setVideoContentMode:(UIViewContentMode)mode { - _metalView.contentMode = mode; -} - -#pragma mark - Private - -+ (BOOL)isMetalAvailable { - return MTLCreateSystemDefaultDevice() != nil; -} - -+ (MTKView *)createMetalView:(CGRect)frame { - return [[MTKViewClass alloc] initWithFrame:frame]; -} - -+ (RTCMTLNV12Renderer *)createNV12Renderer { - return [[RTCMTLNV12RendererClass alloc] init]; -} - -+ (RTCMTLI420Renderer *)createI420Renderer { - return [[RTCMTLI420RendererClass alloc] init]; -} - -+ (RTCMTLRGBRenderer *)createRGBRenderer { - return [[RTCMTLRGBRenderer alloc] init]; -} - -- (void)configure { - NSAssert([VideoMetalView isMetalAvailable], @"Metal not availiable on this device"); - - _metalView = [VideoMetalView createMetalView:self.bounds]; - _metalView.delegate = self; - _metalView.contentMode = UIViewContentModeScaleAspectFill; - [self addSubview:_metalView]; - _videoFrameSize = CGSizeZero; -} - -- (void)setMultipleTouchEnabled:(BOOL)multipleTouchEnabled { - [super setMultipleTouchEnabled:multipleTouchEnabled]; - _metalView.multipleTouchEnabled = multipleTouchEnabled; -} - -- (void)layoutSubviews { - [super layoutSubviews]; - - CGRect bounds = self.bounds; - _metalView.frame = bounds; - if (!CGSizeEqualToSize(_videoFrameSize, CGSizeZero)) { - _metalView.drawableSize = [self drawableSize]; - } else { - _metalView.drawableSize = bounds.size; - } -} - -#pragma mark - MTKViewDelegate methods - -- (void)drawInMTKView:(nonnull MTKView *)view { - NSAssert(view == _metalView, @"Receiving draw callbacks from foreign instance."); - RTCVideoFrame *videoFrame = _videoFrame; - // Skip rendering if we've already rendered this frame. - if (!videoFrame || videoFrame.timeStampNs == _lastFrameTimeNs) { - return; - } - - if (CGRectIsEmpty(view.bounds)) { - return; - } - - RTCMTLRenderer *renderer; - if ([videoFrame.buffer isKindOfClass:[RTCCVPixelBuffer class]]) { - RTCCVPixelBuffer *buffer = (RTCCVPixelBuffer*)videoFrame.buffer; - const OSType pixelFormat = CVPixelBufferGetPixelFormatType(buffer.pixelBuffer); - if (pixelFormat == kCVPixelFormatType_32BGRA || pixelFormat == kCVPixelFormatType_32ARGB) { - if (!_rendererRGB) { - _rendererRGB = [VideoMetalView createRGBRenderer]; - if (![_rendererRGB addRenderingDestination:_metalView]) { - _rendererRGB = nil; - RTCLogError(@"Failed to create RGB renderer"); - return; - } - } - renderer = _rendererRGB; - } else { - if (!_rendererNV12) { - _rendererNV12 = [VideoMetalView createNV12Renderer]; - if (![_rendererNV12 addRenderingDestination:_metalView]) { - _rendererNV12 = nil; - RTCLogError(@"Failed to create NV12 renderer"); - return; - } - } - renderer = _rendererNV12; - } - } else { - if (!_rendererI420) { - _rendererI420 = [VideoMetalView createI420Renderer]; - if (![_rendererI420 addRenderingDestination:_metalView]) { - _rendererI420 = nil; - RTCLogError(@"Failed to create I420 renderer"); - return; - } - } - renderer = _rendererI420; - } - - renderer.rotationOverride = _rotationOverride; - - [renderer drawFrame:videoFrame]; - _lastFrameTimeNs = videoFrame.timeStampNs; -} - -- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { -} - -#pragma mark - - -- (void)setRotationOverride:(NSValue *)rotationOverride { - _rotationOverride = rotationOverride; - - _metalView.drawableSize = [self drawableSize]; - [self setNeedsLayout]; -} - -- (RTCVideoRotation)frameRotation { - if (_rotationOverride) { - RTCVideoRotation rotation; - if (@available(iOS 11, *)) { - [_rotationOverride getValue:&rotation size:sizeof(rotation)]; - } else { - [_rotationOverride getValue:&rotation]; - } - return rotation; - } - - return _videoFrame.rotation; -} - -- (CGSize)drawableSize { - // Flip width/height if the rotations are not the same. - CGSize videoFrameSize = _videoFrameSize; - RTCVideoRotation frameRotation = [self frameRotation]; - - BOOL useLandscape = - (frameRotation == RTCVideoRotation_0) || (frameRotation == RTCVideoRotation_180); - BOOL sizeIsLandscape = (_videoFrame.rotation == RTCVideoRotation_0) || - (_videoFrame.rotation == RTCVideoRotation_180); - - if (useLandscape == sizeIsLandscape) { - return videoFrameSize; - } else { - return CGSizeMake(videoFrameSize.height, videoFrameSize.width); - } -} - -#pragma mark - RTCVideoRenderer - -- (void)setSize:(CGSize)size { - __weak VideoMetalView *weakSelf = self; - dispatch_async(dispatch_get_main_queue(), ^{ - __strong VideoMetalView *strongSelf = weakSelf; - if (strongSelf == nil) { - return; - } - - strongSelf->_videoFrameSize = size; - CGSize drawableSize = [strongSelf drawableSize]; - - strongSelf->_metalView.drawableSize = drawableSize; - [strongSelf setNeedsLayout]; - //[strongSelf.delegate videoView:self didChangeVideoSize:size]; - }); -} - -- (void)renderFrame:(nullable RTCVideoFrame *)frame { - if (!self.isEnabled) { - return; - } - - if (frame == nil) { - RTCLogInfo(@"Incoming frame is nil. Exiting render callback."); - return; - } - _videoFrame = frame; -} - -- (void)addToTrack:(rtc::scoped_refptr)track { - track->AddOrUpdateSink(_sink.get(), rtc::VideoSinkWants()); -} - -@end diff --git a/submodules/TgVoipWebrtcCustom/Sources/VideoRendererAdapter.h b/submodules/TgVoipWebrtcCustom/Sources/VideoRendererAdapter.h deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/submodules/TgVoipWebrtcCustom/Sources/VideoRendererAdapter.mm b/submodules/TgVoipWebrtcCustom/Sources/VideoRendererAdapter.mm deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.cpp deleted file mode 100644 index d5105f055f..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.cpp +++ /dev/null @@ -1,811 +0,0 @@ -/* - * Copyright 2011 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_dtls_transport.h" - -#include -#include -#include - -#include "api/rtc_event_log/rtc_event_log.h" -#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h" -#include "logging/rtc_event_log/events/rtc_event_dtls_writable_state.h" -#include "p2p/base/packet_transport_internal.h" -#include "rtc_base/buffer.h" -#include "rtc_base/checks.h" -#include "rtc_base/dscp.h" -#include "rtc_base/logging.h" -#include "rtc_base/message_queue.h" -#include "rtc_base/rtc_certificate.h" -#include "rtc_base/ssl_stream_adapter.h" -#include "rtc_base/stream.h" -#include "rtc_base/thread.h" - -namespace cricket { - -// We don't pull the RTP constants from rtputils.h, to avoid a layer violation. -static const size_t kDtlsRecordHeaderLen = 13; -static const size_t kMaxDtlsPacketLen = 2048; -static const size_t kMinRtpPacketLen = 12; - -// Maximum number of pending packets in the queue. Packets are read immediately -// after they have been written, so a capacity of "1" is sufficient. -static const size_t kMaxPendingPackets = 1; - -// Minimum and maximum values for the initial DTLS handshake timeout. We'll pick -// an initial timeout based on ICE RTT estimates, but clamp it to this range. -static const int kMinHandshakeTimeout = 50; -static const int kMaxHandshakeTimeout = 3000; - -static bool IsDtlsPacket(const char* data, size_t len) { - const uint8_t* u = reinterpret_cast(data); - return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64)); -} -static bool IsDtlsClientHelloPacket(const char* data, size_t len) { - if (!IsDtlsPacket(data, len)) { - return false; - } - const uint8_t* u = reinterpret_cast(data); - return len > 17 && u[0] == 22 && u[13] == 1; -} -static bool IsRtpPacket(const char* data, size_t len) { - const uint8_t* u = reinterpret_cast(data); - return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80); -} - -/*StreamInterfaceChannel::StreamInterfaceChannel( - IceTransportInternal* ice_transport) - : ice_transport_(ice_transport), - state_(rtc::SS_OPEN), - packets_(kMaxPendingPackets, kMaxDtlsPacketLen) {} - -rtc::StreamResult StreamInterfaceChannel::Read(void* buffer, - size_t buffer_len, - size_t* read, - int* error) { - if (state_ == rtc::SS_CLOSED) - return rtc::SR_EOS; - if (state_ == rtc::SS_OPENING) - return rtc::SR_BLOCK; - - if (!packets_.ReadFront(buffer, buffer_len, read)) { - return rtc::SR_BLOCK; - } - - return rtc::SR_SUCCESS; -} - -rtc::StreamResult StreamInterfaceChannel::Write(const void* data, - size_t data_len, - size_t* written, - int* error) { - // Always succeeds, since this is an unreliable transport anyway. - // TODO(zhihuang): Should this block if ice_transport_'s temporarily - // unwritable? - rtc::PacketOptions packet_options; - ice_transport_->SendPacket(static_cast(data), data_len, - packet_options); - if (written) { - *written = data_len; - } - return rtc::SR_SUCCESS; -} - -bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) { - // We force a read event here to ensure that we don't overflow our queue. - bool ret = packets_.WriteBack(data, size, NULL); - RTC_CHECK(ret) << "Failed to write packet to queue."; - if (ret) { - SignalEvent(this, rtc::SE_READ, 0); - } - return ret; -} - -rtc::StreamState StreamInterfaceChannel::GetState() const { - return state_; -} - -void StreamInterfaceChannel::Close() { - packets_.Clear(); - state_ = rtc::SS_CLOSED; -}*/ - -TgDtlsTransport::TgDtlsTransport(IceTransportInternal* ice_transport, - const webrtc::CryptoOptions& crypto_options, - webrtc::RtcEventLog* event_log) - : transport_name_(ice_transport->transport_name()), - component_(ice_transport->component()), - ice_transport_(ice_transport), - downward_(NULL), - srtp_ciphers_(crypto_options.GetSupportedDtlsSrtpCryptoSuites()), - ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12), - crypto_options_(crypto_options), - event_log_(event_log) { - RTC_DCHECK(ice_transport_); - ConnectToIceTransport(); -} - -TgDtlsTransport::~TgDtlsTransport() = default; - -const webrtc::CryptoOptions& TgDtlsTransport::crypto_options() const { - return crypto_options_; -} - -DtlsTransportState TgDtlsTransport::dtls_state() const { - return dtls_state_; -} - -const std::string& TgDtlsTransport::transport_name() const { - return transport_name_; -} - -int TgDtlsTransport::component() const { - return component_; -} - -bool TgDtlsTransport::IsDtlsActive() const { - return dtls_active_; -} - -bool TgDtlsTransport::SetLocalCertificate( - const rtc::scoped_refptr& certificate) { - if (dtls_active_) { - if (certificate == local_certificate_) { - // This may happen during renegotiation. - RTC_LOG(LS_INFO) << ToString() << ": Ignoring identical DTLS identity"; - return true; - } else { - RTC_LOG(LS_ERROR) << ToString() - << ": Can't change DTLS local identity in this state"; - return false; - } - } - - if (certificate) { - local_certificate_ = certificate; - dtls_active_ = true; - } else { - RTC_LOG(LS_INFO) << ToString() - << ": NULL DTLS identity supplied. Not doing DTLS"; - } - - return true; -} - -rtc::scoped_refptr TgDtlsTransport::GetLocalCertificate() - const { - return local_certificate_; -} - -bool TgDtlsTransport::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) { - if (dtls_active_) { - RTC_LOG(LS_ERROR) << "Not changing max. protocol version " - "while DTLS is negotiating"; - return false; - } - - ssl_max_version_ = version; - return true; -} - -bool TgDtlsTransport::SetDtlsRole(rtc::SSLRole role) { - if (dtls_) { - RTC_DCHECK(dtls_role_); - if (*dtls_role_ != role) { - RTC_LOG(LS_ERROR) - << "SSL Role can't be reversed after the session is setup."; - return false; - } - return true; - } - - dtls_role_ = role; - return true; -} - -bool TgDtlsTransport::GetDtlsRole(rtc::SSLRole* role) const { - if (!dtls_role_) { - return false; - } - *role = *dtls_role_; - return true; -} - -bool TgDtlsTransport::GetSslCipherSuite(int* cipher) { - if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { - return false; - } - - return dtls_->GetSslCipherSuite(cipher); -} - -bool TgDtlsTransport::SetRemoteFingerprint(const std::string& digest_alg, - const uint8_t* digest, - size_t digest_len) { - rtc::Buffer remote_fingerprint_value(digest, digest_len); - - // Once we have the local certificate, the same remote fingerprint can be set - // multiple times. - if (dtls_active_ && remote_fingerprint_value_ == remote_fingerprint_value && - !digest_alg.empty()) { - // This may happen during renegotiation. - RTC_LOG(LS_INFO) << ToString() - << ": Ignoring identical remote DTLS fingerprint"; - return true; - } - - // If the other side doesn't support DTLS, turn off |dtls_active_|. - // TODO(deadbeef): Remove this. It's dangerous, because it relies on higher - // level code to ensure DTLS is actually used, but there are tests that - // depend on it, for the case where an m= section is rejected. In that case - // SetRemoteFingerprint shouldn't even be called though. - if (digest_alg.empty()) { - RTC_DCHECK(!digest_len); - RTC_LOG(LS_INFO) << ToString() << ": Other side didn't support DTLS."; - dtls_active_ = false; - return true; - } - - // Otherwise, we must have a local certificate before setting remote - // fingerprint. - if (!dtls_active_) { - RTC_LOG(LS_ERROR) << ToString() - << ": Can't set DTLS remote settings in this state."; - return false; - } - - // At this point we know we are doing DTLS - bool fingerprint_changing = remote_fingerprint_value_.size() > 0u; - remote_fingerprint_value_ = std::move(remote_fingerprint_value); - remote_fingerprint_algorithm_ = digest_alg; - - if (dtls_ && !fingerprint_changing) { - // This can occur if DTLS is set up before a remote fingerprint is - // received. For instance, if we set up DTLS due to receiving an early - // ClientHello. - rtc::SSLPeerCertificateDigestError err; - if (!dtls_->SetPeerCertificateDigest( - remote_fingerprint_algorithm_, - reinterpret_cast(remote_fingerprint_value_.data()), - remote_fingerprint_value_.size(), &err)) { - RTC_LOG(LS_ERROR) << ToString() - << ": Couldn't set DTLS certificate digest."; - set_dtls_state(DTLS_TRANSPORT_FAILED); - // If the error is "verification failed", don't return false, because - // this means the fingerprint was formatted correctly but didn't match - // the certificate from the DTLS handshake. Thus the DTLS state should go - // to "failed", but SetRemoteDescription shouldn't fail. - return err == rtc::SSLPeerCertificateDigestError::VERIFICATION_FAILED; - } - return true; - } - - // If the fingerprint is changing, we'll tear down the DTLS association and - // create a new one, resetting our state. - if (dtls_ && fingerprint_changing) { - dtls_.reset(nullptr); - set_dtls_state(DTLS_TRANSPORT_NEW); - set_writable(false); - } - - if (!SetupDtls()) { - set_dtls_state(DTLS_TRANSPORT_FAILED); - return false; - } - - return true; -} - -std::unique_ptr TgDtlsTransport::GetRemoteSSLCertChain() - const { - if (!dtls_) { - return nullptr; - } - - return dtls_->GetPeerSSLCertChain(); -} - -bool TgDtlsTransport::ExportKeyingMaterial(const std::string& label, - const uint8_t* context, - size_t context_len, - bool use_context, - uint8_t* result, - size_t result_len) { - return (dtls_.get()) - ? dtls_->ExportKeyingMaterial(label, context, context_len, - use_context, result, result_len) - : false; -} - -bool TgDtlsTransport::SetupDtls() { - RTC_DCHECK(dtls_role_); - StreamInterfaceChannel* downward = new StreamInterfaceChannel(ice_transport_); - - dtls_.reset(rtc::SSLStreamAdapter::Create(downward)); - if (!dtls_) { - RTC_LOG(LS_ERROR) << ToString() << ": Failed to create DTLS adapter."; - delete downward; - return false; - } - - downward_ = downward; - - dtls_->SetIdentity(local_certificate_->identity()->GetReference()); - dtls_->SetMode(rtc::SSL_MODE_DTLS); - dtls_->SetMaxProtocolVersion(ssl_max_version_); - dtls_->SetServerRole(*dtls_role_); - dtls_->SignalEvent.connect(this, &TgDtlsTransport::OnDtlsEvent); - dtls_->SignalSSLHandshakeError.connect(this, - &TgDtlsTransport::OnDtlsHandshakeError); - if (remote_fingerprint_value_.size() && - !dtls_->SetPeerCertificateDigest( - remote_fingerprint_algorithm_, - reinterpret_cast(remote_fingerprint_value_.data()), - remote_fingerprint_value_.size())) { - RTC_LOG(LS_ERROR) << ToString() - << ": Couldn't set DTLS certificate digest."; - return false; - } - - // Set up DTLS-SRTP, if it's been enabled. - if (!srtp_ciphers_.empty()) { - if (!dtls_->SetDtlsSrtpCryptoSuites(srtp_ciphers_)) { - RTC_LOG(LS_ERROR) << ToString() << ": Couldn't set DTLS-SRTP ciphers."; - return false; - } - } else { - RTC_LOG(LS_INFO) << ToString() << ": Not using DTLS-SRTP."; - } - - RTC_LOG(LS_INFO) << ToString() << ": DTLS setup complete."; - - // If the underlying ice_transport is already writable at this point, we may - // be able to start DTLS right away. - MaybeStartDtls(); - return true; -} - -bool TgDtlsTransport::GetSrtpCryptoSuite(int* cipher) { - if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { - return false; - } - - return dtls_->GetDtlsSrtpCryptoSuite(cipher); -} - -bool TgDtlsTransport::GetSslVersionBytes(int* version) const { - if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { - return false; - } - - return dtls_->GetSslVersionBytes(version); -} - -// Called from upper layers to send a media packet. -int TgDtlsTransport::SendPacket(const char* data, - size_t size, - const rtc::PacketOptions& options, - int flags) { - if (!dtls_active_) { - // Not doing DTLS. - return ice_transport_->SendPacket(data, size, options); - } - - switch (dtls_state()) { - case DTLS_TRANSPORT_NEW: - // Can't send data until the connection is active. - // TODO(ekr@rtfm.com): assert here if dtls_ is NULL? - return -1; - case DTLS_TRANSPORT_CONNECTING: - // Can't send data until the connection is active. - return -1; - case DTLS_TRANSPORT_CONNECTED: - if (flags & PF_SRTP_BYPASS) { - RTC_DCHECK(!srtp_ciphers_.empty()); - if (!IsRtpPacket(data, size)) { - return -1; - } - - return ice_transport_->SendPacket(data, size, options); - } else { - return (dtls_->WriteAll(data, size, NULL, NULL) == rtc::SR_SUCCESS) - ? static_cast(size) - : -1; - } - case DTLS_TRANSPORT_FAILED: - case DTLS_TRANSPORT_CLOSED: - // Can't send anything when we're closed. - return -1; - default: - RTC_NOTREACHED(); - return -1; - } -} - -IceTransportInternal* TgDtlsTransport::ice_transport() { - return ice_transport_; -} - -bool TgDtlsTransport::IsDtlsConnected() { - return dtls_ && dtls_->IsTlsConnected(); -} - -bool TgDtlsTransport::receiving() const { - return receiving_; -} - -bool TgDtlsTransport::writable() const { - return writable_; -} - -int TgDtlsTransport::GetError() { - return ice_transport_->GetError(); -} - -absl::optional TgDtlsTransport::network_route() const { - return ice_transport_->network_route(); -} - -bool TgDtlsTransport::GetOption(rtc::Socket::Option opt, int* value) { - return ice_transport_->GetOption(opt, value); -} - -int TgDtlsTransport::SetOption(rtc::Socket::Option opt, int value) { - return ice_transport_->SetOption(opt, value); -} - -void TgDtlsTransport::ConnectToIceTransport() { - RTC_DCHECK(ice_transport_); - ice_transport_->SignalWritableState.connect(this, - &TgDtlsTransport::OnWritableState); - ice_transport_->SignalReadPacket.connect(this, &TgDtlsTransport::OnReadPacket); - ice_transport_->SignalSentPacket.connect(this, &TgDtlsTransport::OnSentPacket); - ice_transport_->SignalReadyToSend.connect(this, - &TgDtlsTransport::OnReadyToSend); - ice_transport_->SignalReceivingState.connect( - this, &TgDtlsTransport::OnReceivingState); - ice_transport_->SignalNetworkRouteChanged.connect( - this, &TgDtlsTransport::OnNetworkRouteChanged); -} - -// The state transition logic here is as follows: -// (1) If we're not doing DTLS-SRTP, then the state is just the -// state of the underlying impl() -// (2) If we're doing DTLS-SRTP: -// - Prior to the DTLS handshake, the state is neither receiving nor -// writable -// - When the impl goes writable for the first time we -// start the DTLS handshake -// - Once the DTLS handshake completes, the state is that of the -// impl again -void TgDtlsTransport::OnWritableState(rtc::PacketTransportInternal* transport) { - RTC_DCHECK_RUN_ON(&thread_checker_); - RTC_DCHECK(transport == ice_transport_); - RTC_LOG(LS_VERBOSE) << ToString() - << ": ice_transport writable state changed to " - << ice_transport_->writable(); - - if (!dtls_active_) { - // Not doing DTLS. - // Note: SignalWritableState fired by set_writable. - set_writable(ice_transport_->writable()); - return; - } - - switch (dtls_state()) { - case DTLS_TRANSPORT_NEW: - MaybeStartDtls(); - break; - case DTLS_TRANSPORT_CONNECTED: - // Note: SignalWritableState fired by set_writable. - set_writable(ice_transport_->writable()); - break; - case DTLS_TRANSPORT_CONNECTING: - // Do nothing. - break; - case DTLS_TRANSPORT_FAILED: - case DTLS_TRANSPORT_CLOSED: - // Should not happen. Do nothing. - break; - } -} - -void TgDtlsTransport::OnReceivingState(rtc::PacketTransportInternal* transport) { - RTC_DCHECK_RUN_ON(&thread_checker_); - RTC_DCHECK(transport == ice_transport_); - RTC_LOG(LS_VERBOSE) << ToString() - << ": ice_transport " - "receiving state changed to " - << ice_transport_->receiving(); - if (!dtls_active_ || dtls_state() == DTLS_TRANSPORT_CONNECTED) { - // Note: SignalReceivingState fired by set_receiving. - set_receiving(ice_transport_->receiving()); - } -} - -void TgDtlsTransport::OnReadPacket(rtc::PacketTransportInternal* transport, - const char* data, - size_t size, - const int64_t& packet_time_us, - int flags) { - RTC_DCHECK_RUN_ON(&thread_checker_); - RTC_DCHECK(transport == ice_transport_); - RTC_DCHECK(flags == 0); - - if (!dtls_active_) { - // Not doing DTLS. - SignalReadPacket(this, data, size, packet_time_us, 0); - return; - } - - switch (dtls_state()) { - case DTLS_TRANSPORT_NEW: - if (dtls_) { - RTC_LOG(LS_INFO) << ToString() - << ": Packet received before DTLS started."; - } else { - RTC_LOG(LS_WARNING) << ToString() - << ": Packet received before we know if we are " - "doing DTLS or not."; - } - // Cache a client hello packet received before DTLS has actually started. - if (IsDtlsClientHelloPacket(data, size)) { - RTC_LOG(LS_INFO) << ToString() - << ": Caching DTLS ClientHello packet until DTLS is " - "started."; - cached_client_hello_.SetData(data, size); - // If we haven't started setting up DTLS yet (because we don't have a - // remote fingerprint/role), we can use the client hello as a clue that - // the peer has chosen the client role, and proceed with the handshake. - // The fingerprint will be verified when it's set. - if (!dtls_ && local_certificate_) { - SetDtlsRole(rtc::SSL_SERVER); - SetupDtls(); - } - } else { - RTC_LOG(LS_INFO) << ToString() - << ": Not a DTLS ClientHello packet; dropping."; - } - break; - - case DTLS_TRANSPORT_CONNECTING: - case DTLS_TRANSPORT_CONNECTED: - // We should only get DTLS or SRTP packets; STUN's already been demuxed. - // Is this potentially a DTLS packet? - if (IsDtlsPacket(data, size)) { - if (!HandleDtlsPacket(data, size)) { - RTC_LOG(LS_ERROR) << ToString() << ": Failed to handle DTLS packet."; - return; - } - } else { - // Not a DTLS packet; our handshake should be complete by now. - if (dtls_state() != DTLS_TRANSPORT_CONNECTED) { - RTC_LOG(LS_ERROR) << ToString() - << ": Received non-DTLS packet before DTLS " - "complete."; - return; - } - - // And it had better be a SRTP packet. - if (!IsRtpPacket(data, size)) { - RTC_LOG(LS_ERROR) - << ToString() << ": Received unexpected non-DTLS packet."; - return; - } - - // Sanity check. - RTC_DCHECK(!srtp_ciphers_.empty()); - - // Signal this upwards as a bypass packet. - SignalReadPacket(this, data, size, packet_time_us, PF_SRTP_BYPASS); - } - break; - case DTLS_TRANSPORT_FAILED: - case DTLS_TRANSPORT_CLOSED: - // This shouldn't be happening. Drop the packet. - break; - } -} - -void TgDtlsTransport::OnSentPacket(rtc::PacketTransportInternal* transport, - const rtc::SentPacket& sent_packet) { - RTC_DCHECK_RUN_ON(&thread_checker_); - SignalSentPacket(this, sent_packet); -} - -void TgDtlsTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) { - RTC_DCHECK_RUN_ON(&thread_checker_); - if (writable()) { - SignalReadyToSend(this); - } -} - -void TgDtlsTransport::OnDtlsEvent(rtc::StreamInterface* dtls, int sig, int err) { - RTC_DCHECK_RUN_ON(&thread_checker_); - RTC_DCHECK(dtls == dtls_.get()); - if (sig & rtc::SE_OPEN) { - // This is the first time. - RTC_LOG(LS_INFO) << ToString() << ": DTLS handshake complete."; - if (dtls_->GetState() == rtc::SS_OPEN) { - // The check for OPEN shouldn't be necessary but let's make - // sure we don't accidentally frob the state if it's closed. - set_dtls_state(DTLS_TRANSPORT_CONNECTED); - set_writable(true); - } - } - if (sig & rtc::SE_READ) { - char buf[kMaxDtlsPacketLen]; - size_t read; - int read_error; - rtc::StreamResult ret; - // The underlying DTLS stream may have received multiple DTLS records in - // one packet, so read all of them. - do { - ret = dtls_->Read(buf, sizeof(buf), &read, &read_error); - if (ret == rtc::SR_SUCCESS) { - SignalReadPacket(this, buf, read, rtc::TimeMicros(), 0); - } else if (ret == rtc::SR_EOS) { - // Remote peer shut down the association with no error. - RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote"; - set_writable(false); - set_dtls_state(DTLS_TRANSPORT_CLOSED); - } else if (ret == rtc::SR_ERROR) { - // Remote peer shut down the association with an error. - RTC_LOG(LS_INFO) - << ToString() - << ": Closed by remote with DTLS transport error, code=" - << read_error; - set_writable(false); - set_dtls_state(DTLS_TRANSPORT_FAILED); - } - } while (ret == rtc::SR_SUCCESS); - } - if (sig & rtc::SE_CLOSE) { - RTC_DCHECK(sig == rtc::SE_CLOSE); // SE_CLOSE should be by itself. - set_writable(false); - if (!err) { - RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed"; - set_dtls_state(DTLS_TRANSPORT_CLOSED); - } else { - RTC_LOG(LS_INFO) << ToString() << ": DTLS transport error, code=" << err; - set_dtls_state(DTLS_TRANSPORT_FAILED); - } - } -} - -void TgDtlsTransport::OnNetworkRouteChanged( - absl::optional network_route) { - RTC_DCHECK_RUN_ON(&thread_checker_); - SignalNetworkRouteChanged(network_route); -} - -void TgDtlsTransport::MaybeStartDtls() { - if (dtls_ && ice_transport_->writable()) { - ConfigureHandshakeTimeout(); - - if (dtls_->StartSSL()) { - // This should never fail: - // Because we are operating in a nonblocking mode and all - // incoming packets come in via OnReadPacket(), which rejects - // packets in this state, the incoming queue must be empty. We - // ignore write errors, thus any errors must be because of - // configuration and therefore are our fault. - RTC_NOTREACHED() << "StartSSL failed."; - RTC_LOG(LS_ERROR) << ToString() << ": Couldn't start DTLS handshake"; - set_dtls_state(DTLS_TRANSPORT_FAILED); - return; - } - RTC_LOG(LS_INFO) << ToString() << ": DtlsTransport: Started DTLS handshake"; - set_dtls_state(DTLS_TRANSPORT_CONNECTING); - // Now that the handshake has started, we can process a cached ClientHello - // (if one exists). - if (cached_client_hello_.size()) { - if (*dtls_role_ == rtc::SSL_SERVER) { - RTC_LOG(LS_INFO) << ToString() - << ": Handling cached DTLS ClientHello packet."; - if (!HandleDtlsPacket(cached_client_hello_.data(), - cached_client_hello_.size())) { - RTC_LOG(LS_ERROR) << ToString() << ": Failed to handle DTLS packet."; - } - } else { - RTC_LOG(LS_WARNING) << ToString() - << ": Discarding cached DTLS ClientHello packet " - "because we don't have the server role."; - } - cached_client_hello_.Clear(); - } - } -} - -// Called from OnReadPacket when a DTLS packet is received. -bool TgDtlsTransport::HandleDtlsPacket(const char* data, size_t size) { - // Sanity check we're not passing junk that - // just looks like DTLS. - const uint8_t* tmp_data = reinterpret_cast(data); - size_t tmp_size = size; - while (tmp_size > 0) { - if (tmp_size < kDtlsRecordHeaderLen) - return false; // Too short for the header - - size_t record_len = (tmp_data[11] << 8) | (tmp_data[12]); - if ((record_len + kDtlsRecordHeaderLen) > tmp_size) - return false; // Body too short - - tmp_data += record_len + kDtlsRecordHeaderLen; - tmp_size -= record_len + kDtlsRecordHeaderLen; - } - - // Looks good. Pass to the SIC which ends up being passed to - // the DTLS stack. - return downward_->OnPacketReceived(data, size); -} - -void TgDtlsTransport::set_receiving(bool receiving) { - if (receiving_ == receiving) { - return; - } - receiving_ = receiving; - SignalReceivingState(this); -} - -void TgDtlsTransport::set_writable(bool writable) { - if (writable_ == writable) { - return; - } - if (event_log_) { - event_log_->Log( - std::make_unique(writable)); - } - RTC_LOG(LS_VERBOSE) << ToString() << ": set_writable to: " << writable; - writable_ = writable; - if (writable_) { - SignalReadyToSend(this); - } - SignalWritableState(this); -} - -void TgDtlsTransport::set_dtls_state(DtlsTransportState state) { - if (dtls_state_ == state) { - return; - } - if (event_log_) { - event_log_->Log(std::make_unique( - ConvertDtlsTransportState(state))); - } - RTC_LOG(LS_VERBOSE) << ToString() << ": set_dtls_state from:" << dtls_state_ - << " to " << state; - dtls_state_ = state; - SignalDtlsState(this, state); -} - -void TgDtlsTransport::OnDtlsHandshakeError(rtc::SSLHandshakeError error) { - SignalDtlsHandshakeError(error); -} - -void TgDtlsTransport::ConfigureHandshakeTimeout() { - RTC_DCHECK(dtls_); - absl::optional rtt = ice_transport_->GetRttEstimate(); - if (rtt) { - // Limit the timeout to a reasonable range in case the ICE RTT takes - // extreme values. - int initial_timeout = std::max(kMinHandshakeTimeout, - std::min(kMaxHandshakeTimeout, 2 * (*rtt))); - RTC_LOG(LS_INFO) << ToString() << ": configuring DTLS handshake timeout " - << initial_timeout << " based on ICE RTT " << *rtt; - - dtls_->SetInitialRetransmissionTimeout(initial_timeout); - } else { - RTC_LOG(LS_INFO) - << ToString() - << ": no RTT estimate - using default DTLS handshake timeout"; - } -} - -} // namespace cricket diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.h b/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.h deleted file mode 100644 index 6753d68286..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_dtls_transport.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2011 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TG_P2P_BASE_DTLS_TRANSPORT_H_ -#define TG_P2P_BASE_DTLS_TRANSPORT_H_ - -#include -#include -#include - -#include "p2p/base/dtls_transport.h" -#include "api/crypto/crypto_options.h" -#include "p2p/base/dtls_transport_internal.h" -#include "p2p/base/ice_transport_internal.h" -#include "rtc_base/buffer.h" -#include "rtc_base/buffer_queue.h" -#include "rtc_base/constructor_magic.h" -#include "rtc_base/ssl_stream_adapter.h" -#include "rtc_base/stream.h" -#include "rtc_base/strings/string_builder.h" -#include "rtc_base/thread_checker.h" - -namespace rtc { -class PacketTransportInternal; -} - -namespace cricket { - -// This class provides a DTLS SSLStreamAdapter inside a TransportChannel-style -// packet-based interface, wrapping an existing TransportChannel instance -// (e.g a P2PTransportChannel) -// Here's the way this works: -// -// DtlsTransport { -// SSLStreamAdapter* dtls_ { -// StreamInterfaceChannel downward_ { -// IceTransportInternal* ice_transport_; -// } -// } -// } -// -// - Data which comes into DtlsTransport from the underlying -// ice_transport_ via OnReadPacket() is checked for whether it is DTLS -// or not, and if it is, is passed to DtlsTransport::HandleDtlsPacket, -// which pushes it into to downward_. dtls_ is listening for events on -// downward_, so it immediately calls downward_->Read(). -// -// - Data written to DtlsTransport is passed either to downward_ or directly -// to ice_transport_, depending on whether DTLS is negotiated and whether -// the flags include PF_SRTP_BYPASS -// -// - The SSLStreamAdapter writes to downward_->Write() which translates it -// into packet writes on ice_transport_. -// -// This class is not thread safe; all methods must be called on the same thread -// as the constructor. -class TgDtlsTransport : public DtlsTransportInternal { - public: - // |ice_transport| is the ICE transport this DTLS transport is wrapping. It - // must outlive this DTLS transport. - // - // |crypto_options| are the options used for the DTLS handshake. This affects - // whether GCM crypto suites are negotiated. - // - // |event_log| is an optional RtcEventLog for logging state changes. It should - // outlive the DtlsTransport. - explicit TgDtlsTransport(IceTransportInternal* ice_transport, - const webrtc::CryptoOptions& crypto_options, - webrtc::RtcEventLog* event_log); - - ~TgDtlsTransport() override; - - const webrtc::CryptoOptions& crypto_options() const override; - DtlsTransportState dtls_state() const override; - const std::string& transport_name() const override; - int component() const override; - - // DTLS is active if a local certificate was set. Otherwise this acts in a - // "passthrough" mode, sending packets directly through the underlying ICE - // transport. - // TODO(deadbeef): Remove this weirdness, and handle it in the upper layers. - bool IsDtlsActive() const override; - - // SetLocalCertificate is what makes DTLS active. It must be called before - // SetRemoteFinterprint. - // TODO(deadbeef): Once DtlsTransport no longer has the concept of being - // "active" or not (acting as a passthrough if not active), just require this - // certificate on construction or "Start". - bool SetLocalCertificate( - const rtc::scoped_refptr& certificate) override; - rtc::scoped_refptr GetLocalCertificate() const override; - - // SetRemoteFingerprint must be called after SetLocalCertificate, and any - // other methods like SetDtlsRole. It's what triggers the actual DTLS setup. - // TODO(deadbeef): Rename to "Start" like in ORTC? - bool SetRemoteFingerprint(const std::string& digest_alg, - const uint8_t* digest, - size_t digest_len) override; - - // Called to send a packet (via DTLS, if turned on). - int SendPacket(const char* data, - size_t size, - const rtc::PacketOptions& options, - int flags) override; - - bool GetOption(rtc::Socket::Option opt, int* value) override; - - bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override; - - // Find out which TLS version was negotiated - bool GetSslVersionBytes(int* version) const override; - // Find out which DTLS-SRTP cipher was negotiated - bool GetSrtpCryptoSuite(int* cipher) override; - - bool GetDtlsRole(rtc::SSLRole* role) const override; - bool SetDtlsRole(rtc::SSLRole role) override; - - // Find out which DTLS cipher was negotiated - bool GetSslCipherSuite(int* cipher) override; - - // Once DTLS has been established, this method retrieves the certificate - // chain in use by the remote peer, for use in external identity - // verification. - std::unique_ptr GetRemoteSSLCertChain() const override; - - // Once DTLS has established (i.e., this ice_transport is writable), this - // method extracts the keys negotiated during the DTLS handshake, for use in - // external encryption. DTLS-SRTP uses this to extract the needed SRTP keys. - // See the SSLStreamAdapter documentation for info on the specific parameters. - bool ExportKeyingMaterial(const std::string& label, - const uint8_t* context, - size_t context_len, - bool use_context, - uint8_t* result, - size_t result_len) override; - - IceTransportInternal* ice_transport() override; - - // For informational purposes. Tells if the DTLS handshake has finished. - // This may be true even if writable() is false, if the remote fingerprint - // has not yet been verified. - bool IsDtlsConnected(); - - bool receiving() const override; - bool writable() const override; - - int GetError() override; - - absl::optional network_route() const override; - - int SetOption(rtc::Socket::Option opt, int value) override; - - std::string ToString() const { - const absl::string_view RECEIVING_ABBREV[2] = {"_", "R"}; - const absl::string_view WRITABLE_ABBREV[2] = {"_", "W"}; - rtc::StringBuilder sb; - sb << "DtlsTransport[" << transport_name_ << "|" << component_ << "|" - << RECEIVING_ABBREV[receiving()] << WRITABLE_ABBREV[writable()] << "]"; - return sb.Release(); - } - - private: - void ConnectToIceTransport(); - - void OnWritableState(rtc::PacketTransportInternal* transport); - void OnReadPacket(rtc::PacketTransportInternal* transport, - const char* data, - size_t size, - const int64_t& packet_time_us, - int flags); - void OnSentPacket(rtc::PacketTransportInternal* transport, - const rtc::SentPacket& sent_packet); - void OnReadyToSend(rtc::PacketTransportInternal* transport); - void OnReceivingState(rtc::PacketTransportInternal* transport); - void OnDtlsEvent(rtc::StreamInterface* stream_, int sig, int err); - void OnNetworkRouteChanged(absl::optional network_route); - bool SetupDtls(); - void MaybeStartDtls(); - bool HandleDtlsPacket(const char* data, size_t size); - void OnDtlsHandshakeError(rtc::SSLHandshakeError error); - void ConfigureHandshakeTimeout(); - - void set_receiving(bool receiving); - void set_writable(bool writable); - // Sets the DTLS state, signaling if necessary. - void set_dtls_state(DtlsTransportState state); - - rtc::ThreadChecker thread_checker_; - - std::string transport_name_; - int component_; - DtlsTransportState dtls_state_ = DTLS_TRANSPORT_NEW; - // Underlying ice_transport, not owned by this class. - IceTransportInternal* ice_transport_; - std::unique_ptr dtls_; // The DTLS stream - StreamInterfaceChannel* - downward_; // Wrapper for ice_transport_, owned by dtls_. - std::vector srtp_ciphers_; // SRTP ciphers to use with DTLS. - bool dtls_active_ = false; - rtc::scoped_refptr local_certificate_; - absl::optional dtls_role_; - rtc::SSLProtocolVersion ssl_max_version_; - webrtc::CryptoOptions crypto_options_; - rtc::Buffer remote_fingerprint_value_; - std::string remote_fingerprint_algorithm_; - - // Cached DTLS ClientHello packet that was received before we started the - // DTLS handshake. This could happen if the hello was received before the - // ice transport became writable, or before a remote fingerprint was received. - rtc::Buffer cached_client_hello_; - - bool receiving_ = false; - bool writable_ = false; - - webrtc::RtcEventLog* const event_log_; - - RTC_DISALLOW_COPY_AND_ASSIGN(TgDtlsTransport); -}; - -} // namespace cricket - -#endif // P2P_BASE_DTLS_TRANSPORT_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.cpp deleted file mode 100644 index dbaaedc4b3..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.cpp +++ /dev/null @@ -1,868 +0,0 @@ -/* - * Copyright 2018 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_jsep_transport.h" - -#include -#include - -#include -#include -#include // for std::pair - -#include "api/array_view.h" -#include "api/candidate.h" -#include "p2p/base/p2p_constants.h" -#include "p2p/base/p2p_transport_channel.h" -#include "pc/sctp_data_channel_transport.h" -#include "rtc_base/checks.h" -#include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/logging.h" -#include "rtc_base/strings/string_builder.h" - -using webrtc::SdpType; - -namespace cricket { - -static bool VerifyIceParams(const TgJsepTransportDescription& jsep_description) { - // For legacy protocols. - // TODO(zhihuang): Remove this once the legacy protocol is no longer - // supported. - if (jsep_description.transport_desc.ice_ufrag.empty() && - jsep_description.transport_desc.ice_pwd.empty()) { - return true; - } - - if (jsep_description.transport_desc.ice_ufrag.length() < - ICE_UFRAG_MIN_LENGTH || - jsep_description.transport_desc.ice_ufrag.length() > - ICE_UFRAG_MAX_LENGTH) { - return false; - } - if (jsep_description.transport_desc.ice_pwd.length() < ICE_PWD_MIN_LENGTH || - jsep_description.transport_desc.ice_pwd.length() > ICE_PWD_MAX_LENGTH) { - return false; - } - return true; -} - -TgJsepTransportDescription::TgJsepTransportDescription() {} - -TgJsepTransportDescription::TgJsepTransportDescription( - bool rtcp_mux_enabled, - const std::vector& cryptos, - const std::vector& encrypted_header_extension_ids, - int rtp_abs_sendtime_extn_id, - const TransportDescription& transport_desc, - absl::optional media_alt_protocol, - absl::optional data_alt_protocol) - : rtcp_mux_enabled(rtcp_mux_enabled), - cryptos(cryptos), - encrypted_header_extension_ids(encrypted_header_extension_ids), - rtp_abs_sendtime_extn_id(rtp_abs_sendtime_extn_id), - transport_desc(transport_desc), - media_alt_protocol(media_alt_protocol), - data_alt_protocol(data_alt_protocol) {} - -TgJsepTransportDescription::TgJsepTransportDescription( - const TgJsepTransportDescription& from) - : rtcp_mux_enabled(from.rtcp_mux_enabled), - cryptos(from.cryptos), - encrypted_header_extension_ids(from.encrypted_header_extension_ids), - rtp_abs_sendtime_extn_id(from.rtp_abs_sendtime_extn_id), - transport_desc(from.transport_desc), - media_alt_protocol(from.media_alt_protocol), - data_alt_protocol(from.data_alt_protocol) {} - -TgJsepTransportDescription::~TgJsepTransportDescription() = default; - -TgJsepTransportDescription& TgJsepTransportDescription::operator=( - const TgJsepTransportDescription& from) { - if (this == &from) { - return *this; - } - rtcp_mux_enabled = from.rtcp_mux_enabled; - cryptos = from.cryptos; - encrypted_header_extension_ids = from.encrypted_header_extension_ids; - rtp_abs_sendtime_extn_id = from.rtp_abs_sendtime_extn_id; - transport_desc = from.transport_desc; - media_alt_protocol = from.media_alt_protocol; - data_alt_protocol = from.data_alt_protocol; - - return *this; -} - -TgJsepTransport::TgJsepTransport( - const std::string& mid, - const rtc::scoped_refptr& local_certificate, - rtc::scoped_refptr ice_transport, - rtc::scoped_refptr rtcp_ice_transport, - std::unique_ptr unencrypted_rtp_transport, - std::unique_ptr sdes_transport, - std::unique_ptr dtls_srtp_transport, - std::unique_ptr datagram_rtp_transport, - std::unique_ptr rtp_dtls_transport, - std::unique_ptr rtcp_dtls_transport, - std::unique_ptr sctp_transport, - std::unique_ptr datagram_transport, - webrtc::DataChannelTransportInterface* data_channel_transport) - : network_thread_(rtc::Thread::Current()), - mid_(mid), - local_certificate_(local_certificate), - ice_transport_(std::move(ice_transport)), - rtcp_ice_transport_(std::move(rtcp_ice_transport)), - unencrypted_rtp_transport_(std::move(unencrypted_rtp_transport)), - sdes_transport_(std::move(sdes_transport)), - dtls_srtp_transport_(std::move(dtls_srtp_transport)), - rtp_dtls_transport_( - rtp_dtls_transport ? new rtc::RefCountedObject( - std::move(rtp_dtls_transport)) - : nullptr), - rtcp_dtls_transport_( - rtcp_dtls_transport - ? new rtc::RefCountedObject( - std::move(rtcp_dtls_transport)) - : nullptr), - sctp_data_channel_transport_( - sctp_transport ? std::make_unique( - sctp_transport.get()) - : nullptr), - sctp_transport_(sctp_transport - ? new rtc::RefCountedObject( - std::move(sctp_transport)) - : nullptr), - datagram_transport_(std::move(datagram_transport)), - datagram_rtp_transport_(std::move(datagram_rtp_transport)), - data_channel_transport_(data_channel_transport) { - RTC_DCHECK(ice_transport_); - RTC_DCHECK(rtp_dtls_transport_); - // |rtcp_ice_transport_| must be present iff |rtcp_dtls_transport_| is - // present. - RTC_DCHECK_EQ((rtcp_ice_transport_ != nullptr), - (rtcp_dtls_transport_ != nullptr)); - // Verify the "only one out of these three can be set" invariant. - if (unencrypted_rtp_transport_) { - RTC_DCHECK(!sdes_transport); - RTC_DCHECK(!dtls_srtp_transport); - } else if (sdes_transport_) { - RTC_DCHECK(!unencrypted_rtp_transport); - RTC_DCHECK(!dtls_srtp_transport); - } else { - RTC_DCHECK(dtls_srtp_transport_); - RTC_DCHECK(!unencrypted_rtp_transport); - RTC_DCHECK(!sdes_transport); - } - - if (sctp_transport_) { - sctp_transport_->SetDtlsTransport(rtp_dtls_transport_); - } - - if (datagram_rtp_transport_ && default_rtp_transport()) { - composite_rtp_transport_ = std::make_unique( - std::vector{ - datagram_rtp_transport_.get(), default_rtp_transport()}); - } - - if (data_channel_transport_ && sctp_data_channel_transport_) { - composite_data_channel_transport_ = - std::make_unique( - std::vector{ - data_channel_transport_, sctp_data_channel_transport_.get()}); - } -} - -TgJsepTransport::~TgJsepTransport() { - if (sctp_transport_) { - sctp_transport_->Clear(); - } - - // Clear all DtlsTransports. There may be pointers to these from - // other places, so we can't assume they'll be deleted by the destructor. - rtp_dtls_transport_->Clear(); - if (rtcp_dtls_transport_) { - rtcp_dtls_transport_->Clear(); - } - - // ICE will be the last transport to be deleted. -} - -webrtc::RTCError TgJsepTransport::SetLocalJsepTransportDescription( - const TgJsepTransportDescription& jsep_description, - SdpType type) { - webrtc::RTCError error; - - RTC_DCHECK_RUN_ON(network_thread_); - if (!VerifyIceParams(jsep_description)) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Invalid ice-ufrag or ice-pwd length."); - } - - if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type, - ContentSource::CS_LOCAL)) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Failed to setup RTCP mux."); - } - - // If doing SDES, setup the SDES crypto parameters. - { - rtc::CritScope scope(&accessor_lock_); - if (sdes_transport_) { - RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!dtls_srtp_transport_); - if (!SetSdes(jsep_description.cryptos, - jsep_description.encrypted_header_extension_ids, type, - ContentSource::CS_LOCAL)) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Failed to setup SDES crypto parameters."); - } - } else if (dtls_srtp_transport_) { - RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!sdes_transport_); - dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds( - jsep_description.encrypted_header_extension_ids); - } - } - bool ice_restarting = - local_description_ != nullptr && - IceCredentialsChanged(local_description_->transport_desc.ice_ufrag, - local_description_->transport_desc.ice_pwd, - jsep_description.transport_desc.ice_ufrag, - jsep_description.transport_desc.ice_pwd); - local_description_.reset(new TgJsepTransportDescription(jsep_description)); - - rtc::SSLFingerprint* local_fp = - local_description_->transport_desc.identity_fingerprint.get(); - - if (!local_fp) { - local_certificate_ = nullptr; - } else { - error = VerifyCertificateFingerprint(local_certificate_, local_fp); - if (!error.ok()) { - local_description_.reset(); - return error; - } - } - { - rtc::CritScope scope(&accessor_lock_); - RTC_DCHECK(rtp_dtls_transport_->internal()); - SetLocalIceParameters(rtp_dtls_transport_->internal()->ice_transport()); - - if (rtcp_dtls_transport_) { - RTC_DCHECK(rtcp_dtls_transport_->internal()); - SetLocalIceParameters(rtcp_dtls_transport_->internal()->ice_transport()); - } - } - // If PRANSWER/ANSWER is set, we should decide transport protocol type. - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - error = NegotiateAndSetDtlsParameters(type); - NegotiateDatagramTransport(type); - } - if (!error.ok()) { - local_description_.reset(); - return error; - } - { - rtc::CritScope scope(&accessor_lock_); - if (needs_ice_restart_ && ice_restarting) { - needs_ice_restart_ = false; - RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag cleared for transport " - << mid(); - } - } - - return webrtc::RTCError::OK(); -} - -webrtc::RTCError TgJsepTransport::SetRemoteJsepTransportDescription( - const TgJsepTransportDescription& jsep_description, - webrtc::SdpType type) { - webrtc::RTCError error; - - RTC_DCHECK_RUN_ON(network_thread_); - if (!VerifyIceParams(jsep_description)) { - remote_description_.reset(); - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Invalid ice-ufrag or ice-pwd length."); - } - - if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type, - ContentSource::CS_REMOTE)) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Failed to setup RTCP mux."); - } - - // If doing SDES, setup the SDES crypto parameters. - { - rtc::CritScope lock(&accessor_lock_); - if (sdes_transport_) { - RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!dtls_srtp_transport_); - if (!SetSdes(jsep_description.cryptos, - jsep_description.encrypted_header_extension_ids, type, - ContentSource::CS_REMOTE)) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Failed to setup SDES crypto parameters."); - } - sdes_transport_->CacheRtpAbsSendTimeHeaderExtension( - jsep_description.rtp_abs_sendtime_extn_id); - } else if (dtls_srtp_transport_) { - RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!sdes_transport_); - dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds( - jsep_description.encrypted_header_extension_ids); - dtls_srtp_transport_->CacheRtpAbsSendTimeHeaderExtension( - jsep_description.rtp_abs_sendtime_extn_id); - } - } - - remote_description_.reset(new TgJsepTransportDescription(jsep_description)); - RTC_DCHECK(rtp_dtls_transport()); - SetRemoteIceParameters(rtp_dtls_transport()->ice_transport()); - - if (rtcp_dtls_transport()) { - SetRemoteIceParameters(rtcp_dtls_transport()->ice_transport()); - } - - // If PRANSWER/ANSWER is set, we should decide transport protocol type. - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - error = NegotiateAndSetDtlsParameters(SdpType::kOffer); - NegotiateDatagramTransport(type); - } - if (!error.ok()) { - remote_description_.reset(); - return error; - } - return webrtc::RTCError::OK(); -} - -webrtc::RTCError TgJsepTransport::AddRemoteCandidates( - const Candidates& candidates) { - RTC_DCHECK_RUN_ON(network_thread_); - if (!local_description_ || !remote_description_) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE, - mid() + - " is not ready to use the remote candidate " - "because the local or remote description is " - "not set."); - } - - for (const cricket::Candidate& candidate : candidates) { - auto transport = - candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP - ? rtp_dtls_transport_ - : rtcp_dtls_transport_; - if (!transport) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Candidate has an unknown component: " + - candidate.ToSensitiveString() + " for mid " + - mid()); - } - RTC_DCHECK(transport->internal() && transport->internal()->ice_transport()); - transport->internal()->ice_transport()->AddRemoteCandidate(candidate); - } - return webrtc::RTCError::OK(); -} - -void TgJsepTransport::SetNeedsIceRestartFlag() { - rtc::CritScope scope(&accessor_lock_); - if (!needs_ice_restart_) { - needs_ice_restart_ = true; - RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag set for transport " << mid(); - } -} - -absl::optional TgJsepTransport::GetDtlsRole() const { - RTC_DCHECK_RUN_ON(network_thread_); - rtc::CritScope scope(&accessor_lock_); - RTC_DCHECK(rtp_dtls_transport_); - RTC_DCHECK(rtp_dtls_transport_->internal()); - rtc::SSLRole dtls_role; - if (!rtp_dtls_transport_->internal()->GetDtlsRole(&dtls_role)) { - return absl::optional(); - } - - return absl::optional(dtls_role); -} - -absl::optional -TgJsepTransport::GetTransportParameters() const { - rtc::CritScope scope(&accessor_lock_); - if (!datagram_transport()) { - return absl::nullopt; - } - - OpaqueTransportParameters params; - params.parameters = datagram_transport()->GetTransportParameters(); - return params; -} - -bool TgJsepTransport::GetStats(TransportStats* stats) { - RTC_DCHECK_RUN_ON(network_thread_); - rtc::CritScope scope(&accessor_lock_); - stats->transport_name = mid(); - stats->channel_stats.clear(); - RTC_DCHECK(rtp_dtls_transport_->internal()); - bool ret = GetTransportStats(rtp_dtls_transport_->internal(), stats); - if (rtcp_dtls_transport_) { - RTC_DCHECK(rtcp_dtls_transport_->internal()); - ret &= GetTransportStats(rtcp_dtls_transport_->internal(), stats); - } - return ret; -} - -webrtc::RTCError TgJsepTransport::VerifyCertificateFingerprint( - const rtc::RTCCertificate* certificate, - const rtc::SSLFingerprint* fingerprint) const { - RTC_DCHECK_RUN_ON(network_thread_); - if (!fingerprint) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "No fingerprint"); - } - if (!certificate) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Fingerprint provided but no identity available."); - } - std::unique_ptr fp_tmp = - rtc::SSLFingerprint::CreateUnique(fingerprint->algorithm, - *certificate->identity()); - RTC_DCHECK(fp_tmp.get() != NULL); - if (*fp_tmp == *fingerprint) { - return webrtc::RTCError::OK(); - } - char ss_buf[1024]; - rtc::SimpleStringBuilder desc(ss_buf); - desc << "Local fingerprint does not match identity. Expected: "; - desc << fp_tmp->ToString(); - desc << " Got: " << fingerprint->ToString(); - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - std::string(desc.str())); -} - -void TgJsepTransport::SetActiveResetSrtpParams(bool active_reset_srtp_params) { - RTC_DCHECK_RUN_ON(network_thread_); - rtc::CritScope scope(&accessor_lock_); - if (dtls_srtp_transport_) { - RTC_LOG(INFO) - << "Setting active_reset_srtp_params of DtlsSrtpTransport to: " - << active_reset_srtp_params; - dtls_srtp_transport_->SetActiveResetSrtpParams(active_reset_srtp_params); - } -} - -void TgJsepTransport::SetLocalIceParameters(IceTransportInternal* ice_transport) { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK(ice_transport); - RTC_DCHECK(local_description_); - ice_transport->SetIceParameters( - local_description_->transport_desc.GetIceParameters()); -} - -void TgJsepTransport::SetRemoteIceParameters( - IceTransportInternal* ice_transport) { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK(ice_transport); - RTC_DCHECK(remote_description_); - ice_transport->SetRemoteIceParameters( - remote_description_->transport_desc.GetIceParameters()); - ice_transport->SetRemoteIceMode(remote_description_->transport_desc.ice_mode); -} - -webrtc::RTCError TgJsepTransport::SetNegotiatedDtlsParameters( - DtlsTransportInternal* dtls_transport, - absl::optional dtls_role, - rtc::SSLFingerprint* remote_fingerprint) { - RTC_DCHECK_RUN_ON(network_thread_); - RTC_DCHECK(dtls_transport); - // Set SSL role. Role must be set before fingerprint is applied, which - // initiates DTLS setup. - if (dtls_role && !dtls_transport->SetDtlsRole(*dtls_role)) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Failed to set SSL role for the transport."); - } - // Apply remote fingerprint. - if (!remote_fingerprint || - !dtls_transport->SetRemoteFingerprint( - remote_fingerprint->algorithm, remote_fingerprint->digest.cdata(), - remote_fingerprint->digest.size())) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "Failed to apply remote fingerprint."); - } - return webrtc::RTCError::OK(); -} - -bool TgJsepTransport::SetRtcpMux(bool enable, - webrtc::SdpType type, - ContentSource source) { - RTC_DCHECK_RUN_ON(network_thread_); - bool ret = false; - switch (type) { - case SdpType::kOffer: - ret = rtcp_mux_negotiator_.SetOffer(enable, source); - break; - case SdpType::kPrAnswer: - // This may activate RTCP muxing, but we don't yet destroy the transport - // because the final answer may deactivate it. - ret = rtcp_mux_negotiator_.SetProvisionalAnswer(enable, source); - break; - case SdpType::kAnswer: - ret = rtcp_mux_negotiator_.SetAnswer(enable, source); - if (ret && rtcp_mux_negotiator_.IsActive()) { - ActivateRtcpMux(); - } - break; - default: - RTC_NOTREACHED(); - } - - if (!ret) { - return false; - } - - auto transport = rtp_transport(); - transport->SetRtcpMuxEnabled(rtcp_mux_negotiator_.IsActive()); - return ret; -} - -void TgJsepTransport::ActivateRtcpMux() { - { - // Don't hold the network_thread_ lock while calling other functions, - // since they might call other functions that call RTC_DCHECK_RUN_ON. - // TODO(https://crbug.com/webrtc/10318): Simplify when possible. - RTC_DCHECK_RUN_ON(network_thread_); - } - { - rtc::CritScope scope(&accessor_lock_); - if (unencrypted_rtp_transport_) { - RTC_DCHECK(!sdes_transport_); - RTC_DCHECK(!dtls_srtp_transport_); - unencrypted_rtp_transport_->SetRtcpPacketTransport(nullptr); - } else if (sdes_transport_) { - RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!dtls_srtp_transport_); - sdes_transport_->SetRtcpPacketTransport(nullptr); - } else if (dtls_srtp_transport_) { - RTC_DCHECK(dtls_srtp_transport_); - RTC_DCHECK(!unencrypted_rtp_transport_); - RTC_DCHECK(!sdes_transport_); - dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport(), - /*rtcp_dtls_transport=*/nullptr); - } - rtcp_dtls_transport_ = nullptr; // Destroy this reference. - } - // Notify the JsepTransportController to update the aggregate states. - SignalRtcpMuxActive(); -} - -bool TgJsepTransport::SetSdes(const std::vector& cryptos, - const std::vector& encrypted_extension_ids, - webrtc::SdpType type, - ContentSource source) { - RTC_DCHECK_RUN_ON(network_thread_); - rtc::CritScope scope(&accessor_lock_); - bool ret = false; - ret = sdes_negotiator_.Process(cryptos, type, source); - if (!ret) { - return ret; - } - - if (source == ContentSource::CS_LOCAL) { - recv_extension_ids_ = encrypted_extension_ids; - } else { - send_extension_ids_ = encrypted_extension_ids; - } - - // If setting an SDES answer succeeded, apply the negotiated parameters - // to the SRTP transport. - if ((type == SdpType::kPrAnswer || type == SdpType::kAnswer) && ret) { - if (sdes_negotiator_.send_cipher_suite() && - sdes_negotiator_.recv_cipher_suite()) { - RTC_DCHECK(send_extension_ids_); - RTC_DCHECK(recv_extension_ids_); - ret = sdes_transport_->SetRtpParams( - *(sdes_negotiator_.send_cipher_suite()), - sdes_negotiator_.send_key().data(), - static_cast(sdes_negotiator_.send_key().size()), - *(send_extension_ids_), *(sdes_negotiator_.recv_cipher_suite()), - sdes_negotiator_.recv_key().data(), - static_cast(sdes_negotiator_.recv_key().size()), - *(recv_extension_ids_)); - } else { - RTC_LOG(LS_INFO) << "No crypto keys are provided for SDES."; - if (type == SdpType::kAnswer) { - // Explicitly reset the |sdes_transport_| if no crypto param is - // provided in the answer. No need to call |ResetParams()| for - // |sdes_negotiator_| because it resets the params inside |SetAnswer|. - sdes_transport_->ResetParams(); - } - } - } - return ret; -} - -webrtc::RTCError TgJsepTransport::NegotiateAndSetDtlsParameters( - SdpType local_description_type) { - RTC_DCHECK_RUN_ON(network_thread_); - if (!local_description_ || !remote_description_) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE, - "Applying an answer transport description " - "without applying any offer."); - } - std::unique_ptr remote_fingerprint; - absl::optional negotiated_dtls_role; - - rtc::SSLFingerprint* local_fp = - local_description_->transport_desc.identity_fingerprint.get(); - rtc::SSLFingerprint* remote_fp = - remote_description_->transport_desc.identity_fingerprint.get(); - if (remote_fp && local_fp) { - remote_fingerprint = std::make_unique(*remote_fp); - webrtc::RTCError error = - NegotiateDtlsRole(local_description_type, - local_description_->transport_desc.connection_role, - remote_description_->transport_desc.connection_role, - &negotiated_dtls_role); - if (!error.ok()) { - return error; - } - } else if (local_fp && (local_description_type == SdpType::kAnswer)) { - return webrtc::RTCError( - webrtc::RTCErrorType::INVALID_PARAMETER, - "Local fingerprint supplied when caller didn't offer DTLS."); - } else { - // We are not doing DTLS - remote_fingerprint = std::make_unique( - "", rtc::ArrayView()); - } - // Now that we have negotiated everything, push it downward. - // Note that we cache the result so that if we have race conditions - // between future SetRemote/SetLocal invocations and new transport - // creation, we have the negotiation state saved until a new - // negotiation happens. - RTC_DCHECK(rtp_dtls_transport()); - webrtc::RTCError error = SetNegotiatedDtlsParameters( - rtp_dtls_transport(), negotiated_dtls_role, remote_fingerprint.get()); - if (!error.ok()) { - return error; - } - - if (rtcp_dtls_transport()) { - error = SetNegotiatedDtlsParameters( - rtcp_dtls_transport(), negotiated_dtls_role, remote_fingerprint.get()); - } - return error; -} - -webrtc::RTCError TgJsepTransport::NegotiateDtlsRole( - SdpType local_description_type, - ConnectionRole local_connection_role, - ConnectionRole remote_connection_role, - absl::optional* negotiated_dtls_role) { - // From RFC 4145, section-4.1, The following are the values that the - // 'setup' attribute can take in an offer/answer exchange: - // Offer Answer - // ________________ - // active passive / holdconn - // passive active / holdconn - // actpass active / passive / holdconn - // holdconn holdconn - // - // Set the role that is most conformant with RFC 5763, Section 5, bullet 1 - // The endpoint MUST use the setup attribute defined in [RFC4145]. - // The endpoint that is the offerer MUST use the setup attribute - // value of setup:actpass and be prepared to receive a client_hello - // before it receives the answer. The answerer MUST use either a - // setup attribute value of setup:active or setup:passive. Note that - // if the answerer uses setup:passive, then the DTLS handshake will - // not begin until the answerer is received, which adds additional - // latency. setup:active allows the answer and the DTLS handshake to - // occur in parallel. Thus, setup:active is RECOMMENDED. Whichever - // party is active MUST initiate a DTLS handshake by sending a - // ClientHello over each flow (host/port quartet). - // IOW - actpass and passive modes should be treated as server and - // active as client. - bool is_remote_server = false; - if (local_description_type == SdpType::kOffer) { - if (local_connection_role != CONNECTIONROLE_ACTPASS) { - return webrtc::RTCError( - webrtc::RTCErrorType::INVALID_PARAMETER, - "Offerer must use actpass value for setup attribute."); - } - - if (remote_connection_role == CONNECTIONROLE_ACTIVE || - remote_connection_role == CONNECTIONROLE_PASSIVE || - remote_connection_role == CONNECTIONROLE_NONE) { - is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE); - } else { - return webrtc::RTCError( - webrtc::RTCErrorType::INVALID_PARAMETER, - "Answerer must use either active or passive value " - "for setup attribute."); - } - // If remote is NONE or ACTIVE it will act as client. - } else { - if (remote_connection_role != CONNECTIONROLE_ACTPASS && - remote_connection_role != CONNECTIONROLE_NONE) { - // Accept a remote role attribute that's not "actpass", but matches the - // current negotiated role. This is allowed by dtls-sdp, though our - // implementation will never generate such an offer as it's not - // recommended. - // - // See https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-dtls-sdp, - // section 5.5. - auto current_dtls_role = GetDtlsRole(); - if (!current_dtls_role || - (*current_dtls_role == rtc::SSL_CLIENT && - remote_connection_role == CONNECTIONROLE_ACTIVE) || - (*current_dtls_role == rtc::SSL_SERVER && - remote_connection_role == CONNECTIONROLE_PASSIVE)) { - return webrtc::RTCError( - webrtc::RTCErrorType::INVALID_PARAMETER, - "Offerer must use actpass value or current negotiated role for " - "setup attribute."); - } - } - - if (local_connection_role == CONNECTIONROLE_ACTIVE || - local_connection_role == CONNECTIONROLE_PASSIVE) { - is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE); - } else { - return webrtc::RTCError( - webrtc::RTCErrorType::INVALID_PARAMETER, - "Answerer must use either active or passive value " - "for setup attribute."); - } - - // If local is passive, local will act as server. - } - - *negotiated_dtls_role = - (is_remote_server ? rtc::SSL_CLIENT : rtc::SSL_SERVER); - return webrtc::RTCError::OK(); -} - -bool TgJsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport, - TransportStats* stats) { - RTC_DCHECK_RUN_ON(network_thread_); - rtc::CritScope scope(&accessor_lock_); - RTC_DCHECK(dtls_transport); - TransportChannelStats substats; - if (rtcp_dtls_transport_) { - substats.component = dtls_transport == rtcp_dtls_transport_->internal() - ? ICE_CANDIDATE_COMPONENT_RTCP - : ICE_CANDIDATE_COMPONENT_RTP; - } else { - substats.component = ICE_CANDIDATE_COMPONENT_RTP; - } - dtls_transport->GetSslVersionBytes(&substats.ssl_version_bytes); - dtls_transport->GetSrtpCryptoSuite(&substats.srtp_crypto_suite); - dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite); - substats.dtls_state = dtls_transport->dtls_state(); - if (!dtls_transport->ice_transport()->GetStats( - &substats.ice_transport_stats)) { - return false; - } - stats->channel_stats.push_back(substats); - return true; -} - -void TgJsepTransport::NegotiateDatagramTransport(SdpType type) { - RTC_DCHECK(type == SdpType::kAnswer || type == SdpType::kPrAnswer); - rtc::CritScope lock(&accessor_lock_); - if (!datagram_transport_) { - return; // No need to negotiate the use of datagram transport. - } - - bool compatible_datagram_transport = - remote_description_->transport_desc.opaque_parameters && - remote_description_->transport_desc.opaque_parameters == - local_description_->transport_desc.opaque_parameters; - - bool use_datagram_transport_for_media = - compatible_datagram_transport && - remote_description_->media_alt_protocol == - remote_description_->transport_desc.opaque_parameters->protocol && - remote_description_->media_alt_protocol == - local_description_->media_alt_protocol; - - bool use_datagram_transport_for_data = - compatible_datagram_transport && - remote_description_->data_alt_protocol == - remote_description_->transport_desc.opaque_parameters->protocol && - remote_description_->data_alt_protocol == - local_description_->data_alt_protocol; - - RTC_LOG(LS_INFO) - << "Negotiating datagram transport, use_datagram_transport_for_media=" - << use_datagram_transport_for_media - << ", use_datagram_transport_for_data=" << use_datagram_transport_for_data - << " answer type=" << (type == SdpType::kAnswer ? "answer" : "pr_answer"); - - // A provisional or full or answer lets the peer start sending on one of the - // transports. - if (composite_rtp_transport_) { - composite_rtp_transport_->SetSendTransport( - use_datagram_transport_for_media ? datagram_rtp_transport_.get() - : default_rtp_transport()); - } - if (composite_data_channel_transport_) { - composite_data_channel_transport_->SetSendTransport( - use_datagram_transport_for_data ? data_channel_transport_ - : sctp_data_channel_transport_.get()); - } - - if (type != SdpType::kAnswer) { - return; - } - - if (composite_rtp_transport_) { - if (use_datagram_transport_for_media) { - // Negotiated use of datagram transport for RTP, so remove the - // non-datagram RTP transport. - composite_rtp_transport_->RemoveTransport(default_rtp_transport()); - if (unencrypted_rtp_transport_) { - unencrypted_rtp_transport_ = nullptr; - } else if (sdes_transport_) { - sdes_transport_ = nullptr; - } else { - dtls_srtp_transport_ = nullptr; - } - } else { - composite_rtp_transport_->RemoveTransport(datagram_rtp_transport_.get()); - datagram_rtp_transport_ = nullptr; - } - } - - if (composite_data_channel_transport_) { - if (use_datagram_transport_for_data) { - // Negotiated use of datagram transport for data channels, so remove the - // non-datagram data channel transport. - composite_data_channel_transport_->RemoveTransport( - sctp_data_channel_transport_.get()); - sctp_data_channel_transport_ = nullptr; - sctp_transport_ = nullptr; - } else { - composite_data_channel_transport_->RemoveTransport( - data_channel_transport_); - data_channel_transport_ = nullptr; - } - } else if (data_channel_transport_ && !use_datagram_transport_for_data) { - // The datagram transport has been rejected without a fallback. We still - // need to inform the application and delete it. - SignalDataChannelTransportNegotiated(this, nullptr); - data_channel_transport_ = nullptr; - } - - if (!use_datagram_transport_for_media && !use_datagram_transport_for_data) { - // Datagram transport is not being used for anything, so clean it up. - datagram_transport_ = nullptr; - } -} - -} // namespace cricket diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.h b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.h deleted file mode 100644 index 9959f0346a..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport.h +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright 2018 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TG_PC_JSEP_TRANSPORT_H_ -#define TG_PC_JSEP_TRANSPORT_H_ - -#include -#include -#include -#include - -#include "absl/types/optional.h" -#include "api/candidate.h" -#include "api/ice_transport_interface.h" -#include "api/jsep.h" -#include "api/transport/datagram_transport_interface.h" -#include "media/sctp/sctp_transport_internal.h" -#include "p2p/base/dtls_transport.h" -#include "p2p/base/p2p_constants.h" -#include "p2p/base/transport_info.h" -#include "pc/composite_data_channel_transport.h" -#include "pc/composite_rtp_transport.h" -#include "pc/dtls_srtp_transport.h" -#include "pc/dtls_transport.h" -#include "pc/rtcp_mux_filter.h" -#include "pc/rtp_transport.h" -#include "pc/sctp_transport.h" -#include "pc/session_description.h" -#include "pc/srtp_filter.h" -#include "pc/srtp_transport.h" -#include "pc/transport_stats.h" -#include "rtc_base/constructor_magic.h" -#include "rtc_base/message_queue.h" -#include "rtc_base/rtc_certificate.h" -#include "rtc_base/ssl_stream_adapter.h" -#include "rtc_base/third_party/sigslot/sigslot.h" -#include "rtc_base/thread_checker.h" - -#include "tg_rtp_transport.h" - -namespace cricket { - -class DtlsTransportInternal; - -struct TgJsepTransportDescription { - public: - TgJsepTransportDescription(); - TgJsepTransportDescription( - bool rtcp_mux_enabled, - const std::vector& cryptos, - const std::vector& encrypted_header_extension_ids, - int rtp_abs_sendtime_extn_id, - const TransportDescription& transport_description, - absl::optional media_alt_protocol, - absl::optional data_alt_protocol); - TgJsepTransportDescription(const TgJsepTransportDescription& from); - ~TgJsepTransportDescription(); - - TgJsepTransportDescription& operator=(const TgJsepTransportDescription& from); - - bool rtcp_mux_enabled = true; - std::vector cryptos; - std::vector encrypted_header_extension_ids; - int rtp_abs_sendtime_extn_id = -1; - // TODO(zhihuang): Add the ICE and DTLS related variables and methods from - // TransportDescription and remove this extra layer of abstraction. - TransportDescription transport_desc; - - // Alt-protocols that apply to this TgJsepTransport. Presence indicates a - // request to use an alternative protocol for media and/or data. The - // alt-protocol is handled by a datagram transport. If one or both of these - // values are present, TgJsepTransport will attempt to negotiate use of the - // datagram transport for media and/or data. - absl::optional media_alt_protocol; - absl::optional data_alt_protocol; -}; - -// Helper class used by TgJsepTransportController that processes -// TransportDescriptions. A TransportDescription represents the -// transport-specific properties of an SDP m= section, processed according to -// JSEP. Each transport consists of DTLS and ICE transport channels for RTP -// (and possibly RTCP, if rtcp-mux isn't used). -// -// On Threading: TgJsepTransport performs work solely on the network thread, and -// so its methods should only be called on the network thread. -class TgJsepTransport : public sigslot::has_slots<> { - public: - // |mid| is just used for log statements in order to identify the Transport. - // Note that |local_certificate| is allowed to be null since a remote - // description may be set before a local certificate is generated. - TgJsepTransport( - const std::string& mid, - const rtc::scoped_refptr& local_certificate, - rtc::scoped_refptr ice_transport, - rtc::scoped_refptr rtcp_ice_transport, - std::unique_ptr unencrypted_rtp_transport, - std::unique_ptr sdes_transport, - std::unique_ptr dtls_srtp_transport, - std::unique_ptr datagram_rtp_transport, - std::unique_ptr rtp_dtls_transport, - std::unique_ptr rtcp_dtls_transport, - std::unique_ptr sctp_transport, - std::unique_ptr datagram_transport, - webrtc::DataChannelTransportInterface* data_channel_transport); - - ~TgJsepTransport() override; - - // Returns the MID of this transport. This is only used for logging. - const std::string& mid() const { return mid_; } - - // Must be called before applying local session description. - // Needed in order to verify the local fingerprint. - void SetLocalCertificate( - const rtc::scoped_refptr& local_certificate) { - RTC_DCHECK_RUN_ON(network_thread_); - local_certificate_ = local_certificate; - } - - // Return the local certificate provided by SetLocalCertificate. - rtc::scoped_refptr GetLocalCertificate() const { - RTC_DCHECK_RUN_ON(network_thread_); - return local_certificate_; - } - - webrtc::RTCError SetLocalJsepTransportDescription( - const TgJsepTransportDescription& jsep_description, - webrtc::SdpType type); - - // Set the remote TransportDescription to be used by DTLS and ICE channels - // that are part of this Transport. - webrtc::RTCError SetRemoteJsepTransportDescription( - const TgJsepTransportDescription& jsep_description, - webrtc::SdpType type); - webrtc::RTCError AddRemoteCandidates(const Candidates& candidates); - - // Set the "needs-ice-restart" flag as described in JSEP. After the flag is - // set, offers should generate new ufrags/passwords until an ICE restart - // occurs. - // - // This and the below method can be called safely from any thread as long as - // SetXTransportDescription is not in progress. - void SetNeedsIceRestartFlag(); - // Returns true if the ICE restart flag above was set, and no ICE restart has - // occurred yet for this transport (by applying a local description with - // changed ufrag/password). - bool needs_ice_restart() const { - rtc::CritScope scope(&accessor_lock_); - return needs_ice_restart_; - } - - // Returns role if negotiated, or empty absl::optional if it hasn't been - // negotiated yet. - absl::optional GetDtlsRole() const; - - absl::optional GetTransportParameters() const; - - // TODO(deadbeef): Make this const. See comment in transportcontroller.h. - bool GetStats(TransportStats* stats); - - const TgJsepTransportDescription* local_description() const { - RTC_DCHECK_RUN_ON(network_thread_); - return local_description_.get(); - } - - const TgJsepTransportDescription* remote_description() const { - RTC_DCHECK_RUN_ON(network_thread_); - return remote_description_.get(); - } - - webrtc::RtpTransportInternal* rtp_transport() const { - rtc::CritScope scope(&accessor_lock_); - if (composite_rtp_transport_) { - return composite_rtp_transport_.get(); - } else if (datagram_rtp_transport_) { - return datagram_rtp_transport_.get(); - } else { - return default_rtp_transport(); - } - } - - const DtlsTransportInternal* rtp_dtls_transport() const { - rtc::CritScope scope(&accessor_lock_); - if (rtp_dtls_transport_) { - return rtp_dtls_transport_->internal(); - } else { - return nullptr; - } - } - - DtlsTransportInternal* rtp_dtls_transport() { - rtc::CritScope scope(&accessor_lock_); - if (rtp_dtls_transport_) { - return rtp_dtls_transport_->internal(); - } else { - return nullptr; - } - } - - const DtlsTransportInternal* rtcp_dtls_transport() const { - rtc::CritScope scope(&accessor_lock_); - if (rtcp_dtls_transport_) { - return rtcp_dtls_transport_->internal(); - } else { - return nullptr; - } - } - - DtlsTransportInternal* rtcp_dtls_transport() { - rtc::CritScope scope(&accessor_lock_); - if (rtcp_dtls_transport_) { - return rtcp_dtls_transport_->internal(); - } else { - return nullptr; - } - } - - rtc::scoped_refptr RtpDtlsTransport() { - rtc::CritScope scope(&accessor_lock_); - return rtp_dtls_transport_; - } - - rtc::scoped_refptr SctpTransport() const { - rtc::CritScope scope(&accessor_lock_); - return sctp_transport_; - } - - webrtc::DataChannelTransportInterface* data_channel_transport() const { - rtc::CritScope scope(&accessor_lock_); - if (composite_data_channel_transport_) { - return composite_data_channel_transport_.get(); - } else if (sctp_data_channel_transport_) { - return sctp_data_channel_transport_.get(); - } - return data_channel_transport_; - } - - // Returns datagram transport, if available. - webrtc::DatagramTransportInterface* datagram_transport() const { - rtc::CritScope scope(&accessor_lock_); - return datagram_transport_.get(); - } - - // This is signaled when RTCP-mux becomes active and - // |rtcp_dtls_transport_| is destroyed. The TgJsepTransportController will - // handle the signal and update the aggregate transport states. - sigslot::signal<> SignalRtcpMuxActive; - - // Signals that a data channel transport was negotiated and may be used to - // send data. The first parameter is |this|. The second parameter is the - // transport that was negotiated, or null if negotiation rejected the data - // channel transport. The third parameter (bool) indicates whether the - // negotiation was provisional or final. If true, it is provisional, if - // false, it is final. - sigslot::signal2 - SignalDataChannelTransportNegotiated; - - // TODO(deadbeef): The methods below are only public for testing. Should make - // them utility functions or objects so they can be tested independently from - // this class. - - // Returns an error if the certificate's identity does not match the - // fingerprint, or either is NULL. - webrtc::RTCError VerifyCertificateFingerprint( - const rtc::RTCCertificate* certificate, - const rtc::SSLFingerprint* fingerprint) const; - - void SetActiveResetSrtpParams(bool active_reset_srtp_params); - - private: - bool SetRtcpMux(bool enable, webrtc::SdpType type, ContentSource source); - - void ActivateRtcpMux(); - - bool SetSdes(const std::vector& cryptos, - const std::vector& encrypted_extension_ids, - webrtc::SdpType type, - ContentSource source); - - // Negotiates and sets the DTLS parameters based on the current local and - // remote transport description, such as the DTLS role to use, and whether - // DTLS should be activated. - // - // Called when an answer TransportDescription is applied. - webrtc::RTCError NegotiateAndSetDtlsParameters( - webrtc::SdpType local_description_type); - - // Negotiates the DTLS role based off the offer and answer as specified by - // RFC 4145, section-4.1. Returns an RTCError if role cannot be determined - // from the local description and remote description. - webrtc::RTCError NegotiateDtlsRole( - webrtc::SdpType local_description_type, - ConnectionRole local_connection_role, - ConnectionRole remote_connection_role, - absl::optional* negotiated_dtls_role); - - // Pushes down the ICE parameters from the local description, such - // as the ICE ufrag and pwd. - void SetLocalIceParameters(IceTransportInternal* ice); - - // Pushes down the ICE parameters from the remote description. - void SetRemoteIceParameters(IceTransportInternal* ice); - - // Pushes down the DTLS parameters obtained via negotiation. - webrtc::RTCError SetNegotiatedDtlsParameters( - DtlsTransportInternal* dtls_transport, - absl::optional dtls_role, - rtc::SSLFingerprint* remote_fingerprint); - - bool GetTransportStats(DtlsTransportInternal* dtls_transport, - TransportStats* stats); - - // Deactivates, signals removal, and deletes |composite_rtp_transport_| if the - // current state of negotiation is sufficient to determine which rtp_transport - // and data channel transport to use. - void NegotiateDatagramTransport(webrtc::SdpType type) - RTC_RUN_ON(network_thread_); - - // Returns the default (non-datagram) rtp transport, if any. - webrtc::RtpTransportInternal* default_rtp_transport() const - RTC_EXCLUSIVE_LOCKS_REQUIRED(accessor_lock_) { - if (dtls_srtp_transport_) { - return dtls_srtp_transport_.get(); - } else if (sdes_transport_) { - return sdes_transport_.get(); - } else if (unencrypted_rtp_transport_) { - return unencrypted_rtp_transport_.get(); - } else { - return nullptr; - } - } - - // Owning thread, for safety checks - const rtc::Thread* const network_thread_; - // Critical scope for fields accessed off-thread - // TODO(https://bugs.webrtc.org/10300): Stop doing this. - rtc::CriticalSection accessor_lock_; - const std::string mid_; - // needs-ice-restart bit as described in JSEP. - bool needs_ice_restart_ RTC_GUARDED_BY(accessor_lock_) = false; - rtc::scoped_refptr local_certificate_ - RTC_GUARDED_BY(network_thread_); - std::unique_ptr local_description_ - RTC_GUARDED_BY(network_thread_); - std::unique_ptr remote_description_ - RTC_GUARDED_BY(network_thread_); - - // Ice transport which may be used by any of upper-layer transports (below). - // Owned by TgJsepTransport and guaranteed to outlive the transports below. - const rtc::scoped_refptr ice_transport_; - const rtc::scoped_refptr rtcp_ice_transport_; - - // To avoid downcasting and make it type safe, keep three unique pointers for - // different SRTP mode and only one of these is non-nullptr. - std::unique_ptr unencrypted_rtp_transport_ - RTC_GUARDED_BY(accessor_lock_); - std::unique_ptr sdes_transport_ - RTC_GUARDED_BY(accessor_lock_); - std::unique_ptr dtls_srtp_transport_ - RTC_GUARDED_BY(accessor_lock_); - - // If multiple RTP transports are in use, |composite_rtp_transport_| will be - // passed to callers. This is only valid for offer-only, receive-only - // scenarios, as it is not possible for the composite to correctly choose - // which transport to use for sending. - std::unique_ptr composite_rtp_transport_ - RTC_GUARDED_BY(accessor_lock_); - - rtc::scoped_refptr rtp_dtls_transport_ - RTC_GUARDED_BY(accessor_lock_); - rtc::scoped_refptr rtcp_dtls_transport_ - RTC_GUARDED_BY(accessor_lock_); - rtc::scoped_refptr datagram_dtls_transport_ - RTC_GUARDED_BY(accessor_lock_); - - std::unique_ptr - sctp_data_channel_transport_ RTC_GUARDED_BY(accessor_lock_); - rtc::scoped_refptr sctp_transport_ - RTC_GUARDED_BY(accessor_lock_); - - SrtpFilter sdes_negotiator_ RTC_GUARDED_BY(network_thread_); - RtcpMuxFilter rtcp_mux_negotiator_ RTC_GUARDED_BY(network_thread_); - - // Cache the encrypted header extension IDs for SDES negoitation. - absl::optional> send_extension_ids_ - RTC_GUARDED_BY(network_thread_); - absl::optional> recv_extension_ids_ - RTC_GUARDED_BY(network_thread_); - - // Optional datagram transport (experimental). - std::unique_ptr datagram_transport_ - RTC_GUARDED_BY(accessor_lock_); - - std::unique_ptr datagram_rtp_transport_ - RTC_GUARDED_BY(accessor_lock_); - - // Non-SCTP data channel transport. Set to |datagram_transport_| if that - // transport should be used for data chanels. Unset otherwise. - webrtc::DataChannelTransportInterface* data_channel_transport_ - RTC_GUARDED_BY(accessor_lock_) = nullptr; - - // Composite data channel transport, used during negotiation. - std::unique_ptr - composite_data_channel_transport_ RTC_GUARDED_BY(accessor_lock_); - - RTC_DISALLOW_COPY_AND_ASSIGN(TgJsepTransport); -}; - -} // namespace cricket - -#endif // PC_JSEP_TRANSPORT_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp deleted file mode 100644 index 3c5c11d1f6..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.cpp +++ /dev/null @@ -1,1699 +0,0 @@ -/* - * Copyright 2017 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_jsep_transport_controller.h" - -#include -#include - -#include "absl/algorithm/container.h" -#include "api/ice_transport_factory.h" -#include "api/transport/datagram_transport_interface.h" -#include "api/transport/media/media_transport_interface.h" -#include "p2p/base/ice_transport_internal.h" -#include "p2p/base/port.h" -#include "pc/datagram_rtp_transport.h" -#include "pc/srtp_filter.h" -#include "rtc_base/bind.h" -#include "rtc_base/checks.h" -#include "rtc_base/thread.h" - -#include "tg_dtls_transport.h" - -using webrtc::SdpType; - -namespace { - -webrtc::RTCError VerifyCandidate(const cricket::Candidate& cand) { - // No address zero. - if (cand.address().IsNil() || cand.address().IsAnyIP()) { - return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, - "candidate has address of zero"); - } - - // Disallow all ports below 1024, except for 80 and 443 on public addresses. - int port = cand.address().port(); - if (cand.protocol() == cricket::TCP_PROTOCOL_NAME && - (cand.tcptype() == cricket::TCPTYPE_ACTIVE_STR || port == 0)) { - // Expected for active-only candidates per - // http://tools.ietf.org/html/rfc6544#section-4.5 so no error. - // Libjingle clients emit port 0, in "active" mode. - return webrtc::RTCError::OK(); - } - if (port < 1024) { - if ((port != 80) && (port != 443)) { - return webrtc::RTCError( - webrtc::RTCErrorType::INVALID_PARAMETER, - "candidate has port below 1024, but not 80 or 443"); - } - - if (cand.address().IsPrivateIP()) { - return webrtc::RTCError( - webrtc::RTCErrorType::INVALID_PARAMETER, - "candidate has port of 80 or 443 with private IP address"); - } - } - - return webrtc::RTCError::OK(); -} - -webrtc::RTCError VerifyCandidates(const cricket::Candidates& candidates) { - for (const cricket::Candidate& candidate : candidates) { - webrtc::RTCError error = VerifyCandidate(candidate); - if (!error.ok()) { - return error; - } - } - return webrtc::RTCError::OK(); -} - -} // namespace - -namespace webrtc { - -TgJsepTransportController::TgJsepTransportController( - rtc::Thread* signaling_thread, - rtc::Thread* network_thread, - cricket::PortAllocator* port_allocator, - AsyncResolverFactory* async_resolver_factory, - Config config) - : signaling_thread_(signaling_thread), - network_thread_(network_thread), - port_allocator_(port_allocator), - async_resolver_factory_(async_resolver_factory), - config_(config) { - // The |transport_observer| is assumed to be non-null. - RTC_DCHECK(config_.transport_observer); - RTC_DCHECK(config_.rtcp_handler); - RTC_DCHECK(config_.ice_transport_factory); -} - -TgJsepTransportController::~TgJsepTransportController() { - // Channel destructors may try to send packets, so this needs to happen on - // the network thread. - network_thread_->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgJsepTransportController::DestroyAllJsepTransports_n, this)); -} - -RTCError TgJsepTransportController::SetLocalDescription( - SdpType type, - const cricket::SessionDescription* description) { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke( - RTC_FROM_HERE, [=] { return SetLocalDescription(type, description); }); - } - - if (!initial_offerer_.has_value()) { - initial_offerer_.emplace(type == SdpType::kOffer); - if (*initial_offerer_) { - SetIceRole_n(cricket::ICEROLE_CONTROLLING); - } else { - SetIceRole_n(cricket::ICEROLE_CONTROLLED); - } - } - return ApplyDescription_n(/*local=*/true, type, description); -} - -RTCError TgJsepTransportController::SetRemoteDescription( - SdpType type, - const cricket::SessionDescription* description) { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke( - RTC_FROM_HERE, [=] { return SetRemoteDescription(type, description); }); - } - - return ApplyDescription_n(/*local=*/false, type, description); -} - -RtpTransportInternal* TgJsepTransportController::GetRtpTransport( - const std::string& mid) const { - auto jsep_transport = GetJsepTransportForMid(mid); - if (!jsep_transport) { - return nullptr; - } - return jsep_transport->rtp_transport(); -} - -MediaTransportConfig TgJsepTransportController::GetMediaTransportConfig( - const std::string& mid) const { - auto jsep_transport = GetJsepTransportForMid(mid); - if (!jsep_transport) { - return MediaTransportConfig(); - } - - DatagramTransportInterface* datagram_transport = nullptr; - if (config_.use_datagram_transport) { - datagram_transport = jsep_transport->datagram_transport(); - } - - if (datagram_transport) { - return MediaTransportConfig( - /*rtp_max_packet_size=*/datagram_transport->GetLargestDatagramSize()); - } else { - return MediaTransportConfig(); - } -} - -DataChannelTransportInterface* TgJsepTransportController::GetDataChannelTransport( - const std::string& mid) const { - auto jsep_transport = GetJsepTransportForMid(mid); - if (!jsep_transport) { - return nullptr; - } - return jsep_transport->data_channel_transport(); -} - -cricket::DtlsTransportInternal* TgJsepTransportController::GetDtlsTransport( - const std::string& mid) { - auto jsep_transport = GetJsepTransportForMid(mid); - if (!jsep_transport) { - return nullptr; - } - return jsep_transport->rtp_dtls_transport(); -} - -const cricket::DtlsTransportInternal* -TgJsepTransportController::GetRtcpDtlsTransport(const std::string& mid) const { - auto jsep_transport = GetJsepTransportForMid(mid); - if (!jsep_transport) { - return nullptr; - } - return jsep_transport->rtcp_dtls_transport(); -} - -rtc::scoped_refptr -TgJsepTransportController::LookupDtlsTransportByMid(const std::string& mid) { - auto jsep_transport = GetJsepTransportForMid(mid); - if (!jsep_transport) { - return nullptr; - } - return jsep_transport->RtpDtlsTransport(); -} - -rtc::scoped_refptr TgJsepTransportController::GetSctpTransport( - const std::string& mid) const { - auto jsep_transport = GetJsepTransportForMid(mid); - if (!jsep_transport) { - return nullptr; - } - return jsep_transport->SctpTransport(); -} - -void TgJsepTransportController::SetIceConfig(const cricket::IceConfig& config) { - if (!network_thread_->IsCurrent()) { - network_thread_->Invoke(RTC_FROM_HERE, [&] { SetIceConfig(config); }); - return; - } - - ice_config_ = config; - for (auto& dtls : GetDtlsTransports()) { - dtls->ice_transport()->SetIceConfig(ice_config_); - } -} - -void TgJsepTransportController::SetNeedsIceRestartFlag() { - for (auto& kv : jsep_transports_by_name_) { - kv.second->SetNeedsIceRestartFlag(); - } -} - -bool TgJsepTransportController::NeedsIceRestart( - const std::string& transport_name) const { - const cricket::TgJsepTransport* transport = - GetJsepTransportByName(transport_name); - if (!transport) { - return false; - } - return transport->needs_ice_restart(); -} - -absl::optional TgJsepTransportController::GetDtlsRole( - const std::string& mid) const { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke>( - RTC_FROM_HERE, [&] { return GetDtlsRole(mid); }); - } - - const cricket::TgJsepTransport* t = GetJsepTransportForMid(mid); - if (!t) { - return absl::optional(); - } - return t->GetDtlsRole(); -} - -bool TgJsepTransportController::SetLocalCertificate( - const rtc::scoped_refptr& certificate) { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke( - RTC_FROM_HERE, [&] { return SetLocalCertificate(certificate); }); - } - - // Can't change a certificate, or set a null certificate. - if (certificate_ || !certificate) { - return false; - } - certificate_ = certificate; - - // Set certificate for TgJsepTransport, which verifies it matches the - // fingerprint in SDP, and DTLS transport. - // Fallback from DTLS to SDES is not supported. - for (auto& kv : jsep_transports_by_name_) { - kv.second->SetLocalCertificate(certificate_); - } - for (auto& dtls : GetDtlsTransports()) { - bool set_cert_success = dtls->SetLocalCertificate(certificate_); - RTC_DCHECK(set_cert_success); - } - return true; -} - -rtc::scoped_refptr -TgJsepTransportController::GetLocalCertificate( - const std::string& transport_name) const { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke>( - RTC_FROM_HERE, [&] { return GetLocalCertificate(transport_name); }); - } - - const cricket::TgJsepTransport* t = GetJsepTransportByName(transport_name); - if (!t) { - return nullptr; - } - return t->GetLocalCertificate(); -} - -std::unique_ptr -TgJsepTransportController::GetRemoteSSLCertChain( - const std::string& transport_name) const { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke>( - RTC_FROM_HERE, [&] { return GetRemoteSSLCertChain(transport_name); }); - } - - // Get the certificate from the RTP transport's DTLS handshake. Should be - // identical to the RTCP transport's, since they were given the same remote - // fingerprint. - auto jsep_transport = GetJsepTransportByName(transport_name); - if (!jsep_transport) { - return nullptr; - } - auto dtls = jsep_transport->rtp_dtls_transport(); - if (!dtls) { - return nullptr; - } - - return dtls->GetRemoteSSLCertChain(); -} - -void TgJsepTransportController::MaybeStartGathering() { - if (!network_thread_->IsCurrent()) { - network_thread_->Invoke(RTC_FROM_HERE, - [&] { MaybeStartGathering(); }); - return; - } - - for (auto& dtls : GetDtlsTransports()) { - dtls->ice_transport()->MaybeStartGathering(); - } -} - -RTCError TgJsepTransportController::AddRemoteCandidates( - const std::string& transport_name, - const cricket::Candidates& candidates) { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke(RTC_FROM_HERE, [&] { - return AddRemoteCandidates(transport_name, candidates); - }); - } - - // Verify each candidate before passing down to the transport layer. - RTCError error = VerifyCandidates(candidates); - if (!error.ok()) { - return error; - } - auto jsep_transport = GetJsepTransportByName(transport_name); - if (!jsep_transport) { - RTC_LOG(LS_WARNING) << "Not adding candidate because the TgJsepTransport " - "doesn't exist. Ignore it."; - return RTCError::OK(); - } - return jsep_transport->AddRemoteCandidates(candidates); -} - -RTCError TgJsepTransportController::RemoveRemoteCandidates( - const cricket::Candidates& candidates) { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke( - RTC_FROM_HERE, [&] { return RemoveRemoteCandidates(candidates); }); - } - - // Verify each candidate before passing down to the transport layer. - RTCError error = VerifyCandidates(candidates); - if (!error.ok()) { - return error; - } - - std::map candidates_by_transport_name; - for (const cricket::Candidate& cand : candidates) { - if (!cand.transport_name().empty()) { - candidates_by_transport_name[cand.transport_name()].push_back(cand); - } else { - RTC_LOG(LS_ERROR) << "Not removing candidate because it does not have a " - "transport name set: " - << cand.ToSensitiveString(); - } - } - - for (const auto& kv : candidates_by_transport_name) { - const std::string& transport_name = kv.first; - const cricket::Candidates& candidates = kv.second; - cricket::TgJsepTransport* jsep_transport = - GetJsepTransportByName(transport_name); - if (!jsep_transport) { - RTC_LOG(LS_WARNING) - << "Not removing candidate because the TgJsepTransport doesn't exist."; - continue; - } - for (const cricket::Candidate& candidate : candidates) { - cricket::DtlsTransportInternal* dtls = - candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP - ? jsep_transport->rtp_dtls_transport() - : jsep_transport->rtcp_dtls_transport(); - if (dtls) { - dtls->ice_transport()->RemoveRemoteCandidate(candidate); - } - } - } - return RTCError::OK(); -} - -bool TgJsepTransportController::GetStats(const std::string& transport_name, - cricket::TransportStats* stats) { - if (!network_thread_->IsCurrent()) { - return network_thread_->Invoke( - RTC_FROM_HERE, [=] { return GetStats(transport_name, stats); }); - } - - cricket::TgJsepTransport* transport = GetJsepTransportByName(transport_name); - if (!transport) { - return false; - } - return transport->GetStats(stats); -} - -void TgJsepTransportController::SetActiveResetSrtpParams( - bool active_reset_srtp_params) { - if (!network_thread_->IsCurrent()) { - network_thread_->Invoke(RTC_FROM_HERE, [=] { - SetActiveResetSrtpParams(active_reset_srtp_params); - }); - return; - } - - RTC_LOG(INFO) - << "Updating the active_reset_srtp_params for JsepTransportController: " - << active_reset_srtp_params; - config_.active_reset_srtp_params = active_reset_srtp_params; - for (auto& kv : jsep_transports_by_name_) { - kv.second->SetActiveResetSrtpParams(active_reset_srtp_params); - } -} - -void TgJsepTransportController::SetMediaTransportSettings( - bool use_datagram_transport, - bool use_datagram_transport_for_data_channels, - bool use_datagram_transport_for_data_channels_receive_only) { - config_.use_datagram_transport = use_datagram_transport; - config_.use_datagram_transport_for_data_channels = - use_datagram_transport_for_data_channels; - config_.use_datagram_transport_for_data_channels_receive_only = - use_datagram_transport_for_data_channels_receive_only; -} - -void TgJsepTransportController::RollbackTransportForMids( - const std::vector& mids) { - if (!network_thread_->IsCurrent()) { - network_thread_->Invoke(RTC_FROM_HERE, - [=] { RollbackTransportForMids(mids); }); - return; - } - for (auto&& mid : mids) { - RemoveTransportForMid(mid); - } - for (auto&& mid : mids) { - MaybeDestroyJsepTransport(mid); - } -} - -rtc::scoped_refptr -TgJsepTransportController::CreateIceTransport(const std::string& transport_name, - bool rtcp) { - int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP - : cricket::ICE_CANDIDATE_COMPONENT_RTP; - - IceTransportInit init; - init.set_port_allocator(port_allocator_); - init.set_async_resolver_factory(async_resolver_factory_); - init.set_event_log(config_.event_log); - return config_.ice_transport_factory->CreateIceTransport( - transport_name, component, std::move(init)); -} - -std::unique_ptr -TgJsepTransportController::CreateDtlsTransport( - const cricket::ContentInfo& content_info, - cricket::IceTransportInternal* ice, - DatagramTransportInterface* datagram_transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - - std::unique_ptr dtls; - - if (datagram_transport) { - RTC_DCHECK(config_.use_datagram_transport || - config_.use_datagram_transport_for_data_channels); - } else if (config_.dtls_transport_factory) { - dtls = config_.dtls_transport_factory->CreateDtlsTransport( - ice, config_.crypto_options); - } else { - dtls = std::make_unique(ice, config_.crypto_options, - config_.event_log); - } - - RTC_DCHECK(dtls); - dtls->SetSslMaxProtocolVersion(config_.ssl_max_version); - dtls->ice_transport()->SetIceRole(ice_role_); - dtls->ice_transport()->SetIceTiebreaker(ice_tiebreaker_); - dtls->ice_transport()->SetIceConfig(ice_config_); - if (certificate_) { - bool set_cert_success = dtls->SetLocalCertificate(certificate_); - RTC_DCHECK(set_cert_success); - } - - // Connect to signals offered by the DTLS and ICE transport. - dtls->SignalWritableState.connect( - this, &TgJsepTransportController::OnTransportWritableState_n); - dtls->SignalReceivingState.connect( - this, &TgJsepTransportController::OnTransportReceivingState_n); - dtls->SignalDtlsHandshakeError.connect( - this, &TgJsepTransportController::OnDtlsHandshakeError); - dtls->ice_transport()->SignalGatheringState.connect( - this, &TgJsepTransportController::OnTransportGatheringState_n); - dtls->ice_transport()->SignalCandidateGathered.connect( - this, &TgJsepTransportController::OnTransportCandidateGathered_n); - dtls->ice_transport()->SignalCandidateError.connect( - this, &TgJsepTransportController::OnTransportCandidateError_n); - dtls->ice_transport()->SignalCandidatesRemoved.connect( - this, &TgJsepTransportController::OnTransportCandidatesRemoved_n); - dtls->ice_transport()->SignalRoleConflict.connect( - this, &TgJsepTransportController::OnTransportRoleConflict_n); - dtls->ice_transport()->SignalStateChanged.connect( - this, &TgJsepTransportController::OnTransportStateChanged_n); - dtls->ice_transport()->SignalIceTransportStateChanged.connect( - this, &TgJsepTransportController::OnTransportStateChanged_n); - dtls->ice_transport()->SignalCandidatePairChanged.connect( - this, &TgJsepTransportController::OnTransportCandidatePairChanged_n); - return dtls; -} - -std::unique_ptr -TgJsepTransportController::CreateUnencryptedRtpTransport( - const std::string& transport_name, - rtc::PacketTransportInternal* rtp_packet_transport, - rtc::PacketTransportInternal* rtcp_packet_transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - auto unencrypted_rtp_transport = - std::make_unique(rtcp_packet_transport == nullptr); - unencrypted_rtp_transport->SetRtpPacketTransport(rtp_packet_transport); - if (rtcp_packet_transport) { - unencrypted_rtp_transport->SetRtcpPacketTransport(rtcp_packet_transport); - } - return unencrypted_rtp_transport; -} - -std::unique_ptr -TgJsepTransportController::CreateSdesTransport( - const std::string& transport_name, - cricket::DtlsTransportInternal* rtp_dtls_transport, - cricket::DtlsTransportInternal* rtcp_dtls_transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - auto srtp_transport = - std::make_unique(rtcp_dtls_transport == nullptr); - RTC_DCHECK(rtp_dtls_transport); - srtp_transport->SetRtpPacketTransport(rtp_dtls_transport); - if (rtcp_dtls_transport) { - srtp_transport->SetRtcpPacketTransport(rtcp_dtls_transport); - } - if (config_.enable_external_auth) { - srtp_transport->EnableExternalAuth(); - } - return srtp_transport; -} - -std::unique_ptr -TgJsepTransportController::CreateDtlsSrtpTransport( - const std::string& transport_name, - cricket::DtlsTransportInternal* rtp_dtls_transport, - cricket::DtlsTransportInternal* rtcp_dtls_transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - auto dtls_srtp_transport = std::make_unique( - rtcp_dtls_transport == nullptr); - if (config_.enable_external_auth) { - dtls_srtp_transport->EnableExternalAuth(); - } - - dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport, - rtcp_dtls_transport); - dtls_srtp_transport->SetActiveResetSrtpParams( - config_.active_reset_srtp_params); - dtls_srtp_transport->SignalDtlsStateChange.connect( - this, &TgJsepTransportController::UpdateAggregateStates_n); - return dtls_srtp_transport; -} - -std::vector -TgJsepTransportController::GetDtlsTransports() { - std::vector dtls_transports; - for (auto it = jsep_transports_by_name_.begin(); - it != jsep_transports_by_name_.end(); ++it) { - auto jsep_transport = it->second.get(); - RTC_DCHECK(jsep_transport); - if (jsep_transport->rtp_dtls_transport()) { - dtls_transports.push_back(jsep_transport->rtp_dtls_transport()); - } - - if (jsep_transport->rtcp_dtls_transport()) { - dtls_transports.push_back(jsep_transport->rtcp_dtls_transport()); - } - } - return dtls_transports; -} - -RTCError TgJsepTransportController::ApplyDescription_n( - bool local, - SdpType type, - const cricket::SessionDescription* description) { - RTC_DCHECK(network_thread_->IsCurrent()); - RTC_DCHECK(description); - - if (local) { - local_desc_ = description; - } else { - remote_desc_ = description; - } - - RTCError error; - error = ValidateAndMaybeUpdateBundleGroup(local, type, description); - if (!error.ok()) { - return error; - } - - std::vector merged_encrypted_extension_ids; - absl::optional bundle_media_alt_protocol; - absl::optional bundle_data_alt_protocol; - if (bundle_group_) { - merged_encrypted_extension_ids = - MergeEncryptedHeaderExtensionIdsForBundle(description); - error = GetAltProtocolsForBundle(description, &bundle_media_alt_protocol, - &bundle_data_alt_protocol); - if (!error.ok()) { - return error; - } - } - - for (const cricket::ContentInfo& content_info : description->contents()) { - // Don't create transports for rejected m-lines and bundled m-lines." - if (content_info.rejected || - (IsBundled(content_info.name) && content_info.name != *bundled_mid())) { - continue; - } - error = MaybeCreateJsepTransport(local, content_info, *description); - if (!error.ok()) { - return error; - } - } - - RTC_DCHECK(description->contents().size() == - description->transport_infos().size()); - for (size_t i = 0; i < description->contents().size(); ++i) { - const cricket::ContentInfo& content_info = description->contents()[i]; - const cricket::MediaContentDescription* media_description = - content_info.media_description(); - const cricket::TransportInfo& transport_info = - description->transport_infos()[i]; - if (content_info.rejected) { - HandleRejectedContent(content_info, description); - continue; - } - - if (IsBundled(content_info.name) && content_info.name != *bundled_mid()) { - if (!HandleBundledContent(content_info)) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "Failed to process the bundled m= section."); - } - continue; - } - - error = ValidateContent(content_info); - if (!error.ok()) { - return error; - } - - std::vector extension_ids; - absl::optional media_alt_protocol; - absl::optional data_alt_protocol; - if (bundled_mid() && content_info.name == *bundled_mid()) { - extension_ids = merged_encrypted_extension_ids; - media_alt_protocol = bundle_media_alt_protocol; - data_alt_protocol = bundle_data_alt_protocol; - } else { - extension_ids = GetEncryptedHeaderExtensionIds(content_info); - switch (media_description->type()) { - case cricket::MEDIA_TYPE_AUDIO: - case cricket::MEDIA_TYPE_VIDEO: - media_alt_protocol = media_description->alt_protocol(); - break; - case cricket::MEDIA_TYPE_DATA: - data_alt_protocol = media_description->alt_protocol(); - break; - } - } - - int rtp_abs_sendtime_extn_id = - GetRtpAbsSendTimeHeaderExtensionId(content_info); - - cricket::TgJsepTransport* transport = - GetJsepTransportForMid(content_info.name); - RTC_DCHECK(transport); - - SetIceRole_n(DetermineIceRole(transport, transport_info, type, local)); - - cricket::TgJsepTransportDescription jsep_description = - CreateJsepTransportDescription(content_info, transport_info, - extension_ids, rtp_abs_sendtime_extn_id, - media_alt_protocol, data_alt_protocol); - if (local) { - error = - transport->SetLocalJsepTransportDescription(jsep_description, type); - } else { - error = - transport->SetRemoteJsepTransportDescription(jsep_description, type); - } - - if (!error.ok()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Failed to apply the description for " + - content_info.name + ": " + error.message()); - } - } - return RTCError::OK(); -} - -RTCError TgJsepTransportController::ValidateAndMaybeUpdateBundleGroup( - bool local, - SdpType type, - const cricket::SessionDescription* description) { - RTC_DCHECK(description); - const cricket::ContentGroup* new_bundle_group = - description->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - - // The BUNDLE group containing a MID that no m= section has is invalid. - if (new_bundle_group) { - for (const auto& content_name : new_bundle_group->content_names()) { - if (!description->GetContentByName(content_name)) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "The BUNDLE group contains MID:" + content_name + - " matching no m= section."); - } - } - } - - if (type == SdpType::kAnswer) { - const cricket::ContentGroup* offered_bundle_group = - local ? remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE) - : local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - - if (new_bundle_group) { - // The BUNDLE group in answer should be a subset of offered group. - for (const auto& content_name : new_bundle_group->content_names()) { - if (!offered_bundle_group || - !offered_bundle_group->HasContentName(content_name)) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "The BUNDLE group in answer contains a MID that was " - "not in the offered group."); - } - } - } - - if (bundle_group_) { - for (const auto& content_name : bundle_group_->content_names()) { - // An answer that removes m= sections from pre-negotiated BUNDLE group - // without rejecting it, is invalid. - if (!new_bundle_group || - !new_bundle_group->HasContentName(content_name)) { - auto* content_info = description->GetContentByName(content_name); - if (!content_info || !content_info->rejected) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "Answer cannot remove m= section " + content_name + - " from already-established BUNDLE group."); - } - } - } - } - } - - if (config_.bundle_policy == - PeerConnectionInterface::kBundlePolicyMaxBundle && - !description->HasGroup(cricket::GROUP_TYPE_BUNDLE)) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "max-bundle is used but no bundle group found."); - } - - if (ShouldUpdateBundleGroup(type, description)) { - bundle_group_ = *new_bundle_group; - } - - if (!bundled_mid()) { - return RTCError::OK(); - } - - auto bundled_content = description->GetContentByName(*bundled_mid()); - if (!bundled_content) { - return RTCError( - RTCErrorType::INVALID_PARAMETER, - "An m= section associated with the BUNDLE-tag doesn't exist."); - } - - // If the |bundled_content| is rejected, other contents in the bundle group - // should be rejected. - if (bundled_content->rejected) { - for (const auto& content_name : bundle_group_->content_names()) { - auto other_content = description->GetContentByName(content_name); - if (!other_content->rejected) { - return RTCError( - RTCErrorType::INVALID_PARAMETER, - "The m= section:" + content_name + " should be rejected."); - } - } - } - - return RTCError::OK(); -} - -RTCError TgJsepTransportController::ValidateContent( - const cricket::ContentInfo& content_info) { - if (config_.rtcp_mux_policy == - PeerConnectionInterface::kRtcpMuxPolicyRequire && - content_info.type == cricket::MediaProtocolType::kRtp && - !content_info.media_description()->rtcp_mux()) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "The m= section:" + content_info.name + - " is invalid. RTCP-MUX is not " - "enabled when it is required."); - } - return RTCError::OK(); -} - -void TgJsepTransportController::HandleRejectedContent( - const cricket::ContentInfo& content_info, - const cricket::SessionDescription* description) { - // If the content is rejected, let the - // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first, - // then destroy the cricket::TgJsepTransport. - RemoveTransportForMid(content_info.name); - if (content_info.name == bundled_mid()) { - for (const auto& content_name : bundle_group_->content_names()) { - RemoveTransportForMid(content_name); - } - bundle_group_.reset(); - } else if (IsBundled(content_info.name)) { - // Remove the rejected content from the |bundle_group_|. - bundle_group_->RemoveContentName(content_info.name); - // Reset the bundle group if nothing left. - if (!bundle_group_->FirstContentName()) { - bundle_group_.reset(); - } - } - MaybeDestroyJsepTransport(content_info.name); -} - -bool TgJsepTransportController::HandleBundledContent( - const cricket::ContentInfo& content_info) { - auto jsep_transport = GetJsepTransportByName(*bundled_mid()); - RTC_DCHECK(jsep_transport); - // If the content is bundled, let the - // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first, - // then destroy the cricket::JsepTransport. - if (SetTransportForMid(content_info.name, jsep_transport)) { - // TODO(bugs.webrtc.org/9719) For media transport this is far from ideal, - // because it means that we first create media transport and start - // connecting it, and then we destroy it. We will need to address it before - // video path is enabled. - MaybeDestroyJsepTransport(content_info.name); - return true; - } - return false; -} - -bool TgJsepTransportController::SetTransportForMid( - const std::string& mid, - cricket::TgJsepTransport* jsep_transport) { - RTC_DCHECK(jsep_transport); - if (mid_to_transport_[mid] == jsep_transport) { - return true; - } - - mid_to_transport_[mid] = jsep_transport; - return config_.transport_observer->OnTransportChanged( - mid, jsep_transport->rtp_transport(), jsep_transport->RtpDtlsTransport(), - jsep_transport->data_channel_transport()); -} - -void TgJsepTransportController::RemoveTransportForMid(const std::string& mid) { - bool ret = config_.transport_observer->OnTransportChanged(mid, nullptr, - nullptr, nullptr); - // Calling OnTransportChanged with nullptr should always succeed, since it is - // only expected to fail when adding media to a transport (not removing). - RTC_DCHECK(ret); - mid_to_transport_.erase(mid); -} - -cricket::TgJsepTransportDescription -TgJsepTransportController::CreateJsepTransportDescription( - const cricket::ContentInfo& content_info, - const cricket::TransportInfo& transport_info, - const std::vector& encrypted_extension_ids, - int rtp_abs_sendtime_extn_id, - absl::optional media_alt_protocol, - absl::optional data_alt_protocol) { - const cricket::MediaContentDescription* content_desc = - content_info.media_description(); - RTC_DCHECK(content_desc); - bool rtcp_mux_enabled = content_info.type == cricket::MediaProtocolType::kSctp - ? true - : content_desc->rtcp_mux(); - - return cricket::TgJsepTransportDescription( - rtcp_mux_enabled, content_desc->cryptos(), encrypted_extension_ids, - rtp_abs_sendtime_extn_id, transport_info.description, media_alt_protocol, - data_alt_protocol); -} - -bool TgJsepTransportController::ShouldUpdateBundleGroup( - SdpType type, - const cricket::SessionDescription* description) { - if (config_.bundle_policy == - PeerConnectionInterface::kBundlePolicyMaxBundle) { - return true; - } - - if (type != SdpType::kAnswer) { - return false; - } - - RTC_DCHECK(local_desc_ && remote_desc_); - const cricket::ContentGroup* local_bundle = - local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - const cricket::ContentGroup* remote_bundle = - remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - return local_bundle && remote_bundle; -} - -std::vector TgJsepTransportController::GetEncryptedHeaderExtensionIds( - const cricket::ContentInfo& content_info) { - const cricket::MediaContentDescription* content_desc = - content_info.media_description(); - - if (!config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions) { - return std::vector(); - } - - std::vector encrypted_header_extension_ids; - for (const auto& extension : content_desc->rtp_header_extensions()) { - if (!extension.encrypt) { - continue; - } - if (!absl::c_linear_search(encrypted_header_extension_ids, extension.id)) { - encrypted_header_extension_ids.push_back(extension.id); - } - } - return encrypted_header_extension_ids; -} - -std::vector -TgJsepTransportController::MergeEncryptedHeaderExtensionIdsForBundle( - const cricket::SessionDescription* description) { - RTC_DCHECK(description); - RTC_DCHECK(bundle_group_); - - std::vector merged_ids; - // Union the encrypted header IDs in the group when bundle is enabled. - for (const cricket::ContentInfo& content_info : description->contents()) { - if (bundle_group_->HasContentName(content_info.name)) { - std::vector extension_ids = - GetEncryptedHeaderExtensionIds(content_info); - for (int id : extension_ids) { - if (!absl::c_linear_search(merged_ids, id)) { - merged_ids.push_back(id); - } - } - } - } - return merged_ids; -} - -RTCError TgJsepTransportController::GetAltProtocolsForBundle( - const cricket::SessionDescription* description, - absl::optional* media_alt_protocol, - absl::optional* data_alt_protocol) { - RTC_DCHECK(description); - RTC_DCHECK(bundle_group_); - RTC_DCHECK(media_alt_protocol); - RTC_DCHECK(data_alt_protocol); - - bool found_media = false; - bool found_data = false; - for (const cricket::ContentInfo& content : description->contents()) { - if (bundle_group_->HasContentName(content.name)) { - const cricket::MediaContentDescription* media_description = - content.media_description(); - switch (media_description->type()) { - case cricket::MEDIA_TYPE_AUDIO: - case cricket::MEDIA_TYPE_VIDEO: - if (found_media && - *media_alt_protocol != media_description->alt_protocol()) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "The BUNDLE group contains conflicting " - "alt-protocols for media ('" + - media_alt_protocol->value_or("") + "' and '" + - media_description->alt_protocol().value_or("") + - "')"); - } - found_media = true; - *media_alt_protocol = media_description->alt_protocol(); - break; - case cricket::MEDIA_TYPE_DATA: - if (found_data && - *data_alt_protocol != media_description->alt_protocol()) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "The BUNDLE group contains conflicting " - "alt-protocols for data ('" + - data_alt_protocol->value_or("") + "' and '" + - media_description->alt_protocol().value_or("") + - "')"); - } - found_data = true; - *data_alt_protocol = media_description->alt_protocol(); - break; - } - } - } - return RTCError::OK(); -} - -int TgJsepTransportController::GetRtpAbsSendTimeHeaderExtensionId( - const cricket::ContentInfo& content_info) { - if (!config_.enable_external_auth) { - return -1; - } - - const cricket::MediaContentDescription* content_desc = - content_info.media_description(); - - const webrtc::RtpExtension* send_time_extension = - webrtc::RtpExtension::FindHeaderExtensionByUri( - content_desc->rtp_header_extensions(), - webrtc::RtpExtension::kAbsSendTimeUri); - return send_time_extension ? send_time_extension->id : -1; -} - -const cricket::TgJsepTransport* TgJsepTransportController::GetJsepTransportForMid( - const std::string& mid) const { - auto it = mid_to_transport_.find(mid); - return it == mid_to_transport_.end() ? nullptr : it->second; -} - -cricket::TgJsepTransport* TgJsepTransportController::GetJsepTransportForMid( - const std::string& mid) { - auto it = mid_to_transport_.find(mid); - return it == mid_to_transport_.end() ? nullptr : it->second; -} - -const cricket::TgJsepTransport* TgJsepTransportController::GetJsepTransportByName( - const std::string& transport_name) const { - auto it = jsep_transports_by_name_.find(transport_name); - return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get(); -} - -cricket::TgJsepTransport* TgJsepTransportController::GetJsepTransportByName( - const std::string& transport_name) { - auto it = jsep_transports_by_name_.find(transport_name); - return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get(); -} - -// TODO(sukhanov): Refactor to avoid code duplication for Media and Datagram -// transports setup. -std::unique_ptr -TgJsepTransportController::MaybeCreateDatagramTransport( - const cricket::ContentInfo& content_info, - const cricket::SessionDescription& description, - bool local) { - if (config_.media_transport_factory == nullptr) { - return nullptr; - } - - if (!(config_.use_datagram_transport || - config_.use_datagram_transport_for_data_channels)) { - return nullptr; - } - - // Caller (offerer) datagram transport. - if (offer_datagram_transport_) { - RTC_DCHECK(local); - RTC_LOG(LS_INFO) << "Offered datagram transport has now been activated."; - return std::move(offer_datagram_transport_); - } - - const cricket::TransportDescription* transport_description = - description.GetTransportDescriptionByName(content_info.mid()); - RTC_DCHECK(transport_description) - << "Missing transport description for mid=" << content_info.mid(); - - if (!transport_description->opaque_parameters) { - RTC_LOG(LS_INFO) - << "No opaque transport parameters, not creating datagram transport"; - return nullptr; - } - - if (transport_description->opaque_parameters->protocol != - config_.media_transport_factory->GetTransportName()) { - RTC_LOG(LS_INFO) << "Opaque transport parameters for protocol=" - << transport_description->opaque_parameters->protocol - << ", which does not match supported protocol=" - << config_.media_transport_factory->GetTransportName(); - return nullptr; - } - - RTC_DCHECK(!local); - // When bundle is enabled, two TgJsepTransports are created, and then - // the second transport is destroyed (right away). - // For datagram transport, we don't want to create the second - // datagram transport in the first place. - RTC_LOG(LS_INFO) << "Returning new, client datagram transport."; - - MediaTransportSettings settings; - settings.is_caller = local; - settings.remote_transport_parameters = - transport_description->opaque_parameters->parameters; - settings.event_log = config_.event_log; - - auto datagram_transport_result = - config_.media_transport_factory->CreateDatagramTransport(network_thread_, - settings); - - // TODO(sukhanov): Proper error handling. - RTC_CHECK(datagram_transport_result.ok()); - - return datagram_transport_result.MoveValue(); -} - -RTCError TgJsepTransportController::MaybeCreateJsepTransport( - bool local, - const cricket::ContentInfo& content_info, - const cricket::SessionDescription& description) { - RTC_DCHECK(network_thread_->IsCurrent()); - cricket::TgJsepTransport* transport = GetJsepTransportByName(content_info.name); - if (transport) { - return RTCError::OK(); - } - - const cricket::MediaContentDescription* content_desc = - content_info.media_description(); - if (certificate_ && !content_desc->cryptos().empty()) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "SDES and DTLS-SRTP cannot be enabled at the same time."); - } - - rtc::scoped_refptr ice = - CreateIceTransport(content_info.name, /*rtcp=*/false); - RTC_DCHECK(ice); - - std::unique_ptr datagram_transport = - MaybeCreateDatagramTransport(content_info, description, local); - if (datagram_transport) { - datagram_transport->Connect(ice->internal()); - } - - std::unique_ptr rtp_dtls_transport = - CreateDtlsTransport(content_info, ice->internal(), nullptr); - - //std::unique_ptr rtcp_dtls_transport; - //std::unique_ptr unencrypted_rtp_transport; - //std::unique_ptr sdes_transport; - std::unique_ptr dtls_srtp_transport; - //std::unique_ptr datagram_rtp_transport; - - //rtc::scoped_refptr rtcp_ice; - //if (config_.rtcp_mux_policy != - // PeerConnectionInterface::kRtcpMuxPolicyRequire && - // content_info.type == cricket::MediaProtocolType::kRtp) { - // RTC_DCHECK(datagram_transport == nullptr); - // rtcp_ice = CreateIceTransport(content_info.name, /*rtcp=*/true); - // rtcp_dtls_transport = - // CreateDtlsTransport(content_info, rtcp_ice->internal(), - // /*datagram_transport=*/nullptr); - //} - - /*// Only create a datagram RTP transport if the datagram transport should be - // used for RTP. - if (datagram_transport && config_.use_datagram_transport) { - // TODO(sukhanov): We use unencrypted RTP transport over DatagramTransport, - // because MediaTransport encrypts. In the future we may want to - // implement our own version of RtpTransport over MediaTransport, because - // it will give us more control over things like: - // - Fusing - // - Rtp header compression - // - Handling Rtcp feedback. - RTC_LOG(LS_INFO) << "Creating UnencryptedRtpTransport, because datagram " - "transport is used."; - RTC_DCHECK(!rtcp_dtls_transport); - datagram_rtp_transport = std::make_unique( - content_info.media_description()->rtp_header_extensions(), - ice->internal(), datagram_transport.get()); - }*/ - - /*if (config_.disable_encryption) { - RTC_LOG(LS_INFO) - << "Creating UnencryptedRtpTransport, becayse encryption is disabled."; - unencrypted_rtp_transport = CreateUnencryptedRtpTransport( - content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); - } else if (!content_desc->cryptos().empty()) { - sdes_transport = CreateSdesTransport( - content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get()); - RTC_LOG(LS_INFO) << "Creating SdesTransport."; - } else {*/ - RTC_LOG(LS_INFO) << "Creating DtlsSrtpTransport."; - dtls_srtp_transport = CreateDtlsSrtpTransport( - content_info.name, rtp_dtls_transport.get(), nullptr); - //} - - /*std::unique_ptr sctp_transport; - if (config_.sctp_factory) { - sctp_transport = - config_.sctp_factory->CreateSctpTransport(rtp_dtls_transport.get()); - }*/ - - /*DataChannelTransportInterface* data_channel_transport = nullptr; - if (config_.use_datagram_transport_for_data_channels) { - data_channel_transport = datagram_transport.get(); - }*/ - - std::unique_ptr jsep_transport = - std::make_unique( - content_info.name, certificate_, std::move(ice), /*std::move(rtcp_ice)*/nullptr, - /*std::move(unencrypted_rtp_transport)*/nullptr, /*std::move(sdes_transport)*/nullptr, - std::move(dtls_srtp_transport), /*std::move(datagram_rtp_transport)*/nullptr, - std::move(rtp_dtls_transport), /*std::move(rtcp_dtls_transport)*/nullptr, - /*std::move(sctp_transport)*/nullptr, /*std::move(datagram_transport)*/nullptr, - /*data_channel_transport*/nullptr); - - jsep_transport->rtp_transport()->SignalRtcpPacketReceived.connect( - this, &TgJsepTransportController::OnRtcpPacketReceived_n); - - jsep_transport->SignalRtcpMuxActive.connect( - this, &TgJsepTransportController::UpdateAggregateStates_n); - jsep_transport->SignalDataChannelTransportNegotiated.connect( - this, &TgJsepTransportController::OnDataChannelTransportNegotiated_n); - SetTransportForMid(content_info.name, jsep_transport.get()); - - jsep_transports_by_name_[content_info.name] = std::move(jsep_transport); - UpdateAggregateStates_n(); - return RTCError::OK(); -} - -void TgJsepTransportController::MaybeDestroyJsepTransport( - const std::string& mid) { - auto jsep_transport = GetJsepTransportByName(mid); - if (!jsep_transport) { - return; - } - - // Don't destroy the JsepTransport if there are still media sections referring - // to it. - for (const auto& kv : mid_to_transport_) { - if (kv.second == jsep_transport) { - return; - } - } - - jsep_transports_by_name_.erase(mid); - UpdateAggregateStates_n(); -} - -void TgJsepTransportController::DestroyAllJsepTransports_n() { - RTC_DCHECK(network_thread_->IsCurrent()); - - for (const auto& jsep_transport : jsep_transports_by_name_) { - config_.transport_observer->OnTransportChanged(jsep_transport.first, - nullptr, nullptr, nullptr); - } - - jsep_transports_by_name_.clear(); -} - -void TgJsepTransportController::SetIceRole_n(cricket::IceRole ice_role) { - RTC_DCHECK(network_thread_->IsCurrent()); - - ice_role_ = ice_role; - for (auto& dtls : GetDtlsTransports()) { - dtls->ice_transport()->SetIceRole(ice_role_); - } -} - -cricket::IceRole TgJsepTransportController::DetermineIceRole( - cricket::TgJsepTransport* jsep_transport, - const cricket::TransportInfo& transport_info, - SdpType type, - bool local) { - cricket::IceRole ice_role = ice_role_; - auto tdesc = transport_info.description; - if (local) { - // The initial offer side may use ICE Lite, in which case, per RFC5245 - // Section 5.1.1, the answer side should take the controlling role if it is - // in the full ICE mode. - // - // When both sides use ICE Lite, the initial offer side must take the - // controlling role, and this is the default logic implemented in - // SetLocalDescription in TgJsepTransportController. - if (jsep_transport->remote_description() && - jsep_transport->remote_description()->transport_desc.ice_mode == - cricket::ICEMODE_LITE && - ice_role_ == cricket::ICEROLE_CONTROLLED && - tdesc.ice_mode == cricket::ICEMODE_FULL) { - ice_role = cricket::ICEROLE_CONTROLLING; - } - - // Older versions of Chrome expect the ICE role to be re-determined when an - // ICE restart occurs, and also don't perform conflict resolution correctly, - // so for now we can't safely stop doing this, unless the application opts - // in by setting |config_.redetermine_role_on_ice_restart_| to false. See: - // https://bugs.chromium.org/p/chromium/issues/detail?id=628676 - // TODO(deadbeef): Remove this when these old versions of Chrome reach a low - // enough population. - if (config_.redetermine_role_on_ice_restart && - jsep_transport->local_description() && - cricket::IceCredentialsChanged( - jsep_transport->local_description()->transport_desc.ice_ufrag, - jsep_transport->local_description()->transport_desc.ice_pwd, - tdesc.ice_ufrag, tdesc.ice_pwd) && - // Don't change the ICE role if the remote endpoint is ICE lite; we - // should always be controlling in that case. - (!jsep_transport->remote_description() || - jsep_transport->remote_description()->transport_desc.ice_mode != - cricket::ICEMODE_LITE)) { - ice_role = (type == SdpType::kOffer) ? cricket::ICEROLE_CONTROLLING - : cricket::ICEROLE_CONTROLLED; - } - } else { - // If our role is cricket::ICEROLE_CONTROLLED and the remote endpoint - // supports only ice_lite, this local endpoint should take the CONTROLLING - // role. - // TODO(deadbeef): This is a session-level attribute, so it really shouldn't - // be in a TransportDescription in the first place... - if (ice_role_ == cricket::ICEROLE_CONTROLLED && - tdesc.ice_mode == cricket::ICEMODE_LITE) { - ice_role = cricket::ICEROLE_CONTROLLING; - } - - // If we use ICE Lite and the remote endpoint uses the full implementation - // of ICE, the local endpoint must take the controlled role, and the other - // side must be the controlling role. - if (jsep_transport->local_description() && - jsep_transport->local_description()->transport_desc.ice_mode == - cricket::ICEMODE_LITE && - ice_role_ == cricket::ICEROLE_CONTROLLING && - tdesc.ice_mode == cricket::ICEMODE_FULL) { - ice_role = cricket::ICEROLE_CONTROLLED; - } - } - - return ice_role; -} - -void TgJsepTransportController::OnTransportWritableState_n( - rtc::PacketTransportInternal* transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - RTC_LOG(LS_INFO) << " Transport " << transport->transport_name() - << " writability changed to " << transport->writable() - << "."; - UpdateAggregateStates_n(); -} - -void TgJsepTransportController::OnTransportReceivingState_n( - rtc::PacketTransportInternal* transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - UpdateAggregateStates_n(); -} - -void TgJsepTransportController::OnTransportGatheringState_n( - cricket::IceTransportInternal* transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - UpdateAggregateStates_n(); -} - -void TgJsepTransportController::OnTransportCandidateGathered_n( - cricket::IceTransportInternal* transport, - const cricket::Candidate& candidate) { - RTC_DCHECK(network_thread_->IsCurrent()); - - // We should never signal peer-reflexive candidates. - if (candidate.type() == cricket::PRFLX_PORT_TYPE) { - RTC_NOTREACHED(); - return; - } - std::string transport_name = transport->transport_name(); - invoker_.AsyncInvoke( - RTC_FROM_HERE, signaling_thread_, [this, transport_name, candidate] { - SignalIceCandidatesGathered(transport_name, {candidate}); - }); -} - -void TgJsepTransportController::OnTransportCandidateError_n( - cricket::IceTransportInternal* transport, - const cricket::IceCandidateErrorEvent& event) { - RTC_DCHECK(network_thread_->IsCurrent()); - - invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, - [this, event] { SignalIceCandidateError(event); }); -} -void TgJsepTransportController::OnTransportCandidatesRemoved_n( - cricket::IceTransportInternal* transport, - const cricket::Candidates& candidates) { - invoker_.AsyncInvoke( - RTC_FROM_HERE, signaling_thread_, - [this, candidates] { SignalIceCandidatesRemoved(candidates); }); -} -void TgJsepTransportController::OnTransportCandidatePairChanged_n( - const cricket::CandidatePairChangeEvent& event) { - invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, [this, event] { - SignalIceCandidatePairChanged(event); - }); -} - -void TgJsepTransportController::OnTransportRoleConflict_n( - cricket::IceTransportInternal* transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - // Note: since the role conflict is handled entirely on the network thread, - // we don't need to worry about role conflicts occurring on two ports at - // once. The first one encountered should immediately reverse the role. - cricket::IceRole reversed_role = (ice_role_ == cricket::ICEROLE_CONTROLLING) - ? cricket::ICEROLE_CONTROLLED - : cricket::ICEROLE_CONTROLLING; - RTC_LOG(LS_INFO) << "Got role conflict; switching to " - << (reversed_role == cricket::ICEROLE_CONTROLLING - ? "controlling" - : "controlled") - << " role."; - SetIceRole_n(reversed_role); -} - -void TgJsepTransportController::OnTransportStateChanged_n( - cricket::IceTransportInternal* transport) { - RTC_DCHECK(network_thread_->IsCurrent()); - RTC_LOG(LS_INFO) << transport->transport_name() << " Transport " - << transport->component() - << " state changed. Check if state is complete."; - UpdateAggregateStates_n(); -} - -void TgJsepTransportController::OnDataChannelTransportNegotiated_n( - cricket::TgJsepTransport* transport, - DataChannelTransportInterface* data_channel_transport) { - for (auto it : mid_to_transport_) { - if (it.second == transport) { - config_.transport_observer->OnTransportChanged( - it.first, transport->rtp_transport(), transport->RtpDtlsTransport(), - data_channel_transport); - } - } -} - -void TgJsepTransportController::UpdateAggregateStates_n() { - RTC_DCHECK(network_thread_->IsCurrent()); - - auto dtls_transports = GetDtlsTransports(); - cricket::IceConnectionState new_connection_state = - cricket::kIceConnectionConnecting; - PeerConnectionInterface::IceConnectionState new_ice_connection_state = - PeerConnectionInterface::IceConnectionState::kIceConnectionNew; - PeerConnectionInterface::PeerConnectionState new_combined_state = - PeerConnectionInterface::PeerConnectionState::kNew; - cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew; - bool any_failed = false; - bool all_connected = !dtls_transports.empty(); - bool all_completed = !dtls_transports.empty(); - bool any_gathering = false; - bool all_done_gathering = !dtls_transports.empty(); - - std::map ice_state_counts; - std::map dtls_state_counts; - - for (const auto& dtls : dtls_transports) { - any_failed = any_failed || dtls->ice_transport()->GetState() == - cricket::IceTransportState::STATE_FAILED; - all_connected = all_connected && dtls->writable(); - all_completed = - all_completed && dtls->writable() && - dtls->ice_transport()->GetState() == - cricket::IceTransportState::STATE_COMPLETED && - dtls->ice_transport()->GetIceRole() == cricket::ICEROLE_CONTROLLING && - dtls->ice_transport()->gathering_state() == - cricket::kIceGatheringComplete; - any_gathering = any_gathering || dtls->ice_transport()->gathering_state() != - cricket::kIceGatheringNew; - all_done_gathering = - all_done_gathering && dtls->ice_transport()->gathering_state() == - cricket::kIceGatheringComplete; - - dtls_state_counts[dtls->dtls_state()]++; - ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++; - } - - if (any_failed) { - new_connection_state = cricket::kIceConnectionFailed; - } else if (all_completed) { - new_connection_state = cricket::kIceConnectionCompleted; - } else if (all_connected) { - new_connection_state = cricket::kIceConnectionConnected; - } - if (ice_connection_state_ != new_connection_state) { - ice_connection_state_ = new_connection_state; - invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, - [this, new_connection_state] { - SignalIceConnectionState(new_connection_state); - }); - } - - // Compute the current RTCIceConnectionState as described in - // https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate. - // The PeerConnection is responsible for handling the "closed" state. - int total_ice_checking = ice_state_counts[IceTransportState::kChecking]; - int total_ice_connected = ice_state_counts[IceTransportState::kConnected]; - int total_ice_completed = ice_state_counts[IceTransportState::kCompleted]; - int total_ice_failed = ice_state_counts[IceTransportState::kFailed]; - int total_ice_disconnected = - ice_state_counts[IceTransportState::kDisconnected]; - int total_ice_closed = ice_state_counts[IceTransportState::kClosed]; - int total_ice_new = ice_state_counts[IceTransportState::kNew]; - int total_ice = (int)dtls_transports.size(); - - if (total_ice_failed > 0) { - // Any RTCIceTransports are in the "failed" state. - new_ice_connection_state = PeerConnectionInterface::kIceConnectionFailed; - } else if (total_ice_disconnected > 0) { - // None of the previous states apply and any RTCIceTransports are in the - // "disconnected" state. - new_ice_connection_state = - PeerConnectionInterface::kIceConnectionDisconnected; - } else if (total_ice_new + total_ice_closed == total_ice) { - // None of the previous states apply and all RTCIceTransports are in the - // "new" or "closed" state, or there are no transports. - new_ice_connection_state = PeerConnectionInterface::kIceConnectionNew; - } else if (total_ice_new + total_ice_checking > 0) { - // None of the previous states apply and any RTCIceTransports are in the - // "new" or "checking" state. - new_ice_connection_state = PeerConnectionInterface::kIceConnectionChecking; - } else if (total_ice_completed + total_ice_closed == total_ice || - all_completed) { - // None of the previous states apply and all RTCIceTransports are in the - // "completed" or "closed" state. - // - // TODO(https://bugs.webrtc.org/10356): The all_completed condition is added - // to mimic the behavior of the old ICE connection state, and should be - // removed once we get end-of-candidates signaling in place. - new_ice_connection_state = PeerConnectionInterface::kIceConnectionCompleted; - } else if (total_ice_connected + total_ice_completed + total_ice_closed == - total_ice) { - // None of the previous states apply and all RTCIceTransports are in the - // "connected", "completed" or "closed" state. - new_ice_connection_state = PeerConnectionInterface::kIceConnectionConnected; - } else { - RTC_NOTREACHED(); - } - - if (standardized_ice_connection_state_ != new_ice_connection_state) { - if (standardized_ice_connection_state_ == - PeerConnectionInterface::kIceConnectionChecking && - new_ice_connection_state == - PeerConnectionInterface::kIceConnectionCompleted) { - // Ensure that we never skip over the "connected" state. - invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, [this] { - SignalStandardizedIceConnectionState( - PeerConnectionInterface::kIceConnectionConnected); - }); - } - standardized_ice_connection_state_ = new_ice_connection_state; - invoker_.AsyncInvoke( - RTC_FROM_HERE, signaling_thread_, [this, new_ice_connection_state] { - SignalStandardizedIceConnectionState(new_ice_connection_state); - }); - } - - // Compute the current RTCPeerConnectionState as described in - // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnectionstate. - // The PeerConnection is responsible for handling the "closed" state. - // Note that "connecting" is only a valid state for DTLS transports while - // "checking", "completed" and "disconnected" are only valid for ICE - // transports. - int total_connected = total_ice_connected + - dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTED]; - int total_dtls_connecting = - dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTING]; - int total_failed = - total_ice_failed + dtls_state_counts[cricket::DTLS_TRANSPORT_FAILED]; - int total_closed = - total_ice_closed + dtls_state_counts[cricket::DTLS_TRANSPORT_CLOSED]; - int total_new = - total_ice_new + dtls_state_counts[cricket::DTLS_TRANSPORT_NEW]; - int total_transports = total_ice * 2; - - if (total_failed > 0) { - // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state. - new_combined_state = PeerConnectionInterface::PeerConnectionState::kFailed; - } else if (total_ice_disconnected > 0) { - // None of the previous states apply and any RTCIceTransports or - // RTCDtlsTransports are in the "disconnected" state. - new_combined_state = - PeerConnectionInterface::PeerConnectionState::kDisconnected; - } else if (total_new + total_closed == total_transports) { - // None of the previous states apply and all RTCIceTransports and - // RTCDtlsTransports are in the "new" or "closed" state, or there are no - // transports. - new_combined_state = PeerConnectionInterface::PeerConnectionState::kNew; - } else if (total_new + total_dtls_connecting + total_ice_checking > 0) { - // None of the previous states apply and all RTCIceTransports or - // RTCDtlsTransports are in the "new", "connecting" or "checking" state. - new_combined_state = - PeerConnectionInterface::PeerConnectionState::kConnecting; - } else if (total_connected + total_ice_completed + total_closed == - total_transports) { - // None of the previous states apply and all RTCIceTransports and - // RTCDtlsTransports are in the "connected", "completed" or "closed" state. - new_combined_state = - PeerConnectionInterface::PeerConnectionState::kConnected; - } else { - RTC_NOTREACHED(); - } - - if (combined_connection_state_ != new_combined_state) { - combined_connection_state_ = new_combined_state; - invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, - [this, new_combined_state] { - SignalConnectionState(new_combined_state); - }); - } - - if (all_done_gathering) { - new_gathering_state = cricket::kIceGatheringComplete; - } else if (any_gathering) { - new_gathering_state = cricket::kIceGatheringGathering; - } - if (ice_gathering_state_ != new_gathering_state) { - ice_gathering_state_ = new_gathering_state; - invoker_.AsyncInvoke(RTC_FROM_HERE, signaling_thread_, - [this, new_gathering_state] { - SignalIceGatheringState(new_gathering_state); - }); - } -} - -void TgJsepTransportController::OnRtcpPacketReceived_n( - rtc::CopyOnWriteBuffer* packet, - int64_t packet_time_us) { - RTC_DCHECK(config_.rtcp_handler); - config_.rtcp_handler(*packet, packet_time_us); -} - -void TgJsepTransportController::OnDtlsHandshakeError( - rtc::SSLHandshakeError error) { - SignalDtlsHandshakeError(error); -} - -absl::optional -TgJsepTransportController::GetTransportParameters(const std::string& mid) { - if (!(config_.use_datagram_transport || - config_.use_datagram_transport_for_data_channels)) { - return absl::nullopt; - } - - cricket::TgJsepTransport* transport = GetJsepTransportForMid(mid); - if (transport) { - absl::optional params = - transport->GetTransportParameters(); - if (params) { - params->protocol = config_.media_transport_factory->GetTransportName(); - } - return params; - } - - RTC_DCHECK(!local_desc_ && !remote_desc_) - << "TgJsepTransport should exist for every mid once any description is set"; - - if (config_.use_datagram_transport_for_data_channels_receive_only) { - return absl::nullopt; - } - - // Need to generate a transport for the offer. - if (!offer_datagram_transport_) { - webrtc::MediaTransportSettings settings; - settings.is_caller = true; - settings.pre_shared_key = rtc::CreateRandomString(32); - settings.event_log = config_.event_log; - auto datagram_transport_or_error = - config_.media_transport_factory->CreateDatagramTransport( - network_thread_, settings); - - if (datagram_transport_or_error.ok()) { - offer_datagram_transport_ = - std::move(datagram_transport_or_error.value()); - } else { - RTC_LOG(LS_INFO) << "Unable to create datagram transport, error=" - << datagram_transport_or_error.error().message(); - } - } - - // We have prepared a transport for the offer, and can now use its parameters. - cricket::OpaqueTransportParameters params; - params.parameters = offer_datagram_transport_->GetTransportParameters(); - params.protocol = config_.media_transport_factory->GetTransportName(); - return params; -} - -} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.h b/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.h deleted file mode 100644 index 2777ab87a2..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_jsep_transport_controller.h +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright 2017 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TG_PC_JSEP_TRANSPORT_CONTROLLER_H_ -#define TG_PC_JSEP_TRANSPORT_CONTROLLER_H_ - -#include -#include -#include -#include -#include - -#include "api/candidate.h" -#include "api/crypto/crypto_options.h" -#include "api/ice_transport_factory.h" -#include "api/peer_connection_interface.h" -#include "api/rtc_event_log/rtc_event_log.h" -#include "api/transport/media/media_transport_config.h" -#include "media/sctp/sctp_transport_internal.h" -#include "p2p/base/dtls_transport.h" -#include "p2p/base/dtls_transport_factory.h" -#include "p2p/base/p2p_transport_channel.h" -#include "pc/channel.h" -#include "pc/dtls_srtp_transport.h" -#include "pc/dtls_transport.h" -#include "pc/rtp_transport.h" -#include "pc/srtp_transport.h" -#include "rtc_base/async_invoker.h" -#include "rtc_base/constructor_magic.h" -#include "rtc_base/ref_counted_object.h" -#include "rtc_base/third_party/sigslot/sigslot.h" - -#include "tg_jsep_transport.h" -#include "tg_rtp_transport.h" - -namespace rtc { -class Thread; -class PacketTransportInternal; -} // namespace rtc - -namespace webrtc { - -class TgJsepTransportController : public sigslot::has_slots<> { - public: - // Used when the RtpTransport/DtlsTransport of the m= section is changed - // because the section is rejected or BUNDLE is enabled. - class Observer { - public: - virtual ~Observer() {} - - // Returns true if media associated with |mid| was successfully set up to be - // demultiplexed on |rtp_transport|. Could return false if two bundled m= - // sections use the same SSRC, for example. - // - // If a data channel transport must be negotiated, |data_channel_transport| - // and |negotiation_state| indicate negotiation status. If - // |data_channel_transport| is null, the data channel transport should not - // be used. Otherwise, the value is a pointer to the transport to be used - // for data channels on |mid|, if any. - // - // The observer should not send data on |data_channel_transport| until - // |negotiation_state| is provisional or final. It should not delete - // |data_channel_transport| or any fallback transport until - // |negotiation_state| is final. - virtual bool OnTransportChanged( - const std::string& mid, - RtpTransportInternal* rtp_transport, - rtc::scoped_refptr dtls_transport, - DataChannelTransportInterface* data_channel_transport) = 0; - }; - - struct Config { - // If |redetermine_role_on_ice_restart| is true, ICE role is redetermined - // upon setting a local transport description that indicates an ICE - // restart. - bool redetermine_role_on_ice_restart = true; - rtc::SSLProtocolVersion ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; - // |crypto_options| is used to determine if created DTLS transports - // negotiate GCM crypto suites or not. - webrtc::CryptoOptions crypto_options; - PeerConnectionInterface::BundlePolicy bundle_policy = - PeerConnectionInterface::kBundlePolicyBalanced; - PeerConnectionInterface::RtcpMuxPolicy rtcp_mux_policy = - PeerConnectionInterface::kRtcpMuxPolicyRequire; - bool disable_encryption = false; - bool enable_external_auth = false; - // Used to inject the ICE/DTLS transports created externally. - webrtc::IceTransportFactory* ice_transport_factory = nullptr; - cricket::DtlsTransportFactory* dtls_transport_factory = nullptr; - Observer* transport_observer = nullptr; - // Must be provided and valid for the lifetime of the - // TgJsepTransportController instance. - std::function - rtcp_handler; - bool active_reset_srtp_params = false; - RtcEventLog* event_log = nullptr; - - // Factory for SCTP transports. - cricket::SctpTransportInternalFactory* sctp_factory = nullptr; - - // Whether an RtpMediaTransport should be created as default, when no - // MediaTransportFactory is provided. - bool use_rtp_media_transport = false; - - // Use encrypted datagram transport to send packets. - bool use_datagram_transport = false; - - // Use datagram transport's implementation of data channels instead of SCTP. - bool use_datagram_transport_for_data_channels = false; - - // Whether |use_datagram_transport_for_data_channels| applies to outgoing - // calls. If true, |use_datagram_transport_for_data_channels| applies only - // to incoming calls. - bool use_datagram_transport_for_data_channels_receive_only = false; - - // Optional media transport factory (experimental). If provided it will be - // used to create datagram_transport (as long as either - // |use_datagram_transport| or - // |use_datagram_transport_for_data_channels| is set to true). However, - // whether it will be used to send / receive audio and video frames instead - // of RTP is determined by |use_datagram_transport|. Note that currently - // datagram_transport co-exists with RTP / RTCP transports and may use the - // same underlying ICE transport. - MediaTransportFactory* media_transport_factory = nullptr; - }; - - // The ICE related events are signaled on the |signaling_thread|. - // All the transport related methods are called on the |network_thread|. - TgJsepTransportController(rtc::Thread* signaling_thread, - rtc::Thread* network_thread, - cricket::PortAllocator* port_allocator, - AsyncResolverFactory* async_resolver_factory, - Config config); - virtual ~TgJsepTransportController(); - - // The main method to be called; applies a description at the transport - // level, creating/destroying transport objects as needed and updating their - // properties. This includes RTP, DTLS, and ICE (but not SCTP). At least not - // yet? May make sense to in the future. - RTCError SetLocalDescription(SdpType type, - const cricket::SessionDescription* description); - - RTCError SetRemoteDescription(SdpType type, - const cricket::SessionDescription* description); - - // Get transports to be used for the provided |mid|. If bundling is enabled, - // calling GetRtpTransport for multiple MIDs may yield the same object. - RtpTransportInternal* GetRtpTransport(const std::string& mid) const; - cricket::DtlsTransportInternal* GetDtlsTransport(const std::string& mid); - const cricket::DtlsTransportInternal* GetRtcpDtlsTransport( - const std::string& mid) const; - // Gets the externally sharable version of the DtlsTransport. - rtc::scoped_refptr LookupDtlsTransportByMid( - const std::string& mid); - rtc::scoped_refptr GetSctpTransport( - const std::string& mid) const; - - MediaTransportConfig GetMediaTransportConfig(const std::string& mid) const; - - DataChannelTransportInterface* GetDataChannelTransport( - const std::string& mid) const; - - /********************* - * ICE-related methods - ********************/ - // This method is public to allow PeerConnection to update it from - // SetConfiguration. - void SetIceConfig(const cricket::IceConfig& config); - // Set the "needs-ice-restart" flag as described in JSEP. After the flag is - // set, offers should generate new ufrags/passwords until an ICE restart - // occurs. - void SetNeedsIceRestartFlag(); - // Returns true if the ICE restart flag above was set, and no ICE restart has - // occurred yet for this transport (by applying a local description with - // changed ufrag/password). If the transport has been deleted as a result of - // bundling, returns false. - bool NeedsIceRestart(const std::string& mid) const; - // Start gathering candidates for any new transports, or transports doing an - // ICE restart. - void MaybeStartGathering(); - RTCError AddRemoteCandidates( - const std::string& mid, - const std::vector& candidates); - RTCError RemoveRemoteCandidates( - const std::vector& candidates); - - /********************** - * DTLS-related methods - *********************/ - // Specifies the identity to use in this session. - // Can only be called once. - bool SetLocalCertificate( - const rtc::scoped_refptr& certificate); - rtc::scoped_refptr GetLocalCertificate( - const std::string& mid) const; - // Caller owns returned certificate chain. This method mainly exists for - // stats reporting. - std::unique_ptr GetRemoteSSLCertChain( - const std::string& mid) const; - // Get negotiated role, if one has been negotiated. - absl::optional GetDtlsRole(const std::string& mid) const; - - // TODO(deadbeef): GetStats isn't const because all the way down to - // OpenSSLStreamAdapter, GetSslCipherSuite and GetDtlsSrtpCryptoSuite are not - // const. Fix this. - bool GetStats(const std::string& mid, cricket::TransportStats* stats); - - bool initial_offerer() const { return initial_offerer_ && *initial_offerer_; } - - void SetActiveResetSrtpParams(bool active_reset_srtp_params); - - // Allows to overwrite the settings from config. You may set or reset the - // media transport configuration on the jsep transport controller, as long as - // you did not call 'GetMediaTransport' or 'MaybeCreateJsepTransport'. Once - // Jsep transport is created, you can't change this setting. - void SetMediaTransportSettings( - bool use_datagram_transport, - bool use_datagram_transport_for_data_channels, - bool use_datagram_transport_for_data_channels_receive_only); - - // TODO(elrello): For now the rollback only removes mid to transport mappings - // and deletes unused transports, but doesn't consider anything more complex. - void RollbackTransportForMids(const std::vector& mids); - - // Gets the transport parameters for the transport identified by |mid|. - // If |mid| is bundled, returns the parameters for the bundled transport. - // If the transport for |mid| has not been created yet, it may be allocated in - // order to generate transport parameters. - absl::optional GetTransportParameters( - const std::string& mid); - - // All of these signals are fired on the signaling thread. - - // If any transport failed => failed, - // Else if all completed => completed, - // Else if all connected => connected, - // Else => connecting - sigslot::signal1 SignalIceConnectionState; - - sigslot::signal1 - SignalConnectionState; - sigslot::signal1 - SignalStandardizedIceConnectionState; - - // If all transports done gathering => complete, - // Else if any are gathering => gathering, - // Else => new - sigslot::signal1 SignalIceGatheringState; - - // (mid, candidates) - sigslot::signal2&> - SignalIceCandidatesGathered; - - sigslot::signal1 - SignalIceCandidateError; - - sigslot::signal1&> - SignalIceCandidatesRemoved; - - sigslot::signal1 - SignalIceCandidatePairChanged; - - sigslot::signal1 SignalDtlsHandshakeError; - - private: - RTCError ApplyDescription_n(bool local, - SdpType type, - const cricket::SessionDescription* description); - RTCError ValidateAndMaybeUpdateBundleGroup( - bool local, - SdpType type, - const cricket::SessionDescription* description); - RTCError ValidateContent(const cricket::ContentInfo& content_info); - - void HandleRejectedContent(const cricket::ContentInfo& content_info, - const cricket::SessionDescription* description); - bool HandleBundledContent(const cricket::ContentInfo& content_info); - - bool SetTransportForMid(const std::string& mid, - cricket::TgJsepTransport* jsep_transport); - void RemoveTransportForMid(const std::string& mid); - - cricket::TgJsepTransportDescription CreateJsepTransportDescription( - const cricket::ContentInfo& content_info, - const cricket::TransportInfo& transport_info, - const std::vector& encrypted_extension_ids, - int rtp_abs_sendtime_extn_id, - absl::optional media_alt_protocol, - absl::optional data_alt_protocol); - - absl::optional bundled_mid() const { - absl::optional bundled_mid; - if (bundle_group_ && bundle_group_->FirstContentName()) { - bundled_mid = *(bundle_group_->FirstContentName()); - } - return bundled_mid; - } - - bool IsBundled(const std::string& mid) const { - return bundle_group_ && bundle_group_->HasContentName(mid); - } - - bool ShouldUpdateBundleGroup(SdpType type, - const cricket::SessionDescription* description); - - std::vector MergeEncryptedHeaderExtensionIdsForBundle( - const cricket::SessionDescription* description); - std::vector GetEncryptedHeaderExtensionIds( - const cricket::ContentInfo& content_info); - - // Extracts the alt-protocol settings that apply to the bundle group. - RTCError GetAltProtocolsForBundle( - const cricket::SessionDescription* description, - absl::optional* media_alt_protocol, - absl::optional* data_alt_protocol); - - int GetRtpAbsSendTimeHeaderExtensionId( - const cricket::ContentInfo& content_info); - - // This method takes the BUNDLE group into account. If the TgJsepTransport is - // destroyed because of BUNDLE, it would return the transport which other - // transports are bundled on (In current implementation, it is the first - // content in the BUNDLE group). - const cricket::TgJsepTransport* GetJsepTransportForMid( - const std::string& mid) const; - cricket::TgJsepTransport* GetJsepTransportForMid(const std::string& mid); - - // Get the JsepTransport without considering the BUNDLE group. Return nullptr - // if the JsepTransport is destroyed. - const cricket::TgJsepTransport* GetJsepTransportByName( - const std::string& transport_name) const; - cricket::TgJsepTransport* GetJsepTransportByName( - const std::string& transport_name); - - // Creates jsep transport. Noop if transport is already created. - // Transport is created either during SetLocalDescription (|local| == true) or - // during SetRemoteDescription (|local| == false). Passing |local| helps to - // differentiate initiator (caller) from answerer (callee). - RTCError MaybeCreateJsepTransport( - bool local, - const cricket::ContentInfo& content_info, - const cricket::SessionDescription& description); - - // Creates datagram transport if config wants to use it, and a=x-mt line is - // present for the current media transport. Returned - // DatagramTransportInterface is not connected, and must be connected to ICE. - // You must call |GenerateOrGetLastMediaTransportOffer| on the caller before - // calling MaybeCreateDatagramTransport. - std::unique_ptr - MaybeCreateDatagramTransport(const cricket::ContentInfo& content_info, - const cricket::SessionDescription& description, - bool local); - - void MaybeDestroyJsepTransport(const std::string& mid); - void DestroyAllJsepTransports_n(); - - void SetIceRole_n(cricket::IceRole ice_role); - - cricket::IceRole DetermineIceRole( - cricket::TgJsepTransport* jsep_transport, - const cricket::TransportInfo& transport_info, - SdpType type, - bool local); - - std::unique_ptr CreateDtlsTransport( - const cricket::ContentInfo& content_info, - cricket::IceTransportInternal* ice, - DatagramTransportInterface* datagram_transport); - rtc::scoped_refptr CreateIceTransport( - const std::string& transport_name, - bool rtcp); - - std::unique_ptr CreateUnencryptedRtpTransport( - const std::string& transport_name, - rtc::PacketTransportInternal* rtp_packet_transport, - rtc::PacketTransportInternal* rtcp_packet_transport); - std::unique_ptr CreateSdesTransport( - const std::string& transport_name, - cricket::DtlsTransportInternal* rtp_dtls_transport, - cricket::DtlsTransportInternal* rtcp_dtls_transport); - std::unique_ptr CreateDtlsSrtpTransport( - const std::string& transport_name, - cricket::DtlsTransportInternal* rtp_dtls_transport, - cricket::DtlsTransportInternal* rtcp_dtls_transport); - - // Collect all the DtlsTransports, including RTP and RTCP, from the - // JsepTransports. JsepTransportController can iterate all the DtlsTransports - // and update the aggregate states. - std::vector GetDtlsTransports(); - - // Handlers for signals from Transport. - void OnTransportWritableState_n(rtc::PacketTransportInternal* transport); - void OnTransportReceivingState_n(rtc::PacketTransportInternal* transport); - void OnTransportGatheringState_n(cricket::IceTransportInternal* transport); - void OnTransportCandidateGathered_n(cricket::IceTransportInternal* transport, - const cricket::Candidate& candidate); - void OnTransportCandidateError_n( - cricket::IceTransportInternal* transport, - const cricket::IceCandidateErrorEvent& event); - void OnTransportCandidatesRemoved_n(cricket::IceTransportInternal* transport, - const cricket::Candidates& candidates); - void OnTransportRoleConflict_n(cricket::IceTransportInternal* transport); - void OnTransportStateChanged_n(cricket::IceTransportInternal* transport); - void OnTransportCandidatePairChanged_n( - const cricket::CandidatePairChangeEvent& event); - void OnDataChannelTransportNegotiated_n( - cricket::TgJsepTransport* transport, - DataChannelTransportInterface* data_channel_transport); - - void UpdateAggregateStates_n(); - - void OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer* packet, - int64_t packet_time_us); - - void OnDtlsHandshakeError(rtc::SSLHandshakeError error); - - rtc::Thread* const signaling_thread_ = nullptr; - rtc::Thread* const network_thread_ = nullptr; - cricket::PortAllocator* const port_allocator_ = nullptr; - AsyncResolverFactory* const async_resolver_factory_ = nullptr; - - std::map> - jsep_transports_by_name_; - // This keeps track of the mapping between media section - // (BaseChannel/SctpTransport) and the TgJsepTransport underneath. - std::map mid_to_transport_; - - // Aggregate states for Transports. - // standardized_ice_connection_state_ is intended to replace - // ice_connection_state, see bugs.webrtc.org/9308 - cricket::IceConnectionState ice_connection_state_ = - cricket::kIceConnectionConnecting; - PeerConnectionInterface::IceConnectionState - standardized_ice_connection_state_ = - PeerConnectionInterface::kIceConnectionNew; - PeerConnectionInterface::PeerConnectionState combined_connection_state_ = - PeerConnectionInterface::PeerConnectionState::kNew; - cricket::IceGatheringState ice_gathering_state_ = cricket::kIceGatheringNew; - - Config config_; - - // Early on in the call we don't know if datagram transport is going to be - // used, but we need to get the server-supported parameters to add to an SDP. - // This server datagram transport will be promoted to the used datagram - // transport after the local description is set, and the ownership will be - // transferred to the actual TgJsepTransport. This "offer" datagram transport is - // not created if it's done on the party that provides answer. This offer - // datagram transport is only created once at the beginning of the connection, - // and never again. - std::unique_ptr offer_datagram_transport_ = - nullptr; - - const cricket::SessionDescription* local_desc_ = nullptr; - const cricket::SessionDescription* remote_desc_ = nullptr; - absl::optional initial_offerer_; - - absl::optional bundle_group_; - - cricket::IceConfig ice_config_; - cricket::IceRole ice_role_ = cricket::ICEROLE_CONTROLLING; - uint64_t ice_tiebreaker_ = rtc::CreateRandomId64(); - rtc::scoped_refptr certificate_; - rtc::AsyncInvoker invoker_; - - RTC_DISALLOW_COPY_AND_ASSIGN(TgJsepTransportController); -}; - -} // namespace webrtc - -#endif // PC_JSEP_TRANSPORT_CONTROLLER_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp deleted file mode 100644 index 7e21008d0d..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.cpp +++ /dev/null @@ -1,6629 +0,0 @@ -/* - * Copyright 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_peer_connection.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "absl/algorithm/container.h" -#include "absl/strings/match.h" -#include "api/jsep_ice_candidate.h" -#include "api/jsep_session_description.h" -#include "api/media_stream_proxy.h" -#include "api/media_stream_track_proxy.h" -#include "api/rtc_error.h" -#include "api/rtc_event_log/rtc_event_log.h" -#include "api/rtc_event_log_output_file.h" -#include "api/rtp_parameters.h" -#include "api/uma_metrics.h" -#include "api/video/builtin_video_bitrate_allocator_factory.h" -#include "call/call.h" -#include "logging/rtc_event_log/ice_logger.h" -#include "media/base/rid_description.h" -#include "media/sctp/sctp_transport.h" -#include "pc/audio_rtp_receiver.h" -#include "pc/audio_track.h" -#include "pc/channel.h" -#include "pc/channel_manager.h" -#include "pc/dtmf_sender.h" -#include "pc/media_stream.h" -#include "pc/media_stream_observer.h" -#include "pc/remote_audio_source.h" -#include "pc/rtp_media_utils.h" -#include "pc/rtp_receiver.h" -#include "pc/rtp_sender.h" -#include "pc/sctp_transport.h" -#include "pc/sctp_utils.h" -#include "pc/sdp_utils.h" -#include "pc/stream_collection.h" -#include "pc/video_rtp_receiver.h" -#include "pc/video_track.h" -#include "rtc_base/bind.h" -#include "rtc_base/checks.h" -#include "rtc_base/logging.h" -#include "rtc_base/string_encode.h" -#include "rtc_base/strings/string_builder.h" -#include "rtc_base/system/fallthrough.h" -#include "rtc_base/trace_event.h" -#include "system_wrappers/include/clock.h" -#include "system_wrappers/include/field_trial.h" -#include "system_wrappers/include/metrics.h" - -#include "tg_jsep_transport.h" -#include "tg_jsep_transport_controller.h" - -#include "tg_rtp_sender.h" - -using cricket::ContentInfo; -using cricket::ContentInfos; -using cricket::MediaContentDescription; -using cricket::MediaProtocolType; -using cricket::RidDescription; -using cricket::RidDirection; -using cricket::SessionDescription; -using cricket::SimulcastDescription; -using cricket::SimulcastLayer; -using cricket::SimulcastLayerList; -using cricket::StreamParams; -using cricket::TransportInfo; - -using cricket::LOCAL_PORT_TYPE; -using cricket::PRFLX_PORT_TYPE; -using cricket::RELAY_PORT_TYPE; -using cricket::STUN_PORT_TYPE; - -namespace webrtc { - -RTCError TgPeerConnectionInterface::RemoveTrackNew( - rtc::scoped_refptr sender) { - return RTCError(RemoveTrack(sender) ? RTCErrorType::NONE - : RTCErrorType::INTERNAL_ERROR); -} - -RTCError TgPeerConnectionInterface::SetConfiguration( - const PeerConnectionInterface::RTCConfiguration& config) { - return RTCError(); -} - -RTCError TgPeerConnectionInterface::SetBitrate(const BitrateSettings& bitrate) { - PeerConnectionInterface::BitrateParameters bitrate_parameters; - bitrate_parameters.min_bitrate_bps = bitrate.min_bitrate_bps; - bitrate_parameters.current_bitrate_bps = bitrate.start_bitrate_bps; - bitrate_parameters.max_bitrate_bps = bitrate.max_bitrate_bps; - return SetBitrate(bitrate_parameters); -} - -RTCError TgPeerConnectionInterface::SetBitrate( - const PeerConnectionInterface::BitrateParameters& bitrate_parameters) { - BitrateSettings bitrate; - bitrate.min_bitrate_bps = bitrate_parameters.min_bitrate_bps; - bitrate.start_bitrate_bps = bitrate_parameters.current_bitrate_bps; - bitrate.max_bitrate_bps = bitrate_parameters.max_bitrate_bps; - return SetBitrate(bitrate); -} - -// Error messages -const char kBundleWithoutRtcpMux[] = -"rtcp-mux must be enabled when BUNDLE " -"is enabled."; -const char kInvalidCandidates[] = "Description contains invalid candidates."; -const char kInvalidSdp[] = "Invalid session description."; -const char kMlineMismatchInAnswer[] = -"The order of m-lines in answer doesn't match order in offer. Rejecting " -"answer."; -const char kMlineMismatchInSubsequentOffer[] = -"The order of m-lines in subsequent offer doesn't match order from " -"previous offer/answer."; -const char kSdpWithoutDtlsFingerprint[] = -"Called with SDP without DTLS fingerprint."; -const char kSdpWithoutSdesCrypto[] = "Called with SDP without SDES crypto."; -const char kSdpWithoutIceUfragPwd[] = -"Called with SDP without ice-ufrag and ice-pwd."; -const char kSessionError[] = "Session error code: "; -const char kSessionErrorDesc[] = "Session error description: "; -const char kDtlsSrtpSetupFailureRtp[] = -"Couldn't set up DTLS-SRTP on RTP channel."; -const char kDtlsSrtpSetupFailureRtcp[] = -"Couldn't set up DTLS-SRTP on RTCP channel."; - -namespace { - -// Field trials. -// Controls datagram transport support. -const char kDatagramTransportFieldTrial[] = "WebRTC-DatagramTransport"; -// Controls datagram transport data channel support. -const char kDatagramTransportDataChannelFieldTrial[] = -"WebRTC-DatagramTransportDataChannels"; - -// UMA metric names. -const char kSimulcastVersionApplyLocalDescription[] = -"WebRTC.PeerConnection.Simulcast.ApplyLocalDescription"; -const char kSimulcastVersionApplyRemoteDescription[] = -"WebRTC.PeerConnection.Simulcast.ApplyRemoteDescription"; -const char kSimulcastNumberOfEncodings[] = -"WebRTC.PeerConnection.Simulcast.NumberOfSendEncodings"; -const char kSimulcastDisabled[] = "WebRTC.PeerConnection.Simulcast.Disabled"; - -static const char kDefaultStreamId[] = "default"; -static const char kDefaultAudioSenderId[] = "defaulta0"; -static const char kDefaultVideoSenderId[] = "defaultv0"; - -// The length of RTCP CNAMEs. -static const int kRtcpCnameLength = 16; - -enum { - MSG_SET_SESSIONDESCRIPTION_SUCCESS = 0, - MSG_SET_SESSIONDESCRIPTION_FAILED, - MSG_CREATE_SESSIONDESCRIPTION_FAILED, - MSG_REPORT_USAGE_PATTERN, -}; - -static const int REPORT_USAGE_PATTERN_DELAY_MS = 60000; - -struct SetSessionDescriptionMsg : public rtc::MessageData { - explicit SetSessionDescriptionMsg( - webrtc::SetSessionDescriptionObserver* observer) - : observer(observer) {} - - rtc::scoped_refptr observer; - RTCError error; -}; - -struct CreateSessionDescriptionMsg : public rtc::MessageData { - explicit CreateSessionDescriptionMsg( - webrtc::CreateSessionDescriptionObserver* observer) - : observer(observer) {} - - rtc::scoped_refptr observer; - RTCError error; -}; - -struct GetStatsMsg : public rtc::MessageData { - GetStatsMsg(webrtc::StatsObserver* observer, - webrtc::MediaStreamTrackInterface* track) - : observer(observer), track(track) {} - rtc::scoped_refptr observer; - rtc::scoped_refptr track; -}; - -// Check if we can send |new_stream| on a PeerConnection. -bool CanAddLocalMediaStream(webrtc::StreamCollectionInterface* current_streams, - webrtc::MediaStreamInterface* new_stream) { - if (!new_stream || !current_streams) { - return false; - } - if (current_streams->find(new_stream->id()) != nullptr) { - RTC_LOG(LS_ERROR) << "MediaStream with ID " << new_stream->id() - << " is already added."; - return false; - } - return true; -} - -// If the direction is "recvonly" or "inactive", treat the description -// as containing no streams. -// See: https://code.google.com/p/webrtc/issues/detail?id=5054 -std::vector GetActiveStreams( - const cricket::MediaContentDescription* desc) { - return RtpTransceiverDirectionHasSend(desc->direction()) - ? desc->streams() - : std::vector(); -} - -bool IsValidOfferToReceiveMedia(int value) { - typedef PeerConnectionInterface::RTCOfferAnswerOptions Options; - return (value >= Options::kUndefined) && - (value <= Options::kMaxOfferToReceiveMedia); -} - -uint32_t ConvertIceTransportTypeToCandidateFilter( - PeerConnectionInterface::IceTransportsType type) { - switch (type) { - case PeerConnectionInterface::kNone: - return cricket::CF_NONE; - case PeerConnectionInterface::kRelay: - return cricket::CF_RELAY; - case PeerConnectionInterface::kNoHost: - return (cricket::CF_ALL & ~cricket::CF_HOST); - case PeerConnectionInterface::kAll: - return cricket::CF_ALL; - default: - RTC_NOTREACHED(); - } - return cricket::CF_NONE; -} - -std::string GetSignalingStateString( - PeerConnectionInterface::SignalingState state) { - switch (state) { - case PeerConnectionInterface::kStable: - return "kStable"; - case PeerConnectionInterface::kHaveLocalOffer: - return "kHaveLocalOffer"; - case PeerConnectionInterface::kHaveLocalPrAnswer: - return "kHavePrAnswer"; - case PeerConnectionInterface::kHaveRemoteOffer: - return "kHaveRemoteOffer"; - case PeerConnectionInterface::kHaveRemotePrAnswer: - return "kHaveRemotePrAnswer"; - case PeerConnectionInterface::kClosed: - return "kClosed"; - } - RTC_NOTREACHED(); - return ""; -} - -IceCandidatePairType GetIceCandidatePairCounter( - const cricket::Candidate& local, - const cricket::Candidate& remote) { - const auto& l = local.type(); - const auto& r = remote.type(); - const auto& host = LOCAL_PORT_TYPE; - const auto& srflx = STUN_PORT_TYPE; - const auto& relay = RELAY_PORT_TYPE; - const auto& prflx = PRFLX_PORT_TYPE; - if (l == host && r == host) { - bool local_hostname = - !local.address().hostname().empty() && local.address().IsUnresolvedIP(); - bool remote_hostname = !remote.address().hostname().empty() && - remote.address().IsUnresolvedIP(); - bool local_private = IPIsPrivate(local.address().ipaddr()); - bool remote_private = IPIsPrivate(remote.address().ipaddr()); - if (local_hostname) { - if (remote_hostname) { - return kIceCandidatePairHostNameHostName; - } else if (remote_private) { - return kIceCandidatePairHostNameHostPrivate; - } else { - return kIceCandidatePairHostNameHostPublic; - } - } else if (local_private) { - if (remote_hostname) { - return kIceCandidatePairHostPrivateHostName; - } else if (remote_private) { - return kIceCandidatePairHostPrivateHostPrivate; - } else { - return kIceCandidatePairHostPrivateHostPublic; - } - } else { - if (remote_hostname) { - return kIceCandidatePairHostPublicHostName; - } else if (remote_private) { - return kIceCandidatePairHostPublicHostPrivate; - } else { - return kIceCandidatePairHostPublicHostPublic; - } - } - } - if (l == host && r == srflx) - return kIceCandidatePairHostSrflx; - if (l == host && r == relay) - return kIceCandidatePairHostRelay; - if (l == host && r == prflx) - return kIceCandidatePairHostPrflx; - if (l == srflx && r == host) - return kIceCandidatePairSrflxHost; - if (l == srflx && r == srflx) - return kIceCandidatePairSrflxSrflx; - if (l == srflx && r == relay) - return kIceCandidatePairSrflxRelay; - if (l == srflx && r == prflx) - return kIceCandidatePairSrflxPrflx; - if (l == relay && r == host) - return kIceCandidatePairRelayHost; - if (l == relay && r == srflx) - return kIceCandidatePairRelaySrflx; - if (l == relay && r == relay) - return kIceCandidatePairRelayRelay; - if (l == relay && r == prflx) - return kIceCandidatePairRelayPrflx; - if (l == prflx && r == host) - return kIceCandidatePairPrflxHost; - if (l == prflx && r == srflx) - return kIceCandidatePairPrflxSrflx; - if (l == prflx && r == relay) - return kIceCandidatePairPrflxRelay; - return kIceCandidatePairMax; -} - -// Logic to decide if an m= section can be recycled. This means that the new -// m= section is not rejected, but the old local or remote m= section is -// rejected. |old_content_one| and |old_content_two| refer to the m= section -// of the old remote and old local descriptions in no particular order. -// We need to check both the old local and remote because either -// could be the most current from the latest negotation. -bool IsMediaSectionBeingRecycled(SdpType type, - const ContentInfo& content, - const ContentInfo* old_content_one, - const ContentInfo* old_content_two) { - return type == SdpType::kOffer && !content.rejected && - ((old_content_one && old_content_one->rejected) || - (old_content_two && old_content_two->rejected)); -} - -// Verify that the order of media sections in |new_desc| matches -// |current_desc|. The number of m= sections in |new_desc| should be no -// less than |current_desc|. In the case of checking an answer's -// |new_desc|, the |current_desc| is the last offer that was set as the -// local or remote. In the case of checking an offer's |new_desc| we -// check against the local and remote descriptions stored from the last -// negotiation, because either of these could be the most up to date for -// possible rejected m sections. These are the |current_desc| and -// |secondary_current_desc|. -bool MediaSectionsInSameOrder(const SessionDescription& current_desc, - const SessionDescription* secondary_current_desc, - const SessionDescription& new_desc, - const SdpType type) { - if (current_desc.contents().size() > new_desc.contents().size()) { - return false; - } - - for (size_t i = 0; i < current_desc.contents().size(); ++i) { - const cricket::ContentInfo* secondary_content_info = nullptr; - if (secondary_current_desc && - i < secondary_current_desc->contents().size()) { - secondary_content_info = &secondary_current_desc->contents()[i]; - } - if (IsMediaSectionBeingRecycled(type, new_desc.contents()[i], - ¤t_desc.contents()[i], - secondary_content_info)) { - // For new offer descriptions, if the media section can be recycled, it's - // valid for the MID and media type to change. - continue; - } - if (new_desc.contents()[i].name != current_desc.contents()[i].name) { - return false; - } - const MediaContentDescription* new_desc_mdesc = - new_desc.contents()[i].media_description(); - const MediaContentDescription* current_desc_mdesc = - current_desc.contents()[i].media_description(); - if (new_desc_mdesc->type() != current_desc_mdesc->type()) { - return false; - } - } - return true; -} - -bool MediaSectionsHaveSameCount(const SessionDescription& desc1, - const SessionDescription& desc2) { - return desc1.contents().size() == desc2.contents().size(); -} - -// Checks that each non-rejected content has ice-ufrag and ice-pwd set, unless -// it's in a BUNDLE group, in which case only the BUNDLE-tag section (first -// media section/description in the BUNDLE group) needs a ufrag and pwd. -bool VerifyIceUfragPwdPresent(const SessionDescription* desc) { - const cricket::ContentGroup* bundle = - desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - for (const cricket::ContentInfo& content_info : desc->contents()) { - if (content_info.rejected) { - continue; - } - const std::string& mid = content_info.name; - if (bundle && bundle->HasContentName(mid) && - mid != *(bundle->FirstContentName())) { - // This isn't the first media section in the BUNDLE group, so it's not - // required to have ufrag/password, since only the ufrag/password from - // the first section actually get used. - continue; - } - - // If the content isn't rejected or bundled into another m= section, - // ice-ufrag and ice-pwd must be present. - const TransportInfo* tinfo = desc->GetTransportInfoByName(mid); - if (!tinfo) { - // Something is not right. - RTC_LOG(LS_ERROR) << kInvalidSdp; - return false; - } - if (tinfo->description.ice_ufrag.empty() || - tinfo->description.ice_pwd.empty()) { - RTC_LOG(LS_ERROR) << "Session description must have ice ufrag and pwd."; - return false; - } - } - return true; -} - -// Returns true if |new_desc| requests an ICE restart (i.e., new ufrag/pwd). -bool CheckForRemoteIceRestart(const SessionDescriptionInterface* old_desc, - const SessionDescriptionInterface* new_desc, - const std::string& content_name) { - if (!old_desc) { - return false; - } - const SessionDescription* new_sd = new_desc->description(); - const SessionDescription* old_sd = old_desc->description(); - const ContentInfo* cinfo = new_sd->GetContentByName(content_name); - if (!cinfo || cinfo->rejected) { - return false; - } - // If the content isn't rejected, check if ufrag and password has changed. - const cricket::TransportDescription* new_transport_desc = - new_sd->GetTransportDescriptionByName(content_name); - const cricket::TransportDescription* old_transport_desc = - old_sd->GetTransportDescriptionByName(content_name); - if (!new_transport_desc || !old_transport_desc) { - // No transport description exists. This is not an ICE restart. - return false; - } - if (cricket::IceCredentialsChanged( - old_transport_desc->ice_ufrag, old_transport_desc->ice_pwd, - new_transport_desc->ice_ufrag, new_transport_desc->ice_pwd)) { - RTC_LOG(LS_INFO) << "Remote peer requests ICE restart for " << content_name - << "."; - return true; - } - return false; -} - -// Generates a string error message for SetLocalDescription/SetRemoteDescription -// from an RTCError. -std::string GetSetDescriptionErrorMessage(cricket::ContentSource source, - SdpType type, - const RTCError& error) { - rtc::StringBuilder oss; - oss << "Failed to set " << (source == cricket::CS_LOCAL ? "local" : "remote") - << " " << SdpTypeToString(type) << " sdp: " << error.message(); - return oss.Release(); -} - -std::string GetStreamIdsString(rtc::ArrayView stream_ids) { - std::string output = "streams=["; - const char* separator = ""; - for (const auto& stream_id : stream_ids) { - output.append(separator).append(stream_id); - separator = ", "; - } - output.append("]"); - return output; -} - -absl::optional RTCConfigurationToIceConfigOptionalInt( - int rtc_configuration_parameter) { - if (rtc_configuration_parameter == - webrtc::PeerConnectionInterface::RTCConfiguration::kUndefined) { - return absl::nullopt; - } - return rtc_configuration_parameter; -} - -void ReportSimulcastApiVersion(const char* name, - const SessionDescription& session) { - bool has_legacy = false; - bool has_spec_compliant = false; - for (const ContentInfo& content : session.contents()) { - if (!content.media_description()) { - continue; - } - has_spec_compliant |= content.media_description()->HasSimulcast(); - for (const StreamParams& sp : content.media_description()->streams()) { - has_legacy |= sp.has_ssrc_group(cricket::kSimSsrcGroupSemantics); - } - } - - if (has_legacy) { - RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionLegacy, - kSimulcastApiVersionMax); - } - if (has_spec_compliant) { - RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionSpecCompliant, - kSimulcastApiVersionMax); - } - if (!has_legacy && !has_spec_compliant) { - RTC_HISTOGRAM_ENUMERATION(name, kSimulcastApiVersionNone, - kSimulcastApiVersionMax); - } -} - -const ContentInfo* FindTransceiverMSection( - RtpTransceiverProxyWithInternal* transceiver, - const SessionDescriptionInterface* session_description) { - return transceiver->mid() - ? session_description->description()->GetContentByName( - *transceiver->mid()) - : nullptr; -} - -// Wraps a CreateSessionDescriptionObserver and an OperationsChain operation -// complete callback. When the observer is invoked, the wrapped observer is -// invoked followed by invoking the completion callback. -class CreateSessionDescriptionObserverOperationWrapper -: public CreateSessionDescriptionObserver { -public: - CreateSessionDescriptionObserverOperationWrapper( - rtc::scoped_refptr observer, - std::function operation_complete_callback) - : observer_(std::move(observer)), - operation_complete_callback_(std::move(operation_complete_callback)) { - RTC_DCHECK(observer_); - } - ~CreateSessionDescriptionObserverOperationWrapper() override { - RTC_DCHECK(was_called_); - } - - void OnSuccess(SessionDescriptionInterface* desc) override { - RTC_DCHECK(!was_called_); -#ifdef RTC_DCHECK_IS_ON - was_called_ = true; -#endif // RTC_DCHECK_IS_ON - // Completing the operation before invoking the observer allows the observer - // to execute SetLocalDescription() without delay. - operation_complete_callback_(); - observer_->OnSuccess(desc); - } - - void OnFailure(RTCError error) override { - RTC_DCHECK(!was_called_); -#ifdef RTC_DCHECK_IS_ON - was_called_ = true; -#endif // RTC_DCHECK_IS_ON - operation_complete_callback_(); - observer_->OnFailure(std::move(error)); - } - -private: -#ifdef RTC_DCHECK_IS_ON - bool was_called_ = false; -#endif // RTC_DCHECK_IS_ON - rtc::scoped_refptr observer_; - std::function operation_complete_callback_; -}; - -} // namespace - -// Used by parameterless SetLocalDescription() to create an offer or answer. -// Upon completion of creating the session description, SetLocalDescription() is -// invoked with the result. -// For consistency with DoSetLocalDescription(), if the PeerConnection is -// destroyed midst operation, we DO NOT inform the -// |set_local_description_observer| that the operation failed. -// TODO(hbos): If/when we process SLD messages in ~PeerConnection, the -// consistent thing would be to inform the observer here. -class TgPeerConnection::ImplicitCreateSessionDescriptionObserver -: public CreateSessionDescriptionObserver { -public: - ImplicitCreateSessionDescriptionObserver( - rtc::WeakPtr pc, - rtc::scoped_refptr - set_local_description_observer) - : pc_(std::move(pc)), - set_local_description_observer_( - std::move(set_local_description_observer)) {} - ~ImplicitCreateSessionDescriptionObserver() override { - RTC_DCHECK(was_called_); - } - - void SetOperationCompleteCallback( - std::function operation_complete_callback) { - operation_complete_callback_ = std::move(operation_complete_callback); - } - - bool was_called() const { return was_called_; } - - void OnSuccess(SessionDescriptionInterface* desc_ptr) override { - RTC_DCHECK(!was_called_); - std::unique_ptr desc(desc_ptr); - was_called_ = true; - - // Abort early if |pc_| is no longer valid. - if (!pc_) { - operation_complete_callback_(); - return; - } - // DoSetLocalDescription() is currently implemented as a synchronous - // operation but where the |set_local_description_observer_|'s callbacks are - // invoked asynchronously in a post to TgPeerConnection::OnMessage(). - pc_->DoSetLocalDescription(std::move(desc), - std::move(set_local_description_observer_)); - // For backwards-compatability reasons, we declare the operation as - // completed here (rather than in TgPeerConnection::OnMessage()). This ensures - // that subsequent offer/answer operations can start immediately (without - // waiting for OnMessage()). - operation_complete_callback_(); - } - - void OnFailure(RTCError error) override { - RTC_DCHECK(!was_called_); - was_called_ = true; - - // Abort early if |pc_| is no longer valid. - if (!pc_) { - operation_complete_callback_(); - return; - } - // DoSetLocalDescription() reports its failures in a post. We do the - // same thing here for consistency. - pc_->PostSetSessionDescriptionFailure( - set_local_description_observer_, - RTCError(error.type(), - std::string("SetLocalDescription failed to create " - "session description - ") + - error.message())); - operation_complete_callback_(); - } - -private: - bool was_called_ = false; - rtc::WeakPtr pc_; - rtc::scoped_refptr - set_local_description_observer_; - std::function operation_complete_callback_; -}; - -class TgPeerConnection::LocalIceCredentialsToReplace { -public: - // Sets the ICE credentials that need restarting to the ICE credentials of - // the current and pending descriptions. - void SetIceCredentialsFromLocalDescriptions( - const SessionDescriptionInterface* current_local_description, - const SessionDescriptionInterface* pending_local_description) { - ice_credentials_.clear(); - if (current_local_description) { - AppendIceCredentialsFromSessionDescription(*current_local_description); - } - if (pending_local_description) { - AppendIceCredentialsFromSessionDescription(*pending_local_description); - } - } - - void ClearIceCredentials() { ice_credentials_.clear(); } - - // Returns true if we have ICE credentials that need restarting. - bool HasIceCredentials() const { return !ice_credentials_.empty(); } - - // Returns true if |local_description| shares no ICE credentials with the - // ICE credentials that need restarting. - bool SatisfiesIceRestart( - const SessionDescriptionInterface& local_description) const { - for (const auto& transport_info : - local_description.description()->transport_infos()) { - if (ice_credentials_.find(std::make_pair( - transport_info.description.ice_ufrag, - transport_info.description.ice_pwd)) != ice_credentials_.end()) { - return false; - } - } - return true; - } - -private: - void AppendIceCredentialsFromSessionDescription( - const SessionDescriptionInterface& desc) { - for (const auto& transport_info : desc.description()->transport_infos()) { - ice_credentials_.insert( - std::make_pair(transport_info.description.ice_ufrag, - transport_info.description.ice_pwd)); - } - } - - std::set> ice_credentials_; -}; - -// Upon completion, posts a task to execute the callback of the -// SetSessionDescriptionObserver asynchronously on the same thread. At this -// point, the state of the peer connection might no longer reflect the effects -// of the SetRemoteDescription operation, as the peer connection could have been -// modified during the post. -// TODO(hbos): Remove this class once we remove the version of -// PeerConnectionInterface::SetRemoteDescription() that takes a -// SetSessionDescriptionObserver as an argument. -class TgPeerConnection::SetRemoteDescriptionObserverAdapter -: public rtc::RefCountedObject { -public: - SetRemoteDescriptionObserverAdapter( - rtc::scoped_refptr pc, - rtc::scoped_refptr wrapper) - : pc_(std::move(pc)), wrapper_(std::move(wrapper)) {} - - // SetRemoteDescriptionObserverInterface implementation. - void OnSetRemoteDescriptionComplete(RTCError error) override { - if (error.ok()) - pc_->PostSetSessionDescriptionSuccess(wrapper_); - else - pc_->PostSetSessionDescriptionFailure(wrapper_, std::move(error)); - } - -private: - rtc::scoped_refptr pc_; - rtc::scoped_refptr wrapper_; -}; - -void TgPeerConnection::TransceiverStableState::set_newly_created() { - RTC_DCHECK(!has_m_section_); - newly_created_ = true; -} - -void TgPeerConnection::TransceiverStableState::SetMSectionIfUnset( - absl::optional mid, - absl::optional mline_index) { - if (!has_m_section_) { - mid_ = mid; - mline_index_ = mline_index; - has_m_section_ = true; - } -} - -void TgPeerConnection::TransceiverStableState::SetRemoteStreamIdsIfUnset( - const std::vector& ids) { - if (!remote_stream_ids_.has_value()) { - remote_stream_ids_ = ids; - } -} - -// Generate a RTCP CNAME when a TgPeerConnection is created. -std::string TgGenerateRtcpCname() { - std::string cname; - if (!rtc::CreateRandomString(kRtcpCnameLength, &cname)) { - RTC_LOG(LS_ERROR) << "Failed to generate CNAME."; - RTC_NOTREACHED(); - } - return cname; -} - -bool TgValidateOfferAnswerOptions( - const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) { - return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) && - IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video); -} - -// From |rtc_options|, fill parts of |session_options| shared by all generated -// m= sections (in other words, nothing that involves a map/array). -void TgExtractSharedMediaSessionOptions( - const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options, - cricket::MediaSessionOptions* session_options) { - session_options->vad_enabled = rtc_options.voice_activity_detection; - session_options->bundle_enabled = rtc_options.use_rtp_mux; - session_options->raw_packetization_for_video = - rtc_options.raw_packetization_for_video; -} - -TgPeerConnection::TgPeerConnection(TgPeerConnectionFactory* factory, - std::unique_ptr event_log, - std::unique_ptr call) -: factory_(factory), -event_log_(std::move(event_log)), -event_log_ptr_(event_log_.get()), -operations_chain_(rtc::OperationsChain::Create()), -datagram_transport_config_( - field_trial::FindFullName(kDatagramTransportFieldTrial)), -datagram_transport_data_channel_config_( - field_trial::FindFullName(kDatagramTransportDataChannelFieldTrial)), -rtcp_cname_(TgGenerateRtcpCname()), -local_streams_(StreamCollection::Create()), -remote_streams_(StreamCollection::Create()), -call_(std::move(call)), -call_ptr_(call_.get()), -local_ice_credentials_to_replace_(new LocalIceCredentialsToReplace()), -weak_ptr_factory_(this) {} - -TgPeerConnection::~TgPeerConnection() { - TRACE_EVENT0("webrtc", "TgPeerConnection::~TgPeerConnection"); - RTC_DCHECK_RUN_ON(signaling_thread()); - - weak_ptr_factory_.InvalidateWeakPtrs(); - - // Need to stop transceivers before destroying the stats collector because - // TgAudioRtpSender has a reference to the StatsCollector it will update when - // stopping. - for (const auto& transceiver : transceivers_) { - transceiver->Stop(); - } - - // Don't destroy BaseChannels until after stats has been cleaned up so that - // the last stats request can still read from the channels. - DestroyAllChannels(); - - RTC_LOG(LS_INFO) << "Session: " << session_id() << " is destroyed."; - - webrtc_session_desc_factory_.reset(); - sctp_factory_.reset(); - transport_controller_.reset(); - - // port_allocator_ lives on the network thread and should be destroyed there. - network_thread()->Invoke(RTC_FROM_HERE, [this] { - RTC_DCHECK_RUN_ON(network_thread()); - port_allocator_.reset(); - }); - // call_ and event_log_ must be destroyed on the worker thread. - worker_thread()->Invoke(RTC_FROM_HERE, [this] { - RTC_DCHECK_RUN_ON(worker_thread()); - call_.reset(); - // The event log must outlive call (and any other object that uses it). - event_log_.reset(); - }); - - // Process all pending notifications in the message queue. If we don't do - // this, requests will linger and not know they succeeded or failed. - rtc::MessageList list; - signaling_thread()->Clear(this, rtc::MQID_ANY, &list); - for (auto& msg : list) { - if (msg.message_id == MSG_CREATE_SESSIONDESCRIPTION_FAILED) { - // Processing CreateOffer() and CreateAnswer() messages ensures their - // observers are invoked even if the TgPeerConnection is destroyed early. - OnMessage(&msg); - } else { - // TODO(hbos): Consider processing all pending messages. This would mean - // that SetLocalDescription() and SetRemoteDescription() observers are - // informed of successes and failures; this is currently NOT the case. - delete msg.pdata; - } - } -} - -void TgPeerConnection::DestroyAllChannels() { - // Destroy video channels first since they may have a pointer to a voice - // channel. - for (const auto& transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { - DestroyTransceiverChannel(transceiver); - } - } - for (const auto& transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - DestroyTransceiverChannel(transceiver); - } - } -} - -bool TgPeerConnection::Initialize( - const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies dependencies) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::Initialize"); - - RTCError config_error = ValidateConfiguration(configuration); - if (!config_error.ok()) { - RTC_LOG(LS_ERROR) << "Invalid configuration: " << config_error.message(); - return false; - } - - if (!dependencies.allocator) { - RTC_LOG(LS_ERROR) - << "TgPeerConnection initialized without a PortAllocator? " - "This shouldn't happen if using PeerConnectionFactory."; - return false; - } - - if (!dependencies.observer) { - // TODO(deadbeef): Why do we do this? - RTC_LOG(LS_ERROR) << "TgPeerConnection initialized without a " - "PeerConnectionObserver"; - return false; - } - - observer_ = dependencies.observer; - async_resolver_factory_ = std::move(dependencies.async_resolver_factory); - port_allocator_ = std::move(dependencies.allocator); - ice_transport_factory_ = std::move(dependencies.ice_transport_factory); - tls_cert_verifier_ = std::move(dependencies.tls_cert_verifier); - - cricket::ServerAddresses stun_servers; - std::vector turn_servers; - - RTCErrorType parse_error = - ParseIceServers(configuration.servers, &stun_servers, &turn_servers); - if (parse_error != RTCErrorType::NONE) { - return false; - } - - // Add the turn logging id to all turn servers - for (cricket::RelayServerConfig& turn_server : turn_servers) { - turn_server.turn_logging_id = configuration.turn_logging_id; - } - - // The port allocator lives on the network thread and should be initialized - // there. - const auto pa_result = - network_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnection::InitializePortAllocator_n, this, - stun_servers, turn_servers, configuration)); - - // If initialization was successful, note if STUN or TURN servers - // were supplied. - if (!stun_servers.empty()) { - NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); - } - if (!turn_servers.empty()) { - NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); - } - - // Send information about IPv4/IPv6 status. - PeerConnectionAddressFamilyCounter address_family; - if (pa_result.enable_ipv6) { - address_family = kPeerConnection_IPv6; - } else { - address_family = kPeerConnection_IPv4; - } - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", address_family, - kPeerConnectionAddressFamilyCounter_Max); - - const PeerConnectionFactoryInterface::Options& options = factory_->options(); - - // RFC 3264: The numeric value of the session id and version in the - // o line MUST be representable with a "64 bit signed integer". - // Due to this constraint session id |session_id_| is max limited to - // LLONG_MAX. - session_id_ = rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX); - TgJsepTransportController::Config config; - config.redetermine_role_on_ice_restart = - configuration.redetermine_role_on_ice_restart; - config.ssl_max_version = factory_->options().ssl_max_version; - config.disable_encryption = options.disable_encryption; - config.bundle_policy = configuration.bundle_policy; - config.rtcp_mux_policy = configuration.rtcp_mux_policy; - // TODO(bugs.webrtc.org/9891) - Remove options.crypto_options then remove this - // stub. - config.crypto_options = configuration.crypto_options.has_value() - ? *configuration.crypto_options - : options.crypto_options; - config.transport_observer = this; - // It's safe to pass |this| and using |rtcp_invoker_| and the |call_| pointer - // since the TgJsepTransportController instance is owned by this PeerConnection - // instance and is destroyed before both |rtcp_invoker_| and the |call_| - // pointer. - config.rtcp_handler = [this](const rtc::CopyOnWriteBuffer& packet, - int64_t packet_time_us) { - RTC_DCHECK_RUN_ON(network_thread()); - rtcp_invoker_.AsyncInvoke( - RTC_FROM_HERE, worker_thread(), [this, packet, packet_time_us] { - RTC_DCHECK_RUN_ON(worker_thread()); - // |call_| is reset on the worker thread in the PeerConnection - // destructor, so we check that it's still valid before propagating - // the packet. - if (call_) { - call_->Receiver()->DeliverPacket(MediaType::ANY, packet, - packet_time_us); - } - }); - }; - config.event_log = event_log_ptr_; -#if defined(ENABLE_EXTERNAL_AUTH) - config.enable_external_auth = true; -#endif - config.active_reset_srtp_params = configuration.active_reset_srtp_params; - - use_datagram_transport_ = datagram_transport_config_.enabled && - configuration.use_datagram_transport.value_or( - datagram_transport_config_.default_value); - use_datagram_transport_for_data_channels_ = - datagram_transport_data_channel_config_.enabled && - configuration.use_datagram_transport_for_data_channels.value_or( - datagram_transport_data_channel_config_.default_value); - use_datagram_transport_for_data_channels_receive_only_ = - configuration.use_datagram_transport_for_data_channels_receive_only - .value_or(datagram_transport_data_channel_config_.receive_only); - if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { - if (!factory_->media_transport_factory()) { - RTC_DCHECK(false) - << "PeerConnecton is initialized with use_datagram_transport = true " - "or use_datagram_transport_for_data_channels = true " - << "but media transport factory is not set in PeerConnectionFactory"; - return false; - } - - config.use_datagram_transport = use_datagram_transport_; - config.use_datagram_transport_for_data_channels = - use_datagram_transport_for_data_channels_; - config.use_datagram_transport_for_data_channels_receive_only = - use_datagram_transport_for_data_channels_receive_only_; - config.media_transport_factory = factory_->media_transport_factory(); - } - - // Obtain a certificate from RTCConfiguration if any were provided (optional). - rtc::scoped_refptr certificate; - if (!configuration.certificates.empty()) { - // TODO(hbos,torbjorng): Decide on certificate-selection strategy instead of - // just picking the first one. The decision should be made based on the DTLS - // handshake. The DTLS negotiations need to know about all certificates. - certificate = configuration.certificates[0]; - } - - if (options.disable_encryption) { - dtls_enabled_ = false; - } else { - // Enable DTLS by default if we have an identity store or a certificate. - dtls_enabled_ = (dependencies.cert_generator || certificate); - // |configuration| can override the default |dtls_enabled_| value. - if (configuration.enable_dtls_srtp) { - dtls_enabled_ = *(configuration.enable_dtls_srtp); - } - } - - sctp_factory_ = factory_->CreateSctpTransportInternalFactory(); - - if (use_datagram_transport_for_data_channels_) { - if (configuration.enable_rtp_data_channel) { - RTC_LOG(LS_ERROR) << "enable_rtp_data_channel and " - "use_datagram_transport_for_data_channels are " - "incompatible and cannot both be set to true"; - return false; - } - if (configuration.enable_dtls_srtp && !*configuration.enable_dtls_srtp) { - } else { - config.sctp_factory = sctp_factory_.get(); - } - } else if (configuration.enable_rtp_data_channel) { - // Enable creation of RTP data channels if the kEnableRtpDataChannels is - // set. It takes precendence over the disable_sctp_data_channels - // PeerConnectionFactoryInterface::Options. - } else { - // DTLS has to be enabled to use SCTP. - if (!options.disable_sctp_data_channels && dtls_enabled_) { - config.sctp_factory = sctp_factory_.get(); - } - } - - config.ice_transport_factory = ice_transport_factory_.get(); - - transport_controller_.reset(new TgJsepTransportController( - signaling_thread(), network_thread(), port_allocator_.get(), - async_resolver_factory_.get(), config)); - transport_controller_->SignalIceConnectionState.connect( - this, &TgPeerConnection::OnTransportControllerConnectionState); - transport_controller_->SignalStandardizedIceConnectionState.connect( - this, &TgPeerConnection::SetStandardizedIceConnectionState); - transport_controller_->SignalConnectionState.connect( - this, &TgPeerConnection::SetConnectionState); - transport_controller_->SignalIceGatheringState.connect( - this, &TgPeerConnection::OnTransportControllerGatheringState); - transport_controller_->SignalIceCandidatesGathered.connect( - this, &TgPeerConnection::OnTransportControllerCandidatesGathered); - transport_controller_->SignalIceCandidateError.connect( - this, &TgPeerConnection::OnTransportControllerCandidateError); - transport_controller_->SignalIceCandidatesRemoved.connect( - this, &TgPeerConnection::OnTransportControllerCandidatesRemoved); - transport_controller_->SignalDtlsHandshakeError.connect( - this, &TgPeerConnection::OnTransportControllerDtlsHandshakeError); - transport_controller_->SignalIceCandidatePairChanged.connect( - this, &TgPeerConnection::OnTransportControllerCandidateChanged); - - configuration_ = configuration; - - transport_controller_->SetIceConfig(ParseIceConfig(configuration)); - - video_options_.screencast_min_bitrate_kbps = - configuration.screencast_min_bitrate; - audio_options_.combined_audio_video_bwe = - configuration.combined_audio_video_bwe; - - audio_options_.audio_jitter_buffer_max_packets = - configuration.audio_jitter_buffer_max_packets; - - audio_options_.audio_jitter_buffer_fast_accelerate = - configuration.audio_jitter_buffer_fast_accelerate; - - audio_options_.audio_jitter_buffer_min_delay_ms = - configuration.audio_jitter_buffer_min_delay_ms; - - audio_options_.audio_jitter_buffer_enable_rtx_handling = - configuration.audio_jitter_buffer_enable_rtx_handling; - - // Whether the certificate generator/certificate is null or not determines - // what PeerConnectionDescriptionFactory will do, so make sure that we give it - // the right instructions by clearing the variables if needed. - if (!dtls_enabled_) { - dependencies.cert_generator.reset(); - certificate = nullptr; - } else if (certificate) { - // Favor generated certificate over the certificate generator. - dependencies.cert_generator.reset(); - } - - webrtc_session_desc_factory_.reset(new TgWebRtcSessionDescriptionFactory( - signaling_thread(), channel_manager(), this, session_id(), - std::move(dependencies.cert_generator), certificate, &ssrc_generator_)); - webrtc_session_desc_factory_->SignalCertificateReady.connect( - this, &TgPeerConnection::OnCertificateReady); - - if (options.disable_encryption) { - webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED); - } - - webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions( - GetCryptoOptions().srtp.enable_encrypted_rtp_header_extensions); - webrtc_session_desc_factory_->set_is_unified_plan(IsUnifiedPlan()); - - // Add default audio/video transceivers for Plan B SDP. - if (!IsUnifiedPlan()) { - transceivers_.push_back( - RtpTransceiverProxyWithInternal::Create( - signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_AUDIO))); - transceivers_.push_back( - RtpTransceiverProxyWithInternal::Create( - signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO))); - } - int delay_ms = - return_histogram_very_quickly_ ? 0 : REPORT_USAGE_PATTERN_DELAY_MS; - signaling_thread()->PostDelayed(RTC_FROM_HERE, delay_ms, this, - MSG_REPORT_USAGE_PATTERN, nullptr); - - if (dependencies.video_bitrate_allocator_factory) { - video_bitrate_allocator_factory_ = - std::move(dependencies.video_bitrate_allocator_factory); - } else { - video_bitrate_allocator_factory_ = - CreateBuiltinVideoBitrateAllocatorFactory(); - } - return true; -} - -RTCError TgPeerConnection::ValidateConfiguration( - const PeerConnectionInterface::RTCConfiguration& config) const { - if (config.ice_regather_interval_range && - config.continual_gathering_policy == PeerConnectionInterface::GATHER_ONCE) { - return RTCError(RTCErrorType::INVALID_PARAMETER, - "ice_regather_interval_range specified but continual " - "gathering policy is GATHER_ONCE"); - } - auto result = - cricket::P2PTransportChannel::ValidateIceConfig(ParseIceConfig(config)); - return result; -} - -rtc::scoped_refptr TgPeerConnection::local_streams() { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "local_streams is not available with Unified " - "Plan SdpSemantics. Please use GetSenders " - "instead."; - return local_streams_; -} - -rtc::scoped_refptr TgPeerConnection::remote_streams() { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "remote_streams is not available with Unified " - "Plan SdpSemantics. Please use GetReceivers " - "instead."; - return remote_streams_; -} - -bool TgPeerConnection::AddStream(MediaStreamInterface* local_stream) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "AddStream is not available with Unified Plan " - "SdpSemantics. Please use AddTrack instead."; - TRACE_EVENT0("webrtc", "TgPeerConnection::AddStream"); - if (IsClosed()) { - return false; - } - if (!CanAddLocalMediaStream(local_streams_, local_stream)) { - return false; - } - - local_streams_->AddStream(local_stream); - MediaStreamObserver* observer = new MediaStreamObserver(local_stream); - observer->SignalAudioTrackAdded.connect(this, - &TgPeerConnection::OnAudioTrackAdded); - observer->SignalAudioTrackRemoved.connect( - this, &TgPeerConnection::OnAudioTrackRemoved); - observer->SignalVideoTrackAdded.connect(this, - &TgPeerConnection::OnVideoTrackAdded); - observer->SignalVideoTrackRemoved.connect( - this, &TgPeerConnection::OnVideoTrackRemoved); - stream_observers_.push_back(std::unique_ptr(observer)); - - for (const auto& track : local_stream->GetAudioTracks()) { - AddAudioTrack(track.get(), local_stream); - } - for (const auto& track : local_stream->GetVideoTracks()) { - AddVideoTrack(track.get(), local_stream); - } - - UpdateNegotiationNeeded(); - return true; -} - -void TgPeerConnection::RemoveStream(MediaStreamInterface* local_stream) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "RemoveStream is not available with Unified " - "Plan SdpSemantics. Please use RemoveTrack " - "instead."; - TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveStream"); - if (!IsClosed()) { - for (const auto& track : local_stream->GetAudioTracks()) { - RemoveAudioTrack(track.get(), local_stream); - } - for (const auto& track : local_stream->GetVideoTracks()) { - RemoveVideoTrack(track.get(), local_stream); - } - } - local_streams_->RemoveStream(local_stream); - stream_observers_.erase( - std::remove_if( - stream_observers_.begin(), stream_observers_.end(), - [local_stream](const std::unique_ptr& observer) { - return observer->stream()->id().compare(local_stream->id()) == 0; - }), - stream_observers_.end()); - - if (IsClosed()) { - return; - } - UpdateNegotiationNeeded(); -} - -RTCErrorOr> TgPeerConnection::AddTrack( - rtc::scoped_refptr track, - const std::vector& stream_ids) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::AddTrack"); - if (!track) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Track is null."); - } - if (!(track->kind() == MediaStreamTrackInterface::kAudioKind || - track->kind() == MediaStreamTrackInterface::kVideoKind)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Track has invalid kind: " + track->kind()); - } - if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "TgPeerConnection is closed."); - } - if (FindSenderForTrack(track)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Sender already exists for track " + track->id() + "."); - } - auto sender_or_error = AddTrackUnifiedPlan(track, stream_ids); - if (sender_or_error.ok()) { - UpdateNegotiationNeeded(); - } - return sender_or_error; -} - -RTCErrorOr> -TgPeerConnection::AddTrackUnifiedPlan( - rtc::scoped_refptr track, - const std::vector& stream_ids) { - auto transceiver = FindFirstTransceiverForAddedTrack(track); - if (transceiver) { - RTC_LOG(LS_INFO) << "Reusing an existing " - << cricket::MediaTypeToString(transceiver->media_type()) - << " transceiver for AddTrack."; - if (transceiver->direction() == RtpTransceiverDirection::kRecvOnly) { - transceiver->internal()->set_direction( - RtpTransceiverDirection::kSendRecv); - } else if (transceiver->direction() == RtpTransceiverDirection::kInactive) { - transceiver->internal()->set_direction( - RtpTransceiverDirection::kSendOnly); - } - transceiver->sender()->SetTrack(track); - transceiver->internal()->sender_internal()->set_stream_ids(stream_ids); - transceiver->internal()->set_reused_for_addtrack(true); - } else { - cricket::MediaType media_type = - (track->kind() == MediaStreamTrackInterface::kAudioKind - ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO); - RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type) - << " transceiver in response to a call to AddTrack."; - std::string sender_id = track->id(); - // Avoid creating a sender with an existing ID by generating a random ID. - // This can happen if this is the second time AddTrack has created a sender - // for this track. - if (FindSenderById(sender_id)) { - sender_id = rtc::CreateRandomUuid(); - } - auto sender = CreateSender(media_type, sender_id, track, stream_ids, {}); - auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid()); - transceiver = CreateAndAddTransceiver(sender, receiver); - transceiver->internal()->set_created_by_addtrack(true); - transceiver->internal()->set_direction(RtpTransceiverDirection::kSendRecv); - } - return transceiver->sender(); -} - -rtc::scoped_refptr> -TgPeerConnection::FindFirstTransceiverForAddedTrack( - rtc::scoped_refptr track) { - RTC_DCHECK(track); - for (auto transceiver : transceivers_) { - if (!transceiver->sender()->track() && - cricket::MediaTypeToString(transceiver->media_type()) == - track->kind() && - !transceiver->internal()->has_ever_been_used_to_send() && - !transceiver->stopped()) { - return transceiver; - } - } - return nullptr; -} - -bool TgPeerConnection::RemoveTrack(RtpSenderInterface* sender) { - TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveTrack"); - return RemoveTrackNew(sender).ok(); -} - -RTCError TgPeerConnection::RemoveTrackNew( - rtc::scoped_refptr sender) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!sender) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "Sender is null."); - } - if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "TgPeerConnection is closed."); - } - if (IsUnifiedPlan()) { - auto transceiver = FindTransceiverBySender(sender); - if (!transceiver || !sender->track()) { - return RTCError::OK(); - } - sender->SetTrack(nullptr); - if (transceiver->direction() == RtpTransceiverDirection::kSendRecv) { - transceiver->internal()->set_direction( - RtpTransceiverDirection::kRecvOnly); - } else if (transceiver->direction() == RtpTransceiverDirection::kSendOnly) { - transceiver->internal()->set_direction( - RtpTransceiverDirection::kInactive); - } - } else { - bool removed; - if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) { - removed = GetAudioTransceiver()->internal()->RemoveSender(sender); - } else { - RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, sender->media_type()); - removed = GetVideoTransceiver()->internal()->RemoveSender(sender); - } - if (!removed) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Couldn't find sender " + sender->id() + " to remove."); - } - } - UpdateNegotiationNeeded(); - return RTCError::OK(); -} - -rtc::scoped_refptr> -TgPeerConnection::FindTransceiverBySender( - rtc::scoped_refptr sender) { - for (auto transceiver : transceivers_) { - if (transceiver->sender() == sender) { - return transceiver; - } - } - return nullptr; -} - -RTCErrorOr> -TgPeerConnection::AddTransceiver( - rtc::scoped_refptr track) { - return AddTransceiver(track, RtpTransceiverInit()); -} - -RTCErrorOr> -TgPeerConnection::AddTransceiver( - rtc::scoped_refptr track, - const RtpTransceiverInit& init) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(IsUnifiedPlan()) - << "AddTransceiver is only available with Unified Plan SdpSemantics"; - if (!track) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "track is null"); - } - cricket::MediaType media_type; - if (track->kind() == MediaStreamTrackInterface::kAudioKind) { - media_type = cricket::MEDIA_TYPE_AUDIO; - } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) { - media_type = cricket::MEDIA_TYPE_VIDEO; - } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Track kind is not audio or video"); - } - return AddTransceiver(media_type, track, init); -} - -RTCErrorOr> -TgPeerConnection::AddTransceiver(cricket::MediaType media_type) { - return AddTransceiver(media_type, RtpTransceiverInit()); -} - -RTCErrorOr> -TgPeerConnection::AddTransceiver(cricket::MediaType media_type, - const RtpTransceiverInit& init) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(IsUnifiedPlan()) - << "AddTransceiver is only available with Unified Plan SdpSemantics"; - if (!(media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "media type is not audio or video"); - } - return AddTransceiver(media_type, nullptr, init); -} - -RTCErrorOr> -TgPeerConnection::AddTransceiver( - cricket::MediaType media_type, - rtc::scoped_refptr track, - const RtpTransceiverInit& init, - bool update_negotiation_needed) { - RTC_DCHECK((media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO)); - if (track) { - RTC_DCHECK_EQ(media_type, - (track->kind() == MediaStreamTrackInterface::kAudioKind - ? cricket::MEDIA_TYPE_AUDIO - : cricket::MEDIA_TYPE_VIDEO)); - } - - RTC_HISTOGRAM_COUNTS_LINEAR(kSimulcastNumberOfEncodings, - (int)init.send_encodings.size(), 0, 7, 8); - - size_t num_rids = absl::c_count_if(init.send_encodings, - [](const RtpEncodingParameters& encoding) { - return !encoding.rid.empty(); - }); - if (num_rids > 0 && num_rids != init.send_encodings.size()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "RIDs must be provided for either all or none of the send encodings."); - } - - if (num_rids > 0 && absl::c_any_of(init.send_encodings, - [](const RtpEncodingParameters& encoding) { - return !IsLegalRsidName(encoding.rid); - })) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Invalid RID value provided."); - } - - if (absl::c_any_of(init.send_encodings, - [](const RtpEncodingParameters& encoding) { - return encoding.ssrc.has_value(); - })) { - LOG_AND_RETURN_ERROR( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); - } - - RtpParameters parameters; - parameters.encodings = init.send_encodings; - - // Encodings are dropped from the tail if too many are provided. - if (parameters.encodings.size() > kMaxSimulcastStreams) { - parameters.encodings.erase( - parameters.encodings.begin() + kMaxSimulcastStreams, - parameters.encodings.end()); - } - - // Single RID should be removed. - if (parameters.encodings.size() == 1 && - !parameters.encodings[0].rid.empty()) { - RTC_LOG(LS_INFO) << "Removing RID: " << parameters.encodings[0].rid << "."; - parameters.encodings[0].rid.clear(); - } - - // If RIDs were not provided, they are generated for simulcast scenario. - if (parameters.encodings.size() > 1 && num_rids == 0) { - rtc::UniqueStringGenerator rid_generator; - for (RtpEncodingParameters& encoding : parameters.encodings) { - encoding.rid = rid_generator(); - } - } - - if (TgUnimplementedRtpParameterHasValue(parameters)) { - LOG_AND_RETURN_ERROR( - RTCErrorType::UNSUPPORTED_PARAMETER, - "Attempted to set an unimplemented parameter of RtpParameters."); - } - - auto result = cricket::CheckRtpParametersValues(parameters); - if (!result.ok()) { - LOG_AND_RETURN_ERROR(result.type(), result.message()); - } - - RTC_LOG(LS_INFO) << "Adding " << cricket::MediaTypeToString(media_type) - << " transceiver in response to a call to AddTransceiver."; - // Set the sender ID equal to the track ID if the track is specified unless - // that sender ID is already in use. - std::string sender_id = - (track && !FindSenderById(track->id()) ? track->id() - : rtc::CreateRandomUuid()); - auto sender = CreateSender(media_type, sender_id, track, init.stream_ids, - parameters.encodings); - auto receiver = CreateReceiver(media_type, rtc::CreateRandomUuid()); - auto transceiver = CreateAndAddTransceiver(sender, receiver); - transceiver->internal()->set_direction(init.direction); - - if (update_negotiation_needed) { - UpdateNegotiationNeeded(); - } - - return rtc::scoped_refptr(transceiver); -} - -rtc::scoped_refptr> -TgPeerConnection::CreateSender( - cricket::MediaType media_type, - const std::string& id, - rtc::scoped_refptr track, - const std::vector& stream_ids, - const std::vector& send_encodings) { - RTC_DCHECK_RUN_ON(signaling_thread()); - rtc::scoped_refptr> sender; - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - RTC_DCHECK(!track || - (track->kind() == MediaStreamTrackInterface::kAudioKind)); - sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), - TgAudioRtpSender::Create(worker_thread(), id, this)); - NoteUsageEvent(UsageEvent::AUDIO_ADDED); - } else { - RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); - RTC_DCHECK(!track || - (track->kind() == MediaStreamTrackInterface::kVideoKind)); - sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), TgVideoRtpSender::Create(worker_thread(), id, this)); - NoteUsageEvent(UsageEvent::VIDEO_ADDED); - } - bool set_track_succeeded = sender->SetTrack(track); - RTC_DCHECK(set_track_succeeded); - sender->internal()->set_stream_ids(stream_ids); - sender->internal()->set_init_send_encodings(send_encodings); - return sender; -} - -rtc::scoped_refptr> -TgPeerConnection::CreateReceiver(cricket::MediaType media_type, - const std::string& receiver_id) { - rtc::scoped_refptr> - receiver; - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), new AudioRtpReceiver(worker_thread(), receiver_id, - std::vector({}))); - NoteUsageEvent(UsageEvent::AUDIO_ADDED); - } else { - RTC_DCHECK_EQ(media_type, cricket::MEDIA_TYPE_VIDEO); - receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), new VideoRtpReceiver(worker_thread(), receiver_id, - std::vector({}))); - NoteUsageEvent(UsageEvent::VIDEO_ADDED); - } - return receiver; -} - -rtc::scoped_refptr> -TgPeerConnection::CreateAndAddTransceiver( - rtc::scoped_refptr> sender, - rtc::scoped_refptr> - receiver) { - // Ensure that the new sender does not have an ID that is already in use by - // another sender. - // Allow receiver IDs to conflict since those come from remote SDP (which - // could be invalid, but should not cause a crash). - RTC_DCHECK(!FindSenderById(sender->id())); - auto transceiver = RtpTransceiverProxyWithInternal::Create( - signaling_thread(), - new RtpTransceiver(sender, receiver, channel_manager())); - transceivers_.push_back(transceiver); - transceiver->internal()->SignalNegotiationNeeded.connect( - this, &TgPeerConnection::OnNegotiationNeeded); - return transceiver; -} - -void TgPeerConnection::OnNegotiationNeeded() { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(!IsClosed()); - UpdateNegotiationNeeded(); -} - -rtc::scoped_refptr TgPeerConnection::CreateSender( - const std::string& kind, - const std::string& stream_id) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(!IsUnifiedPlan()) << "CreateSender is not available with Unified " - "Plan SdpSemantics. Please use AddTransceiver " - "instead."; - TRACE_EVENT0("webrtc", "TgPeerConnection::CreateSender"); - if (IsClosed()) { - return nullptr; - } - - // Internally we need to have one stream with Plan B semantics, so we - // generate a random stream ID if not specified. - std::vector stream_ids; - if (stream_id.empty()) { - stream_ids.push_back(rtc::CreateRandomUuid()); - RTC_LOG(LS_INFO) - << "No stream_id specified for sender. Generated stream ID: " - << stream_ids[0]; - } else { - stream_ids.push_back(stream_id); - } - - // TODO(steveanton): Move construction of the RtpSenders to RtpTransceiver. - rtc::scoped_refptr> new_sender; - if (kind == MediaStreamTrackInterface::kAudioKind) { - auto audio_sender = TgAudioRtpSender::Create( - worker_thread(), rtc::CreateRandomUuid(), this); - audio_sender->SetMediaChannel(voice_media_channel()); - new_sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), audio_sender); - GetAudioTransceiver()->internal()->AddSender(new_sender); - } else if (kind == MediaStreamTrackInterface::kVideoKind) { - auto video_sender = - TgVideoRtpSender::Create(worker_thread(), rtc::CreateRandomUuid(), this); - video_sender->SetMediaChannel(video_media_channel()); - new_sender = RtpSenderProxyWithInternal::Create( - signaling_thread(), video_sender); - GetVideoTransceiver()->internal()->AddSender(new_sender); - } else { - RTC_LOG(LS_ERROR) << "CreateSender called with invalid kind: " << kind; - return nullptr; - } - new_sender->internal()->set_stream_ids(stream_ids); - - return new_sender; -} - -std::vector> TgPeerConnection::GetSenders() -const { - RTC_DCHECK_RUN_ON(signaling_thread()); - std::vector> ret; - for (const auto& sender : GetSendersInternal()) { - ret.push_back(sender); - } - return ret; -} - -std::vector>> -TgPeerConnection::GetSendersInternal() const { - std::vector>> - all_senders; - for (const auto& transceiver : transceivers_) { - auto senders = transceiver->internal()->senders(); - all_senders.insert(all_senders.end(), senders.begin(), senders.end()); - } - return all_senders; -} - -std::vector> -TgPeerConnection::GetReceivers() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - std::vector> ret; - for (const auto& receiver : GetReceiversInternal()) { - ret.push_back(receiver); - } - return ret; -} - -std::vector< -rtc::scoped_refptr>> -TgPeerConnection::GetReceiversInternal() const { - std::vector< - rtc::scoped_refptr>> - all_receivers; - for (const auto& transceiver : transceivers_) { - auto receivers = transceiver->internal()->receivers(); - all_receivers.insert(all_receivers.end(), receivers.begin(), - receivers.end()); - } - return all_receivers; -} - -rtc::scoped_refptr TgPeerConnection::CreateDataChannel( -const std::string& label, - const DataChannelInit* config) { - return nullptr; -} - -std::vector> -TgPeerConnection::GetTransceivers() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_CHECK(IsUnifiedPlan()) - << "GetTransceivers is only supported with Unified Plan SdpSemantics."; - std::vector> all_transceivers; - for (const auto& transceiver : transceivers_) { - all_transceivers.push_back(transceiver); - } - return all_transceivers; -} - -PeerConnectionInterface::SignalingState TgPeerConnection::signaling_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return signaling_state_; -} - -PeerConnectionInterface::IceConnectionState -TgPeerConnection::ice_connection_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return ice_connection_state_; -} - -PeerConnectionInterface::IceConnectionState -TgPeerConnection::standardized_ice_connection_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return standardized_ice_connection_state_; -} - -PeerConnectionInterface::PeerConnectionState -TgPeerConnection::peer_connection_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return connection_state_; -} - -PeerConnectionInterface::IceGatheringState -TgPeerConnection::ice_gathering_state() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return ice_gathering_state_; -} - -void TgPeerConnection::RestartIce() { - RTC_DCHECK_RUN_ON(signaling_thread()); - local_ice_credentials_to_replace_->SetIceCredentialsFromLocalDescriptions( - current_local_description_.get(), pending_local_description_.get()); - UpdateNegotiationNeeded(); -} - -void TgPeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = - rtc::scoped_refptr(observer), - options](std::function operations_chain_callback) { - // Abort early if |this_weak_ptr| is no longer valid. - if (!this_weak_ptr) { - observer_refptr->OnFailure( - RTCError(RTCErrorType::INTERNAL_ERROR, - "CreateOffer failed because the session was shut down")); - operations_chain_callback(); - return; - } - // The operation completes asynchronously when the wrapper is invoked. - rtc::scoped_refptr - observer_wrapper(new rtc::RefCountedObject< - CreateSessionDescriptionObserverOperationWrapper>( - std::move(observer_refptr), - std::move(operations_chain_callback))); - this_weak_ptr->DoCreateOffer(options, observer_wrapper); - }); -} - -void TgPeerConnection::DoCreateOffer( - const PeerConnectionInterface::RTCOfferAnswerOptions& options, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::DoCreateOffer"); - - if (!observer) { - RTC_LOG(LS_ERROR) << "CreateOffer - observer is NULL."; - return; - } - - if (IsClosed()) { - std::string error = "CreateOffer called when TgPeerConnection is closed."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); - return; - } - - // If a session error has occurred the TgPeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "CreateOffer: " << error_message; - PostCreateSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - - if (!TgValidateOfferAnswerOptions(options)) { - std::string error = "CreateOffer called with invalid options."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_PARAMETER, std::move(error))); - return; - } - - // Legacy handling for offer_to_receive_audio and offer_to_receive_video. - // Specified in WebRTC section 4.4.3.2 "Legacy configuration extensions". - if (IsUnifiedPlan()) { - RTCError error = HandleLegacyOfferOptions(options); - if (!error.ok()) { - PostCreateSessionDescriptionFailure(observer, std::move(error)); - return; - } - } - - cricket::MediaSessionOptions session_options; - GetOptionsForOffer(options, &session_options); - webrtc_session_desc_factory_->CreateOffer(observer, options, session_options); -} - -RTCError TgPeerConnection::HandleLegacyOfferOptions( - const PeerConnectionInterface::RTCOfferAnswerOptions& options) { - RTC_DCHECK(IsUnifiedPlan()); - - if (options.offer_to_receive_audio == 0) { - RemoveRecvDirectionFromReceivingTransceiversOfType( - cricket::MEDIA_TYPE_AUDIO); - } else if (options.offer_to_receive_audio == 1) { - AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_AUDIO); - } else if (options.offer_to_receive_audio > 1) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, - "offer_to_receive_audio > 1 is not supported."); - } - - if (options.offer_to_receive_video == 0) { - RemoveRecvDirectionFromReceivingTransceiversOfType( - cricket::MEDIA_TYPE_VIDEO); - } else if (options.offer_to_receive_video == 1) { - AddUpToOneReceivingTransceiverOfType(cricket::MEDIA_TYPE_VIDEO); - } else if (options.offer_to_receive_video > 1) { - LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_PARAMETER, - "offer_to_receive_video > 1 is not supported."); - } - - return RTCError::OK(); -} - -void TgPeerConnection::RemoveRecvDirectionFromReceivingTransceiversOfType( - cricket::MediaType media_type) { - for (const auto& transceiver : GetReceivingTransceiversOfType(media_type)) { - RtpTransceiverDirection new_direction = - RtpTransceiverDirectionWithRecvSet(transceiver->direction(), false); - if (new_direction != transceiver->direction()) { - RTC_LOG(LS_INFO) << "Changing " << cricket::MediaTypeToString(media_type) - << " transceiver (MID=" - << transceiver->mid().value_or("") << ") from " - << RtpTransceiverDirectionToString( - transceiver->direction()) - << " to " - << RtpTransceiverDirectionToString(new_direction) - << " since CreateOffer specified offer_to_receive=0"; - transceiver->internal()->set_direction(new_direction); - } - } -} - -void TgPeerConnection::AddUpToOneReceivingTransceiverOfType( - cricket::MediaType media_type) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (GetReceivingTransceiversOfType(media_type).empty()) { - RTC_LOG(LS_INFO) - << "Adding one recvonly " << cricket::MediaTypeToString(media_type) - << " transceiver since CreateOffer specified offer_to_receive=1"; - RtpTransceiverInit init; - init.direction = RtpTransceiverDirection::kRecvOnly; - AddTransceiver(media_type, nullptr, init, - /*update_negotiation_needed=*/false); - } -} - -std::vector>> -TgPeerConnection::GetReceivingTransceiversOfType(cricket::MediaType media_type) { - std::vector< - rtc::scoped_refptr>> - receiving_transceivers; - for (const auto& transceiver : transceivers_) { - if (!transceiver->stopped() && transceiver->media_type() == media_type && - RtpTransceiverDirectionHasRecv(transceiver->direction())) { - receiving_transceivers.push_back(transceiver); - } - } - return receiving_transceivers; -} - -void TgPeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = - rtc::scoped_refptr(observer), - options](std::function operations_chain_callback) { - // Abort early if |this_weak_ptr| is no longer valid. - if (!this_weak_ptr) { - observer_refptr->OnFailure(RTCError( - RTCErrorType::INTERNAL_ERROR, - "CreateAnswer failed because the session was shut down")); - operations_chain_callback(); - return; - } - // The operation completes asynchronously when the wrapper is invoked. - rtc::scoped_refptr - observer_wrapper(new rtc::RefCountedObject< - CreateSessionDescriptionObserverOperationWrapper>( - std::move(observer_refptr), - std::move(operations_chain_callback))); - this_weak_ptr->DoCreateAnswer(options, observer_wrapper); - }); -} - -void TgPeerConnection::DoCreateAnswer( - const PeerConnectionInterface::RTCOfferAnswerOptions& options, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::DoCreateAnswer"); - if (!observer) { - RTC_LOG(LS_ERROR) << "CreateAnswer - observer is NULL."; - return; - } - - // If a session error has occurred the TgPeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "CreateAnswer: " << error_message; - PostCreateSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - - if (!(signaling_state_ == PeerConnectionInterface::kHaveRemoteOffer || - signaling_state_ == PeerConnectionInterface::kHaveLocalPrAnswer)) { - std::string error = - "TgPeerConnection cannot create an answer in a state other than " - "have-remote-offer or have-local-pranswer."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailure( - observer, RTCError(RTCErrorType::INVALID_STATE, std::move(error))); - return; - } - - // The remote description should be set if we're in the right state. - RTC_DCHECK(remote_description()); - - if (IsUnifiedPlan()) { - if (options.offer_to_receive_audio != PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) { - RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_audio is not " - "supported with Unified Plan semantics. Use the " - "RtpTransceiver API instead."; - } - if (options.offer_to_receive_video != PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) { - RTC_LOG(LS_WARNING) << "CreateAnswer: offer_to_receive_video is not " - "supported with Unified Plan semantics. Use the " - "RtpTransceiver API instead."; - } - } - - cricket::MediaSessionOptions session_options; - GetOptionsForAnswer(options, &session_options); - - webrtc_session_desc_factory_->CreateAnswer(observer, session_options); -} - -void TgPeerConnection::SetLocalDescription( - SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc_ptr) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = - rtc::scoped_refptr(observer), - desc = std::unique_ptr(desc_ptr)]( - std::function operations_chain_callback) mutable { - // Abort early if |this_weak_ptr| is no longer valid. - if (!this_weak_ptr) { - // For consistency with DoSetLocalDescription(), we DO NOT inform the - // |observer_refptr| that the operation failed in this case. - // TODO(hbos): If/when we process SLD messages in ~TgPeerConnection, - // the consistent thing would be to inform the observer here. - operations_chain_callback(); - return; - } - this_weak_ptr->DoSetLocalDescription(std::move(desc), - std::move(observer_refptr)); - // DoSetLocalDescription() is currently implemented as a synchronous - // operation but where the |observer|'s callbacks are invoked - // asynchronously in a post to OnMessage(). - // For backwards-compatability reasons, we declare the operation as - // completed here (rather than in OnMessage()). This ensures that - // subsequent offer/answer operations can start immediately (without - // waiting for OnMessage()). - operations_chain_callback(); - }); -} - -void TgPeerConnection::SetLocalDescription( - SetSessionDescriptionObserver* observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // The |create_sdp_observer| handles performing DoSetLocalDescription() with - // the resulting description as well as completing the operation. - rtc::scoped_refptr - create_sdp_observer( - new rtc::RefCountedObject( - weak_ptr_factory_.GetWeakPtr(), - rtc::scoped_refptr(observer))); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - create_sdp_observer](std::function operations_chain_callback) { - // The |create_sdp_observer| is responsible for completing the - // operation. - create_sdp_observer->SetOperationCompleteCallback( - std::move(operations_chain_callback)); - // Abort early if |this_weak_ptr| is no longer valid. This triggers the - // same code path as if DoCreateOffer() or DoCreateAnswer() failed. - if (!this_weak_ptr) { - create_sdp_observer->OnFailure(RTCError( - RTCErrorType::INTERNAL_ERROR, - "SetLocalDescription failed because the session was shut down")); - return; - } - switch (this_weak_ptr->signaling_state()) { - case PeerConnectionInterface::kStable: - case PeerConnectionInterface::kHaveLocalOffer: - case PeerConnectionInterface::kHaveRemotePrAnswer: - // TODO(hbos): If [LastCreatedOffer] exists and still represents the - // current state of the system, use that instead of creating another - // offer. - this_weak_ptr->DoCreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions(), - create_sdp_observer); - break; - case PeerConnectionInterface::kHaveLocalPrAnswer: - case PeerConnectionInterface::kHaveRemoteOffer: - // TODO(hbos): If [LastCreatedAnswer] exists and still represents - // the current state of the system, use that instead of creating - // another answer. - this_weak_ptr->DoCreateAnswer(PeerConnectionInterface::RTCOfferAnswerOptions(), - create_sdp_observer); - break; - case PeerConnectionInterface::kClosed: - create_sdp_observer->OnFailure(RTCError( - RTCErrorType::INVALID_STATE, - "SetLocalDescription called when TgPeerConnection is closed.")); - break; - } - }); -} - -void TgPeerConnection::DoSetLocalDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::DoSetLocalDescription"); - - if (!observer) { - RTC_LOG(LS_ERROR) << "SetLocalDescription - observer is NULL."; - return; - } - - if (!desc) { - PostSetSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, "SessionDescription is NULL.")); - return; - } - - // If a session error has occurred the TgPeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "SetLocalDescription: " << error_message; - PostSetSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - - // For SLD we support only explicit rollback. - if (desc->GetType() == SdpType::kRollback) { - if (IsUnifiedPlan()) { - RTCError error = Rollback(desc->GetType()); - if (error.ok()) { - PostSetSessionDescriptionSuccess(observer); - } else { - PostSetSessionDescriptionFailure(observer, std::move(error)); - } - } else { - PostSetSessionDescriptionFailure( - observer, RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Rollback not supported in Plan B")); - } - return; - } - - RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL); - if (!error.ok()) { - std::string error_message = GetSetDescriptionErrorMessage( - cricket::CS_LOCAL, desc->GetType(), error); - RTC_LOG(LS_ERROR) << error_message; - PostSetSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - - // Grab the description type before moving ownership to ApplyLocalDescription, - // which may destroy it before returning. - const SdpType type = desc->GetType(); - - error = ApplyLocalDescription(std::move(desc)); - // |desc| may be destroyed at this point. - - if (!error.ok()) { - // If ApplyLocalDescription fails, the TgPeerConnection could be in an - // inconsistent state, so act conservatively here and set the session error - // so that future calls to SetLocalDescription/SetRemoteDescription fail. - SetSessionError(SessionError::kContent, error.message()); - std::string error_message = - GetSetDescriptionErrorMessage(cricket::CS_LOCAL, type, error); - RTC_LOG(LS_ERROR) << error_message; - PostSetSessionDescriptionFailure( - observer, - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - RTC_DCHECK(local_description()); - - PostSetSessionDescriptionSuccess(observer); - - // MaybeStartGathering needs to be called after posting - // MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates - // before signaling that SetLocalDescription completed. - transport_controller_->MaybeStartGathering(); - - if (local_description()->GetType() == SdpType::kAnswer) { - // TODO(deadbeef): We already had to hop to the network thread for - // MaybeStartGathering... - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, - port_allocator_.get())); - // Make UMA notes about what was agreed to. - ReportNegotiatedSdpSemantics(*local_description()); - } - - if (IsUnifiedPlan()) { - bool was_negotiation_needed = is_negotiation_needed_; - UpdateNegotiationNeeded(); - if (signaling_state() == PeerConnectionInterface::kStable && was_negotiation_needed && - is_negotiation_needed_) { - Observer()->OnRenegotiationNeeded(); - } - } - - NoteUsageEvent(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED); -} - -RTCError TgPeerConnection::ApplyLocalDescription( - std::unique_ptr desc) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(desc); - - // Take a reference to the old local description since it's used below to - // compare against the new local description. When setting the new local - // description, grab ownership of the replaced session description in case it - // is the same as |old_local_description|, to keep it alive for the duration - // of the method. - const SessionDescriptionInterface* old_local_description = - local_description(); - std::unique_ptr replaced_local_description; - SdpType type = desc->GetType(); - if (type == SdpType::kAnswer) { - replaced_local_description = pending_local_description_ - ? std::move(pending_local_description_) - : std::move(current_local_description_); - current_local_description_ = std::move(desc); - pending_local_description_ = nullptr; - current_remote_description_ = std::move(pending_remote_description_); - } else { - replaced_local_description = std::move(pending_local_description_); - pending_local_description_ = std::move(desc); - } - // The session description to apply now must be accessed by - // |local_description()|. - RTC_DCHECK(local_description()); - - // Report statistics about any use of simulcast. - ReportSimulcastApiVersion(kSimulcastVersionApplyLocalDescription, - *local_description()->description()); - - if (!is_caller_) { - if (remote_description()) { - // Remote description was applied first, so this PC is the callee. - is_caller_ = false; - } else { - // Local description is applied first, so this PC is the caller. - is_caller_ = true; - } - } - - RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type); - if (!error.ok()) { - return error; - } - - error = UpdateTransceiversAndDataChannels( - cricket::CS_LOCAL, *local_description(), old_local_description, - remote_description()); - if (!error.ok()) { - return error; - } - std::vector> remove_list; - std::vector> removed_streams; - for (const auto& transceiver : transceivers_) { - // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots. - // Note that code paths that don't set MID won't be able to use - // information about DTLS transports. - if (transceiver->mid()) { - auto dtls_transport = - LookupDtlsTransportByMidInternal(*transceiver->mid()); - transceiver->internal()->sender_internal()->set_transport( - dtls_transport); - transceiver->internal()->receiver_internal()->set_transport( - dtls_transport); - } - - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, local_description()); - if (!content) { - continue; - } - const MediaContentDescription* media_desc = content->media_description(); - // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run - // the following steps: - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and - // transceiver's [[FiredDirection]] slot is either "sendrecv" or - // "recvonly", process the removal of a remote track for the media - // description, given transceiver, removeList, and muteTracks. - if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) && - (transceiver->internal()->fired_direction() && - RtpTransceiverDirectionHasRecv( - *transceiver->internal()->fired_direction()))) { - ProcessRemovalOfRemoteTrack(transceiver, &remove_list, - &removed_streams); - } - // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and - // [[FiredDirection]] slots to direction. - transceiver->internal()->set_current_direction(media_desc->direction()); - transceiver->internal()->set_fired_direction(media_desc->direction()); - } - } - auto observer = Observer(); - for (const auto& transceiver : remove_list) { - observer->OnRemoveTrack(transceiver->receiver()); - } - for (const auto& stream : removed_streams) { - observer->OnRemoveStream(stream); - } - - error = UpdateSessionState(type, cricket::CS_LOCAL, - local_description()->description()); - if (!error.ok()) { - return error; - } - - if (remote_description()) { - // Now that we have a local description, we can push down remote candidates. - UseCandidatesInSessionDescription(remote_description()); - } - - pending_ice_restarts_.clear(); - if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); - } - - if (IsUnifiedPlan()) { - for (const auto& transceiver : transceivers_) { - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, local_description()); - if (!content) { - continue; - } - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (content->rejected || !channel || channel->local_streams().empty()) { - // 0 is a special value meaning "this sender has no associated send - // stream". Need to call this so the sender won't attempt to configure - // a no longer existing stream and run into DCHECKs in the lower - // layers. - transceiver->internal()->sender_internal()->SetSsrc(0); - } else { - // Get the StreamParams from the channel which could generate SSRCs. - const std::vector& streams = channel->local_streams(); - transceiver->internal()->sender_internal()->set_stream_ids( - streams[0].stream_ids()); - transceiver->internal()->sender_internal()->SetSsrc( - streams[0].first_ssrc()); - } - } - } else { - // Plan B semantics. - - // Update state and SSRC of local MediaStreams and DataChannels based on the - // local session description. - const cricket::ContentInfo* audio_content = - GetFirstAudioContent(local_description()->description()); - if (audio_content) { - if (audio_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_AUDIO); - } else { - const cricket::AudioContentDescription* audio_desc = - audio_content->media_description()->as_audio(); - UpdateLocalSenders(audio_desc->streams(), audio_desc->type()); - } - } - - const cricket::ContentInfo* video_content = - GetFirstVideoContent(local_description()->description()); - if (video_content) { - if (video_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_VIDEO); - } else { - const cricket::VideoContentDescription* video_desc = - video_content->media_description()->as_video(); - UpdateLocalSenders(video_desc->streams(), video_desc->type()); - } - } - } - - if (type == SdpType::kAnswer && - local_ice_credentials_to_replace_->SatisfiesIceRestart( - *current_local_description_)) { - local_ice_credentials_to_replace_->ClearIceCredentials(); - } - - return RTCError::OK(); -} - -void TgPeerConnection::FillInMissingRemoteMids( - cricket::SessionDescription* new_remote_description) { - RTC_DCHECK(new_remote_description); - const cricket::ContentInfos no_infos; - const cricket::ContentInfos& local_contents = - (local_description() ? local_description()->description()->contents() - : no_infos); - const cricket::ContentInfos& remote_contents = - (remote_description() ? remote_description()->description()->contents() - : no_infos); - for (size_t i = 0; i < new_remote_description->contents().size(); ++i) { - cricket::ContentInfo& content = new_remote_description->contents()[i]; - if (!content.name.empty()) { - continue; - } - std::string new_mid; - absl::string_view source_explanation; - - if (i < local_contents.size()) { - new_mid = local_contents[i].name; - source_explanation = "from the matching local media section"; - } else if (i < remote_contents.size()) { - new_mid = remote_contents[i].name; - source_explanation = "from the matching previous remote media section"; - } else { - new_mid = mid_generator_(); - source_explanation = "generated just now"; - } - - RTC_DCHECK(!new_mid.empty()); - content.name = new_mid; - new_remote_description->transport_infos()[i].content_name = new_mid; - RTC_LOG(LS_INFO) << "SetRemoteDescription: Remote media section at i=" << i - << " is missing an a=mid line. Filling in the value '" - << new_mid << "' " << source_explanation << "."; - } -} - -void TgPeerConnection::SetRemoteDescription( - SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc_ptr) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - observer_refptr = - rtc::scoped_refptr(observer), - desc = std::unique_ptr(desc_ptr)]( - std::function operations_chain_callback) mutable { - // Abort early if |this_weak_ptr| is no longer valid. - if (!this_weak_ptr) { - // For consistency with SetRemoteDescriptionObserverAdapter, we DO NOT - // inform the |observer_refptr| that the operation failed in this - // case. - // TODO(hbos): If/when we process SRD messages in ~TgPeerConnection, - // the consistent thing would be to inform the observer here. - operations_chain_callback(); - return; - } - this_weak_ptr->DoSetRemoteDescription( - std::move(desc), - rtc::scoped_refptr( - new SetRemoteDescriptionObserverAdapter( - this_weak_ptr.get(), std::move(observer_refptr)))); - // DoSetRemoteDescription() is currently implemented as a synchronous - // operation but where SetRemoteDescriptionObserverAdapter ensures that - // the |observer|'s callbacks are invoked asynchronously in a post to - // OnMessage(). - // For backwards-compatability reasons, we declare the operation as - // completed here (rather than in OnMessage()). This ensures that - // subsequent offer/answer operations can start immediately (without - // waiting for OnMessage()). - operations_chain_callback(); - }); -} - -void TgPeerConnection::SetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), observer, - desc = std::move(desc)]( - std::function operations_chain_callback) mutable { - // Abort early if |this_weak_ptr| is no longer valid. - if (!this_weak_ptr) { - // For consistency with DoSetRemoteDescription(), we DO inform the - // |observer| that the operation failed in this case. - observer->OnSetRemoteDescriptionComplete(RTCError( - RTCErrorType::INVALID_STATE, - "Failed to set remote offer sdp: failed because the session was " - "shut down")); - operations_chain_callback(); - return; - } - this_weak_ptr->DoSetRemoteDescription(std::move(desc), - std::move(observer)); - // DoSetRemoteDescription() is currently implemented as a synchronous - // operation. The |observer| will already have been informed that it - // completed, and we can mark this operation as complete without any - // loose ends. - operations_chain_callback(); - }); -} - -void TgPeerConnection::DoSetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::DoSetRemoteDescription"); - - if (!observer) { - RTC_LOG(LS_ERROR) << "SetRemoteDescription - observer is NULL."; - return; - } - - if (!desc) { - observer->OnSetRemoteDescriptionComplete(RTCError( - RTCErrorType::INVALID_PARAMETER, "SessionDescription is NULL.")); - return; - } - - // If a session error has occurred the TgPeerConnection is in a possibly - // inconsistent state so fail right away. - if (session_error() != SessionError::kNone) { - std::string error_message = GetSessionErrorMsg(); - RTC_LOG(LS_ERROR) << "SetRemoteDescription: " << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message))); - return; - } - if (IsUnifiedPlan()) { - if (configuration_.enable_implicit_rollback) { - if (desc->GetType() == SdpType::kOffer && - signaling_state() == PeerConnectionInterface::kHaveLocalOffer) { - Rollback(desc->GetType()); - } - } - // Explicit rollback. - if (desc->GetType() == SdpType::kRollback) { - observer->OnSetRemoteDescriptionComplete(Rollback(desc->GetType())); - return; - } - } else if (desc->GetType() == SdpType::kRollback) { - observer->OnSetRemoteDescriptionComplete( - RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Rollback not supported in Plan B")); - return; - } - if (desc->GetType() == SdpType::kOffer) { - // Report to UMA the format of the received offer. - ReportSdpFormatReceived(*desc); - } - - // Handle remote descriptions missing a=mid lines for interop with legacy end - // points. - FillInMissingRemoteMids(desc->description()); - - RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_REMOTE); - if (!error.ok()) { - std::string error_message = GetSetDescriptionErrorMessage( - cricket::CS_REMOTE, desc->GetType(), error); - RTC_LOG(LS_ERROR) << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(error.type(), std::move(error_message))); - return; - } - - // Grab the description type before moving ownership to - // ApplyRemoteDescription, which may destroy it before returning. - const SdpType type = desc->GetType(); - - error = ApplyRemoteDescription(std::move(desc)); - // |desc| may be destroyed at this point. - - if (!error.ok()) { - // If ApplyRemoteDescription fails, the TgPeerConnection could be in an - // inconsistent state, so act conservatively here and set the session error - // so that future calls to SetLocalDescription/SetRemoteDescription fail. - SetSessionError(SessionError::kContent, error.message()); - std::string error_message = - GetSetDescriptionErrorMessage(cricket::CS_REMOTE, type, error); - RTC_LOG(LS_ERROR) << error_message; - observer->OnSetRemoteDescriptionComplete( - RTCError(error.type(), std::move(error_message))); - return; - } - RTC_DCHECK(remote_description()); - - if (type == SdpType::kAnswer) { - // TODO(deadbeef): We already had to hop to the network thread for - // MaybeStartGathering... - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, - port_allocator_.get())); - // Make UMA notes about what was agreed to. - ReportNegotiatedSdpSemantics(*remote_description()); - } - - if (IsUnifiedPlan()) { - bool was_negotiation_needed = is_negotiation_needed_; - UpdateNegotiationNeeded(); - if (signaling_state() == PeerConnectionInterface::kStable && was_negotiation_needed && - is_negotiation_needed_) { - Observer()->OnRenegotiationNeeded(); - } - } - - observer->OnSetRemoteDescriptionComplete(RTCError::OK()); - NoteUsageEvent(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED); -} - -RTCError TgPeerConnection::ApplyRemoteDescription( - std::unique_ptr desc) { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(desc); - - // Take a reference to the old remote description since it's used below to - // compare against the new remote description. When setting the new remote - // description, grab ownership of the replaced session description in case it - // is the same as |old_remote_description|, to keep it alive for the duration - // of the method. - const SessionDescriptionInterface* old_remote_description = - remote_description(); - std::unique_ptr replaced_remote_description; - SdpType type = desc->GetType(); - if (type == SdpType::kAnswer) { - replaced_remote_description = pending_remote_description_ - ? std::move(pending_remote_description_) - : std::move(current_remote_description_); - current_remote_description_ = std::move(desc); - pending_remote_description_ = nullptr; - current_local_description_ = std::move(pending_local_description_); - } else { - replaced_remote_description = std::move(pending_remote_description_); - pending_remote_description_ = std::move(desc); - } - // The session description to apply now must be accessed by - // |remote_description()|. - RTC_DCHECK(remote_description()); - - // Report statistics about any use of simulcast. - ReportSimulcastApiVersion(kSimulcastVersionApplyRemoteDescription, - *remote_description()->description()); - - RTCError error = PushdownTransportDescription(cricket::CS_REMOTE, type); - if (!error.ok()) { - return error; - } - // Transport and Media channels will be created only when offer is set. - if (IsUnifiedPlan()) { - RTCError error = UpdateTransceiversAndDataChannels( - cricket::CS_REMOTE, *remote_description(), local_description(), - old_remote_description); - if (!error.ok()) { - return error; - } - } else { - // Media channels will be created only when offer is set. These may use new - // transports just created by PushdownTransportDescription. - if (type == SdpType::kOffer) { - // TODO(mallinath) - Handle CreateChannel failure, as new local - // description is applied. Restore back to old description. - RTCError error = CreateChannels(*remote_description()->description()); - if (!error.ok()) { - return error; - } - } - // Remove unused channels if MediaContentDescription is rejected. - RemoveUnusedChannels(remote_description()->description()); - } - - // NOTE: Candidates allocation will be initiated only when - // SetLocalDescription is called. - error = UpdateSessionState(type, cricket::CS_REMOTE, - remote_description()->description()); - if (!error.ok()) { - return error; - } - - if (local_description() && - !UseCandidatesInSessionDescription(remote_description())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidCandidates); - } - - if (old_remote_description) { - for (const cricket::ContentInfo& content : - old_remote_description->description()->contents()) { - // Check if this new SessionDescription contains new ICE ufrag and - // password that indicates the remote peer requests an ICE restart. - // TODO(deadbeef): When we start storing both the current and pending - // remote description, this should reset pending_ice_restarts and compare - // against the current description. - if (CheckForRemoteIceRestart(old_remote_description, remote_description(), - content.name)) { - if (type == SdpType::kOffer) { - pending_ice_restarts_.insert(content.name); - } - } else { - // We retain all received candidates only if ICE is not restarted. - // When ICE is restarted, all previous candidates belong to an old - // generation and should not be kept. - // TODO(deadbeef): This goes against the W3C spec which says the remote - // description should only contain candidates from the last set remote - // description plus any candidates added since then. We should remove - // this once we're sure it won't break anything. - TgWebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( - old_remote_description, content.name, mutable_remote_description()); - } - } - } - - if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); - } - - // Set the the ICE connection state to connecting since the connection may - // become writable with peer reflexive candidates before any remote candidate - // is signaled. - // TODO(pthatcher): This is a short-term solution for crbug/446908. A real fix - // is to have a new signal the indicates a change in checking state from the - // transport and expose a new checking() member from transport that can be - // read to determine the current checking state. The existing SignalConnecting - // actually means "gathering candidates", so cannot be be used here. - if (remote_description()->GetType() != SdpType::kOffer && - remote_description()->number_of_mediasections() > 0u && - ice_connection_state() == PeerConnectionInterface::kIceConnectionNew) { - SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); - } - - if (IsUnifiedPlan()) { - std::vector> - now_receiving_transceivers; - std::vector> remove_list; - std::vector> added_streams; - std::vector> removed_streams; - for (const auto& transceiver : transceivers_) { - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, remote_description()); - if (!content) { - continue; - } - const MediaContentDescription* media_desc = content->media_description(); - RtpTransceiverDirection local_direction = - RtpTransceiverDirectionReversed(media_desc->direction()); - // Roughly the same as steps 2.2.8.6 of section 4.4.1.6 "Set the - // RTCSessionDescription: Set the associated remote streams given - // transceiver.[[Receiver]], msids, addList, and removeList". - // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription - if (RtpTransceiverDirectionHasRecv(local_direction)) { - std::vector stream_ids; - if (!media_desc->streams().empty()) { - // The remote description has signaled the stream IDs. - stream_ids = media_desc->streams()[0].stream_ids(); - } - transceiver_stable_states_by_transceivers_[transceiver] - .SetRemoteStreamIdsIfUnset(transceiver->receiver()->stream_ids()); - - RTC_LOG(LS_INFO) << "Processing the MSIDs for MID=" << content->name - << " (" << GetStreamIdsString(stream_ids) << ")."; - SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(), - stream_ids, &added_streams, - &removed_streams); - // From the WebRTC specification, steps 2.2.8.5/6 of section 4.4.1.6 - // "Set the RTCSessionDescription: If direction is sendrecv or recvonly, - // and transceiver's current direction is neither sendrecv nor recvonly, - // process the addition of a remote track for the media description. - if (!transceiver->fired_direction() || - !RtpTransceiverDirectionHasRecv(*transceiver->fired_direction())) { - RTC_LOG(LS_INFO) - << "Processing the addition of a remote track for MID=" - << content->name << "."; - now_receiving_transceivers.push_back(transceiver); - } - } - // 2.2.8.1.9: If direction is "sendonly" or "inactive", and transceiver's - // [[FiredDirection]] slot is either "sendrecv" or "recvonly", process the - // removal of a remote track for the media description, given transceiver, - // removeList, and muteTracks. - if (!RtpTransceiverDirectionHasRecv(local_direction) && - (transceiver->fired_direction() && - RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) { - ProcessRemovalOfRemoteTrack(transceiver, &remove_list, - &removed_streams); - } - // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction. - transceiver->internal()->set_fired_direction(local_direction); - // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run - // the following steps: - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to - // direction. - transceiver->internal()->set_current_direction(local_direction); - // 2.2.8.1.11.[3-6]: Set the transport internal slots. - if (transceiver->mid()) { - auto dtls_transport = - LookupDtlsTransportByMidInternal(*transceiver->mid()); - transceiver->internal()->sender_internal()->set_transport( - dtls_transport); - transceiver->internal()->receiver_internal()->set_transport( - dtls_transport); - } - } - // 2.2.8.1.12: If the media description is rejected, and transceiver is - // not already stopped, stop the RTCRtpTransceiver transceiver. - if (content->rejected && !transceiver->stopped()) { - RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name - << " since the media section was rejected."; - transceiver->Stop(); - } - if (!content->rejected && - RtpTransceiverDirectionHasRecv(local_direction)) { - if (!media_desc->streams().empty() && - media_desc->streams()[0].has_ssrcs()) { - uint32_t ssrc = media_desc->streams()[0].first_ssrc(); - transceiver->internal()->receiver_internal()->SetupMediaChannel(ssrc); - } else { - transceiver->internal() - ->receiver_internal() - ->SetupUnsignaledMediaChannel(); - } - } - } - // Once all processing has finished, fire off callbacks. - auto observer = Observer(); - for (const auto& transceiver : now_receiving_transceivers) { - observer->OnTrack(transceiver); - observer->OnAddTrack(transceiver->receiver(), - transceiver->receiver()->streams()); - } - for (const auto& stream : added_streams) { - observer->OnAddStream(stream); - } - for (const auto& transceiver : remove_list) { - observer->OnRemoveTrack(transceiver->receiver()); - } - for (const auto& stream : removed_streams) { - observer->OnRemoveStream(stream); - } - } - - const cricket::ContentInfo* audio_content = - GetFirstAudioContent(remote_description()->description()); - const cricket::ContentInfo* video_content = - GetFirstVideoContent(remote_description()->description()); - const cricket::AudioContentDescription* audio_desc = - GetFirstAudioContentDescription(remote_description()->description()); - const cricket::VideoContentDescription* video_desc = - GetFirstVideoContentDescription(remote_description()->description()); - - // Check if the descriptions include streams, just in case the peer supports - // MSID, but doesn't indicate so with "a=msid-semantic". - if (remote_description()->description()->msid_supported() || - (audio_desc && !audio_desc->streams().empty()) || - (video_desc && !video_desc->streams().empty())) { - remote_peer_supports_msid_ = true; - } - - // We wait to signal new streams until we finish processing the description, - // since only at that point will new streams have all their tracks. - rtc::scoped_refptr new_streams(StreamCollection::Create()); - - if (!IsUnifiedPlan()) { - // TODO(steveanton): When removing RTP senders/receivers in response to a - // rejected media section, there is some cleanup logic that expects the - // voice/ video channel to still be set. But in this method the voice/video - // channel would have been destroyed by the SetRemoteDescription caller - // above so the cleanup that relies on them fails to run. The RemoveSenders - // calls should be moved to right before the DestroyChannel calls to fix - // this. - - // Find all audio rtp streams and create corresponding remote AudioTracks - // and MediaStreams. - if (audio_content) { - if (audio_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_AUDIO); - } else { - bool default_audio_track_needed = - !remote_peer_supports_msid_ && - RtpTransceiverDirectionHasSend(audio_desc->direction()); - UpdateRemoteSendersList(GetActiveStreams(audio_desc), - default_audio_track_needed, audio_desc->type(), - new_streams); - } - } - - // Find all video rtp streams and create corresponding remote VideoTracks - // and MediaStreams. - if (video_content) { - if (video_content->rejected) { - RemoveSenders(cricket::MEDIA_TYPE_VIDEO); - } else { - bool default_video_track_needed = - !remote_peer_supports_msid_ && - RtpTransceiverDirectionHasSend(video_desc->direction()); - UpdateRemoteSendersList(GetActiveStreams(video_desc), - default_video_track_needed, video_desc->type(), - new_streams); - } - } - - // Iterate new_streams and notify the observer about new MediaStreams. - auto observer = Observer(); - for (size_t i = 0; i < new_streams->count(); ++i) { - MediaStreamInterface* new_stream = new_streams->at(i); - observer->OnAddStream( - rtc::scoped_refptr(new_stream)); - } - - UpdateEndedRemoteMediaStreams(); - } - - if (type == SdpType::kAnswer && - local_ice_credentials_to_replace_->SatisfiesIceRestart( - *current_local_description_)) { - local_ice_credentials_to_replace_->ClearIceCredentials(); - } - - return RTCError::OK(); -} - -void TgPeerConnection::SetAssociatedRemoteStreams( - rtc::scoped_refptr receiver, - const std::vector& stream_ids, - std::vector>* added_streams, - std::vector>* removed_streams) { - std::vector> media_streams; - for (const std::string& stream_id : stream_ids) { - rtc::scoped_refptr stream = - remote_streams_->find(stream_id); - if (!stream) { - stream = MediaStreamProxy::Create(rtc::Thread::Current(), - MediaStream::Create(stream_id)); - remote_streams_->AddStream(stream); - added_streams->push_back(stream); - } - media_streams.push_back(stream); - } - // Special case: "a=msid" missing, use random stream ID. - if (media_streams.empty() && - !(remote_description()->description()->msid_signaling() & - cricket::kMsidSignalingMediaSection)) { - if (!missing_msid_default_stream_) { - missing_msid_default_stream_ = MediaStreamProxy::Create( - rtc::Thread::Current(), MediaStream::Create(rtc::CreateRandomUuid())); - added_streams->push_back(missing_msid_default_stream_); - } - media_streams.push_back(missing_msid_default_stream_); - } - std::vector> previous_streams = - receiver->streams(); - // SetStreams() will add/remove the receiver's track to/from the streams. This - // differs from the spec - the spec uses an "addList" and "removeList" to - // update the stream-track relationships in a later step. We do this earlier, - // changing the order of things, but the end-result is the same. - // TODO(hbos): When we remove remote_streams(), use set_stream_ids() - // instead. https://crbug.com/webrtc/9480 - receiver->SetStreams(media_streams); - RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams); -} - -void TgPeerConnection::ProcessRemovalOfRemoteTrack( - rtc::scoped_refptr> - transceiver, - std::vector>* remove_list, - std::vector>* removed_streams) { - RTC_DCHECK(transceiver->mid()); - RTC_LOG(LS_INFO) << "Processing the removal of a track for MID=" - << *transceiver->mid(); - std::vector> previous_streams = - transceiver->internal()->receiver_internal()->streams(); - // This will remove the remote track from the streams. - transceiver->internal()->receiver_internal()->set_stream_ids({}); - remove_list->push_back(transceiver); - RemoveRemoteStreamsIfEmpty(previous_streams, removed_streams); -} - -void TgPeerConnection::RemoveRemoteStreamsIfEmpty( - const std::vector>& remote_streams, - std::vector>* removed_streams) { - // TODO(https://crbug.com/webrtc/9480): When we use stream IDs instead of - // streams, see if the stream was removed by checking if this was the last - // receiver with that stream ID. - for (const auto& remote_stream : remote_streams) { - if (remote_stream->GetAudioTracks().empty() && - remote_stream->GetVideoTracks().empty()) { - remote_streams_->RemoveStream(remote_stream); - removed_streams->push_back(remote_stream); - } - } -} - -RTCError TgPeerConnection::UpdateTransceiversAndDataChannels( - cricket::ContentSource source, - const SessionDescriptionInterface& new_session, - const SessionDescriptionInterface* old_local_description, - const SessionDescriptionInterface* old_remote_description) { - RTC_DCHECK(IsUnifiedPlan()); - - const cricket::ContentGroup* bundle_group = nullptr; - if (new_session.GetType() == SdpType::kOffer) { - auto bundle_group_or_error = - GetEarlyBundleGroup(*new_session.description()); - if (!bundle_group_or_error.ok()) { - return bundle_group_or_error.MoveError(); - } - bundle_group = bundle_group_or_error.MoveValue(); - } - - const ContentInfos& new_contents = new_session.description()->contents(); - for (size_t i = 0; i < new_contents.size(); ++i) { - const cricket::ContentInfo& new_content = new_contents[i]; - cricket::MediaType media_type = new_content.media_description()->type(); - mid_generator_.AddKnownId(new_content.name); - if (media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO) { - const cricket::ContentInfo* old_local_content = nullptr; - if (old_local_description && - i < old_local_description->description()->contents().size()) { - old_local_content = - &old_local_description->description()->contents()[i]; - } - const cricket::ContentInfo* old_remote_content = nullptr; - if (old_remote_description && - i < old_remote_description->description()->contents().size()) { - old_remote_content = - &old_remote_description->description()->contents()[i]; - } - auto transceiver_or_error = - AssociateTransceiver(source, new_session.GetType(), i, new_content, - old_local_content, old_remote_content); - if (!transceiver_or_error.ok()) { - return transceiver_or_error.MoveError(); - } - auto transceiver = transceiver_or_error.MoveValue(); - RTCError error = - UpdateTransceiverChannel(transceiver, new_content, bundle_group); - if (!error.ok()) { - return error; - } - } else { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Unknown section type."); - } - } - - return RTCError::OK(); -} - -RTCError TgPeerConnection::UpdateTransceiverChannel( - rtc::scoped_refptr> - transceiver, - const cricket::ContentInfo& content, - const cricket::ContentGroup* bundle_group) { - RTC_DCHECK(IsUnifiedPlan()); - RTC_DCHECK(transceiver); - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (content.rejected) { - if (channel) { - transceiver->internal()->SetChannel(nullptr); - DestroyChannelInterface(channel); - } - } else { - if (!channel) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - channel = CreateVoiceChannel(content.name); - } else { - RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, transceiver->media_type()); - channel = CreateVideoChannel(content.name); - } - if (!channel) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INTERNAL_ERROR, - "Failed to create channel for mid=" + content.name); - } - transceiver->internal()->SetChannel(channel); - } - } - return RTCError::OK(); -} - -// This method will extract any send encodings that were sent by the remote -// connection. This is currently only relevant for Simulcast scenario (where -// the number of layers may be communicated by the server). -static std::vector GetSendEncodingsFromRemoteDescription( - const MediaContentDescription& desc) { - if (!desc.HasSimulcast()) { - return {}; - } - std::vector result; - const SimulcastDescription& simulcast = desc.simulcast_description(); - - // This is a remote description, the parameters we are after should appear - // as receive streams. - for (const auto& alternatives : simulcast.receive_layers()) { - RTC_DCHECK(!alternatives.empty()); - // There is currently no way to specify or choose from alternatives. - // We will always use the first alternative, which is the most preferred. - const SimulcastLayer& layer = alternatives[0]; - RtpEncodingParameters parameters; - parameters.rid = layer.rid; - parameters.active = !layer.is_paused; - result.push_back(parameters); - } - - return result; -} - -static RTCError UpdateSimulcastLayerStatusInSender( - const std::vector& layers, - rtc::scoped_refptr sender) { - RTC_DCHECK(sender); - RtpParameters parameters = sender->GetParametersInternal(); - std::vector disabled_layers; - - // The simulcast envelope cannot be changed, only the status of the streams. - // So we will iterate over the send encodings rather than the layers. - for (RtpEncodingParameters& encoding : parameters.encodings) { - auto iter = std::find_if(layers.begin(), layers.end(), - [&encoding](const SimulcastLayer& layer) { - return layer.rid == encoding.rid; - }); - // A layer that cannot be found may have been removed by the remote party. - if (iter == layers.end()) { - disabled_layers.push_back(encoding.rid); - continue; - } - - encoding.active = !iter->is_paused; - } - - RTCError result = sender->SetParametersInternal(parameters); - if (result.ok()) { - result = sender->DisableEncodingLayers(disabled_layers); - } - - return result; -} - -static bool SimulcastIsRejected( - const ContentInfo* local_content, - const MediaContentDescription& answer_media_desc) { - bool simulcast_offered = local_content && - local_content->media_description() && - local_content->media_description()->HasSimulcast(); - bool simulcast_answered = answer_media_desc.HasSimulcast(); - bool rids_supported = RtpExtension::FindHeaderExtensionByUri( - answer_media_desc.rtp_header_extensions(), RtpExtension::kRidUri); - return simulcast_offered && (!simulcast_answered || !rids_supported); -} - -static RTCError DisableSimulcastInSender( - rtc::scoped_refptr sender) { - RTC_DCHECK(sender); - RtpParameters parameters = sender->GetParametersInternal(); - if (parameters.encodings.size() <= 1) { - return RTCError::OK(); - } - - std::vector disabled_layers; - std::transform( - parameters.encodings.begin() + 1, parameters.encodings.end(), - std::back_inserter(disabled_layers), - [](const RtpEncodingParameters& encoding) { return encoding.rid; }); - return sender->DisableEncodingLayers(disabled_layers); -} - -RTCErrorOr>> -TgPeerConnection::AssociateTransceiver(cricket::ContentSource source, - SdpType type, - size_t mline_index, - const ContentInfo& content, - const ContentInfo* old_local_content, - const ContentInfo* old_remote_content) { - RTC_DCHECK(IsUnifiedPlan()); - // If this is an offer then the m= section might be recycled. If the m= - // section is being recycled (defined as: rejected in the current local or - // remote description and not rejected in new description), dissociate the - // currently associated RtpTransceiver by setting its mid property to null, - // and discard the mapping between the transceiver and its m= section index. - if (IsMediaSectionBeingRecycled(type, content, old_local_content, - old_remote_content)) { - // We want to dissociate the transceiver that has the rejected mid. - const std::string& old_mid = - (old_local_content && old_local_content->rejected) - ? old_local_content->name - : old_remote_content->name; - auto old_transceiver = GetAssociatedTransceiver(old_mid); - if (old_transceiver) { - RTC_LOG(LS_INFO) << "Dissociating transceiver for MID=" << old_mid - << " since the media section is being recycled."; - old_transceiver->internal()->set_mid(absl::nullopt); - old_transceiver->internal()->set_mline_index(absl::nullopt); - } - } - const MediaContentDescription* media_desc = content.media_description(); - auto transceiver = GetAssociatedTransceiver(content.name); - if (source == cricket::CS_LOCAL) { - // Find the RtpTransceiver that corresponds to this m= section, using the - // mapping between transceivers and m= section indices established when - // creating the offer. - if (!transceiver) { - transceiver = GetTransceiverByMLineIndex(mline_index); - } - if (!transceiver) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Unknown transceiver"); - } - } else { - RTC_DCHECK_EQ(source, cricket::CS_REMOTE); - // If the m= section is sendrecv or recvonly, and there are RtpTransceivers - // of the same type... - // When simulcast is requested, a transceiver cannot be associated because - // AddTrack cannot be called to initialize it. - if (!transceiver && - RtpTransceiverDirectionHasRecv(media_desc->direction()) && - !media_desc->HasSimulcast()) { - transceiver = FindAvailableTransceiverToReceive(media_desc->type()); - } - // If no RtpTransceiver was found in the previous step, create one with a - // recvonly direction. - if (!transceiver) { - RTC_LOG(LS_INFO) << "Adding " - << cricket::MediaTypeToString(media_desc->type()) - << " transceiver for MID=" << content.name - << " at i=" << mline_index - << " in response to the remote description."; - std::string sender_id = rtc::CreateRandomUuid(); - std::vector send_encodings = - GetSendEncodingsFromRemoteDescription(*media_desc); - auto sender = CreateSender(media_desc->type(), sender_id, nullptr, {}, - send_encodings); - std::string receiver_id; - if (!media_desc->streams().empty()) { - receiver_id = media_desc->streams()[0].id; - } else { - receiver_id = rtc::CreateRandomUuid(); - } - auto receiver = CreateReceiver(media_desc->type(), receiver_id); - transceiver = CreateAndAddTransceiver(sender, receiver); - transceiver->internal()->set_direction( - RtpTransceiverDirection::kRecvOnly); - if (type == SdpType::kOffer) { - transceiver_stable_states_by_transceivers_[transceiver] - .set_newly_created(); - } - } - // Check if the offer indicated simulcast but the answer rejected it. - // This can happen when simulcast is not supported on the remote party. - if (SimulcastIsRejected(old_local_content, *media_desc)) { - RTC_HISTOGRAM_BOOLEAN(kSimulcastDisabled, true); - /*RTCError error = - DisableSimulcastInSender(transceiver->internal()->sender_internal()); - if (!error.ok()) { - RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast."; - return std::move(error); - }*/ - } - } - RTC_DCHECK(transceiver); - if (transceiver->media_type() != media_desc->type()) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_PARAMETER, - "Transceiver type does not match media description type."); - } - if (media_desc->HasSimulcast()) { - std::vector layers = - source == cricket::CS_LOCAL - ? media_desc->simulcast_description().send_layers().GetAllLayers() - : media_desc->simulcast_description() - .receive_layers() - .GetAllLayers(); - RTCError error = UpdateSimulcastLayerStatusInSender( - layers, transceiver->internal()->sender_internal()); - if (!error.ok()) { - RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers."; - return std::move(error); - } - } - if (type == SdpType::kOffer) { - bool state_changes = transceiver->internal()->mid() != content.name || - transceiver->internal()->mline_index() != mline_index; - if (state_changes) { - transceiver_stable_states_by_transceivers_[transceiver] - .SetMSectionIfUnset(transceiver->internal()->mid(), - transceiver->internal()->mline_index()); - } - } - // Associate the found or created RtpTransceiver with the m= section by - // setting the value of the RtpTransceiver's mid property to the MID of the m= - // section, and establish a mapping between the transceiver and the index of - // the m= section. - transceiver->internal()->set_mid(content.name); - transceiver->internal()->set_mline_index(mline_index); - return std::move(transceiver); -} - -rtc::scoped_refptr> -TgPeerConnection::GetAssociatedTransceiver(const std::string& mid) const { - RTC_DCHECK(IsUnifiedPlan()); - for (auto transceiver : transceivers_) { - if (transceiver->mid() == mid) { - return transceiver; - } - } - return nullptr; -} - -rtc::scoped_refptr> -TgPeerConnection::GetTransceiverByMLineIndex(size_t mline_index) const { - RTC_DCHECK(IsUnifiedPlan()); - for (auto transceiver : transceivers_) { - if (transceiver->internal()->mline_index() == mline_index) { - return transceiver; - } - } - return nullptr; -} - -rtc::scoped_refptr> -TgPeerConnection::FindAvailableTransceiverToReceive( - cricket::MediaType media_type) const { - RTC_DCHECK(IsUnifiedPlan()); - // From JSEP section 5.10 (Applying a Remote Description): - // If the m= section is sendrecv or recvonly, and there are RtpTransceivers of - // the same type that were added to the TgPeerConnection by addTrack and are not - // associated with any m= section and are not stopped, find the first such - // RtpTransceiver. - for (auto transceiver : transceivers_) { - if (transceiver->media_type() == media_type && - transceiver->internal()->created_by_addtrack() && !transceiver->mid() && - !transceiver->stopped()) { - return transceiver; - } - } - return nullptr; -} - -const cricket::ContentInfo* TgPeerConnection::FindMediaSectionForTransceiver( - rtc::scoped_refptr> - transceiver, - const SessionDescriptionInterface* sdesc) const { - RTC_DCHECK(transceiver); - RTC_DCHECK(sdesc); - if (IsUnifiedPlan()) { - if (!transceiver->internal()->mid()) { - // This transceiver is not associated with a media section yet. - return nullptr; - } - return sdesc->description()->GetContentByName( - *transceiver->internal()->mid()); - } else { - // Plan B only allows at most one audio and one video section, so use the - // first media section of that type. - return cricket::GetFirstMediaContent(sdesc->description()->contents(), - transceiver->media_type()); - } -} - -PeerConnectionInterface::RTCConfiguration TgPeerConnection::GetConfiguration() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return configuration_; -} - -RTCError TgPeerConnection::SetConfiguration( - const PeerConnectionInterface::RTCConfiguration& configuration) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::SetConfiguration"); - if (IsClosed()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, - "SetConfiguration: TgPeerConnection is closed."); - } - - // According to JSEP, after setLocalDescription, changing the candidate pool - // size is not allowed, and changing the set of ICE servers will not result - // in new candidates being gathered. - if (local_description() && configuration.ice_candidate_pool_size != - configuration_.ice_candidate_pool_size) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change candidate pool size after calling " - "SetLocalDescription."); - } - - if (local_description() && - configuration.crypto_options != configuration_.crypto_options) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change crypto_options after calling " - "SetLocalDescription."); - } - - if (local_description() && configuration.use_datagram_transport != - configuration_.use_datagram_transport) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport " - "after calling SetLocalDescription."); - } - - if (remote_description() && configuration.use_datagram_transport != - configuration_.use_datagram_transport) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport " - "after calling SetRemoteDescription."); - } - - if (local_description() && - configuration.use_datagram_transport_for_data_channels != - configuration_.use_datagram_transport_for_data_channels) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport_for_data_channels " - "after calling SetLocalDescription."); - } - - if (remote_description() && - configuration.use_datagram_transport_for_data_channels != - configuration_.use_datagram_transport_for_data_channels) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport_for_data_channels " - "after calling SetRemoteDescription."); - } - - if (local_description() && - configuration.use_datagram_transport_for_data_channels_receive_only != - configuration_ - .use_datagram_transport_for_data_channels_receive_only) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport_for_data_channels_receive_only " - "after calling SetLocalDescription."); - } - - if (remote_description() && - configuration.use_datagram_transport_for_data_channels_receive_only != - configuration_ - .use_datagram_transport_for_data_channels_receive_only) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_MODIFICATION, - "Can't change use_datagram_transport_for_data_channels_receive_only " - "after calling SetRemoteDescription."); - } - - if ((configuration.use_datagram_transport && - *configuration.use_datagram_transport) || - (configuration.use_datagram_transport_for_data_channels && - *configuration.use_datagram_transport_for_data_channels)) { - RTC_CHECK(configuration.bundle_policy == PeerConnectionInterface::kBundlePolicyMaxBundle) - << "Media transport requires MaxBundle policy."; - } - - // The simplest (and most future-compatible) way to tell if the config was - // modified in an invalid way is to copy each property we do support - // modifying, then use operator==. There are far more properties we don't - // support modifying than those we do, and more could be added. - PeerConnectionInterface::RTCConfiguration modified_config = configuration_; - modified_config.servers = configuration.servers; - modified_config.type = configuration.type; - modified_config.ice_candidate_pool_size = - configuration.ice_candidate_pool_size; - modified_config.prune_turn_ports = configuration.prune_turn_ports; - modified_config.turn_port_prune_policy = configuration.turn_port_prune_policy; - modified_config.surface_ice_candidates_on_ice_transport_type_changed = - configuration.surface_ice_candidates_on_ice_transport_type_changed; - modified_config.ice_check_min_interval = configuration.ice_check_min_interval; - modified_config.ice_check_interval_strong_connectivity = - configuration.ice_check_interval_strong_connectivity; - modified_config.ice_check_interval_weak_connectivity = - configuration.ice_check_interval_weak_connectivity; - modified_config.ice_unwritable_timeout = configuration.ice_unwritable_timeout; - modified_config.ice_unwritable_min_checks = - configuration.ice_unwritable_min_checks; - modified_config.ice_inactive_timeout = configuration.ice_inactive_timeout; - modified_config.stun_candidate_keepalive_interval = - configuration.stun_candidate_keepalive_interval; - modified_config.turn_customizer = configuration.turn_customizer; - modified_config.network_preference = configuration.network_preference; - modified_config.active_reset_srtp_params = - configuration.active_reset_srtp_params; - modified_config.use_datagram_transport = configuration.use_datagram_transport; - modified_config.use_datagram_transport_for_data_channels = - configuration.use_datagram_transport_for_data_channels; - modified_config.use_datagram_transport_for_data_channels_receive_only = - configuration.use_datagram_transport_for_data_channels_receive_only; - modified_config.turn_logging_id = configuration.turn_logging_id; - modified_config.allow_codec_switching = configuration.allow_codec_switching; - if (configuration != modified_config) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Modifying the configuration in an unsupported way."); - } - - // Validate the modified configuration. - RTCError validate_error = ValidateConfiguration(modified_config); - if (!validate_error.ok()) { - return validate_error; - } - - // Note that this isn't possible through chromium, since it's an unsigned - // short in WebIDL. - if (configuration.ice_candidate_pool_size < 0 || - configuration.ice_candidate_pool_size > static_cast(UINT16_MAX)) { - return RTCError(RTCErrorType::INVALID_RANGE); - } - - // Parse ICE servers before hopping to network thread. - cricket::ServerAddresses stun_servers; - std::vector turn_servers; - RTCErrorType parse_error = - ParseIceServers(configuration.servers, &stun_servers, &turn_servers); - if (parse_error != RTCErrorType::NONE) { - return RTCError(parse_error); - } - // Add the turn logging id to all turn servers - for (cricket::RelayServerConfig& turn_server : turn_servers) { - turn_server.turn_logging_id = configuration.turn_logging_id; - } - - // Note if STUN or TURN servers were supplied. - if (!stun_servers.empty()) { - NoteUsageEvent(UsageEvent::STUN_SERVER_ADDED); - } - if (!turn_servers.empty()) { - NoteUsageEvent(UsageEvent::TURN_SERVER_ADDED); - } - - // In theory this shouldn't fail. - if (!network_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnection::ReconfigurePortAllocator_n, this, - stun_servers, turn_servers, modified_config.type, - modified_config.ice_candidate_pool_size, - modified_config.GetTurnPortPrunePolicy(), - modified_config.turn_customizer, - modified_config.stun_candidate_keepalive_interval, - static_cast(local_description())))) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to apply configuration to PortAllocator."); - } - - // As described in JSEP, calling setConfiguration with new ICE servers or - // candidate policy must set a "needs-ice-restart" bit so that the next offer - // triggers an ICE restart which will pick up the changes. - if (modified_config.servers != configuration_.servers || - modified_config.type != configuration_.type || - modified_config.GetTurnPortPrunePolicy() != - configuration_.GetTurnPortPrunePolicy()) { - transport_controller_->SetNeedsIceRestartFlag(); - } - - transport_controller_->SetIceConfig(ParseIceConfig(modified_config)); - - use_datagram_transport_ = datagram_transport_config_.enabled && - modified_config.use_datagram_transport.value_or( - datagram_transport_config_.default_value); - use_datagram_transport_for_data_channels_ = - datagram_transport_data_channel_config_.enabled && - modified_config.use_datagram_transport_for_data_channels.value_or( - datagram_transport_data_channel_config_.default_value); - use_datagram_transport_for_data_channels_receive_only_ = - modified_config.use_datagram_transport_for_data_channels_receive_only - .value_or(datagram_transport_data_channel_config_.receive_only); - transport_controller_->SetMediaTransportSettings( - use_datagram_transport_, use_datagram_transport_for_data_channels_, - use_datagram_transport_for_data_channels_receive_only_); - - if (configuration_.active_reset_srtp_params != - modified_config.active_reset_srtp_params) { - transport_controller_->SetActiveResetSrtpParams( - modified_config.active_reset_srtp_params); - } - - if (modified_config.allow_codec_switching.has_value()) { - cricket::VideoMediaChannel* video_channel = video_media_channel(); - if (video_channel) { - video_channel->SetVideoCodecSwitchingEnabled( - *modified_config.allow_codec_switching); - } - } - - configuration_ = modified_config; - return RTCError::OK(); -} - -bool TgPeerConnection::AddIceCandidate( - const IceCandidateInterface* ice_candidate) { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::AddIceCandidate"); - if (IsClosed()) { - RTC_LOG(LS_ERROR) << "AddIceCandidate: TgPeerConnection is closed."; - return false; - } - - if (!remote_description()) { - RTC_LOG(LS_ERROR) << "AddIceCandidate: ICE candidates can't be added " - "without any remote session description."; - return false; - } - - if (!ice_candidate) { - RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate is null."; - return false; - } - - bool valid = false; - bool ready = ReadyToUseRemoteCandidate(ice_candidate, nullptr, &valid); - if (!valid) { - return false; - } - - // Add this candidate to the remote session description. - if (!mutable_remote_description()->AddCandidate(ice_candidate)) { - RTC_LOG(LS_ERROR) << "AddIceCandidate: Candidate cannot be used."; - return false; - } - - if (ready) { - bool result = UseCandidate(ice_candidate); - if (result) { - NoteUsageEvent(UsageEvent::ADD_ICE_CANDIDATE_SUCCEEDED); - } else { - } - return result; - } else { - RTC_LOG(LS_INFO) << "AddIceCandidate: Not ready to use candidate."; - return true; - } -} - -void TgPeerConnection::AddIceCandidate( - std::unique_ptr candidate, - std::function callback) { - RTC_DCHECK_RUN_ON(signaling_thread()); - // Chain this operation. If asynchronous operations are pending on the chain, - // this operation will be queued to be invoked, otherwise the contents of the - // lambda will execute immediately. - operations_chain_->ChainOperation( - [this_weak_ptr = weak_ptr_factory_.GetWeakPtr(), - candidate = std::move(candidate), callback = std::move(callback)]( - std::function operations_chain_callback) { - if (!this_weak_ptr) { - operations_chain_callback(); - callback(RTCError( - RTCErrorType::INVALID_STATE, - "AddIceCandidate failed because the session was shut down")); - return; - } - if (!this_weak_ptr->AddIceCandidate(candidate.get())) { - operations_chain_callback(); - // Fail with an error type and message consistent with Chromium. - // TODO(hbos): Fail with error types according to spec. - callback(RTCError(RTCErrorType::UNSUPPORTED_OPERATION, - "Error processing ICE candidate")); - return; - } - operations_chain_callback(); - callback(RTCError::OK()); - }); -} - -bool TgPeerConnection::RemoveIceCandidates( - const std::vector& candidates) { - TRACE_EVENT0("webrtc", "TgPeerConnection::RemoveIceCandidates"); - RTC_DCHECK_RUN_ON(signaling_thread()); - if (IsClosed()) { - RTC_LOG(LS_ERROR) << "RemoveIceCandidates: TgPeerConnection is closed."; - return false; - } - - if (!remote_description()) { - RTC_LOG(LS_ERROR) << "RemoveIceCandidates: ICE candidates can't be removed " - "without any remote session description."; - return false; - } - - if (candidates.empty()) { - RTC_LOG(LS_ERROR) << "RemoveIceCandidates: candidates are empty."; - return false; - } - - size_t number_removed = - mutable_remote_description()->RemoveCandidates(candidates); - if (number_removed != candidates.size()) { - RTC_LOG(LS_ERROR) - << "RemoveIceCandidates: Failed to remove candidates. Requested " - << candidates.size() << " but only " << number_removed - << " are removed."; - } - - // Remove the candidates from the transport controller. - RTCError error = transport_controller_->RemoveRemoteCandidates(candidates); - if (!error.ok()) { - RTC_LOG(LS_ERROR) - << "RemoveIceCandidates: Error when removing remote candidates: " - << error.message(); - } - return true; -} - -RTCError TgPeerConnection::SetBitrate(const BitrateSettings& bitrate) { - if (!worker_thread()->IsCurrent()) { - return worker_thread()->Invoke( - RTC_FROM_HERE, [&]() { return SetBitrate(bitrate); }); - } - RTC_DCHECK_RUN_ON(worker_thread()); - - const bool has_min = bitrate.min_bitrate_bps.has_value(); - const bool has_start = bitrate.start_bitrate_bps.has_value(); - const bool has_max = bitrate.max_bitrate_bps.has_value(); - if (has_min && *bitrate.min_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "min_bitrate_bps <= 0"); - } - if (has_start) { - if (has_min && *bitrate.start_bitrate_bps < *bitrate.min_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "start_bitrate_bps < min_bitrate_bps"); - } else if (*bitrate.start_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "curent_bitrate_bps < 0"); - } - } - if (has_max) { - if (has_start && *bitrate.max_bitrate_bps < *bitrate.start_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < start_bitrate_bps"); - } else if (has_min && *bitrate.max_bitrate_bps < *bitrate.min_bitrate_bps) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < min_bitrate_bps"); - } else if (*bitrate.max_bitrate_bps < 0) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max_bitrate_bps < 0"); - } - } - - RTC_DCHECK(call_.get()); - call_->SetClientBitratePreferences(bitrate); - - return RTCError::OK(); -} - -void TgPeerConnection::SetAudioPlayout(bool playout) { - if (!worker_thread()->IsCurrent()) { - worker_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnection::SetAudioPlayout, this, playout)); - return; - } - auto audio_state = - factory_->channel_manager()->media_engine()->voice().GetAudioState(); - audio_state->SetPlayout(playout); -} - -void TgPeerConnection::SetAudioRecording(bool recording) { - if (!worker_thread()->IsCurrent()) { - worker_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnection::SetAudioRecording, this, recording)); - return; - } - auto audio_state = - factory_->channel_manager()->media_engine()->voice().GetAudioState(); - audio_state->SetRecording(recording); -} - -std::unique_ptr -TgPeerConnection::GetRemoteAudioSSLCertificate() { - std::unique_ptr chain = GetRemoteAudioSSLCertChain(); - if (!chain || !chain->GetSize()) { - return nullptr; - } - return chain->Get(0).Clone(); -} - -std::unique_ptr -TgPeerConnection::GetRemoteAudioSSLCertChain() { - RTC_DCHECK_RUN_ON(signaling_thread()); - auto audio_transceiver = GetFirstAudioTransceiver(); - if (!audio_transceiver || !audio_transceiver->internal()->channel()) { - return nullptr; - } - return transport_controller_->GetRemoteSSLCertChain( - audio_transceiver->internal()->channel()->transport_name()); -} - -rtc::scoped_refptr> -TgPeerConnection::GetFirstAudioTransceiver() const { - for (auto transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - return transceiver; - } - } - return nullptr; -} - -bool TgPeerConnection::StartRtcEventLog(std::unique_ptr output, - int64_t output_period_ms) { - return worker_thread()->Invoke( - RTC_FROM_HERE, - [this, output = std::move(output), output_period_ms]() mutable { - return StartRtcEventLog_w(std::move(output), output_period_ms); - }); -} - -bool TgPeerConnection::StartRtcEventLog( - std::unique_ptr output) { - int64_t output_period_ms = webrtc::RtcEventLog::kImmediateOutput; - if (field_trial::IsEnabled("WebRTC-RtcEventLogNewFormat")) { - output_period_ms = 5000; - } - return StartRtcEventLog(std::move(output), output_period_ms); -} - -void TgPeerConnection::StopRtcEventLog() { - worker_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&TgPeerConnection::StopRtcEventLog_w, this)); -} - -rtc::scoped_refptr -TgPeerConnection::LookupDtlsTransportByMid(const std::string& mid) { - RTC_DCHECK_RUN_ON(signaling_thread()); - return transport_controller_->LookupDtlsTransportByMid(mid); -} - -rtc::scoped_refptr -TgPeerConnection::LookupDtlsTransportByMidInternal(const std::string& mid) { - RTC_DCHECK_RUN_ON(signaling_thread()); - return transport_controller_->LookupDtlsTransportByMid(mid); -} - -rtc::scoped_refptr TgPeerConnection::GetSctpTransport() -const { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!sctp_mid_) { - return nullptr; - } - return transport_controller_->GetSctpTransport(*sctp_mid_); -} - -const SessionDescriptionInterface* TgPeerConnection::local_description() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_local_description_ ? pending_local_description_.get() - : current_local_description_.get(); -} - -const SessionDescriptionInterface* TgPeerConnection::remote_description() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_remote_description_ ? pending_remote_description_.get() - : current_remote_description_.get(); -} - -const SessionDescriptionInterface* TgPeerConnection::current_local_description() -const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return current_local_description_.get(); -} - -const SessionDescriptionInterface* TgPeerConnection::current_remote_description() -const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return current_remote_description_.get(); -} - -const SessionDescriptionInterface* TgPeerConnection::pending_local_description() -const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_local_description_.get(); -} - -const SessionDescriptionInterface* TgPeerConnection::pending_remote_description() -const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_remote_description_.get(); -} - -void TgPeerConnection::Close() { - RTC_DCHECK_RUN_ON(signaling_thread()); - TRACE_EVENT0("webrtc", "TgPeerConnection::Close"); - - ChangeSignalingState(PeerConnectionInterface::kClosed); - NoteUsageEvent(UsageEvent::CLOSE_CALLED); - - for (const auto& transceiver : transceivers_) { - transceiver->Stop(); - } - - // Don't destroy BaseChannels until after stats has been cleaned up so that - // the last stats request can still read from the channels. - DestroyAllChannels(); - - // The event log is used in the transport controller, which must be outlived - // by the former. CreateOffer by the peer connection is implemented - // asynchronously and if the peer connection is closed without resetting the - // WebRTC session description factory, the session description factory would - // call the transport controller. - webrtc_session_desc_factory_.reset(); - transport_controller_.reset(); - - network_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&cricket::PortAllocator::DiscardCandidatePool, - port_allocator_.get())); - - worker_thread()->Invoke(RTC_FROM_HERE, [this] { - RTC_DCHECK_RUN_ON(worker_thread()); - call_.reset(); - // The event log must outlive call (and any other object that uses it). - event_log_.reset(); - }); - ReportUsagePattern(); - // The .h file says that observer can be discarded after close() returns. - // Make sure this is true. - observer_ = nullptr; -} - -void TgPeerConnection::OnMessage(rtc::Message* msg) { - RTC_DCHECK_RUN_ON(signaling_thread()); - switch (msg->message_id) { - case MSG_SET_SESSIONDESCRIPTION_SUCCESS: { - SetSessionDescriptionMsg* param = - static_cast(msg->pdata); - param->observer->OnSuccess(); - delete param; - break; - } - case MSG_SET_SESSIONDESCRIPTION_FAILED: { - SetSessionDescriptionMsg* param = - static_cast(msg->pdata); - param->observer->OnFailure(std::move(param->error)); - delete param; - break; - } - case MSG_CREATE_SESSIONDESCRIPTION_FAILED: { - CreateSessionDescriptionMsg* param = - static_cast(msg->pdata); - param->observer->OnFailure(std::move(param->error)); - delete param; - break; - } - case MSG_REPORT_USAGE_PATTERN: { - ReportUsagePattern(); - break; - } - default: - RTC_NOTREACHED() << "Not implemented"; - break; - } -} - -cricket::VoiceMediaChannel* TgPeerConnection::voice_media_channel() const { - RTC_DCHECK(!IsUnifiedPlan()); - auto* voice_channel = static_cast( - GetAudioTransceiver()->internal()->channel()); - if (voice_channel) { - return voice_channel->media_channel(); - } else { - return nullptr; - } -} - -cricket::VideoMediaChannel* TgPeerConnection::video_media_channel() const { - RTC_DCHECK(!IsUnifiedPlan()); - auto* video_channel = static_cast( - GetVideoTransceiver()->internal()->channel()); - if (video_channel) { - return video_channel->media_channel(); - } else { - return nullptr; - } -} - -void TgPeerConnection::CreateAudioReceiver( - MediaStreamInterface* stream, - const RtpSenderInfo& remote_sender_info) { - std::vector> streams; - streams.push_back(rtc::scoped_refptr(stream)); - // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use - // the constructor taking stream IDs instead. - auto* audio_receiver = new AudioRtpReceiver( - worker_thread(), remote_sender_info.sender_id, streams); - audio_receiver->SetMediaChannel(voice_media_channel()); - if (remote_sender_info.sender_id == kDefaultAudioSenderId) { - audio_receiver->SetupUnsignaledMediaChannel(); - } else { - audio_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); - } - auto receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), audio_receiver); - GetAudioTransceiver()->internal()->AddReceiver(receiver); - Observer()->OnAddTrack(receiver, streams); - NoteUsageEvent(UsageEvent::AUDIO_ADDED); -} - -void TgPeerConnection::CreateVideoReceiver( - MediaStreamInterface* stream, - const RtpSenderInfo& remote_sender_info) { - std::vector> streams; - streams.push_back(rtc::scoped_refptr(stream)); - // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use - // the constructor taking stream IDs instead. - auto* video_receiver = new VideoRtpReceiver( - worker_thread(), remote_sender_info.sender_id, streams); - video_receiver->SetMediaChannel(video_media_channel()); - if (remote_sender_info.sender_id == kDefaultVideoSenderId) { - video_receiver->SetupUnsignaledMediaChannel(); - } else { - video_receiver->SetupMediaChannel(remote_sender_info.first_ssrc); - } - auto receiver = RtpReceiverProxyWithInternal::Create( - signaling_thread(), video_receiver); - GetVideoTransceiver()->internal()->AddReceiver(receiver); - Observer()->OnAddTrack(receiver, streams); - NoteUsageEvent(UsageEvent::VIDEO_ADDED); -} - -// TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote -// description. -rtc::scoped_refptr TgPeerConnection::RemoveAndStopReceiver( - const RtpSenderInfo& remote_sender_info) { - auto receiver = FindReceiverById(remote_sender_info.sender_id); - if (!receiver) { - RTC_LOG(LS_WARNING) << "RtpReceiver for track with id " - << remote_sender_info.sender_id << " doesn't exist."; - return nullptr; - } - if (receiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - GetAudioTransceiver()->internal()->RemoveReceiver(receiver); - } else { - GetVideoTransceiver()->internal()->RemoveReceiver(receiver); - } - return receiver; -} - -void TgPeerConnection::AddAudioTrack(AudioTrackInterface* track, - MediaStreamInterface* stream) { - RTC_DCHECK(!IsClosed()); - RTC_DCHECK(track); - RTC_DCHECK(stream); - auto sender = FindSenderForTrack(track); - if (sender) { - // We already have a sender for this track, so just change the stream_id - // so that it's correct in the next call to CreateOffer. - sender->internal()->set_stream_ids({stream->id()}); - return; - } - - // Normal case; we've never seen this track before. - auto new_sender = CreateSender(cricket::MEDIA_TYPE_AUDIO, track->id(), track, - {stream->id()}, {}); - new_sender->internal()->SetMediaChannel(voice_media_channel()); - GetAudioTransceiver()->internal()->AddSender(new_sender); - // If the sender has already been configured in SDP, we call SetSsrc, - // which will connect the sender to the underlying transport. This can - // occur if a local session description that contains the ID of the sender - // is set before AddStream is called. It can also occur if the local - // session description is not changed and RemoveStream is called, and - // later AddStream is called again with the same stream. - const RtpSenderInfo* sender_info = - FindSenderInfo(local_audio_sender_infos_, stream->id(), track->id()); - if (sender_info) { - new_sender->internal()->SetSsrc(sender_info->first_ssrc); - } -} - -// TODO(deadbeef): Don't destroy RtpSenders here; they should be kept around -// indefinitely, when we have unified plan SDP. -void TgPeerConnection::RemoveAudioTrack(AudioTrackInterface* track, - MediaStreamInterface* stream) { - RTC_DCHECK(!IsClosed()); - auto sender = FindSenderForTrack(track); - if (!sender) { - RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() - << " doesn't exist."; - return; - } - GetAudioTransceiver()->internal()->RemoveSender(sender); -} - -void TgPeerConnection::AddVideoTrack(VideoTrackInterface* track, - MediaStreamInterface* stream) { - RTC_DCHECK(!IsClosed()); - RTC_DCHECK(track); - RTC_DCHECK(stream); - auto sender = FindSenderForTrack(track); - if (sender) { - // We already have a sender for this track, so just change the stream_id - // so that it's correct in the next call to CreateOffer. - sender->internal()->set_stream_ids({stream->id()}); - return; - } - - // Normal case; we've never seen this track before. - auto new_sender = CreateSender(cricket::MEDIA_TYPE_VIDEO, track->id(), track, - {stream->id()}, {}); - new_sender->internal()->SetMediaChannel(video_media_channel()); - GetVideoTransceiver()->internal()->AddSender(new_sender); - const RtpSenderInfo* sender_info = - FindSenderInfo(local_video_sender_infos_, stream->id(), track->id()); - if (sender_info) { - new_sender->internal()->SetSsrc(sender_info->first_ssrc); - } -} - -void TgPeerConnection::RemoveVideoTrack(VideoTrackInterface* track, - MediaStreamInterface* stream) { - RTC_DCHECK(!IsClosed()); - auto sender = FindSenderForTrack(track); - if (!sender) { - RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id() - << " doesn't exist."; - return; - } - GetVideoTransceiver()->internal()->RemoveSender(sender); -} - -void TgPeerConnection::SetIceConnectionState(PeerConnectionInterface::IceConnectionState new_state) { - if (ice_connection_state_ == new_state) { - return; - } - - // After transitioning to "closed", ignore any additional states from - // TransportController (such as "disconnected"). - if (IsClosed()) { - return; - } - - RTC_LOG(LS_INFO) << "Changing IceConnectionState " << ice_connection_state_ - << " => " << new_state; - RTC_DCHECK(ice_connection_state_ != - PeerConnectionInterface::kIceConnectionClosed); - - ice_connection_state_ = new_state; - Observer()->OnIceConnectionChange(ice_connection_state_); -} - -void TgPeerConnection::SetStandardizedIceConnectionState( - PeerConnectionInterface::IceConnectionState new_state) { - if (standardized_ice_connection_state_ == new_state) { - return; - } - - if (IsClosed()) { - return; - } - - RTC_LOG(LS_INFO) << "Changing standardized IceConnectionState " - << standardized_ice_connection_state_ << " => " << new_state; - - standardized_ice_connection_state_ = new_state; - Observer()->OnStandardizedIceConnectionChange(new_state); -} - -void TgPeerConnection::SetConnectionState( - PeerConnectionInterface::PeerConnectionState new_state) { - if (connection_state_ == new_state) - return; - if (IsClosed()) - return; - connection_state_ = new_state; - Observer()->OnConnectionChange(new_state); -} - -void TgPeerConnection::OnIceGatheringChange( - PeerConnectionInterface::IceGatheringState new_state) { - if (IsClosed()) { - return; - } - ice_gathering_state_ = new_state; - Observer()->OnIceGatheringChange(ice_gathering_state_); -} - -void TgPeerConnection::OnIceCandidate( - std::unique_ptr candidate) { - if (IsClosed()) { - return; - } - ReportIceCandidateCollected(candidate->candidate()); - Observer()->OnIceCandidate(candidate.get()); -} - -void TgPeerConnection::OnIceCandidateError(const std::string& address, - int port, - const std::string& url, - int error_code, - const std::string& error_text) { - if (IsClosed()) { - return; - } - Observer()->OnIceCandidateError(address, port, url, error_code, error_text); - // Leftover not to break wpt test during migration to the new API. - Observer()->OnIceCandidateError(address + ":", url, error_code, error_text); -} - -void TgPeerConnection::OnIceCandidatesRemoved( - const std::vector& candidates) { - if (IsClosed()) { - return; - } - Observer()->OnIceCandidatesRemoved(candidates); -} - -void TgPeerConnection::OnSelectedCandidatePairChanged( - const cricket::CandidatePairChangeEvent& event) { - if (IsClosed()) { - return; - } - - if (event.selected_candidate_pair.local_candidate().type() == - LOCAL_PORT_TYPE && - event.selected_candidate_pair.remote_candidate().type() == - LOCAL_PORT_TYPE) { - NoteUsageEvent(UsageEvent::DIRECT_CONNECTION_SELECTED); - } - - Observer()->OnIceSelectedCandidatePairChanged(event); -} - -void TgPeerConnection::ChangeSignalingState( - PeerConnectionInterface::SignalingState signaling_state) { - if (signaling_state_ == signaling_state) { - return; - } - RTC_LOG(LS_INFO) << "Session: " << session_id() << " Old state: " - << GetSignalingStateString(signaling_state_) - << " New state: " - << GetSignalingStateString(signaling_state); - signaling_state_ = signaling_state; - if (signaling_state == PeerConnectionInterface::kClosed) { - ice_connection_state_ = PeerConnectionInterface::kIceConnectionClosed; - Observer()->OnIceConnectionChange(ice_connection_state_); - standardized_ice_connection_state_ = - PeerConnectionInterface::IceConnectionState::kIceConnectionClosed; - connection_state_ = PeerConnectionInterface::PeerConnectionState::kClosed; - Observer()->OnConnectionChange(connection_state_); - if (ice_gathering_state_ != PeerConnectionInterface::kIceGatheringComplete) { - ice_gathering_state_ = PeerConnectionInterface::kIceGatheringComplete; - Observer()->OnIceGatheringChange(ice_gathering_state_); - } - } - Observer()->OnSignalingChange(signaling_state_); -} - -void TgPeerConnection::OnAudioTrackAdded(AudioTrackInterface* track, - MediaStreamInterface* stream) { - if (IsClosed()) { - return; - } - AddAudioTrack(track, stream); - UpdateNegotiationNeeded(); -} - -void TgPeerConnection::OnAudioTrackRemoved(AudioTrackInterface* track, - MediaStreamInterface* stream) { - if (IsClosed()) { - return; - } - RemoveAudioTrack(track, stream); - UpdateNegotiationNeeded(); -} - -void TgPeerConnection::OnVideoTrackAdded(VideoTrackInterface* track, - MediaStreamInterface* stream) { - if (IsClosed()) { - return; - } - AddVideoTrack(track, stream); - UpdateNegotiationNeeded(); -} - -void TgPeerConnection::OnVideoTrackRemoved(VideoTrackInterface* track, - MediaStreamInterface* stream) { - if (IsClosed()) { - return; - } - RemoveVideoTrack(track, stream); - UpdateNegotiationNeeded(); -} - -void TgPeerConnection::PostSetSessionDescriptionSuccess( - SetSessionDescriptionObserver* observer) { - SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); - signaling_thread()->Post(RTC_FROM_HERE, this, - MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg); -} - -void TgPeerConnection::PostSetSessionDescriptionFailure( - SetSessionDescriptionObserver* observer, - RTCError&& error) { - RTC_DCHECK(!error.ok()); - SetSessionDescriptionMsg* msg = new SetSessionDescriptionMsg(observer); - msg->error = std::move(error); - signaling_thread()->Post(RTC_FROM_HERE, this, - MSG_SET_SESSIONDESCRIPTION_FAILED, msg); -} - -void TgPeerConnection::PostCreateSessionDescriptionFailure( - CreateSessionDescriptionObserver* observer, - RTCError error) { - RTC_DCHECK(!error.ok()); - CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer); - msg->error = std::move(error); - signaling_thread()->Post(RTC_FROM_HERE, this, - MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg); -} - -void TgPeerConnection::GetOptionsForOffer( - const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - TgExtractSharedMediaSessionOptions(offer_answer_options, session_options); - - GetOptionsForUnifiedPlanOffer(offer_answer_options, session_options); - - // Apply ICE restart flag and renomination flag. - bool ice_restart = offer_answer_options.ice_restart || - local_ice_credentials_to_replace_->HasIceCredentials(); - for (auto& options : session_options->media_description_options) { - options.transport_options.ice_restart = ice_restart; - options.transport_options.enable_ice_renomination = - configuration_.enable_ice_renomination; - } - - session_options->rtcp_cname = rtcp_cname_; - session_options->crypto_options = GetCryptoOptions(); - session_options->pooled_ice_credentials = - network_thread()->Invoke>( - RTC_FROM_HERE, - rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials, - port_allocator_.get())); - session_options->offer_extmap_allow_mixed = - configuration_.offer_extmap_allow_mixed; - - // If datagram transport is in use, add opaque transport parameters. - if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { - for (auto& options : session_options->media_description_options) { - absl::optional params = - transport_controller_->GetTransportParameters(options.mid); - if (!params) { - continue; - } - options.transport_options.opaque_parameters = params; - if ((use_datagram_transport_ && - (options.type == cricket::MEDIA_TYPE_AUDIO || - options.type == cricket::MEDIA_TYPE_VIDEO)) || - (use_datagram_transport_for_data_channels_ && - options.type == cricket::MEDIA_TYPE_DATA)) { - options.alt_protocol = params->protocol; - } - } - } - - // Allow fallback for using obsolete SCTP syntax. - // Note that the default in |session_options| is true, while - // the default in |options| is false. - session_options->use_obsolete_sctp_sdp = - offer_answer_options.use_obsolete_sctp_sdp; -} - -static cricket::MediaDescriptionOptions -GetMediaDescriptionOptionsForTransceiver( - rtc::scoped_refptr> - transceiver, - const std::string& mid) { - cricket::MediaDescriptionOptions media_description_options( - transceiver->media_type(), mid, transceiver->direction(), - transceiver->stopped()); - media_description_options.codec_preferences = - transceiver->codec_preferences(); - // This behavior is specified in JSEP. The gist is that: - // 1. The MSID is included if the RtpTransceiver's direction is sendonly or - // sendrecv. - // 2. If the MSID is included, then it must be included in any subsequent - // offer/answer exactly the same until the RtpTransceiver is stopped. - if (transceiver->stopped() || - (!RtpTransceiverDirectionHasSend(transceiver->direction()) && - !transceiver->internal()->has_ever_been_used_to_send())) { - return media_description_options; - } - - cricket::SenderOptions sender_options; - sender_options.track_id = transceiver->sender()->id(); - sender_options.stream_ids = transceiver->sender()->stream_ids(); - - // The following sets up RIDs and Simulcast. - // RIDs are included if Simulcast is requested or if any RID was specified. - RtpParameters send_parameters = - transceiver->internal()->sender_internal()->GetParametersInternal(); - bool has_rids = std::any_of(send_parameters.encodings.begin(), - send_parameters.encodings.end(), - [](const RtpEncodingParameters& encoding) { - return !encoding.rid.empty(); - }); - - std::vector send_rids; - SimulcastLayerList send_layers; - for (const RtpEncodingParameters& encoding : send_parameters.encodings) { - if (encoding.rid.empty()) { - continue; - } - send_rids.push_back(RidDescription(encoding.rid, RidDirection::kSend)); - send_layers.AddLayer(SimulcastLayer(encoding.rid, !encoding.active)); - } - - if (has_rids) { - sender_options.rids = send_rids; - } - - sender_options.simulcast_layers = send_layers; - // When RIDs are configured, we must set num_sim_layers to 0 to. - // Otherwise, num_sim_layers must be 1 because either there is no - // simulcast, or simulcast is acheived by munging the SDP. - sender_options.num_sim_layers = has_rids ? 0 : 1; - media_description_options.sender_options.push_back(sender_options); - - return media_description_options; -} - -// Returns the ContentInfo at mline index |i|, or null if none exists. -static const ContentInfo* GetContentByIndex( - const SessionDescriptionInterface* sdesc, - size_t i) { - if (!sdesc) { - return nullptr; - } - const ContentInfos& contents = sdesc->description()->contents(); - return (i < contents.size() ? &contents[i] : nullptr); -} - -void TgPeerConnection::GetOptionsForUnifiedPlanOffer( - const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - // Rules for generating an offer are dictated by JSEP sections 5.2.1 (Initial - // Offers) and 5.2.2 (Subsequent Offers). - RTC_DCHECK_EQ(session_options->media_description_options.size(), 0); - const ContentInfos no_infos; - const ContentInfos& local_contents = - (local_description() ? local_description()->description()->contents() - : no_infos); - const ContentInfos& remote_contents = - (remote_description() ? remote_description()->description()->contents() - : no_infos); - // The mline indices that can be recycled. New transceivers should reuse these - // slots first. - std::queue recycleable_mline_indices; - // First, go through each media section that exists in either the local or - // remote description and generate a media section in this offer for the - // associated transceiver. If a media section can be recycled, generate a - // default, rejected media section here that can be later overwritten. - for (size_t i = 0; - i < std::max(local_contents.size(), remote_contents.size()); ++i) { - // Either |local_content| or |remote_content| is non-null. - const ContentInfo* local_content = - (i < local_contents.size() ? &local_contents[i] : nullptr); - const ContentInfo* current_local_content = - GetContentByIndex(current_local_description(), i); - const ContentInfo* remote_content = - (i < remote_contents.size() ? &remote_contents[i] : nullptr); - const ContentInfo* current_remote_content = - GetContentByIndex(current_remote_description(), i); - bool had_been_rejected = - (current_local_content && current_local_content->rejected) || - (current_remote_content && current_remote_content->rejected); - const std::string& mid = - (local_content ? local_content->name : remote_content->name); - cricket::MediaType media_type = - (local_content ? local_content->media_description()->type() - : remote_content->media_description()->type()); - if (media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO) { - auto transceiver = GetAssociatedTransceiver(mid); - RTC_CHECK(transceiver); - // A media section is considered eligible for recycling if it is marked as - // rejected in either the current local or current remote description. - if (had_been_rejected && transceiver->stopped()) { - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions(transceiver->media_type(), mid, - RtpTransceiverDirection::kInactive, - /*stopped=*/true)); - recycleable_mline_indices.push(i); - } else { - session_options->media_description_options.push_back( - GetMediaDescriptionOptionsForTransceiver(transceiver, mid)); - // CreateOffer shouldn't really cause any state changes in - // TgPeerConnection, but we need a way to match new transceivers to new - // media sections in SetLocalDescription and JSEP specifies this is done - // by recording the index of the media section generated for the - // transceiver in the offer. - transceiver->internal()->set_mline_index(i); - } - } - } - - // Next, look for transceivers that are newly added (that is, are not stopped - // and not associated). Reuse media sections marked as recyclable first, - // otherwise append to the end of the offer. New media sections should be - // added in the order they were added to the TgPeerConnection. - for (const auto& transceiver : transceivers_) { - if (transceiver->mid() || transceiver->stopped()) { - continue; - } - size_t mline_index; - if (!recycleable_mline_indices.empty()) { - mline_index = recycleable_mline_indices.front(); - recycleable_mline_indices.pop(); - session_options->media_description_options[mline_index] = - GetMediaDescriptionOptionsForTransceiver(transceiver, - mid_generator_()); - } else { - mline_index = session_options->media_description_options.size(); - session_options->media_description_options.push_back( - GetMediaDescriptionOptionsForTransceiver(transceiver, - mid_generator_())); - } - // See comment above for why CreateOffer changes the transceiver's state. - transceiver->internal()->set_mline_index(mline_index); - } -} - -void TgPeerConnection::GetOptionsForAnswer( - const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - TgExtractSharedMediaSessionOptions(offer_answer_options, session_options); - - GetOptionsForUnifiedPlanAnswer(offer_answer_options, session_options); - - // Apply ICE renomination flag. - for (auto& options : session_options->media_description_options) { - options.transport_options.enable_ice_renomination = - configuration_.enable_ice_renomination; - } - - session_options->rtcp_cname = rtcp_cname_; - session_options->crypto_options = GetCryptoOptions(); - session_options->pooled_ice_credentials = - network_thread()->Invoke>( - RTC_FROM_HERE, - rtc::Bind(&cricket::PortAllocator::GetPooledIceCredentials, - port_allocator_.get())); - - // If datagram transport is in use, add opaque transport parameters. - if (use_datagram_transport_ || use_datagram_transport_for_data_channels_) { - for (auto& options : session_options->media_description_options) { - absl::optional params = - transport_controller_->GetTransportParameters(options.mid); - if (!params) { - continue; - } - options.transport_options.opaque_parameters = params; - if ((use_datagram_transport_ && - (options.type == cricket::MEDIA_TYPE_AUDIO || - options.type == cricket::MEDIA_TYPE_VIDEO)) || - (use_datagram_transport_for_data_channels_ && - options.type == cricket::MEDIA_TYPE_DATA)) { - options.alt_protocol = params->protocol; - } - } - } -} - -void TgPeerConnection::GetOptionsForUnifiedPlanAnswer( - const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) { - // Rules for generating an answer are dictated by JSEP sections 5.3.1 (Initial - // Answers) and 5.3.2 (Subsequent Answers). - RTC_DCHECK(remote_description()); - RTC_DCHECK(remote_description()->GetType() == SdpType::kOffer); - for (const ContentInfo& content : - remote_description()->description()->contents()) { - cricket::MediaType media_type = content.media_description()->type(); - if (media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO) { - auto transceiver = GetAssociatedTransceiver(content.name); - RTC_CHECK(transceiver); - session_options->media_description_options.push_back( - GetMediaDescriptionOptionsForTransceiver(transceiver, content.name)); - } - } -} - -void TgPeerConnection::GenerateMediaDescriptionOptions( - const SessionDescriptionInterface* session_desc, - RtpTransceiverDirection audio_direction, - RtpTransceiverDirection video_direction, - absl::optional* audio_index, - absl::optional* video_index, - absl::optional* data_index, - cricket::MediaSessionOptions* session_options) { - for (const cricket::ContentInfo& content : - session_desc->description()->contents()) { - if (IsAudioContent(&content)) { - // If we already have an audio m= section, reject this extra one. - if (*audio_index) { - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions( - cricket::MEDIA_TYPE_AUDIO, content.name, - RtpTransceiverDirection::kInactive, /*stopped=*/true)); - } else { - bool stopped = (audio_direction == RtpTransceiverDirection::kInactive); - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_AUDIO, - content.name, audio_direction, - stopped)); - *audio_index = session_options->media_description_options.size() - 1; - } - } else if (IsVideoContent(&content)) { - // If we already have an video m= section, reject this extra one. - if (*video_index) { - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions( - cricket::MEDIA_TYPE_VIDEO, content.name, - RtpTransceiverDirection::kInactive, /*stopped=*/true)); - } else { - bool stopped = (video_direction == RtpTransceiverDirection::kInactive); - session_options->media_description_options.push_back( - cricket::MediaDescriptionOptions(cricket::MEDIA_TYPE_VIDEO, - content.name, video_direction, - stopped)); - *video_index = session_options->media_description_options.size() - 1; - } - } - } -} - -void TgPeerConnection::RemoveSenders(cricket::MediaType media_type) { - UpdateLocalSenders(std::vector(), media_type); - UpdateRemoteSendersList(std::vector(), false, - media_type, nullptr); -} - -void TgPeerConnection::UpdateRemoteSendersList( - const cricket::StreamParamsVec& streams, - bool default_sender_needed, - cricket::MediaType media_type, - StreamCollection* new_streams) { - RTC_DCHECK(!IsUnifiedPlan()); - - std::vector* current_senders = - GetRemoteSenderInfos(media_type); - - // Find removed senders. I.e., senders where the sender id or ssrc don't match - // the new StreamParam. - for (auto sender_it = current_senders->begin(); - sender_it != current_senders->end(); - /* incremented manually */) { - const RtpSenderInfo& info = *sender_it; - const cricket::StreamParams* params = - cricket::GetStreamBySsrc(streams, info.first_ssrc); - std::string params_stream_id; - if (params) { - params_stream_id = - (!params->first_stream_id().empty() ? params->first_stream_id() - : kDefaultStreamId); - } - bool sender_exists = params && params->id == info.sender_id && - params_stream_id == info.stream_id; - // If this is a default track, and we still need it, don't remove it. - if ((info.stream_id == kDefaultStreamId && default_sender_needed) || - sender_exists) { - ++sender_it; - } else { - OnRemoteSenderRemoved(info, media_type); - sender_it = current_senders->erase(sender_it); - } - } - - // Find new and active senders. - for (const cricket::StreamParams& params : streams) { - if (!params.has_ssrcs()) { - // The remote endpoint has streams, but didn't signal ssrcs. For an active - // sender, this means it is coming from a Unified Plan endpoint,so we just - // create a default. - default_sender_needed = true; - break; - } - - // |params.id| is the sender id and the stream id uses the first of - // |params.stream_ids|. The remote description could come from a Unified - // Plan endpoint, with multiple or no stream_ids() signaled. Since this is - // not supported in Plan B, we just take the first here and create the - // default stream ID if none is specified. - const std::string& stream_id = - (!params.first_stream_id().empty() ? params.first_stream_id() - : kDefaultStreamId); - const std::string& sender_id = params.id; - uint32_t ssrc = params.first_ssrc(); - - rtc::scoped_refptr stream = - remote_streams_->find(stream_id); - if (!stream) { - // This is a new MediaStream. Create a new remote MediaStream. - stream = MediaStreamProxy::Create(rtc::Thread::Current(), - MediaStream::Create(stream_id)); - remote_streams_->AddStream(stream); - new_streams->AddStream(stream); - } - - const RtpSenderInfo* sender_info = - FindSenderInfo(*current_senders, stream_id, sender_id); - if (!sender_info) { - current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); - OnRemoteSenderAdded(current_senders->back(), media_type); - } - } - - // Add default sender if necessary. - if (default_sender_needed) { - rtc::scoped_refptr default_stream = - remote_streams_->find(kDefaultStreamId); - if (!default_stream) { - // Create the new default MediaStream. - default_stream = MediaStreamProxy::Create( - rtc::Thread::Current(), MediaStream::Create(kDefaultStreamId)); - remote_streams_->AddStream(default_stream); - new_streams->AddStream(default_stream); - } - std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO) - ? kDefaultAudioSenderId - : kDefaultVideoSenderId; - const RtpSenderInfo* default_sender_info = - FindSenderInfo(*current_senders, kDefaultStreamId, default_sender_id); - if (!default_sender_info) { - current_senders->push_back( - RtpSenderInfo(kDefaultStreamId, default_sender_id, /*ssrc=*/0)); - OnRemoteSenderAdded(current_senders->back(), media_type); - } - } -} - -void TgPeerConnection::OnRemoteSenderAdded(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) { - RTC_LOG(LS_INFO) << "Creating " << cricket::MediaTypeToString(media_type) - << " receiver for track_id=" << sender_info.sender_id - << " and stream_id=" << sender_info.stream_id; - - MediaStreamInterface* stream = remote_streams_->find(sender_info.stream_id); - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - CreateAudioReceiver(stream, sender_info); - } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { - CreateVideoReceiver(stream, sender_info); - } else { - RTC_NOTREACHED() << "Invalid media type"; - } -} - -void TgPeerConnection::OnRemoteSenderRemoved(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) { - RTC_LOG(LS_INFO) << "Removing " << cricket::MediaTypeToString(media_type) - << " receiver for track_id=" << sender_info.sender_id - << " and stream_id=" << sender_info.stream_id; - - MediaStreamInterface* stream = remote_streams_->find(sender_info.stream_id); - - rtc::scoped_refptr receiver; - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - // When the MediaEngine audio channel is destroyed, the RemoteAudioSource - // will be notified which will end the AudioRtpReceiver::track(). - receiver = RemoveAndStopReceiver(sender_info); - rtc::scoped_refptr audio_track = - stream->FindAudioTrack(sender_info.sender_id); - if (audio_track) { - stream->RemoveTrack(audio_track); - } - } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { - // Stopping or destroying a VideoRtpReceiver will end the - // VideoRtpReceiver::track(). - receiver = RemoveAndStopReceiver(sender_info); - rtc::scoped_refptr video_track = - stream->FindVideoTrack(sender_info.sender_id); - if (video_track) { - // There's no guarantee the track is still available, e.g. the track may - // have been removed from the stream by an application. - stream->RemoveTrack(video_track); - } - } else { - RTC_NOTREACHED() << "Invalid media type"; - } - if (receiver) { - Observer()->OnRemoveTrack(receiver); - } -} - -void TgPeerConnection::UpdateEndedRemoteMediaStreams() { - std::vector> streams_to_remove; - for (size_t i = 0; i < remote_streams_->count(); ++i) { - MediaStreamInterface* stream = remote_streams_->at(i); - if (stream->GetAudioTracks().empty() && stream->GetVideoTracks().empty()) { - streams_to_remove.push_back(stream); - } - } - - for (auto& stream : streams_to_remove) { - remote_streams_->RemoveStream(stream); - Observer()->OnRemoveStream(std::move(stream)); - } -} - -void TgPeerConnection::UpdateLocalSenders( - const std::vector& streams, - cricket::MediaType media_type) { - std::vector* current_senders = GetLocalSenderInfos(media_type); - - // Find removed tracks. I.e., tracks where the track id, stream id or ssrc - // don't match the new StreamParam. - for (auto sender_it = current_senders->begin(); - sender_it != current_senders->end(); - /* incremented manually */) { - const RtpSenderInfo& info = *sender_it; - const cricket::StreamParams* params = - cricket::GetStreamBySsrc(streams, info.first_ssrc); - if (!params || params->id != info.sender_id || - params->first_stream_id() != info.stream_id) { - OnLocalSenderRemoved(info, media_type); - sender_it = current_senders->erase(sender_it); - } else { - ++sender_it; - } - } - - // Find new and active senders. - for (const cricket::StreamParams& params : streams) { - // The sync_label is the MediaStream label and the |stream.id| is the - // sender id. - const std::string& stream_id = params.first_stream_id(); - const std::string& sender_id = params.id; - uint32_t ssrc = params.first_ssrc(); - const RtpSenderInfo* sender_info = - FindSenderInfo(*current_senders, stream_id, sender_id); - if (!sender_info) { - current_senders->push_back(RtpSenderInfo(stream_id, sender_id, ssrc)); - OnLocalSenderAdded(current_senders->back(), media_type); - } - } -} - -void TgPeerConnection::OnLocalSenderAdded(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) { - RTC_DCHECK(!IsUnifiedPlan()); - auto sender = FindSenderById(sender_info.sender_id); - if (!sender) { - RTC_LOG(LS_WARNING) << "An unknown RtpSender with id " - << sender_info.sender_id - << " has been configured in the local description."; - return; - } - - if (sender->media_type() != media_type) { - RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" - " description with an unexpected media type."; - return; - } - - sender->internal()->set_stream_ids({sender_info.stream_id}); - sender->internal()->SetSsrc(sender_info.first_ssrc); -} - -void TgPeerConnection::OnLocalSenderRemoved(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) { - auto sender = FindSenderById(sender_info.sender_id); - if (!sender) { - // This is the normal case. I.e., RemoveStream has been called and the - // SessionDescriptions has been renegotiated. - return; - } - - // A sender has been removed from the SessionDescription but it's still - // associated with the TgPeerConnection. This only occurs if the SDP doesn't - // match with the calls to CreateSender, AddStream and RemoveStream. - if (sender->media_type() != media_type) { - RTC_LOG(LS_WARNING) << "An RtpSender has been configured in the local" - " description with an unexpected media type."; - return; - } - - sender->internal()->SetSsrc(0); -} - -rtc::scoped_refptr> -TgPeerConnection::GetAudioTransceiver() const { - // This method only works with Plan B SDP, where there is a single - // audio/video transceiver. - RTC_DCHECK(!IsUnifiedPlan()); - for (auto transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) { - return transceiver; - } - } - RTC_NOTREACHED(); - return nullptr; -} - -rtc::scoped_refptr> -TgPeerConnection::GetVideoTransceiver() const { - // This method only works with Plan B SDP, where there is a single - // audio/video transceiver. - RTC_DCHECK(!IsUnifiedPlan()); - for (auto transceiver : transceivers_) { - if (transceiver->media_type() == cricket::MEDIA_TYPE_VIDEO) { - return transceiver; - } - } - RTC_NOTREACHED(); - return nullptr; -} - -// TODO(bugs.webrtc.org/7600): Remove this when multiple transceivers with -// individual transceiver directions are supported. -bool TgPeerConnection::HasRtpSender(cricket::MediaType type) const { - switch (type) { - case cricket::MEDIA_TYPE_AUDIO: - return !GetAudioTransceiver()->internal()->senders().empty(); - case cricket::MEDIA_TYPE_VIDEO: - return !GetVideoTransceiver()->internal()->senders().empty(); - case cricket::MEDIA_TYPE_DATA: - return false; - } - RTC_NOTREACHED(); - return false; -} - -rtc::scoped_refptr> -TgPeerConnection::FindSenderForTrack(MediaStreamTrackInterface* track) const { - for (const auto& transceiver : transceivers_) { - for (auto sender : transceiver->internal()->senders()) { - if (sender->track() == track) { - return sender; - } - } - } - return nullptr; -} - -rtc::scoped_refptr> -TgPeerConnection::FindSenderById(const std::string& sender_id) const { - for (const auto& transceiver : transceivers_) { - for (auto sender : transceiver->internal()->senders()) { - if (sender->id() == sender_id) { - return sender; - } - } - } - return nullptr; -} - -rtc::scoped_refptr> -TgPeerConnection::FindReceiverById(const std::string& receiver_id) const { - for (const auto& transceiver : transceivers_) { - for (auto receiver : transceiver->internal()->receivers()) { - if (receiver->id() == receiver_id) { - return receiver; - } - } - } - return nullptr; -} - -std::vector* -TgPeerConnection::GetRemoteSenderInfos(cricket::MediaType media_type) { - RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO); - return (media_type == cricket::MEDIA_TYPE_AUDIO) - ? &remote_audio_sender_infos_ - : &remote_video_sender_infos_; -} - -std::vector* TgPeerConnection::GetLocalSenderInfos( - cricket::MediaType media_type) { - RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO || - media_type == cricket::MEDIA_TYPE_VIDEO); - return (media_type == cricket::MEDIA_TYPE_AUDIO) ? &local_audio_sender_infos_ - : &local_video_sender_infos_; -} - -const TgPeerConnection::RtpSenderInfo* TgPeerConnection::FindSenderInfo( - const std::vector& infos, - const std::string& stream_id, - const std::string sender_id) const { - for (const RtpSenderInfo& sender_info : infos) { - if (sender_info.stream_id == stream_id && - sender_info.sender_id == sender_id) { - return &sender_info; - } - } - return nullptr; -} - -TgPeerConnection::InitializePortAllocatorResult -TgPeerConnection::InitializePortAllocator_n( - const cricket::ServerAddresses& stun_servers, - const std::vector& turn_servers, - const PeerConnectionInterface::RTCConfiguration& configuration) { - RTC_DCHECK_RUN_ON(network_thread()); - - port_allocator_->Initialize(); - // To handle both internal and externally created port allocator, we will - // enable BUNDLE here. - int port_allocator_flags = port_allocator_->flags(); - port_allocator_flags |= cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | - cricket::PORTALLOCATOR_ENABLE_IPV6 | - cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI; - // If the disable-IPv6 flag was specified, we'll not override it - // by experiment. - if (configuration.disable_ipv6) { - port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); - } else if (webrtc::field_trial::FindFullName("WebRTC-IPv6Default") - .find("Disabled") == 0) { - port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6); - } - - if (configuration.disable_ipv6_on_wifi) { - port_allocator_flags &= ~(cricket::PORTALLOCATOR_ENABLE_IPV6_ON_WIFI); - RTC_LOG(LS_INFO) << "IPv6 candidates on Wi-Fi are disabled."; - } - - if (configuration.tcp_candidate_policy == PeerConnectionInterface::kTcpCandidatePolicyDisabled) { - port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_TCP; - RTC_LOG(LS_INFO) << "TCP candidates are disabled."; - } - - if (configuration.candidate_network_policy == - PeerConnectionInterface::kCandidateNetworkPolicyLowCost) { - port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_COSTLY_NETWORKS; - RTC_LOG(LS_INFO) << "Do not gather candidates on high-cost networks"; - } - - if (configuration.disable_link_local_networks) { - port_allocator_flags |= cricket::PORTALLOCATOR_DISABLE_LINK_LOCAL_NETWORKS; - RTC_LOG(LS_INFO) << "Disable candidates on link-local network interfaces."; - } - - port_allocator_->set_flags(port_allocator_flags); - // No step delay is used while allocating ports. - port_allocator_->set_step_delay(cricket::kMinimumStepDelay); - port_allocator_->SetCandidateFilter( - ConvertIceTransportTypeToCandidateFilter(configuration.type)); - port_allocator_->set_max_ipv6_networks(configuration.max_ipv6_networks); - - auto turn_servers_copy = turn_servers; - for (auto& turn_server : turn_servers_copy) { - turn_server.tls_cert_verifier = tls_cert_verifier_.get(); - } - // Call this last since it may create pooled allocator sessions using the - // properties set above. - port_allocator_->SetConfiguration( - stun_servers, std::move(turn_servers_copy), - configuration.ice_candidate_pool_size, - configuration.GetTurnPortPrunePolicy(), configuration.turn_customizer, - configuration.stun_candidate_keepalive_interval); - - InitializePortAllocatorResult res; - res.enable_ipv6 = port_allocator_flags & cricket::PORTALLOCATOR_ENABLE_IPV6; - return res; -} - -bool TgPeerConnection::ReconfigurePortAllocator_n( - const cricket::ServerAddresses& stun_servers, - const std::vector& turn_servers, - PeerConnectionInterface::IceTransportsType type, - int candidate_pool_size, - PortPrunePolicy turn_port_prune_policy, - webrtc::TurnCustomizer* turn_customizer, - absl::optional stun_candidate_keepalive_interval, - bool have_local_description) { - port_allocator_->SetCandidateFilter( - ConvertIceTransportTypeToCandidateFilter(type)); - // According to JSEP, after setLocalDescription, changing the candidate pool - // size is not allowed, and changing the set of ICE servers will not result - // in new candidates being gathered. - if (have_local_description) { - port_allocator_->FreezeCandidatePool(); - } - // Add the custom tls turn servers if they exist. - auto turn_servers_copy = turn_servers; - for (auto& turn_server : turn_servers_copy) { - turn_server.tls_cert_verifier = tls_cert_verifier_.get(); - } - // Call this last since it may create pooled allocator sessions using the - // candidate filter set above. - return port_allocator_->SetConfiguration( - stun_servers, std::move(turn_servers_copy), candidate_pool_size, - turn_port_prune_policy, turn_customizer, - stun_candidate_keepalive_interval); -} - -cricket::ChannelManager* TgPeerConnection::channel_manager() const { - return factory_->channel_manager(); -} - -bool TgPeerConnection::StartRtcEventLog_w( - std::unique_ptr output, - int64_t output_period_ms) { - RTC_DCHECK_RUN_ON(worker_thread()); - if (!event_log_) { - return false; - } - return event_log_->StartLogging(std::move(output), output_period_ms); -} - -void TgPeerConnection::StopRtcEventLog_w() { - RTC_DCHECK_RUN_ON(worker_thread()); - if (event_log_) { - event_log_->StopLogging(); - } -} - -cricket::ChannelInterface* TgPeerConnection::GetChannel( - const std::string& content_name) { - for (const auto& transceiver : transceivers_) { - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel && channel->content_name() == content_name) { - return channel; - } - } - if (rtp_data_channel() && - rtp_data_channel()->content_name() == content_name) { - return rtp_data_channel(); - } - return nullptr; -} - -bool TgPeerConnection::GetSctpSslRole(rtc::SSLRole* role) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!local_description() || !remote_description()) { - RTC_LOG(LS_INFO) - << "Local and Remote descriptions must be applied to get the " - "SSL Role of the SCTP transport."; - return false; - } - - absl::optional dtls_role; - if (sctp_mid_) { - dtls_role = transport_controller_->GetDtlsRole(*sctp_mid_); - if (!dtls_role && is_caller_.has_value()) { - dtls_role = *is_caller_ ? rtc::SSL_SERVER : rtc::SSL_CLIENT; - } - *role = *dtls_role; - return true; - } - return false; -} - -bool TgPeerConnection::GetSslRole(const std::string& content_name, - rtc::SSLRole* role) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!local_description() || !remote_description()) { - RTC_LOG(LS_INFO) - << "Local and Remote descriptions must be applied to get the " - "SSL Role of the session."; - return false; - } - - auto dtls_role = transport_controller_->GetDtlsRole(content_name); - if (dtls_role) { - *role = *dtls_role; - return true; - } - return false; -} - -void TgPeerConnection::SetSessionError(SessionError error, - const std::string& error_desc) { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (error != session_error_) { - session_error_ = error; - session_error_desc_ = error_desc; - } -} - -RTCError TgPeerConnection::UpdateSessionState( - SdpType type, - cricket::ContentSource source, - const cricket::SessionDescription* description) { - RTC_DCHECK_RUN_ON(signaling_thread()); - - // If there's already a pending error then no state transition should happen. - // But all call-sites should be verifying this before calling us! - RTC_DCHECK(session_error() == SessionError::kNone); - - // If this is answer-ish we're ready to let media flow. - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - EnableSending(); - } - - // Update the signaling state according to the specified state machine (see - // https://w3c.github.io/webrtc-pc/#rtcsignalingstate-enum). - if (type == SdpType::kOffer) { - ChangeSignalingState(source == cricket::CS_LOCAL - ? PeerConnectionInterface::kHaveLocalOffer - : PeerConnectionInterface::kHaveRemoteOffer); - } else if (type == SdpType::kPrAnswer) { - ChangeSignalingState(source == cricket::CS_LOCAL - ? PeerConnectionInterface::kHaveLocalPrAnswer - : PeerConnectionInterface::kHaveRemotePrAnswer); - } else { - RTC_DCHECK(type == SdpType::kAnswer); - ChangeSignalingState(PeerConnectionInterface::kStable); - transceiver_stable_states_by_transceivers_.clear(); - } - - // Update internal objects according to the session description's media - // descriptions. - RTCError error = PushdownMediaDescription(type, source); - if (!error.ok()) { - return error; - } - - return RTCError::OK(); -} - -RTCError TgPeerConnection::PushdownMediaDescription( - SdpType type, - cricket::ContentSource source) { - const SessionDescriptionInterface* sdesc = - (source == cricket::CS_LOCAL ? local_description() - : remote_description()); - RTC_DCHECK(sdesc); - - // Push down the new SDP media section for each audio/video transceiver. - for (const auto& transceiver : transceivers_) { - const ContentInfo* content_info = - FindMediaSectionForTransceiver(transceiver, sdesc); - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (!channel || !content_info || content_info->rejected) { - continue; - } - const MediaContentDescription* content_desc = - content_info->media_description(); - if (!content_desc) { - continue; - } - std::string error; - bool success = (source == cricket::CS_LOCAL) - ? channel->SetLocalContent(content_desc, type, &error) - : channel->SetRemoteContent(content_desc, type, &error); - if (!success) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, error); - } - } - - // Need complete offer/answer with an SCTP m= section before starting SCTP, - // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19 - if (sctp_mid_ && local_description() && remote_description()) { - rtc::scoped_refptr sctp_transport = - transport_controller_->GetSctpTransport(*sctp_mid_); - auto local_sctp_description = cricket::GetFirstSctpDataContentDescription( - local_description()->description()); - auto remote_sctp_description = cricket::GetFirstSctpDataContentDescription( - remote_description()->description()); - if (sctp_transport && local_sctp_description && remote_sctp_description) { - int max_message_size; - // A remote max message size of zero means "any size supported". - // We configure the connection with our own max message size. - if (remote_sctp_description->max_message_size() == 0) { - max_message_size = local_sctp_description->max_message_size(); - } else { - max_message_size = - std::min(local_sctp_description->max_message_size(), - remote_sctp_description->max_message_size()); - } - sctp_transport->Start(local_sctp_description->port(), - remote_sctp_description->port(), max_message_size); - } - } - - return RTCError::OK(); -} - -RTCError TgPeerConnection::PushdownTransportDescription( - cricket::ContentSource source, - SdpType type) { - RTC_DCHECK_RUN_ON(signaling_thread()); - - if (source == cricket::CS_LOCAL) { - const SessionDescriptionInterface* sdesc = local_description(); - RTC_DCHECK(sdesc); - return transport_controller_->SetLocalDescription(type, - sdesc->description()); - } else { - const SessionDescriptionInterface* sdesc = remote_description(); - RTC_DCHECK(sdesc); - return transport_controller_->SetRemoteDescription(type, - sdesc->description()); - } -} - -bool TgPeerConnection::GetTransportDescription( - const SessionDescription* description, - const std::string& content_name, - cricket::TransportDescription* tdesc) { - if (!description || !tdesc) { - return false; - } - const TransportInfo* transport_info = - description->GetTransportInfoByName(content_name); - if (!transport_info) { - return false; - } - *tdesc = transport_info->description; - return true; -} - -cricket::IceConfig TgPeerConnection::ParseIceConfig( - const PeerConnectionInterface::RTCConfiguration& config) const { - cricket::ContinualGatheringPolicy gathering_policy; - switch (config.continual_gathering_policy) { - case PeerConnectionInterface::GATHER_ONCE: - gathering_policy = cricket::GATHER_ONCE; - break; - case PeerConnectionInterface::GATHER_CONTINUALLY: - gathering_policy = cricket::GATHER_CONTINUALLY; - break; - default: - RTC_NOTREACHED(); - gathering_policy = cricket::GATHER_ONCE; - } - - cricket::IceConfig ice_config; - ice_config.receiving_timeout = RTCConfigurationToIceConfigOptionalInt( - config.ice_connection_receiving_timeout); - ice_config.prioritize_most_likely_candidate_pairs = - config.prioritize_most_likely_ice_candidate_pairs; - ice_config.backup_connection_ping_interval = - RTCConfigurationToIceConfigOptionalInt( - config.ice_backup_candidate_pair_ping_interval); - ice_config.continual_gathering_policy = gathering_policy; - ice_config.presume_writable_when_fully_relayed = - config.presume_writable_when_fully_relayed; - ice_config.surface_ice_candidates_on_ice_transport_type_changed = - config.surface_ice_candidates_on_ice_transport_type_changed; - ice_config.ice_check_interval_strong_connectivity = - config.ice_check_interval_strong_connectivity; - ice_config.ice_check_interval_weak_connectivity = - config.ice_check_interval_weak_connectivity; - ice_config.ice_check_min_interval = config.ice_check_min_interval; - ice_config.ice_unwritable_timeout = config.ice_unwritable_timeout; - ice_config.ice_unwritable_min_checks = config.ice_unwritable_min_checks; - ice_config.ice_inactive_timeout = config.ice_inactive_timeout; - ice_config.stun_keepalive_interval = config.stun_candidate_keepalive_interval; - ice_config.regather_all_networks_interval_range = - config.ice_regather_interval_range; - ice_config.network_preference = config.network_preference; - return ice_config; -} - -absl::optional TgPeerConnection::sctp_transport_name() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (sctp_mid_ && transport_controller_) { - auto dtls_transport = transport_controller_->GetDtlsTransport(*sctp_mid_); - if (dtls_transport) { - return dtls_transport->transport_name(); - } - return absl::optional(); - } - return absl::optional(); -} - -cricket::CandidateStatsList TgPeerConnection::GetPooledCandidateStats() const { - cricket::CandidateStatsList candidate_states_list; - network_thread()->Invoke( - RTC_FROM_HERE, - rtc::Bind(&cricket::PortAllocator::GetCandidateStatsFromPooledSessions, - port_allocator_.get(), &candidate_states_list)); - return candidate_states_list; -} - -std::map TgPeerConnection::GetTransportNamesByMid() -const { - RTC_DCHECK_RUN_ON(signaling_thread()); - std::map transport_names_by_mid; - for (const auto& transceiver : transceivers_) { - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel) { - transport_names_by_mid[channel->content_name()] = - channel->transport_name(); - } - } - return transport_names_by_mid; -} - -std::map -TgPeerConnection::GetTransportStatsByNames( - const std::set& transport_names) { - if (!network_thread()->IsCurrent()) { - return network_thread() - ->Invoke>( - RTC_FROM_HERE, - [&] { return GetTransportStatsByNames(transport_names); }); - } - RTC_DCHECK_RUN_ON(network_thread()); - std::map transport_stats_by_name; - for (const std::string& transport_name : transport_names) { - cricket::TransportStats transport_stats; - bool success = - transport_controller_->GetStats(transport_name, &transport_stats); - if (success) { - transport_stats_by_name[transport_name] = std::move(transport_stats); - } else { - RTC_LOG(LS_ERROR) << "Failed to get transport stats for transport_name=" - << transport_name; - } - } - return transport_stats_by_name; -} - -bool TgPeerConnection::GetLocalCertificate( - const std::string& transport_name, - rtc::scoped_refptr* certificate) { - if (!certificate) { - return false; - } - *certificate = transport_controller_->GetLocalCertificate(transport_name); - return *certificate != nullptr; -} - -std::unique_ptr TgPeerConnection::GetRemoteSSLCertChain( - const std::string& transport_name) { - return transport_controller_->GetRemoteSSLCertChain(transport_name); -} - -bool TgPeerConnection::IceRestartPending(const std::string& content_name) const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return pending_ice_restarts_.find(content_name) != - pending_ice_restarts_.end(); -} - -bool TgPeerConnection::NeedsIceRestart(const std::string& content_name) const { - return transport_controller_->NeedsIceRestart(content_name); -} - -void TgPeerConnection::OnCertificateReady( - const rtc::scoped_refptr& certificate) { - transport_controller_->SetLocalCertificate(certificate); -} - -void TgPeerConnection::OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp) { - SetSessionError(SessionError::kTransport, - rtcp ? kDtlsSrtpSetupFailureRtcp : kDtlsSrtpSetupFailureRtp); -} - -void TgPeerConnection::OnTransportControllerConnectionState( - cricket::IceConnectionState state) { - switch (state) { - case cricket::kIceConnectionConnecting: - // If the current state is Connected or Completed, then there were - // writable channels but now there are not, so the next state must - // be Disconnected. - // kIceConnectionConnecting is currently used as the default, - // un-connected state by the TransportController, so its only use is - // detecting disconnections. - if (ice_connection_state_ == - PeerConnectionInterface::kIceConnectionConnected || - ice_connection_state_ == - PeerConnectionInterface::kIceConnectionCompleted) { - SetIceConnectionState( - PeerConnectionInterface::kIceConnectionDisconnected); - } - break; - case cricket::kIceConnectionFailed: - SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed); - break; - case cricket::kIceConnectionConnected: - RTC_LOG(LS_INFO) << "Changing to ICE connected state because " - "all transports are writable."; - SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); - NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); - break; - case cricket::kIceConnectionCompleted: - RTC_LOG(LS_INFO) << "Changing to ICE completed state because " - "all transports are complete."; - if (ice_connection_state_ != - PeerConnectionInterface::kIceConnectionConnected) { - // If jumping directly from "checking" to "connected", - // signal "connected" first. - SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected); - } - SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted); - NoteUsageEvent(UsageEvent::ICE_STATE_CONNECTED); - ReportTransportStats(); - break; - default: - RTC_NOTREACHED(); - } -} - -void TgPeerConnection::OnTransportControllerCandidatesGathered( - const std::string& transport_name, - const cricket::Candidates& candidates) { - int sdp_mline_index; - if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) { - RTC_LOG(LS_ERROR) - << "OnTransportControllerCandidatesGathered: content name " - << transport_name << " not found"; - return; - } - - for (cricket::Candidates::const_iterator citer = candidates.begin(); - citer != candidates.end(); ++citer) { - // Use transport_name as the candidate media id. - std::unique_ptr candidate( - new JsepIceCandidate(transport_name, sdp_mline_index, *citer)); - if (local_description()) { - mutable_local_description()->AddCandidate(candidate.get()); - } - OnIceCandidate(std::move(candidate)); - } -} - -void TgPeerConnection::OnTransportControllerCandidateError( - const cricket::IceCandidateErrorEvent& event) { - OnIceCandidateError(event.address, event.port, event.url, event.error_code, - event.error_text); -} - -void TgPeerConnection::OnTransportControllerCandidatesRemoved( - const std::vector& candidates) { - // Sanity check. - for (const cricket::Candidate& candidate : candidates) { - if (candidate.transport_name().empty()) { - RTC_LOG(LS_ERROR) << "OnTransportControllerCandidatesRemoved: " - "empty content name in candidate " - << candidate.ToString(); - return; - } - } - - if (local_description()) { - mutable_local_description()->RemoveCandidates(candidates); - } - OnIceCandidatesRemoved(candidates); -} - -void TgPeerConnection::OnTransportControllerCandidateChanged( - const cricket::CandidatePairChangeEvent& event) { - OnSelectedCandidatePairChanged(event); -} - -void TgPeerConnection::OnTransportControllerDtlsHandshakeError( - rtc::SSLHandshakeError error) { - RTC_HISTOGRAM_ENUMERATION( - "WebRTC.TgPeerConnection.DtlsHandshakeError", static_cast(error), - static_cast(rtc::SSLHandshakeError::MAX_VALUE)); -} - -void TgPeerConnection::EnableSending() { - for (const auto& transceiver : transceivers_) { - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel && !channel->enabled()) { - channel->Enable(true); - } - } -} - -// Returns the media index for a local ice candidate given the content name. -bool TgPeerConnection::GetLocalCandidateMediaIndex( - const std::string& content_name, - int* sdp_mline_index) { - if (!local_description() || !sdp_mline_index) { - return false; - } - - bool content_found = false; - const ContentInfos& contents = local_description()->description()->contents(); - for (size_t index = 0; index < contents.size(); ++index) { - if (contents[index].name == content_name) { - *sdp_mline_index = static_cast(index); - content_found = true; - break; - } - } - return content_found; -} - -bool TgPeerConnection::UseCandidatesInSessionDescription( - const SessionDescriptionInterface* remote_desc) { - if (!remote_desc) { - return true; - } - bool ret = true; - - for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) { - const IceCandidateCollection* candidates = remote_desc->candidates(m); - for (size_t n = 0; n < candidates->count(); ++n) { - const IceCandidateInterface* candidate = candidates->at(n); - bool valid = false; - if (!ReadyToUseRemoteCandidate(candidate, remote_desc, &valid)) { - if (valid) { - RTC_LOG(LS_INFO) - << "UseCandidatesInSessionDescription: Not ready to use " - "candidate."; - } - continue; - } - ret = UseCandidate(candidate); - if (!ret) { - break; - } - } - } - return ret; -} - -bool TgPeerConnection::UseCandidate(const IceCandidateInterface* candidate) { - RTCErrorOr result = - FindContentInfo(remote_description(), candidate); - if (!result.ok()) { - RTC_LOG(LS_ERROR) << "UseCandidate: Invalid candidate. " - << result.error().message(); - return false; - } - std::vector candidates; - candidates.push_back(candidate->candidate()); - // Invoking BaseSession method to handle remote candidates. - RTCError error = transport_controller_->AddRemoteCandidates( - result.value()->name, candidates); - if (error.ok()) { - ReportRemoteIceCandidateAdded(candidate->candidate()); - // Candidates successfully submitted for checking. - if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew || - ice_connection_state_ == - PeerConnectionInterface::kIceConnectionDisconnected) { - // If state is New, then the session has just gotten its first remote ICE - // candidates, so go to Checking. - // If state is Disconnected, the session is re-using old candidates or - // receiving additional ones, so go to Checking. - // If state is Connected, stay Connected. - // TODO(bemasc): If state is Connected, and the new candidates are for a - // newly added transport, then the state actually _should_ move to - // checking. Add a way to distinguish that case. - SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking); - } - // TODO(bemasc): If state is Completed, go back to Connected. - } else { - RTC_LOG(LS_WARNING) << error.message(); - } - return true; -} - -RTCErrorOr TgPeerConnection::FindContentInfo( - const SessionDescriptionInterface* description, - const IceCandidateInterface* candidate) { - if (candidate->sdp_mline_index() >= 0) { - size_t mediacontent_index = - static_cast(candidate->sdp_mline_index()); - size_t content_size = description->description()->contents().size(); - if (mediacontent_index < content_size) { - return &description->description()->contents()[mediacontent_index]; - } else { - return RTCError(RTCErrorType::INVALID_RANGE, - "Media line index (" + - rtc::ToString(candidate->sdp_mline_index()) + - ") out of range (number of mlines: " + - rtc::ToString(content_size) + ")."); - } - } else if (!candidate->sdp_mid().empty()) { - auto& contents = description->description()->contents(); - auto it = absl::c_find_if( - contents, [candidate](const cricket::ContentInfo& content_info) { - return content_info.mid() == candidate->sdp_mid(); - }); - if (it == contents.end()) { - return RTCError( - RTCErrorType::INVALID_PARAMETER, - "Mid " + candidate->sdp_mid() + - " specified but no media section with that mid found."); - } else { - return &*it; - } - } - - return RTCError(RTCErrorType::INVALID_PARAMETER, - "Neither sdp_mline_index nor sdp_mid specified."); -} - -void TgPeerConnection::RemoveUnusedChannels(const SessionDescription* desc) { - // Destroy video channel first since it may have a pointer to the - // voice channel. - const cricket::ContentInfo* video_info = cricket::GetFirstVideoContent(desc); - if (!video_info || video_info->rejected) { - DestroyTransceiverChannel(GetVideoTransceiver()); - } - - const cricket::ContentInfo* audio_info = cricket::GetFirstAudioContent(desc); - if (!audio_info || audio_info->rejected) { - DestroyTransceiverChannel(GetAudioTransceiver()); - } -} - -RTCErrorOr TgPeerConnection::GetEarlyBundleGroup( - const SessionDescription& desc) const { - const cricket::ContentGroup* bundle_group = nullptr; - if (configuration_.bundle_policy == - PeerConnectionInterface::kBundlePolicyMaxBundle) { - bundle_group = desc.GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - if (!bundle_group) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "max-bundle configured but session description " - "has no BUNDLE group"); - } - } - return bundle_group; -} - -RTCError TgPeerConnection::CreateChannels(const SessionDescription& desc) { - // Creating the media channels. Transports should already have been created - // at this point. - const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc); - if (voice && !voice->rejected && - !GetAudioTransceiver()->internal()->channel()) { - cricket::VoiceChannel* voice_channel = CreateVoiceChannel(voice->name); - if (!voice_channel) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create voice channel."); - } - GetAudioTransceiver()->internal()->SetChannel(voice_channel); - } - - const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc); - if (video && !video->rejected && - !GetVideoTransceiver()->internal()->channel()) { - cricket::VideoChannel* video_channel = CreateVideoChannel(video->name); - if (!video_channel) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, - "Failed to create video channel."); - } - GetVideoTransceiver()->internal()->SetChannel(video_channel); - } - - return RTCError::OK(); -} - -// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. -cricket::VoiceChannel* TgPeerConnection::CreateVoiceChannel( - const std::string& mid) { - RtpTransportInternal* rtp_transport = GetRtpTransport(mid); - MediaTransportConfig media_transport_config = - transport_controller_->GetMediaTransportConfig(mid); - - cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel( - call_ptr_, configuration_.media_config, rtp_transport, - media_transport_config, signaling_thread(), mid, SrtpRequired(), - GetCryptoOptions(), &ssrc_generator_, audio_options_); - if (!voice_channel) { - return nullptr; - } - voice_channel->SignalDtlsSrtpSetupFailure.connect( - this, &TgPeerConnection::OnDtlsSrtpSetupFailure); - voice_channel->SignalSentPacket.connect(this, - &TgPeerConnection::OnSentPacket_w); - voice_channel->SetRtpTransport(rtp_transport); - - return voice_channel; -} - -// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver. -cricket::VideoChannel* TgPeerConnection::CreateVideoChannel( - const std::string& mid) { - RtpTransportInternal* rtp_transport = GetRtpTransport(mid); - MediaTransportConfig media_transport_config = - transport_controller_->GetMediaTransportConfig(mid); - - cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel( - call_ptr_, configuration_.media_config, rtp_transport, - media_transport_config, signaling_thread(), mid, SrtpRequired(), - GetCryptoOptions(), &ssrc_generator_, video_options_, - video_bitrate_allocator_factory_.get()); - if (!video_channel) { - return nullptr; - } - video_channel->SignalDtlsSrtpSetupFailure.connect( - this, &TgPeerConnection::OnDtlsSrtpSetupFailure); - video_channel->SignalSentPacket.connect(this, - &TgPeerConnection::OnSentPacket_w); - video_channel->SetRtpTransport(rtp_transport); - - return video_channel; -} - -Call::Stats TgPeerConnection::GetCallStats() { - if (!worker_thread()->IsCurrent()) { - return worker_thread()->Invoke( - RTC_FROM_HERE, rtc::Bind(&TgPeerConnection::GetCallStats, this)); - } - RTC_DCHECK_RUN_ON(worker_thread()); - if (call_) { - return call_->GetStats(); - } else { - return Call::Stats(); - } -} - -// Returns false if bundle is enabled and rtcp_mux is disabled. -bool TgPeerConnection::ValidateBundleSettings(const SessionDescription* desc) { - bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE); - if (!bundle_enabled) - return true; - - const cricket::ContentGroup* bundle_group = - desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE); - RTC_DCHECK(bundle_group != NULL); - - const cricket::ContentInfos& contents = desc->contents(); - for (cricket::ContentInfos::const_iterator citer = contents.begin(); - citer != contents.end(); ++citer) { - const cricket::ContentInfo* content = (&*citer); - RTC_DCHECK(content != NULL); - if (bundle_group->HasContentName(content->name) && !content->rejected && - content->type == MediaProtocolType::kRtp) { - if (!HasRtcpMuxEnabled(content)) - return false; - } - } - // RTCP-MUX is enabled in all the contents. - return true; -} - -bool TgPeerConnection::HasRtcpMuxEnabled(const cricket::ContentInfo* content) { - return content->media_description()->rtcp_mux(); -} - -static RTCError ValidateMids(const cricket::SessionDescription& description) { - std::set mids; - for (const cricket::ContentInfo& content : description.contents()) { - if (content.name.empty()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "A media section is missing a MID attribute."); - } - if (!mids.insert(content.name).second) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Duplicate a=mid value '" + content.name + "'."); - } - } - return RTCError::OK(); -} - -RTCError TgPeerConnection::ValidateSessionDescription( - const SessionDescriptionInterface* sdesc, - cricket::ContentSource source) { - if (session_error() != SessionError::kNone) { - LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, GetSessionErrorMsg()); - } - - if (!sdesc || !sdesc->description()) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, kInvalidSdp); - } - - SdpType type = sdesc->GetType(); - if ((source == cricket::CS_LOCAL && !ExpectSetLocalDescription(type)) || - (source == cricket::CS_REMOTE && !ExpectSetRemoteDescription(type))) { - LOG_AND_RETURN_ERROR( - RTCErrorType::INVALID_STATE, - "Called in wrong state: " + GetSignalingStateString(signaling_state())); - } - - RTCError error = ValidateMids(*sdesc->description()); - if (!error.ok()) { - return error; - } - - // Verify crypto settings. - std::string crypto_error; - - // Verify ice-ufrag and ice-pwd. - if (!VerifyIceUfragPwdPresent(sdesc->description())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kSdpWithoutIceUfragPwd); - } - - if (!ValidateBundleSettings(sdesc->description())) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kBundleWithoutRtcpMux); - } - - // TODO(skvlad): When the local rtcp-mux policy is Require, reject any - // m-lines that do not rtcp-mux enabled. - - // Verify m-lines in Answer when compared against Offer. - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // With an answer we want to compare the new answer session description with - // the offer's session description from the current negotiation. - const cricket::SessionDescription* offer_desc = - (source == cricket::CS_LOCAL) ? remote_description()->description() - : local_description()->description(); - if (!MediaSectionsHaveSameCount(*offer_desc, *sdesc->description()) || - !MediaSectionsInSameOrder(*offer_desc, nullptr, *sdesc->description(), - type)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kMlineMismatchInAnswer); - } - } else { - // The re-offers should respect the order of m= sections in current - // description. See RFC3264 Section 8 paragraph 4 for more details. - // With a re-offer, either the current local or current remote descriptions - // could be the most up to date, so we would like to check against both of - // them if they exist. It could be the case that one of them has a 0 port - // for a media section, but the other does not. This is important to check - // against in the case that we are recycling an m= section. - const cricket::SessionDescription* current_desc = nullptr; - const cricket::SessionDescription* secondary_current_desc = nullptr; - if (local_description()) { - current_desc = local_description()->description(); - if (remote_description()) { - secondary_current_desc = remote_description()->description(); - } - } else if (remote_description()) { - current_desc = remote_description()->description(); - } - if (current_desc && - !MediaSectionsInSameOrder(*current_desc, secondary_current_desc, - *sdesc->description(), type)) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - kMlineMismatchInSubsequentOffer); - } - } - - if (IsUnifiedPlan()) { - // Ensure that each audio and video media section has at most one - // "StreamParams". This will return an error if receiving a session - // description from a "Plan B" endpoint which adds multiple tracks of the - // same type. With Unified Plan, there can only be at most one track per - // media section. - for (const ContentInfo& content : sdesc->description()->contents()) { - const MediaContentDescription& desc = *content.media_description(); - if ((desc.type() == cricket::MEDIA_TYPE_AUDIO || - desc.type() == cricket::MEDIA_TYPE_VIDEO) && - desc.streams().size() > 1u) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, - "Media section has more than one track specified " - "with a=ssrc lines which is not supported with " - "Unified Plan."); - } - } - } - - return RTCError::OK(); -} - -bool TgPeerConnection::ExpectSetLocalDescription(SdpType type) { - PeerConnectionInterface::SignalingState state = signaling_state(); - if (type == SdpType::kOffer) { - return (state == PeerConnectionInterface::kStable) || - (state == PeerConnectionInterface::kHaveLocalOffer); - } else { - RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer); - return (state == PeerConnectionInterface::kHaveRemoteOffer) || - (state == PeerConnectionInterface::kHaveLocalPrAnswer); - } -} - -bool TgPeerConnection::ExpectSetRemoteDescription(SdpType type) { - PeerConnectionInterface::SignalingState state = signaling_state(); - if (type == SdpType::kOffer) { - return (state == PeerConnectionInterface::kStable) || - (state == PeerConnectionInterface::kHaveRemoteOffer); - } else { - RTC_DCHECK(type == SdpType::kPrAnswer || type == SdpType::kAnswer); - return (state == PeerConnectionInterface::kHaveLocalOffer) || - (state == PeerConnectionInterface::kHaveRemotePrAnswer); - } -} - -const char* TgPeerConnection::SessionErrorToString(SessionError error) const { - switch (error) { - case SessionError::kNone: - return "ERROR_NONE"; - case SessionError::kContent: - return "ERROR_CONTENT"; - case SessionError::kTransport: - return "ERROR_TRANSPORT"; - } - RTC_NOTREACHED(); - return ""; -} - -std::string TgPeerConnection::GetSessionErrorMsg() { - rtc::StringBuilder desc; - desc << kSessionError << SessionErrorToString(session_error()) << ". "; - desc << kSessionErrorDesc << session_error_desc() << "."; - return desc.Release(); -} - -void TgPeerConnection::ReportSdpFormatReceived( - const SessionDescriptionInterface& remote_offer) { - int num_audio_mlines = 0; - int num_video_mlines = 0; - int num_audio_tracks = 0; - int num_video_tracks = 0; - for (const ContentInfo& content : remote_offer.description()->contents()) { - cricket::MediaType media_type = content.media_description()->type(); - int num_tracks = std::max( - 1, static_cast(content.media_description()->streams().size())); - if (media_type == cricket::MEDIA_TYPE_AUDIO) { - num_audio_mlines += 1; - num_audio_tracks += num_tracks; - } else if (media_type == cricket::MEDIA_TYPE_VIDEO) { - num_video_mlines += 1; - num_video_tracks += num_tracks; - } - } - SdpFormatReceived format = kSdpFormatReceivedNoTracks; - if (num_audio_mlines > 1 || num_video_mlines > 1) { - format = kSdpFormatReceivedComplexUnifiedPlan; - } else if (num_audio_tracks > 0 || num_video_tracks > 0) { - format = kSdpFormatReceivedSimple; - } - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpFormatReceived", format, - kSdpFormatReceivedMax); -} - -void TgPeerConnection::ReportIceCandidateCollected( - const cricket::Candidate& candidate) { - NoteUsageEvent(UsageEvent::CANDIDATE_COLLECTED); - if (candidate.address().IsPrivateIP()) { - NoteUsageEvent(UsageEvent::PRIVATE_CANDIDATE_COLLECTED); - } - if (candidate.address().IsUnresolvedIP()) { - NoteUsageEvent(UsageEvent::MDNS_CANDIDATE_COLLECTED); - } - if (candidate.address().family() == AF_INET6) { - NoteUsageEvent(UsageEvent::IPV6_CANDIDATE_COLLECTED); - } -} - -void TgPeerConnection::ReportRemoteIceCandidateAdded( - const cricket::Candidate& candidate) { - NoteUsageEvent(UsageEvent::REMOTE_CANDIDATE_ADDED); - if (candidate.address().IsPrivateIP()) { - NoteUsageEvent(UsageEvent::REMOTE_PRIVATE_CANDIDATE_ADDED); - } - if (candidate.address().IsUnresolvedIP()) { - NoteUsageEvent(UsageEvent::REMOTE_MDNS_CANDIDATE_ADDED); - } - if (candidate.address().family() == AF_INET6) { - NoteUsageEvent(UsageEvent::REMOTE_IPV6_CANDIDATE_ADDED); - } -} - -void TgPeerConnection::NoteUsageEvent(UsageEvent event) { - RTC_DCHECK_RUN_ON(signaling_thread()); - usage_event_accumulator_ |= static_cast(event); -} - -void TgPeerConnection::ReportUsagePattern() const { - RTC_DLOG(LS_INFO) << "Usage signature is " << usage_event_accumulator_; - RTC_HISTOGRAM_ENUMERATION_SPARSE("WebRTC.TgPeerConnection.UsagePattern", - usage_event_accumulator_, - static_cast(UsageEvent::MAX_VALUE)); - const int bad_bits = - static_cast(UsageEvent::SET_LOCAL_DESCRIPTION_SUCCEEDED) | - static_cast(UsageEvent::CANDIDATE_COLLECTED); - const int good_bits = - static_cast(UsageEvent::SET_REMOTE_DESCRIPTION_SUCCEEDED) | - static_cast(UsageEvent::REMOTE_CANDIDATE_ADDED) | - static_cast(UsageEvent::ICE_STATE_CONNECTED); - if ((usage_event_accumulator_ & bad_bits) == bad_bits && - (usage_event_accumulator_ & good_bits) == 0) { - // If called after close(), we can't report, because observer may have - // been deallocated, and therefore pointer is null. Write to log instead. - if (observer_) { - Observer()->OnInterestingUsage(usage_event_accumulator_); - } else { - RTC_LOG(LS_INFO) << "Interesting usage signature " - << usage_event_accumulator_ - << " observed after observer shutdown"; - } - } -} - -void TgPeerConnection::ReportNegotiatedSdpSemantics( - const SessionDescriptionInterface& answer) { - SdpSemanticNegotiated semantics_negotiated; - switch (answer.description()->msid_signaling()) { - case 0: - semantics_negotiated = kSdpSemanticNegotiatedNone; - break; - case cricket::kMsidSignalingMediaSection: - semantics_negotiated = kSdpSemanticNegotiatedUnifiedPlan; - break; - case cricket::kMsidSignalingSsrcAttribute: - semantics_negotiated = kSdpSemanticNegotiatedPlanB; - break; - case cricket::kMsidSignalingMediaSection | - cricket::kMsidSignalingSsrcAttribute: - semantics_negotiated = kSdpSemanticNegotiatedMixed; - break; - default: - RTC_NOTREACHED(); - } - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.SdpSemanticNegotiated", - semantics_negotiated, kSdpSemanticNegotiatedMax); -} - -// We need to check the local/remote description for the Transport instead of -// the session, because a new Transport added during renegotiation may have -// them unset while the session has them set from the previous negotiation. -// Not doing so may trigger the auto generation of transport description and -// mess up DTLS identity information, ICE credential, etc. -bool TgPeerConnection::ReadyToUseRemoteCandidate( - const IceCandidateInterface* candidate, - const SessionDescriptionInterface* remote_desc, - bool* valid) { - *valid = true; - - const SessionDescriptionInterface* current_remote_desc = - remote_desc ? remote_desc : remote_description(); - - if (!current_remote_desc) { - return false; - } - - RTCErrorOr result = - FindContentInfo(current_remote_desc, candidate); - if (!result.ok()) { - RTC_LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate. " - << result.error().message(); - - *valid = false; - return false; - } - - std::string transport_name = GetTransportName(result.value()->name); - return !transport_name.empty(); -} - -bool TgPeerConnection::SrtpRequired() const { - return !use_datagram_transport_ && - (dtls_enabled_ || - webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED); -} - -void TgPeerConnection::OnTransportControllerGatheringState( - cricket::IceGatheringState state) { - RTC_DCHECK(signaling_thread()->IsCurrent()); - if (state == cricket::kIceGatheringGathering) { - OnIceGatheringChange(PeerConnectionInterface::kIceGatheringGathering); - } else if (state == cricket::kIceGatheringComplete) { - OnIceGatheringChange(PeerConnectionInterface::kIceGatheringComplete); - } -} - -void TgPeerConnection::ReportTransportStats() { - std::map> - media_types_by_transport_name; - for (const auto& transceiver : transceivers_) { - if (transceiver->internal()->channel()) { - const std::string& transport_name = - transceiver->internal()->channel()->transport_name(); - media_types_by_transport_name[transport_name].insert( - transceiver->media_type()); - } - } - if (rtp_data_channel()) { - media_types_by_transport_name[rtp_data_channel()->transport_name()].insert( - cricket::MEDIA_TYPE_DATA); - } - - absl::optional transport_name = sctp_transport_name(); - if (transport_name) { - media_types_by_transport_name[*transport_name].insert( - cricket::MEDIA_TYPE_DATA); - } - - for (const auto& entry : media_types_by_transport_name) { - const std::string& transport_name = entry.first; - const std::set media_types = entry.second; - cricket::TransportStats stats; - if (transport_controller_->GetStats(transport_name, &stats)) { - ReportBestConnectionState(stats); - ReportNegotiatedCiphers(stats, media_types); - } - } -} -// Walk through the ConnectionInfos to gather best connection usage -// for IPv4 and IPv6. -void TgPeerConnection::ReportBestConnectionState( - const cricket::TransportStats& stats) { - for (const cricket::TransportChannelStats& channel_stats : - stats.channel_stats) { - for (const cricket::ConnectionInfo& connection_info : - channel_stats.ice_transport_stats.connection_infos) { - if (!connection_info.best_connection) { - continue; - } - - const cricket::Candidate& local = connection_info.local_candidate; - const cricket::Candidate& remote = connection_info.remote_candidate; - - // Increment the counter for IceCandidatePairType. - if (local.protocol() == cricket::TCP_PROTOCOL_NAME || - (local.type() == RELAY_PORT_TYPE && - local.relay_protocol() == cricket::TCP_PROTOCOL_NAME)) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.CandidatePairType_TCP", - GetIceCandidatePairCounter(local, remote), - kIceCandidatePairMax); - } else if (local.protocol() == cricket::UDP_PROTOCOL_NAME) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.CandidatePairType_UDP", - GetIceCandidatePairCounter(local, remote), - kIceCandidatePairMax); - } else { - RTC_CHECK(0); - } - - // Increment the counter for IP type. - if (local.address().family() == AF_INET) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", - kBestConnections_IPv4, - kPeerConnectionAddressFamilyCounter_Max); - } else if (local.address().family() == AF_INET6) { - RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.IPMetrics", - kBestConnections_IPv6, - kPeerConnectionAddressFamilyCounter_Max); - } else { - RTC_CHECK(!local.address().hostname().empty() && - local.address().IsUnresolvedIP()); - } - - return; - } - } -} - -void TgPeerConnection::ReportNegotiatedCiphers( - const cricket::TransportStats& stats, - const std::set& media_types) { - if (!dtls_enabled_ || stats.channel_stats.empty()) { - return; - } - - int srtp_crypto_suite = stats.channel_stats[0].srtp_crypto_suite; - int ssl_cipher_suite = stats.channel_stats[0].ssl_cipher_suite; - if (srtp_crypto_suite == rtc::SRTP_INVALID_CRYPTO_SUITE && - ssl_cipher_suite == rtc::TLS_NULL_WITH_NULL_NULL) { - return; - } - - if (srtp_crypto_suite != rtc::SRTP_INVALID_CRYPTO_SUITE) { - for (cricket::MediaType media_type : media_types) { - switch (media_type) { - case cricket::MEDIA_TYPE_AUDIO: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SrtpCryptoSuite.Audio", srtp_crypto_suite, - rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); - break; - case cricket::MEDIA_TYPE_VIDEO: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SrtpCryptoSuite.Video", srtp_crypto_suite, - rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); - break; - case cricket::MEDIA_TYPE_DATA: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SrtpCryptoSuite.Data", srtp_crypto_suite, - rtc::SRTP_CRYPTO_SUITE_MAX_VALUE); - break; - default: - RTC_NOTREACHED(); - continue; - } - } - } - - if (ssl_cipher_suite != rtc::TLS_NULL_WITH_NULL_NULL) { - for (cricket::MediaType media_type : media_types) { - switch (media_type) { - case cricket::MEDIA_TYPE_AUDIO: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SslCipherSuite.Audio", ssl_cipher_suite, - rtc::SSL_CIPHER_SUITE_MAX_VALUE); - break; - case cricket::MEDIA_TYPE_VIDEO: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SslCipherSuite.Video", ssl_cipher_suite, - rtc::SSL_CIPHER_SUITE_MAX_VALUE); - break; - case cricket::MEDIA_TYPE_DATA: - RTC_HISTOGRAM_ENUMERATION_SPARSE( - "WebRTC.PeerConnection.SslCipherSuite.Data", ssl_cipher_suite, - rtc::SSL_CIPHER_SUITE_MAX_VALUE); - break; - default: - RTC_NOTREACHED(); - continue; - } - } - } -} - -void TgPeerConnection::OnSentPacket_w(const rtc::SentPacket& sent_packet) { - RTC_DCHECK_RUN_ON(worker_thread()); - RTC_DCHECK(call_); - call_->OnSentPacket(sent_packet); -} - -const std::string TgPeerConnection::GetTransportName( - const std::string& content_name) { - cricket::ChannelInterface* channel = GetChannel(content_name); - if (channel) { - return channel->transport_name(); - } - // Return an empty string if failed to retrieve the transport name. - return ""; -} - -void TgPeerConnection::DestroyTransceiverChannel( - rtc::scoped_refptr> - transceiver) { - RTC_DCHECK(transceiver); - - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel) { - transceiver->internal()->SetChannel(nullptr); - DestroyChannelInterface(channel); - } -} - -void TgPeerConnection::DestroyChannelInterface( - cricket::ChannelInterface* channel) { - RTC_DCHECK(channel); - switch (channel->media_type()) { - case cricket::MEDIA_TYPE_AUDIO: - channel_manager()->DestroyVoiceChannel( - static_cast(channel)); - break; - case cricket::MEDIA_TYPE_VIDEO: - channel_manager()->DestroyVideoChannel( - static_cast(channel)); - break; - case cricket::MEDIA_TYPE_DATA: - channel_manager()->DestroyRtpDataChannel( - static_cast(channel)); - break; - default: - RTC_NOTREACHED() << "Unknown media type: " << channel->media_type(); - break; - } -} - -bool TgPeerConnection::OnTransportChanged( - const std::string& mid, - RtpTransportInternal* rtp_transport, - rtc::scoped_refptr dtls_transport, - DataChannelTransportInterface* data_channel_transport) { - RTC_DCHECK_RUN_ON(network_thread()); - bool ret = true; - auto base_channel = GetChannel(mid); - if (base_channel) { - ret = base_channel->SetRtpTransport(rtp_transport); - } - return ret; -} - -void TgPeerConnection::OnSetStreams() { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (IsUnifiedPlan()) - UpdateNegotiationNeeded(); -} - -PeerConnectionObserver* TgPeerConnection::Observer() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(observer_); - return observer_; -} - -CryptoOptions TgPeerConnection::GetCryptoOptions() { - // TODO(bugs.webrtc.org/9891) - Remove PeerConnectionFactory::CryptoOptions - // after it has been removed. - return configuration_.crypto_options.has_value() - ? *configuration_.crypto_options - : factory_->options().crypto_options; -} - -void TgPeerConnection::ClearStatsCache() { -} - -void TgPeerConnection::RequestUsagePatternReportForTesting() { - signaling_thread()->Post(RTC_FROM_HERE, this, MSG_REPORT_USAGE_PATTERN, - nullptr); -} - -void TgPeerConnection::UpdateNegotiationNeeded() { - RTC_DCHECK_RUN_ON(signaling_thread()); - if (!IsUnifiedPlan()) { - Observer()->OnRenegotiationNeeded(); - return; - } - - // If connection's [[IsClosed]] slot is true, abort these steps. - if (IsClosed()) - return; - - // If connection's signaling state is not "stable", abort these steps. - if (signaling_state() != PeerConnectionInterface::kStable) - return; - - // NOTE - // The negotiation-needed flag will be updated once the state transitions to - // "stable", as part of the steps for setting an RTCSessionDescription. - - // If the result of checking if negotiation is needed is false, clear the - // negotiation-needed flag by setting connection's [[NegotiationNeeded]] slot - // to false, and abort these steps. - bool is_negotiation_needed = CheckIfNegotiationIsNeeded(); - if (!is_negotiation_needed) { - is_negotiation_needed_ = false; - return; - } - - // If connection's [[NegotiationNeeded]] slot is already true, abort these - // steps. - if (is_negotiation_needed_) - return; - - // Set connection's [[NegotiationNeeded]] slot to true. - is_negotiation_needed_ = true; - - // Queue a task that runs the following steps: - // If connection's [[IsClosed]] slot is true, abort these steps. - // If connection's [[NegotiationNeeded]] slot is false, abort these steps. - // Fire an event named negotiationneeded at connection. - Observer()->OnRenegotiationNeeded(); -} - -bool TgPeerConnection::CheckIfNegotiationIsNeeded() { - RTC_DCHECK_RUN_ON(signaling_thread()); - // 1. If any implementation-specific negotiation is required, as described at - // the start of this section, return true. - - // 2. If connection's [[RestartIce]] internal slot is true, return true. - if (local_ice_credentials_to_replace_->HasIceCredentials()) { - return true; - } - - // 3. Let description be connection.[[CurrentLocalDescription]]. - const SessionDescriptionInterface* description = current_local_description(); - if (!description) - return true; - - // 5. For each transceiver in connection's set of transceivers, perform the - // following checks: - for (const auto& transceiver : transceivers_) { - const ContentInfo* current_local_msection = - FindTransceiverMSection(transceiver.get(), description); - - const ContentInfo* current_remote_msection = FindTransceiverMSection( - transceiver.get(), current_remote_description()); - - // 5.3 If transceiver is stopped and is associated with an m= section, - // but the associated m= section is not yet rejected in - // connection.[[CurrentLocalDescription]] or - // connection.[[CurrentRemoteDescription]], return true. - if (transceiver->stopped()) { - if (current_local_msection && !current_local_msection->rejected && - ((current_remote_msection && !current_remote_msection->rejected) || - !current_remote_msection)) { - return true; - } - continue; - } - - // 5.1 If transceiver isn't stopped and isn't yet associated with an m= - // section in description, return true. - if (!current_local_msection) - return true; - - const MediaContentDescription* current_local_media_description = - current_local_msection->media_description(); - // 5.2 If transceiver isn't stopped and is associated with an m= section - // in description then perform the following checks: - - // 5.2.1 If transceiver.[[Direction]] is "sendrecv" or "sendonly", and the - // associated m= section in description either doesn't contain a single - // "a=msid" line, or the number of MSIDs from the "a=msid" lines in this - // m= section, or the MSID values themselves, differ from what is in - // transceiver.sender.[[AssociatedMediaStreamIds]], return true. - if (RtpTransceiverDirectionHasSend(transceiver->direction())) { - if (current_local_media_description->streams().size() == 0) - return true; - - std::vector msection_msids; - for (const auto& stream : current_local_media_description->streams()) { - for (const std::string& msid : stream.stream_ids()) - msection_msids.push_back(msid); - } - - std::vector transceiver_msids = - transceiver->sender()->stream_ids(); - if (msection_msids.size() != transceiver_msids.size()) - return true; - - absl::c_sort(transceiver_msids); - absl::c_sort(msection_msids); - if (transceiver_msids != msection_msids) - return true; - } - - // 5.2.2 If description is of type "offer", and the direction of the - // associated m= section in neither connection.[[CurrentLocalDescription]] - // nor connection.[[CurrentRemoteDescription]] matches - // transceiver.[[Direction]], return true. - if (description->GetType() == SdpType::kOffer) { - if (!current_remote_description()) - return true; - - if (!current_remote_msection) - return true; - - RtpTransceiverDirection current_local_direction = - current_local_media_description->direction(); - RtpTransceiverDirection current_remote_direction = - current_remote_msection->media_description()->direction(); - if (transceiver->direction() != current_local_direction && - transceiver->direction() != - RtpTransceiverDirectionReversed(current_remote_direction)) { - return true; - } - } - - // 5.2.3 If description is of type "answer", and the direction of the - // associated m= section in the description does not match - // transceiver.[[Direction]] intersected with the offered direction (as - // described in [JSEP] (section 5.3.1.)), return true. - if (description->GetType() == SdpType::kAnswer) { - if (!remote_description()) - return true; - - const ContentInfo* offered_remote_msection = - FindTransceiverMSection(transceiver.get(), remote_description()); - - RtpTransceiverDirection offered_direction = - offered_remote_msection - ? offered_remote_msection->media_description()->direction() - : RtpTransceiverDirection::kInactive; - - if (current_local_media_description->direction() != - (RtpTransceiverDirectionIntersection( - transceiver->direction(), - RtpTransceiverDirectionReversed(offered_direction)))) { - return true; - } - } - } - - // If all the preceding checks were performed and true was not returned, - // nothing remains to be negotiated; return false. - return false; -} - -RTCError TgPeerConnection::Rollback(SdpType sdp_type) { - auto state = signaling_state(); - if (state != PeerConnectionInterface::kHaveLocalOffer && - state != PeerConnectionInterface::kHaveRemoteOffer) { - return RTCError(RTCErrorType::INVALID_STATE, - "Called in wrong signalingState: " + - GetSignalingStateString(signaling_state())); - } - RTC_DCHECK_RUN_ON(signaling_thread()); - RTC_DCHECK(IsUnifiedPlan()); - std::vector mids; - std::vector> all_added_streams; - std::vector> all_removed_streams; - std::vector> removed_receivers; - - for (auto&& transceivers_stable_state_pair : - transceiver_stable_states_by_transceivers_) { - auto transceiver = transceivers_stable_state_pair.first; - auto state = transceivers_stable_state_pair.second; - - if (state.remote_stream_ids()) { - std::vector> added_streams; - std::vector> removed_streams; - SetAssociatedRemoteStreams(transceiver->internal()->receiver_internal(), - state.remote_stream_ids().value(), - &added_streams, &removed_streams); - all_added_streams.insert(all_added_streams.end(), added_streams.begin(), - added_streams.end()); - all_removed_streams.insert(all_removed_streams.end(), - removed_streams.begin(), - removed_streams.end()); - if (!state.has_m_section() && !state.newly_created()) { - continue; - } - } - - RTC_DCHECK(transceiver->internal()->mid().has_value()); - std::string mid = transceiver->internal()->mid().value(); - mids.push_back(mid); - DestroyTransceiverChannel(transceiver); - - if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer && - transceiver->receiver()) { - removed_receivers.push_back(transceiver->receiver()); - } - if (state.newly_created()) { - if (transceiver->internal()->reused_for_addtrack()) { - transceiver->internal()->set_created_by_addtrack(true); - } else { - int remaining_transceiver_count = 0; - for (auto&& t : transceivers_) { - if (t != transceiver) { - transceivers_[remaining_transceiver_count++] = t; - } - } - transceivers_.resize(remaining_transceiver_count); - } - } - transceiver->internal()->sender_internal()->set_transport(nullptr); - transceiver->internal()->receiver_internal()->set_transport(nullptr); - transceiver->internal()->set_mid(state.mid()); - transceiver->internal()->set_mline_index(state.mline_index()); - } - transport_controller_->RollbackTransportForMids(mids); - transceiver_stable_states_by_transceivers_.clear(); - pending_local_description_.reset(); - pending_remote_description_.reset(); - ChangeSignalingState(PeerConnectionInterface::kStable); - - // Once all processing has finished, fire off callbacks. - for (const auto& receiver : removed_receivers) { - Observer()->OnRemoveTrack(receiver); - } - for (const auto& stream : all_added_streams) { - Observer()->OnAddStream(stream); - } - for (const auto& stream : all_removed_streams) { - Observer()->OnRemoveStream(stream); - } - - // The assumption is that in case of implicit rollback UpdateNegotiationNeeded - // gets called in SetRemoteDescription. - if (sdp_type == SdpType::kRollback) { - UpdateNegotiationNeeded(); - if (is_negotiation_needed_) { - Observer()->OnRenegotiationNeeded(); - } - } - return RTCError::OK(); -} - -} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h deleted file mode 100644 index e362101034..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection.h +++ /dev/null @@ -1,1735 +0,0 @@ -/* - * Copyright 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TG_PC_PEER_CONNECTION_H_ -#define TG_PC_PEER_CONNECTION_H_ - -#include -#include -#include -#include -#include -#include - -#include "api/peer_connection_interface.h" -#include "api/transport/data_channel_transport_interface.h" -#include "api/turn_customizer.h" -#include "pc/data_channel_controller.h" -#include "pc/ice_server_parsing.h" -#include "pc/peer_connection_factory.h" -#include "pc/peer_connection_internal.h" -#include "pc/rtc_stats_collector.h" -#include "pc/rtp_sender.h" -#include "pc/rtp_transceiver.h" -#include "pc/sctp_transport.h" -#include "pc/stats_collector.h" -#include "pc/stream_collection.h" -#include "tg_webrtc_session_description_factory.h" -#include "rtc_base/experiments/field_trial_parser.h" -#include "rtc_base/operations_chain.h" -#include "rtc_base/race_checker.h" -#include "rtc_base/unique_id_generator.h" -#include "rtc_base/weak_ptr.h" - -#include "tg_jsep_transport_controller.h" -#include "tg_peer_connection_factory.h" -#include "tg_webrtc_session_description_factory.h" -#include "tg_rtp_sender.h" - -namespace webrtc { - -class MediaStreamObserver; -class VideoRtpReceiver; -class RtcEventLog; - -// TgPeerConnection is the implementation of the TgPeerConnection object as defined -// by the PeerConnectionInterface API surface. -// The class currently is solely responsible for the following: -// - Managing the session state machine (signaling state). -// - Creating and initializing lower-level objects, like PortAllocator and -// BaseChannels. -// - Owning and managing the life cycle of the RtpSender/RtpReceiver and track -// objects. -// - Tracking the current and pending local/remote session descriptions. -// The class currently is jointly responsible for the following: -// - Parsing and interpreting SDP. -// - Generating offers and answers based on the current state. -// - The ICE state machine. -// - Generating stats. - -class RTC_EXPORT TgPeerConnectionInterface : public rtc::RefCountInterface { - public: - // Accessor methods to active local streams. - // This method is not supported with kUnifiedPlan semantics. Please use - // GetSenders() instead. - virtual rtc::scoped_refptr local_streams() = 0; - - // Accessor methods to remote streams. - // This method is not supported with kUnifiedPlan semantics. Please use - // GetReceivers() instead. - virtual rtc::scoped_refptr remote_streams() = 0; - - // Add a new MediaStream to be sent on this PeerConnection. - // Note that a SessionDescription negotiation is needed before the - // remote peer can receive the stream. - // - // This has been removed from the standard in favor of a track-based API. So, - // this is equivalent to simply calling AddTrack for each track within the - // stream, with the one difference that if "stream->AddTrack(...)" is called - // later, the PeerConnection will automatically pick up the new track. Though - // this functionality will be deprecated in the future. - // - // This method is not supported with kUnifiedPlan semantics. Please use - // AddTrack instead. - virtual bool AddStream(MediaStreamInterface* stream) = 0; - - // Remove a MediaStream from this PeerConnection. - // Note that a SessionDescription negotiation is needed before the - // remote peer is notified. - // - // This method is not supported with kUnifiedPlan semantics. Please use - // RemoveTrack instead. - virtual void RemoveStream(MediaStreamInterface* stream) = 0; - - // Add a new MediaStreamTrack to be sent on this PeerConnection, and return - // the newly created RtpSender. The RtpSender will be associated with the - // streams specified in the |stream_ids| list. - // - // Errors: - // - INVALID_PARAMETER: |track| is null, has a kind other than audio or video, - // or a sender already exists for the track. - // - INVALID_STATE: The PeerConnection is closed. - virtual RTCErrorOr> AddTrack( - rtc::scoped_refptr track, - const std::vector& stream_ids) = 0; - - // Remove an RtpSender from this PeerConnection. - // Returns true on success. - // TODO(steveanton): Replace with signature that returns RTCError. - virtual bool RemoveTrack(RtpSenderInterface* sender) = 0; - - // Plan B semantics: Removes the RtpSender from this PeerConnection. - // Unified Plan semantics: Stop sending on the RtpSender and mark the - // corresponding RtpTransceiver direction as no longer sending. - // - // Errors: - // - INVALID_PARAMETER: |sender| is null or (Plan B only) the sender is not - // associated with this PeerConnection. - // - INVALID_STATE: PeerConnection is closed. - // TODO(bugs.webrtc.org/9534): Rename to RemoveTrack once the other signature - // is removed. - virtual RTCError RemoveTrackNew( - rtc::scoped_refptr sender); - - // AddTransceiver creates a new RtpTransceiver and adds it to the set of - // transceivers. Adding a transceiver will cause future calls to CreateOffer - // to add a media description for the corresponding transceiver. - // - // The initial value of |mid| in the returned transceiver is null. Setting a - // new session description may change it to a non-null value. - // - // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver - // - // Optionally, an RtpTransceiverInit structure can be specified to configure - // the transceiver from construction. If not specified, the transceiver will - // default to having a direction of kSendRecv and not be part of any streams. - // - // These methods are only available when Unified Plan is enabled (see - // RTCConfiguration). - // - // Common errors: - // - INTERNAL_ERROR: The configuration does not have Unified Plan enabled. - - // Adds a transceiver with a sender set to transmit the given track. The kind - // of the transceiver (and sender/receiver) will be derived from the kind of - // the track. - // Errors: - // - INVALID_PARAMETER: |track| is null. - virtual RTCErrorOr> - AddTransceiver(rtc::scoped_refptr track) = 0; - virtual RTCErrorOr> - AddTransceiver(rtc::scoped_refptr track, - const RtpTransceiverInit& init) = 0; - - // Adds a transceiver with the given kind. Can either be MEDIA_TYPE_AUDIO or - // MEDIA_TYPE_VIDEO. - // Errors: - // - INVALID_PARAMETER: |media_type| is not MEDIA_TYPE_AUDIO or - // MEDIA_TYPE_VIDEO. - virtual RTCErrorOr> - AddTransceiver(cricket::MediaType media_type) = 0; - virtual RTCErrorOr> - AddTransceiver(cricket::MediaType media_type, - const RtpTransceiverInit& init) = 0; - - // Creates a sender without a track. Can be used for "early media"/"warmup" - // use cases, where the application may want to negotiate video attributes - // before a track is available to send. - // - // The standard way to do this would be through "addTransceiver", but we - // don't support that API yet. - // - // |kind| must be "audio" or "video". - // - // |stream_id| is used to populate the msid attribute; if empty, one will - // be generated automatically. - // - // This method is not supported with kUnifiedPlan semantics. Please use - // AddTransceiver instead. - virtual rtc::scoped_refptr CreateSender( - const std::string& kind, - const std::string& stream_id) = 0; - - // If Plan B semantics are specified, gets all RtpSenders, created either - // through AddStream, AddTrack, or CreateSender. All senders of a specific - // media type share the same media description. - // - // If Unified Plan semantics are specified, gets the RtpSender for each - // RtpTransceiver. - virtual std::vector> GetSenders() - const = 0; - - // If Plan B semantics are specified, gets all RtpReceivers created when a - // remote description is applied. All receivers of a specific media type share - // the same media description. It is also possible to have a media description - // with no associated RtpReceivers, if the directional attribute does not - // indicate that the remote peer is sending any media. - // - // If Unified Plan semantics are specified, gets the RtpReceiver for each - // RtpTransceiver. - virtual std::vector> GetReceivers() - const = 0; - - // Get all RtpTransceivers, created either through AddTransceiver, AddTrack or - // by a remote description applied with SetRemoteDescription. - // - // Note: This method is only available when Unified Plan is enabled (see - // RTCConfiguration). - virtual std::vector> - GetTransceivers() const = 0; - - - // Clear cached stats in the RTCStatsCollector. - // Exposed for testing while waiting for automatic cache clear to work. - // https://bugs.webrtc.org/8693 - virtual void ClearStatsCache() {} - - // Create a data channel with the provided config, or default config if none - // is provided. Note that an offer/answer negotiation is still necessary - // before the data channel can be used. - // - // Also, calling CreateDataChannel is the only way to get a data "m=" section - // in SDP, so it should be done before CreateOffer is called, if the - // application plans to use data channels. - virtual rtc::scoped_refptr CreateDataChannel( - const std::string& label, - const DataChannelInit* config) = 0; - - // Returns the more recently applied description; "pending" if it exists, and - // otherwise "current". See below. - virtual const SessionDescriptionInterface* local_description() const = 0; - virtual const SessionDescriptionInterface* remote_description() const = 0; - - // A "current" description the one currently negotiated from a complete - // offer/answer exchange. - virtual const SessionDescriptionInterface* current_local_description() - const = 0; - virtual const SessionDescriptionInterface* current_remote_description() - const = 0; - - // A "pending" description is one that's part of an incomplete offer/answer - // exchange (thus, either an offer or a pranswer). Once the offer/answer - // exchange is finished, the "pending" description will become "current". - virtual const SessionDescriptionInterface* pending_local_description() - const = 0; - virtual const SessionDescriptionInterface* pending_remote_description() - const = 0; - - // Tells the PeerConnection that ICE should be restarted. This triggers a need - // for negotiation and subsequent CreateOffer() calls will act as if - // RTCOfferAnswerOptions::ice_restart is true. - // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-restartice - // TODO(hbos): Remove default implementation when downstream projects - // implement this. - virtual void RestartIce() = 0; - - // Create a new offer. - // The CreateSessionDescriptionObserver callback will be called when done. - virtual void CreateOffer(CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options) = 0; - - // Create an answer to an offer. - // The CreateSessionDescriptionObserver callback will be called when done. - virtual void CreateAnswer(CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options) = 0; - - // Sets the local session description. - // The PeerConnection takes the ownership of |desc| even if it fails. - // The |observer| callback will be called when done. - // TODO(deadbeef): Change |desc| to be a unique_ptr, to make it clear - // that this method always takes ownership of it. - virtual void SetLocalDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc) = 0; - // Implicitly creates an offer or answer (depending on the current signaling - // state) and performs SetLocalDescription() with the newly generated session - // description. - // TODO(hbos): Make pure virtual when implemented by downstream projects. - virtual void SetLocalDescription(SetSessionDescriptionObserver* observer) {} - // Sets the remote session description. - // The PeerConnection takes the ownership of |desc| even if it fails. - // The |observer| callback will be called when done. - // TODO(hbos): Remove when Chrome implements the new signature. - virtual void SetRemoteDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc) {} - virtual void SetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer) = 0; - - virtual PeerConnectionInterface::RTCConfiguration GetConfiguration() = 0; - - // Sets the PeerConnection's global configuration to |config|. - // - // The members of |config| that may be changed are |type|, |servers|, - // |ice_candidate_pool_size| and |prune_turn_ports| (though the candidate - // pool size can't be changed after the first call to SetLocalDescription). - // Note that this means the BUNDLE and RTCP-multiplexing policies cannot be - // changed with this method. - // - // Any changes to STUN/TURN servers or ICE candidate policy will affect the - // next gathering phase, and cause the next call to createOffer to generate - // new ICE credentials, as described in JSEP. This also occurs when - // |prune_turn_ports| changes, for the same reasoning. - // - // If an error occurs, returns false and populates |error| if non-null: - // - INVALID_MODIFICATION if |config| contains a modified parameter other - // than one of the parameters listed above. - // - INVALID_RANGE if |ice_candidate_pool_size| is out of range. - // - SYNTAX_ERROR if parsing an ICE server URL failed. - // - INVALID_PARAMETER if a TURN server is missing |username| or |password|. - // - INTERNAL_ERROR if an unexpected error occurred. - // - // TODO(nisse): Make this pure virtual once all Chrome subclasses of - // PeerConnectionInterface implement it. - virtual RTCError SetConfiguration( - const PeerConnectionInterface::RTCConfiguration& config); - - // Provides a remote candidate to the ICE Agent. - // A copy of the |candidate| will be created and added to the remote - // description. So the caller of this method still has the ownership of the - // |candidate|. - // TODO(hbos): The spec mandates chaining this operation onto the operations - // chain; deprecate and remove this version in favor of the callback-based - // signature. - virtual bool AddIceCandidate(const IceCandidateInterface* candidate) = 0; - // TODO(hbos): Remove default implementation once implemented by downstream - // projects. - virtual void AddIceCandidate(std::unique_ptr candidate, - std::function callback) {} - - // Removes a group of remote candidates from the ICE agent. Needed mainly for - // continual gathering, to avoid an ever-growing list of candidates as - // networks come and go. - virtual bool RemoveIceCandidates( - const std::vector& candidates) = 0; - - // SetBitrate limits the bandwidth allocated for all RTP streams sent by - // this PeerConnection. Other limitations might affect these limits and - // are respected (for example "b=AS" in SDP). - // - // Setting |current_bitrate_bps| will reset the current bitrate estimate - // to the provided value. - virtual RTCError SetBitrate(const BitrateSettings& bitrate); - - // TODO(nisse): Deprecated - use version above. These two default - // implementations require subclasses to implement one or the other - // of the methods. - virtual RTCError SetBitrate(const PeerConnectionInterface::BitrateParameters& bitrate_parameters); - - // Enable/disable playout of received audio streams. Enabled by default. Note - // that even if playout is enabled, streams will only be played out if the - // appropriate SDP is also applied. Setting |playout| to false will stop - // playout of the underlying audio device but starts a task which will poll - // for audio data every 10ms to ensure that audio processing happens and the - // audio statistics are updated. - // TODO(henrika): deprecate and remove this. - virtual void SetAudioPlayout(bool playout) {} - - // Enable/disable recording of transmitted audio streams. Enabled by default. - // Note that even if recording is enabled, streams will only be recorded if - // the appropriate SDP is also applied. - // TODO(henrika): deprecate and remove this. - virtual void SetAudioRecording(bool recording) {} - - // Looks up the DtlsTransport associated with a MID value. - // In the Javascript API, DtlsTransport is a property of a sender, but - // because the PeerConnection owns the DtlsTransport in this implementation, - // it is better to look them up on the PeerConnection. - virtual rtc::scoped_refptr LookupDtlsTransportByMid( - const std::string& mid) = 0; - - // Returns the SCTP transport, if any. - virtual rtc::scoped_refptr GetSctpTransport() - const = 0; - - // Returns the current SignalingState. - virtual PeerConnectionInterface::SignalingState signaling_state() = 0; - - // Returns an aggregate state of all ICE *and* DTLS transports. - // This is left in place to avoid breaking native clients who expect our old, - // nonstandard behavior. - // TODO(jonasolsson): deprecate and remove this. - virtual PeerConnectionInterface::IceConnectionState ice_connection_state() = 0; - - // Returns an aggregated state of all ICE transports. - virtual PeerConnectionInterface::IceConnectionState standardized_ice_connection_state() = 0; - - // Returns an aggregated state of all ICE and DTLS transports. - virtual PeerConnectionInterface::PeerConnectionState peer_connection_state() = 0; - - virtual PeerConnectionInterface::IceGatheringState ice_gathering_state() = 0; - - // Start RtcEventLog using an existing output-sink. Takes ownership of - // |output| and passes it on to Call, which will take the ownership. If the - // operation fails the output will be closed and deallocated. The event log - // will send serialized events to the output object every |output_period_ms|. - // Applications using the event log should generally make their own trade-off - // regarding the output period. A long period is generally more efficient, - // with potential drawbacks being more bursty thread usage, and more events - // lost in case the application crashes. If the |output_period_ms| argument is - // omitted, webrtc selects a default deemed to be workable in most cases. - virtual bool StartRtcEventLog(std::unique_ptr output, - int64_t output_period_ms) = 0; - virtual bool StartRtcEventLog(std::unique_ptr output) = 0; - - // Stops logging the RtcEventLog. - virtual void StopRtcEventLog() = 0; - - // Terminates all media, closes the transports, and in general releases any - // resources used by the PeerConnection. This is an irreversible operation. - // - // Note that after this method completes, the PeerConnection will no longer - // use the PeerConnectionObserver interface passed in on construction, and - // thus the observer object can be safely destroyed. - virtual void Close() = 0; - - protected: - // Dtor protected as objects shouldn't be deleted via this interface. - ~TgPeerConnectionInterface() override = default; -}; - -class TgPeerConnection : public TgPeerConnectionInterface, -public TgJsepTransportController::Observer, -public RtpSenderBase::SetStreamsObserver, -public rtc::MessageHandler, -public sigslot::has_slots<> { - public: - // A bit in the usage pattern is registered when its defining event occurs at - // least once. - enum class UsageEvent : int { - TURN_SERVER_ADDED = 0x01, - STUN_SERVER_ADDED = 0x02, - DATA_ADDED = 0x04, - AUDIO_ADDED = 0x08, - VIDEO_ADDED = 0x10, - // |SetLocalDescription| returns successfully. - SET_LOCAL_DESCRIPTION_SUCCEEDED = 0x20, - // |SetRemoteDescription| returns successfully. - SET_REMOTE_DESCRIPTION_SUCCEEDED = 0x40, - // A local candidate (with type host, server-reflexive, or relay) is - // collected. - CANDIDATE_COLLECTED = 0x80, - // A remote candidate is successfully added via |AddIceCandidate|. - ADD_ICE_CANDIDATE_SUCCEEDED = 0x100, - ICE_STATE_CONNECTED = 0x200, - CLOSE_CALLED = 0x400, - // A local candidate with private IP is collected. - PRIVATE_CANDIDATE_COLLECTED = 0x800, - // A remote candidate with private IP is added, either via AddiceCandidate - // or from the remote description. - REMOTE_PRIVATE_CANDIDATE_ADDED = 0x1000, - // A local mDNS candidate is collected. - MDNS_CANDIDATE_COLLECTED = 0x2000, - // A remote mDNS candidate is added, either via AddIceCandidate or from the - // remote description. - REMOTE_MDNS_CANDIDATE_ADDED = 0x4000, - // A local candidate with IPv6 address is collected. - IPV6_CANDIDATE_COLLECTED = 0x8000, - // A remote candidate with IPv6 address is added, either via AddIceCandidate - // or from the remote description. - REMOTE_IPV6_CANDIDATE_ADDED = 0x10000, - // A remote candidate (with type host, server-reflexive, or relay) is - // successfully added, either via AddIceCandidate or from the remote - // description. - REMOTE_CANDIDATE_ADDED = 0x20000, - // An explicit host-host candidate pair is selected, i.e. both the local and - // the remote candidates have the host type. This does not include candidate - // pairs formed with equivalent prflx remote candidates, e.g. a host-prflx - // pair where the prflx candidate has the same base as a host candidate of - // the remote peer. - DIRECT_CONNECTION_SELECTED = 0x40000, - MAX_VALUE = 0x80000, - }; - - explicit TgPeerConnection(TgPeerConnectionFactory* factory, - std::unique_ptr event_log, - std::unique_ptr call); - - bool Initialize( - const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies dependencies); - - rtc::scoped_refptr local_streams(); - rtc::scoped_refptr remote_streams(); - bool AddStream(MediaStreamInterface* local_stream); - void RemoveStream(MediaStreamInterface* local_stream); - - RTCErrorOr> AddTrack( - rtc::scoped_refptr track, - const std::vector& stream_ids); - bool RemoveTrack(RtpSenderInterface* sender); - RTCError RemoveTrackNew( - rtc::scoped_refptr sender); - - RTCErrorOr> AddTransceiver( - rtc::scoped_refptr track); - RTCErrorOr> AddTransceiver( - rtc::scoped_refptr track, - const RtpTransceiverInit& init); - RTCErrorOr> AddTransceiver( - cricket::MediaType media_type); - RTCErrorOr> AddTransceiver( - cricket::MediaType media_type, - const RtpTransceiverInit& init); - - // Gets the DTLS SSL certificate associated with the audio transport on the - // remote side. This will become populated once the DTLS connection with the - // peer has been completed, as indicated by the ICE connection state - // transitioning to kIceConnectionCompleted. - // Note that this will be removed once we implement RTCDtlsTransport which - // has standardized method for getting this information. - // See https://www.w3.org/TR/webrtc/#rtcdtlstransport-interface - std::unique_ptr GetRemoteAudioSSLCertificate(); - - // Version of the above method that returns the full certificate chain. - std::unique_ptr GetRemoteAudioSSLCertChain(); - - rtc::scoped_refptr CreateSender( - const std::string& kind, - const std::string& stream_id); - - std::vector> GetSenders() - const; - std::vector> GetReceivers() - const; - std::vector> GetTransceivers() - const; - - rtc::scoped_refptr CreateDataChannel( - const std::string& label, - const DataChannelInit* config); - // WARNING: LEGACY. See peerconnectioninterface.h - bool GetStats(StatsObserver* observer, - webrtc::MediaStreamTrackInterface* track, - PeerConnectionInterface::StatsOutputLevel level); - // Spec-complaint GetStats(). See peerconnectioninterface.h - void GetStats(RTCStatsCollectorCallback* callback); - void GetStats( - rtc::scoped_refptr selector, - rtc::scoped_refptr callback); - void GetStats( - rtc::scoped_refptr selector, - rtc::scoped_refptr callback); - void ClearStatsCache(); - - PeerConnectionInterface::SignalingState signaling_state(); - - PeerConnectionInterface::IceConnectionState ice_connection_state(); - PeerConnectionInterface::IceConnectionState standardized_ice_connection_state(); - PeerConnectionInterface::PeerConnectionState peer_connection_state(); - PeerConnectionInterface::IceGatheringState ice_gathering_state(); - - const SessionDescriptionInterface* local_description() const; - const SessionDescriptionInterface* remote_description() const; - const SessionDescriptionInterface* current_local_description() const; - const SessionDescriptionInterface* current_remote_description() - const; - const SessionDescriptionInterface* pending_local_description() const; - const SessionDescriptionInterface* pending_remote_description() - const; - - void RestartIce(); - - // JSEP01 - void CreateOffer(CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options); - void CreateAnswer(CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options); - void SetLocalDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc); - void SetLocalDescription(SetSessionDescriptionObserver* observer); - void SetRemoteDescription(SetSessionDescriptionObserver* observer, - SessionDescriptionInterface* desc); - void SetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer); - PeerConnectionInterface::RTCConfiguration GetConfiguration(); - RTCError SetConfiguration( - const PeerConnectionInterface::RTCConfiguration& configuration); - bool AddIceCandidate(const IceCandidateInterface* candidate); - void AddIceCandidate(std::unique_ptr candidate, - std::function callback); - bool RemoveIceCandidates( - const std::vector& candidates); - - RTCError SetBitrate(const BitrateSettings& bitrate); - - void SetAudioPlayout(bool playout); - void SetAudioRecording(bool recording); - - rtc::scoped_refptr LookupDtlsTransportByMid( - const std::string& mid); - rtc::scoped_refptr LookupDtlsTransportByMidInternal( - const std::string& mid); - - rtc::scoped_refptr GetSctpTransport() const; - - bool StartRtcEventLog(std::unique_ptr output, - int64_t output_period_ms); - bool StartRtcEventLog(std::unique_ptr output); - void StopRtcEventLog(); - - void Close(); - - // PeerConnectionInternal implementation. - rtc::Thread* network_thread() const { - return factory_->network_thread(); - } - rtc::Thread* worker_thread() const { return factory_->worker_thread(); } - rtc::Thread* signaling_thread() const { - return factory_->signaling_thread(); - } - - std::string session_id() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return session_id_; - } - - bool initial_offerer() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return transport_controller_ && transport_controller_->initial_offerer(); - } - - std::vector< - rtc::scoped_refptr>> - GetTransceiversInternal() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return transceivers_; - } - - sigslot::signal1& SignalDataChannelCreated() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return SignalDataChannelCreated_; - } - - cricket::RtpDataChannel* rtp_data_channel() const { - return nullptr; - } - - std::vector> sctp_data_channels() - const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return std::vector>(); - } - - absl::optional sctp_content_name() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return sctp_mid_; - } - - absl::optional sctp_transport_name() const; - - cricket::CandidateStatsList GetPooledCandidateStats() const; - std::map GetTransportNamesByMid() const; - std::map GetTransportStatsByNames( - const std::set& transport_names); - Call::Stats GetCallStats(); - - bool GetLocalCertificate( - const std::string& transport_name, - rtc::scoped_refptr* certificate); - std::unique_ptr GetRemoteSSLCertChain( - const std::string& transport_name); - bool IceRestartPending(const std::string& content_name) const; - bool NeedsIceRestart(const std::string& content_name) const; - bool GetSslRole(const std::string& content_name, rtc::SSLRole* role); - - // Functions needed by DataChannelController - void NoteDataAddedEvent() { NoteUsageEvent(UsageEvent::DATA_ADDED); } - // Returns the observer. Will crash on CHECK if the observer is removed. - PeerConnectionObserver* Observer() const; - bool IsClosed() const { - RTC_DCHECK_RUN_ON(signaling_thread()); - return signaling_state_ == PeerConnectionInterface::kClosed; - } - // Get current SSL role used by SCTP's underlying transport. - bool GetSctpSslRole(rtc::SSLRole* role); - // Handler for the "channel closed" signal - void OnSctpDataChannelClosed(DataChannel* channel); - - // Functions made public for testing. - void ReturnHistogramVeryQuicklyForTesting() { - RTC_DCHECK_RUN_ON(signaling_thread()); - return_histogram_very_quickly_ = true; - } - void RequestUsagePatternReportForTesting(); - - protected: - ~TgPeerConnection() override; - - private: - class ImplicitCreateSessionDescriptionObserver; - friend class ImplicitCreateSessionDescriptionObserver; - class SetRemoteDescriptionObserverAdapter; - friend class SetRemoteDescriptionObserverAdapter; - - sigslot::signal1 SignalDataChannelCreated_ RTC_GUARDED_BY(signaling_thread()); - - // Represents the [[LocalIceCredentialsToReplace]] internal slot in the spec. - // It makes the next CreateOffer() produce new ICE credentials even if - // RTCOfferAnswerOptions::ice_restart is false. - // https://w3c.github.io/webrtc-pc/#dfn-localufragstoreplace - // TODO(hbos): When TgJsepTransportController/TgJsepTransport supports rollback, - // move this type of logic to TgJsepTransportController/TgJsepTransport. - class LocalIceCredentialsToReplace; - - struct RtpSenderInfo { - RtpSenderInfo() : first_ssrc(0) {} - RtpSenderInfo(const std::string& stream_id, - const std::string sender_id, - uint32_t ssrc) - : stream_id(stream_id), sender_id(sender_id), first_ssrc(ssrc) {} - bool operator==(const RtpSenderInfo& other) { - return this->stream_id == other.stream_id && - this->sender_id == other.sender_id && - this->first_ssrc == other.first_ssrc; - } - std::string stream_id; - std::string sender_id; - // An RtpSender can have many SSRCs. The first one is used as a sort of ID - // for communicating with the lower layers. - uint32_t first_ssrc; - }; - - // Field-trial based configuration for datagram transport. - struct DatagramTransportConfig { - explicit DatagramTransportConfig(const std::string& field_trial) - : enabled("enabled", true), default_value("default_value", false) { - ParseFieldTrial({&enabled, &default_value}, field_trial); - } - - // Whether datagram transport support is enabled at all. Defaults to true, - // allowing datagram transport to be used if (a) the application provides a - // factory for it and (b) the configuration specifies its use. This flag - // provides a kill-switch to force-disable datagram transport across all - // applications, without code changes. - FieldTrialFlag enabled; - - // Whether the datagram transport is enabled or disabled by default. - // Defaults to false, meaning that applications must configure use of - // datagram transport through RTCConfiguration. If set to true, - // applications will use the datagram transport by default (but may still - // explicitly configure themselves not to use it through RTCConfiguration). - FieldTrialFlag default_value; - }; - - // Field-trial based configuration for datagram transport data channels. - struct DatagramTransportDataChannelConfig { - explicit DatagramTransportDataChannelConfig(const std::string& field_trial) - : enabled("enabled", true), - default_value("default_value", false), - receive_only("receive_only", false) { - ParseFieldTrial({&enabled, &default_value, &receive_only}, field_trial); - } - - // Whether datagram transport data channel support is enabled at all. - // Defaults to true, allowing datagram transport to be used if (a) the - // application provides a factory for it and (b) the configuration specifies - // its use. This flag provides a kill-switch to force-disable datagram - // transport across all applications, without code changes. - FieldTrialFlag enabled; - - // Whether the datagram transport data channels are enabled or disabled by - // default. Defaults to false, meaning that applications must configure use - // of datagram transport through RTCConfiguration. If set to true, - // applications will use the datagram transport by default (but may still - // explicitly configure themselves not to use it through RTCConfiguration). - FieldTrialFlag default_value; - - // Whether the datagram transport is enabled in receive-only mode. If true, - // and if the datagram transport is enabled, it will only be used when - // receiving incoming calls, not when placing outgoing calls. - FieldTrialFlag receive_only; - }; - - // Captures partial state to be used for rollback. Applicable only in - // Unified Plan. - class TransceiverStableState { - public: - TransceiverStableState() {} - void set_newly_created(); - void SetMSectionIfUnset(absl::optional mid, - absl::optional mline_index); - void SetRemoteStreamIdsIfUnset(const std::vector& ids); - absl::optional mid() const { return mid_; } - absl::optional mline_index() const { return mline_index_; } - absl::optional> remote_stream_ids() const { - return remote_stream_ids_; - } - bool has_m_section() const { return has_m_section_; } - bool newly_created() const { return newly_created_; } - - private: - absl::optional mid_; - absl::optional mline_index_; - absl::optional> remote_stream_ids_; - // Indicates that mid value from stable state has been captured and - // that rollback has to restore the transceiver. Also protects against - // subsequent overwrites. - bool has_m_section_ = false; - // Indicates that the transceiver was created as part of applying a - // description to track potential need for removing transceiver during - // rollback. - bool newly_created_ = false; - }; - - // Implements MessageHandler. - void OnMessage(rtc::Message* msg) override; - - // Plan B helpers for getting the voice/video media channels for the single - // audio/video transceiver, if it exists. - cricket::VoiceMediaChannel* voice_media_channel() const - RTC_RUN_ON(signaling_thread()); - cricket::VideoMediaChannel* video_media_channel() const - RTC_RUN_ON(signaling_thread()); - - std::vector>> - GetSendersInternal() const RTC_RUN_ON(signaling_thread()); - std::vector< - rtc::scoped_refptr>> - GetReceiversInternal() const RTC_RUN_ON(signaling_thread()); - - rtc::scoped_refptr> - GetAudioTransceiver() const RTC_RUN_ON(signaling_thread()); - rtc::scoped_refptr> - GetVideoTransceiver() const RTC_RUN_ON(signaling_thread()); - - rtc::scoped_refptr> - GetFirstAudioTransceiver() const RTC_RUN_ON(signaling_thread()); - - // Implementation of the offer/answer exchange operations. These are chained - // onto the |operations_chain_| when the public CreateOffer(), CreateAnswer(), - // SetLocalDescription() and SetRemoteDescription() methods are invoked. - void DoCreateOffer( - const PeerConnectionInterface::RTCOfferAnswerOptions& options, - rtc::scoped_refptr observer); - void DoCreateAnswer( - const PeerConnectionInterface::RTCOfferAnswerOptions& options, - rtc::scoped_refptr observer); - void DoSetLocalDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer); - void DoSetRemoteDescription( - std::unique_ptr desc, - rtc::scoped_refptr observer); - - void CreateAudioReceiver(MediaStreamInterface* stream, - const RtpSenderInfo& remote_sender_info) - RTC_RUN_ON(signaling_thread()); - - void CreateVideoReceiver(MediaStreamInterface* stream, - const RtpSenderInfo& remote_sender_info) - RTC_RUN_ON(signaling_thread()); - rtc::scoped_refptr RemoveAndStopReceiver( - const RtpSenderInfo& remote_sender_info) RTC_RUN_ON(signaling_thread()); - - // May be called either by AddStream/RemoveStream, or when a track is - // added/removed from a stream previously added via AddStream. - void AddAudioTrack(AudioTrackInterface* track, MediaStreamInterface* stream) - RTC_RUN_ON(signaling_thread()); - void RemoveAudioTrack(AudioTrackInterface* track, - MediaStreamInterface* stream) - RTC_RUN_ON(signaling_thread()); - void AddVideoTrack(VideoTrackInterface* track, MediaStreamInterface* stream) - RTC_RUN_ON(signaling_thread()); - void RemoveVideoTrack(VideoTrackInterface* track, - MediaStreamInterface* stream) - RTC_RUN_ON(signaling_thread()); - - // AddTrack implementation when Unified Plan is specified. - RTCErrorOr> AddTrackUnifiedPlan( - rtc::scoped_refptr track, - const std::vector& stream_ids) - RTC_RUN_ON(signaling_thread()); - // AddTrack implementation when Plan B is specified. - RTCErrorOr> AddTrackPlanB( - rtc::scoped_refptr track, - const std::vector& stream_ids) - RTC_RUN_ON(signaling_thread()); - - // Returns the first RtpTransceiver suitable for a newly added track, if such - // transceiver is available. - rtc::scoped_refptr> - FindFirstTransceiverForAddedTrack( - rtc::scoped_refptr track) - RTC_RUN_ON(signaling_thread()); - - rtc::scoped_refptr> - FindTransceiverBySender(rtc::scoped_refptr sender) - RTC_RUN_ON(signaling_thread()); - - // Internal implementation for AddTransceiver family of methods. If - // |fire_callback| is set, fires OnRenegotiationNeeded callback if successful. - RTCErrorOr> AddTransceiver( - cricket::MediaType media_type, - rtc::scoped_refptr track, - const RtpTransceiverInit& init, - bool fire_callback = true) RTC_RUN_ON(signaling_thread()); - - rtc::scoped_refptr> - CreateSender(cricket::MediaType media_type, - const std::string& id, - rtc::scoped_refptr track, - const std::vector& stream_ids, - const std::vector& send_encodings); - - rtc::scoped_refptr> - CreateReceiver(cricket::MediaType media_type, const std::string& receiver_id); - - // Create a new RtpTransceiver of the given type and add it to the list of - // transceivers. - rtc::scoped_refptr> - CreateAndAddTransceiver( - rtc::scoped_refptr> sender, - rtc::scoped_refptr> - receiver) RTC_RUN_ON(signaling_thread()); - - void SetIceConnectionState(PeerConnectionInterface::IceConnectionState new_state) - RTC_RUN_ON(signaling_thread()); - void SetStandardizedIceConnectionState( - PeerConnectionInterface::IceConnectionState new_state) - RTC_RUN_ON(signaling_thread()); - void SetConnectionState( - PeerConnectionInterface::PeerConnectionState new_state) - RTC_RUN_ON(signaling_thread()); - - // Called any time the IceGatheringState changes. - void OnIceGatheringChange(PeerConnectionInterface::IceGatheringState new_state) - RTC_RUN_ON(signaling_thread()); - // New ICE candidate has been gathered. - void OnIceCandidate(std::unique_ptr candidate) - RTC_RUN_ON(signaling_thread()); - // Gathering of an ICE candidate failed. - void OnIceCandidateError(const std::string& address, - int port, - const std::string& url, - int error_code, - const std::string& error_text) - RTC_RUN_ON(signaling_thread()); - // Some local ICE candidates have been removed. - void OnIceCandidatesRemoved(const std::vector& candidates) - RTC_RUN_ON(signaling_thread()); - - void OnSelectedCandidatePairChanged( - const cricket::CandidatePairChangeEvent& event) - RTC_RUN_ON(signaling_thread()); - - // Update the state, signaling if necessary. - void ChangeSignalingState(PeerConnectionInterface::SignalingState signaling_state) - RTC_RUN_ON(signaling_thread()); - - // Signals from MediaStreamObserver. - void OnAudioTrackAdded(AudioTrackInterface* track, - MediaStreamInterface* stream) - RTC_RUN_ON(signaling_thread()); - void OnAudioTrackRemoved(AudioTrackInterface* track, - MediaStreamInterface* stream) - RTC_RUN_ON(signaling_thread()); - void OnVideoTrackAdded(VideoTrackInterface* track, - MediaStreamInterface* stream) - RTC_RUN_ON(signaling_thread()); - void OnVideoTrackRemoved(VideoTrackInterface* track, - MediaStreamInterface* stream) - RTC_RUN_ON(signaling_thread()); - - void PostSetSessionDescriptionSuccess( - SetSessionDescriptionObserver* observer); - void PostSetSessionDescriptionFailure(SetSessionDescriptionObserver* observer, - RTCError&& error); - void PostCreateSessionDescriptionFailure( - CreateSessionDescriptionObserver* observer, - RTCError error); - - // Synchronous implementations of SetLocalDescription/SetRemoteDescription - // that return an RTCError instead of invoking a callback. - RTCError ApplyLocalDescription( - std::unique_ptr desc); - RTCError ApplyRemoteDescription( - std::unique_ptr desc); - - // Updates the local RtpTransceivers according to the JSEP rules. Called as - // part of setting the local/remote description. - RTCError UpdateTransceiversAndDataChannels( - cricket::ContentSource source, - const SessionDescriptionInterface& new_session, - const SessionDescriptionInterface* old_local_description, - const SessionDescriptionInterface* old_remote_description) - RTC_RUN_ON(signaling_thread()); - - // Either creates or destroys the transceiver's BaseChannel according to the - // given media section. - RTCError UpdateTransceiverChannel( - rtc::scoped_refptr> - transceiver, - const cricket::ContentInfo& content, - const cricket::ContentGroup* bundle_group) RTC_RUN_ON(signaling_thread()); - - // Either creates or destroys the local data channel according to the given - // media section. - RTCError UpdateDataChannel(cricket::ContentSource source, - const cricket::ContentInfo& content, - const cricket::ContentGroup* bundle_group) - RTC_RUN_ON(signaling_thread()); - - // Associate the given transceiver according to the JSEP rules. - RTCErrorOr< - rtc::scoped_refptr>> - AssociateTransceiver(cricket::ContentSource source, - SdpType type, - size_t mline_index, - const cricket::ContentInfo& content, - const cricket::ContentInfo* old_local_content, - const cricket::ContentInfo* old_remote_content) - RTC_RUN_ON(signaling_thread()); - - // Returns the RtpTransceiver, if found, that is associated to the given MID. - rtc::scoped_refptr> - GetAssociatedTransceiver(const std::string& mid) const - RTC_RUN_ON(signaling_thread()); - - // Returns the RtpTransceiver, if found, that was assigned to the given mline - // index in CreateOffer. - rtc::scoped_refptr> - GetTransceiverByMLineIndex(size_t mline_index) const - RTC_RUN_ON(signaling_thread()); - - // Returns an RtpTransciever, if available, that can be used to receive the - // given media type according to JSEP rules. - rtc::scoped_refptr> - FindAvailableTransceiverToReceive(cricket::MediaType media_type) const - RTC_RUN_ON(signaling_thread()); - - // Returns the media section in the given session description that is - // associated with the RtpTransceiver. Returns null if none found or this - // RtpTransceiver is not associated. Logic varies depending on the - // SdpSemantics specified in the configuration. - const cricket::ContentInfo* FindMediaSectionForTransceiver( - rtc::scoped_refptr> - transceiver, - const SessionDescriptionInterface* sdesc) const - RTC_RUN_ON(signaling_thread()); - - // Runs the algorithm **set the associated remote streams** specified in - // https://w3c.github.io/webrtc-pc/#set-associated-remote-streams. - void SetAssociatedRemoteStreams( - rtc::scoped_refptr receiver, - const std::vector& stream_ids, - std::vector>* added_streams, - std::vector>* removed_streams) - RTC_RUN_ON(signaling_thread()); - - // Runs the algorithm **process the removal of a remote track** specified in - // the WebRTC specification. - // This method will update the following lists: - // |remove_list| is the list of transceivers for which the receiving track is - // being removed. - // |removed_streams| is the list of streams which no longer have a receiving - // track so should be removed. - // https://w3c.github.io/webrtc-pc/#process-remote-track-removal - void ProcessRemovalOfRemoteTrack( - rtc::scoped_refptr> - transceiver, - std::vector>* remove_list, - std::vector>* removed_streams) - RTC_RUN_ON(signaling_thread()); - - void RemoveRemoteStreamsIfEmpty( - const std::vector>& - remote_streams, - std::vector>* removed_streams) - RTC_RUN_ON(signaling_thread()); - - void OnNegotiationNeeded(); - - // Returns a MediaSessionOptions struct with options decided by |options|, - // the local MediaStreams and DataChannels. - void GetOptionsForOffer(const PeerConnectionInterface::RTCOfferAnswerOptions& - offer_answer_options, - cricket::MediaSessionOptions* session_options) - RTC_RUN_ON(signaling_thread()); - void GetOptionsForPlanBOffer( - const PeerConnectionInterface::RTCOfferAnswerOptions& - offer_answer_options, - cricket::MediaSessionOptions* session_options) - RTC_RUN_ON(signaling_thread()); - void GetOptionsForUnifiedPlanOffer( - const PeerConnectionInterface::RTCOfferAnswerOptions& - offer_answer_options, - cricket::MediaSessionOptions* session_options) - RTC_RUN_ON(signaling_thread()); - - RTCError HandleLegacyOfferOptions(const PeerConnectionInterface::RTCOfferAnswerOptions& options) - RTC_RUN_ON(signaling_thread()); - void RemoveRecvDirectionFromReceivingTransceiversOfType( - cricket::MediaType media_type) RTC_RUN_ON(signaling_thread()); - void AddUpToOneReceivingTransceiverOfType(cricket::MediaType media_type); - std::vector< - rtc::scoped_refptr>> - GetReceivingTransceiversOfType(cricket::MediaType media_type) - RTC_RUN_ON(signaling_thread()); - - // Returns a MediaSessionOptions struct with options decided by - // |constraints|, the local MediaStreams and DataChannels. - void GetOptionsForAnswer(const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, - cricket::MediaSessionOptions* session_options) - RTC_RUN_ON(signaling_thread()); - void GetOptionsForPlanBAnswer( - const PeerConnectionInterface::RTCOfferAnswerOptions& - offer_answer_options, - cricket::MediaSessionOptions* session_options) - RTC_RUN_ON(signaling_thread()); - void GetOptionsForUnifiedPlanAnswer( - const PeerConnectionInterface::RTCOfferAnswerOptions& - offer_answer_options, - cricket::MediaSessionOptions* session_options) - RTC_RUN_ON(signaling_thread()); - - // Generates MediaDescriptionOptions for the |session_opts| based on existing - // local description or remote description. - void GenerateMediaDescriptionOptions( - const SessionDescriptionInterface* session_desc, - RtpTransceiverDirection audio_direction, - RtpTransceiverDirection video_direction, - absl::optional* audio_index, - absl::optional* video_index, - absl::optional* data_index, - cricket::MediaSessionOptions* session_options) - RTC_RUN_ON(signaling_thread()); - - // Remove all local and remote senders of type |media_type|. - // Called when a media type is rejected (m-line set to port 0). - void RemoveSenders(cricket::MediaType media_type) - RTC_RUN_ON(signaling_thread()); - - // Makes sure a MediaStreamTrack is created for each StreamParam in |streams|, - // and existing MediaStreamTracks are removed if there is no corresponding - // StreamParam. If |default_track_needed| is true, a default MediaStreamTrack - // is created if it doesn't exist; if false, it's removed if it exists. - // |media_type| is the type of the |streams| and can be either audio or video. - // If a new MediaStream is created it is added to |new_streams|. - void UpdateRemoteSendersList( - const std::vector& streams, - bool default_track_needed, - cricket::MediaType media_type, - StreamCollection* new_streams) RTC_RUN_ON(signaling_thread()); - - // Triggered when a remote sender has been seen for the first time in a remote - // session description. It creates a remote MediaStreamTrackInterface - // implementation and triggers CreateAudioReceiver or CreateVideoReceiver. - void OnRemoteSenderAdded(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) - RTC_RUN_ON(signaling_thread()); - - // Triggered when a remote sender has been removed from a remote session - // description. It removes the remote sender with id |sender_id| from a remote - // MediaStream and triggers DestroyAudioReceiver or DestroyVideoReceiver. - void OnRemoteSenderRemoved(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) - RTC_RUN_ON(signaling_thread()); - - // Finds remote MediaStreams without any tracks and removes them from - // |remote_streams_| and notifies the observer that the MediaStreams no longer - // exist. - void UpdateEndedRemoteMediaStreams() RTC_RUN_ON(signaling_thread()); - - // Loops through the vector of |streams| and finds added and removed - // StreamParams since last time this method was called. - // For each new or removed StreamParam, OnLocalSenderSeen or - // OnLocalSenderRemoved is invoked. - void UpdateLocalSenders(const std::vector& streams, - cricket::MediaType media_type) - RTC_RUN_ON(signaling_thread()); - - // Triggered when a local sender has been seen for the first time in a local - // session description. - // This method triggers CreateAudioSender or CreateVideoSender if the rtp - // streams in the local SessionDescription can be mapped to a MediaStreamTrack - // in a MediaStream in |local_streams_| - void OnLocalSenderAdded(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) - RTC_RUN_ON(signaling_thread()); - - // Triggered when a local sender has been removed from a local session - // description. - // This method triggers DestroyAudioSender or DestroyVideoSender if a stream - // has been removed from the local SessionDescription and the stream can be - // mapped to a MediaStreamTrack in a MediaStream in |local_streams_|. - void OnLocalSenderRemoved(const RtpSenderInfo& sender_info, - cricket::MediaType media_type) - RTC_RUN_ON(signaling_thread()); - - // Returns true if the TgPeerConnection is configured to use Unified Plan - // semantics for creating offers/answers and setting local/remote - // descriptions. If this is true the RtpTransceiver API will also be available - // to the user. If this is false, Plan B semantics are assumed. - // TODO(bugs.webrtc.org/8530): Flip the default to be Unified Plan once - // sufficient time has passed. - bool IsUnifiedPlan() const RTC_RUN_ON(signaling_thread()) { - return configuration_.sdp_semantics == SdpSemantics::kUnifiedPlan; - } - - // The offer/answer machinery assumes the media section MID is present and - // unique. To support legacy end points that do not supply a=mid lines, this - // method will modify the session description to add MIDs generated according - // to the SDP semantics. - void FillInMissingRemoteMids(cricket::SessionDescription* remote_description) - RTC_RUN_ON(signaling_thread()); - - // Is there an RtpSender of the given type? - bool HasRtpSender(cricket::MediaType type) const - RTC_RUN_ON(signaling_thread()); - - // Return the RtpSender with the given track attached. - rtc::scoped_refptr> - FindSenderForTrack(MediaStreamTrackInterface* track) const - RTC_RUN_ON(signaling_thread()); - - // Return the RtpSender with the given id, or null if none exists. - rtc::scoped_refptr> - FindSenderById(const std::string& sender_id) const - RTC_RUN_ON(signaling_thread()); - - // Return the RtpReceiver with the given id, or null if none exists. - rtc::scoped_refptr> - FindReceiverById(const std::string& receiver_id) const - RTC_RUN_ON(signaling_thread()); - - std::vector* GetRemoteSenderInfos( - cricket::MediaType media_type); - std::vector* GetLocalSenderInfos( - cricket::MediaType media_type); - const RtpSenderInfo* FindSenderInfo(const std::vector& infos, - const std::string& stream_id, - const std::string sender_id) const; - - // Returns the specified SCTP DataChannel in sctp_data_channels_, - // or nullptr if not found. - DataChannel* FindDataChannelBySid(int sid) const - RTC_RUN_ON(signaling_thread()); - - // Called when first configuring the port allocator. - struct InitializePortAllocatorResult { - bool enable_ipv6; - }; - InitializePortAllocatorResult InitializePortAllocator_n( - const cricket::ServerAddresses& stun_servers, - const std::vector& turn_servers, - const PeerConnectionInterface::RTCConfiguration& configuration); - // Called when SetConfiguration is called to apply the supported subset - // of the configuration on the network thread. - bool ReconfigurePortAllocator_n( - const cricket::ServerAddresses& stun_servers, - const std::vector& turn_servers, - PeerConnectionInterface::IceTransportsType type, - int candidate_pool_size, - PortPrunePolicy turn_port_prune_policy, - webrtc::TurnCustomizer* turn_customizer, - absl::optional stun_candidate_keepalive_interval, - bool have_local_description); - - // Starts output of an RTC event log to the given output object. - // This function should only be called from the worker thread. - bool StartRtcEventLog_w(std::unique_ptr output, - int64_t output_period_ms); - - // Stops recording an RTC event log. - // This function should only be called from the worker thread. - void StopRtcEventLog_w(); - - // Ensures the configuration doesn't have any parameters with invalid values, - // or values that conflict with other parameters. - // - // Returns RTCError::OK() if there are no issues. - RTCError ValidateConfiguration(const PeerConnectionInterface::RTCConfiguration& config) const; - - cricket::ChannelManager* channel_manager() const; - - enum class SessionError { - kNone, // No error. - kContent, // Error in BaseChannel SetLocalContent/SetRemoteContent. - kTransport, // Error from the underlying transport. - }; - - // Returns the last error in the session. See the enum above for details. - SessionError session_error() const RTC_RUN_ON(signaling_thread()) { - return session_error_; - } - const std::string& session_error_desc() const { return session_error_desc_; } - - cricket::ChannelInterface* GetChannel(const std::string& content_name); - - cricket::IceConfig ParseIceConfig( - const PeerConnectionInterface::RTCConfiguration& config) const; - - // Called when an RTCCertificate is generated or retrieved by - // WebRTCSessionDescriptionFactory. Should happen before setLocalDescription. - void OnCertificateReady( - const rtc::scoped_refptr& certificate); - void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp); - - // Non-const versions of local_description()/remote_description(), for use - // internally. - SessionDescriptionInterface* mutable_local_description() - RTC_RUN_ON(signaling_thread()) { - return pending_local_description_ ? pending_local_description_.get() - : current_local_description_.get(); - } - SessionDescriptionInterface* mutable_remote_description() - RTC_RUN_ON(signaling_thread()) { - return pending_remote_description_ ? pending_remote_description_.get() - : current_remote_description_.get(); - } - - // Updates the error state, signaling if necessary. - void SetSessionError(SessionError error, const std::string& error_desc); - - RTCError UpdateSessionState(SdpType type, - cricket::ContentSource source, - const cricket::SessionDescription* description); - // Push the media parts of the local or remote session description - // down to all of the channels. - RTCError PushdownMediaDescription(SdpType type, cricket::ContentSource source) - RTC_RUN_ON(signaling_thread()); - - RTCError PushdownTransportDescription(cricket::ContentSource source, - SdpType type); - - // Returns true and the TransportInfo of the given |content_name| - // from |description|. Returns false if it's not available. - static bool GetTransportDescription( - const cricket::SessionDescription* description, - const std::string& content_name, - cricket::TransportDescription* info); - - // Enables media channels to allow sending of media. - // This enables media to flow on all configured audio/video channels and the - // RtpDataChannel. - void EnableSending() RTC_RUN_ON(signaling_thread()); - - // Destroys all BaseChannels and destroys the SCTP data channel, if present. - void DestroyAllChannels() RTC_RUN_ON(signaling_thread()); - - // Returns the media index for a local ice candidate given the content name. - // Returns false if the local session description does not have a media - // content called |content_name|. - bool GetLocalCandidateMediaIndex(const std::string& content_name, - int* sdp_mline_index) - RTC_RUN_ON(signaling_thread()); - // Uses all remote candidates in |remote_desc| in this session. - bool UseCandidatesInSessionDescription( - const SessionDescriptionInterface* remote_desc) - RTC_RUN_ON(signaling_thread()); - // Uses |candidate| in this session. - bool UseCandidate(const IceCandidateInterface* candidate) - RTC_RUN_ON(signaling_thread()); - RTCErrorOr FindContentInfo( - const SessionDescriptionInterface* description, - const IceCandidateInterface* candidate) RTC_RUN_ON(signaling_thread()); - // Deletes the corresponding channel of contents that don't exist in |desc|. - // |desc| can be null. This means that all channels are deleted. - void RemoveUnusedChannels(const cricket::SessionDescription* desc) - RTC_RUN_ON(signaling_thread()); - - // Allocates media channels based on the |desc|. If |desc| doesn't have - // the BUNDLE option, this method will disable BUNDLE in PortAllocator. - // This method will also delete any existing media channels before creating. - RTCError CreateChannels(const cricket::SessionDescription& desc) - RTC_RUN_ON(signaling_thread()); - - // If the BUNDLE policy is max-bundle, then we know for sure that all - // transports will be bundled from the start. This method returns the BUNDLE - // group if that's the case, or null if BUNDLE will be negotiated later. An - // error is returned if max-bundle is specified but the session description - // does not have a BUNDLE group. - RTCErrorOr GetEarlyBundleGroup( - const cricket::SessionDescription& desc) const - RTC_RUN_ON(signaling_thread()); - - // Helper methods to create media channels. - cricket::VoiceChannel* CreateVoiceChannel(const std::string& mid) - RTC_RUN_ON(signaling_thread()); - cricket::VideoChannel* CreateVideoChannel(const std::string& mid) - RTC_RUN_ON(signaling_thread()); - bool CreateDataChannel(const std::string& mid) RTC_RUN_ON(signaling_thread()); - - bool SetupDataChannelTransport_n(const std::string& mid) - RTC_RUN_ON(network_thread()); - void TeardownDataChannelTransport_n() RTC_RUN_ON(network_thread()); - - bool ValidateBundleSettings(const cricket::SessionDescription* desc); - bool HasRtcpMuxEnabled(const cricket::ContentInfo* content); - // Below methods are helper methods which verifies SDP. - RTCError ValidateSessionDescription(const SessionDescriptionInterface* sdesc, - cricket::ContentSource source) - RTC_RUN_ON(signaling_thread()); - - // Check if a call to SetLocalDescription is acceptable with a session - // description of the given type. - bool ExpectSetLocalDescription(SdpType type); - // Check if a call to SetRemoteDescription is acceptable with a session - // description of the given type. - bool ExpectSetRemoteDescription(SdpType type); - // Verifies a=setup attribute as per RFC 5763. - bool ValidateDtlsSetupAttribute(const cricket::SessionDescription* desc, - SdpType type); - - // Returns true if we are ready to push down the remote candidate. - // |remote_desc| is the new remote description, or NULL if the current remote - // description should be used. Output |valid| is true if the candidate media - // index is valid. - bool ReadyToUseRemoteCandidate(const IceCandidateInterface* candidate, - const SessionDescriptionInterface* remote_desc, - bool* valid) RTC_RUN_ON(signaling_thread()); - - // Returns true if SRTP (either using DTLS-SRTP or SDES) is required by - // this session. - bool SrtpRequired() const RTC_RUN_ON(signaling_thread()); - - // TgJsepTransportController signal handlers. - void OnTransportControllerConnectionState(cricket::IceConnectionState state) - RTC_RUN_ON(signaling_thread()); - void OnTransportControllerGatheringState(cricket::IceGatheringState state) - RTC_RUN_ON(signaling_thread()); - void OnTransportControllerCandidatesGathered( - const std::string& transport_name, - const std::vector& candidates) - RTC_RUN_ON(signaling_thread()); - void OnTransportControllerCandidateError( - const cricket::IceCandidateErrorEvent& event) - RTC_RUN_ON(signaling_thread()); - void OnTransportControllerCandidatesRemoved( - const std::vector& candidates) - RTC_RUN_ON(signaling_thread()); - void OnTransportControllerCandidateChanged( - const cricket::CandidatePairChangeEvent& event) - RTC_RUN_ON(signaling_thread()); - void OnTransportControllerDtlsHandshakeError(rtc::SSLHandshakeError error); - - const char* SessionErrorToString(SessionError error) const; - std::string GetSessionErrorMsg() RTC_RUN_ON(signaling_thread()); - - // Report the UMA metric SdpFormatReceived for the given remote offer. - void ReportSdpFormatReceived(const SessionDescriptionInterface& remote_offer); - - // Report inferred negotiated SDP semantics from a local/remote answer to the - // UMA observer. - void ReportNegotiatedSdpSemantics(const SessionDescriptionInterface& answer); - - // Invoked when TransportController connection completion is signaled. - // Reports stats for all transports in use. - void ReportTransportStats() RTC_RUN_ON(signaling_thread()); - - // Gather the usage of IPv4/IPv6 as best connection. - void ReportBestConnectionState(const cricket::TransportStats& stats); - - void ReportNegotiatedCiphers(const cricket::TransportStats& stats, - const std::set& media_types) - RTC_RUN_ON(signaling_thread()); - void ReportIceCandidateCollected(const cricket::Candidate& candidate) - RTC_RUN_ON(signaling_thread()); - void ReportRemoteIceCandidateAdded(const cricket::Candidate& candidate) - RTC_RUN_ON(signaling_thread()); - - void NoteUsageEvent(UsageEvent event); - void ReportUsagePattern() const RTC_RUN_ON(signaling_thread()); - - void OnSentPacket_w(const rtc::SentPacket& sent_packet); - - const std::string GetTransportName(const std::string& content_name) - RTC_RUN_ON(signaling_thread()); - - // Functions for dealing with transports. - // Note that cricket code uses the term "channel" for what other code - // refers to as "transport". - - // Destroys and clears the BaseChannel associated with the given transceiver, - // if such channel is set. - void DestroyTransceiverChannel( - rtc::scoped_refptr> - transceiver); - - // Destroys the given ChannelInterface. - // The channel cannot be accessed after this method is called. - void DestroyChannelInterface(cricket::ChannelInterface* channel); - - // TgJsepTransportController::Observer override. - // - // Called by |transport_controller_| when processing transport information - // from a session description, and the mapping from m= sections to transports - // changed (as a result of BUNDLE negotiation, or m= sections being - // rejected). - bool OnTransportChanged( - const std::string& mid, - RtpTransportInternal* rtp_transport, - rtc::scoped_refptr dtls_transport, - DataChannelTransportInterface* data_channel_transport) override; - - // RtpSenderBase::SetStreamsObserver override. - void OnSetStreams() override; - - // Returns the CryptoOptions for this TgPeerConnection. This will always - // return the RTCConfiguration.crypto_options if set and will only default - // back to the PeerConnectionFactory settings if nothing was set. - CryptoOptions GetCryptoOptions() RTC_RUN_ON(signaling_thread()); - - // Returns rtp transport, result can not be nullptr. - RtpTransportInternal* GetRtpTransport(const std::string& mid) - RTC_RUN_ON(signaling_thread()) { - auto rtp_transport = transport_controller_->GetRtpTransport(mid); - RTC_DCHECK(rtp_transport); - return rtp_transport; - } - - void UpdateNegotiationNeeded(); - bool CheckIfNegotiationIsNeeded(); - - // | sdp_type | is the type of the SDP that caused the rollback. - RTCError Rollback(SdpType sdp_type); - - // Storing the factory as a scoped reference pointer ensures that the memory - // in the PeerConnectionFactoryImpl remains available as long as the - // TgPeerConnection is running. It is passed to TgPeerConnection as a raw pointer. - // However, since the reference counting is done in the - // PeerConnectionFactoryInterface all instances created using the raw pointer - // will refer to the same reference count. - const rtc::scoped_refptr factory_; - PeerConnectionObserver* observer_ RTC_GUARDED_BY(signaling_thread()) = - nullptr; - - // The EventLog needs to outlive |call_| (and any other object that uses it). - std::unique_ptr event_log_ RTC_GUARDED_BY(worker_thread()); - - // Points to the same thing as `event_log_`. Since it's const, we may read the - // pointer (but not touch the object) from any thread. - RtcEventLog* const event_log_ptr_ RTC_PT_GUARDED_BY(worker_thread()); - - // The operations chain is used by the offer/answer exchange methods to ensure - // they are executed in the right order. For example, if - // SetRemoteDescription() is invoked while CreateOffer() is still pending, the - // SRD operation will not start until CreateOffer() has completed. See - // https://w3c.github.io/webrtc-pc/#dfn-operations-chain. - rtc::scoped_refptr operations_chain_ - RTC_GUARDED_BY(signaling_thread()); - - PeerConnectionInterface::SignalingState signaling_state_ RTC_GUARDED_BY(signaling_thread()) = PeerConnectionInterface::kStable; - PeerConnectionInterface::IceConnectionState ice_connection_state_ RTC_GUARDED_BY(signaling_thread()) = - PeerConnectionInterface::kIceConnectionNew; - PeerConnectionInterface::IceConnectionState standardized_ice_connection_state_ - RTC_GUARDED_BY(signaling_thread()) = PeerConnectionInterface::kIceConnectionNew; - PeerConnectionInterface::PeerConnectionState connection_state_ - RTC_GUARDED_BY(signaling_thread()) = PeerConnectionInterface::PeerConnectionState::kNew; - - PeerConnectionInterface::IceGatheringState ice_gathering_state_ RTC_GUARDED_BY(signaling_thread()) = - PeerConnectionInterface::kIceGatheringNew; - PeerConnectionInterface::RTCConfiguration configuration_ - RTC_GUARDED_BY(signaling_thread()); - - // Field-trial based configuration for datagram transport. - const DatagramTransportConfig datagram_transport_config_; - - // Field-trial based configuration for datagram transport data channels. - const DatagramTransportDataChannelConfig - datagram_transport_data_channel_config_; - - // Final, resolved value for whether datagram transport is in use. - bool use_datagram_transport_ RTC_GUARDED_BY(signaling_thread()) = false; - - // Equivalent of |use_datagram_transport_|, but for its use with data - // channels. - bool use_datagram_transport_for_data_channels_ - RTC_GUARDED_BY(signaling_thread()) = false; - - // Resolved value of whether to use data channels only for incoming calls. - bool use_datagram_transport_for_data_channels_receive_only_ - RTC_GUARDED_BY(signaling_thread()) = false; - - // TODO(zstein): |async_resolver_factory_| can currently be nullptr if it - // is not injected. It should be required once chromium supplies it. - std::unique_ptr async_resolver_factory_ - RTC_GUARDED_BY(signaling_thread()); - std::unique_ptr - port_allocator_; // TODO(bugs.webrtc.org/9987): Accessed on both - // signaling and network thread. - std::unique_ptr - ice_transport_factory_; // TODO(bugs.webrtc.org/9987): Accessed on the - // signaling thread but the underlying raw - // pointer is given to - // |jsep_transport_controller_| and used on the - // network thread. - std::unique_ptr - tls_cert_verifier_; // TODO(bugs.webrtc.org/9987): Accessed on both - // signaling and network thread. - - // One TgPeerConnection has only one RTCP CNAME. - // https://tools.ietf.org/html/draft-ietf-rtcweb-rtp-usage-26#section-4.9 - const std::string rtcp_cname_; - - // Streams added via AddStream. - const rtc::scoped_refptr local_streams_ - RTC_GUARDED_BY(signaling_thread()); - // Streams created as a result of SetRemoteDescription. - const rtc::scoped_refptr remote_streams_ - RTC_GUARDED_BY(signaling_thread()); - - std::vector> stream_observers_ - RTC_GUARDED_BY(signaling_thread()); - - // These lists store sender info seen in local/remote descriptions. - std::vector remote_audio_sender_infos_ - RTC_GUARDED_BY(signaling_thread()); - std::vector remote_video_sender_infos_ - RTC_GUARDED_BY(signaling_thread()); - std::vector local_audio_sender_infos_ - RTC_GUARDED_BY(signaling_thread()); - std::vector local_video_sender_infos_ - RTC_GUARDED_BY(signaling_thread()); - - bool remote_peer_supports_msid_ RTC_GUARDED_BY(signaling_thread()) = false; - - // The unique_ptr belongs to the worker thread, but the Call object manages - // its own thread safety. - std::unique_ptr call_ RTC_GUARDED_BY(worker_thread()); - - rtc::AsyncInvoker rtcp_invoker_ RTC_GUARDED_BY(network_thread()); - - // Points to the same thing as `call_`. Since it's const, we may read the - // pointer from any thread. - Call* const call_ptr_; - - // Holds changes made to transceivers during applying descriptors for - // potential rollback. Gets cleared once signaling state goes to stable. - std::map>, - TransceiverStableState> - transceiver_stable_states_by_transceivers_; - // Holds remote stream ids for transceivers from stable state. - std::map>, - std::vector> - remote_stream_ids_by_transceivers_; - std::vector< - rtc::scoped_refptr>> - transceivers_; // TODO(bugs.webrtc.org/9987): Accessed on both signaling - // and network thread. - - // In Unified Plan, if we encounter remote SDP that does not contain an a=msid - // line we create and use a stream with a random ID for our receivers. This is - // to support legacy endpoints that do not support the a=msid attribute (as - // opposed to streamless tracks with "a=msid:-"). - rtc::scoped_refptr missing_msid_default_stream_ - RTC_GUARDED_BY(signaling_thread()); - // MIDs will be generated using this generator which will keep track of - // all the MIDs that have been seen over the life of the TgPeerConnection. - rtc::UniqueStringGenerator mid_generator_ RTC_GUARDED_BY(signaling_thread()); - - SessionError session_error_ RTC_GUARDED_BY(signaling_thread()) = - SessionError::kNone; - std::string session_error_desc_ RTC_GUARDED_BY(signaling_thread()); - - std::string session_id_ RTC_GUARDED_BY(signaling_thread()); - - std::unique_ptr - transport_controller_; // TODO(bugs.webrtc.org/9987): Accessed on both - // signaling and network thread. - std::unique_ptr - sctp_factory_; // TODO(bugs.webrtc.org/9987): Accessed on both - // signaling and network thread. - - // |sctp_mid_| is the content name (MID) in SDP. - // Note: this is used as the data channel MID by both SCTP and data channel - // transports. It is set when either transport is initialized and unset when - // both transports are deleted. - absl::optional - sctp_mid_; // TODO(bugs.webrtc.org/9987): Accessed on both signaling - // and network thread. - - // Whether this peer is the caller. Set when the local description is applied. - absl::optional is_caller_ RTC_GUARDED_BY(signaling_thread()); - - - - std::unique_ptr current_local_description_ - RTC_GUARDED_BY(signaling_thread()); - std::unique_ptr pending_local_description_ - RTC_GUARDED_BY(signaling_thread()); - std::unique_ptr current_remote_description_ - RTC_GUARDED_BY(signaling_thread()); - std::unique_ptr pending_remote_description_ - RTC_GUARDED_BY(signaling_thread()); - bool dtls_enabled_ RTC_GUARDED_BY(signaling_thread()) = false; - - // List of content names for which the remote side triggered an ICE restart. - std::set pending_ice_restarts_ - RTC_GUARDED_BY(signaling_thread()); - - std::unique_ptr webrtc_session_desc_factory_ - RTC_GUARDED_BY(signaling_thread()); - - // Member variables for caching global options. - cricket::AudioOptions audio_options_ RTC_GUARDED_BY(signaling_thread()); - cricket::VideoOptions video_options_ RTC_GUARDED_BY(signaling_thread()); - - int usage_event_accumulator_ RTC_GUARDED_BY(signaling_thread()) = 0; - bool return_histogram_very_quickly_ RTC_GUARDED_BY(signaling_thread()) = - false; - - // This object should be used to generate any SSRC that is not explicitly - // specified by the user (or by the remote party). - // The generator is not used directly, instead it is passed on to the - // channel manager and the session description factory. - rtc::UniqueRandomIdGenerator ssrc_generator_ - RTC_GUARDED_BY(signaling_thread()); - - // A video bitrate allocator factory. - // This can injected using the PeerConnectionDependencies, - // or else the CreateBuiltinVideoBitrateAllocatorFactory() will be called. - // Note that one can still choose to override this in a MediaEngine - // if one wants too. - std::unique_ptr - video_bitrate_allocator_factory_; - - std::unique_ptr - local_ice_credentials_to_replace_ RTC_GUARDED_BY(signaling_thread()); - bool is_negotiation_needed_ RTC_GUARDED_BY(signaling_thread()) = false; - - rtc::WeakPtrFactory weak_ptr_factory_ - RTC_GUARDED_BY(signaling_thread()); -}; - -} // namespace webrtc - -#endif // PC_PEER_CONNECTION_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp deleted file mode 100644 index 0cfc58193f..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.cpp +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright 2004 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_peer_connection_factory.h" - -#include -#include -#include - -#include "api/fec_controller.h" -#include "api/media_stream_proxy.h" -#include "api/media_stream_track_proxy.h" -#include "api/network_state_predictor.h" -#include "api/peer_connection_factory_proxy.h" -#include "api/peer_connection_proxy.h" -#include "api/rtc_event_log/rtc_event_log.h" -#include "api/transport/field_trial_based_config.h" -#include "api/transport/media/media_transport_interface.h" -#include "api/turn_customizer.h" -#include "api/units/data_rate.h" -#include "api/video_track_source_proxy.h" -#include "media/sctp/sctp_transport.h" -#include "p2p/base/basic_packet_socket_factory.h" -#include "p2p/base/default_ice_transport_factory.h" -#include "p2p/client/basic_port_allocator.h" -#include "pc/audio_track.h" -#include "pc/local_audio_source.h" -#include "pc/media_stream.h" -#include "pc/peer_connection.h" -#include "pc/rtp_parameters_conversion.h" -#include "pc/video_track.h" -#include "rtc_base/bind.h" -#include "rtc_base/checks.h" -#include "rtc_base/experiments/field_trial_parser.h" -#include "rtc_base/experiments/field_trial_units.h" -#include "rtc_base/numerics/safe_conversions.h" -#include "rtc_base/system/file_wrapper.h" - -#include "tg_rtp_data_engine.h" -#include "tg_peer_connection.h" - -namespace webrtc { - -rtc::scoped_refptr -TgPeerConnectionFactoryInterface::CreatePeerConnection( - const PeerConnectionInterface::RTCConfiguration& configuration, - std::unique_ptr allocator, - std::unique_ptr cert_generator, - PeerConnectionObserver* observer) { - return nullptr; -} - -rtc::scoped_refptr -TgPeerConnectionFactoryInterface::CreatePeerConnection( - const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies dependencies) { - return nullptr; -} - -RtpCapabilities TgPeerConnectionFactoryInterface::GetRtpSenderCapabilities( - cricket::MediaType kind) const { - return {}; -} - -RtpCapabilities TgPeerConnectionFactoryInterface::GetRtpReceiverCapabilities( - cricket::MediaType kind) const { - return {}; -} - -BEGIN_SIGNALING_PROXY_MAP(TgPeerConnection) -PROXY_SIGNALING_THREAD_DESTRUCTOR() -PROXY_METHOD0(rtc::scoped_refptr, local_streams) -PROXY_METHOD0(rtc::scoped_refptr, remote_streams) -PROXY_METHOD1(bool, AddStream, MediaStreamInterface*) -PROXY_METHOD1(void, RemoveStream, MediaStreamInterface*) -PROXY_METHOD2(RTCErrorOr>, - AddTrack, - rtc::scoped_refptr, - const std::vector&) -PROXY_METHOD1(bool, RemoveTrack, RtpSenderInterface*) -PROXY_METHOD1(RTCError, RemoveTrackNew, rtc::scoped_refptr) -PROXY_METHOD1(RTCErrorOr>, - AddTransceiver, - rtc::scoped_refptr) -PROXY_METHOD2(RTCErrorOr>, - AddTransceiver, - rtc::scoped_refptr, - const RtpTransceiverInit&) -PROXY_METHOD1(RTCErrorOr>, - AddTransceiver, - cricket::MediaType) -PROXY_METHOD2(RTCErrorOr>, - AddTransceiver, - cricket::MediaType, - const RtpTransceiverInit&) -PROXY_METHOD2(rtc::scoped_refptr, - CreateSender, - const std::string&, - const std::string&) -PROXY_CONSTMETHOD0(std::vector>, - GetSenders) -PROXY_CONSTMETHOD0(std::vector>, - GetReceivers) -PROXY_CONSTMETHOD0(std::vector>, - GetTransceivers) -PROXY_METHOD0(void, ClearStatsCache) -PROXY_METHOD2(rtc::scoped_refptr, - CreateDataChannel, - const std::string&, - const DataChannelInit*) -PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, local_description) -PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, remote_description) -PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, - current_local_description) -PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, - current_remote_description) -PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, - pending_local_description) -PROXY_CONSTMETHOD0(const SessionDescriptionInterface*, - pending_remote_description) -PROXY_METHOD0(void, RestartIce) -PROXY_METHOD2(void, - CreateOffer, - CreateSessionDescriptionObserver*, - const PeerConnectionInterface::RTCOfferAnswerOptions&) -PROXY_METHOD2(void, - CreateAnswer, - CreateSessionDescriptionObserver*, - const PeerConnectionInterface::RTCOfferAnswerOptions&) -PROXY_METHOD2(void, - SetLocalDescription, - SetSessionDescriptionObserver*, - SessionDescriptionInterface*) -PROXY_METHOD1(void, SetLocalDescription, SetSessionDescriptionObserver*) -PROXY_METHOD2(void, - SetRemoteDescription, - SetSessionDescriptionObserver*, - SessionDescriptionInterface*) -PROXY_METHOD2(void, - SetRemoteDescription, - std::unique_ptr, - rtc::scoped_refptr) -PROXY_METHOD0(PeerConnectionInterface::RTCConfiguration, GetConfiguration) -PROXY_METHOD1(RTCError, - SetConfiguration, - const PeerConnectionInterface::RTCConfiguration&) -PROXY_METHOD1(bool, AddIceCandidate, const IceCandidateInterface*) -PROXY_METHOD2(void, - AddIceCandidate, - std::unique_ptr, - std::function) -PROXY_METHOD1(bool, RemoveIceCandidates, const std::vector&) -PROXY_METHOD1(RTCError, SetBitrate, const BitrateSettings&) -PROXY_METHOD1(void, SetAudioPlayout, bool) -PROXY_METHOD1(void, SetAudioRecording, bool) -PROXY_METHOD1(rtc::scoped_refptr, - LookupDtlsTransportByMid, - const std::string&) -PROXY_CONSTMETHOD0(rtc::scoped_refptr, GetSctpTransport) -PROXY_METHOD0(PeerConnectionInterface::SignalingState, signaling_state) -PROXY_METHOD0(PeerConnectionInterface::IceConnectionState, ice_connection_state) -PROXY_METHOD0(PeerConnectionInterface::IceConnectionState, standardized_ice_connection_state) -PROXY_METHOD0(PeerConnectionInterface::PeerConnectionState, peer_connection_state) -PROXY_METHOD0(PeerConnectionInterface::IceGatheringState, ice_gathering_state) -PROXY_METHOD2(bool, - StartRtcEventLog, - std::unique_ptr, - int64_t) -PROXY_METHOD1(bool, StartRtcEventLog, std::unique_ptr) -PROXY_METHOD0(void, StopRtcEventLog) -PROXY_METHOD0(void, Close) -END_PROXY_MAP() - -TgPeerConnectionFactory::TgPeerConnectionFactory( - PeerConnectionFactoryDependencies dependencies) - : wraps_current_thread_(false), - network_thread_(dependencies.network_thread), - worker_thread_(dependencies.worker_thread), - signaling_thread_(dependencies.signaling_thread), - task_queue_factory_(std::move(dependencies.task_queue_factory)), - media_engine_(std::move(dependencies.media_engine)), - call_factory_(std::move(dependencies.call_factory)), - event_log_factory_(std::move(dependencies.event_log_factory)), - fec_controller_factory_(std::move(dependencies.fec_controller_factory)), - network_state_predictor_factory_( - std::move(dependencies.network_state_predictor_factory)), - injected_network_controller_factory_( - std::move(dependencies.network_controller_factory)), - media_transport_factory_(std::move(dependencies.media_transport_factory)), - neteq_factory_(std::move(dependencies.neteq_factory)), - trials_(dependencies.trials ? std::move(dependencies.trials) - : std::make_unique()) { - if (!network_thread_) { - owned_network_thread_ = rtc::Thread::CreateWithSocketServer(); - owned_network_thread_->SetName("pc_network_thread", nullptr); - owned_network_thread_->Start(); - network_thread_ = owned_network_thread_.get(); - } - - if (!worker_thread_) { - owned_worker_thread_ = rtc::Thread::Create(); - owned_worker_thread_->SetName("pc_worker_thread", nullptr); - owned_worker_thread_->Start(); - worker_thread_ = owned_worker_thread_.get(); - } - - if (!signaling_thread_) { - signaling_thread_ = rtc::Thread::Current(); - if (!signaling_thread_) { - // If this thread isn't already wrapped by an rtc::Thread, create a - // wrapper and own it in this class. - signaling_thread_ = rtc::ThreadManager::Instance()->WrapCurrentThread(); - wraps_current_thread_ = true; - } - } - - options_.disable_encryption = true; -} - -TgPeerConnectionFactory::~TgPeerConnectionFactory() { - RTC_DCHECK(signaling_thread_->IsCurrent()); - channel_manager_.reset(nullptr); - - // Make sure |worker_thread_| and |signaling_thread_| outlive - // |default_socket_factory_| and |default_network_manager_|. - default_socket_factory_ = nullptr; - default_network_manager_ = nullptr; - - if (wraps_current_thread_) - rtc::ThreadManager::Instance()->UnwrapCurrentThread(); -} - -bool TgPeerConnectionFactory::Initialize() { - RTC_DCHECK(signaling_thread_->IsCurrent()); - rtc::InitRandom(rtc::Time32()); - - default_network_manager_.reset(new rtc::BasicNetworkManager()); - if (!default_network_manager_) { - return false; - } - - default_socket_factory_.reset( - new rtc::BasicPacketSocketFactory(network_thread_)); - if (!default_socket_factory_) { - return false; - } - - channel_manager_ = std::make_unique( - std::move(media_engine_), std::make_unique(), - worker_thread_, network_thread_); - - channel_manager_->SetVideoRtxEnabled(true); - if (!channel_manager_->Init()) { - return false; - } - - return true; -} - -void TgPeerConnectionFactory::SetOptions(const PeerConnectionFactory::Options& options) { - options_ = options; -} - -RtpCapabilities TgPeerConnectionFactory::GetRtpSenderCapabilities( - cricket::MediaType kind) const { - RTC_DCHECK_RUN_ON(signaling_thread_); - switch (kind) { - case cricket::MEDIA_TYPE_AUDIO: { - cricket::AudioCodecs cricket_codecs; - cricket::RtpHeaderExtensions cricket_extensions; - channel_manager_->GetSupportedAudioSendCodecs(&cricket_codecs); - channel_manager_->GetSupportedAudioRtpHeaderExtensions( - &cricket_extensions); - return ToRtpCapabilities(cricket_codecs, cricket_extensions); - } - case cricket::MEDIA_TYPE_VIDEO: { - cricket::VideoCodecs cricket_codecs; - cricket::RtpHeaderExtensions cricket_extensions; - channel_manager_->GetSupportedVideoCodecs(&cricket_codecs); - channel_manager_->GetSupportedVideoRtpHeaderExtensions( - &cricket_extensions); - return ToRtpCapabilities(cricket_codecs, cricket_extensions); - } - case cricket::MEDIA_TYPE_DATA: - return RtpCapabilities(); - } - // Not reached; avoids compile warning. - FATAL(); -} - -RtpCapabilities TgPeerConnectionFactory::GetRtpReceiverCapabilities( - cricket::MediaType kind) const { - RTC_DCHECK_RUN_ON(signaling_thread_); - switch (kind) { - case cricket::MEDIA_TYPE_AUDIO: { - cricket::AudioCodecs cricket_codecs; - cricket::RtpHeaderExtensions cricket_extensions; - channel_manager_->GetSupportedAudioReceiveCodecs(&cricket_codecs); - channel_manager_->GetSupportedAudioRtpHeaderExtensions( - &cricket_extensions); - return ToRtpCapabilities(cricket_codecs, cricket_extensions); - } - case cricket::MEDIA_TYPE_VIDEO: { - cricket::VideoCodecs cricket_codecs; - cricket::RtpHeaderExtensions cricket_extensions; - channel_manager_->GetSupportedVideoCodecs(&cricket_codecs); - channel_manager_->GetSupportedVideoRtpHeaderExtensions( - &cricket_extensions); - return ToRtpCapabilities(cricket_codecs, cricket_extensions); - } - case cricket::MEDIA_TYPE_DATA: - return RtpCapabilities(); - } - // Not reached; avoids compile warning. - FATAL(); -} - -rtc::scoped_refptr -TgPeerConnectionFactory::CreateAudioSource(const cricket::AudioOptions& options) { - RTC_DCHECK(signaling_thread_->IsCurrent()); - rtc::scoped_refptr source( - LocalAudioSource::Create(&options)); - return source; -} - -bool TgPeerConnectionFactory::StartAecDump(FILE* file, int64_t max_size_bytes) { - RTC_DCHECK(signaling_thread_->IsCurrent()); - return channel_manager_->StartAecDump(FileWrapper(file), max_size_bytes); -} - -void TgPeerConnectionFactory::StopAecDump() { - RTC_DCHECK(signaling_thread_->IsCurrent()); - channel_manager_->StopAecDump(); -} - -rtc::scoped_refptr -TgPeerConnectionFactory::CreatePeerConnection( - const PeerConnectionInterface::RTCConfiguration& configuration, - std::unique_ptr allocator, - std::unique_ptr cert_generator, - PeerConnectionObserver* observer) { - // Convert the legacy API into the new dependency structure. - PeerConnectionDependencies dependencies(observer); - dependencies.allocator = std::move(allocator); - dependencies.cert_generator = std::move(cert_generator); - // Pass that into the new API. - return CreatePeerConnection(configuration, std::move(dependencies)); -} - -rtc::scoped_refptr -TgPeerConnectionFactory::CreatePeerConnection( - const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies dependencies) { - RTC_DCHECK(signaling_thread_->IsCurrent()); - RTC_DCHECK(!(dependencies.allocator && dependencies.packet_socket_factory)) - << "You can't set both allocator and packet_socket_factory; " - "the former is going away (see bugs.webrtc.org/7447"; - - // Set internal defaults if optional dependencies are not set. - if (!dependencies.cert_generator) { - dependencies.cert_generator = - std::make_unique(signaling_thread_, - network_thread_); - } - if (!dependencies.allocator) { - rtc::PacketSocketFactory* packet_socket_factory; - if (dependencies.packet_socket_factory) - packet_socket_factory = dependencies.packet_socket_factory.get(); - else - packet_socket_factory = default_socket_factory_.get(); - - network_thread_->Invoke(RTC_FROM_HERE, [this, &configuration, - &dependencies, - &packet_socket_factory]() { - dependencies.allocator = std::make_unique( - default_network_manager_.get(), packet_socket_factory, - configuration.turn_customizer); - }); - } - - if (!dependencies.ice_transport_factory) { - dependencies.ice_transport_factory = - std::make_unique(); - } - - // TODO(zstein): Once chromium injects its own AsyncResolverFactory, set - // |dependencies.async_resolver_factory| to a new - // |rtc::BasicAsyncResolverFactory| if no factory is provided. - - network_thread_->Invoke( - RTC_FROM_HERE, - rtc::Bind(&cricket::PortAllocator::SetNetworkIgnoreMask, - dependencies.allocator.get(), options_.network_ignore_mask)); - - std::unique_ptr event_log = - worker_thread_->Invoke>( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnectionFactory::CreateRtcEventLog_w, this)); - - std::unique_ptr call = worker_thread_->Invoke>( - RTC_FROM_HERE, - rtc::Bind(&TgPeerConnectionFactory::CreateCall_w, this, event_log.get())); - - rtc::scoped_refptr pc( - new rtc::RefCountedObject(this, std::move(event_log), - std::move(call))); - if (!pc->Initialize(configuration, std::move(dependencies))) { - return nullptr; - } - return TgPeerConnectionProxy::Create(signaling_thread(), pc); -} - -rtc::scoped_refptr -TgPeerConnectionFactory::CreateLocalMediaStream(const std::string& stream_id) { - RTC_DCHECK(signaling_thread_->IsCurrent()); - return MediaStreamProxy::Create(signaling_thread_, - MediaStream::Create(stream_id)); -} - -rtc::scoped_refptr TgPeerConnectionFactory::CreateVideoTrack( - const std::string& id, - VideoTrackSourceInterface* source) { - RTC_DCHECK(signaling_thread_->IsCurrent()); - rtc::scoped_refptr track( - VideoTrack::Create(id, source, worker_thread_)); - return VideoTrackProxy::Create(signaling_thread_, worker_thread_, track); -} - -rtc::scoped_refptr TgPeerConnectionFactory::CreateAudioTrack( - const std::string& id, - AudioSourceInterface* source) { - RTC_DCHECK(signaling_thread_->IsCurrent()); - rtc::scoped_refptr track(AudioTrack::Create(id, source)); - return AudioTrackProxy::Create(signaling_thread_, track); -} - -std::unique_ptr -TgPeerConnectionFactory::CreateSctpTransportInternalFactory() { -#ifdef HAVE_SCTP - return std::make_unique(network_thread()); -#else - return nullptr; -#endif -} - -cricket::ChannelManager* TgPeerConnectionFactory::channel_manager() { - return channel_manager_.get(); -} - -std::unique_ptr TgPeerConnectionFactory::CreateRtcEventLog_w() { - RTC_DCHECK_RUN_ON(worker_thread_); - - auto encoding_type = RtcEventLog::EncodingType::Legacy; - if (IsTrialEnabled("WebRTC-RtcEventLogNewFormat")) - encoding_type = RtcEventLog::EncodingType::NewFormat; - return event_log_factory_ - ? event_log_factory_->CreateRtcEventLog(encoding_type) - : std::make_unique(); -} - -std::unique_ptr TgPeerConnectionFactory::CreateCall_w( - RtcEventLog* event_log) { - RTC_DCHECK_RUN_ON(worker_thread_); - - webrtc::Call::Config call_config(event_log); - if (!channel_manager_->media_engine() || !call_factory_) { - return nullptr; - } - call_config.audio_state = - channel_manager_->media_engine()->voice().GetAudioState(); - - FieldTrialParameter min_bandwidth("min", DataRate::kbps(30)); - FieldTrialParameter start_bandwidth("start", DataRate::kbps(300)); - FieldTrialParameter max_bandwidth("max", DataRate::kbps(2000)); - ParseFieldTrial({&min_bandwidth, &start_bandwidth, &max_bandwidth}, - trials_->Lookup("WebRTC-PcFactoryDefaultBitrates")); - - call_config.bitrate_config.min_bitrate_bps = - rtc::saturated_cast(min_bandwidth->bps()); - call_config.bitrate_config.start_bitrate_bps = - rtc::saturated_cast(start_bandwidth->bps()); - call_config.bitrate_config.max_bitrate_bps = - rtc::saturated_cast(max_bandwidth->bps()); - - call_config.fec_controller_factory = fec_controller_factory_.get(); - call_config.task_queue_factory = task_queue_factory_.get(); - call_config.network_state_predictor_factory = - network_state_predictor_factory_.get(); - call_config.neteq_factory = neteq_factory_.get(); - - if (IsTrialEnabled("WebRTC-Bwe-InjectedCongestionController")) { - RTC_LOG(LS_INFO) << "Using injected network controller factory"; - call_config.network_controller_factory = - injected_network_controller_factory_.get(); - } else { - RTC_LOG(LS_INFO) << "Using default network controller factory"; - } - - call_config.trials = trials_.get(); - - return std::unique_ptr(call_factory_->CreateCall(call_config)); -} - -bool TgPeerConnectionFactory::IsTrialEnabled(absl::string_view key) const { - RTC_DCHECK(trials_); - return trials_->Lookup(key).find("Enabled") == 0; -} - -} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h b/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h deleted file mode 100644 index e97b02a2a4..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_peer_connection_factory.h +++ /dev/null @@ -1,261 +0,0 @@ - -/* - * Copyright 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TG_PC_PEER_CONNECTION_FACTORY_H_ -#define TG_PC_PEER_CONNECTION_FACTORY_H_ - -#include -#include - -#include "api/media_stream_interface.h" -#include "api/peer_connection_interface.h" -#include "api/scoped_refptr.h" -#include "api/transport/media/media_transport_interface.h" -#include "media/sctp/sctp_transport_internal.h" -#include "pc/channel_manager.h" -#include "rtc_base/rtc_certificate_generator.h" -#include "rtc_base/thread.h" -#include "pc/peer_connection_factory.h" - -namespace rtc { -class BasicNetworkManager; -class BasicPacketSocketFactory; -} // namespace rtc - -namespace webrtc { - -class RtcEventLog; -class TgPeerConnection; -class TgPeerConnectionInterface; - -class RTC_EXPORT TgPeerConnectionFactoryInterface - : public rtc::RefCountInterface { - public: - // Set the options to be used for subsequently created PeerConnections. - virtual void SetOptions(const PeerConnectionFactoryInterface::Options& options) = 0; - - // The preferred way to create a new peer connection. Simply provide the - // configuration and a PeerConnectionDependencies structure. - // TODO(benwright): Make pure virtual once downstream mock PC factory classes - // are updated. - virtual rtc::scoped_refptr CreatePeerConnection( - const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies dependencies); - - // Deprecated; |allocator| and |cert_generator| may be null, in which case - // default implementations will be used. - // - // |observer| must not be null. - // - // Note that this method does not take ownership of |observer|; it's the - // responsibility of the caller to delete it. It can be safely deleted after - // Close has been called on the returned PeerConnection, which ensures no - // more observer callbacks will be invoked. - virtual rtc::scoped_refptr CreatePeerConnection( - const PeerConnectionInterface::RTCConfiguration& configuration, - std::unique_ptr allocator, - std::unique_ptr cert_generator, - PeerConnectionObserver* observer); - - // Returns the capabilities of an RTP sender of type |kind|. - // If for some reason you pass in MEDIA_TYPE_DATA, returns an empty structure. - // TODO(orphis): Make pure virtual when all subclasses implement it. - virtual RtpCapabilities GetRtpSenderCapabilities( - cricket::MediaType kind) const; - - // Returns the capabilities of an RTP receiver of type |kind|. - // If for some reason you pass in MEDIA_TYPE_DATA, returns an empty structure. - // TODO(orphis): Make pure virtual when all subclasses implement it. - virtual RtpCapabilities GetRtpReceiverCapabilities( - cricket::MediaType kind) const; - - virtual rtc::scoped_refptr CreateLocalMediaStream( - const std::string& stream_id) = 0; - - // Creates an AudioSourceInterface. - // |options| decides audio processing settings. - virtual rtc::scoped_refptr CreateAudioSource( - const cricket::AudioOptions& options) = 0; - - // Creates a new local VideoTrack. The same |source| can be used in several - // tracks. - virtual rtc::scoped_refptr CreateVideoTrack( - const std::string& label, - VideoTrackSourceInterface* source) = 0; - - // Creates an new AudioTrack. At the moment |source| can be null. - virtual rtc::scoped_refptr CreateAudioTrack( - const std::string& label, - AudioSourceInterface* source) = 0; - - // Starts AEC dump using existing file. Takes ownership of |file| and passes - // it on to VoiceEngine (via other objects) immediately, which will take - // the ownerhip. If the operation fails, the file will be closed. - // A maximum file size in bytes can be specified. When the file size limit is - // reached, logging is stopped automatically. If max_size_bytes is set to a - // value <= 0, no limit will be used, and logging will continue until the - // StopAecDump function is called. - // TODO(webrtc:6463): Delete default implementation when downstream mocks - // classes are updated. - virtual bool StartAecDump(FILE* file, int64_t max_size_bytes) { - return false; - } - - // Stops logging the AEC dump. - virtual void StopAecDump() = 0; - - protected: - // Dtor and ctor protected as objects shouldn't be created or deleted via - // this interface. - TgPeerConnectionFactoryInterface() {} - ~TgPeerConnectionFactoryInterface() override = default; -}; - -class TgPeerConnectionFactory: public TgPeerConnectionFactoryInterface { - public: - void SetOptions(const PeerConnectionFactoryInterface::Options& options); - - rtc::scoped_refptr CreatePeerConnection( - const PeerConnectionInterface::RTCConfiguration& configuration, - std::unique_ptr allocator, - std::unique_ptr cert_generator, - PeerConnectionObserver* observer); - - rtc::scoped_refptr CreatePeerConnection( - const PeerConnectionInterface::RTCConfiguration& configuration, - PeerConnectionDependencies dependencies); - - bool Initialize(); - - RtpCapabilities GetRtpSenderCapabilities( - cricket::MediaType kind) const; - - RtpCapabilities GetRtpReceiverCapabilities( - cricket::MediaType kind) const; - - rtc::scoped_refptr CreateLocalMediaStream( - const std::string& stream_id); - - rtc::scoped_refptr CreateAudioSource( - const cricket::AudioOptions& options); - - rtc::scoped_refptr CreateVideoTrack( - const std::string& id, - VideoTrackSourceInterface* video_source); - - rtc::scoped_refptr CreateAudioTrack( - const std::string& id, - AudioSourceInterface* audio_source); - - bool StartAecDump(FILE* file, int64_t max_size_bytes); - void StopAecDump(); - - virtual std::unique_ptr - CreateSctpTransportInternalFactory(); - - virtual cricket::ChannelManager* channel_manager(); - - rtc::Thread* signaling_thread() { - // This method can be called on a different thread when the factory is - // created in CreatePeerConnectionFactory(). - return signaling_thread_; - } - rtc::Thread* worker_thread() { return worker_thread_; } - rtc::Thread* network_thread() { return network_thread_; } - - const PeerConnectionFactoryInterface::Options& options() const { return options_; } - - MediaTransportFactory* media_transport_factory() { - return media_transport_factory_.get(); - } - - protected: - // This structure allows simple management of all new dependencies being added - // to the PeerConnectionFactory. - explicit TgPeerConnectionFactory( - PeerConnectionFactoryDependencies dependencies); - - // Hook to let testing framework insert actions between - // "new RTCPeerConnection" and "pc.Initialize" - virtual void ActionsBeforeInitializeForTesting(PeerConnectionInterface*) {} - - virtual ~TgPeerConnectionFactory(); - - private: - bool IsTrialEnabled(absl::string_view key) const; - - std::unique_ptr CreateRtcEventLog_w(); - std::unique_ptr CreateCall_w(RtcEventLog* event_log); - - bool wraps_current_thread_; - rtc::Thread* network_thread_; - rtc::Thread* worker_thread_; - rtc::Thread* signaling_thread_; - std::unique_ptr owned_network_thread_; - std::unique_ptr owned_worker_thread_; - const std::unique_ptr task_queue_factory_; - PeerConnectionFactoryInterface::Options options_; - std::unique_ptr channel_manager_; - std::unique_ptr default_network_manager_; - std::unique_ptr default_socket_factory_; - std::unique_ptr media_engine_; - std::unique_ptr call_factory_; - std::unique_ptr event_log_factory_; - std::unique_ptr fec_controller_factory_; - std::unique_ptr - network_state_predictor_factory_; - std::unique_ptr - injected_network_controller_factory_; - std::unique_ptr media_transport_factory_; - std::unique_ptr neteq_factory_; - const std::unique_ptr trials_; -}; - -BEGIN_SIGNALING_PROXY_MAP(TgPeerConnectionFactory) -PROXY_SIGNALING_THREAD_DESTRUCTOR() -PROXY_METHOD1(void, SetOptions, const PeerConnectionFactory::Options&) -PROXY_METHOD4(rtc::scoped_refptr, - CreatePeerConnection, - const PeerConnectionInterface::RTCConfiguration&, - std::unique_ptr, - std::unique_ptr, - PeerConnectionObserver*) -PROXY_METHOD2(rtc::scoped_refptr, - CreatePeerConnection, - const PeerConnectionInterface::RTCConfiguration&, - PeerConnectionDependencies) -PROXY_CONSTMETHOD1(webrtc::RtpCapabilities, - GetRtpSenderCapabilities, - cricket::MediaType) -PROXY_CONSTMETHOD1(webrtc::RtpCapabilities, - GetRtpReceiverCapabilities, - cricket::MediaType) -PROXY_METHOD1(rtc::scoped_refptr, - CreateLocalMediaStream, - const std::string&) -PROXY_METHOD1(rtc::scoped_refptr, - CreateAudioSource, - const cricket::AudioOptions&) -PROXY_METHOD2(rtc::scoped_refptr, - CreateVideoTrack, - const std::string&, - VideoTrackSourceInterface*) -PROXY_METHOD2(rtc::scoped_refptr, - CreateAudioTrack, - const std::string&, - AudioSourceInterface*) -PROXY_METHOD2(bool, StartAecDump, FILE*, int64_t) -PROXY_METHOD0(void, StopAecDump) -END_PROXY_MAP() - -} // namespace webrtc - -#endif // PC_PEER_CONNECTION_FACTORY_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp deleted file mode 100644 index a784e3814c..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_rtp_data_engine.h" - -#include - -#include "absl/strings/match.h" -#include "media/base/codec.h" -#include "media/base/media_constants.h" -#include "media/base/rtp_utils.h" -#include "media/base/stream_params.h" -#include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/data_rate_limiter.h" -#include "rtc_base/helpers.h" -#include "rtc_base/logging.h" -#include "rtc_base/sanitizer.h" - -namespace cricket { - -// We want to avoid IP fragmentation. -static const size_t kDataMaxRtpPacketLen = 1200U; -// We reserve space after the RTP header for future wiggle room. -static const unsigned char kReservedSpace[] = {0x00, 0x00, 0x00, 0x00}; - -// Amount of overhead SRTP may take. We need to leave room in the -// buffer for it, otherwise SRTP will fail later. If SRTP ever uses -// more than this, we need to increase this number. -static const size_t kMaxSrtpHmacOverhead = 16; - -TgRtpDataEngine::TgRtpDataEngine() { - data_codecs_.push_back( - DataCodec(kGoogleRtpDataCodecPlType, kGoogleRtpDataCodecName)); -} - -DataMediaChannel* TgRtpDataEngine::CreateChannel(const MediaConfig& config) { - return new TgRtpDataMediaChannel(config); -} - -static const DataCodec* FindCodecByName(const std::vector& codecs, - const std::string& name) { - for (const DataCodec& codec : codecs) { - if (absl::EqualsIgnoreCase(name, codec.name)) - return &codec; - } - return nullptr; -} - -TgRtpDataMediaChannel::TgRtpDataMediaChannel(const MediaConfig& config) - : DataMediaChannel(config) { - Construct(); - SetPreferredDscp(rtc::DSCP_AF41); -} - -void TgRtpDataMediaChannel::Construct() { - sending_ = false; - receiving_ = false; - send_limiter_.reset(new rtc::DataRateLimiter(kDataMaxBandwidth / 8, 1.0)); -} - -TgRtpDataMediaChannel::~TgRtpDataMediaChannel() { - std::map::const_iterator iter; - for (iter = rtp_clock_by_send_ssrc_.begin(); - iter != rtp_clock_by_send_ssrc_.end(); ++iter) { - delete iter->second; - } -} - -const DataCodec* TgFindUnknownCodec(const std::vector& codecs) { - DataCodec data_codec(kGoogleRtpDataCodecPlType, kGoogleRtpDataCodecName); - std::vector::const_iterator iter; - for (iter = codecs.begin(); iter != codecs.end(); ++iter) { - if (!iter->Matches(data_codec)) { - return &(*iter); - } - } - return NULL; -} - -const DataCodec* TgFindKnownCodec(const std::vector& codecs) { - DataCodec data_codec(kGoogleRtpDataCodecPlType, kGoogleRtpDataCodecName); - std::vector::const_iterator iter; - for (iter = codecs.begin(); iter != codecs.end(); ++iter) { - if (iter->Matches(data_codec)) { - return &(*iter); - } - } - return NULL; -} - -bool TgRtpDataMediaChannel::SetRecvCodecs(const std::vector& codecs) { - const DataCodec* unknown_codec = TgFindUnknownCodec(codecs); - if (unknown_codec) { - RTC_LOG(LS_WARNING) << "Failed to SetRecvCodecs because of unknown codec: " - << unknown_codec->ToString(); - return false; - } - - recv_codecs_ = codecs; - return true; -} - -bool TgRtpDataMediaChannel::SetSendCodecs(const std::vector& codecs) { - const DataCodec* known_codec = TgFindKnownCodec(codecs); - if (!known_codec) { - RTC_LOG(LS_WARNING) - << "Failed to SetSendCodecs because there is no known codec."; - return false; - } - - send_codecs_ = codecs; - return true; -} - -bool TgRtpDataMediaChannel::SetSendParameters(const DataSendParameters& params) { - return (SetSendCodecs(params.codecs) && - SetMaxSendBandwidth(params.max_bandwidth_bps)); -} - -bool TgRtpDataMediaChannel::SetRecvParameters(const DataRecvParameters& params) { - return SetRecvCodecs(params.codecs); -} - -bool TgRtpDataMediaChannel::AddSendStream(const StreamParams& stream) { - if (!stream.has_ssrcs()) { - return false; - } - - if (GetStreamBySsrc(send_streams_, stream.first_ssrc())) { - RTC_LOG(LS_WARNING) << "Not adding data send stream '" << stream.id - << "' with ssrc=" << stream.first_ssrc() - << " because stream already exists."; - return false; - } - - send_streams_.push_back(stream); - // TODO(pthatcher): This should be per-stream, not per-ssrc. - // And we should probably allow more than one per stream. - rtp_clock_by_send_ssrc_[stream.first_ssrc()] = - new RtpClock(kDataCodecClockrate, rtc::CreateRandomNonZeroId(), - rtc::CreateRandomNonZeroId()); - - RTC_LOG(LS_INFO) << "Added data send stream '" << stream.id - << "' with ssrc=" << stream.first_ssrc(); - return true; -} - -bool TgRtpDataMediaChannel::RemoveSendStream(uint32_t ssrc) { - if (!GetStreamBySsrc(send_streams_, ssrc)) { - return false; - } - - RemoveStreamBySsrc(&send_streams_, ssrc); - delete rtp_clock_by_send_ssrc_[ssrc]; - rtp_clock_by_send_ssrc_.erase(ssrc); - return true; -} - -bool TgRtpDataMediaChannel::AddRecvStream(const StreamParams& stream) { - if (!stream.has_ssrcs()) { - return false; - } - - if (GetStreamBySsrc(recv_streams_, stream.first_ssrc())) { - RTC_LOG(LS_WARNING) << "Not adding data recv stream '" << stream.id - << "' with ssrc=" << stream.first_ssrc() - << " because stream already exists."; - return false; - } - - recv_streams_.push_back(stream); - RTC_LOG(LS_INFO) << "Added data recv stream '" << stream.id - << "' with ssrc=" << stream.first_ssrc(); - return true; -} - -bool TgRtpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) { - RemoveStreamBySsrc(&recv_streams_, ssrc); - return true; -} - -// Not implemented. -void TgRtpDataMediaChannel::ResetUnsignaledRecvStream() {} - -void TgRtpDataMediaChannel::OnPacketReceived(rtc::CopyOnWriteBuffer packet, - int64_t /* packet_time_us */) { - RtpHeader header; - if (!GetRtpHeader(packet.cdata(), packet.size(), &header)) { - return; - } - - size_t header_length; - if (!GetRtpHeaderLen(packet.cdata(), packet.size(), &header_length)) { - return; - } - const char* data = - packet.cdata() + header_length + sizeof(kReservedSpace); - size_t data_len = packet.size() - header_length - sizeof(kReservedSpace); - - if (!receiving_) { - RTC_LOG(LS_WARNING) << "Not receiving packet " << header.ssrc << ":" - << header.seq_num << " before SetReceive(true) called."; - return; - } - - if (!FindCodecById(recv_codecs_, header.payload_type)) { - return; - } - - if (!GetStreamBySsrc(recv_streams_, header.ssrc)) { - RTC_LOG(LS_WARNING) << "Received packet for unknown ssrc: " << header.ssrc; - return; - } - - // Uncomment this for easy debugging. - // const auto* found_stream = GetStreamBySsrc(recv_streams_, header.ssrc); - // RTC_LOG(LS_INFO) << "Received packet" - // << " groupid=" << found_stream.groupid - // << ", ssrc=" << header.ssrc - // << ", seqnum=" << header.seq_num - // << ", timestamp=" << header.timestamp - // << ", len=" << data_len; - - ReceiveDataParams params; - params.ssrc = header.ssrc; - params.seq_num = header.seq_num; - params.timestamp = header.timestamp; - SignalDataReceived(params, data, data_len); -} - -bool TgRtpDataMediaChannel::SetMaxSendBandwidth(int bps) { - if (bps <= 0) { - bps = kDataMaxBandwidth; - } - send_limiter_.reset(new rtc::DataRateLimiter(bps / 8, 1.0)); - RTC_LOG(LS_INFO) << "TgRtpDataMediaChannel::SetSendBandwidth to " << bps - << "bps."; - return true; -} - -bool TgRtpDataMediaChannel::SendData(const SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - SendDataResult* result) { - if (result) { - // If we return true, we'll set this to SDR_SUCCESS. - *result = SDR_ERROR; - } - if (!sending_) { - RTC_LOG(LS_WARNING) << "Not sending packet with ssrc=" << params.ssrc - << " len=" << payload.size() - << " before SetSend(true)."; - return false; - } - - if (params.type != cricket::DMT_TEXT) { - RTC_LOG(LS_WARNING) - << "Not sending data because binary type is unsupported."; - return false; - } - - const StreamParams* found_stream = - GetStreamBySsrc(send_streams_, params.ssrc); - if (!found_stream) { - RTC_LOG(LS_WARNING) << "Not sending data because ssrc is unknown: " - << params.ssrc; - return false; - } - - const DataCodec* found_codec = - FindCodecByName(send_codecs_, kGoogleRtpDataCodecName); - if (!found_codec) { - RTC_LOG(LS_WARNING) << "Not sending data because codec is unknown: " - << kGoogleRtpDataCodecName; - return false; - } - - size_t packet_len = (kMinRtpPacketLen + sizeof(kReservedSpace) + - payload.size() + kMaxSrtpHmacOverhead); - if (packet_len > kDataMaxRtpPacketLen) { - return false; - } - - double now = - rtc::TimeMicros() / static_cast(rtc::kNumMicrosecsPerSec); - - if (!send_limiter_->CanUse(packet_len, now)) { - RTC_LOG(LS_VERBOSE) << "Dropped data packet of len=" << packet_len - << "; already sent " << send_limiter_->used_in_period() - << "/" << send_limiter_->max_per_period(); - return false; - } - - RtpHeader header; - header.payload_type = found_codec->id; - header.ssrc = params.ssrc; - rtp_clock_by_send_ssrc_[header.ssrc]->Tick(now, &header.seq_num, - &header.timestamp); - - rtc::CopyOnWriteBuffer packet(kMinRtpPacketLen, packet_len); - if (!SetRtpHeader(packet.data(), packet.size(), header)) { - return false; - } - packet.AppendData(kReservedSpace); - packet.AppendData(payload); - - RTC_LOG(LS_VERBOSE) << "Sent RTP data packet: " - << " stream=" << found_stream->id - << " ssrc=" << header.ssrc - << ", seqnum=" << header.seq_num - << ", timestamp=" << header.timestamp - << ", len=" << payload.size(); - - rtc::PacketOptions options; - options.info_signaled_after_sent.packet_type = rtc::PacketType::kData; - MediaChannel::SendPacket(&packet, options); - send_limiter_->Use(packet_len, now); - if (result) { - *result = SDR_SUCCESS; - } - return true; -} - -} // namespace cricket diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.h b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.h deleted file mode 100644 index ed2a9f3dc3..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_data_engine.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TG_MEDIA_BASE_RTP_DATA_ENGINE_H_ -#define TG_MEDIA_BASE_RTP_DATA_ENGINE_H_ - -#include -#include -#include -#include - -#include "media/base/codec.h" -#include "media/base/media_channel.h" -#include "media/base/media_constants.h" -#include "media/base/media_engine.h" - -namespace rtc { -class DataRateLimiter; -} - -namespace cricket { - -class TgRtpDataEngine : public DataEngineInterface { - public: - TgRtpDataEngine(); - - virtual DataMediaChannel* CreateChannel(const MediaConfig& config); - - virtual const std::vector& data_codecs() { return data_codecs_; } - - private: - std::vector data_codecs_; -}; - -// Keep track of sequence number and timestamp of an RTP stream. The -// sequence number starts with a "random" value and increments. The -// timestamp starts with a "random" value and increases monotonically -// according to the clockrate. -class RtpClock { - public: - RtpClock(int clockrate, uint16_t first_seq_num, uint32_t timestamp_offset) - : clockrate_(clockrate), - last_seq_num_(first_seq_num), - timestamp_offset_(timestamp_offset) {} - - // Given the current time (in number of seconds which must be - // monotonically increasing), Return the next sequence number and - // timestamp. - void Tick(double now, int* seq_num, uint32_t* timestamp); - - private: - int clockrate_; - uint16_t last_seq_num_; - uint32_t timestamp_offset_; -}; - -class TgRtpDataMediaChannel : public DataMediaChannel { - public: - explicit TgRtpDataMediaChannel(const MediaConfig& config); - virtual ~TgRtpDataMediaChannel(); - - virtual bool SetSendParameters(const DataSendParameters& params); - virtual bool SetRecvParameters(const DataRecvParameters& params); - virtual bool AddSendStream(const StreamParams& sp); - virtual bool RemoveSendStream(uint32_t ssrc); - virtual bool AddRecvStream(const StreamParams& sp); - virtual bool RemoveRecvStream(uint32_t ssrc); - virtual void ResetUnsignaledRecvStream(); - virtual bool SetSend(bool send) { - sending_ = send; - return true; - } - virtual bool SetReceive(bool receive) { - receiving_ = receive; - return true; - } - virtual void OnPacketReceived(rtc::CopyOnWriteBuffer packet, - int64_t packet_time_us); - virtual void OnReadyToSend(bool ready) {} - virtual bool SendData(const SendDataParams& params, - const rtc::CopyOnWriteBuffer& payload, - SendDataResult* result); - - private: - void Construct(); - bool SetMaxSendBandwidth(int bps); - bool SetSendCodecs(const std::vector& codecs); - bool SetRecvCodecs(const std::vector& codecs); - - bool sending_; - bool receiving_; - std::vector send_codecs_; - std::vector recv_codecs_; - std::vector send_streams_; - std::vector recv_streams_; - std::map rtp_clock_by_send_ssrc_; - std::unique_ptr send_limiter_; -}; - -} // namespace cricket - -#endif // MEDIA_BASE_RTP_DATA_ENGINE_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp deleted file mode 100644 index f612cf3eb4..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright 2015 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_rtp_sender.h" - -#include -#include -#include - -#include "api/audio_options.h" -#include "api/media_stream_interface.h" -#include "media/base/media_engine.h" -#include "pc/peer_connection.h" -#include "pc/stats_collector.h" -#include "rtc_base/checks.h" -#include "rtc_base/helpers.h" -#include "rtc_base/location.h" -#include "rtc_base/logging.h" -#include "rtc_base/trace_event.h" - -namespace webrtc { - -namespace { - -// This function is only expected to be called on the signaling thread. -// On the other hand, some test or even production setups may use -// several signaling threads. -int GenerateUniqueId() { - static std::atomic g_unique_id{0}; - - return ++g_unique_id; -} - -// Returns true if a "per-sender" encoding parameter contains a value that isn't -// its default. Currently max_bitrate_bps and bitrate_priority both are -// implemented "per-sender," meaning that these encoding parameters -// are used for the RtpSender as a whole, not for a specific encoding layer. -// This is done by setting these encoding parameters at index 0 of -// RtpParameters.encodings. This function can be used to check if these -// parameters are set at any index other than 0 of RtpParameters.encodings, -// because they are currently unimplemented to be used for a specific encoding -// layer. -bool PerSenderRtpEncodingParameterHasValue( - const RtpEncodingParameters& encoding_params) { - if (encoding_params.bitrate_priority != kDefaultBitratePriority || - encoding_params.network_priority != kDefaultBitratePriority) { - return true; - } - return false; -} - -void RemoveEncodingLayers(const std::vector& rids, - std::vector* encodings) { - RTC_DCHECK(encodings); - encodings->erase( - std::remove_if(encodings->begin(), encodings->end(), - [&rids](const RtpEncodingParameters& encoding) { - return absl::c_linear_search(rids, encoding.rid); - }), - encodings->end()); -} - -RtpParameters RestoreEncodingLayers( - const RtpParameters& parameters, - const std::vector& removed_rids, - const std::vector& all_layers) { - RTC_DCHECK_EQ(parameters.encodings.size() + removed_rids.size(), - all_layers.size()); - RtpParameters result(parameters); - result.encodings.clear(); - size_t index = 0; - for (const RtpEncodingParameters& encoding : all_layers) { - if (absl::c_linear_search(removed_rids, encoding.rid)) { - result.encodings.push_back(encoding); - continue; - } - result.encodings.push_back(parameters.encodings[index++]); - } - return result; -} - -} // namespace - -// Returns true if any RtpParameters member that isn't implemented contains a -// value. -bool TgUnimplementedRtpParameterHasValue(const RtpParameters& parameters) { - if (!parameters.mid.empty()) { - return true; - } - for (size_t i = 0; i < parameters.encodings.size(); ++i) { - // Encoding parameters that are per-sender should only contain value at - // index 0. - if (i != 0 && - PerSenderRtpEncodingParameterHasValue(parameters.encodings[i])) { - return true; - } - } - return false; -} - -TgLocalAudioSinkAdapter::TgLocalAudioSinkAdapter() : sink_(nullptr) {} - -TgLocalAudioSinkAdapter::~TgLocalAudioSinkAdapter() { - rtc::CritScope lock(&lock_); - if (sink_) - sink_->OnClose(); -} - -void TgLocalAudioSinkAdapter::OnData(const void* audio_data, - int bits_per_sample, - int sample_rate, - size_t number_of_channels, - size_t number_of_frames) { - rtc::CritScope lock(&lock_); - if (sink_) { - sink_->OnData(audio_data, bits_per_sample, sample_rate, number_of_channels, - number_of_frames); - } -} - -void TgLocalAudioSinkAdapter::SetSink(cricket::AudioSource::Sink* sink) { - rtc::CritScope lock(&lock_); - RTC_DCHECK(!sink || !sink_); - sink_ = sink; -} - -rtc::scoped_refptr TgAudioRtpSender::Create( - rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer) { - return rtc::scoped_refptr( - new rtc::RefCountedObject(worker_thread, id, - set_streams_observer)); -} - -TgAudioRtpSender::TgAudioRtpSender(rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer) - : RtpSenderBase(worker_thread, id, set_streams_observer), - dtmf_sender_proxy_(DtmfSenderProxy::Create( - rtc::Thread::Current(), - DtmfSender::Create(rtc::Thread::Current(), this))), - sink_adapter_(new TgLocalAudioSinkAdapter()) {} - -TgAudioRtpSender::~TgAudioRtpSender() { - // For DtmfSender. - SignalDestroyed(); - Stop(); -} - -bool TgAudioRtpSender::CanInsertDtmf() { - if (!media_channel_) { - RTC_LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists."; - return false; - } - // Check that this RTP sender is active (description has been applied that - // matches an SSRC to its ID). - if (!ssrc_) { - RTC_LOG(LS_ERROR) << "CanInsertDtmf: Sender does not have SSRC."; - return false; - } - return worker_thread_->Invoke( - RTC_FROM_HERE, [&] { return voice_media_channel()->CanInsertDtmf(); }); -} - -bool TgAudioRtpSender::InsertDtmf(int code, int duration) { - if (!media_channel_) { - RTC_LOG(LS_ERROR) << "InsertDtmf: No audio channel exists."; - return false; - } - if (!ssrc_) { - RTC_LOG(LS_ERROR) << "InsertDtmf: Sender does not have SSRC."; - return false; - } - bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return voice_media_channel()->InsertDtmf(ssrc_, code, duration); - }); - if (!success) { - RTC_LOG(LS_ERROR) << "Failed to insert DTMF to channel."; - } - return success; -} - -sigslot::signal0<>* TgAudioRtpSender::GetOnDestroyedSignal() { - return &SignalDestroyed; -} - -void TgAudioRtpSender::OnChanged() { - TRACE_EVENT0("webrtc", "TgAudioRtpSender::OnChanged"); - RTC_DCHECK(!stopped_); - if (cached_track_enabled_ != track_->enabled()) { - cached_track_enabled_ = track_->enabled(); - if (can_send_track()) { - SetSend(); - } - } -} - -void TgAudioRtpSender::DetachTrack() { - RTC_DCHECK(track_); - audio_track()->RemoveSink(sink_adapter_.get()); -} - -void TgAudioRtpSender::AttachTrack() { - RTC_DCHECK(track_); - cached_track_enabled_ = track_->enabled(); - audio_track()->AddSink(sink_adapter_.get()); -} - -void TgAudioRtpSender::AddTrackToStats() { -} - -void TgAudioRtpSender::RemoveTrackFromStats() { -} - -rtc::scoped_refptr TgAudioRtpSender::GetDtmfSender() const { - return dtmf_sender_proxy_; -} - -void TgAudioRtpSender::SetSend() { - RTC_DCHECK(!stopped_); - RTC_DCHECK(can_send_track()); - if (!media_channel_) { - RTC_LOG(LS_ERROR) << "SetAudioSend: No audio channel exists."; - return; - } - cricket::AudioOptions options; -#if !defined(WEBRTC_CHROMIUM_BUILD) && !defined(WEBRTC_WEBKIT_BUILD) - // TODO(tommi): Remove this hack when we move CreateAudioSource out of - // PeerConnection. This is a bit of a strange way to apply local audio - // options since it is also applied to all streams/channels, local or remote. - if (track_->enabled() && audio_track()->GetSource() && - !audio_track()->GetSource()->remote()) { - options = audio_track()->GetSource()->options(); - } -#endif - - // |track_->enabled()| hops to the signaling thread, so call it before we hop - // to the worker thread or else it will deadlock. - bool track_enabled = track_->enabled(); - bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return voice_media_channel()->SetAudioSend(ssrc_, track_enabled, &options, - sink_adapter_.get()); - }); - if (!success) { - RTC_LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc_; - } -} - -void TgAudioRtpSender::ClearSend() { - RTC_DCHECK(ssrc_ != 0); - RTC_DCHECK(!stopped_); - if (!media_channel_) { - RTC_LOG(LS_WARNING) << "ClearAudioSend: No audio channel exists."; - return; - } - cricket::AudioOptions options; - bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return voice_media_channel()->SetAudioSend(ssrc_, false, &options, nullptr); - }); - if (!success) { - RTC_LOG(LS_WARNING) << "ClearAudioSend: ssrc is incorrect: " << ssrc_; - } -} - -rtc::scoped_refptr TgVideoRtpSender::Create( - rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer) { - return rtc::scoped_refptr( - new rtc::RefCountedObject(worker_thread, id, - set_streams_observer)); -} - -TgVideoRtpSender::TgVideoRtpSender(rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer) - : RtpSenderBase(worker_thread, id, set_streams_observer) {} - -TgVideoRtpSender::~TgVideoRtpSender() { - Stop(); -} - -void TgVideoRtpSender::OnChanged() { - TRACE_EVENT0("webrtc", "TgVideoRtpSender::OnChanged"); - RTC_DCHECK(!stopped_); - if (cached_track_content_hint_ != video_track()->content_hint()) { - cached_track_content_hint_ = video_track()->content_hint(); - if (can_send_track()) { - SetSend(); - } - } -} - -void TgVideoRtpSender::AttachTrack() { - RTC_DCHECK(track_); - cached_track_content_hint_ = video_track()->content_hint(); -} - -rtc::scoped_refptr TgVideoRtpSender::GetDtmfSender() const { - RTC_LOG(LS_ERROR) << "Tried to get DTMF sender from video sender."; - return nullptr; -} - -void TgVideoRtpSender::SetSend() { - RTC_DCHECK(!stopped_); - RTC_DCHECK(can_send_track()); - if (!media_channel_) { - RTC_LOG(LS_ERROR) << "SetVideoSend: No video channel exists."; - return; - } - cricket::VideoOptions options; - VideoTrackSourceInterface* source = video_track()->GetSource(); - if (source) { - options.is_screencast = source->is_screencast(); - options.video_noise_reduction = source->needs_denoising(); - } - switch (cached_track_content_hint_) { - case VideoTrackInterface::ContentHint::kNone: - break; - case VideoTrackInterface::ContentHint::kFluid: - options.is_screencast = false; - break; - case VideoTrackInterface::ContentHint::kDetailed: - case VideoTrackInterface::ContentHint::kText: - options.is_screencast = true; - break; - } - bool success = worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return video_media_channel()->SetVideoSend(ssrc_, &options, video_track()); - }); - RTC_DCHECK(success); -} - -void TgVideoRtpSender::ClearSend() { - RTC_DCHECK(ssrc_ != 0); - RTC_DCHECK(!stopped_); - if (!media_channel_) { - RTC_LOG(LS_WARNING) << "SetVideoSend: No video channel exists."; - return; - } - // Allow SetVideoSend to fail since |enable| is false and |source| is null. - // This the normal case when the underlying media channel has already been - // deleted. - worker_thread_->Invoke(RTC_FROM_HERE, [&] { - return video_media_channel()->SetVideoSend(ssrc_, nullptr, nullptr); - }); -} - -} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h deleted file mode 100644 index 0822bd4ce4..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_sender.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2015 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -// This file contains classes that implement RtpSenderInterface. -// An RtpSender associates a MediaStreamTrackInterface with an underlying -// transport (provided by AudioProviderInterface/VideoProviderInterface) - -#ifndef TG_PC_RTP_SENDER_H_ -#define TG_PC_RTP_SENDER_H_ - -#include -#include -#include - -#include "api/media_stream_interface.h" -#include "api/rtp_sender_interface.h" -#include "media/base/audio_source.h" -#include "media/base/media_channel.h" -#include "pc/dtmf_sender.h" -#include "rtc_base/critical_section.h" - -#include "pc/rtp_sender.h" - -namespace webrtc { - -class StatsCollector; - -bool TgUnimplementedRtpParameterHasValue(const RtpParameters& parameters); - -// TgLocalAudioSinkAdapter receives data callback as a sink to the local -// AudioTrack, and passes the data to the sink of AudioSource. -class TgLocalAudioSinkAdapter : public AudioTrackSinkInterface, - public cricket::AudioSource { - public: - TgLocalAudioSinkAdapter(); - virtual ~TgLocalAudioSinkAdapter(); - - private: - // AudioSinkInterface implementation. - void OnData(const void* audio_data, - int bits_per_sample, - int sample_rate, - size_t number_of_channels, - size_t number_of_frames) override; - - // cricket::AudioSource implementation. - void SetSink(cricket::AudioSource::Sink* sink) override; - - cricket::AudioSource::Sink* sink_; - // Critical section protecting |sink_|. - rtc::CriticalSection lock_; -}; - -class TgAudioRtpSender : public DtmfProviderInterface, public RtpSenderBase { - public: - // Construct an RtpSender for audio with the given sender ID. - // The sender is initialized with no track to send and no associated streams. - // StatsCollector provided so that Add/RemoveLocalAudioTrack can be called - // at the appropriate times. - // If |set_streams_observer| is not null, it is invoked when SetStreams() - // is called. |set_streams_observer| is not owned by this object. If not - // null, it must be valid at least until this sender becomes stopped. - static rtc::scoped_refptr Create( - rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer); - virtual ~TgAudioRtpSender(); - - // DtmfSenderProvider implementation. - bool CanInsertDtmf() override; - bool InsertDtmf(int code, int duration) override; - sigslot::signal0<>* GetOnDestroyedSignal() override; - - // ObserverInterface implementation. - void OnChanged() override; - - cricket::MediaType media_type() const override { - return cricket::MEDIA_TYPE_AUDIO; - } - std::string track_kind() const override { - return MediaStreamTrackInterface::kAudioKind; - } - - rtc::scoped_refptr GetDtmfSender() const override; - - protected: - TgAudioRtpSender(rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer); - - void SetSend() override; - void ClearSend() override; - - // Hooks to allow custom logic when tracks are attached and detached. - void AttachTrack() override; - void DetachTrack() override; - void AddTrackToStats() override; - void RemoveTrackFromStats() override; - - private: - cricket::VoiceMediaChannel* voice_media_channel() { - return static_cast(media_channel_); - } - rtc::scoped_refptr audio_track() const { - return rtc::scoped_refptr( - static_cast(track_.get())); - } - sigslot::signal0<> SignalDestroyed; - - rtc::scoped_refptr dtmf_sender_proxy_; - bool cached_track_enabled_ = false; - - // Used to pass the data callback from the |track_| to the other end of - // cricket::AudioSource. - std::unique_ptr sink_adapter_; -}; - -class TgVideoRtpSender : public RtpSenderBase { - public: - // Construct an RtpSender for video with the given sender ID. - // The sender is initialized with no track to send and no associated streams. - // If |set_streams_observer| is not null, it is invoked when SetStreams() - // is called. |set_streams_observer| is not owned by this object. If not - // null, it must be valid at least until this sender becomes stopped. - static rtc::scoped_refptr Create( - rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer); - virtual ~TgVideoRtpSender(); - - // ObserverInterface implementation - void OnChanged() override; - - cricket::MediaType media_type() const override { - return cricket::MEDIA_TYPE_VIDEO; - } - std::string track_kind() const override { - return MediaStreamTrackInterface::kVideoKind; - } - - rtc::scoped_refptr GetDtmfSender() const override; - - protected: - TgVideoRtpSender(rtc::Thread* worker_thread, - const std::string& id, - SetStreamsObserver* set_streams_observer); - - void SetSend() override; - void ClearSend() override; - - // Hook to allow custom logic when tracks are attached. - void AttachTrack() override; - - private: - cricket::VideoMediaChannel* video_media_channel() { - return static_cast(media_channel_); - } - rtc::scoped_refptr video_track() const { - return rtc::scoped_refptr( - static_cast(track_.get())); - } - - VideoTrackInterface::ContentHint cached_track_content_hint_ = - VideoTrackInterface::ContentHint::kNone; -}; - -} // namespace webrtc - -#endif // PC_RTP_SENDER_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.cpp deleted file mode 100644 index 7c1e75cfb8..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_rtp_transport.h" - -#include - -#include -#include - -#include "api/rtp_headers.h" -#include "api/rtp_parameters.h" -#include "media/base/rtp_utils.h" -#include "modules/rtp_rtcp/source/rtp_packet_received.h" -#include "rtc_base/checks.h" -#include "rtc_base/copy_on_write_buffer.h" -#include "rtc_base/logging.h" -#include "rtc_base/third_party/sigslot/sigslot.h" -#include "rtc_base/trace_event.h" - -namespace webrtc { - -void TgRtpTransport::SetRtcpMuxEnabled(bool enable) { - rtcp_mux_enabled_ = enable; - MaybeSignalReadyToSend(); -} - -const std::string& TgRtpTransport::transport_name() const { - return rtp_packet_transport_->transport_name(); -} - -int TgRtpTransport::SetRtpOption(rtc::Socket::Option opt, int value) { - return rtp_packet_transport_->SetOption(opt, value); -} - -int TgRtpTransport::SetRtcpOption(rtc::Socket::Option opt, int value) { - if (rtcp_packet_transport_) { - return rtcp_packet_transport_->SetOption(opt, value); - } - return -1; -} - -void TgRtpTransport::SetRtpPacketTransport( - rtc::PacketTransportInternal* new_packet_transport) { - if (new_packet_transport == rtp_packet_transport_) { - return; - } - if (rtp_packet_transport_) { - rtp_packet_transport_->SignalReadyToSend.disconnect(this); - rtp_packet_transport_->SignalReadPacket.disconnect(this); - rtp_packet_transport_->SignalNetworkRouteChanged.disconnect(this); - rtp_packet_transport_->SignalWritableState.disconnect(this); - rtp_packet_transport_->SignalSentPacket.disconnect(this); - // Reset the network route of the old transport. - SignalNetworkRouteChanged(absl::optional()); - } - if (new_packet_transport) { - new_packet_transport->SignalReadyToSend.connect( - this, &TgRtpTransport::OnReadyToSend); - new_packet_transport->SignalReadPacket.connect(this, - &TgRtpTransport::OnReadPacket); - new_packet_transport->SignalNetworkRouteChanged.connect( - this, &TgRtpTransport::OnNetworkRouteChanged); - new_packet_transport->SignalWritableState.connect( - this, &TgRtpTransport::OnWritableState); - new_packet_transport->SignalSentPacket.connect(this, - &TgRtpTransport::OnSentPacket); - // Set the network route for the new transport. - SignalNetworkRouteChanged(new_packet_transport->network_route()); - } - - rtp_packet_transport_ = new_packet_transport; - // Assumes the transport is ready to send if it is writable. If we are wrong, - // ready to send will be updated the next time we try to send. - SetReadyToSend(false, - rtp_packet_transport_ && rtp_packet_transport_->writable()); -} - -void TgRtpTransport::SetRtcpPacketTransport( - rtc::PacketTransportInternal* new_packet_transport) { - if (new_packet_transport == rtcp_packet_transport_) { - return; - } - if (rtcp_packet_transport_) { - rtcp_packet_transport_->SignalReadyToSend.disconnect(this); - rtcp_packet_transport_->SignalReadPacket.disconnect(this); - rtcp_packet_transport_->SignalNetworkRouteChanged.disconnect(this); - rtcp_packet_transport_->SignalWritableState.disconnect(this); - rtcp_packet_transport_->SignalSentPacket.disconnect(this); - // Reset the network route of the old transport. - SignalNetworkRouteChanged(absl::optional()); - } - if (new_packet_transport) { - new_packet_transport->SignalReadyToSend.connect( - this, &TgRtpTransport::OnReadyToSend); - new_packet_transport->SignalReadPacket.connect(this, - &TgRtpTransport::OnReadPacket); - new_packet_transport->SignalNetworkRouteChanged.connect( - this, &TgRtpTransport::OnNetworkRouteChanged); - new_packet_transport->SignalWritableState.connect( - this, &TgRtpTransport::OnWritableState); - new_packet_transport->SignalSentPacket.connect(this, - &TgRtpTransport::OnSentPacket); - // Set the network route for the new transport. - SignalNetworkRouteChanged(new_packet_transport->network_route()); - } - rtcp_packet_transport_ = new_packet_transport; - - // Assumes the transport is ready to send if it is writable. If we are wrong, - // ready to send will be updated the next time we try to send. - SetReadyToSend(true, - rtcp_packet_transport_ && rtcp_packet_transport_->writable()); -} - -bool TgRtpTransport::IsWritable(bool rtcp) const { - rtc::PacketTransportInternal* transport = rtcp && !rtcp_mux_enabled_ - ? rtcp_packet_transport_ - : rtp_packet_transport_; - return transport && transport->writable(); -} - -bool TgRtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer* packet, - const rtc::PacketOptions& options, - int flags) { - return SendPacket(false, packet, options, flags); -} - -bool TgRtpTransport::SendRtcpPacket(rtc::CopyOnWriteBuffer* packet, - const rtc::PacketOptions& options, - int flags) { - return SendPacket(true, packet, options, flags); -} - -bool TgRtpTransport::SendPacket(bool rtcp, - rtc::CopyOnWriteBuffer* packet, - const rtc::PacketOptions& options, - int flags) { - rtc::PacketTransportInternal* transport = rtcp && !rtcp_mux_enabled_ - ? rtcp_packet_transport_ - : rtp_packet_transport_; - int ret = transport->SendPacket(packet->cdata(), packet->size(), - options, flags); - if (ret != static_cast(packet->size())) { - if (transport->GetError() == ENOTCONN) { - RTC_LOG(LS_WARNING) << "Got ENOTCONN from transport."; - SetReadyToSend(rtcp, false); - } - return false; - } - return true; -} - -void TgRtpTransport::UpdateRtpHeaderExtensionMap( - const cricket::RtpHeaderExtensions& header_extensions) { - header_extension_map_ = RtpHeaderExtensionMap(header_extensions); -} - -bool TgRtpTransport::RegisterRtpDemuxerSink(const RtpDemuxerCriteria& criteria, - RtpPacketSinkInterface* sink) { - rtp_demuxer_.RemoveSink(sink); - if (!rtp_demuxer_.AddSink(criteria, sink)) { - RTC_LOG(LS_ERROR) << "Failed to register the sink for RTP demuxer."; - return false; - } - return true; -} - -bool TgRtpTransport::UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) { - if (!rtp_demuxer_.RemoveSink(sink)) { - RTC_LOG(LS_ERROR) << "Failed to unregister the sink for RTP demuxer."; - return false; - } - return true; -} - -void TgRtpTransport::DemuxPacket(rtc::CopyOnWriteBuffer packet, - int64_t packet_time_us) { - webrtc::RtpPacketReceived parsed_packet(&header_extension_map_); - if (!parsed_packet.Parse(std::move(packet))) { - RTC_LOG(LS_ERROR) - << "Failed to parse the incoming RTP packet before demuxing. Drop it."; - return; - } - - if (packet_time_us != -1) { - parsed_packet.set_arrival_time_ms((packet_time_us + 500) / 1000); - } - if (!rtp_demuxer_.OnRtpPacket(parsed_packet)) { - RTC_LOG(LS_WARNING) << "Failed to demux RTP packet: " - << RtpDemuxer::DescribePacket(parsed_packet); - } -} - -bool TgRtpTransport::IsTransportWritable() { - auto rtcp_packet_transport = - rtcp_mux_enabled_ ? nullptr : rtcp_packet_transport_; - return rtp_packet_transport_ && rtp_packet_transport_->writable() && - (!rtcp_packet_transport || rtcp_packet_transport->writable()); -} - -void TgRtpTransport::OnReadyToSend(rtc::PacketTransportInternal* transport) { - SetReadyToSend(transport == rtcp_packet_transport_, true); -} - -void TgRtpTransport::OnNetworkRouteChanged( - absl::optional network_route) { - SignalNetworkRouteChanged(network_route); -} - -void TgRtpTransport::OnWritableState( - rtc::PacketTransportInternal* packet_transport) { - RTC_DCHECK(packet_transport == rtp_packet_transport_ || - packet_transport == rtcp_packet_transport_); - SignalWritableState(IsTransportWritable()); -} - -void TgRtpTransport::OnSentPacket(rtc::PacketTransportInternal* packet_transport, - const rtc::SentPacket& sent_packet) { - RTC_DCHECK(packet_transport == rtp_packet_transport_ || - packet_transport == rtcp_packet_transport_); - SignalSentPacket(sent_packet); -} - -void TgRtpTransport::OnRtpPacketReceived(rtc::CopyOnWriteBuffer packet, - int64_t packet_time_us) { - DemuxPacket(packet, packet_time_us); -} - -void TgRtpTransport::OnRtcpPacketReceived(rtc::CopyOnWriteBuffer packet, - int64_t packet_time_us) { - SignalRtcpPacketReceived(&packet, packet_time_us); -} - -void TgRtpTransport::OnReadPacket(rtc::PacketTransportInternal* transport, - const char* data, - size_t len, - const int64_t& packet_time_us, - int flags) { - TRACE_EVENT0("webrtc", "TgRtpTransport::OnReadPacket"); - - // When using RTCP multiplexing we might get RTCP packets on the RTP - // transport. We check the RTP payload type to determine if it is RTCP. - auto array_view = rtc::MakeArrayView(data, len); - cricket::RtpPacketType packet_type = cricket::InferRtpPacketType(array_view); - // Filter out the packet that is neither RTP nor RTCP. - if (packet_type == cricket::RtpPacketType::kUnknown) { - return; - } - - // Protect ourselves against crazy data. - if (!cricket::IsValidRtpPacketSize(packet_type, len)) { - RTC_LOG(LS_ERROR) << "Dropping incoming " - << cricket::RtpPacketTypeToString(packet_type) - << " packet: wrong size=" << len; - return; - } - - rtc::CopyOnWriteBuffer packet(data, len); - if (packet_type == cricket::RtpPacketType::kRtcp) { - OnRtcpPacketReceived(std::move(packet), packet_time_us); - } else { - OnRtpPacketReceived(std::move(packet), packet_time_us); - } -} - -void TgRtpTransport::SetReadyToSend(bool rtcp, bool ready) { - if (rtcp) { - rtcp_ready_to_send_ = ready; - } else { - rtp_ready_to_send_ = ready; - } - - MaybeSignalReadyToSend(); -} - -void TgRtpTransport::MaybeSignalReadyToSend() { - bool ready_to_send = - rtp_ready_to_send_ && (rtcp_ready_to_send_ || rtcp_mux_enabled_); - if (ready_to_send != ready_to_send_) { - ready_to_send_ = ready_to_send; - SignalReadyToSend(ready_to_send); - } -} - -} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.h b/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.h deleted file mode 100644 index 591be9890a..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_rtp_transport.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2017 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TG_PC_RTP_TRANSPORT_H_ -#define TG_PC_RTP_TRANSPORT_H_ - -#include - -#include "call/rtp_demuxer.h" -#include "modules/rtp_rtcp/include/rtp_header_extension_map.h" -#include "pc/rtp_transport_internal.h" -#include "rtc_base/third_party/sigslot/sigslot.h" - -namespace rtc { - -class CopyOnWriteBuffer; -struct PacketOptions; -class PacketTransportInternal; - -} // namespace rtc - -namespace webrtc { - -class TgRtpTransport : public RtpTransportInternal { - public: - TgRtpTransport(const TgRtpTransport&) = delete; - TgRtpTransport& operator=(const TgRtpTransport&) = delete; - - explicit TgRtpTransport(bool rtcp_mux_enabled) - : rtcp_mux_enabled_(rtcp_mux_enabled) {} - - bool rtcp_mux_enabled() const override { return rtcp_mux_enabled_; } - void SetRtcpMuxEnabled(bool enable) override; - - const std::string& transport_name() const override; - - int SetRtpOption(rtc::Socket::Option opt, int value) override; - int SetRtcpOption(rtc::Socket::Option opt, int value) override; - - rtc::PacketTransportInternal* rtp_packet_transport() const { - return rtp_packet_transport_; - } - void SetRtpPacketTransport(rtc::PacketTransportInternal* rtp); - - rtc::PacketTransportInternal* rtcp_packet_transport() const { - return rtcp_packet_transport_; - } - void SetRtcpPacketTransport(rtc::PacketTransportInternal* rtcp); - - bool IsReadyToSend() const override { return ready_to_send_; } - - bool IsWritable(bool rtcp) const override; - - bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet, - const rtc::PacketOptions& options, - int flags) override; - - bool SendRtcpPacket(rtc::CopyOnWriteBuffer* packet, - const rtc::PacketOptions& options, - int flags) override; - - bool IsSrtpActive() const override { return false; } - - void UpdateRtpHeaderExtensionMap( - const cricket::RtpHeaderExtensions& header_extensions) override; - - bool RegisterRtpDemuxerSink(const RtpDemuxerCriteria& criteria, - RtpPacketSinkInterface* sink) override; - - bool UnregisterRtpDemuxerSink(RtpPacketSinkInterface* sink) override; - - protected: - // These methods will be used in the subclasses. - void DemuxPacket(rtc::CopyOnWriteBuffer packet, int64_t packet_time_us); - - bool SendPacket(bool rtcp, - rtc::CopyOnWriteBuffer* packet, - const rtc::PacketOptions& options, - int flags); - - // Overridden by SrtpTransport. - virtual void OnNetworkRouteChanged( - absl::optional network_route); - virtual void OnRtpPacketReceived(rtc::CopyOnWriteBuffer packet, - int64_t packet_time_us); - virtual void OnRtcpPacketReceived(rtc::CopyOnWriteBuffer packet, - int64_t packet_time_us); - // Overridden by SrtpTransport and DtlsSrtpTransport. - virtual void OnWritableState(rtc::PacketTransportInternal* packet_transport); - - private: - void OnReadyToSend(rtc::PacketTransportInternal* transport); - void OnSentPacket(rtc::PacketTransportInternal* packet_transport, - const rtc::SentPacket& sent_packet); - void OnReadPacket(rtc::PacketTransportInternal* transport, - const char* data, - size_t len, - const int64_t& packet_time_us, - int flags); - - // Updates "ready to send" for an individual channel and fires - // SignalReadyToSend. - void SetReadyToSend(bool rtcp, bool ready); - - void MaybeSignalReadyToSend(); - - bool IsTransportWritable(); - - bool rtcp_mux_enabled_; - - rtc::PacketTransportInternal* rtp_packet_transport_ = nullptr; - rtc::PacketTransportInternal* rtcp_packet_transport_ = nullptr; - - bool ready_to_send_ = false; - bool rtp_ready_to_send_ = false; - bool rtcp_ready_to_send_ = false; - - RtpDemuxer rtp_demuxer_; - - // Used for identifying the MID for RtpDemuxer. - RtpHeaderExtensionMap header_extension_map_; -}; - -} // namespace webrtc - -#endif // PC_RTP_TRANSPORT_H_ diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.cpp b/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.cpp deleted file mode 100644 index 94e64e2e3f..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.cpp +++ /dev/null @@ -1,501 +0,0 @@ -/* - * Copyright 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "tg_webrtc_session_description_factory.h" - -#include - -#include -#include -#include -#include - -#include "absl/algorithm/container.h" -#include "absl/types/optional.h" -#include "api/jsep.h" -#include "api/jsep_session_description.h" -#include "api/rtc_error.h" -#include "pc/session_description.h" -#include "rtc_base/checks.h" -#include "rtc_base/location.h" -#include "rtc_base/logging.h" -#include "rtc_base/ref_counted_object.h" -#include "rtc_base/ssl_identity.h" -#include "rtc_base/ssl_stream_adapter.h" -#include "rtc_base/string_encode.h" - -#include "tg_peer_connection.h" - -using cricket::MediaSessionOptions; -using rtc::UniqueRandomIdGenerator; - -namespace webrtc { -namespace { -static const char kFailedDueToIdentityFailed[] = - " failed because DTLS identity request failed"; -static const char kFailedDueToSessionShutdown[] = - " failed because the session was shut down"; - -static const uint64_t kInitSessionVersion = 2; - -// Check that each sender has a unique ID. -static bool ValidMediaSessionOptions( - const cricket::MediaSessionOptions& session_options) { - std::vector sorted_senders; - for (const cricket::MediaDescriptionOptions& media_description_options : - session_options.media_description_options) { - sorted_senders.insert(sorted_senders.end(), - media_description_options.sender_options.begin(), - media_description_options.sender_options.end()); - } - absl::c_sort(sorted_senders, [](const cricket::SenderOptions& sender1, - const cricket::SenderOptions& sender2) { - return sender1.track_id < sender2.track_id; - }); - return absl::c_adjacent_find(sorted_senders, - [](const cricket::SenderOptions& sender1, - const cricket::SenderOptions& sender2) { - return sender1.track_id == sender2.track_id; - }) == sorted_senders.end(); -} - -enum { - MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, - MSG_CREATE_SESSIONDESCRIPTION_FAILED, - MSG_USE_CONSTRUCTOR_CERTIFICATE -}; - -struct CreateSessionDescriptionMsg : public rtc::MessageData { - explicit CreateSessionDescriptionMsg( - webrtc::CreateSessionDescriptionObserver* observer, - RTCError error_in) - : observer(observer), error(std::move(error_in)) {} - - rtc::scoped_refptr observer; - RTCError error; - std::unique_ptr description; -}; -} // namespace - -void TgWebRtcCertificateGeneratorCallback::OnFailure() { - SignalRequestFailed(); -} - -void TgWebRtcCertificateGeneratorCallback::OnSuccess( - const rtc::scoped_refptr& certificate) { - SignalCertificateReady(certificate); -} - -// static -void TgWebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription( - const SessionDescriptionInterface* source_desc, - const std::string& content_name, - SessionDescriptionInterface* dest_desc) { - if (!source_desc) { - return; - } - const cricket::ContentInfos& contents = - source_desc->description()->contents(); - const cricket::ContentInfo* cinfo = - source_desc->description()->GetContentByName(content_name); - if (!cinfo) { - return; - } - size_t mediasection_index = static_cast(cinfo - &contents[0]); - const IceCandidateCollection* source_candidates = - source_desc->candidates(mediasection_index); - const IceCandidateCollection* dest_candidates = - dest_desc->candidates(mediasection_index); - if (!source_candidates || !dest_candidates) { - return; - } - for (size_t n = 0; n < source_candidates->count(); ++n) { - const IceCandidateInterface* new_candidate = source_candidates->at(n); - if (!dest_candidates->HasCandidate(new_candidate)) { - dest_desc->AddCandidate(source_candidates->at(n)); - } - } -} - -TgWebRtcSessionDescriptionFactory::TgWebRtcSessionDescriptionFactory( - rtc::Thread* signaling_thread, - cricket::ChannelManager* channel_manager, - TgPeerConnection* pc, - const std::string& session_id, - std::unique_ptr cert_generator, - const rtc::scoped_refptr& certificate, - UniqueRandomIdGenerator* ssrc_generator) - : signaling_thread_(signaling_thread), - session_desc_factory_(channel_manager, - &transport_desc_factory_, - ssrc_generator), - // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp - // as the session id and session version. To simplify, it should be fine - // to just use a random number as session id and start version from - // |kInitSessionVersion|. - session_version_(kInitSessionVersion), - cert_generator_(std::move(cert_generator)), - pc_(pc), - session_id_(session_id), - certificate_request_state_(CERTIFICATE_NOT_NEEDED) { - RTC_DCHECK(signaling_thread_); - RTC_DCHECK(!(cert_generator_ && certificate)); - bool dtls_enabled = cert_generator_ || certificate; - // SRTP-SDES is disabled if DTLS is on. - SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED); - if (!dtls_enabled) { - RTC_LOG(LS_VERBOSE) << "DTLS-SRTP disabled."; - return; - } - - if (certificate) { - // Use |certificate|. - certificate_request_state_ = CERTIFICATE_WAITING; - - RTC_LOG(LS_VERBOSE) << "DTLS-SRTP enabled; has certificate parameter."; - // We already have a certificate but we wait to do |SetIdentity|; if we do - // it in the constructor then the caller has not had a chance to connect to - // |SignalCertificateReady|. - signaling_thread_->Post( - RTC_FROM_HERE, this, MSG_USE_CONSTRUCTOR_CERTIFICATE, - new rtc::ScopedRefMessageData(certificate)); - } else { - // Generate certificate. - certificate_request_state_ = CERTIFICATE_WAITING; - - rtc::scoped_refptr callback( - new rtc::RefCountedObject()); - callback->SignalRequestFailed.connect( - this, &TgWebRtcSessionDescriptionFactory::OnCertificateRequestFailed); - callback->SignalCertificateReady.connect( - this, &TgWebRtcSessionDescriptionFactory::SetCertificate); - - rtc::KeyParams key_params = rtc::KeyParams(); - RTC_LOG(LS_VERBOSE) - << "DTLS-SRTP enabled; sending DTLS identity request (key type: " - << key_params.type() << ")."; - - // Request certificate. This happens asynchronously, so that the caller gets - // a chance to connect to |SignalCertificateReady|. - cert_generator_->GenerateCertificateAsync(key_params, absl::nullopt, - callback); - } -} - -TgWebRtcSessionDescriptionFactory::~TgWebRtcSessionDescriptionFactory() { - RTC_DCHECK(signaling_thread_->IsCurrent()); - - // Fail any requests that were asked for before identity generation completed. - FailPendingRequests(kFailedDueToSessionShutdown); - - // Process all pending notifications in the message queue. If we don't do - // this, requests will linger and not know they succeeded or failed. - rtc::MessageList list; - signaling_thread_->Clear(this, rtc::MQID_ANY, &list); - for (auto& msg : list) { - if (msg.message_id != MSG_USE_CONSTRUCTOR_CERTIFICATE) { - OnMessage(&msg); - } else { - // Skip MSG_USE_CONSTRUCTOR_CERTIFICATE because we don't want to trigger - // SetIdentity-related callbacks in the destructor. This can be a problem - // when WebRtcSession listens to the callback but it was the WebRtcSession - // destructor that caused TgWebRtcSessionDescriptionFactory's destruction. - // The callback is then ignored, leaking memory allocated by OnMessage for - // MSG_USE_CONSTRUCTOR_CERTIFICATE. - delete msg.pdata; - } - } -} - -void TgWebRtcSessionDescriptionFactory::CreateOffer( - CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options, - const cricket::MediaSessionOptions& session_options) { - std::string error = "CreateOffer"; - if (certificate_request_state_ == CERTIFICATE_FAILED) { - error += kFailedDueToIdentityFailed; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailed(observer, error); - return; - } - - if (!ValidMediaSessionOptions(session_options)) { - error += " called with invalid session options"; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailed(observer, error); - return; - } - - TgCreateSessionDescriptionRequest request( - TgCreateSessionDescriptionRequest::kOffer, observer, session_options); - if (certificate_request_state_ == CERTIFICATE_WAITING) { - create_session_description_requests_.push(request); - } else { - RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED || - certificate_request_state_ == CERTIFICATE_NOT_NEEDED); - InternalCreateOffer(request); - } -} - -void TgWebRtcSessionDescriptionFactory::CreateAnswer( - CreateSessionDescriptionObserver* observer, - const cricket::MediaSessionOptions& session_options) { - std::string error = "CreateAnswer"; - if (certificate_request_state_ == CERTIFICATE_FAILED) { - error += kFailedDueToIdentityFailed; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailed(observer, error); - return; - } - if (!pc_->remote_description()) { - error += " can't be called before SetRemoteDescription."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailed(observer, error); - return; - } - if (pc_->remote_description()->GetType() != SdpType::kOffer) { - error += " failed because remote_description is not an offer."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailed(observer, error); - return; - } - - if (!ValidMediaSessionOptions(session_options)) { - error += " called with invalid session options."; - RTC_LOG(LS_ERROR) << error; - PostCreateSessionDescriptionFailed(observer, error); - return; - } - - TgCreateSessionDescriptionRequest request( - TgCreateSessionDescriptionRequest::kAnswer, observer, session_options); - if (certificate_request_state_ == CERTIFICATE_WAITING) { - create_session_description_requests_.push(request); - } else { - RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED || - certificate_request_state_ == CERTIFICATE_NOT_NEEDED); - InternalCreateAnswer(request); - } -} - -void TgWebRtcSessionDescriptionFactory::SetSdesPolicy( - cricket::SecurePolicy secure_policy) { - session_desc_factory_.set_secure(secure_policy); -} - -cricket::SecurePolicy TgWebRtcSessionDescriptionFactory::SdesPolicy() const { - return session_desc_factory_.secure(); -} - -void TgWebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) { - switch (msg->message_id) { - case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: { - CreateSessionDescriptionMsg* param = - static_cast(msg->pdata); - param->observer->OnSuccess(param->description.release()); - delete param; - break; - } - case MSG_CREATE_SESSIONDESCRIPTION_FAILED: { - CreateSessionDescriptionMsg* param = - static_cast(msg->pdata); - param->observer->OnFailure(std::move(param->error)); - delete param; - break; - } - case MSG_USE_CONSTRUCTOR_CERTIFICATE: { - rtc::ScopedRefMessageData* param = - static_cast*>( - msg->pdata); - RTC_LOG(LS_INFO) << "Using certificate supplied to the constructor."; - SetCertificate(param->data()); - delete param; - break; - } - default: - RTC_NOTREACHED(); - break; - } -} - -void TgWebRtcSessionDescriptionFactory::InternalCreateOffer( - TgCreateSessionDescriptionRequest request) { - if (pc_->local_description()) { - // If the needs-ice-restart flag is set as described by JSEP, we should - // generate an offer with a new ufrag/password to trigger an ICE restart. - for (cricket::MediaDescriptionOptions& options : - request.options.media_description_options) { - if (pc_->NeedsIceRestart(options.mid)) { - options.transport_options.ice_restart = true; - } - } - } - - std::unique_ptr desc = - session_desc_factory_.CreateOffer( - request.options, pc_->local_description() - ? pc_->local_description()->description() - : nullptr); - if (!desc) { - PostCreateSessionDescriptionFailed(request.observer, - "Failed to initialize the offer."); - return; - } - - // RFC 3264 - // When issuing an offer that modifies the session, - // the "o=" line of the new SDP MUST be identical to that in the - // previous SDP, except that the version in the origin field MUST - // increment by one from the previous SDP. - - // Just increase the version number by one each time when a new offer - // is created regardless if it's identical to the previous one or not. - // The |session_version_| is a uint64_t, the wrap around should not happen. - RTC_DCHECK(session_version_ + 1 > session_version_); - auto offer = std::make_unique( - SdpType::kOffer, std::move(desc), session_id_, - rtc::ToString(session_version_++)); - if (pc_->local_description()) { - for (const cricket::MediaDescriptionOptions& options : - request.options.media_description_options) { - if (!options.transport_options.ice_restart) { - CopyCandidatesFromSessionDescription(pc_->local_description(), - options.mid, offer.get()); - } - } - } - PostCreateSessionDescriptionSucceeded(request.observer, std::move(offer)); -} - -void TgWebRtcSessionDescriptionFactory::InternalCreateAnswer( - TgCreateSessionDescriptionRequest request) { - if (pc_->remote_description()) { - for (cricket::MediaDescriptionOptions& options : - request.options.media_description_options) { - // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1 - // an answer should also contain new ICE ufrag and password if an offer - // has been received with new ufrag and password. - options.transport_options.ice_restart = - pc_->IceRestartPending(options.mid); - // We should pass the current SSL role to the transport description - // factory, if there is already an existing ongoing session. - rtc::SSLRole ssl_role; - if (pc_->GetSslRole(options.mid, &ssl_role)) { - options.transport_options.prefer_passive_role = - (rtc::SSL_SERVER == ssl_role); - } - } - } - - std::unique_ptr desc = - session_desc_factory_.CreateAnswer( - pc_->remote_description() ? pc_->remote_description()->description() - : nullptr, - request.options, - pc_->local_description() ? pc_->local_description()->description() - : nullptr); - if (!desc) { - PostCreateSessionDescriptionFailed(request.observer, - "Failed to initialize the answer."); - return; - } - - // RFC 3264 - // If the answer is different from the offer in any way (different IP - // addresses, ports, etc.), the origin line MUST be different in the answer. - // In that case, the version number in the "o=" line of the answer is - // unrelated to the version number in the o line of the offer. - // Get a new version number by increasing the |session_version_answer_|. - // The |session_version_| is a uint64_t, the wrap around should not happen. - RTC_DCHECK(session_version_ + 1 > session_version_); - auto answer = std::make_unique( - SdpType::kAnswer, std::move(desc), session_id_, - rtc::ToString(session_version_++)); - if (pc_->local_description()) { - // Include all local ICE candidates in the SessionDescription unless - // the remote peer has requested an ICE restart. - for (const cricket::MediaDescriptionOptions& options : - request.options.media_description_options) { - if (!options.transport_options.ice_restart) { - CopyCandidatesFromSessionDescription(pc_->local_description(), - options.mid, answer.get()); - } - } - } - PostCreateSessionDescriptionSucceeded(request.observer, std::move(answer)); -} - -void TgWebRtcSessionDescriptionFactory::FailPendingRequests( - const std::string& reason) { - RTC_DCHECK(signaling_thread_->IsCurrent()); - while (!create_session_description_requests_.empty()) { - const TgCreateSessionDescriptionRequest& request = - create_session_description_requests_.front(); - PostCreateSessionDescriptionFailed( - request.observer, - ((request.type == TgCreateSessionDescriptionRequest::kOffer) - ? "CreateOffer" - : "CreateAnswer") + - reason); - create_session_description_requests_.pop(); - } -} - -void TgWebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed( - CreateSessionDescriptionObserver* observer, - const std::string& error) { - CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg( - observer, RTCError(RTCErrorType::INTERNAL_ERROR, std::string(error))); - signaling_thread_->Post(RTC_FROM_HERE, this, - MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg); - RTC_LOG(LS_ERROR) << "Create SDP failed: " << error; -} - -void TgWebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded( - CreateSessionDescriptionObserver* observer, - std::unique_ptr description) { - CreateSessionDescriptionMsg* msg = - new CreateSessionDescriptionMsg(observer, RTCError::OK()); - msg->description = std::move(description); - signaling_thread_->Post(RTC_FROM_HERE, this, - MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg); -} - -void TgWebRtcSessionDescriptionFactory::OnCertificateRequestFailed() { - RTC_DCHECK(signaling_thread_->IsCurrent()); - - RTC_LOG(LS_ERROR) << "Asynchronous certificate generation request failed."; - certificate_request_state_ = CERTIFICATE_FAILED; - - FailPendingRequests(kFailedDueToIdentityFailed); -} - -void TgWebRtcSessionDescriptionFactory::SetCertificate( - const rtc::scoped_refptr& certificate) { - RTC_DCHECK(certificate); - RTC_LOG(LS_VERBOSE) << "Setting new certificate."; - - certificate_request_state_ = CERTIFICATE_SUCCEEDED; - SignalCertificateReady(certificate); - - transport_desc_factory_.set_certificate(certificate); - transport_desc_factory_.set_secure(cricket::SEC_ENABLED); - - while (!create_session_description_requests_.empty()) { - if (create_session_description_requests_.front().type == - TgCreateSessionDescriptionRequest::kOffer) { - InternalCreateOffer(create_session_description_requests_.front()); - } else { - InternalCreateAnswer(create_session_description_requests_.front()); - } - create_session_description_requests_.pop(); - } -} -} // namespace webrtc diff --git a/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.h b/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.h deleted file mode 100644 index 655253b4d5..0000000000 --- a/submodules/TgVoipWebrtcCustom/Sources/tg_webrtc_session_description_factory.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2013 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef TG_PC_WEBRTC_SESSION_DESCRIPTION_FACTORY_H_ -#define TG_PC_WEBRTC_SESSION_DESCRIPTION_FACTORY_H_ - -#include - -#include -#include -#include - -#include "api/jsep.h" -#include "api/peer_connection_interface.h" -#include "api/scoped_refptr.h" -#include "p2p/base/transport_description.h" -#include "p2p/base/transport_description_factory.h" -#include "pc/media_session.h" -#include "pc/peer_connection_internal.h" -#include "rtc_base/constructor_magic.h" -#include "rtc_base/message_handler.h" -#include "rtc_base/message_queue.h" -#include "rtc_base/rtc_certificate.h" -#include "rtc_base/rtc_certificate_generator.h" -#include "rtc_base/third_party/sigslot/sigslot.h" -#include "rtc_base/thread.h" -#include "rtc_base/unique_id_generator.h" - -namespace webrtc { - -class TgPeerConnection; - -// DTLS certificate request callback class. -class TgWebRtcCertificateGeneratorCallback - : public rtc::RTCCertificateGeneratorCallback, - public sigslot::has_slots<> { - public: - // |rtc::RTCCertificateGeneratorCallback| overrides. - void OnSuccess( - const rtc::scoped_refptr& certificate) override; - void OnFailure() override; - - sigslot::signal0<> SignalRequestFailed; - sigslot::signal1&> - SignalCertificateReady; -}; - -struct TgCreateSessionDescriptionRequest { - enum Type { - kOffer, - kAnswer, - }; - - TgCreateSessionDescriptionRequest(Type type, - CreateSessionDescriptionObserver* observer, - const cricket::MediaSessionOptions& options) - : type(type), observer(observer), options(options) {} - - Type type; - rtc::scoped_refptr observer; - cricket::MediaSessionOptions options; -}; - -// This class is used to create offer/answer session description. Certificates -// for WebRtcSession/DTLS are either supplied at construction or generated -// asynchronously. It queues the create offer/answer request until the -// certificate generation has completed, i.e. when OnCertificateRequestFailed or -// OnCertificateReady is called. -class TgWebRtcSessionDescriptionFactory : public rtc::MessageHandler, - public sigslot::has_slots<> { - public: - // Can specify either a |cert_generator| or |certificate| to enable DTLS. If - // a certificate generator is given, starts generating the certificate - // asynchronously. If a certificate is given, will use that for identifying - // over DTLS. If neither is specified, DTLS is disabled. - TgWebRtcSessionDescriptionFactory( - rtc::Thread* signaling_thread, - cricket::ChannelManager* channel_manager, - TgPeerConnection* pc, - const std::string& session_id, - std::unique_ptr cert_generator, - const rtc::scoped_refptr& certificate, - rtc::UniqueRandomIdGenerator* ssrc_generator); - virtual ~TgWebRtcSessionDescriptionFactory(); - - static void CopyCandidatesFromSessionDescription( - const SessionDescriptionInterface* source_desc, - const std::string& content_name, - SessionDescriptionInterface* dest_desc); - - void CreateOffer( - CreateSessionDescriptionObserver* observer, - const PeerConnectionInterface::RTCOfferAnswerOptions& options, - const cricket::MediaSessionOptions& session_options); - void CreateAnswer(CreateSessionDescriptionObserver* observer, - const cricket::MediaSessionOptions& session_options); - - void SetSdesPolicy(cricket::SecurePolicy secure_policy); - cricket::SecurePolicy SdesPolicy() const; - - void set_enable_encrypted_rtp_header_extensions(bool enable) { - session_desc_factory_.set_enable_encrypted_rtp_header_extensions(enable); - } - - void set_is_unified_plan(bool is_unified_plan) { - session_desc_factory_.set_is_unified_plan(is_unified_plan); - } - - sigslot::signal1&> - SignalCertificateReady; - - // For testing. - bool waiting_for_certificate_for_testing() const { - return certificate_request_state_ == CERTIFICATE_WAITING; - } - - private: - enum CertificateRequestState { - CERTIFICATE_NOT_NEEDED, - CERTIFICATE_WAITING, - CERTIFICATE_SUCCEEDED, - CERTIFICATE_FAILED, - }; - - // MessageHandler implementation. - virtual void OnMessage(rtc::Message* msg); - - void InternalCreateOffer(TgCreateSessionDescriptionRequest request); - void InternalCreateAnswer(TgCreateSessionDescriptionRequest request); - // Posts failure notifications for all pending session description requests. - void FailPendingRequests(const std::string& reason); - void PostCreateSessionDescriptionFailed( - CreateSessionDescriptionObserver* observer, - const std::string& error); - void PostCreateSessionDescriptionSucceeded( - CreateSessionDescriptionObserver* observer, - std::unique_ptr description); - - void OnCertificateRequestFailed(); - void SetCertificate( - const rtc::scoped_refptr& certificate); - - std::queue - create_session_description_requests_; - rtc::Thread* const signaling_thread_; - cricket::TransportDescriptionFactory transport_desc_factory_; - cricket::MediaSessionDescriptionFactory session_desc_factory_; - uint64_t session_version_; - const std::unique_ptr cert_generator_; - // TODO(jiayl): remove the dependency on peer connection once bug 2264 is - // fixed. - TgPeerConnection* const pc_; - const std::string session_id_; - CertificateRequestState certificate_request_state_; - - RTC_DISALLOW_COPY_AND_ASSIGN(TgWebRtcSessionDescriptionFactory); -}; -} // namespace webrtc - -#endif // PC_WEBRTC_SESSION_DESCRIPTION_FACTORY_H_