2022-03-31 Amr-nb格式字节对齐、非字节对齐的转换和解码处理(含源码)

常用音视频处理格式AMR-NB

AMR的格式分为AMR-NB、AMR-WB、AMR-WB+
三种格式的区分和差异可以自行百度,在这里就不做细分了。 我相信,找到这篇文章的基本已经是入门了。


这一次我们主要处理的是AMR-NB格式的数据。本文主要贴出了如何区分字节对齐、非字节对齐的方法,以及如何处理解码?(需要完整解决方案或者答疑的,可以私聊获取相关帮助)。


前言

解决什么问题:

这次,我们需要解决的是将AMRNB的UDP包数据,进行解码;这边涉及到的知识点是:

  1. opencore-amr-0.1.5,下载点击: https://download.csdn.net/download/shayueqing/85059816
  2. winpcap库,用于抓取网卡数据包。

提示:以下是本篇文章正文内容,下面案例可供参考

一、理论知识(熟悉可跳过)

摘自网络:原文链接:https://blog.csdn.net/runningya/article/details/6227384

  1. AMR编码介绍
    AMR编码是一种自适应多速率编码,根据传输信道的实际情况,调整编码模式、速率和纠错码位数来保证语音质量,在数据压缩和容错上面取得平衡。一般语音质量越高抗干扰能力越弱。在GSM网络,基站、基站控制器可根据网络质量和信号质量情况动态调整语音编码模式以提高不同网络状况下的语音质量。现在手机终端基本上都支持AMR编码,Nokia从2004年开始提供支持AMR的终端,目前所有的新型号终端都支持AMR。
  2. AMR在IP域中的净荷格式
    RFC3267/4867协议描述AMR编码在RTP中的载荷格式,是其在IP域的存在形式。
    对于每个RTP会话,AMR净荷有两种模式,分别为节省带宽模式和字节对齐模式,具体采用那种模式,由信令协商决定;采用后一模式可以后可以引入鲁棒排序、帧交叉编码和CRC校验等方式提高语音传输的质量。
  3. AMR NB/WB信令协商参数
    AMR常用如下
    octet-align,是节省带宽模式还是字节对节模式,一般取值为1表示字节对齐,为0或不带默认表示为节省带宽模式;
    mode-set,一旦请求带了模式集,之后AMR媒体流只选择属于这个模式集的模式来发送;
    mode-change-period,模式改变周期;
    maxptime,最大支持的打包时长(ms);
    crc,净荷是否进行CRC校验,一般为1表示需要,媒体流会携带校验位;
    robust-sorting,是否进行鲁棒排序;
    interleaving,定义交错帧块数;
    ptime,指包时长;
    channels,音频通道数,例如采用左右通道数值则为2;

二、我们要做哪些事?

如何提取AMR-NB,哪里有amr字节和采样率的对应,可以参考一些我的其他博文:https://blog.csdn.net/shayueqing/article/details/107319891

解决什么问题,对你是否有帮助?

  1. 从网卡上抓取rtp包,提取出payload;
  2. 根据playload,结合amr-nb的“采样率字节表”,解析出实际的数据长度,并且进行解码;

三、代码干货(底下附完整实现)

1.提取payload数据,并进行两步处理

  • 将amr-nb非字节对齐转成字节对齐;
  • 区分噪音帧和音频帧;

代码如下(示例):

//这一步是从RTP包获取payload
unsigned char*pbuffer = (u_char*)((u_char*)uh + sizeof(udp_header) + sizeof(rtp_header));
//这一步进行非字节对齐与字节对齐的转换
int size = 0;
unsigned char outbuf[400] = { 0 };
bool bRet = prase_amr(pbuffer, 32, outbuf, &size, &data.pData[0]);
if (bRet == false)
{
	//为噪音帧
	memset(data.pData, 0, 32);
	data.iBnoiseFrame = 1;			
}
else
{
	//为正常
	data.length = size + 1;
	memcpy(&data.pData[1], outbuf, size);
	data.iBnoiseFrame = 0;			
}		
//这一步将数据投递到解码队列
pRecordData->push(data);

2.核心函数 prase_amr

  • 这部分是非字节对齐与字节对齐转换的关键,参考了Pjmedia开源库;

	bool bNoise = false;		//是否为噪音帧
	unsigned cnt = 0;
	bool octet_aligned = true;		//默认字节对齐
	pj_uint8_t nFramSize = 0;
	/* Read cursor */
	pj_uint8_t r_bitptr = 0;
	pj_uint8_t *r = (pj_uint8_t*)data;
	pjmedia_codec_amr_bit_info info;
	/* env vars for AMR or AMRWB */
	pj_uint8_t				SID_FT;
	const pj_uint8_t	    *framelen_tbl;
	const pj_uint16_t	    *framelenbit_tbl;
	const pj_int16_t* const *order_maps;

	/* frame info */
	//pjmedia_codec_amr_bit_info *info;
	
	if (amr_nb) {
		SID_FT = 8;
		framelen_tbl = pjmedia_codec_amrnb_framelen;
		framelenbit_tbl = pjmedia_codec_amrnb_framelenbits;
		order_maps = pjmedia_codec_amrnb_ordermaps;
	}
	else {
		SID_FT = 9;
		framelen_tbl = pjmedia_codec_amrwb_framelen;
		framelenbit_tbl = pjmedia_codec_amrwb_framelenbits;
		order_maps = pjmedia_codec_amrwb_ordermaps;
	}

	

	/* Code Mode Request, 4 bits */
	pj_uint8_t cmr = (pj_uint8_t)((*r >> 4) & 0x0F);
	pj_uint8_t reserver = (pj_uint8_t)((*r) & 0x0F);
	if (reserver != 0)
	{
		//非字节对齐
		octet_aligned = false;
	}

	r_bitptr = 4;
	if (octet_aligned) {
		++r;
		r_bitptr = 0;
	}

	/* Table Of Contents, 6 bits each */
	for (;;) {
		pj_uint8_t TOC = 0;
		pj_uint8_t F, FT, Q;

		if (r_bitptr == 0) {
			TOC = (pj_uint8_t)(*r >> 2);
			r_bitptr = 6;
		}
		else if (r_bitptr == 2) {
			TOC = (pj_uint8_t)(*r++ & 0x3F);
			r_bitptr = 0;
		}
		else if (r_bitptr == 4) {
			TOC = (pj_uint8_t)((*r++ & 0x0f) << 2);
			TOC |= *r >> 6;
			r_bitptr = 2;
		}
		else if (r_bitptr == 6) {
			TOC = (pj_uint8_t)((*r++ & 0x03) << 4);
			TOC |= *r >> 4;
			r_bitptr = 4;
		}

		F = (pj_uint8_t)(TOC >> 5);
		FT = (pj_uint8_t)((TOC >> 1) & 0x0F);
		Q = (pj_uint8_t)(TOC & 1);

		if (FT > SID_FT && FT < 14) {
			
			cnt = 0;
			//解析错误
			break;
		}

		if (octet_aligned) {
			++r;
			r_bitptr = 0;
		}

		/* Set frame attributes */
		//info = (pjmedia_codec_amr_bit_info*)&frames[cnt].bit_info;
		info.frame_type = FT;
		info.mode = (pj_int8_t)((FT < SID_FT) ? FT : -1);
		info.good_quality = (pj_uint8_t)(Q == 1);
		info.start_bit = 0;
		info.STI = 0;
	
		if (!F)
			break;
	}
	//*nframes = cnt;

	if (info.frame_type == SID_FT)
		return true;


	/* Speech frames */
	
	pj_uint8_t FT;
	FT = info.frame_type;

	pj_uint8_t *buf = r;
	pj_uint8_t start_bit = r_bitptr;

	//if (FT == SID_FT) {
	//	unsigned sti_bitptr;
	//	sti_bitptr = r_bitptr + 35;
	//	info->STI = (pj_uint8_t)
	//		(r[sti_bitptr >> 3] >> (7 - (sti_bitptr % 8))) & 1;
	//}

	if (octet_aligned) {
		r += framelen_tbl[FT];
		nFramSize = framelen_tbl[FT];
	}
	else {
		if (FT == 14 || FT == 15) {
			/* NO DATA */
			//frames[cnt].size = 0;
		}
		else {
			unsigned adv_bit;

			adv_bit = framelenbit_tbl[FT] + r_bitptr;
			r += adv_bit >> 3;
			r_bitptr = (pj_uint8_t)(adv_bit % 8);

			nFramSize = adv_bit >> 3;
			if (r_bitptr)
			{
				++nFramSize;
			}
			//++frames[cnt].size;
		}
	}
	

	
	// d0 开始, start位=2,字节31

	pj_int8_t    amr_bits[477 + 7] = { 0 };
	pj_int8_t   *p_amr_bits = &amr_bits[0];

	unsigned char outbuf[1024] = { 0 };

	pj_uint8_t  *r1 = (pj_uint8_t*)buf;  /* read cursor */
	pj_uint8_t  *w = (pj_uint8_t*)outbuf; /* write cursor */

	/* env vars for AMR or AMRWB */




	unsigned i;

	//*out_info = *in_info;
	order_maps = pjmedia_codec_amrnb_ordermaps;
	if (amr_nb) {
		SID_FT = 8;
		framelen_tbl = pjmedia_codec_amrnb_framelen;
		framelenbit_tbl = pjmedia_codec_amrnb_framelenbits;
		//bitrate_tbl = pjmedia_codec_amrnb_bitrates;

	}
	else {
		SID_FT = 9;
		framelen_tbl = pjmedia_codec_amrwb_framelen;
		framelenbit_tbl = pjmedia_codec_amrwb_framelenbits;
		//bitrate_tbl = pjmedia_codec_amrwb_bitrates;
		//order_maps = pjmedia_codec_amrwb_ordermaps;
	}

	//PJ_UNUSED_ARG(bitrate_tbl);
	int  frame_type = info.frame_type;

	/* unpack AMR bitstream if there is any data */
	if (frame_type <= SID_FT) {
		i = 0;
		if (start_bit) {
			for (; i < (unsigned)(8 - start_bit); ++i)
				*p_amr_bits++ = (pj_uint8_t)
				((*r1 >> (7 - start_bit - i)) & 1);
			++r1;
		}
		for (; i < framelenbit_tbl[frame_type]; i += 8, ++r1) {
			*p_amr_bits++ = (pj_uint8_t)((*r1 >> 7) & 1);
			*p_amr_bits++ = (pj_uint8_t)((*r1 >> 6) & 1);
			*p_amr_bits++ = (pj_uint8_t)((*r1 >> 5) & 1);
			*p_amr_bits++ = (pj_uint8_t)((*r1 >> 4) & 1);
			*p_amr_bits++ = (pj_uint8_t)((*r1 >> 3) & 1);
			*p_amr_bits++ = (pj_uint8_t)((*r1 >> 2) & 1);
			*p_amr_bits++ = (pj_uint8_t)((*r1 >> 1) & 1);
			*p_amr_bits++ = (pj_uint8_t)((*r1) & 1);
		}
	}



	if (frame_type < SID_FT) {

		/* Speech */
		int mode = info.frame_type;
		// mode = 7, size = 31
		/*out_info->mode = in_info->frame_type;
		size = framelen_tbl[out_info->mode];*/

		//pj_bzero(out->buf, out->size);

		if (reorder)
		{
			const pj_int16_t *order_map = order_maps[mode];

			for (i = 0; i < framelenbit_tbl[mode]; ++i) {
				if (amr_bits[i]) {
					pj_uint16_t bitpos;
					bitpos = order_map[i];
					w[bitpos >> 3] |= 1 << (7 - (bitpos % 8));
				}
			}
		}
		else {
			for (i = 0; i < framelenbit_tbl[mode]; ++i) {
				if (amr_bits[i])
					w[i >> 3] |= 1 << (7 - (i % 8));
			}
		}
		*framesize = nFramSize;
		*byte0 = (info.frame_type << 3) | (info.good_quality << 2);
		memcpy(datsdata, outbuf, nFramSize);
		return true;
	}
	else
	{
		//memcpy(datsdata, outbuf, 31);
		*framesize = 0;
	}
	return false;

不理解程序原理的可以私聊我本人,获取帮助。下面附完整实现。

2.完整源码实现

附下载链接: 完整转换代码

四、amr分类

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

五、一些参考资料

1.说明

RFC3267说明中讲到以下内容:

3.8节,介绍了 Bandwidth Efficient or Octet-aligned Mode,大家可以看下;

  1. 介绍了节省带宽或高效带宽传输模式、八位字节对齐模式;
  2. 使用何种模式,主要是由会话协商决定;
  3. 两种模式之间,八位字节对齐模式能够更好的处理丢包和比特错误;

在这里插入图片描述

4.4节描述了octet-aligned Mode的详细内容
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ShaYQ

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值