Flex客户端设置speex编码时Red5对音频数据的处理以及将speex解码
§?客户端设置音频编码格式speex且包中只一个帧 §?red5接收帧并添加帧长 §?解码时依据帧长获取帧数据解码 1.flex客户端的设置 因为flex只支持Nellymoser(默认的)和speex,我们决定使用speex编码方式,解码后转成AAC编码格式,我们看一下flex提供的音频接口,引用一篇博客的内容: 一、flex中提供的音频接口 ? ? 用flex编写客户端,它提供的接口是封装过的,与speex标准编解码器之间的调用实际上相当于一个黑盒,它们之间的差异需要我们分析。 ? ? 麦克风音频的接口由类Mircophone提供,大多都有中文注释,我就不一一赘述了,只挑出其中一些做自己的讲解。
? ? ?speex编码有三种模式
二、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时,就表示这个TAG是audio。 StreamID之后的数据就表示是AudioTagHeader,AudioTagHeader结构如下:
? AudioTagHeader的头1个字节,也就是接跟着StreamID的1个字节包含着音频类型、采样率等的基本信息.表里列的十分清楚. ? ? 有一点要注意的是,上面提到过的,当类型是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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |