Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Данный пример может использоваться как стример для публикации WebRTC-видеопотока с экрана и отдельного аудиопотока для голосового сопровожденияс захватом микрофона или системного звука. Пример работает с iOS SDK 2.6.82 и новее.

На скриншоте ниже представлен общий вид приложения. Поля ввода:

  • WCS Websocket URL
  • имя видео потока с экрана
  • имя аудио потока с микрофона

Image RemovedImage Added

Экран приложения в начале публикации

Image RemovedImage Added

Для захвата экрана используется отдельный процесс расширения, который работает до тех пор, пока устройство не будет заблокировано или пока публикация экрана не будет остановлена.

Работа с кодом примера

Для разбора кода возьмем версию примера ScreenCapturer, которая доступна для скачивания на GitHub.

Классы

1. Импорт API 

code

Code Block
languagejs
themeRDark
import FPWCSApi2Swift

2. Создание сессии для публикации аудио

WCSSession, WCSSession.connect  code

В параметрах сессии указываются:

...

Настройка параметров расширения для захвата экрана

code

Параметр UserDefaults.suiteName должен совпадать с идентификатором группы расширения

Code Block
languagejs
themeRDark
    @IBAction func publishAudioPressedbroadcastBtnPressed(_ sender: Any) {
        if (publishAudioButton.title(for: .normal) == "Publish Audio") {
    ...
        pickerView.showsMicrophoneButton = systemOrMicSwitch.isOn

        let optionsuserDefaults = FPWCSApi2SessionOptions(UserDefaults.init(suiteName: "group.com.flashphoner.ScreenCapturerSwift")
        userDefaults?.set(urlField.text, forKey: "wcsUrl")
      options.urlServer = self.urlField.textuserDefaults?.set(publishVideoName.text, forKey: "streamName")
        userDefaults?.set(systemOrMicSwitch.isOn, forKey: "useMic")
      options.appKey = "defaultApp" ...
    }

3. Получение параметров захвата экрана в расширении

code

Code Block
languagejs
themeRDark
    override func broadcastStarted(withSetupInfo setupInfo: [String :   do NSObject]?) {
        ...
        let userDefaults =  try session = WCSSession(options)
UserDefaults.init(suiteName: "group.com.flashphoner.ScreenCapturerSwift")
        let wcsUrl = userDefaults?.string(forKey: "wcsUrl")
       } catchif {
 wcsUrl != self.wcsUrl || session?.getStatus() != .fpwcsSessionStatusEstablished {
            printsession?.disconnect(error)
            session =   }nil
        }
        self...
wcsUrl = wcsUrl ?? self.wcsUrl
            session?.connect()
        let streamName   changeViewState(publishAudioButton, false= userDefaults?.string(forKey: "streamName")
        }self.streamName else= {
streamName ?? self.streamName
             ...
        }
    }

3. Публикация аудио

WCSSession.createStream, WCSStream.publish  code

Методу createStream передаются параметры:

  • имя публикуемого потока
  • ограничения для захвата только аудио

4. Настройка для захвата звука при публикации экрана

FPWCSApi2.getAudioManager().useAudioModule code

Code Block
languagejs
themeRDark
    func onConnected(_ session:WCSSession) throws {
            let optionsuseMic = FPWCSApi2StreamOptions()
            options.name = publishAudioName.textuserDefaults?.bool(forKey: "useMic")

            options.constraints capturer = FPWCSApi2MediaConstraintsScreenRTCVideoCapturer(audiouseMic: true, video: false);
            do {useMic ?? true)

                publishStream = try session.createStream(options)
FPWCSApi2.getAudioManager().useAudioModule(true)

5. Создание сессии для публикации экрана

WCSSession, WCSSession.connect  code

Code Block
languagejs
themeRDark
        if (session == nil) } catch {
            let options =  printFPWCSApi2SessionOptions(error);
            }options.urlServer = self.wcsUrl
            ...options.appKey = "defaultApp"
            do {
                try publishStream?.publish(session = WCSSession(options)
            } catch {
                print(error);
            }
    }

4. Настройка параметров расширения для захвата экрана

code

Параметр UserDefaults.suiteName должен совпадать с идентификатором группы расширения

Code Block
languagejs
themeRDark
    @objc func pickerAction() {
        //#WCS-3207 - Use suite name as group id in entitlements
        let userDefaults = UserDefaults.init(suiteName: "group.com.flashphoner.ScreenCapturerSwift")
        userDefaults?.set(urlField.text, forKey: "wcsUrl")
        userDefaultssession?.set(publishVideoName.text, forKey: "streamName"connect()
        
    }

5. Настройка класса для захвата экрана

code

Code Block
languagejs
themeRDark
    fileprivate var capturer: ScreenRTCVideoCapturer = ScreenRTCVideoCapturer()

6. Получение параметров захвата экрана

code6. Публикация потока с экрана

WCSSession.createStream, WCSStream.publish  code

Методу createStream передаются параметры:

  • имя публикуемого потока
  • объект ScreenRTCVideoCapturer для захвата потока с экрана
Code Block
languagejs
themeRDark
    override func broadcastStartedonConnected(withSetupInfo_ setupInfosession: [String : NSObject]?) {
WCSSession) throws {
        let options =  //#WCS-3207 - Use suite name as group id in entitlementsFPWCSApi2StreamOptions()
        options.name = streamName
        let userDefaultsoptions.constraints = UserDefaults.init(suiteName: "group.com.flashphoner.ScreenCapturerSwift")
FPWCSApi2MediaConstraints(audio: false, videoCapturer: capturer);

        lettry wcsUrlpublishStream = userDefaults?session.string(forKey: "wcsUrl"createStream(options)
        if...  wcsUrl != self.wcsUrl || session?.getStatus() != .fpwcsSessionStatusEstablished {
        try    sessionpublishStream?.disconnectpublish()
    }

7. Инициализация класса ScreenRTCVideoCapturer

code

Code Block
languagejs
themeRDark
fileprivate class ScreenRTCVideoCapturer: RTCVideoCapturer {
    let sessionkNanosecondsPerSecond = nil1000000000
    var useMic: Bool = }
        self.wcsUrl = wcsUrl ?? self.wcsUrltrue;
    
    init(useMic: Bool)   {
        let streamName = userDefaults?.string(forKey: "streamName"super.init()
        self.streamNameuseMic = streamName ?? self.streamNameuseMic
    }
    ...
    }

7. Создание сессии для публикации экрана

WCSSession, WCSSession.connect  8. Захват системного аудио в расширении

FPWCSApi2.getAudioManager().getAudioModule().deliverRecordedData() code

Code Block
languagejs
themeRDark
    func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, ifwith (session == nilsampleBufferType: RPSampleBufferType) {
        switch    let options = FPWCSApi2SessionOptions()sampleBufferType {
            options.urlServer = self.wcsUrl...
        case    options.appKey = "defaultApp"
RPSampleBufferType.audioApp:
            if (!useMic) do {
                try session = WCSSession(optionsFPWCSApi2.getAudioManager().getAudioModule().deliverRecordedData(sampleBuffer)
            } catch {
                print(error)
        break
    }
            ...
            session?.connect()
        }

8. Публикация потока с экрана

WCSSession.createStream, WCSStream.publish  code

Методу createStream передаются параметры:

  • имя публикуемого потока
  • объект ScreenRTCVideoCapturer для захвата потока с экрана
}
}

9. Захват аудио с микрофона в расширении

FPWCSApi2.getAudioManager().getAudioModule().deliverRecordedData() code

Code Block
languagejs
themeRDark
    func onConnectedprocessSampleBuffer(_ session:WCSSessionsampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) throws {
        letswitch options = FPWCSApi2StreamOptions()sampleBufferType {
        options.name = streamName...
        case RPSampleBufferType.audioMic:
        options.constraints = FPWCSApi2MediaConstraints(audio: false, videoCapturer:if capturer(useMic);

 {
             try publishStream = session.createStream(options)
 FPWCSApi2.getAudioManager().getAudioModule().deliverRecordedData(sampleBuffer)
            }
    ...        break
        try publishStream?.publish()...
    }
}

Известные ограничения

1. При захвате системного звука на устройстве не будет играть музыка из iTunes.

2. Если какое-либо запущенное приложение захватывает микрофон, приложение ScreenCapturerSwift получает тишину в sampleBuffer как для микрофона, так и для системного звука. После того, как приложение, захватившее микрофон, освободит его, необходимо остановить публикацию экрана и начать ее заново, чтобы снова получать звук.