SRS之SrsRtmpConn::stream_service_cycle详解

首先使用 obs 推流符合如下流程:参考自 Hanvision Makito X cann't publish to SRS. .

FFMPEG:

C/S: Handshake
C: ConnectApp() tcUrl=xxx
S: Ack Size 2500,000
S: Set Peer Bandwidth 2500,000
S: Set Chunk Size 60,000
C: Set Chunk Size 60,000
S: ConnectApp() _result
S: onBWDone()

C: releaseStream+FCPublish(s0)
C: createStream()
S: releaseStream _result
C: _checkbw()
S: FCPublish() _result
S: createStream() _result
C: publish(s0)
S: onFCPublish()
S: onStatus()

下面的分析是继服务器发送 onBWDone 后,进入 while 循环开始执行 stream_service_cycle。

1. SrsRtmpConn::stream_service_cycle

int SrsRtmpConn::stream_service_cycle()
{
    int ret = ERROR_SUCCESS;
    
    /* the rtmp client type: play/publish/unknown */
    SrsRtmpConnType type;
    /* 首先鉴别客户端请求的类型,是play/publish 或其他,还有播放/推流的流名称 */
    if ((ret = rtmp->identify_client(res->stream_id, type, req->stream, req->duration)) 
        != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("identify client failed. ret=%d", ret);
        }
        return ret;
    }
    req->strip();
    srs_trace("client identified, type=%s, stream_name=%s, duration=%.2f", 
        srs_client_type_string(type).c_str(), req->stream.c_str(), req->duration);
    
    /* 只有当配置文件中使能了 security 配置项,才会真正进入到该 check 函数进行
     * 一系列的检测 */
    /* allow all if security disabled. */
    // secutity check
    if ((ret = security->check(type, ip, req)) != ERROR_SUCCESS) {
        srs_error("security check failed. ret=%d", ret);
        return ret;
    }
    srs_info("security check ok");
    
    /* SRS 不允许请求的流名称为空 */
    // Never allow the empty stream name, for HLS may write to a file with empty name.
    // @see https://github.com/ossrs/srs/issues/834: 
    //      SRS2 crashed for TS encoder assert failed
    if (req->stream.empty()) {
        ret = ERROR_RTMP_STREAM_NAME_EMPTY;
        srs_error("RTMP: Empty stream name not allowed, ret=%d", ret);
        return ret;
    }
    
    /* 设置服务器 send/recv 的超时时间 */
    // client is identified, set the timeout to service timeout.
    rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
    rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US);
    
    /* 首先根据 vhost/app/stream 构造一个 stream_url,然后根据该 stream_url 在 SrsSource::pool
     * 中查找是否存在一个 stream_url 对应的 SrsSource,若能找到,则直接返回该 SrsSource,否则,
     * 新构造一个 SrsSource,并将其按 stream_url 放到 SrsSource::pool map 容器中 */
    // find a source to serve.
    SrsSource* source = NULL;
    if ((ret = SrsSorce::fetch_or_create(req, server, &source)) != ERROR_SUCCESS) {
        return ret;
    }
    srs_assert(source != NULL);
    
    /* 构造统计类,将统计当前的 vhost、stream 等信息 */
    // update the statistic when source disconveried.
    SrsStatistic* stat = SrsStatistic::instance();
    if ((ret = stat->on_client(_srs_context->get_id(), req, this, type)) != ERROR_SUCCESS) 
    {
        srs_error("stat client failed. ret=%d", ret);
        return ret;
    }
    
    /* 若 vhost 中没有配置 mode,则返回 false */
    bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost);
    /* 默认开始 gop_cache */
    bool enabled_cache = _srs_config->get_gop_cache(req->vhost);
    srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%d[%d]", 
        req->get_stream_url().c_str(), ip.c_str(), enabled_cache, vhost_is_edge,
        source->source_id(), source->source_id());
    /* 根据 enabled_cache 设置是否启动 gop_cache,为 true,则启动 */
    source->set_cache(enabled_cache);
    
    /* 根据鉴别到的客户端的类型:play 或者 publish,开始进行相应的处理 */
    /* The type of client, play or publish. */
    client_type = type;
    switch (type) {
        case SrsRtmpConnPlay: {
            srs_verbose("start to play stream %s.", req->stream.c_str());
            
            // response connection start play
            if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) {
                srs_error("start to play stream failed. ret=%d", ret);
                return ret;
            }
            if ((ret = http_hooks_on_play()) != ERROR_SUCCESS) {
                srs_error("http hook on_play failed. ret=%d", ret);
                return ret;
            }
            
            srs_info("start to play stream %s success", req->stream.c_str());
            ret = playing(source);
            http_hooks_on_stop();
            
            return ret;
        }
        /* 由前面知,若 obs 推流的话为该类型 */
        case SrsRtmpConnFMLEPublish: {
            srs_verbose("FMLE start to publish stream %s.", req->stream.c_str());
            
            /* 该函数主要是接收并响应一系列消息:
             * C: FCPublish
             * S: FCPublish response
             * C: createStream
             * S: createStream response
             * C: publish
             * S: publish response onFCPublish(NetStream.Publish.Start)
             * S: publish response onStatus(NetStream.Publish.Start) */
            if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) {
                srs_error("start to publish stream failed. ret=%d", ret);
                return ret;
            }
            
            /* 服务器响应客户端的publish消息后,即开始进入接收客户端推流的
             * metadata、video、audio等数据的处理 */
            return publishing(source);
        }
        case SrsRtmpConnHaivisionPublish: {
            srs_verbose("Haivision start to publish stream %s.", req->stream.c_str());
            
            if ((ret = rtmp->start_haivision_publish(res->stream_id)) != ERROR_SUCCESS) {
                srs_error("start to publish stream failed. ret=%d", ret);
                return ret;
            }
            
            return publishing(source);
        }
        case SrsRtmpConnFlashPublish: {
            srs_verbose("flash start to publish stream %s.", req->stream.c_str());
            
            if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) {
                srs_error("flash start to publish stream failed. ret=%d", ret);
                return ret;
            }
            
            return publishing(source);
        }
        default: {
            ret = ERROR_SYSTEM_CLIENT_INVALID;
            srs_info("invalid client type=%d. ret=%d", type, ret);
            return ret;
        }
    }
    
    return ret;
}

2. SrsRtmpServer::identify_client

该函数是对客户端请求进行鉴定,以便做出相应的处理。

int SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, 
    string& stream_name, double& duration)
{
    type = SrsRtmpConnUnknown;
    int ret = ERROR_SUCCESS;
    
    while (true) {
        SrsCommonMessage* msg = NULL;
        /* 接收一个完整的消息 */
        if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) {
            if (!srs_is_client_gracefully_close(ret)) {
                srs_error("recv identify client message failed. ret=%d", ret);
            }
            return ret;
        }
        
        SrsAutoFree(SrsCommonMessage, msg);
        SrsMessageHeader& h = msg->header;
        
        if (h.is_ackledgement() || h.is_set_chunk_size() || 
            h.is_windonw_ackledgenemt_size() || h.is_user_control_message()) {
            continue;
        }
        
        /* 若不为 amf 类型的消息,则忽略该消息,继续接收下一个消息 */
        if (!h.is_amf0_commnad() && !h.is_amf3_command()) {
            srs_trace("identify ignore message except "
                "AMF0/AMF3 command message. type=%#x", h.message_type);
            continue;
        }
        
        SrsPacket* pkt = NULL;
        /* 对接收到的 amf 命令消息进行解码,解码后的数据保存在 pkt 指向的子类中 */
        if ((ret = protocol->decode_message(msg, &pkt)) != ERROR_SUCCESS) {
            srs_error("identify decode message failed. ret=%d", ret);
            return ret;
        }
        
        SrsAutoFree(SrsPacket, pkt);
        
        /* 下面是通过 dynamic_cast 动态转换尝试将 pkt 转为指定的类型,
         * 若不为 NULL,则表明接收到的消息即为所要的消息 */
        if (dynamic_cast<SrsCreateStreamPacket*>(pkt)) {
            srs_info("identify client by create stream, play or flash publish.");
            return identify_create_stream_client(dynamic_cast<SrsCreateStreamPacket*>(pkt), 
                stream_id, type, stream_name, duration);
        }
        /* 当接收到的是 releaseStream/FCPublish/FCUnpublish 这三个中的一个时,
         * 构造的类都为 SrsFMLEStartPacket */
        if (dynamic_cast<SrsFMLEStartPacket*>(pkt)) {
            srs_info("identify client by releaseStream, fmle publish");
            /* 这里即可确定 client 的类型为 publish  */
            return identify_fmle_publish_client(dynamic_cast<SrsFMLEStartPacket*>(pkt), 
                                                type, stream_name);
        }
        if (dynamic_cast<SrsPlayPacket*>(pkt)) {
            srs_info("level0 identify client by play.");
            return identify_play_client(dynamic_cast<SrsPlayPacket*>(pkt), type, 
                                        stream_name, duration);
        }
        /* call msg,
         * support response null first,
         * @see https://github.com/ossrs/srs/issues/106
         * TODO: FIXME: response in right way, or forward in edge mode. */
        SrsCallPacket* call = dynamic_cast<SrsCallPacket*>(pkt);
        if (call) {
            SrsCallResPacket* res = new SrsCallResPacket(call->transaction_id);
            res->command_object = SrsAmf0Any::null();
            res->response = SrsAmf0Any::null();
            if ((ret = protocol->send_and_free_packet(res, 0)) != ERROR_SUCCESS) {
                if (!srs_is_system_control_error(ret) && 
                    !srs_is_client_gracefully_close(ret)) {
                    srs_warn("response call failed. ret=%d", ret);
                }
                return ret;
            }
            
            /* For encoder of Haivision, it always send a _checkbe call message.
             * @remark the next message is createStream, so we continue to identify it.
             * @see https://github.com/ossrs/srs/issues/844 */
            if (call->command_name == "_checkbw") {
                srs_info("Havision encoder identified.");
                continue;
            }
            continue;
        }
        
        srs_trace("ignore AMF0/AMF3 command message.");
    }
    
    return ret;
}

2.1 SrsProtocol::recv_message

int SrsProtocol::recv_message(SrsCommonMessage* pmsg) 
{
    *pmsg = NULL;
    
    int ret = ERROR_SUCCESS;
    
    while (true) {
        SrsCommonMessage* msg = NULL;
        
        /* 从 socket 中读取一个消息 */
        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;
        }
        
        /* 若接收到的是一些control消息,如 set chunk size 等,则更改上下文信息,
         * 其他的消息如音视频或amf类型的则不做处理 */
        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.1.1 SrsProtocol::recv_interlaced_message

int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg)
{
    int ret = ERROR_SUCCESS;
    
    // chunk stream basic header.
    char fmt = 0;
    int cid = 0;
    /* 读取 chunk 的基本头 */
    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);
    
    // thd cid must not negative.
    srs_assert(cid >= 0);
    
    /* 构造一个块流缓存,因为一个 RTMP 消息可能包含多个块,
     * 因此这里使用该块流缓存将多个块的信息保存起来,直到
     * 接收到一个完整的消息为止 */
    // get the cached chunk stream.
    SrsChunkStream* chunk = NULL;
    
    /* SrsChunkStream** cs_cache: 
     * SrsProtocol 类在构造的时候就为 cs_cache 数组分配了 SRS_PERF_CHUNK_STREAM_CACHE(16)
     * 个 SrsChunkStream 元素空间,当 chunk 的 cid 不超过该数组最大值时,可以直接从该数组中
     * 取出一个项,即 SrsChunkStream 使用 */
    // use chunk stream cache to get the chunk info.
    // @see https://github.com/ossrs/srs/issues/249
    if (cid < SRS_PERF_CHUNK_STREAM_CACHE) {
        // chunk stream cache hit.
        srs_verbose("cs-cache hit, cid=%d", cid);
        // already init, use it directly
        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.
        if (chunk_streams.find(cid) == chunk_streams.end()) {
            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);
        }
    }
    
    /* 根据 fmt 接收 chunk 的消息头 */
    // 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 SrsProtocol::decode_message

int SrsProtocol::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket)
{
    *ppacket = NULL;
    
    int ret = ERROR_SUCCESS;
    
    srs_assert(msg != NULL);
    srs_assert(msg->payload != NULL);
    srs_assert(msg->size > 0);
    
    SrsStream stream;
    
    /* 将消息负载的数据使用 SrsStream 类进行初始化 */
    // initialize the decode stream for all message,
    // it's ok for the initialize if fase and without memory copy.
    if ((ret = stream.initialize(msg->payload, msg->size)) != ERROR_SUCCESS) {
        srs_error("initialize stream failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("decode stream initialized success");
    
    // decode the packet.
    SrsPacket* packet = NULL;
    if ((ret = do_decode_message(msg->header, &stream, &packet)) != ERROR_SUCCESS) {
        srs_freep(packet);
        return ret;
    }
    
    // set to output ppacket only when success.
    *ppacket = packet;
    
    return ret;
}

2.2.1 SrsProtocol::do_decode_message

int SrsProtocol::do_decode_message(SrsMessageHeader& header, SrsStream* stream, 
    SrsPacket** packet)
{
    int ret = ERROR_SUCCESS;
    
    SrsPacket* packet = NULL;
    
    // decode specified packet type
    if (header.is_amf0_command() || header.is_amf3_command() 
        || header.is_amf0_data() || header.is_amf3_data()) {
        srs_verbose("start to decode AMF0/AMF3 command message.");
        
        // skip 1bytes to decode the amf3 command.
        if (header.is_amf3_command) && stream->require(1)) {
            srs_verbose("skip 1bytes to decode AMF3 command");
            stream->skip(1);
        }
        
        // amf0 command message.
        // need to read the command name.
        std::string command;
        /* 读取消息的命令名 */
        if ((ret = srs_amfo_read_string(stream, command)) != ERROR_SUCCESS) {
            srs_error("decode AMF0/AMF3 command name failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("AMF0/AMF3 command message, command_name=%s", command.c_str());
        
        // result/error packet
        if (command == RTMP_AMF0_COMMAND_RESULT || command == RTMP_AMF0_COMMAND_ERROR) {
            double transactionId = 0.0;
            if ((ret = srs_amf0_read_number(stream, transactionId)) != ERROR_SUCCESS) {
                srs_error("decode AMF0/AMF3 transcationId failed. ret=%d", ret);
                return ret;
            }
            srs_verbose("AMF0/AMF3 command id, transcationId=%.2f", transactionId);
            
            // reset stream, for header read completed.
            stream->skip(-1 * stream->pos());
            if (header.is_amf3_command()) {
                stream->skip(1);
            }
            
            // find the call name
            if (requests.find(transactionId) == requests.end()) {
                ret = ERROR_RTMP_NO_REQUEST;
                srs_error("decode AMF0/AMF3 request failed. ret=%d", ret);
                return ret;
            }
            
            std::string request_name = requests[transactionId];
            srs_verbose("AMF0/AMF3 request parsed. request_name=%s", request_name.c_str());
            
            if (request_name == RTMP_AMF0_COMMAND_CONNECT) {
                srs_info("decode the AMF0/AMF3 response command(%s message).", 
                         request_name.c_str());
                *ppacket = packet = new SrsConnectAppResPacket();
                return packet->decode(stream);
            } else if (request_name == RTMP_AMF0_COMMAND_CREATE_STREAM) {
                srs_info("decode the AMF0/AMF3 response command(%s message).", 
                         request_name.c_str());
                *ppacket = packet = new SrsCreateStreamResPacket(0, 0);
                return packet->decode(stream);
            } else if (request_name == RTMP_AMF0_COMMAND_RELEASE_STREAM
                || request_name == RTMP_AMF0_COMMAND_FC_PUBLISH
                || request_name == RTMP_AMF0_COMMAND_UNPUBLISH) {
                srs_info("decode the AMF0/AMF3 response command(%s message).", 
                         request_name.c_str());
                *ppacket = packet = new SrsFMLEStartResPacket(0);
                return packet->decode(stream);
            } else {
                ret = ERROR_RTMP_NO_REQUEST;
                srs_error("decode AMF0/AMF3 request failed. "
                    "request_name=%s, transactionId=%.2f, ret=%d", 
                    request_name.c_str(), transactionId, ret);
                return ret;
            }
        }
        
        // reset to zero(amf3 to 1) to restart decode.
        stream->skip(-1 * stream->pos());
        if (header.is_amf3_command()) {
            stream->skip(1);
        }
        
        /* 根据消息的命令名来构造对应的类,然后进行解码 */
        // decode command object.
        if (command == RTMP_AMF0_COMMAND_CONNECT) {
            srs_info("decode the AMF0/AMF3 command(connect vhost/app message).");
            *ppacket = packet = new SrsConnectAppPacket();
            return packet->decode(stream);
        } else if (command == RTMP_AMF0_COMMAND_CREATE_STREAM) {
            srs_info("decode the AMF0/AMF3 command(createStream message).");
            *ppacket = packet = new SrsCreateStreamPacket();
            return packet->decode(stream);
        } else if (command == RTMP_AMF0_COMMAND_PLAY) {
            srs_info("decode the AMF0/AMF3 command(paly message).");
            *ppacket = packet = new SrsPlayPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_PAUSE) {
            srs_info("decode the AMF0/AMF3 command(pause message).");
            *ppacket = packet = new SrsPausePacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_RELEASE_STREAM) {
            srs_info("decode the AMF0/AMF3 command(FMLE releaseStream message).");
            *ppacket = packet = new SrsFMLEStartPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_FC_PUBLISH) {
            srs_info("decode the AMF0/AMF3 command(FMLE FCPublish message).");
            *ppacket = packet = new SrsFMLEStartPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_PUBLISH) {
            srs_info("decode the AMF0/AMF3 command(publish message).");
            *ppacket = packet = new SrsPublishPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_UNPUBLISH) {
            srs_info("decode the AMF0/AMF3 command(unpublish message).");
            *ppacket = packet = new SrsFMLEStartPacket();
            return packet->decode(stream);
        } else if(command == SRS_CONSTS_RTMP_SET_DATAFRAME || 
                  command == SRS_CONSTS_RTMP_ON_METADATA) {
            srs_info("decode the AMF0/AMF3 data(onMetaData message).");
            *ppacket = packet = new SrsOnMetaDataPacket();
            return packet->decode(stream);
        } else if(command == SRS_BW_CHECK_FINISHED
            || command == SRS_BW_CHECK_PLAYING
            || command == SRS_BW_CHECK_PUBLISHING
            || command == SRS_BW_CHECK_STARTING_PLAY
            || command == SRS_BW_CHECK_STARTING_PUBLISH
            || command == SRS_BW_CHECK_START_PLAY
            || command == SRS_BW_CHECK_START_PUBLISH
            || command == SRS_BW_CHECK_STOPPED_PLAY
            || command == SRS_BW_CHECK_STOP_PLAY
            || command == SRS_BW_CHECK_STOP_PUBLISH
            || command == SRS_BW_CHECK_STOPPED_PUBLISH
            || command == SRS_BW_CHECK_FINAL)
        {
            srs_info("decode the AMF0/AMF3 band width check message.");
            *ppacket = packet = new SrsBandwidthPacket();
            return packet->decode(stream);
        } else if (command == RTMP_AMF0_COMMAND_CLOSE_STREAM) {
            srs_info("decode the AMF0/AMF3 closeStream message.");
            *ppacket = packet = new SrsCloseStreamPacket();
            return packet->decode(stream);
        } else if (header.is_amf0_command() || header.is_amf3_command()) {
            srs_info("decode the AMF0/AMF3 call message.");
            *ppacket = packet = new SrsCallPacket();
            return packet->decode(stream);
        }
        
        // default packet to drop message.
        srs_info("drop the AMF0/AMF3 command message, command_name=%s", command.c_str());
        *ppacket = packet = new SrsPacket();
        return ret;
    } else if (header.is_user_control_message()) {
        srs_verbose("start to decode user control message.");
        *ppacket = packet = new SrsUserControlPacket();
        return packet->decode(stream);
    } else if(header.is_window_ackledgement_size()) {
        srs_verbose("start to decode set ack window size message.");
        *ppacket = packet = new SrsSetWindowAckSizePacket();
        return packet->decode(stream);
    } else if(header.is_set_chunk_size()) {
        srs_verbose("start to decode set chunk size message.");
        *ppacket = packet = new SrsSetChunkSizePacket();
        return packet->decode(stream);
    } else {
        if (!header.is_set_peer_bandwidth() && !header.is_ackledgement()) {
            srs_trace("drop unknown message, type=%d", header.message_type);
        }
    }
    
    return ret;
}

由开始的流程知,服务器发送 onBWDone 后,接下来会接收到客户端的 releaseStream 命令。对于 releaseStream/PublishStream/FCPublish/FCUnpublish 等都是使用 SrsFMLEStartPacket 类来构造的。

2.2.2 构造 SrsFMLEStartPacket 类

/**
 * FMLE start publish: ReleaseStream/PublishStream/FCPublish/FCUnpublish
 */
SrsFMLEStartPacket::SrsFMLEStartPacket()
{
    /* 命令名:releaseStream */
    command_name = RTMP_AMF0_COMMAND_RELEASE_STREAM;
    /* the transaction ID to get the response. */
    transaction_id = 0;
    /**
     * If there exists any command info this is set, else this is set to null type.
     * @remark, never be NULL, an AMF0 null instance.
     */
    command_object = SrsAmf0Any::null();
}

2.2.3 SrsFMLEStartPacket::decode

开始解析 releaseStream 消息的负载。

int SrsFMLEStartPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    /* 读取该消息的命令名称 */
    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty()
        || (command_name != RTMP_AMF0_COMMAND_RELEASE_STREAM 
        && command_name != RTMP_AMF0_COMMAND_FC_PUBLISH
        && command_name != RTMP_AMF0_COMMAND_UNPUBLISH))
    {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode FMLE start command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start command_object failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start stream_name failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("amf0 decode FMLE start packet success");
    
    return ret;
}

recv:releaseStream('live')

2.3 SrsRtmpServer::identify_fmle_publish_client

当解析接收到的消息为 releaseStream 的时候,会调用该函数。

int SrsRtmpServer::identify_fmle_publish_client(SrsFMLEStartPacket* req, 
    SrsRtmpConnType& type, string& stream_name)
{
    int ret = ERROR_SUCCESS;
    
    /* 鉴别 client 的类型为 publish */
    type = SrsRtmpConnFMLEPublish;
    /* 客户端 publish 的流名称 */
    stream_name = req->stream_name;
    
    /* 下面是对 releaseStream 消息的响应 */
    // releaseStream response
    if (true) {
        SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id);
        if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send releaseStream response message failed. ret=%d", ret);
            return ret;
        }
        srs_info("send releaseStream response message success.");
    }
    
    return ret;
}

该函数中是对 releaseStream 的响应。 发送的包如下图:

send: response for releaseStream

2.3.1 构造 SrsFMLEStartResPacket 类

/**
 * response for SrsFMLEStartPacket.
 */
SrsFMLEStartResPacket::SrsFMLEStartResPacket(double _transaction_id)
{
    /* 响应消息的名称:_result */
    command_name = RTMP_AMF0_COMMAND_RESULT;
    transaction_id = _transaction_id;
    /**
     * If there exists any command info this is set, else this is set to null type.
     * @remark, never be NULL, an AMF0 null instance.
     */
    command_object = SrsAmf0Any::null();
    /**
     * the optional args, set to undefined.
     * @remark, never be NULL, an AMF0 undefined instance.
     */
    args = SrsAmf0Any::undefined();
}

2.3.2 SrsFMLEStartResPacket::encode_packet

构建 releaseStream response 消息的负载.

int SrsFMLEStartResPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    if ((ret = srs_amf0_write_undefined(stream)) != ERROR_SUCCESS) {
        srs_error("encode args failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode args success.");
    
    srs_info("encode FMLE start response packet success.");
    
    return ret;
}

3. SrsSource::fetch_or_create

/**
 * create source when fetch from cache failed.
 * @param r the client request.
 * @param h the event handler for source.
 * @param pps the matches source, if success never be NULL.
 */
int SrsSource::fetch_or_create(SrsRequest* r, ISrsSourceHandler* h, SrsSource** pps)
{
    int ret = ERROR_SUCCESS;
    
    SrsSource* source = NULL;
    /* 根据 vhost/app/stream 组成的 stream_url 在 pool 中查找是否存在
     * 该与之对应的 SrsSource,若不存在,则返回 NULL */
    if ((source = fetch(r)) != NULL) {
        *pps = source;
        return ret;
    }
    
    /* vhost/app/stream 生成一个 stream_url */
    string stream_url = r->get_stream_url();
    string vhost = r->vhost;
    
    /* 必须确定在 pool 中没有 stream_url 对应的项 */
    // should always not exists for create a source.
    srs_assert(pool.find(stream_url) == pool.end());
    
    /* 构建一个新的 SrsSource */
    source = new SrsSource();
    if ((ret = source->initialize(r, h)) != ERROR_SUCCESS) {
        srs_freep(source);
        return ret;
    }
    
    /* 将该新生成的 source 放入到 pool 中 */
    pool[stream_url] = source;
    srs_info("create new source for url=%s, vhost=%s", stream_url.c_str(), vhost.c_str());
    
    /* 通过 pps 返回该新生成的 source */
    *pps = source;
    
    return ret;
}

3.1 SrsSource::fetch

/** 
 * get the exists source, NULL when not exists.
 * update the request and return the exists source.
 */
SrsSource* SrsSource::fetch(SrsRequest* r)
{
    SrsSource* source = NULL;
    
    /* get the stream identify, vhost/app/stream. */
    string stream_url = r->get_stream_url();
    if (pool.find(stream_url) == pool.end()) {
        return NULL;
    }
    
    source = pool[stream_url];
    
    /* we always update the request of resource,
     * for origin auth is on, the token in request maybe invalid,
     * and we only need to update the token of request, it's simple. */
    source->_req->update_auth(r);
    
    return source;
}

3.2 构造 SrsSource 类

构造一个直播流源。

/**
 * the time jitter algorithm:
 * 1. full, to ensure stream start at zero, and ensure stream monotonically increasing.
 * 2. zero, only ensure stream start at zero, ignore timestamp jitter.
 * 3. off, disable the time jitter algorithm, like atc.
 */
enum SrsRtmpJitterAlgorithm
{
    SrsRtmpJitterAlgorithmFULL = 0x01,
    SrsRtmpJitterAlgorithmZERO,
    SrsRtmpJitterAlgorithmOFF
};

SrsSource::SrsSource()
{
    /* _req: deep copy of client request. */
    _req = NULL;
    /* the time jitter algorithm for vhost: vhost 的时间抖动算法 */
    jitter_algorithm = SrsRtmpJitterAlgorithmOFF;
    /* whether use interlaced/mixed algorithm to correct timestamp. 
     * 这里初始化禁止 */
    mix_correct = false;
    mix_queue = new SrsMixQueue();
    
#ifdef SRS_AUTO_HLS
    /* 构造一个 hls handler */
    hls = new SrsHls();
#endif
#ifdef SRS_AUTO_DVR
    /* 构造一个 dvr handler */
    dvr = new SrsDvr();
#endif
#ifdef SRS_AUTO_TRANSCODE
    /* 构造一个 transcoding handler */
    encoder = new SrsEncoder();
#endif
#ifdef SRS_AUTO_HDS
    hds = new SrsHds(this);
#endif
    
    /**
     * cache_sh_video: the cached video sequence header.
     * cache_sh_audio: the cached audio sequence header.
     */
    cache_metadata = cache_sh_video = cache_sh_audio = NULL;
    
    /* can publish, true when is not streaming */
    _can_publish = true;
    /**
     * source id,
     * for publish, it's the publish client id.
     * for edge, it's the edge ingest id.
     * when source id changed, for example, the edge reconnect,
     * invoke the on_source_id_changed() to let all clients know.
     *
     * _pre_source_id: previous source id.
     */
    _pre_source_id = _source_id = -1;
    /**
     * last die time, when all consumeers quit and no publisher,
     * we will remove the source when source die.
     */
    die_at = -1;
    
    /* edge control service */
    play_edge = new SrsPlayEdge();
    publish_edge = new SrsPublishEdge();
    /* gop cache for client fast startup. */
    gop_cache = new SrsGopCache();
    /* for aggregate message */
    aggregate_stream = new SrsStream();
    
    /* whether stream is monotonically increase. */
    is_monotonicaly_increase = false;
    last_packet_time = 0;
    
    _srs_config->subscribe(this);
    /**
     * atc whether atc(use absolute time and donot adjust time),
     * directly use msg time and donot adjust if atc is true,
     * otherwise, adjust msg time to start from 0 to make flash happy.
     *
     * TODO: FIXME: to support reload atc.
     */
    atc = false;
}

3.3 SrsSource::initialize

int SrsSource::initialize(SrsRequest* r, ISrsSourceHandler* h)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(h);
    srs_assert(!_req);
    
    handler = h;
    /* 深度拷贝,将 r 中的内容完全拷贝到 _req 中 */
    _req = r->copy();
    /* 若 vhost 中没有设置 atc 配置项,则返回 false,
     * atc 为 false,则会调整 msg 时间从 0 开始 */
    atc = _srs_config->get_atc(_req->vhost);
    
    /* 暂不分析 */
#ifdef SRS_AUTO_HLS
    if ((ret = hls->initialize(this)) != ERROR_SUCCESS) {
        return ret;
    }
#endif

#ifdef SRS_AUTO_DVR
    if ((ret = dvr->initialize(this, _req)) != ERROR_SUCCESS) {
        return ret;
    }
#endif

    if ((ret = play_edge->initialize(this, _req)) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = publish_edge->initialize(this, _req)) != ERROR_SUCCESS) {
        return ret;
    }
    
    /* 若 vhost 没有设置 queue_length,则使用默认的,为 30 */
    double queue_size = _srs_config->get_queue_length(_req->vhost);
    publish_edge->set_queue_size(queue_size);
    
    jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_time_jitter(_req->vhost);
    mix_correct = _srs_config->get_mix_correct(_req->vhost);
    
    return ret;
    
}

3.4 SrsStatistic::on_client

/**
 * when got a client to publish/play stream,
 * @param id, the client srs id.
 * @param req, the client request object.
 * @param conn, the physical abstract connection object.
 * @param type, the type of connection.
 */
int SrsStatistic::on_client(int id, SrsRequest* req, SrsConnection* conn, 
    SrsRtmpConnType type)
{
    int ret = ERROR_SUCCESS;
    
    SrsStatisticVhost* vhost = create_vhost(req);
    SrsStatisticStream* stream = create_stream(vhost, req);
    
    // create client if not exists
    SrsStatisticClient* client = NULL;
    if (clients.find(id) == clients.end()) {
        client = new SrsStatisticClient();
        client->id = id;
        client->stream = stream;
        clients[id] = client;
    } else {
        client = clients[id];
    }
    
    // got client
    client->conn = conn;
    client->req = req;
    client->type = type;
    stream->nb_clients++;
    vhost->nb_clients++;
    
    return ret;
}

3.4.1 SrsStatistic::create_vhost

SrsStatisticVhost* SrsStatistic::create_vhost(SrsRequest* req)
{
    SrsStatisticVhost* vhost = NULL;
    
    /**
     * rvhost: 
     * key: vhost url, value: vhost Object.
     * @remark a fast index for vhost.
     */
    // create vhost if not exists.
    if (rvhosts.find(req->vhost) == rvhost.end()) {
        vhost = new SrsStatisticVhost();
        vhost->vhost = req->vhost;
        rvhosts[req->vhost] = vhost;
        /* vhosts - key: vhost id, value: vhost object. */
        vhosts[vhost->id] = vhost;
        return vhost;
    }
    
    vhost = rvhosts[req->vhost];
    
    return vhost;
}

3.4.2 SrsStatistic::create_stream

SrsStatisticStream* SrsStatistic::create_stream(SrsStatisticVhost* vhost, SrsRequest* req)
{
    std::string url = req->get_stream_url();
    
    SrsStatisticStream* stream = NULL;
    
    // create stream if not exists.
    if (rstreams.find(url) == rstreams.end()) {
        stream = new SrsStatisticStream();
        stream->vhost = vhost;
        stream->stream = req->stream;
        stream->app = req->app;
        stream->url = url;
        rstreams[url] = stream;
        streams[stream->id] = stream;
        return stream;
    }
    
    stream = rstreams[url];
    
    return stream;
}

4. SrsSource::set_cache

void SrsSource::set_cache(bool enabled)
{
    /* SrsGopCache* gop_cache: gop cache for client fast startup. */
    gop_cache->set(enabled);
}

4.1 SrsGopCache::set

/**
 * to enabled or disable the gop cache.
 */
void SrsGopCache::set(bool enabled)
{
    /* if disabled the gop cache, the client will wait for the next 
     * keyframe for h264, and will be black-screan. */
    enabled_gop_cache = enabled;
    
    if (!enabled) {
        srs_info("disable gop cache, clear %d packets.", (int)gop_cache.size());
        clear();
        return;
    }
    
    srs_info("enable gop cache");
}

5. SrsRtmpServer::start_fmle_publish

/**
 * when client type is publish, response with packets:
 * releaseStream response
 * FCPublish
 * FCPublish response
 * createStream response
 * onFCPublish(NetStream.Publish.Start)
 * onStatus(NetStream.Publish.Start)
 */
int SrsRtmpServer::start_fmle_publish(int stream_id)
{
    int ret = ERROR_SUCCESS;
    
    // FCPublish
    double fc_publish_tid = 0;
    if (true) {
        SrsCommonMessage* msg = NULL;
        SrsFMLEStartPacket* pkt = NULL;
        /* 指定接收这几个中的一个消息:ReleaseStream/FCPublish/FCUnpublish,若不是其中之一,
         * 则丢弃,直到接收到其中一个才返回 
         * 由开始的流程知,这里应该是接收 FCPublish */
        if ((ret = expect_message<SrsFMLEStartPacket>(&msg, &pkt)) != ERROR_SUCCESS) {
            srs_error("recv FCPublish message failed. ret=%d", ret);
            return ret;
        }
        srs_info("recv FCPublish request message success.");
        
        SrsAutoFree(SrsCommonMessage, msg);
        SrsAutoFree(SrsFMLEStartPacket, pkt);
        
        fc_publish_tid = pkt->transaction_id;
    }
    // FCPublish response
    if (true) {
        SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid);
        if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send FCPublish response message failed. ret=%d", ret);
            return ret;
        }
        srs_info("send FCPublish response message success.");
    }
    
    // createStream
    double create_stream_tid = 0;
    if (true) {
        SrsCommonMessage* msg = NULL;
        SrsCreateStreamPacket* pkt = NULL;
        if ((ret = expect_message<SrsCreateStreamPacket>(&msg, &pkt)) != ERROR_SUCCESS) {
            srs_error("recv createStream message failed. ret=%d", ret);
            return ret;
        }
        srs_info("recv createStream request message success.");
        
        SrsAutoFree(SrsCommonMessage, msg);
        SrsAutoFree(SrsCreateStreamPacket, pkt);
        
        create_stream_tid = pkt->transaction_id;
    }
    // createStream response
    if (true) {
        SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, 
                                                                     stream_id);
        if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send createStream response message failed. ret=%d", ret);
            return ret;
        }
        srs_info("send createStream response message success.");
    }
    
    // publish
    if (true) {
        SrsCommonMessage* msg = NULL;
        SrsPublishPacket* pkt = NULL;
        if ((ret = expect_message<SrsPublishPacket>(&msg, &pkt)) != ERROR_SUCCESS) {
            srs_error("recv publish message failed. ret=%d", ret);
            return ret;
        }
        srs_info("recv publish request message success.");
        
        SrsAutoFree(SrsCommonMessage, msg);
        SrsAutoFree(SrsPublishPacket, pkt);
    }
    // publish response onFCPublish(NetStream.Publish.Start)
    if (true) {
        SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
        
        pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH;
        pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart));
        pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream."));
        
        if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) {
            srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", 
                      ret);
            return ret;
        }
        srs_info("send onFCPublish(NetStream.Publish.Start) message success.");
    }
    // publish response onStatus(NetStream.Publish.Start)
    if (true) {
        SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
        
        pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus));
        pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart));
        pkt->data->set(StatusDescritption, SrsAmf0Any::str("Started publishing stream."));
        pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID));
        
        if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) {
            srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", 
                      ret);
            return ret;
        }
        srs_info("send onStatus(NetStream.Publish.Start) message success.");
    }
    
    srs_info("FMLE publish success.");
    
    return ret;
}

5.1 FCPublish

5.1.1 FCPublish 接收

接收 FCPublish 后的解析如下代码所示。

SrsProtocol::do_decode_message:

int SrsProtocol::do_decode_message(SrsMessageHeader& header, SrsStream* stream, 
    SrsPacket** ppacket)
{
    ...
    
    /* FCPublish */
    else if(command == RTMP_AMF0_COMMAND_FC_PUBLISH) {
        srs_info("decode the AMF0/AMF3 command(FMLE FCPublish message).");
        *ppacket = packet = new SrsFMLEStartPacket();
        return packet->decode(stream);
    } 
    
    ...
}

SrsFMLEStartPacket::decode

int SrsFMLEStartPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    /* 读取消息的命令名,即 "FCPublish" */
    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() 
        || (command_name != RTMP_AMF0_COMMAND_RELEASE_STREAM 
        && command_name != RTMP_AMF0_COMMAND_FC_PUBLISH
        && command_name != RTMP_AMF0_COMMAND_UNPUBLISH)
    ) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode FMLE start command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start command_object failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start stream_name failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("amf0 decode FMLE start packet success");
    
    return ret;
}

5.1.2 FCPublish response

FCPublish 的响应用 SrsFMLEStartResPacket 类构造数据。该类的构造如下:

SrsFMLEStartResPacket 构造函数

/**
 * response for SrsFMLEStartPacket.
 */
SrsFMLEStartResPacket::SrsFMLEStartResPacket(double _transaction_id)
{
    /* _result */
    command_name = RTMP_AMF0_COMMAND_RESULT;
    transaction_id = _transaction_id;
    command_object = SrsAmf0Any::null();
    args = SrsAmf0Any::undefined();
}

SrsFMLEStartResPacket::encode_packet

/**
 * subpacket can override to encode the payload to stream.
 * @remark never invoke the super.encode_packet, it always failed.
 */
int SrsFMLEStartResPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    if ((ret = srs_amf0_write_undefined(stream)) != ERROR_SUCCESS) {
        srs_error("encode args failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode args success.");
    
    
    srs_info("encode FMLE start response packet success.");
    
    return ret;
}

send: FCPublish response

5.2 createStream

5.2.1 createStream 接收

createStream 消息的代表类为 SrsCreateStreamPacket,该类的构造如下。

SrsCreateStreamPacket 构造函数

/**
 * createStream
 * The client sends this command to the server to create a logical
 * channel for message communication The publishing of audio, video, and
 * metadata is carried out over stream channel created using the 
 * createStream command.
 */
SrsCreateStreamPacket::SrsCreateStreamPacket()
{
    /* createStream */
    command_name = RTMP_AMF0_COMMAND_CREATE_STREAM;
    /**
     * Transaction ID of the command.
     */
    transaction_id = 2;
    /**
     * If there exists any command info this is set, else this is set to null type.
     * @remark, never be NULL, an AMF0 null instance.
     */
    command_object = SrsAmf0Any::null();
}

接收 createStream 后对该消息的解码如下:

SrsCreateStreamPacket::decode

int SrsCreateStreamPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode createStream command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_CREATE_STREAM) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode createStream command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode createStream transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode createStream command_object failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("amf0 decode createStream packet success");
    
    return ret;
}

5.2.2 createStream response

createStream 的响应消息是通过 SrsCreateStreamResPacket 类构造的,该类的构造如下:

SrsCreateStreamResPacket 构造函数

SrsCreateStreamResPacket::SrsCreateStreamResPacket(double _transaction_id, 
        double _stream_id)
{
    /* _result */
    command_name = RTMP_AMF0_COMMAND_RESULT;
    /**
     * ID of the command that response belongs to.
     */
    transaction_id = _transaction_id;
    /**
     * If there exists any command info this is set, else this is set to null type.
     * @remark, never be NULL, an AMF0 null instance.
     */
    command_object = SrsAmf0Any::null();
    /* The return value is either a stream ID or an error information object. */
    stream_id = _stream_id;
}

接着对该 createStream response 消息的负载数据进行编码(即打包)。

SrsCreateStreamResPacket::encode_packet

int SrsCreateStreamResPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    if ((ret = srs_amf0_write_number(stream, stream_id)) != ERROR_SUCCESS) {
        srs_error("encode stream_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode stream_id success.");
    
    
    srs_info("encode createStream response packet success.");
    
    return ret;
}

send: createStream response

5.3 publish

5.3.1 publish 接收

publish 消息用 SrsPublishPacket 类代表。该类的构造如下:

SrsPublishPacket 构造函数

/**
 * FMLE/flash publish
 * Publish
 * The client sends the publish command to publish a named stream to the
 * server. Using this name, any client can play this stream and receive
 * the published audio, video, and data messages.
 */
SrsPublishPacket::SrsPublishPacket()
{
    /* Name of the command, set to "publish". */
    command_name = RTMP_AMF0_COMMAND_PUBLISH;
    /* Transaction ID set to 0. */
    transaction_id = 0;
    /**
     * Command information object does not exist. Set to null type.
     * @remark, never be NULL, an AMF0 null instance.
     */
    command_object = SrsAmf0Any::null();
    /**
     * Type of publishing. Set to "live", "record", or "append".
     *   record: The stream is published and the data is recorded to a new file. The file
     *           is stored on the server in a subdirectory within the directory that
     *           contains the server application. If the file already exists, it is
     *           overwritten.
     *   append: The stream is published and the data is appended to a file. If no file
     *           is found, it is created.
     *   live: Live data is published without recording it in a file.
     * @remark, SRS only support live.
     * @remark, optional, default to live.
     */
    type = "live";
}

recv: publish

该 publish 消息的解析如下代码。

SrsPublishPacket::decode

int SrsPublishPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PUBLISH) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode publish command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish command_object failed. ret=%d", ret);
        return ret;
    }
    
    /* 读取推流的流名称 */
    if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish stream_name failed. ret=%d", ret);
        return ret;
    }
    
    /* 读取推流的类型,SRS 仅支持 live */
    if (!stream->empty() && (ret = srs_amf0_read_string(stream, type)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish type failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("amf0 decode publish packet success");
    
    return ret;
}

5.3.2 publish response onFCPublish(NetStream.Publish.Start)

该 publish 的响应 onFCPublish 使用 SrsOnStatusCallPacket 构造,该类的构造函数如下。

SrsOnStatusCallPacket 构造函数

SrsOnStatusCallPacket::SrsOnStatusCallPacket()
{
    /* Name of command. Set to "onStatus" */
    command_name = RTMP_AMF0_COMMAND_ON_STATUS;
    /* Transaction ID set to 0. */
    transaction_id = 0;
    /**
     * Command information does not exist. Set to null type.
     * @remark, never be NULL, an AMF0 null instance.
     */
    args = SrsAmf0Any::null();
    /**
     * Name-value pairs that describe the response from the server. 
     * 'code','level', 'description' are names of few among such information.
     * @remark, never be NULL, an AMF0 object instance.
     */
    data = SrsAmf0Any::object();
}

注:publish 的响应消息 onFCPublish 的消息名为 onFCPublish。该消息的抓包如下:

send: onFCPublish

该 onFCPublish 消息负载数据的编码如下。

SrsOnStatusCallPacket::encode_packet

int SrsOnStatusCallPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode args failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode args success.");;
    
    if ((ret = data->write(stream)) != ERROR_SUCCESS) {
        srs_error("encode data failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode data success.");
    
    srs_info("encode onStatus(Call) packet success.");
    
    return ret;
}

5.3.3 publish response onStatus(NetStream.Publish.Start)

该响应消息同样使用 SrsOnStatusCallPacket 类构造,该消息的名称即为 onStatus。抓包如下图

send: onStatus

6. SrsRtmpConn::publishing

当服务器成功响应 obs 发送的 publish 消息后,即进入 SrsRtmpConn::publishing 函数,开始处理 obs 推送的媒体数据。具体分析见 SRS之SrsRtmpConn::publishing详解.

posted @ 2018-05-24 22:54  季末的天堂  阅读(2616)  评论(0编辑  收藏  举报