AgoraIO / Basic-Video-Broadcasting

Sample app to join/leave a channel, set the role as a host/audience, mute/unmute, switch between front/rear cameras, and set the video parameters.
MIT License
269 stars 287 forks source link

Not able to stream in newly created channel #116

Open iosfitness opened 3 years ago

iosfitness commented 3 years ago

I am trying to dynamically create a channel and publish but its not sending stream on audience Apps

Below is the code snippet

import UIKit
import AgoraRtcKit
import AgoraRtmKit

enum LoginStatus {
    case online, offline
}

class AgoraRtm: NSObject {
        static let kit = AgoraRtmKit(appId: "cb9d6201ddbb4cf4845b67385508dd68", delegate: nil)
    static var current: String? = "random"
    static var status: LoginStatus = .offline
    static func updateKit(delegate: AgoraRtmDelegate) {
        guard let kit = kit else {
            return
        }
        kit.agoraRtmDelegate = delegate
    }
}

protocol ShowAlertProtocol: UIViewController {
    func showAlert(_ message: String, handler: ((UIAlertAction) -> Void)?)
    func showAlert(_ message: String)
}

extension ShowAlertProtocol {
    func showAlert(_ message: String, handler: ((UIAlertAction) -> Void)?) {
        view.endEditing(true)
        let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: handler))
        present(alert, animated: true, completion: nil)
    }

    func showAlert(_ message: String) {
        showAlert(message, handler: nil)
    }
}

class AgoraVideoViewController: UIViewController, ShowAlertProtocol {

    @IBOutlet weak var localVideoView: UIView!
    @IBOutlet weak var muteButton: UIButton!
    @IBOutlet weak var hangUpButton: UIButton!

    @IBOutlet weak var txtMessage: UITextField!

    let appID = "cb9d6201ddbb4cf4845b67385508dd68"
    var agoraKit: AgoraRtcEngineKit?
        var agoraChatKit: AgoraRtmKit?
    let tempToken: String? = nil
    var userID: UInt = 0
    var userName: String? = nil
    var channelName = "default"
      //var channelRTM = "default"
    var remoteUserIDs: [UInt] = []

        var rtmChannel: AgoraRtmChannel?
    var rtcChannel: AgoraRtcChannel!

    let isBroadcaster = false //TODO: Hardik : Get this role in login API

    var muted = false {
        didSet {
            if muted {
                muteButton.setTitle("Unmute", for: .normal)
            } else {
                muteButton.setTitle("Mute", for: .normal)
            }
        }
    }

    private func getAgoraEngine() -> AgoraRtcEngineKit {
            if agoraKit == nil {
                    agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: appID, delegate: self)
            }
            return agoraKit!
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        channelName = "default"
        userName = ToolsAndFunctions.randomString(length: 10)

        // Do any additional setup after loading the view.
        //RTC
        setUpVideo()
        joinChannel()

        //RTM
        login()

    }

    /*func getResourceID(){
        var jsonDict = [String : Any]()

        let clientRequestJsonDict = [String : Any]()

        jsonDict.updateValue("tt", forKey: "cname")
        jsonDict.updateValue("123456", forKey: "uid")
        jsonDict.updateValue(clientRequestJsonDict, forKey: "clientRequest")

        httHelper.getResourceIdAPI(_for: "https://api.agora.io/v1/apps/\(appID)/cloud_recording/acquire", jsonBody: jsonDict)
    }*/

//MARK: =================RTC WORKING=============================
    func setUpVideo() {
        getAgoraEngine().enableVideo()
        getAgoraEngine().enableAudio()

        getAgoraEngine().setChannelProfile(.liveBroadcasting)

        //Warning: only enable dual stream mode if there will be more than one broadcaster in the channel
        //getAgoraEngine().enableDualStreamMode(true)

        if isBroadcaster{

            let videoCanvas = AgoraRtcVideoCanvas()
            videoCanvas.uid = userID
            videoCanvas.view = localVideoView
            videoCanvas.renderMode = .fit

            getAgoraEngine().startPreview()
            getAgoraEngine().muteLocalAudioStream(false)
            getAgoraEngine().muteLocalVideoStream(false)

            getAgoraEngine().setClientRole(.broadcaster)
            getAgoraEngine().setupLocalVideo(videoCanvas)

        }
        else{
            getAgoraEngine().setClientRole(.audience)
            getAgoraEngine().muteLocalAudioStream(true)
            getAgoraEngine().muteLocalVideoStream(true)

        }

        //Configuration
        getAgoraEngine().setVideoEncoderConfiguration(
                             AgoraVideoEncoderConfiguration(
                                size: AgoraVideoDimension640x480,
                                     frameRate: .fps30,
                                     bitrate: AgoraVideoBitrateStandard,
                                     orientationMode: .adaptative
                             )
                     )

    }

    func joinChannel() {
        //TODO: JB : Call API HERE
        localVideoView.isHidden = false
        //TODO: What is this check for userName
        // What is tempToken. Check and explain over call. How can I stop live-streaming

                let options = AgoraRtcChannelMediaOptions()
                        options.autoSubscribeAudio = true
                        options.autoSubscribeVideo = true
                        if (rtcChannel != nil) {
                                rtcChannel.destroy()
                        }
                        rtcChannel = getAgoraEngine().createRtcChannel("Test123213")
                        rtcChannel.setRtcChannelDelegate(self)
                rtcChannel.publish()
                        let success = rtcChannel.join(byToken: nil, info: nil, uid: 0, options: options)

                print(success)

            //APPROACH FROM https://docs.agora.io/en/Interactive%20Broadcast/start_live_ios?platform=iOS
        /*if let name = userName {
            let channelCode =   getAgoraEngine().joinChannel(byUserAccount: name, token: tempToken, channelId: channelName) { [weak self] (sid, uid, elapsed) in
                //TODO: Hardik : Call API HERE for join
                self?.userID = uid
            }
            print("RTC---",channelCode)
        } else {
            let channelCode =   getAgoraEngine().joinChannel(byToken: tempToken, channelId:channelName, info: nil, uid: userID) { [weak self] (sid, uid, elapsed) in
                //TODO: Hardik : Call API HERE for join
                self?.userID = uid

            }
            print("RTC---",channelCode)
        }*/

    }

    /*@IBAction func didToggleMute(_ sender: Any) {
        if muted {
            getAgoraEngine().muteLocalAudioStream(false)
        } else {
            getAgoraEngine().muteLocalAudioStream(true)
        }
        muted = !muted
    }

 */
    @IBAction func didTapHangUp(_ sender: Any) {
        leaveChannel()

    }

      @IBAction func btnSendMsgTapped(_ sender: Any) {
        send(message: txtMessage.text!)
    }

    func leaveChannel() {
        //TODO: Hardik : Call API HERE for leave
        getAgoraEngine().leaveChannel(nil)
        leaveRtmChannel()
        // Stop preview after leave channel
        if isBroadcaster{
            getAgoraEngine().stopPreview()
        }

            localVideoView.isHidden = true
            remoteUserIDs.removeAll()

         setIdleTimerActive(true)

         logout()

    }

    func setIdleTimerActive(_ active: Bool) {
        UIApplication.shared.isIdleTimerDisabled = !active
    }

    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */

}

extension AgoraVideoViewController: AgoraRtcEngineDelegate {
    func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
            //Get count of user's joined
          remoteUserIDs.append(uid)

    }

    //Sometimes, user info isn't immediately available when a remote user joins - if we get it later, reload their nameplate.
    func rtcEngine(_ engine: AgoraRtcEngineKit, didUpdatedUserInfo userInfo: AgoraUserInfo, withUid uid: UInt) {
        if let index = remoteUserIDs.first(where: { $0 == uid }) {
           // collectionView.reloadItems(at: [IndexPath(item: Int(index), section: 0)])

                    //TODO: Check this. Retrieve name from AgoraUserInfo

            if let userInfo = getAgoraEngine().getUserInfo(byUid: index, withError: nil),
                           let username = userInfo.userAccount {
                          self.view.makeToast(username, duration: 0.5, position: .top)
                       }

        }
    }

    func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
        if let index = remoteUserIDs.firstIndex(where: { $0 == uid }) {
            remoteUserIDs.remove(at: index)

        }
    }

    // first remote video frame
    func rtcEngine(_ engine: AgoraRtcEngineKit, firstRemoteVideoDecodedOfUid uid: UInt, size: CGSize, elapsed: Int) {

        if !isBroadcaster{
            let videoCanvas = AgoraRtcVideoCanvas()
            videoCanvas.uid = uid
            videoCanvas.view = localVideoView
            videoCanvas.renderMode = .fit
            getAgoraEngine().setupRemoteVideo(videoCanvas)
        }

    }
}

//MARK: =================RTM WORKING=============================

 private extension AgoraVideoViewController {

     private func getAgoraChatEngine() -> AgoraRtmKit {
                     if agoraChatKit == nil {
                             agoraChatKit = AgoraRtmKit(appId: appID, delegate: self)
                     }
                     return agoraChatKit!
             }

     func createChannel(_ channel: String) {
         let errorHandle = { [weak self] (action: UIAlertAction) in
             guard let strongSelf = self else {
                 return
             }
             strongSelf.navigationController?.popViewController(animated: true)
         }

         guard let rtmChannel = AgoraRtm.kit?.createChannel(withId: channel, delegate: self) else {
             showAlert("join channel fail", handler: errorHandle)
             return
         }

         rtmChannel.join { [weak self] (error) in
             if error != .channelErrorOk, let strongSelf = self {
                 strongSelf.showAlert("join channel error: \(error.rawValue)", handler: errorHandle)
             }
         }

         self.rtmChannel = rtmChannel
     }

     func leaveRtmChannel() {
         rtmChannel?.leave { (error) in
             print("leave channel error: \(error.rawValue)")
         }
     }
 }

// MARK: Chat Channel
 // MARK: Send Message
 private extension AgoraVideoViewController {

    func login() {
           guard let account = userName, account.count > 0 else{
               return
           }

           AgoraRtm.updateKit(delegate: self)
           AgoraRtm.current = account
//           AgoraRtm.oneToOneMessageType = enableOneToOneSwitch.isOn ? .offline : .normal

           AgoraRtm.kit?.login(byToken: nil, user: account) { [unowned self] (errorCode) in
               guard errorCode == .ok else {
                   self.showAlert("login error: \(errorCode.rawValue)")
                   return
               }

               AgoraRtm.status = .online
                    DispatchQueue.main.async {
                self.createChannel(self.channelName)
                         print("Loggef in")
            }

           }
       }

    func logout() {
        guard AgoraRtm.status == .online else {
            return
        }

        AgoraRtm.kit?.logout(completion: { (error) in
            guard error == .ok else {
                return
            }

            AgoraRtm.status = .offline
        })
    }

     func send(message: String) {

            //TODO: Send a json string here
         let rtmMessage = AgoraRtmMessage(text: message)

             rtmChannel?.send(rtmMessage) { (error) in
                print(error.rawValue)

         }
     }
 }

 // MARK: AgoraRtmDelegate
 extension AgoraVideoViewController: AgoraRtmDelegate {
     func rtmKit(_ kit: AgoraRtmKit, connectionStateChanged state: AgoraRtmConnectionState, reason: AgoraRtmConnectionChangeReason) {
         showAlert("connection state changed: \(state.rawValue)") { [weak self] (_) in
             if reason == .remoteLogin, let strongSelf = self {
                 strongSelf.navigationController?.popToRootViewController(animated: true)
             }
         }
     }

     func rtmKit(_ kit: AgoraRtmKit, messageReceived message: AgoraRtmMessage, fromPeer peerId: String) {
//         appendMessage(user: peerId, content: message.text)
        print(message.text, peerId)
     }
 }

 // MARK: AgoraRtmChannelDelegate
 extension AgoraVideoViewController: AgoraRtmChannelDelegate {
     func channel(_ channel: AgoraRtmChannel, memberJoined member: AgoraRtmMember) {
         DispatchQueue.main.async { [unowned self] in
             self.showAlert("\(member.userId) join")
         }
     }

     func channel(_ channel: AgoraRtmChannel, memberLeft member: AgoraRtmMember) {
         DispatchQueue.main.async { [unowned self] in
             self.showAlert("\(member.userId) left")
         }
     }

     func channel(_ channel: AgoraRtmChannel, messageReceived message: AgoraRtmMessage, from member: AgoraRtmMember) {
//         appendMessage(user: member.userId, content: message.text)
        print(message.text, member.userId)
     }
 }
extension AgoraVideoViewController: AgoraRtcChannelDelegate {
    func rtcChannelDidJoin(_ rtcChannel: AgoraRtcChannel, withUid uid: UInt, elapsed: Int) {

            if !isBroadcaster{
                let videoCanvas = AgoraRtcVideoCanvas()
                videoCanvas.uid = uid
                videoCanvas.view = localVideoView
                videoCanvas.renderMode = .fit
                getAgoraEngine().setupRemoteVideo(videoCanvas)
            }
    }

    func rtcChannel(_ rtcChannel: AgoraRtcChannel, didJoinedOfUid uid: UInt, elapsed: Int) {

    }

    func rtcChannel(_ rtcChannel: AgoraRtcChannel, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {

    }

    func rtcChannelDidLeave(_ rtcChannel: AgoraRtcChannel, with stats: AgoraChannelStats) {

    }

    func rtcChannel(_ rtcChannel: AgoraRtcChannel, didOccurWarning warningCode: AgoraWarningCode) {

    }

    func rtcChannel(_ rtcChannel: AgoraRtcChannel, didOccurError errorCode: AgoraErrorCode) {

    }

}

Can anyone please suggest the right approach to do it?. My requirement is to create a channel when the host/broadcaster joins it. Reference :

https://docs.agora.io/en/Interactive%20Broadcast/start_live_ios?platform=iOS https://github.com/AgoraIO-Usecase/Breakout-Class/blob/master/breakout-ios/AgoraDualChannels/AgoraDualChannels/Controllers/LiveRoomViewController.swift

plutoless commented 3 years ago

i'm not sure if i understand your requirements correctly. A channel will be automatically created when you host/audience joins, you do not need to manually create it. And if you want an audience to switch to host and stream out video, you will need to call setClientRole in the channel.