H264视频通过RTMP直播

 前面的文章中提到了通过RTSP(Real Time Streaming Protocol)的方式来实现视频的直播,但RTSP方式的一个弊端是如果需要支持客户端通过网页来访问,就需要在在页面中嵌入一个ActiveX控件,而ActiveX一般都需要签名才能正常使用,否则用户在使用时还需要更改浏览器设置,并且ActiveX还只支持IE内核的浏览器,Chrome、FireFox需要IE插件才能运行,因此会特别影响用户体验。而RTMP(Real Time Messaging Protocol)很好的解决了这一个问题。由于RTMP是针对FLASH的流媒体协议,视频通过RTMP直播后,只需要在WEB上嵌入一个Web Player(如Jwplayer)即可观看,而且对平台也没什么限制,还可以方便的通过手机观看。

       视频通过RTMP方式发布需要一个RTMP Server(常见的有FMS、Wowza Media Server, 开源的有CRtmpServer、Red5等),原始视频只要按照RTMP协议发送给RTMP Server就可以RTMP视频流的发布了。为了便于视频的打包发布,封装了一个RTMPStream,目前只支持发送H264的视频文件。可以直接发送H264数据帧或H264文件,RTMPStream提供的接口如下。

  1. class CRTMPStream    
  2. {    
  3. public:    
  4.     CRTMPStream(void);    
  5.     ~CRTMPStream(void);    
  6. public:    
  7.     // 连接到RTMP Server     
  8.     bool Connect(const char* url);    
  9.     // 断开连接     
  10.     void Close();    
  11.     // 发送MetaData     
  12.     bool SendMetadata(LPRTMPMetadata lpMetaData);    
  13.     // 发送H264数据帧     
  14.     bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);    
  15.     // 发送H264文件     
  16.     bool SendH264File(const char *pFileName);    
  17. //...     
  18. }    
  1. class CRTMPStream    
  2. {    
  3. public:    
  4.     CRTMPStream(void);    
  5.     ~CRTMPStream(void);    
  6. public:    
  7.     // 连接到RTMP Server    
  8.     bool Connect(const char* url);    
  9.     // 断开连接    
  10.     void Close();    
  11.     // 发送MetaData    
  12.     bool SendMetadata(LPRTMPMetadata lpMetaData);    
  13.     // 发送H264数据帧    
  14.     bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);    
  15.     // 发送H264文件    
  16.     bool SendH264File(const char *pFileName);    
  17. //...    
  18. }    
调用示例:
  1. #include <stdio.h>     
  2. #include "RTMPStream\RTMPStream.h"     
  3.     
  4. int main(int argc,char* argv[])    
  5. {    
  6.     CRTMPStream rtmpSender;    
  7.     
  8.     bool bRet = rtmpSender.Connect("rtmp://192.168.1.104/live/test");    
  9.     
  10.     rtmpSender.SendH264File("E:\\video\\test.264");    
  11.     
  12.     rtmpSender.Close();    
  13. }    
  1. #include <stdio.h>    
  2. #include "RTMPStream\RTMPStream.h"    
  3.     
  4. int main(int argc,char* argv[])    
  5. {    
  6.     CRTMPStream rtmpSender;    
  7.     
  8.     bool bRet = rtmpSender.Connect("rtmp://192.168.1.104/live/test");    
  9.     
  10.     rtmpSender.SendH264File("E:\\video\\test.264");    
  11.     
  12.     rtmpSender.Close();    
  13. }    

通过JwPlayer播放效果如下:


最后附上RTMPStream完整的代码:

  1. /********************************************************************  
  2. filename:   RTMPStream.h 
  3. created:    2013-04-3 
  4. author:     firehood  
  5. purpose:    发送H264视频到RTMP Server,使用libRtmp库 
  6. *********************************************************************/   
  7. #pragma once   
  8. #include "rtmp.h"   
  9. #include "rtmp_sys.h"   
  10. #include "amf.h"   
  11. #include <stdio.h>   
  12.   
  13. #define FILEBUFSIZE (1024 * 1024 * 10)       //  10M   
  14.   
  15. // NALU单元   
  16. typedef struct _NaluUnit  
  17. {  
  18.     int type;  
  19.     int size;  
  20.     unsigned char *data;  
  21. }NaluUnit;  
  22.   
  23. typedef struct _RTMPMetadata  
  24. {  
  25.     // video, must be h264 type   
  26.     unsigned int    nWidth;  
  27.     unsigned int    nHeight;  
  28.     unsigned int    nFrameRate;     // fps   
  29.     unsigned int    nVideoDataRate; // bps   
  30.     unsigned int    nSpsLen;  
  31.     unsigned char   Sps[1024];  
  32.     unsigned int    nPpsLen;  
  33.     unsigned char   Pps[1024];  
  34.   
  35.     // audio, must be aac type   
  36.     bool            bHasAudio;  
  37.     unsigned int    nAudioSampleRate;  
  38.     unsigned int    nAudioSampleSize;  
  39.     unsigned int    nAudioChannels;  
  40.     char            pAudioSpecCfg;  
  41.     unsigned int    nAudioSpecCfgLen;  
  42.   
  43. } RTMPMetadata,*LPRTMPMetadata;  
  44.   
  45.   
  46. class CRTMPStream  
  47. {  
  48. public:  
  49.     CRTMPStream(void);  
  50.     ~CRTMPStream(void);  
  51. public:  
  52.     // 连接到RTMP Server   
  53.     bool Connect(const char* url);  
  54.     // 断开连接   
  55.     void Close();  
  56.     // 发送MetaData   
  57.     bool SendMetadata(LPRTMPMetadata lpMetaData);  
  58.     // 发送H264数据帧   
  59.     bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);  
  60.     // 发送H264文件   
  61.     bool SendH264File(const char *pFileName);  
  62. private:  
  63.     // 送缓存中读取一个NALU包   
  64.     bool ReadOneNaluFromBuf(NaluUnit &nalu);  
  65.     // 发送数据   
  66.     int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp);  
  67. private:  
  68.     RTMP* m_pRtmp;  
  69.     unsigned char* m_pFileBuf;  
  70.     unsigned int  m_nFileBufSize;  
  71.     unsigned int  m_nCurPos;  
  72. };  
  1. /********************************************************************  
  2. filename:   RTMPStream.h 
  3. created:    2013-04-3 
  4. author:     firehood  
  5. purpose:    发送H264视频到RTMP Server,使用libRtmp库 
  6. *********************************************************************/   
  7. #pragma once  
  8. #include "rtmp.h"  
  9. #include "rtmp_sys.h"  
  10. #include "amf.h"  
  11. #include <stdio.h>  
  12.   
  13. #define FILEBUFSIZE (1024 * 1024 * 10)       //  10M  
  14.   
  15. // NALU单元  
  16. typedef struct _NaluUnit  
  17. {  
  18.     int type;  
  19.     int size;  
  20.     unsigned char *data;  
  21. }NaluUnit;  
  22.   
  23. typedef struct _RTMPMetadata  
  24. {  
  25.     // video, must be h264 type  
  26.     unsigned int    nWidth;  
  27.     unsigned int    nHeight;  
  28.     unsigned int    nFrameRate;     // fps  
  29.     unsigned int    nVideoDataRate; // bps  
  30.     unsigned int    nSpsLen;  
  31.     unsigned char   Sps[1024];  
  32.     unsigned int    nPpsLen;  
  33.     unsigned char   Pps[1024];  
  34.   
  35.     // audio, must be aac type  
  36.     bool            bHasAudio;  
  37.     unsigned int    nAudioSampleRate;  
  38.     unsigned int    nAudioSampleSize;  
  39.     unsigned int    nAudioChannels;  
  40.     char            pAudioSpecCfg;  
  41.     unsigned int    nAudioSpecCfgLen;  
  42.   
  43. } RTMPMetadata,*LPRTMPMetadata;  
  44.   
  45.   
  46. class CRTMPStream  
  47. {  
  48. public:  
  49.     CRTMPStream(void);  
  50.     ~CRTMPStream(void);  
  51. public:  
  52.     // 连接到RTMP Server  
  53.     bool Connect(const char* url);  
  54.     // 断开连接  
  55.     void Close();  
  56.     // 发送MetaData  
  57.     bool SendMetadata(LPRTMPMetadata lpMetaData);  
  58.     // 发送H264数据帧  
  59.     bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);  
  60.     // 发送H264文件  
  61.     bool SendH264File(const char *pFileName);  
  62. private:  
  63.     // 送缓存中读取一个NALU包  
  64.     bool ReadOneNaluFromBuf(NaluUnit &nalu);  
  65.     // 发送数据  
  66.     int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp);  
  67. private:  
  68.     RTMP* m_pRtmp;  
  69.     unsigned char* m_pFileBuf;  
  70.     unsigned int  m_nFileBufSize;  
  71.     unsigned int  m_nCurPos;  
  72. };  

  1. /********************************************************************  
  2. filename:   RTMPStream.cpp 
  3. created:    2013-04-3 
  4. author:     firehood  
  5. purpose:    发送H264视频到RTMP Server,使用libRtmp库 
  6. *********************************************************************/   
  7. #include "RTMPStream.h"   
  8. #include "SpsDecode.h"   
  9. #ifdef WIN32     
  10. #include <windows.h>   
  11. #endif   
  12.   
  13. #ifdef WIN32   
  14. #pragma comment(lib,"WS2_32.lib")   
  15. #pragma comment(lib,"winmm.lib")   
  16. #endif   
  17.   
  18. enum  
  19. {  
  20.     FLV_CODECID_H264 = 7,  
  21. };  
  22.   
  23. int InitSockets()    
  24. {    
  25. #ifdef WIN32     
  26.     WORD version;    
  27.     WSADATA wsaData;    
  28.     version = MAKEWORD(1, 1);    
  29.     return (WSAStartup(version, &wsaData) == 0);    
  30. #else     
  31.     return TRUE;    
  32. #endif     
  33. }    
  34.   
  35. inline void CleanupSockets()    
  36. {    
  37. #ifdef WIN32     
  38.     WSACleanup();    
  39. #endif     
  40. }    
  41.   
  42. char * put_byte( char *output, uint8_t nVal )    
  43. {    
  44.     output[0] = nVal;    
  45.     return output+1;    
  46. }    
  47. char * put_be16(char *output, uint16_t nVal )    
  48. {    
  49.     output[1] = nVal & 0xff;    
  50.     output[0] = nVal >> 8;    
  51.     return output+2;    
  52. }    
  53. char * put_be24(char *output,uint32_t nVal )    
  54. {    
  55.     output[2] = nVal & 0xff;    
  56.     output[1] = nVal >> 8;    
  57.     output[0] = nVal >> 16;    
  58.     return output+3;    
  59. }    
  60. char * put_be32(char *output, uint32_t nVal )    
  61. {    
  62.     output[3] = nVal & 0xff;    
  63.     output[2] = nVal >> 8;    
  64.     output[1] = nVal >> 16;    
  65.     output[0] = nVal >> 24;    
  66.     return output+4;    
  67. }    
  68. char *  put_be64( char *output, uint64_t nVal )    
  69. {    
  70.     output=put_be32( output, nVal >> 32 );    
  71.     output=put_be32( output, nVal );    
  72.     return output;    
  73. }    
  74. char * put_amf_string( char *c, const char *str )    
  75. {    
  76.     uint16_t len = strlen( str );    
  77.     c=put_be16( c, len );    
  78.     memcpy(c,str,len);    
  79.     return c+len;    
  80. }    
  81. char * put_amf_double( char *c, double d )    
  82. {    
  83.     *c++ = AMF_NUMBER;  /* type: Number */    
  84.     {    
  85.         unsigned char *ci, *co;    
  86.         ci = (unsigned char *)&d;    
  87.         co = (unsigned char *)c;    
  88.         co[0] = ci[7];    
  89.         co[1] = ci[6];    
  90.         co[2] = ci[5];    
  91.         co[3] = ci[4];    
  92.         co[4] = ci[3];    
  93.         co[5] = ci[2];    
  94.         co[6] = ci[1];    
  95.         co[7] = ci[0];    
  96.     }    
  97.     return c+8;    
  98. }  
  99.   
  100. CRTMPStream::CRTMPStream(void):  
  101. m_pRtmp(NULL),  
  102. m_nFileBufSize(0),  
  103. m_nCurPos(0)  
  104. {  
  105.     m_pFileBuf = new unsigned char[FILEBUFSIZE];  
  106.     memset(m_pFileBuf,0,FILEBUFSIZE);  
  107.     InitSockets();  
  108.     m_pRtmp = RTMP_Alloc();    
  109.     RTMP_Init(m_pRtmp);    
  110. }  
  111.   
  112. CRTMPStream::~CRTMPStream(void)  
  113. {  
  114.     Close();  
  115.     WSACleanup();    
  116.     delete[] m_pFileBuf;  
  117. }  
  118.   
  119. bool CRTMPStream::Connect(const char* url)  
  120. {  
  121.     if(RTMP_SetupURL(m_pRtmp, (char*)url)<0)  
  122.     {  
  123.         return FALSE;  
  124.     }  
  125.     RTMP_EnableWrite(m_pRtmp);  
  126.     if(RTMP_Connect(m_pRtmp, NULL)<0)  
  127.     {  
  128.         return FALSE;  
  129.     }  
  130.     if(RTMP_ConnectStream(m_pRtmp,0)<0)  
  131.     {  
  132.         return FALSE;  
  133.     }  
  134.     return TRUE;  
  135. }  
  136.   
  137. void CRTMPStream::Close()  
  138. {  
  139.     if(m_pRtmp)  
  140.     {  
  141.         RTMP_Close(m_pRtmp);  
  142.         RTMP_Free(m_pRtmp);  
  143.         m_pRtmp = NULL;  
  144.     }  
  145. }  
  146.   
  147. int CRTMPStream::SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp)  
  148. {  
  149.     if(m_pRtmp == NULL)  
  150.     {  
  151.         return FALSE;  
  152.     }  
  153.   
  154.     RTMPPacket packet;  
  155.     RTMPPacket_Reset(&packet);  
  156.     RTMPPacket_Alloc(&packet,size);  
  157.   
  158.     packet.m_packetType = nPacketType;  
  159.     packet.m_nChannel = 0x04;    
  160.     packet.m_headerType = RTMP_PACKET_SIZE_LARGE;    
  161.     packet.m_nTimeStamp = nTimestamp;    
  162.     packet.m_nInfoField2 = m_pRtmp->m_stream_id;  
  163.     packet.m_nBodySize = size;  
  164.     memcpy(packet.m_body,data,size);  
  165.   
  166.     int nRet = RTMP_SendPacket(m_pRtmp,&packet,0);  
  167.   
  168.     RTMPPacket_Free(&packet);  
  169.   
  170.     return nRet;  
  171. }  
  172.   
  173. bool CRTMPStream::SendMetadata(LPRTMPMetadata lpMetaData)  
  174. {  
  175.     if(lpMetaData == NULL)  
  176.     {  
  177.         return false;  
  178.     }  
  179.     char body[1024] = {0};;  
  180.       
  181.     char * p = (char *)body;    
  182.     p = put_byte(p, AMF_STRING );  
  183.     p = put_amf_string(p , "@setDataFrame" );  
  184.   
  185.     p = put_byte( p, AMF_STRING );  
  186.     p = put_amf_string( p, "onMetaData" );  
  187.   
  188.     p = put_byte(p, AMF_OBJECT );    
  189.     p = put_amf_string( p, "copyright" );    
  190.     p = put_byte(p, AMF_STRING );    
  191.     p = put_amf_string( p, "firehood" );    
  192.   
  193.     p =put_amf_string( p, "width");  
  194.     p =put_amf_double( p, lpMetaData->nWidth);  
  195.   
  196.     p =put_amf_string( p, "height");  
  197.     p =put_amf_double( p, lpMetaData->nHeight);  
  198.   
  199.     p =put_amf_string( p, "framerate" );  
  200.     p =put_amf_double( p, lpMetaData->nFrameRate);   
  201.   
  202.     p =put_amf_string( p, "videocodecid" );  
  203.     p =put_amf_double( p, FLV_CODECID_H264 );  
  204.   
  205.     p =put_amf_string( p, "" );  
  206.     p =put_byte( p, AMF_OBJECT_END  );  
  207.   
  208.     int index = p-body;  
  209.   
  210.     SendPacket(RTMP_PACKET_TYPE_INFO,(unsigned char*)body,p-body,0);  
  211.   
  212.     int i = 0;  
  213.     body[i++] = 0x17; // 1:keyframe  7:AVC   
  214.     body[i++] = 0x00; // AVC sequence header   
  215.   
  216.     body[i++] = 0x00;  
  217.     body[i++] = 0x00;  
  218.     body[i++] = 0x00; // fill in 0;   
  219.   
  220.     // AVCDecoderConfigurationRecord.   
  221.     body[i++] = 0x01; // configurationVersion   
  222.     body[i++] = lpMetaData->Sps[1]; // AVCProfileIndication   
  223.     body[i++] = lpMetaData->Sps[2]; // profile_compatibility   
  224.     body[i++] = lpMetaData->Sps[3]; // AVCLevelIndication    
  225.     body[i++] = 0xff; // lengthSizeMinusOne     
  226.   
  227.     // sps nums   
  228.     body[i++] = 0xE1; //&0x1f   
  229.     // sps data length   
  230.     body[i++] = lpMetaData->nSpsLen>>8;  
  231.     body[i++] = lpMetaData->nSpsLen&0xff;  
  232.     // sps data   
  233.     memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);  
  234.     i= i+lpMetaData->nSpsLen;  
  235.   
  236.     // pps nums   
  237.     body[i++] = 0x01; //&0x1f   
  238.     // pps data length    
  239.     body[i++] = lpMetaData->nPpsLen>>8;  
  240.     body[i++] = lpMetaData->nPpsLen&0xff;  
  241.     // sps data   
  242.     memcpy(&body[i],lpMetaData->Pps,lpMetaData->nPpsLen);  
  243.     i= i+lpMetaData->nPpsLen;  
  244.   
  245.     return SendPacket(RTMP_PACKET_TYPE_VIDEO,(unsigned char*)body,i,0);  
  246.   
  247. }  
  248.   
  249. bool CRTMPStream::SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp)  
  250. {  
  251.     if(data == NULL && size<11)  
  252.     {  
  253.         return false;  
  254.     }  
  255.   
  256.     unsigned char *body = new unsigned char[size+9];  
  257.   
  258.     int i = 0;  
  259.     if(bIsKeyFrame)  
  260.     {  
  261.         body[i++] = 0x17;// 1:Iframe  7:AVC   
  262.     }  
  263.     else  
  264.     {  
  265.         body[i++] = 0x27;// 2:Pframe  7:AVC   
  266.     }  
  267.     body[i++] = 0x01;// AVC NALU   
  268.     body[i++] = 0x00;  
  269.     body[i++] = 0x00;  
  270.     body[i++] = 0x00;  
  271.   
  272.     // NALU size   
  273.     body[i++] = size>>24;  
  274.     body[i++] = size>>16;  
  275.     body[i++] = size>>8;  
  276.     body[i++] = size&0xff;;  
  277.   
  278.     // NALU data   
  279.     memcpy(&body[i],data,size);  
  280.   
  281.     bool bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp);  
  282.   
  283.     delete[] body;  
  284.   
  285.     return bRet;  
  286. }  
  287.   
  288. bool CRTMPStream::SendH264File(const char *pFileName)  
  289. {  
  290.     if(pFileName == NULL)  
  291.     {  
  292.         return FALSE;  
  293.     }  
  294.     FILE *fp = fopen(pFileName, "rb");    
  295.     if(!fp)    
  296.     {    
  297.         printf("ERROR:open file %s failed!",pFileName);  
  298.     }    
  299.     fseek(fp, 0, SEEK_SET);  
  300.     m_nFileBufSize = fread(m_pFileBuf, sizeof(unsigned char), FILEBUFSIZE, fp);  
  301.     if(m_nFileBufSize >= FILEBUFSIZE)  
  302.     {  
  303.         printf("warning : File size is larger than BUFSIZE\n");  
  304.     }  
  305.     fclose(fp);    
  306.   
  307.     RTMPMetadata metaData;  
  308.     memset(&metaData,0,sizeof(RTMPMetadata));  
  309.   
  310.     NaluUnit naluUnit;  
  311.     // 读取SPS帧   
  312.     ReadOneNaluFromBuf(naluUnit);  
  313.     metaData.nSpsLen = naluUnit.size;  
  314.     memcpy(metaData.Sps,naluUnit.data,naluUnit.size);  
  315.   
  316.     // 读取PPS帧   
  317.     ReadOneNaluFromBuf(naluUnit);  
  318.     metaData.nPpsLen = naluUnit.size;  
  319.     memcpy(metaData.Pps,naluUnit.data,naluUnit.size);  
  320.   
  321.     // 解码SPS,获取视频图像宽、高信息   
  322.     int width = 0,height = 0;  
  323.     h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height);  
  324.     metaData.nWidth = width;  
  325.     metaData.nHeight = height;  
  326.     metaData.nFrameRate = 25;  
  327.      
  328.     // 发送MetaData   
  329.     SendMetadata(&metaData);  
  330.   
  331.     unsigned int tick = 0;  
  332.     while(ReadOneNaluFromBuf(naluUnit))  
  333.     {  
  334.         bool bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;  
  335.         // 发送H264数据帧   
  336.         SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick);  
  337.         msleep(40);  
  338.         tick +=40;  
  339.     }  
  340.   
  341.     return TRUE;  
  342. }  
  343.   
  344. bool CRTMPStream::ReadOneNaluFromBuf(NaluUnit &nalu)  
  345. {  
  346.     int i = m_nCurPos;  
  347.     while(i<m_nFileBufSize)  
  348.     {  
  349.         if(m_pFileBuf[i++] == 0x00 &&  
  350.             m_pFileBuf[i++] == 0x00 &&  
  351.             m_pFileBuf[i++] == 0x00 &&  
  352.             m_pFileBuf[i++] == 0x01  
  353.             )  
  354.         {  
  355.             int pos = i;  
  356.             while (pos<m_nFileBufSize)  
  357.             {  
  358.                 if(m_pFileBuf[pos++] == 0x00 &&  
  359.                     m_pFileBuf[pos++] == 0x00 &&  
  360.                     m_pFileBuf[pos++] == 0x00 &&  
  361.                     m_pFileBuf[pos++] == 0x01  
  362.                     )  
  363.                 {  
  364.                     break;  
  365.                 }  
  366.             }  
  367.             if(pos == nBufferSize)  
  368.             {  
  369.                 nalu.size = pos-i;    
  370.             }  
  371.             else  
  372.             {  
  373.                 nalu.size = (pos-4)-i;  
  374.             }  
  375.             nalu.type = m_pFileBuf[i]&0x1f;  
  376.             nalu.data = &m_pFileBuf[i];  
  377.   
  378.             m_nCurPos = pos-4;  
  379.             return TRUE;  
  380.         }  
  381.     }  
  382.     return FALSE;  
  383. }  
  1. /********************************************************************  
  2. filename:   RTMPStream.cpp 
  3. created:    2013-04-3 
  4. author:     firehood  
  5. purpose:    发送H264视频到RTMP Server,使用libRtmp库 
  6. *********************************************************************/   
  7. #include "RTMPStream.h"  
  8. #include "SpsDecode.h"  
  9. #ifdef WIN32    
  10. #include <windows.h>  
  11. #endif  
  12.   
  13. #ifdef WIN32  
  14. #pragma comment(lib,"WS2_32.lib")  
  15. #pragma comment(lib,"winmm.lib")  
  16. #endif  
  17.   
  18. enum  
  19. {  
  20.     FLV_CODECID_H264 = 7,  
  21. };  
  22.   
  23. int InitSockets()    
  24. {    
  25. #ifdef WIN32    
  26.     WORD version;    
  27.     WSADATA wsaData;    
  28.     version = MAKEWORD(1, 1);    
  29.     return (WSAStartup(version, &wsaData) == 0);    
  30. #else    
  31.     return TRUE;    
  32. #endif    
  33. }    
  34.   
  35. inline void CleanupSockets()    
  36. {    
  37. #ifdef WIN32    
  38.     WSACleanup();    
  39. #endif    
  40. }    
  41.   
  42. char * put_byte( char *output, uint8_t nVal )    
  43. {    
  44.     output[0] = nVal;    
  45.     return output+1;    
  46. }    
  47. char * put_be16(char *output, uint16_t nVal )    
  48. {    
  49.     output[1] = nVal & 0xff;    
  50.     output[0] = nVal >> 8;    
  51.     return output+2;    
  52. }    
  53. char * put_be24(char *output,uint32_t nVal )    
  54. {    
  55.     output[2] = nVal & 0xff;    
  56.     output[1] = nVal >> 8;    
  57.     output[0] = nVal >> 16;    
  58.     return output+3;    
  59. }    
  60. char * put_be32(char *output, uint32_t nVal )    
  61. {    
  62.     output[3] = nVal & 0xff;    
  63.     output[2] = nVal >> 8;    
  64.     output[1] = nVal >> 16;    
  65.     output[0] = nVal >> 24;    
  66.     return output+4;    
  67. }    
  68. char *  put_be64( char *output, uint64_t nVal )    
  69. {    
  70.     output=put_be32( output, nVal >> 32 );    
  71.     output=put_be32( output, nVal );    
  72.     return output;    
  73. }    
  74. char * put_amf_string( char *c, const char *str )    
  75. {    
  76.     uint16_t len = strlen( str );    
  77.     c=put_be16( c, len );    
  78.     memcpy(c,str,len);    
  79.     return c+len;    
  80. }    
  81. char * put_amf_double( char *c, double d )    
  82. {    
  83.     *c++ = AMF_NUMBER;  /* type: Number */    
  84.     {    
  85.         unsigned char *ci, *co;    
  86.         ci = (unsigned char *)&d;    
  87.         co = (unsigned char *)c;    
  88.         co[0] = ci[7];    
  89.         co[1] = ci[6];    
  90.         co[2] = ci[5];    
  91.         co[3] = ci[4];    
  92.         co[4] = ci[3];    
  93.         co[5] = ci[2];    
  94.         co[6] = ci[1];    
  95.         co[7] = ci[0];    
  96.     }    
  97.     return c+8;    
  98. }  
  99.   
  100. CRTMPStream::CRTMPStream(void):  
  101. m_pRtmp(NULL),  
  102. m_nFileBufSize(0),  
  103. m_nCurPos(0)  
  104. {  
  105.     m_pFileBuf = new unsigned char[FILEBUFSIZE];  
  106.     memset(m_pFileBuf,0,FILEBUFSIZE);  
  107.     InitSockets();  
  108.     m_pRtmp = RTMP_Alloc();    
  109.     RTMP_Init(m_pRtmp);    
  110. }  
  111.   
  112. CRTMPStream::~CRTMPStream(void)  
  113. {  
  114.     Close();  
  115.     WSACleanup();    
  116.     delete[] m_pFileBuf;  
  117. }  
  118.   
  119. bool CRTMPStream::Connect(const char* url)  
  120. {  
  121.     if(RTMP_SetupURL(m_pRtmp, (char*)url)<0)  
  122.     {  
  123.         return FALSE;  
  124.     }  
  125.     RTMP_EnableWrite(m_pRtmp);  
  126.     if(RTMP_Connect(m_pRtmp, NULL)<0)  
  127.     {  
  128.         return FALSE;  
  129.     }  
  130.     if(RTMP_ConnectStream(m_pRtmp,0)<0)  
  131.     {  
  132.         return FALSE;  
  133.     }  
  134.     return TRUE;  
  135. }  
  136.   
  137. void CRTMPStream::Close()  
  138. {  
  139.     if(m_pRtmp)  
  140.     {  
  141.         RTMP_Close(m_pRtmp);  
  142.         RTMP_Free(m_pRtmp);  
  143.         m_pRtmp = NULL;  
  144.     }  
  145. }  
  146.   
  147. int CRTMPStream::SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp)  
  148. {  
  149.     if(m_pRtmp == NULL)  
  150.     {  
  151.         return FALSE;  
  152.     }  
  153.   
  154.     RTMPPacket packet;  
  155.     RTMPPacket_Reset(&packet);  
  156.     RTMPPacket_Alloc(&packet,size);  
  157.   
  158.     packet.m_packetType = nPacketType;  
  159.     packet.m_nChannel = 0x04;    
  160.     packet.m_headerType = RTMP_PACKET_SIZE_LARGE;    
  161.     packet.m_nTimeStamp = nTimestamp;    
  162.     packet.m_nInfoField2 = m_pRtmp->m_stream_id;  
  163.     packet.m_nBodySize = size;  
  164.     memcpy(packet.m_body,data,size);  
  165.   
  166.     int nRet = RTMP_SendPacket(m_pRtmp,&packet,0);  
  167.   
  168.     RTMPPacket_Free(&packet);  
  169.   
  170.     return nRet;  
  171. }  
  172.   
  173. bool CRTMPStream::SendMetadata(LPRTMPMetadata lpMetaData)  
  174. {  
  175.     if(lpMetaData == NULL)  
  176.     {  
  177.         return false;  
  178.     }  
  179.     char body[1024] = {0};;  
  180.       
  181.     char * p = (char *)body;    
  182.     p = put_byte(p, AMF_STRING );  
  183.     p = put_amf_string(p , "@setDataFrame" );  
  184.   
  185.     p = put_byte( p, AMF_STRING );  
  186.     p = put_amf_string( p, "onMetaData" );  
  187.   
  188.     p = put_byte(p, AMF_OBJECT );    
  189.     p = put_amf_string( p, "copyright" );    
  190.     p = put_byte(p, AMF_STRING );    
  191.     p = put_amf_string( p, "firehood" );    
  192.   
  193.     p =put_amf_string( p, "width");  
  194.     p =put_amf_double( p, lpMetaData->nWidth);  
  195.   
  196.     p =put_amf_string( p, "height");  
  197.     p =put_amf_double( p, lpMetaData->nHeight);  
  198.   
  199.     p =put_amf_string( p, "framerate" );  
  200.     p =put_amf_double( p, lpMetaData->nFrameRate);   
  201.   
  202.     p =put_amf_string( p, "videocodecid" );  
  203.     p =put_amf_double( p, FLV_CODECID_H264 );  
  204.   
  205.     p =put_amf_string( p, "" );  
  206.     p =put_byte( p, AMF_OBJECT_END  );  
  207.   
  208.     int index = p-body;  
  209.   
  210.     SendPacket(RTMP_PACKET_TYPE_INFO,(unsigned char*)body,p-body,0);  
  211.   
  212.     int i = 0;  
  213.     body[i++] = 0x17; // 1:keyframe  7:AVC  
  214.     body[i++] = 0x00; // AVC sequence header  
  215.   
  216.     body[i++] = 0x00;  
  217.     body[i++] = 0x00;  
  218.     body[i++] = 0x00; // fill in 0;  
  219.   
  220.     // AVCDecoderConfigurationRecord.  
  221.     body[i++] = 0x01; // configurationVersion  
  222.     body[i++] = lpMetaData->Sps[1]; // AVCProfileIndication  
  223.     body[i++] = lpMetaData->Sps[2]; // profile_compatibility  
  224.     body[i++] = lpMetaData->Sps[3]; // AVCLevelIndication   
  225.     body[i++] = 0xff; // lengthSizeMinusOne    
  226.   
  227.     // sps nums  
  228.     body[i++] = 0xE1; //&0x1f  
  229.     // sps data length  
  230.     body[i++] = lpMetaData->nSpsLen>>8;  
  231.     body[i++] = lpMetaData->nSpsLen&0xff;  
  232.     // sps data  
  233.     memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);  
  234.     i= i+lpMetaData->nSpsLen;  
  235.   
  236.     // pps nums  
  237.     body[i++] = 0x01; //&0x1f  
  238.     // pps data length   
  239.     body[i++] = lpMetaData->nPpsLen>>8;  
  240.     body[i++] = lpMetaData->nPpsLen&0xff;  
  241.     // sps data  
  242.     memcpy(&body[i],lpMetaData->Pps,lpMetaData->nPpsLen);  
  243.     i= i+lpMetaData->nPpsLen;  
  244.   
  245.     return SendPacket(RTMP_PACKET_TYPE_VIDEO,(unsigned char*)body,i,0);  
  246.   
  247. }  
  248.   
  249. bool CRTMPStream::SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp)  
  250. {  
  251.     if(data == NULL && size<11)  
  252.     {  
  253.         return false;  
  254.     }  
  255.   
  256.     unsigned char *body = new unsigned char[size+9];  
  257.   
  258.     int i = 0;  
  259.     if(bIsKeyFrame)  
  260.     {  
  261.         body[i++] = 0x17;// 1:Iframe  7:AVC  
  262.     }  
  263.     else  
  264.     {  
  265.         body[i++] = 0x27;// 2:Pframe  7:AVC  
  266.     }  
  267.     body[i++] = 0x01;// AVC NALU  
  268.     body[i++] = 0x00;  
  269.     body[i++] = 0x00;  
  270.     body[i++] = 0x00;  
  271.   
  272.     // NALU size  
  273.     body[i++] = size>>24;  
  274.     body[i++] = size>>16;  
  275.     body[i++] = size>>8;  
  276.     body[i++] = size&0xff;;  
  277.   
  278.     // NALU data  
  279.     memcpy(&body[i],data,size);  
  280.   
  281.     bool bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp);  
  282.   
  283.     delete[] body;  
  284.   
  285.     return bRet;  
  286. }  
  287.   
  288. bool CRTMPStream::SendH264File(const char *pFileName)  
  289. {  
  290.     if(pFileName == NULL)  
  291.     {  
  292.         return FALSE;  
  293.     }  
  294.     FILE *fp = fopen(pFileName, "rb");    
  295.     if(!fp)    
  296.     {    
  297.         printf("ERROR:open file %s failed!",pFileName);  
  298.     }    
  299.     fseek(fp, 0, SEEK_SET);  
  300.     m_nFileBufSize = fread(m_pFileBuf, sizeof(unsigned char), FILEBUFSIZE, fp);  
  301.     if(m_nFileBufSize >= FILEBUFSIZE)  
  302.     {  
  303.         printf("warning : File size is larger than BUFSIZE\n");  
  304.     }  
  305.     fclose(fp);    
  306.   
  307.     RTMPMetadata metaData;  
  308.     memset(&metaData,0,sizeof(RTMPMetadata));  
  309.   
  310.     NaluUnit naluUnit;  
  311.     // 读取SPS帧  
  312.     ReadOneNaluFromBuf(naluUnit);  
  313.     metaData.nSpsLen = naluUnit.size;  
  314.     memcpy(metaData.Sps,naluUnit.data,naluUnit.size);  
  315.   
  316.     // 读取PPS帧  
  317.     ReadOneNaluFromBuf(naluUnit);  
  318.     metaData.nPpsLen = naluUnit.size;  
  319.     memcpy(metaData.Pps,naluUnit.data,naluUnit.size);  
  320.   
  321.     // 解码SPS,获取视频图像宽、高信息  
  322.     int width = 0,height = 0;  
  323.     h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height);  
  324.     metaData.nWidth = width;  
  325.     metaData.nHeight = height;  
  326.     metaData.nFrameRate = 25;  
  327.      
  328.     // 发送MetaData  
  329.     SendMetadata(&metaData);  
  330.   
  331.     unsigned int tick = 0;  
  332.     while(ReadOneNaluFromBuf(naluUnit))  
  333.     {  
  334.         bool bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;  
  335.         // 发送H264数据帧  
  336.         SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick);  
  337.         msleep(40);  
  338.         tick +=40;  
  339.     }  
  340.   
  341.     return TRUE;  
  342. }  
  343.   
  344. bool CRTMPStream::ReadOneNaluFromBuf(NaluUnit &nalu)  
  345. {  
  346.     int i = m_nCurPos;  
  347.     while(i<m_nFileBufSize)  
  348.     {  
  349.         if(m_pFileBuf[i++] == 0x00 &&  
  350.             m_pFileBuf[i++] == 0x00 &&  
  351.             m_pFileBuf[i++] == 0x00 &&  
  352.             m_pFileBuf[i++] == 0x01  
  353.             )  
  354.         {  
  355.             int pos = i;  
  356.             while (pos<m_nFileBufSize)  
  357.             {  
  358.                 if(m_pFileBuf[pos++] == 0x00 &&  
  359.                     m_pFileBuf[pos++] == 0x00 &&  
  360.                     m_pFileBuf[pos++] == 0x00 &&  
  361.                     m_pFileBuf[pos++] == 0x01  
  362.                     )  
  363.                 {  
  364.                     break;  
  365.                 }  
  366.             }  
  367.             if(pos == nBufferSize)  
  368.             {  
  369.                 nalu.size = pos-i;    
  370.             }  
  371.             else  
  372.             {  
  373.                 nalu.size = (pos-4)-i;  
  374.             }  
  375.             nalu.type = m_pFileBuf[i]&0x1f;  
  376.             nalu.data = &m_pFileBuf[i];  
  377.   
  378.             m_nCurPos = pos-4;  
  379.             return TRUE;  
  380.         }  
  381.     }  
  382.     return FALSE;  
  383. }  

附上SpsDecode.h文件:

  1. #include <stdio.h>   
  2. #include <math.h>   
  3.   
  4. UINT Ue(BYTE *pBuff, UINT nLen, UINT &nStartBit)  
  5. {  
  6.     //计算0bit的个数   
  7.     UINT nZeroNum = 0;  
  8.     while (nStartBit < nLen * 8)  
  9.     {  
  10.         if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8))) //&:按位与,%取余   
  11.         {  
  12.             break;  
  13.         }  
  14.         nZeroNum++;  
  15.         nStartBit++;  
  16.     }  
  17.     nStartBit ++;  
  18.   
  19.   
  20.     //计算结果   
  21.     DWORD dwRet = 0;  
  22.     for (UINT i=0; i<nZeroNum; i++)  
  23.     {  
  24.         dwRet <<= 1;  
  25.         if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))  
  26.         {  
  27.             dwRet += 1;  
  28.         }  
  29.         nStartBit++;  
  30.     }  
  31.     return (1 << nZeroNum) - 1 + dwRet;  
  32. }  
  33.   
  34.   
  35. int Se(BYTE *pBuff, UINT nLen, UINT &nStartBit)  
  36. {  
  37.     int UeVal=Ue(pBuff,nLen,nStartBit);  
  38.     double k=UeVal;  
  39.     int nValue=ceil(k/2);//ceil函数:ceil函数的作用是求不小于给定实数的最小整数。ceil(2)=ceil(1.2)=cei(1.5)=2.00   
  40.     if (UeVal % 2==0)  
  41.         nValue=-nValue;  
  42.     return nValue;  
  43. }  
  44.   
  45.   
  46. DWORD u(UINT BitCount,BYTE * buf,UINT &nStartBit)  
  47. {  
  48.     DWORD dwRet = 0;  
  49.     for (UINT i=0; i<BitCount; i++)  
  50.     {  
  51.         dwRet <<= 1;  
  52.         if (buf[nStartBit / 8] & (0x80 >> (nStartBit % 8)))  
  53.         {  
  54.             dwRet += 1;  
  55.         }  
  56.         nStartBit++;  
  57.     }  
  58.     return dwRet;  
  59. }  
  60.   
  61.   
  62. bool h264_decode_sps(BYTE * buf,unsigned int nLen,int &width,int &height)  
  63. {  
  64.     UINT StartBit=0;   
  65.     int forbidden_zero_bit=u(1,buf,StartBit);  
  66.     int nal_ref_idc=u(2,buf,StartBit);  
  67.     int nal_unit_type=u(5,buf,StartBit);  
  68.     if(nal_unit_type==7)  
  69.     {  
  70.         int profile_idc=u(8,buf,StartBit);  
  71.         int constraint_set0_flag=u(1,buf,StartBit);//(buf[1] & 0x80)>>7;   
  72.         int constraint_set1_flag=u(1,buf,StartBit);//(buf[1] & 0x40)>>6;   
  73.         int constraint_set2_flag=u(1,buf,StartBit);//(buf[1] & 0x20)>>5;   
  74.         int constraint_set3_flag=u(1,buf,StartBit);//(buf[1] & 0x10)>>4;   
  75.         int reserved_zero_4bits=u(4,buf,StartBit);  
  76.         int level_idc=u(8,buf,StartBit);  
  77.   
  78.         int seq_parameter_set_id=Ue(buf,nLen,StartBit);  
  79.   
  80.         if( profile_idc == 100 || profile_idc == 110 ||  
  81.             profile_idc == 122 || profile_idc == 144 )  
  82.         {  
  83.             int chroma_format_idc=Ue(buf,nLen,StartBit);  
  84.             if( chroma_format_idc == 3 )  
  85.                 int residual_colour_transform_flag=u(1,buf,StartBit);  
  86.             int bit_depth_luma_minus8=Ue(buf,nLen,StartBit);  
  87.             int bit_depth_chroma_minus8=Ue(buf,nLen,StartBit);  
  88.             int qpprime_y_zero_transform_bypass_flag=u(1,buf,StartBit);  
  89.             int seq_scaling_matrix_present_flag=u(1,buf,StartBit);  
  90.   
  91.             int seq_scaling_list_present_flag[8];  
  92.             if( seq_scaling_matrix_present_flag )  
  93.             {  
  94.                 forint i = 0; i < 8; i++ ) {  
  95.                     seq_scaling_list_present_flag[i]=u(1,buf,StartBit);  
  96.                 }  
  97.             }  
  98.         }  
  99.         int log2_max_frame_num_minus4=Ue(buf,nLen,StartBit);  
  100.         int pic_order_cnt_type=Ue(buf,nLen,StartBit);  
  101.         if( pic_order_cnt_type == 0 )  
  102.             int log2_max_pic_order_cnt_lsb_minus4=Ue(buf,nLen,StartBit);  
  103.         else if( pic_order_cnt_type == 1 )  
  104.         {  
  105.             int delta_pic_order_always_zero_flag=u(1,buf,StartBit);  
  106.             int offset_for_non_ref_pic=Se(buf,nLen,StartBit);  
  107.             int offset_for_top_to_bottom_field=Se(buf,nLen,StartBit);  
  108.             int num_ref_frames_in_pic_order_cnt_cycle=Ue(buf,nLen,StartBit);  
  109.   
  110.             int *offset_for_ref_frame=new int[num_ref_frames_in_pic_order_cnt_cycle];  
  111.             forint i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )  
  112.                 offset_for_ref_frame[i]=Se(buf,nLen,StartBit);  
  113.             delete [] offset_for_ref_frame;  
  114.         }  
  115.         int num_ref_frames=Ue(buf,nLen,StartBit);  
  116.         int gaps_in_frame_num_value_allowed_flag=u(1,buf,StartBit);  
  117.         int pic_width_in_mbs_minus1=Ue(buf,nLen,StartBit);  
  118.         int pic_height_in_map_units_minus1=Ue(buf,nLen,StartBit);  
  119.   
  120.         width=(pic_width_in_mbs_minus1+1)*16;  
  121.         height=(pic_height_in_map_units_minus1+1)*16;  
  122.   
  123.         return true;  
  124.     }  
  125.     else  
  126.         return false;  
  127. }  
展开阅读全文

没有更多推荐了,返回首页