扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
序言
巴青ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为创新互联建站的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:028-86922220(备注:SSL证书合作)期待与您的合作!微信现在这么普及,功能也做的越来越强大,不知大家对于微信朋友圈发视频截取的功能或者苹果拍视频对视频编辑的功能有没有了解(作者这里也猜测,微信的这个功能也是仿苹果的)。感觉这个功能确实很方便实用,近来作者也在研究音视频功能,所以就实现了一下这个功能。
功能其实看着挺简单,实现过程也踩了不少坑。一方面记录一下;另一方面也算是对实现过程的再一次梳理,这样大家看代码也会比较明白。
效果
我们先看看我实现的效果
实现
实现过程分析
整个功能可以分为三部分:
这部分我们单独封装一个视频播放器即可
这部分实现过程比较复杂,一共分成了4部分。灰色遮盖、左右把手滑块、滑块中间上下两条线、图片管理视图
控制器视图逻辑组装和功能实现
这里使用AVPlayer、playerLayer、AVPlayerItem这三个类实现了视频播放功能;由于整个事件都是基于KVO监听的,所以增加了Block代码提供了对外监听使用。
#import "FOFMoviePlayer.h" @interface FOFMoviePlayer() { AVPlayerLooper *_playerLooper; AVPlayerItem *_playItem; BOOL _loop; } @property(nonatomic,strong)NSURL *url; @property(nonatomic,strong)AVPlayer *player; @property(nonatomic,strong)AVPlayerLayer *playerLayer; @property(nonatomic,strong)AVPlayerItem *playItem; @property (nonatomic,assign) CMTime duration; @end @implementation FOFMoviePlayer -(instancetype)initWithFrame:(CGRect)frame url:(NSURL *)url superLayer:(CALayer *)superLayer{ self = [super init]; if (self) { [self initplayers:superLayer]; _playerLayer.frame = frame; self.url = url; } return self; } -(instancetype)initWithFrame:(CGRect)frame url:(NSURL *)url superLayer:(CALayer *)superLayer loop:(BOOL)loop{ self = [self initWithFrame:frame url:url superLayer:superLayer]; if (self) { _loop = loop; } return self; } - (void)initplayers:(CALayer *)superLayer{ self.player = [[AVPlayer alloc] init]; self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; self.playerLayer.videoGravity = AVLayerVideoGravityResize; [superLayer addSublayer:self.playerLayer]; } - (void)initLoopPlayers:(CALayer *)superLayer{ self.player = [[AVQueuePlayer alloc] init]; self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; self.playerLayer.videoGravity = AVLayerVideoGravityResize; [superLayer addSublayer:self.playerLayer]; } -(void)fof_play{ [self.player play]; } -(void)fof_pause{ [self.player pause]; } #pragma mark - Observe -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context{ if ([keyPath isEqualToString:@"status"]) { AVPlayerItem *item = (AVPlayerItem *)object; AVPlayerItemStatus status = [[change objectForKey:@"new"] intValue]; // 获取更改后的状态 if (status == AVPlayerItemStatusReadyToPlay) { _duration = item.duration;//只有在此状态下才能获取,不能在AVPlayerItem初始化后马上获取 NSLog(@"准备播放"); if (self.blockStatusReadyPlay) { self.blockStatusReadyPlay(item); } } else if (status == AVPlayerItemStatusFailed) { if (self.blockStatusFailed) { self.blockStatusFailed(); } AVPlayerItem *item = (AVPlayerItem *)object; NSLog(@"%@",item.error); NSLog(@"AVPlayerStatusFailed"); } else { self.blockStatusUnknown(); NSLog(@"%@",item.error); NSLog(@"AVPlayerStatusUnknown"); } }else if ([keyPath isEqualToString:@"tracking"]){ NSInteger status = [change[@"new"] integerValue]; if (self.blockTracking) { self.blockTracking(status); } if (status) {//正在拖动 [self.player pause]; }else{//停止拖动 } }else if ([keyPath isEqualToString:@"loadedTimeRanges"]){ NSArray *array = _playItem.loadedTimeRanges; CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围 CGFloat startSeconds = CMTimeGetSeconds(timeRange.start); CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration); NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度 double progress = totalBuffer/CMTimeGetSeconds(_duration); if (self.blockLoadedTimeRanges) { self.blockLoadedTimeRanges(progress); } NSLog(@"当前缓冲时间:%f",totalBuffer); }else if ([keyPath isEqualToString:@"playbackBufferEmpty"]){ NSLog(@"缓存不够,不能播放!"); }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]){ if (self.blockPlaybackLikelyToKeepUp) { self.blockPlaybackLikelyToKeepUp([change[@"new"] boolValue]); } } } -(void)setUrl:(NSURL *)url{ _url = url; [self.player replaceCurrentItemWithPlayerItem:self.playItem]; } -(AVPlayerItem *)playItem{ _playItem = [[AVPlayerItem alloc] initWithURL:_url]; //监听播放器的状态,准备好播放、失败、未知错误 [_playItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; // 监听缓存的时间 [_playItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; // 监听获取当缓存不够,视频加载不出来的情况: [_playItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil]; // 用于监听缓存足够播放的状态 [_playItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(private_playerMovieFinish) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; return _playItem; } - (void)private_playerMovieFinish{ NSLog(@"播放结束"); if (self.blockPlayToEndTime) { self.blockPlayToEndTime(); } if (_loop) {//默认提供一个循环播放的功能 [self.player pause]; CMTime time = CMTimeMake(1, 1); __weak typeof(self)this = self; [self.player seekToTime:time completionHandler:^(BOOL finished) { [this.player play]; }]; } } -(void)dealloc{ NSLog(@"-----销毁-----"); } @end
另外有需要云服务器可以了解下创新互联建站www.cdcxhl.com,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流