title: how to live a live application screen having camera view with some other UIViews over Camera view
body: <p>Actually, I want to broadcast a live match with some overlays over it like sponsors images on top corners of the screen and a score card on the bottom of the screen. Can someone help me or guide me on a way of implementation I use this pod (haishinkit) but this pod is not serving the purpose. I use rtmpstream.attachScreen function for broadcasting my UIView but this function is not picking up my camera view (AVCaptureVideoPreviewLayer) other than this scorecard and sponsor images are broadcasting. I want to broadcast my Camera Screen along with Scorecard, other images along with the audio.</p>
  <pre><code>import UIKit
  import HaishinKit
  import AVFoundation
  import VideoToolbox
  import Loaf
  import WebKit
  
  
  class BroadcastViewController: UIViewController, RTMPStreamDelegate {
      
      // Camera Preview View
      @IBOutlet private weak var previewView: UIView!
      
      @IBOutlet weak var videoView: UIView!
      // Camera Selector
      @IBOutlet weak var cameraSelector: UISegmentedControl!
      
      @IBOutlet weak var webview: WKWebView!
      // Go Live Button
      @IBOutlet weak var startStopButton: UIButton!
      
      // FPS and Bitrate Labels
      @IBOutlet weak var fpsLabel: UILabel!
      @IBOutlet weak var bitrateLabel: UILabel!
      
      // RTMP Connection &amp; RTMP Stream
      private var rtmpConnection = RTMPConnection()
      private var rtmpStream: RTMPStream!
  
      // Default Camera
      private var defaultCamera: AVCaptureDevice.Position = .back
      
      // Flag indicates if we should be attempting to go live
      private var liveDesired = false
      
      // Reconnect attempt tracker
      private var reconnectAttempt = 0
      
      // The RTMP Stream key to broadcast to.
      public var streamKey: String!
      
      // The Preset to use
      public var preset: Preset!
      
      // A tracker of the last time we changed the bitrate in ABR
      private var lastBwChange = 0
      
      // The RTMP endpoint
      let rtmpEndpoint = &quot;rtmps://live-api-s.facebook.com:443/rtmp/&quot;
      
      
      //Camera Capture requiered properties
          var videoDataOutput: AVCaptureVideoDataOutput!
          var videoDataOutputQueue: DispatchQueue!
          var previewLayer:AVCaptureVideoPreviewLayer!
          var captureDevice : AVCaptureDevice!
          let session = AVCaptureSession()
      
      var isPublic = false
  
      // Some basic presets for live streaming
      enum Preset {
          case hd_1080p_30fps_5mbps
          case hd_720p_30fps_3mbps
          case sd_540p_30fps_2mbps
          case sd_360p_30fps_1mbps
      }
      
      // An encoding profile - width, height, framerate, video bitrate
      private class Profile {
          public var width : Int = 0
          public var height : Int = 0
          public var frameRate : Int = 0
          public var bitrate : Int = 0
          
          init(width: Int, height: Int, frameRate: Int, bitrate: Int) {
              self.width = width
              self.height = height
              self.frameRate = frameRate
              self.bitrate = bitrate
          }
      }
      
      // Converts a Preset to a Profile
      private func presetToProfile(preset: Preset) -&gt; Profile {
          switch preset {
          case .hd_1080p_30fps_5mbps:
              return Profile(width: 1920, height: 1080, frameRate: 30, bitrate: 5000000)
          case .hd_720p_30fps_3mbps:
              return Profile(width: 1280, height: 720, frameRate: 30, bitrate: 3000000)
          case .sd_540p_30fps_2mbps:
              return Profile(width: 960, height: 540, frameRate: 30, bitrate: 2000000)
          case .sd_360p_30fps_1mbps:
              return Profile(width: 640, height: 360, frameRate: 30, bitrate: 1000000)
          }
      }
  
      // Configures the live stream
      private func configureStream(preset: Preset) {
          
          let profile = presetToProfile(preset: preset)
          
          // Configure the capture settings from the camera
          rtmpStream.captureSettings = [
              .sessionPreset: AVCaptureSession.Preset.hd1920x1080,
              .continuousAutofocus: true,
              .continuousExposure: true,
              .fps: profile.frameRate
          ]
          
          // Get the orientation of the app, and set the video orientation appropriately
          if #available(iOS 13.0, *) {
              if let orientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation {
  //                let videoOrientation = DeviceUtil.videoOrientation(by: orientation)
                  rtmpStream.orientation = .landscapeRight
                  rtmpStream.videoSettings = [
                      .width: (orientation.isPortrait) ? profile.height : profile.width,
                      .height: (orientation.isPortrait) ? profile.width : profile.height,
                      .bitrate: profile.bitrate,
                      .profileLevel: kVTProfileLevel_H264_Main_AutoLevel,
                      .maxKeyFrameIntervalDuration: 2, // 2 seconds
                  ]
              }
          } else {
              // Fallback on earlier versions
          }
          
          // Configure the RTMP audio stream
  //        rtmpStream.audioSettings = [
  //            .bitrate: 128000 // Always use 128kbps
  //        ]
      }
      
      
      // Publishes the live stream
      private func publishStream() {
          print(&quot;Calling publish()&quot;)
          rtmpStream.attachScreen(ScreenCaptureSession(viewToCapture: previewView))
          rtmpStream.publish(&quot;minestreamkey&quot;)
          
          DispatchQueue.main.async {
              self.startStopButton.setTitle(&quot;Stop Streaming!&quot;, for: .normal)
          }
      }
      
      // Triggers and attempt to connect to an RTMP hostname
      private func connectRTMP() {
          print(&quot;Calling connect()&quot;)
          rtmpConnection.connect(rtmpEndpoint)
      }
      override func viewWillAppear(_ animated: Bool) {
          super.viewWillAppear(animated)
  //        videoView.startSession()
      }
      override func viewDidLoad() {
          super.viewDidLoad()
          
          self.setupAVCapture()
          previewView.bringSubviewToFront(webview)
          webview.load(NSURLRequest(url: NSURL(string: &quot;https://graphics.crickslab.com/scorecard/0865e840-f147-11eb-95cb-65228ef0512c/Blitzz-vs-Crickslab-Officials-Fri30Jul2021-1201AM-&quot;)! as URL) as URLRequest)
          print(&quot;Broadcast View Controller Init&quot;)
          
          print(&quot;Stream Key: &quot; + &quot;FB-3940543509404805-0-AbxeU6r48NpFcasH&quot;)
          
          // Work out the orientation of the device, and set this on the RTMP Stream
          rtmpStream = RTMPStream(connection: rtmpConnection)
          
          // Get the orientation of the app, and set the video orientation appropriately
          if #available(iOS 13.0, *) {
              if let orientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation {
                  let videoOrientation = DeviceUtil.videoOrientation(by: orientation)
                  rtmpStream.orientation = videoOrientation!
              }
          } else {
              // Fallback on earlier versions
          }
          
          // And a listener for orientation changes
          // Note: Changing the orientation once the stream has been started will not change the orientation of the live stream, only the preview.
          NotificationCenter.default.addObserver(self, selector: #selector(on(_:)), name: UIDevice.orientationDidChangeNotification, object: nil)
          
          // Configure the encoder profile
          configureStream(preset: self.preset)
       
  //         Attatch to the default audio device
  //        rtmpStream.attachAudio(AVCaptureDevice.default(for: .audio)) { error in
  //            print(error.description)
  //        }
  //
  //        // Attatch to the default camera
  //        rtmpStream.attachCamera(DeviceUtil.device(withPosition: defaultCamera)) { error in
  //            print(error.description)
  //        }
  
          // Register a tap gesture recogniser so we can use tap to focus
          let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
          previewView.addGestureRecognizer(tap)
          previewView.isUserInteractionEnabled = true
          
          // Attatch the preview view
  //        previewView?.attachStream(rtmpStream)
          
          // Add event listeners for RTMP status changes and IO Errors
          rtmpConnection.addEventListener(.rtmpStatus, selector: #selector(rtmpStatusHandler), observer: self)
          rtmpConnection.addEventListener(.ioError, selector: #selector(rtmpErrorHandler), observer: self)
          
          rtmpStream.delegate = self
                  
          startStopButton.setTitle(&quot;Go Live!&quot;, for: .normal)
      }
      
      //  Tap to focus / exposure
      @objc func handleTap(_ sender: UITapGestureRecognizer) {
          if sender.state == UIGestureRecognizer.State.ended {
              let point = sender.location(in: previewView)
              let pointOfInterest = CGPoint(x: point.x / previewView.bounds.size.width, y: point.y / previewView.bounds.size.height)
              rtmpStream.setPointOfInterest(pointOfInterest, exposure: pointOfInterest)
          }
      }
  
      // Triggered when the user tries to change camera
      @IBAction func changeCameraToggle(_ sender: UISegmentedControl) {
          
          switch cameraSelector.selectedSegmentIndex
          {
          case 0:
              rtmpStream.attachCamera(DeviceUtil.device(withPosition: AVCaptureDevice.Position.back))
          case 1:
              rtmpStream.attachCamera(DeviceUtil.device(withPosition: AVCaptureDevice.Position.front))
          default:
              rtmpStream.attachCamera(DeviceUtil.device(withPosition: defaultCamera))
          }
      }
      
      // Triggered when the user taps the go live button
      @IBAction func goLiveButton(_ sender: UIButton) {
          
          print(&quot;Go Live Button tapped!&quot;)
          
          if !liveDesired {
              
              if rtmpConnection.connected {
                  // If we're already connected to the RTMP server, wr can just call publish() to start the stream
                  publishStream()
              } else {
                  // Otherwise, we need to setup the RTMP connection and wait for a callback before we can safely
                  // call publish() to start the stream
                  connectRTMP()
              }
  
              // Modify application state to streaming
              liveDesired = true
              startStopButton.setTitle(&quot;Connecting...&quot;, for: .normal)
          } else {
              // Unpublish the live stream
              rtmpStream.close()
  
              // Modify application state to idle
              liveDesired = false
              startStopButton.setTitle(&quot;Go Live!&quot;, for: .normal)
          }
      }
      
      // Called when the RTMPStream or RTMPConnection changes status
      @objc
      private func rtmpStatusHandler(_ notification: Notification) {
          print(&quot;RTMP Status Handler called.&quot;)
          
          let e = Event.from(notification)
                  guard let data: ASObject = e.data as? ASObject, let code: String = data[&quot;code&quot;] as? String else {
                      return
                  }
  
          // Send a nicely styled notification about the RTMP Status
          var loafStyle = Loaf.State.info
          switch code {
          case RTMPConnection.Code.connectSuccess.rawValue, RTMPStream.Code.publishStart.rawValue, RTMPStream.Code.unpublishSuccess.rawValue:
              loafStyle = Loaf.State.success
          case RTMPConnection.Code.connectFailed.rawValue:
              loafStyle = Loaf.State.error
          case RTMPConnection.Code.connectClosed.rawValue:
              loafStyle = Loaf.State.warning
          default:
              break
          }
          DispatchQueue.main.async {
              Loaf(&quot;RTMP Status: &quot; + code, state: loafStyle, location: .top,  sender: self).show(.short)
          }
          
          switch code {
          case RTMPConnection.Code.connectSuccess.rawValue:
              reconnectAttempt = 0
              if liveDesired {
                  // Publish our stream to our stream key
                  publishStream()
              }
          case RTMPConnection.Code.connectFailed.rawValue, RTMPConnection.Code.connectClosed.rawValue:
              print(&quot;RTMP Connection was not successful.&quot;)
              
              // Retry the connection if &quot;live&quot; is still the desired state
              if liveDesired {
                  
                  reconnectAttempt += 1
                  
                  DispatchQueue.main.async {
                      self.startStopButton.setTitle(&quot;Reconnect attempt &quot; + String(self.reconnectAttempt) + &quot; (Cancel)&quot; , for: .normal)
                  }
                  // Retries the RTMP connection every 5 seconds
                  DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
                      self.connectRTMP()
                  }
              }
          default:
              break
          }
      }
  
      // Called when there's an RTMP Error
      @objc
      private func rtmpErrorHandler(_ notification: Notification) {
          print(&quot;RTMP Error Handler called.&quot;)
      }
      
      // Called when the device changes rotation
      @objc
      private func on(_ notification: Notification) {
          if #available(iOS 13.0, *) {
              if let orientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation {
                  let videoOrientation = DeviceUtil.videoOrientation(by: orientation)
                  rtmpStream.orientation = videoOrientation!
                  
                  // Do not change the outpur rotation if the stream has already started.
                  if liveDesired == false {
                      let profile = presetToProfile(preset: self.preset)
                      rtmpStream.videoSettings = [
                          .width: (orientation.isPortrait) ? profile.height : profile.width,
                          .height: (orientation.isPortrait) ? profile.width : profile.height
                      ]
                  }
              }
          } else {
              // Fallback on earlier versions
          }
      }
      
      // Button tapped to return to the configuration screen
      @IBAction func closeButton(_ sender: Any) {
          self.dismiss(animated: true, completion: nil)
      }
      
      // RTMPStreamDelegate callbacks
      
      func rtmpStreamDidClear(_ stream: RTMPStream) {
      }
      
      // Statistics callback
      func rtmpStream(_ stream: RTMPStream, didStatics connection: RTMPConnection) {
          DispatchQueue.main.async {
              self.fpsLabel.text = String(stream.currentFPS) + &quot; fps&quot;
              self.bitrateLabel.text = String((connection.currentBytesOutPerSecond / 125)) + &quot; kbps&quot;
          }
      }
      
      // Insufficient bandwidth callback
      func rtmpStream(_ stream: RTMPStream, didPublishInsufficientBW connection: RTMPConnection) {
          print(&quot;ABR: didPublishInsufficientBW&quot;)
          
          // If we last changed bandwidth over 10 seconds ago
          if (Int(NSDate().timeIntervalSince1970) - lastBwChange) &gt; 5 {
              print(&quot;ABR: Will try to change bitrate&quot;)
              
              // Reduce bitrate by 30% every 10 seconds
              let b = Double(stream.videoSettings[.bitrate] as! UInt32) * Double(0.7)
              print(&quot;ABR: Proposed bandwidth: &quot; + String(b))
              stream.videoSettings[.bitrate] = b
              lastBwChange = Int(NSDate().timeIntervalSince1970)
              
              DispatchQueue.main.async {
                  Loaf(&quot;Insuffient Bandwidth, changing video bandwidth to: &quot; + String(b), state: Loaf.State.warning, location: .top,  sender: self).show(.short)
              }
              
          } else {
              print(&quot;ABR: Still giving grace time for last bandwidth change&quot;)
          }
      }
      
      // Today this example doesn't attempt to increase bandwidth to find a sweet spot.
      // An implementation might be to gently increase bandwidth by a few percent, but that's hard without getting into an aggressive cycle.
      func rtmpStream(_ stream: RTMPStream, didPublishSufficientBW connection: RTMPConnection) {
      }
  }
  
  // AVCaptureVideoDataOutputSampleBufferDelegate protocol and related methods
  extension BroadcastViewController:  AVCaptureVideoDataOutputSampleBufferDelegate{
       func setupAVCapture(){
          session.sessionPreset = AVCaptureSession.Preset.vga640x480
          guard let device = AVCaptureDevice
          .default(AVCaptureDevice.DeviceType.builtInWideAngleCamera,
                   for: .video,
                   position: AVCaptureDevice.Position.back) else {
                              return
          }
          captureDevice = device
          beginSession()
      }
  
      func beginSession(){
          var deviceInput: AVCaptureDeviceInput!
  
          do {
              deviceInput = try AVCaptureDeviceInput(device: captureDevice)
              guard deviceInput != nil else {
                  print(&quot;error: cant get deviceInput&quot;)
                  return
              }
  
              if self.session.canAddInput(deviceInput){
                  self.session.addInput(deviceInput)
              }
  
              videoDataOutput = AVCaptureVideoDataOutput()
              videoDataOutput.alwaysDiscardsLateVideoFrames=true
              videoDataOutputQueue = DispatchQueue(label: &quot;VideoDataOutputQueue&quot;)
              videoDataOutput.setSampleBufferDelegate(self, queue:self.videoDataOutputQueue)
  
              if session.canAddOutput(self.videoDataOutput){
                  session.addOutput(self.videoDataOutput)
              }
  
              videoDataOutput.connection(with: .video)?.isEnabled = true
  
              previewLayer = AVCaptureVideoPreviewLayer(session: self.session)
              previewLayer.videoGravity = AVLayerVideoGravity.resizeAspect
  
  //            let rootLayer :CALayer = self.previewView.layer
              self.videoView.layer.masksToBounds=true
              previewLayer.frame = videoView.bounds
              videoView.layer.addSublayer(self.previewLayer)
              
              session.startRunning()
          } catch let error as NSError {
              deviceInput = nil
              print(&quot;error: \(error.localizedDescription)&quot;)
          }
      }
  
      
      func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
          // do stuff here
          if let description = CMSampleBufferGetFormatDescription(sampleBuffer) {
              let dimensions = CMVideoFormatDescriptionGetDimensions(description)
              rtmpStream.videoSettings = [
                  .width: dimensions.width,
                  .height: dimensions.height ,
                  .profileLevel: kVTProfileLevel_H264_Baseline_AutoLevel
              ]
          }
          rtmpStream.appendSampleBuffer(sampleBuffer, withType: .video)
          
      }
  
      // clean up AVCapture
      func stopCamera(){
          session.stopRunning()
      }
  
  }
  </code></pre>