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, 具體使用方式今天就再也不作詳細介紹。

相關文章
相關標籤/搜索