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提供的接口如下。
- <span style="font-family:KaiTi_GB2312;">class CRTMPStream
- {
- public:
- CRTMPStream(void);
- ~CRTMPStream(void);
- public:
-
- bool Connect(const char* url);
-
- void Close();
-
- bool SendMetadata(LPRTMPMetadata lpMetaData);
-
- bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
-
- bool SendH264File(const char *pFileName);
-
- } </span>
调用示例:
- <span style="font-family:KaiTi_GB2312;">#include <stdio.h>
- #include "RTMPStream\RTMPStream.h"
-
- int main(int argc,char* argv[])
- {
- CRTMPStream rtmpSender;
-
- bool bRet = rtmpSender.Connect("rtmp://192.168.1.104/live/test");
-
- rtmpSender.SendH264File("E:\\video\\test.264");
-
- rtmpSender.Close();
- } </span>
- </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">
-
-
-
-
-
- #pragma once
- #include "rtmp.h"
- #include "rtmp_sys.h"
- #include "amf.h"
- #include <stdio.h>
-
- #define FILEBUFSIZE (1024 * 1024 * 10) // 10M
-
-
- typedef struct _NaluUnit
- {
- int type;
- int size;
- unsigned char *data;
- }NaluUnit;
-
- typedef struct _RTMPMetadata
- {
-
- unsigned int nWidth;
- unsigned int nHeight;
- unsigned int nFrameRate;
- unsigned int nVideoDataRate;
- unsigned int nSpsLen;
- unsigned char Sps[1024];
- unsigned int nPpsLen;
- unsigned char Pps[1024];
-
-
- bool bHasAudio;
- unsigned int nAudioSampleRate;
- unsigned int nAudioSampleSize;
- unsigned int nAudioChannels;
- char pAudioSpecCfg;
- unsigned int nAudioSpecCfgLen;
-
- } RTMPMetadata,*LPRTMPMetadata;
-
-
- class CRTMPStream
- {
- public:
- CRTMPStream(void);
- ~CRTMPStream(void);
- public:
-
- bool Connect(const char* url);
-
- void Close();
-
- bool SendMetadata(LPRTMPMetadata lpMetaData);
-
- bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
-
- bool SendH264File(const char *pFileName);
- private:
-
- bool ReadOneNaluFromBuf(NaluUnit &nalu);
-
- int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp);
- private:
- RTMP* m_pRtmp;
- unsigned char* m_pFileBuf;
- unsigned int m_nFileBufSize;
- unsigned int m_nCurPos;
- };
- <pre name="code" class="cpp">
-
-
-
-
-
- #include "RTMPStream.h"
- #include "SpsDecode.h"
- #ifdef WIN32
- #include <windows.h>
- #endif
-
- enum
- {
- FLV_CODECID_H264 = 7,
- };
-
- int InitSockets()
- {
- #ifdef WIN32
- WORD version;
- WSADATA wsaData;
- version = MAKEWORD(1, 1);
- return (WSAStartup(version, &wsaData) == 0);
- #else
- return TRUE;
- #endif
- }
-
- inline void CleanupSockets()
- {
- #ifdef WIN32
- WSACleanup();
- #endif
- }
-
- char * put_byte( char *output, uint8_t nVal )
- {
- output[0] = nVal;
- return output+1;
- }
- char * put_be16(char *output, uint16_t nVal )
- {
- output[1] = nVal & 0xff;
- output[0] = nVal >> 8;
- return output+2;
- }
- char * put_be24(char *output,uint32_t nVal )
- {
- output[2] = nVal & 0xff;
- output[1] = nVal >> 8;
- output[0] = nVal >> 16;
- return output+3;
- }
- char * put_be32(char *output, uint32_t nVal )
- {
- output[3] = nVal & 0xff;
- output[2] = nVal >> 8;
- output[1] = nVal >> 16;
- output[0] = nVal >> 24;
- return output+4;
- }
- char * put_be64( char *output, uint64_t nVal )
- {
- output=put_be32( output, nVal >> 32 );
- output=put_be32( output, nVal );
- return output;
- }
- char * put_amf_string( char *c, const char *str )
- {
- uint16_t len = strlen( str );
- c=put_be16( c, len );
- memcpy(c,str,len);
- return c+len;
- }
- char * put_amf_double( char *c, double d )
- {
- *c++ = AMF_NUMBER;
- {
- unsigned char *ci, *co;
- ci = (unsigned char *)&d;
- co = (unsigned char *)c;
- co[0] = ci[7];
- co[1] = ci[6];
- co[2] = ci[5];
- co[3] = ci[4];
- co[4] = ci[3];
- co[5] = ci[2];
- co[6] = ci[1];
- co[7] = ci[0];
- }
- return c+8;
- }
-
- CRTMPStream::CRTMPStream(void):
- m_pRtmp(NULL),
- m_nFileBufSize(0),
- m_nCurPos(0)
- {
- m_pFileBuf = new unsigned char[FILEBUFSIZE];
- memset(m_pFileBuf,0,FILEBUFSIZE);
- InitSockets();
- m_pRtmp = RTMP_Alloc();
- RTMP_Init(m_pRtmp);
- }
-
- CRTMPStream::~CRTMPStream(void)
- {
- Close();
- WSACleanup();
- delete[] m_pFileBuf;
- }
-
- bool CRTMPStream::Connect(const char* url)
- {
- if(RTMP_SetupURL(m_pRtmp, (char*)url)<0)
- {
- return FALSE;
- }
- RTMP_EnableWrite(m_pRtmp);
- if(RTMP_Connect(m_pRtmp, NULL)<0)
- {
- return FALSE;
- }
- if(RTMP_ConnectStream(m_pRtmp,0)<0)
- {
- return FALSE;
- }
- return TRUE;
- }
-
- void CRTMPStream::Close()
- {
- if(m_pRtmp)
- {
- RTMP_Close(m_pRtmp);
- RTMP_Free(m_pRtmp);
- m_pRtmp = NULL;
- }
- }
-
- int CRTMPStream::SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp)
- {
- if(m_pRtmp == NULL)
- {
- return FALSE;
- }
-
- RTMPPacket packet;
- RTMPPacket_Reset(&packet);
- RTMPPacket_Alloc(&packet,size);
-
- packet.m_packetType = nPacketType;
- packet.m_nChannel = 0x04;
- packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
- packet.m_nTimeStamp = nTimestamp;
- packet.m_nInfoField2 = m_pRtmp->m_stream_id;
- packet.m_nBodySize = size;
- memcpy(packet.m_body,data,size);
-
- int nRet = RTMP_SendPacket(m_pRtmp,&packet,0);
-
- RTMPPacket_Free(&packet);
-
- return nRet;
- }
-
- bool CRTMPStream::SendMetadata(LPRTMPMetadata lpMetaData)
- {
- if(lpMetaData == NULL)
- {
- return false;
- }
- char body[1024] = {0};;
-
- char * p = (char *)body;
- p = put_byte(p, AMF_STRING );
- p = put_amf_string(p , "@setDataFrame" );
-
- p = put_byte( p, AMF_STRING );
- p = put_amf_string( p, "onMetaData" );
-
- p = put_byte(p, AMF_OBJECT );
- p = put_amf_string( p, "copyright" );
- p = put_byte(p, AMF_STRING );
- p = put_amf_string( p, "firehood" );
-
- p =put_amf_string( p, "width");
- p =put_amf_double( p, lpMetaData->nWidth);
-
- p =put_amf_string( p, "height");
- p =put_amf_double( p, lpMetaData->nHeight);
-
- p =put_amf_string( p, "framerate" );
- p =put_amf_double( p, lpMetaData->nFrameRate);
-
- p =put_amf_string( p, "videocodecid" );
- p =put_amf_double( p, FLV_CODECID_H264 );
-
- p =put_amf_string( p, "" );
- p =put_byte( p, AMF_OBJECT_END );
-
- int index = p-body;
-
- SendPacket(RTMP_PACKET_TYPE_INFO,(unsigned char*)body,p-body,0);
-
- int i = 0;
- body[i++] = 0x17;
- body[i++] = 0x00;
-
- body[i++] = 0x00;
- body[i++] = 0x00;
- body[i++] = 0x00;
-
-
- body[i++] = 0x01;
- body[i++] = lpMetaData->Sps[1];
- body[i++] = lpMetaData->Sps[2];
- body[i++] = lpMetaData->Sps[3];
- body[i++] = 0xff;
-
-
- body[i++] = 0xE1;
-
- body[i++] = lpMetaData->nSpsLen>>8;
- body[i++] = lpMetaData->nSpsLen&0xff;
-
- memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);
- i= i+lpMetaData->nSpsLen;
-
-
- body[i++] = 0x01;
-
- body[i++] = lpMetaData->nPpsLen>>8;
- body[i++] = lpMetaData->nPpsLen&0xff;
-
- memcpy(&body[i],lpMetaData->Pps,lpMetaData->nPpsLen);
- i= i+lpMetaData->nPpsLen;
-
- return SendPacket(RTMP_PACKET_TYPE_VIDEO,(unsigned char*)body,i,0);
-
- }
-
- bool CRTMPStream::SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp)
- {
- if(data == NULL && size<11)
- {
- return false;
- }
-
- unsigned char *body = new unsigned char[size+9];
-
- int i = 0;
- if(bIsKeyFrame)
- {
- body[i++] = 0x17;
- }
- else
- {
- body[i++] = 0x27;
- }
- body[i++] = 0x01;
- body[i++] = 0x00;
- body[i++] = 0x00;
- body[i++] = 0x00;
-
-
- body[i++] = size>>24;
- body[i++] = size>>16;
- body[i++] = size>>8;
- body[i++] = size&0xff;;
-
-
- memcpy(&body[i],data,size);
-
- bool bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp);
-
- delete[] body;
-
- return bRet;
- }
-
- bool CRTMPStream::SendH264File(const char *pFileName)
- {
- if(pFileName == NULL)
- {
- return FALSE;
- }
- FILE *fp = fopen(pFileName, "rb");
- if(!fp)
- {
- printf("ERROR:open file %s failed!",pFileName);
- }
- fseek(fp, 0, SEEK_SET);
- m_nFileBufSize = fread(m_pFileBuf, sizeof(unsigned char), FILEBUFSIZE, fp);
- if(m_nFileBufSize >= FILEBUFSIZE)
- {
- printf("warning : File size is larger than BUFSIZE\n");
- }
- fclose(fp);
-
- RTMPMetadata metaData;
- memset(&metaData,0,sizeof(RTMPMetadata));
-
- NaluUnit naluUnit;
-
- ReadOneNaluFromBuf(naluUnit);
- metaData.nSpsLen = naluUnit.size;
- memcpy(metaData.Sps,naluUnit.data,naluUnit.size);
-
-
- ReadOneNaluFromBuf(naluUnit);
- metaData.nPpsLen = naluUnit.size;
- memcpy(metaData.Pps,naluUnit.data,naluUnit.size);
-
-
- int width = 0,height = 0;
- h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height);
- metaData.nWidth = width;
- metaData.nHeight = height;
- metaData.nFrameRate = 25;
-
-
- SendMetadata(&metaData);
-
- unsigned int tick = 0;
- while(ReadOneNaluFromBuf(naluUnit))
- {
- bool bKeyframe = (naluUnit.type == 0x05) ? TRUE : FALSE;
-
- SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick);
- msleep(40);
- tick +=40;
- }
-
- return TRUE;
- }
-
- bool CRTMPStream::ReadOneNaluFromBuf(NaluUnit &nalu)
- {
- int i = m_nCurPos;
- while(i<m_nFileBufSize-4)
- {
- if(m_pFileBuf[i++] == 0x00 &&
- m_pFileBuf[i++] == 0x00 &&
- m_pFileBuf[i++] == 0x00 &&
- m_pFileBuf[i++] == 0x01
- )
- {
- int pos = i;
- while (pos<m_nFileBufSize-4)
- {
- if(m_pFileBuf[pos++] == 0x00 &&
- m_pFileBuf[pos++] == 0x00 &&
- m_pFileBuf[pos++] == 0x00 &&
- m_pFileBuf[pos++] == 0x01
- )
- {
- break;
- }
- }
- nalu.type = m_pFileBuf[i]&0x1f;
- nalu.size = (pos-4)-i;
- nalu.data = &m_pFileBuf[i];
-
- m_nCurPos = pos-4;
- return TRUE;
- }
- }
- return FALSE;
- }
|