update quic service

This commit is contained in:
linbo 2026-07-27 16:07:41 +08:00
parent 428ee328de
commit 7c9a37f808
7 changed files with 206 additions and 3 deletions

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEKzCCApOgAwIBAgIUTtZCyKM8INYxZKkfwbKdpHKLjn4wDQYJKoZIhvcNAQEL
BQAwHTEbMBkGA1UEAwwSQ01WUiBRVUlDIExvY2FsIENBMB4XDTI2MDcyNDA2NDQy
MFoXDTM2MDcyMTA2NDQyMFowHTEbMBkGA1UEAwwSQ01WUiBRVUlDIExvY2FsIENB
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAmq2rHldOobaemqNfWggS
OVj3inKy6AYjfgtcXUfKs48DDbpZ9gyEd/YPJXA8C2jGPXpxzgnc7a4UCUVQZ8ah
ddoJtFcC+Q6BgjeMVqUdUubu5Y9HpkfU3lvnp4KhzvOeFnkKtrCzYIPa2nK3zLc7
uCiuLlB+91KQSRXPFbc6N7H/EAfGmUHIwlZGysAkRN7b2TAoR4C7E96JLVtuUQsS
VtlEGpunSfuefFzeeZCMS6avLbB+a8Q6yUzLt6pqnheNsDB+jCCXodlJs5XS1AOB
W2GOpGFMj7dLoTD+eBIlAlrhWFcwKjzFmtp6LGl/Jy0O+E99X4TL72oNSOdTrO2O
iavXABz9IvWR2BrAyo5AKlTJqO6tmZw77iVti8jYi+HsXIVGQKMYwvv5k0jdHeIZ
FaDToUbFPP/zj0m8ZraMy+8eNAhScnx6Zs56fcncBuDti6pT+zKisjV1rH/sFvZY
wO1UvJlOEbvXrfYLPp58Aqe/toG2nV45a2Q4+x6AETV1AgMBAAGjYzBhMB0GA1Ud
DgQWBBRdHz+g2vVmqghDZi2kEHtZfoyHZzAfBgNVHSMEGDAWgBRdHz+g2vVmqghD
Zi2kEHtZfoyHZzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkq
hkiG9w0BAQsFAAOCAYEASuEoMFiVcg4iTMxO2kshFTJ6LIrqqGXBn+1j+yQ1ennG
mqPMo5fBOe/Kp3YWCnREQWu0+EEPEC9qWgIDOIm3v7ch4mZiW31GUOqae6bjprBe
er7ySElKGZ5GefKAq++we19A6WHnNxtNAT9BE1VSKUmkxEsnIkuwd+QMmQ9eaIRm
8RvzshWdUcyiBJg07sI3rPzPD/YpnfcYlAa2a0+oXJ3o3zlhdbs2S+9fIl+Y/zFN
wBaUV6ZNk0RFzOCA+jUq2jU5Y1pODcot3Mp2jCHzp4uTFyVqsoqc5USRB2TUILCP
acSbEjD9GnKaNU4miTcftnC85fnBAiZ5btZkXIs72ij+J3VW7QnbcFQG8bElngoR
mWizh6ByPQrsvyT0evJjdjpuOzILsVTAn64m0cYpnlR4g5orcEkIJHv4DzMhpXc2
CvpcWwk2vJrUuws/LGxDATTAcqtIUIxAkxffqUsdB/6j8fdykNCvXFwNh/uPcawd
4kzh6dr+RFzHSMQ6oN16
-----END CERTIFICATE-----

View File

@ -0,0 +1,40 @@
-----BEGIN PRIVATE KEY-----
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
BprrEQmcdeZA8LE1zgcKtkZR1w==
-----END PRIVATE KEY-----

View File

@ -0,0 +1 @@
2C945D70B02014891B6E09D57E377CEFB6D18498

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDuzCCAiOgAwIBAgIULJRdcLAgFIkbbgnVfjd877bRhJgwDQYJKoZIhvcNAQEL
BQAwHTEbMBkGA1UEAwwSQ01WUiBRVUlDIExvY2FsIENBMB4XDTI2MDcyNDA2NDQz
NloXDTI4MTAyNjA2NDQzNlowGDEWMBQGA1UEAwwNMTkyLjE2OC4wLjIyMjCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdM3i1FYFKqNWJzhfhsD9nRUAuK
pzilz5uqCKAt8lKYYC9WnLHOYdiEjcHGnGr02yd6sWFH/LBbxNhzx8M7h4S4izuO
bhSlG1EIhkMiojzVD1e3P7YzXdEoVxTCfmMgBZQJG63GNOfzRawFYtEeGv7ndFVw
kitCYlyTza5KlBNlWpiNOPmmx4dLTdGLUk8a5TUm0zJ+b/LyzVsWUtr9sxKmKeG8
0/77AeiL0hQE3xUt5QROTZjRTVhNHowv410dFMJyIfY4sab8ndc4SIwE9PCKg068
4705vbFInBS3eTvQur5VLSZLPatnXGKzCXjci1lIQX2p/QIqADMGFWIN9OECAwEA
AaN4MHYwDwYDVR0RBAgwBocEwKgA3jAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAww
CgYIKwYBBQUHAwEwHQYDVR0OBBYEFLmoEtsglxm3mXh4l8OF31bt2ZhnMB8GA1Ud
IwQYMBaAFF0fP6Da9WaqCENmLaQQe1l+jIdnMA0GCSqGSIb3DQEBCwUAA4IBgQAR
eK1mD9rJkzHe4OusimQfcuDQW+0J32e4T/34RHlW+lIj7botFaElXIzO9S80tDwq
4d4ozNPKysqgJN9hv/BBMzJpZLwP2XozPaGLTNl1jRTCc9UhFUPrUeu0LbpQGBfC
6Ghq42V94zPAw4lnMujnkq8botk21hclbJORQ9kblXP31IdWCgiKSFLy1NTBmQmc
IxmR+SldMWYrWGWv/0I85AeMu6HR3+NKHmzDblm1HUHFekyC1f7sypNG+D1r8ab2
GSijoCMKHSEOm81Vl/j5bgWQygnnIOhsLOUf2DZO6jC+VZmTKpMEJNHgwk23WBlu
FYz/X9p5z9ZucL8aBxegj7G1fI5Ik2O05+LLeqJMfspAm6ZcnRDxCVZWQ+K/dAle
fz+gTASzhHTsjEBeiX46LP0L2PVyBiPvNtD66e52LoM5ZigechZq91niLO+ipWdh
xq7ryJzmaAEQKPuYaoswriWzJam07ywX9yupGHzBA6vVgGy0jPAsnGCuZXTfDyA=
-----END CERTIFICATE-----

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICpDCCAYwCAQAwGDEWMBQGA1UEAwwNMTkyLjE2OC4wLjIyMjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAKdM3i1FYFKqNWJzhfhsD9nRUAuKpzilz5uq
CKAt8lKYYC9WnLHOYdiEjcHGnGr02yd6sWFH/LBbxNhzx8M7h4S4izuObhSlG1EI
hkMiojzVD1e3P7YzXdEoVxTCfmMgBZQJG63GNOfzRawFYtEeGv7ndFVwkitCYlyT
za5KlBNlWpiNOPmmx4dLTdGLUk8a5TUm0zJ+b/LyzVsWUtr9sxKmKeG80/77AeiL
0hQE3xUt5QROTZjRTVhNHowv410dFMJyIfY4sab8ndc4SIwE9PCKg0684705vbFI
nBS3eTvQur5VLSZLPatnXGKzCXjci1lIQX2p/QIqADMGFWIN9OECAwEAAaBHMEUG
CSqGSIb3DQEJDjE4MDYwDwYDVR0RBAgwBocEwKgA3jAOBgNVHQ8BAf8EBAMCBaAw
EwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAGIcLeCE344z
PENI1/oONVHBzMMt5VN0P8jbkJOFgZ3a6AUhfqAmDNBr+8SBym+cX2Y9Q2BsaWAu
TKOBN+fs+fh5/NqF0hTNvmXzp89NFK5SlsTjoC21HJvK1HTNuNW8drOxNfWgFW3/
gSCutcsWS9hVtYrV2FHQzOvVXvXKfmuTkZE8g92P3BCkvRm+ORxI0QfVS81/Ibnv
Yl+t/o2QCphdP+1OWzU6+Pccqi2xCyIc6jqCmh0b01zg265PTTOfICO6WQNtYcOn
308avDDpTneGl5vWRVdmmiXPqkc+dCesoguIEDUlerFpDKKY81P2GTpfjki93K7Z
2DJPs4P1cU0=
-----END CERTIFICATE REQUEST-----

View File

@ -14,6 +14,8 @@
#include <unordered_set> #include <unordered_set>
#include <utility> #include <utility>
#include "common/base/logging/logger.h"
namespace cmvr::quic_edge { namespace cmvr::quic_edge {
namespace { namespace {
@ -112,6 +114,12 @@ public:
const std::string& server_name = const std::string& server_name =
config.tls().allow_insecure() || config.tls().server_name().empty() config.tls().allow_insecure() || config.tls().server_name().empty()
? config.server_host() : config.tls().server_name(); ? config.server_host() : config.tls().server_name();
CMVR_LOG(INFO) << "[MsQuicTransport] ConnectionStart"
<< ", connect_host=" << server_name
<< ", configured_server_host=" << config.server_host()
<< ", port=" << config.server_port()
<< ", ca_file=" << config.tls().ca_file()
<< ", allow_insecure=" << config.tls().allow_insecure();
status = api_->ConnectionStart( status = api_->ConnectionStart(
opened_connection, configuration_, QUIC_ADDRESS_FAMILY_UNSPEC, opened_connection, configuration_, QUIC_ADDRESS_FAMILY_UNSPEC,
server_name.c_str(), server_name.c_str(),

View File

@ -23,6 +23,7 @@
#include <unistd.h> #include <unistd.h>
#include "cmvr/quic_edge/v1/quic_edge.pb.h" #include "cmvr/quic_edge/v1/quic_edge.pb.h"
#include "common/base/logging/logger.h"
namespace cmvr::quic_edge { namespace cmvr::quic_edge {
namespace { namespace {
@ -718,12 +719,24 @@ void QuicEdgeService::run()
} }
resetConnectionStatus(); resetConnectionStatus();
setState(QuicEdgeServiceState::CONNECTING); setState(QuicEdgeServiceState::CONNECTING);
CMVR_LOG(INFO) << "[QuicEdgeService] connecting"
<< ", target=" << config_.server_host() << ':'
<< config_.server_port()
<< ", tls_server_name=" << config_.tls().server_name()
<< ", alpn=" << config_.alpn()
<< ", node_id=" << node_id_;
std::string error; std::string error;
if (!transport_->connect( if (!transport_->connect(
config_, config_,
std::chrono::milliseconds(config_.reconnect().connect_timeout_ms()), std::chrono::milliseconds(config_.reconnect().connect_timeout_ms()),
&error)) { &error)) {
CMVR_LOG(ERROR) << "[QuicEdgeService] connect failed"
<< ", target=" << config_.server_host() << ':'
<< config_.server_port()
<< ", error="
<< (error.empty() ? "QUIC connect failed" : error)
<< ", backoff_ms=" << backoff.count();
setState(QuicEdgeServiceState::BACKOFF, setState(QuicEdgeServiceState::BACKOFF,
error.empty() ? "QUIC connect failed" : error); error.empty() ? "QUIC connect failed" : error);
if (waitForStop(jittered(backoff))) break; if (waitForStop(jittered(backoff))) break;
@ -745,6 +758,10 @@ void QuicEdgeService::run()
has_inbound_message_sequence_ = false; has_inbound_message_sequence_ = false;
ControlFrameDecoder decoder(config_.maximum_control_frame_bytes()); ControlFrameDecoder decoder(config_.maximum_control_frame_bytes());
setState(QuicEdgeServiceState::REGISTERING); setState(QuicEdgeServiceState::REGISTERING);
CMVR_LOG(INFO) << "[QuicEdgeService] connected, registering node"
<< ", target=" << config_.server_host() << ':'
<< config_.server_port()
<< ", node_id=" << node_id_;
if (!performRegistration(&decoder, &error)) { if (!performRegistration(&decoder, &error)) {
transport_->disconnect(); transport_->disconnect();
@ -752,12 +769,22 @@ void QuicEdgeService::run()
std::lock_guard lock(mutex_); std::lock_guard lock(mutex_);
if (stop_requested_) break; if (stop_requested_) break;
} }
CMVR_LOG(ERROR) << "[QuicEdgeService] registration failed"
<< ", target=" << config_.server_host() << ':'
<< config_.server_port()
<< ", error=" << error
<< ", backoff_ms=" << backoff.count();
setState(QuicEdgeServiceState::BACKOFF, error); setState(QuicEdgeServiceState::BACKOFF, error);
if (waitForStop(jittered(backoff))) break; if (waitForStop(jittered(backoff))) break;
backoff = nextBackoff(backoff); backoff = nextBackoff(backoff);
continue; continue;
} }
setState(QuicEdgeServiceState::ONLINE); setState(QuicEdgeServiceState::ONLINE);
CMVR_LOG(INFO) << "[QuicEdgeService] online"
<< ", target=" << config_.server_host() << ':'
<< config_.server_port()
<< ", node_id=" << node_id_
<< ", session=" << session_id_;
if (!startMediaWorker(&error)) { if (!startMediaWorker(&error)) {
transport_->disconnect(); transport_->disconnect();
@ -1084,6 +1111,17 @@ bool QuicEdgeService::sendNodeRegistration(std::string* error)
return false; return false;
} }
if (!sendControlEnvelope(serialized, error)) return false; if (!sendControlEnvelope(serialized, error)) return false;
const auto& node = request->node();
CMVR_LOG(INFO) << "[QuicEdgeService] sent NodeRegisterRequest"
<< ", message_sequence=" << envelope.message_sequence()
<< ", payload_bytes=" << serialized.size()
<< ", node_id=" << node.node_id()
<< ", boot_id=" << node.boot_id()
<< ", grpc_endpoint=" << node.grpc_endpoint().host()
<< ':' << node.grpc_endpoint().port()
<< ", grpc_tls=" << node.grpc_endpoint().tls()
<< ", interfaces=" << node.local_interfaces_size()
<< ", sent_at_unix_ms=" << request->sent_at_unix_ms();
std::lock_guard lock(mutex_); std::lock_guard lock(mutex_);
++stats_.registrations_sent; ++stats_.registrations_sent;
return true; return true;
@ -1143,7 +1181,18 @@ bool QuicEdgeService::sendHeartbeat(const std::uint64_t sequence,
setError(error, "failed to serialize NodeHeartbeat"); setError(error, "failed to serialize NodeHeartbeat");
return false; return false;
} }
return sendControlEnvelope(serialized, error); if (!sendControlEnvelope(serialized, error)) return false;
// CMVR_LOG(INFO) << "[QuicEdgeService] sent NodeHeartbeat"
// << ", message_sequence=" << envelope.message_sequence()
// << ", payload_bytes=" << serialized.size()
// << ", session=" << session_id
// << ", heartbeat_sequence=" << sequence
// << ", grpc_endpoint=" << heartbeat->grpc_endpoint().host()
// << ':' << heartbeat->grpc_endpoint().port()
// << ", interfaces=" << heartbeat->local_interfaces_size()
// << ", devices=" << heartbeat->device_manager().devices_size()
// << ", sent_at_unix_ms=" << sent_at_unix_ms;
return true;
} }
bool QuicEdgeService::receiveAndDispatchControl( bool QuicEdgeService::receiveAndDispatchControl(
@ -1419,7 +1468,14 @@ bool QuicEdgeService::sendSessionOpen(std::string* error)
setError(error, "failed to serialize MediaSessionOpen"); setError(error, "failed to serialize MediaSessionOpen");
return false; return false;
} }
return sendControlEnvelope(serialized, error); if (!sendControlEnvelope(serialized, error)) return false;
CMVR_LOG(INFO) << "[QuicEdgeService] sent MediaSessionOpen"
<< ", message_sequence=" << envelope.message_sequence()
<< ", payload_bytes=" << serialized.size()
<< ", session=" << session_id
<< ", session_epoch=" << session_epoch_
<< ", node_id=" << node_id_;
return true;
} }
bool QuicEdgeService::sendTrackDescription( bool QuicEdgeService::sendTrackDescription(
@ -1453,7 +1509,25 @@ bool QuicEdgeService::sendTrackDescription(
setError(error, "failed to serialize MediaTrackDescriptor"); setError(error, "failed to serialize MediaTrackDescriptor");
return false; return false;
} }
return sendControlEnvelope(serialized, error); if (!sendControlEnvelope(serialized, error)) return false;
CMVR_LOG(INFO) << "[QuicEdgeService] sent MediaTrackDescriptor"
<< ", message_sequence=" << envelope.message_sequence()
<< ", payload_bytes=" << serialized.size()
<< ", track_id=" << description.track_id
<< ", source_track_id=" << description.source_track_id
<< ", source_id=" << description.source_id
<< ", kind=" << static_cast<int>(description.kind)
<< ", codec=" << codecName(description.codec)
<< ", payload_format=" << payloadFormatName(description.payload_format)
<< ", codec_generation=" << description.codec_generation
<< ", codec_generation_token="
<< description.codec_generation_token
<< ", size=" << description.width << 'x' << description.height
<< ", fps=" << description.nominal_rate
<< ", sample_rate=" << description.sample_rate
<< ", channels=" << description.channels
<< ", codec_config_bytes=" << description.codec_config.size();
return true;
} }
bool QuicEdgeService::sendControlEnvelope(const std::string& serialized, bool QuicEdgeService::sendControlEnvelope(const std::string& serialized,
@ -1646,6 +1720,22 @@ bool QuicEdgeService::processTrack(ActiveTrack* track,
std::lock_guard lock(mutex_); std::lock_guard lock(mutex_);
++stats_.frames_queued; ++stats_.frames_queued;
stats_.datagrams_queued += datagram_count; stats_.datagrams_queued += datagram_count;
CMVR_LOG(INFO) << "[QuicEdgeService] queued media frame"
<< ", track_id=" << track->config.track_id()
<< ", source_track_id=" << track->source_track_id
<< ", frame_sequence=" << frame->sequence
<< ", wire_frame_sequence=" << wire_frame_sequence
<< ", payload_bytes=" << frame->size()
<< ", datagrams=" << datagram_count
<< ", key_frame=" << frame->key_frame
<< ", discontinuity=" << discontinuity
<< ", codec=" << codecName(description.codec)
<< ", payload_format="
<< payloadFormatName(description.payload_format)
<< ", capture_timestamp_us="
<< frame->capture_time_ns / 1000U
<< ", source_timestamp=" << frame->source_timestamp
<< ", source_frame_number=" << frame->source_frame_number;
return true; return true;
} }
if (result == TransportSendResult::WOULD_BLOCK) { if (result == TransportSendResult::WOULD_BLOCK) {