您的位置:首页 > 其它

通过LIBRTMP发送H264和AAC

2017-03-24 11:14 381 查看
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提供的接口如下。

[cpp] view
plain copy

<span style="font-family:KaiTi_GB2312;">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);

//...

} </span>

调用示例:

[cpp] view
plain copy

<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>

[cpp] view
plain copy

</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.myexception.cn/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">/********************************************************************

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
plain copy

<pre name="code" class="cpp">/********************************************************************

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

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-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;

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