1、README
a. 编译
编译demo
由于提供的.a静态库是在x86_64的机器上编译的,所以仅支持该架构的主机上编译运行。
$ make
编译opencore-amr
如果想要在其他架构的CPU上编译运行,可以使用以下命令(脚本)编译opencore-amr
[下载地址]得到相应的库文件进行替换:
#!/bin/bash
tar 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. 参考文章
-
“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 0
int 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/* 参数未使用 */);
#endif
fwrite(acPcmBuf, 1, 320, fpPcm);
}
/* 关闭文件 */
fclose(fpAmr);
fclose(fpPcm);
/* 关闭解码器 */
Decoder_Interface_exit(vpDecoder);
printf("%s -> %s : Success!\n", argv[1], argv[2]);
return 0;
}