-
-
Notifications
You must be signed in to change notification settings - Fork 653
Moving from 1.9.x APIs to 2.0.0
- I will update the documentation based on the finalized sections. I aim to complete it before the 2.0.0 release.
- The update to Swift 6.0 will be done in version 2.1.0 + Xcode 16.0. Version 2.0.0 targets Swift 5.10 + Xcode 15.4, which is compatible with Swift 6.0.
I redesigned the system to address the demand for streaming to multiple services. The responsibilities are split into MediaMixer, which handles capture from the device, and HKStream, which focuses on live streaming video and audio.
stream: IOStream
view: MTHKView
view.attachStream(stream)
mixer: MediaMixer
stream: HKStream
view: MTHKView
// view2: MTHKView
mixer.addOutput(stream)
stream.addOutput(view)
// stream.addOutput(view2)
It is possible to set multiple views on the mixer. This allows you to directly monitor the video CMSampleBuffer during capture. You can switch between views by changing the track. Setting track = UInt8.max makes it equivalent to the value being output to HKStream.
mixer: MediaMixer
view0: MTHKView
view1: MTHKView
view0.track = 0
view1.track = UInt8.max
mixer.addOutput(view0)
mixer.addOutput(view1)
stream: IOStream
stream.attachCamera(AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back), track: 0) { _, error in
if let error {
logger.warn(error)
}
}
stream.attachAudio(AVCaptureDevice.default(for: .audio)) { _, error in
if let error {
logger.warn(error)
}
}
mixer: MediaMixer
stream: RTMPStream or SRTStream
do {
try await mixer.attachVideo(AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back))
} catch {
logger.warn(error)
}
do {
try await mixer.attachAudio(AVCaptureDevice.default(for: .audio))
} catch {
logger.warn(error)
}
mixer.addOutput(stream)
- RTMPT, RTMPTS protocols.
- I decided this because I judged the usage frequency to be low.
Inheritance is no longer possible due to the transition to actors. If you were using inheritance and are now facing issues, please let us know how you were using it, and I can discuss possible solutions.
1.9.x | 2.0.0 |
---|---|
public class RTMPConnection | actor RTMPConnection |
open class RTMPStream | actor RTMPStream |
Regarding the specification of detailed properties.
public actor RTMPConnection {
/// Specifies the URL of .swf.
public var swfUrl: String?
/// Specifies the URL of an HTTP referer.
public var pageUrl: String?
/// Specifies the time to wait for TCP/IP Handshake done.
...
}
The constructor now accepts it as an argument.
public actor RTMPConnection {
/// The URL of .swf.
public let swfUrl: String?
/// The URL of an HTTP referer.
public let pageUrl: String?
/// The name of application.
public let flashVer: String
/// The time to wait for TCP/IP Handshake done.
public let timeout: Int
/// The RTMP request timeout value. Defaul value is 500 msec.
public let requestTimeout: UInt64
/// The outgoing RTMPChunkSize.
public let chunkSize: Int
/// The dispatchQos for socket.
public let qualityOfService: DispatchQoS
/// Creates a new connection.
public init(
swfUrl: String? = nil,
pageUrl: String? = nil,
flashVer: String = RTMPConnection.defaultFlashVer,
timeout: Int = RTMPConnection.defaultTimeout,
requestTimeout: UInt64 = RTMPConnection.defaultRequestTimeout,
chunkSize: Int = RTMPConnection.defaultChunkSizeS,
qualityOfService: DispatchQoS = .userInitiated) {
self.swfUrl = swfUrl
self.pageUrl = pageUrl
self.flashVer = flashVer
self.timeout = timeout
self.requestTimeout = requestTimeout
self.chunkSize = chunkSize
self.qualityOfService = qualityOfService
}
}
Discontinued event handling related to addEventHandler
.
connection.addEventListener(.rtmpStatus, selector: #selector(rtmpStatusHandler), observer: self)
connection.addEventListener(.ioError, selector: #selector(rtmpErrorHandler), observer: self)
@objc
private func rtmpStatusHandler(_ notification: Notification) {
let e = Event.from(notification)
guard let data: ASObject = e.data as? ASObject, let code: String = data["code"] as? String else {
return
}
logger.info(code)
switch code {
case RTMPConnection.Code.connectSuccess.rawValue:
stream?.play(Preference.default.streamName!)
case RTMPConnection.Code.connectFailed.rawValue, RTMPConnection.Code.connectClosed.rawValue:
connection?.connect(uri)
default:
break
}
}
connection: RTMPConnection
stream: RTMPStream
do {
let response = try await connection.connect(preference.uri ?? "")
logger.info(response)
let response = try await stream.publish(Preference.default.streamName)
} catch RTMPConnection.Error.requestFailed(let response) {
logger.warn(response)
} catch RTMPStream.Error.requestFailed(let response) {
logger.warn(response)
} catch {
logger.warn(error)
}
/// The interface a RTMPConnectionDelegate uses to inform its delegate.
public protocol RTMPConnectionDelegate: AnyObject {
/// Tells the receiver to publish insufficient bandwidth occured.
func connection(_ connection: RTMPConnection, publishInsufficientBWOccured stream: RTMPStream)
/// Tells the receiver to publish sufficient bandwidth occured.
func connection(_ connection: RTMPConnection, publishSufficientBWOccured stream: RTMPStream)
/// Tells the receiver to update statistics.
func connection(_ connection: RTMPConnection, updateStats stream: RTMPStream)
}
var connection: RTMPConnect
connection.delegate = self
/// A type with a network bitrate strategy representation.
public protocol HKStreamBitRateStrategy: Sendable {
/// The mamimum video bitRate.
var mamimumVideoBitRate: Int { get }
/// The mamimum audio bitRate.
var mamimumAudioBitRate: Int { get }
/// Adjust a bitRate.
func adjustBitrate(_ event: NetworkMonitorEvent, stream: some HKStream) async
}
/// An enumeration that indicate the network monitor event.
public enum NetworkMonitorEvent: Sendable {
/// To update statistics.
case status(report: NetworkMonitorReport)
/// To publish sufficient bandwidth occured.
case publishInsufficientBWOccured(report: NetworkMonitorReport)
/// To reset statistics.
case reset
}
final final actor MyStreamBitRateStrategy: HKStreamBitRateStrategy {
func adjustBitrate(_ event: NetworkMonitorEvent, stream: some HKStream) async {
}
}
var stream: (any HKStream)
var strategy = MyStreamBitRateStrategy()
async stream.setBitrateStrategy(strategy)
1.9.x | 2.0.0 |
---|---|
public class SRTConnection | actor SRTConnection |
final class SRTStream | actor SRTStream |
- Support Cartahge.
1.9.x | 2.0.0 |
---|---|
ASObject | AMFObject |
ASUndefined | AMFUndefined |
ASTypedObject | AMFTypedObject |
ASArray | AMFArray |
ASXMLDocument | AMFXMLDocument |
ASXMLDocument | AMFXMLDocument |
ASXML | AMFXML |
HaishinKit.swift | 🇬🇧 HaishinKit.kt | 🇯🇵 Zenn