5. 라이브 송출 제어하기

5.1. 송출 화면 제어하기

FlipFlop은 단순히 카메라 화면을 송출하는 것에 더하여 다양한 화면 제어 기능을 제공하고 있습니다.

5.1.1. FFView

FFView에 기본으로 화면 터치시 터치 영역에 포커스를 맞추는 기능과 핀치줌에 따른 카메라 줌 기능이 들어가 있습니다.

핀치 줌으로 카메라 줌이 되는 경우 줌이 얼마로 변하는지를 zoomChangeListener를 통해 알 수 있습니다. streamer 생성시 zoomChangeListener를 지정해줌으로서 줌 값을 알 수 있습니다.

streamer = sdk.getStreamer()?.apply {
listener = this@StreamingFragment
prepare(requireContext(), binding.liveView, FFStreamerConfig(videoBitrate = 2000 * 1024, fps = 30, sampleRate = 44100))
zoomChangeListener = object : FFZoomChangeListener {
override fun onZoomChanged(zoomFactor: Float) {
// zoom changed to 'zoomFactor'
}
}
}

5.1.2 카메라 제어하기

보통의 카메라 앱에서 보여주는 것과 같은 카메라 기능이 있습니다.

함수설명
switchCamera카메라 전면/후면 전환
videoMirror카메라 좌우 반전(true or false)
zoom카메라 줌(factor : 줌 크기, 예: 2배면 2f를 넣어준다.)
setPointOfInterest카메라 포커스(선택 지점 지정)
enableAutoFocus오토 포커스 시작
disableAutoFocus오토 포커스 종료
tapToFocus수동 포커스(x, y 좌표 지정)
getExposureCompensationRange카메라 노출 정보 알아오기(설정 가능한 값의 min, max, step을 얻어온다.)
exposureCompensation카메라 노출 설정하기(getExposureCompensationRange에서 얻어온 영역의 값중 원하는 값으로 설정한다.)
  • 카메라 포커스의 경우 기본으로 FFView에 기능이 들어가 있지만 tapToFocus() 함수를 사용하면 수동으로도 제어가 가능합니다.

  • 카메라 노출을 설정하려면 getExposureCompensationRange() 함수로 먼저 노출을 위한 최대, 최소, 스텝 값을 얻어온 후 exposureCompensation() 함수를 통해 값을 설정해 줍니다.

  • ExposureCompensationRange에는 다음의 필드가 있습니다.

    • min: 노출을 위한 최소값
    • max: 노출을 위한 최대값
    • step: 최소와 최대값 사이의 노출 간격

5.2. 오디오 뮤트하기

라이브중 오디오만 잠시 중단하고 싶은 경우 mute() 함수를 사용합니다.

// turn audio off
streamer?.mute(false)
함수설명
mute오디오 뮤트(true or false)

5.3. 카메라 화면에 필터 적용하기

FlipFlop에서는 화면에 필터를 적용하는 기능을 제공합니다. 현재는 5가지 필터를 제공하고 있습니다.

  • TONE_DARK
  • TONE_DRAMATIC_COOL
  • TONE_VIVID_DARK
  • TONE_VIVID_WARM
  • TONE_WARM
// apply TONE_DARK filter
streamer?.setFilter(FFFilterType.TONE_DARK)

5.4. 이펙트 기능

라이브 중 화면 중간에 특수 효과를 넣어 줄 수 있습니다. 카메라 화면과 설정한 이미지를 합성하여 송출합니다. 현재는 animated GIF만 지원합니다.

  • 예: 상품을 판매중인데 상품이 품절이 된 경우 화면상에 재미있는 움직이는 품절 이미지를 영상에 포함해서 보여준다.
// there is a animated gif named soldout in raw directory
val inputStream = resources.openRawResource(R.raw.soldout)
// FFScaleMode : NONE, CENTER_FIT, CENTER_CROP, FILL
val scaleMode = FFScaleMode.CENTER_FIT
streamer.setOverlayImage(inputStream, scaleMode)

setOverlayImage 함수를 사용해서 이펙트를 보여준 후 적당한 시점에 종료해 주어야 합니다.(예를 들어 이미지가 animated gif 인 경우 종료를 해주지 않으면 애니메이션이 계속 반복될 수 있습니다.)

// with no parameter
streamer.setOverlayImage()

5.5. 영상의 비트레이트 조정하기

수동으로 비트레이트를 조정하지 않고 자동으로 알아서 조정이 되게 하고 싶으면 adaptiveBitrate 값을 true로 설정해 줍니다. true로 설정하면 네트워크의 상태에 따라 SDK에서 알아서 비트레이트를 적당히 조절해 가면서 라이브 송출을 하게 됩니다.

streamer?.adaptiveBitrate = true

앞에서 라이브 송출을 시작하기 전에 prepare()의 파라메터로 FFStreamerConfig를 통해 비트레이트를 지정해 줄 수 있었습니다. 이에 더해 라이브 진행중에도 비트레이트를 변경할 수 있습니다. videoBitrateOnFly 변수에 값을 지정해 주면 이 값으로 영상의 비트레이트가 변경이 됩니다. 항상 고정된 비트레이트로 동작하도록 하고 싶거나 네트워크의 상태가 나빠져서 비트레이트 값을 내려서 송출하고 싶은 경우에 사용합니다.

고정된 비트레이트를 사용하는 경우에는 adaptiveBitrate 값이 false여야 합니다.

// apply video bitrate
streamer?.videoBitrateOnFly = 2000 * 1024

5.6. 카메라 화면 대신 이미지 보여주기

라이브를 송출하는 도중에 카메라 화면말고 이미지를 보여주고 싶은 경우에 사용합니다. 이미지를 보여줄 때 애니메이션을 지정할 수 있습니다. 이미지를 보여줄 떄는 showImage() 함수를 사용하고, 이미지를 제거할 때는 hideImage() 함수를 사용합니다.

showImage() 호출시 파라메터로 애니메이션의 형태를 지정할 수 있습니다. 설정 가능한 애니메이션은 다음과 같습니다.

  • ENTER_TOP을 사용하는 예
val resource: Bitmap = image.toBitmap() // get bitmap from image
val transitionParams = FFTransitionParams(
transitionType = FFTransitionType.ENTER_TOP,
duration = 500,
)
streamer?.showImage(resource, FFScaleMode.CENTER_FIT, transitionParams)
  • FADE_IN_PIP 애니메이션을 사용하는 예
val resource: Bitmap = image.toBitmap() // get bitmap from image
val transitionParams = FFTransitionParams(
transitionType = FFTransitionType.FADE_IN_PIP,
duration = 500,
pipTop = 0.2, // specify how far to fall from the top of screen
pipRight = 0.2, // specify how far to fall from the right of screen
pipRatio = 0.2 // specify how much to scale the camera screen
)
streamer?.showImage(resource, FFScaleMode.CENTER_FIT, transitionParams)
  • SLIDE_TO_CAMERA_BOTTOM을 사용하는 예
val resource: Bitmap = image.toBitmap() // get bitmap from image
val transitionParams = FFTransitionParams(
transitionType = FFTransitionType.SLIDE_TO_CAMERA_BOTTOM,
duration = 500,
cameraRatio = 0.2, // specify how far down the camera screen
)
streamer?.showImage(resource, FFScaleMode.CENTER_FIT, transitionParams)
  • 제공하는 전체 애니메이션은 다음과 같습니다.
애니메이션설명
NONE애니메이션 없이 이미지만 보여준다.
FADE_IN_OUT카메라 화면은 점점 사라지고 이미지는 점점 나타난다.
SLIDE_TO_LEFT카메라 화면은 오른쪽으로 사라지고 이미지는 오른쪽에서 나타난다.
SLIDE_TO_TOP카메라 화면은 위로 사라지고 이미지는 아래에서 나타난다.
SLIDE_TO_CAMERA_BOTTOM카메라 화면은 아래로 지정한 비율만큼 내려가고 이미지는 지정한 비율만큼 위에서 내려온다.
FADE_IN_PIP이미지는 점점 나타나고 카메라 화면은 지정한 크기와 위치로 축소된다.
ENTER_TOP카메라 화면은 점점 사라지고 이미지는 위에서 나타난다.
ENTER_FADE_IN카메라 화면은 바로 없어지고 이미지는 점점 나타난다.

showImage()를 통해 보여줬던 이미지를 제거할 때는 hideImage()를 호출합니다. 이때의 애니메이션은 showImage()를 할 때의 애니메이션 효과와 반대로 진행이 됩니다.

5.7. 송출 상태 알림 받기

네트워크 상태에 따라 라이브 송출 상태가 변할 수 있습니다. SDK로부터 송출 상태에 대한 정보를 받을 수 있습니다. streamer instance에 alarmListener를 등록해두면 상태 변경을 받을 수 있습니다.

streamer = sdk.getStreamer()?.apply {
listener = this@StreamingFragment
prepare(requireContext(), binding.liveView, FFStreamerConfig(videoBitrate = 2000 * 1024, fps = 30, sampleRate = 44100))
zoomChangeListener = object : FFZoomChangeListener {
override fun onZoomChanged(zoomFactor: Float) {
// zoom changed to 'zoomFactor'
}
}
alarmListener = object : FFAlarmListener {
override fun onAlarmPublished(state: AlarmState) {
// changed alarm state
}
}
}
  • alarmListener에서 받아오는 AlarmState는 다음과 같이 3단계의 값을 갖습니다.
    • AlarmState의 값이 순서대로 호출되지는 않습니다. 만약 송출 상태가 갑자기 나빠진다면 ALERT_3이 제일 먼저 호출될 수 있습니다.
상태설명
NORMAL정상
ALERT_1송출 상태가 약간 좋지 않음
ALERT_2송출 상태가 조금 좋지 않음
ALERT_3송출 상태가 많이 좋지 않음