/KSPlayer

iOS/macOS/tvOS video player

Primary LanguageSwiftGNU Lesser General Public License v2.1LGPL-2.1

Build Status Platform License

KSPlayer

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

English | 简体中文

Based On

  • FFmpeg
  • Metal
  • AVAudioEngine

Features

  • iOS, tvOS, macOS,Mac Catalyst, Apple Silicon M1, SwiftUI.
  • 360° panorama video.
  • Background playback.
  • RTMP/RTSP/Dash/HLS streaming.
  • Setting playback speed.
  • Multiple audio/video tracks.
  • H.264/H.265 hardware accelerator.
  • 4k/HDR
  • subtitle/dvb_subtitle
  • Picture in Picture

Requirements

  • iOS 13 +, macOS 10.15 +, tvOS 13 +
  • Xcode 13
  • Swift 5.5

Demo

  • Open Demo/Demo.xcworkspace with Xcode.

Quick Start

CocoaPods

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

target 'ProjectName' do
    use_frameworks!
    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'
end

Swift Package Manager

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

Usage

initialize

KSPlayerManager.secondPlayerType = KSMEPlayer.self
playerView = IOSVideoPlayerView()
view.addSubview(playerView)
playerView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    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 header = ["User-Agent":"KSPlayer"]
let options = KSOptions()
options.avOptions = ["AVURLAssetHTTPHeaderFieldsKey":header]

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 KSPlayerManager and KSOptions.

    public struct KSPlayerManager {
         /// 顶部返回、标题、AirPlay按钮 显示选项,默认.Always,可选.HorizantalOnly、.None
        public static var topBarShowInCase = KSPlayerTopBarShowCase.always
        /// 自动隐藏操作栏的时间间隔 默认5秒
        public static var animateDelayTimeInterval = TimeInterval(5)
        /// 开启亮度手势 默认true
        public static var enableBrightnessGestures = true
        /// 开启音量手势 默认true
        public static var enableVolumeGestures = true
        /// 开启进度滑动手势 默认true
        public static var enablePlaytimeGestures = true
        /// 播放内核选择策略 先使用firstPlayer,失败了自动切换到secondPlayer,播放内核有KSAVPlayer、KSMEPlayer两个选项
        public static var firstPlayerType: MediaPlayerProtocol.Type = KSAVPlayer.self
        public static var secondPlayerType: MediaPlayerProtocol.Type?
        /// 是否能后台播放视频
        public static var canBackgroundPlay = false
        /// 日志输出方式
        public static var logFunctionPoint: (String) -> Void = {
            print($0)
        }
        /// 开启VR模式的陀飞轮
        public static var enableSensor = true
        /// 日志级别
        public static var logLevel = LogLevel.warning
        public static var stackSize = 16384
    }
    public class KSOptions {
        /// 视频颜色编码方式 支持kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange kCVPixelFormatType_420YpCbCr8BiPlanarFullRange kCVPixelFormatType_32BGRA kCVPixelFormatType_420YpCbCr8Planar
        public static var bufferPixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
        /// 最低缓存视频时间
        public static var preferredForwardBufferDuration = 3.0
        /// 最大缓存视频时间
        public static var maxBufferDuration = 30.0
        /// 是否开启秒开
        public static var isSecondOpen = false
        /// 开启精确seek
        public static var isAccurateSeek = true
        /// 开启无缝循环播放
        public static var isLoopPlay = false
        /// 是否自动播放,默认false
        public static var isAutoPlay = false
        /// seek完是否自动播放
        public static var isSeekedAutoPlay = true
    }

Effect

gif

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

run demo-macOS

6

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. :)

Communication

Reference

This item references the ZFPlayerSGPlayer