mirror of
https://github.com/HaishinKit/HaishinKit.swift.git
synced 2026-05-07 20:12:28 +00:00
220 lines
6.8 KiB
Swift
220 lines
6.8 KiB
Swift
import Foundation
|
|
import libsrt
|
|
|
|
/// A structure that represents a Key-Value-Object for the SRTSocket.
|
|
public struct SRTSocketOption: Sendable {
|
|
/// The error domain codes.
|
|
public enum Error: Swift.Error {
|
|
case invalidOption(_ message: String)
|
|
case invalidArgument(_ message: String)
|
|
}
|
|
|
|
private static let trueStringLiterals: [String: Bool] = [
|
|
"1": true,
|
|
"on": true,
|
|
"yes": true,
|
|
"true": true
|
|
]
|
|
|
|
private static let falseStringLiterals: [String: Bool] = [
|
|
"0": false,
|
|
"off": false,
|
|
"no": false,
|
|
"false": false
|
|
]
|
|
|
|
/// Constants that indicate the sockopt c-types.
|
|
public enum CType: Int, Sendable {
|
|
case string
|
|
case int32
|
|
case int64
|
|
case bool
|
|
|
|
var size: Int {
|
|
switch self {
|
|
case .string:
|
|
return 512
|
|
case .int32:
|
|
return MemoryLayout<Int32>.size
|
|
case .int64:
|
|
return MemoryLayout<Int64>.size
|
|
case .bool:
|
|
return MemoryLayout<Bool>.size
|
|
}
|
|
}
|
|
|
|
func data(_ value: any Sendable) throws -> Data {
|
|
switch self {
|
|
case .string:
|
|
guard let data = String(describing: value).data(using: .utf8) else {
|
|
throw Error.invalidArgument("\(value)")
|
|
}
|
|
return data
|
|
case .bool:
|
|
guard var value = value as? Bool else {
|
|
throw Error.invalidArgument("\(value)")
|
|
}
|
|
return .init(bytes: &value, count: size)
|
|
case .int32:
|
|
guard var value = value as? Int32 else {
|
|
throw Error.invalidArgument("\(value)")
|
|
}
|
|
return .init(bytes: &value, count: size)
|
|
case .int64:
|
|
guard var value = value as? Int64 else {
|
|
throw Error.invalidArgument("\(value)")
|
|
}
|
|
return .init(bytes: &value, count: size)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Constants that indicate the sockopt binding timings.
|
|
public enum Restriction: Int, Sendable {
|
|
case preBind
|
|
case pre
|
|
case post
|
|
}
|
|
|
|
/// A structure that defines the name of a SRTSocket option.
|
|
public struct Name: Sendable {
|
|
public typealias RawValue = String
|
|
|
|
public let rawValue: String
|
|
let symbol: SRT_SOCKOPT
|
|
let restriction: Restriction?
|
|
let type: CType
|
|
|
|
public init(rawValue: String, symbol: SRT_SOCKOPT, restriction: Restriction?, type: CType) {
|
|
self.rawValue = rawValue
|
|
self.symbol = symbol
|
|
self.restriction = restriction
|
|
self.type = type
|
|
}
|
|
}
|
|
|
|
/// The socket option's name.
|
|
public let name: Name
|
|
|
|
/// The socket option's value expressed as a String value.
|
|
public var stringValue: String {
|
|
switch name.type {
|
|
case .string:
|
|
return String(data: data, encoding: .utf8) ?? ""
|
|
case .int32:
|
|
return data.withUnsafeBytes { $0.load(as: Int32.self) }.description
|
|
case .int64:
|
|
return data.withUnsafeBytes { $0.load(as: Int64.self) }.description
|
|
case .bool:
|
|
return (data[0] == 1).description
|
|
}
|
|
}
|
|
|
|
/// The socket option's value expressed as a Int value.
|
|
public var intValue: Int {
|
|
switch name.type {
|
|
case .string:
|
|
return -1
|
|
case .int32:
|
|
return Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
|
|
case .int64:
|
|
return Int(data.withUnsafeBytes { $0.load(as: Int64.self) })
|
|
case .bool:
|
|
return Int(data[0])
|
|
}
|
|
}
|
|
|
|
/// The socket option's value expressed as a Boolean value.
|
|
public var boolValue: Bool {
|
|
switch name.type {
|
|
case .string:
|
|
return false
|
|
case .int32:
|
|
return Int(data.withUnsafeBytes { $0.load(as: Int32.self) }) == 1
|
|
case .int64:
|
|
return Int(data.withUnsafeBytes { $0.load(as: Int64.self) }) == 1
|
|
case .bool:
|
|
return data[0] == 1
|
|
}
|
|
}
|
|
|
|
private let data: Data
|
|
|
|
/// Creates an option.
|
|
public init(name: Name, value: String) throws {
|
|
self.name = name
|
|
switch name.type {
|
|
case .string:
|
|
self.data = try name.type.data(value)
|
|
case .int32:
|
|
switch name.rawValue {
|
|
case "transtype":
|
|
switch value {
|
|
case "live":
|
|
self.data = try name.type.data(Int32(SRTT_LIVE.rawValue))
|
|
case "file":
|
|
self.data = try name.type.data(Int32(SRTT_FILE.rawValue))
|
|
default:
|
|
throw Error.invalidOption(name.rawValue)
|
|
}
|
|
default:
|
|
self.data = try name.type.data(Int32(value))
|
|
}
|
|
case .int64:
|
|
self.data = try name.type.data(Int64(value))
|
|
case .bool:
|
|
let key = String(describing: value).lowercased()
|
|
if let bool = Self.trueStringLiterals[key] {
|
|
self.data = try name.type.data(bool)
|
|
} else if let bool = Self.falseStringLiterals[key] {
|
|
self.data = try name.type.data(bool)
|
|
} else {
|
|
throw Error.invalidOption(name.rawValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Creates an option.
|
|
public init(name: Name, value: Int) throws {
|
|
self.name = name
|
|
switch name.type {
|
|
case .string:
|
|
self.data = try name.type.data(value.description)
|
|
case .int32:
|
|
self.data = try name.type.data(Int32(value))
|
|
case .int64:
|
|
self.data = try name.type.data(Int64(value))
|
|
case .bool:
|
|
self.data = try name.type.data(value == 1)
|
|
}
|
|
}
|
|
|
|
init(name: Name, socket: SRTSOCKET) throws {
|
|
self.name = name
|
|
var data = Data(repeating: 0, count: name.type.size)
|
|
var length = Int32(name.type.size)
|
|
let result: Int32 = data.withUnsafeMutableBytes {
|
|
guard let buffer = $0.baseAddress else {
|
|
return -1
|
|
}
|
|
return srt_getsockflag(socket, name.symbol, buffer, &length)
|
|
}
|
|
if result < 0 {
|
|
throw Error.invalidOption(String(cString: srt_getlasterror_str()))
|
|
}
|
|
self.data = data.subdata(in: 0..<Data.Index(length))
|
|
}
|
|
|
|
func setSockflag(_ socket: SRTSOCKET) throws {
|
|
let result: Int32 = data.withUnsafeBytes { pointer in
|
|
guard let buffer = pointer.baseAddress else {
|
|
return -1
|
|
}
|
|
return srt_setsockflag(socket, name.symbol, buffer, Int32(data.count))
|
|
}
|
|
if result < 0 {
|
|
throw Error.invalidOption(String(cString: srt_getlasterror_str()))
|
|
}
|
|
}
|
|
}
|