라이브 하기

라이브를 하려면 크게 라이브를 위한 인스턴스와 카메라 화면을 보여주기 위한 View 두가지가 필요합니다. 이 두 가지를 가지고 화면 송출을 위한 준비과정을 거친 후 라이브 인스턴스의 start 함수를 호출하면 라이브가 시작됩니다.

1. 화면 구성하기

라이브 화면을 보여주기 위해 UIView를 설정합니다. 스토리 보드나 SnapKit 등을 사용해서 UIView를 만들어 줍니다.

2. 라이브를 위한 인스턴스 생성

라이브를 하려면 먼저 streamer 인스턴스를 생성해야 합니다. 생성방법은 다음과 같습니다.

// 라이브를 시작하기 위한 객체 생성
let streamer = FlipFlop.getRTMPStreamer()

위의 '1. 화면 구성하기'에서 구성한 뷰를 인스턴스에 연결합니다.

// 카메라 화면(UIView)을 연결하고 라이브 설정값을 지정합니다.
// self.preview : 1. 화면 구성하기에서 설정한 UIView
@IBOutlet weak var preview: UIView!
let config = FFStreamerConfig()
config.preset = .hd1280x720 // or hd1920x1080
config.sampleRate = 32000
config.cameraPos = .back
streamer.prepare(preview: self.preview, config: config)

위 prepare 함수 호출할 때 라이브 송출에 필요한 설정 값들을 조정해 줄 수 있습니다. 설정해줄 수 있는 값은 다음과 같습니다.

설명기본값
preset송출 화면의 width&height.hd1280x720
videoBitrate비디오 비트레이트2000 * 1024
keyFrameInterval키 프레임 간격2
fps프레임 레이트(framerate)30
sampleRate오디오 샘플레이트48000
audioBitrate오디오 비트레이트64 * 1024
cameraPos카메라 위치(전면 또는 후면전면

라이브의 상태를 알고 싶은 경우 인스턴스(streamer)에 delegate를 등록해줍니다. 현재의 ViewController에 FFStreamerDelegate protocol을 구현해주고 streamer의 delegate와 연결해줍니다.

// 라이브의 상태 리스너를 연결합니다.
streamer.delegate = self
extension XXXViewController : FFStreamerDelegate {
// 관련 함수를 구현합니다.
}

FFStreamerDelegate의 내용은 다음과 같습니다.

함수설명
onPrepared라이브가 준비가 되었음을 알려줍니다. prepare 함수 실행이 정상적으로 이루어지면 호출됩니다.
onStarted라이브가 정상적으로 시작되면 호출됩니다.
onChatMessageReceived채팅 메시지가 들어오면 호출됩니다.
onChatStatReceived채팅 관련 통계 정보가 들어오면 호출됩니다.
onStopped라이브가 중단되면 호출됩니다.
onError라이브 중 에러가 발생하면 호출됩니다.

3. 라이브 시작하기

라이브를 시작하기 전에 라이브 관련 정보를 먼저 FlipFlop에 등록해야 합니다. 등록이 가능한 라이브 정보는 제목, 내용, 썸네일이 있습니다.

val title = “제목"
val content = “내용"
// 라이브의 제목과 내용을 등록합니다.
sdk.createVideo(title: title, content: content) { [weak self] (result) in
switch result {
case .failure(let error):
// error
case .success(let video):
// video: 비디오 정보
self.streamkey = video.stream_key
self.livekey = video.live_key
}
}

추가로 썸네일도 등록하고 싶으면 updateVideoThumbnail 함수를 사용해서 썸네일을 저장 할 수 있습니다. 선택한 파일(thumbnail)은 FlipFlop에서 사용하는 저장소에 업로드가 되고, 라이브 정보의 thumbnail_url에 저장됩니다.

sdk.uploadVideoThumbnail(videokey: video, image: image) {
// handle completion
}

4. 라이브 송출 시작하기

start 함수를 호출하면 라이브가 시작됩니다. 지금 상태로 라이브를 시작하면 기본 기능으로 라이브가 시작됩니다. FlipFlop에서는 라이브 화면 송출을 위한 다양한 기능을 제공하고 있습니다. 이에 대한 내용은 뒤의 6,7,8 섹션을 참고하시기 바랍니다.

// 위 항목에서 받아온 스트림 키와 라이브 키를 사용해서 라이브를 시작합니다.
self?.streamer?.start(sdk: sdk, streamkey: streamkey, livekey: livekey)

5. 채팅 메시지 보내고 받기

라이브를 하면서 라이브를 보고 있는 사용자들과 함께 채팅 메시지를 주고 받을 수 있습니다. (메시지를 주고 받을 때 사용하는 메시지 타입에 대한 자세한 내용은 채팅 섹션에서 확인하실 수 있습니다.)

채팅 메시지를 보내고 싶으면 streamer의 sendMessage 함수를 사용합니다.

// 메시지 보내기
val message = “안녕하세요.
streamer.sendMessage(text: message)

지정한 사용자에게만 메시지를 보내고 싶으면 'sendDM' 함수를 사용합니다.

// 메시지 보내기
val receiver = "100"
val message = “안녕하세요.
streamer.sendDM(receiver: receiver, text: message)

다른 사용자가 채팅 메시지를 보냈을 경우는 FFStreamerDelegate의 onChatMessageReceived로 채팅 메시지가 들어옵니다. 이 함수 안에서 받은 메시지에 대한 처리를 합니다.

override fun onChatMessageReceived(message: FFMessage) {
// 받은 메시지 처리
}

6. 라이브 송출 중단하기

라이브를 중단하는 방법은 두 가지가 있을 수 있습니다.

기본적으로는 FlipFlop 서버에 라이브가 끝났음을 알리고 종료하는 것입니다. stopVideo와 reset 함수를 호출하여 현재 동작중인 라이브를 종료합니다. 옵션으로 stopVideo 함수 호출시 keepLive 파라메터를 통해 상태를 조정할 수 있습니다. keepLive가 false이면 라이브를 종료 상태(재시작 불가)로 변경하고, true이면 중단 상태(재시작 가능)로 변경합니다.

// sdk의 함수임에 주의!
// keepLive가 true이면 라이브를 종료 상태로 변경하고, false이면 중단 상태로 변경합니다.
// 보통의 경우는 false로 주어 바로 종료로 합니다.
// videokey는 라이브를 시작할 때 사용한 videokey
self.streamer?.stop()
sdk.endVideo(videokey: videokey, keepLive: false, completion: nil)

두번째는 명시적으로 서버에 종료 사실을 알리지 않고 클라이언트에서 송출만 중단하는 것입니다.

streamer.reset()

keepLive를 true로 해서 ‘stopVideo’를 호출하거나, reset 함수만을 호출해서 라이브를 중단하는 경우는 주의해야 할 것이 있습니다. 이러한 경우 서버(FlipFlop)는 라이브의 상태를 일시 중단으로 놓고 클라이언트로부터 라이브 송출이 다시 시작하기를 기다립니다. 네트워크 이상으로 라이브가 끊겼다가 다시 연결되거나 또는 잠시 홈화면으로 가서 일시적으로 송출만 중단했다가 나중에 다시 시작하는 등의 작업이 가능하도록 하기 위함입니다.(FlipFlop에서의 라이브는 한번 종료 상태로 바뀌면 기존 라이브를 다시 시작할 수 없고, 다른 라이브를 다시 시작해야 합니다.) 다만, 무한정 기다리지는 않고, 기본 설정으로 10분 동안 송출이 중단되면 라이브를 종료로 변경하게 되어 있습니다. 따라서, 다시 시작하고 싶으면 10분 이내에 라이브를 다시 시작해야 합니다. 다시 시작하는 경우는 startStreaming을 videoKey와 함께 다시 호출해 주면 됩니다.(라이브 시작하기 항목 참고)

7. 라이브 송출 재시작하기

네트워크는 항상 일정하지 않습니다. 또한 알 수 없는 이유로 송출이 일시적으로 중단될 수 있습니다. 이런 경우 다시 송출을 시작하고 싶은 경우 restart 함수를 호출합니다.

// delay : 지정된 시간 이후에 재접속을 시도하도록 한다.(milliseconds)
// 기존 정보가 남아 있을 수 있으므로 2,3초 후에 다시 시도하는 것이 좋습니다.
streamer.restart(client: sdk)

8. 카메라 제어 관련 기능

함수설명
switchCamera카메라 전면/후면 전환
videoMirror카메라 좌우 반전(true or false)
zoom카메라 줌(factor : 줌 크기, 예: 2배면 2f를 넣어준다.)
setPointOfInterest카메라 포커스(선택 지점 지정)
continuousAutoFocus오토 포커스(true or false)
minExposureTargetBias디바이스 최소 카메라 노출 bias
maxExposureTargetBias디바이스 최대 카메라 노출 bias
exposureTargetBias최소, 최대 사이의 카메라 노출 bias 값 설정하기

9. 라이브 송출 화면 관련 기능

  • 필터

라이브 화면을 원본 그대로 송출하지 않고 필터를 적용해서 송출할 수 있습니다. 현재는 sephia, blendMaks, sourceOverComposit, colorOverlay, custom을 지원하고 있습니다.

public enum VideoFilter {
case none
case sephia(value: Float)
case blendMaks(overlayImage: CIImage, maskImage: CIImage)
case sourceOverComposit(overlayImage: CIImage)
case colorOverlay(color: UIColor)
case custom(effect: VideoEffect)
}
streamer.setFilter(filter: VideoFilter)
  • PIP

라이브 화면의 배경에 이미지를 보여주고 카메라 화면은 우상단에 보여주는 기능입니다. 예를 들어, 말로 이것저것 설명하기 보다는 관련 설명이 있는 이미지를 보여주고 싶은 경우 사용하면 유용한 기능입니다. backgroundImage에 nil을 주면 PIP 모드를 중지합니다.

let image = 배경에 보여줄 이미지
let scale = 0.25 // 원래 라이브 송출 영상의 크기를 얼마나 작게 보여줄지 지정
streamer.setBackground(backgroundImage: image, scale: scale)
  • 이펙트 기능

라이브 중 화면 중간에 특수 효과를 넣어 줄 수 있습니다. 예를 들어, 상품을 판매중인데 상품이 품절이 된 경우 말로 이야기하는 것 보다는 화면상에 재미있는 움직이는 품절 이미지(Animated GIF)를 영상에 포함해서 보여주면 좋을 것입니다.

// named에 animated gif의 이름을 넣어줍니다.
// animated gif의 애니메이션이 끝나면 completion이 호출됩니다.
streamer.playGif(named: String, {
print("gif ended")
})
  • 오디오 뮤트

라이브를 하는 중에 잠시 오디오를 끄고 싶을 수 있습니다. 이런 경우 mute 함수를 사용해서 켜고 끌 수 있습니다.

// true or false
streamer.mute(true)

10. 영상 제어하기

송출 영상의 비트레이트를 조정하여 영상의 화질을 조정할 수 있습니다.

  • 비트레이트 변경

고정된 비트레이트로 영상이 유지됩니다. 비트레이트 값을 올리면 화질이 좋아지기는 하지만 영상의 용량이 커지기 때문에 좋지 않은 네트워크 상황에서는 라이브가 불안정해 질 수 있습니다. 따라서, 네트워크 상황에 따라 적절히 사용해 주는 것이 좋습니다.

// KB unit
streamer.videoBitrateOnFly = 2000 * 1024 // 2MB
  • 자동 비트레이트 설정

adaptive video bitrate를 true로 설정하면 네트워크 상황에 따라 자동으로 조정이 됩니다. 자동으로 바뀌니 좋아 보이지면 이 설정에도 단점은 있습니다. 값이 실시간으로 바로 바로 바뀌는 것은 아니기 때문입니다. 따라서, 네트워크의 급격한 변화에는 대응하지 못할 수도 있습니다.

// true or false
streamer.adaptiveBitrate = true