Skip to end of metadata
Go to start of metadata

Пример iOS-приложения для управления медиа-устройствами

Данный пример может использоваться как стример для публикации WebRTC-видеопотока с Web Call Server и позволяет выбрать медиа-устройства и следующие параметры для публикуемого и проигрываемого видео

  • разрешение (ширина, высота)
  • скорость передачи (bitrate)
  • FPS (Frames Per Second) - для публикуемого видео
  • quality - для проигрываемого видео

Поток может быть опубликован как с аудио и видео, так и без аудио или видео (переключатели 'Send Audio' и 'Send Video').

Аудио и видео в публикуемом потоке могут быть выключены/включены соответствующими переключателями 'Mute Audio' и 'Mute Video' во время или до начала публикации.

Видео потоки могут воспроизводиться с видео или без видео (переключатель 'Play Video').

При необходимости, определенные кодеки можно исключить из публикации или воспроизведения (поле 'Strip codecs')

На скриншоте ниже представлен пример во время публикации потока. Слева отображается видео с камеры, справа воспроизводится опубликованный поток.


Вид с настройками публикации показывается при нажатии на кнопку 'Local settings'

а вид с настройками воспроизведения - при нажатии на кнопку 'Remote settings'.

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

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

Классы видов

  • класс для основного вида приложения: ViewController (файл имплементации ViewController.swift)
  • класс для вида с настройками публикации: LocalViewController (файл имплементации LocalViewController.swift)
  • класс для вида с настройками воспроизведения: RemoteViewController (файл имплементации RemoteViewController.swift)

1. Импорт API.

code

import FPWCSApi2Swift

2. Получение списка доступных медиа-устройств.

WCSApi2.getMediaDevices code

localDevices = WCSApi2.getMediaDevices()

3. Определение камеры и микрофона для использования по умолчанию

FPWCSApi2MediaDeviceList.audio FPWCSApi2MediaDeviceList.video code

        if (localDevices?.audio?.count ?? 0 > 0) {
            microphone.text = (localDevices?.audio[0] as AnyObject).label
        }
        if (localDevices?.video?.count ?? 0 > 0) {
            camera.text = (localDevices?.video[0] as AnyObject).label
        }

4. Определение параметров аудио и видео для публикуемого потока.

FPWCSApi2VideoConstraints, FPWCSApi2AudioConstraints code

    func toMediaConstraints() -> FPWCSApi2MediaConstraints {
        let ret = FPWCSApi2MediaConstraints()
        if (self.audioSend.isOn) {
            let audio = FPWCSApi2AudioConstraints()
            audio.useFEC = audioFEC.isOn
            audio.useStereo = audioStereo.isOn
            audio.bitrate = Int(audioBitrate.text ?? "0") ?? 0
            ret.audio = audio
        }
        if (self.videoSend.isOn) {
            let video = FPWCSApi2VideoConstraints()
            for device in localDevices!.video {
                if ((device as AnyObject).label == camera.text) {
                    video.deviceID = (device as AnyObject).deviceID;
                }
            }
            video.minWidth = Int(videoWidth.text ?? "0") ?? 0
            video.maxWidth = video.minWidth
            video.minHeight = Int(videoHeight.text ?? "0") ?? 0
            video.maxHeight = video.minHeight
            video.minFrameRate = Int(videoFPS.text ?? "0") ?? 0
            video.maxFrameRate = video.minFrameRate
            video.minBitrate = Int(videoMinBitrate.text ?? "0") ?? 0
            video.maxBitrate = Int(videoMaxBitrate.text ?? "0") ?? 0
            ret.video = video;
        }
        return ret;
    }

5. Определение параметров для проигрываемого потока.

FPWCSApi2VideoConstraints, FPWCSApi2AudioConstraints  code

    func toMediaConstraints() -> FPWCSApi2MediaConstraints {
        let ret = FPWCSApi2MediaConstraints();
        ret.audio = FPWCSApi2AudioConstraints();
        if (playVideo.isOn) {
            let video = FPWCSApi2VideoConstraints();
            video.minWidth = Int(videoWidth.text ?? "0") ?? 0
            video.maxWidth = video.minWidth
            video.minHeight = Int(videoHeight.text ?? "0") ?? 0
            video.maxHeight = video.minWidth
            video.bitrate = Int(videoBitrate.text ?? "0") ?? 0
            video.quality = Int(videoQuality.text ?? "0") ?? 0
            ret.video = video;
        }
        return ret;
    }

6. Локальное тестирование микрофона и камеры

WCSApi2.getMediaAccess  code

    @IBAction func testPressed(_ sender: Any) {
        if (testButton.title(for: .normal) == "TEST") {
            let constraints = FPWCSApi2MediaConstraints(audio: true, video: true)!
            do {
                try WCSApi2.getMediaAccess(constraints, localDisplay.videoView)
            } catch {
                print(error)
            }
            testButton.setTitle("RELEASE", for: .normal)
        } else {
            WCSApi2.releaseLocalMedia(display: localDisplay.videoView);
            testButton.setTitle("TEST", for: .normal)
        }
    }

7. Создание сессии и подключение к серверу.

WCSSession, WCSSession.connect  code

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

  • URL WCS-сервера
  • имя серверного приложения defaultApp
    @IBAction func connectPressed(_ sender: Any) {
        changeViewState(connectButton, false)
        if (connectButton.title(for: .normal) == "CONNECT") {
            if (session == nil) {
                let options = FPWCSApi2SessionOptions()
                options.urlServer = urlField.text
                options.appKey = "defaultApp"
                do {
                session = try WCSSession(options)
                } catch {
                    print(error);
                }
            }
            ...
            changeViewState(urlField, false)
            session?.connect()
        }
    }

8. Публикация потока.

WCSSession.createStream, WCSStream.publish  code

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

  • options.name - имя публикуемого потока
  • options.display - вид для локального отображения
  • options.constraints - параметры аудио и видео
  • options.stripCodecs - массив кодеков, которые должны быть исключены из SDP при публикации
  • options.transport - используемый WebRTC транспорт
    @IBAction func publishPressed(_ sender: Any) {
        changeViewState(publishButton,false)
        if (publishButton.title(for: .normal) == "PUBLISH") {
            let options = FPWCSApi2StreamOptions()
            options.name = publishName.text
            options.display = localDisplay.videoView
            options.constraints = localMediaConstrains;
            options.stripCodecs = localStripCodecs?.split(separator: ",")
            options.transport = tcpTransport.isOn ? kFPWCSTransport.fpwcsTransportTCP : kFPWCSTransport.fpwcsTransportUDP;
            do {
                try publishStream = session!.createStream(options)
            } catch {
                print(error);
            }
            ...
            do {
                try publishStream?.publish()
            } catch {
                print(error);
            }
        }        
    }

9. Воспроизведение видеопотока после публикации

WCSSession.createStream, WCSStream.play  code

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

  • options.name - имя публикуемого потока
  • options.display - вид для локального отображения
  • options.constraints - параметры аудио и видео
  • options.stripCodecs - массив кодеков, которые должны быть исключены из SDP при проигрывании
  • options.transport - используемый WebRTC транспорт
    @IBAction func playPressed(_ sender: Any) {
        changeViewState(playButton,false)
        if (playButton.title(for: .normal) == "PLAY") {
            let options = FPWCSApi2StreamOptions()
            options.name = playName.text;
            options.display = remoteDisplay.videoView;
            options.constraints = remoteMediaConstrains;
            options.stripCodecs = remoteStripCodecs?.split(separator: ",")
            options.transport = tcpTransport.isOn ? kFPWCSTransport.fpwcsTransportTCP : kFPWCSTransport.fpwcsTransportUDP;
            do {
            playStream = try session!.createStream(options)
            } catch {
                print(error);
            }
            ...
            do {
                try playStream?.play()
            } catch {
                print(error);
            }
        }
    }

10. Остановка воспроизведения потока.

WCSStream.stop  code

    @IBAction func playPressed(_ sender: Any) {
        changeViewState(playButton,false)
        if (playButton.title(for: .normal) == "PLAY") {
            ...
        } else{
            do {
                try playStream?.stop();
            } catch {
                print(error);
            }
        }
    }

11. Остановка публикации потока.

WCSStream.stop  code

    @IBAction func publishPressed(_ sender: Any) {
        changeViewState(publishButton,false)
        if (publishButton.title(for: .normal) == "PUBLISH") {
            ...
        } else {
            do {
                try publishStream?.stop();
            } catch {
                print(error);
            }
        }
        
    }
  • No labels