音频demo:使用opencore-amr将PCM数据与AMR-NB数据进行相互编解码

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单声道
编码参数要求

amr enc params

(截图来源:opencore-amr-0.1.3/test/amrnb-enc.c)
解码输出参数

amr dec params

(截图来源:opencore-amr-0.1.3/test/amrnb-dec.c)
c. 参考文章
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;
}

3、demo下载地址(任选一个)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R-QWERT

你的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值