KSPlayer is a powerful media play framework for iOS, tvOS, macOS, xrOS, visionOS, Mac Catalyst, SwiftUI, Apple Silicon M1 .
English | 简体中文
- FFmpeg
- Metal
- AVAudioEngine
- iOS, tvOS, macOS, visionOS, Mac Catalyst, Apple Silicon M1, SwiftUI.
- Multiple audio/video tracks.
- hardware accelerator.
- 4k/HDR/HDR10/HDR10+/Dolby Vision
- text subtitle/Closed Captions/image subtitle(dvbsub/dvdsub/pgssub)
- Picture in Picture
- Record video
- De-interlace auto detect
- Spatial Audio
- 360° panorama video.
- libsmbclient protocol
- iOS 13 +, macOS 10.15 +, tvOS 13 +, xrOS 1 +
cd Demo
pod install
- Open Demo/Demo.xcworkspace with Xcode.
https://testflight.apple.com/join/eNmYbmZN
App Store https://apps.apple.com/app/tracyplayer/id6450770064
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 'DisplayCriteria',:git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'develop'
pod 'FFmpegKit',:git => 'https://github.com/kingslay/FFmpegKit.git', :branch => 'main'
pod 'Libass',:git => 'https://github.com/kingslay/FFmpegKit.git', :branch => 'main'
end
dependencies: [
.package(url: "https://github.com/kingslay/KSPlayer.git", .branch("develop"))
]
KSOptions.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)
}
}
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")))
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)
let options = KSOptions()
options.appendHeader(["Referer":"https:www.xxx.com"])
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)
// Listen to play time change
playerView.playTimeDidChange = { (currentTime: TimeInterval, totalTime: TimeInterval) in
print("playTimeDidChange currentTime: \(currentTime) totalTime: \(totalTime)")
}
// Delegates
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` indicates first time loading
func playerController(bufferedCount: Int, consumeTime: TimeInterval)
}
-
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 { // Your own button press behaviour here } else { super.onButtonPressed(type: type, button: button) } } }
-
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) } }
-
open class 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 /* AVSEEK_FLAG_BACKWARD: 1 AVSEEK_FLAG_BYTE: 2 AVSEEK_FLAG_ANY: 4 AVSEEK_FLAG_FRAME: 8 */ public var seekFlags = Int32(0) // ffmpeg only cache http public var cache = false public var outputURL: URL? public var display = DisplayEnum.plane public var avOptions = [String: Any]() public var formatContextOptions = [String: Any]() public var decoderOptions = [String: Any]() public var probesize: Int64? public var maxAnalyzeDuration: Int64? public var lowres = UInt8(0) public var startPlayTime: TimeInterval = 0 public var startPlayRate: Float = 1.0 public var registerRemoteControll: Bool = true // 默认支持来自系统控制中心的控制 public var referer: String? public var userAgent: String? // audio public var audioFilters = [String]() public var syncDecodeAudio = false // sutile public var autoSelectEmbedSubtitle = true public var subtitleDisable = false public var isSeekImageSubtitle = false // video public var videoDelay = 0.0 // s public var autoDeInterlace = false public var autoRotate = true public var destinationDynamicRange: DynamicRange? public var videoAdaptable = true public var videoFilters = [String]() public var syncDecodeVideo = false public var hardwareDecode = KSOptions.hardwareDecode public var asynchronousDecompression = true public var videoDisable = false public var canStartPictureInPictureAutomaticallyFromInline = true }
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. :)
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.
UnknownCoder807 skrew Kimentanm nakiostudio byMohamedali pcccccc
Thanks to nightfall708 for sponsoring a mac mini
Thanks to cdguy UnknownCoder807 skrew and LillyPlayer community for sponsoring a LG S95QR Sound Bar
Thanks to skrew and LillyPlayer community for sponsoring a 2022 Apple TV 4K
If you have a business cooperation project or want to initiate a paid consultation, you can contact me via email
- Email : [email protected]