iOS AVDemo(3):音频封装,采集编码并封装为 M4A

本文介绍了如何在iOS平台上进行音频封装的实践,通过实现音频采集、编码和封装模块,详细讲解了AAC编码和M4A封装的过程。利用音视频工具分析和播放M4A文件,帮助开发者理解音视频开发流程。
摘要由CSDN通过智能技术生成

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

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

这里是第三篇:iOS 音频封装 Demo。这个 Demo 里包含以下内容:

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

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

  • 3)实现一个音频封装模块;

  • 4)串联音频采集、编码、封装模块,将采集到的音频数据输入给 AAC 编码模块进行编码,再将编码后的数据输入给 M4A 封装模块封装和存储;

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

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 的实现与《iOS 音频编码 Demo》中一样,这里就不再重复介绍了,其接口如下:

#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

3、音频封装模块

接下来,我们来实现一个音频封装模块,在这里输入编码后的数据,输出封装后的文件。

这次我们要封装的格式是 M4A,属于 MPEG-4 标准,通常普通的 MPEG-4 文件扩展名是 .mp4,只包含音频的 MPEG-4 文件扩展名用 .m4a。所以,其实我们这里实现的是一个 MP4 封装模块,支持将音频编码数据封装成 M4A,也支持将音视频数据封装成 MP4。关于 MP4 格式,可以看一看《MP4 格式》这篇文章了解一下。

由于 MP4 封装涉及到一些参数设置,所以我们先实现一个 KFMuxerConfig 类用于定义 MP4 封装的参数的配置。这里包括了:封装文件输出地址、封装文件类型、图像变换信息这几个参数。

KFMuxerConfig.h
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import "KFMediaBase.h"
​
NS_ASSUME_NONNULL_BEGIN
​
@interface KFMuxerConfig : NSObject
@property (nonatomic, strong) NSURL *outputURL; // 封装文件输出地址。
@property (nonatomic, assign) KFMediaType muxerType; // 封装文件类型。
@property (nonatomic, assign) CGAffineTransform preferredTransform; // 图像的变换信息。比如:视频图像旋转。
@end
​
NS_ASSUME_NONNULL_END
KFMuxerConfig.m
#import "KFMuxerConfig.h"
​
@implementation KFMuxerConfig
​
- (instancetype)init {
    self = [super init];
    if (self) {
        _muxerType = KFMediaAV;
        _preferredTransform = CGAffineTransformIdentity;
    }
    return self;
}
​
@end

其中用到的 KFMediaType 是定义在 KFMediaBase.h 中的一个枚举:

KFMediaBase.h
#ifndef KFMediaBase_h
#define KFMediaBase_h
​
#import <Foundation/Foundation.h>
​
typedef NS_ENUM(NSInteger, KFMediaType) {
    KFMediaNone = 0,
    KFMediaAudio = 1 << 0, // 仅音频。
    KFMediaVideo = 1 << 1, // 仅视频。
    KFMediaAV = KFMediaAudio | KFMediaVideo,  // 音视频都有。
};
​
#endif /* KFMediaBase_h */

接下来,我们来实现 KFMP4Muxer 模块。

KFMP4Muxer.h
#import <Foundation/Foundation.h>
#import <CoreMedia/CoreMedia.h>
#import "KFMuxerConfig.h"
​
NS_ASSUME_NONNULL_BEGIN
​
@interface KFMP4Muxer : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfig:(KFMuxerConfig *)config;
​
@property (nonatomic, strong, readonly) KFMuxerConfig *config;
@property (nonatomic, copy) void (^errorCallBack)(NSError *error); // 封装错误回调。
​
- (void)startWriting; // 开始封装写入数据。
- (void)cancelWriting; // 取消封装写入数据。
- (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer; // 添加封装数据。
- (void)stopWriting:(void (^)(BOOL success, NSError *error))completeHandler; // 停止封装写入数据。
@end
​
NS_ASSUME_NONNULL_END

上面是 KFMP4Muxer 的接口设计,除了初始化方法,主要是有获取封装配置以及封装错误回调的接口,另外就是开始写入封装数据取消写入封装数据添加封装数据停止写入封装数据的接口。

在上面的添加封装数据接口中,我们使用的是依然 CMSampleBufferRef[1] 作为参数类型,再次体现了它作为 iOS 音视频处理 pipeline 中的流通货币的通用性。关于这点,我们在《iOS 音频采集 Demo》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值