1、README
a. 编译
编译demo
由于提供的.a静态库是在x86_64的机器上编译的,所以仅支持该架构的主机上编译运行。
$ make
编译opencore-amr
如果想要在其他架构的CPU上编译运行,可以使用以下命令(脚本)编译opencore-amr
[下载地址]得到相应的库文件进行替换:
#!/bin/bashtar xzf opencore-amr-0.1.3.tar.gz
cd opencore-amr-0.1.3/
./configure --prefix=$PWD/_install # --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc
make -j96
make install
b. 使用
本示例是使用amr(nb)与pcm(8KHz,16bits,单/双声道)音频数据进行相互转化(编解码),使用如下:
$ ./pcm2amrnb ./audio/test_8000_16_1.pcm out.amr # 不管输入的PCM是单声道还是双声道,这里输出的amr都是单声道的
$ ./amrnb2pcm ./audio/test.amr out_8000_16_1.pcm # 解码出来的PCM都是8KHz单声道
编码参数要求
解码输出参数
c. 参考文章
-
https://blog.csdn.net/hanzhen7541/article/details/100932834
-
https://blog.csdn.net/dinggo/article/details/1966444
-
“Amr supports only 8000Hz sample rate and 4.75k, 5.15k, …”: https://stackoverflow.com/questions/2559746/getting-error-while-converting-wav-to-amr-using-ffmpeg#
d. 附录
$ tree
.
├── audio
│ ├── test_8000_16_1.pcm
│ ├── test_8000_16_2.pcm
│ └── test.amr
├── docs
│ ├── AMR文件格式分析_dinggo的专栏-CSDN博客_amr格式.mhtml
│ ├── AMR编码文件解析_hanzhen7541的博客-CSDN博客.mhtml
│ └── audio - getting error while converting wav to amr using ffmpeg - Stack Overflow.mhtml
├── include
│ ├── interf_dec.h
│ └── interf_enc.h
├── libs
│ └── libopencore-amrnb.a
├── main_amrnb2pcm.c
├── main_pcm2amrnb.c
├── Makefile
└── README.md
2、主要代码片段
main_pcm2amrnb.c
#include <stdio.h>
#include <stdlib.h>#include "interf_enc.h"/* PCM参数 */
#define PCM_SAMPLERATE (8000) /* 只能编码 8 khz */
#define PCM_SAMPLEBITS (16) /* 只支持16位 */
#define PCM_CHANNELS (1) /* 不管PCM输入是单声道还是双声道,这里输出的amr都是单声道的 *//* amr一帧数据是20ms,一秒50帧。8000,16,1 ==> 320 Bytes */
#define PCM_ONE_FRAME_SIZE (PCM_SAMPLERATE/50 * PCM_SAMPLEBITS/8 * PCM_CHANNELS)/* AMR参数 */
#define AMR_ENCODE_MODE MR122
#define AMR_ONE_FRAME_SIZE (32) /* MR122格式是32字节一帧 *//* 是否使能背景噪声编码模式 */
#define DTX_DECODE_ENABLE 1
#define DTX_DECODE_DISABLE 0int main(int argc, char *argv[])
{int dtx = DTX_DECODE_ENABLE;void *vpAmr = NULL;FILE *fpAmr = NULL;FILE *fpPcm = NULL;/* 检查参数 */if(argc != 3){printf("Usage: \n""\t %s ./audio/test_8000_16_1.pcm out.amr\n", argv[0]);return -1;}printf("It will encode a PCM file as [sample rate: %d] - [sample bits: %d] - [channels: %d] !\n", PCM_SAMPLERATE, PCM_SAMPLEBITS, PCM_CHANNELS);/* 初始化编码器 */vpAmr = Encoder_Interface_init(dtx);if(vpAmr == NULL){printf("Encoder_Interface_init error!\n");return -1;}/* 打开pcm文件 */fpPcm = fopen(argv[1], "rb");if(fpPcm == NULL) { perror("argv[1]");return -1;}/* 打开amr文件 */fpAmr = fopen(argv[2], "wb");if(fpAmr == NULL){perror("argv[2]");return -1;}/* 先写入amr头部 */fwrite("#!AMR\n", 1, 6, fpAmr);/* 循环编码 */while(1){unsigned char acPcmBuf[PCM_ONE_FRAME_SIZE] = {0}; /* 保存在文件中一帧(20ms)PCM数据,8bit为单位,这里是unsigned */short asEncInBuf[PCM_ONE_FRAME_SIZE/2] = {0}; /* 编码需要的一帧(20ms)PCM数据,16bit为单位 */char acEncOutBuf[AMR_ONE_FRAME_SIZE] = {0}; /* 编码出来的一帧(20ms)AMR数据 */int iReadPcmBytes = 0; /* 从PCM文件中读取出的数据大小,单位:字节 */int iEncAmrBytes = 0; /* 编码出的AMR数据大小,单位:字节 *//* 读出一帧PCM数据 */iReadPcmBytes = fread(acPcmBuf, 1, PCM_ONE_FRAME_SIZE, fpPcm);if(iReadPcmBytes <= 0){break;}//printf("iReadPcmBytes = %d\n", iReadPcmBytes);#if 0/* 编码方式 1:像官方测试程序一样转换为short类型再进行编码 */for(int i = 0; i < PCM_ONE_FRAME_SIZE/2; i++){unsigned char *p = &acPcmBuf[2*PCM_CHANNELS*i];asEncInBuf[i] = (p[1] << 8) | p[0];}/* 编码 */iEncAmrBytes = Encoder_Interface_Encode(vpAmr, AMR_ENCODE_MODE, asEncInBuf, acEncOutBuf, 0/* 参数未使用 */);
#else/* 编码方式 2:传参时直接类型强制转换即可 *//* 编码 */iEncAmrBytes = Encoder_Interface_Encode(vpAmr, AMR_ENCODE_MODE, (short *)acPcmBuf, acEncOutBuf, 0/* 参数未使用 */);
#endif//printf("iEncAmrBytes = %d\n", iEncAmrBytes);/* 写入到AMR文件中 */fwrite(acEncOutBuf, 1, iEncAmrBytes, fpAmr);}/* 关闭文件 */fclose(fpAmr);fclose(fpPcm);/* 关闭编码器 */Encoder_Interface_exit(vpAmr);printf("%s -> %s: Success!\n", argv[1], argv[2]);return 0;
}
main_amrnb2pcm.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "interf_dec.h"/* amrnb解码出来的PCM就是这个参数 */
#define PCM_SAMPLERATE (8000)
#define PCM_SAMPLEBITS (16)
#define PCM_CHANNELS (1)/* amr一帧数据是20ms,一秒50帧。8000,16,1 ==> 320 Bytes */
#define PCM_ONE_FRAME_SIZE (PCM_SAMPLERATE/50 * PCM_SAMPLEBITS/8 * PCM_CHANNELS)/* AMR参数 */
#define AMR_ONE_FRAME_SIZE (32) /* 对于NB,一般占用字节最大的MR122格式是32字节一帧 */int main(int argc, char *argv[])
{void *vpDecoder = NULL;FILE *fpAmr = NULL;FILE *fpPcm = NULL;char acAmrHeader[6] = {0};int iReadBytes = 0;int iFrameSizes[] = {12, 13, 15, 17, 19, 20, 26, 31, 5, 6, 5, 5, 0, 0, 0, 0};/* 检查参数 */if(argc != 3){printf("Usage: \n""\t %s ./audio/test.amr out_8000_16_1.pcm\n", argv[0]);return -1;}/* 初始化解码器 */vpDecoder = Decoder_Interface_init();if(vpDecoder == NULL){printf("Decoder_Interface_init() error!\n");return -1;}/* 打开文件 */fpPcm = fopen(argv[2], "wb");if(fpPcm == NULL){perror("test_enc.amr");return -1;}fpAmr = fopen(argv[1], "rb");if(fpAmr == NULL) { perror("argv[1]");return -1;}/* 判断是否为AMR文件 */iReadBytes = fread(acAmrHeader, 1, 6, fpAmr);if (iReadBytes != 6 || memcmp(acAmrHeader, "#!AMR\n", 6)) {printf("%s is not a amr file!\n", argv[1]);return -1;}/* 循环解码 */while(1){unsigned char acAmrBuf[AMR_ONE_FRAME_SIZE] = {0}; // 对于NB,一般最大是32字节,从amr文件读出一帧最大是32(31)字节unsigned char acPcmBuf[PCM_ONE_FRAME_SIZE] = {0}; // 解码出来的是以8bit为单位short asDecBuf[PCM_ONE_FRAME_SIZE/2] = {0}; // 解码出来的是以16bit为单位int iFrameSize = 0; // 根据AMR文件每帧的头部获取该帧数据大小/* 获取AMR规格 */iReadBytes = fread(acAmrBuf, 1, 1, fpAmr);if(iReadBytes <= 0)break;/* 获取一帧的大小, 对于 12.2 kbps 是 31 bytes */iFrameSize = iFrameSizes[(acAmrBuf[0] >> 3) & 0x0F];/* 读取一帧AMR数据,需要注意的是记得偏移一个地址存入31字节,而解码时需要32字节一起解码 */iReadBytes = fread(acAmrBuf + 1, 1, iFrameSize, fpAmr);if(iFrameSize != iReadBytes)break;#if 0/* 解码方式 1:像官方测试程序一样解码出来存到short类型的缓存里 *//* 将AMR数据解码 */Decoder_Interface_Decode(vpDecoder, acAmrBuf, asDecBuf, 0/* 参数未使用 */);char *p = acPcmBuf;for(int i = 0; i < 160; i++){*p++ = (asDecBuf[i] >> 0) & 0xff;*p++ = (asDecBuf[i] >> 8) & 0xff; }
#else/* 解码方式2:传参时直接强制类型转换即可 *//* 将AMR数据解码 */Decoder_Interface_Decode(vpDecoder, acAmrBuf, (short *)acPcmBuf, 0/* 参数未使用 */);
#endiffwrite(acPcmBuf, 1, 320, fpPcm);}/* 关闭文件 */fclose(fpAmr);fclose(fpPcm);/* 关闭解码器 */Decoder_Interface_exit(vpDecoder);printf("%s -> %s : Success!\n", argv[1], argv[2]);return 0;
}
3、demo下载地址(任选一个)
-
https://download.csdn.net/download/weixin_44498318/89525120
-
https://gitee.com/linriming/audio_pcm_amrnb_enc_dec.git
-
https://github.com/linriming20/audio_pcm_amrnb_enc_dec.git