Telegram-Android/TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.cpp

2009 lines
80 KiB
C++
Raw Normal View History

2021-06-25 02:43:10 +02:00
#include "v2/InstanceV2Impl.h"
#include "LogSinkImpl.h"
#include "VideoCaptureInterfaceImpl.h"
#include "VideoCapturerInterface.h"
#include "v2/NativeNetworkingImpl.h"
#include "v2/Signaling.h"
#include "CodecSelectHelper.h"
#include "platform/PlatformInterface.h"
#include "api/audio_codecs/audio_decoder_factory_template.h"
#include "api/audio_codecs/audio_encoder_factory_template.h"
#include "api/audio_codecs/opus/audio_decoder_opus.h"
#include "api/audio_codecs/opus/audio_decoder_multi_channel_opus.h"
#include "api/audio_codecs/opus/audio_encoder_opus.h"
#include "api/audio_codecs/L16/audio_decoder_L16.h"
#include "api/audio_codecs/L16/audio_encoder_L16.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "media/engine/webrtc_media_engine.h"
#include "system_wrappers/include/field_trial.h"
#include "api/video/builtin_video_bitrate_allocator_factory.h"
#include "call/call.h"
#include "modules/rtp_rtcp/source/rtp_utility.h"
#include "api/call/audio_sink.h"
#include "modules/audio_processing/audio_buffer.h"
#include "absl/strings/match.h"
#include "modules/audio_processing/agc2/vad_with_level.h"
#include "pc/channel_manager.h"
#include "media/base/rtp_data_engine.h"
#include "audio/audio_state.h"
#include "modules/audio_coding/neteq/default_neteq_factory.h"
#include "modules/audio_coding/include/audio_coding_module.h"
#include "api/candidate.h"
#include "api/jsep_ice_candidate.h"
#include "media/base/h264_profile_level_id.h"
#include "pc/used_ids.h"
#include "AudioFrame.h"
#include "ThreadLocalObject.h"
#include "Manager.h"
#include "NetworkManager.h"
#include "VideoCaptureInterfaceImpl.h"
#include "platform/PlatformInterface.h"
#include "LogSinkImpl.h"
#include "CodecSelectHelper.h"
#include "AudioDeviceHelper.h"
#include "SignalingEncryption.h"
#include <random>
#include <sstream>
namespace tgcalls {
namespace {
static int stringToInt(std::string const &string) {
std::stringstream stringStream(string);
int value = 0;
stringStream >> value;
return value;
}
static std::string intToString(int value) {
std::ostringstream stringStream;
stringStream << value;
return stringStream.str();
}
static VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(VideoCaptureInterface *videoCapture) {
return videoCapture
? static_cast<VideoCaptureInterfaceImpl*>(videoCapture)->object()->getSyncAssumingSameThread()
: nullptr;
}
struct OutgoingVideoFormat {
cricket::VideoCodec videoCodec;
absl::optional<cricket::VideoCodec> rtxCodec;
};
static void addDefaultFeedbackParams(cricket::VideoCodec *codec) {
// Don't add any feedback params for RED and ULPFEC.
if (codec->name == cricket::kRedCodecName || codec->name == cricket::kUlpfecCodecName) {
return;
}
codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamRemb, cricket::kParamValueEmpty));
codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc, cricket::kParamValueEmpty));
// Don't add any more feedback params for FLEXFEC.
if (codec->name == cricket::kFlexfecCodecName) {
return;
}
codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir));
codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
codec->AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamNack, cricket::kRtcpFbNackParamPli));
}
template <class C>
static bool IsRtxCodec(const C& codec) {
return absl::EqualsIgnoreCase(codec.name, cricket::kRtxCodecName);
}
template <class C>
static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
const int codec1_id,
const std::vector<C>& codecs2,
const int codec2_id) {
const C* codec1 = FindCodecById(codecs1, codec1_id);
const C* codec2 = FindCodecById(codecs2, codec2_id);
return codec1 != nullptr && codec2 != nullptr && codec1->Matches(*codec2);
}
// Finds a codec in |codecs2| that matches |codec_to_match|, which is
// a member of |codecs1|. If |codec_to_match| is an RTX codec, both
// the codecs themselves and their associated codecs must match.
template <class C>
static bool FindMatchingCodec(const std::vector<C>& codecs1,
const std::vector<C>& codecs2,
const C& codec_to_match,
C* found_codec) {
// |codec_to_match| should be a member of |codecs1|, in order to look up RTX
// codecs' associated codecs correctly. If not, that's a programming error.
RTC_DCHECK(absl::c_any_of(codecs1, [&codec_to_match](const C& codec) {
return &codec == &codec_to_match;
}));
for (const C& potential_match : codecs2) {
if (potential_match.Matches(codec_to_match)) {
if (IsRtxCodec(codec_to_match)) {
int apt_value_1 = 0;
int apt_value_2 = 0;
if (!codec_to_match.GetParam(cricket::kCodecParamAssociatedPayloadType,
&apt_value_1) ||
!potential_match.GetParam(cricket::kCodecParamAssociatedPayloadType,
&apt_value_2)) {
RTC_LOG(LS_WARNING) << "RTX missing associated payload type.";
continue;
}
if (!ReferencedCodecsMatch(codecs1, apt_value_1, codecs2,
apt_value_2)) {
continue;
}
}
if (found_codec) {
*found_codec = potential_match;
}
return true;
}
}
return false;
}
template <class C>
static void NegotiatePacketization(const C& local_codec,
const C& remote_codec,
C* negotiated_codec) {}
template <>
void NegotiatePacketization(const cricket::VideoCodec& local_codec,
const cricket::VideoCodec& remote_codec,
cricket::VideoCodec* negotiated_codec) {
negotiated_codec->packetization =
cricket::VideoCodec::IntersectPacketization(local_codec, remote_codec);
}
template <class C>
static void NegotiateCodecs(const std::vector<C>& local_codecs,
const std::vector<C>& offered_codecs,
std::vector<C>* negotiated_codecs,
bool keep_offer_order) {
for (const C& ours : local_codecs) {
C theirs;
// Note that we intentionally only find one matching codec for each of our
// local codecs, in case the remote offer contains duplicate codecs.
if (FindMatchingCodec(local_codecs, offered_codecs, ours, &theirs)) {
C negotiated = ours;
NegotiatePacketization(ours, theirs, &negotiated);
negotiated.IntersectFeedbackParams(theirs);
if (IsRtxCodec(negotiated)) {
const auto apt_it =
theirs.params.find(cricket::kCodecParamAssociatedPayloadType);
// FindMatchingCodec shouldn't return something with no apt value.
RTC_DCHECK(apt_it != theirs.params.end());
negotiated.SetParam(cricket::kCodecParamAssociatedPayloadType, apt_it->second);
}
if (absl::EqualsIgnoreCase(ours.name, cricket::kH264CodecName)) {
webrtc::H264::GenerateProfileLevelIdForAnswer(
ours.params, theirs.params, &negotiated.params);
}
negotiated.id = theirs.id;
negotiated.name = theirs.name;
negotiated_codecs->push_back(std::move(negotiated));
}
}
if (keep_offer_order) {
// RFC3264: Although the answerer MAY list the formats in their desired
// order of preference, it is RECOMMENDED that unless there is a
// specific reason, the answerer list formats in the same relative order
// they were present in the offer.
// This can be skipped when the transceiver has any codec preferences.
std::unordered_map<int, int> payload_type_preferences;
int preference = static_cast<int>(offered_codecs.size() + 1);
for (const C& codec : offered_codecs) {
payload_type_preferences[codec.id] = preference--;
}
absl::c_sort(*negotiated_codecs, [&payload_type_preferences](const C& a,
const C& b) {
return payload_type_preferences[a.id] > payload_type_preferences[b.id];
});
}
}
// Find the codec in |codec_list| that |rtx_codec| is associated with.
template <class C>
static const C* GetAssociatedCodec(const std::vector<C>& codec_list,
const C& rtx_codec) {
std::string associated_pt_str;
if (!rtx_codec.GetParam(cricket::kCodecParamAssociatedPayloadType,
&associated_pt_str)) {
RTC_LOG(LS_WARNING) << "RTX codec " << rtx_codec.name
<< " is missing an associated payload type.";
return nullptr;
}
int associated_pt;
if (!rtc::FromString(associated_pt_str, &associated_pt)) {
RTC_LOG(LS_WARNING) << "Couldn't convert payload type " << associated_pt_str
<< " of RTX codec " << rtx_codec.name
<< " to an integer.";
return nullptr;
}
// Find the associated reference codec for the reference RTX codec.
const C* associated_codec = FindCodecById(codec_list, associated_pt);
if (!associated_codec) {
RTC_LOG(LS_WARNING) << "Couldn't find associated codec with payload type "
<< associated_pt << " for RTX codec " << rtx_codec.name
<< ".";
}
return associated_codec;
}
// Adds all codecs from |reference_codecs| to |offered_codecs| that don't
// already exist in |offered_codecs| and ensure the payload types don't
// collide.
template <class C>
static void MergeCodecs(const std::vector<C>& reference_codecs,
std::vector<C>* offered_codecs,
cricket::UsedPayloadTypes* used_pltypes) {
// Add all new codecs that are not RTX codecs.
for (const C& reference_codec : reference_codecs) {
if (!IsRtxCodec(reference_codec) &&
!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
reference_codec, nullptr)) {
C codec = reference_codec;
used_pltypes->FindAndSetIdUsed(&codec);
offered_codecs->push_back(codec);
}
}
// Add all new RTX codecs.
for (const C& reference_codec : reference_codecs) {
if (IsRtxCodec(reference_codec) &&
!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
reference_codec, nullptr)) {
C rtx_codec = reference_codec;
const C* associated_codec =
GetAssociatedCodec(reference_codecs, rtx_codec);
if (!associated_codec) {
continue;
}
// Find a codec in the offered list that matches the reference codec.
// Its payload type may be different than the reference codec.
C matching_codec;
if (!FindMatchingCodec<C>(reference_codecs, *offered_codecs,
*associated_codec, &matching_codec)) {
RTC_LOG(LS_WARNING)
<< "Couldn't find matching " << associated_codec->name << " codec.";
continue;
}
rtx_codec.params[cricket::kCodecParamAssociatedPayloadType] =
rtc::ToString(matching_codec.id);
used_pltypes->FindAndSetIdUsed(&rtx_codec);
offered_codecs->push_back(rtx_codec);
}
}
}
static std::vector<OutgoingVideoFormat> generateAvailableVideoFormats(std::vector<webrtc::SdpVideoFormat> const &formats) {
if (formats.empty()) {
return {};
}
constexpr int kFirstDynamicPayloadType = 120;
constexpr int kLastDynamicPayloadType = 127;
int payload_type = kFirstDynamicPayloadType;
std::vector<OutgoingVideoFormat> result;
bool codecSelected = false;
for (const auto &format : formats) {
if (codecSelected) {
break;
}
OutgoingVideoFormat resultFormat;
cricket::VideoCodec codec(format);
codec.id = payload_type;
addDefaultFeedbackParams(&codec);
if (!absl::EqualsIgnoreCase(codec.name, cricket::kVp8CodecName)) {
continue;
}
resultFormat.videoCodec = codec;
codecSelected = true;
// Increment payload type.
++payload_type;
if (payload_type > kLastDynamicPayloadType) {
RTC_LOG(LS_ERROR) << "Out of dynamic payload types, skipping the rest.";
break;
}
// Add associated RTX codec for non-FEC codecs.
if (!absl::EqualsIgnoreCase(codec.name, cricket::kUlpfecCodecName) &&
!absl::EqualsIgnoreCase(codec.name, cricket::kFlexfecCodecName)) {
resultFormat.rtxCodec = cricket::VideoCodec::CreateRtxCodec(payload_type, codec.id);
// Increment payload type.
++payload_type;
if (payload_type > kLastDynamicPayloadType) {
RTC_LOG(LS_ERROR) << "Out of dynamic payload types, skipping the rest.";
break;
}
}
result.push_back(std::move(resultFormat));
}
return result;
}
static void getCodecsFromMediaContent(signaling::MediaContent const &content, std::vector<cricket::VideoCodec> &codecs) {
for (const auto &payloadType : content.payloadTypes) {
cricket::VideoCodec codec(payloadType.id, payloadType.name);
for (const auto &feedbackType : payloadType.feedbackTypes) {
codec.AddFeedbackParam(cricket::FeedbackParam(feedbackType.type, feedbackType.subtype));
}
for (const auto &parameter : payloadType.parameters) {
codec.SetParam(parameter.first, parameter.second);
}
codecs.push_back(std::move(codec));
}
}
static std::vector<signaling::PayloadType> getPayloadTypesFromVideoCodecs(std::vector<cricket::VideoCodec> const &codecs) {
std::vector<signaling::PayloadType> payloadTypes;
for (const auto &codec : codecs) {
signaling::PayloadType payloadType;
payloadType.id = codec.id;
payloadType.name = codec.name;
payloadType.clockrate = 90000;
payloadType.channels = 0;
for (const auto &feedbackParam : codec.feedback_params.params()) {
signaling::FeedbackType feedbackType;
feedbackType.type = feedbackParam.id();
feedbackType.subtype = feedbackParam.param();
payloadType.feedbackTypes.push_back(std::move(feedbackType));
}
for (const auto &param : codec.params) {
payloadType.parameters.push_back(std::make_pair(param.first, param.second));
}
payloadTypes.push_back(std::move(payloadType));
}
return payloadTypes;
}
static void getCodecsFromMediaContent(signaling::MediaContent const &content, std::vector<cricket::AudioCodec> &codecs) {
for (const auto &payloadType : content.payloadTypes) {
cricket::AudioCodec codec(payloadType.id, payloadType.name, payloadType.clockrate, 0, payloadType.channels);
for (const auto &feedbackType : payloadType.feedbackTypes) {
codec.AddFeedbackParam(cricket::FeedbackParam(feedbackType.type, feedbackType.subtype));
}
for (const auto &parameter : payloadType.parameters) {
codec.SetParam(parameter.first, parameter.second);
}
codecs.push_back(std::move(codec));
}
}
static std::vector<signaling::PayloadType> getPayloadTypesFromAudioCodecs(std::vector<cricket::AudioCodec> const &codecs) {
std::vector<signaling::PayloadType> payloadTypes;
for (const auto &codec : codecs) {
signaling::PayloadType payloadType;
payloadType.id = codec.id;
payloadType.name = codec.name;
payloadType.clockrate = codec.clockrate;
payloadType.channels = (uint32_t)codec.channels;
for (const auto &feedbackParam : codec.feedback_params.params()) {
signaling::FeedbackType feedbackType;
feedbackType.type = feedbackParam.id();
feedbackType.subtype = feedbackParam.param();
payloadType.feedbackTypes.push_back(std::move(feedbackType));
}
for (const auto &param : codec.params) {
payloadType.parameters.push_back(std::make_pair(param.first, param.second));
}
payloadTypes.push_back(std::move(payloadType));
}
return payloadTypes;
}
template <class C>
struct NegotiatedMediaContent {
uint32_t ssrc = 0;
std::vector<signaling::SsrcGroup> ssrcGroups;
std::vector<webrtc::RtpExtension> rtpExtensions;
std::vector<C> codecs;
};
static bool FindByUri(const cricket::RtpHeaderExtensions& extensions,
const webrtc::RtpExtension& ext_to_match,
webrtc::RtpExtension* found_extension) {
// We assume that all URIs are given in a canonical format.
const webrtc::RtpExtension* found =
webrtc::RtpExtension::FindHeaderExtensionByUri(extensions,
ext_to_match.uri);
if (!found) {
return false;
}
if (found_extension) {
*found_extension = *found;
}
return true;
}
template <class C>
static NegotiatedMediaContent<C> negotiateMediaContent(signaling::MediaContent const &baseMediaContent, signaling::MediaContent const &localContent, signaling::MediaContent const &remoteContent, bool isAnswer) {
std::vector<C> localCodecs;
getCodecsFromMediaContent(localContent, localCodecs);
std::vector<C> remoteCodecs;
getCodecsFromMediaContent(remoteContent, remoteCodecs);
std::vector<C> negotiatedCodecs;
cricket::UsedPayloadTypes usedPayloadTypes;
NegotiateCodecs<C>(localCodecs, remoteCodecs, &negotiatedCodecs, true);
NegotiatedMediaContent<C> result;
result.ssrc = baseMediaContent.ssrc;
result.ssrcGroups = baseMediaContent.ssrcGroups;
result.codecs = std::move(negotiatedCodecs);
cricket::UsedRtpHeaderExtensionIds extensionIds(cricket::UsedRtpHeaderExtensionIds::IdDomain::kOneByteOnly);
for (const auto &extension : remoteContent.rtpExtensions) {
if (isAnswer) {
webrtc::RtpExtension found;
if (!FindByUri(localContent.rtpExtensions, extension, &found)) {
continue;
}
}
webrtc::RtpExtension mutableExtension = extension;
extensionIds.FindAndSetIdUsed(&mutableExtension);
result.rtpExtensions.push_back(std::move(mutableExtension));
}
if (!isAnswer) {
for (const auto &extension : localContent.rtpExtensions) {
webrtc::RtpExtension found;
if (!FindByUri(result.rtpExtensions, extension, &found)) {
webrtc::RtpExtension mutableExtension = extension;
extensionIds.FindAndSetIdUsed(&mutableExtension);
result.rtpExtensions.push_back(std::move(mutableExtension));
}
}
}
return result;
}
class OutgoingAudioChannel : public sigslot::has_slots<> {
public:
static absl::optional<signaling::MediaContent> createOutgoingContentDescription() {
signaling::MediaContent mediaContent;
auto generator = std::mt19937(std::random_device()());
auto distribution = std::uniform_int_distribution<uint32_t>();
do {
mediaContent.ssrc = distribution(generator) & 0x7fffffffU;
} while (!mediaContent.ssrc);
mediaContent.rtpExtensions.emplace_back(webrtc::RtpExtension::kAudioLevelUri, 1);
mediaContent.rtpExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, 2);
mediaContent.rtpExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, 3);
cricket::AudioCodec opusCodec(109, "opus", 48000, 0, 2);
opusCodec.AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc));
opusCodec.SetParam(cricket::kCodecParamUseInbandFec, 1);
opusCodec.SetParam(cricket::kCodecParamMinPTime, 60);
mediaContent.payloadTypes = getPayloadTypesFromAudioCodecs({ opusCodec });
return mediaContent;
}
public:
OutgoingAudioChannel(
webrtc::Call *call,
cricket::ChannelManager *channelManager,
rtc::UniqueRandomIdGenerator *uniqueRandomIdGenerator,
webrtc::LocalAudioSinkAdapter *audioSource,
webrtc::RtpTransport *rtpTransport,
NegotiatedMediaContent<cricket::AudioCodec> const &mediaContent,
std::shared_ptr<Threads> threads
) :
_ssrc(mediaContent.ssrc),
_call(call),
_channelManager(channelManager),
_audioSource(audioSource) {
cricket::AudioOptions audioOptions;
bool _disableOutgoingAudioProcessing = false;
if (_disableOutgoingAudioProcessing) {
audioOptions.echo_cancellation = false;
audioOptions.noise_suppression = false;
audioOptions.auto_gain_control = false;
audioOptions.highpass_filter = false;
audioOptions.typing_detection = false;
audioOptions.experimental_agc = false;
audioOptions.experimental_ns = false;
audioOptions.residual_echo_detector = false;
} else {
audioOptions.echo_cancellation = true;
audioOptions.noise_suppression = true;
}
std::vector<std::string> streamIds;
streamIds.push_back("1");
_outgoingAudioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), rtpTransport, threads->getMediaThread(), "audio0", false, NativeNetworkingImpl::getDefaulCryptoOptions(), uniqueRandomIdGenerator, audioOptions);
std::vector<cricket::AudioCodec> codecs;
for (const auto &codec : mediaContent.codecs) {
if (codec.name == "opus") {
auto mutableCodec = codec;
const uint8_t opusMinBitrateKbps = 16;
const uint8_t opusMaxBitrateKbps = 32;
const uint8_t opusStartBitrateKbps = 32;
const uint8_t opusPTimeMs = 60;
mutableCodec.SetParam(cricket::kCodecParamMinBitrate, opusMinBitrateKbps);
mutableCodec.SetParam(cricket::kCodecParamStartBitrate, opusStartBitrateKbps);
mutableCodec.SetParam(cricket::kCodecParamMaxBitrate, opusMaxBitrateKbps);
mutableCodec.SetParam(cricket::kCodecParamUseInbandFec, 1);
mutableCodec.SetParam(cricket::kCodecParamPTime, opusPTimeMs);
codecs.push_back(std::move(mutableCodec));
}
}
auto outgoingAudioDescription = std::make_unique<cricket::AudioContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
outgoingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id));
}
outgoingAudioDescription->set_rtcp_mux(true);
outgoingAudioDescription->set_rtcp_reduced_size(true);
outgoingAudioDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly);
outgoingAudioDescription->set_codecs(codecs);
outgoingAudioDescription->set_bandwidth(1032000);
outgoingAudioDescription->AddStream(cricket::StreamParams::CreateLegacy(_ssrc));
auto incomingAudioDescription = std::make_unique<cricket::AudioContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
incomingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id));
}
incomingAudioDescription->set_rtcp_mux(true);
incomingAudioDescription->set_rtcp_reduced_size(true);
incomingAudioDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly);
incomingAudioDescription->set_codecs(codecs);
incomingAudioDescription->set_bandwidth(1032000);
_outgoingAudioChannel->SetPayloadTypeDemuxingEnabled(false);
_outgoingAudioChannel->SetLocalContent(outgoingAudioDescription.get(), webrtc::SdpType::kOffer, nullptr);
_outgoingAudioChannel->SetRemoteContent(incomingAudioDescription.get(), webrtc::SdpType::kAnswer, nullptr);
_outgoingAudioChannel->SignalSentPacket().connect(this, &OutgoingAudioChannel::OnSentPacket_w);
//_outgoingAudioChannel->UpdateRtpTransport(nullptr);
setIsMuted(false);
}
~OutgoingAudioChannel() {
_outgoingAudioChannel->SignalSentPacket().disconnect(this);
_outgoingAudioChannel->media_channel()->SetAudioSend(_ssrc, false, nullptr, _audioSource);
_outgoingAudioChannel->Enable(false);
_channelManager->DestroyVoiceChannel(_outgoingAudioChannel);
_outgoingAudioChannel = nullptr;
}
void setIsMuted(bool isMuted) {
if (_isMuted != isMuted) {
_isMuted = false;
_outgoingAudioChannel->Enable(!_isMuted);
_outgoingAudioChannel->media_channel()->SetAudioSend(_ssrc, !_isMuted, nullptr, _audioSource);
}
}
private:
void OnSentPacket_w(const rtc::SentPacket& sent_packet) {
_call->OnSentPacket(sent_packet);
}
private:
uint32_t _ssrc = 0;
webrtc::Call *_call = nullptr;
cricket::ChannelManager *_channelManager = nullptr;
webrtc::LocalAudioSinkAdapter *_audioSource = nullptr;
cricket::VoiceChannel *_outgoingAudioChannel = nullptr;
bool _isMuted = true;
};
class IncomingV2AudioChannel : public sigslot::has_slots<> {
public:
IncomingV2AudioChannel(
cricket::ChannelManager *channelManager,
webrtc::Call *call,
webrtc::RtpTransport *rtpTransport,
rtc::UniqueRandomIdGenerator *randomIdGenerator,
NegotiatedMediaContent<cricket::AudioCodec> const &mediaContent,
std::shared_ptr<Threads> threads) :
_ssrc(mediaContent.ssrc),
_channelManager(channelManager),
_call(call) {
_creationTimestamp = rtc::TimeMillis();
cricket::AudioOptions audioOptions;
audioOptions.audio_jitter_buffer_fast_accelerate = true;
audioOptions.audio_jitter_buffer_min_delay_ms = 50;
std::string streamId = std::string("stream1");
_audioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), rtpTransport, threads->getMediaThread(), "0", false, NativeNetworkingImpl::getDefaulCryptoOptions(), randomIdGenerator, audioOptions);
auto audioCodecs = mediaContent.codecs;
auto outgoingAudioDescription = std::make_unique<cricket::AudioContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
outgoingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id));
}
outgoingAudioDescription->set_rtcp_mux(true);
outgoingAudioDescription->set_rtcp_reduced_size(true);
outgoingAudioDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly);
outgoingAudioDescription->set_codecs(audioCodecs);
outgoingAudioDescription->set_bandwidth(1032000);
auto incomingAudioDescription = std::make_unique<cricket::AudioContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
incomingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id));
}
incomingAudioDescription->set_rtcp_mux(true);
incomingAudioDescription->set_rtcp_reduced_size(true);
incomingAudioDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly);
incomingAudioDescription->set_codecs(audioCodecs);
incomingAudioDescription->set_bandwidth(1032000);
cricket::StreamParams streamParams = cricket::StreamParams::CreateLegacy(mediaContent.ssrc);
streamParams.set_stream_ids({ streamId });
incomingAudioDescription->AddStream(streamParams);
_audioChannel->SetPayloadTypeDemuxingEnabled(false);
_audioChannel->SetLocalContent(outgoingAudioDescription.get(), webrtc::SdpType::kOffer, nullptr);
_audioChannel->SetRemoteContent(incomingAudioDescription.get(), webrtc::SdpType::kAnswer, nullptr);
outgoingAudioDescription.reset();
incomingAudioDescription.reset();
//std::unique_ptr<AudioSinkImpl> audioLevelSink(new AudioSinkImpl(onAudioLevelUpdated, _ssrc, std::move(onAudioFrame)));
//_audioChannel->media_channel()->SetRawAudioSink(ssrc.networkSsrc, std::move(audioLevelSink));
_audioChannel->SignalSentPacket().connect(this, &IncomingV2AudioChannel::OnSentPacket_w);
//_audioChannel->UpdateRtpTransport(nullptr);
_audioChannel->Enable(true);
}
~IncomingV2AudioChannel() {
_audioChannel->SignalSentPacket().disconnect(this);
_audioChannel->Enable(false);
_channelManager->DestroyVoiceChannel(_audioChannel);
_audioChannel = nullptr;
}
void setVolume(double value) {
_audioChannel->media_channel()->SetOutputVolume(_ssrc, value);
}
void updateActivity() {
_activityTimestamp = rtc::TimeMillis();
}
int64_t getActivity() {
return _activityTimestamp;
}
private:
void OnSentPacket_w(const rtc::SentPacket& sent_packet) {
_call->OnSentPacket(sent_packet);
}
private:
uint32_t _ssrc = 0;
// Memory is managed by _channelManager
cricket::VoiceChannel *_audioChannel = nullptr;
// Memory is managed externally
cricket::ChannelManager *_channelManager = nullptr;
webrtc::Call *_call = nullptr;
int64_t _creationTimestamp = 0;
int64_t _activityTimestamp = 0;
};
class OutgoingVideoChannel : public sigslot::has_slots<>, public std::enable_shared_from_this<OutgoingVideoChannel> {
public:
static absl::optional<signaling::MediaContent> createOutgoingContentDescription(std::vector<webrtc::SdpVideoFormat> const &availableVideoFormats) {
signaling::MediaContent mediaContent;
auto generator = std::mt19937(std::random_device()());
auto distribution = std::uniform_int_distribution<uint32_t>();
do {
mediaContent.ssrc = distribution(generator) & 0x7fffffffU;
} while (!mediaContent.ssrc);
mediaContent.rtpExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, 2);
mediaContent.rtpExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, 3);
mediaContent.rtpExtensions.emplace_back(webrtc::RtpExtension::kVideoRotationUri, 13);
signaling::SsrcGroup fidGroup;
fidGroup.semantics = "FID";
fidGroup.ssrcs.push_back(mediaContent.ssrc);
fidGroup.ssrcs.push_back(mediaContent.ssrc + 1);
mediaContent.ssrcGroups.push_back(std::move(fidGroup));
const auto videoFormats = generateAvailableVideoFormats(availableVideoFormats);
for (const auto &format : videoFormats) {
signaling::PayloadType videoPayload;
videoPayload.id = format.videoCodec.id;
videoPayload.name = format.videoCodec.name;
videoPayload.clockrate = format.videoCodec.clockrate;
videoPayload.channels = 0;
std::vector<signaling::FeedbackType> videoFeedbackTypes;
signaling::FeedbackType fbGoogRemb;
fbGoogRemb.type = "goog-remb";
videoFeedbackTypes.push_back(fbGoogRemb);
signaling::FeedbackType fbTransportCc;
fbTransportCc.type = "transport-cc";
videoFeedbackTypes.push_back(fbTransportCc);
signaling::FeedbackType fbCcmFir;
fbCcmFir.type = "ccm";
fbCcmFir.subtype = "fir";
videoFeedbackTypes.push_back(fbCcmFir);
signaling::FeedbackType fbNack;
fbNack.type = "nack";
videoFeedbackTypes.push_back(fbNack);
signaling::FeedbackType fbNackPli;
fbNackPli.type = "nack";
fbNackPli.subtype = "pli";
videoFeedbackTypes.push_back(fbNackPli);
videoPayload.feedbackTypes = videoFeedbackTypes;
videoPayload.parameters = {};
mediaContent.payloadTypes.push_back(std::move(videoPayload));
if (format.rtxCodec) {
signaling::PayloadType rtxPayload;
rtxPayload.id = format.rtxCodec->id;
rtxPayload.name = format.rtxCodec->name;
rtxPayload.clockrate = format.rtxCodec->clockrate;
rtxPayload.parameters.push_back(std::make_pair("apt", intToString(videoPayload.id)));
mediaContent.payloadTypes.push_back(std::move(rtxPayload));
}
}
return mediaContent;
}
public:
OutgoingVideoChannel(
std::shared_ptr<Threads> threads,
cricket::ChannelManager *channelManager,
webrtc::Call *call,
webrtc::RtpTransport *rtpTransport,
rtc::UniqueRandomIdGenerator *randomIdGenerator,
webrtc::VideoBitrateAllocatorFactory *videoBitrateAllocatorFactory,
std::function<void()> rotationUpdated,
NegotiatedMediaContent<cricket::VideoCodec> const &mediaContent
) :
_threads(threads),
_mainSsrc(mediaContent.ssrc),
_call(call),
_channelManager(channelManager),
_rotationUpdated(rotationUpdated) {
_outgoingVideoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), rtpTransport, threads->getMediaThread(), "out1", false, NativeNetworkingImpl::getDefaulCryptoOptions(), randomIdGenerator, cricket::VideoOptions(), videoBitrateAllocatorFactory);
auto videoCodecs = mediaContent.codecs;
auto outgoingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
outgoingVideoDescription->AddRtpHeaderExtension(rtpExtension);
}
outgoingVideoDescription->set_rtcp_mux(true);
outgoingVideoDescription->set_rtcp_reduced_size(true);
outgoingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly);
outgoingVideoDescription->set_codecs(videoCodecs);
outgoingVideoDescription->set_bandwidth(1032000);
cricket::StreamParams videoSendStreamParams;
for (const auto &ssrcGroup : mediaContent.ssrcGroups) {
for (auto ssrc : ssrcGroup.ssrcs) {
videoSendStreamParams.ssrcs.push_back(ssrc);
}
cricket::SsrcGroup mappedGroup(ssrcGroup.semantics, ssrcGroup.ssrcs);
videoSendStreamParams.ssrc_groups.push_back(std::move(mappedGroup));
}
videoSendStreamParams.cname = "cname";
outgoingVideoDescription->AddStream(videoSendStreamParams);
auto incomingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id));
}
incomingVideoDescription->set_rtcp_mux(true);
incomingVideoDescription->set_rtcp_reduced_size(true);
incomingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly);
incomingVideoDescription->set_codecs(videoCodecs);
incomingVideoDescription->set_bandwidth(1032000);
_outgoingVideoChannel->SetPayloadTypeDemuxingEnabled(false);
_outgoingVideoChannel->SetLocalContent(outgoingVideoDescription.get(), webrtc::SdpType::kOffer, nullptr);
_outgoingVideoChannel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, nullptr);
webrtc::RtpParameters rtpParameters = _outgoingVideoChannel->media_channel()->GetRtpSendParameters(mediaContent.ssrc);
_outgoingVideoChannel->media_channel()->SetRtpSendParameters(mediaContent.ssrc, rtpParameters);
_outgoingVideoChannel->SignalSentPacket().connect(this, &OutgoingVideoChannel::OnSentPacket_w);
//_outgoingVideoChannel->UpdateRtpTransport(nullptr);
_outgoingVideoChannel->Enable(false);
_outgoingVideoChannel->media_channel()->SetVideoSend(mediaContent.ssrc, NULL, nullptr);
}
~OutgoingVideoChannel() {
_outgoingVideoChannel->SignalSentPacket().disconnect(this);
_outgoingVideoChannel->media_channel()->SetVideoSend(_mainSsrc, nullptr, nullptr);
_outgoingVideoChannel->Enable(false);
_channelManager->DestroyVideoChannel(_outgoingVideoChannel);
_outgoingVideoChannel = nullptr;
}
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) {
_videoCapture = videoCapture;
if (_videoCapture) {
_outgoingVideoChannel->Enable(true);
auto videoCaptureImpl = GetVideoCaptureAssumingSameThread(_videoCapture.get());
_outgoingVideoChannel->media_channel()->SetVideoSend(_mainSsrc, NULL, videoCaptureImpl->source());
const auto weak = std::weak_ptr<OutgoingVideoChannel>(shared_from_this());
videoCaptureImpl->setRotationUpdated([threads = _threads, weak](int angle) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
signaling::MediaStateMessage::VideoRotation videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation0;
switch (angle) {
case 0: {
videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation0;
break;
}
case 90: {
videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation90;
break;
}
case 180: {
videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation180;
break;
}
case 270: {
videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation270;
break;
}
default: {
videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation0;
break;
}
}
if (strong->_videoRotation != videoRotation) {
strong->_videoRotation = videoRotation;
strong->_rotationUpdated();
}
});
});
switch (videoCaptureImpl->getRotation()) {
case 0: {
_videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation0;
break;
}
case 90: {
_videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation90;
break;
}
case 180: {
_videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation180;
break;
}
case 270: {
_videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation270;
break;
}
default: {
_videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation0;
break;
}
}
} else {
_videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation0;
_outgoingVideoChannel->Enable(false);
_outgoingVideoChannel->media_channel()->SetVideoSend(_mainSsrc, NULL, nullptr);
}
}
public:
std::shared_ptr<VideoCaptureInterface> videoCapture() {
return _videoCapture;
}
signaling::MediaStateMessage::VideoRotation getRotation() {
return _videoRotation;
}
private:
void OnSentPacket_w(const rtc::SentPacket& sent_packet) {
_call->OnSentPacket(sent_packet);
}
private:
std::shared_ptr<Threads> _threads;
uint32_t _mainSsrc = 0;
webrtc::Call *_call = nullptr;
cricket::ChannelManager *_channelManager = nullptr;
cricket::VideoChannel *_outgoingVideoChannel = nullptr;
std::function<void()> _rotationUpdated;
std::shared_ptr<VideoCaptureInterface> _videoCapture;
signaling::MediaStateMessage::VideoRotation _videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation0;
};
class VideoSinkImpl : public rtc::VideoSinkInterface<webrtc::VideoFrame> {
public:
VideoSinkImpl() {
}
virtual ~VideoSinkImpl() {
}
virtual void OnFrame(const webrtc::VideoFrame& frame) override {
//_lastFrame = frame;
for (int i = (int)(_sinks.size()) - 1; i >= 0; i--) {
auto strong = _sinks[i].lock();
if (!strong) {
_sinks.erase(_sinks.begin() + i);
} else {
strong->OnFrame(frame);
}
}
}
virtual void OnDiscardedFrame() override {
for (int i = (int)(_sinks.size()) - 1; i >= 0; i--) {
auto strong = _sinks[i].lock();
if (!strong) {
_sinks.erase(_sinks.begin() + i);
} else {
strong->OnDiscardedFrame();
}
}
}
void addSink(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> impl) {
_sinks.push_back(impl);
if (_lastFrame) {
auto strong = impl.lock();
if (strong) {
strong->OnFrame(_lastFrame.value());
}
}
}
private:
std::vector<std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>>> _sinks;
absl::optional<webrtc::VideoFrame> _lastFrame;
};
class IncomingV2VideoChannel : public sigslot::has_slots<> {
public:
IncomingV2VideoChannel(
cricket::ChannelManager *channelManager,
webrtc::Call *call,
webrtc::RtpTransport *rtpTransport,
rtc::UniqueRandomIdGenerator *randomIdGenerator,
NegotiatedMediaContent<cricket::VideoCodec> const &mediaContent,
std::shared_ptr<Threads> threads) :
_channelManager(channelManager),
_call(call) {
_videoSink.reset(new VideoSinkImpl());
std::string streamId = "1";
_videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory();
_videoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), rtpTransport, threads->getMediaThread(), "1", false, NativeNetworkingImpl::getDefaulCryptoOptions(), randomIdGenerator, cricket::VideoOptions(), _videoBitrateAllocatorFactory.get());
std::vector<cricket::VideoCodec> videoCodecs = mediaContent.codecs;
auto outgoingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
outgoingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id));
}
outgoingVideoDescription->set_rtcp_mux(true);
outgoingVideoDescription->set_rtcp_reduced_size(true);
outgoingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly);
outgoingVideoDescription->set_codecs(videoCodecs);
outgoingVideoDescription->set_bandwidth(1032000);
cricket::StreamParams videoRecvStreamParams;
_mainVideoSsrc = mediaContent.ssrc;
std::vector<uint32_t> allSsrcs;
for (const auto &group : mediaContent.ssrcGroups) {
for (auto ssrc : group.ssrcs) {
if (std::find(allSsrcs.begin(), allSsrcs.end(), ssrc) == allSsrcs.end()) {
allSsrcs.push_back(ssrc);
}
}
cricket::SsrcGroup parsedGroup(group.semantics, group.ssrcs);
videoRecvStreamParams.ssrc_groups.push_back(parsedGroup);
}
videoRecvStreamParams.ssrcs = allSsrcs;
videoRecvStreamParams.cname = "cname";
videoRecvStreamParams.set_stream_ids({ streamId });
auto incomingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(rtpExtension.uri, rtpExtension.id));
}
incomingVideoDescription->set_rtcp_mux(true);
incomingVideoDescription->set_rtcp_reduced_size(true);
incomingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly);
incomingVideoDescription->set_codecs(videoCodecs);
incomingVideoDescription->set_bandwidth(1032000);
incomingVideoDescription->AddStream(videoRecvStreamParams);
_videoChannel->SetPayloadTypeDemuxingEnabled(false);
_videoChannel->SetLocalContent(outgoingVideoDescription.get(), webrtc::SdpType::kOffer, nullptr);
_videoChannel->SetRemoteContent(incomingVideoDescription.get(), webrtc::SdpType::kAnswer, nullptr);
_videoChannel->media_channel()->SetSink(_mainVideoSsrc, _videoSink.get());
_videoChannel->SignalSentPacket().connect(this, &IncomingV2VideoChannel::OnSentPacket_w);
//_videoChannel->UpdateRtpTransport(nullptr);
_videoChannel->Enable(true);
}
~IncomingV2VideoChannel() {
_videoChannel->Enable(false);
_channelManager->DestroyVideoChannel(_videoChannel);
_videoChannel = nullptr;
}
void addSink(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> impl) {
_videoSink->addSink(impl);
}
private:
void OnSentPacket_w(const rtc::SentPacket& sent_packet) {
_call->OnSentPacket(sent_packet);
}
private:
uint32_t _mainVideoSsrc = 0;
std::unique_ptr<VideoSinkImpl> _videoSink;
std::unique_ptr<webrtc::VideoBitrateAllocatorFactory> _videoBitrateAllocatorFactory;
// Memory is managed by _channelManager
cricket::VideoChannel *_videoChannel;
// Memory is managed externally
cricket::ChannelManager *_channelManager = nullptr;
webrtc::Call *_call = nullptr;
};
} // namespace
class InstanceV2ImplInternal : public std::enable_shared_from_this<InstanceV2ImplInternal> {
public:
InstanceV2ImplInternal(Descriptor &&descriptor, std::shared_ptr<Threads> threads) :
_threads(threads),
_rtcServers(descriptor.rtcServers),
_encryptionKey(std::move(descriptor.encryptionKey)),
_stateUpdated(descriptor.stateUpdated),
_signalBarsUpdated(descriptor.signalBarsUpdated),
_audioLevelUpdated(descriptor.audioLevelUpdated),
_remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated),
_remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated),
_remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated),
_signalingDataEmitted(descriptor.signalingDataEmitted),
_createAudioDeviceModule(descriptor.createAudioDeviceModule),
_eventLog(std::make_unique<webrtc::RtcEventLogNull>()),
_taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()),
_videoCapture(descriptor.videoCapture) {
}
~InstanceV2ImplInternal() {
_networking->perform(RTC_FROM_HERE, [](NativeNetworkingImpl *networking) {
networking->stop();
});
_threads->getNetworkThread()->Invoke<void>(RTC_FROM_HERE, []() {
});
}
void start() {
const auto weak = std::weak_ptr<InstanceV2ImplInternal>(shared_from_this());
_networking.reset(new ThreadLocalObject<NativeNetworkingImpl>(_threads->getNetworkThread(), [weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers]() {
return new NativeNetworkingImpl((NativeNetworkingImpl::Configuration){
.isOutgoing = isOutgoing,
.enableStunMarking = false,
.enableTCP = false,
.enableP2P = true,
.rtcServers = rtcServers,
.stateUpdated = [threads, weak](const NativeNetworkingImpl::State &state) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->onNetworkStateUpdated(state);
});
},
.candidateGathered = [threads, weak](const cricket::Candidate &candidate) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->sendCandidate(candidate);
});
},
.transportMessageReceived = [threads, weak](rtc::CopyOnWriteBuffer const &packet, bool isMissing) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
});
},
.rtcpPacketReceived = [threads, weak](rtc::CopyOnWriteBuffer const &packet, int64_t timestamp) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->_call->Receiver()->DeliverPacket(webrtc::MediaType::ANY, packet, timestamp);
});
},
.dataChannelStateUpdated = [threads, weak](bool isDataChannelOpen) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->onDataChannelStateUpdated(isDataChannelOpen);
});
},
.dataChannelMessageReceived = [threads, weak](std::string const &message) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->onDataChannelMessage(message);
});
},
.threads = threads
});
}));
PlatformInterface::SharedInstance()->configurePlatformAudio();
cricket::MediaEngineDependencies mediaDeps;
mediaDeps.task_queue_factory = _taskQueueFactory.get();
mediaDeps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory<webrtc::AudioEncoderOpus, webrtc::AudioEncoderL16>();
mediaDeps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory<webrtc::AudioDecoderOpus, webrtc::AudioDecoderL16>();
mediaDeps.video_encoder_factory = PlatformInterface::SharedInstance()->makeVideoEncoderFactory();
mediaDeps.video_decoder_factory = PlatformInterface::SharedInstance()->makeVideoDecoderFactory();
_audioDeviceModule = createAudioDeviceModule();
if (!_audioDeviceModule) {
return;
}
mediaDeps.adm = _audioDeviceModule;
_availableVideoFormats = mediaDeps.video_encoder_factory->GetSupportedFormats();
std::unique_ptr<cricket::MediaEngineInterface> mediaEngine = cricket::CreateMediaEngine(std::move(mediaDeps));
_channelManager = cricket::ChannelManager::Create(
std::move(mediaEngine),
std::make_unique<cricket::RtpDataEngine>(),
true,
_threads->getMediaThread(),
_threads->getNetworkThread()
);
//setAudioInputDevice(_initialInputDeviceId);
//setAudioOutputDevice(_initialOutputDeviceId);
webrtc::Call::Config callConfig(_eventLog.get());
callConfig.task_queue_factory = _taskQueueFactory.get();
callConfig.trials = &_fieldTrials;
callConfig.audio_state = _channelManager->media_engine()->voice().GetAudioState();
_call.reset(webrtc::Call::Create(callConfig));
_uniqueRandomIdGenerator.reset(new rtc::UniqueRandomIdGenerator());
_threads->getNetworkThread()->Invoke<void>(RTC_FROM_HERE, [this]() {
_rtpTransport = _networking->getSyncAssumingSameThread()->getRtpTransport();
});
_videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory();
_networking->perform(RTC_FROM_HERE, [](NativeNetworkingImpl *networking) {
networking->start();
});
if (_videoCapture) {
setVideoCapture(_videoCapture);
}
beginSignaling();
adjustBitratePreferences(true);
}
void sendSignalingMessage(signaling::Message const &message) {
auto data = message.serialize();
RTC_LOG(LS_INFO) << "sendSignalingMessage: " << std::string(data.begin(), data.end());
if (_signalingEncryption) {
if (const auto encryptedData = _signalingEncryption->encryptOutgoing(data)) {
_signalingDataEmitted(std::vector<uint8_t>(encryptedData->data(), encryptedData->data() + encryptedData->size()));
} else {
RTC_LOG(LS_ERROR) << "sendSignalingMessage: failed to encrypt payload";
}
} else {
_signalingDataEmitted(data);
}
}
void beginSignaling() {
_signalingEncryption.reset(new SignalingEncryption(_encryptionKey));
if (_encryptionKey.isOutgoing) {
_outgoingAudioContent = OutgoingAudioChannel::createOutgoingContentDescription();
_outgoingVideoContent = OutgoingVideoChannel::createOutgoingContentDescription(_availableVideoFormats);
sendInitialSetup();
}
}
void createNegotiatedChannels() {
if (_negotiatedOutgoingVideoContent) {
const auto weak = std::weak_ptr<InstanceV2ImplInternal>(shared_from_this());
_outgoingVideoChannel.reset(new OutgoingVideoChannel(
_threads,
_channelManager.get(),
_call.get(),
_rtpTransport,
_uniqueRandomIdGenerator.get(),
_videoBitrateAllocatorFactory.get(),
[threads = _threads, weak]() {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->sendMediaState();
});
},
_negotiatedOutgoingVideoContent.value()
));
if (_videoCapture) {
_outgoingVideoChannel->setVideoCapture(_videoCapture);
}
}
if (_negotiatedOutgoingAudioContent) {
_outgoingAudioChannel.reset(new OutgoingAudioChannel(
_call.get(),
_channelManager.get(),
_uniqueRandomIdGenerator.get(),
&_audioSource,
_rtpTransport,
_negotiatedOutgoingAudioContent.value(),
_threads
));
}
adjustBitratePreferences(true);
}
void sendInitialSetup() {
const auto weak = std::weak_ptr<InstanceV2ImplInternal>(shared_from_this());
_networking->perform(RTC_FROM_HERE, [weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing](NativeNetworkingImpl *networking) {
auto localFingerprint = networking->getLocalFingerprint();
std::string hash = localFingerprint->algorithm;
std::string fingerprint = localFingerprint->GetRfc4572Fingerprint();
std::string setup;
if (isOutgoing) {
setup = "actpass";
} else {
setup = "passive";
}
auto localIceParams = networking->getLocalIceParameters();
std::string ufrag = localIceParams.ufrag;
std::string pwd = localIceParams.pwd;
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, ufrag, pwd, hash, fingerprint, setup, localIceParams]() {
const auto strong = weak.lock();
if (!strong) {
return;
}
signaling::InitialSetupMessage data;
if (strong->_outgoingAudioContent) {
data.audio = strong->_outgoingAudioContent.value();
}
if (strong->_outgoingVideoContent) {
data.video = strong->_outgoingVideoContent.value();
}
data.ufrag = ufrag;
data.pwd = pwd;
signaling::DtlsFingerprint dtlsFingerprint;
dtlsFingerprint.hash = hash;
dtlsFingerprint.fingerprint = fingerprint;
dtlsFingerprint.setup = setup;
data.fingerprints.push_back(std::move(dtlsFingerprint));
signaling::Message message;
message.data = std::move(data);
strong->sendSignalingMessage(message);
});
});
}
void receiveSignalingData(const std::vector<uint8_t> &data) {
std::vector<uint8_t> decryptedData;
if (_signalingEncryption) {
const auto rawDecryptedData = _signalingEncryption->decryptIncoming(data);
if (!rawDecryptedData) {
RTC_LOG(LS_ERROR) << "receiveSignalingData: could not decrypt payload";
return;
}
decryptedData = std::vector<uint8_t>(rawDecryptedData->data(), rawDecryptedData->data() + rawDecryptedData->size());
} else {
decryptedData = data;
}
processSignalingData(decryptedData);
}
void processSignalingData(const std::vector<uint8_t> &data) {
RTC_LOG(LS_INFO) << "processSignalingData: " << std::string(data.begin(), data.end());
const auto message = signaling::Message::parse(data);
if (!message) {
return;
}
const auto messageData = &message->data;
if (const auto initialSetup = absl::get_if<signaling::InitialSetupMessage>(messageData)) {
PeerIceParameters remoteIceParameters;
remoteIceParameters.ufrag = initialSetup->ufrag;
remoteIceParameters.pwd = initialSetup->pwd;
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
std::string sslSetup;
if (initialSetup->fingerprints.size() != 0) {
fingerprint = rtc::SSLFingerprint::CreateUniqueFromRfc4572(initialSetup->fingerprints[0].hash, initialSetup->fingerprints[0].fingerprint);
sslSetup = initialSetup->fingerprints[0].setup;
}
_networking->perform(RTC_FROM_HERE, [threads = _threads, remoteIceParameters = std::move(remoteIceParameters), fingerprint = std::move(fingerprint), sslSetup = std::move(sslSetup)](NativeNetworkingImpl *networking) {
networking->setRemoteParams(remoteIceParameters, fingerprint.get(), sslSetup);
});
if (const auto audio = initialSetup->audio) {
if (_encryptionKey.isOutgoing) {
if (_outgoingAudioContent) {
_negotiatedOutgoingAudioContent = negotiateMediaContent<cricket::AudioCodec>(_outgoingAudioContent.value(), _outgoingAudioContent.value(), audio.value(), false);
const auto incomingAudioContent = negotiateMediaContent<cricket::AudioCodec>(audio.value(), _outgoingAudioContent.value(), audio.value(), false);
signaling::MediaContent outgoingAudioContent;
outgoingAudioContent.ssrc = _outgoingAudioContent->ssrc;
outgoingAudioContent.ssrcGroups = _outgoingAudioContent->ssrcGroups;
outgoingAudioContent.rtpExtensions = _negotiatedOutgoingAudioContent->rtpExtensions;
outgoingAudioContent.payloadTypes = getPayloadTypesFromAudioCodecs(_negotiatedOutgoingAudioContent->codecs);
_outgoingAudioContent = std::move(outgoingAudioContent);
_incomingAudioChannel.reset(new IncomingV2AudioChannel(
_channelManager.get(),
_call.get(),
_rtpTransport,
_uniqueRandomIdGenerator.get(),
incomingAudioContent,
_threads
));
}
} else {
const auto generatedOutgoingContent = OutgoingAudioChannel::createOutgoingContentDescription();
if (generatedOutgoingContent) {
_negotiatedOutgoingAudioContent = negotiateMediaContent<cricket::AudioCodec>(generatedOutgoingContent.value(), generatedOutgoingContent.value(), audio.value(), true);
const auto incomingAudioContent = negotiateMediaContent<cricket::AudioCodec>(audio.value(), generatedOutgoingContent.value(), audio.value(), true);
if (_negotiatedOutgoingAudioContent) {
signaling::MediaContent outgoingAudioContent;
outgoingAudioContent.ssrc = generatedOutgoingContent->ssrc;
outgoingAudioContent.ssrcGroups = generatedOutgoingContent->ssrcGroups;
outgoingAudioContent.rtpExtensions = _negotiatedOutgoingAudioContent->rtpExtensions;
outgoingAudioContent.payloadTypes = getPayloadTypesFromAudioCodecs(_negotiatedOutgoingAudioContent->codecs);
_outgoingAudioContent = std::move(outgoingAudioContent);
_incomingAudioChannel.reset(new IncomingV2AudioChannel(
_channelManager.get(),
_call.get(),
_rtpTransport,
_uniqueRandomIdGenerator.get(),
incomingAudioContent,
_threads
));
}
}
}
}
if (const auto video = initialSetup->video) {
if (_encryptionKey.isOutgoing) {
if (_outgoingVideoContent) {
_negotiatedOutgoingVideoContent = negotiateMediaContent<cricket::VideoCodec>(_outgoingVideoContent.value(), _outgoingVideoContent.value(), video.value(), false);
const auto incomingVideoContent = negotiateMediaContent<cricket::VideoCodec>(video.value(), _outgoingVideoContent.value(), video.value(), false);
signaling::MediaContent outgoingVideoContent;
outgoingVideoContent.ssrc = _outgoingVideoContent->ssrc;
outgoingVideoContent.ssrcGroups = _outgoingVideoContent->ssrcGroups;
outgoingVideoContent.rtpExtensions = _negotiatedOutgoingVideoContent->rtpExtensions;
outgoingVideoContent.payloadTypes = getPayloadTypesFromVideoCodecs(_negotiatedOutgoingVideoContent->codecs);
_outgoingVideoContent = std::move(outgoingVideoContent);
_incomingVideoChannel.reset(new IncomingV2VideoChannel(
_channelManager.get(),
_call.get(),
_rtpTransport,
_uniqueRandomIdGenerator.get(),
incomingVideoContent,
_threads
));
}
} else {
const auto generatedOutgoingContent = OutgoingVideoChannel::createOutgoingContentDescription(_availableVideoFormats);
if (generatedOutgoingContent) {
_negotiatedOutgoingVideoContent = negotiateMediaContent<cricket::VideoCodec>(generatedOutgoingContent.value(), generatedOutgoingContent.value(), video.value(), true);
const auto incomingVideoContent = negotiateMediaContent<cricket::VideoCodec>(video.value(), generatedOutgoingContent.value(), video.value(), true);
if (_negotiatedOutgoingVideoContent) {
signaling::MediaContent outgoingVideoContent;
outgoingVideoContent.ssrc = generatedOutgoingContent->ssrc;
outgoingVideoContent.ssrcGroups = generatedOutgoingContent->ssrcGroups;
outgoingVideoContent.rtpExtensions = _negotiatedOutgoingVideoContent->rtpExtensions;
outgoingVideoContent.payloadTypes = getPayloadTypesFromVideoCodecs(_negotiatedOutgoingVideoContent->codecs);
_outgoingVideoContent = std::move(outgoingVideoContent);
_incomingVideoChannel.reset(new IncomingV2VideoChannel(
_channelManager.get(),
_call.get(),
_rtpTransport,
_uniqueRandomIdGenerator.get(),
incomingVideoContent,
_threads
));
}
}
}
}
createNegotiatedChannels();
if (!_encryptionKey.isOutgoing) {
sendInitialSetup();
}
_handshakeCompleted = true;
commitPendingIceCandidates();
} else if (const auto candidatesList = absl::get_if<signaling::CandidatesMessage>(messageData)) {
for (const auto &candidate : candidatesList->iceCandidates) {
webrtc::JsepIceCandidate parseCandidate{ std::string(), 0 };
if (!parseCandidate.Initialize(candidate.sdpString, nullptr)) {
RTC_LOG(LS_ERROR) << "Could not parse candidate: " << candidate.sdpString;
continue;
}
_pendingIceCandidates.push_back(parseCandidate.candidate());
}
if (_handshakeCompleted) {
commitPendingIceCandidates();
}
} else if (const auto mediaState = absl::get_if<signaling::MediaStateMessage>(messageData)) {
AudioState mappedAudioState;
if (mediaState->isMuted) {
mappedAudioState = AudioState::Muted;
} else {
mappedAudioState = AudioState::Active;
}
VideoState mappedVideoState;
switch (mediaState->videoState) {
case signaling::MediaStateMessage::VideoState::Inactive: {
mappedVideoState = VideoState::Inactive;
break;
}
case signaling::MediaStateMessage::VideoState::Suspended: {
mappedVideoState = VideoState::Paused;
break;
}
case signaling::MediaStateMessage::VideoState::Active: {
mappedVideoState = VideoState::Active;
break;
}
default: {
RTC_FATAL() << "Unknown videoState";
break;
}
}
if (_remoteMediaStateUpdated) {
_remoteMediaStateUpdated(mappedAudioState, mappedVideoState);
}
if (_remoteBatteryLevelIsLowUpdated) {
_remoteBatteryLevelIsLowUpdated(mediaState->isBatteryLow);
}
}
}
void commitPendingIceCandidates() {
if (_pendingIceCandidates.size() == 0) {
return;
}
_networking->perform(RTC_FROM_HERE, [threads = _threads, parsedCandidates = _pendingIceCandidates](NativeNetworkingImpl *networking) {
networking->addCandidates(parsedCandidates);
});
_pendingIceCandidates.clear();
}
void onNetworkStateUpdated(NativeNetworkingImpl::State const &state) {
State mappedState;
if (state.isReadyToSendData) {
mappedState = State::Established;
} else {
mappedState = State::Reconnecting;
}
_stateUpdated(mappedState);
}
void onDataChannelStateUpdated(bool isDataChannelOpen) {
if (_isDataChannelOpen != isDataChannelOpen) {
_isDataChannelOpen = isDataChannelOpen;
if (_isDataChannelOpen) {
sendMediaState();
}
}
}
void sendDataChannelMessage(signaling::Message const &message) {
if (!_isDataChannelOpen) {
RTC_LOG(LS_ERROR) << "sendDataChannelMessage called, but data channel is not open";
return;
}
auto data = message.serialize();
std::string stringData(data.begin(), data.end());
RTC_LOG(LS_INFO) << "sendDataChannelMessage: " << stringData;
_networking->perform(RTC_FROM_HERE, [stringData = std::move(stringData)](NativeNetworkingImpl *networking) {
networking->sendDataChannelMessage(stringData);
});
}
void onDataChannelMessage(std::string const &message) {
RTC_LOG(LS_INFO) << "dataChannelMessage received: " << message;
std::vector<uint8_t> data(message.begin(), message.end());
processSignalingData(data);
}
void sendMediaState() {
if (!_isDataChannelOpen) {
return;
}
signaling::Message message;
signaling::MediaStateMessage data;
data.isMuted = _isMicrophoneMuted;
data.isBatteryLow = _isBatteryLow;
if (_outgoingVideoChannel) {
if (_outgoingVideoChannel->videoCapture()) {
data.videoState = signaling::MediaStateMessage::VideoState::Active;
} else{
data.videoState = signaling::MediaStateMessage::VideoState::Inactive;
}
data.videoRotation = _outgoingVideoChannel->getRotation();
} else {
data.videoState = signaling::MediaStateMessage::VideoState::Inactive;
data.videoRotation = signaling::MediaStateMessage::VideoRotation::Rotation0;
}
message.data = std::move(data);
sendDataChannelMessage(message);
}
void sendCandidate(const cricket::Candidate &candidate) {
cricket::Candidate patchedCandidate = candidate;
patchedCandidate.set_component(1);
signaling::CandidatesMessage data;
signaling::IceCandidate serializedCandidate;
webrtc::JsepIceCandidate iceCandidate{ std::string(), 0 };
iceCandidate.SetCandidate(patchedCandidate);
std::string serialized;
const auto success = iceCandidate.ToString(&serialized);
assert(success);
serializedCandidate.sdpString = serialized;
data.iceCandidates.push_back(std::move(serializedCandidate));
signaling::Message message;
message.data = std::move(data);
sendSignalingMessage(message);
}
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) {
_videoCapture = videoCapture;
if (_outgoingVideoChannel) {
_outgoingVideoChannel->setVideoCapture(videoCapture);
sendMediaState();
adjustBitratePreferences(true);
}
}
void setRequestedVideoAspect(float aspect) {
}
void setNetworkType(NetworkType networkType) {
}
void setMuteMicrophone(bool muteMicrophone) {
if (_isMicrophoneMuted != muteMicrophone) {
_isMicrophoneMuted = muteMicrophone;
if (_outgoingAudioChannel) {
_outgoingAudioChannel->setIsMuted(muteMicrophone);
}
sendMediaState();
}
}
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
if (_incomingVideoChannel) {
_incomingVideoChannel->addSink(sink);
}
}
void setAudioInputDevice(std::string id) {
}
void setAudioOutputDevice(std::string id) {
}
void setIsLowBatteryLevel(bool isLowBatteryLevel) {
if (_isBatteryLow != isLowBatteryLevel) {
_isBatteryLow = isLowBatteryLevel;
sendMediaState();
}
}
void stop(std::function<void(FinalState)> completion) {
completion({});
}
void adjustBitratePreferences(bool resetStartBitrate) {
webrtc::BitrateConstraints preferences;
if (_videoCapture) {
preferences.min_bitrate_bps = 64000;
if (resetStartBitrate) {
preferences.start_bitrate_bps = (100 + 800 + 32 + 100) * 1000;
}
preferences.max_bitrate_bps = (100 + 200 + 800 + 32 + 100) * 1000;
} else {
preferences.min_bitrate_bps = 32000;
if (resetStartBitrate) {
preferences.start_bitrate_bps = 32000;
}
preferences.max_bitrate_bps = 32000;
}
_call->GetTransportControllerSend()->SetSdpBitrateParameters(preferences);
}
private:
rtc::scoped_refptr<webrtc::AudioDeviceModule> createAudioDeviceModule() {
const auto create = [&](webrtc::AudioDeviceModule::AudioLayer layer) {
return webrtc::AudioDeviceModule::Create(
layer,
_taskQueueFactory.get());
};
const auto check = [&](const rtc::scoped_refptr<webrtc::AudioDeviceModule> &result) {
return (result && result->Init() == 0) ? result : nullptr;
};
if (_createAudioDeviceModule) {
if (const auto result = check(_createAudioDeviceModule(_taskQueueFactory.get()))) {
return result;
}
}
return check(create(webrtc::AudioDeviceModule::kPlatformDefaultAudio));
}
private:
std::shared_ptr<Threads> _threads;
std::vector<RtcServer> _rtcServers;
EncryptionKey _encryptionKey;
std::function<void(State)> _stateUpdated;
std::function<void(int)> _signalBarsUpdated;
std::function<void(float)> _audioLevelUpdated;
std::function<void(bool)> _remoteBatteryLevelIsLowUpdated;
std::function<void(AudioState, VideoState)> _remoteMediaStateUpdated;
std::function<void(float)> _remotePrefferedAspectRatioUpdated;
std::function<void(const std::vector<uint8_t> &)> _signalingDataEmitted;
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
std::unique_ptr<SignalingEncryption> _signalingEncryption;
bool _handshakeCompleted = false;
std::vector<cricket::Candidate> _pendingIceCandidates;
bool _isDataChannelOpen = false;
std::unique_ptr<webrtc::RtcEventLogNull> _eventLog;
std::unique_ptr<webrtc::TaskQueueFactory> _taskQueueFactory;
std::unique_ptr<cricket::MediaEngineInterface> _mediaEngine;
std::unique_ptr<webrtc::Call> _call;
webrtc::FieldTrialBasedConfig _fieldTrials;
webrtc::LocalAudioSinkAdapter _audioSource;
rtc::scoped_refptr<webrtc::AudioDeviceModule> _audioDeviceModule;
std::unique_ptr<rtc::UniqueRandomIdGenerator> _uniqueRandomIdGenerator;
webrtc::RtpTransport *_rtpTransport = nullptr;
std::unique_ptr<cricket::ChannelManager> _channelManager;
std::unique_ptr<webrtc::VideoBitrateAllocatorFactory> _videoBitrateAllocatorFactory;
std::shared_ptr<ThreadLocalObject<NativeNetworkingImpl>> _networking;
absl::optional<signaling::MediaContent> _outgoingAudioContent;
absl::optional<NegotiatedMediaContent<cricket::AudioCodec>> _negotiatedOutgoingAudioContent;
std::unique_ptr<OutgoingAudioChannel> _outgoingAudioChannel;
bool _isMicrophoneMuted = false;
std::vector<webrtc::SdpVideoFormat> _availableVideoFormats;
absl::optional<signaling::MediaContent> _outgoingVideoContent;
absl::optional<NegotiatedMediaContent<cricket::VideoCodec>> _negotiatedOutgoingVideoContent;
std::shared_ptr<OutgoingVideoChannel> _outgoingVideoChannel;
bool _isBatteryLow = false;
std::unique_ptr<IncomingV2AudioChannel> _incomingAudioChannel;
std::unique_ptr<IncomingV2VideoChannel> _incomingVideoChannel;
std::shared_ptr<VideoCaptureInterface> _videoCapture;
};
InstanceV2Impl::InstanceV2Impl(Descriptor &&descriptor) {
if (descriptor.config.logPath.data.size() != 0) {
_logSink = std::make_unique<LogSinkImpl>(descriptor.config.logPath);
}
rtc::LogMessage::LogToDebug(rtc::LS_INFO);
rtc::LogMessage::SetLogToStderr(false);
if (_logSink) {
rtc::LogMessage::AddLogToStream(_logSink.get(), rtc::LS_INFO);
}
_threads = StaticThreads::getThreads();
_internal.reset(new ThreadLocalObject<InstanceV2ImplInternal>(_threads->getMediaThread(), [descriptor = std::move(descriptor), threads = _threads]() mutable {
return new InstanceV2ImplInternal(std::move(descriptor), threads);
}));
_internal->perform(RTC_FROM_HERE, [](InstanceV2ImplInternal *internal) {
internal->start();
});
}
InstanceV2Impl::~InstanceV2Impl() {
rtc::LogMessage::RemoveLogToStream(_logSink.get());
}
void InstanceV2Impl::receiveSignalingData(const std::vector<uint8_t> &data) {
_internal->perform(RTC_FROM_HERE, [data](InstanceV2ImplInternal *internal) {
internal->receiveSignalingData(data);
});
}
void InstanceV2Impl::setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) {
_internal->perform(RTC_FROM_HERE, [videoCapture](InstanceV2ImplInternal *internal) {
internal->setVideoCapture(videoCapture);
});
}
void InstanceV2Impl::setRequestedVideoAspect(float aspect) {
_internal->perform(RTC_FROM_HERE, [aspect](InstanceV2ImplInternal *internal) {
internal->setRequestedVideoAspect(aspect);
});
}
void InstanceV2Impl::setNetworkType(NetworkType networkType) {
_internal->perform(RTC_FROM_HERE, [networkType](InstanceV2ImplInternal *internal) {
internal->setNetworkType(networkType);
});
}
void InstanceV2Impl::setMuteMicrophone(bool muteMicrophone) {
_internal->perform(RTC_FROM_HERE, [muteMicrophone](InstanceV2ImplInternal *internal) {
internal->setMuteMicrophone(muteMicrophone);
});
}
void InstanceV2Impl::setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
_internal->perform(RTC_FROM_HERE, [sink](InstanceV2ImplInternal *internal) {
internal->setIncomingVideoOutput(sink);
});
}
void InstanceV2Impl::setAudioInputDevice(std::string id) {
_internal->perform(RTC_FROM_HERE, [id](InstanceV2ImplInternal *internal) {
internal->setAudioInputDevice(id);
});
}
void InstanceV2Impl::setAudioOutputDevice(std::string id) {
_internal->perform(RTC_FROM_HERE, [id](InstanceV2ImplInternal *internal) {
internal->setAudioOutputDevice(id);
});
}
void InstanceV2Impl::setIsLowBatteryLevel(bool isLowBatteryLevel) {
_internal->perform(RTC_FROM_HERE, [isLowBatteryLevel](InstanceV2ImplInternal *internal) {
internal->setIsLowBatteryLevel(isLowBatteryLevel);
});
}
void InstanceV2Impl::setInputVolume(float level) {
}
void InstanceV2Impl::setOutputVolume(float level) {
}
void InstanceV2Impl::setAudioOutputDuckingEnabled(bool enabled) {
}
void InstanceV2Impl::setAudioOutputGainControlEnabled(bool enabled) {
}
void InstanceV2Impl::setEchoCancellationStrength(int strength) {
}
std::vector<std::string> InstanceV2Impl::GetVersions() {
std::vector<std::string> result;
result.push_back("4.0.0");
return result;
}
int InstanceV2Impl::GetConnectionMaxLayer() {
return 92;
}
std::string InstanceV2Impl::getLastError() {
return "";
}
std::string InstanceV2Impl::getDebugInfo() {
return "";
}
int64_t InstanceV2Impl::getPreferredRelayId() {
return 0;
}
TrafficStats InstanceV2Impl::getTrafficStats() {
return {};
}
PersistentState InstanceV2Impl::getPersistentState() {
return {};
}
void InstanceV2Impl::stop(std::function<void(FinalState)> completion) {
std::string debugLog;
if (_logSink) {
debugLog = _logSink->result();
}
_internal->perform(RTC_FROM_HERE, [completion, debugLog = std::move(debugLog)](InstanceV2ImplInternal *internal) mutable {
internal->stop([completion, debugLog = std::move(debugLog)](FinalState finalState) mutable {
finalState.debugLog = debugLog;
completion(finalState);
});
});
}
template <>
bool Register<InstanceV2Impl>() {
return Meta::RegisterOne<InstanceV2Impl>();
}
} // namespace tgcalls