iOS视频播放(AVFoundation)

iOS视频播放(AVFoundation)

关于iOS平台的音视频处理,苹果官方提供了OC和swift接口的AVFoundation框架,可以进行各种音频播放和剪辑,底层实现使用了GPU加速,编解码效率比软编软解提高很多。所以在iOS平台音视频方面的操作,AVFoundation是不二之选。iOS音视频处理库结构如下图:

可以看到AVFoundation处于中间,它封装了底层的一些C接口,抽象了一层面向对象的OC接口调用,比如视频编解码我们可以直接调用VideoToolBox的c接口,也可以使用AVAssertWriter和AVAssertReader类,它们进行了较好的封装,便于使用。苹果推荐我们使用后者。


1.1音视频播放

AVKit框架提供了简单易用的调用,直接封装的一个播放器,而且控制UI都提供了。这个很简单不用说了。主要介绍一下AVPlayer和AVPlayerItem。AVAssert是作为音视频文件的一个抽象,AVPlayer不直接对接AVAssert,通过AVPlayerItem控制播放源的状态,代码如下:


AVAsset *assert = [AVAsset assetWithURL:[NSURL URLWithString:@"example.mp4"]];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:assert];
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];


1.2使用AVPlayerlayer播放

视频图像要播放出来需要绘制在窗口上面,AVPlayer配套使用AVPlayerlayer。
代码:

// PlayerView.h
@interface PlayerView : UIView
@property AVPlayer *player;
@property (readonly) AVPlayerLayer *playerLayer;
@end
 
// PlayerView.m
@implementation PlayerView
- (AVPlayer *)player {
    return self.playerLayer.player;
}
 
- (void)setPlayer:(AVPlayer *)player {
    self.playerLayer.player = player;
}
 
// Override UIView method
+ (Class)layerClass {
    return [AVPlayerLayer class];
}
 
- (AVPlayerLayer *)playerLayer {
    return (AVPlayerLayer *)self.layer;
}
@end

1.3使用AVPlayerItemVideoOutput

有时候我们需要进一步对输出的图像进行处理,然后中绘制到view上面。这个时候就需要AVPlayerItemVideoOutput,它相当于在一个视频播放源上安装一个水龙头,然后解码后的视频数据就从里面出来,我们还可以指定输出数据的格式,便于下一步处理。代码如下:

//输出yuv 420格式
NSDictionary *pixBuffAttributes = @{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)}; 
AVPlayerItemVideoOutput *output = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:pixBuffAttributes];
[playerItem addOutput:output]; //put output to item


使用CADisplayLink开启定时回调方法,从output中获取图像数据进行处理,代码如下:


- (void)displayLinkCallback:(CADisplayLink *)sender
{
   
    CMTime outputItemTime = kCMTimeInvalid;
    CFTimeInterval nextVSync = ([sender timestamp] + [sender duration]);
    outputItemTime = [[self videoOutput] itemTimeForHostTime:nextVSync];
    if ([[self videoOutput] hasNewPixelBufferForItemTime:outputItemTime]) {
        CVPixelBufferRef pixelBuffer = NULL;
        pixelBuffer = [[self videoOutput] copyPixelBufferForItemTime:outputItemTime itemTimeForDisplay:NULL];
        // ..... do something with pixbuffer
        if (pixelBuffer != NULL) {
            CFRelease(pixelBuffer);
        }
    }
}

CADisplayLink的回调次数是根据屏幕刷新频率来的,它的timestamp方法可以获取上次的刷新时间戳,然后加上刷新间隔,获得当前时间戳,从output获得相应数据数据,进行处理。


参考:

https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/02_Playback.html#//apple_ref/doc/uid/TP40010188-CH3-SW4

https://developer.apple.com/library/archive/samplecode/AVBasicVideoOutput/Introduction/Intro.html#//apple_ref/doc/uid/DTS40013109

原文地址:https://www.cnblogs.com/song-jw/p/9230150.html