Files
HaishinKit.swift/HaishinKit/Sources/Stream/StreamBitRateStrategy.swift
2025-07-21 19:45:37 +09:00

74 lines
3.0 KiB
Swift

import Foundation
/// A type with a network bitrate strategy representation.
public protocol StreamBitRateStrategy: 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 StreamConvertible) async
}
/// An actor provides an algorithm that focuses on video bitrate control.
public final actor StreamVideoAdaptiveBitRateStrategy: StreamBitRateStrategy {
/// The status counts threshold for restoring the status
public static let statusCountsThreshold: Int = 15
public let mamimumVideoBitRate: Int
public let mamimumAudioBitRate: Int = 0
private var sufficientBWCounts: Int = 0
private var zeroBytesOutPerSecondCounts: Int = 0
/// Creates a new instance.
public init(mamimumVideoBitrate: Int) {
self.mamimumVideoBitRate = mamimumVideoBitrate
}
public func adjustBitrate(_ event: NetworkMonitorEvent, stream: some StreamConvertible) async {
switch event {
case .status:
var videoSettings = await stream.videoSettings
if videoSettings.bitRate == mamimumVideoBitRate {
return
}
if Self.statusCountsThreshold <= sufficientBWCounts {
let incremental = mamimumVideoBitRate / 10
videoSettings.bitRate = min(videoSettings.bitRate + incremental, mamimumVideoBitRate)
try? await stream.setVideoSettings(videoSettings)
sufficientBWCounts = 0
} else {
sufficientBWCounts += 1
}
case .publishInsufficientBWOccured(let report):
sufficientBWCounts = 0
var videoSettings = await stream.videoSettings
let audioSettings = await stream.audioSettings
if 0 < report.currentBytesOutPerSecond {
let bitRate = Int(report.currentBytesOutPerSecond * 8) / (zeroBytesOutPerSecondCounts + 1)
videoSettings.bitRate = max(bitRate - audioSettings.bitRate, mamimumVideoBitRate / 10)
videoSettings.frameInterval = 0.0
sufficientBWCounts = 0
zeroBytesOutPerSecondCounts = 0
} else {
switch zeroBytesOutPerSecondCounts {
case 2:
videoSettings.frameInterval = VideoCodecSettings.frameInterval10
case 4:
videoSettings.frameInterval = VideoCodecSettings.frameInterval05
default:
break
}
try? await stream.setVideoSettings(videoSettings)
zeroBytesOutPerSecondCounts += 1
}
case .reset:
var videoSettings = await stream.videoSettings
zeroBytesOutPerSecondCounts = 0
videoSettings.bitRate = mamimumVideoBitRate
try? await stream.setVideoSettings(videoSettings)
}
}
}