FWPlayer
A video player SDK for iOS, it is based on AVPlayer. https://se.linkedin.com/in/foks-huiwang, https://www.fokswang.com/
Install / Use
/learn @FoksWang/FWPlayerREADME
FWPlayer
A video player SDK for iOS, it is based on AVPlayer.
Features
- [x] Supports horizontal and vertical playback
- [x] Supports auto-rotating screen playback
- [x] Supports the full-screen and mini-player playback
- [x] Supports mini-player position to drag freely
- [x] Supports the network and local video playback
- [x] Supports full-screen lock
- [x] Supports playback while downloading (Media Cache)
- [x] Supports vertical slide on the left side of the screen to adjust the brightness
- [x] Supports the vertical slide on the right side of the screen to adjust the volume
- [x] Supports gesture swipe fast-forward and rewind
- [x] Supports drag slider fast-forward and rewind
- [x] Supports direct jump to a point in the timeline to play
- [x] Supports multiple video formats
- [x] Supports UITableView playback
- [x] Supports UICollectionView playback
- [x] Supports UIScrollView playback
- [x] Supports background playback
- [x] Supports play sound in silent mode by default
- [x] Supports speed rate playback (0.5x, 1.0x, 1.25x, 1.5x, 2.0x)
- [x] Supports custom player view
- [x] Supports advertising view
- [x] Supports adding Http headers and other options to AVURLAsset
- [x] Supports iPhone X and above
- [x] Supports iOS 13 +
- FFmpeg is not supported because OpenGL ES was deprecated in iOS 12
Requirements
- iOS 10 +
- Xcode 11 +
Installation
You can install FWPlayer SDK in several ways:
CocoaPods
CocoaPods is an easy way to install FWPlayer.
- Add following pod to your
Podfile:
platform :ios, '10.0'
target 'Your App' do
pod 'FWPlayer'
end
- Then, run the following command:
$ pod install
- Switch over to
Build Phasesand add aNew Run Script Phaseby clicking the+in the top left of the editor. Add the following command to solve the issue of App Store submission.
bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/FWPlayerCore.framework/strip-frameworks.sh"
Carthage
Since FWPlayer SDK is distributed as a binary, you need to use custom binary rule in your Cartfile.
- Add following to your
Cartfile:
binary "https://raw.githubusercontent.com/FoksWang/FWPlayer/master/Carthage/FWPlayer.json" ~> 1.0.13
- Fetch framework by running:
$ carthage update --platform iOS
Manual Installation
- Add the FWPlayer framework to
Embedded Binariesfor your target:
FWPlayerCore.framework
- Make sure you link with the following
Linked Frameworks and Libraries:
FWPlayerCore.framework
Usage
Normal Style
Objective-C
FWAVPlayerManager *playerManager = [[FWAVPlayerManager alloc] init];
self.player = [FWPlayerController playerWithPlayerManager:playerManager containerView:self.containerView];
self.player.controlView = self.controlView;
Swift
private var player: FWPlayerController?
private lazy var containerView: UIImageView = {
let imageView = UIImageView()
imageView.setImageWithURLString(coverImageUrl, placeholder: UIImage(named: "placeholder"))
return imageView
}()
private lazy var controlView: FWPlayerControlView = {
let view = FWPlayerControlView()
view.fastViewAnimated = true
view.autoHiddenTimeInterval = 5.0
view.autoFadeTimeInterval = 0.5
view.prepareShowLoading = true
view.prepareShowControlView = true
return view
}()
let playerManager = FWAVPlayerManager()
playerManager.isEnableMediaCache = false
// Setup player
self.player = FWPlayerController(playerManager: playerManager, containerView: self.containerView)
self.player?.controlView = self.controlView
// Setup continue playing in the background
self.player?.pauseWhenAppResignActive = true
self.player?.orientationWillChange = { [weak self] (player, isFullScreen) in
self?.setNeedsStatusBarAppearanceUpdate()
}
// Finished playing
self.player?.playerDidToEnd = { [weak self] (asset) in
guard let strongSelf = self else {
return
}
strongSelf.player?.currentPlayerManager.replay!()
strongSelf.player?.playTheNext()
if strongSelf.player?.isLastAssetURL == false {
strongSelf.controlView.showTitle("Video Title", coverURLString: strongSelf.kVideoCover, fullScreenMode: .landscape)
} else {
strongSelf.player?.stop()
}
}
self.player?.assetURLs = self.assetURLs
To play the next or previous video, just set:
Objective-C
self.player.assetURLs = self.assetURLs;
Swift
self.player!.assetURLs = self.assetURLs
- To play the next video, please call method
playTheNext - To play the previous video, please call method
playThePrevious - To play the video from the asset list, please call method
playTheIndex:index
For example, play the next video:
Objective-C
if (!self.player.isLastAssetURL) {
[self.player playTheNext];
[self.controlView showTitle:@"Video title" coverURLString:kVideoCover fullScreenMode:FWFullScreenModeAutomatic];
} else {
NSLog(@"No more videos");
}
Swift
if self.player!.isLastAssetURL == false {
self.player!.playTheNext()
self.controlView.showTitle("Video title", coverURLString: kVideoCover, fullScreenMode: .automatic)
} else {
print("No more videos")
}
Quick demo for Normal Style
Step 1: In AppDelegate, make sure you have var window: UIWindow?
Swift
var window: UIWindow?
static var shared: AppDelegate {
UIApplication.shared.delegate as! AppDelegate
}
Step 2: In SceneDelegate, make sure you have var window: UIWindow?
Swift
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
AppDelegate.shared.window = window
}
Step 3: Use quick demo code ViewController
Swift
import UIKit
import FWPlayerCore
class ViewController: UIViewController {
private let kVideoCover = "https://github.com/FoksWang/FWPlayer/blob/master/Example/FWPlayer/Images.xcassets/Common/cover_image_placeholder.imageset/cover_image_placeholder.jpg?raw=true"
private var player: FWPlayerController?
private lazy var containerView: UIImageView = {
let imageView = UIImageView()
let color = UIColor(red: 220.0/255.0, green: 220.0/255.0, blue: 220.0/255.0, alpha: 1)
let placeholderImage = FWUtilities.image(with: color, size: CGSize(width: 1, height: 1))
imageView.setImageWithURLString(kVideoCover, placeholder: placeholderImage)
return imageView
}()
private lazy var controlView: FWPlayerControlView = {
let view = FWPlayerControlView()
view.fastViewAnimated = true
view.autoHiddenTimeInterval = 5.0
view.autoFadeTimeInterval = 0.5
view.prepareShowLoading = true
view.prepareShowControlView = true
return view
}()
private lazy var playButton: UIButton = {
var button = UIButton(type: .custom)
button.setImage(FWUtilities.imageNamed("FWPlayer_all_play"), for: .normal)
button.addTarget(self, action: #selector(playButtonClick), for: .touchUpInside)
return button
}()
@objc private func playButtonClick() {
print("playButtonClick")
self.player!.playTheIndex(assetIndex)
self.controlView.showTitle("Video title 1", coverURLString: kVideoCover, fullScreenMode: .automatic)
}
private lazy var nextButton: UIButton = {
var button = UIButton(type: .system)
button.setTitle("Next", for: .normal)
button.addTarget(self, action: #selector(nextButtonClick), for: .touchUpInside)
return button
}()
@objc private func nextButtonClick() {
print("nextButtonClick")
assetIndex += 1
if assetIndex > assetURLs.count - 1 {
assetIndex = assetURLs.count - 1
}
self.player!.playTheIndex(assetIndex)
self.controlView.showTitle("Video title 2", coverURLString: kVideoCover, fullScreenMode: .automatic)
}
private var assetIndex = 0
private lazy var assetURLs: Array<URL> = {
var assetList = [
URL(string: "https://svt1-b.akamaized.net/se/svt1/master.m3u8")!,
URL(string: "https://svt1-b.akamaized.net/se/svt2/master.m3u8")!,
URL(string: "https://www.radiantmediaplayer.com/media/bbb-360p.mp4")!,
URL(string: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDre
Related Skills
qqbot-channel
354.0kQQ 频道管理技能。查询频道列表、子频道、成员、发帖、公告、日程等操作。使用 qqbot_channel_api 工具代理 QQ 开放平台 HTTP 接口,自动处理 Token 鉴权。当用户需要查看频道、管理子频道、查询成员、发布帖子/公告/日程时使用。
docs-writer
100.8k`docs-writer` skill instructions As an expert technical writer and editor for the Gemini CLI project, you produce accurate, clear, and consistent documentation. When asked to write, edit, or revie
model-usage
354.0kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
arscontexta
3.1kClaude Code plugin that generates individualized knowledge systems from conversation. You describe how you think and work, have a conversation and get a complete second brain as markdown files you own.
