iOS Media Devices Swift¶
Пример 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¶
2. Получение списка доступных медиа-устройств.¶
WCSApi2.getMediaDevices
code
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.minBitrate = Int(videoMinBitrate.text ?? "0") ?? 0
video.maxBitrate = Int(videoMaxBitrate.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-сервера
- имя серверного REST hook приложения
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