NekoX/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceCustomImpl.cpp

2410 lines
94 KiB
C++

#include "GroupInstanceCustomImpl.h"
#include <memory>
#include <iomanip>
#include "Instance.h"
#include "VideoCaptureInterfaceImpl.h"
#include "VideoCapturerInterface.h"
#include "CodecSelectHelper.h"
#include "Message.h"
#include "platform/PlatformInterface.h"
#include "StaticThreads.h"
#include "GroupNetworkManager.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 "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 "StreamingPart.h"
#include "AudioDeviceHelper.h"
#include <random>
#include <sstream>
#include <iostream>
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 std::string uint32ToString(uint32_t value) {
std::ostringstream stringStream;
stringStream << value;
return stringStream.str();
}
static uint32_t stringToUInt32(std::string const &string) {
std::stringstream stringStream(string);
uint32_t value = 0;
stringStream >> value;
return value;
}
static uint16_t stringToUInt16(std::string const &string) {
std::stringstream stringStream(string);
uint16_t value = 0;
stringStream >> value;
return value;
}
static std::string formatTimestampMillis(int64_t timestamp) {
std::ostringstream stringStream;
stringStream << std::fixed << std::setprecision(3) << (double)timestamp / 1000.0;
return stringStream.str();
}
static VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(VideoCaptureInterface *videoCapture) {
return videoCapture
? static_cast<VideoCaptureInterfaceImpl*>(videoCapture)->object()->getSyncAssumingSameThread()
: nullptr;
}
struct OutgoingVideoFormat {
cricket::VideoCodec videoCodec;
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));
}
static absl::optional<OutgoingVideoFormat> assignPayloadTypes(std::vector<webrtc::SdpVideoFormat> const &formats) {
if (formats.empty()) {
return absl::nullopt;
}
constexpr int kFirstDynamicPayloadType = 100;
constexpr int kLastDynamicPayloadType = 127;
int payload_type = kFirstDynamicPayloadType;
auto result = OutgoingVideoFormat();
bool codecSelected = false;
for (const auto &format : formats) {
if (codecSelected) {
break;
}
cricket::VideoCodec codec(format);
codec.id = payload_type;
addDefaultFeedbackParams(&codec);
if (!absl::EqualsIgnoreCase(codec.name, cricket::kVp8CodecName)) {
continue;
}
result.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)) {
result.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;
}
}
}
return result;
}
struct VideoSsrcs {
struct SimulcastLayer {
uint32_t ssrc = 0;
uint32_t fidSsrc = 0;
SimulcastLayer(uint32_t ssrc_, uint32_t fidSsrc_) :
ssrc(ssrc_), fidSsrc(fidSsrc_) {
}
SimulcastLayer(const SimulcastLayer &other) :
ssrc(other.ssrc), fidSsrc(other.fidSsrc) {
}
};
std::vector<SimulcastLayer> simulcastLayers;
VideoSsrcs() {
}
VideoSsrcs(const VideoSsrcs &other) :
simulcastLayers(other.simulcastLayers) {
}
};
struct ChannelId {
uint32_t networkSsrc = 0;
uint32_t actualSsrc = 0;
ChannelId(uint32_t networkSsrc_, uint32_t actualSsrc_) :
networkSsrc(networkSsrc_),
actualSsrc(actualSsrc_) {
}
explicit ChannelId(uint32_t networkSsrc_) :
networkSsrc(networkSsrc_),
actualSsrc(networkSsrc_) {
}
bool operator <(const ChannelId& rhs) const {
if (networkSsrc != rhs.networkSsrc) {
return networkSsrc < rhs.networkSsrc;
}
return actualSsrc < rhs.actualSsrc;
}
std::string name() {
if (networkSsrc == actualSsrc) {
return uint32ToString(networkSsrc);
} else {
return uint32ToString(networkSsrc) + "to" + uint32ToString(actualSsrc);
}
}
};
class NetworkInterfaceImpl : public cricket::MediaChannel::NetworkInterface {
public:
NetworkInterfaceImpl(std::function<void(rtc::CopyOnWriteBuffer const *, rtc::SentPacket)> sendPacket) :
_sendPacket(sendPacket) {
}
bool SendPacket(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) {
rtc::SentPacket sentPacket(options.packet_id, rtc::TimeMillis(), options.info_signaled_after_sent);
_sendPacket(packet, sentPacket);
return true;
}
bool SendRtcp(rtc::CopyOnWriteBuffer *packet, const rtc::PacketOptions& options) {
rtc::SentPacket sentPacket(options.packet_id, rtc::TimeMillis(), options.info_signaled_after_sent);
_sendPacket(packet, sentPacket);
return true;
}
int SetOption(cricket::MediaChannel::NetworkInterface::SocketType, rtc::Socket::Option, int) {
return -1;
}
private:
std::function<void(rtc::CopyOnWriteBuffer const *, rtc::SentPacket)> _sendPacket;
};
static const int kVadResultHistoryLength = 6;
class CombinedVad {
private:
webrtc::VadLevelAnalyzer _vadWithLevel;
float _vadResultHistory[kVadResultHistoryLength];
std::atomic<int32_t> _waitingFramesToProcess{0};
bool _countFrames;
public:
CombinedVad(bool count = false) {
for (float & i : _vadResultHistory) {
i = 0.0f;
}
_countFrames = count;
}
~CombinedVad() = default;
bool incWaitingFrames() {
if (_waitingFramesToProcess > 5) {
return false;
}
_waitingFramesToProcess++;
return true;
}
bool update(webrtc::AudioBuffer *buffer) {
if (buffer) {
webrtc::AudioFrameView<float> frameView(buffer->channels(), buffer->num_channels(), buffer->num_frames());
auto result = _vadWithLevel.AnalyzeFrame(frameView);
float speech_probability = result.speech_probability;
for (int i = 1; i < kVadResultHistoryLength; i++) {
_vadResultHistory[i - 1] = _vadResultHistory[i];
}
_vadResultHistory[kVadResultHistoryLength - 1] = speech_probability;
if (_countFrames) {
_waitingFramesToProcess--;
}
}
float movingAverage = 0.0f;
for (float i : _vadResultHistory) {
movingAverage += i;
}
movingAverage /= (float)kVadResultHistoryLength;
return movingAverage > 0.6f;
}
};
class AudioSinkImpl: public webrtc::AudioSinkInterface {
public:
struct Update {
float level = 0.0f;
std::shared_ptr<webrtc::AudioBuffer> buffer;
std::shared_ptr<CombinedVad> vad;
Update(float level_, webrtc::AudioBuffer *buffer_, std::shared_ptr<CombinedVad> vad_) :
level(level_), buffer(std::shared_ptr<webrtc::AudioBuffer>(buffer_)), vad(vad_) {
}
};
public:
AudioSinkImpl(std::function<void(Update)> update,
ChannelId channel_id, std::function<void(uint32_t, const AudioFrame &)> onAudioFrame) :
_update(update), _channel_id(channel_id), _onAudioFrame(std::move(onAudioFrame)) {
_vad = std::make_shared<CombinedVad>(true);
}
virtual ~AudioSinkImpl() {
}
virtual void OnData(const Data& audio) override {
if (_onAudioFrame) {
AudioFrame frame;
frame.audio_samples = audio.data;
frame.num_samples = audio.samples_per_channel;
frame.bytes_per_sample = 2;
frame.num_channels = audio.channels;
frame.samples_per_sec = audio.sample_rate;
frame.elapsed_time_ms = 0;
frame.ntp_time_ms = 0;
_onAudioFrame(_channel_id.actualSsrc, frame);
}
if (_update && audio.channels == 1) {
const int16_t *samples = (const int16_t *)audio.data;
int numberOfSamplesInFrame = (int)audio.samples_per_channel;
for (int i = 0; i < numberOfSamplesInFrame; i++) {
int16_t sample = samples[i];
if (sample < 0) {
sample = -sample;
}
if (_peak < sample) {
_peak = sample;
}
_peakCount += 1;
}
if (_peakCount >= 1200) {
float level = ((float)(_peak)) / 4000.0f;
_peak = 0;
_peakCount = 0;
webrtc::AudioBuffer *buffer;
if (_vad->incWaitingFrames()) {
buffer = new webrtc::AudioBuffer(audio.sample_rate, 1, 48000, 1, 48000, 1);
webrtc::StreamConfig config(audio.sample_rate, 1);
buffer->CopyFrom(samples, config);
} else {
buffer = nullptr;
}
_update(Update(level, buffer, _vad));
}
}
}
private:
std::function<void(Update)> _update;
ChannelId _channel_id;
std::function<void(uint32_t, const AudioFrame &)> _onAudioFrame;
std::shared_ptr<CombinedVad> _vad;
int _peakCount = 0;
uint16_t _peak = 0;
};
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 AudioCaptureAnalyzer : public webrtc::CustomAudioAnalyzer {
private:
void Initialize(int sample_rate_hz, int num_channels) override {
}
void Analyze(const webrtc::AudioBuffer* buffer) override {
if (!buffer) {
return;
}
if (buffer->num_channels() != 1) {
return;
}
float peak = 0;
int peakCount = 0;
const float *samples = buffer->channels_const()[0];
for (int i = 0; i < buffer->num_frames(); i++) {
float sample = samples[i];
if (sample < 0) {
sample = -sample;
}
if (peak < sample) {
peak = sample;
}
peakCount += 1;
}
bool vadStatus = _vad.update((webrtc::AudioBuffer *)buffer);
_peakCount += peakCount;
if (_peak < peak) {
_peak = peak;
}
if (_peakCount >= 1200) {
float level = _peak / 4000.0f;
_peak = 0;
_peakCount = 0;
_updated(GroupLevelValue{
level,
vadStatus,
});
}
}
std::string ToString() const override {
return "analyzing";
}
private:
std::function<void(GroupLevelValue const &)> _updated;
CombinedVad _vad;
int32_t _peakCount = 0;
float _peak = 0;
public:
AudioCaptureAnalyzer(std::function<void(GroupLevelValue const &)> updated) :
_updated(updated) {
}
virtual ~AudioCaptureAnalyzer() = default;
};
class IncomingAudioChannel : public sigslot::has_slots<> {
public:
IncomingAudioChannel(
cricket::ChannelManager *channelManager,
webrtc::Call *call,
webrtc::RtpTransport *rtpTransport,
rtc::UniqueRandomIdGenerator *randomIdGenerator,
bool isRawPcm,
ChannelId ssrc,
std::function<void(AudioSinkImpl::Update)> &&onAudioLevelUpdated,
std::function<void(uint32_t, const AudioFrame &)> onAudioFrame,
Threads &threads) :
_ssrc(ssrc),
_channelManager(channelManager),
_call(call) {
_creationTimestamp = rtc::TimeMillis();
cricket::AudioOptions audioOptions;
audioOptions.echo_cancellation = true;
audioOptions.noise_suppression = true;
audioOptions.audio_jitter_buffer_fast_accelerate = true;
audioOptions.audio_jitter_buffer_min_delay_ms = 50;
std::string streamId = std::string("stream") + ssrc.name();
_audioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), rtpTransport, threads.getMediaThread(), std::string("audio") + uint32ToString(ssrc.networkSsrc), false, GroupNetworkManager::getDefaulCryptoOptions(), randomIdGenerator, audioOptions);
const uint8_t opusMinBitrateKbps = 32;
const uint8_t opusMaxBitrateKbps = 32;
const uint8_t opusStartBitrateKbps = 32;
const uint8_t opusPTimeMs = 120;
cricket::AudioCodec opusCodec(111, "opus", 48000, 0, 2);
opusCodec.SetParam(cricket::kCodecParamMinBitrate, opusMinBitrateKbps);
opusCodec.SetParam(cricket::kCodecParamStartBitrate, opusStartBitrateKbps);
opusCodec.SetParam(cricket::kCodecParamMaxBitrate, opusMaxBitrateKbps);
opusCodec.SetParam(cricket::kCodecParamUseInbandFec, 1);
opusCodec.SetParam(cricket::kCodecParamPTime, opusPTimeMs);
cricket::AudioCodec pcmCodec(112, "l16", 48000, 0, 1);
auto outgoingAudioDescription = std::make_unique<cricket::AudioContentDescription>();
if (!isRawPcm) {
outgoingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAudioLevelUri, 1));
outgoingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, 2));
outgoingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kTransportSequenceNumberUri, 3));
}
outgoingAudioDescription->set_rtcp_mux(true);
outgoingAudioDescription->set_rtcp_reduced_size(true);
outgoingAudioDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly);
outgoingAudioDescription->set_codecs({ opusCodec, pcmCodec });
auto incomingAudioDescription = std::make_unique<cricket::AudioContentDescription>();
if (!isRawPcm) {
incomingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAudioLevelUri, 1));
incomingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, 2));
incomingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kTransportSequenceNumberUri, 3));
}
incomingAudioDescription->set_rtcp_mux(true);
incomingAudioDescription->set_rtcp_reduced_size(true);
incomingAudioDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly);
incomingAudioDescription->set_codecs({ opusCodec, pcmCodec });
cricket::StreamParams streamParams = cricket::StreamParams::CreateLegacy(ssrc.networkSsrc);
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, &IncomingAudioChannel::OnSentPacket_w);
//_audioChannel->UpdateRtpTransport(nullptr);
_audioChannel->Enable(true);
}
~IncomingAudioChannel() {
_audioChannel->SignalSentPacket().disconnect(this);
_audioChannel->Enable(false);
_channelManager->DestroyVoiceChannel(_audioChannel);
_audioChannel = nullptr;
}
void setVolume(double value) {
_audioChannel->media_channel()->SetOutputVolume(_ssrc.networkSsrc, 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:
ChannelId _ssrc;
// 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 IncomingVideoChannel : public sigslot::has_slots<> {
public:
IncomingVideoChannel(
cricket::ChannelManager *channelManager,
webrtc::Call *call,
webrtc::RtpTransport *rtpTransport,
rtc::UniqueRandomIdGenerator *randomIdGenerator,
std::vector<webrtc::SdpVideoFormat> const &availableVideoFormats,
GroupParticipantDescription const &description,
Threads &threads) :
_channelManager(channelManager),
_call(call) {
_videoSink.reset(new VideoSinkImpl());
std::string streamId = std::string("stream") + uint32ToString(description.audioSsrc);
_videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory();
_videoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), rtpTransport, threads.getMediaThread(), std::string("video") + uint32ToString(description.audioSsrc), false, GroupNetworkManager::getDefaulCryptoOptions(), randomIdGenerator, cricket::VideoOptions(), _videoBitrateAllocatorFactory.get());
auto payloadTypes = assignPayloadTypes(availableVideoFormats);
if (!payloadTypes.has_value()) {
return;
}
auto outgoingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
outgoingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, 2));
outgoingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kTransportSequenceNumberUri, 3));
outgoingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kVideoRotationUri, 13));
outgoingVideoDescription->set_rtcp_mux(true);
outgoingVideoDescription->set_rtcp_reduced_size(true);
outgoingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly);
outgoingVideoDescription->set_codecs({ payloadTypes->videoCodec, payloadTypes->rtxCodec });
cricket::StreamParams videoRecvStreamParams;
std::vector<uint32_t> allSsrcs;
for (const auto &group : description.videoSourceGroups) {
for (auto ssrc : group.ssrcs) {
if (std::find(allSsrcs.begin(), allSsrcs.end(), ssrc) == allSsrcs.end()) {
allSsrcs.push_back(ssrc);
}
}
if (group.semantics == "SIM") {
if (_mainVideoSsrc == 0) {
_mainVideoSsrc = group.ssrcs[0];
}
}
cricket::SsrcGroup parsedGroup(group.semantics, group.ssrcs);
videoRecvStreamParams.ssrc_groups.push_back(parsedGroup);
}
videoRecvStreamParams.ssrcs = allSsrcs;
if (_mainVideoSsrc == 0) {
if (description.videoSourceGroups.size() == 1) {
_mainVideoSsrc = description.videoSourceGroups[0].ssrcs[0];
}
}
videoRecvStreamParams.cname = "cname";
videoRecvStreamParams.set_stream_ids({ streamId });
auto incomingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, 2));
incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kTransportSequenceNumberUri, 3));
incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kVideoRotationUri, 13));
incomingVideoDescription->set_rtcp_mux(true);
incomingVideoDescription->set_rtcp_reduced_size(true);
incomingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly);
incomingVideoDescription->set_codecs({ payloadTypes->videoCodec, payloadTypes->rtxCodec });
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, &IncomingVideoChannel::OnSentPacket_w);
//_videoChannel->UpdateRtpTransport(nullptr);
_videoChannel->Enable(true);
}
~IncomingVideoChannel() {
_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::vector<GroupJoinPayloadVideoSourceGroup> _ssrcGroups;
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;
};
struct SsrcMappingInfo {
uint32_t ssrc = 0;
bool isVideo = false;
std::string endpointId;
};
class MissingSsrcPacketBuffer {
public:
MissingSsrcPacketBuffer(int limit) :
_limit(limit) {
}
~MissingSsrcPacketBuffer() {
}
void add(uint32_t ssrc, rtc::CopyOnWriteBuffer const &packet) {
if (_packets.size() == _limit) {
_packets.erase(_packets.begin());
}
_packets.push_back(std::make_pair(ssrc, packet));
}
std::vector<rtc::CopyOnWriteBuffer> get(uint32_t ssrc) {
std::vector<rtc::CopyOnWriteBuffer> result;
for (auto it = _packets.begin(); it != _packets.end(); ) {
if (it->first == ssrc) {
result.push_back(it->second);
_packets.erase(it);
} else {
it++;
}
}
return result;
}
private:
int _limit = 0;
std::vector<std::pair<uint32_t, rtc::CopyOnWriteBuffer>> _packets;
};
class RequestedBroadcastPart {
public:
int64_t timestamp = 0;
std::shared_ptr<BroadcastPartTask> task;
explicit RequestedBroadcastPart(int64_t timestamp_, std::shared_ptr<BroadcastPartTask> task_) :
timestamp(timestamp_), task(task_) {
}
};
struct DecodedBroadcastPart {
struct DecodedBroadcastPartChannel {
uint32_t ssrc = 0;
std::vector<int16_t> pcmData;
};
DecodedBroadcastPart(int numSamples_, std::vector<DecodedBroadcastPartChannel> &&_channels) :
numSamples(numSamples_), channels(std::move(_channels)) {
}
int numSamples = 0;
std::vector<DecodedBroadcastPartChannel> channels;
};
} // namespace
class GroupInstanceCustomInternal : public sigslot::has_slots<>, public std::enable_shared_from_this<GroupInstanceCustomInternal> {
public:
GroupInstanceCustomInternal(GroupInstanceDescriptor &&descriptor, std::shared_ptr<Threads> threads) :
_threads(std::move(threads)),
_networkStateUpdated(descriptor.networkStateUpdated),
_audioLevelsUpdated(descriptor.audioLevelsUpdated),
_onAudioFrame(descriptor.onAudioFrame),
_incomingVideoSourcesUpdated(descriptor.incomingVideoSourcesUpdated),
_participantDescriptionsRequired(descriptor.participantDescriptionsRequired),
_requestBroadcastPart(descriptor.requestBroadcastPart),
_videoCapture(descriptor.videoCapture),
_disableIncomingChannels(descriptor.disableIncomingChannels),
_useDummyChannel(descriptor.useDummyChannel),
_eventLog(std::make_unique<webrtc::RtcEventLogNull>()),
_taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()),
_createAudioDeviceModule(descriptor.createAudioDeviceModule),
_initialInputDeviceId(std::move(descriptor.initialInputDeviceId)),
_initialOutputDeviceId(std::move(descriptor.initialOutputDeviceId)),
_missingPacketBuffer(100),
_platformContext(descriptor.platformContext) {
assert(_threads->getMediaThread()->IsCurrent());
auto generator = std::mt19937(std::random_device()());
auto distribution = std::uniform_int_distribution<uint32_t>();
do {
_outgoingAudioSsrc = distribution(generator) & 0x7fffffffU;
} while (!_outgoingAudioSsrc);
uint32_t outgoingVideoSsrcBase = _outgoingAudioSsrc + 1;
int numVideoSimulcastLayers = 2;
for (int layerIndex = 0; layerIndex < numVideoSimulcastLayers; layerIndex++) {
_outgoingVideoSsrcs.simulcastLayers.push_back(VideoSsrcs::SimulcastLayer(outgoingVideoSsrcBase + layerIndex * 2 + 0, outgoingVideoSsrcBase + layerIndex * 2 + 1));
}
}
~GroupInstanceCustomInternal() {
_call->SignalChannelNetworkState(webrtc::MediaType::AUDIO, webrtc::kNetworkDown);
_call->SignalChannelNetworkState(webrtc::MediaType::VIDEO, webrtc::kNetworkDown);
_incomingAudioChannels.clear();
_incomingVideoChannels.clear();
destroyOutgoingAudioChannel();
if (_outgoingVideoChannel) {
_outgoingVideoChannel->SignalSentPacket().disconnect(this);
_outgoingVideoChannel->media_channel()->SetVideoSend(_outgoingVideoSsrcs.simulcastLayers[0].ssrc, nullptr, nullptr);
_outgoingVideoChannel->Enable(false);
_channelManager->DestroyVideoChannel(_outgoingVideoChannel);
_outgoingVideoChannel = nullptr;
}
_channelManager = nullptr;
_audioDeviceModule = nullptr;
}
void start() {
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
webrtc::field_trial::InitFieldTrialsFromString(
"WebRTC-Audio-Allocation/min:32kbps,max:32kbps/"
"WebRTC-Audio-OpusMinPacketLossRate/Enabled-1/"
// "WebRTC-TaskQueuePacer/Enabled/"
);
_networkManager.reset(new ThreadLocalObject<GroupNetworkManager>(_threads->getNetworkThread(), [weak, threads = _threads] () mutable {
return new GroupNetworkManager(
[=](const GroupNetworkManager::State &state) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->setIsRtcConnected(state.isReadyToSendData);
});
},
[=](rtc::CopyOnWriteBuffer const &message, bool isUnresolved) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, message, isUnresolved]() mutable {
if (const auto strong = weak.lock()) {
strong->receivePacket(message, isUnresolved);
}
});
},
[=](rtc::CopyOnWriteBuffer const &message, int64_t timestamp) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, message, timestamp]() mutable {
if (const auto strong = weak.lock()) {
strong->receiveRtcpPacket(message, timestamp);
}
});
},
[=](bool isDataChannelOpen) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, isDataChannelOpen]() mutable {
if (const auto strong = weak.lock()) {
strong->updateIsDataChannelOpen(isDataChannelOpen);
}
});
},
[=](std::string const &message) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, message]() mutable {
if (const auto strong = weak.lock()) {
}
});
}, 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(_platformContext);
mediaDeps.video_decoder_factory = PlatformInterface::SharedInstance()->makeVideoDecoderFactory(_platformContext);
if (_audioLevelsUpdated) {
auto analyzer = new AudioCaptureAnalyzer([weak, threads = _threads](GroupLevelValue const &level) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, level, threads]() {
auto strong = weak.lock();
if (!strong) {
return;
}
strong->_myAudioLevel = level;
threads->getMediaThread()->Invoke<void>(RTC_FROM_HERE, [strong] {});
});
});
webrtc::AudioProcessingBuilder builder;
builder.SetCaptureAnalyzer(std::unique_ptr<AudioCaptureAnalyzer>(analyzer));
mediaDeps.audio_processing = builder.Create();
}
_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.reset(new cricket::ChannelManager(std::move(mediaEngine), std::make_unique<cricket::RtpDataEngine>(), _threads->getMediaThread(), _threads->getNetworkThread()));
_channelManager->Init();
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, _threads->getSharedModuleThread()));
_call.reset(webrtc::Call::Create(callConfig));
_uniqueRandomIdGenerator.reset(new rtc::UniqueRandomIdGenerator());
_threads->getNetworkThread()->Invoke<void>(RTC_FROM_HERE, [this]() {
_rtpTransport = _networkManager->getSyncAssumingSameThread()->getRtpTransport();
});
_videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory();
//_outgoingVideoChannel = _channelManager->CreateVideoChannel(_call.get(), cricket::MediaConfig(), _rtpTransport, _threads->getMediaThread(), "1", false, GroupNetworkManager::getDefaulCryptoOptions(), _uniqueRandomIdGenerator.get(), cricket::VideoOptions(), _videoBitrateAllocatorFactory.get());
configureSendVideo();
if (_outgoingVideoChannel) {
_outgoingVideoChannel->SignalSentPacket().connect(this, &GroupInstanceCustomInternal::OnSentPacket_w);
//_outgoingVideoChannel->UpdateRtpTransport(nullptr);
}
if (_audioLevelsUpdated) {
beginLevelsTimer(50);
}
if (_videoCapture) {
setVideoCapture(_videoCapture, [](GroupJoinPayload) {}, true);
}
adjustBitratePreferences(true);
if (_useDummyChannel) {
addIncomingAudioChannel("_dummy", ChannelId(1), true);
}
beginNetworkStatusTimer(0);
}
void destroyOutgoingAudioChannel() {
if (!_outgoingAudioChannel) {
return;
}
_outgoingAudioChannel->SignalSentPacket().disconnect(this);
_outgoingAudioChannel->media_channel()->SetAudioSend(_outgoingAudioSsrc, false, nullptr, &_audioSource);
_outgoingAudioChannel->Enable(false);
_channelManager->DestroyVoiceChannel(_outgoingAudioChannel);
_outgoingAudioChannel = nullptr;
}
void createOutgoingAudioChannel() {
if (_outgoingAudioChannel) {
return;
}
cricket::AudioOptions audioOptions;
audioOptions.echo_cancellation = true;
audioOptions.noise_suppression = true;
audioOptions.audio_jitter_buffer_fast_accelerate = true;
std::vector<std::string> streamIds;
streamIds.push_back("1");
_outgoingAudioChannel = _channelManager->CreateVoiceChannel(_call.get(), cricket::MediaConfig(), _rtpTransport, _threads->getMediaThread(), "0", false, GroupNetworkManager::getDefaulCryptoOptions(), _uniqueRandomIdGenerator.get(), audioOptions);
const uint8_t opusMinBitrateKbps = 32;
const uint8_t opusMaxBitrateKbps = 32;
const uint8_t opusStartBitrateKbps = 32;
const uint8_t opusPTimeMs = 120;
cricket::AudioCodec opusCodec(111, "opus", 48000, 0, 2);
opusCodec.AddFeedbackParam(cricket::FeedbackParam(cricket::kRtcpFbParamTransportCc));
opusCodec.SetParam(cricket::kCodecParamMinBitrate, opusMinBitrateKbps);
opusCodec.SetParam(cricket::kCodecParamStartBitrate, opusStartBitrateKbps);
opusCodec.SetParam(cricket::kCodecParamMaxBitrate, opusMaxBitrateKbps);
opusCodec.SetParam(cricket::kCodecParamUseInbandFec, 1);
opusCodec.SetParam(cricket::kCodecParamPTime, opusPTimeMs);
auto outgoingAudioDescription = std::make_unique<cricket::AudioContentDescription>();
outgoingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAudioLevelUri, 1));
outgoingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, 2));
outgoingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kTransportSequenceNumberUri, 3));
outgoingAudioDescription->set_rtcp_mux(true);
outgoingAudioDescription->set_rtcp_reduced_size(true);
outgoingAudioDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly);
outgoingAudioDescription->set_codecs({ opusCodec });
outgoingAudioDescription->AddStream(cricket::StreamParams::CreateLegacy(_outgoingAudioSsrc));
auto incomingAudioDescription = std::make_unique<cricket::AudioContentDescription>();
incomingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAudioLevelUri, 1));
incomingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, 2));
incomingAudioDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kTransportSequenceNumberUri, 3));
incomingAudioDescription->set_rtcp_mux(true);
incomingAudioDescription->set_rtcp_reduced_size(true);
incomingAudioDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly);
incomingAudioDescription->set_codecs({ opusCodec });
_outgoingAudioChannel->SetPayloadTypeDemuxingEnabled(false);
_outgoingAudioChannel->SetLocalContent(outgoingAudioDescription.get(), webrtc::SdpType::kOffer, nullptr);
_outgoingAudioChannel->SetRemoteContent(incomingAudioDescription.get(), webrtc::SdpType::kAnswer, nullptr);
_outgoingAudioChannel->SignalSentPacket().connect(this, &GroupInstanceCustomInternal::OnSentPacket_w);
//_outgoingAudioChannel->UpdateRtpTransport(nullptr);
onUpdatedIsMuted();
}
void stop() {
}
void beginLevelsTimer(int timeoutMs) {
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
auto strong = weak.lock();
if (!strong) {
return;
}
GroupLevelsUpdate levelsUpdate;
levelsUpdate.updates.reserve(strong->_audioLevels.size() + 1);
for (auto &it : strong->_audioLevels) {
if (it.second.level > 0.001f) {
uint32_t effectiveSsrc = it.first.actualSsrc;
if (std::find_if(levelsUpdate.updates.begin(), levelsUpdate.updates.end(), [&](GroupLevelUpdate const &item) {
return item.ssrc == effectiveSsrc;
}) != levelsUpdate.updates.end()) {
continue;
}
levelsUpdate.updates.push_back(GroupLevelUpdate{
effectiveSsrc,
it.second,
});
}
}
auto myAudioLevel = strong->_myAudioLevel;
if (strong->_isMuted) {
myAudioLevel.level = 0.0f;
myAudioLevel.voice = false;
}
levelsUpdate.updates.push_back(GroupLevelUpdate{ 0, myAudioLevel });
strong->_audioLevels.clear();
if (strong->_audioLevelsUpdated) {
strong->_audioLevelsUpdated(levelsUpdate);
}
strong->beginLevelsTimer(50);
}, timeoutMs);
}
void beginNetworkStatusTimer(int delayMs) {
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
auto strong = weak.lock();
if (!strong) {
return;
}
if (strong->_connectionMode == GroupConnectionMode::GroupConnectionModeBroadcast || strong->_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
strong->updateBroadcastNetworkStatus();
}
strong->beginNetworkStatusTimer(500);
}, delayMs);
}
void updateBroadcastNetworkStatus() {
auto timestamp = rtc::TimeMillis();
bool isBroadcastConnected = true;
if (_lastBroadcastPartReceivedTimestamp < timestamp - 3000) {
isBroadcastConnected = false;
}
if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
auto timestamp = rtc::TimeMillis();
if (std::abs(timestamp - _broadcastEnabledUntilRtcIsConnectedAtTimestamp.value()) > 3000) {
_broadcastEnabledUntilRtcIsConnectedAtTimestamp = absl::nullopt;
if (_currentRequestedBroadcastPart) {
if (_currentRequestedBroadcastPart->task) {
_currentRequestedBroadcastPart->task->cancel();
}
_currentRequestedBroadcastPart.reset();
}
isBroadcastConnected = false;
}
}
if (isBroadcastConnected != _isBroadcastConnected) {
_isBroadcastConnected = isBroadcastConnected;
updateIsConnected();
}
}
absl::optional<DecodedBroadcastPart> getNextBroadcastPart() {
while (true) {
if (_sourceBroadcastParts.size() != 0) {
auto readChannels = _sourceBroadcastParts[0]->get10msPerChannel();
if (readChannels.size() == 0 || readChannels[0].pcmData.size() == 0) {
_sourceBroadcastParts.erase(_sourceBroadcastParts.begin());
} else {
std::vector<DecodedBroadcastPart::DecodedBroadcastPartChannel> channels;
int numSamples = (int)readChannels[0].pcmData.size();
for (auto &readChannel : readChannels) {
DecodedBroadcastPart::DecodedBroadcastPartChannel channel;
channel.ssrc = readChannel.ssrc;
channel.pcmData = std::move(readChannel.pcmData);
channels.push_back(channel);
}
absl::optional<DecodedBroadcastPart> decodedPart;
decodedPart.emplace(numSamples, std::move(channels));
return decodedPart;
}
} else {
return absl::nullopt;
}
}
return absl::nullopt;
}
void commitBroadcastPackets() {
int numMillisecondsInQueue = 0;
for (const auto &part : _sourceBroadcastParts) {
numMillisecondsInQueue += part->getRemainingMilliseconds();
}
int commitMilliseconds = 20;
if (numMillisecondsInQueue > 1000) {
commitMilliseconds = numMillisecondsInQueue - 1000;
}
std::set<ChannelId> channelsWithActivity;
for (int msIndex = 0; msIndex < commitMilliseconds; msIndex += 10) {
auto packetData = getNextBroadcastPart();
if (!packetData) {
break;
}
for (const auto &decodedChannel : packetData->channels) {
if (decodedChannel.ssrc == _outgoingAudioSsrc) {
continue;
}
ChannelId channelSsrc = ChannelId(decodedChannel.ssrc + 1000, decodedChannel.ssrc);
if (_incomingAudioChannels.find(channelSsrc) == _incomingAudioChannels.end()) {
std::ostringstream os;
os << "broadcast" << channelSsrc.name();
addIncomingAudioChannel(os.str(), channelSsrc, true);
}
webrtc::RtpPacket packet(nullptr, 12 + decodedChannel.pcmData.size() * 2);
packet.SetMarker(false);
packet.SetPayloadType(112);
uint16_t packetSeq = 0;
auto it = _broadcastSeqBySsrc.find(channelSsrc.networkSsrc);
if (it == _broadcastSeqBySsrc.end()) {
packetSeq = 1000;
_broadcastSeqBySsrc.insert(std::make_pair(channelSsrc.networkSsrc, packetSeq));
} else {
it->second++;
packetSeq = it->second;
}
packet.SetSequenceNumber(packetSeq);
packet.SetTimestamp(_broadcastTimestamp);
packet.SetSsrc(channelSsrc.networkSsrc);
uint8_t *payload = packet.SetPayloadSize(decodedChannel.pcmData.size() * 2);
memcpy(payload, decodedChannel.pcmData.data(), decodedChannel.pcmData.size() * 2);
for (int i = 0; i < decodedChannel.pcmData.size() * 2; i += 2) {
auto temp = payload[i];
payload[i] = payload[i + 1];
payload[i + 1] = temp;
}
auto buffer = packet.Buffer();
_call->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO, buffer, -1);
channelsWithActivity.insert(ChannelId(channelSsrc));
}
for (const auto channelId : channelsWithActivity) {
const auto it = _incomingAudioChannels.find(channelId);
if (it != _incomingAudioChannels.end()) {
it->second->updateActivity();
}
}
_broadcastTimestamp += packetData->numSamples;
}
}
void requestNextBroadcastPart() {
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
auto requestedPartId = _nextBroadcastTimestampMilliseconds;
auto task = _requestBroadcastPart(_platformContext, requestedPartId, _broadcastPartDurationMilliseconds, [weak, threads = _threads, requestedPartId](BroadcastPart &&part) {
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, part = std::move(part), requestedPartId]() mutable {
auto strong = weak.lock();
if (!strong) {
return;
}
if (strong->_currentRequestedBroadcastPart && strong->_currentRequestedBroadcastPart->timestamp == requestedPartId) {
strong->onReceivedNextBroadcastPart(std::move(part));
}
});
});
if (_currentRequestedBroadcastPart) {
if (_currentRequestedBroadcastPart->task) {
_currentRequestedBroadcastPart->task->cancel();
}
_currentRequestedBroadcastPart.reset();
}
_currentRequestedBroadcastPart.emplace(requestedPartId, task);
}
void requestNextBroadcastPartWithDelay(int timeoutMs) {
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
auto strong = weak.lock();
if (!strong) {
return;
}
strong->requestNextBroadcastPart();
}, timeoutMs);
}
void onReceivedNextBroadcastPart(BroadcastPart &&part) {
_currentRequestedBroadcastPart.reset();
if (_connectionMode != GroupConnectionMode::GroupConnectionModeBroadcast && !_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
return;
}
int64_t responseTimestampMilliseconds = (int64_t)(part.responseTimestamp * 1000.0);
int64_t responseTimestampBoundary = (responseTimestampMilliseconds / _broadcastPartDurationMilliseconds) * _broadcastPartDurationMilliseconds;
switch (part.status) {
case BroadcastPart::Status::Success: {
_lastBroadcastPartReceivedTimestamp = rtc::TimeMillis();
updateBroadcastNetworkStatus();
if (std::abs((int64_t)(part.responseTimestamp * 1000.0) - part.timestampMilliseconds) > 2000) {
_nextBroadcastTimestampMilliseconds = std::max(part.timestampMilliseconds + _broadcastPartDurationMilliseconds, responseTimestampBoundary);
} else {
_nextBroadcastTimestampMilliseconds = part.timestampMilliseconds + _broadcastPartDurationMilliseconds;
}
_sourceBroadcastParts.emplace_back(new StreamingPart(std::move(part.oggData)));
break;
}
case BroadcastPart::Status::NotReady: {
_nextBroadcastTimestampMilliseconds = part.timestampMilliseconds;
break;
}
case BroadcastPart::Status::ResyncNeeded: {
_nextBroadcastTimestampMilliseconds = responseTimestampBoundary;
break;
}
default: {
//RTC_FATAL() << "Unknown part.status";
break;
}
}
int64_t nextDelay = _nextBroadcastTimestampMilliseconds - responseTimestampMilliseconds;
int clippedDelay = std::max((int)nextDelay, 100);
//RTC_LOG(LS_INFO) << "requestNextBroadcastPartWithDelay(" << clippedDelay << ") (from " << nextDelay << ")";
requestNextBroadcastPartWithDelay(clippedDelay);
}
void beginBroadcastPartsDecodeTimer(int timeoutMs) {
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
auto strong = weak.lock();
if (!strong) {
return;
}
if (strong->_connectionMode != GroupConnectionMode::GroupConnectionModeBroadcast && !strong->_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
return;
}
strong->commitBroadcastPackets();
strong->beginBroadcastPartsDecodeTimer(20);
}, timeoutMs);
}
void configureSendVideo() {
if (!_outgoingVideoChannel) {
return;
}
auto payloadTypes = assignPayloadTypes(_availableVideoFormats);
if (!payloadTypes.has_value()) {
return;
}
GroupJoinPayloadVideoPayloadType vp8Payload;
vp8Payload.id = payloadTypes.value().videoCodec.id;
vp8Payload.name = payloadTypes.value().videoCodec.name;
vp8Payload.clockrate = payloadTypes.value().videoCodec.clockrate;
vp8Payload.channels = 0;
std::vector<GroupJoinPayloadVideoPayloadFeedbackType> vp8FeedbackTypes;
GroupJoinPayloadVideoPayloadFeedbackType fbGoogRemb;
fbGoogRemb.type = "goog-remb";
vp8FeedbackTypes.push_back(fbGoogRemb);
GroupJoinPayloadVideoPayloadFeedbackType fbTransportCc;
fbTransportCc.type = "transport-cc";
vp8FeedbackTypes.push_back(fbTransportCc);
GroupJoinPayloadVideoPayloadFeedbackType fbCcmFir;
fbCcmFir.type = "ccm";
fbCcmFir.subtype = "fir";
vp8FeedbackTypes.push_back(fbCcmFir);
GroupJoinPayloadVideoPayloadFeedbackType fbNack;
fbNack.type = "nack";
vp8FeedbackTypes.push_back(fbNack);
GroupJoinPayloadVideoPayloadFeedbackType fbNackPli;
fbNackPli.type = "nack";
fbNackPli.subtype = "pli";
vp8FeedbackTypes.push_back(fbNackPli);
vp8Payload.feedbackTypes = vp8FeedbackTypes;
vp8Payload.parameters = {};
_videoPayloadTypes.push_back(std::move(vp8Payload));
GroupJoinPayloadVideoPayloadType rtxPayload;
rtxPayload.id = payloadTypes.value().rtxCodec.id;
rtxPayload.name = payloadTypes.value().rtxCodec.name;
rtxPayload.clockrate = payloadTypes.value().rtxCodec.clockrate;
rtxPayload.parameters.push_back(std::make_pair("apt", intToString(payloadTypes.value().videoCodec.id)));
_videoPayloadTypes.push_back(std::move(rtxPayload));
auto outgoingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
outgoingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, 2));
outgoingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kTransportSequenceNumberUri, 3));
outgoingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kVideoRotationUri, 13));
for (const auto &extension : outgoingVideoDescription->rtp_header_extensions()) {
_videoExtensionMap.push_back(std::make_pair(extension.id, extension.uri));
}
outgoingVideoDescription->set_rtcp_mux(true);
outgoingVideoDescription->set_rtcp_reduced_size(true);
outgoingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kSendOnly);
outgoingVideoDescription->set_codecs({ payloadTypes->videoCodec, payloadTypes->rtxCodec });
cricket::StreamParams videoSendStreamParams;
std::vector<uint32_t> simulcastGroupSsrcs;
std::vector<cricket::SsrcGroup> fidGroups;
for (const auto &layer : _outgoingVideoSsrcs.simulcastLayers) {
simulcastGroupSsrcs.push_back(layer.ssrc);
videoSendStreamParams.ssrcs.push_back(layer.ssrc);
videoSendStreamParams.ssrcs.push_back(layer.fidSsrc);
cricket::SsrcGroup fidGroup(cricket::kFidSsrcGroupSemantics, { layer.ssrc, layer.fidSsrc });
fidGroups.push_back(fidGroup);
}
if (simulcastGroupSsrcs.size() > 1) {
cricket::SsrcGroup simulcastGroup(cricket::kSimSsrcGroupSemantics, simulcastGroupSsrcs);
videoSendStreamParams.ssrc_groups.push_back(simulcastGroup);
GroupJoinPayloadVideoSourceGroup payloadSimulcastGroup;
payloadSimulcastGroup.semantics = "SIM";
payloadSimulcastGroup.ssrcs = simulcastGroupSsrcs;
_videoSourceGroups.push_back(payloadSimulcastGroup);
}
for (auto fidGroup : fidGroups) {
videoSendStreamParams.ssrc_groups.push_back(fidGroup);
GroupJoinPayloadVideoSourceGroup payloadFidGroup;
payloadFidGroup.semantics = "FID";
payloadFidGroup.ssrcs = fidGroup.ssrcs;
_videoSourceGroups.push_back(payloadFidGroup);
}
videoSendStreamParams.cname = "cname";
outgoingVideoDescription->AddStream(videoSendStreamParams);
auto incomingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, 2));
incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kTransportSequenceNumberUri, 3));
incomingVideoDescription->AddRtpHeaderExtension(webrtc::RtpExtension(webrtc::RtpExtension::kVideoRotationUri, 13));
incomingVideoDescription->set_rtcp_mux(true);
incomingVideoDescription->set_rtcp_reduced_size(true);
incomingVideoDescription->set_direction(webrtc::RtpTransceiverDirection::kRecvOnly);
incomingVideoDescription->set_codecs({ payloadTypes->videoCodec, payloadTypes->rtxCodec });
_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(_outgoingVideoSsrcs.simulcastLayers[0].ssrc);
if (rtpParameters.encodings.size() == 3) {
for (int i = 0; i < (int)rtpParameters.encodings.size(); i++) {
if (i == 0) {
rtpParameters.encodings[i].min_bitrate_bps = 50000;
rtpParameters.encodings[i].max_bitrate_bps = 100000;
rtpParameters.encodings[i].scale_resolution_down_by = 4.0;
} else if (i == 1) {
rtpParameters.encodings[i].max_bitrate_bps = 150000;
rtpParameters.encodings[i].max_bitrate_bps = 200000;
rtpParameters.encodings[i].scale_resolution_down_by = 2.0;
} else if (i == 2) {
rtpParameters.encodings[i].min_bitrate_bps = 300000;
rtpParameters.encodings[i].max_bitrate_bps = 800000;
}
}
} else if (rtpParameters.encodings.size() == 2) {
for (int i = 0; i < (int)rtpParameters.encodings.size(); i++) {
if (i == 0) {
rtpParameters.encodings[i].min_bitrate_bps = 50000;
rtpParameters.encodings[i].max_bitrate_bps = 100000;
rtpParameters.encodings[i].scale_resolution_down_by = 4.0;
} else if (i == 1) {
rtpParameters.encodings[i].min_bitrate_bps = 200000;
rtpParameters.encodings[i].max_bitrate_bps = 800000;
}
}
}
_outgoingVideoChannel->media_channel()->SetRtpSendParameters(_outgoingVideoSsrcs.simulcastLayers[0].ssrc, rtpParameters);
}
void OnSentPacket_w(const rtc::SentPacket& sent_packet) {
_call->OnSentPacket(sent_packet);
}
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);
}
void setIsRtcConnected(bool isConnected) {
if (_isRtcConnected == isConnected) {
return;
}
_isRtcConnected = isConnected;
RTC_LOG(LS_INFO) << formatTimestampMillis(rtc::TimeMillis()) << ": " << "setIsRtcConnected: " << _isRtcConnected;
if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
_broadcastEnabledUntilRtcIsConnectedAtTimestamp = absl::nullopt;
if (_currentRequestedBroadcastPart) {
if (_currentRequestedBroadcastPart->task) {
_currentRequestedBroadcastPart->task->cancel();
}
_currentRequestedBroadcastPart.reset();
}
}
updateIsConnected();
}
void updateIsConnected() {
bool isEffectivelyConnected = false;
bool isTransitioningFromBroadcastToRtc = false;
switch (_connectionMode) {
case GroupConnectionMode::GroupConnectionModeNone: {
isEffectivelyConnected = false;
if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp && _isBroadcastConnected) {
isEffectivelyConnected = true;
isTransitioningFromBroadcastToRtc = true;
}
break;
}
case GroupConnectionMode::GroupConnectionModeRtc: {
isEffectivelyConnected = _isRtcConnected;
if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp && _isBroadcastConnected) {
isEffectivelyConnected = true;
isTransitioningFromBroadcastToRtc = true;
}
break;
}
case GroupConnectionMode::GroupConnectionModeBroadcast: {
isEffectivelyConnected = _isBroadcastConnected;
break;
}
}
GroupNetworkState effectiveNetworkState;
effectiveNetworkState.isConnected = isEffectivelyConnected;
effectiveNetworkState.isTransitioningFromBroadcastToRtc = isTransitioningFromBroadcastToRtc;
if (_effectiveNetworkState.isConnected != effectiveNetworkState.isConnected || _effectiveNetworkState.isTransitioningFromBroadcastToRtc != effectiveNetworkState.isTransitioningFromBroadcastToRtc) {
_effectiveNetworkState = effectiveNetworkState;
if (_effectiveNetworkState.isConnected) {
_call->SignalChannelNetworkState(webrtc::MediaType::AUDIO, webrtc::kNetworkUp);
_call->SignalChannelNetworkState(webrtc::MediaType::VIDEO, webrtc::kNetworkUp);
} else {
_call->SignalChannelNetworkState(webrtc::MediaType::AUDIO, webrtc::kNetworkDown);
_call->SignalChannelNetworkState(webrtc::MediaType::VIDEO, webrtc::kNetworkDown);
}
if (_networkStateUpdated) {
_networkStateUpdated(_effectiveNetworkState);
}
}
}
void updateIsDataChannelOpen(bool isDataChannelOpen) {
if (_isDataChannelOpen == isDataChannelOpen) {
return;
}
_isDataChannelOpen = isDataChannelOpen;
if (_isDataChannelOpen) {
maybeUpdateRemoteVideoConstaints();
}
}
void receivePacket(rtc::CopyOnWriteBuffer const &packet, bool isUnresolved) {
if (packet.size() >= 4) {
if (packet.data()[0] == 0x13 && packet.data()[1] == 0x88 && packet.data()[2] == 0x13 && packet.data()[3] == 0x88) {
// SCTP packet header (source port 5000, destination port 5000)
return;
}
}
webrtc::RtpUtility::RtpHeaderParser rtpParser(packet.data(), packet.size());
webrtc::RTPHeader header;
if (rtpParser.RTCP()) {
if (!rtpParser.ParseRtcp(&header)) {
RTC_LOG(LS_INFO) << "Could not parse rtcp header";
return;
}
_call->Receiver()->DeliverPacket(webrtc::MediaType::ANY, packet, -1);
} else {
if (!rtpParser.Parse(&header)) {
// Probably a data channel message
return;
}
if (header.ssrc == _outgoingAudioSsrc) {
return;
}
auto it = _ssrcMapping.find(header.ssrc);
if (it == _ssrcMapping.end()) {
if (isUnresolved) {
maybeReportUnknownSsrc(header.ssrc);
_missingPacketBuffer.add(header.ssrc, packet);
}
} else {
const auto it = _incomingAudioChannels.find(ChannelId(header.ssrc));
if (it != _incomingAudioChannels.end()) {
it->second->updateActivity();
}
}
}
}
void receiveRtcpPacket(rtc::CopyOnWriteBuffer const &packet, int64_t timestamp) {
_call->Receiver()->DeliverPacket(webrtc::MediaType::ANY, packet, timestamp);
}
void maybeReportUnknownSsrc(uint32_t ssrc) {
if (_reportedUnknownSsrcs.find(ssrc) == _reportedUnknownSsrcs.end()) {
_reportedUnknownSsrcs.insert(ssrc);
_pendingUnknownSsrcs.insert(ssrc);
if (!_isUnknownSsrcsScheduled) {
auto timestamp = rtc::TimeMillis();
if (_lastUnknownSsrcsReport < timestamp - 100) {
doReportPendingUnknownSsrcs();
} else {
_isUnknownSsrcsScheduled = true;
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
auto strong = weak.lock();
if (!strong) {
return;
}
strong->_isUnknownSsrcsScheduled = false;
strong->doReportPendingUnknownSsrcs();
}, 100);
}
}
}
}
void doReportPendingUnknownSsrcs() {
std::vector<uint32_t> ssrcs;
for (auto ssrc : _pendingUnknownSsrcs) {
ssrcs.push_back(ssrc);
}
_pendingUnknownSsrcs.clear();
if (ssrcs.size() != 0) {
_lastUnknownSsrcsReport = rtc::TimeMillis();
if (_participantDescriptionsRequired) {
_participantDescriptionsRequired(ssrcs);
} else {
std::vector<GroupParticipantDescription> participants;
for (auto ssrc : ssrcs) {
GroupParticipantDescription description;
description.audioSsrc = ssrc;
participants.push_back(std::move(description));
}
addParticipants(std::move(participants));
}
}
}
void maybeDeliverBufferedPackets(uint32_t ssrc) {
// TODO: Re-enable after implementing custom transport
/*auto packets = _missingPacketBuffer.get(ssrc);
if (packets.size() != 0) {
auto it = _ssrcMapping.find(ssrc);
if (it != _ssrcMapping.end()) {
for (const auto &packet : packets) {
_threads->getNetworkThread()->Invoke<void>(RTC_FROM_HERE, [this, packet]() {
_rtpTransport->DemuxPacketInternal(packet, -1);
});
}
}
}*/
}
void maybeUpdateRemoteVideoConstaints() {
if (!_isDataChannelOpen) {
return;
}
std::vector<std::string> endpointIds;
for (const auto &incomingVideoChannel : _incomingVideoChannels) {
auto ssrcMapping = _ssrcMapping.find(incomingVideoChannel.first);
if (ssrcMapping != _ssrcMapping.end()) {
if (std::find(endpointIds.begin(), endpointIds.end(), ssrcMapping->second.endpointId) == endpointIds.end()) {
endpointIds.push_back(ssrcMapping->second.endpointId);
}
}
}
std::sort(endpointIds.begin(), endpointIds.end());
std::string pinnedEndpoint;
std::ostringstream string;
string << "{" << "\n";
string << " \"colibriClass\": \"ReceiverVideoConstraintsChangedEvent\"," << "\n";
string << " \"videoConstraints\": [" << "\n";
bool isFirst = true;
for (size_t i = 0; i < endpointIds.size(); i++) {
int idealHeight = 180;
if (_currentHighQualityVideoEndpointId == endpointIds[i]) {
idealHeight = 720;
}
if (isFirst) {
isFirst = false;
} else {
if (i != 0) {
string << ",";
}
}
string << " {\n";
string << " \"id\": \"" << endpointIds[i] << "\",\n";
string << " \"idealHeight\": " << idealHeight << "\n";
string << " }";
string << "\n";
}
string << " ]" << "\n";
string << "}";
std::string result = string.str();
_networkManager->perform(RTC_FROM_HERE, [result = std::move(result)](GroupNetworkManager *networkManager) {
networkManager->sendDataChannelMessage(result);
});
}
void setConnectionMode(GroupConnectionMode connectionMode, bool keepBroadcastIfWasEnabled) {
if (_connectionMode != connectionMode) {
GroupConnectionMode previousMode = _connectionMode;
_connectionMode = connectionMode;
onConnectionModeUpdated(previousMode, keepBroadcastIfWasEnabled);
}
}
void onConnectionModeUpdated(GroupConnectionMode previousMode, bool keepBroadcastIfWasEnabled) {
RTC_CHECK(_connectionMode != previousMode);
if (previousMode == GroupConnectionMode::GroupConnectionModeRtc) {
_networkManager->perform(RTC_FROM_HERE, [](GroupNetworkManager *networkManager) {
networkManager->stop();
});
} else if (previousMode == GroupConnectionMode::GroupConnectionModeBroadcast) {
if (keepBroadcastIfWasEnabled) {
_broadcastEnabledUntilRtcIsConnectedAtTimestamp = rtc::TimeMillis();
} else {
if (_currentRequestedBroadcastPart) {
if (_currentRequestedBroadcastPart->task) {
_currentRequestedBroadcastPart->task->cancel();
}
_currentRequestedBroadcastPart.reset();
}
}
}
if (_connectionMode == GroupConnectionMode::GroupConnectionModeNone) {
destroyOutgoingAudioChannel();
auto generator = std::mt19937(std::random_device()());
auto distribution = std::uniform_int_distribution<uint32_t>();
do {
_outgoingAudioSsrc = distribution(generator) & 0x7fffffffU;
} while (!_outgoingAudioSsrc);
if (!_isMuted) {
createOutgoingAudioChannel();
}
}
switch (_connectionMode) {
case GroupConnectionMode::GroupConnectionModeNone: {
break;
}
case GroupConnectionMode::GroupConnectionModeRtc: {
_networkManager->perform(RTC_FROM_HERE, [](GroupNetworkManager *networkManager) {
networkManager->start();
});
break;
}
case GroupConnectionMode::GroupConnectionModeBroadcast: {
_broadcastTimestamp = 100001;
_isBroadcastConnected = false;
beginBroadcastPartsDecodeTimer(0);
requestNextBroadcastPart();
break;
}
default: {
//RTC_FATAL() << "Unknown connectionMode";
break;
}
}
updateIsConnected();
}
void emitJoinPayload(std::function<void(GroupJoinPayload)> completion) {
_networkManager->perform(RTC_FROM_HERE, [outgoingAudioSsrc = _outgoingAudioSsrc, videoPayloadTypes = _videoPayloadTypes, videoExtensionMap = _videoExtensionMap, videoSourceGroups = _videoSourceGroups, completion](GroupNetworkManager *networkManager) {
GroupJoinPayload payload;
payload.ssrc = outgoingAudioSsrc;
/*payload.videoPayloadTypes = videoPayloadTypes;
payload.videoExtensionMap = videoExtensionMap;
payload.videoSourceGroups = videoSourceGroups;*/
auto localIceParameters = networkManager->getLocalIceParameters();
payload.ufrag = localIceParameters.ufrag;
payload.pwd = localIceParameters.pwd;
auto localFingerprint = networkManager->getLocalFingerprint();
if (localFingerprint) {
GroupJoinPayloadFingerprint serializedFingerprint;
serializedFingerprint.hash = localFingerprint->algorithm;
serializedFingerprint.fingerprint = localFingerprint->GetRfc4572Fingerprint();
serializedFingerprint.setup = "passive";
payload.fingerprints.push_back(std::move(serializedFingerprint));
}
completion(payload);
});
}
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture, std::function<void(GroupJoinPayload)> completion, bool isInitializing) {
bool resetBitrate = (_videoCapture == nullptr) != (videoCapture == nullptr) && !isInitializing;
if (!isInitializing && _videoCapture == videoCapture) {
return;
}
_videoCapture = videoCapture;
if (_outgoingVideoChannel) {
if (_videoCapture) {
_outgoingVideoChannel->Enable(true);
_outgoingVideoChannel->media_channel()->SetVideoSend(_outgoingVideoSsrcs.simulcastLayers[0].ssrc, NULL, GetVideoCaptureAssumingSameThread(_videoCapture.get())->source());
} else {
_outgoingVideoChannel->Enable(false);
_outgoingVideoChannel->media_channel()->SetVideoSend(_outgoingVideoSsrcs.simulcastLayers[0].ssrc, NULL, nullptr);
}
}
if (resetBitrate) {
adjustBitratePreferences(true);
}
}
void setAudioOutputDevice(const std::string &id) {
#if not defined(WEBRTC_IOS) && not defined(WEBRTC_ANDROID)
SetAudioOutputDeviceById(_audioDeviceModule.get(), id);
#endif // WEBRTC_IOS
}
void setAudioInputDevice(const std::string &id) {
#if not defined(WEBRTC_IOS) && not defined(WEBRTC_ANDROID)
SetAudioInputDeviceById(_audioDeviceModule.get(), id);
#endif // WEBRTC_IOS
}
void setJoinResponsePayload(GroupJoinResponsePayload payload, std::vector<tgcalls::GroupParticipantDescription> &&participants) {
RTC_LOG(LS_INFO) << formatTimestampMillis(rtc::TimeMillis()) << ": " << "setJoinResponsePayload";
_networkManager->perform(RTC_FROM_HERE, [payload](GroupNetworkManager *networkManager) {
PeerIceParameters remoteIceParameters;
remoteIceParameters.ufrag = payload.ufrag;
remoteIceParameters.pwd = payload.pwd;
std::vector<cricket::Candidate> iceCandidates;
for (auto const &candidate : payload.candidates) {
rtc::SocketAddress address(candidate.ip, stringToInt(candidate.port));
cricket::Candidate parsedCandidate(
/*component=*/stringToInt(candidate.component),
/*protocol=*/candidate.protocol,
/*address=*/address,
/*priority=*/stringToUInt32(candidate.priority),
/*username=*/payload.ufrag,
/*password=*/payload.pwd,
/*type=*/candidate.type,
/*generation=*/stringToUInt32(candidate.generation),
/*foundation=*/candidate.foundation,
/*network_id=*/stringToUInt16(candidate.network),
/*network_cost=*/0
);
iceCandidates.push_back(parsedCandidate);
}
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
if (payload.fingerprints.size() != 0) {
fingerprint = rtc::SSLFingerprint::CreateUniqueFromRfc4572(payload.fingerprints[0].hash, payload.fingerprints[0].fingerprint);
}
networkManager->setRemoteParams(remoteIceParameters, iceCandidates, fingerprint.get());
});
addParticipants(std::move(participants));
}
void addParticipants(std::vector<GroupParticipantDescription> &&participants) {
if (_disableIncomingChannels) {
return;
}
for (const auto &participant : participants) {
if (participant.audioSsrc == _outgoingAudioSsrc) {
continue;
}
_reportedUnknownSsrcs.erase(participant.audioSsrc);
if (_incomingAudioChannels.find(ChannelId(participant.audioSsrc)) == _incomingAudioChannels.end()) {
addIncomingAudioChannel(participant.endpointId, ChannelId(participant.audioSsrc));
}
if (participant.videoPayloadTypes.size() != 0 && participant.videoSourceGroups.size() != 0) {
if (_incomingVideoChannels.find(participant.audioSsrc) == _incomingVideoChannels.end()) {
addIncomingVideoChannel(participant);
}
}
}
}
void removeSsrcs(std::vector<uint32_t> ssrcs) {
bool updatedIncomingVideoChannels = false;
for (auto ssrc : ssrcs) {
auto it = _ssrcMapping.find(ssrc);
if (it != _ssrcMapping.end()) {
auto mainSsrc = it->second.ssrc;
auto audioChannel = _incomingAudioChannels.find(ChannelId(mainSsrc));
if (audioChannel != _incomingAudioChannels.end()) {
_incomingAudioChannels.erase(audioChannel);
}
auto videoChannel = _incomingVideoChannels.find(mainSsrc);
if (videoChannel != _incomingVideoChannels.end()) {
_incomingVideoChannels.erase(videoChannel);
updatedIncomingVideoChannels = true;
}
}
}
if (updatedIncomingVideoChannels) {
updateIncomingVideoSources();
}
}
void setIsMuted(bool isMuted) {
if (_isMuted == isMuted) {
return;
}
_isMuted = isMuted;
onUpdatedIsMuted();
}
void onUpdatedIsMuted() {
if (!_isMuted) {
if (!_outgoingAudioChannel) {
createOutgoingAudioChannel();
}
}
if (_outgoingAudioChannel) {
_outgoingAudioChannel->Enable(!_isMuted);
_outgoingAudioChannel->media_channel()->SetAudioSend(_outgoingAudioSsrc, _isRtcConnected && !_isMuted, nullptr, &_audioSource);
}
}
void addIncomingVideoOutput(uint32_t ssrc, std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
auto it = _incomingVideoChannels.find(ssrc);
if (it != _incomingVideoChannels.end()) {
it->second->addSink(sink);
}
}
void addIncomingAudioChannel(std::string const &endpointId, ChannelId ssrc, bool isRawPcm = false) {
if (_incomingAudioChannels.find(ssrc) != _incomingAudioChannels.end()) {
return;
}
if (_incomingAudioChannels.size() > 5) {
int64_t minActivity = INT64_MAX;
ChannelId minActivityChannelId(0, 0);
for (const auto &it : _incomingAudioChannels) {
auto activity = it.second->getActivity();
if (activity < minActivity) {
minActivity = activity;
minActivityChannelId = it.first;
}
}
if (minActivityChannelId.networkSsrc != 0) {
const auto it = _incomingAudioChannels.find(minActivityChannelId);
if (it != _incomingAudioChannels.end()) {
_incomingAudioChannels.erase(it);
}
auto reportedIt = _reportedUnknownSsrcs.find(minActivityChannelId.actualSsrc);
if (reportedIt != _reportedUnknownSsrcs.end()) {
_reportedUnknownSsrcs.erase(reportedIt);
}
auto mappingIt = _ssrcMapping.find(minActivityChannelId.actualSsrc);
if (mappingIt != _ssrcMapping.end()) {
_ssrcMapping.erase(mappingIt);
}
}
}
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
std::function<void(AudioSinkImpl::Update)> onAudioSinkUpdate;
if (_audioLevelsUpdated) {
onAudioSinkUpdate = [weak, ssrc = ssrc, threads = _threads](AudioSinkImpl::Update update) {
threads->getProcessThread()->PostTask(RTC_FROM_HERE, [weak, ssrc, update, threads]() {
bool voice = update.vad->update(update.buffer.get());
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, ssrc, update, voice]() {
auto strong = weak.lock();
if (!strong) {
return;
}
GroupLevelValue mappedUpdate;
mappedUpdate.level = update.level;
mappedUpdate.voice = voice;
strong->_audioLevels[ssrc] = mappedUpdate;
});
});
};
}
std::unique_ptr<IncomingAudioChannel> channel(new IncomingAudioChannel(
_channelManager.get(),
_call.get(),
_rtpTransport,
_uniqueRandomIdGenerator.get(),
isRawPcm,
ssrc,
std::move(onAudioSinkUpdate),
_onAudioFrame,
*_threads
));
auto volume = _volumeBySsrc.find(ssrc.actualSsrc);
if (volume != _volumeBySsrc.end()) {
channel->setVolume(volume->second);
}
_incomingAudioChannels.insert(std::make_pair(ssrc, std::move(channel)));
SsrcMappingInfo mapping;
mapping.ssrc = ssrc.networkSsrc;
mapping.isVideo = false;
mapping.endpointId = endpointId;
_ssrcMapping.insert(std::make_pair(ssrc.networkSsrc, mapping));
maybeDeliverBufferedPackets(ssrc.networkSsrc);
}
void addIncomingVideoChannel(GroupParticipantDescription const &participant) {
if (_incomingVideoChannels.find(participant.audioSsrc) != _incomingVideoChannels.end()) {
return;
}
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
std::unique_ptr<IncomingVideoChannel> channel(new IncomingVideoChannel(
_channelManager.get(),
_call.get(),
_rtpTransport,
_uniqueRandomIdGenerator.get(),
_availableVideoFormats,
participant,
*_threads
));
_incomingVideoChannels.insert(std::make_pair(participant.audioSsrc, std::move(channel)));
std::vector<uint32_t> allSsrcs;
for (const auto &group : participant.videoSourceGroups) {
for (auto ssrc : group.ssrcs) {
if (_ssrcMapping.find(ssrc) == _ssrcMapping.end()) {
allSsrcs.push_back(ssrc);
SsrcMappingInfo mapping;
mapping.ssrc = participant.audioSsrc;
mapping.isVideo = true;
mapping.endpointId = participant.endpointId;
_ssrcMapping.insert(std::make_pair(ssrc, mapping));
}
}
}
updateIncomingVideoSources();
for (auto ssrc : allSsrcs) {
maybeDeliverBufferedPackets(ssrc);
}
}
void updateIncomingVideoSources() {
if (_incomingVideoSourcesUpdated) {
std::vector<uint32_t> videoChannelSsrcs;
for (const auto &it : _incomingVideoChannels) {
videoChannelSsrcs.push_back(it.first);
}
_incomingVideoSourcesUpdated(videoChannelSsrcs);
}
}
void setVolume(uint32_t ssrc, double volume) {
auto current = _volumeBySsrc.find(ssrc);
if (current != _volumeBySsrc.end() && std::abs(current->second - volume) < 0.0001) {
return;
}
_volumeBySsrc[ssrc] = volume;
auto it = _incomingAudioChannels.find(ChannelId(ssrc));
if (it != _incomingAudioChannels.end()) {
it->second->setVolume(volume);
}
it = _incomingAudioChannels.find(ChannelId(ssrc + 1000, ssrc));
if (it != _incomingAudioChannels.end()) {
it->second->setVolume(volume);
}
}
void setFullSizeVideoSsrc(uint32_t ssrc) {
auto ssrcMapping = _ssrcMapping.find(ssrc);
std::string currentHighQualityVideoEndpointId;
if (ssrcMapping != _ssrcMapping.end()) {
currentHighQualityVideoEndpointId = ssrcMapping->second.endpointId;
}
if (_currentHighQualityVideoEndpointId != currentHighQualityVideoEndpointId) {
_currentHighQualityVideoEndpointId = currentHighQualityVideoEndpointId;
maybeUpdateRemoteVideoConstaints();
}
}
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;
GroupConnectionMode _connectionMode = GroupConnectionMode::GroupConnectionModeNone;
std::function<void(GroupNetworkState)> _networkStateUpdated;
std::function<void(GroupLevelsUpdate const &)> _audioLevelsUpdated;
std::function<void(uint32_t, const AudioFrame &)> _onAudioFrame;
std::function<void(std::vector<uint32_t> const &)> _incomingVideoSourcesUpdated;
std::function<void(std::vector<uint32_t> const &)> _participantDescriptionsRequired;
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, std::function<void(BroadcastPart &&)>)> _requestBroadcastPart;
std::shared_ptr<VideoCaptureInterface> _videoCapture;
bool _disableIncomingChannels = false;
bool _useDummyChannel{true};
int64_t _lastUnknownSsrcsReport = 0;
std::set<uint32_t> _pendingUnknownSsrcs;
bool _isUnknownSsrcsScheduled = false;
std::set<uint32_t> _reportedUnknownSsrcs;
std::unique_ptr<ThreadLocalObject<GroupNetworkManager>> _networkManager;
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::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
std::string _initialInputDeviceId;
std::string _initialOutputDeviceId;
// _outgoingAudioChannel memory is managed by _channelManager
cricket::VoiceChannel *_outgoingAudioChannel = nullptr;
uint32_t _outgoingAudioSsrc = 0;
std::vector<webrtc::SdpVideoFormat> _availableVideoFormats;
std::vector<GroupJoinPayloadVideoPayloadType> _videoPayloadTypes;
std::vector<std::pair<uint32_t, std::string>> _videoExtensionMap;
std::vector<GroupJoinPayloadVideoSourceGroup> _videoSourceGroups;
std::unique_ptr<rtc::UniqueRandomIdGenerator> _uniqueRandomIdGenerator;
webrtc::RtpTransport *_rtpTransport = nullptr;
std::unique_ptr<cricket::ChannelManager> _channelManager;
std::unique_ptr<webrtc::VideoBitrateAllocatorFactory> _videoBitrateAllocatorFactory;
// _outgoingVideoChannel memory is managed by _channelManager
cricket::VideoChannel *_outgoingVideoChannel = nullptr;
VideoSsrcs _outgoingVideoSsrcs;
std::map<ChannelId, GroupLevelValue> _audioLevels;
GroupLevelValue _myAudioLevel;
bool _isMuted = true;
MissingSsrcPacketBuffer _missingPacketBuffer;
std::map<uint32_t, SsrcMappingInfo> _ssrcMapping;
std::map<uint32_t, double> _volumeBySsrc;
std::map<ChannelId, std::unique_ptr<IncomingAudioChannel>> _incomingAudioChannels;
std::map<uint32_t, std::unique_ptr<IncomingVideoChannel>> _incomingVideoChannels;
std::string _currentHighQualityVideoEndpointId;
int64_t _broadcastPartDurationMilliseconds = 500;
std::vector<std::unique_ptr<StreamingPart>> _sourceBroadcastParts;
std::map<uint32_t, uint16_t> _broadcastSeqBySsrc;
uint32_t _broadcastTimestamp = 0;
int64_t _nextBroadcastTimestampMilliseconds = 0;
absl::optional<RequestedBroadcastPart> _currentRequestedBroadcastPart;
int64_t _lastBroadcastPartReceivedTimestamp = 0;
bool _isRtcConnected = false;
bool _isBroadcastConnected = false;
absl::optional<int64_t> _broadcastEnabledUntilRtcIsConnectedAtTimestamp;
bool _isDataChannelOpen = false;
GroupNetworkState _effectiveNetworkState;
std::shared_ptr<PlatformContext> _platformContext;
};
GroupInstanceCustomImpl::GroupInstanceCustomImpl(GroupInstanceDescriptor &&descriptor) {
if (descriptor.config.need_log) {
_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 = descriptor.threads;
_internal.reset(new ThreadLocalObject<GroupInstanceCustomInternal>(_threads->getMediaThread(), [descriptor = std::move(descriptor), threads = _threads]() mutable {
return new GroupInstanceCustomInternal(std::move(descriptor), threads);
}));
_internal->perform(RTC_FROM_HERE, [](GroupInstanceCustomInternal *internal) {
internal->start();
});
}
GroupInstanceCustomImpl::~GroupInstanceCustomImpl() {
if (_logSink) {
rtc::LogMessage::RemoveLogToStream(_logSink.get());
}
_internal.reset();
// Wait until _internal is destroyed
_threads->getMediaThread()->Invoke<void>(RTC_FROM_HERE, [] {});
}
void GroupInstanceCustomImpl::stop() {
_internal->perform(RTC_FROM_HERE, [](GroupInstanceCustomInternal *internal) {
internal->stop();
});
}
void GroupInstanceCustomImpl::setConnectionMode(GroupConnectionMode connectionMode, bool keepBroadcastIfWasEnabled) {
_internal->perform(RTC_FROM_HERE, [connectionMode, keepBroadcastIfWasEnabled](GroupInstanceCustomInternal *internal) {
internal->setConnectionMode(connectionMode, keepBroadcastIfWasEnabled);
});
}
void GroupInstanceCustomImpl::emitJoinPayload(std::function<void(GroupJoinPayload)> completion) {
_internal->perform(RTC_FROM_HERE, [completion](GroupInstanceCustomInternal *internal) {
internal->emitJoinPayload(completion);
});
}
void GroupInstanceCustomImpl::setJoinResponsePayload(GroupJoinResponsePayload payload, std::vector<tgcalls::GroupParticipantDescription> &&participants) {
_internal->perform(RTC_FROM_HERE, [payload, participants = std::move(participants)](GroupInstanceCustomInternal *internal) mutable {
internal->setJoinResponsePayload(payload, std::move(participants));
});
}
void GroupInstanceCustomImpl::addParticipants(std::vector<GroupParticipantDescription> &&participants) {
_internal->perform(RTC_FROM_HERE, [participants = std::move(participants)](GroupInstanceCustomInternal *internal) mutable {
internal->addParticipants(std::move(participants));
});
}
void GroupInstanceCustomImpl::removeSsrcs(std::vector<uint32_t> ssrcs) {
_internal->perform(RTC_FROM_HERE, [ssrcs = std::move(ssrcs)](GroupInstanceCustomInternal *internal) mutable {
internal->removeSsrcs(ssrcs);
});
}
void GroupInstanceCustomImpl::setIsMuted(bool isMuted) {
_internal->perform(RTC_FROM_HERE, [isMuted](GroupInstanceCustomInternal *internal) {
internal->setIsMuted(isMuted);
});
}
void GroupInstanceCustomImpl::setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture, std::function<void(GroupJoinPayload)> completion) {
_internal->perform(RTC_FROM_HERE, [videoCapture, completion](GroupInstanceCustomInternal *internal) {
internal->setVideoCapture(videoCapture, completion, false);
});
}
void GroupInstanceCustomImpl::setAudioOutputDevice(std::string id) {
_internal->perform(RTC_FROM_HERE, [id](GroupInstanceCustomInternal *internal) {
internal->setAudioOutputDevice(id);
});
}
void GroupInstanceCustomImpl::setAudioInputDevice(std::string id) {
_internal->perform(RTC_FROM_HERE, [id](GroupInstanceCustomInternal *internal) {
internal->setAudioInputDevice(id);
});
}
void GroupInstanceCustomImpl::addIncomingVideoOutput(uint32_t ssrc, std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
_internal->perform(RTC_FROM_HERE, [ssrc, sink](GroupInstanceCustomInternal *internal) mutable {
internal->addIncomingVideoOutput(ssrc, sink);
});
}
void GroupInstanceCustomImpl::setVolume(uint32_t ssrc, double volume) {
_internal->perform(RTC_FROM_HERE, [ssrc, volume](GroupInstanceCustomInternal *internal) {
internal->setVolume(ssrc, volume);
});
}
void GroupInstanceCustomImpl::setFullSizeVideoSsrc(uint32_t ssrc) {
_internal->perform(RTC_FROM_HERE, [ssrc](GroupInstanceCustomInternal *internal) {
internal->setFullSizeVideoSsrc(ssrc);
});
}
std::vector<GroupInstanceInterface::AudioDevice> GroupInstanceInterface::getAudioDevices(AudioDevice::Type type) {
auto result = std::vector<AudioDevice>();
#ifdef WEBRTC_LINUX //Not needed for ios, and some crl::sync stuff is needed for windows
const auto resolve = [&] {
const auto queueFactory = webrtc::CreateDefaultTaskQueueFactory();
const auto info = webrtc::AudioDeviceModule::Create(
webrtc::AudioDeviceModule::kPlatformDefaultAudio,
queueFactory.get());
if (!info || info->Init() < 0) {
return;
}
const auto count = type == AudioDevice::Type::Input ? info->RecordingDevices() : info->PlayoutDevices();
if (count <= 0) {
return;
}
for (auto i = int16_t(); i != count; ++i) {
char name[webrtc::kAdmMaxDeviceNameSize + 1] = { 0 };
char id[webrtc::kAdmMaxGuidSize + 1] = { 0 };
if (type == AudioDevice::Type::Input) {
info->RecordingDeviceName(i, name, id);
} else {
info->PlayoutDeviceName(i, name, id);
}
result.push_back({ id, name });
}
};
resolve();
#endif
return result;
}
} // namespace tgcalls