Пример iOS-приложения для управления медиа-устройствами
Данный пример может использоваться как стример для публикации WebRTC-видеопотока с Web Call Server и позволяет выбрать медиа-устройства и следующие параметры для публикуемого и проигрываемого видео
- разрешение (ширина, высота)
- скорость передачи (bitrate)
- FPS (Frames Per Second) - для публикуемого видео
- quality - для проигрываемого видео
Поток может быть опубикован как с аудио и видео, так и без аудио или видео (переключатели 'Send Audio' и 'Send Video').
Аудио и видео в публикуемом потоке могут быть выключены/включены соответствующими переключателями 'Mute Audio' и 'Mute Video' во время или до начала публикации.
Видео потоки могут воспроизводиться с видео или без видео (переключатель 'Play Video').
На скриншоте ниже представлен пример во время публикации потока.
В URL в поле ввода demo.flashphoner.com - адрес WCS-сервера.
Слева отображается видео с камеры, справа воспроизводится опубликованный поток.
Вид с контролами для настроек публикации показывается при нажатии на кнопку 'Local settings'
а вид с контролами для настроек воспроизведения - при нажатии на кнопку 'Remote settings'.
Работа с кодом примера
Для разбора кода возьмем версию примера MediaDevices, которая доступна для скачивания на 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. Определение параметров аудио и видео для публикуемого потока.
FPWCSApi2AudioConstraints, 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.bitrate = Int(videoBitrate.text ?? "0") ?? 0 ret.video = video; } return ret; }
5. Определение параметров для проигрываемого потока.
FPWCSApi2AudioConstraints, 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 передаются параметры:
- имя публикуемого потока
- вид для локального отображения
- параметры аудио и видео
@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; do { try publishStream = session!.createStream(options) } catch { print(error); } ... do { try publishStream?.publish() } catch { print(error); } } }
9. Воспроизведение видеопотока после публикации
WCSSession.createStream, WCSStream.play code
Методу createStream передаются параметры:
- имя воспроизводимого потока
- вид для отображения потока
- параметры видео и аудио
@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; 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); } } }
12. Остановка публикации потока.
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); } } }