iOS/macOS/tvOS video player

Build Status Platform License


KSPlayer is a powerful media play framework foriOS, tvOS, macOS,Mac Catalyst, SwiftUI, Apple Silicon M1 .

English | 简体中文

Based On

  • FFmpeg
  • Metal
  • AVAudioEngine


  • iOS, tvOS, macOS,Mac Catalyst, Apple Silicon M1, SwiftUI.
  • Multiple audio/video tracks.
  • H.264/H.265 hardware accelerator.
  • 4k/HDR
  • text subtitle/image subtitle(dvbsub/dvdsub/pgssub)
  • Picture in Picture
  • Record video
  • De-interlace auto detect
  • 360° panorama video.


  • iOS 13 +, macOS 10.15 +, tvOS 13 +


  • Open Demo/Demo.xcworkspace with Xcode.

Quick Start


Make sure to use the latest version cocoapods 1.10.1+, which can be installed using the command brew install cocoapods

target 'ProjectName' do
    pod 'KSPlayer',:git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'develop'
    pod 'FFmpeg',:git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'develop'
    pod 'OpenSSL',:git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'develop'

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/kingslay/KSPlayer.git", .branch("develop"))



KSOptions.secondPlayerType = KSMEPlayer.self
playerView = IOSVideoPlayerView()
playerView.translatesAutoresizingMaskIntoConstraints = false
    playerView.topAnchor.constraint(equalTo: view.readableContentGuide.topAnchor),
    playerView.leftAnchor.constraint(equalTo: view.leftAnchor),
    playerView.rightAnchor.constraint(equalTo: view.rightAnchor),
    playerView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
playerView.backBlock = { [unowned self] in
    if UIApplication.shared.statusBarOrientation.isLandscape {
        self.playerView.updateUI(isLandscape: false)
    } else {
        self.navigationController?.popViewController(animated: true)

Setting up a regular video

playerView.set(url:URL(string: "http://baobab.wdjcdn.com/14525705791193.mp4")!)
playerView.set(resource: KSPlayerResource(url: url, name: name!, cover: URL(string: "http://img.wdjimg.com/image/video/447f973848167ee5e44b67c8d4df9839_0_0.jpeg"), subtitleURL: URL(string: "http://example.ksplay.subtitle")))

Multi-definition, with cover video

let res0 = KSPlayerResourceDefinition(url: URL(string: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")!,
                                      definition: "高清")
let res1 = KSPlayerResourceDefinition(url: URL(string: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")!,
                                      definition: "标清")
let asset = KSPlayerResource(name: "Big Buck Bunny",
                             definitions: [res0, res1],
                             cover: URL(string: "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Big_buck_bunny_poster_big.jpg/848px-Big_buck_bunny_poster_big.jpg"))
playerView.set(resource: asset)

Setting up an HTTP header

let options = KSOptions()
let definition = KSPlayerResourceDefinition(url: URL(string: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")!,
                                            definition: "高清",
                                            options: options)
let asset = KSPlayerResource(name: "Video Name",
                             definitions: [definition])
playerView.set(resource: asset)

Listening status change

//Listen to when the play time changes
playerView.playTimeDidChange = { (currentTime: TimeInterval, totalTime: TimeInterval) in
    print("playTimeDidChange currentTime: \(currentTime) totalTime: \(totalTime)")
public protocol PlayerControllerDelegate: class {
    func playerController(state: KSPlayerState)
    func playerController(currentTime: TimeInterval, totalTime: TimeInterval)
    func playerController(finish error: Error?)
    func playerController(maskShow: Bool)
    func playerController(action: PlayerButtonType)
    // bufferedCount: 0代表首次加载
    func playerController(bufferedCount: Int, consumeTime: TimeInterval)

Advanced Usage

  • Inherits PlayerView's custom play logic and UI.

    class CustomVideoPlayerView: IOSVideoPlayerView {
        override func updateUI(isLandscape: Bool) {
            super.updateUI(isLandscape: isLandscape)
            toolBar.playbackRateButton.isHidden = true
        override func onButtonPressed(type: PlayerButtonType, button: UIButton) {
            if type == .landscape {
                // xx
            } else {
                super.onButtonPressed(type: type, button: button)
  • Selecting Tracks

       override open func player(layer: KSPlayerLayer, state: KSPlayerState) {
            super.player(layer: layer, state: state)
            if state == .readyToPlay, let player = layer.player {
                let tracks = player.tracks(mediaType: .audio)
                let track = tracks[1]
                /// the name of the track
                let name = track.name
                /// the language of the track
                let language = track.language
                /// selecting the one
                player.select(track: track)
  • Set the properties in KSOptions

    open class KSOptions {
      //    public static let shared = KSOptions()
      /// 最低缓存视频时间
      @Published public var preferredForwardBufferDuration = KSOptions.preferredForwardBufferDuration
      /// 最大缓存视频时间
      public var maxBufferDuration = KSOptions.maxBufferDuration
      /// 是否开启秒开
      public var isSecondOpen = KSOptions.isSecondOpen
      /// 开启精确seek
      public var isAccurateSeek = KSOptions.isAccurateSeek
      /// Applies to short videos only
      public var isLoopPlay = KSOptions.isLoopPlay
      /// 是否自动播放,默认false
      public var isAutoPlay = KSOptions.isAutoPlay
      /// seek完是否自动播放
      public var isSeekedAutoPlay = KSOptions.isSeekedAutoPlay
      public var seekFlags = Int32(1)
      // ffmpeg only cache http
      public var cache = false
      public var outputURL: URL?
      public var display = DisplayEnum.plane
      public var audioDelay = 0.0 // s
      public var subtitleDelay = 0.0 // s
      public var videoDisable = false
      public var audioFilters: String?
      public var videoFilters: String?
      public var subtitleDisable = false
      public var videoAdaptable = true
      public var syncDecodeAudio = false
      public var syncDecodeVideo = false
      public var avOptions = [String: Any]()
      public var formatContextOptions = [String: Any]()
      public var hardwareDecode = true
      public var decoderOptions = [String: Any]()
      public var probesize: Int64?
      public var maxAnalyzeDuration: Int64?
      public var lowres = UInt8(0)
      public var autoSelectEmbedSubtitle = true
      public var asynchronousDecompression = false
      public var autoDeInterlace = false
      @Published var preferredFramesPerSecond = Float(60)



Custom FFmpeg

edit BuildFFmpeg.swift And run

swift run build-FFmpeg enable-openssl

Debug FFmpeg

swift run build-FFmpeg enable-debug
dwarfdump -F --debug-info Sources/libavformat.xcframework/macos-arm64_x86_64/Libavformat.framework/Libavformat | head -n 20

然后把DW_AT_comp_dir对应的路径拖到工程里面。选择Create groups. Add to targets不用选择

run demo-macOS


Developments and Tests

Any contributing and pull requests are warmly welcome. However, before you plan to implement some features or try to fix an uncertain issue, it is recommended to open a discussion first. It would be appreciated if your pull requests could build and with all tests green. :)

Backers & Sponsors

Open-source projects cannot live long without your help. If you find KSPlayer to be useful, please consider supporting this project by becoming a sponsor.

Become a sponsor through GitHub Sponsors. ❤️

Your user icon or company logo shows up this with a link to your home page.





  • Swift Tools 5.7.0
View More Packages from this Author


Last updated: Sat Nov 26 2022 20:26:20 GMT-0500 (GMT-05:00)