/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/voice_engine/channel.h" #include #include #include "webrtc/audio/utility/audio_frame_operations.h" #include "webrtc/base/array_view.h" #include "webrtc/base/checks.h" #include "webrtc/base/criticalsection.h" #include "webrtc/base/format_macros.h" #include "webrtc/base/location.h" #include "webrtc/base/logging.h" #include "webrtc/base/rate_limiter.h" #include "webrtc/base/task_queue.h" #include "webrtc/base/thread_checker.h" #include "webrtc/base/timeutils.h" #include "webrtc/call/rtp_transport_controller_send.h" #include "webrtc/config.h" #include "webrtc/logging/rtc_event_log/rtc_event_log.h" #include "webrtc/modules/audio_coding/codecs/audio_format_conversion.h" #include "webrtc/modules/audio_device/include/audio_device.h" #include "webrtc/modules/audio_processing/include/audio_processing.h" #include "webrtc/modules/include/module_common_types.h" #include "webrtc/modules/pacing/packet_router.h" #include "webrtc/modules/rtp_rtcp/include/receive_statistics.h" #include "webrtc/modules/rtp_rtcp/include/rtp_payload_registry.h" #include "webrtc/modules/rtp_rtcp/include/rtp_receiver.h" #include "webrtc/modules/rtp_rtcp/source/rtp_packet_received.h" #include "webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h" #include "webrtc/modules/utility/include/process_thread.h" #include "webrtc/system_wrappers/include/field_trial.h" #include "webrtc/system_wrappers/include/trace.h" #include "webrtc/voice_engine/include/voe_rtp_rtcp.h" #include "webrtc/voice_engine/output_mixer.h" #include "webrtc/voice_engine/statistics.h" #include "webrtc/voice_engine/utility.h" namespace webrtc { namespace voe { namespace { constexpr int64_t kMaxRetransmissionWindowMs = 1000; constexpr int64_t kMinRetransmissionWindowMs = 30; } // namespace const int kTelephoneEventAttenuationdB = 10; class RtcEventLogProxy final : public webrtc::RtcEventLog { public: RtcEventLogProxy() : event_log_(nullptr) {} bool StartLogging(const std::string& file_name, int64_t max_size_bytes) override { RTC_NOTREACHED(); return false; } bool StartLogging(rtc::PlatformFile log_file, int64_t max_size_bytes) override { RTC_NOTREACHED(); return false; } void StopLogging() override { RTC_NOTREACHED(); } void LogVideoReceiveStreamConfig( const webrtc::VideoReceiveStream::Config& config) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogVideoReceiveStreamConfig(config); } } void LogVideoSendStreamConfig( const webrtc::VideoSendStream::Config& config) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogVideoSendStreamConfig(config); } } void LogAudioReceiveStreamConfig( const webrtc::AudioReceiveStream::Config& config) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogAudioReceiveStreamConfig(config); } } void LogAudioSendStreamConfig( const webrtc::AudioSendStream::Config& config) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogAudioSendStreamConfig(config); } } void LogRtpHeader(webrtc::PacketDirection direction, webrtc::MediaType media_type, const uint8_t* header, size_t packet_length) override { LogRtpHeader(direction, media_type, header, packet_length, PacedPacketInfo::kNotAProbe); } void LogRtpHeader(webrtc::PacketDirection direction, webrtc::MediaType media_type, const uint8_t* header, size_t packet_length, int probe_cluster_id) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogRtpHeader(direction, media_type, header, packet_length, probe_cluster_id); } } void LogRtcpPacket(webrtc::PacketDirection direction, webrtc::MediaType media_type, const uint8_t* packet, size_t length) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogRtcpPacket(direction, media_type, packet, length); } } void LogAudioPlayout(uint32_t ssrc) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogAudioPlayout(ssrc); } } void LogLossBasedBweUpdate(int32_t bitrate_bps, uint8_t fraction_loss, int32_t total_packets) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogLossBasedBweUpdate(bitrate_bps, fraction_loss, total_packets); } } void LogDelayBasedBweUpdate(int32_t bitrate_bps, BandwidthUsage detector_state) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogDelayBasedBweUpdate(bitrate_bps, detector_state); } } void LogAudioNetworkAdaptation( const AudioNetworkAdaptor::EncoderRuntimeConfig& config) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogAudioNetworkAdaptation(config); } } void LogProbeClusterCreated(int id, int bitrate_bps, int min_probes, int min_bytes) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogProbeClusterCreated(id, bitrate_bps, min_probes, min_bytes); } }; void LogProbeResultSuccess(int id, int bitrate_bps) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogProbeResultSuccess(id, bitrate_bps); } }; void LogProbeResultFailure(int id, ProbeFailureReason failure_reason) override { rtc::CritScope lock(&crit_); if (event_log_) { event_log_->LogProbeResultFailure(id, failure_reason); } }; void SetEventLog(RtcEventLog* event_log) { rtc::CritScope lock(&crit_); event_log_ = event_log; } private: rtc::CriticalSection crit_; RtcEventLog* event_log_ GUARDED_BY(crit_); RTC_DISALLOW_COPY_AND_ASSIGN(RtcEventLogProxy); }; class RtcpRttStatsProxy final : public RtcpRttStats { public: RtcpRttStatsProxy() : rtcp_rtt_stats_(nullptr) {} void OnRttUpdate(int64_t rtt) override { rtc::CritScope lock(&crit_); if (rtcp_rtt_stats_) rtcp_rtt_stats_->OnRttUpdate(rtt); } int64_t LastProcessedRtt() const override { rtc::CritScope lock(&crit_); if (!rtcp_rtt_stats_) return 0; return rtcp_rtt_stats_->LastProcessedRtt(); } void SetRtcpRttStats(RtcpRttStats* rtcp_rtt_stats) { rtc::CritScope lock(&crit_); rtcp_rtt_stats_ = rtcp_rtt_stats; } private: rtc::CriticalSection crit_; RtcpRttStats* rtcp_rtt_stats_ GUARDED_BY(crit_); RTC_DISALLOW_COPY_AND_ASSIGN(RtcpRttStatsProxy); }; class TransportFeedbackProxy : public TransportFeedbackObserver { public: TransportFeedbackProxy() : feedback_observer_(nullptr) { pacer_thread_.DetachFromThread(); network_thread_.DetachFromThread(); } void SetTransportFeedbackObserver( TransportFeedbackObserver* feedback_observer) { RTC_DCHECK(thread_checker_.CalledOnValidThread()); rtc::CritScope lock(&crit_); feedback_observer_ = feedback_observer; } // Implements TransportFeedbackObserver. void AddPacket(uint32_t ssrc, uint16_t sequence_number, size_t length, const PacedPacketInfo& pacing_info) override { RTC_DCHECK(pacer_thread_.CalledOnValidThread()); rtc::CritScope lock(&crit_); if (feedback_observer_) feedback_observer_->AddPacket(ssrc, sequence_number, length, pacing_info); } void OnTransportFeedback(const rtcp::TransportFeedback& feedback) override { RTC_DCHECK(network_thread_.CalledOnValidThread()); rtc::CritScope lock(&crit_); if (feedback_observer_) feedback_observer_->OnTransportFeedback(feedback); } std::vector GetTransportFeedbackVector() const override { RTC_NOTREACHED(); return std::vector(); } private: rtc::CriticalSection crit_; rtc::ThreadChecker thread_checker_; rtc::ThreadChecker pacer_thread_; rtc::ThreadChecker network_thread_; TransportFeedbackObserver* feedback_observer_ GUARDED_BY(&crit_); }; class TransportSequenceNumberProxy : public TransportSequenceNumberAllocator { public: TransportSequenceNumberProxy() : seq_num_allocator_(nullptr) { pacer_thread_.DetachFromThread(); } void SetSequenceNumberAllocator( TransportSequenceNumberAllocator* seq_num_allocator) { RTC_DCHECK(thread_checker_.CalledOnValidThread()); rtc::CritScope lock(&crit_); seq_num_allocator_ = seq_num_allocator; } // Implements TransportSequenceNumberAllocator. uint16_t AllocateSequenceNumber() override { RTC_DCHECK(pacer_thread_.CalledOnValidThread()); rtc::CritScope lock(&crit_); if (!seq_num_allocator_) return 0; return seq_num_allocator_->AllocateSequenceNumber(); } private: rtc::CriticalSection crit_; rtc::ThreadChecker thread_checker_; rtc::ThreadChecker pacer_thread_; TransportSequenceNumberAllocator* seq_num_allocator_ GUARDED_BY(&crit_); }; class RtpPacketSenderProxy : public RtpPacketSender { public: RtpPacketSenderProxy() : rtp_packet_sender_(nullptr) {} void SetPacketSender(RtpPacketSender* rtp_packet_sender) { RTC_DCHECK(thread_checker_.CalledOnValidThread()); rtc::CritScope lock(&crit_); rtp_packet_sender_ = rtp_packet_sender; } // Implements RtpPacketSender. void InsertPacket(Priority priority, uint32_t ssrc, uint16_t sequence_number, int64_t capture_time_ms, size_t bytes, bool retransmission) override { rtc::CritScope lock(&crit_); if (rtp_packet_sender_) { rtp_packet_sender_->InsertPacket(priority, ssrc, sequence_number, capture_time_ms, bytes, retransmission); } } private: rtc::ThreadChecker thread_checker_; rtc::CriticalSection crit_; RtpPacketSender* rtp_packet_sender_ GUARDED_BY(&crit_); }; class VoERtcpObserver : public RtcpBandwidthObserver { public: explicit VoERtcpObserver(Channel* owner) : owner_(owner), bandwidth_observer_(nullptr) {} virtual ~VoERtcpObserver() {} void SetBandwidthObserver(RtcpBandwidthObserver* bandwidth_observer) { rtc::CritScope lock(&crit_); bandwidth_observer_ = bandwidth_observer; } void OnReceivedEstimatedBitrate(uint32_t bitrate) override { rtc::CritScope lock(&crit_); if (bandwidth_observer_) { bandwidth_observer_->OnReceivedEstimatedBitrate(bitrate); } } void OnReceivedRtcpReceiverReport(const ReportBlockList& report_blocks, int64_t rtt, int64_t now_ms) override { { rtc::CritScope lock(&crit_); if (bandwidth_observer_) { bandwidth_observer_->OnReceivedRtcpReceiverReport(report_blocks, rtt, now_ms); } } // TODO(mflodman): Do we need to aggregate reports here or can we jut send // what we get? I.e. do we ever get multiple reports bundled into one RTCP // report for VoiceEngine? if (report_blocks.empty()) return; int fraction_lost_aggregate = 0; int total_number_of_packets = 0; // If receiving multiple report blocks, calculate the weighted average based // on the number of packets a report refers to. for (ReportBlockList::const_iterator block_it = report_blocks.begin(); block_it != report_blocks.end(); ++block_it) { // Find the previous extended high sequence number for this remote SSRC, // to calculate the number of RTP packets this report refers to. Ignore if // we haven't seen this SSRC before. std::map::iterator seq_num_it = extended_max_sequence_number_.find(block_it->sourceSSRC); int number_of_packets = 0; if (seq_num_it != extended_max_sequence_number_.end()) { number_of_packets = block_it->extendedHighSeqNum - seq_num_it->second; } fraction_lost_aggregate += number_of_packets * block_it->fractionLost; total_number_of_packets += number_of_packets; extended_max_sequence_number_[block_it->sourceSSRC] = block_it->extendedHighSeqNum; } int weighted_fraction_lost = 0; if (total_number_of_packets > 0) { weighted_fraction_lost = (fraction_lost_aggregate + total_number_of_packets / 2) / total_number_of_packets; } owner_->OnUplinkPacketLossRate(weighted_fraction_lost / 255.0f); } private: Channel* owner_; // Maps remote side ssrc to extended highest sequence number received. std::map extended_max_sequence_number_; rtc::CriticalSection crit_; RtcpBandwidthObserver* bandwidth_observer_ GUARDED_BY(crit_); }; class Channel::ProcessAndEncodeAudioTask : public rtc::QueuedTask { public: ProcessAndEncodeAudioTask(std::unique_ptr audio_frame, Channel* channel) : audio_frame_(std::move(audio_frame)), channel_(channel) { RTC_DCHECK(channel_); } private: bool Run() override { RTC_DCHECK_RUN_ON(channel_->encoder_queue_); channel_->ProcessAndEncodeAudioOnTaskQueue(audio_frame_.get()); return true; } std::unique_ptr audio_frame_; Channel* const channel_; }; int32_t Channel::SendData(FrameType frameType, uint8_t payloadType, uint32_t timeStamp, const uint8_t* payloadData, size_t payloadSize, const RTPFragmentationHeader* fragmentation) { RTC_DCHECK_RUN_ON(encoder_queue_); WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendData(frameType=%u, payloadType=%u, timeStamp=%u," " payloadSize=%" PRIuS ", fragmentation=0x%x)", frameType, payloadType, timeStamp, payloadSize, fragmentation); if (_includeAudioLevelIndication) { // Store current audio level in the RTP/RTCP module. // The level will be used in combination with voice-activity state // (frameType) to add an RTP header extension _rtpRtcpModule->SetAudioLevel(rms_level_.Average()); } // Push data from ACM to RTP/RTCP-module to deliver audio frame for // packetization. // This call will trigger Transport::SendPacket() from the RTP/RTCP module. if (!_rtpRtcpModule->SendOutgoingData( (FrameType&)frameType, payloadType, timeStamp, // Leaving the time when this frame was // received from the capture device as // undefined for voice for now. -1, payloadData, payloadSize, fragmentation, nullptr, nullptr)) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceWarning, "Channel::SendData() failed to send data to RTP/RTCP module"); return -1; } return 0; } bool Channel::SendRtp(const uint8_t* data, size_t len, const PacketOptions& options) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendPacket(channel=%d, len=%" PRIuS ")", len); rtc::CritScope cs(&_callbackCritSect); if (_transportPtr == NULL) { WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendPacket() failed to send RTP packet due to" " invalid transport object"); return false; } uint8_t* bufferToSendPtr = (uint8_t*)data; size_t bufferLength = len; if (!_transportPtr->SendRtp(bufferToSendPtr, bufferLength, options)) { std::string transport_name = _externalTransport ? "external transport" : "WebRtc sockets"; WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendPacket() RTP transmission using %s failed", transport_name.c_str()); return false; } return true; } bool Channel::SendRtcp(const uint8_t* data, size_t len) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendRtcp(len=%" PRIuS ")", len); rtc::CritScope cs(&_callbackCritSect); if (_transportPtr == NULL) { WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendRtcp() failed to send RTCP packet" " due to invalid transport object"); return false; } uint8_t* bufferToSendPtr = (uint8_t*)data; size_t bufferLength = len; int n = _transportPtr->SendRtcp(bufferToSendPtr, bufferLength); if (n < 0) { std::string transport_name = _externalTransport ? "external transport" : "WebRtc sockets"; WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendRtcp() transmission using %s failed", transport_name.c_str()); return false; } return true; } void Channel::OnIncomingSSRCChanged(uint32_t ssrc) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::OnIncomingSSRCChanged(SSRC=%d)", ssrc); // Update ssrc so that NTP for AV sync can be updated. _rtpRtcpModule->SetRemoteSSRC(ssrc); } void Channel::OnIncomingCSRCChanged(uint32_t CSRC, bool added) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::OnIncomingCSRCChanged(CSRC=%d, added=%d)", CSRC, added); } int32_t Channel::OnInitializeDecoder( int8_t payloadType, const char payloadName[RTP_PAYLOAD_NAME_SIZE], int frequency, size_t channels, uint32_t rate) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::OnInitializeDecoder(payloadType=%d, " "payloadName=%s, frequency=%u, channels=%" PRIuS ", rate=%u)", payloadType, payloadName, frequency, channels, rate); CodecInst receiveCodec = {0}; CodecInst dummyCodec = {0}; receiveCodec.pltype = payloadType; receiveCodec.plfreq = frequency; receiveCodec.channels = channels; receiveCodec.rate = rate; strncpy(receiveCodec.plname, payloadName, RTP_PAYLOAD_NAME_SIZE - 1); audio_coding_->Codec(payloadName, &dummyCodec, frequency, channels); receiveCodec.pacsize = dummyCodec.pacsize; // Register the new codec to the ACM if (!audio_coding_->RegisterReceiveCodec(receiveCodec.pltype, CodecInstToSdp(receiveCodec))) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::OnInitializeDecoder() invalid codec (" "pt=%d, name=%s) received - 1", payloadType, payloadName); _engineStatisticsPtr->SetLastError(VE_AUDIO_CODING_MODULE_ERROR); return -1; } return 0; } int32_t Channel::OnReceivedPayloadData(const uint8_t* payloadData, size_t payloadSize, const WebRtcRTPHeader* rtpHeader) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::OnReceivedPayloadData(payloadSize=%" PRIuS "," " payloadType=%u, audioChannel=%" PRIuS ")", payloadSize, rtpHeader->header.payloadType, rtpHeader->type.Audio.channel); if (!channel_state_.Get().playing) { // Avoid inserting into NetEQ when we are not playing. Count the // packet as discarded. WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "received packet is discarded since playing is not" " activated"); return 0; } // Push the incoming payload (parsed and ready for decoding) into the ACM if (audio_coding_->IncomingPacket(payloadData, payloadSize, *rtpHeader) != 0) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceWarning, "Channel::OnReceivedPayloadData() unable to push data to the ACM"); return -1; } int64_t round_trip_time = 0; _rtpRtcpModule->RTT(rtp_receiver_->SSRC(), &round_trip_time, NULL, NULL, NULL); std::vector nack_list = audio_coding_->GetNackList(round_trip_time); if (!nack_list.empty()) { // Can't use nack_list.data() since it's not supported by all // compilers. ResendPackets(&(nack_list[0]), static_cast(nack_list.size())); } return 0; } bool Channel::OnRecoveredPacket(const uint8_t* rtp_packet, size_t rtp_packet_length) { RTPHeader header; if (!rtp_header_parser_->Parse(rtp_packet, rtp_packet_length, &header)) { WEBRTC_TRACE(kTraceDebug, webrtc::kTraceVoice, _channelId, "IncomingPacket invalid RTP header"); return false; } header.payload_type_frequency = rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType); if (header.payload_type_frequency < 0) return false; return ReceivePacket(rtp_packet, rtp_packet_length, header, false); } MixerParticipant::AudioFrameInfo Channel::GetAudioFrameWithMuted( int32_t id, AudioFrame* audioFrame) { unsigned int ssrc; RTC_CHECK_EQ(GetRemoteSSRC(ssrc), 0); event_log_proxy_->LogAudioPlayout(ssrc); // Get 10ms raw PCM data from the ACM (mixer limits output frequency) bool muted; if (audio_coding_->PlayoutData10Ms(audioFrame->sample_rate_hz_, audioFrame, &muted) == -1) { WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::GetAudioFrame() PlayoutData10Ms() failed!"); // In all likelihood, the audio in this frame is garbage. We return an // error so that the audio mixer module doesn't add it to the mix. As // a result, it won't be played out and the actions skipped here are // irrelevant. return MixerParticipant::AudioFrameInfo::kError; } if (muted) { // TODO(henrik.lundin): We should be able to do better than this. But we // will have to go through all the cases below where the audio samples may // be used, and handle the muted case in some way. AudioFrameOperations::Mute(audioFrame); } // Convert module ID to internal VoE channel ID audioFrame->id_ = VoEChannelId(audioFrame->id_); // Store speech type for dead-or-alive detection _outputSpeechType = audioFrame->speech_type_; ChannelState::State state = channel_state_.Get(); { // Pass the audio buffers to an optional sink callback, before applying // scaling/panning, as that applies to the mix operation. // External recipients of the audio (e.g. via AudioTrack), will do their // own mixing/dynamic processing. rtc::CritScope cs(&_callbackCritSect); if (audio_sink_) { AudioSinkInterface::Data data( &audioFrame->data_[0], audioFrame->samples_per_channel_, audioFrame->sample_rate_hz_, audioFrame->num_channels_, audioFrame->timestamp_); audio_sink_->OnData(data); } } float output_gain = 1.0f; { rtc::CritScope cs(&volume_settings_critsect_); output_gain = _outputGain; } // Output volume scaling if (output_gain < 0.99f || output_gain > 1.01f) { // TODO(solenberg): Combine with mute state - this can cause clicks! AudioFrameOperations::ScaleWithSat(output_gain, audioFrame); } // Mix decoded PCM output with file if file mixing is enabled if (state.output_file_playing) { MixAudioWithFile(*audioFrame, audioFrame->sample_rate_hz_); muted = false; // We may have added non-zero samples. } // Record playout if enabled { rtc::CritScope cs(&_fileCritSect); if (_outputFileRecording && output_file_recorder_) { output_file_recorder_->RecordAudioToFile(*audioFrame); } } // Measure audio level (0-9) // TODO(henrik.lundin) Use the |muted| information here too. _outputAudioLevel.ComputeLevel(*audioFrame); if (capture_start_rtp_time_stamp_ < 0 && audioFrame->timestamp_ != 0) { // The first frame with a valid rtp timestamp. capture_start_rtp_time_stamp_ = audioFrame->timestamp_; } if (capture_start_rtp_time_stamp_ >= 0) { // audioFrame.timestamp_ should be valid from now on. // Compute elapsed time. int64_t unwrap_timestamp = rtp_ts_wraparound_handler_->Unwrap(audioFrame->timestamp_); audioFrame->elapsed_time_ms_ = (unwrap_timestamp - capture_start_rtp_time_stamp_) / (GetRtpTimestampRateHz() / 1000); { rtc::CritScope lock(&ts_stats_lock_); // Compute ntp time. audioFrame->ntp_time_ms_ = ntp_estimator_.Estimate(audioFrame->timestamp_); // |ntp_time_ms_| won't be valid until at least 2 RTCP SRs are received. if (audioFrame->ntp_time_ms_ > 0) { // Compute |capture_start_ntp_time_ms_| so that // |capture_start_ntp_time_ms_| + |elapsed_time_ms_| == |ntp_time_ms_| capture_start_ntp_time_ms_ = audioFrame->ntp_time_ms_ - audioFrame->elapsed_time_ms_; } } } return muted ? MixerParticipant::AudioFrameInfo::kMuted : MixerParticipant::AudioFrameInfo::kNormal; } AudioMixer::Source::AudioFrameInfo Channel::GetAudioFrameWithInfo( int sample_rate_hz, AudioFrame* audio_frame) { audio_frame->sample_rate_hz_ = sample_rate_hz; const auto frame_info = GetAudioFrameWithMuted(-1, audio_frame); using FrameInfo = AudioMixer::Source::AudioFrameInfo; FrameInfo new_audio_frame_info = FrameInfo::kError; switch (frame_info) { case MixerParticipant::AudioFrameInfo::kNormal: new_audio_frame_info = FrameInfo::kNormal; break; case MixerParticipant::AudioFrameInfo::kMuted: new_audio_frame_info = FrameInfo::kMuted; break; case MixerParticipant::AudioFrameInfo::kError: new_audio_frame_info = FrameInfo::kError; break; } return new_audio_frame_info; } int32_t Channel::NeededFrequency(int32_t id) const { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::NeededFrequency(id=%d)", id); int highestNeeded = 0; // Determine highest needed receive frequency int32_t receiveFrequency = audio_coding_->ReceiveFrequency(); // Return the bigger of playout and receive frequency in the ACM. if (audio_coding_->PlayoutFrequency() > receiveFrequency) { highestNeeded = audio_coding_->PlayoutFrequency(); } else { highestNeeded = receiveFrequency; } // Special case, if we're playing a file on the playout side // we take that frequency into consideration as well // This is not needed on sending side, since the codec will // limit the spectrum anyway. if (channel_state_.Get().output_file_playing) { rtc::CritScope cs(&_fileCritSect); if (output_file_player_) { if (output_file_player_->Frequency() > highestNeeded) { highestNeeded = output_file_player_->Frequency(); } } } return (highestNeeded); } int32_t Channel::CreateChannel(Channel*& channel, int32_t channelId, uint32_t instanceId, const VoEBase::ChannelConfig& config) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, channelId), "Channel::CreateChannel(channelId=%d, instanceId=%d)", channelId, instanceId); channel = new Channel(channelId, instanceId, config); if (channel == NULL) { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId, channelId), "Channel::CreateChannel() unable to allocate memory for" " channel"); return -1; } return 0; } void Channel::PlayNotification(int32_t id, uint32_t durationMs) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::PlayNotification(id=%d, durationMs=%d)", id, durationMs); // Not implement yet } void Channel::RecordNotification(int32_t id, uint32_t durationMs) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::RecordNotification(id=%d, durationMs=%d)", id, durationMs); // Not implement yet } void Channel::PlayFileEnded(int32_t id) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::PlayFileEnded(id=%d)", id); if (id == _inputFilePlayerId) { channel_state_.SetInputFilePlaying(false); WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::PlayFileEnded() => input file player module is" " shutdown"); } else if (id == _outputFilePlayerId) { channel_state_.SetOutputFilePlaying(false); WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::PlayFileEnded() => output file player module is" " shutdown"); } } void Channel::RecordFileEnded(int32_t id) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::RecordFileEnded(id=%d)", id); assert(id == _outputFileRecorderId); rtc::CritScope cs(&_fileCritSect); _outputFileRecording = false; WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::RecordFileEnded() => output file recorder module is" " shutdown"); } Channel::Channel(int32_t channelId, uint32_t instanceId, const VoEBase::ChannelConfig& config) : _instanceId(instanceId), _channelId(channelId), event_log_proxy_(new RtcEventLogProxy()), rtcp_rtt_stats_proxy_(new RtcpRttStatsProxy()), rtp_header_parser_(RtpHeaderParser::Create()), rtp_payload_registry_(new RTPPayloadRegistry()), rtp_receive_statistics_( ReceiveStatistics::Create(Clock::GetRealTimeClock())), rtp_receiver_( RtpReceiver::CreateAudioReceiver(Clock::GetRealTimeClock(), this, this, rtp_payload_registry_.get())), telephone_event_handler_(rtp_receiver_->GetTelephoneEventHandler()), _outputAudioLevel(), _externalTransport(false), // Avoid conflict with other channels by adding 1024 - 1026, // won't use as much as 1024 channels. _inputFilePlayerId(VoEModuleId(instanceId, channelId) + 1024), _outputFilePlayerId(VoEModuleId(instanceId, channelId) + 1025), _outputFileRecorderId(VoEModuleId(instanceId, channelId) + 1026), _outputFileRecording(false), _timeStamp(0), // This is just an offset, RTP module will add it's own // random offset ntp_estimator_(Clock::GetRealTimeClock()), playout_timestamp_rtp_(0), playout_delay_ms_(0), send_sequence_number_(0), rtp_ts_wraparound_handler_(new rtc::TimestampWrapAroundHandler()), capture_start_rtp_time_stamp_(-1), capture_start_ntp_time_ms_(-1), _engineStatisticsPtr(NULL), _outputMixerPtr(NULL), _moduleProcessThreadPtr(NULL), _audioDeviceModulePtr(NULL), _voiceEngineObserverPtr(NULL), _callbackCritSectPtr(NULL), _transportPtr(NULL), input_mute_(false), previous_frame_muted_(false), _outputGain(1.0f), _mixFileWithMicrophone(false), _includeAudioLevelIndication(false), transport_overhead_per_packet_(0), rtp_overhead_per_packet_(0), _outputSpeechType(AudioFrame::kNormalSpeech), restored_packet_in_use_(false), rtcp_observer_(new VoERtcpObserver(this)), associate_send_channel_(ChannelOwner(nullptr)), pacing_enabled_(config.enable_voice_pacing), feedback_observer_proxy_(new TransportFeedbackProxy()), seq_num_allocator_proxy_(new TransportSequenceNumberProxy()), rtp_packet_sender_proxy_(new RtpPacketSenderProxy()), retransmission_rate_limiter_(new RateLimiter(Clock::GetRealTimeClock(), kMaxRetransmissionWindowMs)), decoder_factory_(config.acm_config.decoder_factory), use_twcc_plr_for_ana_( webrtc::field_trial::FindFullName("UseTwccPlrForAna") == "Enabled") { WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Channel() - ctor"); AudioCodingModule::Config acm_config(config.acm_config); acm_config.id = VoEModuleId(instanceId, channelId); acm_config.neteq_config.enable_muted_state = true; audio_coding_.reset(AudioCodingModule::Create(acm_config)); _outputAudioLevel.Clear(); RtpRtcp::Configuration configuration; configuration.audio = true; configuration.outgoing_transport = this; configuration.overhead_observer = this; configuration.receive_statistics = rtp_receive_statistics_.get(); configuration.bandwidth_callback = rtcp_observer_.get(); if (pacing_enabled_) { configuration.paced_sender = rtp_packet_sender_proxy_.get(); configuration.transport_sequence_number_allocator = seq_num_allocator_proxy_.get(); configuration.transport_feedback_callback = feedback_observer_proxy_.get(); } configuration.event_log = &(*event_log_proxy_); configuration.rtt_stats = &(*rtcp_rtt_stats_proxy_); configuration.retransmission_rate_limiter = retransmission_rate_limiter_.get(); _rtpRtcpModule.reset(RtpRtcp::CreateRtpRtcp(configuration)); _rtpRtcpModule->SetSendingMediaStatus(false); } Channel::~Channel() { RTC_DCHECK(!channel_state_.Get().sending); RTC_DCHECK(!channel_state_.Get().playing); } int32_t Channel::Init() { RTC_DCHECK(construction_thread_.CalledOnValidThread()); WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Init()"); channel_state_.Reset(); // --- Initial sanity if ((_engineStatisticsPtr == NULL) || (_moduleProcessThreadPtr == NULL)) { WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Init() must call SetEngineInformation() first"); return -1; } // --- Add modules to process thread (for periodic schedulation) _moduleProcessThreadPtr->RegisterModule(_rtpRtcpModule.get(), RTC_FROM_HERE); // --- ACM initialization if (audio_coding_->InitializeReceiver() == -1) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "Channel::Init() unable to initialize the ACM - 1"); return -1; } // --- RTP/RTCP module initialization // Ensure that RTCP is enabled by default for the created channel. // Note that, the module will keep generating RTCP until it is explicitly // disabled by the user. // After StopListen (when no sockets exists), RTCP packets will no longer // be transmitted since the Transport object will then be invalid. telephone_event_handler_->SetTelephoneEventForwardToDecoder(true); // RTCP is enabled by default. _rtpRtcpModule->SetRTCPStatus(RtcpMode::kCompound); // --- Register all permanent callbacks if (audio_coding_->RegisterTransportCallback(this) == -1) { _engineStatisticsPtr->SetLastError( VE_CANNOT_INIT_CHANNEL, kTraceError, "Channel::Init() callbacks not registered"); return -1; } // Register a default set of send codecs. const int nSupportedCodecs = AudioCodingModule::NumberOfCodecs(); for (int idx = 0; idx < nSupportedCodecs; idx++) { CodecInst codec; RTC_CHECK_EQ(0, audio_coding_->Codec(idx, &codec)); // Ensure that PCMU is used as default send codec. if (STR_CASE_CMP(codec.plname, "PCMU") == 0 && codec.channels == 1) { SetSendCodec(codec); } // Register default PT for 'telephone-event' if (STR_CASE_CMP(codec.plname, "telephone-event") == 0) { if (_rtpRtcpModule->RegisterSendPayload(codec) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Init() failed to register outband " "'telephone-event' (%d/%d) correctly", codec.pltype, codec.plfreq); } } if (STR_CASE_CMP(codec.plname, "CN") == 0) { if (!codec_manager_.RegisterEncoder(codec) || !codec_manager_.MakeEncoder(&rent_a_codec_, audio_coding_.get()) || _rtpRtcpModule->RegisterSendPayload(codec) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Init() failed to register CN (%d/%d) " "correctly - 1", codec.pltype, codec.plfreq); } } } return 0; } void Channel::RegisterLegacyReceiveCodecs() { const int nSupportedCodecs = AudioCodingModule::NumberOfCodecs(); for (int idx = 0; idx < nSupportedCodecs; idx++) { CodecInst codec; RTC_CHECK_EQ(0, audio_coding_->Codec(idx, &codec)); // Open up the RTP/RTCP receiver for all supported codecs if (rtp_receiver_->RegisterReceivePayload(codec) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Init() unable to register %s " "(%d/%d/%" PRIuS "/%d) to RTP/RTCP receiver", codec.plname, codec.pltype, codec.plfreq, codec.channels, codec.rate); } else { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Init() %s (%d/%d/%" PRIuS "/%d) has been " "added to the RTP/RTCP receiver", codec.plname, codec.pltype, codec.plfreq, codec.channels, codec.rate); } // Register default PT for 'telephone-event' if (STR_CASE_CMP(codec.plname, "telephone-event") == 0) { if (!audio_coding_->RegisterReceiveCodec(codec.pltype, CodecInstToSdp(codec))) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Init() failed to register inband " "'telephone-event' (%d/%d) correctly", codec.pltype, codec.plfreq); } } if (STR_CASE_CMP(codec.plname, "CN") == 0) { if (!audio_coding_->RegisterReceiveCodec(codec.pltype, CodecInstToSdp(codec))) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Init() failed to register CN (%d/%d) " "correctly - 1", codec.pltype, codec.plfreq); } } } } void Channel::Terminate() { RTC_DCHECK(construction_thread_.CalledOnValidThread()); // Must be called on the same thread as Init(). WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::Terminate"); rtp_receive_statistics_->RegisterRtcpStatisticsCallback(NULL); StopSend(); StopPlayout(); { rtc::CritScope cs(&_fileCritSect); if (input_file_player_) { input_file_player_->RegisterModuleFileCallback(NULL); input_file_player_->StopPlayingFile(); } if (output_file_player_) { output_file_player_->RegisterModuleFileCallback(NULL); output_file_player_->StopPlayingFile(); } if (output_file_recorder_) { output_file_recorder_->RegisterModuleFileCallback(NULL); output_file_recorder_->StopRecording(); } } // The order to safely shutdown modules in a channel is: // 1. De-register callbacks in modules // 2. De-register modules in process thread // 3. Destroy modules if (audio_coding_->RegisterTransportCallback(NULL) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Terminate() failed to de-register transport callback" " (Audio coding module)"); } if (audio_coding_->RegisterVADCallback(NULL) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Terminate() failed to de-register VAD callback" " (Audio coding module)"); } // De-register modules in process thread if (_moduleProcessThreadPtr) _moduleProcessThreadPtr->DeRegisterModule(_rtpRtcpModule.get()); // End of modules shutdown } int32_t Channel::SetEngineInformation(Statistics& engineStatistics, OutputMixer& outputMixer, ProcessThread& moduleProcessThread, AudioDeviceModule& audioDeviceModule, VoiceEngineObserver* voiceEngineObserver, rtc::CriticalSection* callbackCritSect, rtc::TaskQueue* encoder_queue) { RTC_DCHECK(encoder_queue); RTC_DCHECK(!encoder_queue_); WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetEngineInformation()"); _engineStatisticsPtr = &engineStatistics; _outputMixerPtr = &outputMixer; _moduleProcessThreadPtr = &moduleProcessThread; _audioDeviceModulePtr = &audioDeviceModule; _voiceEngineObserverPtr = voiceEngineObserver; _callbackCritSectPtr = callbackCritSect; encoder_queue_ = encoder_queue; return 0; } void Channel::SetSink(std::unique_ptr sink) { rtc::CritScope cs(&_callbackCritSect); audio_sink_ = std::move(sink); } const rtc::scoped_refptr& Channel::GetAudioDecoderFactory() const { return decoder_factory_; } int32_t Channel::StartPlayout() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StartPlayout()"); if (channel_state_.Get().playing) { return 0; } // Add participant as candidates for mixing. if (_outputMixerPtr->SetMixabilityStatus(*this, true) != 0) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CONF_MIX_MODULE_ERROR, kTraceError, "StartPlayout() failed to add participant to mixer"); return -1; } channel_state_.SetPlaying(true); if (RegisterFilePlayingToMixer() != 0) return -1; return 0; } int32_t Channel::StopPlayout() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StopPlayout()"); if (!channel_state_.Get().playing) { return 0; } // Remove participant as candidates for mixing if (_outputMixerPtr->SetMixabilityStatus(*this, false) != 0) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CONF_MIX_MODULE_ERROR, kTraceError, "StopPlayout() failed to remove participant from mixer"); return -1; } channel_state_.SetPlaying(false); _outputAudioLevel.Clear(); return 0; } int32_t Channel::StartSend() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StartSend()"); if (channel_state_.Get().sending) { return 0; } channel_state_.SetSending(true); // Resume the previous sequence number which was reset by StopSend(). This // needs to be done before |sending| is set to true on the RTP/RTCP module. if (send_sequence_number_) { _rtpRtcpModule->SetSequenceNumber(send_sequence_number_); } _rtpRtcpModule->SetSendingMediaStatus(true); if (_rtpRtcpModule->SetSendingStatus(true) != 0) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceError, "StartSend() RTP/RTCP failed to start sending"); _rtpRtcpModule->SetSendingMediaStatus(false); rtc::CritScope cs(&_callbackCritSect); channel_state_.SetSending(false); return -1; } return 0; } void Channel::StopSend() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StopSend()"); if (!channel_state_.Get().sending) { return; } channel_state_.SetSending(false); // Post a task to the encoder thread which sets an event when the task is // executed. We know that no more encoding tasks will be added to the task // queue for this channel since sending is now deactivated. It means that, // if we wait for the event to bet set, we know that no more pending tasks // exists and it is therfore guaranteed that the task queue will never try // to acccess and invalid channel object. RTC_DCHECK(encoder_queue_); rtc::Event flush(false, false); encoder_queue_->PostTask([&flush]() { flush.Set(); }); flush.Wait(rtc::Event::kForever); // Store the sequence number to be able to pick up the same sequence for // the next StartSend(). This is needed for restarting device, otherwise // it might cause libSRTP to complain about packets being replayed. // TODO(xians): Remove this workaround after RtpRtcpModule's refactoring // CL is landed. See issue // https://code.google.com/p/webrtc/issues/detail?id=2111 . send_sequence_number_ = _rtpRtcpModule->SequenceNumber(); // Reset sending SSRC and sequence number and triggers direct transmission // of RTCP BYE if (_rtpRtcpModule->SetSendingStatus(false) == -1) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceWarning, "StartSend() RTP/RTCP failed to stop sending"); } _rtpRtcpModule->SetSendingMediaStatus(false); } int32_t Channel::RegisterVoiceEngineObserver(VoiceEngineObserver& observer) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::RegisterVoiceEngineObserver()"); rtc::CritScope cs(&_callbackCritSect); if (_voiceEngineObserverPtr) { _engineStatisticsPtr->SetLastError( VE_INVALID_OPERATION, kTraceError, "RegisterVoiceEngineObserver() observer already enabled"); return -1; } _voiceEngineObserverPtr = &observer; return 0; } int32_t Channel::DeRegisterVoiceEngineObserver() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::DeRegisterVoiceEngineObserver()"); rtc::CritScope cs(&_callbackCritSect); if (!_voiceEngineObserverPtr) { _engineStatisticsPtr->SetLastError( VE_INVALID_OPERATION, kTraceWarning, "DeRegisterVoiceEngineObserver() observer already disabled"); return 0; } _voiceEngineObserverPtr = NULL; return 0; } int32_t Channel::GetSendCodec(CodecInst& codec) { auto send_codec = codec_manager_.GetCodecInst(); if (send_codec) { codec = *send_codec; return 0; } return -1; } int32_t Channel::GetRecCodec(CodecInst& codec) { return (audio_coding_->ReceiveCodec(&codec)); } int32_t Channel::SetSendCodec(const CodecInst& codec) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetSendCodec()"); if (!codec_manager_.RegisterEncoder(codec) || !codec_manager_.MakeEncoder(&rent_a_codec_, audio_coding_.get())) { WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, _channelId), "SetSendCodec() failed to register codec to ACM"); return -1; } if (_rtpRtcpModule->RegisterSendPayload(codec) != 0) { _rtpRtcpModule->DeRegisterSendPayload(codec.pltype); if (_rtpRtcpModule->RegisterSendPayload(codec) != 0) { WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, _channelId), "SetSendCodec() failed to register codec to" " RTP/RTCP module"); return -1; } } return 0; } void Channel::SetBitRate(int bitrate_bps, int64_t probing_interval_ms) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetBitRate(bitrate_bps=%d)", bitrate_bps); audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) { (*encoder)->OnReceivedUplinkBandwidth( bitrate_bps, rtc::Optional(probing_interval_ms)); } }); retransmission_rate_limiter_->SetMaxRate(bitrate_bps); } void Channel::OnTwccBasedUplinkPacketLossRate(float packet_loss_rate) { if (!use_twcc_plr_for_ana_) return; audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) { (*encoder)->OnReceivedUplinkPacketLossFraction(packet_loss_rate); } }); } void Channel::OnRecoverableUplinkPacketLossRate( float recoverable_packet_loss_rate) { audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) { (*encoder)->OnReceivedUplinkRecoverablePacketLossFraction( recoverable_packet_loss_rate); } }); } void Channel::OnUplinkPacketLossRate(float packet_loss_rate) { if (use_twcc_plr_for_ana_) return; audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) { (*encoder)->OnReceivedUplinkPacketLossFraction(packet_loss_rate); } }); } int32_t Channel::SetVADStatus(bool enableVAD, ACMVADMode mode, bool disableDTX) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetVADStatus(mode=%d)", mode); RTC_DCHECK(!(disableDTX && enableVAD)); // disableDTX mode is deprecated. if (!codec_manager_.SetVAD(enableVAD, mode) || !codec_manager_.MakeEncoder(&rent_a_codec_, audio_coding_.get())) { _engineStatisticsPtr->SetLastError(VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetVADStatus() failed to set VAD"); return -1; } return 0; } int32_t Channel::GetVADStatus(bool& enabledVAD, ACMVADMode& mode, bool& disabledDTX) { const auto* params = codec_manager_.GetStackParams(); enabledVAD = params->use_cng; mode = params->vad_mode; disabledDTX = !params->use_cng; return 0; } void Channel::SetReceiveCodecs(const std::map& codecs) { rtp_payload_registry_->SetAudioReceivePayloads(codecs); audio_coding_->SetReceiveCodecs(codecs); } int32_t Channel::SetRecPayloadType(const CodecInst& codec) { return SetRecPayloadType(codec.pltype, CodecInstToSdp(codec)); } int32_t Channel::SetRecPayloadType(int payload_type, const SdpAudioFormat& format) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetRecPayloadType()"); if (channel_state_.Get().playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceError, "SetRecPayloadType() unable to set PT while playing"); return -1; } const CodecInst codec = SdpToCodecInst(payload_type, format); if (payload_type == -1) { // De-register the selected codec (RTP/RTCP module and ACM) int8_t pltype(-1); CodecInst rxCodec = codec; // Get payload type for the given codec rtp_payload_registry_->ReceivePayloadType(rxCodec, &pltype); rxCodec.pltype = pltype; if (rtp_receiver_->DeRegisterReceivePayload(pltype) != 0) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceError, "SetRecPayloadType() RTP/RTCP-module deregistration " "failed"); return -1; } if (audio_coding_->UnregisterReceiveCodec(rxCodec.pltype) != 0) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetRecPayloadType() ACM deregistration failed - 1"); return -1; } return 0; } if (rtp_receiver_->RegisterReceivePayload(codec) != 0) { // First attempt to register failed => de-register and try again // TODO(kwiberg): Retrying is probably not necessary, since // AcmReceiver::AddCodec also retries. rtp_receiver_->DeRegisterReceivePayload(codec.pltype); if (rtp_receiver_->RegisterReceivePayload(codec) != 0) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceError, "SetRecPayloadType() RTP/RTCP-module registration failed"); return -1; } } if (!audio_coding_->RegisterReceiveCodec(payload_type, format)) { audio_coding_->UnregisterReceiveCodec(payload_type); if (!audio_coding_->RegisterReceiveCodec(payload_type, format)) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetRecPayloadType() ACM registration failed - 1"); return -1; } } return 0; } int32_t Channel::GetRecPayloadType(CodecInst& codec) { int8_t payloadType(-1); if (rtp_payload_registry_->ReceivePayloadType(codec, &payloadType) != 0) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceWarning, "GetRecPayloadType() failed to retrieve RX payload type"); return -1; } codec.pltype = payloadType; return 0; } int32_t Channel::SetSendCNPayloadType(int type, PayloadFrequencies frequency) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetSendCNPayloadType()"); CodecInst codec; int32_t samplingFreqHz(-1); const size_t kMono = 1; if (frequency == kFreq32000Hz) samplingFreqHz = 32000; else if (frequency == kFreq16000Hz) samplingFreqHz = 16000; if (audio_coding_->Codec("CN", &codec, samplingFreqHz, kMono) == -1) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetSendCNPayloadType() failed to retrieve default CN codec " "settings"); return -1; } // Modify the payload type (must be set to dynamic range) codec.pltype = type; if (!codec_manager_.RegisterEncoder(codec) || !codec_manager_.MakeEncoder(&rent_a_codec_, audio_coding_.get())) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetSendCNPayloadType() failed to register CN to ACM"); return -1; } if (_rtpRtcpModule->RegisterSendPayload(codec) != 0) { _rtpRtcpModule->DeRegisterSendPayload(codec.pltype); if (_rtpRtcpModule->RegisterSendPayload(codec) != 0) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceError, "SetSendCNPayloadType() failed to register CN to RTP/RTCP " "module"); return -1; } } return 0; } int Channel::SetOpusMaxPlaybackRate(int frequency_hz) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetOpusMaxPlaybackRate()"); if (audio_coding_->SetOpusMaxPlaybackRate(frequency_hz) != 0) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetOpusMaxPlaybackRate() failed to set maximum playback rate"); return -1; } return 0; } int Channel::SetOpusDtx(bool enable_dtx) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetOpusDtx(%d)", enable_dtx); int ret = enable_dtx ? audio_coding_->EnableOpusDtx() : audio_coding_->DisableOpusDtx(); if (ret != 0) { _engineStatisticsPtr->SetLastError(VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetOpusDtx() failed"); return -1; } return 0; } int Channel::GetOpusDtx(bool* enabled) { int success = -1; audio_coding_->QueryEncoder([&](AudioEncoder const* encoder) { if (encoder) { *enabled = encoder->GetDtx(); success = 0; } }); return success; } bool Channel::EnableAudioNetworkAdaptor(const std::string& config_string) { bool success = false; audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) { success = (*encoder)->EnableAudioNetworkAdaptor( config_string, event_log_proxy_.get(), Clock::GetRealTimeClock()); } }); return success; } void Channel::DisableAudioNetworkAdaptor() { audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) (*encoder)->DisableAudioNetworkAdaptor(); }); } void Channel::SetReceiverFrameLengthRange(int min_frame_length_ms, int max_frame_length_ms) { audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) { (*encoder)->SetReceiverFrameLengthRange(min_frame_length_ms, max_frame_length_ms); } }); } int32_t Channel::RegisterExternalTransport(Transport* transport) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::RegisterExternalTransport()"); rtc::CritScope cs(&_callbackCritSect); if (_externalTransport) { _engineStatisticsPtr->SetLastError( VE_INVALID_OPERATION, kTraceError, "RegisterExternalTransport() external transport already enabled"); return -1; } _externalTransport = true; _transportPtr = transport; return 0; } int32_t Channel::DeRegisterExternalTransport() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::DeRegisterExternalTransport()"); rtc::CritScope cs(&_callbackCritSect); if (_transportPtr) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "DeRegisterExternalTransport() all transport is disabled"); } else { _engineStatisticsPtr->SetLastError( VE_INVALID_OPERATION, kTraceWarning, "DeRegisterExternalTransport() external transport already " "disabled"); } _externalTransport = false; _transportPtr = NULL; return 0; } // TODO(nisse): Delete this method together with ReceivedRTPPacket. // It's a temporary hack to support both ReceivedRTPPacket and // OnRtpPacket interfaces without too much code duplication. bool Channel::OnRtpPacketWithHeader(const uint8_t* received_packet, size_t length, RTPHeader *header) { // Store playout timestamp for the received RTP packet UpdatePlayoutTimestamp(false); header->payload_type_frequency = rtp_payload_registry_->GetPayloadTypeFrequency(header->payloadType); if (header->payload_type_frequency < 0) return false; bool in_order = IsPacketInOrder(*header); rtp_receive_statistics_->IncomingPacket( *header, length, IsPacketRetransmitted(*header, in_order)); rtp_payload_registry_->SetIncomingPayloadType(*header); return ReceivePacket(received_packet, length, *header, in_order); } int32_t Channel::ReceivedRTPPacket(const uint8_t* received_packet, size_t length, const PacketTime& packet_time) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::ReceivedRTPPacket()"); RTPHeader header; if (!rtp_header_parser_->Parse(received_packet, length, &header)) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId, "Incoming packet: invalid RTP header"); return -1; } return OnRtpPacketWithHeader(received_packet, length, &header) ? 0 : -1; } void Channel::OnRtpPacket(const RtpPacketReceived& packet) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::ReceivedRTPPacket()"); RTPHeader header; packet.GetHeader(&header); OnRtpPacketWithHeader(packet.data(), packet.size(), &header); } bool Channel::ReceivePacket(const uint8_t* packet, size_t packet_length, const RTPHeader& header, bool in_order) { if (rtp_payload_registry_->IsRtx(header)) { return HandleRtxPacket(packet, packet_length, header); } const uint8_t* payload = packet + header.headerLength; assert(packet_length >= header.headerLength); size_t payload_length = packet_length - header.headerLength; PayloadUnion payload_specific; if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType, &payload_specific)) { return false; } return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length, payload_specific, in_order); } bool Channel::HandleRtxPacket(const uint8_t* packet, size_t packet_length, const RTPHeader& header) { if (!rtp_payload_registry_->IsRtx(header)) return false; // Remove the RTX header and parse the original RTP header. if (packet_length < header.headerLength) return false; if (packet_length > kVoiceEngineMaxIpPacketSizeBytes) return false; if (restored_packet_in_use_) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId, "Multiple RTX headers detected, dropping packet"); return false; } if (!rtp_payload_registry_->RestoreOriginalPacket( restored_packet_, packet, &packet_length, rtp_receiver_->SSRC(), header)) { WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId, "Incoming RTX packet: invalid RTP header"); return false; } restored_packet_in_use_ = true; bool ret = OnRecoveredPacket(restored_packet_, packet_length); restored_packet_in_use_ = false; return ret; } bool Channel::IsPacketInOrder(const RTPHeader& header) const { StreamStatistician* statistician = rtp_receive_statistics_->GetStatistician(header.ssrc); if (!statistician) return false; return statistician->IsPacketInOrder(header.sequenceNumber); } bool Channel::IsPacketRetransmitted(const RTPHeader& header, bool in_order) const { // Retransmissions are handled separately if RTX is enabled. if (rtp_payload_registry_->RtxEnabled()) return false; StreamStatistician* statistician = rtp_receive_statistics_->GetStatistician(header.ssrc); if (!statistician) return false; // Check if this is a retransmission. int64_t min_rtt = 0; _rtpRtcpModule->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL); return !in_order && statistician->IsRetransmitOfOldPacket(header, min_rtt); } int32_t Channel::ReceivedRTCPPacket(const uint8_t* data, size_t length) { WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::ReceivedRTCPPacket()"); // Store playout timestamp for the received RTCP packet UpdatePlayoutTimestamp(true); // Deliver RTCP packet to RTP/RTCP module for parsing if (_rtpRtcpModule->IncomingRtcpPacket(data, length) == -1) { _engineStatisticsPtr->SetLastError( VE_SOCKET_TRANSPORT_MODULE_ERROR, kTraceWarning, "Channel::IncomingRTPPacket() RTCP packet is invalid"); } int64_t rtt = GetRTT(true); if (rtt == 0) { // Waiting for valid RTT. return 0; } int64_t nack_window_ms = rtt; if (nack_window_ms < kMinRetransmissionWindowMs) { nack_window_ms = kMinRetransmissionWindowMs; } else if (nack_window_ms > kMaxRetransmissionWindowMs) { nack_window_ms = kMaxRetransmissionWindowMs; } retransmission_rate_limiter_->SetWindowSize(nack_window_ms); // Invoke audio encoders OnReceivedRtt(). audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) (*encoder)->OnReceivedRtt(rtt); }); uint32_t ntp_secs = 0; uint32_t ntp_frac = 0; uint32_t rtp_timestamp = 0; if (0 != _rtpRtcpModule->RemoteNTP(&ntp_secs, &ntp_frac, NULL, NULL, &rtp_timestamp)) { // Waiting for RTCP. return 0; } { rtc::CritScope lock(&ts_stats_lock_); ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp); } return 0; } int Channel::StartPlayingFileLocally(const char* fileName, bool loop, FileFormats format, int startPosition, float volumeScaling, int stopPosition, const CodecInst* codecInst) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StartPlayingFileLocally(fileNameUTF8[]=%s, loop=%d," " format=%d, volumeScaling=%5.3f, startPosition=%d, " "stopPosition=%d)", fileName, loop, format, volumeScaling, startPosition, stopPosition); if (channel_state_.Get().output_file_playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceError, "StartPlayingFileLocally() is already playing"); return -1; } { rtc::CritScope cs(&_fileCritSect); if (output_file_player_) { output_file_player_->RegisterModuleFileCallback(NULL); output_file_player_.reset(); } output_file_player_ = FilePlayer::CreateFilePlayer( _outputFilePlayerId, (const FileFormats)format); if (!output_file_player_) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "StartPlayingFileLocally() filePlayer format is not correct"); return -1; } const uint32_t notificationTime(0); if (output_file_player_->StartPlayingFile( fileName, loop, startPosition, volumeScaling, notificationTime, stopPosition, (const CodecInst*)codecInst) != 0) { _engineStatisticsPtr->SetLastError( VE_BAD_FILE, kTraceError, "StartPlayingFile() failed to start file playout"); output_file_player_->StopPlayingFile(); output_file_player_.reset(); return -1; } output_file_player_->RegisterModuleFileCallback(this); channel_state_.SetOutputFilePlaying(true); } if (RegisterFilePlayingToMixer() != 0) return -1; return 0; } int Channel::StartPlayingFileLocally(InStream* stream, FileFormats format, int startPosition, float volumeScaling, int stopPosition, const CodecInst* codecInst) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StartPlayingFileLocally(format=%d," " volumeScaling=%5.3f, startPosition=%d, stopPosition=%d)", format, volumeScaling, startPosition, stopPosition); if (stream == NULL) { _engineStatisticsPtr->SetLastError( VE_BAD_FILE, kTraceError, "StartPlayingFileLocally() NULL as input stream"); return -1; } if (channel_state_.Get().output_file_playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceError, "StartPlayingFileLocally() is already playing"); return -1; } { rtc::CritScope cs(&_fileCritSect); // Destroy the old instance if (output_file_player_) { output_file_player_->RegisterModuleFileCallback(NULL); output_file_player_.reset(); } // Create the instance output_file_player_ = FilePlayer::CreateFilePlayer( _outputFilePlayerId, (const FileFormats)format); if (!output_file_player_) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "StartPlayingFileLocally() filePlayer format isnot correct"); return -1; } const uint32_t notificationTime(0); if (output_file_player_->StartPlayingFile(stream, startPosition, volumeScaling, notificationTime, stopPosition, codecInst) != 0) { _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError, "StartPlayingFile() failed to " "start file playout"); output_file_player_->StopPlayingFile(); output_file_player_.reset(); return -1; } output_file_player_->RegisterModuleFileCallback(this); channel_state_.SetOutputFilePlaying(true); } if (RegisterFilePlayingToMixer() != 0) return -1; return 0; } int Channel::StopPlayingFileLocally() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StopPlayingFileLocally()"); if (!channel_state_.Get().output_file_playing) { return 0; } { rtc::CritScope cs(&_fileCritSect); if (output_file_player_->StopPlayingFile() != 0) { _engineStatisticsPtr->SetLastError( VE_STOP_RECORDING_FAILED, kTraceError, "StopPlayingFile() could not stop playing"); return -1; } output_file_player_->RegisterModuleFileCallback(NULL); output_file_player_.reset(); channel_state_.SetOutputFilePlaying(false); } // _fileCritSect cannot be taken while calling // SetAnonymousMixibilityStatus. Refer to comments in // StartPlayingFileLocally(const char* ...) for more details. if (_outputMixerPtr->SetAnonymousMixabilityStatus(*this, false) != 0) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CONF_MIX_MODULE_ERROR, kTraceError, "StopPlayingFile() failed to stop participant from playing as" "file in the mixer"); return -1; } return 0; } int Channel::IsPlayingFileLocally() const { return channel_state_.Get().output_file_playing; } int Channel::RegisterFilePlayingToMixer() { // Return success for not registering for file playing to mixer if: // 1. playing file before playout is started on that channel. // 2. starting playout without file playing on that channel. if (!channel_state_.Get().playing || !channel_state_.Get().output_file_playing) { return 0; } // |_fileCritSect| cannot be taken while calling // SetAnonymousMixabilityStatus() since as soon as the participant is added // frames can be pulled by the mixer. Since the frames are generated from // the file, _fileCritSect will be taken. This would result in a deadlock. if (_outputMixerPtr->SetAnonymousMixabilityStatus(*this, true) != 0) { channel_state_.SetOutputFilePlaying(false); rtc::CritScope cs(&_fileCritSect); _engineStatisticsPtr->SetLastError( VE_AUDIO_CONF_MIX_MODULE_ERROR, kTraceError, "StartPlayingFile() failed to add participant as file to mixer"); output_file_player_->StopPlayingFile(); output_file_player_.reset(); return -1; } return 0; } int Channel::StartPlayingFileAsMicrophone(const char* fileName, bool loop, FileFormats format, int startPosition, float volumeScaling, int stopPosition, const CodecInst* codecInst) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StartPlayingFileAsMicrophone(fileNameUTF8[]=%s, " "loop=%d, format=%d, volumeScaling=%5.3f, startPosition=%d, " "stopPosition=%d)", fileName, loop, format, volumeScaling, startPosition, stopPosition); rtc::CritScope cs(&_fileCritSect); if (channel_state_.Get().input_file_playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceWarning, "StartPlayingFileAsMicrophone() filePlayer is playing"); return 0; } // Destroy the old instance if (input_file_player_) { input_file_player_->RegisterModuleFileCallback(NULL); input_file_player_.reset(); } // Create the instance input_file_player_ = FilePlayer::CreateFilePlayer(_inputFilePlayerId, (const FileFormats)format); if (!input_file_player_) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "StartPlayingFileAsMicrophone() filePlayer format isnot correct"); return -1; } const uint32_t notificationTime(0); if (input_file_player_->StartPlayingFile( fileName, loop, startPosition, volumeScaling, notificationTime, stopPosition, (const CodecInst*)codecInst) != 0) { _engineStatisticsPtr->SetLastError( VE_BAD_FILE, kTraceError, "StartPlayingFile() failed to start file playout"); input_file_player_->StopPlayingFile(); input_file_player_.reset(); return -1; } input_file_player_->RegisterModuleFileCallback(this); channel_state_.SetInputFilePlaying(true); return 0; } int Channel::StartPlayingFileAsMicrophone(InStream* stream, FileFormats format, int startPosition, float volumeScaling, int stopPosition, const CodecInst* codecInst) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StartPlayingFileAsMicrophone(format=%d, " "volumeScaling=%5.3f, startPosition=%d, stopPosition=%d)", format, volumeScaling, startPosition, stopPosition); if (stream == NULL) { _engineStatisticsPtr->SetLastError( VE_BAD_FILE, kTraceError, "StartPlayingFileAsMicrophone NULL as input stream"); return -1; } rtc::CritScope cs(&_fileCritSect); if (channel_state_.Get().input_file_playing) { _engineStatisticsPtr->SetLastError( VE_ALREADY_PLAYING, kTraceWarning, "StartPlayingFileAsMicrophone() is playing"); return 0; } // Destroy the old instance if (input_file_player_) { input_file_player_->RegisterModuleFileCallback(NULL); input_file_player_.reset(); } // Create the instance input_file_player_ = FilePlayer::CreateFilePlayer(_inputFilePlayerId, (const FileFormats)format); if (!input_file_player_) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "StartPlayingInputFile() filePlayer format isnot correct"); return -1; } const uint32_t notificationTime(0); if (input_file_player_->StartPlayingFile(stream, startPosition, volumeScaling, notificationTime, stopPosition, codecInst) != 0) { _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError, "StartPlayingFile() failed to start " "file playout"); input_file_player_->StopPlayingFile(); input_file_player_.reset(); return -1; } input_file_player_->RegisterModuleFileCallback(this); channel_state_.SetInputFilePlaying(true); return 0; } int Channel::StopPlayingFileAsMicrophone() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StopPlayingFileAsMicrophone()"); rtc::CritScope cs(&_fileCritSect); if (!channel_state_.Get().input_file_playing) { return 0; } if (input_file_player_->StopPlayingFile() != 0) { _engineStatisticsPtr->SetLastError( VE_STOP_RECORDING_FAILED, kTraceError, "StopPlayingFile() could not stop playing"); return -1; } input_file_player_->RegisterModuleFileCallback(NULL); input_file_player_.reset(); channel_state_.SetInputFilePlaying(false); return 0; } int Channel::IsPlayingFileAsMicrophone() const { return channel_state_.Get().input_file_playing; } int Channel::StartRecordingPlayout(const char* fileName, const CodecInst* codecInst) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StartRecordingPlayout(fileName=%s)", fileName); if (_outputFileRecording) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), "StartRecordingPlayout() is already recording"); return 0; } FileFormats format; const uint32_t notificationTime(0); // Not supported in VoE CodecInst dummyCodec = {100, "L16", 16000, 320, 1, 320000}; if ((codecInst != NULL) && ((codecInst->channels < 1) || (codecInst->channels > 2))) { _engineStatisticsPtr->SetLastError( VE_BAD_ARGUMENT, kTraceError, "StartRecordingPlayout() invalid compression"); return (-1); } if (codecInst == NULL) { format = kFileFormatPcm16kHzFile; codecInst = &dummyCodec; } else if ((STR_CASE_CMP(codecInst->plname, "L16") == 0) || (STR_CASE_CMP(codecInst->plname, "PCMU") == 0) || (STR_CASE_CMP(codecInst->plname, "PCMA") == 0)) { format = kFileFormatWavFile; } else { format = kFileFormatCompressedFile; } rtc::CritScope cs(&_fileCritSect); // Destroy the old instance if (output_file_recorder_) { output_file_recorder_->RegisterModuleFileCallback(NULL); output_file_recorder_.reset(); } output_file_recorder_ = FileRecorder::CreateFileRecorder( _outputFileRecorderId, (const FileFormats)format); if (!output_file_recorder_) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "StartRecordingPlayout() fileRecorder format isnot correct"); return -1; } if (output_file_recorder_->StartRecordingAudioFile( fileName, (const CodecInst&)*codecInst, notificationTime) != 0) { _engineStatisticsPtr->SetLastError( VE_BAD_FILE, kTraceError, "StartRecordingAudioFile() failed to start file recording"); output_file_recorder_->StopRecording(); output_file_recorder_.reset(); return -1; } output_file_recorder_->RegisterModuleFileCallback(this); _outputFileRecording = true; return 0; } int Channel::StartRecordingPlayout(OutStream* stream, const CodecInst* codecInst) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::StartRecordingPlayout()"); if (_outputFileRecording) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1), "StartRecordingPlayout() is already recording"); return 0; } FileFormats format; const uint32_t notificationTime(0); // Not supported in VoE CodecInst dummyCodec = {100, "L16", 16000, 320, 1, 320000}; if (codecInst != NULL && codecInst->channels != 1) { _engineStatisticsPtr->SetLastError( VE_BAD_ARGUMENT, kTraceError, "StartRecordingPlayout() invalid compression"); return (-1); } if (codecInst == NULL) { format = kFileFormatPcm16kHzFile; codecInst = &dummyCodec; } else if ((STR_CASE_CMP(codecInst->plname, "L16") == 0) || (STR_CASE_CMP(codecInst->plname, "PCMU") == 0) || (STR_CASE_CMP(codecInst->plname, "PCMA") == 0)) { format = kFileFormatWavFile; } else { format = kFileFormatCompressedFile; } rtc::CritScope cs(&_fileCritSect); // Destroy the old instance if (output_file_recorder_) { output_file_recorder_->RegisterModuleFileCallback(NULL); output_file_recorder_.reset(); } output_file_recorder_ = FileRecorder::CreateFileRecorder( _outputFileRecorderId, (const FileFormats)format); if (!output_file_recorder_) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "StartRecordingPlayout() fileRecorder format isnot correct"); return -1; } if (output_file_recorder_->StartRecordingAudioFile(stream, *codecInst, notificationTime) != 0) { _engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError, "StartRecordingPlayout() failed to " "start file recording"); output_file_recorder_->StopRecording(); output_file_recorder_.reset(); return -1; } output_file_recorder_->RegisterModuleFileCallback(this); _outputFileRecording = true; return 0; } int Channel::StopRecordingPlayout() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1), "Channel::StopRecordingPlayout()"); if (!_outputFileRecording) { WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1), "StopRecordingPlayout() isnot recording"); return -1; } rtc::CritScope cs(&_fileCritSect); if (output_file_recorder_->StopRecording() != 0) { _engineStatisticsPtr->SetLastError( VE_STOP_RECORDING_FAILED, kTraceError, "StopRecording() could not stop recording"); return (-1); } output_file_recorder_->RegisterModuleFileCallback(NULL); output_file_recorder_.reset(); _outputFileRecording = false; return 0; } void Channel::SetMixWithMicStatus(bool mix) { rtc::CritScope cs(&_fileCritSect); _mixFileWithMicrophone = mix; } int Channel::GetSpeechOutputLevel() const { return _outputAudioLevel.Level(); } int Channel::GetSpeechOutputLevelFullRange() const { return _outputAudioLevel.LevelFullRange(); } void Channel::SetInputMute(bool enable) { rtc::CritScope cs(&volume_settings_critsect_); input_mute_ = enable; } bool Channel::InputMute() const { rtc::CritScope cs(&volume_settings_critsect_); return input_mute_; } void Channel::SetChannelOutputVolumeScaling(float scaling) { rtc::CritScope cs(&volume_settings_critsect_); _outputGain = scaling; } int Channel::SendTelephoneEventOutband(int event, int duration_ms) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendTelephoneEventOutband(...)"); RTC_DCHECK_LE(0, event); RTC_DCHECK_GE(255, event); RTC_DCHECK_LE(0, duration_ms); RTC_DCHECK_GE(65535, duration_ms); if (!Sending()) { return -1; } if (_rtpRtcpModule->SendTelephoneEventOutband( event, duration_ms, kTelephoneEventAttenuationdB) != 0) { _engineStatisticsPtr->SetLastError( VE_SEND_DTMF_FAILED, kTraceWarning, "SendTelephoneEventOutband() failed to send event"); return -1; } return 0; } int Channel::SetSendTelephoneEventPayloadType(int payload_type, int payload_frequency) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetSendTelephoneEventPayloadType()"); RTC_DCHECK_LE(0, payload_type); RTC_DCHECK_GE(127, payload_type); CodecInst codec = {0}; codec.pltype = payload_type; codec.plfreq = payload_frequency; memcpy(codec.plname, "telephone-event", 16); if (_rtpRtcpModule->RegisterSendPayload(codec) != 0) { _rtpRtcpModule->DeRegisterSendPayload(codec.pltype); if (_rtpRtcpModule->RegisterSendPayload(codec) != 0) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceError, "SetSendTelephoneEventPayloadType() failed to register send" "payload type"); return -1; } } return 0; } int Channel::SetLocalSSRC(unsigned int ssrc) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetLocalSSRC()"); if (channel_state_.Get().sending) { _engineStatisticsPtr->SetLastError(VE_ALREADY_SENDING, kTraceError, "SetLocalSSRC() already sending"); return -1; } _rtpRtcpModule->SetSSRC(ssrc); return 0; } int Channel::GetLocalSSRC(unsigned int& ssrc) { ssrc = _rtpRtcpModule->SSRC(); return 0; } int Channel::GetRemoteSSRC(unsigned int& ssrc) { ssrc = rtp_receiver_->SSRC(); return 0; } int Channel::SetSendAudioLevelIndicationStatus(bool enable, unsigned char id) { _includeAudioLevelIndication = enable; return SetSendRtpHeaderExtension(enable, kRtpExtensionAudioLevel, id); } int Channel::SetReceiveAudioLevelIndicationStatus(bool enable, unsigned char id) { rtp_header_parser_->DeregisterRtpHeaderExtension(kRtpExtensionAudioLevel); if (enable && !rtp_header_parser_->RegisterRtpHeaderExtension(kRtpExtensionAudioLevel, id)) { return -1; } return 0; } void Channel::EnableSendTransportSequenceNumber(int id) { int ret = SetSendRtpHeaderExtension(true, kRtpExtensionTransportSequenceNumber, id); RTC_DCHECK_EQ(0, ret); } void Channel::EnableReceiveTransportSequenceNumber(int id) { rtp_header_parser_->DeregisterRtpHeaderExtension( kRtpExtensionTransportSequenceNumber); bool ret = rtp_header_parser_->RegisterRtpHeaderExtension( kRtpExtensionTransportSequenceNumber, id); RTC_DCHECK(ret); } void Channel::RegisterSenderCongestionControlObjects( RtpTransportControllerSendInterface* transport, RtcpBandwidthObserver* bandwidth_observer) { RtpPacketSender* rtp_packet_sender = transport->packet_sender(); TransportFeedbackObserver* transport_feedback_observer = transport->transport_feedback_observer(); PacketRouter* packet_router = transport->packet_router(); RTC_DCHECK(rtp_packet_sender); RTC_DCHECK(transport_feedback_observer); RTC_DCHECK(packet_router && !packet_router_); rtcp_observer_->SetBandwidthObserver(bandwidth_observer); feedback_observer_proxy_->SetTransportFeedbackObserver( transport_feedback_observer); seq_num_allocator_proxy_->SetSequenceNumberAllocator(packet_router); rtp_packet_sender_proxy_->SetPacketSender(rtp_packet_sender); _rtpRtcpModule->SetStorePacketsStatus(true, 600); packet_router->AddSendRtpModule(_rtpRtcpModule.get()); packet_router_ = packet_router; } void Channel::RegisterReceiverCongestionControlObjects( PacketRouter* packet_router) { RTC_DCHECK(packet_router && !packet_router_); packet_router->AddReceiveRtpModule(_rtpRtcpModule.get()); packet_router_ = packet_router; } void Channel::ResetSenderCongestionControlObjects() { RTC_DCHECK(packet_router_); _rtpRtcpModule->SetStorePacketsStatus(false, 600); rtcp_observer_->SetBandwidthObserver(nullptr); feedback_observer_proxy_->SetTransportFeedbackObserver(nullptr); seq_num_allocator_proxy_->SetSequenceNumberAllocator(nullptr); packet_router_->RemoveSendRtpModule(_rtpRtcpModule.get()); packet_router_ = nullptr; rtp_packet_sender_proxy_->SetPacketSender(nullptr); } void Channel::ResetReceiverCongestionControlObjects() { RTC_DCHECK(packet_router_); packet_router_->RemoveReceiveRtpModule(_rtpRtcpModule.get()); packet_router_ = nullptr; } void Channel::SetRTCPStatus(bool enable) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetRTCPStatus()"); _rtpRtcpModule->SetRTCPStatus(enable ? RtcpMode::kCompound : RtcpMode::kOff); } int Channel::GetRTCPStatus(bool& enabled) { RtcpMode method = _rtpRtcpModule->RTCP(); enabled = (method != RtcpMode::kOff); return 0; } int Channel::SetRTCP_CNAME(const char cName[256]) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetRTCP_CNAME()"); if (_rtpRtcpModule->SetCNAME(cName) != 0) { _engineStatisticsPtr->SetLastError( VE_RTP_RTCP_MODULE_ERROR, kTraceError, "SetRTCP_CNAME() failed to set RTCP CNAME"); return -1; } return 0; } int Channel::GetRemoteRTCP_CNAME(char cName[256]) { if (cName == NULL) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "GetRemoteRTCP_CNAME() invalid CNAME input buffer"); return -1; } char cname[RTCP_CNAME_SIZE]; const uint32_t remoteSSRC = rtp_receiver_->SSRC(); if (_rtpRtcpModule->RemoteCNAME(remoteSSRC, cname) != 0) { _engineStatisticsPtr->SetLastError( VE_CANNOT_RETRIEVE_CNAME, kTraceError, "GetRemoteRTCP_CNAME() failed to retrieve remote RTCP CNAME"); return -1; } strcpy(cName, cname); return 0; } int Channel::SendApplicationDefinedRTCPPacket( unsigned char subType, unsigned int name, const char* data, unsigned short dataLengthInBytes) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SendApplicationDefinedRTCPPacket()"); if (!channel_state_.Get().sending) { _engineStatisticsPtr->SetLastError( VE_NOT_SENDING, kTraceError, "SendApplicationDefinedRTCPPacket() not sending"); return -1; } if (NULL == data) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "SendApplicationDefinedRTCPPacket() invalid data value"); return -1; } if (dataLengthInBytes % 4 != 0) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "SendApplicationDefinedRTCPPacket() invalid length value"); return -1; } RtcpMode status = _rtpRtcpModule->RTCP(); if (status == RtcpMode::kOff) { _engineStatisticsPtr->SetLastError( VE_RTCP_ERROR, kTraceError, "SendApplicationDefinedRTCPPacket() RTCP is disabled"); return -1; } // Create and schedule the RTCP APP packet for transmission if (_rtpRtcpModule->SetRTCPApplicationSpecificData( subType, name, (const unsigned char*)data, dataLengthInBytes) != 0) { _engineStatisticsPtr->SetLastError( VE_SEND_ERROR, kTraceError, "SendApplicationDefinedRTCPPacket() failed to send RTCP packet"); return -1; } return 0; } int Channel::GetRemoteRTCPReportBlocks( std::vector* report_blocks) { if (report_blocks == NULL) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "GetRemoteRTCPReportBlock()s invalid report_blocks."); return -1; } // Get the report blocks from the latest received RTCP Sender or Receiver // Report. Each element in the vector contains the sender's SSRC and a // report block according to RFC 3550. std::vector rtcp_report_blocks; if (_rtpRtcpModule->RemoteRTCPStat(&rtcp_report_blocks) != 0) { return -1; } if (rtcp_report_blocks.empty()) return 0; std::vector::const_iterator it = rtcp_report_blocks.begin(); for (; it != rtcp_report_blocks.end(); ++it) { ReportBlock report_block; report_block.sender_SSRC = it->remoteSSRC; report_block.source_SSRC = it->sourceSSRC; report_block.fraction_lost = it->fractionLost; report_block.cumulative_num_packets_lost = it->cumulativeLost; report_block.extended_highest_sequence_number = it->extendedHighSeqNum; report_block.interarrival_jitter = it->jitter; report_block.last_SR_timestamp = it->lastSR; report_block.delay_since_last_SR = it->delaySinceLastSR; report_blocks->push_back(report_block); } return 0; } int Channel::GetRTPStatistics(CallStatistics& stats) { // --- RtcpStatistics // The jitter statistics is updated for each received RTP packet and is // based on received packets. RtcpStatistics statistics; StreamStatistician* statistician = rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC()); if (statistician) { statistician->GetStatistics(&statistics, _rtpRtcpModule->RTCP() == RtcpMode::kOff); } stats.fractionLost = statistics.fraction_lost; stats.cumulativeLost = statistics.cumulative_lost; stats.extendedMax = statistics.extended_max_sequence_number; stats.jitterSamples = statistics.jitter; // --- RTT stats.rttMs = GetRTT(true); // --- Data counters size_t bytesSent(0); uint32_t packetsSent(0); size_t bytesReceived(0); uint32_t packetsReceived(0); if (statistician) { statistician->GetDataCounters(&bytesReceived, &packetsReceived); } if (_rtpRtcpModule->DataCountersRTP(&bytesSent, &packetsSent) != 0) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "GetRTPStatistics() failed to retrieve RTP datacounters =>" " output will not be complete"); } stats.bytesSent = bytesSent; stats.packetsSent = packetsSent; stats.bytesReceived = bytesReceived; stats.packetsReceived = packetsReceived; // --- Timestamps { rtc::CritScope lock(&ts_stats_lock_); stats.capture_start_ntp_time_ms_ = capture_start_ntp_time_ms_; } return 0; } int Channel::SetCodecFECStatus(bool enable) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetCodecFECStatus()"); if (!codec_manager_.SetCodecFEC(enable) || !codec_manager_.MakeEncoder(&rent_a_codec_, audio_coding_.get())) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetCodecFECStatus() failed to set FEC state"); return -1; } return 0; } bool Channel::GetCodecFECStatus() { return codec_manager_.GetStackParams()->use_codec_fec; } void Channel::SetNACKStatus(bool enable, int maxNumberOfPackets) { // None of these functions can fail. // If pacing is enabled we always store packets. if (!pacing_enabled_) _rtpRtcpModule->SetStorePacketsStatus(enable, maxNumberOfPackets); rtp_receive_statistics_->SetMaxReorderingThreshold(maxNumberOfPackets); if (enable) audio_coding_->EnableNack(maxNumberOfPackets); else audio_coding_->DisableNack(); } // Called when we are missing one or more packets. int Channel::ResendPackets(const uint16_t* sequence_numbers, int length) { return _rtpRtcpModule->SendNACK(sequence_numbers, length); } void Channel::ProcessAndEncodeAudio(const AudioFrame& audio_input) { RTC_DCHECK(channel_state_.Get().sending); std::unique_ptr audio_frame(new AudioFrame()); // TODO(henrika): try to avoid copying by moving ownership of audio frame // either into pool of frames or into the task itself. audio_frame->CopyFrom(audio_input); audio_frame->id_ = ChannelId(); encoder_queue_->PostTask(std::unique_ptr( new ProcessAndEncodeAudioTask(std::move(audio_frame), this))); } void Channel::ProcessAndEncodeAudio(const int16_t* audio_data, int sample_rate, size_t number_of_frames, size_t number_of_channels) { RTC_DCHECK(channel_state_.Get().sending); CodecInst codec; GetSendCodec(codec); std::unique_ptr audio_frame(new AudioFrame()); audio_frame->id_ = ChannelId(); audio_frame->sample_rate_hz_ = std::min(codec.plfreq, sample_rate); audio_frame->num_channels_ = std::min(number_of_channels, codec.channels); RemixAndResample(audio_data, number_of_frames, number_of_channels, sample_rate, &input_resampler_, audio_frame.get()); encoder_queue_->PostTask(std::unique_ptr( new ProcessAndEncodeAudioTask(std::move(audio_frame), this))); } void Channel::ProcessAndEncodeAudioOnTaskQueue(AudioFrame* audio_input) { RTC_DCHECK_RUN_ON(encoder_queue_); RTC_DCHECK_GT(audio_input->samples_per_channel_, 0); RTC_DCHECK_LE(audio_input->num_channels_, 2); RTC_DCHECK_EQ(audio_input->id_, ChannelId()); if (channel_state_.Get().input_file_playing) { MixOrReplaceAudioWithFile(audio_input); } bool is_muted = InputMute(); AudioFrameOperations::Mute(audio_input, previous_frame_muted_, is_muted); if (_includeAudioLevelIndication) { size_t length = audio_input->samples_per_channel_ * audio_input->num_channels_; RTC_CHECK_LE(length, sizeof(audio_input->data_)); if (is_muted && previous_frame_muted_) { rms_level_.AnalyzeMuted(length); } else { rms_level_.Analyze( rtc::ArrayView(audio_input->data_, length)); } } previous_frame_muted_ = is_muted; // Add 10ms of raw (PCM) audio data to the encoder @ 32kHz. // The ACM resamples internally. audio_input->timestamp_ = _timeStamp; // This call will trigger AudioPacketizationCallback::SendData if encoding // is done and payload is ready for packetization and transmission. // Otherwise, it will return without invoking the callback. if (audio_coding_->Add10MsData(*audio_input) < 0) { LOG(LS_ERROR) << "ACM::Add10MsData() failed for channel " << _channelId; return; } _timeStamp += static_cast(audio_input->samples_per_channel_); } void Channel::set_associate_send_channel(const ChannelOwner& channel) { RTC_DCHECK(!channel.channel() || channel.channel()->ChannelId() != _channelId); rtc::CritScope lock(&assoc_send_channel_lock_); associate_send_channel_ = channel; } void Channel::DisassociateSendChannel(int channel_id) { rtc::CritScope lock(&assoc_send_channel_lock_); Channel* channel = associate_send_channel_.channel(); if (channel && channel->ChannelId() == channel_id) { // If this channel is associated with a send channel of the specified // Channel ID, disassociate with it. ChannelOwner ref(NULL); associate_send_channel_ = ref; } } void Channel::SetRtcEventLog(RtcEventLog* event_log) { event_log_proxy_->SetEventLog(event_log); } void Channel::SetRtcpRttStats(RtcpRttStats* rtcp_rtt_stats) { rtcp_rtt_stats_proxy_->SetRtcpRttStats(rtcp_rtt_stats); } void Channel::UpdateOverheadForEncoder() { size_t overhead_per_packet = transport_overhead_per_packet_ + rtp_overhead_per_packet_; audio_coding_->ModifyEncoder([&](std::unique_ptr* encoder) { if (*encoder) { (*encoder)->OnReceivedOverhead(overhead_per_packet); } }); } void Channel::SetTransportOverhead(size_t transport_overhead_per_packet) { rtc::CritScope cs(&overhead_per_packet_lock_); transport_overhead_per_packet_ = transport_overhead_per_packet; UpdateOverheadForEncoder(); } // TODO(solenberg): Make AudioSendStream an OverheadObserver instead. void Channel::OnOverheadChanged(size_t overhead_bytes_per_packet) { rtc::CritScope cs(&overhead_per_packet_lock_); rtp_overhead_per_packet_ = overhead_bytes_per_packet; UpdateOverheadForEncoder(); } int Channel::GetNetworkStatistics(NetworkStatistics& stats) { return audio_coding_->GetNetworkStatistics(&stats); } void Channel::GetDecodingCallStatistics(AudioDecodingCallStats* stats) const { audio_coding_->GetDecodingCallStatistics(stats); } uint32_t Channel::GetDelayEstimate() const { rtc::CritScope lock(&video_sync_lock_); return audio_coding_->FilteredCurrentDelayMs() + playout_delay_ms_; } int Channel::SetMinimumPlayoutDelay(int delayMs) { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::SetMinimumPlayoutDelay()"); if ((delayMs < kVoiceEngineMinMinPlayoutDelayMs) || (delayMs > kVoiceEngineMaxMinPlayoutDelayMs)) { _engineStatisticsPtr->SetLastError( VE_INVALID_ARGUMENT, kTraceError, "SetMinimumPlayoutDelay() invalid min delay"); return -1; } if (audio_coding_->SetMinimumPlayoutDelay(delayMs) != 0) { _engineStatisticsPtr->SetLastError( VE_AUDIO_CODING_MODULE_ERROR, kTraceError, "SetMinimumPlayoutDelay() failed to set min playout delay"); return -1; } return 0; } int Channel::GetPlayoutTimestamp(unsigned int& timestamp) { uint32_t playout_timestamp_rtp = 0; { rtc::CritScope lock(&video_sync_lock_); playout_timestamp_rtp = playout_timestamp_rtp_; } if (playout_timestamp_rtp == 0) { _engineStatisticsPtr->SetLastError( VE_CANNOT_RETRIEVE_VALUE, kTraceStateInfo, "GetPlayoutTimestamp() failed to retrieve timestamp"); return -1; } timestamp = playout_timestamp_rtp; return 0; } int Channel::GetRtpRtcp(RtpRtcp** rtpRtcpModule, RtpReceiver** rtp_receiver) const { *rtpRtcpModule = _rtpRtcpModule.get(); *rtp_receiver = rtp_receiver_.get(); return 0; } // TODO(andrew): refactor Mix functions here and in transmit_mixer.cc to use // a shared helper. int32_t Channel::MixOrReplaceAudioWithFile(AudioFrame* audio_input) { RTC_DCHECK_RUN_ON(encoder_queue_); std::unique_ptr fileBuffer(new int16_t[640]); size_t fileSamples(0); const int mixingFrequency = audio_input->sample_rate_hz_; { rtc::CritScope cs(&_fileCritSect); if (!input_file_player_) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::MixOrReplaceAudioWithFile() fileplayer" " doesnt exist"); return -1; } if (input_file_player_->Get10msAudioFromFile(fileBuffer.get(), &fileSamples, mixingFrequency) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::MixOrReplaceAudioWithFile() file mixing " "failed"); return -1; } if (fileSamples == 0) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::MixOrReplaceAudioWithFile() file is ended"); return 0; } } RTC_DCHECK_EQ(audio_input->samples_per_channel_, fileSamples); if (_mixFileWithMicrophone) { // Currently file stream is always mono. // TODO(xians): Change the code when FilePlayer supports real stereo. MixWithSat(audio_input->data_, audio_input->num_channels_, fileBuffer.get(), 1, fileSamples); } else { // Replace ACM audio with file. // Currently file stream is always mono. // TODO(xians): Change the code when FilePlayer supports real stereo. audio_input->UpdateFrame( _channelId, 0xFFFFFFFF, fileBuffer.get(), fileSamples, mixingFrequency, AudioFrame::kNormalSpeech, AudioFrame::kVadUnknown, 1); } return 0; } int32_t Channel::MixAudioWithFile(AudioFrame& audioFrame, int mixingFrequency) { assert(mixingFrequency <= 48000); std::unique_ptr fileBuffer(new int16_t[960]); size_t fileSamples(0); { rtc::CritScope cs(&_fileCritSect); if (!output_file_player_) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::MixAudioWithFile() file mixing failed"); return -1; } // We should get the frequency we ask for. if (output_file_player_->Get10msAudioFromFile( fileBuffer.get(), &fileSamples, mixingFrequency) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::MixAudioWithFile() file mixing failed"); return -1; } } if (audioFrame.samples_per_channel_ == fileSamples) { // Currently file stream is always mono. // TODO(xians): Change the code when FilePlayer supports real stereo. MixWithSat(audioFrame.data_, audioFrame.num_channels_, fileBuffer.get(), 1, fileSamples); } else { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::MixAudioWithFile() samples_per_channel_(%" PRIuS ") != " "fileSamples(%" PRIuS ")", audioFrame.samples_per_channel_, fileSamples); return -1; } return 0; } void Channel::UpdatePlayoutTimestamp(bool rtcp) { jitter_buffer_playout_timestamp_ = audio_coding_->PlayoutTimestamp(); if (!jitter_buffer_playout_timestamp_) { // This can happen if this channel has not received any RTP packets. In // this case, NetEq is not capable of computing a playout timestamp. return; } uint16_t delay_ms = 0; if (_audioDeviceModulePtr->PlayoutDelay(&delay_ms) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::UpdatePlayoutTimestamp() failed to read playout" " delay from the ADM"); _engineStatisticsPtr->SetLastError( VE_CANNOT_RETRIEVE_VALUE, kTraceError, "UpdatePlayoutTimestamp() failed to retrieve playout delay"); return; } RTC_DCHECK(jitter_buffer_playout_timestamp_); uint32_t playout_timestamp = *jitter_buffer_playout_timestamp_; // Remove the playout delay. playout_timestamp -= (delay_ms * (GetRtpTimestampRateHz() / 1000)); WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::UpdatePlayoutTimestamp() => playoutTimestamp = %lu", playout_timestamp); { rtc::CritScope lock(&video_sync_lock_); if (!rtcp) { playout_timestamp_rtp_ = playout_timestamp; } playout_delay_ms_ = delay_ms; } } void Channel::RegisterReceiveCodecsToRTPModule() { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::RegisterReceiveCodecsToRTPModule()"); CodecInst codec; const uint8_t nSupportedCodecs = AudioCodingModule::NumberOfCodecs(); for (int idx = 0; idx < nSupportedCodecs; idx++) { // Open up the RTP/RTCP receiver for all supported codecs if ((audio_coding_->Codec(idx, &codec) == -1) || (rtp_receiver_->RegisterReceivePayload(codec) == -1)) { WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::RegisterReceiveCodecsToRTPModule() unable" " to register %s (%d/%d/%" PRIuS "/%d) to RTP/RTCP " "receiver", codec.plname, codec.pltype, codec.plfreq, codec.channels, codec.rate); } else { WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), "Channel::RegisterReceiveCodecsToRTPModule() %s " "(%d/%d/%" PRIuS "/%d) has been added to the RTP/RTCP " "receiver", codec.plname, codec.pltype, codec.plfreq, codec.channels, codec.rate); } } } int Channel::SetSendRtpHeaderExtension(bool enable, RTPExtensionType type, unsigned char id) { int error = 0; _rtpRtcpModule->DeregisterSendRtpHeaderExtension(type); if (enable) { error = _rtpRtcpModule->RegisterSendRtpHeaderExtension(type, id); } return error; } int Channel::GetRtpTimestampRateHz() const { const auto format = audio_coding_->ReceiveFormat(); // Default to the playout frequency if we've not gotten any packets yet. // TODO(ossu): Zero clockrate can only happen if we've added an external // decoder for a format we don't support internally. Remove once that way of // adding decoders is gone! return (format && format->clockrate_hz != 0) ? format->clockrate_hz : audio_coding_->PlayoutFrequency(); } int64_t Channel::GetRTT(bool allow_associate_channel) const { RtcpMode method = _rtpRtcpModule->RTCP(); if (method == RtcpMode::kOff) { return 0; } std::vector report_blocks; _rtpRtcpModule->RemoteRTCPStat(&report_blocks); int64_t rtt = 0; if (report_blocks.empty()) { if (allow_associate_channel) { rtc::CritScope lock(&assoc_send_channel_lock_); Channel* channel = associate_send_channel_.channel(); // Tries to get RTT from an associated channel. This is important for // receive-only channels. if (channel) { // To prevent infinite recursion and deadlock, calling GetRTT of // associate channel should always use "false" for argument: // |allow_associate_channel|. rtt = channel->GetRTT(false); } } return rtt; } uint32_t remoteSSRC = rtp_receiver_->SSRC(); std::vector::const_iterator it = report_blocks.begin(); for (; it != report_blocks.end(); ++it) { if (it->remoteSSRC == remoteSSRC) break; } if (it == report_blocks.end()) { // We have not received packets with SSRC matching the report blocks. // To calculate RTT we try with the SSRC of the first report block. // This is very important for send-only channels where we don't know // the SSRC of the other end. remoteSSRC = report_blocks[0].remoteSSRC; } int64_t avg_rtt = 0; int64_t max_rtt = 0; int64_t min_rtt = 0; if (_rtpRtcpModule->RTT(remoteSSRC, &rtt, &avg_rtt, &min_rtt, &max_rtt) != 0) { return 0; } return rtt; } } // namespace voe } // namespace webrtc