RTMP使用者控制訊息事件

weixin_33724059發表於2018-10-26

Handle_Control

本文記錄 RTMP 中的使用者控制訊息事件, 客戶端和伺服器傳送這一訊息來通知對端使用者控制事件

支援以下使用者控制事件型別:

  • Stream Begin(=0) : 伺服器傳送這個事件來通知客戶端一個流已經就緒並可以用來通訊。預設情況下,這一事件在成功接收到客戶端的應用連線命令之後以ID0傳送,這一事件資料為4位元組,代表了已就緒的流ID.
  • Stream EOF(=1):伺服器傳送這一事件來通知客戶端請求的流的回放資料已經結束。在傳送額外的命令之前不再傳送任何資料。客戶端將丟棄接收到這個流的訊息。 這一事件資料為4位元組,代表了回放已結束的流的流ID.
  • Stream Dry(=2):伺服器端傳送這一事件來通知客戶端當前流中以沒有資料。當伺服器端在一段時間內沒有檢測到任何訊息。它可以通知相關客戶端當前流已經沒有資料了。這一事件資料為4位元組,代表了已沒有的流的流ID.
  • SetBuffer Length(=3):客戶端傳送這一事件來通知伺服器端用於緩衝流中任何資料的快取大小(以毫秒為單位)。這一事件在服務端開始處理流之前就傳送。這一事件資料的前4個位元組代表了流ID4個位元組代表了以毫秒為單位的快取的長度.
  • StreamIs Recorded(=4):服務端傳送這一事件來通知客戶端當前流是一個錄製流。這一事件資料為4位元組,代表了錄製流的流ID
  • PingRequest(=6):服務端傳送這一事件用來測試是否能夠送達客戶端。事件資料為一個4位元組的TimeStamp,代表了服務端傳送這一命令時的伺服器本地時間。客戶端在接收到這一訊息後會立即傳送PingResponse回覆.

下面的程式碼時librtmp關於使用者控制訊息的處理

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

相關文章