公司做视频服务的,写了一个播放器Demo。主要功能都已经实现,自己再插入几个控制控件。
先梳理几个问题:
1.AVplayer的播放
2.uislider进度条
3.全屏模式
4.单个页面强制横屏
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
自定义一个view用来播放视频。
//
// PlayerView.m
// videodemo
//
// Created by meipaipai on 17/2/3.
// Copyright © 2017年 meipaipai. All rights reserved.
//
#import "PlayerView.h"
#import "LykSlider.h"
@interface PlayerView ()
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) AVPlayerItem *playerItem;
@property (nonatomic ,strong) id playbackTimeObserver;
@property (nonatomic, strong) NSString *totalTime;//视频总时间
@property (nonatomic, strong) NSDateFormatter *dateFormatter;//时间格式
//控制台
@property (nonatomic, strong) UIView *controlView;//控制台视图
@property (nonatomic, strong) UIButton *playButton;//播放按钮
@property (nonatomic, strong) LykSlider *playSlider;//进度条
@property (nonatomic, strong) UILabel *playTime;//播放时间
@property (nonatomic, strong) UIButton *fullScreen;//全屏
@end
static UIImage *thumbImage;
@implementation PlayerView
-(void)dealloc{
[self.playerItem removeObserver:self forKeyPath:@"status" context:nil];
[self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges" context:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
[self.player removeTimeObserver:self.playbackTimeObserver];
}
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
NSURL *videoUrl = [NSURL URLWithString:@"http://v.jxvdy.com/sendfile/w5bgP3A8JgiQQo5l0hvoNGE2H16WbN09X-ONHPq3P3C1BISgf7C-qVs6_c8oaw3zKScO78I--b0BGFBRxlpw13sf2e54QA"];
self.playerItem = [AVPlayerItem playerItemWithURL:videoUrl];
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
//解决iOS 10偶尔播放不了的问题
if([[UIDevice currentDevice] systemVersion].intValue>=10){
// 增加下面这行可以解决ios10兼容性问题了
self.player.automaticallyWaitsToMinimizeStalling = NO;
}
// 添加视频播放结束通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
// 监听status属性
[self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
// 监听loadedTimeRanges属性
[self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
[self.player play];
}
return self;
}
//重写layer方法
+ (Class)layerClass {
return [AVPlayerLayer class];
}
//重写get方法
- (AVPlayer *)player {
return [(AVPlayerLayer *)[self layer] player];
}
//重写set方法
- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *)[self layer] setPlayer:player];
}
//KVO
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
AVPlayerItem *playerItem = (AVPlayerItem *)object;
if ([keyPath isEqualToString:@"status"]) {
//准备播放
if (playerItem.status == AVPlayerItemStatusReadyToPlay) {
// 转换成秒
CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;
// 转换成播放时间
self.totalTime = [self convertTime:totalSecond];
// 监听播放状态
[self monitoringPlayback:self.playerItem];
//控制台UI
[self customVideoSlider:totalSecond];
} else if (playerItem.status == AVPlayerStatusFailed) {
NSLog(@"播放失败");
}
} else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度
NSLog(@"Time Interval:%f",timeInterval);
CGFloat currentSecond = playerItem.currentTime.value / playerItem.currentTime.timescale;
//缓冲好后自动播放
if (timeInterval > currentSecond && self.playButton.selected == NO) {
[self.player play];
}
}
}
//缓冲
- (NSTimeInterval)availableDuration {
NSArray *loadedTimeRanges = [[self.player currentItem] loadedTimeRanges];
CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
float startSeconds = CMTimeGetSeconds(timeRange.start);
float durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度
return result;
}
//视频总时间转换
- (NSString *)convertTime:(CGFloat)second{
NSDate *d = [NSDate dateWithTimeIntervalSince1970:second];
if (second/3600 >= 1) {
[[self dateFormatter] setDateFormat:@"HH:mm:ss"];
} else {
[[self dateFormatter] setDateFormat:@"mm:ss"];
}
NSString *showtimeNew = [[self dateFormatter] stringFromDate:d];
return showtimeNew;
}
//dateFormatter懒加载
- (NSDateFormatter *)dateFormatter {
if (!_dateFormatter) {
_dateFormatter = [[NSDateFormatter alloc] init];
}
return _dateFormatter;
}
//监听播放状态
- (void)monitoringPlayback:(AVPlayerItem *)playerItem {
__weak typeof(self) weakSelf = self;
self.playbackTimeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:NULL usingBlock:^(CMTime time) {
// 计算当前在第几秒
CGFloat currentSecond = playerItem.currentTime.value / playerItem.currentTime.timescale;
NSString *timeString = [weakSelf convertTime:currentSecond];
NSLog(@"%@", timeString);
weakSelf.playSlider.value = currentSecond;
weakSelf.playTime.text = [NSString stringWithFormat:@"%@/%@", timeString, weakSelf.totalTime];
}];
}
//自定义控制台
- (void)customVideoSlider:(CGFloat)second {
//控制台视图
self.controlView = [[UIView alloc] init];
self.controlView.alpha = 0.8;
self.controlView.backgroundColor = [UIColor blackColor];
[self addSubview:self.controlView];
//播放按钮
float itemY = self.controlView.bounds.size.height / 6;
self.playButton = [UIButton buttonWithType:UIButtonTypeCustom];
if (self.playButton.selected) {
[self.playButton setImage:[UIImage imageNamed:@"videoPlay"] forState:UIControlStateNormal];
} else {
[self.playButton setImage:[UIImage imageNamed:@"videoStop"] forState:UIControlStateNormal];
}
[self.playButton addTarget:self action:@selector(clickButton:) forControlEvents:UIControlEventTouchDown];
[self.controlView addSubview:self.playButton];
//视频时间
self.playTime = [[UILabel alloc] init];
self.playTime.textColor = [UIColor whiteColor];
self.playTime.font = [UIFont systemFontOfSize:itemY * 2];
self.playTime.text = [NSString stringWithFormat:@"00:00/%@", self.totalTime];
[self.controlView addSubview:self.playTime];
//全屏
self.fullScreen = [UIButton buttonWithType:UIButtonTypeCustom];
[self.fullScreen setImage:[UIImage imageNamed:@"videoBig"] forState:UIControlStateNormal];
[self.fullScreen addTarget:self action:@selector(clickFullButton:) forControlEvents:UIControlEventTouchDown];
[self.controlView addSubview:self.fullScreen];
//进度条
self.playSlider = [[LykSlider alloc] init];
self.playSlider.minimumValue = 0;// 设置最小值
self.playSlider.maximumValue = second;// 设置最大值
self.playSlider.value = 0;// 设置初始值
self.playSlider.continuous = NO;// 设置可连续变化,yes连续变化会触发方法,no当滑块拖动停止会触发方法
[self.playSlider addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
[self.controlView addSubview:self.playSlider];
[self changeFrame:self.frame];
}
//控制台按钮点击事件
-(void)clickButton:(UIButton *)sender{
sender.selected = !sender.selected;
if (sender.selected) {
[self.playButton setImage:[UIImage imageNamed:@"videoPlay"] forState:UIControlStateNormal];
[self.player pause];
} else {
[self.playButton setImage:[UIImage imageNamed:@"videoStop"] forState:UIControlStateNormal];
[self.player play];
}
}
//全屏按钮点击事件
-(void)clickFullButton:(UIButton *)sender{
sender.selected = !sender.selected;
if (sender.selected) {
[self.fullScreen setImage:[UIImage imageNamed:@"videoSmall"] forState:UIControlStateNormal];
//调用全屏block
self.pushBlockWithView();
} else {
[self.fullScreen setImage:[UIImage imageNamed:@"videoBig"] forState:UIControlStateNormal];
//调用小屏block
self.popBlockWithView();
}
}
//改变frame
-(void)changeFrame:(CGRect)frame{
self.frame = frame;
self.controlView.frame = CGRectMake(0, self.bounds.size.height / 7 * 6, self.bounds.size.width, self.bounds.size.height / 7);
float itemY = self.controlView.bounds.size.height / 6;
self.playButton.frame = CGRectMake(itemY, itemY, self.controlView.bounds.size.height - itemY * 2, self.controlView.bounds.size.height - itemY * 2);
self.playTime.frame = CGRectMake(self.playButton.frame.size.width + itemY * 6, itemY * 2, 200, itemY * 2);
self.playTime.font = [UIFont systemFontOfSize:itemY * 2];
self.fullScreen.frame = CGRectMake(self.bounds.size.width - self.controlView.bounds.size.height, itemY, self.controlView.bounds.size.height - itemY * 2, self.controlView.bounds.size.height - itemY * 2);
self.playSlider.frame = CGRectMake(0, -itemY, self.controlView.bounds.size.width, itemY * 2);
thumbImage = [self OriginImage:[UIImage imageNamed:@"videoThum"] scaleToSize:CGSizeMake(itemY * 2, itemY * 2)];
[self.playSlider setThumbImage:thumbImage forState:UIControlStateNormal];
}
//裁剪图片大小
-(UIImage *)OriginImage:(UIImage *)image scaleToSize:(CGSize)size {
UIGraphicsBeginImageContext(size); //size 为CGSize类型,即你所需要的图片尺寸
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage; //返回的就是已经改变的图片
}
//进度条拖动方法
-(void)sliderValueChanged:(UISlider *)sender{
[self.player seekToTime:CMTimeMake(sender.value,1)];
}
//播放完成处理
- (void)moviePlayDidEnd:(NSNotification *)notification {
[self.player seekToTime:kCMTimeZero completionHandler:^(BOOL finished) {
}];
}
@end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
解决问题:
1.uislider滑块大小无法控制
解决:
自定义图片更改滑块图片
- (void)setThumbImage:(nullable UIImage *)image forState:(UIControlState)state;
通过控制图片大小来改变滑块大小
//裁剪图片大小
-(UIImage *)OriginImage:(UIImage *)image scaleToSize:(CGSize)size {
UIGraphicsBeginImageContext(size); //size 为CGSize类型,即你所需要的图片尺寸
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage; //返回的就是已经改变的图片
}
解决:传言都说uislider frame的高度没用,实际高度是一个范围,只是对样式没有改变,问题的原因就是高度值太小,点不中。
3.视频播放不了
解决:iOS 10版本问题,avplayer新增属性automaticallyWaitsToMinimizeStalling,设置为no就可以
4.全屏强制横屏
解决:
appdelegate.h
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
/**
* 是否强制横屏
*/
@property BOOL isForceLandscape;
/**
* 是否强制竖屏
*/
@property BOOL isForcePortrait;
@end
appdelegate.m
static AppDelegate *_appDelegate;
+(AppDelegate *)appDelegate{
return _appDelegate;
}
-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
if (self.isForceLandscape) {
return UIInterfaceOrientationMaskLandscape;
}else if (self.isForcePortrait){
return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskAll;
}
-(void)viewWillAppear:(BOOL)animated{
[self forceOrientationLandscape];
}
/**
* 强制横屏
*/
-(void)forceOrientationLandscape{
//这段代码,只能旋转屏幕不能达到强制横屏的效果
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = UIInterfaceOrientationLandscapeRight;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
//加上代理类里的方法,旋转屏幕可以达到强制横屏的效果
AppDelegate *appdelegate=(AppDelegate *)[UIApplication sharedApplication].delegate;
appdelegate.isForceLandscape=YES;
[appdelegate application:[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:self.view.window];
}
-(void)viewWillDisappear:(BOOL)animated{
AppDelegate *appdelegate=(AppDelegate *)[UIApplication sharedApplication].delegate;
appdelegate.isForcePortrait=NO;
appdelegate.isForceLandscape=NO;
}