MMPlayerView : Custom AVPlayerLayer on view and transition player with good effect like youtube and facebook

MMPlayerView

List / Shrink / Transition / Landscape




MMPlayerLayer

ex. use when change player view frequently like tableView / collectionView
import MMPlayerView
mmPlayerLayer.playView = cell.imgView
mmPlayerLayer.getStatusBlock { [weak self] (status) in

}
self.mmPlayerLayer.set(url: DemoSource.shared.demoData[indexPath.row].play_Url)
self.mmPlayerLayer.resume()

Transition

##PresentedViewController
1. Set transition config
    required init?(coder aDecoder: NSCoder) {
       super.init(coder: aDecoder)
        self.mmPlayerTransition.present.pass { (config) in
            // setting
            .....
        }
    }
2. Set MMPLayerToProtocol on PresentedViewController
3. Set MMPlayerPrsentFromProtocol on PresentingViewController

Landscape

1.Set MMPlayerLayer
   // roation screen to landscape can change player to fullscreen
   mmplayerLayer.fullScreenWhenLandscape = true
   
2. Set from code
   mmplayerLayer.setOrientation(.landsacpeLeft)
   
3. Observer
  mmPlayerLayer.getOrientationChange { (status) in
        print("Player OrientationChange \(status)")
  }

Cover View

## add cover item view on player
play.replace(cover: CoverA.instantiateFromNib())

Progress

// Custom your progress view and it will add on player center
// view need to implement ProgressProtocol, and add progress in this view, when start/stop control what need to do
.custom(view: <#T##MMProgressProtocol#>)
 public protocol MMProgressProtocol {
     func start()
     func stop()
 }

Cache

playerLayer.cacheType = .memory(count: 10)
public enum MMPlayerCacheType {
    case none
    case memory(count: Int) // set this to cache seek time in memory and if cache out of count will remove first you    
    stored
}

Layer Protocol

// detect if touch in videoRect
// if touch out of videoRect will not trigger show/hide cover view event
public protocol MMPlayerLayerProtocol: class {

func touchInVideoRect(contain: 

) // 
}

Downloader

        var downloadObservation: MMPlayerObservation?

        downloadObservation = MMPlayerDownloader.shared.observe(downloadURL: downloadURL) { [weak self] (status) in
            switch status {
            case .cancelled:
                print("Canceld")
            case .completed:
                DispatchQueue.main.async {
                    self?.downloadBtn.setTitle("Delete", for: .normal)
                    self?.downloadBtn.isHidden = false
                    self?.progress.isHidden = true
                }
            case .downloading(let value):
                self?.downloadBtn.isHidden = true
                self?.progress.isHidden = false
                self?.progress.progress = value
                print("Exporting \(value) \(downloadURL)")
            case .failed(let err):
                DispatchQueue.main.async {
                    self?.downloadBtn.setTitle("Download", for: .normal)
                }
                
                self?.downloadBtn.isHidden = false
                self?.progress.isHidden = true
                print("Download Failed \(err)")
            case .none:
                DispatchQueue.main.async {
                    self?.downloadBtn.setTitle("Download", for: .normal)
                }
                self?.downloadBtn.isHidden = false
                self?.progress.isHidden = true
            case .exist:
                DispatchQueue.main.async {
                    self?.downloadBtn.setTitle("Delete", for: .normal)
                }
                self?.downloadBtn.isHidden = false
                self?.progress.isHidden = true
            }

Delete

        if let info = MMPlayerDownloader.shared.localFileFrom(url: downloadURL)  {
            MMPlayerDownloader.shared.deleteVideo(info)
        }

Subtitle

     let subTitleStr = Bundle.main.path(forResource: "test", ofType: "srt")!
         if let str = try? String.init(contentsOfFile: subTitleStr) {
             self.mmPlayerLayer.subtitleSetting.subtitleType = .srt(info: str)
             self.mmPlayerLayer.subtitleSetting.defaultTextColor = .red
             self.mmPlayerLayer.subtitleSetting.defaultFont = UIFont.boldSystemFont(ofSize: 20)
         }
     }

Shrink

    // Return a view which you want back
    self.mmPlayerLayer.shrinkView(onVC: self, isHiddenVC: false) { [weak self] () -> UIView? in
        guard let self = self, let path = self.findCurrentPath() else {return nil}
        let cell = self.findCurrentCell(path: path) as! PlayerCell
        self.mmPlayerLayer.set(url: cell.data!.play_Url)
        self.mmPlayerLayer.resume()
        return cell.imgView
    }
    // Check player is shrink or not
    self.mmPlayerL.isShrink

Demo-SwiftUI

Control

let control = MMPlayerControl()
//Play
control.set(url: self.videoList[idx].play_Url)
control.resume()
//InValidate
control.invalidate()
//Observation parameter
@Published
public var orientation: PlayerOrientation = .protrait
@Published
public var timeInfo = TimeInfo()
@Published
public var isMuted = false
@Published
public var isBackgroundPause = true
@Published
@Published
public var repeatWhenEnd: Bool = false
@Published
public var isLoading: Bool = false
public var cacheType: PlayerCacheType = .none
@Published
public private(set) var isCoverShow = false
@Published
public var autoHideCoverType = CoverAutoHideType.disable
public var coverAnimationInterval = 0.3
@Published
public var error: MMPlayerViewUIError?

Init View

let player = MMPlayerViewUI(control: self.control, cover: CoverAUI())

Download

view.modifier(MMPlayerDownloaderModifier(url: obj.play_Url!, status: $downloadStatus))

Transition Player

//func playerTransition<Content: View>(view: Content, from: CGRect?) -> AnyTransition
if showDetailIdx != nil {
    DetailView(obj: self.playListViewModel.videoList[showDetailIdx!], showDetailIdx: $showDetailIdx)
       .edgesIgnoringSafeArea(.all)
       .transition(.playerTransition(view: MMPlayerViewUI(control: control) ,from: fromFrame))
        .zIndex(1)
}
// Trigger transition
withAnimation { 
      self.fromFrame = obj.frame
      self.showDetailIdx = offset
}

Requirements

iOS 12.0+
Swift 5.0+

Installation

MMPlayerView is available through CocoaPods. To install
it, simply add the following line to your Podfile:

Swift 5 
pod 'MMPlayerView'

Author

millmanyang@gmail.com

License

MMPlayerView is available under the MIT license. See the LICENSE file for more info.

GitHub

https://github.com/MillmanY/MMPlayerView