iOS AVDemo(8):视频编码,H.264 和 H.265 都支持

iOS/Android 客户端开发同学如果想要开始学习音视频开发,最丝滑的方式是对音视频基础概念知识有一定了解后,再借助 iOS/Android 平台的音视频能力上手去实践音视频的采集 → 编码 → 封装 → 解封装 → 解码 → 渲染过程,并借助音视频工具来分析和理解对应的音视频数据。

音视频工程示例这个栏目,我们将通过拆解采集 → 编码 → 封装 → 解封装 → 解码 → 渲染流程并实现 Demo 来向大家介绍如何在 iOS/Android 平台上手音视频开发。

这里是第八篇:iOS 视频编码 Demo。这个 Demo 里包含以下内容:

  • 1)实现一个视频采集模块;

  • 2)实现一个视频编码模块,支持 H.264/H.265;

  • 3)串联视频采集和编码模块,将采集到的视频数据输入给编码模块进行编码,并存储为文件;

  • 4)详尽的代码注释,帮你理解代码逻辑和原理。

想要了解视频编码,可以看看这几篇:

1、视频采集模块

在这个 Demo 中,视频采集模块 KFVideoCapture 的实现与 《iOS 视频采集 Demo》 中一样,这里就不再重复介绍了,其接口如下:

KFVideoCapture.h
#import <Foundation/Foundation.h>
#import "KFVideoCaptureConfig.h"
​
NS_ASSUME_NONNULL_BEGIN
​
@interface KFVideoCapture : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfig:(KFVideoCaptureConfig *)config;
​
@property (nonatomic, strong, readonly) KFVideoCaptureConfig *config;
@property (nonatomic, strong, readonly) AVCaptureVideoPreviewLayer *previewLayer; // 视频预览渲染 layer。
@property (nonatomic, copy) void (^sampleBufferOutputCallBack)(CMSampleBufferRef sample); // 视频采集数据回调。
@property (nonatomic, copy) void (^sessionErrorCallBack)(NSError *error); // 视频采集会话错误回调。
@property (nonatomic, copy) void (^sessionInitSuccessCallBack)(void); // 视频采集会话初始化成功回调。
​
- (void)startRunning; // 开始采集。
- (void)stopRunning; // 停止采集。
- (void)changeDevicePosition:(AVCaptureDevicePosition)position; // 切换摄像头。
@end
​
NS_ASSUME_NONNULL_END

2、视频编码模块

在实现视频编码模块之前,我们先实现一个视频编码配置类 KFVideoEncoderConfig

KFVideoEncoderConfig.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
​
NS_ASSUME_NONNULL_BEGIN
​
@interface KFVideoEncoderConfig : NSObject
@property (nonatomic, assign) CGSize size; // 分辨率。
@property (nonatomic, assign) NSInteger bitrate; // 码率。
@property (nonatomic, assign) NSInteger fps; // 帧率。
@property (nonatomic, assign) NSInteger gopSize; // GOP 帧数。
@property (nonatomic, assign) BOOL openBFrame; // 编码是否使用 B 帧。
@property (nonatomic, assign) CMVideoCodecType codecType; // 编码器类型。
@property (nonatomic, assign) NSString *profile; // 编码 profile。
@end
​
NS_ASSUME_NONNULL_END
KFVideoEncoderConfig.m
#import "KFVideoEncoderConfig.h"
#import <VideoToolBox/VideoToolBox.h>
​
@implementation KFVideoEncoderConfig
​
- (instancetype)init {
    self = [super init];
    if (self) {
        _size = CGSizeMake(1080, 1920);
        _bitrate = 5000 * 1024;
        _fps = 30;
        _gopSize = _fps * 5;
        _openBFrame = YES;
        
        BOOL supportHEVC = NO;
        if (@available(iOS 11.0, *)) {
            if (&VTIsHardwareDecodeSupported) {
                supportHEVC = VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC);
            }
        }
        
        _codecType = supportHEVC ? kCMVideoCodecType_HEVC : kCMVideoCodecType_H264;
        _profile = supportHEVC ? (__bridge NSString *) kVTProfileLevel_HEVC_Main_AutoLevel : AVVideoProfileLevelH264HighAutoLevel;
    }
    
    return self;
}
​
@end

这里实现了在设备支持 H.265 时,默认选择 H.265 编码。

接下来,我们来实现一个视频编码模块 KFVideoEncoder,在这里输入采集后的数据,输出编码后的数据。

KFVideoEncoder.h
#import <Foundation/Foundation.h>
#import "KFVideoEncoderConfig.h"
​
NS_ASSUME_NONNULL_BEGIN
​
@interface KFVideoEncoder : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfig:(KFVideoEncoderConfig*)config;
​
@property (nonatomic, strong, readonly) KFVideoEncoderConfig *config; // 视频编码配置参数。
@property (nonatomic, copy) void (^sampleBufferOutputCallBack)(CMSampleBufferRef sampleBuffer); // 视频编码数据回调。
@property (nonatomic, copy) void (^errorCallBack)(NSError *error); // 视频编码错误回调。
​
- (void)encodePixelBuffer:(CVPixelBufferRef)pixelBuffer ptsTime:(CMTime)timeStamp; // 编码。
- (void)refresh; // 刷新重建编码器。
- (void)flush; // 清空编码缓冲区。
- (void)flushWithCompleteHandler:(void (^)(void))completeHandler; // 清空编码缓冲区并回调完成。
@end
​
NS_ASSUME_NONNULL_END

上面是 KFVideoEncoder 接口的设计,除了初始化方法,主要是有获取视频编码配置以及视频编码数据回调错误回调的接口,另外就是编码刷新重建编码器清空编码缓冲区的接口。

其中编码接口对应着视频编码模块输入,数据回调接口则对应着输出。可以看到这里输出参数我们依然用的是 CMSampleBufferRef[1] 这个数据结构。不过输入的参数换成了 CVPixelBufferRef[2] 这个数据结构。它是对 CVPixelBuffer 的一个引用。

之前我们介绍过,CMSampleBuffer 中包含着零个或多个某一类型(audio、video、muxed 等)的采样数据。比如:

  • 要么是一个或多个媒体采样的 CMBlockBuffer[3]。其中可以封装:音频采集后、编码后、解码后的数据(如:PCM 数据、AAC 数据);视频编码后的数据(如:H.264/H.265 数据)。

  • 要么是一个 CVImageBuffer[4](也作 CVPixelBuffer[5])。其中包含媒体流中 CMSampleBuffers 的格式描述、每个采样的宽高和时序信息、缓冲级别和采样级别的附属信息。缓冲级别的附属信息是指缓冲区整体的信息,比如播放速度、对后续缓冲数据的操作等。采样级别的附属信息是指单个采样的信息,比如视频帧的时间戳、是否关键帧等。其中可以封装:视频采集后、解码后等未经编码的数据(如:YCbCr 数据、RGBA 数据)。

所以,因为是视频编码的接口,这里用 CVPixelBufferRef 也就是图一个方便,其实也可以用 CMSampleBufferRefÿ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值