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到单独的转码服务器去做。







0 0
原创粉丝点击