Files
streamyfin/docs/ks-player/TypesAndProtocols.md

12 KiB

Types and Protocols

This reference documents all core types, protocols, and enums in KSPlayer.

Protocols

MediaPlayerProtocol

The main protocol that player implementations (KSAVPlayer, KSMEPlayer) must conform to.

public protocol MediaPlayerProtocol: MediaPlayback {
    var delegate: MediaPlayerDelegate? { get set }
    var view: UIView? { get }
    var playableTime: TimeInterval { get }
    var isReadyToPlay: Bool { get }
    var playbackState: MediaPlaybackState { get }
    var loadState: MediaLoadState { get }
    var isPlaying: Bool { get }
    var seekable: Bool { get }
    var isMuted: Bool { get set }
    var allowsExternalPlayback: Bool { get set }
    var usesExternalPlaybackWhileExternalScreenIsActive: Bool { get set }
    var isExternalPlaybackActive: Bool { get }
    var playbackRate: Float { get set }
    var playbackVolume: Float { get set }
    var contentMode: UIViewContentMode { get set }
    var subtitleDataSouce: SubtitleDataSouce? { get }
    var dynamicInfo: DynamicInfo? { get }
    
    @available(macOS 12.0, iOS 15.0, tvOS 15.0, *)
    var playbackCoordinator: AVPlaybackCoordinator { get }
    
    @available(tvOS 14.0, *)
    var pipController: KSPictureInPictureController? { get }
    
    init(url: URL, options: KSOptions)
    func replace(url: URL, options: KSOptions)
    func play()
    func pause()
    func enterBackground()
    func enterForeground()
    func thumbnailImageAtCurrentTime() async -> CGImage?
    func tracks(mediaType: AVFoundation.AVMediaType) -> [MediaPlayerTrack]
    func select(track: some MediaPlayerTrack)
}

MediaPlayback

Base protocol for playback functionality:

public protocol MediaPlayback: AnyObject {
    var duration: TimeInterval { get }
    var fileSize: Double { get }
    var naturalSize: CGSize { get }
    var chapters: [Chapter] { get }
    var currentPlaybackTime: TimeInterval { get }
    
    func prepareToPlay()
    func shutdown()
    func seek(time: TimeInterval, completion: @escaping ((Bool) -> Void))
}

MediaPlayerDelegate

Delegate for receiving player events:

@MainActor
public protocol MediaPlayerDelegate: AnyObject {
    func readyToPlay(player: some MediaPlayerProtocol)
    func changeLoadState(player: some MediaPlayerProtocol)
    func changeBuffering(player: some MediaPlayerProtocol, progress: Int)
    func playBack(player: some MediaPlayerProtocol, loopCount: Int)
    func finish(player: some MediaPlayerProtocol, error: Error?)
}

MediaPlayerTrack

Protocol for audio/video/subtitle track information:

public protocol MediaPlayerTrack: AnyObject, CustomStringConvertible {
    var trackID: Int32 { get }
    var name: String { get }
    var languageCode: String? { get }
    var mediaType: AVFoundation.AVMediaType { get }
    var nominalFrameRate: Float { get set }
    var bitRate: Int64 { get }
    var bitDepth: Int32 { get }
    var isEnabled: Bool { get set }
    var isImageSubtitle: Bool { get }
    var rotation: Int16 { get }
    var dovi: DOVIDecoderConfigurationRecord? { get }
    var fieldOrder: FFmpegFieldOrder { get }
    var formatDescription: CMFormatDescription? { get }
}

Extension Properties

extension MediaPlayerTrack {
    var language: String?        // Localized language name
    var codecType: FourCharCode
    var dynamicRange: DynamicRange?
    var colorSpace: CGColorSpace?
    var mediaSubType: CMFormatDescription.MediaSubType
    var audioStreamBasicDescription: AudioStreamBasicDescription?
    var naturalSize: CGSize
    var colorPrimaries: String?
    var transferFunction: String?
    var yCbCrMatrix: String?
}

KSPlayerLayerDelegate

Delegate for KSPlayerLayer events:

@MainActor
public protocol KSPlayerLayerDelegate: AnyObject {
    func player(layer: KSPlayerLayer, state: KSPlayerState)
    func player(layer: KSPlayerLayer, currentTime: TimeInterval, totalTime: TimeInterval)
    func player(layer: KSPlayerLayer, finish error: Error?)
    func player(layer: KSPlayerLayer, bufferedCount: Int, consumeTime: TimeInterval)
}

PlayerControllerDelegate

Delegate for PlayerView events:

public protocol PlayerControllerDelegate: AnyObject {
    func playerController(state: KSPlayerState)
    func playerController(currentTime: TimeInterval, totalTime: TimeInterval)
    func playerController(finish error: Error?)
    func playerController(maskShow: Bool)
    func playerController(action: PlayerButtonType)
    func playerController(bufferedCount: Int, consumeTime: TimeInterval)
    func playerController(seek: TimeInterval)
}

CapacityProtocol

Buffer capacity information:

public protocol CapacityProtocol {
    var fps: Float { get }
    var packetCount: Int { get }
    var frameCount: Int { get }
    var frameMaxCount: Int { get }
    var isEndOfFile: Bool { get }
    var mediaType: AVFoundation.AVMediaType { get }
}

Enums

KSPlayerState

Player state enumeration:

public enum KSPlayerState: CustomStringConvertible {
    case initialized      // Player created
    case preparing        // Loading media
    case readyToPlay      // Ready to start playback
    case buffering        // Buffering data
    case bufferFinished   // Buffer sufficient, playing
    case paused           // Playback paused
    case playedToTheEnd   // Reached end of media
    case error            // Error occurred
    
    public var isPlaying: Bool  // true for .buffering or .bufferFinished
}

MediaPlaybackState

Low-level playback state:

public enum MediaPlaybackState: Int {
    case idle
    case playing
    case paused
    case seeking
    case finished
    case stopped
}

MediaLoadState

Loading state:

public enum MediaLoadState: Int {
    case idle
    case loading
    case playable
}

DynamicRange

HDR/SDR content range:

public enum DynamicRange: Int32 {
    case sdr = 0
    case hdr10 = 2
    case hlg = 3
    case dolbyVision = 5
    
    static var availableHDRModes: [DynamicRange]  // Device-supported modes
}

DisplayEnum

Video display mode:

@MainActor
public enum DisplayEnum {
    case plane    // Normal 2D display
    case vr       // VR mode (spherical)
    case vrBox    // VR Box mode (side-by-side)
}

FFmpegFieldOrder

Video interlacing:

public enum FFmpegFieldOrder: UInt8 {
    case unknown = 0
    case progressive
    case tt    // Top coded first, top displayed first
    case bb    // Bottom coded first, bottom displayed first
    case tb    // Top coded first, bottom displayed first
    case bt    // Bottom coded first, top displayed first
}

VideoInterlacingType

Detected interlacing type:

public enum VideoInterlacingType: String {
    case tff          // Top field first
    case bff          // Bottom field first
    case progressive  // Progressive scan
    case undetermined
}

ClockProcessType

Internal clock synchronization:

public enum ClockProcessType {
    case remain
    case next
    case dropNextFrame
    case dropNextPacket
    case dropGOPPacket
    case flush
    case seek
}

PlayerButtonType

UI button types:

public enum PlayerButtonType: Int {
    case play = 101
    case pause
    case back
    case srt              // Subtitles
    case landscape        // Fullscreen
    case replay
    case lock
    case rate             // Playback speed
    case definition       // Quality
    case pictureInPicture
    case audioSwitch
    case videoSwitch
}

KSPlayerTopBarShowCase

Top bar visibility:

public enum KSPlayerTopBarShowCase {
    case always         // Always show
    case horizantalOnly // Only in landscape
    case none           // Never show
}

KSPanDirection

Gesture direction:

public enum KSPanDirection {
    case horizontal
    case vertical
}

TimeType

Time formatting:

public enum TimeType {
    case min           // MM:SS
    case hour          // H:MM:SS
    case minOrHour     // MM:SS or H:MM:SS based on duration
    case millisecond   // HH:MM:SS.ms
}

LogLevel

Logging levels:

public enum LogLevel: Int32 {
    case panic = 0
    case fatal = 8
    case error = 16
    case warning = 24
    case info = 32
    case verbose = 40
    case debug = 48
    case trace = 56
}

Structs

Chapter

Video chapter information:

public struct Chapter {
    public let start: TimeInterval
    public let end: TimeInterval
    public let title: String
}

LoadingState

Buffer loading state:

public struct LoadingState {
    public let loadedTime: TimeInterval
    public let progress: TimeInterval
    public let packetCount: Int
    public let frameCount: Int
    public let isEndOfFile: Bool
    public let isPlayable: Bool
    public let isFirst: Bool
    public let isSeek: Bool
}

VideoAdaptationState

Adaptive bitrate state:

public struct VideoAdaptationState {
    public struct BitRateState {
        let bitRate: Int64
        let time: TimeInterval
    }
    
    public let bitRates: [Int64]
    public let duration: TimeInterval
    public internal(set) var fps: Float
    public internal(set) var bitRateStates: [BitRateState]
    public internal(set) var currentPlaybackTime: TimeInterval
    public internal(set) var isPlayable: Bool
    public internal(set) var loadedCount: Int
}

DOVIDecoderConfigurationRecord

Dolby Vision configuration:

public struct DOVIDecoderConfigurationRecord {
    public let dv_version_major: UInt8
    public let dv_version_minor: UInt8
    public let dv_profile: UInt8
    public let dv_level: UInt8
    public let rpu_present_flag: UInt8
    public let el_present_flag: UInt8
    public let bl_present_flag: UInt8
    public let dv_bl_signal_compatibility_id: UInt8
}

KSClock

Internal clock for A/V sync:

public struct KSClock {
    public private(set) var lastMediaTime: CFTimeInterval
    public internal(set) var position: Int64
    public internal(set) var time: CMTime
    
    func getTime() -> TimeInterval
}

TextPosition

Subtitle text positioning:

public struct TextPosition {
    public var verticalAlign: VerticalAlignment = .bottom
    public var horizontalAlign: HorizontalAlignment = .center
    public var leftMargin: CGFloat = 0
    public var rightMargin: CGFloat = 0
    public var verticalMargin: CGFloat = 10
    public var edgeInsets: EdgeInsets { get }
}

Classes

DynamicInfo

Runtime playback information:

public class DynamicInfo: ObservableObject {
    public var metadata: [String: String]       // Media metadata
    public var bytesRead: Int64                 // Bytes transferred
    public var audioBitrate: Int                // Current audio bitrate
    public var videoBitrate: Int                // Current video bitrate
    
    @Published
    public var displayFPS: Double = 0.0         // Current display FPS
    public var audioVideoSyncDiff: Double = 0.0 // A/V sync difference
    public var droppedVideoFrameCount: UInt32   // Dropped frames
    public var droppedVideoPacketCount: UInt32  // Dropped packets
}

Error Handling

KSPlayerErrorCode

public enum KSPlayerErrorCode: Int {
    case unknown
    case formatCreate
    case formatOpenInput
    case formatOutputCreate
    case formatWriteHeader
    case formatFindStreamInfo
    case readFrame
    case codecContextCreate
    case codecContextSetParam
    case codecContextFindDecoder
    case codesContextOpen
    case codecVideoSendPacket
    case codecAudioSendPacket
    case codecVideoReceiveFrame
    case codecAudioReceiveFrame
    case auidoSwrInit
    case codecSubtitleSendPacket
    case videoTracksUnplayable
    case subtitleUnEncoding
    case subtitleUnParse
    case subtitleFormatUnSupport
    case subtitleParamsEmpty
}

Error Domain

public let KSPlayerErrorDomain = "KSPlayerErrorDomain"

Extensions

TimeInterval Formatting

extension TimeInterval {
    func toString(for type: TimeType) -> String
}

// Example:
let time: TimeInterval = 3661  // 1 hour, 1 minute, 1 second
time.toString(for: .min)       // "61:01"
time.toString(for: .hour)      // "1:01:01"
time.toString(for: .minOrHour) // "1:01:01"

Int Formatting

extension Int {
    func toString(for type: TimeType) -> String
}

extension FixedWidthInteger {
    var kmFormatted: String  // "1.5K", "2.3M", etc.
}