分享

通过RTMP发送264流(文件或帧)

 昵称597197 2015-09-14
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. <span style="font-family:KaiTi_GB2312;">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. }  </span>  
调用示例:
  1. <span style="font-family:KaiTi_GB2312;">#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. }  </span>  


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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多