ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

728x90
๋ฐ˜์‘ํ˜•

ํ•ด๋‹น ๊ธ€์€, ํ™”๋ฉด๋ฐฉ์†ก์— ๋Œ€ํ•œ ๊ฐœ์ธ ๊ณต๋ถ€๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ์‹œ์ฒญํ•œ WWDC 2018 - Live Screen Broadcast with ReplayKit์„ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

ํ•ด์„๊ณผ ์ •๋ฆฌ๋œ ๋‚ด์šฉ์ด ์™„๋ฒฝํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์ž˜๋ชป๋œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„  ์ง€์  ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๐Ÿ™

 

 

1. WWDC 2016 - Go Live with ReplayKit
2. WWDC 2018 - Live Screen Broadcast with ReplayKit

 


 

 

- ReplayKit์€ ์‚ฌ์šฉ์ž์˜ App์˜ ํ™”๋ฉด, ์˜ค๋””์˜ค ๋ฐ ๋งˆ์ดํฌ ์‚ฌ์šด๋“œ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ธฐ๋กํ•˜๊ณ  ์ˆ˜์ •ํ•˜๊ณ  ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค.
- ReplayKit์€ Live Broadcast๋ฅผ ์ง€์›ํ•œ๋‹ค.

 

ReplayKit

 

  • ReplayKit์€ ํ™”๋ฉด๊ณผ ์˜ค๋””์˜ค ๊ทธ๋ฆฌ๊ณ  3rd-party ๋ผ์ด๋ธŒ์ŠคํŠธ๋ฆฌ๋ฐ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ReplayKit์€ ์‹ค์‹œ๊ฐ„๋ฐฉ์†ก ์•ฑ์„ ์œ„ํ•ด ๋‹ค๋ฅธ Application์—์„œ ์บก์ฒ˜ํ•œ ์ฝ˜ํ…์ธ ๋ฅผ ์ˆ˜์‹ ๋ฐ›๊ณ , ๋™์ผํ•œ ๋””๋ฐ”์ด์Šค์—์„œ ๋ฐ”๋กœ ์„œ๋ฒ„๋กœ ์ธ์ฝ”๋”ฉ ๋ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

 

ReplayKit์€ ์•„๋ž˜์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํŠน์ง•์„ ๊ฐ€์ง„๋‹ค

  • HD ํ™”์งˆ์„ ์ œ๊ณต
  • ๋‚ฎ์€ ์ง€์—ฐ ์‹œ๊ฐ„ 
  • ์ ์€ ์„ฑ๋Šฅ ์˜ํ–ฅ
  • ์ ์€ ๋ฐฐํ„ฐ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰
  • ๊ฐœ์ธ ์ •๋ณด ๋ณดํ˜ธ ๋ฐฉ์•ˆ ๋งˆ๋ จ

Live Broadcast

  • 3rd-party ์„œ๋น„์Šค๋ฅผ ํ†ตํ•œ ๋ผ์ด๋ธŒ ๋ฐฉ์†ก ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ์˜ค๋””์˜ค ๋ฐ ๋น„๋””์˜ค๋ฅผ ์‚ฌ์šฉ์ž์˜ ๋””๋ฐ”์ด์Šค์—์„œ ์ง์ ‘ stream ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ํ™”๋ฉด ๋…นํ™”๋ฅผ ํ•˜๋Š” ๋™์‹œ์— ๋งˆ์ดํฌ๋ฅผ ํ†ตํ•œ ์‚ฌ์šฉ์ž์˜ ์Œ์„ฑ๋„ ๋…น์Œ ๋ฐ FaceTime์„ ํ™œ์šฉํ•œ ์นด๋ฉ”๋ผ ๊ธฐ๋Šฅ๋„ ์ œ๊ณตํ•œ๋‹ค (์นด๋ฉ”๋ผ๋Š” iOS ํ•œ์ •)
  • ๋ชจ๋“  ์ฝ˜ํ…์ธ ๋“ค์€ ์•ˆ์ „ํ•˜๊ณ , ์˜ค์ง broadcast service๋ฅผ ํ†ตํ•ด์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

Usage example

  • Youtube๋ฅผ ํ†ตํ•œ ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด ์˜์ƒ
  • WebEx ์ „ํ™”๋ฅผ ํ†ตํ•œ ํ™”๋ฉด ๊ณต์œ 
  • TeamViewerQS์™€ ๊ฐ™์€ ์†Œ๋น„์ž ์ง€์› ์ž‘์—…
  • ๊ทธ๋ฆผ ๊ทธ๋ฆฌ๋Š” ์•ฑ์„ Streaming ํ•˜๋Š” Facebook

 

ReplayKit VS ReplayKit 2

  • ๊ธฐ์กด์˜ ReplayKit์„ ํ™œ์šฉํ•œ broadcast์—์„œ๋Š”, ์‚ฌ์šฉ์ž๊ฐ€ App์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฉ์†ก์„ ์‹œ์ž‘ํ•˜๊ณ  ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ถ€๋ชจ App์€ ReplayKit API์™€ ์ง์ ‘ ํ†ต์‹ ํ•˜์—ฌ Extension์•ฑ์„ ํ†ตํ•ด ๋ถ€๋ชจ App์˜ ๋ฏธ๋””์–ด ๋ฐ ์˜ค๋””์˜ค ์ฝ˜ํ…์ธ ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • Extension์•ฑ์€ ํ•ด๋‹น ์ฝ˜ํ…์ธ ๋“ค์„ ์ธ์ฝ”๋”ฉํ•˜์—ฌ ์„œ๋ฒ„๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ด ์ „์ฒด์˜ ๊ณผ์ •์„ ์šฐ๋ฆฌ๋Š” In-App Broadcast๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

In-App Broadcast ์‹œ๋‚˜๋ฆฌ์˜ค

 

 

์ƒˆ๋กœ์›Œ์ง„ ReplayKit2์—์„œ๋Š” ํŠน์ • ์•ฑ์— ์ข…์†๋˜๋Š” ํ™”๋ฉด์ด๋‚˜ ์˜ค๋””์˜ค๋ฟ ์•„๋‹ˆ๋ผ, ์‹œ์Šคํ…œ ์ „์ฒด์˜ ์˜ค๋””์˜ค๋ฅผ ๋…น์Œํ•˜๊ณ  ํ™”๋ฉด์„ ๋ฐฉ์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์‚ฌ์šฉ์ž๋Š” Contol Center๋ฅผ ํ†ตํ•ด ๋ฐฉ์†ก์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    - Control Center๋ž€, ๋””๋ฐ”์ด์Šค ์ƒ๋‹จ์„ ํ•˜๋‹จ์œผ๋กœ swipe ํ•˜๋ฉด ๋‚˜์˜ค๋Š” ๋‹ค์–‘ํ•œ ์ œ์–ด์„ผํ„ฐ ํ•ญ๋ชฉ๋“ค.. ๊ทธ์ค‘์—์„œ, ํ™”๋ฉด ๋…นํ™” ๋ฒ„ํŠผ์„ ์‚ฌ์šฉ
  • ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋…นํ™”๋˜๋Š” ํ™”๋ฉด๊ณผ ๋…น์Œ๋˜๋Š” ์Œ์„ฑ๋“ค์€ Extension์œผ๋กœ ๊ตฌํ˜„๋œ ์•ฑ์—์„œ Server์™€ ํ†ต์‹ ์„ ํ†ตํ•ด ๋ฐฉ์†ก์ด ๋ฉ๋‹ˆ๋‹ค.

iOS Syetem Broadcast ์‹œ๋‚˜๋ฆฌ์˜ค

 

์ฝ˜์…‰ํŠธ์ ์œผ๋กœ, ์ด ๋‘ ๊ฐœ์˜ ๋ฐฉ์‹์€ ์™„์ „ํžˆ ๋‹ค๋ฅด๋‹ค.

 

1. In-App Broadcast

App or Game (์•ฑ ๋˜๋Š” ๊ฒŒ์ž„)

  • ๋น„๋””์˜ค ๋˜๋Š” ์˜ค๋””์˜ค ์ฝ˜ํ…์ธ ๋ฅผ ์ œ๊ณต: ์•ฑ์ด๋‚˜ ๊ฒŒ์ž„์—์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ™”๋ฉด์„ ๋…นํ™”ํ•˜๊ฑฐ๋‚˜ ๋ฐฉ์†กํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ˜ํ…์ธ ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • App ๋‚ด์—์„œ ์ง์ ‘ ๋ฐฉ์†ก ์‹œ์ž‘๊ณผ ์ค‘์ง€: ์•ฑ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฐฉ์†ก์„ ์‹œ์ž‘ํ•˜๊ณ  ์ค‘์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Broadcaster App (Extension App) (๋ฐฉ์†ก ํ™•์žฅ ์•ฑ)

  • 3rd-party ์ŠคํŠธ๋ฆฌ๋ฐ ์„œ๋น„์Šค์— ๋กœ๊ทธ์ธ: ๋ฐฉ์†ก ํ™•์žฅ ์•ฑ์€ ์‚ฌ์šฉ์ž๊ฐ€ YouTube, Facebook Live ๋“ฑ์˜ ์™ธ๋ถ€ ์ŠคํŠธ๋ฆฌ๋ฐ ์„œ๋น„์Šค์— ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.
  • ๋ฐฉ์†ก ์ฝ˜ํ…์ธ ๋ฅผ ์„œ๋ฒ„์— ์—…๋กœ๋“œ: ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฉ์†ก ์ฝ˜ํ…์ธ ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ ์„œ๋น„์Šค์˜ ์„œ๋ฒ„์— ์ „์†กํ•˜๋Š” ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

2. iOS System Broadcast

  • ํ™”๋ฉด์— ๋…ธ์ถœ๋˜๋Š” ๋ชจ๋“  ํ™œ๋™๊ณผ ์‚ฌ์šด๋“œ๋ฅผ ๋ฐฉ์†กํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Control Center๋ฅผ ํ†ตํ•ด์„œ ๋ฐฉ์†ก์˜ ์‹œ์ž‘ ๋ฐ ์ข…๋ฃŒ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์‹œ์Šคํ…œ ์ „๋ฐ˜์— ๊ฑธ์ณ์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ, ์„ธ์…˜์ด ์•„๋ž˜์˜ ์ƒํ™ฉ์—์„œ๋„ ๊ณ„์† ์ด์–ด์ง„๋‹ค.
    - ํ™ˆ ํ™”๋ฉด์—์„œ ์•ฑ์œผ๋กœ ์ด๋™ํ•˜๊ฑฐ๋‚˜
    - ํ•œ ์•ฑ์—์„œ ๋‹ค๋ฅธ ์•ฑ์œผ๋กœ ์ด๋™ํ•  ๋•Œ
  • iOS11+ ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

 


 

System Broadcast Picker 

  • ์‚ฌ์šฉ์ž๋“ค์€ iOS syetem broadcast๋ฅผ App์„ ๋ฒ—์–ด๋‚˜์ง€ ์•Š๊ณ  ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๊ณ , ๋งค์šฐ ๋‹จ์ˆœํ•œ ๋ฒ„ํŠผ ํ•˜๋‚˜๋กœ ๋™์ž‘์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • iOS 12๋ถ€ํ„ฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค. 

// RPSystemBroadcastPickerView
class RPSyetemBroadcastPickerView: UIView {
		open var preferredExtension: String
}

class ViewController: UIViewController {
    var broadcastPicker: RPSystemBroadcastPickerView?
    override func viewDidLoad() {
        super.viewDidLoad()
        broadcastPicker = RPSystemBroadcastPickerView(frame: ์›ํ•˜๋Š”_ํ”„๋ ˆ์ž„)
        view.addSubview(broadcastPicker!)
    }
}
  • UIView๋ฅผ ์„œ๋ธŒํด๋ž˜์‹ฑํ•œ RPSystemBroadcastPickerView๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • Storyboard์˜ UI-Component๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ์ฝ”๋“œ ๋ฒ ์ด์Šค๋กœ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์ด ์ƒˆ๋กœ์šด API๋ฅผ ํ†ตํ•ด, ์‚ฌ์šฉ์ž๋Š” Application์—์„œ ์ง์ ‘ ๋ฐฉ์†ก์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๊ณ , ํ™”๋ฉด๋…นํ™” ๋ฐ ์„ค์ •์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ• ๋“ฑ์„
    Control Center์—์„œ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

์‚ฌ์šฉ์ž๋Š” ์ž์‹ ์˜ ์„œ๋น„์Šค๋งŒ์ด PickerView์— ๋…ธ์ถœ๋˜๊ธฐ๋ฅผ ์›ํ•  ๊ฒƒ์ด๊ณ , 
ReplayKit์€ ์ด๊ฒƒ์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด preferredExtension์ด๋ผ๋Š” API๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 

 

preferredExtension

๊ธฐ๋ณธ์ ์œผ๋กœ RPBroadcastPickerView๋Š” Syetem์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‹ค์–‘ํ•œ `Broadcast Extension`์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ์ง€๋งŒ, preferredExtension`์„ ์„ค์ •ํ•˜์—ฌ ํŠน์ • Broadcast Extension๋งŒ ์„ ํƒ์ ์œผ๋กœ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

 

class ViewController: UIViewController {
    var broadcastPicker: RPSystemBroadcastPickerView?
    override func viewDidLoad() {
        super.viewDidLoad()
        broadcastPicker = RPSystemBroadcastPickerView(frame: ์›ํ•˜๋Š”_ํ”„๋ ˆ์ž„)
        broadcastPicker?.preferredExtension = "com.vaultmicro.camerafi.ios.extension" // ๋‚˜์˜ Extension์— ๋Œ€ํ•œ bundle identifier
        view.addSubview(broadcastPicker!)
    }
}
  • preferredExtension์†์„ฑ์€ broadcast picker๋ฅผ ํŠน์ • Broadcast Extension์— ์—ฐ๊ฒฐํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.
  • preferredExtension์—๋Š” ์‚ฌ์šฉ์ž์˜ Broadcast Extension์— ๋Œ€ํ•œ Bundle Identifier๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.
    - ์ด๋ ‡๊ฒŒ ๋˜๋ฉด, PickerView์—๋Š” ์‚ฌ์šฉ์ž์˜ ์•ฑ๋งŒ ๋ชฉ๋ก์— ๋‚˜์˜ด.
  • ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น property๋Š” `RPBroadcastPickerView`๊ฐ€ present ๋˜๊ธฐ ์ „์— ๋ฏธ๋ฆฌ setting ํ•ด์•ผ ํ•œ๋‹ค.

 

RPSystemBroadcastPickerView

  • RPSystemBroadcastPickerView๋Š” ๋ฐฉ์†ก์„ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์Šคํ…œ์˜ UI๋ฅผ ํ‘œ์‹œํ•œ๋‹ค.
  • RPSyetemBroadcastPickerView๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐฉ์†ก์„ ์‹œ์ž‘ํ•  ๋•Œ, ์—ฌ๋Ÿฌ ๋ฐฉ์†ก ์˜ต์…˜์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค€๋‹ค.
  • RPSyetemBroadcastPickerView๋Š” ๋ฐฉ์†ก ์„ธ์…˜์˜ ์ƒํƒœ๋ฅผ ์ง์ ‘์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๋Š”๋‹ค.
    - ํ•ด๋‹น ๋ทฐ๋Š” broadcast session์„ ์‹œ์ž‘ํ•˜๊ณ , ํ™”๋ฉด์— ๋ฐฉ์†ก์„ ์„ ํƒํ•˜๋Š” UI๋ฅผ ํ‘œ์‹œํ•˜์ง€๋งŒ, ๋ฐฉ์†ก ์„ธ์…˜ ์ž์ฒด๋Š” ๋‹ค๋ฅธ ํด๋ž˜์Šค์—์„œ ๊ด€๋ฆฌํ•œ๋‹ค.

 

Session๊ณผ์˜ ์—ฐ๊ฒฐ ์š”์†Œ

1. Screen recording control: ํ™”๋ฉด ๋…นํ™” ์ œ์–ด๋ฅผ ๋‹ด๋‹นํ•œ๋‹ค.
2. Broadcast picker:
๋ฐฉ์†ก ์„œ๋น„์Šค๋ฅผ ์„ ํƒํ•˜๊ณ , RPSystemBroadcastPickerView๊ฐ€ ๋‹ด๋‹น
3. Status bar delegate:
์ƒํƒœ ํ‘œ์‹œ์ค„์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„

 

 


 

Developing Broadcast Extensions (For ReplayKit 2)

 

 

Broadcast App and Extension

  • Broadcast App๊ณผ Extension์€ ๋ถ„๋ฆฌ๋œ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ๊ฐ๊ฐ์€ ๋…๋ฆฝ๋œ ํ”„๋กœ์„ธ์Šค์—์„œ ๋™์ž‘ํ•œ๋‹ค.

Broadcast Application: ์ŠคํŠธ๋ฆฌ๋ฐ ์‚ฌ์ดํŠธ ๋กœ๊ทธ์ธ, ๋ฐฉ์†ก ์ œ๋ชฉ ์„ค์ • ๋“ฑ๋“ฑ

Broadcast Upload Extension: sample์„ ์ธ์ฝ”๋”ฉ, ์‹ค์ œ ์„œ๋ฒ„์— upload!

 

 

 

Broadcast Upload Extension

  • App์œผ๋กœ๋ถ€ํ„ฐ ์˜ค๋””์˜ค์™€ ๋น„๋””์˜ค sample์„ ๋ฐ›์•„์˜จ๋‹ค.
  • ํ•ด๋‹น sample์„ ์ธ์ฝ”๋”ฉํ•ด์„œ, Video Stream์— ์—…๋กœ๋“œํ•œ๋‹ค.
  • ๋””๋ฐ”์ด์Šค์˜ ํšŒ์ „์„ ๊ฐ์ง€ํ•œ๋‹ค.

 

 

// SampleHandler created by Xcode templates for Upload Extension
class SampleHandler: RPBroadcastSampleHandler {
	// User has requested to start the broadcast
	override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?)

	// User has requested to finish the broadcast
	override func broadcastFinished()

	// Handle the sample buffer here
	override func processSampleBuffer(
			_ sampleBuffer: CMSampleBuffer,
			with sampleBufferType: RPSampleBufferType			
	)

	// Use details of application to annotate the broadcast
	override func broadcastAnnotated(withApplicationInfo info: [String: NSObject])
}

 

  • Broadcast Upload Extension์„ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด, SampleHandler๋ผ๋Š” Class๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
  • ํ•ด๋‹น Class๋ฅผ ํ™œ์šฉํ•ด์„œ, Broadcast๋‚ด์˜ ๊ฐ์ข… ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค.
  • ๋˜ํ•œ, App์œผ๋กœ๋ถ€ํ„ฐ ๋“ค์–ด์˜ค๋Š” ์˜ค๋””์˜ค์™€ ๋น„๋””์˜ค sample์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•˜๋‹ค

 


 

Broadcast Lifecycle

 

1. Handling Sign-In and Broadcast Setup

  • ์ด ๋‹จ๊ณ„์—์„œ๋Š” ์•„์ง, Extension๊ณผ์˜ ์—ฐ๊ฒฐ์ด ๋˜์ง€ ์•Š์€ ์ƒํƒœ์ด๋‹ค.
  • ๋˜ํ•œ, Extension๊ณผ ์—ฐ๊ฒฐ๋˜๊ธฐ ์ „ Broadcast Service์˜ ๋กœ๊ทธ์ธ์ด๋‚˜, ๋ฐฉ์†ก ์ œ๋ชฉ ์„ค์ • ๋“ฑ๊ณผ ๊ฐ™์€ ๋ฐฉ์†ก ์ค€๋น„ ๊ณผ์ •์„ ์„ธํŒ…ํ•˜๊ฒŒ ๋œ๋‹ค.

2. Initialization

  • Control Center๋ฅผ ํ†ตํ•ด์„œ ํ˜น์€ Broadcast Picker๊ฐ€ ๊ตฌํ˜„๋œ ์‚ฌ์šฉ์ž์˜ App์„ ํ†ตํ•ด   
  • ReplayKit์ด Extension์˜ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰์‹œํ‚ค๋ฉด, SampleHandler ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. 
class SampleHandler: RPBroadcastSampleHandler {
	// override init to read login credentials from shared keychain
	override func init() {
			super.init()
			session = BroadcastSession.instance
			var credentials = KeychainAccess.getLoginCredentials()
			session.authentificate(credentials)
	}
}

3. Handling broadcastStarted

  • Extension์ด ์‹คํ–‰๋˜๊ณ  SamplerHandler ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด, Extension์€ ReplayKit์ด broadcastStarted(withSetupInfo:)๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋น„๋””์˜ค์™€ ์˜ค๋””์˜ค sample์„ ์ œ๊ณตํ•˜๊ธฐ ์‹œ์ž‘ํ•จ์„ ์•Œ๋ฆฐ๋‹ค.
  • ์ด broadcastStarted(withSetupInfo:)๋ฅผ ํ†ตํ•ด, ์˜์ƒ ์—”์ง„์„ ๋งŒ๋“ค๊ฑฐ๋‚˜ sample์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ›์•„์„œ ์ธ์ฝ”๋”ฉํ•˜์—ฌ ์„œ๋ฒ„์— ์—…๋กœ๋“œํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ž‘์—…๋“ค์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์‹ถ์„ ๊ฒƒ์ด๋‹ค.

4. Processing Media Samples

  • ReplayKit์€ ์˜์ƒ ๋ฐ ์‚ฌ์šด๋“œ sample์˜ raw data์„ ์ œ๊ณตํ•ด ์ฃผ๊ณ , Extension์€ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ธ์ฝ”๋”ฉํ•˜๋Š” ์ž‘์—…๊ณผ ์ด๊ฒƒ์„ ์‹ค์ œ ์„œ๋น„์Šค์— ์ „์†กํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค.
๋”๋ณด๊ธฐ

processSampleBuffer

ReplayKit์€ Extension์— 3๊ฐ€์ง€ ํƒ€์ž…์˜ sample๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

  • Video (๋…นํ™”ํ•˜๋Š” ํ™”๋ฉด)
  • Audio[App] (์‹คํ–‰์ค‘์ธ Application์˜ ์‚ฌ์šด๋“œ)
  • Audio[Microphone] (ํ˜„์žฌ ๋งˆ์ดํฌ์—์„œ ์ˆ˜์Œ๋˜๋Š” ์‚ฌ์šด๋“œ)

Extension์€ ์ด 3๊ฐ€์ง€ ํƒ€์ž…์„ ๋ชจ๋‘ ์ธ์ฝ”๋”ฉํ•˜์—ฌ, ์›ํ•˜๋Š” ์ŠคํŠธ๋ฆฌ๋ฐ ์„œ๋น„์Šค์— upload ํ•œ๋‹ค.

override func processSampleBuffer(
	_ sampleBuffer: CMSampleBuffer,
	with sampleBufferType: RPSampleBufferType
) {
   switch sampleBufferType {
		    case .video:
				   var imageBuffer: CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
				   var pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) as CMTime
				   VTCompressionSessionEncoderFrame(session, imageBuffer, pts, kCMTimeInvalid, nil, nil, nil)
				   break
			case .audioApp:
					// Handle audio sample buffer for app audio
					break
			case .audioMic:
					// Handle audio sample buffer for mic audio
					break
   } 
}
  • ์ด ํ•จ์ˆ˜๋Š” buffer์˜ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜์ธ CMSampleBuffer๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„์˜จ๋‹ค.
  • ๋˜ํ•œ, ์ด ์ฝ”๋“œ ์˜ˆ์‹œ์—์„  CMSampleBuffer๋ฅผ Encode ํ•˜๋Š” ๊ณผ์ •์„ ๋ณด์—ฌ์ค€๋‹ค.

5. Handling Application Information

  • ์ด๋ ‡๊ฒŒ sample์„ ์ ์ ˆํ•˜๊ฒŒ encode ํ•ด์„œ ์„œ๋ฒ„์— upload ํ•˜๋ฉด, ์ „ ์„ธ๊ณ„์˜ ์‚ฌ๋žŒ๋“ค์€ ํ•ด๋‹น ๋ฐฉ์†ก์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ, ์ด ์ „ ์„ธ๊ณ„์˜ ์‹œ์ฒญ์ž๋“ค์—๊ฒŒ '๋‚˜์˜ ๋ฐฉ์†ก'์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์•Œ๋ ค์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ, ReplayKit์€ broadcastAnnotatedWithApplicationInfo๋ผ๋Š” API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋”๋ณด๊ธฐ

broadcastAnnotatedWithApplicationInfo

  • ์ด API๋Š” ๋ฐฉ์†ก์„ ์—…๋กœ๋“œ ํ•  ๋•Œ, ํ•ด๋‹น ๋ฐฉ์†ก์— ๋Œ€ํ•œ ์ •๋ณด (๋ฐฉ์†ก ์ œ๋ชฉ, ์„ค๋ช…, ๋ฐฉ์†กํ•˜๋Š” ๊ฒŒ์ž„ ์ด๋ฆ„ ๋“ฑ)์„ ์„œ๋ฒ„์— ์ถ”๊ฐ€๋กœ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณต
  • ๋น„๋””์˜ค ๋ฐ ์˜ค๋””์˜ค sample์„ Extension์„ ํ†ตํ•ด ์„œ๋ฒ„์— uploadํ•  ๋•Œ, ์ด ๋ฐฉ์†ก์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๊ป˜ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
override func broadcastAnnotated(withApplicationInfo applicationInfo: [AnyHashable : Any]) {
	var bundleIdentifier = applicationInfo[RPApplicationInfoBundleIdentifierKey]
	if bundleIdentifier != nil {
			session.addMetadataWithApplicationInfo(bundleIdentifier)
	}
}

6. Handling broadcastFinished

  • ์–ด๋– ํ•œ ์ด์œ ๋กœ ๋ฐฉ์†ก์ด ์ข…๋ฃŒ๋˜๋ฉด Extension์—์„œ broadcastFinished ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.

7. Handling Error(ex. Sign-In)

  • ๋ฐฉ์†ก์„ ์‹คํ–‰ํ•˜๋Š” ๊ณผ์ •์—์„œ, ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด (์˜ˆ๋ฅผ ๋“ค๋ฉด ๊ณ„์ • ๊ด€๋ จ) ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ReplayKit์€ ํŠน์ • API๋ฅผ ์ œ๊ณตํ•ด ์ค€๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  Extension์€ ํŠน์ • API๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ReplayKit์€ Session์„ ์ข…๋ฃŒํ•˜๊ณ  Extension์ด ์ œ๊ณตํ•œ Error Message๋ฅผ ํฌํ•จํ•œ Alert์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณตํ•œ๋‹ค.

  • ๊ทธ๋ฆฌ๊ณ , Alert ํ•˜๋‹จ์˜ [Go To Application]์„ ์„ ํƒํ•˜๋ฉด, ReplayKit์€ ๊ธฐ์กด์˜ Application์œผ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ๋Œ๋ ค๋ณด๋‚ธ๋‹ค.
override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
	// Verify user is logged in and there's network connectivity
	if session.userLoggedin() {
			session.createMediaEngine()
	} else {
			let userInfo = [NSLocalizedFailureReasonErrorKey: "Not Logged In"] 
			let error = NSError(domain: "RPBroadcastErrorDomain", code: 401, userInfo: userInfo)
			finishBroadcastWithError(error)
	}
}

 

728x90
๋ฐ˜์‘ํ˜•
๋Œ“๊ธ€
๋ฐ˜์‘ํ˜•
๋งํฌ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
Total
Today
Yesterday