Comment lire une vidéo locale avec Swift?


132

J'ai un court mp4fichier vidéo que j'ai ajouté à mon projet Xcode6 Beta actuel.

Je souhaite lire la vidéo dans mon application.

Après des heures de recherche, je ne trouve rien d'utile à distance. Existe-t-il un moyen d'accomplir cela avec Swift ou devez-vous utiliser Objective-C? Puis-je être dirigé dans la bonne direction? Je ne peux pas être le seul à me demander ça.


3
puisque vous pouvez le faire dans Cocoa, vous pouvez le faire dans Swift
Abizern

Réponses:


273

Bien sûr, vous pouvez utiliser Swift!

1. Ajout du fichier vidéo

Ajoutez la vidéo (appelons-la video.m4v) à votre projet Xcode

2. Vérifier que votre vidéo fait partie du pack

Ouvrez le Project Navigator cmd+1

Sélectionnez ensuite your project root> your Target> Build Phases> Copy Bundle Resources.

Votre vidéo DOIT être ici. Si ce n'est pas le cas, vous devez l'ajouter à l'aide du bouton plus

entrez la description de l'image ici

3. Code

Ouvrez votre View Controller et écrivez ce code.

import UIKit
import AVKit
import AVFoundation

class ViewController: UIViewController {

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        playVideo()
    }

    private func playVideo() {
        guard let path = Bundle.main.path(forResource: "video", ofType:"m4v") else {
            debugPrint("video.m4v not found")
            return
        }
        let player = AVPlayer(url: URL(fileURLWithPath: path))
        let playerController = AVPlayerViewController()
        playerController.player = player
        present(playerController, animated: true) {
            player.play()
        }
    }
}

4
La plupart des éléments de cette solution sont de vérifier ce champ "Ajouter aux cibles" -> Nom du projet lorsque nous copions cette ressource dans notre projet. Merci cette solution a fonctionné pour moi ..
Pramod

2
La classe MPMoviePlayerController est formellement obsolète dans iOS 9. (La classe MPMoviePlayerViewController est également formellement obsolète.) Pour lire du contenu vidéo dans iOS 9 et versions ultérieures, utilisez plutôt la classe AVPictureInPictureController ou AVPlayerViewController du framework AVKit ou la classe WKWebView de WebKit. Guide de référence Apple
Henry Glendening

pouvez-vous s'il vous plaît vérifier ces stackoverflow.com/questions/32952056/…

Avoir des problèmes avec cela dans la dernière version bêta de xcode 7.2 -initializer for conditional binding must have optional type not nsurl
Brian Wigginton

1
@mobibob Bien sûr, AVPlayerViewController existe :-) Avez-vous import AVKit?
Luca Angeletti du

26

un autre exemple de Swift 3. La solution fournie n'a pas fonctionné pour moi.

private func playVideo(from file:String) {
    let file = file.components(separatedBy: ".")

    guard let path = Bundle.main.path(forResource: file[0], ofType:file[1]) else {
        debugPrint( "\(file.joined(separator: ".")) not found")
        return
    }
    let player = AVPlayer(url: URL(fileURLWithPath: path))

    let playerLayer = AVPlayerLayer(player: player)
    playerLayer.frame = self.view.bounds
    self.view.layer.addSublayer(playerLayer)
    player.play()
}

utilisation:

playVideo(from: "video.extension")

Remarque: vérifiez Copier les ressources de l'ensemble sous Phases de construction pour vous assurer que la vidéo est disponible pour le projet.


Cette solution fonctionne avec d'autres couches ensemble, c'est une meilleure solution que la majorité votée, merci!
Mike Lee

13

U peut configurer AVPlayer d'une autre manière, qui s'ouvre pour une personnalisation complète de l'écran de votre lecteur vidéo

Swift 2.3

  1. Créez une UIViewsous-classe pour la lecture de vidéo (en gros, vous pouvez utiliser n'importe quel UIViewobjet et seul le besoin est AVPlayerLayer. J'ai configuré de cette manière parce que c'est beaucoup plus clair pour moi)

    import AVFoundation
    import UIKit
    
    class PlayerView: UIView {
    
    override class func layerClass() -> AnyClass {
        return AVPlayerLayer.self
    }
    
    var player:AVPlayer? {
        set {
            if let layer = layer as? AVPlayerLayer {
                layer.player = player
            }
        }
        get {
            if let layer = layer as? AVPlayerLayer {
                return layer.player
            } else {
                return nil
            }
        }
    }
    }
  2. Configurez votre lecteur

    import AVFoundation
    import Foundation
    
    protocol VideoPlayerDelegate {
        func downloadedProgress(progress:Double)
        func readyToPlay()
        func didUpdateProgress(progress:Double)
        func didFinishPlayItem()
        func didFailPlayToEnd()
    }
    
    let videoContext:UnsafeMutablePointer<Void> = nil
    
    class VideoPlayer : NSObject {
    
        private var assetPlayer:AVPlayer?
        private var playerItem:AVPlayerItem?
        private var urlAsset:AVURLAsset?
        private var videoOutput:AVPlayerItemVideoOutput?
    
        private var assetDuration:Double = 0
        private var playerView:PlayerView?
    
        private var autoRepeatPlay:Bool = true
        private var autoPlay:Bool = true
    
        var delegate:VideoPlayerDelegate?
    
        var playerRate:Float = 1 {
            didSet {
                if let player = assetPlayer {
                    player.rate = playerRate > 0 ? playerRate : 0.0
                }
            }
        }
    
        var volume:Float = 1.0 {
            didSet {
                if let player = assetPlayer {
                    player.volume = volume > 0 ? volume : 0.0
                }
            }
        }
    
        // MARK: - Init
    
        convenience init(urlAsset:NSURL, view:PlayerView, startAutoPlay:Bool = true, repeatAfterEnd:Bool = true) {
            self.init()
    
            playerView = view
            autoPlay = startAutoPlay
            autoRepeatPlay = repeatAfterEnd
    
            if let playView = playerView, let playerLayer = playView.layer as? AVPlayerLayer {
                playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
            }
            initialSetupWithURL(urlAsset)
            prepareToPlay()
        }
    
        override init() {
            super.init()
        }
    
        // MARK: - Public
    
        func isPlaying() -> Bool {
            if let player = assetPlayer {
                return player.rate > 0
            } else {
                return false
            }
        }
    
        func seekToPosition(seconds:Float64) {
            if let player = assetPlayer {
                pause()
                if let timeScale = player.currentItem?.asset.duration.timescale {
                    player.seekToTime(CMTimeMakeWithSeconds(seconds, timeScale), completionHandler: { (complete) in
                        self.play()
                    })
                }
            }
        }
    
        func pause() {
            if let player = assetPlayer {
                player.pause()
            }
        }
    
        func play() {
            if let player = assetPlayer {
                if (player.currentItem?.status == .ReadyToPlay) {
                    player.play()
                    player.rate = playerRate
                }
            }
        }
    
        func cleanUp() {
            if let item = playerItem {
                item.removeObserver(self, forKeyPath: "status")
                item.removeObserver(self, forKeyPath: "loadedTimeRanges")
            }
            NSNotificationCenter.defaultCenter().removeObserver(self)
            assetPlayer = nil
            playerItem = nil
            urlAsset = nil
        }
    
        // MARK: - Private
    
        private func prepareToPlay() {
            let keys = ["tracks"]
            if let asset = urlAsset {
                asset.loadValuesAsynchronouslyForKeys(keys, completionHandler: {
                    dispatch_async(dispatch_get_main_queue(), {
                        self.startLoading()
                    })
                })
            }
        }
    
        private func startLoading(){
            var error:NSError?
            guard let asset = urlAsset else {return}
            let status:AVKeyValueStatus = asset.statusOfValueForKey("tracks", error: &error)
    
            if status == AVKeyValueStatus.Loaded {
                assetDuration = CMTimeGetSeconds(asset.duration)
    
                let videoOutputOptions = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)]
                videoOutput = AVPlayerItemVideoOutput(pixelBufferAttributes: videoOutputOptions)
                playerItem = AVPlayerItem(asset: asset)
    
                if let item = playerItem {
                    item.addObserver(self, forKeyPath: "status", options: .Initial, context: videoContext)
                    item.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.New, .Old], context: videoContext)
    
                    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(playerItemDidReachEnd), name: AVPlayerItemDidPlayToEndTimeNotification, object: nil)
                    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(didFailedToPlayToEnd), name: AVPlayerItemFailedToPlayToEndTimeNotification, object: nil)
    
                    if let output = videoOutput {
                        item.addOutput(output)
    
                        item.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed
                        assetPlayer = AVPlayer(playerItem: item)
    
                        if let player = assetPlayer {
                            player.rate = playerRate
                        }
    
                        addPeriodicalObserver()
                        if let playView = playerView, let layer = playView.layer as? AVPlayerLayer {
                            layer.player = assetPlayer
                            print("player created")
                        }
                    }
                }
            }        
        }
    
        private func addPeriodicalObserver() {
            let timeInterval = CMTimeMake(1, 1)
    
            if let player = assetPlayer {
                player.addPeriodicTimeObserverForInterval(timeInterval, queue: dispatch_get_main_queue(), usingBlock: { (time) in
                    self.playerDidChangeTime(time)
                })
            }
        }
    
        private func playerDidChangeTime(time:CMTime) {
            if let player = assetPlayer {
                let timeNow = CMTimeGetSeconds(player.currentTime())
                let progress = timeNow / assetDuration
    
                delegate?.didUpdateProgress(progress)
            }
        }
    
        @objc private func playerItemDidReachEnd() {
            delegate?.didFinishPlayItem()
    
            if let player = assetPlayer {
                player.seekToTime(kCMTimeZero)
                if autoRepeatPlay == true {
                    play()
                }
            }
        }
    
        @objc private func didFailedToPlayToEnd() {
            delegate?.didFailPlayToEnd()
        }
    
        private func playerDidChangeStatus(status:AVPlayerStatus) {
            if status == .Failed {
                print("Failed to load video")
            } else if status == .ReadyToPlay, let player = assetPlayer {
                volume = player.volume
                delegate?.readyToPlay()
    
                if autoPlay == true && player.rate == 0.0 {
                    play()
                }
            }
        }
    
        private func moviewPlayerLoadedTimeRangeDidUpdated(ranges:Array<NSValue>) {
            var maximum:NSTimeInterval = 0
            for value in ranges {
                let range:CMTimeRange = value.CMTimeRangeValue
                let currentLoadedTimeRange = CMTimeGetSeconds(range.start) + CMTimeGetSeconds(range.duration)
                if currentLoadedTimeRange > maximum {
                    maximum = currentLoadedTimeRange
                }
            }
            let progress:Double = assetDuration == 0 ? 0.0 : Double(maximum) / assetDuration
    
            delegate?.downloadedProgress(progress)
        }
    
        deinit {
            cleanUp()
        }
    
        private func initialSetupWithURL(url:NSURL) {
            let options = [AVURLAssetPreferPreciseDurationAndTimingKey : true]
            urlAsset = AVURLAsset(URL: url, options: options)
        }
    
        // MARK: - Observations
    
        override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
            if context == videoContext {
                if let key = keyPath {
                    if key == "status", let player = assetPlayer {
                        playerDidChangeStatus(player.status)
                    } else if key == "loadedTimeRanges", let item = playerItem {
                        moviewPlayerLoadedTimeRangeDidUpdated(item.loadedTimeRanges)
                    }
                }
            }
        }
    
    }
  3. Usage:

suppose que tu as vue

@IBOutlet private weak var playerView: PlayerView!
private var videoPlayer:VideoPlayer?

et en viewDidLoad()

    private func preparePlayer() {
        if let filePath = NSBundle.mainBundle().pathForResource("intro", ofType: "m4v") {
            let fileURL = NSURL(fileURLWithPath: filePath)
            videoPlayer = VideoPlayer(urlAsset: fileURL, view: playerView)
            if let player = videoPlayer {
                player.playerRate = 0.67
            }
        }
    }

Objectif c

PlayerView.h

    #import <AVFoundation/AVFoundation.h>
    #import <UIKit/UIKit.h>

    /*!
     @class PlayerView
     @discussion Represent View for playinv video. Layer - PlayerLayer
     @availability iOS 7 and Up
     */
    @interface PlayerView : UIView

    /*!
     @var player
     @discussion Player object
     */
    @property (strong, nonatomic) AVPlayer *player;

    @end

PlayerView.m

    #import "PlayerView.h"

    @implementation PlayerView

    #pragma mark - LifeCycle

    + (Class)layerClass
    {
        return [AVPlayerLayer class];
    }

    #pragma mark - Setter/Getter

    - (AVPlayer*)player
    {
        return [(AVPlayerLayer *)[self layer] player];
    }

    - (void)setPlayer:(AVPlayer *)player
    {
        [(AVPlayerLayer *)[self layer] setPlayer:player];
    }

    @end

VideoPlayer.h

    #import <AVFoundation/AVFoundation.h>
    #import <UIKit/UIKit.h>
    #import "PlayerView.h"

    /*!
     @protocol VideoPlayerDelegate
     @discussion Events from VideoPlayer
     */
    @protocol VideoPlayerDelegate <NSObject>

    @optional

    /*!
     @brief Called whenever time when progress of played item changed
     @param progress
     Playing progress
     */
    - (void)progressDidUpdate:(CGFloat)progress;

    /*!
     @brief Called whenever downloaded item progress changed
     @param progress
     Playing progress
     */
    - (void)downloadingProgress:(CGFloat)progress;

    /*!
     @brief Called when playing time changed
     @param time
     Playing progress
     */
    - (void)progressTimeChanged:(CMTime)time;

    /*!
     @brief Called when player finish play item
    */
    - (void)playerDidPlayItem;

    /*!
     @brief Called when player ready to play item
     */
    - (void)isReadyToPlay;

    @end

    /*!
     @class VideoPlayer
     @discussion Video Player
     @code
         self.videoPlayer = [[VideoPlayer alloc] initVideoPlayerWithURL:someURL playerView:self.playerView];
         [self.videoPlayer prepareToPlay];
         self.videoPlayer.delegate = self; //optional

         //after when required play item
         [self.videoPlayer play];
     @endcode
     */
    @interface VideoPlayer : NSObject

    /*!
     @var delegate
     @abstract Delegate for VideoPlayer
     @discussion Set object to this property for getting response and notifications from this class
     */
    @property (weak, nonatomic) id <VideoPlayerDelegate> delegate;

    /*!
     @var volume
     @discussion volume of played asset
     */
    @property (assign, nonatomic) CGFloat volume;

    /*!
     @var autoRepeat
     @discussion indicate whenever player should repeat content on finish playing
     */
    @property (assign, nonatomic) BOOL autoRepeat;

    /*!
     @brief Create player with asset URL
     @param urlAsset
     Source URL
     @result
     instance of VideoPlayer
     */
    - (instancetype)initVideoPlayerWithURL:(NSURL *)urlAsset;

    /*!
     @brief Create player with asset URL and configure selected view for showing result
     @param urlAsset
     Source URL
     @param view
     View on wchich result will be showed
     @result
     instance of VideoPlayer
     */
    - (instancetype)initVideoPlayerWithURL:(NSURL *)urlAsset playerView:(PlayerView *)view;

    /*!
     @brief Call this method after creating player to prepare player to play
    */
    - (void)prepareToPlay;

    /*!
     @brief Play item
     */
    - (void)play;
    /*!
     @brief Pause item
     */
    - (void)pause;
    /*!
     @brief Stop item
     */
    - (void)stop;

    /*!
     @brief Seek required position in item and pla if rquired
     @param progressValue
     % of position to seek
     @param isPlaying
     YES if player should start to play item implicity
     */
    - (void)seekPositionAtProgress:(CGFloat)progressValue withPlayingStatus:(BOOL)isPlaying;

    /*!
     @brief Player state
     @result
     YES - if playing, NO if not playing
     */
    - (BOOL)isPlaying;

    /*!
     @brief Indicate whenever player can provide CVPixelBufferRef frame from item
     @result
     YES / NO
     */
    - (BOOL)canProvideFrame;

    /*!
     @brief CVPixelBufferRef frame from item
     @result
     CVPixelBufferRef frame
     */
    - (CVPixelBufferRef)getCurrentFramePicture;

    @end

VideoPlayer.m

    #import "VideoPlayer.h"

    typedef NS_ENUM(NSUInteger, InternalStatus) {
        InternalStatusPreparation,
        InternalStatusReadyToPlay,
    };

    static const NSString *ItemStatusContext;

    @interface VideoPlayer()

    @property (strong, nonatomic) AVPlayer *assetPlayer;
    @property (strong, nonatomic) AVPlayerItem *playerItem;
    @property (strong, nonatomic) AVURLAsset *urlAsset;
    @property (strong, atomic) AVPlayerItemVideoOutput *videoOutput;

    @property (assign, nonatomic) CGFloat assetDuration;
    @property (strong, nonatomic) PlayerView *playerView;

    @property (assign, nonatomic) InternalStatus status;

    @end

    @implementation VideoPlayer

    #pragma mark - LifeCycle

    - (instancetype)initVideoPlayerWithURL:(NSURL *)urlAsset
    {
        if (self = [super init]) {
            [self initialSetupWithURL:urlAsset];
        }
        return self;
    }

    - (instancetype)initVideoPlayerWithURL:(NSURL *)urlAsset playerView:(PlayerView *)view
    {
        if (self = [super init]) {
            ((AVPlayerLayer *)view.layer).videoGravity = AVLayerVideoGravityResizeAspectFill;
            [self initialSetupWithURL:urlAsset playerView:view];
        }
        return self;
    }

    #pragma mark - Public

    - (void)play
    {
        if ((self.assetPlayer.currentItem) && (self.assetPlayer.currentItem.status == AVPlayerItemStatusReadyToPlay)) {
            [self.assetPlayer play];
        }
    }

    - (void)pause
    {
        [self.assetPlayer pause];
    }

    - (void)seekPositionAtProgress:(CGFloat)progressValue withPlayingStatus:(BOOL)isPlaying
    {
        [self.assetPlayer pause];
        int32_t timeScale = self.assetPlayer.currentItem.asset.duration.timescale;

        __weak typeof(self) weakSelf = self;
        [self.assetPlayer seekToTime:CMTimeMakeWithSeconds(progressValue, timeScale) completionHandler:^(BOOL finished) {
            DLog(@"SEEK To time %f - success", progressValue);
            if (isPlaying && finished) {
                [weakSelf.assetPlayer play];
            }
        }];
    }

    - (void)setPlayerVolume:(CGFloat)volume
    {
        self.assetPlayer.volume = volume > .0 ? MAX(volume, 0.7) : 0.0f;
        [self.assetPlayer play];
    }

    - (void)setPlayerRate:(CGFloat)rate
    {
        self.assetPlayer.rate = rate > .0 ? rate : 0.0f;
    }

    - (void)stop
    {
        [self.assetPlayer seekToTime:kCMTimeZero];
        self.assetPlayer.rate =.0f;
    }

    - (BOOL)isPlaying
    {
        return self.assetPlayer.rate > 0 ? YES : NO;
    }

    #pragma mark - Private

    - (void)initialSetupWithURL:(NSURL *)url
    {
        self.status = InternalStatusPreparation;
        [self setupPlayerWithURL:url];
    }

    - (void)initialSetupWithURL:(NSURL *)url playerView:(PlayerView *)view
    {
        [self setupPlayerWithURL:url];
        self.playerView = view;
    }

    - (void)setupPlayerWithURL:(NSURL *)url
    {
        NSDictionary *assetOptions = @{ AVURLAssetPreferPreciseDurationAndTimingKey : @YES };
        self.urlAsset = [AVURLAsset URLAssetWithURL:url options:assetOptions];
    }

    - (void)prepareToPlay
    {
        NSArray *keys = @[@"tracks"];
        __weak VideoPlayer *weakSelf = self;
        [weakSelf.urlAsset loadValuesAsynchronouslyForKeys:keys completionHandler:^{
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf startLoading];
            });
        }];
    }

    - (void)startLoading
    {
        NSError *error;
        AVKeyValueStatus status = [self.urlAsset statusOfValueForKey:@"tracks" error:&error];
        if (status == AVKeyValueStatusLoaded) {
            self.assetDuration = CMTimeGetSeconds(self.urlAsset.duration);
            NSDictionary* videoOutputOptions = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)};
            self.videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:videoOutputOptions];
            self.playerItem = [AVPlayerItem playerItemWithAsset: self.urlAsset];

            [self.playerItem addObserver:self
                              forKeyPath:@"status"
                                 options:NSKeyValueObservingOptionInitial
                                 context:&ItemStatusContext];
            [self.playerItem addObserver:self
                              forKeyPath:@"loadedTimeRanges"
                                 options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
                                 context:&ItemStatusContext];
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(playerItemDidReachEnd:)
                                                         name:AVPlayerItemDidPlayToEndTimeNotification
                                                       object:self.playerItem];
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(didFailedToPlayToEnd)
                                                         name:AVPlayerItemFailedToPlayToEndTimeNotification
                                                       object:nil];

            [self.playerItem addOutput:self.videoOutput];
            self.assetPlayer = [AVPlayer playerWithPlayerItem:self.playerItem];
            [self addPeriodicalObserver];
            [((AVPlayerLayer *)self.playerView.layer) setPlayer:self.assetPlayer];
            DLog(@"Player created");
        } else {
            DLog(@"The asset's tracks were not loaded:\n%@", error.localizedDescription);
        }
    }

    #pragma mark - Observation

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        BOOL isOldKey = [change[NSKeyValueChangeNewKey] isEqual:change[NSKeyValueChangeOldKey]];

        if (!isOldKey) {
            if (context == &ItemStatusContext) {
                if ([keyPath isEqualToString:@"status"] && !self.status) {
                    if (self.assetPlayer.status == AVPlayerItemStatusReadyToPlay) {
                        self.status = InternalStatusReadyToPlay;
                    }
                    [self moviePlayerDidChangeStatus:self.assetPlayer.status];
                } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
                    [self moviewPlayerLoadedTimeRangeDidUpdated:self.playerItem.loadedTimeRanges];
                }
            }
        }
    }

    - (void)moviePlayerDidChangeStatus:(AVPlayerStatus)status
    {
        if (status == AVPlayerStatusFailed) {
            DLog(@"Failed to load video");
        } else if (status == AVPlayerItemStatusReadyToPlay) {
            DLog(@"Player ready to play");
            self.volume = self.assetPlayer.volume;

            if (self.delegate && [self.delegate respondsToSelector:@selector(isReadyToPlay)]) {
                [self.delegate isReadyToPlay];
            }
        }
    }

    - (void)moviewPlayerLoadedTimeRangeDidUpdated:(NSArray *)ranges
    {
        NSTimeInterval maximum = 0;

        for (NSValue *value in ranges) {
            CMTimeRange range;
            [value getValue:&range];
            NSTimeInterval currenLoadedRangeTime = CMTimeGetSeconds(range.start) + CMTimeGetSeconds(range.duration);
            if (currenLoadedRangeTime > maximum) {
                maximum = currenLoadedRangeTime;
            }
        }
        CGFloat progress = (self.assetDuration == 0) ? 0 : maximum / self.assetDuration;
        if (self.delegate && [self.delegate respondsToSelector:@selector(downloadingProgress:)]) {
            [self.delegate downloadingProgress:progress];
        }
    }

    - (void)playerItemDidReachEnd:(NSNotification *)notification
    {
        if (self.delegate && [self.delegate respondsToSelector:@selector(playerDidPlayItem)]){
            [self.delegate playerDidPlayItem];
        }
        [self.assetPlayer seekToTime:kCMTimeZero];
        if (self.autoRepeat) {
            [self.assetPlayer play];
        }
    }

    - (void)didFailedToPlayToEnd
    {
        DLog(@"Failed play video to the end");
    }

    - (void)addPeriodicalObserver
    {
        CMTime interval = CMTimeMake(1, 1);
        __weak typeof(self) weakSelf = self;
        [self.assetPlayer addPeriodicTimeObserverForInterval:interval queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
            [weakSelf playerTimeDidChange:time];
        }];
    }

    - (void)playerTimeDidChange:(CMTime)time
    {
        double timeNow = CMTimeGetSeconds(self.assetPlayer.currentTime);
        if (self.delegate && [self.delegate respondsToSelector:@selector(progressDidUpdate:)]) {
            [self.delegate progressDidUpdate:(CGFloat) (timeNow / self.assetDuration)];
        }
    }

    #pragma mark - Notification

    - (void)setupAppNotification
    {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
    }

    - (void)didEnterBackground
    {
        [self.assetPlayer pause];
    }

    - (void)willEnterForeground
    {
        [self.assetPlayer pause];
    }

    #pragma mark - GetImagesFromVideoPlayer

    - (BOOL)canProvideFrame
    {
        return self.assetPlayer.status == AVPlayerItemStatusReadyToPlay;
    }

    - (CVPixelBufferRef)getCurrentFramePicture
    {
        CMTime currentTime = [self.videoOutput itemTimeForHostTime:CACurrentMediaTime()];
        if (self.delegate && [self.delegate respondsToSelector:@selector(progressTimeChanged:)]) {
            [self.delegate progressTimeChanged:currentTime];
        }
        if (![self.videoOutput hasNewPixelBufferForItemTime:currentTime]) {
            return 0;
        }
        CVPixelBufferRef buffer = [self.videoOutput copyPixelBufferForItemTime:currentTime itemTimeForDisplay:NULL];

        return buffer;
    }

    #pragma mark - CleanUp

    - (void)removeObserversFromPlayer
    {
        @try {
            [self.playerItem removeObserver:self forKeyPath:@"status"];
            [self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
            [[NSNotificationCenter defaultCenter] removeObserver:self];
            [[NSNotificationCenter defaultCenter] removeObserver:self.assetPlayer];        
        }
        @catch (NSException *ex) {
            DLog(@"Cant remove observer in Player - %@", ex.description);
        }
    }

    - (void)cleanUp
    {
        [self removeObserversFromPlayer];

        self.assetPlayer.rate = 0;
        self.assetPlayer = nil;
        self.playerItem = nil;
        self.urlAsset = nil;
    }

    - (void)dealloc
    {
        [self cleanUp];    
    }

    @end

L'appartenance cible de la ressource hors cause (fichier vidéo) doit être définie pour votre projet

entrez la description de l'image ici

Supplémentaire - lien vers le guide du développeur Apple parfait


La ligne layer.player = player(chez Swift's PlayerView) ne devrait-elle pas l' être layer.player = newValue? Puisque vous voulez définir la nouvelle valeur et non la valeur actuelle, qui est nulle.
Ferran Maylinch

8

Swift 3

if let filePath = Bundle.main.path(forResource: "small", ofType: ".mp4") {
    let filePathURL = NSURL.fileURL(withPath: filePath)

    let player = AVPlayer(url: filePathURL)
    let playerController = AVPlayerViewController()
    playerController.player = player
    self.present(playerController, animated: true) {
        player.play()
    }
}

5

ce code est le code converti de la réponse gbk dans swift 4

1. dans votre vue principale du contrôleur:

 if let filePath = Bundle.main.path(forResource: "clip", ofType: "mp4") {
            let fileURL = NSURL(fileURLWithPath: filePath)
            videoPlayer = VideoPlayer(urlAsset: fileURL, view: playerView)
            if let player = videoPlayer {
                player.playerRate = 1.00
            }
        }
  1. vous avez besoin de la classe VideoPlayer

    import AVFoundation
    
    import Foundation
    
    protocol VideoPlayerDelegate {
        func downloadedProgress(progress:Double)
        func readyToPlay()
        func didUpdateProgress(progress:Double)
        func didFinishPlayItem()
        func didFailPlayToEnd()
    }
    
    let videoContext:UnsafeMutablePointer<Void>? = nil
    class VideoPlayer : NSObject {
        private var assetPlayer:AVPlayer?
        private var playerItem:AVPlayerItem?
        private var urlAsset:AVURLAsset?
        private var videoOutput:AVPlayerItemVideoOutput?
    
        private var assetDuration:Double = 0
        private var playerView:PlayerView?
    
        private var autoRepeatPlay:Bool = true
        private var autoPlay:Bool = true
    
        var delegate:VideoPlayerDelegate?
    
        var playerRate:Float = 1 {
            didSet {
                if let player = assetPlayer {
                    player.rate = playerRate > 0 ? playerRate : 0.0
                }
            }
        }
    
        var volume:Float = 0 {
            didSet {
                if let player = assetPlayer {
                    player.volume = 50
                }
            }
        }
    
        // MARK: - Init
    
        convenience init(urlAsset:NSURL, view:PlayerView, startAutoPlay:Bool = true, repeatAfterEnd:Bool = true) {
            self.init()
    
            playerView = view
            autoPlay = startAutoPlay
            autoRepeatPlay = repeatAfterEnd
    
            if let playView = playerView{
                if let playerLayer = playView.layer as? AVPlayerLayer {
                    playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
                }
            }
    
            initialSetupWithURL(url: urlAsset)
            prepareToPlay()
        }
    
        override init() {
            super.init()
        }
    
        // MARK: - Public
    
        func isPlaying() -> Bool {
            if let player = assetPlayer {
                return player.rate > 0
            } else {
                return false
            }
        }
    
        func seekToPosition(seconds:Float64) {
            if let player = assetPlayer {
                pause()
                if let timeScale = player.currentItem?.asset.duration.timescale {
                    player.seek(to: CMTimeMakeWithSeconds(seconds, timeScale), completionHandler: { (complete) in
                        self.play()
                    })
                }
            }
        }
    
        func pause() {
            if let player = assetPlayer {
                player.pause()
            }
        }
    
        func play() {
            if let player = assetPlayer {
                if (player.currentItem?.status == .readyToPlay) {
                    player.play()
                    player.rate = playerRate
                }
            }
        }
    
        func cleanUp() {
            if let item = playerItem {
                item.removeObserver(self, forKeyPath: "status")
                item.removeObserver(self, forKeyPath: "loadedTimeRanges")
            }
            NotificationCenter.default.removeObserver(self)
            assetPlayer = nil
            playerItem = nil
            urlAsset = nil
        }
    
        // MARK: - Private
    
        private func prepareToPlay() {
            let keys = ["tracks"]
            if let asset = urlAsset {
    
    
    
                asset.loadValuesAsynchronously(forKeys: keys, completionHandler: {
                    DispatchQueue.global(qos: .userInitiated).async {
                        // Bounce back to the main thread to update the UI
                        DispatchQueue.main.async {
                            self.startLoading()
                        }
                    }
                })
    
            }
        }
    
        private func startLoading(){
            var error:NSError?
    
            guard let asset = urlAsset else {return}
    
    
    //         let status:AVKeyValueStatus = asset.statusOfValueForKey("tracks", error: &error)
       let status:AVKeyValueStatus = asset.statusOfValue(forKey: "tracks", error: nil)
    
    
            if status == AVKeyValueStatus.loaded {
                assetDuration = CMTimeGetSeconds(asset.duration)
    
                let videoOutputOptions = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)]
                videoOutput = AVPlayerItemVideoOutput(pixelBufferAttributes: videoOutputOptions)
                playerItem = AVPlayerItem(asset: asset)
    
                if let item = playerItem {
                    item.addObserver(self, forKeyPath: "status", options: .initial, context: videoContext)
                    item.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.new, .old], context: videoContext)
    
                    NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
                    NotificationCenter.default.addObserver(self, selector: #selector(didFailedToPlayToEnd), name:NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, object: nil)
    
    
                    if let output = videoOutput {
                        item.add(output)
    
                        item.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithm.varispeed
                        assetPlayer = AVPlayer(playerItem: item)
    
                        if let player = assetPlayer {
                            player.rate = playerRate
                        }
    
                        addPeriodicalObserver()
                        if let playView = playerView, let layer = playView.layer as? AVPlayerLayer {
                            layer.player = assetPlayer
                        }
                    }
                }
            }
        }
    
        private func addPeriodicalObserver() {
            let timeInterval = CMTimeMake(1, 1)
    
    
    
    
            if let player = assetPlayer {
                player.addPeriodicTimeObserver(forInterval: timeInterval, queue:
                   DispatchQueue.main
                    , using: { (time) in
                    self.playerDidChangeTime(time: time)
                })
            }
    
    
        }
    
        private func playerDidChangeTime(time:CMTime) {
            if let player = assetPlayer {
                let timeNow = CMTimeGetSeconds(player.currentTime())
                let progress = timeNow / assetDuration
    
                delegate?.didUpdateProgress(progress: progress)
            }
        }
    
        @objc private func playerItemDidReachEnd() {
            delegate?.didFinishPlayItem()
    
            if let player = assetPlayer {
                player.seek(to: kCMTimeZero)
                if autoRepeatPlay == true {
                    play()
                }
            }
        }
    
        @objc private func didFailedToPlayToEnd() {
            delegate?.didFailPlayToEnd()
        }
    
        private func playerDidChangeStatus(status:AVPlayerStatus) {
            if status == .failed {
                print("Failed to load video")
            } else if status == .readyToPlay, let player = assetPlayer {
                volume = player.volume
                delegate?.readyToPlay()
    
                if autoPlay == true && player.rate == 0.0 {
                    play()
                }
            }
        }
    
        private func moviewPlayerLoadedTimeRangeDidUpdated(ranges:Array<NSValue>) {
            var maximum:TimeInterval = 0
            for value in ranges {
                let range:CMTimeRange = value.timeRangeValue
                let currentLoadedTimeRange = CMTimeGetSeconds(range.start) + CMTimeGetSeconds(range.duration)
                if currentLoadedTimeRange > maximum {
                    maximum = currentLoadedTimeRange
                }
            }
            let progress:Double = assetDuration == 0 ? 0.0 : Double(maximum) / assetDuration
    
            delegate?.downloadedProgress(progress: progress)
        }
    
        deinit {
            cleanUp()
        }
    
        private func initialSetupWithURL(url:NSURL) {
            let options = [AVURLAssetPreferPreciseDurationAndTimingKey : true]
    
    
    //        urlAsset = AVURLAsset(URL: url, options: options)
            urlAsset = AVURLAsset(url: url as URL, options: options)
    
        }
    
        // MARK: - Observations
    
    
    
    
        override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?){
            if (context as? UnsafeMutablePointer<Void> ) == videoContext {
                if let key = keyPath {
                    if key == "status", let player = assetPlayer {
                        playerDidChangeStatus(status: player.status)
                    } else if key == "loadedTimeRanges", let item = playerItem {
                        moviewPlayerLoadedTimeRangeDidUpdated(ranges: item.loadedTimeRanges)
                    }
                }
            }
        }
    
    
    }
  2. et une classe PlayerView pour créer une vue vidéo:

      import AVFoundation
    
      import UIKit
    
      class PlayerView: UIView {
    
    
    override class var layerClass: AnyClass {
        get {
          return AVPlayerLayer.self
        }
    }
    
    
    var player:AVPlayer? {
        set {
            if let layer = layer as? AVPlayerLayer {
                layer.player = player
            }
        }
        get {
            if let layer = layer as? AVPlayerLayer {
                return layer.player
            } else {
                return nil
            }
        }
    }
    }

Vous avez 2 cycles de rétention dans ce code. Le premier est dans prepareToPlay, le second est addPeriodicalObserver. Vous devez passer le moi faible aux blocs.
cekisakurek

qu'est-ce que videoPlayer dans mainControllerView et où définissez-vous playerView?
Chandan Jee

@ChandanJee comme vous pouvez le voir, le lecteur vidéo est une instance de VideoPlayer. Au lieu d'utiliser une variable locale dans cette fonction, j'ai utilisé cette variable dans ma classe. si vous voulez l'utiliser dans cette fonction, veuillez écrire comme ceci: var videoPlayer: VideoPlayer ... playerView hérite de UIView. Vous pouvez créer une classe avec son nom, puis copier et coller les codes. après cela, comportez-vous comme les autres points de vue. ajoutez-le dans vos vues nib ou xib
iman kazemayni

4

PlayerView pour Swift 4.2

import AVFoundation
import UIKit

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
    }
}

1
pouvez-vous être plus précis et bref ..!
Vishal Shelake le

3

solution de gbk dans swift 3

PlayerView

import AVFoundation
import UIKit

class PlayerView: UIView {

override class var layerClass: AnyClass {
    return AVPlayerLayer.self
}

var player:AVPlayer? {
    set {
        if let layer = layer as? AVPlayerLayer {
            layer.player = player
        }
    }
    get {
        if let layer = layer as? AVPlayerLayer {
            return layer.player
        } else {
            return nil
        }
    }
}
}

Lecteur vidéo

import AVFoundation
import Foundation

protocol VideoPlayerDelegate {
    func downloadedProgress(progress:Double)
    func readyToPlay()
    func didUpdateProgress(progress:Double)
    func didFinishPlayItem()
    func didFailPlayToEnd()
}

let videoContext:UnsafeMutableRawPointer? = nil

class VideoPlayer : NSObject {



private var assetPlayer:AVPlayer?
private var playerItem:AVPlayerItem?
private var urlAsset:AVURLAsset?
private var videoOutput:AVPlayerItemVideoOutput?

private var assetDuration:Double = 0
private var playerView:PlayerView?

private var autoRepeatPlay:Bool = true
private var autoPlay:Bool = true

var delegate:VideoPlayerDelegate?

var playerRate:Float = 1 {
    didSet {
        if let player = assetPlayer {
            player.rate = playerRate > 0 ? playerRate : 0.0
        }
    }
}

var volume:Float = 1.0 {
    didSet {
        if let player = assetPlayer {
            player.volume = volume > 0 ? volume : 0.0
        }
    }
}

// MARK: - Init

convenience init(urlAsset: URL, view:PlayerView, startAutoPlay:Bool = true, repeatAfterEnd:Bool = true) {
    self.init()

    playerView = view
    autoPlay = startAutoPlay
    autoRepeatPlay = repeatAfterEnd

    if let playView = playerView, let playerLayer = playView.layer as? AVPlayerLayer {
        playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
    }
    initialSetupWithURL(url: urlAsset)
    prepareToPlay()
}

override init() {
    super.init()
}

// MARK: - Public

func isPlaying() -> Bool {
    if let player = assetPlayer {
        return player.rate > 0
    } else {
        return false
    }
}

func seekToPosition(seconds:Float64) {
    if let player = assetPlayer {
        pause()
        if let timeScale = player.currentItem?.asset.duration.timescale {
            player.seek(to: CMTimeMakeWithSeconds(seconds, timeScale), completionHandler: { (complete) in
                self.play()
            })
        }
    }
}

func pause() {
    if let player = assetPlayer {
        player.pause()
    }
}

func play() {
    if let player = assetPlayer {
        if (player.currentItem?.status == .readyToPlay) {
            player.play()
            player.rate = playerRate
        }
    }
}

func cleanUp() {
    if let item = playerItem {
        item.removeObserver(self, forKeyPath: "status")
        item.removeObserver(self, forKeyPath: "loadedTimeRanges")
    }
    NotificationCenter.default.removeObserver(self)
    assetPlayer = nil
    playerItem = nil
    urlAsset = nil
}

// MARK: - Private

private func prepareToPlay() {
    let keys = ["tracks"]
    if let asset = urlAsset {
        asset.loadValuesAsynchronously(forKeys: keys, completionHandler: {
            DispatchQueue.main.async {
                self.startLoading()
            }
        })
    }
}

private func startLoading(){
    var error:NSError?
    guard let asset = urlAsset else {return}
    let status:AVKeyValueStatus = asset.statusOfValue(forKey: "tracks", error: &error)

    if status == AVKeyValueStatus.loaded {
        assetDuration = CMTimeGetSeconds(asset.duration)

        let videoOutputOptions = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)]
        videoOutput = AVPlayerItemVideoOutput(pixelBufferAttributes: videoOutputOptions)
        playerItem = AVPlayerItem(asset: asset)

        if let item = playerItem {
            item.addObserver(self, forKeyPath: "status", options: .initial, context: videoContext)
            item.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.new, .old], context: videoContext)

            NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(didFailedToPlayToEnd), name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, object: nil)

            if let output = videoOutput {
                item.add(output)

                item.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed
                assetPlayer = AVPlayer(playerItem: item)

                if let player = assetPlayer {
                    player.rate = playerRate
                }

                addPeriodicalObserver()
                if let playView = playerView, let layer = playView.layer as? AVPlayerLayer {
                    layer.player = assetPlayer
                    print("player created")
                }
            }
        }
    }
}

private func addPeriodicalObserver() {
    let timeInterval = CMTimeMake(1, 1)

    if let player = assetPlayer {
        player.addPeriodicTimeObserver(forInterval: timeInterval, queue: DispatchQueue.main, using: { (time) in
            self.playerDidChangeTime(time: time)
        })
    }
}

private func playerDidChangeTime(time:CMTime) {
    if let player = assetPlayer {
        let timeNow = CMTimeGetSeconds(player.currentTime())
        let progress = timeNow / assetDuration

        delegate?.didUpdateProgress(progress: progress)
    }
}

@objc private func playerItemDidReachEnd() {
    delegate?.didFinishPlayItem()

    if let player = assetPlayer {
        player.seek(to: kCMTimeZero)
        if autoRepeatPlay == true {
            play()
        }
    }
}

@objc private func didFailedToPlayToEnd() {
    delegate?.didFailPlayToEnd()
}

private func playerDidChangeStatus(status:AVPlayerStatus) {
    if status == .failed {
        print("Failed to load video")
    } else if status == .readyToPlay, let player = assetPlayer {
        volume = player.volume
        delegate?.readyToPlay()

        if autoPlay == true && player.rate == 0.0 {
            play()
        }
    }
}

private func moviewPlayerLoadedTimeRangeDidUpdated(ranges:Array<NSValue>) {
    var maximum:TimeInterval = 0
    for value in ranges {
        let range:CMTimeRange = value.timeRangeValue
        let currentLoadedTimeRange = CMTimeGetSeconds(range.start) + CMTimeGetSeconds(range.duration)
        if currentLoadedTimeRange > maximum {
            maximum = currentLoadedTimeRange
        }
    }
    let progress:Double = assetDuration == 0 ? 0.0 : Double(maximum) / assetDuration

    delegate?.downloadedProgress(progress: progress)
}

deinit {
    cleanUp()
}

private func initialSetupWithURL(url: URL) {
    let options = [AVURLAssetPreferPreciseDurationAndTimingKey : true]
    urlAsset = AVURLAsset(url: url, options: options)
}

// MARK: - Observations

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if context == videoContext {
        if let key = keyPath {
            if key == "status", let player = assetPlayer {
                playerDidChangeStatus(status: player.status)
            } else if key == "loadedTimeRanges", let item = playerItem {
                moviewPlayerLoadedTimeRangeDidUpdated(ranges: item.loadedTimeRanges)
            }
        }
    }
}
}

3

Rien de ce qui précède n'a fonctionné pour moi Swift 5 for Local Video Player

après avoir lu la documentation Apple, j'ai pu créer un exemple simple pour lire une vidéo à partir de ressources locales

Voici un extrait de code

import UIKit
import AVKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //TODO : Make Sure Add and copy "SampleVideo.mp4" file in project before play
    }

    @IBAction func playLocalVideo(_ sender: Any) {

        guard let path = Bundle.main.path(forResource: "SampleVideo", ofType: "mp4") else {
            return
        }
        let videoURL = NSURL(fileURLWithPath: path)

        // Create an AVPlayer, passing it the local video url path
        let player = AVPlayer(url: videoURL as URL)
        let controller = AVPlayerViewController()
        controller.player = player
        present(controller, animated: true) {
            player.play()
        }
    }

}

PS: assurez-vous de ne pas oublier d'ajouter et de copier la vidéo nommée "SampleVideo.mp4" dans le projet


1

Voici une solution pour Swift 5.2

PlayerView.swift:

import AVFoundation
import UIKit

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
    }
}

VideoPlayer.swift

import AVFoundation
import Foundation

protocol VideoPlayerDelegate {
    func downloadedProgress(progress:Double)
    func readyToPlay()
    func didUpdateProgress(progress:Double)
    func didFinishPlayItem()
    func didFailPlayToEnd()
}

let videoContext: UnsafeMutableRawPointer? = nil

class VideoPlayer : NSObject {

    private var assetPlayer:AVPlayer?
    private var playerItem:AVPlayerItem?
    private var urlAsset:AVURLAsset?
    private var videoOutput:AVPlayerItemVideoOutput?

    private var assetDuration:Double = 0
    private var playerView:PlayerView?

    private var autoRepeatPlay:Bool = true
    private var autoPlay:Bool = true

    var delegate:VideoPlayerDelegate?

    var playerRate:Float = 1 {
        didSet {
            if let player = assetPlayer {
                player.rate = playerRate > 0 ? playerRate : 0.0
            }
        }
    }

    var volume:Float = 1.0 {
        didSet {
            if let player = assetPlayer {
                player.volume = volume > 0 ? volume : 0.0
            }
        }
    }

    // MARK: - Init

    convenience init(urlAsset:NSURL, view:PlayerView, startAutoPlay:Bool = true, repeatAfterEnd:Bool = true) {
        self.init()

        playerView = view
        autoPlay = startAutoPlay
        autoRepeatPlay = repeatAfterEnd

        if let playView = playerView, let playerLayer = playView.layer as? AVPlayerLayer {
            playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        }
        initialSetupWithURL(url: urlAsset)
        prepareToPlay()
    }

    override init() {
        super.init()
    }

    // MARK: - Public

    func isPlaying() -> Bool {
        if let player = assetPlayer {
            return player.rate > 0
        } else {
            return false
        }
    }

    func seekToPosition(seconds:Float64) {
        if let player = assetPlayer {
            pause()
            if let timeScale = player.currentItem?.asset.duration.timescale {
                player.seek(to: CMTimeMakeWithSeconds(seconds, preferredTimescale: timeScale), completionHandler: { (complete) in
                    self.play()
                })
            }
        }
    }

    func pause() {
        if let player = assetPlayer {
            player.pause()
        }
    }

    func play() {
        if let player = assetPlayer {
            if (player.currentItem?.status == .readyToPlay) {
                player.play()
                player.rate = playerRate
            }
        }
    }

    func cleanUp() {
        if let item = playerItem {
            item.removeObserver(self, forKeyPath: "status")
            item.removeObserver(self, forKeyPath: "loadedTimeRanges")
        }
        NotificationCenter.default.removeObserver(self)
        assetPlayer = nil
        playerItem = nil
        urlAsset = nil
    }

    // MARK: - Private

    private func prepareToPlay() {
        let keys = ["tracks"]
        if let asset = urlAsset {
            asset.loadValuesAsynchronously(forKeys: keys, completionHandler: {
                DispatchQueue.main.async {
                    self.startLoading()
                }
            })
        }
    }

    private func startLoading(){
        var error:NSError?
        guard let asset = urlAsset else {return}
        let status:AVKeyValueStatus = asset.statusOfValue(forKey: "tracks", error: &error)

        if status == AVKeyValueStatus.loaded {
            assetDuration = CMTimeGetSeconds(asset.duration)

            let videoOutputOptions = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)]
            videoOutput = AVPlayerItemVideoOutput(pixelBufferAttributes: videoOutputOptions)
            playerItem = AVPlayerItem(asset: asset)

            if let item = playerItem {
                item.addObserver(self, forKeyPath: "status", options: .initial, context: videoContext)
                item.addObserver(self, forKeyPath: "loadedTimeRanges", options: [.new, .old], context: videoContext)

                NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
                NotificationCenter.default.addObserver(self, selector: #selector(didFailedToPlayToEnd), name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, object: nil)

                if let output = videoOutput {
                    item.add(output)

                    item.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithm.varispeed
                    assetPlayer = AVPlayer(playerItem: item)

                    if let player = assetPlayer {
                        player.rate = playerRate
                    }

                    addPeriodicalObserver()
                    if let playView = playerView, let layer = playView.layer as? AVPlayerLayer {
                        layer.player = assetPlayer
                        print("player created")
                    }
                }
            }
        }
    }

    private func addPeriodicalObserver() {
        let timeInterval = CMTimeMake(value: 1, timescale: 1)

        if let player = assetPlayer {
            player.addPeriodicTimeObserver(forInterval: timeInterval, queue: DispatchQueue.main, using: { (time) in
                self.playerDidChangeTime(time: time)
            })
        }
    }

    private func playerDidChangeTime(time:CMTime) {
        if let player = assetPlayer {
            let timeNow = CMTimeGetSeconds(player.currentTime())
            let progress = timeNow / assetDuration

            delegate?.didUpdateProgress(progress: progress)
        }
    }

    @objc private func playerItemDidReachEnd() {
        delegate?.didFinishPlayItem()

        if let player = assetPlayer {
            player.seek(to: CMTime.zero)
            if autoRepeatPlay == true {
                play()
            }
        }
    }

    @objc private func didFailedToPlayToEnd() {
        delegate?.didFailPlayToEnd()
    }

    private func playerDidChangeStatus(status:AVPlayer.Status) {
        if status == .failed {
            print("Failed to load video")
        } else if status == .readyToPlay, let player = assetPlayer {
            volume = player.volume
            delegate?.readyToPlay()

            if autoPlay == true && player.rate == 0.0 {
                play()
            }
        }
    }

    private func moviewPlayerLoadedTimeRangeDidUpdated(ranges:Array<NSValue>) {
        var maximum:TimeInterval = 0
        for value in ranges {
            let range:CMTimeRange = value.timeRangeValue
            let currentLoadedTimeRange = CMTimeGetSeconds(range.start) + CMTimeGetSeconds(range.duration)
            if currentLoadedTimeRange > maximum {
                maximum = currentLoadedTimeRange
            }
        }
        let progress:Double = assetDuration == 0 ? 0.0 : Double(maximum) / assetDuration

        delegate?.downloadedProgress(progress: progress)
    }

    deinit {
        cleanUp()
    }

    private func initialSetupWithURL(url:NSURL) {
        let options = [AVURLAssetPreferPreciseDurationAndTimingKey : true]
        urlAsset = AVURLAsset(url: url as URL, options: options)
    }

    // MARK: - Observations
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        if context == videoContext {
            if let key = keyPath {
                if key == "status", let player = assetPlayer {
                    playerDidChangeStatus(status: player.status)
                } else if key == "loadedTimeRanges", let item = playerItem {
                    moviewPlayerLoadedTimeRangeDidUpdated(ranges: item.loadedTimeRanges)
                }
            }
        }
    }
}

Usage:

private var playerView: PlayerView = PlayerView()
private var videoPlayer:VideoPlayer?

et à l'intérieur viewDidLoad():

view.addSubview(playerView)
preparePlayer()

// set Constraints (if you do it purely in code)
playerView.translatesAutoresizingMaskIntoConstraints = false
playerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10.0).isActive = true
playerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10.0).isActive = true
playerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10.0).isActive = true
playerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 10.0).isActive = true
private func preparePlayer() {
    if let filePath = Bundle.main.path(forResource: "my video", ofType: "mp4") {
        let fileURL = NSURL(fileURLWithPath: filePath)
        videoPlayer = VideoPlayer(urlAsset: fileURL, view: playerView)
        if let player = videoPlayer {
            player.playerRate = 0.67
        }
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.