srs代码学习(7)--消息接收过程
来源:互联网 发布:剑灵龙男捏脸数据大全 编辑:程序博客网 时间:2024/05/21 09:49
通过前几篇的分析。我们发现,如果一份数据推上来的话,有点时候需要转发多路出去,比如forward,多路forward,或者多个edge的出现,这时候的消息如果每一路都复制一份的话,内存开销会非常的打。那么srs怎么处理呢?
首先看看消息的结构,为了能正确的解析消息,srs吧接受消息的过程分成了大概五个类来执行,从下往上依次是
SrsStSocket
SrsFastBuffer
SrsChunkStream
SrsCommonMessage
SrsSharedPtrMessage
这五个类的数据拷贝已有依次,就是从fastbufet里会拷贝到chunkstream里面,然后到这层数据指针一直应用到SrsSharedPtrMessage类里,在SrsSharedPtrMessage中会增加conut变量,来做数据引用,而且SrsSharedPtrMessage类隐藏了构造和拷贝函数,只能通过copy()函数来新建一个引用。这样多路发送只需要一份数据就可以了。下面我们看具体的流程。
流程图上可以看出,最底层的是st_thread的库函数st_read,读取的数据经过srssocket的简单转手,就会提交个bufff库,看看buf的grow()函数的实现
int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size){ int ret = ERROR_SUCCESS; // already got required size of bytes. if (end - p >= required_size) { return ret; } // must be positive. srs_assert(required_size > 0); // the free space of buffer, // buffer = consumed_bytes + exists_bytes + free_space. int nb_free_space = (int)(buffer + nb_buffer - end); // resize the space when no left space. if (nb_free_space < required_size) { // the bytes already in buffer int nb_exists_bytes = (int)(end - p); srs_assert(nb_exists_bytes >= 0); srs_verbose("move fast buffer %d bytes", nb_exists_bytes); // reset or move to get more space. if (!nb_exists_bytes) { // reset when buffer is empty. p = end = buffer; srs_verbose("all consumed, reset fast buffer"); } else { // move the left bytes to start of buffer. srs_assert(nb_exists_bytes < nb_buffer); buffer = (char*)memmove(buffer, p, nb_exists_bytes); p = buffer; end = p + nb_exists_bytes; } // check whether enough free space in buffer. nb_free_space = (int)(buffer + nb_buffer - end); if (nb_free_space < required_size) { ret = ERROR_READER_BUFFER_OVERFLOW; srs_error("buffer overflow, required=%d, max=%d, left=%d, ret=%d", required_size, nb_buffer, nb_free_space, ret); return ret; } } // buffer is ok, read required size of bytes. while (end - p < required_size) { ssize_t nread; if ((ret = reader->read(end, nb_free_space, &nread)) != ERROR_SUCCESS) { return ret; } #ifdef SRS_PERF_MERGED_READ /** * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. * @see https://github.com/ossrs/srs/issues/241 */ if (merged_read && _handler) { _handler->on_read(nread); }#endif // we just move the ptr to next. srs_assert((int)nread > 0); end += nread; nb_free_space -= nread; } return ret;}
buff类里会有一个缓冲区buffer,长度为128k
#define SRS_DEFAULT_RECV_BUFFER_SIZE 131072有两个移动指针p和end.来标志里面的数据。那么如果这个空间不够怎么办,有两个办法,一个是数据大小nb_exists_bytes,如果为0,那么表示数据都用完了,reset指针。如果还有数据,那么只好重新分配内存大小 buffer = (char*)memmove(buffer, p, nb_exists_bytes);
在获取到数据后,提供两个接口,让外部类来读取数据
char SrsFastBuffer::read_1byte(){ srs_assert(end - p >= 1); return *p++;}char* SrsFastBuffer::read_slice(int size){ srs_assert(size >= 0); srs_assert(end - p >= size); srs_assert(p + size >= buffer); char* ptr = p; p += size; return ptr;}问:*p++,是怎么个执行过程?
下面就是比较重要的一个函数 recv_interlaced_message,在这个函数中,通过读取basicheader header payload 三个步骤,把数据读包括meta信息,读到了chunksteram里。
int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg){ int ret = ERROR_SUCCESS; // chunk stream basic header. char fmt = 0; int cid = 0; if ((ret = read_basic_header(fmt, cid)) != ERROR_SUCCESS) { if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) { srs_error("read basic header failed. ret=%d", ret); } return ret; } srs_verbose("read basic header success. fmt=%d, cid=%d", fmt, cid); // the cid must not negative. srs_assert(cid >= 0); // get the cached chunk stream. SrsChunkStream* chunk = NULL; // use chunk stream cache to get the chunk info. // @see https://github.com/ossrs/srs/issues/249 if (cid < SRS_PERF_CHUNK_STREAM_CACHE) { // chunk stream cache hit. srs_verbose("cs-cache hit, cid=%d", cid); // already init, use it direclty 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); } } // 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;}
1)为啥函数名字叫recv_interlaced_message,意思是rtmp的消息可能不同消息交织过来?这说明其消息发送是有优先级别的。
2)为啥这这个类里,缓冲了很多的chunksteam?
在获取了完整的消息以后,会调用一次SrsProtocol::on_recv_message(SrsCommonMessage* msg)函数,注意这个时候,参数已经变成了commonmmgs。
int SrsProtocol::on_recv_message(SrsCommonMessage* msg){ int ret = ERROR_SUCCESS; srs_assert(msg != NULL); // acknowledgement if (in_ack_size.ack_window_size > 0 && skt->get_recv_bytes() - in_ack_size.acked_size > in_ack_size.ack_window_size ) { if ((ret = response_acknowledgement_message()) != ERROR_SUCCESS) { return ret; } } SrsPacket* packet = NULL; switch (msg->header.message_type) { case RTMP_MSG_SetChunkSize: case RTMP_MSG_UserControlMessage: case RTMP_MSG_WindowAcknowledgementSize: if ((ret = decode_message(msg, &packet)) != ERROR_SUCCESS) { srs_error("decode packet from message payload failed. ret=%d", ret); return ret; } srs_verbose("decode packet from message payload success."); break; default: return ret; } srs_assert(packet); // always free the packet. SrsAutoFree(SrsPacket, packet); switch (msg->header.message_type) { case RTMP_MSG_WindowAcknowledgementSize: { SrsSetWindowAckSizePacket* pkt = dynamic_cast<SrsSetWindowAckSizePacket*>(packet); srs_assert(pkt != NULL); if (pkt->ackowledgement_window_size > 0) { in_ack_size.ack_window_size = pkt->ackowledgement_window_size; // @remark, we ignore this message, for user noneed to care. // but it's important for dev, for client/server will block if required // ack msg not arrived. srs_info("set ack window size to %d", pkt->ackowledgement_window_size); } else { srs_warn("ignored. set ack window size is %d", pkt->ackowledgement_window_size); } break; } case RTMP_MSG_SetChunkSize: { SrsSetChunkSizePacket* pkt = dynamic_cast<SrsSetChunkSizePacket*>(packet); srs_assert(pkt != NULL); // for some server, the actual chunk size can greater than the max value(65536), // so we just warning the invalid chunk size, and actually use it is ok, // @see: https://github.com/ossrs/srs/issues/160 if (pkt->chunk_size < SRS_CONSTS_RTMP_MIN_CHUNK_SIZE || pkt->chunk_size > SRS_CONSTS_RTMP_MAX_CHUNK_SIZE) { srs_warn("accept chunk size %d, but should in [%d, %d], " "@see: https://github.com/ossrs/srs/issues/160", pkt->chunk_size, SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, SRS_CONSTS_RTMP_MAX_CHUNK_SIZE); } // @see: https://github.com/ossrs/srs/issues/541 if (pkt->chunk_size < SRS_CONSTS_RTMP_MIN_CHUNK_SIZE) { ret = ERROR_RTMP_CHUNK_SIZE; srs_error("chunk size should be %d+, value=%d. ret=%d", SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, pkt->chunk_size, ret); return ret; } in_chunk_size = pkt->chunk_size; srs_trace("input chunk size to %d", pkt->chunk_size); break; } case RTMP_MSG_UserControlMessage: { SrsUserControlPacket* pkt = dynamic_cast<SrsUserControlPacket*>(packet); srs_assert(pkt != NULL); if (pkt->event_type == SrcPCUCSetBufferLength) { srs_trace("ignored. set buffer length to %d", pkt->extra_data); } if (pkt->event_type == SrcPCUCPingRequest) { if ((ret = response_ping_message(pkt->event_data)) != ERROR_SUCCESS) { return ret; } } break; } default: break; } return ret;}
这个函数没有其他意义,就是看看有没有需要回复的包,如果有,就直接回复回去。
数据变成cocmmonmsg以后,通过各种渠道,会最终跑到source类中,举个例子就可以
int SrsSource::on_audio(SrsCommonMessage* shared_audio){ int ret = ERROR_SUCCESS; // monotically increase detect. if (!mix_correct && is_monotonically_increase) { if (last_packet_time > 0 && shared_audio->header.timestamp < last_packet_time) { is_monotonically_increase = false; srs_warn("AUDIO: stream not monotonically increase, please open mix_correct."); } } last_packet_time = shared_audio->header.timestamp; // convert shared_audio to msg, user should not use shared_audio again. // the payload is transfer to msg, and set to NULL in shared_audio. SrsSharedPtrMessage msg; if ((ret = msg.create(shared_audio)) != ERROR_SUCCESS) { srs_error("initialize the audio failed. ret=%d", ret); return ret; } srs_info("Audio dts=%"PRId64", size=%d", msg.timestamp, msg.size); // directly process the audio message. if (!mix_correct) { return on_audio_imp(&msg); } // insert msg to the queue. mix_queue->push(msg.copy()); // fetch someone from mix queue. SrsSharedPtrMessage* m = mix_queue->pop(); if (!m) { return ret; } // consume the monotonically increase message. if (m->is_audio()) { ret = on_audio_imp(m); } else { ret = on_video_imp(m); } srs_freep(m); return ret;}
音频数据来了以后,会转变消息类型为shareptr类型,之后会检测mix_correct是否开启,mix_correct主要是用于时间戳不连续的。一般的话,不要开启。无论如何,最后都会调用进on_audio_imp
int SrsSource::on_audio_imp(SrsSharedPtrMessage* msg){ int ret = ERROR_SUCCESS; srs_info("Audio dts=%"PRId64", size=%d", msg->timestamp, msg->size); bool is_aac_sequence_header = SrsFlvCodec::audio_is_sequence_header(msg->payload, msg->size); bool is_sequence_header = is_aac_sequence_header; // whether consumer should drop for the duplicated sequence header. bool drop_for_reduce = false; if (is_sequence_header && cache_sh_audio && _srs_config->get_reduce_sequence_header(_req->vhost)) { if (cache_sh_audio->size == msg->size) { drop_for_reduce = srs_bytes_equals(cache_sh_audio->payload, msg->payload, msg->size); srs_warn("drop for reduce sh audio, size=%d", msg->size); } } // cache the sequence header if aac // donot cache the sequence header to gop_cache, return here. if (is_aac_sequence_header) { // parse detail audio codec SrsAvcAacCodec codec; SrsCodecSample sample; if ((ret = codec.audio_aac_demux(msg->payload, msg->size, &sample)) != ERROR_SUCCESS) { srs_error("source codec demux audio failed. ret=%d", ret); return ret; } static int flv_sample_sizes[] = {8, 16, 0}; static int flv_sound_types[] = {1, 2, 0}; // when got audio stream info. SrsStatistic* stat = SrsStatistic::instance(); if ((ret = stat->on_audio_info(_req, SrsCodecAudioAAC, sample.sound_rate, sample.sound_type, codec.aac_object)) != ERROR_SUCCESS) { return ret; } srs_trace("%dB audio sh, codec(%d, profile=%s, %dchannels, %dkbps, %dHZ), " "flv(%dbits, %dchannels, %dHZ)", msg->size, codec.audio_codec_id, srs_codec_aac_object2str(codec.aac_object).c_str(), codec.aac_channels, codec.audio_data_rate / 1000, aac_sample_rates[codec.aac_sample_rate], flv_sample_sizes[sample.sound_size], flv_sound_types[sample.sound_type], flv_sample_rates[sample.sound_rate]); } #ifdef SRS_AUTO_HLS if ((ret = hls->on_audio(msg)) != ERROR_SUCCESS) { // apply the error strategy for hls. // @see https://github.com/ossrs/srs/issues/264 std::string hls_error_strategy = _srs_config->get_hls_on_error(_req->vhost); if (srs_config_hls_is_on_error_ignore(hls_error_strategy)) { srs_warn("hls process audio message failed, ignore and disable hls. ret=%d", ret); // unpublish, ignore ret. hls->on_unpublish(); // ignore. ret = ERROR_SUCCESS; } else if (srs_config_hls_is_on_error_continue(hls_error_strategy)) { if (srs_hls_can_continue(ret, cache_sh_audio, msg)) { ret = ERROR_SUCCESS; } else { srs_warn("hls continue audio failed. ret=%d", ret); return ret; } } else { srs_warn("hls disconnect publisher for audio error. ret=%d", ret); return ret; } }#endif #ifdef SRS_AUTO_DVR if ((ret = dvr->on_audio(msg)) != ERROR_SUCCESS) { srs_warn("dvr process audio message failed, ignore and disable dvr. ret=%d", ret); // unpublish, ignore ret. dvr->on_unpublish(); // ignore. ret = ERROR_SUCCESS; }#endif#ifdef SRS_AUTO_HDS if ((ret = hds->on_audio(msg)) != ERROR_SUCCESS) { srs_warn("hds process audio message failed, ignore and disable dvr. ret=%d", ret); // unpublish, ignore ret. hds->on_unpublish(); // ignore. ret = ERROR_SUCCESS; }#endif // copy to all consumer if (!drop_for_reduce) { for (int i = 0; i < (int)consumers.size(); i++) { SrsConsumer* consumer = consumers.at(i); if ((ret = consumer->enqueue(msg, atc, jitter_algorithm)) != ERROR_SUCCESS) { srs_error("dispatch the audio failed. ret=%d", ret); return ret; } } srs_info("dispatch audio success."); } // copy to all forwarders. if (true) { std::vector<SrsForwarder*>::iterator it; for (it = forwarders.begin(); it != forwarders.end(); ++it) { SrsForwarder* forwarder = *it; if ((ret = forwarder->on_audio(msg)) != ERROR_SUCCESS) { srs_error("forwarder process audio message failed. ret=%d", ret); return ret; } } } // cache the sequence header of aac, or first packet of mp3. // for example, the mp3 is used for hls to write the "right" audio codec. // TODO: FIXME: to refine the stream info system. if (is_aac_sequence_header || !cache_sh_audio) { srs_freep(cache_sh_audio); cache_sh_audio = msg->copy(); } // when sequence header, donot push to gop cache and adjust the timestamp. if (is_sequence_header) { return ret; } // cache the last gop packets if ((ret = gop_cache->cache(msg)) != ERROR_SUCCESS) { srs_error("shrink gop cache failed. ret=%d", ret); return ret; } srs_verbose("cache gop success."); // if atc, update the sequence header to abs time. if (atc) { if (cache_sh_audio) { cache_sh_audio->timestamp = msg->timestamp; } if (cache_metadata) { cache_metadata->timestamp = msg->timestamp; } } return ret;}
代码也很多。主要做的事情就是分发数据和缓冲数据。其他没有。
从这块分析可以看出,服务器基本是收到什么转发什么,中间除了头部外,对数据没有做解析过程。有时候需要做实时转码。如果压力不大的话,可以在这里做。如果考虑服务器压力,可以forward到单独的转码服务器去做。
- srs代码学习(7)--消息接收过程
- srs代码学习(1)--listen建立过程
- srs代码学习(1)--listen建立过程
- srs代码学习(1)--listen建立过程
- srs代码学习(2)- 线程模型
- srs代码学习(3)-链接管理
- srs代码学习(8)--rtmp发送
- srs代码学习(2)- 线程模型
- srs代码学习(4)-怎么转发流
- srs代码学习(6)--如何实现edge
- srs代码学习(9)----http_api处理流程
- srs代码学习(4)-怎么转发流
- 窗口过程接收的消息
- srs代码学习(5)--一些与运营相关的技术点
- TCP带外数据学习总结(概念,发送接收过程,数据到达检测,代码实现)
- SRS 代码分析
- androoid framework学习之 - RILd启动过程和如何接收framwork层的消息流程
- androoid framework学习之 - RILd启动过程和如何接收framwork层的消息流程(二)
- 阿里云文件迁移工具使用小结
- 周鸿祎:真想不通是张小龙这样的人做出了微信!
- 【bzoj4152】【AMPPZ2014】The Captain
- LeetCode OJ (2)
- 什么是交叉编译,为什么要使用交叉编译?
- srs代码学习(7)--消息接收过程
- java 对象 和 json 之间互转 例子
- POJ 1221 UNIMODAL PALINDROMIC DECOMPOSITIONS
- MySQL出现Errcode:28错误提示解决办法
- Linux存储IO栈(1)-- 内核对象与对象集
- 链表的使用
- C++ 类的使用
- Linux中ib开头的一些命令,先收藏一下.
- C++关键字解析与实例