在网上找了一份G726和PCM互相转化的代码(链接)。在VS2015下配置然后编译。使用附带的.pcm音频文件《上海滩》进行其demo代码测试,会发现成功运行但是有问题,pcm转成g726后,再将g726转成pcm,使用audacity进行pcm原始数据播放会发现声音变质了,就是转码有问题。本人基于其源码的(g726.c/g726.h)开发了一个g726转码类(AvG726),亲测可用。
源码g726.h
源代码有点长就不贴了,主要贴g726转码类(全部文件下载链接)
源码g726.c
源代码有点长就不贴了,主要贴g726转码类(全部文件下载链接)
g726转码类(AvG726.h)
#ifndef _AV_G726_H_
#define _AV_G726_H_
/***********************************************************
** Author:kaychan
** Data:2019-11-20
** Mail:1203375695@qq.com
** Explain:a g726 codec class base on g726codec source
***********************************************************/
#include "g726.h"
#include <string.h>
#include <stdlib.h>
typedef enum AvG726Bps_E{
AvG726Bps_16K = 2,
AvG726Bps_24K = 3,
AvG726Bps_32K = 4,
AvG726Bps_40K = 5,
}AvG726Bps;
class AvG726 {
public:
AvG726(AvG726Bps bps = AvG726Bps_32K);
~AvG726();
int encode(unsigned char **odata, unsigned char *idata, int ilen);
int decode(unsigned char **odata, unsigned char *idata, int ilen);
void free_output_data(unsigned char *odata);
private:
g726_state_t *g726_state_;
AvG726Bps bps_;
};
#endif
g726转码类(AvG726.cpp)
#include "AvG726.h"
AvG726::AvG726(AvG726Bps bps) {
g726_state_ = NULL;
g726_state_ = (g726_state_t *)malloc(sizeof(g726_state_t));
if (g726_state_) {
bps_ = bps;
g726_state_ = g726_init(g726_state_, 8000 * bps);
}
}
AvG726::~AvG726() {
free(g726_state_);
}
int AvG726::encode(unsigned char **odata, unsigned char *idata, int ilen) {
if (g726_state_ && ilen > 0) {
int olen = (int)((bps_ * 8000.) / 128000. * ilen);
*odata = (unsigned char *)malloc(sizeof(unsigned char) * olen);
if(*odata)
return g726_encode(g726_state_, *odata, (short *)idata, ilen / 2);
}
return -1;
}
int AvG726::decode(unsigned char **odata, unsigned char *idata, int ilen) {
if (g726_state_ && ilen > 0) {
int olen = (int)(128000. / (bps_ * 8000.) * ilen);
*odata = (unsigned char *)malloc(sizeof(unsigned char) * olen);
if (*odata)
return (2 * g726_decode(g726_state_, (short *)(*odata), idata, ilen));
}
return -1;
}
void AvG726::free_output_data(unsigned char *odata) {
free(odata);
}
源码demo的bug
这里说下源码下demo的bug,demo的encode和decode的时候传入的输入数据的长度这个参数设置的不对。在encode的时候传入的数据长度为读取的pcm数据的一半g726_encode(g726_state_, *odata, (short *)idata, ilen / 2);,在decode的时候传入的数据长度为读取的g726的数据长度,但是在g726_decode函数返回的时候,返回的解码长度应该乘以2,才是真正的解码输出的pcm数据的长度return (2 * g726_decode(g726_state_, (short *)(*odata), idata, ilen));。并且这边通过枚举enum AvG726Bps_E可以精确的计算转码输出的长度olen大小以精确的分配内存,无需用户分配,但是需要用户使用函数void free_output_data(unsigned char *odata);释放内存。
olen的计算方法
encode的时候int olen = (int)((bps_ * 8000.) / 128000. * ilen);
因为raw pcm的比特率为128k,以编码为40kbps的g726为例,则bps_ = 5,5*8k = 40k,则压缩率为40k/128k,再乘以输入的长度可以算出编码后的数据大小,即olen。
decode的时候int olen = (int)(128000. / (bps_ * 8000.) * ilen);
同理,反过来即可算出解码后的pcm数据大小。
调用实例
编码
AvG726 g726(AvG726Bps_32K);
FILE *ifile = fopen("E:/sht.pcm", "rb");
FILE *ofile = fopen("E:/sht_32.g726", "wb");
unsigned char ibuf[200] = { 0 };
int rr = 1;
while (rr > 0) {
rr = fread(ibuf, 1, 200, ifile);
if (rr > 0) {
unsigned char *obuf;
int len = g726.encode(&obuf, ibuf, rr);
fwrite(obuf, 1, len, ofile);
g726.free_output_data(obuf);
memset(ibuf, 0, sizeof(ibuf));
}
}
fclose(ifile);
fclose(ofile);
解码
AvG726 g726(AvG726Bps_32K);
FILE *ifile = fopen("E:/sht_32.g726", "rb");
FILE *ofile = fopen("E:/sht_32.pcm", "wb");
unsigned char ibuf[200] = { 0 };
int rr = 1;
while (rr > 0) {
rr = fread(ibuf, 1, 200, ifile);
if (rr > 0) {
unsigned char *obuf;
int len = g726.decode(&obuf, ibuf, rr);
fwrite(obuf, 1, len, ofile);
g726.free_output_data(obuf);
memset(ibuf, 0, sizeof(ibuf));
}
}
fclose(ifile);
fclose(ofile);
遇到的问题
在使用raw pcm分别转成16k,24k,32k,40kbps的g726时,使用软件Toolsoft Audio Player播放G726时,16k,24k,40kbps完全听不清楚,沙沙沙的声音(ffplayer播放亦是如此),32kbps的可以听出声音,但是有杂音,不知道为何???,但是从改沙沙沙有问题的g726文件转回pcm数据,再去播放pcm声音却是正常的!说明编解码无问题。所以就纳闷了???
更多笔记
1.G726规定了如何将128kbps的raw pcm(64kbps的g711u/g711a)信号转为40kbps,32kbps,24kbps,16kbps的g726音频信号。
3.ffmpeg-ffplayer音频播放命令
ffplay -f g726 -ar 8000 -ac 1 -code_size 3 -i xxx_24.g726 // asf类型g726
ffplay -f g726le -ar 8000 -ac 1 -code_size 3 -i xxx_24.g726 // RFC3551标准g726
ffplay -f s16le -ar 8000 -ac 1 -i xxx.pcm // pcm
ffplay -f alaw -ar 8000 -ac 1 -i 8k_1_16.g711a // g711a 8k 1ch 16bit
ffplay -f mulaw -ar 16000 -ac 2 -i 16k_2_16.g711u // g711u 16k 2ch 16bit
ffplay -f aac -ar 44100 -ac 1 -i xxx.aac // aac samplerate@44100Hz