iOS开发系列--视频处理

MPMoviePlayerController

在iOS中播放视频可使用MediaPlayer.framework种的MPMoviePlayerController类来完成,它支持本地视频和网络视频播放。这个类实现了MPMediaPlayback协议,所以具有通常的播放器控制功能,例如播放、暂停、中止等。可是MPMediaPlayerController自身并非一个完整的视图控制器,若是要在UI中展现视频须要将view属性添加到界面中。下面列出了MPMoviePlayerController的经常使用属性和方法:git

属性 说明
@property (nonatomic, copy) NSURL *contentURL 播放媒体URL,这个URL能够是本地路径,也能够是网络路径
@property (nonatomic, readonly) UIView *view 播放器视图,若是要显示视频必须将此视图添加到控制器视图中
@property (nonatomic, readonly) UIView *backgroundView 播放器背景视图
@property (nonatomic, readonly) MPMoviePlaybackState playbackState 媒体播放状态,枚举类型:
MPMoviePlaybackStateStopped:中止播放
MPMoviePlaybackStatePlaying:正在播放
MPMoviePlaybackStatePaused:暂停
MPMoviePlaybackStateInterrupted:中断
MPMoviePlaybackStateSeekingForward:向前定位
MPMoviePlaybackStateSeekingBackward:向后定位
@property (nonatomic, readonly) MPMovieLoadState loadState 网络媒体加载状态,枚举类型:
MPMovieLoadStateUnknown:位置类型
MPMovieLoadStatePlayable:
MPMovieLoadStatePlaythroughOK:这种状态若是shouldAutoPlay为YES将自动播放
MPMovieLoadStateStalled:停滞状态
@property (nonatomic) MPMovieControlStyle controlStyle 控制面板风格,枚举类型:
MPMovieControlStyleNone:无控制面板 
MPMovieControlStyleEmbedded:嵌入视频风格 
MPMovieControlStyleFullscreen:全屏 
MPMovieControlStyleDefault:默认风格
@property (nonatomic) MPMovieRepeatMode repeatMode; 重复播放模式,枚举类型:
MPMovieRepeatModeNone:不重复,默认值
MPMovieRepeatModeOne:重复播放
@property (nonatomic) BOOL shouldAutoplay 当网络媒体缓存到必定数据时是否自动播放,默认为YES
@property (nonatomic, getter=isFullscreen) BOOL fullscreen 是否全屏展现,默认为NO,注意若是要经过此属性设置全屏必须在视图显示完成后设置,不然无效
@property (nonatomic) MPMovieScalingMode scalingMode 视频缩放填充模式,枚举类型:
MPMovieScalingModeNone:不进行任何缩放
MPMovieScalingModeAspectFit:固定缩放比例而且尽可能所有展现视频,不会裁切视频
MPMovieScalingModeAspectFill:固定缩放比例并填充满整个视图展现,可能会裁切视频
MPMovieScalingModeFill:不固定缩放比例压缩填充整个视图,视频不会被裁切可是比例失衡
@property (nonatomic, readonly) BOOL readyForDisplay 是否有相关媒体被播放
@property (nonatomic, readonly) MPMovieMediaTypeMask movieMediaTypes 媒体类别,枚举类型:
MPMovieMediaTypeMaskNone:未知类型
MPMovieMediaTypeMaskVideo:视频
MPMovieMediaTypeMaskAudio:音频
@property (nonatomic) MPMovieSourceType movieSourceType 媒体源,枚举类型:
MPMovieSourceTypeUnknown:未知来源
MPMovieSourceTypeFile:本地文件
MPMovieSourceTypeStreaming:流媒体(直播或点播)
@property (nonatomic, readonly) NSTimeInterval duration 媒体时长,若是未知则返回0
@property (nonatomic, readonly) NSTimeInterval playableDuration 媒体可播放时长,主要用于表示网络媒体已下载视频时长
@property (nonatomic, readonly) CGSize naturalSize 视频实际尺寸,若是未知则返回CGSizeZero
@property (nonatomic) NSTimeInterval initialPlaybackTime 起始播放时间
@property (nonatomic) NSTimeInterval endPlaybackTime 终止播放时间
@property (nonatomic) BOOL allowsAirPlay 是否容许无线播放,默认为YES
@property (nonatomic, readonly, getter=isAirPlayVideoActive) BOOL airPlayVideoActive 当前媒体是否正在经过AirPlay播放
@property(nonatomic, readonly) BOOL isPreparedToPlay 是否准备好播放
@property(nonatomic) NSTimeInterval currentPlaybackTime 当前播放时间,单位:秒
@property(nonatomic) float currentPlaybackRate 当前播放速度,若是暂停则为0,正常速度为1.0,非0数据表示倍率
对象方法 说明
- (instancetype)initWithContentURL:(NSURL *)url 使用指定的URL初始化媒体播放控制器对象
- (void)setFullscreen:(BOOL)fullscreen animated:(BOOL)animated 设置视频全屏,注意若是要经过此方法设置全屏则必须在其视图显示以后设置,不然无效
- (void)requestThumbnailImagesAtTimes:(NSArray *)playbackTimes timeOption:(MPMovieTimeOption)option 获取在指定播放时间的视频缩略图,第一个参数是获取缩略图的时间点数组;第二个参数表明时间点精度,枚举类型:
MPMovieTimeOptionNearestKeyFrame:时间点附近
MPMovieTimeOptionExact:准确时间
- (void)cancelAllThumbnailImageRequests 取消全部缩略图获取请求
- (void)prepareToPlay 准备播放,加载视频数据到缓存,当调用play方法时若是没有准备好会自动调用此方法
- (void)play 开始播放
- (void)pause 暂停播放
- (void)stop 中止播放
- (void)beginSeekingForward 向前定位
- (void)beginSeekingBackward 向后定位
- (void)endSeeking 中止快进/快退
通知 说明
MPMoviePlayerScalingModeDidChangeNotification 视频缩放填充模式发生改变
MPMoviePlayerPlaybackDidFinishNotification 媒体播放完成或用户手动退出,具体完成缘由能够经过通知userInfo中的key为MPMoviePlayerPlaybackDidFinishReasonUserInfoKey的对象获取
MPMoviePlayerPlaybackStateDidChangeNotification 播放状态改变,可配合playbakcState属性获取具体状态
MPMoviePlayerLoadStateDidChangeNotification 媒体网络加载状态改变
MPMoviePlayerNowPlayingMovieDidChangeNotification 当前播放的媒体内容发生改变
MPMoviePlayerWillEnterFullscreenNotification 将要进入全屏
MPMoviePlayerDidEnterFullscreenNotification 进入全屏后
MPMoviePlayerWillExitFullscreenNotification 将要退出全屏
MPMoviePlayerDidExitFullscreenNotification 退出全屏后
MPMoviePlayerIsAirPlayVideoActiveDidChangeNotification 当媒体开始经过AirPlay播放或者结束AirPlay播放
MPMoviePlayerReadyForDisplayDidChangeNotification 视频显示状态改变
MPMovieMediaTypesAvailableNotification 肯定了媒体可用类型后
MPMovieSourceTypeAvailableNotification 肯定了媒体来源后
MPMovieDurationAvailableNotification 肯定了媒体播放时长后
MPMovieNaturalSizeAvailableNotification 肯定了媒体的实际尺寸后
MPMoviePlayerThumbnailImageRequestDidFinishNotification 缩略图请求完成以后
MPMediaPlaybackIsPreparedToPlayDidChangeNotification 作好播放准备后

注意MPMediaPlayerController的状态等信息并非经过代理来和外界交互的,而是经过通知中心,所以从上面的列表中能够看到经常使用的一些通知。因为MPMoviePlayerController自己对于媒体播放作了深度的封装,使用起来就至关简单:建立MPMoviePlayerController对象,设置frame属性,将MPMoviePlayerController的view添加到控制器视图中。下面的示例中将建立一个播放控制器并添加播放状态改变及播放完成的通知:github

//
//  ViewController.m
//  MPMoviePlayerController
//
//  Created by Kenshin Cui on 14/03/30.
//  Copyright (c) 2014年 cmjstudio. All rights reserved.
//

#import "ViewController.h"
#import <MediaPlayer/MediaPlayer.h>

@interface ViewController ()

@property (nonatomic,strong) MPMoviePlayerController *moviePlayer;//视频播放控制器

@end

@implementation ViewController

#pragma mark - 控制器视图方法
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //播放
    [self.moviePlayer play];
    
    //添加通知
    [self addNotification];
    
}

-(void)dealloc{
    //移除全部通知监控
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


#pragma mark - 私有方法
/**
 *  取得本地文件路径
 *
 *  @return 文件路径
 */
-(NSURL *)getFileUrl{
    NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"The New Look of OS X Yosemite.mp4" ofType:nil];
    NSURL *url=[NSURL fileURLWithPath:urlStr];
    return url;
}

/**
 *  取得网络文件路径
 *
 *  @return 文件路径
 */
-(NSURL *)getNetworkUrl{
    NSString *urlStr=@"http://192.168.1.161/The New Look of OS X Yosemite.mp4";
    urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    return url;
}

/**
 *  建立媒体播放控制器
 *
 *  @return 媒体播放控制器
 */
-(MPMoviePlayerController *)moviePlayer{
    if (!_moviePlayer) {
        NSURL *url=[self getNetworkUrl];
        _moviePlayer=[[MPMoviePlayerController alloc]initWithContentURL:url];
        _moviePlayer.view.frame=self.view.bounds;
        _moviePlayer.view.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
        [self.view addSubview:_moviePlayer.view];
    }
    return _moviePlayer;
}

/**
 *  添加通知监控媒体播放控制器状态
 */
-(void)addNotification{
    NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];
    [notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.moviePlayer];
    [notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer];
    
}

/**
 *  播放状态改变,注意播放完成时的状态是暂停
 *
 *  @param notification 通知对象
 */
-(void)mediaPlayerPlaybackStateChange:(NSNotification *)notification{
    switch (self.moviePlayer.playbackState) {
        case MPMoviePlaybackStatePlaying:
            NSLog(@"正在播放...");
            break;
        case MPMoviePlaybackStatePaused:
            NSLog(@"暂停播放.");
            break;
        case MPMoviePlaybackStateStopped:
            NSLog(@"中止播放.");
            break;
        default:
            NSLog(@"播放状态:%li",self.moviePlayer.playbackState);
            break;
    }
}

/**
 *  播放完成
 *
 *  @param notification 通知对象
 */
-(void)mediaPlayerPlaybackFinished:(NSNotification *)notification{
    NSLog(@"播放完成.%li",self.moviePlayer.playbackState);
}


@end

运行效果:数组

MPMoviePlayerController
从上面的API你们也不难看出其实MPMoviePlayerController功能至关强大,平常开发中做为通常的媒体播放器也彻底没有问题。MPMoviePlayerController除了通常的视频播放和控制外还有一些强大的功能,例如截取视频缩略图。请求视频缩略图时只要调用- (void)requestThumbnailImagesAtTimes:(NSArray *)playbackTimes timeOption:(MPMovieTimeOption)option方法指定得到缩略图的时间点,而后监控MPMoviePlayerThumbnailImageRequestDidFinishNotification通知,每一个时间点的缩略图请求完成就会调用通知,在通知调用方法中能够经过MPMoviePlayerThumbnailImageKey得到UIImage对象处理便可。例以下面的程序演示了在程序启动后得到两个时间点的缩略图的过程,截图成功后保存到相册:缓存

//
//  ViewController.m
//  MPMoviePlayerController
//
//  Created by Kenshin Cui on 14/03/30.
//  Copyright (c) 2014年 cmjstudio. All rights reserved.
//  视频截图

#import "ViewController.h"
#import <MediaPlayer/MediaPlayer.h>

@interface ViewController ()

@property (nonatomic,strong) MPMoviePlayerController *moviePlayer;//视频播放控制器

@end

@implementation ViewController

#pragma mark - 控制器视图方法
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //播放
    [self.moviePlayer play];
    
    //添加通知
    [self addNotification];
    
    //获取缩略图
    [self thumbnailImageRequest];
}

-(void)dealloc{
    //移除全部通知监控
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


#pragma mark - 私有方法
/**
 *  取得本地文件路径
 *
 *  @return 文件路径
 */
-(NSURL *)getFileUrl{
    NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"The New Look of OS X Yosemite.mp4" ofType:nil];
    NSURL *url=[NSURL fileURLWithPath:urlStr];
    return url;
}

/**
 *  取得网络文件路径
 *
 *  @return 文件路径
 */
-(NSURL *)getNetworkUrl{
    NSString *urlStr=@"http://192.168.1.161/The New Look of OS X Yosemite.mp4";
    urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    return url;
}

/**
 *  建立媒体播放控制器
 *
 *  @return 媒体播放控制器
 */
-(MPMoviePlayerController *)moviePlayer{
    if (!_moviePlayer) {
        NSURL *url=[self getNetworkUrl];
        _moviePlayer=[[MPMoviePlayerController alloc]initWithContentURL:url];
        _moviePlayer.view.frame=self.view.bounds;
        _moviePlayer.view.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
        [self.view addSubview:_moviePlayer.view];
    }
    return _moviePlayer;
}

/**
 *  获取视频缩略图
 */
-(void)thumbnailImageRequest{
    //获取13.0s、21.5s的缩略图
    [self.moviePlayer requestThumbnailImagesAtTimes:@[@13.0,@21.5] timeOption:MPMovieTimeOptionNearestKeyFrame];
}

#pragma mark - 控制器通知
/**
 *  添加通知监控媒体播放控制器状态
 */
-(void)addNotification{
    NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];
    [notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.moviePlayer];
    [notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer];
    [notificationCenter addObserver:self selector:@selector(mediaPlayerThumbnailRequestFinished:) name:MPMoviePlayerThumbnailImageRequestDidFinishNotification object:self.moviePlayer];
    
}

/**
 *  播放状态改变,注意播放完成时的状态是暂停
 *
 *  @param notification 通知对象
 */
-(void)mediaPlayerPlaybackStateChange:(NSNotification *)notification{
    switch (self.moviePlayer.playbackState) {
        case MPMoviePlaybackStatePlaying:
            NSLog(@"正在播放...");
            break;
        case MPMoviePlaybackStatePaused:
            NSLog(@"暂停播放.");
            break;
        case MPMoviePlaybackStateStopped:
            NSLog(@"中止播放.");
            break;
        default:
            NSLog(@"播放状态:%li",self.moviePlayer.playbackState);
            break;
    }
}

/**
 *  播放完成
 *
 *  @param notification 通知对象
 */
-(void)mediaPlayerPlaybackFinished:(NSNotification *)notification{
    NSLog(@"播放完成.%li",self.moviePlayer.playbackState);
}

/**
 *  缩略图请求完成,此方法每次截图成功都会调用一次
 *
 *  @param notification 通知对象
 */
-(void)mediaPlayerThumbnailRequestFinished:(NSNotification *)notification{
    NSLog(@"视频截图完成.");
    UIImage *image=notification.userInfo[MPMoviePlayerThumbnailImageKey];
    //保存图片到相册(首次调用会请求用户得到访问相册权限)
    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}

@end

截图效果:网络

MPMoviePlayerController_Thumbnail1     MPMoviePlayerController_Thumbnail2

扩展--使用AVFoundation生成缩略图

经过前面的方法你们应该已经看到,使用MPMoviePlayerController来生成缩略图足够简单,可是若是仅仅是是为了生成缩略图而不进行视频播放的话,此刻使用MPMoviePlayerController就有点大材小用了。其实使用AVFundation框架中的AVAssetImageGenerator就能够获取视频缩略图。使用AVAssetImageGenerator获取缩略图大体分为三个步骤:框架

  1. 建立AVURLAsset对象(此类主要用于获取媒体信息,包括视频、声音等)。
  2. 根据AVURLAsset建立AVAssetImageGenerator对象。
  3. 使用AVAssetImageGenerator的copyCGImageAtTime::方法得到指定时间点的截图。
//
//  ViewController.m
//  AVAssetImageGenerator
//
//  Created by Kenshin Cui on 14/03/30.
//  Copyright (c) 2014年 cmjstudio. All rights reserved.
//

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //获取第13.0s的缩略图
    [self thumbnailImageRequest:13.0];
}

#pragma mark - 私有方法
/**
 *  取得本地文件路径
 *
 *  @return 文件路径
 */
-(NSURL *)getFileUrl{
    NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"The New Look of OS X Yosemite.mp4" ofType:nil];
    NSURL *url=[NSURL fileURLWithPath:urlStr];
    return url;
}

/**
 *  取得网络文件路径
 *
 *  @return 文件路径
 */
-(NSURL *)getNetworkUrl{
    NSString *urlStr=@"http://192.168.1.161/The New Look of OS X Yosemite.mp4";
    urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    return url;
}

/**
 *  截取指定时间的视频缩略图
 *
 *  @param timeBySecond 时间点
 */
-(void)thumbnailImageRequest:(CGFloat )timeBySecond{
    //建立URL
    NSURL *url=[self getNetworkUrl];
    //根据url建立AVURLAsset
    AVURLAsset *urlAsset=[AVURLAsset assetWithURL:url];
    //根据AVURLAsset建立AVAssetImageGenerator
    AVAssetImageGenerator *imageGenerator=[AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];
    /*截图
     * requestTime:缩略图建立时间
     * actualTime:缩略图实际生成的时间
     */
    NSError *error=nil;
    CMTime time=CMTimeMakeWithSeconds(timeBySecond, 10);//CMTime是表示电影时间信息的结构体,第一个参数表示是视频第几秒,第二个参数表示每秒帧数.(若是要活的某一秒的第几帧可使用CMTimeMake方法)
    CMTime actualTime;
    CGImageRef cgImage= [imageGenerator copyCGImageAtTime:time actualTime:&actualTime error:&error];
    if(error){
        NSLog(@"截取视频缩略图时发生错误,错误信息:%@",error.localizedDescription);
        return;
    }
    CMTimeShow(actualTime);
    UIImage *image=[UIImage imageWithCGImage:cgImage];//转化为UIImage
    //保存到相册
    UIImageWriteToSavedPhotosAlbum(image,nil, nil, nil);
    CGImageRelease(cgImage);
}

@end

生成的缩略图效果:iphone

AVAssetImageGenerator_Thumbnail

MPMoviePlayerViewController

其实MPMoviePlayerController若是不做为嵌入视频来播放(例如在新闻中嵌入一个视频),一般在播放时都是占满一个屏幕的,特别是在iPhone、iTouch上。所以从iOS3.2之后苹果也在思考既然MPMoviePlayerController在使用时一般都是将其视图view添加到另一个视图控制器中做为子视图,那么何不直接建立一个控制器视图内部建立一个MPMoviePlayerController属性而且默认全屏播放,开发者在开发的时候直接使用这个视图控制器。这个内部有一个MPMoviePlayerController的视图控制器就是MPMoviePlayerViewController,它继承于UIViewController。MPMoviePlayerViewController内部多了一个moviePlayer属性和一个带有url的初始化方法,同时它内部实现了一些做为模态视图展现所特有的功能,例如默认是全屏模式展现、弹出后自动播放、做为模态窗口展现时若是点击“Done”按钮会自动退出模态窗口等。在下面的示例中就不直接将播放器放到主视图控制器,而是放到一个模态视图控制器中,简单演示MPMoviePlayerViewController的使用。ide

//
//  ViewController.m
//  MPMoviePlayerViewController
//
//  Created by Kenshin Cui on 14/03/30.
//  Copyright (c) 2014年 cmjstudio. All rights reserved.
//  MPMoviePlayerViewController使用

#import "ViewController.h"
#import <MediaPlayer/MediaPlayer.h>

@interface ViewController ()

//播放器视图控制器
@property (nonatomic,strong) MPMoviePlayerViewController *moviePlayerViewController;

@end

@implementation ViewController

#pragma mark - 控制器视图方法
- (void)viewDidLoad {
    [super viewDidLoad];

}

-(void)dealloc{
    //移除全部通知监控
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


#pragma mark - 私有方法
/**
 *  取得本地文件路径
 *
 *  @return 文件路径
 */
-(NSURL *)getFileUrl{
    NSString *urlStr=[[NSBundle mainBundle] pathForResource:@"The New Look of OS X Yosemite.mp4" ofType:nil];
    NSURL *url=[NSURL fileURLWithPath:urlStr];
    return url;
}

/**
 *  取得网络文件路径
 *
 *  @return 文件路径
 */
-(NSURL *)getNetworkUrl{
    NSString *urlStr=@"http://192.168.1.161/The New Look of OS X Yosemite.mp4";
    urlStr=[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    return url;
}

-(MPMoviePlayerViewController *)moviePlayerViewController{
    if (!_moviePlayerViewController) {
        NSURL *url=[self getNetworkUrl];
        _moviePlayerViewController=[[MPMoviePlayerViewController alloc]initWithContentURL:url];
        [self addNotification];
    }
    return _moviePlayerViewController;
}
#pragma mark - UI事件
- (IBAction)playClick:(UIButton *)sender {
    self.moviePlayerViewController=nil;//保证每次点击都从新建立视频播放控制器视图,避免再次点击时因为不播放的问题
//    [self presentViewController:self.moviePlayerViewController animated:YES completion:nil];
    //注意,在MPMoviePlayerViewController.h中对UIViewController扩展两个用于模态展现和关闭MPMoviePlayerViewController的方法,增长了一种下拉展现动画效果
    [self presentMoviePlayerViewControllerAnimated:self.moviePlayerViewController];
}

#pragma mark - 控制器通知
/**
 *  添加通知监控媒体播放控制器状态
 */
-(void)addNotification{
    NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];
    [notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackStateChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.moviePlayerViewController.moviePlayer];
    [notificationCenter addObserver:self selector:@selector(mediaPlayerPlaybackFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayerViewController.moviePlayer];
    
}

/**
 *  播放状态改变,注意播放完成时的状态是暂停
 *
 *  @param notification 通知对象
 */
-(void)mediaPlayerPlaybackStateChange:(NSNotification *)notification{
    switch (self.moviePlayerViewController.moviePlayer.playbackState) {
        case MPMoviePlaybackStatePlaying:
            NSLog(@"正在播放...");
            break;
        case MPMoviePlaybackStatePaused:
            NSLog(@"暂停播放.");
            break;
        case MPMoviePlaybackStateStopped:
            NSLog(@"中止播放.");
            break;
        default:
            NSLog(@"播放状态:%li",self.moviePlayerViewController.moviePlayer.playbackState);
            break;
    }
}

/**
 *  播放完成
 *
 *  @param notification 通知对象
 */
-(void)mediaPlayerPlaybackFinished:(NSNotification *)notification{
    NSLog(@"播放完成.%li",self.moviePlayerViewController.moviePlayer.playbackState);
}

@end

运行效果:动画

MPMoviePlayerViewController

这里须要强调一下,因为MPMoviePlayerViewController的初始化方法作了大量工做(例如设置URL、自动播放、添加点击Done完成的监控等),因此当再次点击播放弹出新的模态窗口的时若是不销毁以前的MPMoviePlayerViewController,那么新的对象就没法完成初始化,这样也就不能再次进行播放。ui

AVPlayer

MPMoviePlayerController足够强大,几乎不用写几行代码就能完成一个播放器,可是正是因为它的高度封装使得要自定义这个播放器变得很复杂,甚至是不可能完成。例若有些时候须要自定义播放器的样式,那么若是要使用MPMoviePlayerController就不合适了,若是要对视频有自由的控制则可使用AVPlayer。AVPlayer存在于AVFoundation中,它更加接近于底层,因此灵活性也更强:

AVFoundation_Framework

AVPlayer自己并不能显示视频,并且它也不像MPMoviePlayerController有一个view属性。若是AVPlayer要显示必须建立一个播放器层AVPlayerLayer用于展现,播放器层继承于CALayer,有了AVPlayerLayer之添加到控制器视图的layer中便可。要使用AVPlayer首先了解一下几个经常使用的类:

AVAsset:主要用于获取多媒体信息,是一个抽象类,不能直接使用。

AVURLAsset:AVAsset的子类,能够根据一个URL路径建立一个包含媒体信息的AVURLAsset对象。

AVPlayerItem:一个媒体资源管理对象,管理者视频的一些基本信息和状态,一个AVPlayerItem对应着一个视频资源。

下面简单经过一个播放器来演示AVPlayer的使用,播放器的效果以下:

AVPlayer_Thumbnail

在这个自定义的播放器中实现了视频播放、暂停、进度展现和视频列表功能,下面将对这些功能一一介绍。

首先说一下视频的播放、暂停功能,这也是最基本的功能,AVPlayer对应着两个方法play、pause来实现。可是关键问题是如何判断当前视频是否在播放,在前面的内容中不管是音频播放器仍是视频播放器都有对应的状态来判断,可是AVPlayer却没有这样的状态属性,一般状况下能够经过判断播放器的播放速度来得到播放状态。若是rate为0说明是中止状态,1是则是正常播放状态。

其次要展现播放进度就没有其余播放器那么简单了。在前面的播放器中一般是使用通知来得到播放器的状态,媒体加载状态等,可是不管是AVPlayer仍是AVPlayerItem(AVPlayer有一个属性currentItem是AVPlayerItem类型,表示当前播放的视频对象)都没法得到这些信息。固然AVPlayerItem是有通知的,可是对于得到播放状态和加载状态有用的通知只有一个:播放完成通知AVPlayerItemDidPlayToEndTimeNotification。在播放视频时,特别是播放网络视频每每须要知道视频加载状况、缓冲状况、播放状况,这些信息能够经过KVO监控AVPlayerItem的status、loadedTimeRanges属性来得到。当AVPlayerItem的status属性为AVPlayerStatusReadyToPlay是说明正在播放,只有处于这个状态时才能得到视频时长等信息;当loadedTimeRanges的改变时(每缓冲一部分数据就会更新此属性)能够得到本次缓冲加载的视频范围(包含起始时间、本次加载时长),这样一来就能够实时得到缓冲状况。而后就是依靠AVPlayer的- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block方法得到播放进度,这个方法会在设定的时间间隔内定时更新播放进度,经过time参数通知客户端。相信有了这些视频信息播放进度就不成问题了,事实上经过这些信息就算是平时看到的其余播放器的缓冲进度显示以及拖动播放的功能也能够顺利的实现。

最后就是视频切换的功能,在前面介绍的全部播放器中每一个播放器对象一次只能播放一个视频,若是要切换视频只能从新建立一个对象,可是AVPlayer却提供了- (void)replaceCurrentItemWithPlayerItem:(AVPlayerItem *)item方法用于在不一样的视频之间切换(事实上在AVFoundation内部还有一个AVQueuePlayer专门处理播放列表切换,有兴趣的朋友能够自行研究,这里再也不赘述)。

下面附上代码:

//
//  ViewController.m
//  AVPlayer
//
//  Created by Kenshin Cui on 14/03/30.
//  Copyright (c) 2014年 cmjstudio. All rights reserved.
//

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()

@property (nonatomic,strong) AVPlayer *player;//播放器对象

@property (weak, nonatomic) IBOutlet UIView *container; //播放器容器
@property (weak, nonatomic) IBOutlet UIButton *playOrPause; //播放/暂停按钮
@property (weak, nonatomic) IBOutlet UIProgressView *progress;//播放进度

@end

@implementation ViewController

#pragma mark - 控制器视图方法
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];
    [self.player play];
}

-(void)dealloc{
    [self removeObserverFromPlayerItem:self.player.currentItem];
    [self removeNotification];
}

#pragma mark - 私有方法
-(void)setupUI{
    //建立播放器层
    AVPlayerLayer *playerLayer=[AVPlayerLayer playerLayerWithPlayer:self.player];
    playerLayer.frame=self.container.frame;
    //playerLayer.videoGravity=AVLayerVideoGravityResizeAspect;//视频填充模式
    [self.container.layer addSublayer:playerLayer];
}

/**
 *  截取指定时间的视频缩略图
 *
 *  @param timeBySecond 时间点
 */

/**
 *  初始化播放器
 *
 *  @return 播放器对象
 */
-(AVPlayer *)player{
    if (!_player) {
        AVPlayerItem *playerItem=[self getPlayItem:0];
        _player=[AVPlayer playerWithPlayerItem:playerItem];
        [self addProgressObserver];
        [self addObserverToPlayerItem:playerItem];
    }
    return _player;
}

/**
 *  根据视频索引取得AVPlayerItem对象
 *
 *  @param videoIndex 视频顺序索引
 *
 *  @return AVPlayerItem对象
 */
-(AVPlayerItem *)getPlayItem:(int)videoIndex{
    NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.161/%i.mp4",videoIndex];
    urlStr =[urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url=[NSURL URLWithString:urlStr];
    AVPlayerItem *playerItem=[AVPlayerItem playerItemWithURL:url];
    return playerItem;
}
#pragma mark - 通知
/**
 *  添加播放器通知
 */
-(void)addNotification{
    //给AVPlayerItem添加播放完成通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem];
}

-(void)removeNotification{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

/**
 *  播放完成通知
 *
 *  @param notification 通知对象
 */
-(void)playbackFinished:(NSNotification *)notification{
    NSLog(@"视频播放完成.");
}

#pragma mark - 监控
/**
 *  给播放器添加进度更新
 */
-(void)addProgressObserver{
    AVPlayerItem *playerItem=self.player.currentItem;
    UIProgressView *progress=self.progress;
    //这里设置每秒执行一次
    [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
        float current=CMTimeGetSeconds(time);
        float total=CMTimeGetSeconds([playerItem duration]);
        NSLog(@"当前已经播放%.2fs.",current);
        if (current) {
            [progress setProgress:(current/total) animated:YES];
        }
    }];
}

/**
 *  给AVPlayerItem添加监控
 *
 *  @param playerItem AVPlayerItem对象
 */
-(void)addObserverToPlayerItem:(AVPlayerItem *)playerItem{
    //监控状态属性,注意AVPlayer也有一个status属性,经过监控它的status也能够得到播放状态
    [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //监控网络加载状况属性
    [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem{
    [playerItem removeObserver:self forKeyPath:@"status"];
    [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}
/**
 *  经过KVO监控播放器状态
 *
 *  @param keyPath 监控属性
 *  @param object  监视器
 *  @param change  状态改变
 *  @param context 上下文
 */
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    AVPlayerItem *playerItem=object;
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerStatus status= [[change objectForKey:@"new"] intValue];
        if(status==AVPlayerStatusReadyToPlay){
            NSLog(@"正在播放...,视频总长度:%.2f",CMTimeGetSeconds(playerItem.duration));
        }
    }else if([keyPath isEqualToString:@"loadedTimeRanges"]){
        NSArray *array=playerItem.loadedTimeRanges;
        CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围
        float startSeconds = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度
        NSLog(@"共缓冲:%.2f",totalBuffer);
//
    }
}

#pragma mark - UI事件
/**
 *  点击播放/暂停按钮
 *
 *  @param sender 播放/暂停按钮
 */
- (IBAction)playClick:(UIButton *)sender {
//    AVPlayerItemDidPlayToEndTimeNotification
    //AVPlayerItem *playerItem= self.player.currentItem;
    if(self.player.rate==0){ //说明时暂停
        [sender setImage:[UIImage imageNamed:@"player_pause"] forState:UIControlStateNormal];
        [self.player play];
    }else if(self.player.rate==1){//正在播放
        [self.player pause];
        [sender setImage:[UIImage imageNamed:@"player_play"] forState:UIControlStateNormal];
    }
}


/**
 *  切换选集,这里使用按钮的tag表明视频名称
 *
 *  @param sender 点击按钮对象
 */
- (IBAction)navigationButtonClick:(UIButton *)sender {
    [self removeNotification];
    [self removeObserverFromPlayerItem:self.player.currentItem];
    AVPlayerItem *playerItem=[self getPlayItem:sender.tag];
    [self addObserverToPlayerItem:playerItem];
    //切换视频
    [self.player replaceCurrentItemWithPlayerItem:playerItem];
    [self addNotification];
}

@end

运行效果:

AVPlayer

到目前为止不管是MPMoviePlayerController仍是AVPlayer来播放视频都至关强大,可是它也存在着一些不可回避的问题,那就是支持的视频编码格式颇有限:H.26四、MPEG-4,扩展名(压缩格式):.mp四、.mov、.m4v、.m2v、.3gp、.3g2等。可是不管是MPMoviePlayerController仍是AVPlayer它们都支持绝大多数音频编码,因此你们若是纯粹是为了播放音乐的话也能够考虑使用这两个播放器。那么如何支持更多视频编码格式呢?目前来讲主要仍是依靠第三方框架,在iOS上经常使用的视频编码、解码框架有:VLCffmpeg, 具体使用方式今天就再也不作详细介绍。

相关文章
相关标签/搜索