音视频编程: iOS 使用 faac 编码
简介 本文分享如何将 WAV 格式的音频文件转换(编码)为 AAC 格式的音频文件并使用 AVAudioPlayer 播放编码后的文件.
这里的编码功能使用 faac 这个库来实现.
该系列博文:
函数介绍
获取 faac 的版本
1 int FAACAPI faacEncGetVersion(char **faac_id_string, char **faac_copyright_string);
示例:
1 2 3 4 char *version; char *copyright; faacEncGetVersion(&version, ©right); printf("FAAC version: %s, copyright: %s", version, copyright);
打印结果:
1 2 3 4 FAAC version: 1.28, copyright: FAAC - Freeware Advanced Audio Coder (http://www.audiocoding.com/) Copyright (C) 1999,2000,2001 Menno Bakker Copyright (C) 2002,2003 Krzysztof Nikiel This software is based on the ISO MPEG-4 reference source code.
打开并初始化 faac 编码器
1 2 3 4 5 6 7 8 // sampleRate: 采样率 // numChannels: 通道数量,1-单声道 2-立体声 // inputSamples: 编码后的数据长度 // maxOutputBytes: 编码后的信息最大长度 faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate, unsigned int numChannels, unsigned long *inputSamples, unsigned long *maxOutputBytes);
该函数返回一个 faac 编码器句柄. 其很多函数都需要这个句柄.
关闭 faac 编码器
1 int FAACAPI faacEncClose(faacEncHandle hEncoder);
将开启 faac 编码器返回的句柄传入即可.
获取配置和设置配置
1 2 3 4 5 6 faacEncConfigurationPtr FAACAPI faacEncGetCurrentConfiguration(faacEncHandle hEncoder); int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder, faacEncConfigurationPtr config);
faac 的配置被定义为了一个结构体 faacEncConfiguration, 大家可以看源码.
编码(编码一帧音频数据)
1 2 3 4 5 6 7 8 // hEncoder: faacEncOpen 返回的编码器句柄 // inputBuffer: 输入信息缓冲区 // samplesInput: faacEncOpen编码后的数据长度,即缓冲区长度 // outputBuffer: 编码后输出信息缓冲区 // bufferSize: 输出信息长度 int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t * inputBuffer, unsigned int samplesInput, unsigned char *outputBuffer, unsigned int bufferSize);
实例 可以自己新建一个 iOS 工程.
准备好一个 wav
格式的音频文件.
效果如下图所示:
新建一个 C++ 文件, MZCodec.
MZCodec.hpp
1 2 3 4 5 6 7 8 #ifndef MZCodec_hpp #define MZCodec_hpp #include <stdio.h> int codeWAV(const char *srcFilePath, const char *destPath); #endif /* MZCodec_hpp */
MZCodec.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 #include "MZCodec.hpp" #include "faac.h" #include <stdio.h> int codeWAV(const char *srcFilePath, const char *destPath) { unsigned long nSampleRate = 44100;//采样率 unsigned int nChannels = 2;//声道数 unsigned int nPCMBitSize = 16;//单样本位数 unsigned long nInputSamples = 0; unsigned long nMaxOutputBytes = 0; int nRet; faacEncHandle hEncoder; faacEncConfigurationPtr pConfiguration; size_t nBytesRead; unsigned long nPCMBufferSize; unsigned char *pbPCMBuffer; unsigned char *pbAACBuffer; FILE *fpIn; // WAV file for input FILE *fpOut; // AAC file for output /// 获取 faac 版本信息 { char *version; char *copyright; faacEncGetVersion(&version, ©right); printf("FAAC version: %s, copyright: %s", version, copyright); } fpIn = fopen(srcFilePath, "rb"); if (NULL == fpIn) { return -2; } fpOut = fopen(destPath, "wb"); /// 1. 打开 FAAC hEncoder = faacEncOpen(nSampleRate, nChannels, &nInputSamples, &nMaxOutputBytes); if (NULL == hEncoder) { printf("[ERROR] Failed to call faacEncOpen()\n"); return -1; } nPCMBufferSize = nInputSamples * nPCMBitSize / 8; pbPCMBuffer = new unsigned char[nPCMBufferSize]; pbAACBuffer = new unsigned char[nMaxOutputBytes]; /// 2.1. 获取当前的编码器配置 pConfiguration = faacEncGetCurrentConfiguration(hEncoder); pConfiguration->inputFormat = FAAC_INPUT_16BIT; // 对象类型只有为 LOW, iOS 的 AVAudioPlayer 才能播放 pConfiguration->aacObjectType = LOW; // 0 = Raw; 1 = ADTS pConfiguration->outputFormat = 1; pConfiguration->mpegVersion = MPEG4; pConfiguration->useTns = 1; pConfiguration->bitRate = 30; /// 2.2. 配置编码器 nRet = faacEncSetConfiguration(hEncoder, pConfiguration); //是wav格式, 先读取前面的 fseek(fpIn, 58, SEEK_SET); do { //读入的实际字节数,最大不会超过 nPCMBufferSize nBytesRead = fread(pbPCMBuffer, 1, nPCMBufferSize, fpIn); //输入样本数,用实际读入字节数计算 //一般只有读到文件尾时才不是 nPCMBufferSize/(nPCMBitSize/8) nInputSamples = nBytesRead / (nPCMBitSize / 8); /// 3. 编码 nRet = faacEncEncode(hEncoder, (int *)pbPCMBuffer, (unsigned int)nInputSamples, pbAACBuffer, (unsigned int)nMaxOutputBytes); fwrite(pbAACBuffer, 1, nRet, fpOut); printf("FaacEncEncode returns %d\n", nRet); } while (nBytesRead > 0); /// 4. 关闭 FAAC nRet = faacEncClose(hEncoder); delete[] pbPCMBuffer; delete[] pbAACBuffer; fclose(fpIn); fclose(fpOut); return 0; }
使用 MZCodec 的 Controller 需要命名为 .mm
文件.
ViewController.mm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 NSBundle *bundle = [NSBundle mainBundle]; NSString *resPath = [bundle pathForResource:@"m" ofType:@"wav"]; NSLog(@"The path of wav file: %@", resPath); NSArray<NSString *> *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *destPath = [[docPath lastObject] stringByAppendingString:@"/out.aac"]; NSLog(@"The path of aac file: %@", destPath); dispatch_async(dispatch_get_global_queue(0, 0), ^{ codeWAV([resPath UTF8String], [destPath UTF8String]); dispatch_async(dispatch_get_main_queue(), ^{ //转换完成. }); });
编码过程需要点时间, 如果文件很大, 时间越久. 所以, 新开线程来进行编码.
编码完成后, 可在对应的沙盒目录找到 out.aac
文件.
具体代码在 Github 上面, 感兴趣的可以点击 前往 .
注意事项 这个 aacObjectType
需要注意, 之前我在这里折腾了很久.
1 pConfiguration->aacObjectType = LOW;
如果设置为其他三种, 编码后的 aac 文件, AVAudioPlayer 播放不了, 初始化 AVAudioPlayer 就会报错, 报错信息如下: