Как отразить (перевернуть) воспроизведение видео с помощью AVPlayer?

Я хочу отразить (Flip) воспроизведение видео с помощью AVPlayer. Как: MirrorTube: - https://chrome.google.com/webstore/detail/mirrortube/olomckflnlligkboahmaihmeaffjdbfm/related?hl=en

Я хочу достичь той же функциональности.

Я попытался изменить CGAffineTransform, но он не работает так же.

Заранее спасибо!

2 ответа

Решение

Вот пример того, как перевернуть плеер по вертикали и горизонтали, используя CGAffineTransform:

PlayerView:

import AVKit

class PlayerView: UIView {
    var player: AVPlayer? {
        get {
            return playerLayer.player
        }
        set {
            playerLayer.player = newValue
        }
    }

    var playerLayer: AVPlayerLayer {
        return layer as! AVPlayerLayer
    }

    // Override UIView property
    override static var layerClass: AnyClass {
        return AVPlayerLayer.self
    }
}

ViewController, использующий playerView, определенный в xib / storyboard:

@IBOutlet var playerView: PlayerView!

@IBAction func flipVerticallyBarButtonItemTouched(_ sender: UIBarButtonItem) {
           UIView.animate(withDuration: 0.2) { [unowned self] in
               self.playerView.transform = self.playerView.transform.concatenating(CGAffineTransform(scaleX: 1, y: -1))
           }
       }
@IBAction func flipHorizontallyBarButtonItemTouched(_ sender: UIBarButtonItem) {
           UIView.animate(withDuration: 0.2) { [unowned self] in
            self.playerView.transform = self.playerView.transform.concatenating(CGAffineTransform(scaleX: -1, y: 1))
           }
}

Видео примера в действии.

Примечание. Для приложения iOS в Mac Catalyst (с подклассом AVPlayerController) корневым представлением является AVPlayerView. Но, как ни странно, AVPlayerView не является общедоступным на iOS> Mac Catalyst, поэтому self.playerView не будет работать, поэтому вы не можете его использовать. Вы получаете класс, не найденный для «AVPlayerView»

Но когда вы запускаете приложение в Mac Catalyst и проверяете его, self.view представляет собой AVPlayerView.

обходной путь: просто переверните корневой вид без приведения - self.view

      `class MyAVPlayerController: AVPlayerController`

to flip horizontal etc

            self.view.transform = self.view.transform.concatenating(CGAffineTransform(scaleX: -1, y: 1))

to flip vertical etc

            self.view.transform = self.view.transform.concatenating(CGAffineTransform(scaleX: 1, y: -1))

KNOWN ISSUE this flips the whole AVPlayerView including controls. in iOS 16 the actual player is a view where Swift.type(of:view) is "__AVPlayerLayerView" so you can walk the hierarchy and find the UIView return it then apply the transform to only that subview




func flipHorizontal(){
    
    print("TradAVPlayerViewController.swift flipHorizontal")
    
    if self.player != nil{
        //------------------------------------------------------------------
        //v1 - flips VC.view which is AVPlayerView
        //but also flips TOAST and CONTROLS
        //https://developer.apple.com/documentation/uikit/uiview/1622459-transform
        //------------------------------------------------------------------
        //OK ROTATES VIEW
        //self.view.transform = CGAffineTransform(rotationAngle: CGFloat((90 * Double.pi)/180))
        
        //OK FLIP VERTICAL
        //self.view.transform = self.view.transform.concatenating(CGAffineTransform(scaleX: 1, y: -1))
        
        //OK FLIPS HORIZONTAL
 //       self.view.transform = self.view.transform.concatenating(CGAffineTransform(scaleX: -1, y: 1))

        //note the flip remains in place as long as self.player isnt recreated so PREV/NEXT will stay flipped
        
        //------------------------------------------------------------------
        //ISSUE flipping the whole view also flips the TOAST and SCRUBBER
        //------------------------------------------------------------------
        //in iOS 15 the player is a class with name "__AVPlayerLayerView"
        //find it in the hierarchy and only apply transform to that on
        if let rootView = self.view{
            let className = "__AVPlayerLayerView"
            if let subViewFound
                = findSubViewByName(name: className,
                                    view: rootView)
            {
                print("FOUND className:'\(className)' flipping it")
                
                subViewFound.transform = subViewFound.transform.concatenating(CGAffineTransform(scaleX: -1, y: 1))
            }else{
                print("NOT FOUND className:'\(className)' CANT FLIP IT - debug hierarchy in 3d and get class name for the AVPlayerLayerView")
            }
        }else{
            logger.error("self.view is nil")
        }
        
        print()
    }else{
        logger.error("self.player is nil")
    }
}

func findSubViewByName(name: String, view: UIView) -> UIView?{
    var viewFound: UIView? = nil
    
    for subView in view.subviews{
    
        //MUST WRAP IN STRING else types wont match
        let typeName = "\(Swift.type(of: subView))"
        //print("typeName:\(typeName)")
        
        if typeName == name {
            print("SUB VIEW - typeName:\(typeName) - MATCH!!")
            viewFound = subView
            break
            
        }else{
            print("SUB VIEW - typeName:\(typeName) - NO MATCH")
        }
        
        //recurse depth first
        
        
        if let subViewFound = findSubViewByName(name: name, view: subView){
            viewFound = subViewFound
            break
        }else{
            //no match in subviewsf
        }
    }
    
    return viewFound
}
Другие вопросы по тегам