ios LFLiveKit 라이브러리 이용해서 RTMP 구현 예제
ios 에서 rtmp 실시간 스트리밍을 구현하기 위해서 이곳에서 라이브러리를 받아주세요.
podfile에 추가하신 후 pod install 명령어로 설치를 해봅니다.
헤더파일 생성
위의 rtmp 라이브러리를 swift에서 사용하기 위해서 헤더 파일을 만들어 줍니다.
그리고 rtmp 라이브러리를 import 해줍니다.
#ifndef Bridging_Header_h
#define Bridging_Header_h
#import <LFLiveKit/LFLiveKit.h>
#endif /* Bridging_Header_h */
그리고 빌드셋팅으로 가셔서 Bridging Header 부분에 위 브릿지 헤더 파일 경로를 작성해 줍니다.
변수 선언
먼저 LFLiveSession 타입의 변수를 선언해줍니다.
//MARK: 세션
var session: LFLiveSession!
ViewDidLoad 작성
ViewDidLoad 부분에서 session을 설정하고 초기화 하는 로직을 작성해줍니다. 그리고 스트리밍 시작과 카메라 전환 버튼도 넣어줍니다.
//MARK: viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
let screenW = UIScreen.main.bounds.size.width
//MARK: 시작/종료버튼
let playBtn = UIButton(frame: CGRect(x: (screenW - 100) / 2, y: 650, width: 100, height: 40))
playBtn.backgroundColor = .black
playBtn.setTitle("play", for: .normal)
playBtn.setTitle("stop", for: .selected)
playBtn.addTarget(self, action: #selector(playBtnOnClick(btn:)), for: .touchUpInside)
view.addSubview(playBtn)
//MARK: 카메라 전환 버튼
let switchCameraBtn = UIButton(frame: CGRect(x: (screenW - 100) / 2, y: 700, width: 100, height: 40))
switchCameraBtn.backgroundColor = .black
switchCameraBtn.setTitle("카메라 전환", for: .normal)
switchCameraBtn.addTarget(self, action: #selector(switchCameraBtnOnClick(btn:)), for: .touchUpInside)
view.addSubview(switchCameraBtn)
//설정값
let audioConfiguration = LFLiveAudioConfiguration.default()
let videoConfiguration = LFLiveVideoConfiguration.defaultConfiguration(for: LFLiveVideoQuality.low1, outputImageOrientation: .portrait)
//MARK: 세션 초기화
session = LFLiveSession(audioConfiguration: audioConfiguration, videoConfiguration: videoConfiguration)
session.delegate = self
session.preView = self.view
session.captureDevicePosition = .back
self.requestAccessForAudio()
self.requestAccessForVideo()
}
오디오 & 비디오 접근 권한 체크 함수
오디오와 비디오 접근 권한을 체크하는 함수를 작성해 줍니다. 승인이 되었다면 session.running = true 로 설정해줍니다.
//MARK: 비디오 접근
func requestAccessForVideo() {
print(":::: 비디오 접근권한 체크 :::: ")
let state = AVCaptureDevice.authorizationStatus(for: .video)
switch state {
case .notDetermined:
print(":::: 비디오 접근 notDetermined :::: ")
AVCaptureDevice.requestAccess(for: .video, completionHandler: { (grandted) in
guard grandted else { return }
DispatchQueue.main.async {
self.session.running = true
}
})
case.authorized:
print(":::: 이미 비디오 접근 승인 얻었음 :::: ")
DispatchQueue.main.async {
self.session.running = true
}
break
case.denied:
print(":::: 비디오 접근 denied :::: ")
break
case.restricted:
print(":::: 비디오 접근 restricted :::: ")
break
@unknown default:
print("....")
}
}
//MARK: 오디오 접근
func requestAccessForAudio() {
print(":::: 오디오 접근권한 체크 :::: ")
let state = AVCaptureDevice.authorizationStatus(for:.audio)
switch state {
case .notDetermined:
print(":::: 오디오 접근 notDetermined :::: ")
AVCaptureDevice.requestAccess(for: .audio, completionHandler: { (granted) in
guard granted else{
return
}
})
case.authorized:
print(":::: 이미 오디오 접근 승인 얻었음 :::: ")
break
case.denied:
print(":::: 오디오 접근 denied :::: ")
break
case.restricted:
print(":::: 오디오 접근 restricted :::: ")
break
@unknown default:
print("....")
}
}
LFLiveSessionDelegate 메소드
//MARK: LFLiveSessionDelegate
extension ViewController : LFLiveSessionDelegate {
//MARK: - Callback
func liveSession(_ session: LFLiveSession?, debugInfo: LFLiveDebug?){
print(":::: debugInfo: \(String(describing: debugInfo?.currentBandwidth))") }
func liveSession(_ session: LFLiveSession?, errorCode: LFLiveSocketErrorCode){
print(":::: errorCode : \(errorCode.rawValue)")
}
func liveSession(_ session: LFLiveSession?, liveStateDidChange state: LFLiveState){
print(":::: liveStateDidChange: \(state.rawValue)")
switch state {
case .ready:
print("준비")
case .pending:
print("연결중...")
case .start:
print("실시간 스트리밍 시작")
case .refresh:
print("리프레쉬...")
case .stop:
print("정지")
case .error:
print("오류...")
@unknown default:
print(".....")
}
}
}
rtmp 스트리밍 시작과 종료 함수
//MARK: 스트리밍 시작
func startLive() -> Void {
print(":::: 라이브 시작 :::: ")
let stream = LFLiveStreamInfo()
//stream.url = "rtmp://나의 도메인/live/test" //hls 방식으로 스트리밍 요청
stream.url = "rtmp://나의 도메인/live_rtmp/test" //rtmp 방식으로 스트리밍 요청
session.startLive(stream)
}
func stopLive() -> Void {
print(":::: 라이브 정지 :::: ")
session.running = false
session.stopLive()
}
플레이버튼과 카메라 전환 버튼 구현
//MARK: - 버튼 Event
@objc func playBtnOnClick(btn: UIButton) {
btn.isSelected = !btn.isSelected
if btn.isSelected {
print(":::: 시작 버튼 클릭됨 :::: ")
startLive()
} else {
print(":::: 종료 버튼 해제됨 :::: ")
stopLive()
}
}
@objc func switchCameraBtnOnClick(btn: UIButton) {
if session.captureDevicePosition == .back {
session.captureDevicePosition = .front
}else if session.captureDevicePosition == .front {
session.captureDevicePosition = .back
}
}
테스트
위의 코드로 플레이 해보면, 현재 아이폰 화면에서 보여지는 화면이 해당 url의 rtmp 서버로 전송이되고, 다른 사람들이 팟 플레이어 등 rtmp 라이브 스트리밍 지원하는 플레이어에서도 시청을 할 수 있습니다.
전체 코드
import UIKit
class ViewController: UIViewController {
//MARK: 세션
var session: LFLiveSession!
//MARK: viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
let screenW = UIScreen.main.bounds.size.width
//MARK: 시작/종료버튼
let playBtn = UIButton(frame: CGRect(x: (screenW - 100) / 2, y: 650, width: 100, height: 40))
playBtn.backgroundColor = .black
playBtn.setTitle("play", for: .normal)
playBtn.setTitle("stop", for: .selected)
playBtn.addTarget(self, action: #selector(playBtnOnClick(btn:)), for: .touchUpInside)
view.addSubview(playBtn)
//MARK: 카메라 전환 버튼
let switchCameraBtn = UIButton(frame: CGRect(x: (screenW - 100) / 2, y: 700, width: 100, height: 40))
switchCameraBtn.backgroundColor = .black
switchCameraBtn.setTitle("카메라 전환", for: .normal)
switchCameraBtn.addTarget(self, action: #selector(switchCameraBtnOnClick(btn:)), for: .touchUpInside)
view.addSubview(switchCameraBtn)
//설정값
let audioConfiguration = LFLiveAudioConfiguration.default()
let videoConfiguration = LFLiveVideoConfiguration.defaultConfiguration(for: LFLiveVideoQuality.low1, outputImageOrientation: .portrait)
//MARK: 세션 초기화
session = LFLiveSession(audioConfiguration: audioConfiguration, videoConfiguration: videoConfiguration)
session.delegate = self
session.preView = self.view
session.captureDevicePosition = .back
self.requestAccessForAudio()
self.requestAccessForVideo()
}
//MARK: - 버튼 Event
@objc func playBtnOnClick(btn: UIButton) {
btn.isSelected = !btn.isSelected
if btn.isSelected {
print(":::: 시작 버튼 클릭됨 :::: ")
startLive()
} else {
print(":::: 종료 버튼 해제됨 :::: ")
stopLive()
}
}
@objc func switchCameraBtnOnClick(btn: UIButton) {
if session.captureDevicePosition == .back {
session.captureDevicePosition = .front
}else if session.captureDevicePosition == .front {
session.captureDevicePosition = .back
}
}
//MARK: 스트리밍 시작
func startLive() -> Void {
print(":::: 라이브 시작 :::: ")
let stream = LFLiveStreamInfo()
//stream.url = "rtmp://나의 도메인/live/test" //hls 방식으로 스트리밍 요청
stream.url = "rtmp://나의 도메인/live_rtmp/test" //rtmp 방식으로 스트리밍 요청
session.startLive(stream)
}
func stopLive() -> Void {
print(":::: 라이브 정지 :::: ")
session.running = false
session.stopLive()
}
//MARK: 비디오 접근
func requestAccessForVideo() {
print(":::: 비디오 접근권한 체크 :::: ")
let state = AVCaptureDevice.authorizationStatus(for: .video)
switch state {
case .notDetermined:
print(":::: 비디오 접근 notDetermined :::: ")
AVCaptureDevice.requestAccess(for: .video, completionHandler: { (grandted) in
guard grandted else { return }
DispatchQueue.main.async {
self.session.running = true
}
})
case.authorized:
print(":::: 이미 비디오 접근 승인 얻었음 :::: ")
DispatchQueue.main.async {
self.session.running = true
}
break
case.denied:
print(":::: 비디오 접근 denied :::: ")
break
case.restricted:
print(":::: 비디오 접근 restricted :::: ")
break
@unknown default:
print("....")
}
}
//MARK: 오디오 접근
func requestAccessForAudio() {
print(":::: 오디오 접근권한 체크 :::: ")
let state = AVCaptureDevice.authorizationStatus(for:.audio)
switch state {
case .notDetermined:
print(":::: 오디오 접근 notDetermined :::: ")
AVCaptureDevice.requestAccess(for: .audio, completionHandler: { (granted) in
guard granted else{
return
}
})
case.authorized:
print(":::: 이미 오디오 접근 승인 얻었음 :::: ")
break
case.denied:
print(":::: 오디오 접근 denied :::: ")
break
case.restricted:
print(":::: 오디오 접근 restricted :::: ")
break
@unknown default:
print("....")
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
stopLive()
}
}
//MARK: LFLiveSessionDelegate
extension ViewController : LFLiveSessionDelegate {
//MARK: - Callback
func liveSession(_ session: LFLiveSession?, debugInfo: LFLiveDebug?){
print(":::: debugInfo: \(String(describing: debugInfo?.currentBandwidth))") }
func liveSession(_ session: LFLiveSession?, errorCode: LFLiveSocketErrorCode){
print(":::: errorCode : \(errorCode.rawValue)")
}
func liveSession(_ session: LFLiveSession?, liveStateDidChange state: LFLiveState){
print(":::: liveStateDidChange: \(state.rawValue)")
switch state {
case .ready:
print("준비")
case .pending:
print("연결중...")
case .start:
print("실시간 스트리밍 시작")
case .refresh:
print("리프레쉬...")
case .stop:
print("정지")
case .error:
print("오류...")
@unknown default:
print(".....")
}
}
}
'아이폰 개발 > ios 개념&튜토리얼' 카테고리의 다른 글
swift - 클래스와 구조체 정리하기 (0) | 2022.10.17 |
---|---|
mac에서 gitlab과 sourceTtree 연동 후 push 까지 (0) | 2022.09.20 |
ios MQTT 통신 - swift로 구현하기 (0) | 2022.09.14 |
swift 프로퍼티 정리 (lazy var , 연산프로퍼티, 저장 프로퍼티) (0) | 2022.09.01 |
ios collectionView Tag cell - 여러 옵션 선택하기 기능 구현 (0) | 2022.08.03 |