iOS AVDemo(2):音频编码,采集 PCM 数据编码为 AAC

本文是iOS音视频开发系列的第二篇,介绍如何实现音频采集和编码,将PCM数据转换为AAC编码并存储。内容涵盖音频采集模块、音频编码模块的实现,以及如何处理和存储AAC数据。通过示例代码和详细注释,帮助理解iOS音视频处理流程。
摘要由CSDN通过智能技术生成

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

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

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

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

  • 2)实现一个音频编码模块;

  • 3)串联音频采集和编码模块,将采集到的音频数据输入给 AAC 编码模块进行编码和存储;

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

想要了解 AAC 编码,可以看看《音频编码:PCM 和 AAC 编码》

1、音频采集模块

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

KFAudioCapture.h
#import <Foundation/Foundation.h>
#import <CoreMedia/CoreMedia.h>
#import "KFAudioConfig.h"
​
NS_ASSUME_NONNULL_BEGIN
​
@interface KFAudioCapture : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfig:(KFAudioConfig *)config;
​
@property (nonatomic, strong, readonly) KFAudioConfig *config;
@property (nonatomic, copy) void (^sampleBufferOutputCallBack)(CMSampleBufferRef sample); // 音频采集数据回调。
@property (nonatomic, copy) void (^errorCallBack)(NSError *error); // 音频采集错误回调。
​
- (void)startRunning; // 开始采集音频数据。
- (void)stopRunning; // 停止采集音频数据。
@end
​
NS_ASSUME_NONNULL_END

2、音频编码模块

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

KFAudioEncoder.h
#import <Foundation/Foundation.h>
#import <CoreMedia/CoreMedia.h>
​
NS_ASSUME_NONNULL_BEGIN
​
@interface KFAudioEncoder : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithAudioBitrate:(NSInteger)audioBitrate;
​
@property (nonatomic, assign, readonly) NSInteger audioBitrate; // 音频编码码率。
@property (nonatomic, copy) void (^sampleBufferOutputCallBack)(CMSampleBufferRef sample); // 音频编码数据回调。
@property (nonatomic, copy) void (^errorCallBack)(NSError *error); // 音频编码错误回调。
​
- (void)encodeSampleBuffer:(CMSampleBufferRef)buffer; // 编码。
@end
​
NS_ASSUME_NONNULL_END

上面是 KFAudioEncoder 接口的设计,除了初始化方法,主要是有获取音频编码码率以及音频编码数据回调错误回调的接口,另外就是编码的接口。

其中编码接口对应着音频编码模块输入,数据回调接口则对应着输出。可以看到这里输入输出的参数都是 CMSampleBufferRef[1] 这个数据结构。它是对 CMSampleBuffer 的一个引用。

CMSampleBuffer 是 iOS 系统用来在音视频处理的 pipeline 中使用和传递媒体采样数据的核心数据结构。你可以认为它是 iOS 音视频处理 pipeline 中的流通货币,摄像头采集的视频数据接口、麦克风采集的音频数据接口、编码和解码数据接口、读取和存储视频接口、视频渲染接口等等,都以它作为参数。我们在 《iOS 音频采集 Demo》 一文中介绍音频采集接口的时候详细介绍过 CMSampleBuffer,可以去看看回顾一下。

所以,在这里我们也以 CMSampleBufferRef 作为编码模块输入和输出的接口参数。

KFAudioEncoder.m
#import "KFAudioEncoder.h"
#import <AudioToolbox/AudioToolbox.h>
​
@interface KFAudioEncoder () {
    char *_leftBuffer; // 待编码缓冲区。
    NSInteger _leftLength; // 待编码缓冲区的长度,动态。
    char *_aacBuffer; // 编码缓冲区。
    NSInteger _bufferLength; // 每次送给编码器的数据长度。
}
@property (nonatomic, assign) AudioConverterRef audioEncoderInstance; // 音频编码器实例。
@property (nonatomic, assign) CMFormatDescriptionRef aacFormat; // 音频编码参数。
@property (nonatomic, assign, readwrite) NSInteger audioBitrate; // 音频编码码率。
@property (nonatomic, assign) BOOL isError;
@property (nonatomic, strong) dispatch_queue_t encoderQueue;
@end
​
@implementation KFAudioEncoder
​
#pragma mark - Lifecycle
- (instancetype)initWithAudioBitrate:(NSInteger)audioBitrate {
    self = [super init];
    if (self) {
        _audioBitrate = audioBitrate;
        _encoderQueue = dispatch_queue_create("com.KeyFrameKit.audioEncoder", DISPATCH_QUEUE_SERIAL);
    }
​
    return self;
}
​
- (void)dealloc {
    // 清理编码器。
    if (_audioEncoderInstance) {
        AudioConverterDispose(_audioEncoderInstance);
        _audioEncoderInstance = nil;
    }
    if (_aacFormat) {
        CFRelease(_aacFormat);
        _aacFormat = NULL;
    }
    
    // 清理缓冲区。
    if (_aacBuffer) {
        free(_aacBuffer);
        _aacBuffer = NULL;
    }
    if (_leftBuffer) {
        free(_leftBuffer);
        _leftBuffer = NULL;
    }
}
​
#pragma mark - Utility
- (void)setupAudioEncoderInstanceWithInputAudioFormat:(AudioStreamBasicDescription)input
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值