Skip to content

Commit f1bb2a5

Browse files
committed
WHIP bearer token setting.
1 parent 2256119 commit f1bb2a5

File tree

7 files changed

+124
-34
lines changed

7 files changed

+124
-34
lines changed

Common/Localizable.xcstrings

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36332,6 +36332,9 @@
3633236332
}
3633336333
}
3633436334
}
36335+
},
36336+
"Bearer token" : {
36337+
3633536338
},
3633636339
"Beauty" : {
3633736340
"localizations" : {

Moblin/Media/HaishinKit/Whip/WhipStream.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@ final class WhipStream {
498498
private var totalByteCount: Int64 = 0
499499
private var sessionUrl: URL?
500500
private var endpointUrl: URL?
501+
private var headers: [SettingsHttpHeader] = []
501502
private var connected = false
502503
private var offerSent = false
503504
private var firstPresentationTimeStamp: Double = .nan
@@ -508,9 +509,9 @@ final class WhipStream {
508509
self.delegate = delegate
509510
}
510511

511-
func start(url: String, iceServers: [String]) {
512+
func start(url: String, headers: [SettingsHttpHeader], iceServers: [String]) {
512513
whipQueue.async {
513-
self.startInternal(url: url, iceServers: iceServers)
514+
self.startInternal(url: url, headers: headers, iceServers: iceServers)
514515
}
515516
}
516517

@@ -526,12 +527,13 @@ final class WhipStream {
526527
}
527528
}
528529

529-
private func startInternal(url: String, iceServers: [String]) {
530+
private func startInternal(url: String, headers: [SettingsHttpHeader], iceServers: [String]) {
530531
stopInternal()
531532
guard let endpointUrl = makeEndpointUrl(url: url) else {
532533
return
533534
}
534535
self.endpointUrl = endpointUrl
536+
self.headers = headers
535537
totalByteCount = 0
536538
connected = false
537539
offerSent = false
@@ -629,6 +631,9 @@ final class WhipStream {
629631
var request = URLRequest(url: endpointUrl)
630632
request.httpMethod = "POST"
631633
request.setContentType("application/sdp")
634+
for header in headers {
635+
request.setValue(header.value, forHTTPHeaderField: header.name)
636+
}
632637
request.httpBody = offer.utf8Data
633638
httpRequest(request: request, queue: whipQueue) { [weak self] data, response, error in
634639
self?.handleOfferResponse(data: data, response: response, error: error)

Moblin/Various/Media.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,9 +627,11 @@ final class Media: NSObject {
627627
ristStream?.stop()
628628
}
629629

630-
func whipStartStream(url: String) {
630+
func whipStartStream(url: String, headers: [SettingsHttpHeader]) {
631631
adaptiveBitrate = nil
632-
whipStream?.start(url: url, iceServers: ["stun:stun.l.google.com:19302"])
632+
whipStream?.start(url: url,
633+
headers: headers,
634+
iceServers: ["stun:stun.l.google.com:19302"])
633635
}
634636

635637
func whipStopStream() {

Moblin/Various/Model/ModelStream.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ extension Model {
236236
}
237237

238238
private func startNetStreamWhip() {
239-
media.whipStartStream(url: stream.url)
239+
media.whipStartStream(url: stream.url, headers: stream.whip.headers)
240240
}
241241

242242
func stopNetStream() {

Moblin/Various/Settings/SettingsStream.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,37 @@ class SettingsStreamRist: Codable {
551551
}
552552
}
553553

554+
struct SettingsHttpHeader: Codable {
555+
var name: String = ""
556+
var value: String = ""
557+
}
558+
559+
class SettingsStreamWhip: Codable, ObservableObject {
560+
@Published var headers: [SettingsHttpHeader] = []
561+
562+
init() {}
563+
564+
enum CodingKeys: CodingKey {
565+
case headers
566+
}
567+
568+
func encode(to encoder: Encoder) throws {
569+
var container = encoder.container(keyedBy: CodingKeys.self)
570+
try container.encode(.headers, headers)
571+
}
572+
573+
required init(from decoder: Decoder) throws {
574+
let container = try decoder.container(keyedBy: CodingKeys.self)
575+
headers = container.decode(.headers, [SettingsHttpHeader].self, [])
576+
}
577+
578+
func clone() -> SettingsStreamWhip {
579+
let new = SettingsStreamWhip()
580+
new.headers = headers
581+
return new
582+
}
583+
}
584+
554585
class SettingsStreamChat: Codable {
555586
var bttvEmotes: Bool = false
556587
var ffzEmotes: Bool = false
@@ -1031,6 +1062,7 @@ class SettingsStream: Codable, Identifiable, Equatable, ObservableObject, Named
10311062
var srt: SettingsStreamSrt = .init()
10321063
var rtmp: SettingsStreamRtmp = .init()
10331064
var rist: SettingsStreamRist = .init()
1065+
var whip: SettingsStreamWhip = .init()
10341066
@Published var maxKeyFrameInterval: Int32 = 2
10351067
@Published var audioCodec: SettingsStreamAudioCodec = .aac
10361068
var audioBitrate: Int = 128_000
@@ -1116,6 +1148,7 @@ class SettingsStream: Codable, Identifiable, Equatable, ObservableObject, Named
11161148
srt,
11171149
rtmp,
11181150
rist,
1151+
whip,
11191152
captureSessionPresetEnabled,
11201153
captureSessionPreset,
11211154
maxKeyFrameInterval,
@@ -1200,6 +1233,7 @@ class SettingsStream: Codable, Identifiable, Equatable, ObservableObject, Named
12001233
try container.encode(.srt, srt)
12011234
try container.encode(.rtmp, rtmp)
12021235
try container.encode(.rist, rist)
1236+
try container.encode(.whip, whip)
12031237
try container.encode(.maxKeyFrameInterval, maxKeyFrameInterval)
12041238
try container.encode(.audioCodec, audioCodec)
12051239
try container.encode(.audioBitrate, audioBitrate)
@@ -1293,6 +1327,7 @@ class SettingsStream: Codable, Identifiable, Equatable, ObservableObject, Named
12931327
srt = container.decode(.srt, SettingsStreamSrt.self, .init())
12941328
rtmp = container.decode(.rtmp, SettingsStreamRtmp.self, .init())
12951329
rist = container.decode(.rist, SettingsStreamRist.self, .init())
1330+
whip = container.decode(.whip, SettingsStreamWhip.self, .init())
12961331
maxKeyFrameInterval = container.decode(.maxKeyFrameInterval, Int32.self, 2)
12971332
audioCodec = container.decode(.audioCodec, SettingsStreamAudioCodec.self, .aac)
12981333
audioBitrate = container.decode(.audioBitrate, Int.self, 128_000)
@@ -1377,6 +1412,7 @@ class SettingsStream: Codable, Identifiable, Equatable, ObservableObject, Named
13771412
new.srt = srt.clone()
13781413
new.rtmp = rtmp.clone()
13791414
new.rist = rist.clone()
1415+
new.whip = whip.clone()
13801416
new.maxKeyFrameInterval = maxKeyFrameInterval
13811417
new.audioCodec = audioCodec
13821418
new.audioBitrate = audioBitrate

Moblin/View/Settings/Streams/Stream/StreamSettingsView.swift

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,38 @@ struct StreamSettingsView: View {
200200
} label: {
201201
TextItemLocalizedView(name: "URL", value: stream.url, sensitive: true)
202202
}
203+
if database.showAllSettings {
204+
switch stream.getProtocol() {
205+
case .srt:
206+
NavigationLink {
207+
StreamSrtSettingsView(stream: stream, srt: stream.srt)
208+
} label: {
209+
Text("SRT(LA)")
210+
}
211+
case .rtmp:
212+
NavigationLink {
213+
StreamRtmpSettingsView(stream: stream)
214+
} label: {
215+
Text("RTMP")
216+
}
217+
StreamMultiStreamingSettingsView(
218+
stream: stream,
219+
multiStreaming: stream.multiStreaming
220+
)
221+
case .rist:
222+
NavigationLink {
223+
StreamRistSettingsView(stream: stream)
224+
} label: {
225+
Text("RIST")
226+
}
227+
case .whip:
228+
NavigationLink {
229+
StreamWhipSettingsView(model: model, stream: stream, whip: stream.whip)
230+
} label: {
231+
Text("WHIP")
232+
}
233+
}
234+
}
203235
NavigationLink {
204236
StreamVideoSettingsView(database: database, stream: stream)
205237
} label: {
@@ -246,34 +278,6 @@ struct StreamSettingsView: View {
246278
}
247279
}
248280
}
249-
if database.showAllSettings {
250-
switch stream.getProtocol() {
251-
case .srt:
252-
NavigationLink {
253-
StreamSrtSettingsView(stream: stream, srt: stream.srt)
254-
} label: {
255-
Text("SRT(LA)")
256-
}
257-
case .rtmp:
258-
NavigationLink {
259-
StreamRtmpSettingsView(stream: stream)
260-
} label: {
261-
Text("RTMP")
262-
}
263-
StreamMultiStreamingSettingsView(
264-
stream: stream,
265-
multiStreaming: stream.multiStreaming
266-
)
267-
case .rist:
268-
NavigationLink {
269-
StreamRistSettingsView(stream: stream)
270-
} label: {
271-
Text("RIST")
272-
}
273-
case .whip:
274-
EmptyView()
275-
}
276-
}
277281
} header: {
278282
Text("Media")
279283
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import SwiftUI
2+
3+
struct StreamWhipSettingsView: View {
4+
let model: Model
5+
let stream: SettingsStream
6+
@ObservedObject var whip: SettingsStreamWhip
7+
8+
private func getBearerToken() -> String {
9+
guard let authorization = whip.headers.first(where: { $0.name == "Authorization" }) else {
10+
return ""
11+
}
12+
guard let match = authorization.value.prefixMatch(of: /Bearer (.*)/) else {
13+
return ""
14+
}
15+
return String(match.output.1)
16+
}
17+
18+
private func setBearerToken(token: String) {
19+
let value = "Bearer \(token)"
20+
if let index = whip.headers.firstIndex(where: { $0.name == "Authorization" }) {
21+
whip.headers[index].value = value
22+
} else {
23+
whip.headers.append(SettingsHttpHeader(name: "Authorization", value: value))
24+
}
25+
model.reloadStreamIfEnabled(stream: stream)
26+
}
27+
28+
var body: some View {
29+
Form {
30+
Section {
31+
TextEditNavigationView(title: String(localized: "Bearer token"),
32+
value: getBearerToken(),
33+
onSubmit: setBearerToken,
34+
sensitive: true)
35+
.disabled(stream.enabled && model.isLive)
36+
}
37+
}
38+
.navigationTitle("WHIP")
39+
}
40+
}

0 commit comments

Comments
 (0)