librtmp获取视频流和音频流1

来源:互联网 发布:创维酷开电视直播软件 编辑:程序博客网 时间:2024/05/29 16:12

感谢作者:http://blog.csdn.net/byxdaz/article/details/53993791


libRTMP 库从 RTMP 直播服务器不断地获取 RTMP 包,从RTMP包中获取音频和视频数据。RTMP只是一个应用层协议,传输的数据格式都是基于FLV格式的。

我们在推送音、视频包之前,会首先向服务器推送一个音、视频同步包,该包包含了 AAC 音频帧以及 H264 码流的解码信息。因此当我们首次向服务器请求 RTMP 包之后,服务器会下发给我们对应的音、视频同步包,这将决定我们如何解析并重组音、视频。

一、首先要了解FLV格式。

flv文件主要由两部分组成:headerbody

1.header

header部分记录了flv的类型、版本等信息,是flv的开头,一般都差不多,占9bytes。具体格式如下:

文件类型

3 bytes

“FLV”

版本

1 byte

一般为0x01

流信息

1 byte

倒数第一位是1表示有视频,倒数第三位是1表示有音频,倒数第二、四位必须为0

header长度

4 bytes

整个header的长度,一般为9;大于9表示下面还有扩展信息

2.body

body部分由一个个Tag组成,每个Tag的下面有一块4bytes的空间,用来记录这个tag的长度,这个后置用于逆向读取处理,他们的关系如下图:

2.1.Tag

每个Tag由也是由两部分组成的:Tag HeaderTag DataTag Header里存放的是当前Tag的类型、数据区(Tag Data)长度等信息,具体如下:

名称

长度

介绍

Tag类型

1 bytes

8:音频
9
:视频
18
:脚本
其他:保留

数据区长度

3 bytes

在数据区的长度

时间戳

3 bytes

整数,单位是毫秒。对于脚本型的tag总是0

时间戳扩展

1 bytes

将时间戳扩展为4bytes,代表高8位。很少用到

StreamsID

3 bytes

总是0

数据区(data)

由数据区长度决定

数据实体

2.2.Tag Data

数据区根据Tag类型的不同可分为三种,音频数据、视频数据和脚本数据。

2.2.1.音频数据

音频数据包括音频同步包和音频raw数据。

音频同步包格式

名称

二进制值

Hex

介绍

音频格式

4 bits

[AAC]1010

0xA

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

采样率

2 bits

[44kHZ]11

 

0 = 5.5-kHz
1 = 11-kHz
2 = 22-kHz
3 = 44-kHz
对于AAC总是3

采样的长度

1 bit

 

0 = snd8Bit
1 = snd16Bit
压缩过的音频都是16bit

音频类型

1 bit

 

0 = sndMono单声道
1 = sndStereo
,立体声
对于AAC总是1

ACCPacketType

8bit

00000000

只有SoundFormat == 10时才有此8bi的字段

 

0x00,表示音频同步包;0x01,表示音频raw数据。

AudioObjectType 

 

5bits   

[AAC LC]00010

 

 

SampleRateIndex 

4bits

[44100]0100

 

 

ChannelConfig     

 

4bits     [Stereo]0010

 

 

FrameLengthFlag  

 

1bit     

0

 

 

dependOnCoreCoder

1bit    

0

 

 

extensionFlag      

1bit 

0

 

 

4个字节(2bytes AACDecoderSpecificInfo2bytesAudioSpecificConfig

注:speex数据

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

AACsequence header存放的是AudioSpecificConfig结构,该结构则在ISO-14496-3Audio中描述。AudioSpecificConfig结构的描述非常复杂,这里我做一下简化,事先设定要将要编码的音频格式,其中,选择"AAC-LC"为音频编码,音频采样率为44100,于是AudioSpecificConfig简化为下表:

音频raw数据格式

名称

二进制值

Hex

介绍

音频格式

4 bits

[AAC]1010

0xA

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

采样率

2 bits

[44kHZ]11

 

0 = 5.5-kHz
1 = 11-kHz
2 = 22-kHz
3 = 44-kHz
对于AAC总是3

采样的长度

1 bit

 

0 = snd8Bit
1 = snd16Bit
压缩过的音频都是16bit

音频类型

1 bit

 

0 = sndMono单声道
1 = sndStereo
,立体声
对于AAC总是1

ACCPacketType

8bit

只有SoundFormat == 10时才有此8bi的字段

 

0x00,表示音频同步包;0x01,表示音频raw数据。

Raw data

 

 

音频raw数据

AudioSpecificConfig部分参数含义见:

https://wiki.multimedia.cx/index.php?title=MPEG-4_Audio

2.2.2.视频数据

视频同步包

Field

Type

Comment

Frame Type

UB [4]

Type of video frame. The following values are defined:
1 = key frame (for AVC, a seekable frame)
2 = inter frame (for AVC, a non-seekable frame)
3 = disposable inter frame (H.263 only)
4 = generated key frame (reserved for server use only)
5 = video info/command frame

CodecID

UB [4]

Codec Identifier. The following values are defined:
2 = Sorenson H.263
3 = Screen video
4 = On2 VP6
5 = On2 VP6 with alpha channel
6 = Screen video version 2
7 = AVC

AVCPacketType

IF CodecID == 7
UI8

The following values are defined:
0 = AVC sequence header
1 = AVC NALU
2 = AVC end of sequence (lower level NALU sequence ender is not required or supported)

CompositionTime

IF CodecID == 7
SI24

IF AVCPacketType == 1
Composition time offset
ELSE
0
See ISO 14496-12, 8.15.3 for an explanation of composition
times. The offset in an FLV file is always in milliseconds.

VideoTagHeader的头1个字节,也就是接跟着StreamID1个字节包含着视频帧类型及视频CodecID最基本信息.表里列的十分清楚.

VideoTagHeader之后跟着的就是VIDEODATA数据了,也就是video payload.当然就像音频AAC一样,这里也有特例就是如果视频的格式是AVCH.264)的话,VideoTagHeader会多出4个字节的信息.

AVCPacketType CompositionTimeAVCPacketType表示接下来 VIDEODATAAVCVIDEOPACKET的内容:

IF AVCPacketType == 0 AVCDecoderConfigurationRecordAVC sequence header
IF AVCPacketType == 1 
One or more NALUs (Full frames are required)

AVCDecoderConfigurationRecord.包含着是H.264解码相关比较重要的spspps信息,再给AVC解码器送数据流之前一定要把spspps信息送出,否则的话解码器不能正常解码。而且在解码器stop之后再次start之前,如seek、快进快退状态切换等,都需要重新送一遍spspps的信息.AVCDecoderConfigurationRecordFLV文件中一般情况也是出现1次,也就是第一个video tag.

AVC sequence header就是AVCDecoderConfigurationRecord结构,该结构在标准文档ISO-14496-15AVC file format中有详细说明。

2.2.3脚本数据

脚本Tag一般只有一个,是flv的第一个Tag,用于存放flv的信息,比如durationaudiodataratecreatorwidth等。

首先介绍下脚本的数据类型。所有数据都是以数据类型+(数据长度)+数据的格式出现的,数据类型占1byte,数据长度看数据类型是否存在,后面才是数据。
其中数据类型的种类有:

  • 0 = Number type
  • 1 = Boolean type
  • 2 = String type
  • 3 = Object type
  • 4 = MovieClip type
  • 5 = Null type
  • 6 = Undefined type
  • 7 = Reference type
  • 8 = ECMA array type
  • 10 = Strict array type
  • 11 = Date type
  • 12 = Long string type

如果类型为String,后面的2bytes为字符串的长度(LongString4bytes),再后面才是字符串数据;如果是Number类型,后面的8bytesDouble类型的数据;Boolean类型,后面1byteBool类型。

知道了这些后再来看看flv中的脚本,一般开头是0x02,表示String类型,后面的2bytes为字符串长度,一般是0x000a“onMetaData”的长度),再后面就是字符串“onMetaData”。好像flv格式的文件都有onMetaData标记,在运行ActionScript的时候会用到它。后面跟的是0x08,表示ECMA Array类型,这个和Map比较相似,一个键跟着一个值。键都是String类型的,所以开头的0x02被省略了,直接跟着的是字符串的长度,然后是字符串,再是值的类型,也就是上面介绍的那些了。

第一个AMF包:第1个字节表示AMF包类型。

第二个AMF包:

1个字节表示AMF包类型,一般总是0x08,表示数组。第2-5个字节为UI32类型值,表示数组元素的个数。后面即为各数组元素的封装,数组元素为元素名称和值组成的对。常见的数组元素如下表:

一、 通过RTMP_ReadPacket获取一包数据,在里面获取视频、音频、scripttag等。

视频H264获取过程:
1、获取第一视频同步包之后,解析到sps、pps数据。视频头增加H264边界分割符(00 00 00 01 000001分隔符)。
2、其他视频包,解析出nalu数据。视频头增加H264边界分割符。

音频AAC获取过程:
1、获取第一音频同步包之后,解析到AACDecoderSpecificInfoAudioSpecificConfigAudioSpecificConfig用于生成ADST
2、其他音频包,解析出原始音频数据(即es)。
一般的AAC解码器都需要把AACES流打包成ADTS的格式,一般是在AAC ES流前添加7个字节的ADTSheader。也就是说你可以吧ADTS这个头看作是AACframeheader

ADTS AAC

ADTS_header

AAC ES

ADTS_header

AAC ES

...

ADTS_header

AAC ES

ADTS内容及结构

ADTS 头中相对有用的信息 采样率、声道数、帧长度。想想也是,我要是解码器的话,你给我一堆得AAC音频ES流我也解不出来。每一个带ADTS头信息的AAC流会清晰的告送解码器他需要的这些信息。

一般情况下ADTS的头信息都是7个字节,分为2部分:

adts_fixed_header();

adts_variable_header();

syncword :同步头总是0xFFF,all bits must be 1,代表着一个ADTS帧的开始

IDMPEGVersion: 0 for MPEG-4, 1 for MPEG-2

Layeralways: '00'

profile:表示使用哪个级别的AAC,有些芯片只支持AAC LC 。在MPEG-2AAC中定义了3种:

sampling_frequency_index:表示使用的采样率下标,通过这个下标在 Sampling Frequencies[ ]数组中查找得知采样率的值。

Thereare 13 supported frequencies:

  • 0: 96000 Hz
  • 1: 88200 Hz
  • 2: 64000 Hz
  • 3: 48000 Hz
  • 4: 44100 Hz
  • 5: 32000 Hz
  • 6: 24000 Hz
  • 7: 22050 Hz
  • 8: 16000 Hz
  • 9: 12000 Hz
  • 10: 11025 Hz
  • 11: 8000 Hz
  • 12: 7350 Hz
  • 13: Reserved
  • 14: Reserved
  • 15: frequency is written explictly

channel_configuration: 表示声道数 

  • 0: Defined in AOT Specifc Config
  • 1: 1 channel: front-center
  • 2: 2 channels: front-left, front-right
  • 3: 3 channels: front-center, front-left, front-right
  • 4: 4 channels: front-center, front-left, front-right, back-center
  • 5: 5 channels: front-center, front-left, front-right, back-left, back-right
  • 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
  • 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
  • 8-15: Reserved

frame_length :一个ADTS帧的长度包括ADTS头和AAC原始流。

adts_buffer_fullness0x7FF说明是码率可变的码流。

代码

[cpp] view plain copy
  1. /** 
  2.  * 本程序使用<span style="font-family: Arial, Helvetica, sans-serif;">RTMP_ReadPacket与</span><span style="font-family: Arial, Helvetica, sans-serif;">RTMP_ClientPacket方式读取流数据,</span>接收RTMP流媒体并存储视频h264和音频aac文件。 
  3. */  
  4. #include <stdio.h>  
  5. #include "librtmp/rtmp_sys.h"  
  6. #include "librtmp/log.h"  
  7.   
  8. //ACC音频ADTS  
  9. typedef struct tagAACDecoderSpecific  
  10. {  
  11.     unsigned char nAudioFortmatType;    //音频编码类型(0:Liner PCM platform endian,1:PCM,2:mp3,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,14:MP3 8-Khz,15:Device-specific sound)  
  12.     unsigned char nAudioSampleType; //音频采样率(0:5.5kHz,1:11KHz,2:22 kHz,3:44 kHz)  
  13.     unsigned char nAudioSizeType;   //音频采样精度(0:8bits,1:16bits)  
  14.     unsigned char nAudioStereo;//是否立体声(0:sndMono,1:sndStereo)  
  15.     unsigned char nAccPacketType;  
  16. }AACDecoderSpecific;  
  17.   
  18. typedef struct tagAudioSpecificConfig  
  19. {  
  20.     unsigned char nAudioObjectType;  
  21.     unsigned char nSampleFrequencyIndex;  
  22.     unsigned char nChannels;  
  23.     unsigned char nFrameLengthFlag;  
  24.     unsigned char nDependOnCoreCoder;  
  25.     unsigned char nExtensionFlag;  
  26. }AudioSpecificConfig;  
  27.   
  28. int InitSockets()  
  29. {  
  30. #ifdef WIN32  
  31.     WORD version;  
  32.     WSADATA wsaData;  
  33.     version = MAKEWORD(1, 1);  
  34.     return (WSAStartup(version, &wsaData) == 0);  
  35. #endif  
  36. }  
  37.   
  38. void CleanupSockets()  
  39. {  
  40. #ifdef WIN32  
  41.     WSACleanup();  
  42. #endif  
  43. }  
  44.   
  45. //解析ScriptTag  
  46. void ParseScriptTag(char *pBuffer, int nBufferLength, int &nVideoCodecId, int &nVideoWidth, int &nVideoHeight, int &nFrameRate, int &nAudioCodecId, int &nAudioSampleRate, int &nAudioSampleSize, bool &bStereo, int &nFileSize)  
  47. {  
  48.     AMFObject obj;  
  49.     AVal val;  
  50.     AMFObjectProperty * property;  
  51.     AMFObject subObject;  
  52.     int nRes = AMF_Decode(&obj, pBuffer, nBufferLength, FALSE);  
  53.     if (nRes < 0)  
  54.     {  
  55.         //RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);  
  56.         return;  
  57.     }  
  58.   
  59.     AMF_Dump(&obj);  
  60.     for (int n = 0; n < obj.o_num; n++)  
  61.     {  
  62.         property = AMF_GetProp(&obj, NULL, n);  
  63.         if (property != NULL)  
  64.         {  
  65.             if (property->p_type == AMF_OBJECT)  
  66.             {  
  67.                 AMFProp_GetObject(property, &subObject);  
  68.                 for (int m = 0; m < subObject.o_num; m++)  
  69.                 {  
  70.                     property = AMF_GetProp(&subObject, NULL, m);  
  71.                     if (property != NULL)  
  72.                     {  
  73.                         if (property->p_type == AMF_OBJECT)  
  74.                         {  
  75.   
  76.                         }  
  77.                         else if (property->p_type == AMF_BOOLEAN)  
  78.                         {  
  79.                             int bVal = AMFProp_GetBoolean(property);  
  80.                             if (strncmp("stereo", property->p_name.av_val, property->p_name.av_len) == 0)  
  81.                             {  
  82.                                 bStereo = bVal > 0 ? true : false;  
  83.                             }  
  84.                         }  
  85.                         else if (property->p_type == AMF_NUMBER)  
  86.                         {  
  87.                             double dVal = AMFProp_GetNumber(property);  
  88.                             if (strncmp("width", property->p_name.av_val, property->p_name.av_len) == 0)  
  89.                             {  
  90.                                 nVideoWidth = (int)dVal;  
  91.                             }  
  92.                             else if (stricmp("height", property->p_name.av_val) == 0)  
  93.                             {  
  94.                                 nVideoHeight = (int)dVal;  
  95.                             }  
  96.                             else if (stricmp("framerate", property->p_name.av_val) == 0)  
  97.                             {  
  98.                                 nFrameRate = (int)dVal;  
  99.                             }  
  100.                             else if (stricmp("videocodecid", property->p_name.av_val) == 0)  
  101.                             {  
  102.                                 nVideoCodecId = (int)dVal;  
  103.                             }  
  104.                             else if (stricmp("audiosamplerate", property->p_name.av_val) == 0)  
  105.                             {  
  106.                                 nAudioSampleRate = (int)dVal;  
  107.                             }  
  108.                             else if (stricmp("audiosamplesize", property->p_name.av_val) == 0)  
  109.                             {  
  110.                                 nAudioSampleSize = (int)dVal;  
  111.                             }  
  112.                             else if (stricmp("audiocodecid", property->p_name.av_val) == 0)  
  113.                             {  
  114.                                 nAudioCodecId = (int)dVal;  
  115.                             }  
  116.                             else if (stricmp("filesize", property->p_name.av_val) == 0)  
  117.                             {  
  118.                                 nFileSize = (int)dVal;  
  119.                             }  
  120.                         }  
  121.                         else if (property->p_type == AMF_STRING)  
  122.                         {  
  123.                             AMFProp_GetString(property, &val);  
  124.                         }  
  125.   
  126.                     }  
  127.                 }  
  128.             }  
  129.             else  
  130.             {  
  131.                 AMFProp_GetString(property, &val);  
  132.             }  
  133.         }  
  134.     }  
  135. }  
  136.   
  137. //生成ADTS  
  138. int CreateADTS(AudioSpecificConfig ascAudioSpecificConfig, unsigned short nAudioFrameLength, char *pADTS)  
  139. {  
  140.         if (pADTS == NULL)  
  141.         return -1;  
  142.   
  143.     unsigned char adts[7] = { 0 };  
  144.     int chanCfg = ascAudioSpecificConfig.nChannels;  
  145.     // fill in ADTS data  
  146.     adts[0] = (byte)0xFF;  
  147.     adts[1] = (byte)0xF9;  
  148.     adts[2] = (byte)(((ascAudioSpecificConfig.nAudioObjectType - 1) << 6) + (ascAudioSpecificConfig.nSampleFrequencyIndex << 2) + (chanCfg >> 2));  
  149.     adts[3] = (byte)(((chanCfg & 3) << 6) + (nAudioFrameLength >> 11));  
  150.     adts[4] = (byte)((nAudioFrameLength & 0x7FF) >> 3);  
  151.     adts[5] = (byte)(((nAudioFrameLength & 7) << 5) + 0x1F);  
  152.     adts[6] = (byte)0xFC;  
  153.   
  154.     /* 
  155.     int i = 0; 
  156.     adts[i++] = 0xFF;   //AAAAAAAA 
  157.     adts[i++] = 0xF9;   //AAAABCCD 
  158.      
  159.     //calc EEFFFFGH 8 bits 
  160.     adts[i] = ((ascAudioSpecificConfig.nAudioObjectType - 1) << 6) & 0xC0; 
  161.     adts[i++] = (ascAudioSpecificConfig.nSampleFrequencyIndex << 2) & 0x3C; 
  162.      
  163.     adts[i] = (ascAudioSpecificConfig.nChannels << 6) & 0xFF; 
  164.     adts[i++] &= 0xC0; 
  165.  
  166.     unsigned short frameLength = nAudioFrameLength;         //一个ADTS帧的长度包括ADTS头和AAC原始流 
  167.     adts[i++] = ((frameLength << 5) & 0xFF00) >> 8; 
  168.     adts[i] = (frameLength & 0x07) << 5; 
  169.     adts[i++] = 0x1F; 
  170.     adts[6] = 0xFC; 
  171.     */  
  172.     if (pADTS != NULL)  
  173.     {  
  174.         memcpy(pADTS, adts, sizeof(adts));  
  175.     }  
  176.     return 0;  
  177. }  
  178.   
  179. int main(int argc, char* argv[])  
  180. {  
  181.     InitSockets();  
  182.       
  183.     double duration=-1;  
  184.     int nRead;  
  185.     //is live stream ?  
  186.     bool bLiveStream=true;                
  187.       
  188.       
  189.     int bufsize=1024*1024*10;             
  190.     char *buf=(char*)malloc(bufsize);  
  191.     memset(buf,0,bufsize);  
  192.     long countbufsize=0;  
  193.       
  194.     //点播  
  195.     if (0 == 1)  
  196.     {  
  197.         FILE *fp = fopen("receive.flv""wb");  
  198.         if (!fp){  
  199.             RTMP_LogPrintf("Open File Error.\n");  
  200.             CleanupSockets();  
  201.             return -1;  
  202.         }  
  203.   
  204.         /* set log level */  
  205.         //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;  
  206.         //RTMP_LogSetLevel(loglvl);  
  207.   
  208.         RTMP *rtmp = RTMP_Alloc();  
  209.         RTMP_Init(rtmp);  
  210.         //set connection timeout,default 30s  
  211.         rtmp->Link.timeout = 30;  
  212.         // HKS's live URL  
  213.         if(!RTMP_SetupURL(rtmp,"rtmp://live.hkstv.hk.lxdns.com/live/hks"))  
  214.         //if(!RTMP_SetupURL(rtmp,"rtmp://localhost/live/hks"))  
  215.         //if (!RTMP_SetupURL(rtmp, "rtmp://localhost:1935/flvplayback/flv:1.flv"))//rtmp://localhost:1935/flvplayback/mp4:2.mp4  
  216.         //if (!RTMP_SetupURL(rtmp, "rtmp://localhost:1935/flvplayback/mp4:2.mp4"))  
  217.         {  
  218.             RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n");  
  219.             RTMP_Free(rtmp);  
  220.             CleanupSockets();  
  221.             return -1;  
  222.         }  
  223.   
  224.         //1hour  
  225.         RTMP_SetBufferMS(rtmp, 3600 * 1000);  
  226.   
  227.         if (!RTMP_Connect(rtmp, NULL)){  
  228.             RTMP_Log(RTMP_LOGERROR, "Connect Err\n");  
  229.             RTMP_Free(rtmp);  
  230.             CleanupSockets();  
  231.             return -1;  
  232.         }  
  233.   
  234.         if (!RTMP_ConnectStream(rtmp, 0)){  
  235.             RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n");  
  236.             RTMP_Close(rtmp);  
  237.             RTMP_Free(rtmp);  
  238.             CleanupSockets();  
  239.             return -1;  
  240.         }  
  241.   
  242.         //它直接输出的就是FLV文件,包括FLV头,可对流按照flv格式解析就可提取音频,视频数据  
  243.         while(nRead=RTMP_Read(rtmp,buf,bufsize)){  
  244.             fwrite(buf,1,nRead,fp);  
  245.             countbufsize+=nRead;  
  246.             RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n",nRead,countbufsize*1.0/1024);  
  247.         }  
  248.         if (fp)  
  249.             fclose(fp);  
  250.   
  251.         if (buf){  
  252.             free(buf);  
  253.         }  
  254.   
  255.         if (rtmp){  
  256.             RTMP_Close(rtmp);  
  257.             RTMP_Free(rtmp);  
  258.             CleanupSockets();  
  259.             rtmp = NULL;  
  260.         }  
  261.         return 0;  
  262.     }  
  263.   
  264.     //直播  
  265.     if (0 == 0)  
  266.     {  
  267.         FILE *fp = fopen("receive.h264""wb");  
  268.         if (!fp){  
  269.             RTMP_LogPrintf("Open File Error.\n");  
  270.             CleanupSockets();  
  271.             return -1;  
  272.         }  
  273.         FILE *fpAAC = fopen("receive.m4a""wb");  
  274.         if(!fpAAC){  
  275.             RTMP_LogPrintf("Open File Error.\n");  
  276.             CleanupSockets();  
  277.             return -1;  
  278.         }  
  279.   
  280.         /* set log level */  
  281.         //RTMP_LogLevel loglvl=RTMP_LOGDEBUG;  
  282.         //RTMP_LogSetLevel(loglvl);  
  283.   
  284.         RTMP *rtmp = RTMP_Alloc();  
  285.         RTMP_Init(rtmp);  
  286.         //set connection timeout,default 30s  
  287.         rtmp->Link.timeout = 30;  
  288.         // HKS's live URL  
  289.         if(!RTMP_SetupURL(rtmp,"rtmp://live.hkstv.hk.lxdns.com/live/hks"))  
  290.         //if(!RTMP_SetupURL(rtmp,"rtmp://localhost/live/hks"))  
  291.         //if (!RTMP_SetupURL(rtmp, "rtmp://localhost:1935/flvplayback/mp4:2.mp4"))//rtmp://localhost:1935/flvplayback/mp4:2.mp4  
  292.         {  
  293.             RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n");  
  294.             RTMP_Free(rtmp);  
  295.             CleanupSockets();  
  296.             return -1;  
  297.         }  
  298.         if (bLiveStream){  
  299.             rtmp->Link.lFlags |= RTMP_LF_LIVE;  
  300.         }  
  301.   
  302.         //1hour  
  303.         RTMP_SetBufferMS(rtmp, 3600 * 1000);  
  304.   
  305.         if (!RTMP_Connect(rtmp, NULL)){  
  306.             RTMP_Log(RTMP_LOGERROR, "Connect Err\n");  
  307.             RTMP_Free(rtmp);  
  308.             CleanupSockets();  
  309.             return -1;  
  310.         }  
  311.   
  312.         if (!RTMP_ConnectStream(rtmp, 0)){  
  313.             RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n");  
  314.             RTMP_Close(rtmp);  
  315.             RTMP_Free(rtmp);  
  316.             CleanupSockets();  
  317.             return -1;  
  318.         }  
  319.   
  320.         RTMPPacket pc = { 0 };// ps = { 0 };  
  321.         bool bVideoFirst = true;  
  322.         bool bAudioFirst = true;  
  323.         unsigned int nFrameType = 0;  
  324.         unsigned char result = 0;  
  325.         char *data = NULL;  
  326.         //FLV文件头  
  327.         static const char flvHeader[] = { 'F''L''V', 0x01,  
  328.             0x00,               /* 0x04代表有音频, 0x01代表有视频 */  
  329.             0x00, 0x00, 0x00, 0x09,  
  330.             0x00, 0x00, 0x00, 0x00  
  331.         };  
  332.         //fwrite(flvHeader, sizeof(flvHeader), 1, fp);  
  333.         int jjj = 0;  
  334.         AudioSpecificConfig ascAudioSpecificConfig = { 0 };  
  335.         while (RTMP_ReadPacket(rtmp, &pc))  
  336.         {  
  337.             if (RTMPPacket_IsReady(&pc))    //是否读取完毕。((a)->m_nBytesRead == (a)->m_nBodySize)    
  338.             {  
  339.                 if (!pc.m_nBodySize)  
  340.                     continue;  
  341.                 if (pc.m_packetType == RTMP_PACKET_TYPE_VIDEO && RTMP_ClientPacket(rtmp, &pc))  
  342.                 {  
  343.                     jjj++;  
  344.                     if (jjj > 1000)  
  345.                         break;  
  346.                     result = pc.m_body[0];  
  347.                     data = pc.m_body;  
  348.                     bool bIsKeyFrame = false;  
  349.                     if (result == 0x17)//I frame  
  350.                     {  
  351.                         bIsKeyFrame = true;  
  352.                     }  
  353.                     else if (result == 0x27)  
  354.                     {  
  355.                         bIsKeyFrame = false;  
  356.                     }  
  357.                     static unsigned char const start_code[4] = { 0x00, 0x00, 0x00, 0x01 };  
  358.                     //fwrite(start_code, 1, 4, fp);  
  359.                     //int ret = fwrite(pc.m_body + 9, 1, pc.m_nBodySize-9, pf);  
  360.                     if (bVideoFirst)  
  361.                     {  
  362.                         fwrite(start_code, 1, 4, fp);  
  363.                         //AVCsequence header  
  364.                         //ioBuffer.put(foredata);  
  365.                         //Access to SPS  
  366.                         int spsnum = data[10] & 0x1f;  
  367.                         int number_sps = 11;  
  368.                         int count_sps = 1;  
  369.                         while (count_sps <= spsnum){  
  370.                             int spslen = (data[number_sps] & 0x000000FF) << 8 | (data[number_sps + 1] & 0x000000FF);  
  371.                             number_sps += 2;  
  372.                             fwrite(data + number_sps, 1, spslen, fp);  
  373.                             fwrite(start_code, 1, 4, fp);  
  374.                             number_sps += spslen;  
  375.                             count_sps++;  
  376.                         }  
  377.                         //Get PPS  
  378.                         int ppsnum = data[number_sps] & 0x1f;  
  379.                         int number_pps = number_sps + 1;  
  380.                         int count_pps = 1;  
  381.                         while (count_pps <= ppsnum){  
  382.                             int ppslen = (data[number_pps] & 0x000000FF) << 8 | data[number_pps + 1] & 0x000000FF;  
  383.                             number_pps += 2;  
  384.                             fwrite(data + number_pps, 1, ppslen, fp);  
  385.                             fwrite(start_code, 1, 4, fp);  
  386.                             number_pps += ppslen;  
  387.                             count_pps++;  
  388.                         }  
  389.                         bVideoFirst = false;  
  390.                     }  
  391.                     else  
  392.                     {  
  393.                         //AVCNALU  
  394.                         int len = 0;  
  395.                         int num = 5;  
  396.                         while (num<pc.m_nBodySize)  
  397.                         {  
  398.                             len = (data[num] & 0x000000FF) << 24 | (data[num + 1] & 0x000000FF) << 16 | (data[num + 2] & 0x000000FF) << 8 | data[num + 3] & 0x000000FF;  
  399.                             num = num + 4;  
  400.                             fwrite(data + num, 1, len, fp);  
  401.                             fwrite(start_code, 1, 4, fp);  
  402.                             num = num + len;  
  403.                         }  
  404.                     }  
  405.                 }  
  406.                 else if (pc.m_packetType == RTMP_PACKET_TYPE_AUDIO && RTMP_ClientPacket(rtmp, &pc))  
  407.                 {  
  408.                     data = pc.m_body;  
  409.                     if (bAudioFirst)  
  410.                     {  
  411.                         //ACC音频同步包(1bytes+3bytes AccAudioData) (4bytes 包含了AACDecoderSpecific和AudioSpecificConfig)  
  412.                         AACDecoderSpecific adsAACDecoderSpecific = { 0 };  
  413.                         adsAACDecoderSpecific.nAudioFortmatType = (data[0] & 0xf0) >> 4;  //音频编码类型(0:Liner PCM platform endian,1:PCM,2:mp3,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,14:MP3 8-Khz,15:Device-specific sound)  
  414.                         adsAACDecoderSpecific.nAudioSampleType = (data[0] & 0x0c) >> 2;   //音频采样率(0:5.5kHz,1:11KHz,2:22 kHz,3:44 kHz)  
  415.                         adsAACDecoderSpecific.nAudioSizeType = (data[0] & 0x02) >> 1; //音频采样精度(0:8bits,1:16bits)  
  416.                         adsAACDecoderSpecific.nAudioStereo = data[0] & 0x01;//是否立体声(0:sndMono,1:sndStereo)  
  417.                         if(adsAACDecoderSpecific.nAudioFortmatType == 10)  
  418.                         {  
  419.                             //The following values are defined:  
  420.                             //0 = AAC sequence header  
  421.                             //1 = AAC raw  
  422.                             adsAACDecoderSpecific.nAccPacketType = data[1];  
  423.                             unsigned short audioSpecificConfig = 0;  
  424.                             audioSpecificConfig = (data[2] & 0xff) << 8;  
  425.                             audioSpecificConfig += 0x00ff & data[3];  
  426.                             ascAudioSpecificConfig.nAudioObjectType = (audioSpecificConfig & 0xF800) >> 11;  
  427.                             ascAudioSpecificConfig.nSampleFrequencyIndex = (audioSpecificConfig & 0x0780) >> 7;  
  428.                             ascAudioSpecificConfig.nChannels = (audioSpecificConfig & 0x78) >> 3;  
  429.                             ascAudioSpecificConfig.nFrameLengthFlag = (audioSpecificConfig & 0x04) >> 2;  
  430.                             ascAudioSpecificConfig.nDependOnCoreCoder = (audioSpecificConfig & 0x02) >> 1;  
  431.                             ascAudioSpecificConfig.nExtensionFlag = audioSpecificConfig & 0x01;  
  432.                         }  
  433.                         else if(adsAACDecoderSpecific.nAudioFortmatType == 11)  
  434.                         {  
  435.                             //speex类型数据时,后面的4位数据不起作用,固定的是16KHZ,单声道,16bit/sample  
  436.                             adsAACDecoderSpecific.nAudioStereo = 0;  
  437.                             adsAACDecoderSpecific.nAudioSizeType = 1;  
  438.                             adsAACDecoderSpecific.nAudioSampleType = 4;  
  439.                         }  
  440.   
  441.                         bAudioFirst = false;  
  442.                     }  
  443.                     else  
  444.                     {  
  445.                         if(pc.m_nBodySize > 2)  
  446.                         {  
  447.                             if(data[1] == 1)  
  448.                             {  
  449.                                 //raw data 获取 audio payload  
  450.                                 //写ADTS数据到文件  
  451.                                 char szADTSTemp[8] = { 0 };  
  452.                                 CreateADTS(ascAudioSpecificConfig, pc.m_nBodySize - 2 + 7, szADTSTemp);  
  453.                                 fwrite(szADTSTemp, 7, 1, fpAAC);  
  454.                                 //写raw数据到文件  
  455.                                 fwrite(pc.m_body + 2, pc.m_nBodySize - 2, 1, fpAAC);  
  456.                             }  
  457.                             else  
  458.                             {  
  459.                                 int kkkkk = 0;  
  460.                             }  
  461.                         }  
  462.                         else  
  463.                         {  
  464.                             int kkkk = 0;  
  465.                         }  
  466.                     }  
  467.                           
  468.                     //The actual audio content for the pkt-> m_body+1, size is pkt-> m_nBodySize-1. Here is the voice of Speex code.  
  469.                 }  
  470.                 else if (pc.m_packetType == RTMP_PACKET_TYPE_INFO && RTMP_ClientPacket(rtmp, &pc))  
  471.                 {  
  472.                     int nVideoCodecId = 0;  
  473.                     int nVideoWidth = 255;  
  474.                     int nVideoHeight = 188;  
  475.                     int nVideoFrameRate = 25;  
  476.                     int nAudioCodecId = 0;  
  477.                     int nAudioSampleRate = 0;  
  478.                     int nAudioSampleSize = 0;  
  479.                     bool bStereo = false;           //立体声  
  480.                     int nFileSize = 0;  
  481.                     ParseScriptTag(pc.m_body, pc.m_nBodySize, nVideoCodecId, nVideoWidth, nVideoHeight, nVideoFrameRate, nAudioCodecId, nAudioSampleRate, nAudioSampleSize, bStereo, nFileSize);  
  482.                     int k = 0;  
  483.                 }  
  484.                 RTMPPacket_Free(&pc);  
  485.             }  
  486.         }  
  487.   
  488.         if (fp)  
  489.             fclose(fp);  
  490.   
  491.         if (fpAAC)  
  492.             fclose(fpAAC);  
  493.   
  494.         if (buf){  
  495.             free(buf);  
  496.         }  
  497.   
  498.         if (rtmp){  
  499.             RTMP_Close(rtmp);  
  500.             RTMP_Free(rtmp);  
  501.             CleanupSockets();  
  502.             rtmp = NULL;  
  503.         }  
  504.     }  
  505.   
  506.     return 0;  
  507. }  

注:H.264视频分析软件(Elecard.Streameye.Tools)分析H264数据,使用暴风影音播放m4a文件中的aac音频。

代码下载:点击链接

参考资料:
http://blog.csdn.net/leixiaohua1020/article/details/42104893
http://blog.chinaunix.net/uid-15063109-id-4273162.html
http://blog.csdn.net/bsplover/article/details/7426511
http://blog.csdn.net/zqf_office/article/details/50868520