|
@@ -0,0 +1,238 @@
|
|
|
+
|
|
|
+//
|
|
|
+// PQVideoPlayer.swift
|
|
|
+// PQSpeed
|
|
|
+//
|
|
|
+// Created by SanW on 2020/6/3.
|
|
|
+// Copyright © 2020 BytesFlow. All rights reserved.
|
|
|
+//
|
|
|
+
|
|
|
+import UIKit
|
|
|
+
|
|
|
+public class PQSingletoVideoPlayer: NSObject {
|
|
|
+ static public let shared = PQSingletoVideoPlayer()
|
|
|
+ public var isPlayEnd: Bool = false // 是否已播放完成
|
|
|
+ var isRealPlay: Bool = false // 是否已真实播放
|
|
|
+ var isSemiRealPlay: Bool = false // 是否已播放到十秒
|
|
|
+ var isPlayBegin: Bool = false // 是否已缓冲完成开始播放
|
|
|
+ var isFirstFrame: Bool = false // 是否已显示第一帧加载完成
|
|
|
+ var isPlayerError: Bool = false // 是否播放失败
|
|
|
+ var loadingTime: TimeInterval = 0 // 加载时长
|
|
|
+
|
|
|
+ var playId: String = getUniqueId(desc: "playId") // 播放ID
|
|
|
+ /// 进度回调
|
|
|
+ var progressBloc: ((_ loadProgress: Float, _ playProgress: Float, _ duration: Float) -> Void)?
|
|
|
+ /// 播放状态回调
|
|
|
+ public var playStatusBloc: ((_ playStatus: PQVIDEO_PLAY_STATUS) -> Void)?
|
|
|
+ public var playControllerView: UIView?
|
|
|
+ public var playVideoData: PQVideoListModel?
|
|
|
+
|
|
|
+ lazy public var player: TXVodPlayer = {
|
|
|
+ let player = TXVodPlayer()
|
|
|
+ let config = TXVodPlayConfig()
|
|
|
+ config.cacheFolderPath = videoCacheDirectory
|
|
|
+ config.maxCacheItems = 5
|
|
|
+ player.config = config
|
|
|
+ player.vodDelegate = self
|
|
|
+ player.setRenderMode(.RENDER_MODE_FILL_EDGE)
|
|
|
+ return player
|
|
|
+ }()
|
|
|
+
|
|
|
+ public func configPlyer(videoData: PQVideoListModel, controllerView: UIView) {
|
|
|
+ isPlayEnd = false
|
|
|
+ isRealPlay = false
|
|
|
+ isSemiRealPlay = false
|
|
|
+ isPlayBegin = false
|
|
|
+ isFirstFrame = false
|
|
|
+ isPlayerError = false
|
|
|
+ loadingTime = Date().timeIntervalSince1970 * 1000
|
|
|
+
|
|
|
+ player.stopPlay()
|
|
|
+ player.removeVideoWidget()
|
|
|
+
|
|
|
+ player.setupVideoWidget(controllerView, insert: 0)
|
|
|
+ player.enableHWAcceleration = true
|
|
|
+ playControllerView = controllerView
|
|
|
+ playVideoData = videoData
|
|
|
+ if playVideoData!.playProgress >= 0.0 {
|
|
|
+ //
|
|
|
+ var progress: CGFloat = playVideoData!.playProgress
|
|
|
+ if progress > 5.0, progress < 20 {
|
|
|
+ progress = progress - 5.0
|
|
|
+ if progress <= 0 {
|
|
|
+ progress = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ BFLog(message: "xxx.playProgress =\(playVideoData!.playProgress),\(progress)")
|
|
|
+ player.setStartTime(progress)
|
|
|
+ }
|
|
|
+ playId = getUniqueId(desc: "playId")
|
|
|
+ BFLog(message: "\(String(describing: videoData.title)) 开始播放 \(videoData.videoPath ?? "")")
|
|
|
+ player.startPlay(videoData.videoPath)
|
|
|
+
|
|
|
+ if PQSingletoMemoryUtil.shared.playCount < 4 {
|
|
|
+ PQSingletoMemoryUtil.shared.playCount = PQSingletoMemoryUtil.shared.playCount + 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func resetPlayer() {
|
|
|
+ if (playControllerView != nil) {
|
|
|
+ player.removeVideoWidget()
|
|
|
+ player.setupVideoWidget(playControllerView, insert: 0)
|
|
|
+ if playVideoData!.playProgress >= 0.0 {
|
|
|
+ //
|
|
|
+ var progress: CGFloat = playVideoData!.playProgress
|
|
|
+ if progress > 5.0, progress < 20 {
|
|
|
+ progress = progress - 5.0
|
|
|
+ if progress <= 0 {
|
|
|
+ progress = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ BFLog(message: "xxx.playProgress =\(playVideoData!.playProgress),\(progress)")
|
|
|
+ player.setStartTime(progress)
|
|
|
+ }
|
|
|
+ playId = getUniqueId(desc: "playId")
|
|
|
+ player.startPlay(playVideoData!.videoPath)
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public func stopPlayer(isRemove: Bool = true) {
|
|
|
+ player.stopPlay()
|
|
|
+ if isRemove {
|
|
|
+ player.removeVideoWidget()
|
|
|
+ playControllerView = nil
|
|
|
+ playVideoData = nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override private init() {
|
|
|
+ super.init()
|
|
|
+ }
|
|
|
+
|
|
|
+ override public func copy() -> Any {
|
|
|
+ return self
|
|
|
+ }
|
|
|
+
|
|
|
+ override public func mutableCopy() -> Any {
|
|
|
+ return self
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension PQSingletoVideoPlayer: TXVodPlayListener {
|
|
|
+ /// 播放进度回调
|
|
|
+ /// - Parameters:
|
|
|
+ /// - player: <#player description#>
|
|
|
+ /// - EvtID: <#EvtID description#>
|
|
|
+ /// - param: <#param description#>
|
|
|
+ public func onPlayEvent(_ player: TXVodPlayer!, event EvtID: Int32, withParam param: [AnyHashable: Any]!) {
|
|
|
+ switch EvtID {
|
|
|
+ case PLAY_EVT_PLAY_PROGRESS.rawValue: // 播放进度
|
|
|
+ // 加载进度, 单位是秒
|
|
|
+ let loadProgress: Float = param[EVT_PLAYABLE_DURATION] as! Float
|
|
|
+ // 播放进度, 单位是秒
|
|
|
+ let playProgress: Float = param[EVT_PLAY_PROGRESS] as! Float
|
|
|
+ // 总长度, 单位是秒
|
|
|
+ let duration: Float = param[EVT_PLAY_DURATION] as! Float
|
|
|
+ if player.currentPlaybackTime() >= 10.0, player.currentPlaybackTime() <= 10.1 {
|
|
|
+ // 10.0上报
|
|
|
+ if !isSemiRealPlay, playVideoData != nil {
|
|
|
+ isSemiRealPlay = true
|
|
|
+ // 播放上报
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_Action, videoData: playVideoData, pageSource: nil, businessType: .bt_videoSemiRealPlay, objectType: nil, extParams: nil, shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if player.currentPlaybackTime() >= 20.0 || ((playProgress / duration) >= 0.3) {
|
|
|
+ // 视频播放到20s或播放到总时长30%,哪个先到为准
|
|
|
+ if !isRealPlay, playVideoData != nil {
|
|
|
+ isRealPlay = true
|
|
|
+ // 播放上报
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_realPlay, videoData: playVideoData, pageSource: nil, businessType: .bt_videoRealPlay, objectType: nil, extParams: nil, shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if progressBloc != nil {
|
|
|
+ progressBloc!(loadProgress, playProgress, duration)
|
|
|
+ }
|
|
|
+ case PLAY_EVT_RCV_FIRST_I_FRAME.rawValue:
|
|
|
+ // 首帧完成开始播放
|
|
|
+ if !isPlayBegin, playVideoData != nil {
|
|
|
+ isPlayBegin = true
|
|
|
+ // 播放上报
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_Action, videoData: playVideoData, pageSource: nil, businessType: .bt_videoPlaySuccess, objectType: nil, extParams: nil, shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ }
|
|
|
+
|
|
|
+ case PLAY_EVT_PLAY_LOADING.rawValue: // 视频播放loading
|
|
|
+ if playStatusBloc != nil {
|
|
|
+ playStatusBloc!(.PQVIDEO_PLAY_STATUS_LOADING)
|
|
|
+ }
|
|
|
+ case PLAY_EVT_PLAY_BEGIN.rawValue: // 开始播放
|
|
|
+ if playStatusBloc != nil {
|
|
|
+ playStatusBloc!(.PQVIDEO_PLAY_STATUS_BEGIN)
|
|
|
+ }
|
|
|
+ // 首帧加载完成
|
|
|
+ if !isFirstFrame, playVideoData != nil {
|
|
|
+ isFirstFrame = true
|
|
|
+ // 播放上报
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_play, videoData: playVideoData, pageSource: nil, businessType: nil, objectType: nil, extParams: nil, shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ let duration = Int((Date().timeIntervalSince1970 * 1000) - loadingTime)
|
|
|
+ BFLog(message: "加载时长:\(duration)")
|
|
|
+ // 加载时间上报
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_Frontend, videoData: playVideoData, pageSource: nil, businessType: .bt_videoPlaySuccessTime, objectType: nil, extParams: ["duration": duration, "proxyEnable": "0", "url": playVideoData?.videoPath ?? "", "videoId": playVideoData?.uniqueId ?? "0"], shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ }
|
|
|
+ BFLog(message: "首帧加载完成")
|
|
|
+ case PLAY_EVT_PLAY_END.rawValue: // 播放结束
|
|
|
+ player.pause()
|
|
|
+ playId = getUniqueId(desc: "playId")
|
|
|
+ if playStatusBloc != nil {
|
|
|
+ playStatusBloc!(.PQVIDEO_PLAY_STATUS_END)
|
|
|
+ }
|
|
|
+ if !isPlayEnd, playVideoData != nil {
|
|
|
+ isPlayEnd = true
|
|
|
+ // 播放结束上报
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_Action, videoData: playVideoData, pageSource: nil, businessType: .bt_videoPlayEnd, objectType: nil, extParams: nil, shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ }
|
|
|
+ case PLAY_ERR_NET_DISCONNECT.rawValue: // 重连失败
|
|
|
+ if playStatusBloc != nil {
|
|
|
+ playStatusBloc!(.PQVIDEO_PLAY_STATUS_DISCONNECT)
|
|
|
+ }
|
|
|
+ if !isPlayerError, playVideoData != nil {
|
|
|
+ isPlayerError = true
|
|
|
+ // 播放失败
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_Frontend, videoData: playVideoData, pageSource: nil, businessType: .bt_videoPlayError, objectType: nil, extParams: ["pageSource": playVideoData!.pageSource.rawValue, "networkType": networkStatus(), "extra": "0", "hasRetry": true, "url": playVideoData?.videoPath ?? "", "videoId": playVideoData?.uniqueId ?? "0", "what": EvtID], shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ }
|
|
|
+ case PLAY_ERR_FILE_NOT_FOUND.rawValue: // 播放文件不存在
|
|
|
+ if playStatusBloc != nil {
|
|
|
+ playStatusBloc!(.PQVIDEO_PLAY_STATUS_NOT_FOUND)
|
|
|
+ }
|
|
|
+ if !isPlayerError, playVideoData != nil {
|
|
|
+ isPlayerError = true
|
|
|
+ // 播放失败
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_Frontend, videoData: playVideoData, pageSource: nil, businessType: .bt_videoPlayError, objectType: nil, extParams: ["pageSource": playVideoData!.pageSource.rawValue, "networkType": networkStatus(), "extra": "0", "hasRetry": false, "url": playVideoData?.videoPath ?? "", "videoId": playVideoData?.uniqueId ?? "0", "what": EvtID], shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ }
|
|
|
+ case PLAY_ERR_HEVC_DECODE_FAIL.rawValue, PLAY_ERR_HLS_KEY.rawValue, PLAY_ERR_GET_PLAYINFO_FAIL.rawValue, PLAY_WARNING_VIDEO_DECODE_FAIL.rawValue, PLAY_WARNING_AUDIO_DECODE_FAIL.rawValue: // H265解码失败,HLS解码key获取失败,获取点播文件信息失败,当前视频解码失败,当前音频解码失败
|
|
|
+
|
|
|
+ if !isPlayerError, playVideoData != nil {
|
|
|
+ isPlayerError = true
|
|
|
+ // 播放失败
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_Frontend, videoData: playVideoData, pageSource: nil, businessType: .bt_videoPlayError, objectType: nil, extParams: ["pageSource": playVideoData!.pageSource.rawValue, "networkType": networkStatus(), "extra": "0", "hasRetry": false, "url": playVideoData?.videoPath ?? "", "videoId": playVideoData?.uniqueId ?? "0", "what": EvtID], shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ }
|
|
|
+ case PLAY_WARNING_RECONNECT.rawValue: // 断线重连已启动重新连接
|
|
|
+ if playStatusBloc != nil {
|
|
|
+ playStatusBloc!(.PQVIDEO_PLAY_STATUS_RECONNECT)
|
|
|
+ }
|
|
|
+ case PLAY_WARNING_RECV_DATA_LAG.rawValue, PLAY_WARNING_VIDEO_PLAY_LAG.rawValue: // 网络来包不稳:可能是下行带宽不足 | 当前视频播放出现卡顿(用户直观感受)
|
|
|
+ // 播放失败
|
|
|
+ PQEventTrackViewModel.videoRelationReportUpload(reportLogType: .reportLogType_Frontend, videoData: playVideoData, pageSource: nil, businessType: .bt_videoPlaySlow, objectType: nil, extParams: ["pageSource": playVideoData!.pageSource.rawValue, "networkType": networkStatus(), "url": playVideoData?.videoPath ?? "", "videoId": playVideoData?.uniqueId ?? "0", "what": EvtID, "position": player.currentPlaybackTime()], shareId: nil, videoIds: nil, playId: playId)
|
|
|
+ default:
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 网络状态回调
|
|
|
+ /// - Parameters:
|
|
|
+ /// - player: <#player description#>
|
|
|
+ /// - param: <#param description#>
|
|
|
+ public func onNetStatus(_: TXVodPlayer!, withParam _: [AnyHashable: Any]!) {
|
|
|
+ // BFLog(message: "onNetStatus:\(param)")
|
|
|
+ }
|
|
|
+}
|