視頻 MPMoviePlayerController

在iOS中播放視頻可以使用MediaPlayer.framework種的MPMoviePlayerController類來完成,它支持本地視頻和網絡視頻播放。這個類實現了MPMediaPlayback協議,因此具備一般的播放器控制功能,例如播放、暫停、停止等。但是MPMediaPlayerController自身並不是一個完整的視圖控制器,如果要在UI中展示視頻需要將view屬性添加到界面中。下面列出了MPMoviePlayerController的常用屬性和方法:

屬性 說明
@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添加到控制器視圖中。下面的示例中將創建一個播放控制器並添加播放狀態改變及播放完成的通知:

//
//  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

生成的縮略圖效果:

AVAssetImageGenerator_Thumbnail

MPMoviePlayerViewController

其實MPMoviePlayerController如果不作爲嵌入視頻來播放(例如在新聞中嵌入一個視頻),通常在播放時都是佔滿一個屏幕的,特別是在iPhone、iTouch上。因此從iOS3.2以後蘋果也在思考既然MPMoviePlayerController在使用時通常都是將其視圖view添加到另外一個視圖控制器中作爲子視圖,那麼何不直接創建一個控制器視圖內部創建一個MPMoviePlayerController屬性並且默認全屏播放,開發者在開發的時候直接使用這個視圖控制器。這個內部有一個MPMoviePlayerController的視圖控制器就是MPMoviePlayerViewController,它繼承於UIViewController。MPMoviePlayerViewController內部多了一個moviePlayer屬性和一個帶有url的初始化方法,同時它內部實現了一些作爲模態視圖展示所特有的功能,例如默認是全屏模式展示、彈出後自動播放、作爲模態窗口展示時如果點擊“Done”按鈕會自動退出模態窗口等。在下面的示例中就不直接將播放器放到主視圖控制器,而是放到一個模態視圖控制器中,簡單演示MPMoviePlayerViewController的使用。

//
//  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,那麼新的對象就無法完成初始化,這樣也就不能再次進行播放。

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.264、MPEG-4,擴展名(壓縮格式):.mp4、.mov、.m4v、.m2v、.3gp、.3g2等。但是無論是MPMoviePlayerController還是AVPlayer它們都支持絕大多數音頻編碼,所以大家如果純粹是爲了播放音樂的話也可以考慮使用這兩個播放器。那麼如何支持更多視頻編碼格式呢?目前來說主要還是依靠第三方框架,在iOS上常用的視頻編碼、解碼框架有:VLCffmpeg, 具體使用方式今天就不再做詳細介紹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章