srt protocol srs server data sending and receiving for streaming media analysis

According to the previous chapter, we know that srt creates a SrsMpegtsSrtConn object for each link, and SrsMpegtsSrtConn calls the do_cycle function. The do_sycle function pushes and pulls streams to call different processing functions. Push stream processing publishing and pull stream processing playing

1. Analyze the streaming data processing flow.


srs_error_t SrsMpegtsSrtConn::publishing()
{
    srs_error_t err = srs_success;

    // We must do stat the client before hooks, because hooks depends on it.
    SrsStatistic* stat = SrsStatistic::instance();
    if ((err = stat->on_client(_srs_context->get_id().c_str(), req_, this, SrsSrtConnPublish)) != srs_success) {
        return srs_error_wrap(err, "srt: stat client");
    }

    // We must do hook after stat, because depends on it.
    if ((err = http_hooks_on_publish()) != srs_success) {
        return srs_error_wrap(err, "srt: callback on publish");
    }
    
    if ((err = acquire_publish()) == srs_success) {
        err = do_publishing();
        release_publish();
    }
    
    http_hooks_on_unpublish();
    
    return err;
}

The do_publishing function calls the srt_conn_->read function to receive push data.

  The on_srt_packet function processes the data.

srs_error_t SrsMpegtsSrtConn::do_publishing()
{
    srs_error_t err = srs_success;

    SrsPithyPrint* pprint = SrsPithyPrint::create_srt_publish();
    SrsAutoFree(SrsPithyPrint, pprint);

    int nb_packets = 0;

    // Max udp packet size equal to 1500.
    char buf[1500];
    while (true) {
        if ((err = trd_->pull()) != srs_success) {
            return srs_error_wrap(err, "srt: thread quit");
        }

        pprint->elapse();
        if (pprint->can_print()) {
            SrsSrtStat s;
            if ((err = s.fetch(srt_fd_, true)) != srs_success) {
                srs_freep(err);
            } else {
                srs_trace("<- " SRS_CONSTS_LOG_SRT_PUBLISH " Transport Stats # pktRecv=%" PRId64 ", pktRcvLoss=%d, pktRcvRetrans=%d, pktRcvDrop=%d",
                    s.pktRecv(), s.pktRcvLoss(), s.pktRcvRetrans(), s.pktRcvDrop());
            }

            kbps_->sample();
            srs_trace("<- " SRS_CONSTS_LOG_SRT_PUBLISH " time=%" PRId64 ", packets=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d",
                srsu2ms(pprint->age()), nb_packets, kbps_->get_send_kbps(), kbps_->get_send_kbps_30s(), kbps_->get_send_kbps_5m(),
                kbps_->get_recv_kbps(), kbps_->get_recv_kbps_30s(), kbps_->get_recv_kbps_5m());
            nb_packets = 0;
        }

        ssize_t nb = 0;
        if ((err = srt_conn_->read(buf, sizeof(buf), &nb)) != srs_success) {
            return srs_error_wrap(err, "srt: recvmsg");
        }

        ++nb_packets;

        if ((err = on_srt_packet(buf, nb)) != srs_success) {
            return srs_error_wrap(err, "srt: process packet");
        }
    }
        
    return err;
}

on_srt_packet creates the SrsSrtPacket function. Execute srt_source_->on_packet .

srs_error_t SrsMpegtsSrtConn::on_srt_packet(char* buf, int nb_buf)
{
    srs_error_t err = srs_success;

    // Ignore if invalid length.
    if (nb_buf <= 0) {
        return err;
    }

    // Check srt payload, mpegts must be N times of SRS_TS_PACKET_SIZE
    if ((nb_buf % SRS_TS_PACKET_SIZE) != 0) {
        return srs_error_new(ERROR_SRT_CONN, "invalid ts packet len=%d", nb_buf);
    }

    // Check srt payload, the first byte must be 0x47
    if (buf[0] != 0x47) {
        return srs_error_new(ERROR_SRT_CONN, "invalid ts packet first=%#x", (uint8_t)buf[0]);
    }

    SrsSrtPacket* packet = new SrsSrtPacket();
    SrsAutoFree(SrsSrtPacket, packet);
    packet->wrap(buf, nb_buf);

    if ((err = srt_source_->on_packet(packet)) != srs_success) {
        return srs_error_wrap(err, "on srt packet");
    }
    
    return err;
}

 on_packet : srt push stream pushes data to each consumer consumer->enqueue;

 bridge_->on_packet : srt converts stream data of other protocols.

srs_error_t SrsSrtSource::on_packet(SrsSrtPacket* packet)
{
    srs_error_t err = srs_success;

    for (int i = 0; i < (int)consumers.size(); i++) {
        SrsSrtConsumer* consumer = consumers.at(i);
        if ((err = consumer->enqueue(packet->copy())) != srs_success) {
            return srs_error_wrap(err, "consume ts packet");
        }
    }

    if ((err = bridge_->on_packet(packet)) != srs_success) {
        return srs_error_wrap(err, "bridge consume message");
    }

    return err;
}

 2. The srs server handles the srt streaming processing. 

srs_error_t SrsMpegtsSrtConn::playing()
{
    srs_error_t err = srs_success;

    // We must do stat the client before hooks, because hooks depends on it.
    SrsStatistic* stat = SrsStatistic::instance();
    if ((err = stat->on_client(_srs_context->get_id().c_str(), req_, this, SrsSrtConnPlay)) != srs_success) {
        return srs_error_wrap(err, "rtmp: stat client");
    }

    // We must do hook after stat, because depends on it.
    if ((err = http_hooks_on_play()) != srs_success) {
        return srs_error_wrap(err, "rtmp: callback on play");
    }
    
    err = do_playing();
    http_hooks_on_stop();
    
    return err;
}

 do_playing() function:

consumer->dump_packet(&pkt); Each pull stream consumer takes out data.

 srt_conn_->write Send data. 

srs_error_t SrsMpegtsSrtConn::do_playing()
{
    srs_error_t err = srs_success;

    SrsSrtConsumer* consumer = NULL;
    SrsAutoFree(SrsSrtConsumer, consumer);
    if ((err = srt_source_->create_consumer(consumer)) != srs_success) {
        return srs_error_wrap(err, "create consumer, ts source=%s", req_->get_stream_url().c_str());
    }
    srs_assert(consumer);

    // TODO: FIXME: Dumps the SPS/PPS from gop cache, without other frames.
    if ((err = srt_source_->consumer_dumps(consumer)) != srs_success) {
        return srs_error_wrap(err, "dumps consumer, url=%s", req_->get_stream_url().c_str());
    }

    SrsPithyPrint* pprint = SrsPithyPrint::create_srt_play();
    SrsAutoFree(SrsPithyPrint, pprint);

    SrsSrtRecvThread srt_recv_trd(srt_conn_);
    if ((err = srt_recv_trd.start()) != srs_success) {
        return srs_error_wrap(err, "start srt recv trd");
    }

    int nb_packets = 0;

    while (true) {
        if ((err = trd_->pull()) != srs_success) {
            return srs_error_wrap(err, "srt play thread");
        }

        if ((err = srt_recv_trd.get_recv_err()) != srs_success) {
            return srs_error_wrap(err, "srt play recv thread");
        }

        // Wait for amount of packets.
        SrsSrtPacket* pkt = NULL;
        SrsAutoFree(SrsSrtPacket, pkt);
        consumer->dump_packet(&pkt);
        if (!pkt) {
            // TODO: FIXME: We should check the quit event.
            consumer->wait(1, 1000 * SRS_UTIME_MILLISECONDS);
            continue;
        }

        ++nb_packets;

        // reportable
        pprint->elapse();
        if (pprint->can_print()) {
            SrsSrtStat s;
            if ((err = s.fetch(srt_fd_, true)) != srs_success) {
                srs_freep(err);
            } else {
                srs_trace("-> " SRS_CONSTS_LOG_SRT_PLAY " Transport Stats # pktSent=%" PRId64 ", pktSndLoss=%d, pktRetrans=%d, pktSndDrop=%d",
                    s.pktSent(), s.pktSndLoss(), s.pktRetrans(), s.pktSndDrop());
            }

            kbps_->sample();
            srs_trace("-> " SRS_CONSTS_LOG_SRT_PLAY " time=%" PRId64 ", packets=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d",
                srsu2ms(pprint->age()), nb_packets, kbps_->get_send_kbps(), kbps_->get_send_kbps_30s(), kbps_->get_send_kbps_5m(),
                kbps_->get_recv_kbps(), kbps_->get_recv_kbps_30s(), kbps_->get_recv_kbps_5m());
            nb_packets = 0;
        }

        ssize_t nb_write = 0;
        if ((err = srt_conn_->write(pkt->data(), pkt->size(), &nb_write)) != srs_success) {
            return srs_error_wrap(err, "srt send, size=%d", pkt->size());
        }

        // Yield to another coroutines.
        // @see https://github.com/ossrs/srs/issues/2194#issuecomment-777542162
        // TODO: FIXME: Please check whether SRT sendmsg causing clock deviation, see srs_thread_yield of SrsUdpMuxSocket::sendto
    }

    return err;
}

 

Guess you like

Origin blog.csdn.net/u012794472/article/details/126947508