KSPlayer 是一个强大的媒体播放框架,适用于 iOS、tvOS、macOS、xrOS、visionOS、Mac Catalyst、SwiftUI、Apple Silicon M1 等平台。
English | 简体中文
GPL 版本和 LGPL 版本的功能差异
特性 | LGPL | GPL |
---|---|---|
Dolby AC-4 | ✅ | ❌ |
AV1 硬件解码 | ✅ | ❌ |
所有解封装器,所有解码器 | ✅ | ❌ |
随时录制视频片段 | ✅ | ❌ |
流畅播放 8K 或 120 FPS 视频 | ✅ | ❌ |
Dovi P5 显示 HDR (不发热) | ✅ | ❌ |
直播支持倒退观看 | ✅ | ❌ |
画中画支持字幕显示 | ✅ | ❌ |
在所有 Apple 平台上播放蓝光光盘 | ✅ | ❌ |
Annex-B 异步硬件解码(直播) | ✅ | ❌ |
使用视频中的字体渲染字幕 | ✅ | ❌ |
使用内存缓存,在短时间内快速Seek | ✅ | ❌ |
缓存数据到硬盘,预加载数据到硬盘 | ✅ | ❌ |
离线 AI 实时字幕生成和翻译 | ✅ | ❌ |
完整显示 ass 字幕效果 (使用 libass 渲染为图像) | ✅ | ❌ |
在 App 的小窗口中播放视频(可恢复,支持 tvos 和 ios) | ✅ | ❌ |
FFmpeg 版本 | 7.0.2 | 6.1.0 |
录制视频 | ✅ | ✅ |
360° 全景视频 | ✅ | ✅ |
画中画 | ✅ | ✅ |
硬件加速 | ✅ | ✅ |
无缝循环播放 | ✅ | ✅ |
自动检测去隔行 | ✅ | ✅ |
4k/HDR/HDR10/HDR10+/杜比视界 | ✅ | ✅ |
自定义 URL 协议,例如 nfs/smb/UPnP | ✅ | ✅ |
多声道音频/空间音频 | ✅ | ✅ |
文本字幕/图像字幕/隐藏式字幕 | ✅ | ✅ |
搜索在线字幕 (shooter/assrt/opensubtitles) | ✅ | ✅ |
低延迟 4K 直播 (局域网内小于 200 毫秒) | ✅ | ✅ |
根据网络自动切换到多码率流 | ✅ | ✅ |
KSPlayer 默认采用 GPL 许可(需要开源您自己的项目代码),我们希望大家自觉遵守 KSPlayer 项目的许可协议。 此外,还有一个付费版本,采用 LGPL 许可(请联系我们)。
如果由于商业原因,您不想遵守 GPL 许可或 LGPL 许可,您可以联系我们。 通过我们的授权,您可以获得更灵活的许可协议。 邮箱:kingslay@icloud.com
cd Demo
pod install
确保使用最新版本的 cocoapods 1.10.1+,可以使用命令 brew install cocoapods
安装
target 'ProjectName' do
use_frameworks!
pod 'KSPlayer',:git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'main'
pod 'DisplayCriteria',:git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'main'
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("main"))
]
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
}
欢迎任何贡献和 pull requests。但是,在您计划实现某些功能或尝试修复不确定的问题之前,建议先进行讨论。 如果您的 pull requests 可以构建并通过所有测试,我们将不胜感激。 :)
没有您的帮助,开源项目无法长久存在。 如果您发现 KSPlayer 有用,请考虑成为赞助商来支持这个项目。
通过 GitHub Sponsors 成为赞助商。 ❤️
您的用户图标或公司徽标将显示在此处,并链接到您的主页。
名字 | Logo |
---|---|
UnknownCoder807 | |
skrew | |
Kimentanm | |
byMohamedali | |
nakiostudio | ![]() |
CodingByJerez | |
andrefmsilva | |
romaingyh | |
FantasyKingdom | |
aart-rainey | |
nihalahmed | |
HatimDa | |
johnil |
感谢 nightfall708 赞助了一台 mac mini
感谢 cdguy UnknownCoder807 skrew 和 LillyPlayer 社区赞助了一台 LG S95QR Sound Bar
感谢 skrew 和 LillyPlayer 社区赞助了一台 2022 Apple TV 4K
如果您有商务合作项目或想要发起付费咨询,您可以通过电子邮件与我联系