音视频:AVKit,AVFoundation,CMTime

31 8月

在IOS中播放音视频用AVKit,它是基于UIKit+AVFoundation的上层封装。外层View,例如播放的用户控制,导航等由UIKit提供。

  • AVFoundation
  • CMTime
  • 单例

AVFoundation

AVFoundation提供了在IOS中播放音视频的能力,内核是Core Audio + Core Media + Core Animation。常用的类:

AVAsset:视频的资源属性。提供与格式无关的媒体数据,截取静态图片,转码,删减,编辑声音,透明度,尺寸等
AVPlayerItem:视频的播放属性。对AVAsset的上层封装,相当于AVAsset和AVPlayer间的中间层。
AVPlayer:开始暂停播放,即其他如快进等能力。
AVPlayerLayer:播放器画面

#import <AVFoundation/AVFoundation.h>

@property (nonatomic, strong, readwrite) AVPlayerItem *videoItem;
@property (nonatomic, strong, readwrite) AVPlayer *avPlayer;
@property (nonatomic, strong, readwrite) AVPlayerLayer *playerLayer;

NSURL *videoURL = [NSURL URLWithString:@"xxxx"];
AVAsset *asset = [AVAsset assetWithURL:videoURL];
_videoItem = [AVPlayerItem playerItemWithAsset:asset];

// 用 KVO 监听视频资源状态
[_videoItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

// 用 KVO 监听视频缓冲进度
[_videoItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

// 创建播放器
_avPlayer = [AVPlayer playerWithPlayerItem:_videoItem];

// 展示 playerLayer
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_avPlayer];
_playerLayer.frame = attachView.bounds;
[attachView.layer addSublayer:_playerLayer];

CMTime

用CMTime来控制视频播放的进度。

// 需要在状态变化后获取时间
CMTime duration = _videoItem.duration;
__unused CGFloat videoDuration = CMTimeGetSeconds(duration);

// 监听播放器播放进度
[_avPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
    NSLog(@"播放进度:%@", @(CMTimeGetSeconds(time)));
}];

 // 接收播放完成 Notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_handlePlayEnd) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

- (void)_handlePlayEnd {
    // 播放完成后循环播放
    [_avPlayer seekToTime:CMTimeMake(0, 1)];
    [_avPlayer play];
}

单例

让APP播放视频不难,但要注意,视频应该被设计为单例模式,通常一个APP内只能同时播放一个视频,用单例非常合适,在单例中处理视频的播放销毁和切换。

+ (VideoPlayer *)Player;

+ (VideoPlayer *)Player {
    static VideoPlayer *player;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        player = [[VideoPlayer alloc] init];
    });
    return player;
}

- (void)playVideoWithUrl:(NSString *)videoUrl attachView:(UIView *)attachView {
    // 全局就一个播放器,每次播放前先销毁之前的播放器
    [self _stopPlay];
    ...  // 创建播放器
    [attachView.layer addSublayer:_playerLayer];    // 最后在 view 上贴上播放器
}

- (void)_stopPlay {
    ... // 移除监听

    // 销毁播放器
    [_playerLayer removeFromSuperlayer];
    _videoItem = nil;
    _avPlayer = nil;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注