您的位置:首页 > 其它

H264视频通过RTMP直播

2014-10-14 17:12 459 查看
前面的文章中提到了通过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提供的接口如下。

[cpp] view plaincopyprint?class CRTMPStream
{
public:
CRTMPStream(void);
~CRTMPStream(void);
public:
// 连接到RTMP Server
bool Connect(const char* url);
// 断开连接
void Close();
// 发送MetaData
bool SendMetadata(LPRTMPMetadata lpMetaData);
// 发送H264数据帧
bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
// 发送H264文件
bool SendH264File(const char *pFileName);
//...
}

class CRTMPStream
{
public:
CRTMPStream(void);
~CRTMPStream(void);
public:
// 连接到RTMP Server
bool Connect(const char* url);
// 断开连接
void Close();
// 发送MetaData
bool SendMetadata(LPRTMPMetadata lpMetaData);
// 发送H264数据帧
bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
// 发送H264文件
bool SendH264File(const char *pFileName);
//...
}  调用示例:
[cpp] view plaincopyprint?#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();
}

#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();
}
通过JwPlayer播放效果如下:

最后附上RTMPStream完整的代码:

[cpp] view plaincopyprint?/********************************************************************
filename:   RTMPStream.h
created:    2013-04-3
author:     firehood
purpose:    发送H264视频到RTMP Server,使用libRtmp库
*********************************************************************/
#pragma once
#include "rtmp.h"
#include "rtmp_sys.h"
#include "amf.h"
#include <stdio.h>

#define FILEBUFSIZE (1024 * 1024 * 10)       //  10M

// NALU单元
typedef struct _NaluUnit
{
int type;
int size;
unsigned char *data;
}NaluUnit;

typedef struct _RTMPMetadata
{
// video, must be h264 type
unsigned int    nWidth;
unsigned int    nHeight;
unsigned int    nFrameRate;     // fps
unsigned int    nVideoDataRate; // bps
unsigned int    nSpsLen;
unsigned char   Sps[1024];
unsigned int    nPpsLen;
unsigned char   Pps[1024];

// audio, must be aac type
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:
// 连接到RTMP Server
bool Connect(const char* url);
// 断开连接
void Close();
// 发送MetaData
bool SendMetadata(LPRTMPMetadata lpMetaData);
// 发送H264数据帧
bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
// 发送H264文件
bool SendH264File(const char *pFileName);
private:
// 送缓存中读取一个NALU包
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;
};

/********************************************************************
filename:   RTMPStream.h
created:    2013-04-3
author:     firehood
purpose:    发送H264视频到RTMP Server,使用libRtmp库
*********************************************************************/
#pragma once
#include "rtmp.h"
#include "rtmp_sys.h"
#include "amf.h"
#include <stdio.h>

#define FILEBUFSIZE (1024 * 1024 * 10)       //  10M

// NALU单元
typedef struct _NaluUnit
{
int type;
int size;
unsigned char *data;
}NaluUnit;

typedef struct _RTMPMetadata
{
// video, must be h264 type
unsigned int	nWidth;
unsigned int	nHeight;
unsigned int	nFrameRate;		// fps
unsigned int	nVideoDataRate;	// bps
unsigned int	nSpsLen;
unsigned char	Sps[1024];
unsigned int	nPpsLen;
unsigned char	Pps[1024];

// audio, must be aac type
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:
// 连接到RTMP Server
bool Connect(const char* url);
// 断开连接
void Close();
// 发送MetaData
bool SendMetadata(LPRTMPMetadata lpMetaData);
// 发送H264数据帧
bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
// 发送H264文件
bool SendH264File(const char *pFileName);
private:
// 送缓存中读取一个NALU包
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;
};

[cpp] view plaincopyprint?/********************************************************************
filename:   RTMPStream.cpp
created:    2013-04-3
author:     firehood
purpose:    发送H264视频到RTMP Server,使用libRtmp库
*********************************************************************/
#include "RTMPStream.h"
#include "SpsDecode.h"
#ifdef WIN32
#include <windows.h>
#endif

#ifdef WIN32
#pragma comment(lib,"WS2_32.lib")
#pragma comment(lib,"winmm.lib")
#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;  /* type: 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; // 1:keyframe  7:AVC
body[i++] = 0x00; // AVC sequence header

body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00; // fill in 0;

// AVCDecoderConfigurationRecord.
body[i++] = 0x01; // configurationVersion
body[i++] = lpMetaData->Sps[1]; // AVCProfileIndication
body[i++] = lpMetaData->Sps[2]; // profile_compatibility
body[i++] = lpMetaData->Sps[3]; // AVCLevelIndication
body[i++] = 0xff; // lengthSizeMinusOne

// sps nums
body[i++] = 0xE1; //&0x1f
// sps data length
body[i++] = lpMetaData->nSpsLen>>8;
body[i++] = lpMetaData->nSpsLen&0xff;
// sps data
memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);
i= i+lpMetaData->nSpsLen;

// pps nums
body[i++] = 0x01; //&0x1f
// pps data length
body[i++] = lpMetaData->nPpsLen>>8;
body[i++] = lpMetaData->nPpsLen&0xff;
// sps data
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;// 1:Iframe  7:AVC
}
else
{
body[i++] = 0x27;// 2:Pframe  7:AVC
}
body[i++] = 0x01;// AVC NALU
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;

// NALU size
body[i++] = size>>24;
body[i++] = size>>16;
body[i++] = size>>8;
body[i++] = size&0xff;;

// NALU data
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;
// 读取SPS帧
ReadOneNaluFromBuf(naluUnit);
metaData.nSpsLen = naluUnit.size;
memcpy(metaData.Sps,naluUnit.data,naluUnit.size);

// 读取PPS帧
ReadOneNaluFromBuf(naluUnit);
metaData.nPpsLen = naluUnit.size;
memcpy(metaData.Pps,naluUnit.data,naluUnit.size);

// 解码SPS,获取视频图像宽、高信息
int width = 0,height = 0;
h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height);
metaData.nWidth = width;
metaData.nHeight = height;
metaData.nFrameRate = 25;

// 发送MetaData
SendMetadata(&metaData);

unsigned int tick = 0;
while(ReadOneNaluFromBuf(naluUnit))
{
bool bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;
// 发送H264数据帧
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)
{
if(m_pFileBuf[i++] == 0x00 &&
m_pFileBuf[i++] == 0x00 &&
m_pFileBuf[i++] == 0x00 &&
m_pFileBuf[i++] == 0x01
)
{
int pos = i;
while (pos<m_nFileBufSize)
{
if(m_pFileBuf[pos++] == 0x00 &&
m_pFileBuf[pos++] == 0x00 &&
m_pFileBuf[pos++] == 0x00 &&
m_pFileBuf[pos++] == 0x01
)
{
break;
}
}
if(pos == nBufferSize)
{
nalu.size = pos-i;
}
else
{
nalu.size = (pos-4)-i;
}
nalu.type = m_pFileBuf[i]&0x1f;
nalu.data = &m_pFileBuf[i];

m_nCurPos = pos-4;
return TRUE;
}
}
return FALSE;
}

/********************************************************************
filename:   RTMPStream.cpp
created:    2013-04-3
author:     firehood
purpose:    发送H264视频到RTMP Server,使用libRtmp库
*********************************************************************/
#include "RTMPStream.h"
#include "SpsDecode.h"
#ifdef WIN32
#include <windows.h>
#endif

#ifdef WIN32
#pragma comment(lib,"WS2_32.lib")
#pragma comment(lib,"winmm.lib")
#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;  /* type: 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; // 1:keyframe  7:AVC
body[i++] = 0x00; // AVC sequence header

body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00; // fill in 0;

// AVCDecoderConfigurationRecord.
body[i++] = 0x01; // configurationVersion
body[i++] = lpMetaData->Sps[1]; // AVCProfileIndication
body[i++] = lpMetaData->Sps[2]; // profile_compatibility
body[i++] = lpMetaData->Sps[3]; // AVCLevelIndication
body[i++] = 0xff; // lengthSizeMinusOne

// sps nums
body[i++] = 0xE1; //&0x1f
// sps data length
body[i++] = lpMetaData->nSpsLen>>8;
body[i++] = lpMetaData->nSpsLen&0xff;
// sps data
memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);
i= i+lpMetaData->nSpsLen;

// pps nums
body[i++] = 0x01; //&0x1f
// pps data length
body[i++] = lpMetaData->nPpsLen>>8;
body[i++] = lpMetaData->nPpsLen&0xff;
// sps data
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;// 1:Iframe  7:AVC
}
else
{
body[i++] = 0x27;// 2:Pframe  7:AVC
}
body[i++] = 0x01;// AVC NALU
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;

// NALU size
body[i++] = size>>24;
body[i++] = size>>16;
body[i++] = size>>8;
body[i++] = size&0xff;;

// NALU data
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;
// 读取SPS帧
ReadOneNaluFromBuf(naluUnit);
metaData.nSpsLen = naluUnit.size;
memcpy(metaData.Sps,naluUnit.data,naluUnit.size);

// 读取PPS帧
ReadOneNaluFromBuf(naluUnit);
metaData.nPpsLen = naluUnit.size;
memcpy(metaData.Pps,naluUnit.data,naluUnit.size);

// 解码SPS,获取视频图像宽、高信息
int width = 0,height = 0;
h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height);
metaData.nWidth = width;
metaData.nHeight = height;
metaData.nFrameRate = 25;

// 发送MetaData
SendMetadata(&metaData);

unsigned int tick = 0;
while(ReadOneNaluFromBuf(naluUnit))
{
bool bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;
// 发送H264数据帧
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)
{
if(m_pFileBuf[i++] == 0x00 &&
m_pFileBuf[i++] == 0x00 &&
m_pFileBuf[i++] == 0x00 &&
m_pFileBuf[i++] == 0x01
)
{
int pos = i;
while (pos<m_nFileBufSize)
{
if(m_pFileBuf[pos++] == 0x00 &&
m_pFileBuf[pos++] == 0x00 &&
m_pFileBuf[pos++] == 0x00 &&
m_pFileBuf[pos++] == 0x01
)
{
break;
}
}
if(pos == nBufferSize)
{
nalu.size = pos-i;
}
else
{
nalu.size = (pos-4)-i;
}
nalu.type = m_pFileBuf[i]&0x1f;
nalu.data = &m_pFileBuf[i];

m_nCurPos = pos-4;
return TRUE;
}
}
return FALSE;
}

附上SpsDecode.h文件:

[cpp] view plaincopyprint?#include <stdio.h>
#include <math.h>

UINT Ue(BYTE *pBuff, UINT nLen, UINT &nStartBit)
{
//计算0bit的个数
UINT nZeroNum = 0;
while (nStartBit < nLen * 8)
{
if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8))) //&:按位与,%取余
{
break;
}
nZeroNum++;
nStartBit++;
}
nStartBit ++;

//计算结果
DWORD dwRet = 0;
for (UINT i=0; i<nZeroNum; i++)
{
dwRet <<= 1;
if (pBuff[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
{
dwRet += 1;
}
nStartBit++;
}
return (1 << nZeroNum) - 1 + dwRet;
}

int Se(BYTE *pBuff, UINT nLen, UINT &nStartBit)
{
int UeVal=Ue(pBuff,nLen,nStartBit);
double k=UeVal;
int nValue=ceil(k/2);//ceil函数:ceil函数的作用是求不小于给定实数的最小整数。ceil(2)=ceil(1.2)=cei(1.5)=2.00
if (UeVal % 2==0)
nValue=-nValue;
return nValue;
}

DWORD u(UINT BitCount,BYTE * buf,UINT &nStartBit)
{
DWORD dwRet = 0;
for (UINT i=0; i<BitCount; i++)
{
dwRet <<= 1;
if (buf[nStartBit / 8] & (0x80 >> (nStartBit % 8)))
{
dwRet += 1;
}
nStartBit++;
}
return dwRet;
}

bool h264_decode_sps(BYTE * buf,unsigned int nLen,int &width,int &height)
{
UINT StartBit=0;
int forbidden_zero_bit=u(1,buf,StartBit);
int nal_ref_idc=u(2,buf,StartBit);
int nal_unit_type=u(5,buf,StartBit);
if(nal_unit_type==7)
{
int profile_idc=u(8,buf,StartBit);
int constraint_set0_flag=u(1,buf,StartBit);//(buf[1] & 0x80)>>7;
int constraint_set1_flag=u(1,buf,StartBit);//(buf[1] & 0x40)>>6;
int constraint_set2_flag=u(1,buf,StartBit);//(buf[1] & 0x20)>>5;
int constraint_set3_flag=u(1,buf,StartBit);//(buf[1] & 0x10)>>4;
int reserved_zero_4bits=u(4,buf,StartBit);
int level_idc=u(8,buf,StartBit);

int seq_parameter_set_id=Ue(buf,nLen,StartBit);

if( profile_idc == 100 || profile_idc == 110 ||
profile_idc == 122 || profile_idc == 144 )
{
int chroma_format_idc=Ue(buf,nLen,StartBit);
if( chroma_format_idc == 3 )
int residual_colour_transform_flag=u(1,buf,StartBit);
int bit_depth_luma_minus8=Ue(buf,nLen,StartBit);
int bit_depth_chroma_minus8=Ue(buf,nLen,StartBit);
int qpprime_y_zero_transform_bypass_flag=u(1,buf,StartBit);
int seq_scaling_matrix_present_flag=u(1,buf,StartBit);

int seq_scaling_list_present_flag[8];
if( seq_scaling_matrix_present_flag )
{
for( int i = 0; i < 8; i++ ) {
seq_scaling_list_present_flag[i]=u(1,buf,StartBit);
}
}
}
int log2_max_frame_num_minus4=Ue(buf,nLen,StartBit);
int pic_order_cnt_type=Ue(buf,nLen,StartBit);
if( pic_order_cnt_type == 0 )
int log2_max_pic_order_cnt_lsb_minus4=Ue(buf,nLen,StartBit);
else if( pic_order_cnt_type == 1 )
{
int delta_pic_order_always_zero_flag=u(1,buf,StartBit);
int offset_for_non_ref_pic=Se(buf,nLen,StartBit);
int offset_for_top_to_bottom_field=Se(buf,nLen,StartBit);
int num_ref_frames_in_pic_order_cnt_cycle=Ue(buf,nLen,StartBit);

int *offset_for_ref_frame=new int[num_ref_frames_in_pic_order_cnt_cycle];
for( int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++ )
offset_for_ref_frame[i]=Se(buf,nLen,StartBit);
delete [] offset_for_ref_frame;
}
int num_ref_frames=Ue(buf,nLen,StartBit);
int gaps_in_frame_num_value_allowed_flag=u(1,buf,StartBit);
int pic_width_in_mbs_minus1=Ue(buf,nLen,StartBit);
int pic_height_in_map_units_minus1=Ue(buf,nLen,StartBit);

width=(pic_width_in_mbs_minus1+1)*16;
height=(pic_height_in_map_units_minus1+1)*16;

return true;
}
else
return false;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: