title: iOS screen sharing (ReplayKit) using WebRTC in swift
body: <p>I implemented webrtc SDK for video calling and its working fine . During video call user can share screen with another user.
  I am using RePlayKit for screen sharing.</p>
  <p>Here is my code</p>
  <pre><code>class SampleHandler: RPBroadcastSampleHandler {
         
      var peerConnectionFactory: RTCPeerConnectionFactory?
       var localVideoSource: RTCVideoSource?
       var videoCapturer: RTCVideoCapturer?
       var peerConnection: RTCPeerConnection?
      var localVideoTrack: RTCVideoTrack?
  
      var disconnectSemaphore: DispatchSemaphore?
      var videodelegate:VideoViewExtensionDelegate?
      var signalClient: SignalingClient? = nil
      let config = Config.default
     
      
      let peerConnectionfactory: RTCPeerConnectionFactory = {
          RTCInitializeSSL()
          let videoEncoderFactory = RTCDefaultVideoEncoderFactory()
          let videoDecoderFactory = RTCDefaultVideoDecoderFactory()
          return RTCPeerConnectionFactory(encoderFactory: videoEncoderFactory, decoderFactory: videoDecoderFactory)
      }()
      
      private let mediaConstrains = [kRTCMediaConstraintsOfferToReceiveAudio: kRTCMediaConstraintsValueFalse,
      kRTCMediaConstraintsOfferToReceiveVideo: kRTCMediaConstraintsValueTrue]
  
  
      static let kAudioSampleType = RPSampleBufferType.audioMic
  
  
      override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) {
          
          self.SetupVideo()
      }
      override func broadcastPaused() {
          // User has requested to pause the broadcast. Samples will stop being delivered.
  //        self.audioTrack?.isEnabled = false
  //        self.screenTrack?.isEnabled = false
      }
  
      override func broadcastResumed() {
          // User has requested to resume the broadcast. Samples delivery will resume.
  //        self.audioTrack?.isEnabled = true
  //        self.screenTrack?.isEnabled = true
      }
  
      override func broadcastFinished() {
          // User has requested to finish the broadcast.
      }
      
      func SetupVideo() {
  
          if #available(iOS 13.0, *) {
                  let webSocketProvider: WebSocketProvider
                  webSocketProvider = NativeWebSocket(url: self.config.signalingServerUrl)
          
          self.signalClient = SignalingClient(webSocket: webSocketProvider)
             let config = RTCConfiguration()
                       // config.iceServers = [RTCIceServer(urlStrings: iceServers)]
                        
                        config.iceServers = [RTCIceServer(urlStrings:[&quot;//turn &amp; sturn serber url&quot;],
                                                          username:&quot;//username&quot;,
                                                          credential:&quot;//password&quot;)]
                        
                        
                        // Unified plan is more superior than planB
                       // config.sdpSemantics = .unifiedPlan
                        
                        // gatherContinually will let WebRTC to listen to any network changes and send any new candidates to the other client
              config.continualGatheringPolicy = .gatherContinually
                  
             let screenSharefactory = self.peerConnectionfactory
                        
              let constraints = RTCMediaConstraints(mandatoryConstraints: nil,
                                                              optionalConstraints: [&quot;DtlsSrtpKeyAgreement&quot;:kRTCMediaConstraintsValueTrue])
          self.peerConnection = screenSharefactory.peerConnection(with: config, constraints: constraints, delegate: nil)
                  
          self.peerConnection?.delegate = self
  
  
          self.localVideoSource = screenSharefactory.videoSource()
          self.videoCapturer = RTCVideoCapturer(delegate: self.localVideoSource!)
          self.localVideoTrack =  screenSharefactory.videoTrack(with: self.localVideoSource!, trackId:&quot;video0&quot;)
           
              //    let videoSender = newpeerConnection.sender(withKind: kRTCMediaStreamTrackKindVideo, streamId: &quot;stream&quot;)
              //    videoSender.track = videoTrack
                  
                  let mediaStream: RTCMediaStream = (screenSharefactory.mediaStream(withStreamId: &quot;1&quot;))
                  mediaStream.addVideoTrack(self.localVideoTrack!)
                 self.peerConnection?.add(mediaStream)
                  
                  self.offer(peerconnection: self.peerConnection!) { (sdp) in
                      self.signalClient?.send(sdp: sdp)
                  }
              
          }
      }
      
      
      func offer(peerconnection : RTCPeerConnection ,completion: @escaping (_ sdp: RTCSessionDescription) -&gt; Void) {
  
          
          let constrains = RTCMediaConstraints(mandatoryConstraints: self.mediaConstrains,
                                               optionalConstraints: nil)
          peerconnection.offer(for: constrains) { (sdp, error) in
              guard let sdp = sdp else {
                  return
              }
              
              peerconnection.setLocalDescription(sdp, completionHandler: { (error) in
                  completion(sdp)
              })
          }
      }
  
      override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
  
          switch sampleBufferType {
          case RPSampleBufferType.video:
  
            guard let imageBuffer: CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
                    break
                }
            let rtcpixelBuffer = RTCCVPixelBuffer(pixelBuffer: imageBuffer)
            let timeStampNs: Int64 = Int64(CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) * 1000000000)
            let videoFrame =  RTCVideoFrame(buffer: rtcpixelBuffer, rotation: RTCVideoRotation._0, timeStampNs: timeStampNs)
              print(videoFrame)
              self.localVideoSource?.capturer(self.videoCapturer!, didCapture: videoFrame)
  
  
  
              break
  
          case RPSampleBufferType.audioApp:
              if (SampleHandler.kAudioSampleType == RPSampleBufferType.audioApp) {
              //    ExampleCoreAudioDeviceCapturerCallback(audioDevice, sampleBuffer)
              }
              break
  
          case RPSampleBufferType.audioMic:
              if (SampleHandler.kAudioSampleType == RPSampleBufferType.audioMic) {
  
              }
              break
          @unknown default:
              return
          }
      }
  }
  
  extension SampleHandler: RTCPeerConnectionDelegate {
      
      func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
          debugPrint(&quot;peerConnection new signaling state: \(stateChanged)&quot;)
      }
      
      func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
          debugPrint(&quot;peerConnection did add stream&quot;)
      }
      
      func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
          debugPrint(&quot;peerConnection did remote stream&quot;)
      }
      
      func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
          debugPrint(&quot;peerConnection should negotiate&quot;)
      }
      
      func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
          debugPrint(&quot;peerConnection new connection state: \(newState)&quot;)
         
      }
      
      func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {
          debugPrint(&quot;peerConnection new gathering state: \(newState)&quot;)
      }
      
      func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
          debugPrint(&quot;peerConnection did Generate&quot;)
  
      }
      
      func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
          debugPrint(&quot;peerConnection did remove candidate(s)&quot;)
      }
      
      func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
          debugPrint(&quot;peerConnection did open data channel&quot;)
         // self.remoteDataChannel = dataChannel
      }
  }
  
  
  extension SampleHandler: RTCDataChannelDelegate {
      func dataChannelDidChangeState(_ dataChannel: RTCDataChannel) {
          debugPrint(&quot;dataChannel did change state: \(dataChannel.readyState)&quot;)
      }
      
      func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer) {
         
      }
  }
  </code></pre>
  <p>I am using this WEBRTC project <a href="https://github.com/stasel/WebRTC-iOS" rel="nofollow noreferrer">https://github.com/stasel/WebRTC-iOS</a>
  I am getting the CMSampleBuffer data and RTCVideoFrame and passing correctly.
  CMSampleBuffer Data for refrence.</p>
  <pre><code>CMSampleBuffer 0x100918370 retainCount: 5 allocator: 0x1e32175e0
      invalid = NO
      dataReady = YES
      makeDataReadyCallback = 0x0
      makeDataReadyRefcon = 0x0
      formatDescription = &lt;CMAudioFormatDescription 0x282bf0e60 [0x1e32175e0]&gt; {
      mediaType:'soun' 
      mediaSubType:'lpcm' 
      mediaSpecific: {
          ASBD: {
              mSampleRate: 44100.000000 
              mFormatID: 'lpcm' 
              mFormatFlags: 0xe 
              mBytesPerPacket: 4 
              mFramesPerPacket: 1 
              mBytesPerFrame: 4 
              mChannelsPerFrame: 2 
              mBitsPerChannel: 16     } 
          cookie: {(null)} 
          ACL: {(null)}
          FormatList Array: {
              Index: 0 
              ChannelLayoutTag: 0x650002 
              ASBD: {
              mSampleRate: 44100.000000 
              mFormatID: 'lpcm' 
              mFormatFlags: 0xe 
              mBytesPerPacket: 4 
              mFramesPerPacket: 1 
              mBytesPerFrame: 4 
              mChannelsPerFrame: 2 
              mBitsPerChannel: 16     }} 
      } 
      extensions: {(null)}
  }
      sbufToTrackReadiness = 0x0
      numSamples = 1024
      outputPTS = {190371138262458/1000000000 = 190371.138}(based on cachedOutputPresentationTimeStamp)
      sampleTimingArray[1] = {
          {PTS = {190371138262458/1000000000 = 190371.138}, DTS = {INVALID}, duration = {1/44100 = 0.000}},
      }
      dataBuffer = 0x2828f1050
  </code></pre>
  <p>I am stuck here ,don't know what is the wrong with my code.ANy help is highly appreciated.</p>