了解RTMP定义
RTMP是Real Time Messaging Protocol(实时消息传输协议)的首字母缩写。该协议基于TCP,是一个协议族,包括RTMP基本协议及RTMPT/RTMPS/RTMPE等多种变种。RTMP是一种设计用来进行实时数据通信的网络协议,主要用来在Flash/AIR平台和支持RTMP协议的流媒体/交互服务器之间进行音视频和数据通信。
准备RTMPDump中的librtmp
- 下载
在librtmp的官网上面可以下载:http://rtmpdump.mplayerhq.hu,如果观望无法访问,也可以在GitHub上面找到相应的代码。最新的版本是:rtmpdump-2.3.tgz。 - 编译
解压rtmpdump-2.3.tgz,可以查看README来获取编译方法:
To compile type “make” with SYS=, e.g.
make SYS=posix
for Linux, MacOSX, Unix, etc. or
make SYS=mingw
for Windows.
You can cross-compile for other platforms using the CROSS_COMPILE variable:
make CROSS_COMPILE=arm-none-linux- INC=-I/my/cross/includes
编译完成以后,可以在librtmp目录下面找到librtmp.a的库文件。在需要使用librtmp的工程里面,将这个lib链接进去就可以了。
使用openssl中的libssl+libcrypto
- 下载
在OpenSSL的官网上面可以获取最新的OpenSSL的源代码。https://www.openssl.org/source/。最新版本的是:1.1.0e版本。 - 编译
编译openssl很简单,如果在linux上面,直接./Configure + make 就可以完成。如果要交叉编译,在Configure对应平台以后,需要去修改一下Makefile中的cc ar prefix相关的变量。
./Configure android-armv7 修改makefile cc ar prefix…. (edited)
编译完成以后,我们在libs目录可以看到libssl.a和libcrypto.a两个lib。在rtmp push的时候,需要这两个libs的支持。
推流工作
整体框架图
Streaming from RTSPGet Audio/Video frameConvert frameRTMP push
使用libtrmp提供的API
librtmp提供了推流的API,可以在rtmp.h文件中查看所有API。我们只需要使用常用的几个API就可以将streaming推送到服务器。
- RTMP_Init()//初始化结构体
- RTMP_Free()
- RTMP_Alloc()
- RTMP_SetupURL()//设置rtmp server地址
- RTMP_EnableWrite()//打开可写选项,设定为推流状态
- RTMP_Connect()//建立NetConnection
- RTMP_Close()//关闭连接
- RTMP_ConnectStream()//建立NetStream
- RTMP_DeleteStream()//删除NetStream
- RTMP_SendPacket()//发送数据
流程图
StartRTMP_Init()RTMP_Alloc()RTMP_Setup()RTMP_EnableWrite()RTMP_Connect()RTMP_ConnectStream()RTMP_SendPacket()End
将streaming封装成为RTMP格式
在发送第一帧Audio和Video的时候,需要将Audio和Video的信息封装成为RTMP header,发送给rtmp server。
Audio头有4字节,包含:头部标记0xaf 0x00、 profile、channel、bitrate 信息。
Video头有16字节,包含IFrame、PFrame、AVC标识,除此之外,还需要将sps和pps放在header 里面。
RTMP协议定义了message Type,其中Type ID为8,9的消息分别用于传输音频和视频数据:
#define RTMP_PACKET_TYPE_AUDIO 0x08
#define RTMP_PACKET_TYPE_VIDEO 0x09
- Audio 格式封装的源码:
AAC header packet:
body = (unsigned char *)malloc(4 + size);
memset(body, 0, 4);
body[0] = 0xaf;
body[1] = 0x00;
switch (profile){
case 0:
body[2]|=(1<<3);
break;
case 1:
body[2]|=(1<<4);
break;
case 2:
body[2]|=(1<<3);
body[2]|=(1<<4);
break;
default:
;
}
switch(this->channel){
case 1:
body[3]|=(1<<3);
break;
case 2:
body[3]|=(1<<4);
break;
default:
;
}
switch(this->rate){
case 48000:
body[2]|=(1);
body[3]|=(1<<7);
break;
case 44100:
body[2]|=(1<<1);
break;
case 32000:
body[2]|=(1<<1);
body[3]|=(1<<7);
break;
default:
;
}
sendPacket(RTMP_PACKET_TYPE_AUDIO, body, 4, 0);
free(body);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- Video 格式封装的源码:
H264 header packet:
body = (unsigned char *)malloc(16 + sps_len + pps_len);
this->videoFist = false;
memset(body, 0, 16 + sps_len + pps_len);
body[i++] = 0x17;
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x01;
body[i++] = sps[1];
body[i++] = sps[2];
body[i++] = sps[3];
body[i++] = 0xff;
body[i++] = 0xe1;
body[i++] = (sps_len >> 8) & 0xff;
body[i++] = sps_len & 0xff;
for (size_t j = 0; j < sps_len; j++)
{
body[i++] = sps[j];
}
body[i++] = 0x01;
body[i++] = (pps_len >> 8) & 0xff;
body[i++] = pps_len & 0xff;
for (size_t j = 0; j < pps_len; j++)
{
body[i++] = pps[j];
}
sendPacket(RTMP_PACKET_TYPE_VIDEO, body, i, nTimeStamp);
free(body);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
只有第一帧Audio和第一帧video才需要发送header信息。之后就直接发送帧数据。
发送Audio的时候,只需要在数据帧前面加上2 byte的header信息:
spec_info[0] = 0xAF;
spec_info[1] = 0x01;
发送Video的时候,需要在header里面标识出I P帧的信息,以及视频帧的长度信息:
body = (unsigned char *)malloc(9 + size);
memset(body, 0, 9);
i = 0;
if (bIsKeyFrame== 0) {
body[i++] = 0x17;
}
else {
body[i++] = 0x27;
}
body[i++] = 0x01;
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = size >> 24 & 0xff;
body[i++] = size >> 16 & 0xff;
body[i++] = size >> 8 & 0xff;
body[i++] = size & 0xff;
memcpy(&body[i], data, size);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
进阶
RTMP client与RTMP server交互流程
- 简介
播放一个RTMP协议的流媒体需要经过:握手、建立链接、建立流、播放/发送四个步骤。握手成功之后,需要在建立链接阶段去建立客户端和服务器之间的“网络链接”。建立流阶段用于建立客户端和服务器之间的“网络流”。播放阶段用于传输音视频数据。 - 握手(HandsShake)
服务器和客户端需要发送大小固定的三个数据块,步骤如下:
- 握手开始于客户端发送C0、C1块。服务器收到C0或C1后发送S0和S1。
- 当客户端收齐S0和S1后,开始发送C2。当服务器收齐C0和C1后,开始发送S2。
- 当客户端和服务器分别收到S2和C2后,握手完成。
![使用雷霄骅所画的握手交互图|center|450*0](https://img-blog.csdn.net/20130915111408781?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpeGlhb2h1YTEwMjA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
- 建立链接(NetConnnet)
- 客户端发送命令消息中的“连接”(connect)到服务器,请求与一个服务应用实例建立连接。
- 服务器接收到连接命令消息后,发送确认窗口大小(Window Acknowledgement Size)协议消息到客户端,同时连接到连接命令中提到的应用程序。
- 服务器发送设置带宽()协议消息到客户端。
- 客户端处理设置带宽协议消息后,发送确认窗口大小(Window Acknowledgement Size)协议消息到服务器端。
- 服务器发送用户控制消息中的“流开始”(Stream Begin)消息到客户端。
- 服务器发送命令消息中的“结果”(_result),通知客户端连接的状态。
![建立连接-雷霄骅|center|500*0](https://img-blog.csdn.net/20130915111423578?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpeGlhb2h1YTEwMjA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
- 建立流(NetStream)
- 客户端发送命令消息中的“创建流”(createStream)命令到服务器端。
- 服务器端接收到“创建流”命令后,发送命令消息中的“结果”(_result),通知客户端流的状态。
![建立流-雷霄骅|center|450*0](https://img-blog.csdn.net/20130915111350062?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpeGlhb2h1YTEwMjA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
- 播放(Play)
- 客户端发送命令消息中的“播放”(play)命令到服务器。
- 接收到播放命令后,服务器发送设置块大小(ChunkSize)协议消息。
- 服务器发送用户控制消息中的“streambegin”,告知客户端流ID。
- 播放命令成功的话,服务器发送命令消息中的“响应状态” NetStream.Play.Start & NetStream.Play.reset,告知客户端“播放”命令执行成功。
- 在此之后服务器发送客户端要播放的音频和视频数据。
![播放|center|480*0](https://img-blog.csdn.net/20130915111446703?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGVpeGlhb2h1YTEwMjA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
- 发送(send)
![send streaming](https://img-blog.csdn.net/20170313134127204?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbG9yeTE3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
RTMPDump源码分析
握手(HandsShake)
static int HandShake(RTMP * r, int FP9HandShake);
HandShake函数在:/rtmp/rtmplib/handshack.h中。
./rtmp.c:69:#define RTMP_SIG_SIZE 1536
695 static int HandShake(RTMP * r, int FP9HandShake){
709 uint8_t clientbuf[RTMP_SIG_SIZE + 4], *clientsig=clientbuf+4;
721 if (encrypted){
722 clientsig[-1] = 0x06;
723 offalg = 1;
724 }else
725 clientsig[-1] = 0x03;
814 if (!WriteN(r, (char *)clientsig-1, RTMP_SIG_SIZE + 1))
815 return FALSE;
817 if (ReadN(r, (char *)&type, 1) != 1)
818 return FALSE;
826 if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
827 return FALSE;
968 if (!WriteN(r, (char *)reply, RTMP_SIG_SIZE))
969 return FALSE;
972 if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
973 return FALSE;
1060 if (memcmp(serversig, clientsig, RTMP_SIG_SIZE) != 0)
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
建立链接(NetConnnet)
RTMP_Connect(RTMP *r, RTMPPacket *cp);
建立连接的代码位于:librtmp/rtmp.c中,定义函数:RTMP_Connect()。RTMP_Conncet()里面又分别调用了两个函数:RTMP_Connect0(), RTMP_Connect1()。RTMP_Connect0()主要进行的是socket的连接,RTMP_Connct1()进行的是RTMP相关的连接动作。
1031 int RTMP_Connect(RTMP *r, RTMPPacket *cp)
1032 {
1033 struct sockaddr_in service;
1034 if (!r->Link.hostname.av_len)
1035 return FALSE;
1036
1037 memset(&service, 0, sizeof(struct sockaddr_in));
1038 service.sin_family = AF_INET;
1039
1040 if (r->Link.socksport)
1041 {
1042
1043 if (!add_addr_info(&service, &r->Link.sockshost, r->Link.socksport))
1044 return FALSE;
1045 }
1046 else
1047 {
1048
1049 if (!add_addr_info(&service, &r->Link.hostname, r->Link.port))
1050 return FALSE;
1051 }
1052
1053 if (!RTMP_Connect0(r, (struct sockaddr *)&service))
1054 return FALSE;
1055
1056 r->m_bSendCounter = TRUE;
1057
1058 return RTMP_Connect1(r, cp);
1059 }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
int RTMP_Connect0(RTMP r, struct sockaddr service);
RTMP_Connect0函数分析:
905 int RTMP_Connect0(RTMP *r, struct sockaddr * service){
913 r->m_sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
916 if (connect(r->m_sb.sb_socket, service, sizeof(struct sockaddr)) < 0)
928 if (!SocksNegotiate(r)){}
956 return TRUE;
}
int RTMP_Connect1(RTMP *r, RTMPPacket *cp);
RTMP_Connect1函数分析:
根据不同的传输协议,选择传送数据的方式。之后进行HandShake,最后调用SendConnectPacket()送Connect packet。
int
RTMP_Connect1(RTMP *r, RTMPPacket *cp)
{
if (r->Link.protocol & RTMP_FEATURE_SSL){
#if defined(CRYPTO) && !defined(NO_SSL)
TLS_client(RTMP_TLS_ctx, r->m_sb.sb_ssl);
TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket);
if (TLS_connect(r->m_sb.sb_ssl) < 0){...}
#else
return FALSE;
#endif
}
if (r->Link.protocol & RTMP_FEATURE_HTTP){
HTTP_Post(r, RTMPT_OPEN, "", 1);
if (HTTP_read(r, 1) != 0){...}
...
}
if (!HandShake(r, TRUE)){...}
if (!SendConnectPacket(r, cp)){...}
return TRUE;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
SendConnectPacket() 里面主要对RTMP信息进行打包,然后调用RTMP_SendPacket函数,将内容发送出去。
static int
SendConnectPacket(RTMP *r, RTMPPacket *cp)
{
RTMPPacket packet;
char pbuf[4096], *pend = pbuf + sizeof(pbuf);
char *enc;
if (cp)
return RTMP_SendPacket(r, cp, TRUE);
packet.m_nChannel = 0x03;
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
packet.m_nTimeStamp = 0;
packet.m_nInfoField2 = 0;
packet.m_hasAbsTimestamp = 0;
packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
enc = packet.m_body;
enc = AMF_EncodeString(enc, pend, &av_connect);
enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
*enc++ = AMF_OBJECT;
...
packet.m_nBodySize = enc - packet.m_body;
return RTMP_SendPacket(r, &packet, TRUE);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
建立流(NetStream)
RTMP_ConnectStream()函数主要用于在NetConnection基础上面建立一个NetStream。
int RTMP_ConnectStream(RTMP *r, int seekTime);
int RTMP_ConnectStream(RTMP *r, int seekTime)
{
RTMPPacket packet = { 0 };
if (seekTime > 0)
r->Link.seekTime = seekTime;
r->m_mediaChannel = 0;
while (!r->m_bPlaying && RTMP_IsConnected(r) && RTMP_ReadPacket(r, &packet)){
if (RTMPPacket_IsReady(&packet)){
if (!packet.m_nBodySize)
continue;
if ((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) ||
(packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) ||
(packet.m_packetType == RTMP_PACKET_TYPE_INFO)){
RTMP_Log(RTMP_LOGWARNING, "Received FLV packet before play()! Ignoring.");
RTMPPacket_Free(&packet);
continue;
}
RTMP_ClientPacket(r, &packet);
RTMPPacket_Free(&packet);
}
}
return r->m_bPlaying;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
简单的一个逻辑判断,重点在while循环里。首先,必须要满足三个条件。其次,进入循环以后只有出错或者建立流(NetStream)完成后,才能退出循环。
有两个重要的函数:
int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);
块格式:
basic header(1-3字节) | chunk msg header(0/3/7/11字节) | Extended Timestamp(0/4字节) | chunk data |
---|
消息格式:
timestamp(3字节) | msg length(3字节) | msg type id(1字节,小端) | msg stream id(4字节) |
---|
int
RTMP_ReadPacket(RTMP *r, RTMPPacket *packet)
{
uint8_t hbuf[RTMP_MAX_HEADER_SIZE] = { 0 };
char *header = (char *)hbuf;
int nSize, hSize, nToRead, nChunk;
int didAlloc = FALSE;
RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d", __FUNCTION__, r->m_sb.sb_socket);
if (ReadN(r, (char *)hbuf, 1) == 0)
{
RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header", __FUNCTION__);
return FALSE;
}
packet->m_headerType = (hbuf[0] & 0xc0) >> 6;
packet->m_nChannel = (hbuf[0] & 0x3f);
header++;
if (packet->m_nChannel == 0)
{
if (ReadN(r, (char *)&hbuf[1], 1) != 1)
{
RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 2nd byte", __FUNCTION__);
return FALSE;
}
packet->m_nChannel = hbuf[1];
packet->m_nChannel += 64;
header++;
}
else if (packet->m_nChannel == 1){
int tmp;
if (ReadN(r, (char *)&hbuf[1], 2) != 2)
{
RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 3nd byte", __FUNCTION__);
return FALSE;
}
tmp = (hbuf[2] << 8) + hbuf[1];
packet->m_nChannel = tmp + 64;
RTMP_Log(RTMP_LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet->m_nChannel);
header += 2;
}
nSize = packetSize[packet->m_headerType];
if (nSize == RTMP_LARGE_HEADER_SIZE)
{
packet->m_hasAbsTimestamp = TRUE;
}else if (nSize < RTMP_LARGE_HEADER_SIZE){
if (r->m_vecChannelsIn[packet->m_nChannel])
memcpy(packet, r->m_vecChannelsIn[packet->m_nChannel], sizeof(RTMPPacket));
}
nSize--;
if (nSize > 0 && ReadN(r, header, nSize) != nSize){
RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header. type: %x",
__FUNCTION__, (unsigned int)hbuf[0]);
return FALSE;
}
hSize = nSize + (header - (char *)hbuf);
if (nSize >= 3){
packet->m_nTimeStamp = AMF_DecodeInt24(header);
if (nSize >= 6)
{
packet->m_nBodySize = AMF_DecodeInt24(header + 3);
packet->m_nBytesRead = 0;
RTMPPacket_Free(packet);
if (nSize > 6)
{
packet->m_packetType = header[6];
if (nSize == 11)
packet->m_nInfoField2 = DecodeInt32LE(header + 7);
}
}
if (packet->m_nTimeStamp == 0xffffff){
if (ReadN(r, header + nSize, 4) != 4)
{
RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp", __FUNCTION__);
return FALSE;
}
packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize);
hSize += 4;
}
}
RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)hbuf, hSize);
if (packet->m_nBodySize > 0 && packet->m_body == NULL){
if (!RTMPPacket_Alloc(packet, packet->m_nBodySize)){
RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__);
return FALSE;
}
didAlloc = TRUE;
packet->m_headerType = (hbuf[0] & 0xc0) >> 6;
}
nToRead = packet->m_nBodySize - packet->m_nBytesRead;
nChunk = r->m_inChunkSize;
if (nToRead < nChunk)
nChunk = nToRead;
if (packet->m_chunk){
packet->m_chunk->c_headerSize = hSize;
memcpy(packet->m_chunk->c_header, hbuf, hSize);
packet->m_chunk->c_chunk = packet->m_body + packet->m_nBytesRead;
packet->m_chunk->c_chunkSize = nChunk;
}
if (ReadN(r, packet->m_body + packet->m_nBytesRead, nChunk) != nChunk){
RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %u",
__FUNCTION__, packet->m_nBodySize);
return FALSE;
}
RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)packet->m_body + packet->m_nBytesRead, nChunk);
packet->m_nBytesRead += nChunk;
if (!r->m_vecChannelsIn[packet->m_nChannel])
r->m_vecChannelsIn[packet->m_nChannel] = malloc(sizeof(RTMPPacket));
memcpy(r->m_vecChannelsIn[packet->m_nChannel], packet, sizeof(RTMPPacket));
if (RTMPPacket_IsReady(packet)){
if (!packet->m_hasAbsTimestamp)
packet->m_nTimeStamp += r->m_channelTimestamp[packet->m_nChannel];
r->m_channelTimestamp[packet->m_nChannel] = packet->m_nTimeStamp;
r->m_vecChannelsIn[packet->m_nChannel]->m_body = NULL;
r->m_vecChannelsIn[packet->m_nChannel]->m_nBytesRead = 0;
r->m_vecChannelsIn[packet->m_nChannel]->m_hasAbsTimestamp = FALSE;
}
else{
packet->m_body = NULL;
}
return TRUE;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
int ReadN(RTMP *r, char *buffer, int n);
static int ReadN(RTMP *r, char *buffer, int n)
{
int nOriginalSize = n;
int avail;
char *ptr;
r->m_sb.sb_timedout = FALSE;
#ifdef _DEBUG
memset(buffer, 0, n);
#endif
ptr = buffer;
while (n > 0){
int nBytes = 0, nRead;
if (r->Link.protocol & RTMP_FEATURE_HTTP)
{
while (!r->m_resplen)
{
if (r->m_sb.sb_size < 144)
{
if (!r->m_unackd)
HTTP_Post(r, RTMPT_IDLE, "", 1);
if (RTMPSockBuf_Fill(r, &r->m_sb) < 1){
if (!r->m_sb.sb_timedout)
RTMP_Close(r);
return 0;
}
}
if (HTTP_read(r, 0) == -1){
RTMP_Log(RTMP_LOGDEBUG, "%s, No valid HTTP response found", __FUNCTION__);
RTMP_Close(r);
return 0;
}
}
if (r->m_resplen && !r->m_sb.sb_size)
RTMPSockBuf_Fill(r, &r->m_sb);
avail = r->m_sb.sb_size;
if (avail > r->m_resplen)
avail = r->m_resplen;
}else{
avail = r->m_sb.sb_size;
if (avail == 0){
if (RTMPSockBuf_Fill(r, &r->m_sb) < 1){
if (!r->m_sb.sb_timedout)
RTMP_Close(r);
return 0;
}
avail = r->m_sb.sb_size;
}
}
nRead = ((n < avail) ? n : avail);
if (nRead > 0){
memcpy(ptr, r->m_sb.sb_start, nRead);
r->m_sb.sb_start += nRead;
r->m_sb.sb_size -= nRead;
nBytes = nRead;
r->m_nBytesIn += nRead;
if (r->m_bSendCounter && r->m_nBytesIn > ( r->m_nBytesInSent + r->m_nClientBW / 10))
if (!SendBytesReceived(r))
return FALSE;
}
if (nBytes == 0){
RTMP_Log(RTMP_LOGDEBUG, "%s, RTMP socket closed by peer", __FUNCTION__);
RTMP_Close(r);
break;
}
if (r->Link.protocol & RTMP_FEATURE_HTTP){
r->m_resplen -= nBytes;
n -= nBytes;
ptr += nBytes;
}
return nOriginalSize - n;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
int RTMPSockBuf_Fill(RTMP *r, RTMPSockBuf *sb);
int RTMPSockBuf_Fill(RTMP *r, RTMPSockBuf *sb)
{
int nBytes;
if (!sb->sb_size)
sb->sb_start = sb->sb_buf;
while (1)
{
nBytes = sizeof(sb->sb_buf) - sb->sb_size - (sb->sb_start - sb->sb_buf);
{
nBytes = r->m_sock.recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, 0);
}
if (nBytes != -1){
sb->sb_size += nBytes;
}else{
int sockerr = r->m_sock.getsockerr();
RTMP_Log(RTMP_LOGDEBUG, "%s, recv returned %d. GetSockError(): %d (%s)",
__FUNCTION__, nBytes, sockerr, strerror(sockerr));
if (sockerr == EINTR && !RTMP_ctrlC)
continue;
if (sockerr == EWOULDBLOCK || sockerr == EAGAIN){
sb->sb_timedout = TRUE;
nBytes = 0;
}
}
break;
}
return nBytes;
}