iOS

前言

	NS_CLASS_AVAILABLE(10_7, 4_0) @interface AVPlayer : NSObject 
	@available(iOS 4.0, *)		   public class AVPlayer : NSObject
	
	NS_CLASS_AVAILABLE_IOS(8_0) @interface AVPlayerViewController : UIViewController
	@available(iOS 8.0, *)		 public class AVPlayerViewController : UIViewController 
  • AVPlayer 既可以播放音乐又可以播放视频;使用 AVPlayer 不能直接显示视频,必须要加入 AVPlayerLayer 中,并添加到其他能显示的 layer 中,AVPlayer 播放界面中不带播放控件。

    • MediaPlayer 的影片是放在 UIView 里面,而 AVPlayer 是放在 AVPlayerLayer 里面,AVPlayerLayer 是 CALayer 的子类別。
    • 使用 MediaPlayer 前,要记得加入 MediaPlayer.framework 及 #import <MediaPlayer/MediaPlayer.h>
    • 使用 AVPlayer 前,要记得加入 AVFoundation.framework 及 #import <AVFoundation/AVFoundation.h>
  • MeidaPlayer 框架中的 MPMoviePlayerController 类和 MPMoviePlayerViewController 类是 iOS 中视频播放的开发相关类和方法。在 iOS8 中,iOS 开发框架中引入了一个新的视频框架 AVKit,其中提供了视频开发类 AVPlayerViewController 用于在应用中嵌入播放视频的控件。AVPlayerViewcontroller 继承自 UIViewController,一般适用于点击一个视频缩略图,modal 出一个新的界面来进行播 放的情况,AVPlayerViewcontroller 既可以播放音乐又可以播放视频,播放界面中自带播放控件。在 iOS8 中,这两个框架中的视频播放功能并无太大差异,基本都可以满足开发者的需求。iOS9 系统后,iPad Air 正式开始支持多任务与画中画的分屏功能,所谓画中画,即是用户可以将当前播放的视频缩小放在屏幕上同时进行其他应用程序的使用。这个革命性的功能将极大的方便用户的使用。于此同时,在 iOS9 中,MPMoviePlayerController 与 MPMoviePlayerViewController 类也被完全弃用,开发者使用 AVPlayerViewController 可以十分方便的实现视频播放的功能并在一些型号的 iPad 上集成画中画的功能。

  • 实现画中画要实现以下三步:

    • 1、确保当前调试版本在 9.0 以上。

    • 2、在项目 TARGETS 中的 Capabilities -> Gapabilities -> Background Mode(选项开关打开)-> 勾选 Audio, AirPlay and Picture in Picture

    • 3、设置 AVAudioSession,这两行必须设置,不然画中画不能用,如果不写模拟器中可以画中画,但是在 iPad 设备中不能。

          AVAudioSession *audioSession = [AVAudioSession sharedInstance];
          [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
      
  • 播放设置:

    • 使用 AVPlayer 播放:

      	添加库文件:AVFoundation.framework
      	包含头文件:#import <AVFoundation/AVFoundation.h>
      
    • 使用 AVPlayerViewController 播放:

      	添加库文件:AVKit.framework
      			   AVFoundation.framework
      
      	包含头文件:#import <AVKit/AVKit.h>
      			   #import <AVFoundation/AVFoundation.h>
      

1、本地/网络音视频播放

1.1 使用 AVPlayer 播放

  • Objective-C

    	添加库文件:AVFoundation.framework
    	包含头文件:#import <AVFoundation/AVFoundation.h>
    
    • 直接由 URL 创建

      	// 加载本地音乐
      	NSURL *movieUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"蓝莲花" ofType:@"mp3"]];
      	
      	// 加载本地视频
      	NSURL *movieUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"步步高手机" ofType:@"mp4"]];
      	
      	// 加载网络视频
      	NSURL *movieUrl = [NSURL URLWithString:@"http://w2.dwstatic.com/1/5/1525/127352-100-1434554639.mp4"];
         
       	// 创建 AVPlayer 播放器
      	AVPlayer *player = [AVPlayer playerWithURL:movieUrl];
       
       	// 将 AVPlayer 添加到 AVPlayerLayer 上
      	AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
      
       	// 设置播放页面大小
       	playerLayer.frame = CGRectMake(10, 30, self.view.bounds.size.width - 20, 200);
       	
       	// 设置画面缩放模式
       	playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
          
       	// 在视图上添加播放器
       	[self.view.layer addSublayer:playerLayer];
        
       	// 开始播放
       	[player play];
      
    • 由 AVPlayerItem 创建

      	// 加载本地音乐
       	NSURL *movieUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"蓝莲花" ofType:@"mp3"]];
       	
       	// 加载本地视频
       	NSURL *movieUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"步步高手机" ofType:@"mp4"]];
       	
       	// 加载网络视频
       	NSURL *movieUrl = [NSURL URLWithString:@"http://w2.dwstatic.com/1/5/1525/127352-100-1434554639.mp4"];
      
       	AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:movieUrl];
      
      	// 创建 AVPlayer 播放器
       	AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
      
      	// 将 AVPlayer 添加到 AVPlayerLayer 上
       	AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
      
      	// 设置播放页面大小
       	playerLayer.frame = CGRectMake(10, 30, self.view.bounds.size.width - 20, 200);
       	
       	// 设置画面缩放模式
       	playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
      
      	// 在视图上添加播放器
       	[self.view.layer addSublayer:playerLayer];
      
      	// 开始播放
       	[player play];
      
  • Swift

    	添加库文件:AVFoundation.framework
    	包含头文件:import AVFoundation
    
    • 直接由 URL 创建

      	// 加载本地音乐
      	let movieUrl:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("蓝莲花", ofType: "mp3")!)
      
      	// 加载本地视频
      	let movieUrl:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("步步高手机", ofType: "mp4")!)
      
      	// 加载网络视频
      	let movieUrl:NSURL = NSURL(string: "http://w2.dwstatic.com/1/5/1525/127352-100-1434554639.mp4")!
      
      	// 创建 AVPlayer 播放器
      	let player:AVPlayer = AVPlayer(URL: movieUrl)
      
      	// 将 AVPlayer 添加到 AVPlayerLayer 上
      	let playerLayer:AVPlayerLayer = AVPlayerLayer(player: player)
      
      	// 设置播放页面大小
      	playerLayer.frame = CGRectMake(10, 30, self.view.bounds.size.width - 20, 200)
      
      	// 设置画面缩放模式                                      
      	playerLayer.videoGravity = AVLayerVideoGravityResizeAspect
      
      	// 在视图上添加播放器
      	self.view.layer.addSublayer(playerLayer)
      
      	// 开始播放
      	player.play()
      
    • 由 AVPlayerItem 创建

      	// 加载本地音乐
      	let movieUrl:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("蓝莲花", ofType: "mp3")!)
      
      	// 加载本地视频
      	let movieUrl:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("步步高手机", ofType: "mp4")!)
      
      	// 加载网络视频
      	let movieUrl:NSURL = NSURL(string: "http://w2.dwstatic.com/1/5/1525/127352-100-1434554639.mp4")!
      
      	let playerItem:AVPlayerItem = AVPlayerItem(URL: movieUrl)
      	            
      	// 创建 AVPlayer 播放器
      	let player:AVPlayer = AVPlayer(playerItem: playerItem)
      
      	// 将 AVPlayer 添加到 AVPlayerLayer 上
      	let playerLayer:AVPlayerLayer = AVPlayerLayer(player: player)
      
      	// 设置播放页面大小
      	playerLayer.frame = CGRectMake(10, 30, self.view.bounds.size.width - 20, 200)
      	            
      	// 设置画面缩放模式                                     
      	playerLayer.videoGravity = AVLayerVideoGravityResizeAspect
      
      	// 在视图上添加播放器
      	self.view.layer.addSublayer(playerLayer)
      
      	// 开始播放
      	player.play()
      

1.2 使用 AVPlayerViewController 播放

  • Objective-C

    	添加库文件:AVKit.framework
    		  	   AVFoundation.framework
    
    	包含头文件:#import <AVKit/AVKit.h>
    		  	   #import <AVFoundation/AVFoundation.h>
    
    • 弹出显示页面直接开始播放

      	// 加载本地音乐
      	NSURL *movieUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"蓝莲花" ofType:@"mp3"]];
      
      	// 加载本地视频
      	NSURL *movieUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"步步高手机" ofType:@"mp4"]];
      
      	// 加载网络视频     	
      	NSURL *movieUrl = [NSURL URLWithString:@"http://w2.dwstatic.com/1/5/1525/127352-100-1434554639.mp4"];
      
      	// 创建播放控制器
      	AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];
      
      	playerViewController.player = [AVPlayer playerWithURL:movieUrl];
      
      	// 弹出播放页面
      	[self presentViewController:playerViewController animated:YES completion:^{
      
      		// 开始播放
      		[playerViewController.player play];
      	}];
      
    • 手动播放

      	// 加载本地视频	
      	NSURL *movieUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"步步高手机" ofType:@"mp4"]];  
      	
      	// 创建播放控制器  	
      	AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];                              	
      	playerViewController.player = [AVPlayer playerWithURL:movieUrl];
      
      	// 弹出播放页面,需要点击播放按钮开始播放
      	[self presentViewController:playerViewController animated:YES completion:nil];
      
  • Swift

    	添加库文件:AVKit.framework
    		       import AVFoundation
    
    
    	包含头文件:import AVKit
    		       AVFoundation.framework
    
    • 弹出显示页面直接开始播放

      	// 加载本地音乐
      	let movieUrl:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("蓝莲花", ofType: "mp3")!)
      	
      	// 加载本地视频 
      	let movieUrl:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("步步高手机", ofType: "mp4")!)
      	
      	// 加载网络视频	
      	let movieUrl:NSURL = NSURL(string: "http://w2.dwstatic.com/1/5/1525/127352-100-1434554639.mp4")!
      
      	// 创建播放控制器
      	let playerViewController:AVPlayerViewController = AVPlayerViewController()
      
      	playerViewController.player = AVPlayer(URL: movieUrl)
      
      	// 弹出播放页面
      	self.presentViewController(playerViewController, animated: true) {
      
      		// 开始播放
          	playerViewController.player?.play()
      	}
      
    • 手动播放

      	// 加载本地视频
      	let movieUrl:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("步步高手机", ofType: "mp4")!)
      	
      	// 创建播放控制器	
      	let playerViewController:AVPlayerViewController = AVPlayerViewController()
      
      	playerViewController.player = AVPlayer(URL: movieUrl)
      
      	// 弹出播放页面,需要点击播放按钮开始播放
      	self.presentViewController(playerViewController, animated: true, completion:nil)
      

2、本地/网络音视频播放设置

2.1 使用 AVPlayer 播放

  • Objective-C

    	// 在视图上添加播放器
    	/*
    		必须添加到 layer 上
    	*/
    	avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:avPlayer];
    	[self.view.layer addSublayer:avPlayerLayer];
    
    	// 设置播放页面大小
    	avPlayerLayer.frame = CGRectMake(10, 30, self.view.bounds.size.width - 20, 200);
    
    	// 设置画面缩放模式
    	/*
    		AVLayerVideoGravityResizeAspect       适应屏幕大小,保持宽高比,默认
    		AVLayerVideoGravityResizeAspectFill   充满屏幕,保持宽高比
    		AVLayerVideoGravityResize             充满屏幕,不保持宽高比
    	*/
    	avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
                
    	// 获取显示在接收视图范围内的视频图像的位置和大小
    	CGRect videoRect = avPlayerLayer.videoRect;
      	
    	// 判断是否准备好显示
    	BOOL readyForDisplay = avPlayerLayer.isReadyForDisplay;
       	
    	// 获取视频准备播放状态
    	/*
    		AVPlayerItemStatusUnknown,      状态未知
    		AVPlayerItemStatusReadyToPlay,  准备好播放
    		AVPlayerItemStatusFailed        准备失败
    	*/
    	AVPlayerItemStatus status = avPlayerItem.status;
     	
    	// 监听准备播放状态属性
    	[avPlayerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    
    	// 系统自带监听方法
    	- (void)observeValueForKeyPath:(NSString *)keyPath 
    	                      ofObject:(id)object 
    	                        change:(NSDictionary<NSString *,id> *)change 
    	                       context:(void *)context {   
    
    		if ([keyPath isEqualToString:@"status"]) {
    
    		}
    	}
    
    	// 获取视频缓冲进度
    	NSArray<NSValue *> *loadedTimeRanges = avPlayerItem.loadedTimeRanges;
      
    	CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];  // 获取缓冲区域                
    	float startSeconds = CMTimeGetSeconds(timeRange.start);
    	float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        
    	float loadedSecond = startSeconds + durationSeconds;                      // 计算缓冲总进度
        
    	// 监听缓冲进度属性
    	[avPlayerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
      
    	// 系统自带监听方法
    	- (void)observeValueForKeyPath:(NSString *)keyPath 
    	                      ofObject:(id)object 
    	                        change:(NSDictionary<NSString *,id> *)change 
    	                       context:(void *)context {   
    
    		if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
    
    		}
    	}
    
    	// 获取当前播放进度
    	/*
    		或用 avPlayerItem.currentTime.value/avPlayerItem.currentTime.timescale;
    	*/
    	CMTime currentTime = avPlayerItem.currentTime;
    	float currentSecond = CMTimeGetSeconds(currentTime);
        
    	// 监听播放进度
    	/*
    		NULL 在主线程中执行,每个一秒执行一次该 Block
    	*/
    	[avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {
    
    	}];
    
    	// 添加播放完成通知
    	[[NSNotificationCenter defaultCenter] addObserver:self 
    	                                         selector:@selector(playDidEnd:) 
    	                                             name:AVPlayerItemDidPlayToEndTimeNotification 
    	                                           object:avPlayerItem];
    
    	// 获取视频总长度
    	/*
    		转换成秒,或用 duration.value / duration.timescale; 计算
    	*/
    	CMTime duration = avPlayerItem.duration;
    	float totalSecond = CMTimeGetSeconds(duration);
    
    	// 跳转到指定位置
    	/*
    		10 / 1 = 10,跳转到第 10 秒的位置处  
    	*/
    	[avPlayerItem seekToTime:CMTimeMake(10, 1)];    
    
    	// 设置播放速率
    	/*
    		默认为 1.0 (normal speed),设为 0.0 时暂停播放。设置后立即开始播放,可放在开始播放后设置
    	*/
    	avPlayer.rate = 1.0;
    
    	// 获取当前播放速率
    	float rate = avPlayer.rate;
       	
    	// 开始播放
    	[avPlayer play];
    	
    	// 暂停播放
    	[avPlayer pause];
    
    	// 设置音量
    	/*
    		范围 0 - 1,默认为 1
    	*/
    	avPlayer.volume = 0;
    
  • Swift

    	// 在视图上添加播放器
    	/*
    		必须添加到 layer 上
    	*/
    	avPlayerLayer = AVPlayerLayer(player: avPlayer)
    	self.view.layer.addSublayer(avPlayerLayer)
    
    	// 设置播放页面大小
    	avPlayerLayer.frame = CGRectMake(10, 30, self.view.bounds.size.width - 20, 200)
    
    	// 设置画面缩放模式
    	/*
    		AVLayerVideoGravityResizeAspect       适应屏幕大小,保持宽高比,默认
    		AVLayerVideoGravityResizeAspectFill   充满屏幕,保持宽高比
    		AVLayerVideoGravityResize             充满屏幕,不保持宽高比
    	*/
    	avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
    
    	// 获取显示在接收视图范围内的视频图像的位置和大小
    	let videoRect:CGRect = avPlayerLayer.videoRect
          	
    	// 判断是否准备好显示
    	let readyForDisplay:Bool = avPlayerLayer.readyForDisplay
          	
    	// 获取视频准备播放状态
    	/*
    		case Unknown       状态未知
    		case ReadyToPlay   准备好播放
    		case Failed        准备失败
    	*/
    	let status:AVPlayerItemStatus = avPlayerItem.status
       	
    	// 监听准备播放状态属性
    	avPlayerItem.addObserver(self, forKeyPath: "status", options: .New, context: nil)
    
    	// 系统自带监听方法
    	override func observeValueForKeyPath(keyPath: String?, 
    	                             ofObject object: AnyObject?, 
    	                                      change: [String : AnyObject]?, 
    	                                     context: UnsafeMutablePointer<Void>) {
    
    		if keyPath == "status" {
    
    		}
    	}
    
    	// 获取视频缓冲进度
    	let loadedTimeRanges:[NSValue] = avPlayerItem.loadedTimeRanges
    
    	let timeRange:CMTimeRange = loadedTimeRanges.first!.CMTimeRangeValue  // 获取缓冲区域
    	let startSeconds = CMTimeGetSeconds(timeRange.start)
    	let durationSeconds = CMTimeGetSeconds(timeRange.duration)
    
    	let loadedSecond:Double = startSeconds + durationSeconds              // 计算缓冲总进度
    
    	// 监听缓冲进度属性	
    	avPlayerItem.addObserver(self, forKeyPath: "loadedTimeRanges", options: .New, context: nil)
    
    	// 系统自带监听方法
    	override func observeValueForKeyPath(keyPath: String?, 
    	                             ofObject object: AnyObject?, 
    	                                      change: [String : AnyObject]?, 
    	                                     context: UnsafeMutablePointer<Void>) {
    
    		if keyPath == "loadedTimeRanges" {
    
    		}
    	}
    
    	// 获取当前播放进度
    	/*
    		或用 avPlayerItem.currentTime.value/avPlayerItem.currentTime.timescale;
    	*/
    	let currentTime:CMTime = self.avPlayerItem.currentTime()
    	let currentSecond = CMTimeGetSeconds(currentTime)
    
    	// 监听播放进度	
    	/*
    		nil 在主线程中执行,每个一秒执行一次该 Block
    	*/	
    	avPlayer.addPeriodicTimeObserverForInterval(CMTimeMake(1, 1), queue: nil) { (time:CMTime) in
    
    	}
    
    	// 添加播放完成通知
    	NSNotificationCenter.defaultCenter().addObserver(self, 
    	                                        selector: #selector(AvPlayer.playDidEnd(_:)), 
    	                                            name: AVPlayerItemDidPlayToEndTimeNotification, 
    	                                          object: avPlayerItem)
    
    	// 获取视频总长度
    	/*
    		转换成秒,或用 duration.value / duration.timescale; 计算
    	*/
    	let duration:CMTime = avPlayerItem.duration
    	let totalSecond:Double = CMTimeGetSeconds(duration)
    
    	// 跳转到指定位置
    	/*
    		10 / 1 = 10,跳转到第 10 秒的位置处 
    	*/
    	avPlayerItem.seekToTime(CMTimeMake(10, 1))
    
    	// 设置播放速率
    	/*
    		默认为 1.0 (normal speed),设为 0.0 时暂停播放。设置后立即开始播放,可放在开始播放后设置
    	*/
    	avPlayer.rate = 1.0
        
    	// 获取当前播放速率
    	let rate:Float = avPlayer.rate
    
    	// 开始播放
    	avPlayer.play()
        
    	// 暂停播放
    	avPlayer.pause()
        
    	// 设置音量
    	/*
    		范围 0 - 1,默认为 1
    	*/
    	avPlayer.volume = 0
    

2.2 使用 AVPlayerViewController 播放

  • Objective-C

    	// 显示播放页面
    	[self presentViewController:avPlayerVC animated:YES completion:nil];
    
    	// 设置画面缩放模式
    	/*
    		AVLayerVideoGravityResizeAspect       适应屏幕大小,保持宽高比,默认
    		AVLayerVideoGravityResizeAspectFill   充满屏幕,保持宽高比
    		AVLayerVideoGravityResize             充满屏幕,不保持宽高比
    	*/
    	avPlayerVC.videoGravity = AVLayerVideoGravityResizeAspect;
    
    	// 获取显示在接收视图范围内的视频图像的位置和大小
    	CGRect videoBounds = avPlayerVC.videoBounds;
    	
    	// 获取播放控件所在的视图
    	/*
    		播放控件与播放内容界面之间的叠加视图
    	*/
    	UIView *contentOverlayView = avPlayerVC.contentOverlayView;
    
    	// 设置是否显示播放控件
    	/*
    		Default is YES
    	*/
    	avPlayerVC.showsPlaybackControls = YES;
    
    	// 设置是否允许画中画播放
    	/*
    		Default is YES
    	*/
    	avPlayerVC.allowsPictureInPicturePlayback = YES;
    
    	// 判断是否准备好显示
    	BOOL readyForDisplay = avPlayerVC.isReadyForDisplay;
    
    	// 设置画中画播放代理,需遵守协议 <AVPlayerViewControllerDelegate>
    	avPlayerVC.delegate = self;
    
    	// 实现画中画播放
    	/*
    		这两行必须设置,不然画中画不能用,如果不写模拟器中可以画中画,但是在设备中不能
    	*/
    	AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    	[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
    
    	// 获取视频准备播放状态
    	/*
    		AVPlayerItemStatusUnknown,      状态未知
    		AVPlayerItemStatusReadyToPlay,  准备好播放
    		AVPlayerItemStatusFailed        准备失败
    	*/
    	AVPlayerItemStatus status = avPlayerVC.player.currentItem.status;
      	  	
    	// 监听准备播放状态属性
    	[avPlayerVC.player.currentItem addObserver:self 
    	                                forKeyPath:@"status" 
    	                                   options:NSKeyValueObservingOptionNew 
    	                                   context:nil];
    
    	// 系统自带监听方法
    	- (void)observeValueForKeyPath:(NSString *)keyPath 
    	                      ofObject:(id)object 
    	                        change:(NSDictionary<NSString *,id> *)change 
    	                       context:(void *)context {   
    
    		if ([keyPath isEqualToString:@"status"]) {
    
    		}
    	}
    
    	// 获取视频缓冲进度
    	NSArray<NSValue *> *loadedTimeRanges = avPlayerVC.player.currentItem.loadedTimeRanges;
        
    	CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];  // 获取缓冲区域
    	float startSeconds = CMTimeGetSeconds(timeRange.start);
    	float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        
    	float loadedSecond = startSeconds + durationSeconds;                      // 计算缓冲总进度
        
    	// 监听缓冲进度属性	
    	[avPlayerVC.player.currentItem addObserver:self 
    	                                forKeyPath:@"loadedTimeRanges" 
    	                                   options:NSKeyValueObservingOptionNew 
    	                                   context:nil];
    
    	// 系统自带监听方法
    	- (void)observeValueForKeyPath:(NSString *)keyPath 
    	                      ofObject:(id)object 
    	                        change:(NSDictionary<NSString *,id> *)change 
    	                       context:(void *)context {   
    
    		if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
    
    		}
    	}
    
    	// 获取当前播放进度
    	/*
    		或用 avPlayerItem.currentTime.value/avPlayerItem.currentTime.timescale;
    	*/
    	CMTime currentTime = avPlayerVC.player.currentItem.currentTime;
    	float currentSecond = CMTimeGetSeconds(currentTime);
        	
    	// 监听播放进度
    	/*
    		NULL 在主线程中执行,每个一秒执行一次该 Block
    	*/
    	[avPlayerVC.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {
    
    	}];
    
    	// 添加播放完成通知
    	[[NSNotificationCenter defaultCenter] addObserver:self 
    	                                         selector:@selector(playDidEnd:) 
    	                                             name:AVPlayerItemDidPlayToEndTimeNotification 
    	                                           object:avPlayerVC.player.currentItem];
    
    	// 获取视频总长度
    	/*
    		转换成秒,或用 duration.value / duration.timescale; 计算
    	*/
    	CMTime duration = avPlayerVC.player.currentItem.duration;
    	float totalSecond = CMTimeGetSeconds(duration);
    
    	// 跳转到指定位置
    	/*
    		10 / 1 = 10,跳转到第 10 秒的位置处 
    	*/
    	[avPlayerVC.player.currentItem seekToTime:CMTimeMake(10, 1)];
    
    	// 设置播放速率
    	/*
    		默认为 1.0 (normal speed),设为 0.0 时暂停播放。设置后立即开始播放,可放在开始播放后设置
    	*/
    	avPlayerVC.player.rate = 1.0;
    
    	// 获取当前播放速率
    	float rate = avPlayerVC.player.rate;
    
    	// 开始播放
    	[avPlayerVC.player play];
        
    	// 暂停播放
    	[avPlayerVC.player pause];
        
    	// 设置音量
    	/*
    		范围 0 - 1,默认为 1
    	*/
    	avPlayerVC.player.volume = 0;
    
  • Swift

    	// 显示播放页面
    	self.presentViewController(avPlayerVC, animated: true, completion:nil)
    
    	// 设置画面缩放模式
    	/*
    		AVLayerVideoGravityResizeAspect      适应屏幕大小,保持宽高比,默认
    		AVLayerVideoGravityResizeAspectFill  充满屏幕,保持宽高比
    		AVLayerVideoGravityResize            充满屏幕,不保持宽高比
    	*/
    	avPlayerVC.videoGravity = AVLayerVideoGravityResizeAspect
    
    	// 获取显示在接收视图范围内的视频图像的位置和大小
    	let videoBounds:CGRect = avPlayerVC.videoBounds
       	
    	// 获取播放控件所在的视图
    	/*
    		播放控件与播放内容界面之间的叠加视图
    	*/
    	let contentOverlayView:UIView = avPlayerVC.contentOverlayView!
       	
    	// 设置是否显示播放控件
    	/*
    		Default is YES
    	*/
    	avPlayerVC.showsPlaybackControls = true
    
    	// 设置是否允许画中画播放
    	/*
    		Default is YES
    	*/
    	avPlayerVC.allowsPictureInPicturePlayback = true
    
    	// 判断是否准备好显示
    	let readyForDisplay:Bool = avPlayerVC.readyForDisplay
       	
    	// 设置画中画播放代理,需遵守协议 <AVPlayerViewControllerDelegate>
    	avPlayerVC.delegate = self
    
    	// 实现画中画播放
    	/*
    		这两行必须设置,不然画中画不能用,如果不写模拟器中可以画中画,但是在设备中不能
    	*/
    	let audioSession:AVAudioSession = AVAudioSession.sharedInstance()
    	try! audioSession.setCategory(AVAudioSessionCategoryPlayback)
    
    	// 获取视频准备播放状态
    	/*
    		AVPlayerItemStatusUnknown,      状态未知
    		AVPlayerItemStatusReadyToPlay,  准备好播放
    		AVPlayerItemStatusFailed        准备失败
    	*/
    	let status:AVPlayerItemStatus = avPlayerVC.player!.currentItem!.status
        	
    	// 监听准备播放状态属性
    	avPlayerVC.player!.currentItem!.addObserver(self, forKeyPath: "status", options: .New, context: nil)
    
    	// 系统自带监听方法
    	override func observeValueForKeyPath(keyPath: String?, 
    	                             ofObject object: AnyObject?, 
    	                                      change: [String : AnyObject]?, 
    	                                     context: UnsafeMutablePointer<Void>) {
    
    		if keyPath == "status" {
    
    		}
    	}
    
    	// 获取视频缓冲进度
    	let loadedTimeRanges:[NSValue] = avPlayerVC.player!.currentItem!.loadedTimeRanges
        
    	let timeRange:CMTimeRange = loadedTimeRanges.first!.CMTimeRangeValue   // 获取缓冲区域
    	let startSeconds = CMTimeGetSeconds(timeRange.start)
    	let durationSeconds = CMTimeGetSeconds(timeRange.duration)
        
    	let loadedSecond:Double = startSeconds + durationSeconds               // 计算缓冲总进度
        
    	// 监听缓冲进度属性	
    	avPlayerVC.player!.currentItem!.addObserver(self, forKeyPath: "loadedTimeRanges", options: .New, context: nil)
    
    	// 系统自带监听方法
    	override func observeValueForKeyPath(keyPath: String?, 
    	                             ofObject object: AnyObject?, 
    	                                      change: [String : AnyObject]?, 
    	                                     context: UnsafeMutablePointer<Void>) {
    
    		if keyPath == "loadedTimeRanges" {
    
    		}
    	}
    
    	// 获取当前播放进度
    	let currentTime:CMTime = self.avPlayerVC.player!.currentItem!.currentTime()
    	let currentSecond = CMTimeGetSeconds(currentTime)
        
    	// 监听播放进度
    	avPlayerVC.player?.addPeriodicTimeObserverForInterval(CMTimeMake(1, 1), queue: nil, usingBlock: { (time:CMTime) in
    	
    	})
    
    	// 添加播放完成通知
    	NSNotificationCenter.defaultCenter().addObserver(self, 
    	                                        selector: #selector(AvPlayer.playDidEnd(_:)), 
    	                                            name: AVPlayerItemDidPlayToEndTimeNotification, 
    	                                          object: avPlayerVC.player!.currentItem!)
    
    	// 获取视频总长度
    	/*
    		转换成秒,或用 duration.value / duration.timescale; 计算
    	*/
    	let duration:CMTime = avPlayerVC.player!.currentItem!.duration
    	let totalSecond = CMTimeGetSeconds(duration)
    
    	// 跳转到指定位置
    	/*
    		10 / 1 = 10,跳转到第 10 秒的位置处 
    	*/
    	avPlayerVC.player!.currentItem!.seekToTime(CMTimeMake(10, 1))
    
    	// 设置播放速率
    	/*
    		默认为 1.0 (normal speed),设为 0.0 时暂停播放。设置后立即开始播放,可放在开始播放后设置
    	*/
    	avPlayerVC.player!.rate = 1.0
        
    	// 获取当前播放速率
    	let rate = avPlayerVC.player!.rate
          	
    	// 开始播放
    	avPlayerVC.player!.play()
        
    	// 暂停播放
    	avPlayerVC.player!.pause()
    
    	// 设置音量
    	/*
    		范围 0 - 1,默认为 1
    	*/
    	avPlayerVC.player!.volume = 0
    

3、AVPlayerViewControllerDelegate 画中画协议方法

  • Objective-C

    	// 将要开始画中画播放
    	- (void)playerViewControllerWillStartPictureInPicture:(AVPlayerViewController *)playerViewController {
        	
    	}
    
    	// 已经开始画中画播放
    	- (void)playerViewControllerDidStartPictureInPicture:(AVPlayerViewController *)playerViewController {
        	
    	}
    
    	// 画中画播放失败
    	- (void)playerViewController:(AVPlayerViewController *)playerViewController failedToStartPictureInPictureWithError:(NSError *)error {
        	
    	}
    
    	// 将要结束画中画播放
    	- (void)playerViewControllerWillStopPictureInPicture:(AVPlayerViewController *)playerViewController {
        	
    	}
    
    	// 已经结束画中画播放
    	- (void)playerViewControllerDidStopPictureInPicture:(AVPlayerViewController *)playerViewController {
        	
    	}
    
    	// 画中画播放开始时播放控制器界面是否自动退出
    	- (BOOL)playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart:(AVPlayerViewController *)playerViewController {
        
        	// 是否在开始画中画时自动将当前的播放界面 dismiss 掉,返回 YES 则自动 dismiss,返回 NO 则不会自动 dismiss
    		return NO;
    	}
    
    	// 画中画停止之前恢复用户界面
    	- (void)playerViewController:(AVPlayerViewController *)playerViewController restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:(void (^)(BOOL 			restored))completionHandler {
    
    		// 用户点击还原按钮,从画中画模式还原回 App 内嵌模式时调用的方法
    	}
    
  • Swift

    	// 将要开始画中画播放
    	func playerViewControllerWillStartPictureInPicture(playerViewController: AVPlayerViewController) {
    
    	}
    
    	// 已经开始画中画播放
    	func playerViewControllerDidStartPictureInPicture(playerViewController: AVPlayerViewController) {
    
    	}
    
    	// 画中画播放失败
    	func playerViewController(playerViewController: AVPlayerViewController, failedToStartPictureInPictureWithError error: NSError) {
    
    	}
    
    	// 将要结束画中画播放
    	func playerViewControllerWillStopPictureInPicture(playerViewController: AVPlayerViewController) {
    
    	}
    
    	// 已经结束画中画播放
    	func playerViewControllerDidStopPictureInPicture(playerViewController: AVPlayerViewController) {
    
    	}
    
    	// 画中画播放开始时播放控制器界面是否自动退出
    	func playerViewControllerShouldAutomaticallyDismissAtPictureInPictureStart(playerViewController: AVPlayerViewController) -> Bool {
    
    		// 是否在开始画中画时自动将当前的播放界面 dismiss 掉,返回 YES 则自动 dismiss,返回 NO 则不会自动 dismiss
    		return false
    	}
    
    	// 画中画停止之前恢复用户界面
    	func playerViewController(playerViewController: AVPlayerViewController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: (Bool) 		-> Void) {
    
    		// 用户点击还原按钮,从画中画模式还原回 App 内嵌模式时调用的方法
    	}
    
原文地址:https://www.cnblogs.com/QianChia/p/5771172.html