AVFoundationを利用した動画表示用の便利クラス

チュートリアルの一部やオープニング画面など、アプリ内で部分的にムービーを表示したい事ありますよね。

MPMoviePlayerControllerを使えば、至れり尽くせりで超簡単!なんですけど、UIを自分で作ったり細かい制御をしたい場合にはやはりAVFoundationを使った方が良さそうです。
AVFoundationを使ってもそんなに難しい処理ではないんですけど、表示アイテムの管理や各種Observerの管理などでちょっと面倒。
そんな時には下記のクラスを使って下さい。
UI無しで指定された動画の再生だけを行います。
再生・一時停止・シークなどはデレゲート、各メソッドを通じて外のUIを使うように作ってあります。

事前に AVFoundation.framework、CoreMedia.framework へのリンクを追加しておいて下さいね。

ヘッダー部から


#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

@protocol MovieViewDelegate;

/**
 * 動画再生をView
 */
@interface MovieView : UIView
{

}

@property (nonatomic,assign) id<MovieViewDelegate> delegate;

//リピート再生するか
@property (nonatomic,assign) BOOL repeat;

//動画の長さ(秒数)
@property (nonatomic,readonly) Float64 movieDuration;

//再生を開始する
-(void)playMovie:(NSURL*)url;

//再生の一時停止
-(void)pauseMovie;

//再生開始
-(void)playMovie;

//再生位置を移動させる
-(void)seekToSeconds:(Float64)seconds;

//ムービーのクリア
- (void)clear;


@end


@protocol MovieViewDelegate <NSObject>

//動画の再生直前に呼び出される
-(void)movieView:(MovieView*)sender movieWillPlayItem:(AVPlayerItem*)playerItem duration:(Float64)duration;

//動画の再生中に一定の間隔で呼び出される
// time:再生位置(秒数)
// duration:動画全体の秒数
-(void)movieView:(MovieView*)sender moviePlayAtTime:(Float64)time duration:(Float64)duration;

@end


次に実装部はこんな感じ


#import "MovieView.h"


#define TIME_OVSERVER_INTERVAL  0.25f

@interface MovieView()
{   
    bool _is_playing;     //ムービーが再生中である事を示す
}

@property (nonatomic,retain) AVPlayerItem* playerItem;
@property (nonatomic,retain) AVPlayer*     player;
@property (nonatomic,assign) id  playTimeObserver;

@end

@implementation MovieView

#pragma mark -
#pragma mark class method

//自身のレイヤーを動画再生用レイヤーを返すようにオーバライド
+ (Class)layerClass
{
    return [AVPlayerLayer class];
}


#pragma mark -
#pragma mark instance management

-(void)dealloc
{
    _delegate = nil;
       
    [self clear];
   
    [_playerItem release];
    [_player release];

    [super dealloc];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        _is_playing = false;
        _repeat = TRUE;
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

#pragma mark property method

-(Float64)movieDuration
{
    if (self.playerItem)
    {
        Float64 duration = CMTimeGetSeconds( [self.player.currentItem duration] );
        return duration;
    } else {
        return 0;
    }
}



#pragma mark -
#pragma mark movie control

-(void)playMovie:(NSURL*)url
{
    //再生用アイテムを生成
    if (_playerItem)
    {
        //前回追加したムービー終了の通知を外す
        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                        name:AVPlayerItemDidPlayToEndTimeNotification
                                                      object:self.playerItem];
       
        self.playerItem = nil;
    }
    self.playerItem = [[[AVPlayerItem alloc] initWithURL:url] autorelease];
   

    //Itemにムービー終了の通知を設定
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playerDidPlayToEndTime:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:self.playerItem];
   
   
    Float64 movieDuration = CMTimeGetSeconds( self.playerItem.duration );
   
    if (self.player)
    {
               
        //アイテムの切り替え
        [self.player replaceCurrentItemWithPlayerItem:self.playerItem];
       
        [_delegate movieView:self movieWillPlayItem:self.playerItem duration:movieDuration];
       
    } else {

        //AVPlayerを生成
        self.player = [[[AVPlayer alloc] initWithPlayerItem:self.playerItem] autorelease];
        AVPlayerLayer* layer = ( AVPlayerLayer* )self.layer;
        layer.videoGravity = AVLayerVideoGravityResizeAspect;
        layer.player       = self.player;
       
        //delegate呼び出し
        [_delegate movieView:self movieWillPlayItem:self.playerItem duration:movieDuration];
       
        // 再生時間とシークバー位置を連動させるためのタイマーを設定
        __block MovieView* weakSelf = self;
        const CMTime intervaltime     = CMTimeMakeWithSeconds( TIME_OVSERVER_INTERVAL, NSEC_PER_SEC );
        self.playTimeObserver = [self.player addPeriodicTimeObserverForInterval:intervaltime
                                                                               queue:NULL
                                                                          usingBlock:^( CMTime time ) {
                                                                              //再生時になにか動作させるのであればここで。
                                                                              [weakSelf moviePlaying:time];
                                                                          }];
    }
   
    [self playMovie];
}




-(void)playMovie
{
    if (self.player)
    {
        [self.player play];
        _is_playing = true;
    }
}

-(void)pauseMovie
{   
    if (_is_playing)
    {
        [self.player pause];
        _is_playing = false;
    }
   
}

-(void)seekToSeconds:(Float64)seconds
{
    if (self.player)
    {
        [self.player seekToTime:CMTimeMakeWithSeconds(seconds, NSEC_PER_SEC )];
    }
}



//再生中のムービーを停止し、プレイヤーなどをクリアする
- (void)clear
{
    if (self.player)
    {
        [self.player removeTimeObserver:self.playTimeObserver];
        [self.player pause];
        [[NSNotificationCenter defaultCenter] removeObserver:self
          name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
       
        AVPlayerLayer* layer = ( AVPlayerLayer* )self.layer;
        layer.player  = nil;
       
        self.player = nil;
        self.playerItem = nil;
        self.playTimeObserver = nil;       
    }
       
}

#pragma mark -
#pragma mark movie events

// TIME_OVSERVER_INTERVALで指定された間隔で実行される
-(void)moviePlaying:(CMTime)time
{
    Float64 duration = CMTimeGetSeconds( [self.player.currentItem duration] );
    Float64 time1    = CMTimeGetSeconds(time);
   
    if (_delegate){
   
        [_delegate movieView:self moviePlayAtTime:time1 duration:duration];
   
    }
   
}

// ムービー完了時に実行される
- (void) playerDidPlayToEndTime:(NSNotification*)notfication
{

    if (self.player)
    {
        if (self.repeat)
        {
            //リピート再生
            [self.player seekToTime:kCMTimeZero];
            [self.player play];
        }
    }
}


@end

使い方のサンプルは長くなったので次回へ持ち越し

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です