SRS之SrsRtmpServer::connect_app详解

1. connect('live')

2. SrsRtmpServer::connect_app

位于 srs_rtmp_stack.cpp。在 SRS 的 RTMP 连接处理线程 conn 中,当与客户端 handshake 完成后,即调用该函数接收客户端第一个 RTMP 消息:connect。如上图.

int SrsRtmpServer::connect_app(SrsRequest* req)
{
    int ret = ERROR_SUCCESS;
    
    SrsCommonMessage* msg = NULL;
    SrsConnectAppPacket* pkt = NULL;
    if ((ret = expect_message<SrsConnectAppPacket>(&msg, &pkt)) != ERROR_SUCCESS) {
        srs_error("expect connect app message failed. ret=%d", ret);
        return ret;
    }
    SrsAutoFree(SrsCommonMessage, msg);
    SrsAutoFree(SrsConnectAppPacket, pkt);
    srs_info("get connect app message");
    
    SrsAmf0Any* prop = NULL;
    
    if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) {
        ret = ERROR_RTMP_REQ_CONNECT;
        srs_error("invalid request, must specifies the tcUrl. ret=%d", ret);
        return ret;
    }
    req->tcUrl = prop->to_str();
    
    if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) {
        req->pageUrl = prop->to_str();
    }
    
    if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) {
        req->swfUrl = prop->to_str();
    }
    
    if ((prop = pkt->commnad_object->ensure_property_number("objectEncoding")) != NULL) {
        req->objectEncoding = prop->to_number();
    }
    
    if (pkt->args) {
        srs_freep(req->args);
        req->args = pkt->args->copy()->to_object();
        srs_info("copy edge traverse to origin auth args.");
    }
    
    srs_info("get connect app message params success.");
    
    srs_discovery_tc_url(req->tcUrl, 
        req->schema, req->host, req->vhost, req->app, req->port, 
        req->param);
    req->strip();
    
    return ret;
}

2.1 SrsRtmpServer: expect_message

位于srs_rtmp_stack.hpp。
这是一个定义在 SrsRtmpServer 类中的模板函数。该函数指定了一个期待接收的消息,若接收到的是其他消息则丢弃,直到获取到指定的消息为止。

/**
 * expect a specified message, drop others until got specified one.
 * @pmsg, user must free it. NULL if not success.
 * @ppacket, user must free it, which decode from payload of message. NULL if not success.
 * @remark, only when success, user can use and must free the pmsg and ppacket.
 * for example:
 *      SrsCommonMessage* msg = NULL;
 *      SrsConnectAppResPacket* pkt = NULL;
 *      if ((ret = server->expect_message<SrsConnectAppResPacket>(protocol, &msg, &pkt)) 
 *          != ERROR_SUCCESS) {
 *          return ret;
 *      }
 *      // use then free msg and pkt
 *      srs_freep(msg);
 *      srs_freep(pkt);
 * user should never recv message and covert it, use this method instead.
 * if need to set timeout, use set timeout of SrsProtocol.
 */
template<class T>
int expect_message(SrsCommmonMessage** pmsg, T** ppacket)
{
    return protocol->expect_message<T>(pmsg, ppacket);
}

接着调用 SrsProtocol 类定义的同名模板函数 expect_message.

在分析 SrsProtocol: expect_message 之前,先看 SrsRtmpServer 类中类型为 SrsProtocol 类的成员 protocol 是如何构造的。

2.1.1 SrsProtocol 的构造函数:

位于 srs_rtmp_stack.hpp。

SrsProtocol 类提供了 RTMP 消息协议服务,从 RTMP chunk stream 中接收 RTMP 消息,或者通过 RTMP chunk stream 发送 RTMP 消息。

/**
 * @io: 构造时传入的是 SrsStSocket 类的指针,ISrsProtocolReaderWriter 类
 * 是 SrsStSocket 类的父类.
 */
SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io)
{
    /* 缓存从 io 中接收到的rtmp消息数据,并为 stream 提供服务,
     * 默认分配大小为 128K */
    in_buffer = new SrsFastBuffer();
    skt = io;
    
    /* 初始化块的大小,这里为 128 */
    in_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE;
    out_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE;
    
    /* out_iovs: cache for multiple messages send. */
    nb_out_iovs = SRS_CONSTS_IOVS_MAX;
    out_iovs = (iovec*)malloc(sizeof(iovec) * nb_out_iovs);
    // each chunk consumers at least 2 iovs
    srs_assert(nb_out_iovs >= 2);
    
    /* 标志位,是否警示用户增长 c0c3 头缓存 */
    warned_c0c3_cache_dry = false;
    /* 标志位,当接收到消息的时候是否自动响应 */
    auto_response_when_recv = true;
    /* 是否打印调试信息 */
    shuw_debug_info = true;
    /* 缓存的大小是由对方设置的 */
    in_buffer_length = 0;
    
    cs_cache = NULL;
    if (SRS_PERF_CHUNK_STREAM_CACHE > 0) {
        /* 构造 SRS_PERF_CHUNK_STREAM_CACHE 个 块流缓存SrsChunkStream  */
        cs_cache = new SrsChunkStream*[SRS_PERF_CHUNK_STREAM_CACHE];
    }
    /* 接收到的 RTMP 块流也许是交错的,因此使用 SrsChunkStream 来缓存输入的 RTMP 块流 */
    for (int cid = 0; cid < SRS_PERF_CHUNK_STREAM_CACHE; cid++) {
        SrsChunkStream* cs = new SrsChunkStream(cid);
        // set the perfer cid of chunk,
        // which will copy to the message received.
        cs->header.perfer_cid = cid;
        
        cs_cache[cid] = cs;
    }
}

2.1.2 SrsFastBuffer 构造函数

SrsFastBuffer 类为协议提供字节缓存,将从 socket 接收到的数据存放到该类中,然后解码为 RTMP 消息。

用法:

ISrsBufferReader* r = ......;
SrsFastBuffer* fb = ......;
fb->grow(r, 1024);
char* header = fb->read_slice(100);
char* payload = fb->read_payload(924);

构造函数具体代码如下:

// the default recv buffer size, 128KB.
#define SRS_DEFAULT_RECV_BUFFER_SIZE 131072

/**
 * SrsFastBuffer:
 *
 * the buffer provices bytes cache for protocol. generally,
 * protocol recv data from socket, put into buffer, decode to RTMP message.
 * Usage: 
 *     ISrsBufferReader* r = ...;
 *     SrsFastBuffer* fb = ......;
 *     fb->grow(r, 1024);
 *     char* header = fb->read_slice(100);
 *     char* payload = fb->read_payload(924);
 */
SrsFastBuffer::SrsFastBuffer()
{
#ifdef SRS_PERF_MERGED_READ
    merged_read = false;
    _handler = NULL;
#endif
    
    /* 设置默认接收缓存的大小,128K */
    nb_buffer = SRS_DEFAULT_RECV_BUFFER_SIZE;
    buffer = (char*)malloc(nb_buffer);
    p = end = buffer;
}

2.1.3 SrsChunkStream 构造函数

接收到的 RTMP 块流也许不是顺序的,是交错的,因此,使用 SrsChunkStream 类去缓存接收到的 RTMP chunk streams。

/**
 * incoming chunk stream maybe interlaced,
 * use the chunk stream to cache the input RTMP chunk streams.
 */
SrsChunkStream::SrsChunkStream(int _cid)
{
    /* 代表基本头中的 chunk type,即fmt */
    fmt = 0;
    /* 初始化当前 SrsChunkStream 所属的块流 */
    cid = _cid;
    /* 标志位,表示 chunk 的消息头中是否有扩展时间戳 */
    extended_timestamp = false;
    /* 读取到的部分/完整的消息 */
    msg = NULL;
    /* 解码的消息计数值,用来鉴别该 chunk stream 是否是新的 */
    msg_count = 0;
}

2.2 SrsProtocol: expect_message

该函数是一个模板函数,通过指定接收的期待接收的类来接收相应 RTMP 消息,如调用时指定 T 为 SrsConnectAppPacket,第二个参数 ppacket 为 SrsConnectAppPacket,则表示本次期望接收到的消息为客户端发送的 connect 命令消息。

/**
 * expect a specified message, drop others until got specified one.
 * @pmsg, user must free it. NULL if not success.
 * @ppacket, user must free it, which decode from payload of message. NULL if not success.
 * @remark, only when success, user can use and must free the pmsg and ppacket.
 * for exampel:
 *     SrsCommomMessage* msg = NULL;
 *     SrsConnectAppResPacket* pkt = NULL;
 *     if ((ret = protocol->expect_message<SrsConnectAppResPacket>(protocol, &msg, &pkt)) 
 *         != ERROR_SUCCESS) {
 *         return ret;
 *     }
 *     // use then free msg and pkt
 *     srs_freep(msg);
 *     srs_freep(pkt);
 * user should never recv message and convert it, use this method instead.
 * if need to set timeout, use set timeout of SrsProtocol.
 */
template<class T>
int expect_message(SrsCommonMessage** pmsg, T** ppacket)
{
    *pmsg = NULL;
    *ppacket = NULL;
    
    int ret = ERROR_SUCCESS;
    
    while (true) {
        SrsCommonMessage* msg = NULL;
        /* 开始接收客户端发送的 RTMP 消息 */
        if ((ret = recv_message(&msg)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("recv message failed. ret=%d", ret);
            }
            return ret;
        }
        srs_verbose("recv message success.");
        
        SrsPacket* packet = NULL;
        if ((ret = decode_message(msg, &packet)) != ERROR_SUCCESS) {
            srs_error("decode message failed. ret=%d", ret);
            srs_freep(msg);
            srs_freep(packet);
            return ret;
        }
        
        /**
         * dynamic_cast:
         *     将基类的指针或引用安全地转换成派生类的指针或引用,并用派生类的指针或引用
         * 调用非虚函数。如果是基类指针或引用调用的是虚函数无需转换就能在运行时调用派生类
         * 的虚函数。
         * @remark,当将 dynamic_cast 用于某种类型的指针或引用时,只有该类含有虚函数时,才能
         * 进行这种转换。否则,编译器会报错。
         */
        T* pkt = dynamic_cast<T*>(packet);
        if (!pkt) {
            srs_info("drop message(type=%d, size=%d, time=%"PRId64", sid=%d).", 
                msg->header.message_type, msg->header.payload_length,
                msg->header.timestamp, msg->header.stream_id);
            srs_freep(msg);
            srs_freep(packet);
            continue;
        }
        
        *pmsg = msg;
        *ppacket = pkt;
        break;
    }
    
    return ret;
}

2.2.1 SrsProtocol::recv_message

位于 srs_rtmp_stack.cpp。

/**
 * recv a RTMP message, which is bytes oriented.
 * user can use decode_message to get the decoded RTMP packet.
 * @param pmsg, set the received message,
 *      always NULL if errno,
 *      NULL for unknown packet but return success.
 *      never NULL if decode success.
 * @remark, drop message when msg is empty or payload length is empty.
 */
int SrsProtocol::recv_message(SrsCommonMessage** pmsg)
{
    *pmsg = NULL;
    
    int ret = ERROR_SUCCESS;
    
    while (true) {
        SrsCommonMessage* msg = NULL;
        
        if ((ret = recv_interlaced_message(&msg)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("recv interlaced message failed. ret=%d", ret);
            }
            srs_freep(msg);
            return ret;
        }
        srs_verbose("entire msg received");
        
        if (!msg) {
            srs_info("got empty message without error.");
            continue;
        }
        
        if (msg->size <= 0 || msg->header.payload_length <= 0) {
            srs_trace("ignore empty message(type=%d, size=%d, time=%"PRId64", sid=%d).",
                msg->header.message_type, msg->header.payload_length,
                msg->header.timestamp, msg->header.stream_id);
            srs_freep(msg);
            continue;
        }
        
        if ((ret = on_recv_message(msg)) != ERROR_SUCCESS) {
            srs_error("hook the received msg failed. ret=%d", ret);
            srs_freep(msg);
            return ret;
        }
        
        srs_verbose("got a msg, cid=%d, type=%d, size=%d, time=%"PRId64, 
            msg->header.perfer_cid, msg->header.message_type, msg->header.payload_length, 
            msg->header.timestamp);
        *pmsg = msg;
        break;
    }
    
    return ret;
}

2.2.2 SrsProtocol::recv_interlaced_message

位于 srs_rtmp_stack.cpp。

/**
 * how many chunk stream to cache, [0, N].
 * to improve about 10% performance when chunk size small, and 5% for large chunk.
 * @see https://github.com/ossrs/srs/issues/249: cache the chunk headers info to 
 *      improve performance
 * @remark 0 to disable the chunk stream cache.
 */
#define SRS_PERF_CHUNK_STREAM_CACHE 16


/**
 * recv bytes oriented RTMP message from protocol stack.
 * return error if error occur and never set the pmsg,
 * return success amd pmsg set to NULL if no entire message got,
 * return success and pmsg set to entire message if got one.
 */
int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg)
{
    int ret = ERROR_SUCCESS;
    
    // chunk stream basic header.
    char fmt = 0;
    int cid = 0;
    /* 读取 RTMP chunk stream 的基本头 */
    if ((ret = read_basic_header(fmt, cid)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read basic header failed. ret=%d", ret);
        }
        return ret;
    }
    srs_verbose("read basic header success. fmt=%d, cid=%d", fmt, cid);
    
    // the cid must not negative.
    srs_assert(cid >= 0);
    
    // get the cached chunk stream.
    SrsChunkStream* chunk = NULL;
    
    // use chunk stream cache to get the chunk info.
    // @see https://github.com/ossrs/srs/issues/249: cache the chunk headers info 
    //      to improve performance 
    if (cid < SRS_PERF_CHUNK_STREAM_CACHE) {
        // chunk stream cache hit.
        srs_verbose("cs-cache hit, cid=%d", cid);
        // already init, use it directly
        /* 从已经分配好 SRS_PERF_CHUNK_STREAM_CACHE 个的 cs_cache 数组中
         * 取出一个 cid 对应位置的 SrsChunkStream */
        chunk = cs_cache[cid];
        srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, "
                    "message(type=%d, size=%d, time=%"PRId64", sid=%d)",
            chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), 
            chunk->header.message_type, chunk->header.payload_length,
            chunk->header.timestamp, chunk->header.stream_id);
    } else {
        // chunk stream cache miss, use map.
        /* 先从 map 类型的容器 chunk_streams 中查找该 cid 对应的 SrsChunkStream 是否存在 */
        if (chunk_streams.find(cid) == chunk_streams.end()) {
            /* 根据 cid 重新构建一个 SrsChunkStream,并将其放入到 map 容器 chunk_streams 中 */
            chunk = chunk_streams[cid] = new SrsChunkStream(cid);
            // set the perfer cid of chunk,
            // which will copy to the message received.
            chunk->header.perfer_cid = cid;
            srs_verbose("cache new chunk stream: fmt=%d, cid=%d", fmt, cid);
        } else {
            chunk = chunk_streams[cid];
            srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, "
                        "message(type=%d, size=%d, time=%"PRId64", sid=%d)",
                chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), 
                chunk->header.message_type, chunk->header.payload_length,
                chunk->header.timestamp, chunk->header.stream_id);
        }
    }
    
    // chunk stream message header
    if ((ret = read_message_header(chunk, fmt)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read message header failed. ret=%d", ret);
        }
        return ret;
    }
    srs_verbose("read message header success. "
            "fmt=%d, ext_time=%d, size=%d, message(type=%d, size=%d, "
            "time=%"PRId64", sid=%d)", 
            fmt, chunk->extended_timestamp, (chunk->msg? chunk->msg->size : 0), 
            chunk->header.message_type, chunk->header.payload_length, 
            chunk->header.timestamp, chunk->header.stream_id);
    
    // read msg payload from chunk stream.
    SrsCommonMessage* msg = NULL;
    if ((ret = read_message_payload(chunk, &msg)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read message payload failed. ret=%d", ret);
        }
        return ret;
    }
    
    // not got an entire RTMP message, try next chunk.
    if (!msg) {
        srs_verbose("get partial message success. size=%d, "
                    "message(type=%d, size=%d, time=%"PRId64", sid=%d)",
                (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), 
                chunk->header.message_type, chunk->header.payload_length,
                chunk->header.timestamp, chunk->header.stream_id);
        return ret;
    }
    
    *pmsg = msg;
    srs_info("get entire message success. size=%d, message(type=%d, "
             "size=%d, time=%"PRId64", sid=%d)",
            (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), 
            chunk->header.message_type, chunk->header.payload_length,
            chunk->header.timestamp, chunk->header.stream_id);
    
    return ret;
}

2.2.3 SrsProtocol::read_basic_header

读取 RTMP chunk stream 的基本头。

/**
 * Chunk Basic Header
 * The Chunk Basic Header encodes the chunk stream ID and the chunk
 * type(represented by fmt field in the figure below). Chunk type
 * determines the format of the encoded message header. Chunk Basic
 * Header field may be 1, 2, or 3 bytes, depending on the chunk stream
 * ID.
 *
 * The bits 0-5 (least significant) in the chunk basic header represent
 * the chunk stream ID.
 *
 * Chunk stream IDs 2-63 can be encoded in the 1-byte version of this 
 * field.
 *    0 1 2 3 4 5 6 7
 *   +-+-+-+-+-+-+-+-+
 *   |fmt|   cs id   |
 *   +-+-+-+-+-+-+-+-+
 *   Figure 6 Chunk basic header 1
 * 
 * Chunk stream IDs 64-319 can be encoded in the 2-byte version of this
 * field. ID is computed as (the second byte + 64).
 *   0                   1
 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   |fmt|    0      | cs id - 64    |
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   Figure 7 Chunk basic header 2
 * 
 * Chunk stream IDs 64-65599 can be encoded in the 3-byte version of
 * this field. ID is computed as ((the third byte)*256 + the second byte
 * + 64).
 *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   |fmt|     1     |         cs id - 64            |
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   Figure 8 Chunk basic header 3
 *
 * cs id: 6 bits
 * fmt: 2 bits
 * cs id - 64: 8 or 16 bits
 *
 * Chunk stream IDs with values 64-319 could be represented by both 2-byte
 * version and 3-byte version of this field.
 */
int SrsProtocol::read_basic_header(char& fmt, int& cid)
{
    int ret = ERROR_SUCCESS;
    
    /* 当前用户需要从SrsFastBuffer中读取 1 字节的数据,但SrsFastBuffer中已有大于或等于
     * 1 字节的数据时,直接返回,否则需要从 socket 中读取数据,将读取到的数据存放到
     * SrsFastBuffer中(实际上该SrsFastBuffer从 socket 中的读取数据大小为当前缓存的
     * 空闲空间大小) */
    if ((ret = in_buffer->grow(skt, 1)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read 1bytes basic header failed. required_size=%d, ret=%d", 1, ret);
        }
        return ret;
    }
    
    /* 从SrsFastBuffer中读取 1 字节的数据 */
    fmt = in_buffer->read_1byte();
    cid = fmt & 0x3f;
    fmt = (fmt >> 6) & 0x03;
    
    // 2-63, 1B chunk header
    if (cid > 1) {
        srs_verbose("basic header parsed. fmt=%d, cid=%d", fmt, cid);
        return ret;
    }
    
    // 64-319, 2B chunk header
    if (cid == 0) {
        /* 若 cid 为 0,则表明basic header为 2 字节 */
        if ((ret = in_buffer->grow(skt, 1)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("read 2bytes basic header failed. required_size=%d, ret=%d", 
                          1, ret);
            }
            return ret;
        }
        
        cid = 64;
        cid += (u_int8_t)in_buffer->read_1byte();
        srs_verbose("2bytes basic header parsed. fmt=%d, cid=%d", fmt, cid);
    // 64-65599, 3B chunk header
    } else if (cid == 1) {
        if ((ret = in_buffer->grow(skt, 2)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("read 3bytes basic header failed. required_size=%d, ret=%d", 
                          2, ret);
            }
            return ret;
        }
        
        cid = 64;
        cid += (u_int8_t)in_buffer->read_1byte();
        cid += ((u_int8_t)in_buffer->read_1byte()) * 256;
        srs_verbose("3bytes basic header parsed. fmt=%d, cid=%d", fmt, cid);
    } else {
        srs_error("invalid path, impossible basic header.");
        srs_assert(false);
    }
    
    return ret;
}

2.2.4 SrsFastBuffer::grow

由前面调用可知,传入的 reader 参数(派生类 -> 父类):SrsStSocket -> ISrsProtocolReaderWriter -> ISrsProtocolReader
and ISrsProtocolWriter.

/**
 * grow buffer to the required size, loop to read from skt to fill.
 * @param reader, read more bytes from reader to fill the buffer to required size.
 * @param required_size, loop to fill to ensure buffer size to required.
 * @return an int error code, error if required_size negative.
 * @remark, we actually maybe read more than required_size, maybe 4K for example.
 */
int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size)
{
    int ret = ERROR_SUCCESS;
    
    // already got required size of bytes.
    if (end - p >= required_size) {
        return ret;
    }
    
    // must be positive.
    srs_assert(required_size > 0);
    
    /**
     * p: ptr to the current read position.
     * end: ptr to the content end.
     * buffer: ptr to the buffer,buffer <= p <= end <= buffer + nb_buffer
     * nb_buffer: the size of buffer
     */
    
    /* 计算当前缓存中空闲空间的大小 */
    // the free space of buffer,
    //     buffer = consumed_bytes + exists_bytes + free_space.
    int nb_free_space = (int)(buffer + nb_buffer - end);
    
    // the bytes already in buffer
    int nb_exists_bytes = (int)(end - p);
    srs_assert(nb_exists_bytes >= 0);
    
    /* 当没有空闲缓存时调整缓存的大小 */
    // resize the space when no left space.
    if (nb_free_space < required_size - nb_exists_bytes) {
        srs_verbose("move fast buffer %d bytes", nb_exists_bytes);
        
        /* 当缓存中没有需要读取的数据时,复位该缓存 */
        // reset or move to get more space.
        if (!nb_exists_bytes) {
            // reset when buffer is empty.
            p = end = buffer;
            srs_verbose("all consumed, reset fast buffer");
            
        /* 否则,若缓存中有未读取的数据,则将这些数据移到缓存的前面 */
        } else if (nb_exists_bytes < nb_buffer && p > buffer) {
            // move the left bytes to start of buffer.
            // @remark Only move memory when space is enough, or failed at next check.
            // @see https://github.com/ossrs/srs/issues/848
            buffer = (char*)memmove(buffer, p, nb_exists_bytes);
            p = buffer;
            end = p + nb_exists_bytes;
        }
        
        /* 检查当调整完缓存的大小后,空闲空间的大小是否足够存放请求的数据大小,若不足,则返回错误 */
        // check whether enough free space in buffer.
        nb_free_space = (int) (buffer + nb_buffer - end);
        if (nb_free_space < required_size - nb_exists_bytes) {
            ret = ERROR_READER_BUFFER_OVERFLOW;
            srs_error("buffer overflow, required=%d, max=%d, left=%d, ret=%d", 
                required_size, nb_buffer, nb_free_space, ret);
            return ret;
        }
    }
    
    /* 检查空闲缓存足够时,则开始读取数据 */
    // buffer is ok, read required size of bytes.
    while (end - p < required_size) {
        ssize_t nread;
        /* 调用子类 SrsStSocket 的 read 函数读取数据 */
        if ((ret = reader->read(end, nb_free_space, &nread)) != ERROR_SUCCESS) {
            return ret;
        }
    
        /* 暂不分析 */
#ifdef SRS_PERF_MERGED_READ
        /**
         * to improve read performance, merge some packets then read,
         * when it on and read small bytes, we sleep to wait more data.
         * that is, we merge some data to read together.
         * @see https://github.com/ossrs/srs/issues/241
         */
        if (merged_read && _handler) {
            _handler->on_read(nread);
        }
#endif
        
        // we just move the ptr to next.
        srs_assert((int)nread > 0);
        end += nread;
        nb_free_space -= nread;
    }
    
    return ret;
}

2.2.5 SrsStSocket::read

int SrsStSocket::read(void* buf, size_t size, ssize_t* nread)
{
    int ret = ERROR_SUCCESS;
    
    /* 该函数在一个 while 循环中首先尝试读取 size 字节的数据,若能读取到,则直接返回。
     * 否则,调用 st_netfd_poll 将当前线程添加到 IO 队列中,并设置监听条件为 POLLIN,
     * 直到读取到指定的数据为止 */
    ssize_t nb_read = st_read(stfd, buf, size, recv_timeout);
    if (nread) {
        *nread = nb_read;
    }
    
    // On success a non-negative integer indicating the number of bytes actually 
    // read is returned (a value of 0 means the network connection is closed or 
    // end of file is reached). Otherwise, a value of -1 is returned and errno 
    // is set to indicate the error.
    if (nb_read <= 0) {
        // @see https://github.com/ossrs/srs/issues/200
        if (nb_read < 0 && errno == ETIME) {
            return ERROR_SOCKET_TIMEOUT;
        }
        
        if (nb_read == 0) {
            errno = ECONNRESET;
        }
        
        return ERROR_SOCKET_READ;
    }
    
    recv_bytes += nb_read;
    
    return ret;
}

2.2.6 SrsProtocol::read_message_header

/**
 * Chunks of Type 0 are 11 bytes long. This type MUST be used at the
 * start of a chunk stream, and whenever the stream timestamp goes
 * backward (e.g., because of a backward seek).
 */
#define RTMP_FMT_TYPE0              0
/**
 * Chunks of Type 1 are 7 bytes long. The message stream ID is not 
 * included; this chunk takes the same stream ID as the preceding chunk.
 * Streams with variable-sized messages (for example, many video
 * formats) SHOULD use this format for the first chunk of each new 
 * message after the first.
 */
#define RTMP_FMT_TYPE1              1

/**
 * parse the message header.
 *    3bytes: timestamp delta, fmt=0,1,2
 *    3bytes: payload length,  fmt=0,1
 *    1byte:  message type,    fmt=0,1
 *    4bytes: stream id,       fmt=0
 * where:
 *    fmt=0, 0x0X
 *    fmt=1, 0x4X
 *    fmt=2, 0x8X
 *    fmt=3, 0xCX
 * 
 * read the chunk message header(timestamp, payload_length, message_type, stream_id) 
 * from chunk stream and save to SrsChunkStream.
 */
int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt)
{
    int ret = ERROR_SUCCESS;
    
    /**
     * we should not assert anything about fmt, for the first packet.
     * (when first packet, the chunk->msg is NULL).
     * the fmt maybe 0/1/2/3, the FMLE will send a 0xC4 for some audio packet.
     * the previous packet is:
     *     04                // fmt=0, cid=4
     *     00 00 1a          // timestamp=26
     *     00 00 9d          // payload_length=157
     *     08                // message_type=8(audio)
     *     01 00 00 00       // stream_id=1
     * the current packet maybe:
     *     c4                // fmt=3, cid=4
     * it's ok, for the packet is audio, and timestamp delta is 26.
     * the current packet must be parsed as:
     *     fmt=0, cid=4
     *     timestamp=26+26=52
     *     payload_length=157
     *     message_type=8(audio)
     *     stream_id=1
     * so we must update the timestamp even fmt=3 for first packet.
     */
     // fresh packet used to update the timestamp even fmt=3 for first packet.
     // fresh packet always means the chunk is the first one of message.
    bool is_first_chunk_of_msg = !chunk->msg;
    
    /* 若当前chunk是消息中的第一个chunk时,当前的chunk的fmt必须是0 */
    // but, we can ensure that when a chunk stream is fresh,
    // the fmt must be 0, a new stream.
    if (chunk->msg_count == 0 && fmt != RTMP_FMT_TYPE0) {
        // for librtmp, if ping, it will send a fresh stream with fmt=1,
        // 0x42             where: fmt=1, cid=2, protocol control user-control message
        // 0x00 0x00 0x00   where: timestamp=0
        // 0x00 0x00 0x06   where: payload_length=6
        // 0x04             where: message_type=4(protocol control user-control message)
        // 0x00 0x06            where: event Ping(0x06)
        // 0x00 0x00 0x0d 0x0f  where: event data 4bytes ping timestamp.
        // @see: https://github.com/ossrs/srs/issues/98
        if (chunk->cid == RTMP_CID_ProtocolControl && fmt == RTMP_FMT_TYPE1) {
            srs_warn("accept cid=2, fmt=1 to make librtmp happy.");
        } else {
            // must be a RTMP protocol level error.
            ret = ERROR_RTMP_CHUNK_START;
            srs_error("chunk stream is fresh, fmt must be %d, actual is %d. cid=%d, ret=%d", 
                RTMP_FMT_TYPE0, fmt, chunk->cid, ret);
            return ret;
        }
    }
    
    // when exists cache msg, means got an partial message,
    // the fmt must not be type0 which means new message.
    if (chunk->msg && fmt == RTMP_FMT_TYPE0) {
        ret = ERROR_RTMP_CHUNK_START;
        srs_error("chunk stream exists, "
            "fmt must not be %d, actual is %d. ret=%d", RTMP_FMT_TYPE0, fmt, ret);
        return ret;
    }
    
    // create msg when new chunk stream start
    if (!chunk->msg) {
        /* 若当前的chunk是块流的第一个块时,构造一个新的 SrsCommonMessage  */
        chunk->msg = new SrsCommonMessage();
        srs_verbose("create message for new chunk, fmt=%d, cid=%d", fmt, chunk->cid);
    }
    
    // read message header from socket to buffer.
    static char mh_sizes[] = {11, 7, 3, 0};
    int mh_size = mg_sizes[(int)fmt];
    srs_verbose("calc chunk message header size. fmt=%d, mh_size=%d", fmt, mh_size);
    
    if (mh_size > 0 && (ret = in_buffer->grow(skt, mh_size)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read %dbytes message header failed. ret=%d", mh_size, ret);
        }
        
        return ret;
    }
    
    /**
     * parse the message header.
     *   3bytes: timestamp delta,    fmt=0,1,2
     *   3bytes: payload length,     fmt=0,1
     *   1bytes: message type,       fmt=0,1
     *   4bytes: stream id,          fmt=0
     * where:
     *   fmt=0, 0x0X
     *   fmt=1, 0x4X
     *   fmt=2, 0x8X
     *   fmt=3, 0xCX
     */
    // see also: ngx_rtmp_recv
    if (fmt <= RTMP_FMT_TYPE2) {
        char* p = in_buffer->read_slice(mh_size);
        
        char* pp = (char*)&chunk->header.timestamp_delta;
        pp[2] = *p++;
        pp[1] = *p++;
        pp[0] = *p++;
        pp[3] = 0;
        
        // fmt: 0
        // timestamp: 3 bytes
        // IF the timestamp is greater than or equal to 16777215
        // (hexadecimal 0x00ffffff), this value MUST be 16777215, and the
        // 'extended timestamp header' MUST be present. Otherwise, this
        // value SHULD be the entire timestamp.
        // 
        // fmt: 1 or 2
        // timestamp delta: 3 bytes
        // If the delta is greater than or equal to 16777215 (hexadecimal
        // 0x00ffffff), this value MUST be 16777215, and the 'extended
        // timestamp header' MUST be present. Otherwise, this value SHOULD be
        // the entire delta.
        chunk->extended_timestamp = (chunk->header.timestamp_delta >= 
                                     RTMP_EXTENDED_TIMESTAMP);
        if (!chunk->extended_timestamp) {
            /*
             * Extended timestamp: 0 or 4 bytes
             * This field MUST be sent when the normal timestamp is set to
             * 0xffffff, it MUST NOT be sent if the normal timestamp is set to
             * anything else. So for values less than 0xffffff the normal 
             * timestamp field SHOULD be used in which case the extended timestamp
             * MUST NOT be present. For values greater than or equal to 0xffffff
             * the normal timestamp field MUST NOT be used and MUST be set to
             * 0xffffff and the extended timestamp MUST be sent.
             */
            if (fmt == RTMP_FMT_TYPE0) {
                /*
                 * Type 0
                 * For a type-0 chunk, the absolute timestamp of the message is sent
                 * here.
                 */
                chunk->header.timestamp = chunk->header.timestamp_delta;
            } else {
                /*
                 * Type 1
                 * Type 2
                 * For a type-1 or type-2 chunk, the difference between the previous
                 * chunk's timestamp and the current chunk's timestamp is sent here.
                 */
                chunk->header.timestamp += chunk->header.timestamp_delta;
            }
        }
        
        if (fmt <= RTMP_FMT_TYPE1) {
            int32_t payload_length = 0;
            pp = (char*)&payload_length;
            pp[2] = *p++;
            pp[1] = *p++;
            pp[0] = *p++;
            pp[3] = 0;
            
            /* for a message, if msg exists in cache, the size must not changed.
             * always use the actual msg size to compare, for the cache payload 
             * length can changed, for the fmt type1(stream_id not changed), 
             * user can change the payload length(it's not allowed in the continue chunks).
             */
            if (!is_first_chunk_of_msg && chunk->header.payload_length != payload_length) {
                ret = ERROR_RTMP_PACKET_SIZE;
                srs_error("msg exists in chunk cache, "
                    "size=%d cannot change to %d, ret=%d", 
                    chunk->header.payload_length, payload_length, ret);
                return ret;
            }
            
            chunk->header.payload_length = payload_length;
            chunk->header.message_type = *p++;
            
            if (fmt == RTMP_FMT_TYPE0) {
                /* 消息流 id 是小端存储的 */
                pp = (char*)&chunk->header.stream_id;
                pp[0] = *p++;
                pp[1] = *p++;
                pp[2] = *p++;
                pp[3] = *p++;
                srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, "
                            "time=%"PRId64", payload=%d, type=%d, sid=%d", 
                    fmt, mh_size, chunk->extended_timestamp, chunk->header.
                    timestamp, chunk->header.payload_length, 
                    chunk->header.message_type, chunk->header.stream_id);
                    
            /* fmt = 1 */
            } else {
                srs_verbose("header read completed. fmt=%d, mh_size=%d, "
                            "ext_time=%d, time=%"PRId64", payload=%d, type=%d", 
                    fmt, mh_size, chunk->extended_timestamp, 
                    chunk->header.timestamp, chunk->header.payload_length, 
                    chunk->header.message_type);
            }
            
        /* fmt = 2 */
        } else {
            srs_verbose("header read completed. fmt=%d, "
                        "mh_size=%d, ext_time=%d, time=%"PRId64"", 
                fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp);
        }
        
    /* 否则为 fmt=3 */
    } else {
        // update the timestamp event fmt=3 for first chunk packet
        if (is_first_chunk_id_msg && !chunk->extended_timestamp) {
            chunk->header.timestamp += chunk->header.timestamp_delta;
        }
        srs_verbose("header read completed. fmt=%d, size=%d, ext_time=%d", 
            fmt, mh_size, chunk->extended_timestamp);
    }
    
    // read extended-timestamp
    if (chunk->extended_timestamp) {
        mh_size += 4;
        srs_verbose("read header ext time. fmt=%d, ext_time=%d, mh_size=%d", 
                    fmt, chunk->extended_timestamp, mh_size);
        if ((ret = in_buffer->grow(skt, 4)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("read %dbytes message header failed. required_size=%d, ret=%d", 
                          mh_size, 4, ret);
            }
            return ret;
        }
        // the ptr to the slice maybe invalid when grow()
        // reset the p to get 4bytes slice.
        char* p = in_buffer->read_slice(4);
        
        u_int32_t timestamp = 0x00;
        char* pp = (char*)&timestamp;
        pp[3] = *p++;
        pp[2] = *p++;
        pp[1] = *p++;
        pp[0] = *p++;
        
        // always use 31bits timestamp, for some server may use 32bits extended timestamp.
        // @see https://github.com/ossrs/srs/issues/111
        timestamp &= 0x7fffffff;
        
        /**
         * RTMP specification and ffmpeg/librtmp is false,
         * but, adobe changed the specification, so flash/FMLE/FMS always true.
         * default to true to support flash/FMLE/FMS.
         *
         * ffmpeg/librtmp may donot send this field, need to detect the value.
         * @see also: http://blog.csdn.net/win_lin/article/details/13363699
         * compare to the chunk timestamp, which is set by chunk message header
         * type 0, 1 or 2.
         *
         * @remark, nginx send the extended-timestamp in sequence-header,
         * and timestamp delta in continue C1 chunks, and so compatible with ffmpeg,
         * that is, there is no continue chunks and extended-timestamp in nginx-rtmp.
         *
         * @remark, srs always send the extenede-timestamp, to keep simple,
         * and compatible with adobe products.
         */
        u_int32_t chunk_timestamp = (u_int32_t) chunk->header.timestamp;
        
        /**
         * if chunk_timestamp<=0, the chunk previous packet has no extended-timestamp,
         * always use the extended timestamp.
         */
        /**
         * about the is_first_chunk_of_msg.
         * @remark, for the first chunk of message, always use the extended timestamp.
         */
        if (!is_first_chunk_of_msg && chunk_timestamp > 0 && chunk_timestamp != timestamp) {
            mg_size -= 4;
            in_buffer->skip(-4);
            srs_info("no 4bytes extended timestamp in the continued chunk");
        } else {
            chunk->header.timestamp = timestamp;
        }
        srs_verbose("header read ext_time completed. time=%"PRId64"", 
                    chunk->header.timestamp);
    }
    
    /**
     * the extended-timestamp must be unsigned-int,
     *     24bits timestamp: 0xffffff = 16777215ms = 16777.215s = 4.66h
     *     32bits timestamp: 0xffffffff = 4294967295ms = 4294967.295s = 1193.046h = 49.71d
     * because the rtmp protocol says the 32bits timestamp is about "50 days":
     *     3. Byte Order, Alignment, and Time Format
     *         Because timestamp are generally only 32 bits long, they will roll
     *         over after fewer than 50 days.
     *
     * but, its sample says the timestamp is 31bits:
     *     An application could assume, for example, that all
     *     adjacent timestamps are within 2^31 milliseconds of each other, so
     *     10000 comes after 4000000000, while 3000000000 comes before
     *     4000000000.
     * and flv specification says timestamp is 31bits:
     *     Extension of the Timestamp field to form a SI32 value. This
     *     field represents the upper 8 bits, while the previous
     *     Timestamp field represent the lower 24 bits of the time in
     *     milliseconds.
     * in a word, 31bits timestamp is ok.
     * convert extended timestamp to 31bits.
     */
    chunk->header.timestamp &= 0x7fffffff;
    
    /* valid message, the payload_length is 24bits,
     * so it shuld never be negative. */
    srs_assert(chunk->header.payload_length >= 0);
    
    /* copy header to msg */
    chunk->msg->header = chunk->header;
    
    /* increase the msg count, the chunk stream can accept fmt=1/2/3 message now. */
    chunk->msg_count++;
    
    return ret;
}

猜你喜欢

转载自www.cnblogs.com/jimodetiantang/p/9072455.html
srs