Swiftでローカルビデオを再生する方法


131

mp4現在のXcode6 Betaプロジェクトに追加した短いビデオファイルがあります。

アプリで動画を再生したい。

何時間も検索しても、リモートで役立つ情報は見つかりません。Swiftでこれを達成する方法はありますか、またはObjective-Cを使用する必要がありますか?正しい方向に向けることができますか?これを考えているのは私だけではありません。


3
あなたはココアでこれを行うことができるので、あなたはスウィフトでそれを行うことができます
アビゼル14

回答:


272

もちろん、Swiftを使用できます。

1.ビデオファイルの追加

video.m4vXcodeプロジェクトにビデオを追加します(これをと呼びます)

2.ビデオがバンドルに含まれていることを確認する

Project Navigator cmd+を開く1

そして、選択your project root> your Target> Build Phases> Copy Bundle Resources

あなたのビデオここになければなりません。そうでない場合は、プラスボタンを使用して追加する必要があります

ここに画像の説明を入力してください

3.コード

View Controllerを開き、このコードを記述します。

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
このソリューションの最も重要な部分は、リソースをプロジェクトにコピーするときに、フィールド「ターゲットに追加」->プロジェクト名をチェックすることです。この解決策が私にとってうまくいったおかげで..
Pramod '21年

2
MPMoviePlayerControllerクラスはiOS 9では正式に非推奨になりました(MPMoviePlayerViewControllerクラスも正式に非推奨になりました)。iOS9以降でビデオコンテンツを再生するには、代わりにAVKitフレームワークのAVPictureInPictureControllerまたはAVPlayerViewControllerクラス、またはWebKitのWKWebViewクラスを使用します。 アップルリファレンスガイド
Henry Glendening '20 / 09/15


最新のxcodeベータ7.2でこれに問題があるinitializer for conditional binding must have optional type not nsurl
Brian Wigginton

1
@mobibobもちろんAVPlayerViewController が存在します:-)しましたかimport AVKit
Luca Angeletti、2017

26

別のSwift 3の例。提供されたソリューションは私にとってはうまくいきませんでした。

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

使用法:

playVideo(from: "video.extension")

注:チェックのコピーバンドルリソースを下のビルドフェーズのビデオプロジェクトに利用可能であることを確実にするために。


このソリューションは他のレイヤーと一緒に機能し、投票した多数のソリューションよりも優れたソリューションです。
マイク・リー

13

UはAVPlayer別の方法でセットアップでき、ビデオプレーヤー画面を完全にカスタマイズできます

Swift 2.3

  1. UIViewビデオを再生するためのサブクラスを作成します(基本的にuは任意のUIViewオブジェクトを使用でき、必要AVPlayerLayerなのはだけです。私にはこの方法で設定した方がわかりやすいためです)。

    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. プレーヤーをセットアップする

    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. 使用法:

あなたが見ていると仮定します

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

そして 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
            }
        }
    }

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

オフ原因リソース(ビデオファイル)には、プロジェクトに設定されたターゲットメンバーシップが必要です

ここに画像の説明を入力してください

追加- 完璧なApple開発者ガイドへのリンク


layer.player = player(スウィフトのPlayerView)ラインはそうではありませんlayer.player = newValueか?現在の値ではなく新しい値を設定したいので、nilです。
Ferran Maylinch 2017

8

スウィフト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

これらのコードは、swift 4のgbk answerから変換されたコードです

1. main controllviewで:

 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. あなたは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. PlayerViewクラスでビデオビューを作成します。

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

このコードには2つの保持サイクルがあります。1つ目はprepareToPlayにあり、2つ目はaddPeriodicalObserverです。弱者をブロックに渡す必要があります。
cekisakurek

mainControllerViewのvideoPlayerとは何ですか。
Chandan Jee

@ChandanJeeご覧のとおり、ビデオプレーヤーはVideoPlayerのインスタンスです。その関数でローカル変数を使用する代わりに、この変数をクラスで使用しました。その関数で使用する場合は、次のように記述してください:var videoPlayer:VideoPlayer ... playerViewはUIViewから継承します。その名前でクラスを作成し、コードをコピーして貼り付けることができます。その後、他のビューと同じように動作します。nibまたはxibビューに追加
iman kazemayni

4

PlayerView for 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
もっと具体的かつ簡潔にできますか?
Vishal Shelake

3

Swift 3のgbkのソリューション

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

VideoPlayer

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

上記のどれも私にとってはうまくいきませんでしたSwift 5 for Local Video Player

アップルのドキュメントを読んだ後、ローカルリソースからビデオを再生するための簡単な例を作成することができました

これがコードスニップです

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:プロジェクトに「SampleVideo.mp4」という名前のビデオを追加してコピーすることを忘れないでください


1

ここで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)
                }
            }
        }
    }
}

使用法:

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

そして中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
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.