加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Flex客户端设置speex编码时Red5对音频数据的处理以及将speex解码

发布时间:2020-12-15 04:33:08 所属栏目:百科 来源:网络整理
导读:§?客户端设置音频编码格式speex且包中只一个帧 §?red5接收帧并添加帧长 §?解码时依据帧长获取帧数据解码 1.flex客户端的设置 因为flex只支持Nellymoser(默认的)和speex,我们决定使用speex编码方式,解码后转成AAC编码格式,我们看一下flex提供的音频接口

§?客户端设置音频编码格式speex且包中只一个帧

§?red5接收帧并添加帧长

§?解码时依据帧长获取帧数据解码

1.flex客户端的设置

因为flex只支持Nellymoser(默认的)和speex,我们决定使用speex编码方式,解码后转成AAC编码格式,我们看一下flex提供的音频接口,引用一篇博客的内容:

一、flex中提供的音频接口

? ? 用flex编写客户端,它提供的接口是封装过的,与speex标准编解码器之间的调用实际上相当于一个黑盒,它们之间的差异需要我们分析。

? ? 麦克风音频的接口由类Mircophone提供,大多都有中文注释,我就不一一赘述了,只挑出其中一些做自己的讲解。

codec

编码格式,只支持Nellymoser、speex两种格式,

Nellymoser多用于游戏开发,而且商业使用限制较多

rate

设置采样频率,注意是麦克风采样率,而非编码采样率

framesPerPacket

一个音频包中包含的音频帧数量(后文会有更详细的说明)

encodeQuality

编码质量,在同等编码采样率下,质量越高,效果越好,

但每帧所包含的数据越多。当该值也确定下来时,

每帧数据的字节大小也就确定了

enableVAD

是否开启Voice Activation Detection。它的作用自己google。

当开启时,静音状态下speex编码器将持续编码10字节大小的音频帧

?

? ?speex编码有三种模式

模式

编码采样率

一帧数据

所表示的时间

编码一帧需要的

sample数量

narrow band(窄带)

8khz

20ms

160

wide band(宽带)

16khz

20ms

320

ultra-wide band(超宽带)

32khz

20ms

640

二、RTMP中的speex数据

? ? 每个音频包(相当于FLV中的audiotag)中,第一个字节的前四位表示编码格式,等于11说明为speex编码。后4个字节分别表示编码采样率、单声道or立体声、每个sample大小8位or16位。但采用speex编码时,它们是固定不变的,协议中的为无效数据。编码采样率恒为16khz,单声道,16bit/sample。

???剩余的数据为音频帧数据,可以为多帧的集合,取决于前文提到过的framesPerPacket。在flex中的默认值为2,故每个音频包中有两帧数据。注意,当前文所说的VAD功能开启时,两帧数据可以是两帧实际数据,也可以是两帧10字节数据,还可以各占一帧。

?

?

?

客户端麦克风属性framesPerPacket 表示一个音频包中包含的音频数量,

framesPerPacket = 1时, 一个音频包中只有一个帧

服务端接收两种长度数据

43=1+42 (1为audio data 的头)

11=1+10

其中43为客户端正常发声是数据,11为客户端不发生数据,

framesPerPacket = 2时,一个音频包中有两个帧

服务端接收三种长度数据

85 = 1+42+42

53= 1+42+10

21 = 1+10+10

最前面一个字节是头,后面跟的都是帧数据

为了简便起见,我们在客户端将framesPerPacket 设置为1这样服务端收到长度为11和长度为43的帧数据,其中第一个字节是头,在这里我们收到的基本是B2一个字节

?

B2化成二进制是1011 0010,前四个字节是整数11,代表这个是speex的Audio data,我们看一下Audio data的解析:

我们在服务端收到的RTMP报中可以获得RTMP数据类型

如果TAG包中的TagType==8时,就表示这个TAGaudio

StreamID之后的数据就表示是AudioTagHeaderAudioTagHeader结构如下:

Field

Type

Comment

SoundFormat

UB [4]

Format of SoundData. The following values are defined:
0 = Linear PCM,platform endian
1 = ADPCM
2 = MP3
3 = Linear PCM,little endian
4 = Nellymoser 16 kHz mono
5 = Nellymoser 8 kHz mono
6 = Nellymoser
7 = G.711 A-law logarithmic PCM
8 = G.711 mu-law logarithmic PCM
9 = reserved
10 = AAC
11 = Speex
14 = MP3 8 kHz
15 = Device-specific sound
Formats 7,8,14,and 15 are reserved.
AAC is supported in Flash Player 9,115,0 and higher.
Speex is supported in Flash Player 10 and higher.

SoundRate

UB [2]

Sampling rate. The following values are defined:
0 = 5.5 kHz
1 = 11 kHz
2 = 22 kHz
3 = 44 kHz

SoundSize

UB [1]

Size of each audio sample. This parameter only pertains to
uncompressed formats. Compressed formats always decode
to 16 bits internally.
0 = 8-bit samples
1 = 16-bit samples

SoundType

UB [1]

Mono or stereo sound
0 = Mono sound
1 = Stereo sound

AACPacketType

IF SoundFormat == 10
UI8

The following values are defined:
0 = AAC sequence header
1 = AAC raw

?

AudioTagHeader的头1个字节,也就是接跟着StreamID1个字节包含着音频类型、采样率等的基本信息.表里列的十分清楚.

?

?

有一点要注意的是,上面提到过的,当类型是11,也就是speex类型数据时,后面的4位数据不起作用,固定的是16KHZ,单声道,16bit/sample

?

这样我们可以分析数据了,第一位是头B2,后面紧跟的是帧数据,当客户端不发声的时候,我们就接受到11字节的数据,发声时接受的是43字节的数据

?

?

2.服务端red5对数据的处理

我们决定根据没真的长度来对speex数据解码,所以在服务端接收到数据时,得出数据的长度,用四个字节(或少点)来保存帧长度(这个长度是去掉头的长的,就是10或42),

然后将长度的四个字节保存下来,再保存帧数据这样我们保存下来的数据应该是

长度,帧数据,长度,帧数据。。。。

?

3.speex解码成pcm

我们使用speex库来解码,speex库依赖于ogg库,所以我们将这两个库编译后添加到工程里。

我使用了objective –c,解码后添加wav头,然后就可以播放了。

?

//按帧的字节数解码

NSData *decodeSpeexData(NSData *data){

???if (data == nil){

??????? NSLog(@"datais nil...");

??????? return nil;

???}

???

???int nPos?= 0;

???char *buf = (char *)[databytes];

???int maxLen = [data length];

?

???nPos += sizeof(SpeexHeader);

???if (nPos >= maxLen) {

??????? return nil;

???}

???

???//这时取出来的是纯speex数据

???buf += nPos;

???//--------------------------------------

???

? ?

???int frames = 0;

???

???short pcmFrame[FRAME_SIZE];

???spx_int16_t output[FRAME_SIZE];

???

???int tmp = 1;

???void *dec_state = speex_decoder_init(&speex_wb_mode);

???speex_decoder_ctl(dec_state, SPEEX_SET_ENH,&tmp);

???

???NSMutableData *PCMRawData = [[[NSMutableDataalloc]init]autorelease];

???

???SpeexBits bits;

???speex_bits_init(&bits);

???for (; ; ) {

??????? int nbBytes = 0;

??????? if (data.length >4) {

??????????? Byte *len = (Byte *)[[datasubdataWithRange:NSMakeRange(0,4)]bytes];

??????????? Byte byte = len[3];

???????????nbBytes = (int)byte;

??????????? NSData *subdata =[data subdataWithRange:NSMakeRange(4,nbBytes)];

???????????buf = (char *)[subdata bytes];

???????????data = [data subdataWithRange:NSMakeRange(nbBytes+4,data.length-nbBytes-4)];

???????

? ??????}else{

??????????? ?break;

??????? }

???????

? ??????speex_bits_read_from(&bits,buf,nbBytes);

??????? speex_decode_int(dec_state,&bits,output);

???????

? ??????for (int i =0; i <FRAME_SIZE; i++) {

???????????pcmFrame[i] = output[i];

??????? }

???????

??????? [PCMRawData appendBytes:pcmFrame length:sizeof(short)*FRAME_SIZE];

???????

//??????? buf += nbBytes;

??????? frames++;

???}

???

???speex_bits_destroy(&bits);

???speex_decoder_destroy(dec_state);

???

???

???NSMutableData *outData = [[[NSMutableDataalloc]init]autorelease];

WriteWAVEHeader(outData,frames);

???[outData appendData:PCMRawData];

???

???NSString *speexfilepath = [[NSHomeDirectory()stringByAppendingPathComponent:@"Documents"]stringByAppendingPathComponent:[NSStringstringWithFormat:@"%.0f.%@",[NSDatetimeIntervalSinceReferenceDate] *1000.0,@"pcm"]];

???[PCMRawData writeToFile:speexfilepathatomically:YES];

???

???return outData;

}

?

void WriteWAVEHeader(NSMutableData*fpwave,int nFrame)

{

char tag[10] ="";

//1. RIFF

RIFFHEADER riff;

strcpy(tag,"RIFF");

memcpy(riff.chRiffID,tag,4);

riff.nRiffSize = 4????????????????????????????????????// WAVE

+ sizeof(XCHUNKHEADER)??????????????// fmt

+ sizeof(WAVEFORMATX)??????????//WAVEFORMATX

+ sizeof(XCHUNKHEADER)??????????????// DATA

+ nFrame*320*sizeof(short);???//

strcpy(tag,"WAVE");

memcpy(riff.chRiffFormat,4);

//fwrite(&riff,1,sizeof(RIFFHEADER),fpwave);

???[fpwave appendBytes:&riff length:sizeof(RIFFHEADER)];

// 2. FMT

XCHUNKHEADER chunk;

WAVEFORMATX wfx;

strcpy(tag,"fmt");

memcpy(chunk.chChunkID,4);

???unsigned?? short?? m_pcmData;

chunk.nChunkSize = sizeof(WAVEFORMATX);

//fwrite(&chunk,sizeof(XCHUNKHEADER),fpwave);

???[fpwave appendBytes:&chunk length:sizeof(XCHUNKHEADER)];

memset(&wfx,0,sizeof(WAVEFORMATX));

wfx.nFormatTag = 1;

wfx.nChannels = 1; // 单声道

wfx.nSamplesPerSec = 16000; // 8khz

wfx.nAvgBytesPerSec = wfx.nSamplesPerSec*sizeof(m_pcmData);

wfx.nBlockAlign = 2;

wfx.nBitsPerSample = 16; // 16

???//fwrite(&wfx,sizeof(WAVEFORMATX),fpwave);

???[fpwave appendBytes:&wfx length:sizeof(WAVEFORMATX)];

// 3. data块头

strcpy(tag,"data");

memcpy(chunk.chChunkID,4);

chunk.nChunkSize = nFrame*320*sizeof(short);

//fwrite(&chunk,fpwave);

???[fpwave appendBytes:&chunk length:sizeof(XCHUNKHEADER)];

???

}

?

?

头文件内容如下

?

#defineFRAME_SIZE 320// speex音频16khz*20ms-> 16000*0.02=320

#define MAX_NB_BYTES 200

#define SPEEX_SAMPLE_RATE 16000

?

typedef struct

{

char chChunkID[4];

int nChunkSize;

}XCHUNKHEADER;

?

typedef struct

{

short nFormatTag;

short nChannels;

intnSamplesPerSec;

intnAvgBytesPerSec;

shortnBlockAlign;

shortnBitsPerSample;

}WAVEFORMAT;

?

typedef struct

{

short nFormatTag;

short nChannels;

int nSamplesPerSec;

intnAvgBytesPerSec;

shortnBlockAlign;

shortnBitsPerSample;

short nExSize;

}WAVEFORMATX;

?

typedef struct

{

char chRiffID[4];

int nRiffSize;

charchRiffFormat[4];

}RIFFHEADER;

?

typedef struct

{

char chFmtID[4];

int nFmtSize;

WAVEFORMAT wf;

}FMTBLOCK;

?

@interface SpeexCodec: NSObject

?

int EncodeWAVEFileToSpeexFile(constchar*pchWAVEFilename,constchar*pchAMRFileName,int nChannels,intnBitsPerSample);

?

int DecodeSpeexFileToWAVEFile(constchar*pchAMRFileName,constchar* pchWAVEFilename);

?

NSData* DecodeSpeexToWAVE(NSData* data);

NSData* EncodeWAVEToSpeex(NSData* data,intnBitsPerSample);

?

NSData* addHeaderSpeexData(NSData *data);

?

// 根据帧头计算当前帧大小

float CalculatePlayTime(NSData *speexData,int frame_size);

?

void encodeToSpeexStream();

void decodeSpeexStream();

NSData *decodeSpeexData(NSData *data);

?

@end

?

?

?

?

?

备注: 一些相关文档的链接

http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Microphone.html#enableVAD

?

http://www.cnblogs.com/chef/archive/2012/08/17/2643464.html

?

http://www.voidcn.com/article/p-wfvsehki-bgv.html

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读