Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
802
TMessagesProj/jni/voip/tgcalls/v2/ContentNegotiation.cpp
Normal file
802
TMessagesProj/jni/voip/tgcalls/v2/ContentNegotiation.cpp
Normal file
|
|
@ -0,0 +1,802 @@
|
|||
#include "v2/ContentNegotiation.h"
|
||||
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "media/base/media_engine.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
namespace {
|
||||
|
||||
signaling::MediaContent convertContentInfoToSingalingContent(cricket::ContentInfo const &content) {
|
||||
signaling::MediaContent mappedContent;
|
||||
|
||||
switch (content.media_description()->type()) {
|
||||
case cricket::MediaType::MEDIA_TYPE_AUDIO: {
|
||||
mappedContent.type = signaling::MediaContent::Type::Audio;
|
||||
|
||||
for (const auto &codec : content.media_description()->as_audio()->codecs()) {
|
||||
signaling::PayloadType mappedPayloadType;
|
||||
mappedPayloadType.id = codec.id;
|
||||
mappedPayloadType.name = codec.name;
|
||||
mappedPayloadType.clockrate = codec.clockrate;
|
||||
mappedPayloadType.channels = (uint32_t)codec.channels;
|
||||
|
||||
for (const auto &feedbackType : codec.feedback_params.params()) {
|
||||
signaling::FeedbackType mappedFeedbackType;
|
||||
mappedFeedbackType.type = feedbackType.id();
|
||||
mappedFeedbackType.subtype = feedbackType.param();
|
||||
mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType));
|
||||
}
|
||||
|
||||
for (const auto ¶meter : codec.params) {
|
||||
mappedPayloadType.parameters.push_back(std::make_pair(parameter.first, parameter.second));
|
||||
}
|
||||
std::sort(mappedPayloadType.parameters.begin(), mappedPayloadType.parameters.end(), [](std::pair<std::string, std::string> const &lhs, std::pair<std::string, std::string> const &rhs) -> bool {
|
||||
return lhs.first < rhs.first;
|
||||
});
|
||||
|
||||
mappedContent.payloadTypes.push_back(std::move(mappedPayloadType));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cricket::MediaType::MEDIA_TYPE_VIDEO: {
|
||||
mappedContent.type = signaling::MediaContent::Type::Video;
|
||||
|
||||
for (const auto &codec : content.media_description()->as_video()->codecs()) {
|
||||
signaling::PayloadType mappedPayloadType;
|
||||
mappedPayloadType.id = codec.id;
|
||||
mappedPayloadType.name = codec.name;
|
||||
mappedPayloadType.clockrate = codec.clockrate;
|
||||
mappedPayloadType.channels = 0;
|
||||
|
||||
for (const auto &feedbackType : codec.feedback_params.params()) {
|
||||
signaling::FeedbackType mappedFeedbackType;
|
||||
mappedFeedbackType.type = feedbackType.id();
|
||||
mappedFeedbackType.subtype = feedbackType.param();
|
||||
mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType));
|
||||
}
|
||||
|
||||
for (const auto ¶meter : codec.params) {
|
||||
mappedPayloadType.parameters.push_back(std::make_pair(parameter.first, parameter.second));
|
||||
}
|
||||
std::sort(mappedPayloadType.parameters.begin(), mappedPayloadType.parameters.end(), [](std::pair<std::string, std::string> const &lhs, std::pair<std::string, std::string> const &rhs) -> bool {
|
||||
return lhs.first < rhs.first;
|
||||
});
|
||||
|
||||
mappedContent.payloadTypes.push_back(std::move(mappedPayloadType));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_FATAL() << "Unknown media type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!content.media_description()->streams().empty()) {
|
||||
mappedContent.ssrc = content.media_description()->streams()[0].first_ssrc();
|
||||
for (const auto &ssrcGroup : content.media_description()->streams()[0].ssrc_groups) {
|
||||
signaling::SsrcGroup mappedSsrcGroup;
|
||||
mappedSsrcGroup.semantics = ssrcGroup.semantics;
|
||||
mappedSsrcGroup.ssrcs = ssrcGroup.ssrcs;
|
||||
mappedContent.ssrcGroups.push_back(std::move(mappedSsrcGroup));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &extension : content.media_description()->rtp_header_extensions()) {
|
||||
mappedContent.rtpExtensions.push_back(extension);
|
||||
}
|
||||
|
||||
return mappedContent;
|
||||
}
|
||||
|
||||
cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &contentId, signaling::MediaContent const &content, webrtc::RtpTransceiverDirection direction) {
|
||||
std::unique_ptr<cricket::MediaContentDescription> contentDescription;
|
||||
|
||||
switch (content.type) {
|
||||
case signaling::MediaContent::Type::Audio: {
|
||||
auto audioDescription = std::make_unique<cricket::AudioContentDescription>();
|
||||
|
||||
for (const auto &payloadType : content.payloadTypes) {
|
||||
cricket::AudioCodec mappedCodec = cricket::CreateAudioCodec((int)payloadType.id, payloadType.name, (int)payloadType.clockrate, payloadType.channels);
|
||||
for (const auto ¶meter : payloadType.parameters) {
|
||||
mappedCodec.params.insert(parameter);
|
||||
}
|
||||
for (const auto &feedbackParam : payloadType.feedbackTypes) {
|
||||
mappedCodec.AddFeedbackParam(cricket::FeedbackParam(feedbackParam.type, feedbackParam.subtype));
|
||||
}
|
||||
audioDescription->AddCodec(mappedCodec);
|
||||
}
|
||||
|
||||
contentDescription = std::move(audioDescription);
|
||||
|
||||
break;
|
||||
}
|
||||
case signaling::MediaContent::Type::Video: {
|
||||
auto videoDescription = std::make_unique<cricket::VideoContentDescription>();
|
||||
|
||||
for (const auto &payloadType : content.payloadTypes) {
|
||||
webrtc::SdpVideoFormat videoFormat(payloadType.name);
|
||||
for (const auto ¶meter : payloadType.parameters) {
|
||||
videoFormat.parameters.insert(parameter);
|
||||
}
|
||||
cricket::VideoCodec mappedCodec = cricket::CreateVideoCodec(videoFormat);
|
||||
mappedCodec.id = (int)payloadType.id;
|
||||
for (const auto &feedbackParam : payloadType.feedbackTypes) {
|
||||
mappedCodec.AddFeedbackParam(cricket::FeedbackParam(feedbackParam.type, feedbackParam.subtype));
|
||||
}
|
||||
videoDescription->AddCodec(mappedCodec);
|
||||
}
|
||||
|
||||
contentDescription = std::move(videoDescription);
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_FATAL() << "Unknown media type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cricket::StreamParams streamParams;
|
||||
streamParams.id = contentId;
|
||||
streamParams.set_stream_ids({ contentId });
|
||||
streamParams.add_ssrc(content.ssrc);
|
||||
for (const auto &ssrcGroup : content.ssrcGroups) {
|
||||
streamParams.ssrc_groups.push_back(cricket::SsrcGroup(ssrcGroup.semantics, ssrcGroup.ssrcs));
|
||||
for (const auto &ssrc : ssrcGroup.ssrcs) {
|
||||
if (!streamParams.has_ssrc(ssrc)) {
|
||||
streamParams.add_ssrc(ssrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
contentDescription->AddStream(streamParams);
|
||||
|
||||
for (const auto &extension : content.rtpExtensions) {
|
||||
contentDescription->AddRtpHeaderExtension(extension);
|
||||
}
|
||||
|
||||
contentDescription->set_direction(direction);
|
||||
contentDescription->set_rtcp_mux(true);
|
||||
|
||||
cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp);
|
||||
mappedContentInfo.name = contentId;
|
||||
mappedContentInfo.rejected = false;
|
||||
mappedContentInfo.bundle_only = false;
|
||||
mappedContentInfo.set_media_description(std::move(contentDescription));
|
||||
|
||||
return mappedContentInfo;
|
||||
}
|
||||
|
||||
cricket::ContentInfo createInactiveContentInfo(std::string const &contentId) {
|
||||
std::unique_ptr<cricket::MediaContentDescription> contentDescription;
|
||||
|
||||
auto audioDescription = std::make_unique<cricket::AudioContentDescription>();
|
||||
contentDescription = std::move(audioDescription);
|
||||
|
||||
contentDescription->set_direction(webrtc::RtpTransceiverDirection::kInactive);
|
||||
contentDescription->set_rtcp_mux(true);
|
||||
|
||||
cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp);
|
||||
mappedContentInfo.name = contentId;
|
||||
mappedContentInfo.rejected = false;
|
||||
mappedContentInfo.bundle_only = false;
|
||||
mappedContentInfo.set_media_description(std::move(contentDescription));
|
||||
|
||||
return mappedContentInfo;
|
||||
}
|
||||
|
||||
std::string contentIdBySsrc(uint32_t ssrc) {
|
||||
return std::to_string(ssrc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ContentNegotiationContext::ContentNegotiationContext(const webrtc::FieldTrialsView& fieldTrials, bool isOutgoing, cricket::MediaEngineInterface *mediaEngine, rtc::UniqueRandomIdGenerator *uniqueRandomIdGenerator) :
|
||||
_isOutgoing(isOutgoing),
|
||||
_uniqueRandomIdGenerator(uniqueRandomIdGenerator) {
|
||||
_transportDescriptionFactory = std::make_unique<cricket::TransportDescriptionFactory>(fieldTrials);
|
||||
|
||||
// tempCertificate is only used to fill in the local SDP
|
||||
auto tempCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
_transportDescriptionFactory->set_certificate(tempCertificate);
|
||||
|
||||
_sessionDescriptionFactory = std::make_unique<cricket::MediaSessionDescriptionFactory>(mediaEngine, true, uniqueRandomIdGenerator, _transportDescriptionFactory.get());
|
||||
|
||||
_needNegotiation = true;
|
||||
}
|
||||
|
||||
ContentNegotiationContext::~ContentNegotiationContext() {
|
||||
|
||||
}
|
||||
|
||||
void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngineInterface *mediaEngine, bool randomize) {
|
||||
cricket::AudioCodecs audioSendCodecs = mediaEngine->voice().send_codecs();
|
||||
cricket::AudioCodecs audioRecvCodecs = mediaEngine->voice().recv_codecs();
|
||||
cricket::VideoCodecs videoSendCodecs = mediaEngine->video().send_codecs();
|
||||
cricket::VideoCodecs videoRecvCodecs = mediaEngine->video().recv_codecs();
|
||||
|
||||
for (const auto &codec : audioSendCodecs) {
|
||||
if (codec.name == "opus") {
|
||||
audioSendCodecs = { codec };
|
||||
audioRecvCodecs = { codec };
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (randomize) {
|
||||
for (auto &codec : audioSendCodecs) {
|
||||
codec.id += 3;
|
||||
}
|
||||
for (auto &codec : videoSendCodecs) {
|
||||
codec.id += 3;
|
||||
}
|
||||
for (auto &codec : audioRecvCodecs) {
|
||||
codec.id += 3;
|
||||
}
|
||||
for (auto &codec : videoRecvCodecs) {
|
||||
codec.id += 3;
|
||||
}
|
||||
}
|
||||
|
||||
_sessionDescriptionFactory->set_audio_codecs(audioSendCodecs, audioRecvCodecs);
|
||||
_sessionDescriptionFactory->set_video_codecs(videoSendCodecs, videoRecvCodecs);
|
||||
|
||||
int absSendTimeUriId = 2;
|
||||
int transportSequenceNumberUriId = 3;
|
||||
int videoRotationUri = 13;
|
||||
|
||||
if (randomize) {
|
||||
absSendTimeUriId = 3;
|
||||
transportSequenceNumberUriId = 2;
|
||||
videoRotationUri = 4;
|
||||
}
|
||||
|
||||
_rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId);
|
||||
_rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId);
|
||||
|
||||
_rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId);
|
||||
_rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId);
|
||||
_rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kVideoRotationUri, videoRotationUri);
|
||||
}
|
||||
|
||||
std::string ContentNegotiationContext::addOutgoingChannel(signaling::MediaContent::Type mediaType) {
|
||||
std::string channelId = takeNextOutgoingChannelId();
|
||||
|
||||
cricket::MediaType mappedMediaType;
|
||||
std::vector<webrtc::RtpHeaderExtensionCapability> rtpExtensions;
|
||||
switch (mediaType) {
|
||||
case signaling::MediaContent::Type::Audio: {
|
||||
mappedMediaType = cricket::MediaType::MEDIA_TYPE_AUDIO;
|
||||
rtpExtensions = _rtpAudioExtensions;
|
||||
break;
|
||||
}
|
||||
case signaling::MediaContent::Type::Video: {
|
||||
mappedMediaType = cricket::MediaType::MEDIA_TYPE_VIDEO;
|
||||
rtpExtensions = _rtpVideoExtensions;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_FATAL() << "Unknown media type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
cricket::MediaDescriptionOptions offerDescription(mappedMediaType, channelId, webrtc::RtpTransceiverDirection::kSendOnly, false);
|
||||
offerDescription.header_extensions = rtpExtensions;
|
||||
|
||||
switch (mediaType) {
|
||||
case signaling::MediaContent::Type::Audio: {
|
||||
offerDescription.AddAudioSender(channelId, { channelId });
|
||||
break;
|
||||
}
|
||||
case signaling::MediaContent::Type::Video: {
|
||||
cricket::SimulcastLayerList simulcastLayers;
|
||||
offerDescription.AddVideoSender(channelId, { channelId }, {}, simulcastLayers, 1);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_FATAL() << "Unknown media type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_outgoingChannelDescriptions.emplace_back(std::move(offerDescription));
|
||||
_needNegotiation = true;
|
||||
|
||||
return channelId;
|
||||
}
|
||||
|
||||
void ContentNegotiationContext::removeOutgoingChannel(std::string const &id) {
|
||||
for (size_t i = 0; i < _outgoingChannels.size(); i++) {
|
||||
if (_outgoingChannelDescriptions[i].description.mid == id) {
|
||||
_outgoingChannelDescriptions.erase(_outgoingChannelDescriptions.begin() + i);
|
||||
|
||||
_needNegotiation = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cricket::SessionDescription> ContentNegotiationContext::currentSessionDescriptionFromCoordinatedState() {
|
||||
if (_channelIdOrder.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto sessionDescription = std::make_unique<cricket::SessionDescription>();
|
||||
|
||||
for (const auto &id : _channelIdOrder) {
|
||||
bool found = false;
|
||||
|
||||
for (const auto &channel : _incomingChannels) {
|
||||
if (contentIdBySsrc(channel.ssrc) == id) {
|
||||
found = true;
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(channel.ssrc), channel, webrtc::RtpTransceiverDirection::kRecvOnly);
|
||||
|
||||
// only required for content negotiation
|
||||
auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
|
||||
if (localCertificate) {
|
||||
fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get());
|
||||
}
|
||||
|
||||
std::vector<std::string> transportOptions;
|
||||
cricket::TransportDescription transportDescription(
|
||||
transportOptions,
|
||||
"ufrag",
|
||||
"pwd",
|
||||
cricket::IceMode::ICEMODE_FULL,
|
||||
cricket::ConnectionRole::CONNECTIONROLE_ACTPASS,
|
||||
fingerprint.get()
|
||||
);
|
||||
cricket::TransportInfo transportInfo(contentIdBySsrc(channel.ssrc), transportDescription);
|
||||
sessionDescription->AddTransportInfo(transportInfo);
|
||||
|
||||
sessionDescription->AddContent(std::move(mappedContent));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &channel : _outgoingChannels) {
|
||||
if (channel.id == id) {
|
||||
found = true;
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kSendOnly);
|
||||
|
||||
// only required for content negotiation
|
||||
auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
|
||||
if (localCertificate) {
|
||||
fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get());
|
||||
}
|
||||
|
||||
std::vector<std::string> transportOptions;
|
||||
cricket::TransportDescription transportDescription(
|
||||
transportOptions,
|
||||
"ufrag",
|
||||
"pwd",
|
||||
cricket::IceMode::ICEMODE_FULL,
|
||||
cricket::ConnectionRole::CONNECTIONROLE_ACTPASS,
|
||||
fingerprint.get()
|
||||
);
|
||||
cricket::TransportInfo transportInfo(mappedContent.name, transportDescription);
|
||||
sessionDescription->AddTransportInfo(transportInfo);
|
||||
|
||||
sessionDescription->AddContent(std::move(mappedContent));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
auto mappedContent = createInactiveContentInfo("_" + id);
|
||||
|
||||
// only required for content negotiation
|
||||
auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
|
||||
if (localCertificate) {
|
||||
fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get());
|
||||
}
|
||||
|
||||
std::vector<std::string> transportOptions;
|
||||
cricket::TransportDescription transportDescription(
|
||||
transportOptions,
|
||||
"ufrag",
|
||||
"pwd",
|
||||
cricket::IceMode::ICEMODE_FULL,
|
||||
cricket::ConnectionRole::CONNECTIONROLE_ACTPASS,
|
||||
fingerprint.get()
|
||||
);
|
||||
cricket::TransportInfo transportInfo(mappedContent.name, transportDescription);
|
||||
sessionDescription->AddTransportInfo(transportInfo);
|
||||
|
||||
sessionDescription->AddContent(std::move(mappedContent));
|
||||
}
|
||||
}
|
||||
|
||||
return sessionDescription;
|
||||
}
|
||||
|
||||
static cricket::MediaDescriptionOptions getIncomingContentDescription(signaling::MediaContent const &content) {
|
||||
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
|
||||
|
||||
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false);
|
||||
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
|
||||
contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
|
||||
}
|
||||
|
||||
return contentDescription;
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiationContext::getPendingOffer() {
|
||||
if (!_needNegotiation) {
|
||||
return nullptr;
|
||||
}
|
||||
if (_pendingOutgoingOffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_needNegotiation = false;
|
||||
|
||||
_pendingOutgoingOffer = std::make_unique<PendingOutgoingOffer>();
|
||||
_pendingOutgoingOffer->exchangeId = _uniqueRandomIdGenerator->GenerateId();
|
||||
|
||||
auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState();
|
||||
|
||||
cricket::MediaSessionOptions offerOptions;
|
||||
offerOptions.offer_extmap_allow_mixed = true;
|
||||
offerOptions.bundle_enabled = true;
|
||||
|
||||
for (const auto &id : _channelIdOrder) {
|
||||
bool found = false;
|
||||
|
||||
for (const auto &channel : _outgoingChannelDescriptions) {
|
||||
if (channel.description.mid == id) {
|
||||
found = true;
|
||||
offerOptions.media_description_options.push_back(channel.description);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &content : _incomingChannels) {
|
||||
if (contentIdBySsrc(content.ssrc) == id) {
|
||||
found = true;
|
||||
offerOptions.media_description_options.push_back(getIncomingContentDescription(content));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false);
|
||||
offerOptions.media_description_options.push_back(contentDescription);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &channel : _outgoingChannelDescriptions) {
|
||||
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), channel.description.mid) == _channelIdOrder.end()) {
|
||||
_channelIdOrder.push_back(channel.description.mid);
|
||||
|
||||
offerOptions.media_description_options.push_back(channel.description);
|
||||
}
|
||||
|
||||
for (const auto &content : _incomingChannels) {
|
||||
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), contentIdBySsrc(content.ssrc)) == _channelIdOrder.end()) {
|
||||
_channelIdOrder.push_back(contentIdBySsrc(content.ssrc));
|
||||
|
||||
offerOptions.media_description_options.push_back(getIncomingContentDescription(content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto offerOrError = _sessionDescriptionFactory->CreateOfferOrError(offerOptions, currentSessionDescription.get());
|
||||
if (!offerOrError.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto offer = offerOrError.MoveValue();
|
||||
|
||||
auto mappedOffer = std::make_unique<ContentNegotiationContext::NegotiationContents>();
|
||||
|
||||
mappedOffer->exchangeId = _pendingOutgoingOffer->exchangeId;
|
||||
|
||||
for (const auto &content : offer->contents()) {
|
||||
auto mappedContent = convertContentInfoToSingalingContent(content);
|
||||
|
||||
if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kSendOnly) {
|
||||
mappedOffer->contents.push_back(std::move(mappedContent));
|
||||
|
||||
for (auto &channel : _outgoingChannelDescriptions) {
|
||||
if (channel.description.mid == content.mid()) {
|
||||
channel.ssrc = mappedContent.ssrc;
|
||||
channel.ssrcGroups = mappedContent.ssrcGroups;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mappedOffer;
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiationContext::setRemoteNegotiationContent(std::unique_ptr<NegotiationContents> &&remoteNegotiationContent) {
|
||||
if (!remoteNegotiationContent) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (_pendingOutgoingOffer) {
|
||||
if (remoteNegotiationContent->exchangeId == _pendingOutgoingOffer->exchangeId) {
|
||||
setAnswer(std::move(remoteNegotiationContent));
|
||||
return nullptr;
|
||||
} else {
|
||||
// race condition detected — call initiator wins
|
||||
if (!_isOutgoing) {
|
||||
_pendingOutgoingOffer.reset();
|
||||
return getAnswer(std::move(remoteNegotiationContent));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return getAnswer(std::move(remoteNegotiationContent));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiationContext::getAnswer(std::unique_ptr<ContentNegotiationContext::NegotiationContents> &&offer) {
|
||||
auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState();
|
||||
|
||||
auto mappedOffer = std::make_unique<cricket::SessionDescription>();
|
||||
|
||||
cricket::MediaSessionOptions answerOptions;
|
||||
answerOptions.offer_extmap_allow_mixed = true;
|
||||
answerOptions.bundle_enabled = true;
|
||||
|
||||
for (const auto &id : _channelIdOrder) {
|
||||
bool found = false;
|
||||
|
||||
for (const auto &channel : _outgoingChannels) {
|
||||
if (channel.id == id) {
|
||||
found = true;
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kRecvOnly);
|
||||
|
||||
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kSendOnly, false);
|
||||
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
|
||||
contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
|
||||
}
|
||||
answerOptions.media_description_options.push_back(contentDescription);
|
||||
|
||||
// only required for content negotiation
|
||||
auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
|
||||
if (localCertificate) {
|
||||
fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get());
|
||||
}
|
||||
|
||||
std::vector<std::string> transportOptions;
|
||||
cricket::TransportDescription transportDescription(
|
||||
transportOptions,
|
||||
"ufrag",
|
||||
"pwd",
|
||||
cricket::IceMode::ICEMODE_FULL,
|
||||
cricket::ConnectionRole::CONNECTIONROLE_ACTPASS,
|
||||
fingerprint.get()
|
||||
);
|
||||
cricket::TransportInfo transportInfo(channel.id, transportDescription);
|
||||
mappedOffer->AddTransportInfo(transportInfo);
|
||||
|
||||
mappedOffer->AddContent(std::move(mappedContent));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &content : offer->contents) {
|
||||
if (contentIdBySsrc(content.ssrc) == id) {
|
||||
found = true;
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
|
||||
|
||||
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false);
|
||||
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
|
||||
contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
|
||||
}
|
||||
answerOptions.media_description_options.push_back(contentDescription);
|
||||
|
||||
// only required for content negotiation
|
||||
auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
|
||||
if (localCertificate) {
|
||||
fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get());
|
||||
}
|
||||
|
||||
std::vector<std::string> transportOptions;
|
||||
cricket::TransportDescription transportDescription(
|
||||
transportOptions,
|
||||
"ufrag",
|
||||
"pwd",
|
||||
cricket::IceMode::ICEMODE_FULL,
|
||||
cricket::ConnectionRole::CONNECTIONROLE_ACTPASS,
|
||||
fingerprint.get()
|
||||
);
|
||||
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
|
||||
mappedOffer->AddTransportInfo(transportInfo);
|
||||
|
||||
mappedOffer->AddContent(std::move(mappedContent));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
auto mappedContent = createInactiveContentInfo("_" + id);
|
||||
|
||||
cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false);
|
||||
answerOptions.media_description_options.push_back(contentDescription);
|
||||
|
||||
// only required for content negotiation
|
||||
auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
|
||||
if (localCertificate) {
|
||||
fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get());
|
||||
}
|
||||
|
||||
std::vector<std::string> transportOptions;
|
||||
cricket::TransportDescription transportDescription(
|
||||
transportOptions,
|
||||
"ufrag",
|
||||
"pwd",
|
||||
cricket::IceMode::ICEMODE_FULL,
|
||||
cricket::ConnectionRole::CONNECTIONROLE_ACTPASS,
|
||||
fingerprint.get()
|
||||
);
|
||||
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
|
||||
mappedOffer->AddTransportInfo(transportInfo);
|
||||
|
||||
mappedOffer->AddContent(std::move(mappedContent));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &content : offer->contents) {
|
||||
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), contentIdBySsrc(content.ssrc)) == _channelIdOrder.end()) {
|
||||
_channelIdOrder.push_back(contentIdBySsrc(content.ssrc));
|
||||
|
||||
answerOptions.media_description_options.push_back(getIncomingContentDescription(content));
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
|
||||
|
||||
// only required for content negotiation
|
||||
auto localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
|
||||
if (localCertificate) {
|
||||
fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*localCertificate.get());
|
||||
}
|
||||
|
||||
std::vector<std::string> transportOptions;
|
||||
cricket::TransportDescription transportDescription(
|
||||
transportOptions,
|
||||
"ufrag",
|
||||
"pwd",
|
||||
cricket::IceMode::ICEMODE_FULL,
|
||||
cricket::ConnectionRole::CONNECTIONROLE_ACTPASS,
|
||||
fingerprint.get()
|
||||
);
|
||||
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
|
||||
mappedOffer->AddTransportInfo(transportInfo);
|
||||
|
||||
mappedOffer->AddContent(std::move(mappedContent));
|
||||
}
|
||||
}
|
||||
|
||||
auto answerOrError = _sessionDescriptionFactory->CreateAnswerOrError(mappedOffer.get(), answerOptions, currentSessionDescription.get());
|
||||
if (!answerOrError.ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto answer = answerOrError.MoveValue();
|
||||
|
||||
auto mappedAnswer = std::make_unique<NegotiationContents>();
|
||||
|
||||
mappedAnswer->exchangeId = offer->exchangeId;
|
||||
|
||||
std::vector<signaling::MediaContent> incomingChannels;
|
||||
|
||||
for (const auto &content : answer->contents()) {
|
||||
auto mappedContent = convertContentInfoToSingalingContent(content);
|
||||
|
||||
if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kRecvOnly) {
|
||||
for (const auto &offerContent : offer->contents) {
|
||||
if (contentIdBySsrc(offerContent.ssrc) == content.mid()) {
|
||||
mappedContent.ssrc = offerContent.ssrc;
|
||||
mappedContent.ssrcGroups = offerContent.ssrcGroups;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
incomingChannels.push_back(mappedContent);
|
||||
mappedAnswer->contents.push_back(std::move(mappedContent));
|
||||
}
|
||||
}
|
||||
|
||||
_incomingChannels = incomingChannels;
|
||||
|
||||
return mappedAnswer;
|
||||
}
|
||||
|
||||
void ContentNegotiationContext::setAnswer(std::unique_ptr<ContentNegotiationContext::NegotiationContents> &&answer) {
|
||||
if (!_pendingOutgoingOffer) {
|
||||
return;
|
||||
}
|
||||
if (_pendingOutgoingOffer->exchangeId != answer->exchangeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingOutgoingOffer.reset();
|
||||
|
||||
_outgoingChannels.clear();
|
||||
|
||||
for (const auto &content : answer->contents) {
|
||||
for (const auto &pendingChannel : _outgoingChannelDescriptions) {
|
||||
if (pendingChannel.ssrc != 0 && content.ssrc == pendingChannel.ssrc) {
|
||||
_outgoingChannels.emplace_back(pendingChannel.description.mid, content);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ContentNegotiationContext::takeNextOutgoingChannelId() {
|
||||
const auto result = "m" + std::to_string(_nextOutgoingChannelId);
|
||||
_nextOutgoingChannelId++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentNegotiationContext::CoordinatedState> ContentNegotiationContext::coordinatedState() const {
|
||||
auto result = std::make_unique<ContentNegotiationContext::CoordinatedState>();
|
||||
|
||||
result->incomingContents = _incomingChannels;
|
||||
for (const auto &channel : _outgoingChannels) {
|
||||
bool found = false;
|
||||
|
||||
for (const auto &channelDescription : _outgoingChannelDescriptions) {
|
||||
if (channelDescription.description.mid == channel.id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
result->outgoingContents.push_back(channel.content);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
absl::optional<uint32_t> ContentNegotiationContext::outgoingChannelSsrc(std::string const &id) const {
|
||||
for (const auto &channel : _outgoingChannels) {
|
||||
bool found = false;
|
||||
|
||||
for (const auto &channelDescription : _outgoingChannelDescriptions) {
|
||||
if (channelDescription.description.mid == channel.id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found && channel.id == id) {
|
||||
if (channel.content.ssrc != 0) {
|
||||
return channel.content.ssrc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
} // namespace tgcalls
|
||||
99
TMessagesProj/jni/voip/tgcalls/v2/ContentNegotiation.h
Normal file
99
TMessagesProj/jni/voip/tgcalls/v2/ContentNegotiation.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#ifndef TGCALLS_CONTENT_NEGOTIATION_H
|
||||
#define TGCALLS_CONTENT_NEGOTIATION_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "media/base/media_engine.h"
|
||||
#include "pc/media_session.h"
|
||||
#include "pc/session_description.h"
|
||||
#include "p2p/base/transport_description_factory.h"
|
||||
|
||||
#include "v2/Signaling.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class ContentNegotiationContext {
|
||||
public:
|
||||
struct NegotiationContents {
|
||||
uint32_t exchangeId = 0;
|
||||
std::vector<signaling::MediaContent> contents;
|
||||
};
|
||||
|
||||
struct PendingOutgoingOffer {
|
||||
uint32_t exchangeId = 0;
|
||||
};
|
||||
|
||||
struct PendingOutgoingChannel {
|
||||
cricket::MediaDescriptionOptions description;
|
||||
|
||||
uint32_t ssrc = 0;
|
||||
std::vector<signaling::SsrcGroup> ssrcGroups;
|
||||
|
||||
PendingOutgoingChannel(cricket::MediaDescriptionOptions &&description_) :
|
||||
description(std::move(description_)) {
|
||||
}
|
||||
};
|
||||
|
||||
struct OutgoingChannel {
|
||||
std::string id;
|
||||
signaling::MediaContent content;
|
||||
|
||||
OutgoingChannel(std::string id_, signaling::MediaContent content_) :
|
||||
id(id_), content(content_) {
|
||||
}
|
||||
};
|
||||
|
||||
struct CoordinatedState {
|
||||
std::vector<signaling::MediaContent> outgoingContents;
|
||||
std::vector<signaling::MediaContent> incomingContents;
|
||||
};
|
||||
|
||||
public:
|
||||
ContentNegotiationContext(const webrtc::FieldTrialsView &fieldTrials, bool isOutgoing, cricket::MediaEngineInterface *mediaEngine, rtc::UniqueRandomIdGenerator *uniqueRandomIdGenerator);
|
||||
~ContentNegotiationContext();
|
||||
|
||||
void copyCodecsFromChannelManager(cricket::MediaEngineInterface *mediaEngine, bool randomize);
|
||||
|
||||
std::string addOutgoingChannel(signaling::MediaContent::Type mediaType);
|
||||
void removeOutgoingChannel(std::string const &id);
|
||||
|
||||
std::unique_ptr<NegotiationContents> getPendingOffer();
|
||||
std::unique_ptr<NegotiationContents> setRemoteNegotiationContent(std::unique_ptr<NegotiationContents> &&remoteNegotiationContent);
|
||||
|
||||
std::unique_ptr<CoordinatedState> coordinatedState() const;
|
||||
absl::optional<uint32_t> outgoingChannelSsrc(std::string const &id) const;
|
||||
|
||||
private:
|
||||
std::string takeNextOutgoingChannelId();
|
||||
std::unique_ptr<cricket::SessionDescription> currentSessionDescriptionFromCoordinatedState();
|
||||
|
||||
std::unique_ptr<NegotiationContents> getAnswer(std::unique_ptr<NegotiationContents> &&offer);
|
||||
void setAnswer(std::unique_ptr<NegotiationContents> &&answer);
|
||||
|
||||
private:
|
||||
bool _isOutgoing = false;
|
||||
rtc::UniqueRandomIdGenerator *_uniqueRandomIdGenerator = nullptr;
|
||||
|
||||
std::unique_ptr<cricket::TransportDescriptionFactory> _transportDescriptionFactory;
|
||||
std::unique_ptr<cricket::MediaSessionDescriptionFactory> _sessionDescriptionFactory;
|
||||
|
||||
std::vector<std::string> _channelIdOrder;
|
||||
|
||||
std::vector<webrtc::RtpHeaderExtensionCapability> _rtpAudioExtensions;
|
||||
std::vector<webrtc::RtpHeaderExtensionCapability> _rtpVideoExtensions;
|
||||
|
||||
std::vector<PendingOutgoingChannel> _outgoingChannelDescriptions;
|
||||
bool _needNegotiation = false;
|
||||
|
||||
std::vector<OutgoingChannel> _outgoingChannels;
|
||||
std::vector<signaling::MediaContent> _incomingChannels;
|
||||
|
||||
std::unique_ptr<PendingOutgoingOffer> _pendingOutgoingOffer;
|
||||
|
||||
int _nextOutgoingChannelId = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
600
TMessagesProj/jni/voip/tgcalls/v2/DirectNetworkingImpl.cpp
Normal file
600
TMessagesProj/jni/voip/tgcalls/v2/DirectNetworkingImpl.cpp
Normal file
|
|
@ -0,0 +1,600 @@
|
|||
#include "v2/DirectNetworkingImpl.h"
|
||||
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
#include "p2p/client/basic_port_allocator.h"
|
||||
#include "p2p/base/p2p_transport_channel.h"
|
||||
#include "p2p/base/basic_async_resolver_factory.h"
|
||||
#include "api/packet_socket_factory.h"
|
||||
#include "rtc_base/rtc_certificate_generator.h"
|
||||
#include "p2p/base/ice_credentials_iterator.h"
|
||||
#include "api/jsep_ice_candidate.h"
|
||||
#include "p2p/base/dtls_transport.h"
|
||||
#include "p2p/base/dtls_transport_factory.h"
|
||||
#include "pc/dtls_srtp_transport.h"
|
||||
#include "pc/dtls_transport.h"
|
||||
#include "pc/jsep_transport_controller.h"
|
||||
#include "api/async_dns_resolver.h"
|
||||
|
||||
#include "TurnCustomizerImpl.h"
|
||||
#include "ReflectorRelayPortFactory.h"
|
||||
#include "SctpDataChannelProviderInterfaceImpl.h"
|
||||
#include "StaticThreads.h"
|
||||
#include "platform/PlatformInterface.h"
|
||||
#include "p2p/base/turn_port.h"
|
||||
|
||||
#include "ReflectorPort.h"
|
||||
#include "FieldTrialsConfig.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
namespace {
|
||||
|
||||
}
|
||||
|
||||
class DirectPacketTransport : public rtc::PacketTransportInternal, public std::enable_shared_from_this<DirectPacketTransport> {
|
||||
public:
|
||||
DirectPacketTransport(
|
||||
rtc::Thread *thread,
|
||||
EncryptionKey const &encryptionKey,
|
||||
std::shared_ptr<DirectConnectionChannel> channel, std::function<void(bool)> &&isConnectedUpdated
|
||||
) :
|
||||
_isConnectedUpdated(std::move(isConnectedUpdated)),
|
||||
_thread(thread),
|
||||
_encryption(
|
||||
EncryptedConnection::Type::Transport,
|
||||
encryptionKey,
|
||||
[=](int delayMs, int cause) {
|
||||
assert(false);
|
||||
}),
|
||||
_channel(channel) {
|
||||
assert(_thread->IsCurrent());
|
||||
}
|
||||
|
||||
virtual ~DirectPacketTransport() {
|
||||
}
|
||||
|
||||
void start() {
|
||||
auto weakSelf = std::weak_ptr<DirectPacketTransport>(shared_from_this());
|
||||
_onIncomingPacketToken = _channel->addOnIncomingPacket([weakSelf, thread = _thread](std::shared_ptr<std::vector<uint8_t>> packet) {
|
||||
thread->PostTask([weakSelf, packet] {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->processIncomingPacket(packet);
|
||||
});
|
||||
});
|
||||
|
||||
updateState();
|
||||
runStateTimer();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!_onIncomingPacketToken.empty()) {
|
||||
_channel->removeOnIncomingPacket(_onIncomingPacketToken);
|
||||
_onIncomingPacketToken.resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual const std::string &transport_name() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
// The transport has been established.
|
||||
virtual bool writable() const override {
|
||||
return _isConnected;
|
||||
}
|
||||
|
||||
// The transport has received a packet in the last X milliseconds, here X is
|
||||
// configured by each implementation.
|
||||
virtual bool receiving() const override {
|
||||
return _isConnected;
|
||||
}
|
||||
|
||||
// Attempts to send the given packet.
|
||||
// The return value is < 0 on failure. The return value in failure case is not
|
||||
// descriptive. Depending on failure cause and implementation details
|
||||
// GetError() returns an descriptive errno.h error value.
|
||||
// This mimics posix socket send() or sendto() behavior.
|
||||
// TODO(johan): Reliable, meaningful, consistent error codes for all
|
||||
// implementations would be nice.
|
||||
// TODO(johan): Remove the default argument once channel code is updated.
|
||||
virtual int SendPacket(const char *data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags = 0) override {
|
||||
if (!_isConnected) {
|
||||
_lastError = ENOTCONN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rtc::CopyOnWriteBuffer buffer;
|
||||
buffer.AppendData(data, len);
|
||||
|
||||
Message message = flags == 0 ? Message { AudioDataMessage { buffer } } : Message { VideoDataMessage { buffer } };
|
||||
|
||||
if (const auto prepared = _encryption.prepareForSending(message)) {
|
||||
rtc::PacketOptions packetOptions;
|
||||
|
||||
rtc::ByteBufferWriter bufferWriter;
|
||||
bufferWriter.WriteUInt32((uint32_t)prepared->bytes.size());
|
||||
bufferWriter.WriteBytes(reinterpret_cast<const uint8_t *>(prepared->bytes.data()), prepared->bytes.size());
|
||||
while (bufferWriter.Length() % 4 != 0) {
|
||||
bufferWriter.WriteUInt8(0);
|
||||
}
|
||||
|
||||
auto packet = std::make_unique<std::vector<uint8_t>>(bufferWriter.Data(), bufferWriter.Data() + bufferWriter.Length());
|
||||
_channel->sendPacket(std::move(packet));
|
||||
}
|
||||
|
||||
rtc::SentPacket sentPacket;
|
||||
sentPacket.packet_id = options.packet_id;
|
||||
sentPacket.send_time_ms = rtc::TimeMillis();
|
||||
SignalSentPacket(this, sentPacket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sets a socket option. Note that not all options are
|
||||
// supported by all transport types.
|
||||
virtual int SetOption(rtc::Socket::Option opt, int value) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO(pthatcher): Once Chrome's MockPacketTransportInterface implements
|
||||
// this, remove the default implementation.
|
||||
virtual bool GetOption(rtc::Socket::Option opt, int *value) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the most recent error that occurred on this channel.
|
||||
virtual int GetError() override {
|
||||
return _lastError;
|
||||
}
|
||||
|
||||
// Returns the current network route with transport overhead.
|
||||
// TODO(zhihuang): Make it pure virtual once the Chrome/remoting is updated.
|
||||
virtual absl::optional<rtc::NetworkRoute> network_route() const override {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
/*sigslot::signal1<PacketTransportInternal*> SignalWritableState;
|
||||
|
||||
// Emitted when the PacketTransportInternal is ready to send packets. "Ready
|
||||
// to send" is more sensitive than the writable state; a transport may be
|
||||
// writable, but temporarily not able to send packets. For example, the
|
||||
// underlying transport's socket buffer may be full, as indicated by
|
||||
// SendPacket's return code and/or GetError.
|
||||
sigslot::signal1<PacketTransportInternal*> SignalReadyToSend;
|
||||
|
||||
// Emitted when receiving state changes to true.
|
||||
sigslot::signal1<PacketTransportInternal*> SignalReceivingState;
|
||||
|
||||
// Signalled each time a packet is received on this channel.
|
||||
sigslot::signal5<PacketTransportInternal*,
|
||||
const char*,
|
||||
size_t,
|
||||
// TODO(bugs.webrtc.org/9584): Change to passing the int64_t
|
||||
// timestamp by value.
|
||||
const int64_t&,
|
||||
int>
|
||||
SignalReadPacket;
|
||||
|
||||
// Signalled each time a packet is sent on this channel.
|
||||
sigslot::signal2<PacketTransportInternal*, const rtc::SentPacket&>
|
||||
SignalSentPacket;
|
||||
|
||||
// Signalled when the current network route has changed.
|
||||
sigslot::signal1<absl::optional<rtc::NetworkRoute>> SignalNetworkRouteChanged;
|
||||
|
||||
// Signalled when the transport is closed.
|
||||
sigslot::signal1<PacketTransportInternal*> SignalClosed;*/
|
||||
|
||||
private:
|
||||
void runStateTimer() {
|
||||
const auto weakSelf = std::weak_ptr<DirectPacketTransport>(shared_from_this());
|
||||
_thread->PostDelayedTask([weakSelf]() {
|
||||
auto strongSelf = weakSelf.lock();
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
strongSelf->updateState();
|
||||
strongSelf->runStateTimer();
|
||||
}, webrtc::TimeDelta::Millis(100));
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint8_t>> makeServerHelloPacket() {
|
||||
rtc::ByteBufferWriter bufferWriter;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
bufferWriter.WriteUInt8(0xffu);
|
||||
}
|
||||
bufferWriter.WriteUInt8(0xfeu);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
bufferWriter.WriteUInt8(0xffu);
|
||||
}
|
||||
bufferWriter.WriteUInt64(123);
|
||||
|
||||
while (bufferWriter.Length() % 4 != 0) {
|
||||
bufferWriter.WriteUInt8(0);
|
||||
}
|
||||
|
||||
return std::make_unique<std::vector<uint8_t>>(bufferWriter.Data(), bufferWriter.Data() + bufferWriter.Length());
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<uint8_t>> makePingPacket() {
|
||||
rtc::ByteBufferWriter bufferWriter;
|
||||
bufferWriter.WriteUInt32(_pingMarker);
|
||||
|
||||
return std::make_unique<std::vector<uint8_t>>(bufferWriter.Data(), bufferWriter.Data() + bufferWriter.Length());
|
||||
}
|
||||
|
||||
void updateState() {
|
||||
auto timestamp = rtc::TimeMillis();
|
||||
|
||||
if (_isConnected && _lastDataReceivedTimestamp < timestamp - _keepalivePingInterval * 2) {
|
||||
_isConnected = false;
|
||||
SignalWritableState(this);
|
||||
|
||||
if (_isConnectedUpdated) {
|
||||
_isConnectedUpdated(_isConnected);
|
||||
}
|
||||
}
|
||||
|
||||
if (_isConnected) {
|
||||
if (_lastPingSentTimestamp < timestamp - _initialPingInterval) {
|
||||
_lastPingSentTimestamp = timestamp;
|
||||
|
||||
auto packet = makePingPacket();
|
||||
_channel->sendPacket(std::move(packet));
|
||||
}
|
||||
} else {
|
||||
if (_lastPingSentTimestamp < timestamp - _keepalivePingInterval) {
|
||||
_lastPingSentTimestamp = timestamp;
|
||||
|
||||
if (_hasCompletedHello) {
|
||||
auto packet = makePingPacket();
|
||||
_channel->sendPacket(std::move(packet));
|
||||
} else {
|
||||
auto packet = makeServerHelloPacket();
|
||||
_channel->sendPacket(std::move(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void processIncomingPacket(std::shared_ptr<std::vector<uint8_t>> const &packet) {
|
||||
rtc::ByteBufferReader reader(rtc::ArrayView<const uint8_t>(reinterpret_cast<const uint8_t *>(packet->data()), packet->size()));
|
||||
|
||||
uint32_t header = 0;
|
||||
if (!reader.ReadUInt32(&header)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_lastDataReceivedTimestamp = rtc::TimeMillis();
|
||||
if (!_isConnected) {
|
||||
_isConnected = true;
|
||||
SignalWritableState(this);
|
||||
SignalReadyToSend(this);
|
||||
SignalReceivingState(this);
|
||||
|
||||
if (_isConnectedUpdated) {
|
||||
_isConnectedUpdated(_isConnected);
|
||||
}
|
||||
}
|
||||
|
||||
if (header == _pingMarker) {
|
||||
} else {
|
||||
bool isSpecialPacket = false;
|
||||
if (packet->size() >= 12) {
|
||||
uint8_t specialTag[12];
|
||||
memcpy(specialTag, packet->data(), 12);
|
||||
|
||||
uint8_t expectedSpecialTag[12];
|
||||
memset(expectedSpecialTag, 0xff, 12);
|
||||
|
||||
if (memcmp(specialTag, expectedSpecialTag, 12) == 0) {
|
||||
isSpecialPacket = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSpecialPacket) {
|
||||
rtc::ByteBufferReader dataPacketReader(rtc::ArrayView<const uint8_t>(reinterpret_cast<const uint8_t *>(packet->data()), packet->size()));
|
||||
uint32_t dataSize = 0;
|
||||
if (!dataPacketReader.ReadUInt32(&dataSize)) {
|
||||
return;
|
||||
}
|
||||
if (dataSize > packet->size() - 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto decrypted = _encryption.handleIncomingPacket(reinterpret_cast<const char *>(packet->data()) + 4, dataSize)) {
|
||||
handleIncomingMessage(decrypted->main);
|
||||
for (auto &message : decrypted->additional) {
|
||||
handleIncomingMessage(message);
|
||||
}
|
||||
|
||||
/*if (_transportMessageReceived) {
|
||||
_transportMessageReceived(std::move(decrypted->main));
|
||||
for (auto &message : decrypted->additional) {
|
||||
_transportMessageReceived(std::move(message));
|
||||
}
|
||||
}*/
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "DirectPacketTransport: could not decrypt incoming packet";
|
||||
}
|
||||
|
||||
/*uint32_t dataSize = 0;
|
||||
memcpy(&dataSize, packet->data(), 4);
|
||||
dataSize = be32toh(dataSize);
|
||||
if (dataSize > packet->size() - 4) {
|
||||
RTC_LOG(LS_WARNING) << "DirectPacketTransport: Received data packet with invalid size tag";
|
||||
} else {
|
||||
SignalReadPacket(this, reinterpret_cast<const char *>(packet->data() + 4), dataSize, rtc::TimeMicros(), 0);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleIncomingMessage(DecryptedMessage const &message) {
|
||||
const auto data = &message.message.data;
|
||||
if (const auto dataMessage = absl::get_if<AudioDataMessage>(data)) {
|
||||
SignalReadPacket(this, reinterpret_cast<const char *>(dataMessage->data.data()), dataMessage->data.size(), rtc::TimeMicros(), 0);
|
||||
} else if (const auto dataMessage = absl::get_if<VideoDataMessage>(data)) {
|
||||
SignalReadPacket(this, reinterpret_cast<const char *>(dataMessage->data.data()), dataMessage->data.size(), rtc::TimeMicros(), 1);
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << "DirectPacketTransport: unknown incoming message";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name = "DirectPacketTransport";
|
||||
|
||||
std::function<void(bool)> _isConnectedUpdated;
|
||||
|
||||
rtc::Thread *_thread = nullptr;
|
||||
EncryptedConnection _encryption;
|
||||
std::shared_ptr<DirectConnectionChannel> _channel;
|
||||
|
||||
std::vector<uint8_t> _onIncomingPacketToken;
|
||||
|
||||
int _lastError = 0;
|
||||
|
||||
int64_t _lastPingSentTimestamp = 0;
|
||||
int64_t _lastDataReceivedTimestamp = 0;
|
||||
bool _isConnected = false;
|
||||
|
||||
bool _hasCompletedHello = false;
|
||||
|
||||
uint32_t _pingMarker = 0xabcd0102;
|
||||
int64_t _initialPingInterval = 100;
|
||||
int64_t _keepalivePingInterval = 1000;
|
||||
};
|
||||
|
||||
class DirectRtpTransport : public webrtc::RtpTransport {
|
||||
public:
|
||||
explicit DirectRtpTransport() :
|
||||
webrtc::RtpTransport(true) {
|
||||
}
|
||||
|
||||
virtual bool IsSrtpActive() const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
DirectNetworkingImpl::DirectNetworkingImpl(Configuration &&configuration) :
|
||||
_threads(std::move(configuration.threads)),
|
||||
_isOutgoing(configuration.isOutgoing),
|
||||
_rtcServers(configuration.rtcServers),
|
||||
_stateUpdated(std::move(configuration.stateUpdated)),
|
||||
_transportMessageReceived(std::move(configuration.transportMessageReceived)),
|
||||
_rtcpPacketReceived(std::move(configuration.rtcpPacketReceived)),
|
||||
_dataChannelStateUpdated(configuration.dataChannelStateUpdated),
|
||||
_dataChannelMessageReceived(configuration.dataChannelMessageReceived) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), true);
|
||||
|
||||
_localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
|
||||
_rtpTransport = std::make_unique<DirectRtpTransport>();
|
||||
|
||||
_rtpTransport->SubscribeReadyToSend(this, [this](bool value) {
|
||||
this->DtlsReadyToSend(value);
|
||||
});
|
||||
_rtpTransport->SubscribeRtcpPacketReceived(this, [this](rtc::CopyOnWriteBuffer *packet, int64_t timestamp) {
|
||||
this->OnRtcpPacketReceived_n(packet, timestamp);
|
||||
});
|
||||
|
||||
_directConnectionChannel = configuration.directConnectionChannel;
|
||||
_packetTransport = std::make_shared<DirectPacketTransport>(
|
||||
_threads->getNetworkThread(),
|
||||
configuration.encryptionKey,
|
||||
_directConnectionChannel,
|
||||
[this](bool isConnected) {
|
||||
this->_transportIsConnected = isConnected;
|
||||
this->UpdateAggregateStates_n();
|
||||
}
|
||||
);
|
||||
|
||||
resetDtlsSrtpTransport();
|
||||
}
|
||||
|
||||
DirectNetworkingImpl::~DirectNetworkingImpl() {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
RTC_LOG(LS_INFO) << "DirectNetworkingImpl::~DirectNetworkingImpl()";
|
||||
|
||||
_rtpTransport.reset();
|
||||
_dataChannelInterface.reset();
|
||||
_packetTransport.reset();
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::resetDtlsSrtpTransport() {
|
||||
_rtpTransport->SetRtpPacketTransport(_packetTransport.get());
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::start() {
|
||||
const auto weak = std::weak_ptr<DirectNetworkingImpl>(shared_from_this());
|
||||
_dataChannelInterface.reset(new SctpDataChannelProviderInterfaceImpl(
|
||||
_packetTransport.get(),
|
||||
_isOutgoing,
|
||||
[weak, threads = _threads](bool state) {
|
||||
assert(threads->getNetworkThread()->IsCurrent());
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
strong->_dataChannelStateUpdated(state);
|
||||
},
|
||||
[weak, threads = _threads]() {
|
||||
assert(threads->getNetworkThread()->IsCurrent());
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
//strong->restartDataChannel();
|
||||
},
|
||||
[weak, threads = _threads](std::string const &message) {
|
||||
assert(threads->getNetworkThread()->IsCurrent());
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
strong->_dataChannelMessageReceived(message);
|
||||
},
|
||||
_threads
|
||||
));
|
||||
|
||||
_lastDisconnectedTimestamp = rtc::TimeMillis();
|
||||
checkConnectionTimeout();
|
||||
|
||||
_packetTransport->start();
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::stop() {
|
||||
_rtpTransport->UnsubscribeReadyToSend(this);
|
||||
|
||||
_dataChannelInterface.reset();
|
||||
_rtpTransport.reset();
|
||||
|
||||
_packetTransport->stop();
|
||||
|
||||
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), true);
|
||||
|
||||
_localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
}
|
||||
|
||||
PeerIceParameters DirectNetworkingImpl::getLocalIceParameters() {
|
||||
return _localIceParameters;
|
||||
}
|
||||
|
||||
std::unique_ptr<rtc::SSLFingerprint> DirectNetworkingImpl::getLocalFingerprint() {
|
||||
auto certificate = _localCertificate;
|
||||
if (!certificate) {
|
||||
return nullptr;
|
||||
}
|
||||
return rtc::SSLFingerprint::CreateFromCertificate(*certificate);
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup) {
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::addCandidates(std::vector<cricket::Candidate> const &candidates) {
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::sendDataChannelMessage(std::string const &message) {
|
||||
if (_dataChannelInterface) {
|
||||
_dataChannelInterface->sendDataChannelMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
webrtc::RtpTransport *DirectNetworkingImpl::getRtpTransport() {
|
||||
return _rtpTransport.get();
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::checkConnectionTimeout() {
|
||||
const auto weak = std::weak_ptr<DirectNetworkingImpl>(shared_from_this());
|
||||
_threads->getNetworkThread()->PostDelayedTask([weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t currentTimestamp = rtc::TimeMillis();
|
||||
const int64_t maxTimeout = 20000;
|
||||
|
||||
if (!strong->_isConnected && strong->_lastDisconnectedTimestamp + maxTimeout < currentTimestamp) {
|
||||
RTC_LOG(LS_INFO) << "DirectNetworkingImpl timeout " << (currentTimestamp - strong->_lastDisconnectedTimestamp) << " ms";
|
||||
|
||||
strong->_isFailed = true;
|
||||
strong->notifyStateUpdated();
|
||||
}
|
||||
|
||||
strong->checkConnectionTimeout();
|
||||
}, webrtc::TimeDelta::Millis(1000));
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::OnTransportWritableState_n(rtc::PacketTransportInternal *transport) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
UpdateAggregateStates_n();
|
||||
}
|
||||
void DirectNetworkingImpl::OnTransportReceivingState_n(rtc::PacketTransportInternal *transport) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
UpdateAggregateStates_n();
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::DtlsReadyToSend(bool isReadyToSend) {
|
||||
UpdateAggregateStates_n();
|
||||
|
||||
if (isReadyToSend) {
|
||||
const auto weak = std::weak_ptr<DirectNetworkingImpl>(shared_from_this());
|
||||
_threads->getNetworkThread()->PostTask([weak]() {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
strong->UpdateAggregateStates_n();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer *packet, int64_t packet_time_us) {
|
||||
if (_rtcpPacketReceived) {
|
||||
_rtcpPacketReceived(*packet, packet_time_us);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::UpdateAggregateStates_n() {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
bool isConnected = _transportIsConnected;
|
||||
|
||||
if (_isConnected != isConnected) {
|
||||
_isConnected = isConnected;
|
||||
|
||||
if (!isConnected) {
|
||||
_lastDisconnectedTimestamp = rtc::TimeMillis();
|
||||
}
|
||||
|
||||
notifyStateUpdated();
|
||||
|
||||
if (_dataChannelInterface) {
|
||||
_dataChannelInterface->updateIsConnected(isConnected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DirectNetworkingImpl::notifyStateUpdated() {
|
||||
DirectNetworkingImpl::State emitState;
|
||||
emitState.isReadyToSendData = _isConnected;
|
||||
emitState.route = _currentRouteDescription;
|
||||
emitState.connection = _currentConnectionDescription;
|
||||
emitState.isFailed = _isFailed;
|
||||
_stateUpdated(emitState);
|
||||
}
|
||||
|
||||
} // namespace tgcalls
|
||||
119
TMessagesProj/jni/voip/tgcalls/v2/DirectNetworkingImpl.h
Normal file
119
TMessagesProj/jni/voip/tgcalls/v2/DirectNetworkingImpl.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#ifndef TGCALLS_DIRECT_NETWORKING_IMPL_H
|
||||
#define TGCALLS_DIRECT_NETWORKING_IMPL_H
|
||||
|
||||
#ifdef WEBRTC_WIN
|
||||
// Compiler errors in conflicting Windows headers if not included here.
|
||||
#include <winsock2.h>
|
||||
#endif // WEBRTC_WIN
|
||||
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "api/candidate.h"
|
||||
#include "media/base/media_channel.h"
|
||||
#include "rtc_base/ssl_fingerprint.h"
|
||||
#include "pc/sctp_data_channel.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "InstanceNetworking.h"
|
||||
#include "Message.h"
|
||||
#include "ThreadLocalObject.h"
|
||||
#include "Instance.h"
|
||||
#include "EncryptedConnection.h"
|
||||
|
||||
namespace rtc {
|
||||
class BasicPacketSocketFactory;
|
||||
class BasicNetworkManager;
|
||||
class PacketTransportInternal;
|
||||
struct NetworkRoute;
|
||||
} // namespace rtc
|
||||
|
||||
namespace cricket {
|
||||
class BasicPortAllocator;
|
||||
class P2PTransportChannel;
|
||||
class IceTransportInternal;
|
||||
class DtlsTransport;
|
||||
class RelayPortFactoryInterface;
|
||||
} // namespace cricket
|
||||
|
||||
namespace webrtc {
|
||||
class TurnCustomizer;
|
||||
class DtlsSrtpTransport;
|
||||
class RtpTransport;
|
||||
class AsyncDnsResolverFactoryInterface;
|
||||
} // namespace webrtc
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
struct Message;
|
||||
class SctpDataChannelProviderInterfaceImpl;
|
||||
class Threads;
|
||||
class DirectPacketTransport;
|
||||
class DirectRtpTransport;
|
||||
|
||||
class DirectNetworkingImpl : public InstanceNetworking, public sigslot::has_slots<>, public std::enable_shared_from_this<DirectNetworkingImpl> {
|
||||
public:
|
||||
static webrtc::CryptoOptions getDefaulCryptoOptions();
|
||||
|
||||
DirectNetworkingImpl(Configuration &&configuration);
|
||||
~DirectNetworkingImpl();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
PeerIceParameters getLocalIceParameters();
|
||||
std::unique_ptr<rtc::SSLFingerprint> getLocalFingerprint();
|
||||
void setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup);
|
||||
void addCandidates(std::vector<cricket::Candidate> const &candidates);
|
||||
|
||||
void sendDataChannelMessage(std::string const &message);
|
||||
|
||||
webrtc::RtpTransport *getRtpTransport();
|
||||
|
||||
private:
|
||||
void checkConnectionTimeout();
|
||||
void notifyStateUpdated();
|
||||
|
||||
void resetDtlsSrtpTransport();
|
||||
|
||||
void DtlsReadyToSend(bool DtlsReadyToSend);
|
||||
void UpdateAggregateStates_n();
|
||||
void OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer *packet, int64_t packet_time_us);
|
||||
void OnTransportWritableState_n(rtc::PacketTransportInternal *transport);
|
||||
void OnTransportReceivingState_n(rtc::PacketTransportInternal *transport);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Threads> _threads;
|
||||
bool _isOutgoing = false;
|
||||
|
||||
webrtc::scoped_refptr<rtc::RTCCertificate> _localCertificate;
|
||||
std::vector<RtcServer> _rtcServers;
|
||||
PeerIceParameters _localIceParameters;
|
||||
|
||||
std::unique_ptr<DirectConnectionChannel> _channel;
|
||||
|
||||
std::shared_ptr<DirectConnectionChannel> _directConnectionChannel;
|
||||
std::shared_ptr<DirectPacketTransport> _packetTransport;
|
||||
std::unique_ptr<DirectRtpTransport> _rtpTransport;
|
||||
std::unique_ptr<SctpDataChannelProviderInterfaceImpl> _dataChannelInterface;
|
||||
|
||||
std::function<void(const DirectNetworkingImpl::State &)> _stateUpdated;
|
||||
std::function<void(rtc::CopyOnWriteBuffer const &, bool)> _transportMessageReceived;
|
||||
std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> _rtcpPacketReceived;
|
||||
std::function<void(bool)> _dataChannelStateUpdated;
|
||||
std::function<void(std::string const &)> _dataChannelMessageReceived;
|
||||
|
||||
bool _transportIsConnected = false;
|
||||
bool _isConnected = false;
|
||||
bool _isFailed = false;
|
||||
int64_t _lastDisconnectedTimestamp = 0;
|
||||
absl::optional<RouteDescription> _currentRouteDescription;
|
||||
absl::optional<ConnectionDescription> _currentConnectionDescription;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#include "v2/ExternalSignalingConnection.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
namespace {
|
||||
|
||||
}
|
||||
|
||||
ExternalSignalingConnection::ExternalSignalingConnection(std::function<void(const std::vector<uint8_t> &)> onIncomingData, std::function<void(const std::vector<uint8_t> &)> emitData) :
|
||||
_onIncomingData(onIncomingData),
|
||||
_emitData(emitData) {
|
||||
}
|
||||
|
||||
ExternalSignalingConnection::~ExternalSignalingConnection() {
|
||||
|
||||
}
|
||||
|
||||
void ExternalSignalingConnection::start() {
|
||||
|
||||
}
|
||||
|
||||
void ExternalSignalingConnection::send(const std::vector<uint8_t> &data) {
|
||||
_emitData(data);
|
||||
}
|
||||
|
||||
void ExternalSignalingConnection::receiveExternal(const std::vector<uint8_t> &data) {
|
||||
_onIncomingData(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef TGCALLS_EXTERNAL_SIGNALING_CONNECTION_H_
|
||||
#define TGCALLS_EXTERNAL_SIGNALING_CONNECTION_H_
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "SignalingConnection.h"
|
||||
|
||||
namespace rtc {
|
||||
class AsyncPacketSocket;
|
||||
}
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class ExternalSignalingConnection : public SignalingConnection {
|
||||
public:
|
||||
ExternalSignalingConnection(std::function<void(const std::vector<uint8_t> &)> onIncomingData, std::function<void(const std::vector<uint8_t> &)> emitData);
|
||||
virtual ~ExternalSignalingConnection();
|
||||
|
||||
virtual void start() override;
|
||||
|
||||
virtual void send(const std::vector<uint8_t> &data) override;
|
||||
virtual void receiveExternal(const std::vector<uint8_t> &data) override;
|
||||
|
||||
private:
|
||||
std::function<void(const std::vector<uint8_t> &)> _onIncomingData;
|
||||
std::function<void(const std::vector<uint8_t> &)> _emitData;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif // TGCALLS_EXTERNAL_SIGNALING_CONNECTION_H_
|
||||
172
TMessagesProj/jni/voip/tgcalls/v2/InstanceNetworking.h
Normal file
172
TMessagesProj/jni/voip/tgcalls/v2/InstanceNetworking.h
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
#ifndef TGCALLS_INSTANCE_NETWORKING_H
|
||||
#define TGCALLS_INSTANCE_NETWORKING_H
|
||||
|
||||
#ifdef WEBRTC_WIN
|
||||
// Compiler errors in conflicting Windows headers if not included here.
|
||||
#include <winsock2.h>
|
||||
#endif // WEBRTC_WIN
|
||||
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "api/candidate.h"
|
||||
#include "media/base/media_channel.h"
|
||||
#include "rtc_base/ssl_fingerprint.h"
|
||||
#include "pc/sctp_data_channel.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "Message.h"
|
||||
#include "ThreadLocalObject.h"
|
||||
#include "Instance.h"
|
||||
|
||||
#include "third-party/json11.hpp"
|
||||
|
||||
namespace rtc {
|
||||
class BasicPacketSocketFactory;
|
||||
class BasicNetworkManager;
|
||||
class PacketTransportInternal;
|
||||
struct NetworkRoute;
|
||||
} // namespace rtc
|
||||
|
||||
namespace cricket {
|
||||
class BasicPortAllocator;
|
||||
class P2PTransportChannel;
|
||||
class IceTransportInternal;
|
||||
class DtlsTransport;
|
||||
class RelayPortFactoryInterface;
|
||||
} // namespace cricket
|
||||
|
||||
namespace webrtc {
|
||||
class TurnCustomizer;
|
||||
class DtlsSrtpTransport;
|
||||
class RtpTransport;
|
||||
class AsyncDnsResolverFactoryInterface;
|
||||
} // namespace webrtc
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
struct Message;
|
||||
class SctpDataChannelProviderInterfaceImpl;
|
||||
class Threads;
|
||||
|
||||
class InstanceNetworking {
|
||||
public:
|
||||
struct RouteDescription {
|
||||
explicit RouteDescription(std::string const &localDescription_, std::string const &remoteDescription_) :
|
||||
localDescription(localDescription_),
|
||||
remoteDescription(remoteDescription_) {
|
||||
}
|
||||
|
||||
std::string localDescription;
|
||||
std::string remoteDescription;
|
||||
|
||||
bool operator==(RouteDescription const &rhs) const {
|
||||
if (localDescription != rhs.localDescription) {
|
||||
return false;
|
||||
}
|
||||
if (remoteDescription != rhs.remoteDescription) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const RouteDescription& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct ConnectionDescription {
|
||||
struct CandidateDescription {
|
||||
std::string protocol;
|
||||
std::string type;
|
||||
std::string address;
|
||||
|
||||
bool operator==(CandidateDescription const &rhs) const {
|
||||
if (protocol != rhs.protocol) {
|
||||
return false;
|
||||
}
|
||||
if (type != rhs.type) {
|
||||
return false;
|
||||
}
|
||||
if (address != rhs.address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const CandidateDescription& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
CandidateDescription local;
|
||||
CandidateDescription remote;
|
||||
|
||||
bool operator==(ConnectionDescription const &rhs) const {
|
||||
if (local != rhs.local) {
|
||||
return false;
|
||||
}
|
||||
if (remote != rhs.remote) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const ConnectionDescription& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
struct State {
|
||||
bool isReadyToSendData = false;
|
||||
bool isFailed = false;
|
||||
absl::optional<RouteDescription> route;
|
||||
absl::optional<ConnectionDescription> connection;
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
EncryptionKey encryptionKey;
|
||||
bool isOutgoing = false;
|
||||
bool enableStunMarking = false;
|
||||
bool enableTCP = false;
|
||||
bool enableP2P = false;
|
||||
std::vector<RtcServer> rtcServers;
|
||||
absl::optional<Proxy> proxy;
|
||||
std::function<void(const InstanceNetworking::State &)> stateUpdated;
|
||||
std::function<void(const cricket::Candidate &)> candidateGathered;
|
||||
std::function<void(rtc::CopyOnWriteBuffer const &, bool)> transportMessageReceived;
|
||||
std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> rtcpPacketReceived;
|
||||
std::function<void(bool)> dataChannelStateUpdated;
|
||||
std::function<void(std::string const &)> dataChannelMessageReceived;
|
||||
std::shared_ptr<Threads> threads;
|
||||
std::shared_ptr<DirectConnectionChannel> directConnectionChannel;
|
||||
std::map<std::string, json11::Json> customParameters;
|
||||
};
|
||||
|
||||
static webrtc::CryptoOptions getDefaulCryptoOptions();
|
||||
static ConnectionDescription::CandidateDescription connectionDescriptionFromCandidate(cricket::Candidate const &candidate);
|
||||
|
||||
virtual ~InstanceNetworking() = default;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual PeerIceParameters getLocalIceParameters() = 0;
|
||||
virtual std::unique_ptr<rtc::SSLFingerprint> getLocalFingerprint() = 0;
|
||||
virtual void setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup) = 0;
|
||||
virtual void addCandidates(std::vector<cricket::Candidate> const &candidates) = 0;
|
||||
|
||||
virtual void sendDataChannelMessage(std::string const &message) = 0;
|
||||
|
||||
virtual webrtc::RtpTransport *getRtpTransport() = 0;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
2425
TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.cpp
Normal file
2425
TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.cpp
Normal file
File diff suppressed because it is too large
Load diff
59
TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.h
Normal file
59
TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef TGCALLS_INSTANCEV2_IMPL_H
|
||||
#define TGCALLS_INSTANCEV2_IMPL_H
|
||||
|
||||
#include "Instance.h"
|
||||
#include "StaticThreads.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class LogSinkImpl;
|
||||
|
||||
class Manager;
|
||||
template <typename T>
|
||||
class ThreadLocalObject;
|
||||
|
||||
class InstanceV2ImplInternal;
|
||||
|
||||
class InstanceV2Impl final : public Instance {
|
||||
public:
|
||||
explicit InstanceV2Impl(Descriptor &&descriptor);
|
||||
~InstanceV2Impl() override;
|
||||
|
||||
void receiveSignalingData(const std::vector<uint8_t> &data) override;
|
||||
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) override;
|
||||
void setRequestedVideoAspect(float aspect) override;
|
||||
void setNetworkType(NetworkType networkType) override;
|
||||
void setMuteMicrophone(bool muteMicrophone) override;
|
||||
bool supportsVideo() override {
|
||||
return true;
|
||||
}
|
||||
void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override;
|
||||
void setAudioOutputGainControlEnabled(bool enabled) override;
|
||||
void setEchoCancellationStrength(int strength) override;
|
||||
void setAudioInputDevice(std::string id) override;
|
||||
void setAudioOutputDevice(std::string id) override;
|
||||
void setInputVolume(float level) override;
|
||||
void setOutputVolume(float level) override;
|
||||
void setAudioOutputDuckingEnabled(bool enabled) override;
|
||||
void setIsLowBatteryLevel(bool isLowBatteryLevel) override;
|
||||
static std::vector<std::string> GetVersions();
|
||||
static int GetConnectionMaxLayer();
|
||||
std::string getLastError() override;
|
||||
std::string getDebugInfo() override;
|
||||
int64_t getPreferredRelayId() override;
|
||||
TrafficStats getTrafficStats() override;
|
||||
PersistentState getPersistentState() override;
|
||||
void stop(std::function<void(FinalState)> completion) override;
|
||||
void sendVideoDeviceUpdated() override {
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Threads> _threads;
|
||||
std::unique_ptr<ThreadLocalObject<InstanceV2ImplInternal>> _internal;
|
||||
std::unique_ptr<LogSinkImpl> _logSink;
|
||||
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
1786
TMessagesProj/jni/voip/tgcalls/v2/InstanceV2ReferenceImpl.cpp
Normal file
1786
TMessagesProj/jni/voip/tgcalls/v2/InstanceV2ReferenceImpl.cpp
Normal file
File diff suppressed because it is too large
Load diff
59
TMessagesProj/jni/voip/tgcalls/v2/InstanceV2ReferenceImpl.h
Normal file
59
TMessagesProj/jni/voip/tgcalls/v2/InstanceV2ReferenceImpl.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef TGCALLS_INSTANCEV2_REFERENCE_IMPL_H
|
||||
#define TGCALLS_INSTANCEV2_REFERENCE_IMPL_H
|
||||
|
||||
#include "Instance.h"
|
||||
#include "StaticThreads.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class LogSinkImpl;
|
||||
|
||||
class Manager;
|
||||
template <typename T>
|
||||
class ThreadLocalObject;
|
||||
|
||||
class InstanceV2ReferenceImplInternal;
|
||||
|
||||
class InstanceV2ReferenceImpl final : public Instance {
|
||||
public:
|
||||
explicit InstanceV2ReferenceImpl(Descriptor &&descriptor);
|
||||
~InstanceV2ReferenceImpl() override;
|
||||
|
||||
void receiveSignalingData(const std::vector<uint8_t> &data) override;
|
||||
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) override;
|
||||
void setRequestedVideoAspect(float aspect) override;
|
||||
void setNetworkType(NetworkType networkType) override;
|
||||
void setMuteMicrophone(bool muteMicrophone) override;
|
||||
bool supportsVideo() override {
|
||||
return true;
|
||||
}
|
||||
void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override;
|
||||
void setAudioOutputGainControlEnabled(bool enabled) override;
|
||||
void setEchoCancellationStrength(int strength) override;
|
||||
void setAudioInputDevice(std::string id) override;
|
||||
void setAudioOutputDevice(std::string id) override;
|
||||
void setInputVolume(float level) override;
|
||||
void setOutputVolume(float level) override;
|
||||
void setAudioOutputDuckingEnabled(bool enabled) override;
|
||||
void setIsLowBatteryLevel(bool isLowBatteryLevel) override;
|
||||
static std::vector<std::string> GetVersions();
|
||||
static int GetConnectionMaxLayer();
|
||||
std::string getLastError() override;
|
||||
std::string getDebugInfo() override;
|
||||
int64_t getPreferredRelayId() override;
|
||||
TrafficStats getTrafficStats() override;
|
||||
PersistentState getPersistentState() override;
|
||||
void stop(std::function<void(FinalState)> completion) override;
|
||||
void sendVideoDeviceUpdated() override {
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Threads> _threads;
|
||||
std::unique_ptr<ThreadLocalObject<InstanceV2ReferenceImplInternal>> _internal;
|
||||
std::unique_ptr<LogSinkImpl> _logSink;
|
||||
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
1070
TMessagesProj/jni/voip/tgcalls/v2/NativeNetworkingImpl.cpp
Normal file
1070
TMessagesProj/jni/voip/tgcalls/v2/NativeNetworkingImpl.cpp
Normal file
File diff suppressed because it is too large
Load diff
142
TMessagesProj/jni/voip/tgcalls/v2/NativeNetworkingImpl.h
Normal file
142
TMessagesProj/jni/voip/tgcalls/v2/NativeNetworkingImpl.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#ifndef TGCALLS_NATIVE_NETWORKING_IMPL_H
|
||||
#define TGCALLS_NATIVE_NETWORKING_IMPL_H
|
||||
|
||||
#ifdef WEBRTC_WIN
|
||||
// Compiler errors in conflicting Windows headers if not included here.
|
||||
#include <winsock2.h>
|
||||
#endif // WEBRTC_WIN
|
||||
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "api/candidate.h"
|
||||
#include "media/base/media_channel.h"
|
||||
#include "rtc_base/ssl_fingerprint.h"
|
||||
#include "pc/sctp_data_channel.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "InstanceNetworking.h"
|
||||
#include "Message.h"
|
||||
#include "ThreadLocalObject.h"
|
||||
#include "Instance.h"
|
||||
|
||||
namespace rtc {
|
||||
class BasicPacketSocketFactory;
|
||||
class BasicNetworkManager;
|
||||
class PacketTransportInternal;
|
||||
struct NetworkRoute;
|
||||
} // namespace rtc
|
||||
|
||||
namespace cricket {
|
||||
class BasicPortAllocator;
|
||||
class P2PTransportChannel;
|
||||
class IceTransportInternal;
|
||||
class DtlsTransport;
|
||||
class RelayPortFactoryInterface;
|
||||
} // namespace cricket
|
||||
|
||||
namespace webrtc {
|
||||
class TurnCustomizer;
|
||||
class DtlsSrtpTransport;
|
||||
class RtpTransport;
|
||||
class AsyncDnsResolverFactoryInterface;
|
||||
} // namespace webrtc
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
struct Message;
|
||||
class SctpDataChannelProviderInterfaceImpl;
|
||||
class Threads;
|
||||
|
||||
class NativeNetworkingImpl : public InstanceNetworking, public sigslot::has_slots<>, public std::enable_shared_from_this<NativeNetworkingImpl> {
|
||||
public:
|
||||
static webrtc::CryptoOptions getDefaulCryptoOptions();
|
||||
|
||||
NativeNetworkingImpl(Configuration &&configuration);
|
||||
virtual ~NativeNetworkingImpl();
|
||||
|
||||
virtual void start() override;
|
||||
virtual void stop() override;
|
||||
|
||||
virtual PeerIceParameters getLocalIceParameters() override;
|
||||
virtual std::unique_ptr<rtc::SSLFingerprint> getLocalFingerprint() override;
|
||||
virtual void setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup) override;
|
||||
virtual void addCandidates(std::vector<cricket::Candidate> const &candidates) override;
|
||||
|
||||
virtual void sendDataChannelMessage(std::string const &message) override;
|
||||
|
||||
virtual webrtc::RtpTransport *getRtpTransport() override;
|
||||
|
||||
private:
|
||||
void resetDtlsSrtpTransport();
|
||||
void checkConnectionTimeout();
|
||||
void candidateGathered(cricket::IceTransportInternal *transport, const cricket::Candidate &candidate);
|
||||
void candidateGatheringState(cricket::IceTransportInternal *transport);
|
||||
void OnTransportWritableState_n(rtc::PacketTransportInternal *transport);
|
||||
void OnTransportReceivingState_n(rtc::PacketTransportInternal *transport);
|
||||
void transportStateChanged(cricket::IceTransportInternal *transport);
|
||||
void transportReadyToSend(cricket::IceTransportInternal *transport);
|
||||
void transportRouteChanged(absl::optional<rtc::NetworkRoute> route);
|
||||
void candidatePairChanged(cricket::CandidatePairChangeEvent const &event);
|
||||
void DtlsReadyToSend(bool DtlsReadyToSend);
|
||||
void UpdateAggregateStates_n();
|
||||
void RtpPacketReceived_n(rtc::CopyOnWriteBuffer *packet, int64_t packet_time_us, bool isUnresolved);
|
||||
void OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer *packet, int64_t packet_time_us);
|
||||
|
||||
void sctpReadyToSendData();
|
||||
|
||||
void notifyStateUpdated();
|
||||
|
||||
void processPendingLocalStandaloneReflectorCandidates();
|
||||
|
||||
std::shared_ptr<Threads> _threads;
|
||||
bool _isOutgoing = false;
|
||||
EncryptionKey _encryptionKey;
|
||||
bool _enableStunMarking = false;
|
||||
bool _enableTCP = false;
|
||||
bool _enableP2P = false;
|
||||
std::vector<RtcServer> _rtcServers;
|
||||
absl::optional<Proxy> _proxy;
|
||||
std::map<std::string, json11::Json> _customParameters;
|
||||
|
||||
std::function<void(const InstanceNetworking::State &)> _stateUpdated;
|
||||
std::function<void(const cricket::Candidate &)> _candidateGathered;
|
||||
std::function<void(rtc::CopyOnWriteBuffer const &, bool)> _transportMessageReceived;
|
||||
std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> _rtcpPacketReceived;
|
||||
std::function<void(bool)> _dataChannelStateUpdated;
|
||||
std::function<void(std::string const &)> _dataChannelMessageReceived;
|
||||
|
||||
std::unique_ptr<rtc::NetworkMonitorFactory> _networkMonitorFactory;
|
||||
rtc::SocketFactory *_underlyingSocketFactory = nullptr;
|
||||
std::unique_ptr<rtc::PacketSocketFactory> _socketFactory;
|
||||
std::unique_ptr<rtc::NetworkManager> _networkManager;
|
||||
std::unique_ptr<webrtc::TurnCustomizer> _turnCustomizer;
|
||||
std::unique_ptr<cricket::RelayPortFactoryInterface> _relayPortFactory;
|
||||
std::unique_ptr<cricket::BasicPortAllocator> _portAllocator;
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverFactoryInterface> _asyncResolverFactory;
|
||||
std::unique_ptr<cricket::P2PTransportChannel> _transportChannel;
|
||||
std::unique_ptr<webrtc::RtpTransport> _mtProtoRtpTransport;
|
||||
std::unique_ptr<cricket::DtlsTransport> _dtlsTransport;
|
||||
std::unique_ptr<webrtc::DtlsSrtpTransport> _dtlsSrtpTransport;
|
||||
|
||||
std::unique_ptr<SctpDataChannelProviderInterfaceImpl> _dataChannelInterface;
|
||||
|
||||
webrtc::scoped_refptr<rtc::RTCCertificate> _localCertificate;
|
||||
PeerIceParameters _localIceParameters;
|
||||
absl::optional<PeerIceParameters> _remoteIceParameters;
|
||||
|
||||
bool _isConnected = false;
|
||||
bool _isFailed = false;
|
||||
int64_t _lastDisconnectedTimestamp = 0;
|
||||
absl::optional<RouteDescription> _currentRouteDescription;
|
||||
absl::optional<ConnectionDescription> _currentConnectionDescription;
|
||||
|
||||
std::vector<cricket::Candidate> _pendingLocalStandaloneReflectorCandidates;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
111
TMessagesProj/jni/voip/tgcalls/v2/RawTcpSocket.cpp
Normal file
111
TMessagesProj/jni/voip/tgcalls/v2/RawTcpSocket.cpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "RawTcpSocket.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/byte_order.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/network/sent_packet.h"
|
||||
#include "rtc_base/time_utils.h" // for TimeMillis
|
||||
|
||||
#if defined(WEBRTC_POSIX)
|
||||
#include <errno.h>
|
||||
#endif // WEBRTC_POSIX
|
||||
|
||||
namespace rtc {
|
||||
|
||||
static const size_t kMaxPacketSize = 64 * 1024;
|
||||
|
||||
static const size_t kBufSize = kMaxPacketSize + 4;
|
||||
|
||||
// RawTcpSocket
|
||||
// Binds and connects `socket` and creates RawTcpSocket for
|
||||
// it. Takes ownership of `socket`. Returns null if bind() or
|
||||
// connect() fail (`socket` is destroyed in that case).
|
||||
RawTcpSocket* RawTcpSocket::Create(Socket* socket,
|
||||
const SocketAddress& bind_address,
|
||||
const SocketAddress& remote_address) {
|
||||
return new RawTcpSocket(
|
||||
AsyncTCPSocketBase::ConnectSocket(socket, bind_address, remote_address));
|
||||
}
|
||||
|
||||
RawTcpSocket::RawTcpSocket(Socket* socket)
|
||||
: AsyncTCPSocketBase(socket, kBufSize) {}
|
||||
|
||||
int RawTcpSocket::Send(const void* pv,
|
||||
size_t cb,
|
||||
const rtc::PacketOptions& options) {
|
||||
if (cb > kBufSize) {
|
||||
SetError(EMSGSIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If we are blocking on send, then silently drop this packet
|
||||
if (!IsOutBufferEmpty())
|
||||
return static_cast<int>(cb);
|
||||
|
||||
if (!did_send_mtproto_prologue_) {
|
||||
did_send_mtproto_prologue_ = true;
|
||||
uint32_t prologue = 0xeeeeeeee;
|
||||
AppendToOutBuffer(&prologue, 4);
|
||||
}
|
||||
|
||||
uint32_t pkt_len = (uint32_t)cb;
|
||||
AppendToOutBuffer(&pkt_len, 4);
|
||||
AppendToOutBuffer(pv, cb);
|
||||
|
||||
int res = FlushOutBuffer();
|
||||
if (res <= 0) {
|
||||
// drop packet if we made no progress
|
||||
ClearOutBuffer();
|
||||
return res;
|
||||
}
|
||||
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
|
||||
options.info_signaled_after_sent);
|
||||
CopySocketInformationToPacketInfo(cb, *this, false, &sent_packet.info);
|
||||
SignalSentPacket(this, sent_packet);
|
||||
|
||||
// We claim to have sent the whole thing, even if we only sent partial
|
||||
return static_cast<int>(cb);
|
||||
}
|
||||
|
||||
size_t RawTcpSocket::ProcessInput(rtc::ArrayView<const uint8_t> data) {
|
||||
SocketAddress remote_addr(GetRemoteAddress());
|
||||
|
||||
size_t processed_bytes = 0;
|
||||
while (true) {
|
||||
size_t bytes_left = data.size() - processed_bytes;
|
||||
if (bytes_left < 4)
|
||||
return processed_bytes;
|
||||
|
||||
uint32_t pkt_len = rtc::GetLE32(data.data() + processed_bytes);
|
||||
if (bytes_left < 4 + pkt_len)
|
||||
return processed_bytes;
|
||||
|
||||
rtc::ReceivedPacket received_packet(
|
||||
data.subview(processed_bytes + 4, pkt_len), remote_addr,
|
||||
webrtc::Timestamp::Micros(rtc::TimeMicros()));
|
||||
NotifyPacketReceived(received_packet);
|
||||
processed_bytes += 4 + pkt_len;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
53
TMessagesProj/jni/voip/tgcalls/v2/RawTcpSocket.h
Normal file
53
TMessagesProj/jni/voip/tgcalls/v2/RawTcpSocket.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef TG_CALLS_RAW_TCP_SOCKET_H_
|
||||
#define TG_CALLS_RAW_TCP_SOCKET_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/buffer.h"
|
||||
#include "rtc_base/socket.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/async_tcp_socket.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
class RawTcpSocket : public AsyncTCPSocketBase {
|
||||
public:
|
||||
// Binds and connects `socket` and creates RawTcpSocket for
|
||||
// it. Takes ownership of `socket`. Returns null if bind() or
|
||||
// connect() fail (`socket` is destroyed in that case).
|
||||
static RawTcpSocket* Create(Socket* socket,
|
||||
const SocketAddress& bind_address,
|
||||
const SocketAddress& remote_address);
|
||||
explicit RawTcpSocket(Socket* socket);
|
||||
~RawTcpSocket() override {}
|
||||
|
||||
RawTcpSocket(const RawTcpSocket&) = delete;
|
||||
RawTcpSocket& operator=(const RawTcpSocket&) = delete;
|
||||
|
||||
int Send(const void* pv,
|
||||
size_t cb,
|
||||
const rtc::PacketOptions& options) override;
|
||||
size_t ProcessInput(rtc::ArrayView<const uint8_t>) override;
|
||||
|
||||
private:
|
||||
bool did_send_mtproto_prologue_ = false;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // TG_CALLS_RAW_TCP_SOCKET_H_
|
||||
933
TMessagesProj/jni/voip/tgcalls/v2/ReflectorPort.cpp
Normal file
933
TMessagesProj/jni/voip/tgcalls/v2/ReflectorPort.cpp
Normal file
|
|
@ -0,0 +1,933 @@
|
|||
#include "v2/ReflectorPort.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/transport/stun.h"
|
||||
#include "p2p/base/connection.h"
|
||||
#include "p2p/base/p2p_constants.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/byte_order.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/net_helpers.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "system_wrappers/include/field_trial.h"
|
||||
#include "rtc_base/byte_order.h"
|
||||
|
||||
#include "RawTcpSocket.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
namespace {
|
||||
|
||||
rtc::CopyOnWriteBuffer parseHex(std::string const &string) {
|
||||
rtc::CopyOnWriteBuffer result;
|
||||
|
||||
for (size_t i = 0; i < string.length(); i += 2) {
|
||||
std::string byteString = string.substr(i, 2);
|
||||
char byte = (char)strtol(byteString.c_str(), NULL, 16);
|
||||
result.AppendData(&byte, 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int BindSocket(rtc::Socket* socket,
|
||||
const rtc::SocketAddress& local_address,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port) {
|
||||
int ret = -1;
|
||||
if (min_port == 0 && max_port == 0) {
|
||||
// If there's no port range, let the OS pick a port for us.
|
||||
ret = socket->Bind(local_address);
|
||||
} else {
|
||||
// Otherwise, try to find a port in the provided range.
|
||||
for (int port = min_port; ret < 0 && port <= max_port; ++port) {
|
||||
ret = socket->Bind(rtc::SocketAddress(local_address.ipaddr(), port));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtc::AsyncPacketSocket *CreateClientRawTcpSocket(
|
||||
rtc::SocketFactory *socket_factory_,
|
||||
const rtc::SocketAddress& local_address,
|
||||
const rtc::SocketAddress& remote_address,
|
||||
const rtc::ProxyInfo& proxy_info,
|
||||
const std::string& user_agent,
|
||||
const rtc::PacketSocketTcpOptions& tcp_options) {
|
||||
rtc::Socket* socket =
|
||||
socket_factory_->CreateSocket(local_address.family(), SOCK_STREAM);
|
||||
if (!socket) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (BindSocket(socket, local_address, 0, 0) < 0) {
|
||||
// Allow BindSocket to fail if we're binding to the ANY address, since this
|
||||
// is mostly redundant in the first place. The socket will be bound when we
|
||||
// call Connect() instead.
|
||||
if (local_address.IsAnyIP()) {
|
||||
RTC_LOG(LS_WARNING) << "TCP bind failed with error " << socket->GetError()
|
||||
<< "; ignoring since socket is using 'any' address.";
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "TCP bind failed with error " << socket->GetError();
|
||||
delete socket;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Set TCP_NODELAY (via OPT_NODELAY) for improved performance; this causes
|
||||
// small media packets to be sent immediately rather than being buffered up,
|
||||
// reducing latency.
|
||||
//
|
||||
// Must be done before calling Connect, otherwise it may fail.
|
||||
if (socket->SetOption(rtc::Socket::OPT_NODELAY, 1) != 0) {
|
||||
RTC_LOG(LS_ERROR) << "Setting TCP_NODELAY option failed with error "
|
||||
<< socket->GetError();
|
||||
}
|
||||
|
||||
if (socket->Connect(remote_address) < 0) {
|
||||
RTC_LOG(LS_ERROR) << "TCP connect failed with error " << socket->GetError();
|
||||
delete socket;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Finally, wrap that socket in a TCP or STUN TCP packet socket.
|
||||
rtc::AsyncPacketSocket* tcp_socket;
|
||||
tcp_socket = new rtc::RawTcpSocket(socket);
|
||||
|
||||
return tcp_socket;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int GetRelayPreference(cricket::ProtocolType proto) {
|
||||
switch (proto) {
|
||||
case cricket::PROTO_TCP:
|
||||
return cricket::ICE_TYPE_PREFERENCE_RELAY_TCP;
|
||||
case cricket::PROTO_TLS:
|
||||
return cricket::ICE_TYPE_PREFERENCE_RELAY_TLS;
|
||||
default:
|
||||
RTC_DCHECK(proto == cricket::PROTO_UDP);
|
||||
return cricket::ICE_TYPE_PREFERENCE_RELAY_UDP;
|
||||
}
|
||||
}
|
||||
|
||||
ReflectorPort::ReflectorPort(const cricket::CreateRelayPortArgs& args,
|
||||
rtc::SocketFactory *underlying_socket_factory,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
uint8_t serverId,
|
||||
int server_priority,
|
||||
bool standaloneReflectorMode,
|
||||
uint32_t standaloneReflectorRoleId)
|
||||
: Port(args.network_thread,
|
||||
cricket::RELAY_PORT_TYPE,
|
||||
args.socket_factory,
|
||||
args.network,
|
||||
args.username,
|
||||
args.password),
|
||||
server_address_(*args.server_address),
|
||||
credentials_(args.config->credentials),
|
||||
socket_(socket),
|
||||
underlying_socket_factory_(underlying_socket_factory),
|
||||
error_(0),
|
||||
stun_dscp_value_(rtc::DSCP_NO_CHANGE),
|
||||
state_(STATE_CONNECTING),
|
||||
server_priority_(server_priority),
|
||||
standaloneReflectorMode_(standaloneReflectorMode),
|
||||
standaloneReflectorRoleId_(standaloneReflectorRoleId) {
|
||||
serverId_ = serverId;
|
||||
|
||||
if (standaloneReflectorMode_) {
|
||||
randomTag_ = standaloneReflectorRoleId_;
|
||||
} else {
|
||||
auto generator = std::mt19937(std::random_device()());
|
||||
auto distribution = std::uniform_int_distribution<uint32_t>();
|
||||
do {
|
||||
randomTag_ = distribution(generator);
|
||||
} while (!randomTag_);
|
||||
}
|
||||
|
||||
auto rawPeerTag = parseHex(args.config->credentials.password);
|
||||
peer_tag_.AppendData(rawPeerTag.data(), rawPeerTag.size() - 4);
|
||||
peer_tag_.AppendData((uint8_t *)&randomTag_, 4);
|
||||
}
|
||||
|
||||
ReflectorPort::ReflectorPort(const cricket::CreateRelayPortArgs& args,
|
||||
rtc::SocketFactory *underlying_socket_factory,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
uint8_t serverId,
|
||||
int server_priority,
|
||||
bool standaloneReflectorMode,
|
||||
uint32_t standaloneReflectorRoleId)
|
||||
: Port(args.network_thread,
|
||||
cricket::RELAY_PORT_TYPE,
|
||||
args.socket_factory,
|
||||
args.network,
|
||||
min_port,
|
||||
max_port,
|
||||
args.username,
|
||||
args.password),
|
||||
server_address_(*args.server_address),
|
||||
credentials_(args.config->credentials),
|
||||
socket_(NULL),
|
||||
underlying_socket_factory_(underlying_socket_factory),
|
||||
error_(0),
|
||||
stun_dscp_value_(rtc::DSCP_NO_CHANGE),
|
||||
state_(STATE_CONNECTING),
|
||||
server_priority_(server_priority),
|
||||
standaloneReflectorMode_(standaloneReflectorMode),
|
||||
standaloneReflectorRoleId_(standaloneReflectorRoleId) {
|
||||
serverId_ = serverId;
|
||||
|
||||
if (standaloneReflectorMode_) {
|
||||
randomTag_ = standaloneReflectorRoleId_;
|
||||
} else {
|
||||
auto generator = std::mt19937(std::random_device()());
|
||||
auto distribution = std::uniform_int_distribution<uint32_t>();
|
||||
do {
|
||||
randomTag_ = distribution(generator);
|
||||
} while (!randomTag_);
|
||||
}
|
||||
|
||||
auto rawPeerTag = parseHex(args.config->credentials.password);
|
||||
peer_tag_.AppendData(rawPeerTag.data(), rawPeerTag.size() - 4);
|
||||
peer_tag_.AppendData((uint8_t *)&randomTag_, 4);
|
||||
}
|
||||
|
||||
ReflectorPort::~ReflectorPort() {
|
||||
// TODO(juberti): Should this even be necessary?
|
||||
|
||||
// release the allocation by sending a refresh with
|
||||
// lifetime 0.
|
||||
if (ready()) {
|
||||
Release();
|
||||
}
|
||||
|
||||
if (!SharedSocket()) {
|
||||
delete socket_;
|
||||
}
|
||||
|
||||
if (server_address_.proto == cricket::PROTO_TCP) {
|
||||
socket_->UnsubscribeCloseEvent(this);
|
||||
}
|
||||
}
|
||||
|
||||
rtc::SocketAddress ReflectorPort::GetLocalAddress() const {
|
||||
return socket_ ? socket_->GetLocalAddress() : rtc::SocketAddress();
|
||||
}
|
||||
|
||||
cricket::ProtocolType ReflectorPort::GetProtocol() const {
|
||||
return server_address_.proto;
|
||||
}
|
||||
|
||||
void ReflectorPort::PrepareAddress() {
|
||||
if (peer_tag_.size() != 16) {
|
||||
RTC_LOG(LS_ERROR) << "Allocation can't be started without setting the"
|
||||
" peer tag.";
|
||||
OnAllocateError(cricket::STUN_ERROR_UNAUTHORIZED,
|
||||
"Missing REFLECTOR server credentials.");
|
||||
return;
|
||||
}
|
||||
if (serverId_ == 0) {
|
||||
RTC_LOG(LS_ERROR) << "Allocation can't be started without setting the"
|
||||
" server id.";
|
||||
OnAllocateError(cricket::STUN_ERROR_UNAUTHORIZED,
|
||||
"Missing REFLECTOR server id.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!server_address_.address.port()) {
|
||||
// We will set default REFLECTOR port, if no port is set in the address.
|
||||
server_address_.address.SetPort(599);
|
||||
}
|
||||
|
||||
if (!AllowedReflectorPort(server_address_.address.port())) {
|
||||
// This can only happen after a 300 ALTERNATE SERVER, since the port can't
|
||||
// be created with a disallowed port number.
|
||||
RTC_LOG(LS_ERROR) << "Attempt to start allocation with disallowed port# "
|
||||
<< server_address_.address.port();
|
||||
OnAllocateError(cricket::STUN_ERROR_SERVER_ERROR,
|
||||
"Attempt to start allocation to a disallowed port");
|
||||
return;
|
||||
}
|
||||
if (server_address_.address.IsUnresolvedIP()) {
|
||||
ResolveTurnAddress(server_address_.address);
|
||||
} else {
|
||||
// If protocol family of server address doesn't match with local, return.
|
||||
if (!IsCompatibleAddress(server_address_.address)) {
|
||||
RTC_LOG(LS_ERROR) << "IP address family does not match. server: "
|
||||
<< server_address_.address.family()
|
||||
<< " local: " << Network()->GetBestIP().family();
|
||||
OnAllocateError(cricket::STUN_ERROR_GLOBAL_FAILURE,
|
||||
"IP address family does not match.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert the current address to prevent redirection pingpong.
|
||||
attempted_server_addresses_.insert(server_address_.address);
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Trying to connect to REFLECTOR server via "
|
||||
<< ProtoToString(server_address_.proto) << " @ "
|
||||
<< server_address_.address.ToSensitiveString();
|
||||
if (!CreateReflectorClientSocket()) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to create REFLECTOR client socket";
|
||||
OnAllocateError(cricket::SERVER_NOT_REACHABLE_ERROR,
|
||||
"Failed to create REFLECTOR client socket.");
|
||||
return;
|
||||
}
|
||||
if (server_address_.proto == cricket::PROTO_UDP) {
|
||||
SendReflectorHello();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectorPort::SendReflectorHello() {
|
||||
if (!(state_ == STATE_CONNECTED || state_ == STATE_READY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< ToString()
|
||||
<< ": REFLECTOR sending ping to " << server_address_.address.ToString();
|
||||
|
||||
if (server_address_.proto == cricket::PROTO_TCP) {
|
||||
rtc::ByteBufferWriter bufferWriter;
|
||||
bufferWriter.WriteBytes((const uint8_t *)peer_tag_.data(), peer_tag_.size());
|
||||
bufferWriter.WriteUInt32(0);
|
||||
|
||||
while (bufferWriter.Length() % 4 != 0) {
|
||||
bufferWriter.WriteUInt8(0);
|
||||
}
|
||||
|
||||
rtc::PacketOptions options;
|
||||
Send(bufferWriter.Data(), bufferWriter.Length(), options);
|
||||
} else {
|
||||
rtc::ByteBufferWriter bufferWriter;
|
||||
bufferWriter.WriteBytes((const uint8_t *)peer_tag_.data(), peer_tag_.size());
|
||||
for (int i = 0; i < 12; i++) {
|
||||
bufferWriter.WriteUInt8(0xffu);
|
||||
}
|
||||
bufferWriter.WriteUInt8(0xfeu);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
bufferWriter.WriteUInt8(0xffu);
|
||||
}
|
||||
bufferWriter.WriteUInt64(123);
|
||||
|
||||
while (bufferWriter.Length() % 4 != 0) {
|
||||
bufferWriter.WriteUInt8(0);
|
||||
}
|
||||
|
||||
rtc::PacketOptions options;
|
||||
Send(bufferWriter.Data(), bufferWriter.Length(), options);
|
||||
}
|
||||
|
||||
if (!is_running_ping_task_) {
|
||||
is_running_ping_task_ = true;
|
||||
|
||||
int timeoutMs = 10000;
|
||||
// Send pings faster until response arrives
|
||||
if (state_ == STATE_CONNECTED) {
|
||||
timeoutMs = 500;
|
||||
}
|
||||
|
||||
thread()->PostDelayedTask(SafeTask(task_safety_.flag(), [this] {
|
||||
is_running_ping_task_ = false;
|
||||
SendReflectorHello();
|
||||
}), webrtc::TimeDelta::Millis(timeoutMs));
|
||||
}
|
||||
}
|
||||
|
||||
bool ReflectorPort::CreateReflectorClientSocket() {
|
||||
RTC_DCHECK(!socket_ || SharedSocket());
|
||||
|
||||
if (server_address_.proto == cricket::PROTO_UDP && !SharedSocket()) {
|
||||
if (standaloneReflectorMode_ && Network()->name() == "shared-reflector-network") {
|
||||
const rtc::IPAddress ipv4_any_address(INADDR_ANY);
|
||||
socket_ = socket_factory()->CreateUdpSocket(rtc::SocketAddress(ipv4_any_address, 12345), min_port(), max_port());
|
||||
} else {
|
||||
socket_ = socket_factory()->CreateUdpSocket(rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port());
|
||||
}
|
||||
} else if (server_address_.proto == cricket::PROTO_TCP) {
|
||||
RTC_DCHECK(!SharedSocket());
|
||||
int opts = 0;
|
||||
|
||||
rtc::PacketSocketTcpOptions tcp_options;
|
||||
tcp_options.opts = opts;
|
||||
socket_ = CreateClientRawTcpSocket(
|
||||
underlying_socket_factory_,
|
||||
rtc::SocketAddress(Network()->GetBestIP(), 0), server_address_.address,
|
||||
proxy(), user_agent(), tcp_options);
|
||||
}
|
||||
|
||||
if (!socket_) {
|
||||
error_ = SOCKET_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply options if any.
|
||||
for (SocketOptionsMap::iterator iter = socket_options_.begin();
|
||||
iter != socket_options_.end(); ++iter) {
|
||||
socket_->SetOption(iter->first, iter->second);
|
||||
}
|
||||
|
||||
if (!SharedSocket()) {
|
||||
// If socket is shared, AllocationSequence will receive the packet.
|
||||
socket_->RegisterReceivedPacketCallback([this](rtc::AsyncPacketSocket *socket, const rtc::ReceivedPacket &packet) {
|
||||
this->OnReadPacket(socket, packet);
|
||||
});
|
||||
}
|
||||
|
||||
socket_->SignalReadyToSend.connect(this, &ReflectorPort::OnReadyToSend);
|
||||
|
||||
socket_->SignalSentPacket.connect(this, &ReflectorPort::OnSentPacket);
|
||||
|
||||
// TCP port is ready to send stun requests after the socket is connected,
|
||||
// while UDP port is ready to do so once the socket is created.
|
||||
if (server_address_.proto == cricket::PROTO_TCP ||
|
||||
server_address_.proto == cricket::PROTO_TLS) {
|
||||
socket_->SignalConnect.connect(this, &ReflectorPort::OnSocketConnect);
|
||||
socket_->SubscribeCloseEvent(this, [this](rtc::AsyncPacketSocket* socket, int error) { OnSocketClose(socket, error); });
|
||||
} else {
|
||||
state_ = STATE_CONNECTED;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReflectorPort::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
|
||||
// This slot should only be invoked if we're using a connection-oriented
|
||||
// protocol.
|
||||
RTC_DCHECK(server_address_.proto == cricket::PROTO_TCP ||
|
||||
server_address_.proto == cricket::PROTO_TLS);
|
||||
|
||||
// Do not use this port if the socket bound to an address not associated with
|
||||
// the desired network interface. This is seen in Chrome, where TCP sockets
|
||||
// cannot be given a binding address, and the platform is expected to pick
|
||||
// the correct local address.
|
||||
//
|
||||
// However, there are two situations in which we allow the bound address to
|
||||
// not be one of the addresses of the requested interface:
|
||||
// 1. The bound address is the loopback address. This happens when a proxy
|
||||
// forces TCP to bind to only the localhost address (see issue 3927).
|
||||
// 2. The bound address is the "any address". This happens when
|
||||
// multiple_routes is disabled (see issue 4780).
|
||||
//
|
||||
// Note that, aside from minor differences in log statements, this logic is
|
||||
// identical to that in TcpPort.
|
||||
const rtc::SocketAddress& socket_address = socket->GetLocalAddress();
|
||||
if (absl::c_none_of(Network()->GetIPs(),
|
||||
[socket_address](const rtc::InterfaceAddress& addr) {
|
||||
return socket_address.ipaddr() == addr;
|
||||
})) {
|
||||
if (socket->GetLocalAddress().IsLoopbackIP()) {
|
||||
RTC_LOG(LS_WARNING) << "Socket is bound to the address:"
|
||||
<< socket_address.ipaddr().ToSensitiveString()
|
||||
<< ", rather than an address associated with network:"
|
||||
<< Network()->ToString()
|
||||
<< ". Still allowing it since it's localhost.";
|
||||
} else if (IPIsAny(Network()->GetBestIP())) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "Socket is bound to the address:"
|
||||
<< socket_address.ipaddr().ToSensitiveString()
|
||||
<< ", rather than an address associated with network:"
|
||||
<< Network()->ToString()
|
||||
<< ". Still allowing it since it's the 'any' address"
|
||||
", possibly caused by multiple_routes being disabled.";
|
||||
} else {
|
||||
RTC_LOG(LS_WARNING) << "Socket is bound to the address:"
|
||||
<< socket_address.ipaddr().ToSensitiveString()
|
||||
<< ", rather than an address associated with network:"
|
||||
<< Network()->ToString() << ". Discarding REFLECTOR port.";
|
||||
OnAllocateError(
|
||||
cricket::STUN_ERROR_GLOBAL_FAILURE,
|
||||
"Address not associated with the desired network interface.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
state_ = STATE_CONNECTED; // It is ready to send stun requests.
|
||||
if (server_address_.address.IsUnresolvedIP()) {
|
||||
server_address_.address = socket_->GetRemoteAddress();
|
||||
}
|
||||
|
||||
RTC_LOG(LS_INFO) << "ReflectorPort connected to "
|
||||
<< socket->GetRemoteAddress().ToSensitiveString()
|
||||
<< " using tcp.";
|
||||
|
||||
if (server_address_.proto == cricket::PROTO_TCP && state_ != STATE_READY) {
|
||||
state_ = STATE_READY;
|
||||
|
||||
RTC_LOG(LS_INFO)
|
||||
<< ToString()
|
||||
<< ": REFLECTOR " << server_address_.address.ToString() << " is now ready";
|
||||
|
||||
const auto ipFormat = "reflector-" + std::to_string((uint32_t)serverId_) + "-" + std::to_string(randomTag_) + ".reflector";
|
||||
rtc::SocketAddress candidateAddress(ipFormat, server_address_.address.port());
|
||||
if (standaloneReflectorMode_) {
|
||||
candidateAddress.SetResolvedIP(server_address_.address.ipaddr());
|
||||
}
|
||||
|
||||
// For relayed candidate, Base is the candidate itself.
|
||||
AddAddress(candidateAddress, // Candidate address.
|
||||
server_address_.address, // Base address.
|
||||
rtc::SocketAddress(), // Related address.
|
||||
cricket::UDP_PROTOCOL_NAME,
|
||||
ProtoToString(server_address_.proto), // The first hop protocol.
|
||||
"", // TCP canddiate type, empty for turn candidates.
|
||||
cricket::RELAY_PORT_TYPE, GetRelayPreference(server_address_.proto),
|
||||
server_priority_, ReconstructedServerUrl(false /* use_hostname */),
|
||||
true);
|
||||
|
||||
SendReflectorHello();
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectorPort::OnSocketClose(rtc::AsyncPacketSocket* socket, int error) {
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": Connection with server failed with error: "
|
||||
<< error;
|
||||
RTC_DCHECK(socket == socket_);
|
||||
//Close();
|
||||
}
|
||||
|
||||
cricket::Connection* ReflectorPort::CreateConnection(const cricket::Candidate& remote_candidate,
|
||||
CandidateOrigin origin) {
|
||||
// REFLECTOR-UDP can only connect to UDP candidates.
|
||||
if (!SupportsProtocol(remote_candidate.protocol())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto remoteHostname = remote_candidate.address().hostname();
|
||||
if (remoteHostname.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto ipFormat = "reflector-" + std::to_string((uint32_t)serverId_) + "-";
|
||||
if (!absl::StartsWith(remoteHostname, ipFormat) || !absl::EndsWith(remoteHostname, ".reflector")) {
|
||||
return nullptr;
|
||||
}
|
||||
if (remote_candidate.address().port() != server_address_.address.port()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (state_ == STATE_DISCONNECTED || state_ == STATE_RECEIVEONLY) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cricket::Candidate updated_remote_candidate = remote_candidate;
|
||||
if (server_address_.proto == cricket::PROTO_TCP) {
|
||||
rtc::SocketAddress updated_address = updated_remote_candidate.address();
|
||||
updated_address.SetResolvedIP(server_address_.address.ipaddr());
|
||||
updated_remote_candidate.set_address(updated_address);
|
||||
}
|
||||
|
||||
cricket::ProxyConnection* conn = new cricket::ProxyConnection(NewWeakPtr(), 0, updated_remote_candidate);
|
||||
AddOrReplaceConnection(conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
bool ReflectorPort::FailAndPruneConnection(const rtc::SocketAddress& address) {
|
||||
cricket::Connection* conn = GetConnection(address);
|
||||
if (conn != nullptr) {
|
||||
conn->FailAndPrune();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ReflectorPort::SetOption(rtc::Socket::Option opt, int value) {
|
||||
// Remember the last requested DSCP value, for STUN traffic.
|
||||
if (opt == rtc::Socket::OPT_DSCP)
|
||||
stun_dscp_value_ = static_cast<rtc::DiffServCodePoint>(value);
|
||||
|
||||
if (!socket_) {
|
||||
// If socket is not created yet, these options will be applied during socket
|
||||
// creation.
|
||||
socket_options_[opt] = value;
|
||||
return 0;
|
||||
}
|
||||
return socket_->SetOption(opt, value);
|
||||
}
|
||||
|
||||
int ReflectorPort::GetOption(rtc::Socket::Option opt, int* value) {
|
||||
if (!socket_) {
|
||||
SocketOptionsMap::const_iterator it = socket_options_.find(opt);
|
||||
if (it == socket_options_.end()) {
|
||||
return -1;
|
||||
}
|
||||
*value = it->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return socket_->GetOption(opt, value);
|
||||
}
|
||||
|
||||
int ReflectorPort::GetError() {
|
||||
return error_;
|
||||
}
|
||||
|
||||
int ReflectorPort::SendTo(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) {
|
||||
rtc::CopyOnWriteBuffer targetPeerTag;
|
||||
|
||||
auto syntheticHostname = addr.hostname();
|
||||
|
||||
uint32_t resolvedPeerTag = 0;
|
||||
auto resolvedPeerTagIt = resolved_peer_tags_by_hostname_.find(syntheticHostname);
|
||||
if (resolvedPeerTagIt != resolved_peer_tags_by_hostname_.end()) {
|
||||
resolvedPeerTag = resolvedPeerTagIt->second;
|
||||
} else {
|
||||
const auto prefixFormat = "reflector-" + std::to_string((uint32_t)serverId_) + "-";
|
||||
std::string suffixFormat = ".reflector";
|
||||
if (!absl::StartsWith(syntheticHostname, prefixFormat) || !absl::EndsWith(syntheticHostname, suffixFormat)) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Discarding SendTo request with destination "
|
||||
<< addr.ToString();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto startPosition = prefixFormat.size();
|
||||
auto tagString = syntheticHostname.substr(startPosition, syntheticHostname.size() - suffixFormat.size() - startPosition);
|
||||
|
||||
std::stringstream tagStringStream(tagString);
|
||||
tagStringStream >> resolvedPeerTag;
|
||||
|
||||
if (resolvedPeerTag == 0) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Discarding SendTo request with destination "
|
||||
<< addr.ToString() << " (could not parse peer tag)";
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
resolved_peer_tags_by_hostname_.insert(std::make_pair(syntheticHostname, resolvedPeerTag));
|
||||
}
|
||||
|
||||
targetPeerTag.AppendData(peer_tag_.data(), peer_tag_.size() - 4);
|
||||
targetPeerTag.AppendData((uint8_t *)&resolvedPeerTag, 4);
|
||||
|
||||
rtc::ByteBufferWriter bufferWriter;
|
||||
bufferWriter.WriteBytes((const uint8_t *)targetPeerTag.data(), targetPeerTag.size());
|
||||
|
||||
bufferWriter.WriteBytes((const uint8_t *)&randomTag_, 4);
|
||||
|
||||
bufferWriter.WriteUInt32((uint32_t)size);
|
||||
bufferWriter.WriteBytes((const uint8_t *)data, size);
|
||||
|
||||
while (bufferWriter.Length() % 4 != 0) {
|
||||
bufferWriter.WriteUInt8(0);
|
||||
}
|
||||
|
||||
rtc::PacketOptions modified_options(options);
|
||||
CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent);
|
||||
|
||||
modified_options.info_signaled_after_sent.turn_overhead_bytes = bufferWriter.Length() - size;
|
||||
|
||||
Send(bufferWriter.Data(), bufferWriter.Length(), modified_options);
|
||||
|
||||
return static_cast<int>(size);
|
||||
}
|
||||
|
||||
bool ReflectorPort::CanHandleIncomingPacketsFrom(
|
||||
const rtc::SocketAddress& addr) const {
|
||||
return server_address_.address == addr;
|
||||
}
|
||||
|
||||
bool ReflectorPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket, rtc::ReceivedPacket const &packet) {
|
||||
if (socket != socket_) {
|
||||
// The packet was received on a shared socket after we've allocated a new
|
||||
// socket for this REFLECTOR port.
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t const *data = packet.payload().begin();
|
||||
size_t size = packet.payload().size();
|
||||
rtc::SocketAddress const &remote_addr = packet.source_address();
|
||||
auto packet_time_us = packet.arrival_time();
|
||||
|
||||
// This is to guard against a STUN response from previous server after
|
||||
// alternative server redirection. TODO(guoweis): add a unit test for this
|
||||
// race condition.
|
||||
if (remote_addr != server_address_.address) {
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": Discarding REFLECTOR message from unknown address: "
|
||||
<< remote_addr.ToSensitiveString()
|
||||
<< " server_address_: "
|
||||
<< server_address_.address.ToSensitiveString();
|
||||
return false;
|
||||
}
|
||||
|
||||
// The message must be at least 16 bytes (peer tag).
|
||||
if (size < 16) {
|
||||
RTC_LOG(LS_WARNING) << ToString()
|
||||
<< ": Received REFLECTOR message that was too short (" << size << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state_ == STATE_DISCONNECTED) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< ToString()
|
||||
<< ": Received REFLECTOR message while the REFLECTOR port is disconnected";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t receivedPeerTag[16];
|
||||
memcpy(receivedPeerTag, data, 16);
|
||||
|
||||
if (memcmp(receivedPeerTag, peer_tag_.data(), 16 - 4) != 0) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< ToString()
|
||||
<< ": Received REFLECTOR message with incorrect peer_tag";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state_ != STATE_READY) {
|
||||
state_ = STATE_READY;
|
||||
|
||||
RTC_LOG(LS_INFO)
|
||||
<< ToString()
|
||||
<< ": REFLECTOR " << server_address_.address.ToString() << " is now ready";
|
||||
|
||||
const auto ipFormat = "reflector-" + std::to_string((uint32_t)serverId_) + "-" + std::to_string(randomTag_) + ".reflector";
|
||||
rtc::SocketAddress candidateAddress(ipFormat, server_address_.address.port());
|
||||
if (standaloneReflectorMode_) {
|
||||
candidateAddress.SetResolvedIP(server_address_.address.ipaddr());
|
||||
}
|
||||
|
||||
// For relayed candidate, Base is the candidate itself.
|
||||
AddAddress(candidateAddress, // Candidate address.
|
||||
server_address_.address, // Base address.
|
||||
rtc::SocketAddress(), // Related address.
|
||||
cricket::UDP_PROTOCOL_NAME,
|
||||
ProtoToString(server_address_.proto), // The first hop protocol.
|
||||
"", // TCP canddiate type, empty for turn candidates.
|
||||
cricket::RELAY_PORT_TYPE, GetRelayPreference(server_address_.proto),
|
||||
server_priority_, ReconstructedServerUrl(false /* use_hostname */),
|
||||
true);
|
||||
}
|
||||
|
||||
if (size > 16 + 4 + 4) {
|
||||
bool isSpecialPacket = false;
|
||||
if (size >= 16 + 12) {
|
||||
uint8_t specialTag[12];
|
||||
memcpy(specialTag, data + 16, 12);
|
||||
|
||||
uint8_t expectedSpecialTag[12];
|
||||
memset(expectedSpecialTag, 0xff, 12);
|
||||
|
||||
if (memcmp(specialTag, expectedSpecialTag, 12) == 0) {
|
||||
isSpecialPacket = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSpecialPacket) {
|
||||
uint32_t senderTag = 0;
|
||||
memcpy(&senderTag, data + 16, 4);
|
||||
|
||||
uint32_t dataSize = 0;
|
||||
memcpy(&dataSize, data + 16 + 4, 4);
|
||||
dataSize = be32toh(dataSize);
|
||||
if (dataSize > size - 16 - 4 - 4) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< ToString()
|
||||
<< ": Received data packet with invalid size tag";
|
||||
} else {
|
||||
const auto ipFormat = "reflector-" + std::to_string((uint32_t)serverId_) + "-" + std::to_string(senderTag) + ".reflector";
|
||||
rtc::SocketAddress candidateAddress(ipFormat, server_address_.address.port());
|
||||
candidateAddress.SetResolvedIP(server_address_.address.ipaddr());
|
||||
|
||||
int64_t packet_timestamp = -1;
|
||||
if (packet_time_us.has_value()) {
|
||||
packet_timestamp = packet_time_us->us_or(-1);
|
||||
}
|
||||
DispatchPacket(rtc::ReceivedPacket::CreateFromLegacy(data + 16 + 4 + 4, dataSize, packet_timestamp, candidateAddress), cricket::ProtocolType::PROTO_UDP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReflectorPort::OnReadPacket(rtc::AsyncPacketSocket* socket, rtc::ReceivedPacket const &packet) {
|
||||
HandleIncomingPacket(socket, packet);
|
||||
}
|
||||
|
||||
void ReflectorPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SentPacket& sent_packet) {
|
||||
PortInterface::SignalSentPacket(sent_packet);
|
||||
}
|
||||
|
||||
void ReflectorPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
|
||||
if (ready()) {
|
||||
Port::OnReadyToSend();
|
||||
}
|
||||
}
|
||||
|
||||
bool ReflectorPort::SupportsProtocol(absl::string_view protocol) const {
|
||||
// Turn port only connects to UDP candidates.
|
||||
return protocol == cricket::UDP_PROTOCOL_NAME;
|
||||
}
|
||||
|
||||
void ReflectorPort::ResolveTurnAddress(const rtc::SocketAddress& address) {
|
||||
if (resolver_)
|
||||
return;
|
||||
|
||||
RTC_LOG(LS_INFO) << ToString() << ": Starting TURN host lookup for "
|
||||
<< address.ToSensitiveString();
|
||||
resolver_ = socket_factory()->CreateAsyncDnsResolver();
|
||||
resolver_->Start(address, [this] {
|
||||
// If DNS resolve is failed when trying to connect to the server using TCP,
|
||||
// one of the reason could be due to DNS queries blocked by firewall.
|
||||
// In such cases we will try to connect to the server with hostname,
|
||||
// assuming socket layer will resolve the hostname through a HTTP proxy (if
|
||||
// any).
|
||||
auto& result = resolver_->result();
|
||||
if (result.GetError() != 0 && (server_address_.proto == cricket::PROTO_TCP ||
|
||||
server_address_.proto == cricket::PROTO_TLS)) {
|
||||
if (!CreateReflectorClientSocket()) {
|
||||
OnAllocateError(cricket::SERVER_NOT_REACHABLE_ERROR,
|
||||
"TURN host lookup received error.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the original server address in `resolved_address`. For TLS based
|
||||
// sockets we need hostname along with resolved address.
|
||||
rtc::SocketAddress resolved_address = server_address_.address;
|
||||
if (result.GetError() != 0 ||
|
||||
!result.GetResolvedAddress(Network()->GetBestIP().family(),
|
||||
&resolved_address)) {
|
||||
RTC_LOG(LS_WARNING) << ToString() << ": TURN host lookup received error "
|
||||
<< result.GetError();
|
||||
error_ = result.GetError();
|
||||
OnAllocateError(cricket::SERVER_NOT_REACHABLE_ERROR,
|
||||
"TURN host lookup received error.");
|
||||
return;
|
||||
}
|
||||
// Signal needs both resolved and unresolved address. After signal is sent
|
||||
// we can copy resolved address back into `server_address_`.
|
||||
SignalResolvedServerAddress(this, server_address_.address,
|
||||
resolved_address);
|
||||
server_address_.address = resolved_address;
|
||||
PrepareAddress();
|
||||
});
|
||||
}
|
||||
|
||||
void ReflectorPort::OnSendStunPacket(const void* data,
|
||||
size_t size,
|
||||
cricket::StunRequest* request) {
|
||||
RTC_DCHECK(connected());
|
||||
rtc::PacketOptions options(StunDscpValue());
|
||||
options.info_signaled_after_sent.packet_type = rtc::PacketType::kTurnMessage;
|
||||
CopyPortInformationToPacketInfo(&options.info_signaled_after_sent);
|
||||
if (Send(data, size, options) < 0) {
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": Failed to send TURN message, error: "
|
||||
<< socket_->GetError();
|
||||
}
|
||||
}
|
||||
|
||||
void ReflectorPort::OnAllocateError(int error_code, const std::string& reason) {
|
||||
// We will send SignalPortError asynchronously as this can be sent during
|
||||
// port initialization. This way it will not be blocking other port
|
||||
// creation.
|
||||
thread()->PostTask(
|
||||
SafeTask(task_safety_.flag(), [this] { SignalPortError(this); }));
|
||||
std::string address = GetLocalAddress().HostAsSensitiveURIString();
|
||||
int port = GetLocalAddress().port();
|
||||
if (server_address_.proto == cricket::PROTO_TCP &&
|
||||
server_address_.address.IsPrivateIP()) {
|
||||
address.clear();
|
||||
port = 0;
|
||||
}
|
||||
SignalCandidateError(this, cricket::IceCandidateErrorEvent(address, port, ReconstructedServerUrl(true /* use_hostname */), error_code, reason));
|
||||
}
|
||||
|
||||
void ReflectorPort::Release() {
|
||||
state_ = STATE_RECEIVEONLY;
|
||||
}
|
||||
|
||||
void ReflectorPort::Close() {
|
||||
if (!ready()) {
|
||||
OnAllocateError(cricket::SERVER_NOT_REACHABLE_ERROR, "");
|
||||
}
|
||||
// Stop the port from creating new connections.
|
||||
state_ = STATE_DISCONNECTED;
|
||||
// Delete all existing connections; stop sending data.
|
||||
for (auto kv : connections()) {
|
||||
kv.second->Destroy();
|
||||
}
|
||||
|
||||
SignalReflectorPortClosed(this);
|
||||
}
|
||||
|
||||
rtc::DiffServCodePoint ReflectorPort::StunDscpValue() const {
|
||||
return stun_dscp_value_;
|
||||
}
|
||||
|
||||
// static
|
||||
bool ReflectorPort::AllowedReflectorPort(int port) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReflectorPort::DispatchPacket(rtc::ReceivedPacket const &packet, cricket::ProtocolType proto) {
|
||||
if (cricket::Connection* conn = GetConnection(packet.source_address())) {
|
||||
conn->OnReadPacket(packet);
|
||||
} else {
|
||||
Port::OnReadPacket(packet, proto);
|
||||
}
|
||||
}
|
||||
|
||||
int ReflectorPort::Send(const void* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options) {
|
||||
return socket_->SendTo(data, len, server_address_.address, options);
|
||||
}
|
||||
|
||||
void ReflectorPort::HandleConnectionDestroyed(cricket::Connection* conn) {
|
||||
}
|
||||
|
||||
std::string ReflectorPort::ReconstructedServerUrl(bool use_hostname) {
|
||||
// draft-petithuguenin-behave-turn-uris-01
|
||||
// turnURI = scheme ":" turn-host [ ":" turn-port ]
|
||||
// [ "?transport=" transport ]
|
||||
// scheme = "turn" / "turns"
|
||||
// transport = "udp" / "tcp" / transport-ext
|
||||
// transport-ext = 1*unreserved
|
||||
// turn-host = IP-literal / IPv4address / reg-name
|
||||
// turn-port = *DIGIT
|
||||
std::string scheme = "turn";
|
||||
std::string transport = "tcp";
|
||||
switch (server_address_.proto) {
|
||||
case cricket::PROTO_SSLTCP:
|
||||
case cricket::PROTO_TLS:
|
||||
scheme = "turns";
|
||||
break;
|
||||
case cricket::PROTO_UDP:
|
||||
transport = "udp";
|
||||
break;
|
||||
case cricket::PROTO_TCP:
|
||||
break;
|
||||
}
|
||||
rtc::StringBuilder url;
|
||||
url << scheme << ":"
|
||||
<< (use_hostname ? server_address_.address.hostname()
|
||||
: server_address_.address.ipaddr().ToString())
|
||||
<< ":" << server_address_.address.port() << "?transport=" << transport;
|
||||
return url.Release();
|
||||
}
|
||||
|
||||
} // namespace cricket
|
||||
250
TMessagesProj/jni/voip/tgcalls/v2/ReflectorPort.h
Normal file
250
TMessagesProj/jni/voip/tgcalls/v2/ReflectorPort.h
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
#ifndef TGCALLS_REFLECTOR_PORT_H_
|
||||
#define TGCALLS_REFLECTOR_PORT_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "api/async_dns_resolver.h"
|
||||
#include "p2p/base/port.h"
|
||||
#include "p2p/client/basic_port_allocator.h"
|
||||
#include "rtc_base/async_packet_socket.h"
|
||||
#include "rtc_base/ssl_certificate.h"
|
||||
|
||||
namespace webrtc {
|
||||
class TurnCustomizer;
|
||||
}
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
extern const int STUN_ATTR_TURN_LOGGING_ID;
|
||||
extern const char TURN_PORT_TYPE[];
|
||||
class TurnAllocateRequest;
|
||||
class TurnEntry;
|
||||
|
||||
class ReflectorPort : public cricket::Port {
|
||||
public:
|
||||
enum PortState {
|
||||
STATE_CONNECTING, // Initial state, cannot send any packets.
|
||||
STATE_CONNECTED, // Socket connected, ready to send stun requests.
|
||||
STATE_READY, // Received allocate success, can send any packets.
|
||||
STATE_RECEIVEONLY, // Had REFRESH_REQUEST error, cannot send any packets.
|
||||
STATE_DISCONNECTED, // TCP connection died, cannot send/receive any
|
||||
// packets.
|
||||
};
|
||||
|
||||
// Create a TURN port using the shared UDP socket, `socket`.
|
||||
static std::unique_ptr<ReflectorPort> Create(
|
||||
const cricket::CreateRelayPortArgs& args,
|
||||
rtc::SocketFactory *underlying_socket_factory,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
uint8_t serverId,
|
||||
int server_priority,
|
||||
bool standaloneReflectorMode,
|
||||
uint32_t standaloneReflectorRoleId
|
||||
) {
|
||||
// Do basic parameter validation.
|
||||
if (args.config->credentials.username.size() > 32) {
|
||||
RTC_LOG(LS_ERROR) << "Attempt to use REFLECTOR with a too long username "
|
||||
<< "of length " << args.config->credentials.username.size();
|
||||
return nullptr;
|
||||
}
|
||||
// Do not connect to low-numbered ports. The default STUN port is 3478.
|
||||
if (!AllowedReflectorPort(args.server_address->address.port())) {
|
||||
RTC_LOG(LS_ERROR) << "Attempt to use REFLECTOR to connect to port "
|
||||
<< args.server_address->address.port();
|
||||
return nullptr;
|
||||
}
|
||||
// Using `new` to access a non-public constructor.
|
||||
return absl::WrapUnique(new ReflectorPort(args, underlying_socket_factory, socket, serverId, server_priority, standaloneReflectorMode, standaloneReflectorRoleId));
|
||||
}
|
||||
|
||||
// Create a TURN port that will use a new socket, bound to `network` and
|
||||
// using a port in the range between `min_port` and `max_port`.
|
||||
static std::unique_ptr<ReflectorPort> Create(
|
||||
const cricket::CreateRelayPortArgs& args,
|
||||
rtc::SocketFactory *underlying_socket_factory,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
uint8_t serverId,
|
||||
int server_priority,
|
||||
bool standaloneReflectorMode,
|
||||
uint32_t standaloneReflectorRoleId
|
||||
) {
|
||||
// Do basic parameter validation.
|
||||
if (args.config->credentials.username.size() > 32) {
|
||||
RTC_LOG(LS_ERROR) << "Attempt to use TURN with a too long username "
|
||||
<< "of length " << args.config->credentials.username.size();
|
||||
return nullptr;
|
||||
}
|
||||
// Do not connect to low-numbered ports. The default STUN port is 3478.
|
||||
if (!AllowedReflectorPort(args.server_address->address.port())) {
|
||||
RTC_LOG(LS_ERROR) << "Attempt to use TURN to connect to port "
|
||||
<< args.server_address->address.port();
|
||||
return nullptr;
|
||||
}
|
||||
// Using `new` to access a non-public constructor.
|
||||
return absl::WrapUnique(new ReflectorPort(args, underlying_socket_factory, min_port, max_port, serverId, server_priority, standaloneReflectorMode, standaloneReflectorRoleId));
|
||||
}
|
||||
|
||||
~ReflectorPort() override;
|
||||
|
||||
const cricket::ProtocolAddress& server_address() const { return server_address_; }
|
||||
// Returns an empty address if the local address has not been assigned.
|
||||
rtc::SocketAddress GetLocalAddress() const;
|
||||
|
||||
bool ready() const { return state_ == STATE_READY; }
|
||||
bool connected() const {
|
||||
return state_ == STATE_READY || state_ == STATE_CONNECTED;
|
||||
}
|
||||
const cricket::RelayCredentials& credentials() const { return credentials_; }
|
||||
|
||||
cricket::ProtocolType GetProtocol() const override;
|
||||
|
||||
// Sets state to STATE_RECEIVEONLY.
|
||||
void Release();
|
||||
|
||||
void PrepareAddress() override;
|
||||
cricket::Connection* CreateConnection(const cricket::Candidate& c,
|
||||
PortInterface::CandidateOrigin origin) override;
|
||||
int SendTo(const void* data,
|
||||
size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) override;
|
||||
int SetOption(rtc::Socket::Option opt, int value) override;
|
||||
int GetOption(rtc::Socket::Option opt, int* value) override;
|
||||
int GetError() override;
|
||||
|
||||
virtual bool HandleIncomingPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::ReceivedPacket& packet) override;
|
||||
bool CanHandleIncomingPacketsFrom(
|
||||
const rtc::SocketAddress& addr) const override;
|
||||
virtual void OnReadPacket(rtc::AsyncPacketSocket* socket, rtc::ReceivedPacket const &packet);
|
||||
|
||||
void OnSentPacket(rtc::AsyncPacketSocket* socket,
|
||||
const rtc::SentPacket& sent_packet) override;
|
||||
virtual void OnReadyToSend(rtc::AsyncPacketSocket* socket);
|
||||
bool SupportsProtocol(absl::string_view protocol) const override;
|
||||
|
||||
void OnSocketConnect(rtc::AsyncPacketSocket* socket);
|
||||
void OnSocketClose(rtc::AsyncPacketSocket* socket, int error);
|
||||
|
||||
int error() const { return error_; }
|
||||
|
||||
rtc::AsyncPacketSocket* socket() const { return socket_; }
|
||||
|
||||
// Signal with resolved server address.
|
||||
// Parameters are port, server address and resolved server address.
|
||||
// This signal will be sent only if server address is resolved successfully.
|
||||
sigslot::
|
||||
signal3<ReflectorPort*, const rtc::SocketAddress&, const rtc::SocketAddress&>
|
||||
SignalResolvedServerAddress;
|
||||
|
||||
// Signal when ReflectorPort is closed,
|
||||
// e.g remote socket closed (TCP)
|
||||
// or receiveing a REFRESH response with lifetime 0.
|
||||
sigslot::signal1<ReflectorPort*> SignalReflectorPortClosed;
|
||||
|
||||
// All public methods/signals below are for testing only.
|
||||
sigslot::signal2<ReflectorPort*, int> SignalTurnRefreshResult;
|
||||
sigslot::signal3<ReflectorPort*, const rtc::SocketAddress&, int>
|
||||
SignalCreatePermissionResult;
|
||||
|
||||
// Visible for testing.
|
||||
// Shuts down the turn port, usually because of some fatal errors.
|
||||
void Close();
|
||||
|
||||
void HandleConnectionDestroyed(cricket::Connection* conn) override;
|
||||
|
||||
protected:
|
||||
ReflectorPort(const cricket::CreateRelayPortArgs& args,
|
||||
rtc::SocketFactory *underlying_socket_factory,
|
||||
rtc::AsyncPacketSocket* socket,
|
||||
uint8_t serverId,
|
||||
int server_priority,
|
||||
bool standaloneReflectorMode,
|
||||
uint32_t standaloneReflectorRoleId);
|
||||
|
||||
ReflectorPort(const cricket::CreateRelayPortArgs& args,
|
||||
rtc::SocketFactory *underlying_socket_factory,
|
||||
uint16_t min_port,
|
||||
uint16_t max_port,
|
||||
uint8_t serverId,
|
||||
int server_priority,
|
||||
bool standaloneReflectorMode,
|
||||
uint32_t standaloneReflectorRoleId);
|
||||
|
||||
rtc::DiffServCodePoint StunDscpValue() const override;
|
||||
|
||||
private:
|
||||
typedef std::map<rtc::Socket::Option, int> SocketOptionsMap;
|
||||
typedef std::set<rtc::SocketAddress> AttemptedServerSet;
|
||||
|
||||
static bool AllowedReflectorPort(int port);
|
||||
|
||||
bool CreateReflectorClientSocket();
|
||||
|
||||
void ResolveTurnAddress(const rtc::SocketAddress& address);
|
||||
void OnResolveResult(rtc::AsyncResolverInterface* resolver);
|
||||
|
||||
void OnSendStunPacket(const void* data, size_t size, cricket::StunRequest* request);
|
||||
|
||||
void OnAllocateError(int error_code, const std::string& reason);
|
||||
|
||||
void DispatchPacket(rtc::ReceivedPacket const &packet, cricket::ProtocolType proto);
|
||||
|
||||
int Send(const void* data, size_t size, const rtc::PacketOptions& options);
|
||||
|
||||
// Marks the connection with remote address `address` failed and
|
||||
// pruned (a.k.a. write-timed-out). Returns true if a connection is found.
|
||||
bool FailAndPruneConnection(const rtc::SocketAddress& address);
|
||||
|
||||
// Reconstruct the URL of the server which the candidate is gathered from.
|
||||
std::string ReconstructedServerUrl(bool use_hostname);
|
||||
|
||||
void SendReflectorHello();
|
||||
|
||||
rtc::CopyOnWriteBuffer peer_tag_;
|
||||
uint32_t randomTag_ = 0;
|
||||
|
||||
cricket::ProtocolAddress server_address_;
|
||||
uint8_t serverId_ = 0;
|
||||
|
||||
std::map<std::string, uint32_t> resolved_peer_tags_by_hostname_;
|
||||
|
||||
cricket::RelayCredentials credentials_;
|
||||
AttemptedServerSet attempted_server_addresses_;
|
||||
|
||||
rtc::AsyncPacketSocket* socket_;
|
||||
rtc::SocketFactory *underlying_socket_factory_;
|
||||
SocketOptionsMap socket_options_;
|
||||
std::unique_ptr<webrtc::AsyncDnsResolverInterface> resolver_;
|
||||
int error_;
|
||||
rtc::DiffServCodePoint stun_dscp_value_;
|
||||
|
||||
PortState state_;
|
||||
// By default the value will be set to 0. This value will be used in
|
||||
// calculating the candidate priority.
|
||||
int server_priority_;
|
||||
bool standaloneReflectorMode_ = false;
|
||||
uint32_t standaloneReflectorRoleId_ = 0;
|
||||
|
||||
// Optional TurnCustomizer that can modify outgoing messages. Once set, this
|
||||
// must outlive the ReflectorPort's lifetime.
|
||||
webrtc::TurnCustomizer* turn_customizer_ = nullptr;
|
||||
|
||||
webrtc::ScopedTaskSafety task_safety_;
|
||||
|
||||
bool is_running_ping_task_ = false;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif // TGCALLS_REFLECTOR_PORT_H_
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#include "v2/ReflectorRelayPortFactory.h"
|
||||
|
||||
#include "p2p/base/turn_port.h"
|
||||
|
||||
#include "v2/ReflectorPort.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
ReflectorRelayPortFactory::ReflectorRelayPortFactory(std::vector<RtcServer> servers, bool standaloneReflectorMode, uint32_t standaloneReflectorRoleId, rtc::SocketFactory *underlyingSocketFactory) :
|
||||
_servers(servers),
|
||||
_standaloneReflectorMode(standaloneReflectorMode),
|
||||
_standaloneReflectorRoleId(standaloneReflectorRoleId),
|
||||
_underlyingSocketFactory(underlyingSocketFactory) {
|
||||
}
|
||||
|
||||
ReflectorRelayPortFactory::~ReflectorRelayPortFactory() {
|
||||
}
|
||||
|
||||
std::unique_ptr<cricket::Port> ReflectorRelayPortFactory::Create(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* udp_socket) {
|
||||
if (args.config->credentials.username == "reflector") {
|
||||
uint8_t id = 0;
|
||||
for (const auto &server : _servers) {
|
||||
rtc::SocketAddress serverAddress(server.host, server.port);
|
||||
if (args.server_address->address == serverAddress) {
|
||||
id = server.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto port = ReflectorPort::Create(args, _underlyingSocketFactory, udp_socket, id, args.relative_priority, _standaloneReflectorMode, _standaloneReflectorRoleId);
|
||||
if (!port) {
|
||||
return nullptr;
|
||||
}
|
||||
return port;
|
||||
} else {
|
||||
auto port = cricket::TurnPort::Create(args, udp_socket);
|
||||
if (!port) {
|
||||
return nullptr;
|
||||
}
|
||||
port->SetTlsCertPolicy(args.config->tls_cert_policy);
|
||||
port->SetTurnLoggingId(args.config->turn_logging_id);
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<cricket::Port> ReflectorRelayPortFactory::Create(const cricket::CreateRelayPortArgs& args, int min_port, int max_port) {
|
||||
if (args.config->credentials.username == "reflector") {
|
||||
uint8_t id = 0;
|
||||
for (const auto &server : _servers) {
|
||||
rtc::SocketAddress serverAddress(server.host, server.port);
|
||||
if (args.server_address->address == serverAddress) {
|
||||
id = server.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto port = ReflectorPort::Create(args, _underlyingSocketFactory, min_port, max_port, id, args.relative_priority, _standaloneReflectorMode, _standaloneReflectorRoleId);
|
||||
if (!port) {
|
||||
return nullptr;
|
||||
}
|
||||
return port;
|
||||
} else {
|
||||
auto port = cricket::TurnPort::Create(args, min_port, max_port);
|
||||
if (!port) {
|
||||
return nullptr;
|
||||
}
|
||||
port->SetTlsCertPolicy(args.config->tls_cert_policy);
|
||||
port->SetTurnLoggingId(args.config->turn_logging_id);
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tgcalls
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef TGCALLS_REFLECTOR_RELAY_PORT_FACTORY_H
|
||||
#define TGCALLS_REFLECTOR_RELAY_PORT_FACTORY_H
|
||||
|
||||
#include "p2p/client/relay_port_factory_interface.h"
|
||||
|
||||
#include "Instance.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
class SocketFactory;
|
||||
|
||||
}
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class ReflectorRelayPortFactory : public cricket::RelayPortFactoryInterface {
|
||||
public:
|
||||
ReflectorRelayPortFactory(std::vector<RtcServer> servers, bool standaloneReflectorMode, uint32_t standaloneReflectorRoleId, rtc::SocketFactory *underlyingSocketFactory);
|
||||
~ReflectorRelayPortFactory() override;
|
||||
|
||||
// This variant is used for UDP connection to the relay server
|
||||
// using a already existing shared socket.
|
||||
virtual std::unique_ptr<cricket::Port> Create(const cricket::CreateRelayPortArgs& args, rtc::AsyncPacketSocket* udp_socket) override;
|
||||
|
||||
// This variant is used for the other cases.
|
||||
virtual std::unique_ptr<cricket::Port> Create(const cricket::CreateRelayPortArgs& args, int min_port, int max_port) override;
|
||||
|
||||
private:
|
||||
std::vector<RtcServer> _servers;
|
||||
bool _standaloneReflectorMode = false;
|
||||
uint32_t _standaloneReflectorRoleId = 0;
|
||||
rtc::SocketFactory *_underlyingSocketFactory = nullptr;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
820
TMessagesProj/jni/voip/tgcalls/v2/Signaling.cpp
Normal file
820
TMessagesProj/jni/voip/tgcalls/v2/Signaling.cpp
Normal file
|
|
@ -0,0 +1,820 @@
|
|||
#include "v2/Signaling.h"
|
||||
|
||||
#include "third-party/json11.hpp"
|
||||
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace tgcalls {
|
||||
namespace signaling {
|
||||
|
||||
static std::string uint32ToString(uint32_t value) {
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
static uint32_t stringToUInt32(std::string const &string) {
|
||||
std::stringstream stringStream(string);
|
||||
uint32_t value = 0;
|
||||
stringStream >> value;
|
||||
return value;
|
||||
}
|
||||
|
||||
json11::Json::object SsrcGroup_serialize(SsrcGroup const &ssrcGroup) {
|
||||
json11::Json::object object;
|
||||
|
||||
json11::Json::array ssrcs;
|
||||
for (auto ssrc : ssrcGroup.ssrcs) {
|
||||
ssrcs.push_back(json11::Json(uint32ToString(ssrc)));
|
||||
}
|
||||
object.insert(std::make_pair("semantics", json11::Json(ssrcGroup.semantics)));
|
||||
object.insert(std::make_pair("ssrcs", json11::Json(std::move(ssrcs))));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
absl::optional<SsrcGroup> SsrcGroup_parse(json11::Json::object const &object) {
|
||||
SsrcGroup result;
|
||||
|
||||
const auto semantics = object.find("semantics");
|
||||
if (semantics == object.end() || !semantics->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: semantics must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.semantics = semantics->second.string_value();
|
||||
|
||||
const auto ssrcs = object.find("ssrcs");
|
||||
if (ssrcs == object.end() || !ssrcs->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: ssrcs must be an array";
|
||||
return absl::nullopt;
|
||||
}
|
||||
for (const auto &ssrc : ssrcs->second.array_items()) {
|
||||
if (ssrc.is_string()) {
|
||||
uint32_t parsedSsrc = stringToUInt32(ssrc.string_value());
|
||||
if (parsedSsrc == 0) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: parsedSsrc must not be 0";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.ssrcs.push_back(parsedSsrc);
|
||||
} else if (ssrc.is_number()) {
|
||||
uint32_t parsedSsrc = (uint32_t)ssrc.number_value();
|
||||
result.ssrcs.push_back(parsedSsrc);
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: ssrcs item must be a string or a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
json11::Json::object FeedbackType_serialize(FeedbackType const &feedbackType) {
|
||||
json11::Json::object object;
|
||||
|
||||
object.insert(std::make_pair("type", json11::Json(feedbackType.type)));
|
||||
object.insert(std::make_pair("subtype", json11::Json(feedbackType.subtype)));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
absl::optional<FeedbackType> FeedbackType_parse(json11::Json::object const &object) {
|
||||
FeedbackType result;
|
||||
|
||||
const auto type = object.find("type");
|
||||
if (type == object.end() || !type->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: type must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.type = type->second.string_value();
|
||||
|
||||
const auto subtype = object.find("subtype");
|
||||
if (subtype == object.end() || !subtype->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: subtype must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.subtype = subtype->second.string_value();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
json11::Json::object RtpExtension_serialize(webrtc::RtpExtension const &rtpExtension) {
|
||||
json11::Json::object object;
|
||||
|
||||
object.insert(std::make_pair("id", json11::Json(rtpExtension.id)));
|
||||
object.insert(std::make_pair("uri", json11::Json(rtpExtension.uri)));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
absl::optional<webrtc::RtpExtension> RtpExtension_parse(json11::Json::object const &object) {
|
||||
const auto id = object.find("id");
|
||||
if (id == object.end() || !id->second.is_number()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: id must be a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
const auto uri = object.find("uri");
|
||||
if (uri == object.end() || !uri->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: uri must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
return webrtc::RtpExtension(uri->second.string_value(), id->second.int_value());
|
||||
}
|
||||
|
||||
json11::Json::object PayloadType_serialize(PayloadType const &payloadType) {
|
||||
json11::Json::object object;
|
||||
|
||||
object.insert(std::make_pair("id", json11::Json((int)payloadType.id)));
|
||||
object.insert(std::make_pair("name", json11::Json(payloadType.name)));
|
||||
object.insert(std::make_pair("clockrate", json11::Json((int)payloadType.clockrate)));
|
||||
object.insert(std::make_pair("channels", json11::Json((int)payloadType.channels)));
|
||||
|
||||
json11::Json::array feedbackTypes;
|
||||
for (const auto &feedbackType : payloadType.feedbackTypes) {
|
||||
feedbackTypes.push_back(FeedbackType_serialize(feedbackType));
|
||||
}
|
||||
object.insert(std::make_pair("feedbackTypes", json11::Json(std::move(feedbackTypes))));
|
||||
|
||||
json11::Json::object parameters;
|
||||
for (auto it : payloadType.parameters) {
|
||||
parameters.insert(std::make_pair(it.first, json11::Json(it.second)));
|
||||
}
|
||||
object.insert(std::make_pair("parameters", json11::Json(std::move(parameters))));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
absl::optional<PayloadType> PayloadType_parse(json11::Json::object const &object) {
|
||||
PayloadType result;
|
||||
|
||||
const auto id = object.find("id");
|
||||
if (id == object.end() || !id->second.is_number()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: id must be a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.id = id->second.int_value();
|
||||
|
||||
const auto name = object.find("name");
|
||||
if (name == object.end() || !name->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: name must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.name = name->second.string_value();
|
||||
|
||||
const auto clockrate = object.find("clockrate");
|
||||
if (clockrate == object.end() || !clockrate->second.is_number()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: clockrate must be a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.clockrate = clockrate->second.int_value();
|
||||
|
||||
const auto channels = object.find("channels");
|
||||
if (channels != object.end()) {
|
||||
if (!channels->second.is_number()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: channels must be a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.channels = channels->second.int_value();
|
||||
}
|
||||
|
||||
const auto feedbackTypes = object.find("feedbackTypes");
|
||||
if (feedbackTypes != object.end()) {
|
||||
if (!feedbackTypes->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: feedbackTypes must be an array";
|
||||
return absl::nullopt;
|
||||
}
|
||||
for (const auto &feedbackType : feedbackTypes->second.array_items()) {
|
||||
if (!feedbackType.is_object()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: feedbackTypes items must be objects";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (const auto parsedFeedbackType = FeedbackType_parse(feedbackType.object_items())) {
|
||||
result.feedbackTypes.push_back(parsedFeedbackType.value());
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse FeedbackType";
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto parameters = object.find("parameters");
|
||||
if (parameters != object.end()) {
|
||||
if (!parameters->second.is_object()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: parameters must be an object";
|
||||
return absl::nullopt;
|
||||
}
|
||||
for (const auto &item : parameters->second.object_items()) {
|
||||
if (!item.second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: parameters items must be strings";
|
||||
return absl::nullopt;
|
||||
}
|
||||
result.parameters.push_back(std::make_pair(item.first, item.second.string_value()));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
json11::Json::object MediaContent_serialize(MediaContent const &mediaContent) {
|
||||
json11::Json::object object;
|
||||
|
||||
std::string mappedType;
|
||||
switch (mediaContent.type) {
|
||||
case MediaContent::Type::Audio: {
|
||||
mappedType = "audio";
|
||||
break;
|
||||
}
|
||||
case MediaContent::Type::Video: {
|
||||
mappedType = "video";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_FATAL() << "Unknown media type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
object.insert(std::make_pair("type", mappedType));
|
||||
|
||||
object.insert(std::make_pair("ssrc", json11::Json(uint32ToString(mediaContent.ssrc))));
|
||||
|
||||
if (mediaContent.ssrcGroups.size() != 0) {
|
||||
json11::Json::array ssrcGroups;
|
||||
for (const auto &group : mediaContent.ssrcGroups) {
|
||||
ssrcGroups.push_back(SsrcGroup_serialize(group));
|
||||
}
|
||||
object.insert(std::make_pair("ssrcGroups", json11::Json(std::move(ssrcGroups))));
|
||||
}
|
||||
|
||||
if (mediaContent.payloadTypes.size() != 0) {
|
||||
json11::Json::array payloadTypes;
|
||||
for (const auto &payloadType : mediaContent.payloadTypes) {
|
||||
payloadTypes.push_back(PayloadType_serialize(payloadType));
|
||||
}
|
||||
object.insert(std::make_pair("payloadTypes", json11::Json(std::move(payloadTypes))));
|
||||
}
|
||||
|
||||
json11::Json::array rtpExtensions;
|
||||
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
|
||||
rtpExtensions.push_back(RtpExtension_serialize(rtpExtension));
|
||||
}
|
||||
object.insert(std::make_pair("rtpExtensions", json11::Json(std::move(rtpExtensions))));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
absl::optional<MediaContent> MediaContent_parse(json11::Json::object const &object) {
|
||||
MediaContent result;
|
||||
|
||||
const auto type = object.find("type");
|
||||
if (type == object.end() || !type->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: type must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (type->second.string_value() == "audio") {
|
||||
result.type = MediaContent::Type::Audio;
|
||||
} else if (type->second.string_value() == "video") {
|
||||
result.type = MediaContent::Type::Video;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: type must be one of [\"audio\", \"video\"]";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
const auto ssrc = object.find("ssrc");
|
||||
if (ssrc == object.end()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: ssrc must be present";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (ssrc->second.is_string()) {
|
||||
result.ssrc = stringToUInt32(ssrc->second.string_value());
|
||||
} else if (ssrc->second.is_number()) {
|
||||
result.ssrc = (uint32_t)ssrc->second.number_value();
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: ssrc must be a string or a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
const auto ssrcGroups = object.find("ssrcGroups");
|
||||
if (ssrcGroups != object.end()) {
|
||||
if (!ssrcGroups->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: ssrcGroups must be an array";
|
||||
return absl::nullopt;
|
||||
}
|
||||
for (const auto &ssrcGroup : ssrcGroups->second.array_items()) {
|
||||
if (!ssrcGroup.is_object()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: ssrcsGroups items must be objects";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (const auto parsedSsrcGroup = SsrcGroup_parse(ssrcGroup.object_items())) {
|
||||
result.ssrcGroups.push_back(parsedSsrcGroup.value());
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse SsrcGroup";
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto payloadTypes = object.find("payloadTypes");
|
||||
if (payloadTypes != object.end()) {
|
||||
if (!payloadTypes->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: payloadTypes must be an array";
|
||||
return absl::nullopt;
|
||||
}
|
||||
for (const auto &payloadType : payloadTypes->second.array_items()) {
|
||||
if (!payloadType.is_object()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: payloadTypes items must be objects";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (const auto parsedPayloadType = PayloadType_parse(payloadType.object_items())) {
|
||||
result.payloadTypes.push_back(parsedPayloadType.value());
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse PayloadType";
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto rtpExtensions = object.find("rtpExtensions");
|
||||
if (rtpExtensions != object.end()) {
|
||||
if (!rtpExtensions->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: rtpExtensions must be an array";
|
||||
return absl::nullopt;
|
||||
}
|
||||
for (const auto &rtpExtension : rtpExtensions->second.array_items()) {
|
||||
if (!rtpExtension.is_object()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: rtpExtensions items must be objects";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (const auto parsedRtpExtension = RtpExtension_parse(rtpExtension.object_items())) {
|
||||
result.rtpExtensions.push_back(parsedRtpExtension.value());
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse RtpExtension";
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> InitialSetupMessage_serialize(const InitialSetupMessage * const message) {
|
||||
json11::Json::object object;
|
||||
|
||||
object.insert(std::make_pair("@type", json11::Json("InitialSetup")));
|
||||
object.insert(std::make_pair("ufrag", json11::Json(message->ufrag)));
|
||||
object.insert(std::make_pair("pwd", json11::Json(message->pwd)));
|
||||
object.insert(std::make_pair("renomination", json11::Json(message->supportsRenomination)));
|
||||
|
||||
json11::Json::array jsonFingerprints;
|
||||
for (const auto &fingerprint : message->fingerprints) {
|
||||
json11::Json::object jsonFingerprint;
|
||||
jsonFingerprint.insert(std::make_pair("hash", json11::Json(fingerprint.hash)));
|
||||
jsonFingerprint.insert(std::make_pair("setup", json11::Json(fingerprint.setup)));
|
||||
jsonFingerprint.insert(std::make_pair("fingerprint", json11::Json(fingerprint.fingerprint)));
|
||||
jsonFingerprints.emplace_back(std::move(jsonFingerprint));
|
||||
}
|
||||
object.insert(std::make_pair("fingerprints", json11::Json(std::move(jsonFingerprints))));
|
||||
|
||||
auto json = json11::Json(std::move(object));
|
||||
std::string result = json.dump();
|
||||
return std::vector<uint8_t>(result.begin(), result.end());
|
||||
}
|
||||
|
||||
absl::optional<InitialSetupMessage> InitialSetupMessage_parse(json11::Json::object const &object) {
|
||||
const auto ufrag = object.find("ufrag");
|
||||
if (ufrag == object.end() || !ufrag->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: ufrag must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
const auto pwd = object.find("pwd");
|
||||
if (pwd == object.end() || !pwd->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: pwd must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
const auto renomination = object.find("renomination");
|
||||
bool renominationValue = false;
|
||||
if (renomination != object.end() && renomination->second.is_bool()) {
|
||||
renominationValue = renomination->second.bool_value();
|
||||
}
|
||||
const auto fingerprints = object.find("fingerprints");
|
||||
if (fingerprints == object.end() || !fingerprints->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: fingerprints must be an array";
|
||||
return absl::nullopt;
|
||||
}
|
||||
std::vector<DtlsFingerprint> parsedFingerprints;
|
||||
for (const auto &fingerprintObject : fingerprints->second.array_items()) {
|
||||
if (!fingerprintObject.is_object()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: fingerprints items must be objects";
|
||||
return absl::nullopt;
|
||||
}
|
||||
const auto hash = fingerprintObject.object_items().find("hash");
|
||||
if (hash == fingerprintObject.object_items().end() || !hash->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: hash must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
const auto setup = fingerprintObject.object_items().find("setup");
|
||||
if (setup == fingerprintObject.object_items().end() || !setup->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: setup must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
const auto fingerprint = fingerprintObject.object_items().find("fingerprint");
|
||||
if (fingerprint == fingerprintObject.object_items().end() || !fingerprint->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: fingerprint must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
DtlsFingerprint parsedFingerprint;
|
||||
parsedFingerprint.hash = hash->second.string_value();
|
||||
parsedFingerprint.setup = setup->second.string_value();
|
||||
parsedFingerprint.fingerprint = fingerprint->second.string_value();
|
||||
|
||||
parsedFingerprints.push_back(std::move(parsedFingerprint));
|
||||
}
|
||||
|
||||
InitialSetupMessage message;
|
||||
message.ufrag = ufrag->second.string_value();
|
||||
message.pwd = pwd->second.string_value();
|
||||
message.supportsRenomination = renominationValue;
|
||||
message.fingerprints = std::move(parsedFingerprints);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> NegotiateChannelsMessage_serialize(const NegotiateChannelsMessage * const message) {
|
||||
json11::Json::object object;
|
||||
|
||||
object.insert(std::make_pair("@type", json11::Json("NegotiateChannels")));
|
||||
|
||||
object.insert(std::make_pair("exchangeId", json11::Json(uint32ToString(message->exchangeId))));
|
||||
|
||||
json11::Json::array contents;
|
||||
for (const auto &content : message->contents) {
|
||||
contents.push_back(json11::Json(MediaContent_serialize(content)));
|
||||
}
|
||||
object.insert(std::make_pair("contents", std::move(contents)));
|
||||
|
||||
auto json = json11::Json(std::move(object));
|
||||
std::string result = json.dump();
|
||||
return std::vector<uint8_t>(result.begin(), result.end());
|
||||
}
|
||||
|
||||
absl::optional<NegotiateChannelsMessage> NegotiateChannelsMessage_parse(json11::Json::object const &object) {
|
||||
NegotiateChannelsMessage message;
|
||||
|
||||
const auto exchangeId = object.find("exchangeId");
|
||||
|
||||
if (exchangeId == object.end()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: exchangeId must be present";
|
||||
return absl::nullopt;
|
||||
} else if (exchangeId->second.is_string()) {
|
||||
message.exchangeId = stringToUInt32(exchangeId->second.string_value());
|
||||
} else if (exchangeId->second.is_number()) {
|
||||
message.exchangeId = (uint32_t)exchangeId->second.number_value();
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: exchangeId must be a string or a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
const auto contents = object.find("contents");
|
||||
if (contents != object.end()) {
|
||||
if (!contents->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: contents must be an array";
|
||||
return absl::nullopt;
|
||||
}
|
||||
for (const auto &content : contents->second.array_items()) {
|
||||
if (!content.is_object()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: contents items must be objects";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (auto parsedContent = MediaContent_parse(content.object_items())) {
|
||||
message.contents.push_back(std::move(parsedContent.value()));
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse MediaContent";
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
json11::Json::object ConnectionAddress_serialize(ConnectionAddress const &connectionAddress) {
|
||||
json11::Json::object object;
|
||||
|
||||
object.insert(std::make_pair("ip", json11::Json(connectionAddress.ip)));
|
||||
object.insert(std::make_pair("port", json11::Json(connectionAddress.port)));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
absl::optional<ConnectionAddress> ConnectionAddress_parse(json11::Json::object const &object) {
|
||||
const auto ip = object.find("ip");
|
||||
if (ip == object.end() || !ip->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: ip must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
const auto port = object.find("port");
|
||||
if (port == object.end() || !port->second.is_number()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: port must be a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
ConnectionAddress address;
|
||||
address.ip = ip->second.string_value();
|
||||
address.port = port->second.int_value();
|
||||
return address;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> CandidatesMessage_serialize(const CandidatesMessage * const message) {
|
||||
json11::Json::array candidates;
|
||||
for (const auto &candidate : message->iceCandidates) {
|
||||
json11::Json::object candidateObject;
|
||||
|
||||
candidateObject.insert(std::make_pair("sdpString", json11::Json(candidate.sdpString)));
|
||||
|
||||
candidates.emplace_back(std::move(candidateObject));
|
||||
}
|
||||
|
||||
json11::Json::object object;
|
||||
|
||||
object.insert(std::make_pair("@type", json11::Json("Candidates")));
|
||||
object.insert(std::make_pair("candidates", json11::Json(std::move(candidates))));
|
||||
|
||||
auto json = json11::Json(std::move(object));
|
||||
std::string result = json.dump();
|
||||
return std::vector<uint8_t>(result.begin(), result.end());
|
||||
}
|
||||
|
||||
absl::optional<CandidatesMessage> CandidatesMessage_parse(json11::Json::object const &object) {
|
||||
const auto candidates = object.find("candidates");
|
||||
if (candidates == object.end() || !candidates->second.is_array()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: candidates must be an array";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
std::vector<IceCandidate> parsedCandidates;
|
||||
for (const auto &candidateObject : candidates->second.array_items()) {
|
||||
if (!candidateObject.is_object()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: candidates items must be objects";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
IceCandidate candidate;
|
||||
|
||||
const auto sdpString = candidateObject.object_items().find("sdpString");
|
||||
if (sdpString == candidateObject.object_items().end() || !sdpString->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: sdpString must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
candidate.sdpString = sdpString->second.string_value();
|
||||
|
||||
parsedCandidates.push_back(std::move(candidate));
|
||||
}
|
||||
|
||||
CandidatesMessage message;
|
||||
message.iceCandidates = std::move(parsedCandidates);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> MediaStateMessage_serialize(const MediaStateMessage * const message) {
|
||||
json11::Json::object object;
|
||||
|
||||
object.insert(std::make_pair("@type", json11::Json("MediaState")));
|
||||
object.insert(std::make_pair("muted", json11::Json(message->isMuted)));
|
||||
object.insert(std::make_pair("lowBattery", json11::Json(message->isBatteryLow)));
|
||||
|
||||
std::string videoStateValue;
|
||||
switch (message->videoState) {
|
||||
case MediaStateMessage::VideoState::Inactive: {
|
||||
videoStateValue = "inactive";
|
||||
break;
|
||||
}
|
||||
case MediaStateMessage::VideoState::Suspended: {
|
||||
videoStateValue = "suspended";
|
||||
break;
|
||||
}
|
||||
case MediaStateMessage::VideoState::Active: {
|
||||
videoStateValue = "active";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_FATAL() << "Unknown videoState";
|
||||
break;
|
||||
}
|
||||
}
|
||||
object.insert(std::make_pair("videoState", json11::Json(videoStateValue)));
|
||||
|
||||
int videoRotationValue = 0;
|
||||
switch (message->videoRotation) {
|
||||
case MediaStateMessage::VideoRotation::Rotation0: {
|
||||
videoRotationValue = 0;
|
||||
break;
|
||||
}
|
||||
case MediaStateMessage::VideoRotation::Rotation90: {
|
||||
videoRotationValue = 90;
|
||||
break;
|
||||
}
|
||||
case MediaStateMessage::VideoRotation::Rotation180: {
|
||||
videoRotationValue = 180;
|
||||
break;
|
||||
}
|
||||
case MediaStateMessage::VideoRotation::Rotation270: {
|
||||
videoRotationValue = 270;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_FATAL() << "Unknown videoRotation";
|
||||
break;
|
||||
}
|
||||
}
|
||||
object.insert(std::make_pair("videoRotation", json11::Json(videoRotationValue)));
|
||||
|
||||
std::string screencastStateValue;
|
||||
switch (message->screencastState) {
|
||||
case MediaStateMessage::VideoState::Inactive: {
|
||||
screencastStateValue = "inactive";
|
||||
break;
|
||||
}
|
||||
case MediaStateMessage::VideoState::Suspended: {
|
||||
screencastStateValue = "suspended";
|
||||
break;
|
||||
}
|
||||
case MediaStateMessage::VideoState::Active: {
|
||||
screencastStateValue = "active";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RTC_FATAL() << "Unknown videoState";
|
||||
break;
|
||||
}
|
||||
}
|
||||
object.insert(std::make_pair("screencastState", json11::Json(screencastStateValue)));
|
||||
|
||||
auto json = json11::Json(std::move(object));
|
||||
std::string result = json.dump();
|
||||
return std::vector<uint8_t>(result.begin(), result.end());
|
||||
}
|
||||
|
||||
absl::optional<MediaStateMessage> MediaStateMessage_parse(json11::Json::object const &object) {
|
||||
MediaStateMessage message;
|
||||
|
||||
const auto muted = object.find("muted");
|
||||
if (muted != object.end()) {
|
||||
if (!muted->second.is_bool()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: muted must be a bool";
|
||||
return absl::nullopt;
|
||||
}
|
||||
message.isMuted = muted->second.bool_value();
|
||||
}
|
||||
|
||||
const auto lowBattery = object.find("lowBattery");
|
||||
if (lowBattery != object.end()) {
|
||||
if (!lowBattery->second.is_bool()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: lowBattery must be a bool";
|
||||
return absl::nullopt;
|
||||
}
|
||||
message.isBatteryLow = lowBattery->second.bool_value();
|
||||
}
|
||||
|
||||
const auto videoState = object.find("videoState");
|
||||
if (videoState != object.end()) {
|
||||
if (!videoState->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: videoState must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (videoState->second.string_value() == "inactive") {
|
||||
message.videoState = MediaStateMessage::VideoState::Inactive;
|
||||
} else if (videoState->second.string_value() == "suspended") {
|
||||
message.videoState = MediaStateMessage::VideoState::Suspended;
|
||||
} else if (videoState->second.string_value() == "active") {
|
||||
message.videoState = MediaStateMessage::VideoState::Active;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "videoState must be one of [\"inactive\", \"suspended\", \"active\"]";
|
||||
}
|
||||
} else {
|
||||
message.videoState = MediaStateMessage::VideoState::Inactive;
|
||||
}
|
||||
|
||||
const auto screencastState = object.find("screencastState");
|
||||
if (screencastState != object.end()) {
|
||||
if (!screencastState->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: screencastState must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (screencastState->second.string_value() == "inactive") {
|
||||
message.screencastState = MediaStateMessage::VideoState::Inactive;
|
||||
} else if (screencastState->second.string_value() == "suspended") {
|
||||
message.screencastState = MediaStateMessage::VideoState::Suspended;
|
||||
} else if (screencastState->second.string_value() == "active") {
|
||||
message.screencastState = MediaStateMessage::VideoState::Active;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: screencastState must be one of [\"inactive\", \"suspended\", \"active\"]";
|
||||
}
|
||||
} else {
|
||||
message.screencastState = MediaStateMessage::VideoState::Inactive;
|
||||
}
|
||||
|
||||
const auto videoRotation = object.find("videoRotation");
|
||||
if (videoRotation != object.end()) {
|
||||
if (!videoRotation->second.is_number()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: videoRotation must be a number";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (videoState->second.int_value() == 0) {
|
||||
message.videoRotation = MediaStateMessage::VideoRotation::Rotation0;
|
||||
} else if (videoState->second.int_value() == 90) {
|
||||
message.videoRotation = MediaStateMessage::VideoRotation::Rotation90;
|
||||
} else if (videoState->second.int_value() == 180) {
|
||||
message.videoRotation = MediaStateMessage::VideoRotation::Rotation180;
|
||||
} else if (videoState->second.int_value() == 270) {
|
||||
message.videoRotation = MediaStateMessage::VideoRotation::Rotation270;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: videoRotation must be one of [0, 90, 180, 270]";
|
||||
message.videoRotation = MediaStateMessage::VideoRotation::Rotation0;
|
||||
}
|
||||
} else {
|
||||
message.videoRotation = MediaStateMessage::VideoRotation::Rotation0;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Message::serialize() const {
|
||||
if (const auto initialSetup = absl::get_if<InitialSetupMessage>(&data)) {
|
||||
return InitialSetupMessage_serialize(initialSetup);
|
||||
} else if (const auto candidates = absl::get_if<CandidatesMessage>(&data)) {
|
||||
return CandidatesMessage_serialize(candidates);
|
||||
} else if (const auto mediaState = absl::get_if<MediaStateMessage>(&data)) {
|
||||
return MediaStateMessage_serialize(mediaState);
|
||||
} else if (const auto negotiateChannels = absl::get_if<NegotiateChannelsMessage>(&data)) {
|
||||
return NegotiateChannelsMessage_serialize(negotiateChannels);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
absl::optional<Message> Message::parse(const std::vector<uint8_t> &data) {
|
||||
std::string parsingError;
|
||||
auto json = json11::Json::parse(std::string(data.begin(), data.end()), parsingError);
|
||||
if (json.type() != json11::Json::OBJECT) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: message must be an object";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
auto type = json.object_items().find("@type");
|
||||
if (type == json.object_items().end()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: message does not contain @type attribute";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (!type->second.is_string()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: @type attribute must be a string";
|
||||
return absl::nullopt;
|
||||
}
|
||||
if (type->second.string_value() == "InitialSetup") {
|
||||
auto parsed = InitialSetupMessage_parse(json.object_items());
|
||||
if (!parsed) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse " << type->second.string_value() << " message";
|
||||
return absl::nullopt;
|
||||
}
|
||||
Message message;
|
||||
message.data = std::move(parsed.value());
|
||||
return message;
|
||||
} else if (type->second.string_value() == "NegotiateChannels") {
|
||||
auto parsed = NegotiateChannelsMessage_parse(json.object_items());
|
||||
if (!parsed) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse " << type->second.string_value() << " message";
|
||||
return absl::nullopt;
|
||||
}
|
||||
Message message;
|
||||
message.data = std::move(parsed.value());
|
||||
return message;
|
||||
} else if (type->second.string_value() == "Candidates") {
|
||||
auto parsed = CandidatesMessage_parse(json.object_items());
|
||||
if (!parsed) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse " << type->second.string_value() << " message";
|
||||
return absl::nullopt;
|
||||
}
|
||||
Message message;
|
||||
message.data = std::move(parsed.value());
|
||||
return message;
|
||||
} else if (type->second.string_value() == "MediaState") {
|
||||
auto parsed = MediaStateMessage_parse(json.object_items());
|
||||
if (!parsed) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: could not parse " << type->second.string_value() << " message";
|
||||
return absl::nullopt;
|
||||
}
|
||||
Message message;
|
||||
message.data = std::move(parsed.value());
|
||||
return message;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: unknown message type " << type->second.string_value();
|
||||
return absl::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace signaling
|
||||
|
||||
} // namespace tgcalls
|
||||
191
TMessagesProj/jni/voip/tgcalls/v2/Signaling.h
Normal file
191
TMessagesProj/jni/voip/tgcalls/v2/Signaling.h
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#ifndef TGCALLS_SIGNALING_H
|
||||
#define TGCALLS_SIGNALING_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/variant.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
namespace signaling {
|
||||
|
||||
struct DtlsFingerprint {
|
||||
std::string hash;
|
||||
std::string setup;
|
||||
std::string fingerprint;
|
||||
};
|
||||
|
||||
struct ConnectionAddress {
|
||||
std::string ip;
|
||||
int port = 0;
|
||||
};
|
||||
|
||||
struct IceCandidate {
|
||||
std::string sdpString;
|
||||
};
|
||||
|
||||
struct SsrcGroup {
|
||||
std::vector<uint32_t> ssrcs;
|
||||
std::string semantics;
|
||||
|
||||
bool operator==(SsrcGroup const &rhs) const {
|
||||
if (ssrcs != rhs.ssrcs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (semantics != rhs.semantics) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct FeedbackType {
|
||||
std::string type;
|
||||
std::string subtype;
|
||||
|
||||
bool operator==(FeedbackType const &rhs) const {
|
||||
if (type != rhs.type) {
|
||||
return false;
|
||||
}
|
||||
if (subtype != rhs.subtype) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct PayloadType {
|
||||
uint32_t id = 0;
|
||||
std::string name;
|
||||
uint32_t clockrate = 0;
|
||||
uint32_t channels = 0;
|
||||
std::vector<FeedbackType> feedbackTypes;
|
||||
std::vector<std::pair<std::string, std::string>> parameters;
|
||||
|
||||
bool operator==(PayloadType const &rhs) const {
|
||||
if (id != rhs.id) {
|
||||
return false;
|
||||
}
|
||||
if (name != rhs.name) {
|
||||
return false;
|
||||
}
|
||||
if (clockrate != rhs.clockrate) {
|
||||
return false;
|
||||
}
|
||||
if (channels != rhs.channels) {
|
||||
return false;
|
||||
}
|
||||
if (feedbackTypes != rhs.feedbackTypes) {
|
||||
return false;
|
||||
}
|
||||
if (parameters != rhs.parameters) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct MediaContent {
|
||||
enum class Type {
|
||||
Audio,
|
||||
Video
|
||||
};
|
||||
|
||||
Type type = Type::Audio;
|
||||
uint32_t ssrc = 0;
|
||||
std::vector<SsrcGroup> ssrcGroups;
|
||||
std::vector<PayloadType> payloadTypes;
|
||||
std::vector<webrtc::RtpExtension> rtpExtensions;
|
||||
|
||||
bool operator==(const MediaContent& rhs) const {
|
||||
if (type != rhs.type) {
|
||||
return false;
|
||||
}
|
||||
if (ssrc != rhs.ssrc) {
|
||||
return false;
|
||||
}
|
||||
if (ssrcGroups != rhs.ssrcGroups) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<PayloadType> sortedPayloadTypes = payloadTypes;
|
||||
std::sort(sortedPayloadTypes.begin(), sortedPayloadTypes.end(), [](PayloadType const &lhs, PayloadType const &rhs) {
|
||||
return lhs.id < rhs.id;
|
||||
});
|
||||
std::vector<PayloadType> sortedRhsPayloadTypes = rhs.payloadTypes;
|
||||
std::sort(sortedRhsPayloadTypes.begin(), sortedRhsPayloadTypes.end(), [](PayloadType const &lhs, PayloadType const &rhs) {
|
||||
return lhs.id < rhs.id;
|
||||
});
|
||||
if (sortedPayloadTypes != sortedRhsPayloadTypes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rtpExtensions != rhs.rtpExtensions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct InitialSetupMessage {
|
||||
std::string ufrag;
|
||||
std::string pwd;
|
||||
bool supportsRenomination = false;
|
||||
std::vector<DtlsFingerprint> fingerprints;
|
||||
};
|
||||
|
||||
struct NegotiateChannelsMessage {
|
||||
uint32_t exchangeId = 0;
|
||||
std::vector<MediaContent> contents;
|
||||
};
|
||||
|
||||
struct CandidatesMessage {
|
||||
std::vector<IceCandidate> iceCandidates;
|
||||
};
|
||||
|
||||
struct MediaStateMessage {
|
||||
enum class VideoState {
|
||||
Inactive,
|
||||
Suspended,
|
||||
Active
|
||||
};
|
||||
|
||||
enum class VideoRotation {
|
||||
Rotation0,
|
||||
Rotation90,
|
||||
Rotation180,
|
||||
Rotation270
|
||||
};
|
||||
|
||||
bool isMuted = false;
|
||||
VideoState videoState = VideoState::Inactive;
|
||||
VideoRotation videoRotation = VideoRotation::Rotation0;
|
||||
VideoState screencastState = VideoState::Inactive;
|
||||
bool isBatteryLow = false;
|
||||
|
||||
};
|
||||
|
||||
struct Message {
|
||||
absl::variant<
|
||||
InitialSetupMessage,
|
||||
NegotiateChannelsMessage,
|
||||
CandidatesMessage,
|
||||
MediaStateMessage> data;
|
||||
|
||||
std::vector<uint8_t> serialize() const;
|
||||
static absl::optional<Message> parse(const std::vector<uint8_t> &data);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#include "v2/SignalingConnection.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
SignalingConnection::SignalingConnection() {
|
||||
}
|
||||
|
||||
}
|
||||
26
TMessagesProj/jni/voip/tgcalls/v2/SignalingConnection.h
Normal file
26
TMessagesProj/jni/voip/tgcalls/v2/SignalingConnection.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef TGCALLS_SIGNALING_CONNECTION_H_
|
||||
#define TGCALLS_SIGNALING_CONNECTION_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace webrtc {
|
||||
}
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class SignalingConnection {
|
||||
public:
|
||||
SignalingConnection();
|
||||
virtual ~SignalingConnection() = default;
|
||||
|
||||
virtual void start() = 0;
|
||||
|
||||
virtual void send(const std::vector<uint8_t> &data) = 0;
|
||||
virtual void receiveExternal(const std::vector<uint8_t> &data) {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif // TGCALLS_SIGNALING_CONNECTION_H_
|
||||
22
TMessagesProj/jni/voip/tgcalls/v2/SignalingEncryption.cpp
Normal file
22
TMessagesProj/jni/voip/tgcalls/v2/SignalingEncryption.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include "v2/SignalingEncryption.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
SignalingEncryption::SignalingEncryption(EncryptionKey const &encryptionKey) {
|
||||
_connection.reset(new EncryptedConnection(EncryptedConnection::Type::Signaling, encryptionKey, [](int, int) {
|
||||
}));
|
||||
}
|
||||
|
||||
SignalingEncryption::~SignalingEncryption() {
|
||||
|
||||
}
|
||||
|
||||
absl::optional<rtc::CopyOnWriteBuffer> SignalingEncryption::encryptOutgoing(std::vector<uint8_t> const &data) {
|
||||
return _connection->encryptRawPacket(rtc::CopyOnWriteBuffer(data.data(), data.size()));
|
||||
}
|
||||
|
||||
absl::optional<rtc::CopyOnWriteBuffer> SignalingEncryption::decryptIncoming(std::vector<uint8_t> const &data) {
|
||||
return _connection->decryptRawPacket(rtc::CopyOnWriteBuffer(data.data(), data.size()));
|
||||
}
|
||||
|
||||
} // namespace tgcalls
|
||||
23
TMessagesProj/jni/voip/tgcalls/v2/SignalingEncryption.h
Normal file
23
TMessagesProj/jni/voip/tgcalls/v2/SignalingEncryption.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef TGCALLS_SIGNALING_ENCRYPTION_H
|
||||
#define TGCALLS_SIGNALING_ENCRYPTION_H
|
||||
|
||||
#include "Instance.h"
|
||||
#include "EncryptedConnection.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class SignalingEncryption {
|
||||
public:
|
||||
SignalingEncryption(EncryptionKey const &encryptionKey);
|
||||
~SignalingEncryption();
|
||||
|
||||
absl::optional<rtc::CopyOnWriteBuffer> encryptOutgoing(std::vector<uint8_t> const &data);
|
||||
absl::optional<rtc::CopyOnWriteBuffer> decryptIncoming(std::vector<uint8_t> const &data);
|
||||
|
||||
private:
|
||||
std::unique_ptr<EncryptedConnection> _connection;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif
|
||||
126
TMessagesProj/jni/voip/tgcalls/v2/SignalingKcpConnection.cpp
Normal file
126
TMessagesProj/jni/voip/tgcalls/v2/SignalingKcpConnection.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#include "v2/SignalingKcpConnection.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "rtc_base/async_tcp_socket.h"
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
|
||||
#include "FieldTrialsConfig.h"
|
||||
|
||||
/* get system time */
|
||||
static inline void itimeofday(long *sec, long *usec) {
|
||||
#ifdef _MSC_VER
|
||||
SYSTEMTIME system_time;
|
||||
FILETIME file_time;
|
||||
uint64_t time;
|
||||
|
||||
GetSystemTime( &system_time );
|
||||
SystemTimeToFileTime( &system_time, &file_time );
|
||||
time = ((uint64_t)file_time.dwLowDateTime ) ;
|
||||
time += ((uint64_t)file_time.dwHighDateTime) << 32;
|
||||
|
||||
// Windows FILETIME epoch (1601-01-01) to Unix epoch (1970-01-01) in 100-nanosecond intervals
|
||||
constexpr auto EPOCH = 11644473600000000ULL;
|
||||
|
||||
if (sec) *sec = (long) ((time - EPOCH) / 10000000L);
|
||||
if (usec) *usec = (long) (system_time.wMilliseconds * 1000);
|
||||
#else
|
||||
struct timeval time;
|
||||
gettimeofday(&time, NULL);
|
||||
if (sec) *sec = time.tv_sec;
|
||||
if (usec) *usec = time.tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* get clock in millisecond 64 */
|
||||
static inline IINT64 iclock64(void) {
|
||||
long s, u;
|
||||
IINT64 value;
|
||||
itimeofday(&s, &u);
|
||||
value = ((IINT64)s) * 1000 + (u / 1000);
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline IUINT32 iclock() {
|
||||
return (IUINT32)(iclock64() & 0xfffffffful);
|
||||
}
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
SignalingKcpConnection::SignalingKcpConnection(std::shared_ptr<Threads> threads, std::function<void(const std::vector<uint8_t> &)> onIncomingData, std::function<void(const std::vector<uint8_t> &)> emitData) :
|
||||
_threads(threads),
|
||||
_emitData(emitData),
|
||||
_onIncomingData(onIncomingData) {
|
||||
_receiveBuffer.resize(512 * 1024);
|
||||
|
||||
_kcp = ikcp_create(0, this);
|
||||
_kcp->output = &SignalingKcpConnection::udpOutput;
|
||||
|
||||
ikcp_wndsize(_kcp, 128, 128);
|
||||
ikcp_nodelay(_kcp, 0, 10, 0, 0);
|
||||
|
||||
//_onIncomingData
|
||||
}
|
||||
|
||||
int SignalingKcpConnection::udpOutput(const char *buf, int len, ikcpcb *kcp, void *user) {
|
||||
SignalingKcpConnection *connection = (SignalingKcpConnection *)user;
|
||||
if (connection) {
|
||||
connection->_emitData(std::vector<uint8_t>(buf, buf + len));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SignalingKcpConnection::~SignalingKcpConnection() {
|
||||
|
||||
}
|
||||
|
||||
void SignalingKcpConnection::start() {
|
||||
scheduleInternalUpdate(0);
|
||||
}
|
||||
|
||||
void SignalingKcpConnection::scheduleInternalUpdate(int timeoutMs) {
|
||||
const auto weak = std::weak_ptr<SignalingKcpConnection>(shared_from_this());
|
||||
_threads->getMediaThread()->PostDelayedTask([weak]() {
|
||||
auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
return;
|
||||
}
|
||||
|
||||
strong->performInternalUpdate();
|
||||
|
||||
strong->scheduleInternalUpdate(10);
|
||||
}, webrtc::TimeDelta::Millis(timeoutMs));
|
||||
}
|
||||
|
||||
void SignalingKcpConnection::performInternalUpdate() {
|
||||
ikcp_update(_kcp, iclock());
|
||||
|
||||
while (true) {
|
||||
int result = ikcp_recv(_kcp, (char *)_receiveBuffer.data(), (int)_receiveBuffer.size());
|
||||
if (result >= 0) {
|
||||
std::vector<uint8_t> packet;
|
||||
packet.resize(result);
|
||||
std::copy(_receiveBuffer.begin(), _receiveBuffer.begin() + result, packet.begin());
|
||||
_onIncomingData(packet);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SignalingKcpConnection::receiveExternal(const std::vector<uint8_t> &data) {
|
||||
ikcp_input(_kcp, (const char *)data.data(), (long)data.size());
|
||||
}
|
||||
|
||||
void SignalingKcpConnection::send(const std::vector<uint8_t> &data) {
|
||||
ikcp_send(_kcp, (const char *)data.data(), (int)data.size());
|
||||
}
|
||||
|
||||
}
|
||||
57
TMessagesProj/jni/voip/tgcalls/v2/SignalingKcpConnection.h
Normal file
57
TMessagesProj/jni/voip/tgcalls/v2/SignalingKcpConnection.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#ifndef TGCALLS_SIGNALING_KCP_CONNECTION_H_
|
||||
#define TGCALLS_SIGNALING_KCP_CONNECTION_H_
|
||||
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/byte_buffer.h"
|
||||
#include "media/base/media_channel.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <absl/types/optional.h>
|
||||
|
||||
#include "StaticThreads.h"
|
||||
#include "SignalingConnection.h"
|
||||
|
||||
#include "ikcp.h"
|
||||
|
||||
namespace rtc {
|
||||
class Socket;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
class SctpTransportFactory;
|
||||
class SctpTransportInternal;
|
||||
};
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class SignalingPacketTransport;
|
||||
|
||||
class SignalingKcpConnection : public sigslot::has_slots<>, public SignalingConnection, public std::enable_shared_from_this<SignalingKcpConnection> {
|
||||
public:
|
||||
SignalingKcpConnection(std::shared_ptr<Threads> threads, std::function<void(const std::vector<uint8_t> &)> onIncomingData, std::function<void(const std::vector<uint8_t> &)> emitData);
|
||||
virtual ~SignalingKcpConnection();
|
||||
|
||||
virtual void receiveExternal(const std::vector<uint8_t> &data) override;
|
||||
virtual void start() override;
|
||||
virtual void send(const std::vector<uint8_t> &data) override;
|
||||
|
||||
private:
|
||||
void scheduleInternalUpdate(int timeoutMs);
|
||||
void performInternalUpdate();
|
||||
static int udpOutput(const char *buf, int len, ikcpcb *kcp, void *user);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Threads> _threads;
|
||||
std::function<void(const std::vector<uint8_t> &)> _emitData;
|
||||
std::function<void(const std::vector<uint8_t> &)> _onIncomingData;
|
||||
|
||||
ikcpcb *_kcp = nullptr;
|
||||
std::vector<uint8_t> _receiveBuffer;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif // TGCALLS_SIGNALING_SCTP_CONNECTION_H_
|
||||
187
TMessagesProj/jni/voip/tgcalls/v2/SignalingSctpConnection.cpp
Normal file
187
TMessagesProj/jni/voip/tgcalls/v2/SignalingSctpConnection.cpp
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
#include "v2/SignalingSctpConnection.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
#include "rtc_base/async_tcp_socket.h"
|
||||
#include "p2p/base/basic_packet_socket_factory.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "p2p/base/packet_transport_internal.h"
|
||||
#include "media/sctp/sctp_transport_factory.h"
|
||||
|
||||
#include "FieldTrialsConfig.h"
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class SignalingPacketTransport : public rtc::PacketTransportInternal {
|
||||
public:
|
||||
SignalingPacketTransport(std::shared_ptr<Threads> threads, std::function<void(const std::vector<uint8_t> &)> emitData) :
|
||||
_threads(threads),
|
||||
_emitData(emitData),
|
||||
_transportName("signaling") {
|
||||
}
|
||||
|
||||
virtual ~SignalingPacketTransport() {
|
||||
}
|
||||
|
||||
void receiveData(std::vector<uint8_t> const &data) {
|
||||
RTC_LOG(LS_INFO) << "SignalingPacketTransport: adding data of " << data.size() << " bytes";
|
||||
SignalReadPacket.emit(this, (const char *)data.data(), data.size(), -1, 0);
|
||||
}
|
||||
|
||||
virtual const std::string& transport_name() const override {
|
||||
return _transportName;
|
||||
}
|
||||
|
||||
virtual bool writable() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool receiving() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempts to send the given packet.
|
||||
// The return value is < 0 on failure. The return value in failure case is not
|
||||
// descriptive. Depending on failure cause and implementation details
|
||||
// GetError() returns an descriptive errno.h error value.
|
||||
// This mimics posix socket send() or sendto() behavior.
|
||||
// TODO(johan): Reliable, meaningful, consistent error codes for all
|
||||
// implementations would be nice.
|
||||
// TODO(johan): Remove the default argument once channel code is updated.
|
||||
virtual int SendPacket(const char* data,
|
||||
size_t len,
|
||||
const rtc::PacketOptions& options,
|
||||
int flags = 0) override {
|
||||
_emitData(std::vector<uint8_t>(data, data + len));
|
||||
|
||||
rtc::SentPacket sentPacket;
|
||||
sentPacket.packet_id = options.packet_id;
|
||||
SignalSentPacket.emit(this, sentPacket);
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
virtual int SetOption(rtc::Socket::Option opt, int value) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool GetOption(rtc::Socket::Option opt, int* value) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual int GetError() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual absl::optional<rtc::NetworkRoute> network_route() const override {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Threads> _threads;
|
||||
std::function<void(const std::vector<uint8_t> &)> _onIncomingData;
|
||||
std::function<void(const std::vector<uint8_t> &)> _emitData;
|
||||
std::string _transportName;
|
||||
};
|
||||
|
||||
SignalingSctpConnection::SignalingSctpConnection(std::shared_ptr<Threads> threads, std::function<void(const std::vector<uint8_t> &)> onIncomingData, std::function<void(const std::vector<uint8_t> &)> emitData) :
|
||||
_threads(threads),
|
||||
_emitData(emitData),
|
||||
_onIncomingData(onIncomingData) {
|
||||
_threads->getNetworkThread()->BlockingCall([&]() {
|
||||
_packetTransport = std::make_unique<SignalingPacketTransport>(threads, emitData);
|
||||
|
||||
_sctpTransportFactory.reset(new cricket::SctpTransportFactory(_threads->getNetworkThread()));
|
||||
|
||||
_sctpTransport = _sctpTransportFactory->CreateSctpTransport(_packetTransport.get());
|
||||
_sctpTransport->OpenStream(0);
|
||||
_sctpTransport->SetDataChannelSink(this);
|
||||
|
||||
// TODO: should we disconnect the data channel sink?
|
||||
|
||||
_sctpTransport->Start(5000, 5000, 262144);
|
||||
});
|
||||
}
|
||||
|
||||
void SignalingSctpConnection::OnReadyToSend() {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
_isReadyToSend = true;
|
||||
|
||||
auto pendingData = _pendingData;
|
||||
_pendingData.clear();
|
||||
|
||||
for (const auto &data : pendingData) {
|
||||
webrtc::SendDataParams params;
|
||||
params.type = webrtc::DataMessageType::kBinary;
|
||||
params.ordered = true;
|
||||
|
||||
rtc::CopyOnWriteBuffer payload;
|
||||
payload.AppendData(data.data(), data.size());
|
||||
|
||||
webrtc::RTCError sendError = _sctpTransport->SendData(0, params, payload);
|
||||
|
||||
if (sendError.ok()) {
|
||||
RTC_LOG(LS_INFO) << "SignalingSctpConnection: sent data of " << data.size() << " bytes";
|
||||
} else {
|
||||
_isReadyToSend = false;
|
||||
_pendingData.push_back(data);
|
||||
RTC_LOG(LS_INFO) << "SignalingSctpConnection: send error, storing data until ready to send (" << _pendingData.size() << " items)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SignalingSctpConnection::OnTransportClosed(webrtc::RTCError error) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
}
|
||||
|
||||
void SignalingSctpConnection::OnDataReceived(int channel_id, webrtc::DataMessageType type, const rtc::CopyOnWriteBuffer& buffer) {
|
||||
assert(_threads->getNetworkThread()->IsCurrent());
|
||||
|
||||
_onIncomingData(std::vector<uint8_t>(buffer.data(), buffer.data() + buffer.size()));
|
||||
}
|
||||
|
||||
SignalingSctpConnection::~SignalingSctpConnection() {
|
||||
_threads->getNetworkThread()->BlockingCall([&]() {
|
||||
_sctpTransport.reset();
|
||||
_sctpTransportFactory.reset();
|
||||
_packetTransport.reset();
|
||||
});
|
||||
}
|
||||
|
||||
void SignalingSctpConnection::start() {
|
||||
}
|
||||
|
||||
void SignalingSctpConnection::receiveExternal(const std::vector<uint8_t> &data) {
|
||||
_threads->getNetworkThread()->BlockingCall([&]() {
|
||||
_packetTransport->receiveData(data);
|
||||
});
|
||||
}
|
||||
|
||||
void SignalingSctpConnection::send(const std::vector<uint8_t> &data) {
|
||||
_threads->getNetworkThread()->BlockingCall([&]() {
|
||||
if (_isReadyToSend) {
|
||||
webrtc::SendDataParams params;
|
||||
params.type = webrtc::DataMessageType::kBinary;
|
||||
params.ordered = true;
|
||||
|
||||
rtc::CopyOnWriteBuffer payload;
|
||||
payload.AppendData(data.data(), data.size());
|
||||
|
||||
webrtc::RTCError sendError = _sctpTransport->SendData(0, params, payload);
|
||||
|
||||
if (!sendError.ok()) {
|
||||
_isReadyToSend = false;
|
||||
_pendingData.push_back(data);
|
||||
RTC_LOG(LS_INFO) << "SignalingSctpConnection: send error, storing data until ready to send (" << _pendingData.size() << " items)";
|
||||
} else {
|
||||
RTC_LOG(LS_INFO) << "SignalingSctpConnection: sent data of " << data.size() << " bytes";
|
||||
}
|
||||
} else {
|
||||
_pendingData.push_back(data);
|
||||
RTC_LOG(LS_INFO) << "SignalingSctpConnection: not ready to send, storing data until ready to send (" << _pendingData.size() << " items)";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
79
TMessagesProj/jni/voip/tgcalls/v2/SignalingSctpConnection.h
Normal file
79
TMessagesProj/jni/voip/tgcalls/v2/SignalingSctpConnection.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#ifndef TGCALLS_SIGNALING_SCTP_CONNECTION_H_
|
||||
#define TGCALLS_SIGNALING_SCTP_CONNECTION_H_
|
||||
|
||||
#ifdef WEBRTC_WIN
|
||||
#include <WinSock2.h>
|
||||
#endif // WEBRTC_WIN
|
||||
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/socket_address.h"
|
||||
#include "rtc_base/copy_on_write_buffer.h"
|
||||
#include "rtc_base/byte_buffer.h"
|
||||
#include "media/base/media_channel.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <absl/types/optional.h>
|
||||
|
||||
#include "StaticThreads.h"
|
||||
#include "SignalingConnection.h"
|
||||
|
||||
namespace rtc {
|
||||
class Socket;
|
||||
}
|
||||
|
||||
namespace cricket {
|
||||
class SctpTransportFactory;
|
||||
class SctpTransportInternal;
|
||||
};
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
class SignalingPacketTransport;
|
||||
|
||||
class SignalingSctpConnection : public sigslot::has_slots<>, public SignalingConnection, public webrtc::DataChannelSink {
|
||||
private:
|
||||
struct PacketReadState {
|
||||
rtc::CopyOnWriteBuffer headerData;
|
||||
int remainingHeaderSize = 0;
|
||||
bool isHeaderCompleted = false;
|
||||
|
||||
rtc::CopyOnWriteBuffer data;
|
||||
int remainingDataSize = 0;
|
||||
bool isDataCompleted = false;
|
||||
};
|
||||
|
||||
public:
|
||||
SignalingSctpConnection(std::shared_ptr<Threads> threads, std::function<void(const std::vector<uint8_t> &)> onIncomingData, std::function<void(const std::vector<uint8_t> &)> emitData);
|
||||
virtual ~SignalingSctpConnection();
|
||||
|
||||
virtual void receiveExternal(const std::vector<uint8_t> &data) override;
|
||||
virtual void start() override;
|
||||
virtual void send(const std::vector<uint8_t> &data) override;
|
||||
|
||||
virtual void OnDataReceived(int channel_id,
|
||||
webrtc::DataMessageType type,
|
||||
const rtc::CopyOnWriteBuffer& buffer) override;
|
||||
virtual void OnReadyToSend() override;
|
||||
virtual void OnTransportClosed(webrtc::RTCError error) override;
|
||||
|
||||
// Unused
|
||||
virtual void OnChannelClosing(int channel_id) override{}
|
||||
virtual void OnChannelClosed(int channel_id) override{}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Threads> _threads;
|
||||
std::function<void(const std::vector<uint8_t> &)> _emitData;
|
||||
std::function<void(const std::vector<uint8_t> &)> _onIncomingData;
|
||||
|
||||
std::unique_ptr<SignalingPacketTransport> _packetTransport;
|
||||
std::unique_ptr<cricket::SctpTransportFactory> _sctpTransportFactory;
|
||||
std::unique_ptr<cricket::SctpTransportInternal> _sctpTransport;
|
||||
|
||||
bool _isReadyToSend = false;
|
||||
std::vector<std::vector<uint8_t>> _pendingData;
|
||||
};
|
||||
|
||||
} // namespace tgcalls
|
||||
|
||||
#endif // TGCALLS_SIGNALING_SCTP_CONNECTION_H_
|
||||
1308
TMessagesProj/jni/voip/tgcalls/v2/ikcp.cpp
Normal file
1308
TMessagesProj/jni/voip/tgcalls/v2/ikcp.cpp
Normal file
File diff suppressed because it is too large
Load diff
416
TMessagesProj/jni/voip/tgcalls/v2/ikcp.h
Normal file
416
TMessagesProj/jni/voip/tgcalls/v2/ikcp.h
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
//=====================================================================
|
||||
//
|
||||
// KCP - A Better ARQ Protocol Implementation
|
||||
// skywind3000 (at) gmail.com, 2010-2011
|
||||
//
|
||||
// Features:
|
||||
// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
|
||||
// + Maximum RTT reduce three times vs tcp.
|
||||
// + Lightweight, distributed as a single source file.
|
||||
//
|
||||
//=====================================================================
|
||||
#ifndef __IKCP_H__
|
||||
#define __IKCP_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// 32BIT INTEGER DEFINITION
|
||||
//=====================================================================
|
||||
#ifndef __INTEGER_32_BITS__
|
||||
#define __INTEGER_32_BITS__
|
||||
#if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \
|
||||
defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \
|
||||
defined(_M_AMD64)
|
||||
typedef unsigned int ISTDUINT32;
|
||||
typedef int ISTDINT32;
|
||||
#elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \
|
||||
defined(__i386) || defined(_M_X86)
|
||||
typedef unsigned long ISTDUINT32;
|
||||
typedef long ISTDINT32;
|
||||
#elif defined(__MACOS__)
|
||||
typedef UInt32 ISTDUINT32;
|
||||
typedef SInt32 ISTDINT32;
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
#include <sys/types.h>
|
||||
typedef u_int32_t ISTDUINT32;
|
||||
typedef int32_t ISTDINT32;
|
||||
#elif defined(__BEOS__)
|
||||
#include <sys/inttypes.h>
|
||||
typedef u_int32_t ISTDUINT32;
|
||||
typedef int32_t ISTDINT32;
|
||||
#elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__))
|
||||
typedef unsigned __int32 ISTDUINT32;
|
||||
typedef __int32 ISTDINT32;
|
||||
#elif defined(__GNUC__)
|
||||
#include <stdint.h>
|
||||
typedef uint32_t ISTDUINT32;
|
||||
typedef int32_t ISTDINT32;
|
||||
#else
|
||||
typedef unsigned long ISTDUINT32;
|
||||
typedef long ISTDINT32;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// Integer Definition
|
||||
//=====================================================================
|
||||
#ifndef __IINT8_DEFINED
|
||||
#define __IINT8_DEFINED
|
||||
typedef char IINT8;
|
||||
#endif
|
||||
|
||||
#ifndef __IUINT8_DEFINED
|
||||
#define __IUINT8_DEFINED
|
||||
typedef unsigned char IUINT8;
|
||||
#endif
|
||||
|
||||
#ifndef __IUINT16_DEFINED
|
||||
#define __IUINT16_DEFINED
|
||||
typedef unsigned short IUINT16;
|
||||
#endif
|
||||
|
||||
#ifndef __IINT16_DEFINED
|
||||
#define __IINT16_DEFINED
|
||||
typedef short IINT16;
|
||||
#endif
|
||||
|
||||
#ifndef __IINT32_DEFINED
|
||||
#define __IINT32_DEFINED
|
||||
typedef ISTDINT32 IINT32;
|
||||
#endif
|
||||
|
||||
#ifndef __IUINT32_DEFINED
|
||||
#define __IUINT32_DEFINED
|
||||
typedef ISTDUINT32 IUINT32;
|
||||
#endif
|
||||
|
||||
#ifndef __IINT64_DEFINED
|
||||
#define __IINT64_DEFINED
|
||||
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
typedef __int64 IINT64;
|
||||
#else
|
||||
typedef long long IINT64;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __IUINT64_DEFINED
|
||||
#define __IUINT64_DEFINED
|
||||
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||
typedef unsigned __int64 IUINT64;
|
||||
#else
|
||||
typedef unsigned long long IUINT64;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef INLINE
|
||||
#if defined(__GNUC__)
|
||||
|
||||
#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
|
||||
#define INLINE __inline__ __attribute__((always_inline))
|
||||
#else
|
||||
#define INLINE __inline__
|
||||
#endif
|
||||
|
||||
#elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__))
|
||||
#define INLINE __inline
|
||||
#else
|
||||
#define INLINE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (!defined(__cplusplus)) && (!defined(inline))
|
||||
#define inline INLINE
|
||||
#endif
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// QUEUE DEFINITION
|
||||
//=====================================================================
|
||||
#ifndef __IQUEUE_DEF__
|
||||
#define __IQUEUE_DEF__
|
||||
|
||||
struct IQUEUEHEAD {
|
||||
struct IQUEUEHEAD *next, *prev;
|
||||
};
|
||||
|
||||
typedef struct IQUEUEHEAD iqueue_head;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// queue init
|
||||
//---------------------------------------------------------------------
|
||||
#define IQUEUE_HEAD_INIT(name) { &(name), &(name) }
|
||||
#define IQUEUE_HEAD(name) \
|
||||
struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name)
|
||||
|
||||
#define IQUEUE_INIT(ptr) ( \
|
||||
(ptr)->next = (ptr), (ptr)->prev = (ptr))
|
||||
|
||||
#define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
|
||||
#define ICONTAINEROF(ptr, type, member) ( \
|
||||
(type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) )
|
||||
|
||||
#define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member)
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// queue operation
|
||||
//---------------------------------------------------------------------
|
||||
#define IQUEUE_ADD(node, head) ( \
|
||||
(node)->prev = (head), (node)->next = (head)->next, \
|
||||
(head)->next->prev = (node), (head)->next = (node))
|
||||
|
||||
#define IQUEUE_ADD_TAIL(node, head) ( \
|
||||
(node)->prev = (head)->prev, (node)->next = (head), \
|
||||
(head)->prev->next = (node), (head)->prev = (node))
|
||||
|
||||
#define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n))
|
||||
|
||||
#define IQUEUE_DEL(entry) (\
|
||||
(entry)->next->prev = (entry)->prev, \
|
||||
(entry)->prev->next = (entry)->next, \
|
||||
(entry)->next = 0, (entry)->prev = 0)
|
||||
|
||||
#define IQUEUE_DEL_INIT(entry) do { \
|
||||
IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0)
|
||||
|
||||
#define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next)
|
||||
|
||||
#define iqueue_init IQUEUE_INIT
|
||||
#define iqueue_entry IQUEUE_ENTRY
|
||||
#define iqueue_add IQUEUE_ADD
|
||||
#define iqueue_add_tail IQUEUE_ADD_TAIL
|
||||
#define iqueue_del IQUEUE_DEL
|
||||
#define iqueue_del_init IQUEUE_DEL_INIT
|
||||
#define iqueue_is_empty IQUEUE_IS_EMPTY
|
||||
|
||||
#define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \
|
||||
for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \
|
||||
&((iterator)->MEMBER) != (head); \
|
||||
(iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER))
|
||||
|
||||
#define iqueue_foreach(iterator, head, TYPE, MEMBER) \
|
||||
IQUEUE_FOREACH(iterator, head, TYPE, MEMBER)
|
||||
|
||||
#define iqueue_foreach_entry(pos, head) \
|
||||
for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next )
|
||||
|
||||
|
||||
#define __iqueue_splice(list, head) do { \
|
||||
iqueue_head *first = (list)->next, *last = (list)->prev; \
|
||||
iqueue_head *at = (head)->next; \
|
||||
(first)->prev = (head), (head)->next = (first); \
|
||||
(last)->next = (at), (at)->prev = (last); } while (0)
|
||||
|
||||
#define iqueue_splice(list, head) do { \
|
||||
if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0)
|
||||
|
||||
#define iqueue_splice_init(list, head) do { \
|
||||
iqueue_splice(list, head); iqueue_init(list); } while (0)
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4311)
|
||||
#pragma warning(disable:4312)
|
||||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// BYTE ORDER & ALIGNMENT
|
||||
//---------------------------------------------------------------------
|
||||
#ifndef IWORDS_BIG_ENDIAN
|
||||
#ifdef _BIG_ENDIAN_
|
||||
#if _BIG_ENDIAN_
|
||||
#define IWORDS_BIG_ENDIAN 1
|
||||
#endif
|
||||
#endif
|
||||
#ifndef IWORDS_BIG_ENDIAN
|
||||
#if defined(__hppa__) || \
|
||||
defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
|
||||
(defined(__MIPS__) && defined(__MIPSEB__)) || \
|
||||
defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
|
||||
defined(__sparc__) || defined(__powerpc__) || \
|
||||
defined(__mc68000__) || defined(__s390x__) || defined(__s390__)
|
||||
#define IWORDS_BIG_ENDIAN 1
|
||||
#endif
|
||||
#endif
|
||||
#ifndef IWORDS_BIG_ENDIAN
|
||||
#define IWORDS_BIG_ENDIAN 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef IWORDS_MUST_ALIGN
|
||||
#if defined(__i386__) || defined(__i386) || defined(_i386_)
|
||||
#define IWORDS_MUST_ALIGN 0
|
||||
#elif defined(_M_IX86) || defined(_X86_) || defined(__x86_64__)
|
||||
#define IWORDS_MUST_ALIGN 0
|
||||
#elif defined(__amd64) || defined(__amd64__)
|
||||
#define IWORDS_MUST_ALIGN 0
|
||||
#else
|
||||
#define IWORDS_MUST_ALIGN 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
//=====================================================================
|
||||
// SEGMENT
|
||||
//=====================================================================
|
||||
struct IKCPSEG
|
||||
{
|
||||
struct IQUEUEHEAD node;
|
||||
IUINT32 conv;
|
||||
IUINT32 cmd;
|
||||
IUINT32 frg;
|
||||
IUINT32 wnd;
|
||||
IUINT32 ts;
|
||||
IUINT32 sn;
|
||||
IUINT32 una;
|
||||
IUINT32 len;
|
||||
IUINT32 resendts;
|
||||
IUINT32 rto;
|
||||
IUINT32 fastack;
|
||||
IUINT32 xmit;
|
||||
char data[1];
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// IKCPCB
|
||||
//---------------------------------------------------------------------
|
||||
struct IKCPCB
|
||||
{
|
||||
IUINT32 conv, mtu, mss, state;
|
||||
IUINT32 snd_una, snd_nxt, rcv_nxt;
|
||||
IUINT32 ts_recent, ts_lastack, ssthresh;
|
||||
IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto;
|
||||
IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe;
|
||||
IUINT32 current, interval, ts_flush, xmit;
|
||||
IUINT32 nrcv_buf, nsnd_buf;
|
||||
IUINT32 nrcv_que, nsnd_que;
|
||||
IUINT32 nodelay, updated;
|
||||
IUINT32 ts_probe, probe_wait;
|
||||
IUINT32 dead_link, incr;
|
||||
struct IQUEUEHEAD snd_queue;
|
||||
struct IQUEUEHEAD rcv_queue;
|
||||
struct IQUEUEHEAD snd_buf;
|
||||
struct IQUEUEHEAD rcv_buf;
|
||||
IUINT32 *acklist;
|
||||
IUINT32 ackcount;
|
||||
IUINT32 ackblock;
|
||||
void *user;
|
||||
char *buffer;
|
||||
int fastresend;
|
||||
int fastlimit;
|
||||
int nocwnd, stream;
|
||||
int logmask;
|
||||
int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user);
|
||||
void (*writelog)(const char *log, struct IKCPCB *kcp, void *user);
|
||||
};
|
||||
|
||||
|
||||
typedef struct IKCPCB ikcpcb;
|
||||
|
||||
#define IKCP_LOG_OUTPUT 1
|
||||
#define IKCP_LOG_INPUT 2
|
||||
#define IKCP_LOG_SEND 4
|
||||
#define IKCP_LOG_RECV 8
|
||||
#define IKCP_LOG_IN_DATA 16
|
||||
#define IKCP_LOG_IN_ACK 32
|
||||
#define IKCP_LOG_IN_PROBE 64
|
||||
#define IKCP_LOG_IN_WINS 128
|
||||
#define IKCP_LOG_OUT_DATA 256
|
||||
#define IKCP_LOG_OUT_ACK 512
|
||||
#define IKCP_LOG_OUT_PROBE 1024
|
||||
#define IKCP_LOG_OUT_WINS 2048
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
// create a new kcp control object, 'conv' must equal in two endpoint
|
||||
// from the same connection. 'user' will be passed to the output callback
|
||||
// output callback can be setup like this: 'kcp->output = my_udp_output'
|
||||
ikcpcb* ikcp_create(IUINT32 conv, void *user);
|
||||
|
||||
// release kcp control object
|
||||
void ikcp_release(ikcpcb *kcp);
|
||||
|
||||
// set output callback, which will be invoked by kcp
|
||||
void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len,
|
||||
ikcpcb *kcp, void *user));
|
||||
|
||||
// user/upper level recv: returns size, returns below zero for EAGAIN
|
||||
int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
|
||||
|
||||
// user/upper level send, returns below zero for error
|
||||
int ikcp_send(ikcpcb *kcp, const char *buffer, int len);
|
||||
|
||||
// update state (call it repeatedly, every 10ms-100ms), or you can ask
|
||||
// ikcp_check when to call it again (without ikcp_input/_send calling).
|
||||
// 'current' - current timestamp in millisec.
|
||||
void ikcp_update(ikcpcb *kcp, IUINT32 current);
|
||||
|
||||
// Determine when should you invoke ikcp_update:
|
||||
// returns when you should invoke ikcp_update in millisec, if there
|
||||
// is no ikcp_input/_send calling. you can call ikcp_update in that
|
||||
// time, instead of call update repeatly.
|
||||
// Important to reduce unnacessary ikcp_update invoking. use it to
|
||||
// schedule ikcp_update (eg. implementing an epoll-like mechanism,
|
||||
// or optimize ikcp_update when handling massive kcp connections)
|
||||
IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current);
|
||||
|
||||
// when you received a low level packet (eg. UDP packet), call it
|
||||
int ikcp_input(ikcpcb *kcp, const char *data, long size);
|
||||
|
||||
// flush pending data
|
||||
void ikcp_flush(ikcpcb *kcp);
|
||||
|
||||
// check the size of next message in the recv queue
|
||||
int ikcp_peeksize(const ikcpcb *kcp);
|
||||
|
||||
// change MTU size, default is 1400
|
||||
int ikcp_setmtu(ikcpcb *kcp, int mtu);
|
||||
|
||||
// set maximum window size: sndwnd=32, rcvwnd=32 by default
|
||||
int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
|
||||
|
||||
// get how many packet is waiting to be sent
|
||||
int ikcp_waitsnd(const ikcpcb *kcp);
|
||||
|
||||
// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
|
||||
// nodelay: 0:disable(default), 1:enable
|
||||
// interval: internal update timer interval in millisec, default is 100ms
|
||||
// resend: 0:disable fast resend(default), 1:enable fast resend
|
||||
// nc: 0:normal congestion control(default), 1:disable congestion control
|
||||
int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc);
|
||||
|
||||
|
||||
void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...);
|
||||
|
||||
// setup allocator
|
||||
void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*));
|
||||
|
||||
// read conv
|
||||
IUINT32 ikcp_getconv(const void *ptr);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue