博客> iOS 视频播放的研究——(初级篇)
iOS 视频播放的研究——(初级篇)
2018-12-18 13:40 评论:0 阅读:905 yuanweiqianyu
ios 视频
    虽然对于视频播放之前就有所了解,但是一直没有过多的在意,更没有研究过,仅仅在于知道。
    这篇文章仅仅是将视频播放的一些控件进行了简单的深入,并没有对框架等进行深挖,想看到更深层次知识的可以跳过了。下面是菜鸟的总结:
    经过一周的了解,如果对于视频播放没有什么特殊的要求,只需要实现播放功能就很简单了。直接使用MPMoviePlayerViewController或者使用MPMoviePlayerController完全够用。两个类很简单,从名字上就可以看出,一个是ViewController,另一个是View。MPMoviePlayerViewController是对MPMoviePlayerController的封装,两个类用法,没有太大不同。

   首先,MPMoviePlayerViewController:
               需要注意一点即可:只能够Present,不能push。
               第一,导入MediaPlayer.framework,将#import <MediaPlayer>导入
                第二,创建,播放,监听播放的状态。
  • (void)viewDidLoad { [super viewDidLoad];
    /* 打开本地视频 NSString url = [[NSBundlemainBundle]pathForResource:@"IMG_0322"ofType:@"mp4"]; MPMoviePlayerViewController playerViewController = [[MPMoviePlayerViewControlleralloc]initWithContentURL:[NSURLfileURLWithPath:url]]; /

    MPMoviePlayerViewController *movie = [[MPMoviePlayerViewController alloc]initWithContentURL:[NSURL URLWithString:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"]];

    //播放模式 /**

    • MPMovieControlStyleNone ————— 相当于播放视频,甚至没有返回操作
    • MPMovieControlStyleEmbedded ———— 嵌入式播放
    • MPMovieControlStyleFullscreen ———— 全屏播放 */ //播放器的样式 movie.moviePlayer.controlStyle = MPMovieControlStyleFullscreen; //控制模式(触摸) // movie.moviePlayer.scalingMode = MPMovieScalingModeAspectFill;

    //有的时候,播放器直接要求横屏播放 //先将整个项目设为竖屏模式,或者将此页面设为竖屏模式,然后写下以下三行代码,即可实现横屏 movie.moviePlayer.fullscreen = NO; CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI/2); movie.view.transform = transform;

    movie.view.backgroundColor = [UIColor clearColor]; movie.view.frame = self.view.bounds; [movie.moviePlayer prepareToPlay]; [self.navigationController presentMoviePlayerViewControllerAnimated:movie];

    //使用NSNotification监听播放器的各种状态 //停止播放 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(movieFinishedCallback:) name:MPMoviePlayerPlaybackDidFinishNotification object:movie.moviePlayer];

    //播放状态 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:movie];

    /**

    • movie.moviePlayer.controlStyle = MPMovieControlStyleEmbedded;状态下才调用 */ //控制器即将进入全屏 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieEventFullscreenHandler:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];

      //command+点击,可以看到notification都可以监听到哪些事件,此处不多列举了

}

/**

  • 停止播放 */

    • (void)movieFinishedCallback:(NSNotification *)noti{

    MPMoviePlayerController* theMovie = [noti object]; [[NSNotificationCenter defaultCenter]removeObserver:self

                                               name:MPMoviePlayerPlaybackDidFinishNotification object:theMovie];

    [self.navigationController dismissMoviePlayerViewControllerAnimated]; }

/**

  • 播放状态改变,注意播放完成时的状态是暂停
  • @param notification 通知对象 / -(void)mediaPlayerPlaybackStateChange:(NSNotification )notification{

    MPMoviePlayerController *theMovie = [notification object];

    switch (theMovie.playbackState) { case MPMoviePlaybackStatePlaying: NSLog(@"正在播放..."); break; case MPMoviePlaybackStatePaused: NSLog(@"暂停播放."); break; case MPMoviePlaybackStateStopped: NSLog(@"停止播放."); break; default: NSLog(@"播放状态:%li",theMovie.playbackState); break; } }

-(void)movieEventFullscreenHandler(NSNotification *)notification{ NSLog(@"即将进入全屏."); }

  • (void)dealloc{

    [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerWillEnterFullscreenNotification object:_moviePlayerController];

    _moviePlayerController = nil; }

二.MPMoviePlayerController(基本和上面的ViewController类似,但是更具与优势,能自己控制播放状态) 稍微难一点的就是,想要点击横屏的按钮,实现横屏状态,花费了一点时间查阅资料,主要是两个通知(MPMoviePlayerWillEnterFullscreenNotification,MPMoviePlayerWillExitFullscreenNotification),监听实现。 但是如果项目一进页面就是实现横屏就简单了,直接上面介绍的MPMoviePlayerViewController的方法拿来使用,就足够了。

  • (void)viewDidLoad { [super viewDidLoad]; _moviePlayerController = [[MPMoviePlayerController alloc]initWithContentURL:[NSURL URLWithString:@"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"]]; // _moviePlayerController.view.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; [_moviePlayerController prepareToPlay]; [self.view addSubview:_moviePlayerController.view];

    _moviePlayerController.shouldAutoplay=YES; _moviePlayerController.controlStyle = MPMovieControlStyleDefault; _moviePlayerController.scalingMode = MPMovieScalingModeAspectFit;

    _moviePlayerController.repeatMode = MPMovieRepeatModeNone; [_moviePlayerController setFullscreen:NO];

    [_moviePlayerController.view setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width)]; height = _moviePlayerController.view.frame.size.height;

    //这里注册监听相关操作的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieFinishedCallback:) name:MPMoviePlayerPlaybackDidFinishNotification object:_moviePlayerController]; //播放完后的通知

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:_moviePlayerController];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieEventWillFullscreenHandler:) name:MPMoviePlayerWillEnterFullscreenNotification object:_moviePlayerController];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieEventWillExitFullscreenHandler:) name:MPMoviePlayerWillExitFullscreenNotification object:_moviePlayerController];
    }

  • (IBAction)BtnClickAction:(id)sender {

    if (sender==_startBtn) { [_moviePlayerController play]; }else if (sender==_stopBtn){ [_moviePlayerController stop]; }else if (sender==_pauseBtn){ [_moviePlayerController pause]; } }

/**

  • 先将整个项目设为横竖屏模式,或者将此页面设为横竖屏模式,然后做以下操作,即可实现横屏竖屏切换 */
  • (void)movieEventWillFullscreenHandler:(NSNotification*)notification {

    NSLog(@"即将进入全屏状态"); MPMoviePlayerController *theMovie = [notification object];

    if ([UIScreen mainScreen].bounds.size.height <= height) {

    [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"];
    height = theMovie.view.frame.size.height;

    } else {

    [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@"orientation"];
    
    height = theMovie.view.frame.size.height;

    } }

  • (void)movieEventWillExitFullscreenHandler:(NSNotification*)notification {

    NSLog(@"即将退出全屏状态"); MPMoviePlayerController *theMovie = [notification object];

    if ([UIScreen mainScreen].bounds.size.height <= height) {

    [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"]; height = theMovie.view.frame.size.height; } else {

    [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@"orientation"];
    
    height = theMovie.view.frame.size.height;

    }

}

  • (void)dealloc{

    [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerWillEnterFullscreenNotification object:_moviePlayerController]; [[NSNotificationCenter defaultCenter]removeObserver:self name:MPMoviePlayerWillExitFullscreenNotification object:_moviePlayerController];

    _moviePlayerController = nil; }

MP类型的基本上在iOS 9之后就被废弃了,虽然好用,但不灵活,为了能够自定义播放起的外观,更方便的支持更多格式,AVFoundation兴起。 AVPlayer相信是现在大多数稍微要求高一点的视频播放都会选择的。

三.AVPlayer 首先,导入AVFoundation框架,在 .m导入#import UI界面的搭建思路,一个总的父视图totalView,在totalView上在搭建三个View,放视频的moviePlayerView,放播放器按钮的consoleView,音量调节的VolumnView,添加好后,添加一行代码,否则, 放播放器按钮的consoleView和音量调节的VolumnView,被播放器遮挡 [self.totalView.layer insertSublayer:_moviePlayerView.layer atIndex:0]; 如果还没有显示调用 [self.totalView bringSubviewToFront:_VolumnView]; [self.totalView bringSubviewToFront:_consoleView];

 Simulator Screen Shot 2016年8月7日 下午4.57.32.png

然后,初始化
  • (void)viewDidLoad { [super viewDidLoad];

    self.title = @"AVPlayer";

    self.progressSlider.minimumValue = 0; self.progressSlider.maximumValue = 100;

    /**

    NSString playString = @"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4"; NSURL url = [NSURL URLWithString:playString];

    //设置播放的项目 _item = [[AVPlayerItem alloc] initWithURL:url];

    //初始化player对象 self.player = [[AVPlayer alloc] initWithPlayerItem:_item];

    //设置播放页面 _layer = [AVPlayerLayer playerLayerWithPlayer:_player]; //设置播放页面的大小 _layer.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300); _layer.backgroundColor = [UIColor darkGrayColor].CGColor; //设置播放窗口和当前视图之间的比例显示内容 _layer.videoGravity = AVLayerVideoGravityResizeAspect; //添加播放视图到self.view [self.moviePlayerView.layer addSublayer:_layer]; //设置播放进度的默认值 self.progressSlider.value = 0; //设置播放的默认音量值(0.0-1,0) self.player.volume = 1.0;

    block UISlider *blockSlider = self.progressSlider; block UILabel blockCurrentLab = self.currentTimeLab; // __block UILabel blockTotalLab = self.totalTimeLab; __block id blockSelf = self;

    block UISlider *imgSlider = self.sliderImg; block UILabel *blockCurrentImgLab = self.currentImgLab;

    //当前播放的进度,不想监听的话直接隐掉 id timeObserve = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { float current = CMTimeGetSeconds(time);//当前播放秒数 float total = CMTimeGetSeconds(_item.duration);//总秒数 if (current) { // NSLog(@"%f", current / total);

        blockSlider.value = current / total * 100;
        imgSlider.value = current / total * 100;

// blockTotalLab.text = [blockSelf convertTime:total]; blockCurrentLab.text = [blockSelf convertTime:current]; blockCurrentImgLab.text = [blockSelf convertTime:current];

    }
}];

//添加自定义按钮
[self addImgViewButton];

//添加手势
[self addTapGestureRcognizeAction];

/**
 *  监听屏幕旋转的通知
 */
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientChange:) name:UIDeviceOrientationDidChangeNotification object:nil];

//有add,必须remove,监听播放状态,否则返回上一页,播放器仍然没有被释放掉*****(五星,非常可重要)
[_item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 监听status属性

//监听缓冲进度

// [item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

}

有几个比较费时间,个人感觉较难的,一个是屏幕旋转,一个是自定义的视屏播放器, 1.屏幕旋转 直接监听整个设备的旋转,一劳永逸,尝试了很多更改frame的,不尽满意。注意,在监听旋转的同时,仍然需要将player的父视图(moviePlayerView)的父视图(totalView)的frame进行相应调整

  • (void)orientChange:(NSNotification *)noti

{

NSDictionary* ntfDict = [noti userInfo];

UIDeviceOrientation  orient = [UIDevice currentDevice].orientation;

/*

 UIDeviceOrientationUnknown,

 UIDeviceOrientationPortrait,            // Device oriented vertically, home button on the bottom

 UIDeviceOrientationPortraitUpsideDown,  // Device oriented vertically, home button on the top

 UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right

 UIDeviceOrientationLandscapeRight,      // Device oriented horizontally, home button on the left

 UIDeviceOrientationFaceUp,              // Device oriented flat, face up

 UIDeviceOrientationFaceDown             // Device oriented flat, face down   */

CGFloat height = _layer.frame.size.height;

switch (orient)

{

    case UIDeviceOrientationPortrait:

        _lockBtn.hidden = YES;

        _layer.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 300);
        _totalView.frame = CGRectMake(0, 64, self.view.frame.size.width, 300);
        _moviePlayerTapView.frame = CGRectMake(0, 64, self.view.frame.size.width, 300);
        _layer.videoGravity = AVLayerVideoGravityResizeAspect;
        height = _layer.frame.size.height;

        break;

    case UIDeviceOrientationLandscapeLeft:

        _lockBtn.hidden = NO;

        _layer.frame = self.view.frame;
        _totalView.frame = self.view.frame;
        _moviePlayerTapView.frame = self.view.frame;
        _layer.videoGravity = AVLayerVideoGravityResizeAspect;

        [self.moviePlayerView.layer insertSublayer:_layer atIndex:0];
        _stateBtn.userInteractionEnabled = YES;
        [self.totalView bringSubviewToFront:_VolumnView];
        [self.totalView bringSubviewToFront:_consoleView];

        break;

    case UIDeviceOrientationPortraitUpsideDown:

        break;

    case UIDeviceOrientationLandscapeRight:

        _lockBtn.hidden = NO;

        _layer.frame = self.view.frame;
        _totalView.frame = self.view.frame;
        _layer.videoGravity = AVLayerVideoGravityResizeAspect;

        [self.moviePlayerView.layer insertSublayer:_layer atIndex:0];
        _stateBtn.userInteractionEnabled = YES;
        [self.totalView bringSubviewToFront:_VolumnView];
        [self.totalView bringSubviewToFront:_consoleView];

        height = _layer.frame.size.height;

        break;
    default:

        break;

}

}

监听播放状态notification调用方法、

  • (void)observeValueForKeyPath:(NSString )keyPath ofObject:(id)object change:(NSDictionary )change context:(void *)context {

    AVPlayerItem playerItem = (AVPlayerItem )object; if ([keyPath isEqualToString:@"status"]) { if ([playerItem status] == AVPlayerStatusReadyToPlay) { self.startBtn.userInteractionEnabled = YES; self.stateBtn.userInteractionEnabled = YES; self.sliderImg.userInteractionEnabled = YES; self.progressSlider.userInteractionEnabled = YES; NSLog(@"AVPlayerStatusReadyToPlay----视频已经可以开始播放");

        CMTime duration = playerItem.duration;// 获取视频总长度
        CGFloat totalSecond = playerItem.duration.value / playerItem.duration.timescale;// 转换成秒
        NSLog(@"movie total duration:%f",CMTimeGetSeconds(duration));
    
        self.totalTimeLab.text = [self convertTime:totalSecond];
        self.totalImgLab.text = [self convertTime:totalSecond];
    
    } else if ([playerItem status] == AVPlayerStatusFailed) {
        NSLog(@"AVPlayerStatusFailed------视频不能播放");
        self.startBtn.userInteractionEnabled = NO;
        self.stateBtn.userInteractionEnabled = NO;
        self.sliderImg.userInteractionEnabled = NO;
        self.progressSlider.userInteractionEnabled = NO;
    }

    } // else if ([keyPath isEqualToString:@"loadedTimeRanges"]) { // NSLog(@"视频缓冲中。。。"); // NSTimeInterval timeInterval = [self availableDuration];// 计算缓冲进度 // NSLog(@"Time Interval:%f",timeInterval); // CMTime duration = playerItem.duration; // CGFloat totalDuration = CMTimeGetSeconds(duration); // NSLog(@"缓冲总进度:%f",totalDuration); // } }

// //- (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]; NSDateFormatter formatter = [[NSDateFormatter alloc] init]; if (second/3600 >= 1) { [formatter setDateFormat:@"HH:mm:ss"]; } else { [formatter setDateFormat:@"mm:ss"]; } NSString showtimeNew = [formatter stringFromDate:d]; return showtimeNew; }

为了使播放器在viewWillDisappear的时候,被释放,必须移除notification*****(五星,重要)

  • (void)viewWillDisappear:(BOOL)animated{

    [_item removeObserver:self forKeyPath:@"status" context:nil];

    [self.player replaceCurrentItemWithPlayerItem:nil]; }

百度云:https://pan.baidu.com/s/1pLzWcsz 如有错误,敬请指正。

收藏
0
sina weixin mail 回到顶部