easyui / EZPlayer

基于AVPlayer封装的视频播放器,功能丰富,快速集成,可定制性强,支持react-native。
MIT License
489 stars 95 forks source link
airplay avplayer carthage fairplay ios javascript m3u8 media picture-in-picture pip player react-native swift5 tableview uitableview video

EZPlayer

EZPlayer

Swift 5 Carthage Compatible Platform License

预览

native

EZPlayer EZPlayer1

react native

EZPlayerRNBase EZPlayerRNList

介绍

基于AVPlayer封装的视频播放器,功能丰富,快速集成,可定制性强。

要求

特性

安装 

ExportFramework

执行项目中的ExportFramework脚本自动生成framework

Carthage

  1. 创建一个 Cartfile ,在这个文件中列出你想使用的 frameworks

    github "easyui/EZPlayer" 
  2. 运行 carthage update ,获取依赖到 Carthage/Checkouts 文件夹,逐个构建

  3. 在工程的 target-> General 选项下,拖拽 Carthage/Build 文件夹内想要添加的 framework 到 “Linked Frameworks and Libraries” 选项下。 (如果不想拖动这个操作的话,可以设置Xcode自动搜索Framework的目录 Target—>Build Setting—>Framework Search Path—>添加路径"$(SRCROOT)/Carthage/Build/iOS")

  4. 在工程的 target-> Build Phases 选项下,点击 “+” 按钮,选择 “New Run Script Phase” ,填入如下内容:

    /usr/local/bin/carthage copy-frameworks

    并在 “Input Files” 选项里添加 framework 路径

    $(SRCROOT)/Carthage/Build/iOS/EZPlayer.framework

CocoaPods

  1. 创建一个 Podfile ,在这个文件中列出你想使用的 frameworks

    project 'EZPlayerExample.xcodeproj'
    platform :ios, '9.0'
    
    target '<Your Target Name>' do
     use_frameworks!
     pod 'EZPlayer' 
    end
  2. Podfile 文件目录下执行

    $ pod install

使用

func playEmbeddedVideo(mediaItem: MediaItem, embeddedContentView contentView: UIView? = nil , userinfo: [AnyHashable : Any]? = nil ) {
        //stop
        self.releasePlayer()
......       
        self.player!.backButtonBlock = { fromDisplayMode in
            if fromDisplayMode == .embedded {
                self.releasePlayer()
            }else if fromDisplayMode == .fullscreen {
                if self.embeddedContentView == nil && self.player!.lastDisplayMode != .float{
                    self.releasePlayer()
                }

            }else if fromDisplayMode == .float {
                self.releasePlayer()
            }

        }

        self.embeddedContentView = contentView
        //self.embeddedContentView为nil时就是全屏播放
        self.player!.playWithURL(mediaItem.url! , embeddedContentView: self.embeddedContentView)
    }

//根据设备横置自动全屏
open var autoLandscapeFullScreenLandscape = UIDevice.current.userInterfaceIdiom == .phone
//指定全屏模式是竖屏还是横屏
open var fullScreenMode = EZPlayerFullScreenMode.landscape

//进去全屏模式
open func toFull(_ orientation:UIDeviceOrientation = .landscapeLeft, animated: Bool = true ,completion: ((Bool) -> Swift.Void)? = nil) 
//进入嵌入屏模式
open func toEmbedded(animated: Bool = true , completion: ((Bool) -> Swift.Void)? = nil)
//进入浮动模式
open func toFloat(animated: Bool = true, completion: ((Bool) -> Swift.Void)? = nil) 

例子:EZPlayerExample-DisplayMode

//播放器浮框模式
public enum EZPlayerFloatMode  {
    case none
    case auto //支持系统pip的就是system,否则是window
    case system //iPhone在ios14一下不显示
    case window
}

/// 全屏的模式
open var floatMode = EZPlayerFloatMode.auto

//自定义皮肤只要实现这两个协议
public protocol EZPlayerHorizontalPan: class {
func player(_ player: EZPlayer ,progressWillChange value: TimeInterval)
func player(_ player: EZPlayer ,progressChanging value: TimeInterval)
func player(_ player: EZPlayer ,progressDidChange value: TimeInterval)
}

public protocol EZPlayerGestureRecognizer: class {
func player(_ player: EZPlayer ,singleTapGestureTapped singleTap: UITapGestureRecognizer)
func player(_ player: EZPlayer ,doubleTapGestureTapped doubleTap: UITapGestureRecognizer)
}
//点击事件还可以接受通知
static let EZPlayerTapGestureRecognizer = Notification.Name(rawValue: "com.ezplayer.EZPlayerTapGestureRecognizer")

/// 支持airplay
open var allowsExternalPlayback = true
/// airplay连接状态
open var isExternalPlaybackActive: Bool 

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
...       MediaManager.sharedInstance.playEmbeddedVideo(url:URL.Test.localMP4_0, embeddedContentView: cell?.contentView)

//主要是设置indexPath和scrollView 属性
MediaManager.sharedInstance.player?.indexPath = indexPath
        MediaManager.sharedInstance.player?.scrollView = tableView
    }

//设置
open var videoGravity = EZPlayerVideoGravity.aspect

主要通过下面两个extension来设置,查看
AVAsset+EZPlayer.swift
//获取所有cc
public var closedCaption: [AVMediaSelectionOption]? 
//获取所有subtitle
public var subtitles: [(subtitle: AVMediaSelectionOption,localDisplayName: String)]? 
//获取所有audio
public var audios: [(audio: AVMediaSelectionOption,localDisplayName: String)]? 

AVPlayerItem+EZPlayer.swift
/// 获取/设置当前subtitle/cc
public var selectedMediaCharacteristicLegibleOption:AVMediaSelectionOption?
/// 获取/设置当前cc
public var selectedClosedCaptionOption:AVMediaSelectionOption?
/// 获取/设置当前subtitle
public var selectedSubtitleOption:AVMediaSelectionOption?
/// 获取/设置当前audio
public var selectedMediaCharacteristicAudibleOption:AVMediaSelectionOption?

//不支持m3u8
open func generateThumbnails(times: [TimeInterval],maximumSize: CGSize, completionHandler: @escaping (([EZPlayerThumbnail]) -> Swift.Void ))
//支持m3u8
func snapshotImage() -> UIImage?

例子:EZPlayerExample-Skin(ad)

假如播放过程中进入广告,那需要临时设置广告皮肤,可以设置属性:

open  var controlViewForIntercept : UIView?

例子:EZPlayerExample-Skin(ad)

可以参考EZPlayerExample_RN项目,使用最新的swift5和最新的react native 0.63.3版本对EZPlayer的封装,实现EZPlayer的大部分功能。

react-native-ezplayer 文件:

EZRNPlayerView.swift : 对EZPlayer的封装,对接javascript

EZRNPlayerViewManager.swift : EZPlayer组件管理器

EZRNPlayerViewBridge.h & EZRNPlayerViewBridge.m : oc桥接

EZPlayer.js : 对EZPlayer封装的js api

属性

key description value
source 视频数据源 PropTypes.object
autoPlay 设置数据源后自动播放 PropTypes.bool
useDefaultUI 使用EZPlayer自带皮肤 PropTypes.bool
videoGravity 视频画面比例 PropTypes.string(aspect,aspectFill,scaleFill)
fullScreenMode 全屏模式是竖屏还是横屏 PropTypes.string(portrait,landscape)
onPlayerHeartbeat 播放器声明周期心跳 PropTypes.func
onPlayerPlaybackTimeDidChange addPeriodicTimeObserver方法的触发 PropTypes.func
onPlayerStatusDidChange 播放器状态改变 PropTypes.func
onPlayerPlaybackDidFinish 视频结束 PropTypes.func
onPlayerLoadingDidChange loading状态改变 PropTypes.func
onPlayerControlsHiddenDidChange 播放器控制条隐藏显示 PropTypes.func
onPlayerDisplayModeDidChange 播放器显示模式改变了(全屏,嵌入屏,浮动) PropTypes.object
onPlayerDisplayModeChangedWillAppear 播放器显示模式动画开始 PropTypes.func
onPlayerDisplayModeChangedDidAppear 播放器显示模式动画结束 PropTypes.func
onPlayerTapGestureRecognizer 点击播放器手势通知 PropTypes.func
onPlayerDidPersistContentKey FairPlay DRM PropTypes.func

方法

function description
play() 播放
pause() 暂停
stop() 结束
seek(time, callback) 设置播放进度,单位秒
replaceToPlay(source) 替换播放源
rate(rate) 设置播放速率
autoPlay(autoPlay) 设置自动播放,autoPlay是PropTypes.bool
videoGravity(videoGravity) 设置视频画面比例,videoGravity:aspect,aspectFill,scaleFill
toEmbedded(animated = true, callback) 进入嵌入屏模式
toFloat(animated = true, callback) 进入悬浮屏模式
toFull(orientation = 'landscapeLeft', animated = true, callback) 进入全屏模式,orientation: landscapeLeft , landscapeRight
fullScreenMode(fullScreenMode) 设置全屏的模式,fullScreenMode:portrait , landscape

demo文件:

BasePlayerExample.js : EZPlayer的基础功能演示

TablePlayerExample.js :EZPlayer 在列表中的演示

TablePlayerCell.js : 列表的cell

EZCustomPlayer.js : 对EZPlayer.js进行封装,使用自定义ui,自定义ui可参考

Utils.js : 工具类

使用

//基本使用
//BasePlayerExample.js
        <EZPlayer
          ref={(e) => this._ezPlayer = e}
          style={styles.player}
          source={this.state.source }

          autoPlay={true}
          videoGravity={'aspect'} 
          fullScreenMode={'landscape'}
        />
//自定义ui
//EZCustomPlayer.js
          <TouchableWithoutFeedback onPress={this.action.onScreenTouch}>
                <View>
                    <EZPlayer
                        {...this.props}
                        useDefaultUI={false}
                        ref={(nativePlayer) => this.player.ref = nativePlayer}
                        style={this.props.style}
                        onPlayerHeartbeat={this.events.onPlayerHeartbeat}
                        onPlayerPlaybackTimeDidChange={this.events.onPlayerPlaybackTimeDidChange}
                        onPlayerStatusDidChange={this.events.onPlayerStatusDidChange}
                        onPlayerPlaybackDidFinish={this.events.onPlayerPlaybackDidFinish}
                        onPlayerLoadingDidChange={this.events.onPlayerLoadingDidChange}
                        onPlayerControlsHiddenDidChange={this.events.onPlayerControlsHiddenDidChange}
                        onPlayerDisplayModeDidChange={this.events.onPlayerDisplayModeDidChange}
                        onPlayerDisplayModeChangedWillAppear={this.events.onPlayerDisplayModeChangedWillAppear}
                        onPlayerDisplayModeChangedDidAppear={this.events.onPlayerDisplayModeChangedDidAppear}
                        onPlayerTapGestureRecognizer={this.events.onPlayerTapGestureRecognizer}
                        onPlayerDidPersistContentKey={this.events.onPlayerDidPersistContentKey}
                    />
                    {this.renderLoader()}
                    {this.renderBottomControls()}
                </View>
            </TouchableWithoutFeedback>

ps: react-native-ezplayer整理中

Todo

License

EZPlayer遵守MIT协议,具体请参考MIT