首先使用 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详解.

SRS之SrsRtmpConn::stream_service_cycle详解的更多相关文章

  1. SRS之SrsRtmpConn::service_cycle详解

    1. SrsRtmpConn::service_cycle 当服务器在 conn 线程的开始调用 connect_app 函数接收并解析客户端发送的 connect 消息后,调用该 service_c ...

  2. SRS之SrsRtmpConn::publishing详解

    1. SrsRtmpConn::publishing int SrsRtmpConn::publishing(SrsSource* source) { int ret = ERROR_SUCCESS; ...

  3. SRS之SrsHlsCache::reap_segment详解

    1. 是否可切片的检测 首先在调用 SrsHlsCache::reap_segment 函数进行切片时,针对音频或视频,都会有一个函数来进行检测当前片的时长是否符合所要求的时长. 对于音频,会调用 S ...

  4. SRS之SrsHls::on_audio详解

    1. SrsHls::on_audio 将音频数据封装到 ts 文件中. /* * mux the audio packet to ts. * @param shared_audio, directl ...

  5. SRS之SrsHls::on_video详解

    1. SrsHls::on_video /* * mux the video packets to ts. * @param shared_video, directly ptr, copy it i ...

  6. SRS之SrsTsContext::encode_pes详解

    1. SrsTsContext::encode_pes 该函数位于 srs_kernel_ts.cpp 中.下面的分析基于假设当前要封装的消息是视频. /* * @msg: 要写入到 ts 文件中的音 ...

  7. SRS之SrsRtmpServer::connect_app详解

    1. connect('live') 2. SrsRtmpServer::connect_app 位于 srs_rtmp_stack.cpp.在 SRS 的 RTMP 连接处理线程 conn 中,当与 ...

  8. linux查看端口及端口详解

    今天现场查看了TCP端口的占用情况,如下图   红色部分是IP,现场那边问我是不是我的程序占用了tcp的链接,,我远程登陆现场查看了一下,这种类型的tcp链接占用了400多个,,后边查了一下资料,说E ...

  9. JMeter学习-023-JMeter 命令行(非GUI)模式详解(一)-执行、输出结果及日志、简单分布执行脚本

    前文 讲述了JMeter分布式运行脚本,以更好的达到预设的性能测试(并发)场景.同时,在前文的第一章节中也提到了 JMeter 命令行(非GUI)模式,那么此文就继续前文,针对 JMeter 的命令行 ...

随机推荐

  1. 转:Git和Github简单教程

    转自:https://www.cnblogs.com/schaepher/p/5561193.html Git和Github简单教程   原文链接:Git和Github简单教程 网络上关于Git和Gi ...

  2. React学习——子组件给父组件传值

    //子组件 var Child = React.createClass({ render: function(){ return ( <div> 请输入邮箱:<input onCha ...

  3. centos 查看ip

    1.现象: 通过ip addr 查找Ip时,发现ens33中没有inet属性,如下图: 2.解决方法 打开网卡配置文件 /etc/sysconfig/network-scripts/ifcfg-ens ...

  4. fastadmin 列表展示时字段值截取

    {field: '字段名', title: __('lang中的语言名'),formatter:function(value,row,index){ value=value?value:''; var ...

  5. Liunx命令问题

    第一个问题是:快速杀死服务 第一步:查看进程号pid        ps -u my_account -o pid,rss,command | grep redis 第二步:杀死进程        k ...

  6. Mac上 intellij IDEA报错:Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk .jdk/Contents/Home/bin/java ( ) and /Library/Java/JavaVirtualMachines/jdk

    解决方案: 点击IDEA菜单里的Help-Edit Custom Properties,没有这个properties文件的话,会提示创建,在里面加上 idea.no.launcher=true 说明: ...

  7. QTP(8)

    一.Action 1.调用Action C:\Program Files\HP\QuickTest Professional\CodeSamplesPlus\Flight_Samples (1)调用A ...

  8. mongodb单机搭建

    参考网站:http://www.runoob.com/mongodb/mongodb-linux-install.html 1.下载 https://www.mongodb.com/download- ...

  9. Linux命令行——scp命令

     原创声明:本文系博主原创文章,转载或引用请注明出处. scp 一般格式: scp [option] src dst 1. src和dst格式为: [user@]host:/path/to/file ...

  10. python+Appium自动化:各种元素定位方法

    name定位 driver.find_element_by_name('飞利浦净水').click() 测试结果报错:selenium.common.exceptions.InvalidSelecto ...