Merge branch 'audio_fec' into min_fec_packets

This commit is contained in:
loki 2021-07-04 16:29:34 +02:00
commit f23fcee382

View File

@ -12,6 +12,7 @@
extern "C" {
#include <moonlight-common-c/src/Video.h>
#include <moonlight-common-c/src/RtpAudioQueue.h>
#include <rs.h>
}
@ -79,11 +80,21 @@ struct audio_packet_raw_t {
RTP_PACKET rtp;
};
struct audio_fec_packet_raw_t {
uint8_t *payload() {
return (uint8_t *)(this + 1);
}
RTP_PACKET rtp;
AUDIO_FEC_HEADER fecHeader;
};
#pragma pack(pop)
using rh_t = util::safe_ptr<reed_solomon, reed_solomon_release>;
using video_packet_t = util::c_ptr<video_packet_raw_t>;
using audio_packet_t = util::c_ptr<audio_packet_raw_t>;
using audio_fec_packet_t = util::c_ptr<audio_fec_packet_raw_t>;
using message_queue_t = std::shared_ptr<safe::queue_t<std::pair<std::uint16_t, std::string>>>;
using message_queue_queue_t = std::shared_ptr<safe::queue_t<std::tuple<socket_e, asio::ip::address, message_queue_t>>>;
@ -181,7 +192,8 @@ struct session_t {
} video;
struct {
std::uint16_t frame;
std::uint16_t sequenceNumber;
std::uint32_t timestamp;
udp::endpoint peer;
} audio;
@ -709,6 +721,39 @@ void audioBroadcastThread(udp::socket &sock) {
auto shutdown_event = mail::man->event<bool>(mail::broadcast_shutdown);
auto packets = mail::man->queue<audio::packet_t>(mail::audio_packets);
auto max_block_size = 2048;
util::buffer_t<char> shards { RTPA_TOTAL_SHARDS * 2048 };
util::buffer_t<uint8_t *> shards_p { RTPA_TOTAL_SHARDS };
for(auto x = 0; x < RTPA_TOTAL_SHARDS; ++x) {
shards_p[x] = (uint8_t *)&shards[x * max_block_size];
}
audio_packet_t audio_packet { (audio_packet_raw_t *)malloc(sizeof(audio_packet_raw_t) + max_block_size) };
audio_fec_packet_t audio_fec_packet { (audio_fec_packet_raw_t *)malloc(sizeof(audio_fec_packet_raw_t) + max_block_size) };
fec::rs_t rs { reed_solomon_new(RTPA_DATA_SHARDS, RTPA_FEC_SHARDS) };
// For unknown reasons, the RS parity matrix computed by our RS implementation
// doesn't match the one Nvidia uses for audio data. I'm not exactly sure why,
// but we can simply replace it with the matrix generated by OpenFEC which
// works correctly. This is possible because the data and FEC shard count is
// constant and known in advance.
const unsigned char parity[] = { 0x77, 0x40, 0x38, 0x0e, 0xc7, 0xa7, 0x0d, 0x6c };
memcpy(&rs.get()->m[16], parity, sizeof(parity));
memcpy(rs.get()->parity, parity, sizeof(parity));
audio_packet->rtp.header = 0x80;
audio_packet->rtp.packetType = 97;
audio_packet->rtp.ssrc = 0;
audio_fec_packet->rtp.header = 0x80;
audio_fec_packet->rtp.packetType = 127;
audio_fec_packet->rtp.timestamp = 0;
audio_fec_packet->rtp.ssrc = 0;
audio_fec_packet->fecHeader.payloadType = audio_packet->rtp.packetType;
audio_fec_packet->fecHeader.ssrc = audio_packet->rtp.ssrc;
while(auto packet = packets->pop()) {
if(shutdown_event->peek()) {
break;
@ -717,20 +762,39 @@ void audioBroadcastThread(udp::socket &sock) {
TUPLE_2D_REF(channel_data, packet_data, *packet);
auto session = (session_t *)channel_data;
auto frame = session->audio.frame++;
auto sequenceNumber = session->audio.sequenceNumber;
auto timestamp = session->audio.timestamp;
audio_packet_t audio_packet { (audio_packet_raw_t *)malloc(sizeof(audio_packet_raw_t) + packet_data.size()) };
audio_packet->rtp.sequenceNumber = util::endian::big(sequenceNumber);
audio_packet->rtp.timestamp = util::endian::big(timestamp);
audio_packet->rtp.header = 0;
audio_packet->rtp.packetType = 97;
audio_packet->rtp.sequenceNumber = util::endian::big(frame);
audio_packet->rtp.timestamp = 0;
audio_packet->rtp.ssrc = 0;
session->audio.sequenceNumber++;
session->audio.timestamp += session->config.audio.packetDuration;
std::copy(std::begin(packet_data), std::end(packet_data), audio_packet->payload());
std::copy(std::begin(packet_data), std::end(packet_data), shards_p[sequenceNumber % RTPA_DATA_SHARDS]);
sock.send_to(asio::buffer((char *)audio_packet.get(), sizeof(audio_packet_raw_t) + packet_data.size()), session->audio.peer);
BOOST_LOG(verbose) << "Audio ["sv << frame << "] :: send..."sv;
BOOST_LOG(verbose) << "Audio ["sv << sequenceNumber << "] :: send..."sv;
// initialize the FEC header at the beginning of the FEC block
if (sequenceNumber % RTPA_DATA_SHARDS == 0) {
audio_fec_packet->fecHeader.baseSequenceNumber = util::endian::big(sequenceNumber);
audio_fec_packet->fecHeader.baseTimestamp = util::endian::big(timestamp);
}
// generate parity shards at the end of the FEC block
if ((sequenceNumber + 1) % RTPA_DATA_SHARDS == 0) {
reed_solomon_encode(rs.get(), shards_p.begin(), RTPA_TOTAL_SHARDS, packet_data.size());
for(auto x = 0; x < RTPA_FEC_SHARDS; ++x) {
audio_fec_packet->rtp.sequenceNumber = util::endian::big(sequenceNumber + x + 1);
audio_fec_packet->fecHeader.fecShardIndex = x;
memcpy(audio_fec_packet->payload(), shards_p[RTPA_DATA_SHARDS + x], packet_data.size());
sock.send_to(asio::buffer((char *)audio_fec_packet.get(), sizeof(audio_fec_packet_raw_t) + packet_data.size()), session->audio.peer);
BOOST_LOG(verbose) << "Audio FEC ["sv << (sequenceNumber & ~(RTPA_DATA_SHARDS - 1)) << ' ' << x <<"] :: send..."sv;
}
}
}
shutdown_event->raise(true);
@ -958,7 +1022,8 @@ std::shared_ptr<session_t> alloc(config_t &config, crypto::aes_t &gcm_key, crypt
session->video.idr_events = mail->event<video::idr_t>(mail::idr);
session->video.lowseq = 0;
session->audio.frame = 1;
session->audio.sequenceNumber = 0;
session->audio.timestamp = 0;
session->control.peer = nullptr;
session->state.store(state_e::STOPPED, std::memory_order_relaxed);