ultravideo / uvgRTP

An open-source library for RTP/SRTP media delivery
BSD 2-Clause "Simplified" License
325 stars 91 forks source link

How to forward to another client and reply from server #228

Open 5secorg opened 1 month ago

5secorg commented 1 month ago

Hi everyone, I am having trouble forwarding to every client that I send. Can you help me or suggest a way to get the data from RTP server?

My other question is: Can RTP respond to the Client? (I'm new to this) I want to create a Basic audio voice chat and this Framework has Opus support so it's of interest to me.

#include <uvgrtp/lib.hh>
#include <climits>
#include <cstring>
#include <vector>
#include <iostream>

 // Network parameters of this example
constexpr char      SEND_ADDR[] = "127.0.0.1";
constexpr char      RECV_ADDR[] = "0.0.0.0";
constexpr uint16_t  SEND_PORT   = 9930;
constexpr uint16_t  RECV_PORT   = 9932;

/* Save audio and video ssrcs for telling streams from each other */
uint32_t audio_ssrc = 0;

void rtp_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame)
{
    uvgrtp::media_stream* receiver_stream = static_cast<uvgrtp::media_stream*>(arg);

    /* Check which ssrcs belong to which streams */
    if (frame->header.payload == RTP_FORMAT_OPUS) {
        if (audio_ssrc == 0) {
            audio_ssrc = frame->header.ssrc;
        }

        if (receiver_stream->push_frame(frame->payload, frame->payload_len, RTP_NO_FLAGS) != RTP_OK)
            std::cout << "Failed to forward data" << std::endl;
        else
            std::cout << "Forward audio data..." << std::endl;
    }

    (void)uvgrtp::frame::dealloc_frame(frame);
}

void sender_hook(uvgrtp::frame::rtcp_sender_report* frame)
{
    uint64_t current_ntp = uvgrtp::clock::ntp::now();
    uint32_t msw = frame->sender_info.ntp_msw;
    uint32_t lsw = frame->sender_info.ntp_lsw;
    uint64_t ntp_ts = (uint64_t(msw) << 32) | lsw;
    uint64_t diff_ms = uvgrtp::clock::ntp::diff(ntp_ts, current_ntp);

    if (frame->ssrc == audio_ssrc) {
        std::cout << "Audio stream RTCP sender report! ----------" << std::endl;
    }

    /* This pair can be used to synchronize the streams */
    std::cout << "---RTP timestamp: " << frame->sender_info.rtp_ts << std::endl;
    std::cout << "---NTP timestamp: " << ntp_ts << std::endl;

    /* Latency between sending and receiving in milliseconds */
    std::cout << "---Difference between SR generation and current NTP time (ms): " << diff_ms << std::endl;

    /* RTCP frames can be deallocated using delete */
    delete frame;
}

void receive_function(uvgrtp::session* receiver_session, int flags,
    std::shared_ptr<std::mutex> print_mutex,
    RTP_FORMAT format, uint16_t receiver_port, uint16_t sender_port)
{

    print_mutex->lock();
    std::cout << "Receiver thread port: " << receiver_port << "<-" << sender_port << std::endl;
    print_mutex->unlock();

    uvgrtp::media_stream* stream_recv =
        receiver_session->create_stream(receiver_port, sender_port, format, flags);

    uvgrtp::media_stream* stream_send =
        receiver_session->create_stream(sender_port, receiver_port, format, flags);

    if (!stream_recv || stream_recv->get_rtcp()->install_sender_hook(sender_hook) != RTP_OK)
    {
        std::cerr << "Failed to install RTCP sender report hook" << std::endl;
    }

    if (!stream_recv || stream_recv->install_receive_hook(stream_recv, rtp_receive_hook) != RTP_OK)
    {
        std::cerr << "Failed to install RTP reception hook";
        return;
    }
}

int main(void)
{
    std::cout << "Starting uvgRTP RTCP stream synchronization example" << std::endl;

    std::shared_ptr<std::mutex> print_mutex = std::shared_ptr<std::mutex>(new std::mutex);
    unsigned rce_dh_flags = RCE_RTCP;
    unsigned rce_multistream_flags = RCE_RTCP;

    std::cout << "Initializing receivers" << std::endl;
    uvgrtp::context receiver_ctx;
    uvgrtp::session* receiver_session = receiver_ctx.create_session(
        SEND_ADDR/*remote address*/,
        RECV_ADDR/*local address*/
    );

    // start the audio and video receivers in different threads
    std::thread a_receiver(receive_function, receiver_session, rce_dh_flags, print_mutex,
        RTP_FORMAT_OPUS, RECV_PORT, SEND_PORT);

    std::cout << "Press 'enter' to continue" << std::endl;

    std::cin.get();

    // wait until all threads have ended
    if (a_receiver.joinable())
        a_receiver.join();

    if (receiver_session)
        receiver_ctx.destroy_session(receiver_session);

    std::cout << "RTCP stream synchronization example finished" << std::endl;
    return EXIT_SUCCESS;
}

If I want to send data from Server to Client, how should I do it? Please help me. Thank you.

jrsnen commented 1 month ago

Hello @5secorg

Clients should send their IP address to the sender somehow (outside the scope of uvgRTP, any method will do). Once the server has the IP address, it can start sending data there. To bypass firewalls, the client needs to send something in the opposite direction. You can use the RCE_HOLEPUNCH_KEEPALIVE enable flag for this, just make sure the session address and media_stream ports are exactly the same (but reversed) in the client and the server.

Hope this helps, Joni Räsänen

5secorg commented 1 month ago

@jrsnen Do you mean creating a UDP Socket yourself and just forwarding the frame to the Client?

jrsnen commented 1 month ago

For getting the IP address, I would recommend TCP or TLS.

For forwarding the packets you can either create the sockets yourself and just forward the packets or you can use uvgRTP for receiving and sending. Both methods should work and have their own upsides and downsides.