Handle_Control
本文記錄 RTMP 中的用戶控制消息事件, 客戶端和服務(wù)器發(fā)送這一消息來(lái)通知對(duì)端用戶控制事件
支持以下用戶控制事件類型:
-
Stream Begin(=0)
: 服務(wù)器發(fā)送這個(gè)事件來(lái)通知客戶端一個(gè)流已經(jīng)就緒并可以用來(lái)通信。默認(rèn)情況下稠诲,這一事件在成功接收到客戶端的應(yīng)用連接命令之后以ID0
發(fā)送奸晴,這一事件數(shù)據(jù)為4
字節(jié)启绰,代表了已就緒的流ID
. -
Stream EOF(=1)
:服務(wù)器發(fā)送這一事件來(lái)通知客戶端請(qǐng)求的流的回放數(shù)據(jù)已經(jīng)結(jié)束号涯。在發(fā)送額外的命令之前不再發(fā)送任何數(shù)據(jù)绕娘。客戶端將丟棄接收到這個(gè)流的消息晓猛。 這一事件數(shù)據(jù)為4
字節(jié)饿幅,代表了回放已結(jié)束的流的流ID
. -
Stream Dry(=2)
:服務(wù)器端發(fā)送這一事件來(lái)通知客戶端當(dāng)前流中以沒(méi)有數(shù)據(jù)。當(dāng)服務(wù)器端在一段時(shí)間內(nèi)沒(méi)有檢測(cè)到任何消息戒职。它可以通知相關(guān)客戶端當(dāng)前流已經(jīng)沒(méi)有數(shù)據(jù)了栗恩。這一事件數(shù)據(jù)為4
字節(jié),代表了已沒(méi)有的流的流ID
. -
SetBuffer Length(=3)
:客戶端發(fā)送這一事件來(lái)通知服務(wù)器端用于緩沖流中任何數(shù)據(jù)的緩存大小(以毫秒為單位)洪燥。這一事件在服務(wù)端開(kāi)始處理流之前就發(fā)送磕秤。這一事件數(shù)據(jù)的前4
個(gè)字節(jié)代表了流ID
后4
個(gè)字節(jié)代表了以毫秒為單位的緩存的長(zhǎng)度. -
StreamIs Recorded(=4)
:服務(wù)端發(fā)送這一事件來(lái)通知客戶端當(dāng)前流是一個(gè)錄制流。這一事件數(shù)據(jù)為4
字節(jié)捧韵,代表了錄制流的流ID
-
PingRequest(=6)
:服務(wù)端發(fā)送這一事件用來(lái)測(cè)試是否能夠送達(dá)客戶端市咆。事件數(shù)據(jù)為一個(gè)4
字節(jié)的TimeStamp
,代表了服務(wù)端發(fā)送這一命令時(shí)的服務(wù)器本地時(shí)間纫版。客戶端在接收到這一消息后會(huì)立即發(fā)送PingResponse
回復(fù).
下面的代碼時(shí)librtmp關(guān)于用戶控制消息的處理
static void
HandleCtrl(RTMP *r, const RTMPPacket *packet)
{
short nType = -1;
unsigned int tmp;
if (packet->m_body && packet->m_nBodySize >= 2)
nType = AMF_DecodeInt16(packet->m_body);
RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType,
packet->m_nBodySize);
/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
if (packet->m_nBodySize >= 6) {
switch (nType) {
case 0:
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Stream Begin %d", __FUNCTION__, tmp);
break;
case 1:
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Stream EOF %d", __FUNCTION__, tmp);
if (r->m_pausing == 1)
r->m_pausing = 2;
break;
case 2:
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Stream Dry %d", __FUNCTION__, tmp);
break;
case 4:
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Stream IsRecorded %d", __FUNCTION__, tmp);
break;
case 6: /* server ping. reply with pong. */
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Ping %d", __FUNCTION__, tmp);
RTMP_SendCtrl(r, 0x07, tmp, 0);
break;
/* FMS 3.5 servers send the following two controls to let the client
* know when the server has sent a complete buffer. I.e., when the
* server has sent an amount of data equal to m_nBufferMS in duration.
* The server meters its output so that data arrives at the client
* in realtime and no faster.
*
* The rtmpdump program tries to set m_nBufferMS as large as
* possible, to force the server to send data as fast as possible.
* In practice, the server appears to cap this at about 1 hour's
* worth of data. After the server has sent a complete buffer, and
* sends this BufferEmpty message, it will wait until the play
* duration of that buffer has passed before sending a new buffer.
* The BufferReady message will be sent when the new buffer starts.
* (There is no BufferReady message for the very first buffer;
* presumably the Stream Begin message is sufficient for that
* purpose.)
*
* If the network speed is much faster than the data bitrate, then
* there may be long delays between the end of one buffer and the
* start of the next.
*
* Since usually the network allows data to be sent at
* faster than realtime, and rtmpdump wants to download the data
* as fast as possible, we use this RTMP_LF_BUFX hack: when we
* get the BufferEmpty message, we send a Pause followed by an
* Unpause. This causes the server to send the next buffer immediately
* instead of waiting for the full duration to elapse. (That's
* also the purpose of the ToggleStream function, which rtmpdump
* calls if we get a read timeout.)
*
* Media player apps don't need this hack since they are just
* going to play the data in realtime anyway. It also doesn't work
* for live streams since they obviously can only be sent in
* realtime. And it's all moot if the network speed is actually
* slower than the media bitrate.
*/
case 31:
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Stream BufferEmpty %d", __FUNCTION__, tmp);
if (!(r->Link.lFlags & RTMP_LF_BUFX))
break;
if (!r->m_pausing) {
r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ?
r->m_channelTimestamp[r->m_mediaChannel] : 0;
RTMP_SendPause(r, TRUE, r->m_pauseStamp);
r->m_pausing = 1;
} else if (r->m_pausing == 2) {
RTMP_SendPause(r, FALSE, r->m_pauseStamp);
r->m_pausing = 3;
}
break;
case 32:
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Stream BufferReady %d", __FUNCTION__, tmp);
break;
default:
tmp = AMF_DecodeInt32(packet->m_body + 2);
RTMP_Log(RTMP_LOGDEBUG, "%s, Stream xx %d", __FUNCTION__, tmp);
break;
}
}
if (nType == 0x1A) {
RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__);
if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01) {
RTMP_Log(RTMP_LOGERROR,
"%s: SWFVerification Type %d request not supported! Patches welcome...",
__FUNCTION__, packet->m_body[2]);
}
#ifdef CRYPTO
/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
/* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
else if (r->Link.SWFSize) {
RTMP_SendCtrl(r, 0x1B, 0, 0);
} else {
RTMP_Log(RTMP_LOGERROR,
"%s: Ignoring SWFVerification request, use --swfVfy!",
__FUNCTION__);
}
#else
RTMP_Log(RTMP_LOGERROR,
"%s: Ignoring SWFVerification request, no CRYPTO support!",
__FUNCTION__);
#endif
}
}