需求设计
做一个小学生教育辅导视频播放器。
参考小猿搜题视频播放器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L0GsyFSt-1675164972791)(https://tva1.sinaimg.cn/large/008vxvgGgy1h9xk4fm5xfj31sx0u0mz0.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XZ7e0Z3Z-1675164972792)(https://tva1.sinaimg.cn/large/008vxvgGgy1h9xk4fm5xfj31sx0u0mz0.jpg)]
主要功能:
- 非VIP用户免费播放开头部分;
- 截屏;
- 倍速播放;
- 进度条快进快退;
实现原理
- 公开属性
#import "WMPlayer.h"
@property (nonatomic, retain) WMPlayerModel *playerModel;
@property (nonatomic, strong) WMPlayer *wmPlayer;
- 实例化播放器
//self.wmPlayer = [[WMPlayer alloc] initWithFrame:CGRectMake(0, [WMPlayer IsiPhoneX]?34:0, self.view.frame.size.width, self.view.frame.size.width*(9.0/16))];self.wmPlayer = [[WMPlayer alloc] initWithFrame:CGRectMake(0, 44, self.view.frame.size.width, self.view.frame.size.height-88)];self.wmPlayer.delegate = self;self.wmPlayer.playerModel = self.playerModel;[self.view addSubview:self.wmPlayer];[self.wmPlayer play];//旋转屏幕通知[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(onDeviceOrientationChange:)name:UIDeviceOrientationDidChangeNotificationobject:nil];
- VC生命周期
#pragma mark - Life Cycle- (void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];[self.navigationController setNavigationBarHidden:YES animated:NO];self.view.frame = UIScreen.mainScreen.bounds;self.wmPlayer.delegate = self;
}-(void)viewDidDisappear:(BOOL)animated{[super viewDidAppear:animated];[self.navigationController setNavigationBarHidden:NO animated:NO];
}- (void)dealloc{[self.wmPlayer pause];[self.wmPlayer removeFromSuperview];self.wmPlayer = nil;[[NSNotificationCenter defaultCenter] removeObserver:self];NSLog(@"DetailViewController dealloc");
}
- 播放器代理
#pragma mark - WMPlayerDelegate
///播放器CloseButton
-(void)wmplayer:(WMPlayer *)wmplayer clickedCloseButton:(UIButton *)closeBtn{if (wmplayer.isFullscreen) {[self exitFullScreen];}else{if (self.presentingViewController) {[self dismissViewControllerAnimated:YES completion:^{}];}else{[self.navigationController popViewControllerAnimated:YES];}}
}
///全屏按钮
-(void)wmplayer:(WMPlayer *)wmplayer clickedFullScreenButton:(UIButton *)fullScreenBtn{if (self.wmPlayer.viewState == PlayerViewStateSmall) {[self enterFullScreen];}
}-(void)enterFullScreen{if (self.wmPlayer.viewState != PlayerViewStateSmall) {return;}LandscapeRightViewController *rightVC = [[LandscapeRightViewController alloc] init];[self presentToVC:rightVC];
}-(void)exitFullScreen{if (self.wmPlayer.viewState!=PlayerViewStateFullScreen) {return;}self.wmPlayer.isFullscreen = NO;self.wmPlayer.viewState = PlayerViewStateAnimating;[self dismissViewControllerAnimated:YES completion:^{self.wmPlayer.viewState = PlayerViewStateSmall;}];
}
- 旋转屏幕通知
#pragma mark - NSNotificationCenter
/*** 旋转屏幕通知*/
- (void)onDeviceOrientationChange:(NSNotification *)notification{if (self.wmPlayer.viewState!=PlayerViewStateSmall) {return;}if (self.wmPlayer.isLockScreen){return;}UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;switch (interfaceOrientation) {case UIInterfaceOrientationPortraitUpsideDown:{}break;case UIInterfaceOrientationPortrait:{}break;case UIInterfaceOrientationLandscapeLeft:{[self presentToVC:[LandscapeLeftViewController new]];}break;case UIInterfaceOrientationLandscapeRight:{[self presentToVC:[LandscapeRightViewController new]];}break;default:break;}
}-(void)presentToVC:(FullScreenHelperViewController *)aHelperVC{self.wmPlayer.viewState = PlayerViewStateAnimating;self.wmPlayer.beforeBounds = self.wmPlayer.bounds;self.wmPlayer.beforeCenter = self.wmPlayer.center;self.wmPlayer.parentView = self.wmPlayer.superview;self.wmPlayer.isFullscreen = YES;aHelperVC.wmPlayer = self.wmPlayer;aHelperVC.modalPresentationStyle = UIModalPresentationFullScreen;aHelperVC.transitioningDelegate = self;[self presentViewController:aHelperVC animated:YES completion:^{self.wmPlayer.viewState = PlayerViewStateFullScreen;}];
}
- 调用播放器
VideoDataModel *videoModel = self.videoDataAry[indexPath.row];WMPlayerModel *playerModel = [WMPlayerModel new];playerModel.videoURL = [NSURL URLWithString:videoModel.video_url];//playerModel.videoURL = [NSURL URLWithString:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"];//playerModel.videoURL = [NSURL URLWithString:@"http://img.zhuoqi.tech/test_h264_level30_480_360.mp4"];playerModel.title = videoModel.nickname;DetailViewController *detailVC = [DetailViewController new];detailVC.playerModel = playerModel;[self.navigationController pushViewController:detailVC animated:YES];
基本概念
一个在线视频能够播放,大致是经过了如下步骤:
HLS(Http Live Streaming)
HLS是苹果推出,实现的基于HTTP的流媒体传输协议:
优点:
1、通过m3u8索引文件可实现针对当前浏览设备的智能选择播放源
2、通过m3u8索引文件可实现添加备份索引文件,防止服务器崩溃视频播放失败
3、和http视频一样不需要太多服务器额外配置
缺点:
1、并非真正实时视频,30s左右时间差
2、需要视频处理
3、因为需要请求索引文件(ts视频文件)请求次数相对较多,对服务器负载较大
AVPlayer支持哪些视频格式
苹果设备支持音视频格式并不是就代表AVPlayer也支持那么多格式,确定AVPlayer的支持格式,我们可以查看AVKit中的一个API:
//展示当前支持的音视频格式
let asset = AVURLAsset.audiovisualTypes()
//打印asset可以得到(已经转过展示格式)
asset type ("audio/aacp","video/3gpp2","audio/mpeg3","audio/mp3","audio/x-caf","audio/mpeg","video/quicktime","audio/x-mpeg3","video/mp4","audio/wav","video/avi","audio/scpls","audio/mp4","audio/x-mpg","video/x-m4v","audio/x-wav","audio/x-aiff","application/vnd.apple.mpegurl","video/3gpp","text/vtt","audio/x-mpeg","audio/wave","audio/x-m4r","audio/x-mp3","audio/AMR","audio/aiff","audio/3gpp2","audio/aac","audio/mpg","audio/mpegurl","audio/x-m4b","application/mp4","audio/x-m4p","audio/x-scpls","audio/x-mpegurl","audio/x-aac","audio/3gpp","audio/basic","audio/x-m4a","application/x-mpegurl"
)
AVPlayer支持的
视频编码格式:H.264、HEVC(iPhone7及以后设备)、MPEG-4。
视频封装格式:.mp4、.mov、.m4v、.3gp、.avi等。
如果想支持更多的视频格式,可以使用使用第三方的框架,常用的视频编码和解码框架有VLC和ffmpeg。
AVPlayerItem的控制
AVPlayerItem
作为资源管理对象,它控制着视频从创建到销毁的诸多状态。
播放状态 status
typedef NS_ENUM(NSInteger,AVPlayerItemStatus) {AVPlayerItemStatusUnknown,//未知AVPlayerItemStatusReadyToPlay,//准备播放AVPlayerItemStatusFailed//播放失败
};
我们使用KVO监测playItem.status,可以获取播放状态的变化
[self.playerItem addObserver:selfforKeyPath:@"status"options:NSKeyValueObservingOptionNewcontext:nil];
在监听回调中:
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context{if([object isKindOfClass:[AVPlayerItemclass]]) {if([keyPath isEqualToString:@"status"]) {switch(_playerItem.status) {caseAVPlayerItemStatusReadyToPlay://推荐将视频播放放这里[self play];break;caseAVPlayerItemStatusUnknown:NSLog(@"AVPlayerItemStatusUnknown");break;caseAVPlayerItemStatusFailed:NSLog(@"AVPlayerItemStatusFailed");break;default:break;}}}
}
虽然设置完播放配置我们可以直接调用[self.player play];
进行播放,但是更稳妥的方法是在回调收到AVPlayerItemStatusReadyToPlay
时进行播放。
参考文章
HTTP Streaming Architecture
HTTP Live Streaming
WMPlayer
SJVideoPlayer
DouYin
TBPlayer
iOS视频播放器开发
iOS视频播放的基本方法
上传到阿里云OSS的视频如何实现在线播放
ZFPlayer 3.0解析
AVPlayer支持的视频格式
iOS音视频播放指南(一)
iOS音视频开发学习指南
iOS视频开发(一):视频采集