2011-07-07 08:21:25 +00:00
|
|
|
/*
|
2012-01-31 08:45:03 +00:00
|
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
2011-07-07 08:21:25 +00:00
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
2013-02-12 21:42:18 +00:00
|
|
|
#include "webrtc/voice_engine/channel.h"
|
|
|
|
|
|
2013-09-12 17:03:00 +00:00
|
|
|
#include "webrtc/common.h"
|
2013-02-12 21:42:18 +00:00
|
|
|
#include "webrtc/modules/audio_device/include/audio_device.h"
|
|
|
|
|
#include "webrtc/modules/audio_processing/include/audio_processing.h"
|
2014-03-20 12:04:09 +00:00
|
|
|
#include "webrtc/modules/interface/module_common_types.h"
|
2013-08-15 23:38:54 +00:00
|
|
|
#include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
|
2014-05-20 22:55:01 +00:00
|
|
|
#include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h"
|
2013-08-15 23:38:54 +00:00
|
|
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
|
|
|
|
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
|
|
|
|
|
#include "webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h"
|
2013-02-12 21:42:18 +00:00
|
|
|
#include "webrtc/modules/utility/interface/audio_frame_operations.h"
|
|
|
|
|
#include "webrtc/modules/utility/interface/process_thread.h"
|
|
|
|
|
#include "webrtc/modules/utility/interface/rtp_dump.h"
|
|
|
|
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
|
|
|
|
#include "webrtc/system_wrappers/interface/logging.h"
|
|
|
|
|
#include "webrtc/system_wrappers/interface/trace.h"
|
2014-03-24 10:38:25 +00:00
|
|
|
#include "webrtc/video_engine/include/vie_network.h"
|
2013-02-12 21:42:18 +00:00
|
|
|
#include "webrtc/voice_engine/include/voe_base.h"
|
|
|
|
|
#include "webrtc/voice_engine/include/voe_external_media.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/transmit_mixer.h"
|
|
|
|
|
#include "webrtc/voice_engine/utility.h"
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
#include <Qos.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-11-14 19:07:54 +00:00
|
|
|
namespace webrtc {
|
|
|
|
|
namespace voe {
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-12-19 13:26:02 +00:00
|
|
|
// Extend the default RTCP statistics struct with max_jitter, defined as the
|
|
|
|
|
// maximum jitter value seen in an RTCP report block.
|
|
|
|
|
struct ChannelStatistics : public RtcpStatistics {
|
|
|
|
|
ChannelStatistics() : rtcp(), max_jitter(0) {}
|
|
|
|
|
|
|
|
|
|
RtcpStatistics rtcp;
|
|
|
|
|
uint32_t max_jitter;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Statistics callback, called at each generation of a new RTCP report block.
|
|
|
|
|
class StatisticsProxy : public RtcpStatisticsCallback {
|
|
|
|
|
public:
|
|
|
|
|
StatisticsProxy(uint32_t ssrc)
|
|
|
|
|
: stats_lock_(CriticalSectionWrapper::CreateCriticalSection()),
|
|
|
|
|
ssrc_(ssrc) {}
|
|
|
|
|
virtual ~StatisticsProxy() {}
|
|
|
|
|
|
|
|
|
|
virtual void StatisticsUpdated(const RtcpStatistics& statistics,
|
|
|
|
|
uint32_t ssrc) OVERRIDE {
|
|
|
|
|
if (ssrc != ssrc_)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
CriticalSectionScoped cs(stats_lock_.get());
|
|
|
|
|
stats_.rtcp = statistics;
|
|
|
|
|
if (statistics.jitter > stats_.max_jitter) {
|
|
|
|
|
stats_.max_jitter = statistics.jitter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResetStatistics() {
|
|
|
|
|
CriticalSectionScoped cs(stats_lock_.get());
|
|
|
|
|
stats_ = ChannelStatistics();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ChannelStatistics GetStats() {
|
|
|
|
|
CriticalSectionScoped cs(stats_lock_.get());
|
|
|
|
|
return stats_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// StatisticsUpdated calls are triggered from threads in the RTP module,
|
|
|
|
|
// while GetStats calls can be triggered from the public voice engine API,
|
|
|
|
|
// hence synchronization is needed.
|
|
|
|
|
scoped_ptr<CriticalSectionWrapper> stats_lock_;
|
|
|
|
|
const uint32_t ssrc_;
|
|
|
|
|
ChannelStatistics stats_;
|
|
|
|
|
};
|
|
|
|
|
|
2014-05-28 09:52:06 +00:00
|
|
|
class VoEBitrateObserver : public BitrateObserver {
|
|
|
|
|
public:
|
|
|
|
|
explicit VoEBitrateObserver(Channel* owner)
|
|
|
|
|
: owner_(owner) {}
|
|
|
|
|
virtual ~VoEBitrateObserver() {}
|
|
|
|
|
|
|
|
|
|
// Implements BitrateObserver.
|
|
|
|
|
virtual void OnNetworkChanged(const uint32_t bitrate_bps,
|
|
|
|
|
const uint8_t fraction_lost,
|
|
|
|
|
const uint32_t rtt) OVERRIDE {
|
|
|
|
|
// |fraction_lost| has a scale of 0 - 255.
|
|
|
|
|
owner_->OnNetworkChanged(bitrate_bps, fraction_lost, rtt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Channel* owner_;
|
|
|
|
|
};
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::SendData(FrameType frameType,
|
2013-04-09 10:09:10 +00:00
|
|
|
uint8_t payloadType,
|
|
|
|
|
uint32_t timeStamp,
|
|
|
|
|
const uint8_t* payloadData,
|
|
|
|
|
uint16_t payloadSize,
|
2011-07-07 08:21:25 +00:00
|
|
|
const RTPFragmentationHeader* fragmentation)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendData(frameType=%u, payloadType=%u, timeStamp=%u,"
|
|
|
|
|
" payloadSize=%u, 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
|
2014-05-05 18:22:21 +00:00
|
|
|
_rtpRtcpModule->SetAudioLevel(rms_level_.RMS());
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SendOutgoingData((FrameType&)frameType,
|
2011-07-07 08:21:25 +00:00
|
|
|
payloadType,
|
|
|
|
|
timeStamp,
|
2012-07-03 13:21:22 +00:00
|
|
|
// Leaving the time when this frame was
|
|
|
|
|
// received from the capture device as
|
|
|
|
|
// undefined for voice for now.
|
|
|
|
|
-1,
|
2011-07-07 08:21:25 +00:00
|
|
|
payloadData,
|
|
|
|
|
payloadSize,
|
|
|
|
|
fragmentation) == -1)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceWarning,
|
|
|
|
|
"Channel::SendData() failed to send data to RTP/RTCP module");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_lastLocalTimeStamp = timeStamp;
|
|
|
|
|
_lastPayloadType = payloadType;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
|
|
|
|
Channel::InFrameType(int16_t frameType)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::InFrameType(frameType=%d)", frameType);
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
// 1 indicates speech
|
|
|
|
|
_sendFrameType = (frameType == 1) ? 1 : 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::OnRxVadDetected(int vadDecision)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::OnRxVadDetected(vadDecision=%d)", vadDecision);
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_rxVadObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_rxVadObserverPtr->OnRxVad(_channelId, vadDecision);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SendPacket(int channel, const void *data, int len)
|
|
|
|
|
{
|
|
|
|
|
channel = VoEChannelId(channel);
|
|
|
|
|
assert(channel == _channelId);
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendPacket(channel=%d, len=%d)", channel, len);
|
|
|
|
|
|
2013-10-18 21:10:51 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_transportPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendPacket() failed to send RTP packet due to"
|
|
|
|
|
" invalid transport object");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
uint8_t* bufferToSendPtr = (uint8_t*)data;
|
|
|
|
|
int32_t bufferLength = len;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// Dump the RTP packet to a file (if RTP dump is enabled).
|
2013-04-09 10:09:10 +00:00
|
|
|
if (_rtpDumpOut.DumpPacket((const uint8_t*)data, len) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendPacket() RTP dump to output file failed");
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-18 21:10:51 +00:00
|
|
|
int n = _transportPtr->SendPacket(channel, bufferToSendPtr,
|
|
|
|
|
bufferLength);
|
|
|
|
|
if (n < 0) {
|
|
|
|
|
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 -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2013-10-18 21:10:51 +00:00
|
|
|
return n;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SendRTCPPacket(int channel, const void *data, int len)
|
|
|
|
|
{
|
|
|
|
|
channel = VoEChannelId(channel);
|
|
|
|
|
assert(channel == _channelId);
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendRTCPPacket(channel=%d, len=%d)", channel, len);
|
|
|
|
|
|
2013-10-18 21:10:51 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
|
|
|
|
if (_transportPtr == NULL)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2013-10-18 21:10:51 +00:00
|
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendRTCPPacket() failed to send RTCP packet"
|
|
|
|
|
" due to invalid transport object");
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
uint8_t* bufferToSendPtr = (uint8_t*)data;
|
|
|
|
|
int32_t bufferLength = len;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// Dump the RTCP packet to a file (if RTP dump is enabled).
|
2013-04-09 10:09:10 +00:00
|
|
|
if (_rtpDumpOut.DumpPacket((const uint8_t*)data, len) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendPacket() RTCP dump to output file failed");
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-18 21:10:51 +00:00
|
|
|
int n = _transportPtr->SendRTCPPacket(channel,
|
|
|
|
|
bufferToSendPtr,
|
|
|
|
|
bufferLength);
|
|
|
|
|
if (n < 0) {
|
|
|
|
|
std::string transport_name =
|
|
|
|
|
_externalTransport ? "external transport" : "WebRtc sockets";
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendRTCPPacket() transmission using %s failed",
|
|
|
|
|
transport_name.c_str());
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2013-10-18 21:10:51 +00:00
|
|
|
return n;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::OnPlayTelephoneEvent(int32_t id,
|
|
|
|
|
uint8_t event,
|
|
|
|
|
uint16_t lengthMs,
|
|
|
|
|
uint8_t volume)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnPlayTelephoneEvent(id=%d, event=%u, lengthMs=%u,"
|
2011-09-15 20:49:50 +00:00
|
|
|
" volume=%u)", id, event, lengthMs, volume);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (!_playOutbandDtmfEvent || (event > 15))
|
|
|
|
|
{
|
|
|
|
|
// Ignore callback since feedback is disabled or event is not a
|
|
|
|
|
// Dtmf tone event.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(_outputMixerPtr != NULL);
|
|
|
|
|
|
|
|
|
|
// Start playing out the Dtmf tone (if playout is enabled).
|
|
|
|
|
// Reduce length of tone with 80ms to the reduce risk of echo.
|
|
|
|
|
_outputMixerPtr->PlayDtmfTone(event, lengthMs - 80, volume);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-08-21 20:58:21 +00:00
|
|
|
Channel::OnIncomingSSRCChanged(int32_t id, uint32_t ssrc)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnIncomingSSRCChanged(id=%d, SSRC=%d)",
|
2013-08-21 20:58:21 +00:00
|
|
|
id, ssrc);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-08-29 07:34:12 +00:00
|
|
|
// Update ssrc so that NTP for AV sync can be updated.
|
|
|
|
|
_rtpRtcpModule->SetRemoteSSRC(ssrc);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-05-14 08:31:39 +00:00
|
|
|
void Channel::OnIncomingCSRCChanged(int32_t id,
|
|
|
|
|
uint32_t CSRC,
|
|
|
|
|
bool added)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnIncomingCSRCChanged(id=%d, CSRC=%d, added=%d)",
|
|
|
|
|
id, CSRC, added);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-21 20:58:21 +00:00
|
|
|
void Channel::ResetStatistics(uint32_t ssrc) {
|
|
|
|
|
StreamStatistician* statistician =
|
|
|
|
|
rtp_receive_statistics_->GetStatistician(ssrc);
|
|
|
|
|
if (statistician) {
|
|
|
|
|
statistician->ResetStatistics();
|
|
|
|
|
}
|
2013-12-19 13:26:02 +00:00
|
|
|
statistics_proxy_->ResetStatistics();
|
2013-08-15 23:38:54 +00:00
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::OnApplicationDataReceived(int32_t id,
|
|
|
|
|
uint8_t subType,
|
|
|
|
|
uint32_t name,
|
|
|
|
|
uint16_t length,
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint8_t* data)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnApplicationDataReceived(id=%d, subType=%u,"
|
|
|
|
|
" name=%u, length=%u)",
|
|
|
|
|
id, subType, name, length);
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t channel = VoEChannelId(id);
|
2011-07-07 08:21:25 +00:00
|
|
|
assert(channel == _channelId);
|
|
|
|
|
|
|
|
|
|
if (_rtcpObserver)
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_rtcpObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_rtcpObserverPtr->OnApplicationDataReceived(channel,
|
|
|
|
|
subType,
|
|
|
|
|
name,
|
|
|
|
|
data,
|
|
|
|
|
length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::OnInitializeDecoder(
|
2013-05-14 08:31:39 +00:00
|
|
|
int32_t id,
|
|
|
|
|
int8_t payloadType,
|
2012-03-01 18:34:25 +00:00
|
|
|
const char payloadName[RTP_PAYLOAD_NAME_SIZE],
|
2013-05-14 08:31:39 +00:00
|
|
|
int frequency,
|
|
|
|
|
uint8_t channels,
|
|
|
|
|
uint32_t rate)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnInitializeDecoder(id=%d, payloadType=%d, "
|
|
|
|
|
"payloadName=%s, frequency=%u, channels=%u, rate=%u)",
|
|
|
|
|
id, payloadType, payloadName, frequency, channels, rate);
|
|
|
|
|
|
2011-08-23 17:53:54 +00:00
|
|
|
assert(VoEChannelId(id) == _channelId);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2012-01-16 08:45:42 +00:00
|
|
|
CodecInst receiveCodec = {0};
|
|
|
|
|
CodecInst dummyCodec = {0};
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
receiveCodec.pltype = payloadType;
|
|
|
|
|
receiveCodec.plfreq = frequency;
|
|
|
|
|
receiveCodec.channels = channels;
|
|
|
|
|
receiveCodec.rate = rate;
|
2012-01-16 08:45:42 +00:00
|
|
|
strncpy(receiveCodec.plname, payloadName, RTP_PAYLOAD_NAME_SIZE - 1);
|
2013-01-22 04:44:30 +00:00
|
|
|
|
2013-09-23 23:02:24 +00:00
|
|
|
audio_coding_->Codec(payloadName, &dummyCodec, frequency, channels);
|
2011-07-07 08:21:25 +00:00
|
|
|
receiveCodec.pacsize = dummyCodec.pacsize;
|
|
|
|
|
|
|
|
|
|
// Register the new codec to the ACM
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterReceiveCodec(receiveCodec) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
2011-08-23 17:53:54 +00:00
|
|
|
VoEId(_instanceId, _channelId),
|
2011-07-07 08:21:25 +00:00
|
|
|
"Channel::OnInitializeDecoder() invalid codec ("
|
|
|
|
|
"pt=%d, name=%s) received - 1", payloadType, payloadName);
|
|
|
|
|
_engineStatisticsPtr->SetLastError(VE_AUDIO_CODING_MODULE_ERROR);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::OnPacketTimeout(int32_t id)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnPacketTimeout(id=%d)", id);
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(_callbackCritSectPtr);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_voiceEngineObserverPtr)
|
|
|
|
|
{
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().receiving || _externalTransport)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t channel = VoEChannelId(id);
|
2011-07-07 08:21:25 +00:00
|
|
|
assert(channel == _channelId);
|
|
|
|
|
// Ensure that next OnReceivedPacket() callback will trigger
|
|
|
|
|
// a VE_PACKET_RECEIPT_RESTARTED callback.
|
|
|
|
|
_rtpPacketTimedOut = true;
|
|
|
|
|
// Deliver callback to the observer
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnPacketTimeout() => "
|
|
|
|
|
"CallbackOnError(VE_RECEIVE_PACKET_TIMEOUT)");
|
|
|
|
|
_voiceEngineObserverPtr->CallbackOnError(channel,
|
|
|
|
|
VE_RECEIVE_PACKET_TIMEOUT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::OnReceivedPacket(int32_t id,
|
|
|
|
|
RtpRtcpPacketType packetType)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnReceivedPacket(id=%d, packetType=%d)",
|
|
|
|
|
id, packetType);
|
|
|
|
|
|
2011-08-23 17:53:54 +00:00
|
|
|
assert(VoEChannelId(id) == _channelId);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// Notify only for the case when we have restarted an RTP session.
|
|
|
|
|
if (_rtpPacketTimedOut && (kPacketRtp == packetType))
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(_callbackCritSectPtr);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_voiceEngineObserverPtr)
|
|
|
|
|
{
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t channel = VoEChannelId(id);
|
2011-07-07 08:21:25 +00:00
|
|
|
assert(channel == _channelId);
|
|
|
|
|
// Reset timeout mechanism
|
|
|
|
|
_rtpPacketTimedOut = false;
|
|
|
|
|
// Deliver callback to the observer
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnPacketTimeout() =>"
|
|
|
|
|
" CallbackOnError(VE_PACKET_RECEIPT_RESTARTED)");
|
|
|
|
|
_voiceEngineObserverPtr->CallbackOnError(
|
|
|
|
|
channel,
|
|
|
|
|
VE_PACKET_RECEIPT_RESTARTED);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::OnPeriodicDeadOrAlive(int32_t id,
|
|
|
|
|
RTPAliveType alive)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnPeriodicDeadOrAlive(id=%d, alive=%d)", id, alive);
|
|
|
|
|
|
2013-04-05 14:34:57 +00:00
|
|
|
{
|
|
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
|
|
|
|
if (!_connectionObserver)
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t channel = VoEChannelId(id);
|
2011-07-07 08:21:25 +00:00
|
|
|
assert(channel == _channelId);
|
|
|
|
|
|
|
|
|
|
// Use Alive as default to limit risk of false Dead detections
|
|
|
|
|
bool isAlive(true);
|
|
|
|
|
|
|
|
|
|
// Always mark the connection as Dead when the module reports kRtpDead
|
|
|
|
|
if (kRtpDead == alive)
|
|
|
|
|
{
|
|
|
|
|
isAlive = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// It is possible that the connection is alive even if no RTP packet has
|
|
|
|
|
// been received for a long time since the other side might use VAD/DTX
|
|
|
|
|
// and a low SID-packet update rate.
|
2014-03-18 10:32:33 +00:00
|
|
|
if ((kRtpNoRtp == alive) && channel_state_.Get().playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
// Detect Alive for all NetEQ states except for the case when we are
|
|
|
|
|
// in PLC_CNG state.
|
|
|
|
|
// PLC_CNG <=> background noise only due to long expand or error.
|
|
|
|
|
// Note that, the case where the other side stops sending during CNG
|
|
|
|
|
// state will be detected as Alive. Dead is is not set until after
|
|
|
|
|
// missing RTCP packets for at least twelve seconds (handled
|
|
|
|
|
// internally by the RTP/RTCP module).
|
|
|
|
|
isAlive = (_outputSpeechType != AudioFrame::kPLCCNG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send callback to the registered observer
|
|
|
|
|
if (_connectionObserver)
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_connectionObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_connectionObserverPtr->OnPeriodicDeadOrAlive(channel, isAlive);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
|
|
|
|
Channel::OnReceivedPayloadData(const uint8_t* payloadData,
|
2013-05-14 08:31:39 +00:00
|
|
|
uint16_t payloadSize,
|
2011-07-07 08:21:25 +00:00
|
|
|
const WebRtcRTPHeader* rtpHeader)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnReceivedPayloadData(payloadSize=%d,"
|
|
|
|
|
" payloadType=%u, audioChannel=%u)",
|
|
|
|
|
payloadSize,
|
|
|
|
|
rtpHeader->header.payloadType,
|
|
|
|
|
rtpHeader->type.Audio.channel);
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (!channel_state_.Get().playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
// 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");
|
|
|
|
|
_numberOfDiscardedPackets++;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Push the incoming payload (parsed and ready for decoding) into the ACM
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->IncomingPacket(payloadData,
|
|
|
|
|
payloadSize,
|
|
|
|
|
*rtpHeader) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceWarning,
|
|
|
|
|
"Channel::OnReceivedPayloadData() unable to push data to the ACM");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-06 21:09:01 +00:00
|
|
|
// Update the packet delay.
|
2011-07-07 08:21:25 +00:00
|
|
|
UpdatePacketDelay(rtpHeader->header.timestamp,
|
|
|
|
|
rtpHeader->header.sequenceNumber);
|
2013-06-06 21:09:01 +00:00
|
|
|
|
2013-08-15 23:38:54 +00:00
|
|
|
uint16_t round_trip_time = 0;
|
|
|
|
|
_rtpRtcpModule->RTT(rtp_receiver_->SSRC(), &round_trip_time,
|
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
|
|
2013-09-23 23:02:24 +00:00
|
|
|
std::vector<uint16_t> nack_list = audio_coding_->GetNackList(
|
2013-08-15 23:38:54 +00:00
|
|
|
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<int>(nack_list.size()));
|
2013-06-06 21:09:01 +00:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-06 13:40:11 +00:00
|
|
|
bool Channel::OnRecoveredPacket(const uint8_t* rtp_packet,
|
|
|
|
|
int 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);
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-14 08:31:39 +00:00
|
|
|
int32_t Channel::GetAudioFrame(int32_t id, AudioFrame& audioFrame)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetAudioFrame(id=%d)", id);
|
|
|
|
|
|
|
|
|
|
// Get 10ms raw PCM data from the ACM (mixer limits output frequency)
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->PlayoutData10Ms(audioFrame.sample_rate_hz_,
|
|
|
|
|
&audioFrame) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetAudioFrame() PlayoutData10Ms() failed!");
|
2012-01-13 00:30:11 +00:00
|
|
|
// 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 -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_RxVadDetection)
|
|
|
|
|
{
|
|
|
|
|
UpdateRxVadDetection(audioFrame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert module ID to internal VoE channel ID
|
2012-05-02 23:56:37 +00:00
|
|
|
audioFrame.id_ = VoEChannelId(audioFrame.id_);
|
2011-07-07 08:21:25 +00:00
|
|
|
// Store speech type for dead-or-alive detection
|
2012-05-02 23:56:37 +00:00
|
|
|
_outputSpeechType = audioFrame.speech_type_;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
ChannelState::State state = channel_state_.Get();
|
|
|
|
|
|
|
|
|
|
if (state.rx_apm_is_enabled) {
|
2014-01-07 17:45:09 +00:00
|
|
|
int err = rx_audioproc_->ProcessStream(&audioFrame);
|
|
|
|
|
if (err) {
|
|
|
|
|
LOG(LS_ERROR) << "ProcessStream() error: " << err;
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-17 18:28:55 +00:00
|
|
|
float output_gain = 1.0f;
|
|
|
|
|
float left_pan = 1.0f;
|
|
|
|
|
float right_pan = 1.0f;
|
|
|
|
|
{
|
|
|
|
|
CriticalSectionScoped cs(&volume_settings_critsect_);
|
|
|
|
|
output_gain = _outputGain;
|
|
|
|
|
left_pan = _panLeft;
|
|
|
|
|
right_pan= _panRight;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
// Output volume scaling
|
2013-10-17 18:28:55 +00:00
|
|
|
if (output_gain < 0.99f || output_gain > 1.01f)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2013-10-17 18:28:55 +00:00
|
|
|
AudioFrameOperations::ScaleWithSat(output_gain, audioFrame);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scale left and/or right channel(s) if stereo and master balance is
|
|
|
|
|
// active
|
|
|
|
|
|
2013-10-17 18:28:55 +00:00
|
|
|
if (left_pan != 1.0f || right_pan != 1.0f)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2012-05-02 23:56:37 +00:00
|
|
|
if (audioFrame.num_channels_ == 1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
// Emulate stereo mode since panning is active.
|
|
|
|
|
// The mono signal is copied to both left and right channels here.
|
2012-06-27 03:25:31 +00:00
|
|
|
AudioFrameOperations::MonoToStereo(&audioFrame);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
// For true stereo mode (when we are receiving a stereo signal), no
|
|
|
|
|
// action is needed.
|
|
|
|
|
|
|
|
|
|
// Do the panning operation (the audio frame contains stereo at this
|
|
|
|
|
// stage)
|
2013-10-17 18:28:55 +00:00
|
|
|
AudioFrameOperations::Scale(left_pan, right_pan, audioFrame);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mix decoded PCM output with file if file mixing is enabled
|
2014-03-18 10:32:33 +00:00
|
|
|
if (state.output_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2012-05-02 23:56:37 +00:00
|
|
|
MixAudioWithFile(audioFrame, audioFrame.sample_rate_hz_);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// External media
|
|
|
|
|
if (_outputExternalMedia)
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2012-05-02 23:56:37 +00:00
|
|
|
const bool isStereo = (audioFrame.num_channels_ == 2);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_outputExternalMediaCallbackPtr)
|
|
|
|
|
{
|
|
|
|
|
_outputExternalMediaCallbackPtr->Process(
|
|
|
|
|
_channelId,
|
|
|
|
|
kPlaybackPerChannel,
|
2013-04-09 10:09:10 +00:00
|
|
|
(int16_t*)audioFrame.data_,
|
2012-05-02 23:56:37 +00:00
|
|
|
audioFrame.samples_per_channel_,
|
|
|
|
|
audioFrame.sample_rate_hz_,
|
2011-07-07 08:21:25 +00:00
|
|
|
isStereo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Record playout if enabled
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_outputFileRecording && _outputFileRecorderPtr)
|
|
|
|
|
{
|
2012-03-26 08:11:25 +00:00
|
|
|
_outputFileRecorderPtr->RecordAudioToFile(audioFrame);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Measure audio level (0-9)
|
|
|
|
|
_outputAudioLevel.ComputeLevel(audioFrame);
|
|
|
|
|
|
2014-05-20 22:55:01 +00:00
|
|
|
audioFrame.ntp_time_ms_ = ntp_estimator_->Estimate(audioFrame.timestamp_);
|
2014-05-19 17:39:11 +00:00
|
|
|
|
|
|
|
|
if (!first_frame_arrived_) {
|
|
|
|
|
first_frame_arrived_ = true;
|
|
|
|
|
capture_start_rtp_time_stamp_ = audioFrame.timestamp_;
|
|
|
|
|
} else {
|
|
|
|
|
// |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_|
|
|
|
|
|
CriticalSectionScoped lock(ts_stats_lock_.get());
|
|
|
|
|
uint32_t elapsed_time_ms =
|
|
|
|
|
(audioFrame.timestamp_ - capture_start_rtp_time_stamp_) /
|
|
|
|
|
(audioFrame.sample_rate_hz_ * 1000);
|
|
|
|
|
capture_start_ntp_time_ms_ = audioFrame.ntp_time_ms_ - elapsed_time_ms;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::NeededFrequency(int32_t id)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::NeededFrequency(id=%d)", id);
|
|
|
|
|
|
|
|
|
|
int highestNeeded = 0;
|
|
|
|
|
|
|
|
|
|
// Determine highest needed receive frequency
|
2013-09-23 23:02:24 +00:00
|
|
|
int32_t receiveFrequency = audio_coding_->ReceiveFrequency();
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// Return the bigger of playout and receive frequency in the ACM.
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->PlayoutFrequency() > receiveFrequency)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2013-09-23 23:02:24 +00:00
|
|
|
highestNeeded = audio_coding_->PlayoutFrequency();
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
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.
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().output_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2014-03-18 10:32:33 +00:00
|
|
|
if (_outputFilePlayerPtr)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
if(_outputFilePlayerPtr->Frequency()>highestNeeded)
|
|
|
|
|
{
|
|
|
|
|
highestNeeded=_outputFilePlayerPtr->Frequency();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(highestNeeded);
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::CreateChannel(Channel*& channel,
|
2013-05-14 08:31:39 +00:00
|
|
|
int32_t channelId,
|
2013-09-12 17:03:00 +00:00
|
|
|
uint32_t instanceId,
|
|
|
|
|
const Config& config)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(instanceId,channelId),
|
|
|
|
|
"Channel::CreateChannel(channelId=%d, instanceId=%d)",
|
|
|
|
|
channelId, instanceId);
|
|
|
|
|
|
2013-09-12 17:03:00 +00:00
|
|
|
channel = new Channel(channelId, instanceId, config);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (channel == NULL)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice,
|
|
|
|
|
VoEId(instanceId,channelId),
|
|
|
|
|
"Channel::CreateChannel() unable to allocate memory for"
|
|
|
|
|
" channel");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::PlayNotification(int32_t id, uint32_t durationMs)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::PlayNotification(id=%d, durationMs=%d)",
|
|
|
|
|
id, durationMs);
|
|
|
|
|
|
|
|
|
|
// Not implement yet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::RecordNotification(int32_t id, uint32_t durationMs)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::RecordNotification(id=%d, durationMs=%d)",
|
|
|
|
|
id, durationMs);
|
|
|
|
|
|
|
|
|
|
// Not implement yet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::PlayFileEnded(int32_t id)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::PlayFileEnded(id=%d)", id);
|
|
|
|
|
|
|
|
|
|
if (id == _inputFilePlayerId)
|
|
|
|
|
{
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetInputFilePlaying(false);
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::PlayFileEnded() => input file player module is"
|
|
|
|
|
" shutdown");
|
|
|
|
|
}
|
|
|
|
|
else if (id == _outputFilePlayerId)
|
|
|
|
|
{
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetOutputFilePlaying(false);
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::PlayFileEnded() => output file player module is"
|
|
|
|
|
" shutdown");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::RecordFileEnded(int32_t id)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::RecordFileEnded(id=%d)", id);
|
|
|
|
|
|
|
|
|
|
assert(id == _outputFileRecorderId);
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
_outputFileRecording = false;
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::RecordFileEnded() => output file recorder module is"
|
|
|
|
|
" shutdown");
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::Channel(int32_t channelId,
|
2013-09-12 17:03:00 +00:00
|
|
|
uint32_t instanceId,
|
|
|
|
|
const Config& config) :
|
2011-07-07 08:21:25 +00:00
|
|
|
_fileCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
|
|
|
|
|
_callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
|
2013-10-17 18:28:55 +00:00
|
|
|
volume_settings_critsect_(*CriticalSectionWrapper::CreateCriticalSection()),
|
2011-07-07 08:21:25 +00:00
|
|
|
_instanceId(instanceId),
|
2011-08-03 12:40:23 +00:00
|
|
|
_channelId(channelId),
|
2013-05-29 12:12:51 +00:00
|
|
|
rtp_header_parser_(RtpHeaderParser::Create()),
|
2013-08-15 23:38:54 +00:00
|
|
|
rtp_payload_registry_(
|
2014-04-08 11:06:12 +00:00
|
|
|
new RTPPayloadRegistry(RTPPayloadStrategy::CreateStrategy(true))),
|
2013-08-15 23:38:54 +00:00
|
|
|
rtp_receive_statistics_(ReceiveStatistics::Create(
|
|
|
|
|
Clock::GetRealTimeClock())),
|
|
|
|
|
rtp_receiver_(RtpReceiver::CreateAudioReceiver(
|
|
|
|
|
VoEModuleId(instanceId, channelId), Clock::GetRealTimeClock(), this,
|
|
|
|
|
this, this, rtp_payload_registry_.get())),
|
|
|
|
|
telephone_event_handler_(rtp_receiver_->GetTelephoneEventHandler()),
|
2014-04-22 19:04:34 +00:00
|
|
|
audio_coding_(AudioCodingModule::Create(
|
2011-08-03 12:40:23 +00:00
|
|
|
VoEModuleId(instanceId, channelId))),
|
2011-07-07 08:21:25 +00:00
|
|
|
_rtpDumpIn(*RtpDump::CreateRtpDump()),
|
|
|
|
|
_rtpDumpOut(*RtpDump::CreateRtpDump()),
|
|
|
|
|
_outputAudioLevel(),
|
|
|
|
|
_externalTransport(false),
|
2013-12-19 13:26:02 +00:00
|
|
|
_audioLevel_dBov(0),
|
2011-07-07 08:21:25 +00:00
|
|
|
_inputFilePlayerPtr(NULL),
|
|
|
|
|
_outputFilePlayerPtr(NULL),
|
|
|
|
|
_outputFileRecorderPtr(NULL),
|
|
|
|
|
// 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),
|
2011-08-03 12:40:23 +00:00
|
|
|
_inbandDtmfQueue(VoEModuleId(instanceId, channelId)),
|
|
|
|
|
_inbandDtmfGenerator(VoEModuleId(instanceId, channelId)),
|
|
|
|
|
_outputExternalMedia(false),
|
2011-07-07 08:21:25 +00:00
|
|
|
_inputExternalMediaCallbackPtr(NULL),
|
|
|
|
|
_outputExternalMediaCallbackPtr(NULL),
|
2011-08-03 12:40:23 +00:00
|
|
|
_timeStamp(0), // This is just an offset, RTP module will add it's own random offset
|
|
|
|
|
_sendTelephoneEventPayloadType(106),
|
2014-05-20 22:55:01 +00:00
|
|
|
ntp_estimator_(new RemoteNtpTimeEstimator(Clock::GetRealTimeClock())),
|
2013-12-13 21:05:07 +00:00
|
|
|
jitter_buffer_playout_timestamp_(0),
|
2013-04-11 20:23:35 +00:00
|
|
|
playout_timestamp_rtp_(0),
|
|
|
|
|
playout_timestamp_rtcp_(0),
|
2013-12-19 13:26:02 +00:00
|
|
|
playout_delay_ms_(0),
|
2011-08-03 12:40:23 +00:00
|
|
|
_numberOfDiscardedPackets(0),
|
Merge r4374 from stable to trunk.
r4374 was mistakenly committed to stable, so this is to re-merge back to trunk.
Store the sequence number in StopSend() and resume it in StartSend().
When restarting the microphone device, we call StopSend() first, then
StartSend() later. Since we reset sequence number in StopSend(), it sometimes
causes libSRTP to complain about packets being replayed. Libjingle work around
it by caching the sequence number in WebRtcVoiceEngine.cc, and call
SetInitSequenceNumber() to resume the sequence number before StartSend().Store the sequence number in StopSend() and resume it in StartSend().
When restarting the microphone device, we call StopSend() first, then
StartSend() later. Since we reset sequence number in StopSend(), it sometimes
causes libSRTP to complain about packets being replayed. Libjingle work around
it by caching the sequence number in WebRtcVoiceEngine.cc, and call
SetInitSequenceNumber() to resume the sequence number before StartSend().
This patch fixes this problem by storing the sequence number in StopSend(), and
resume it in StartSend(). So that we can remove the workaround in libjingle.
BUG=2102
R=tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/1922004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4451 4adac7df-926f-26a2-2b94-8c16560cd09d
2013-07-31 16:30:19 +00:00
|
|
|
send_sequence_number_(0),
|
2014-05-19 17:39:11 +00:00
|
|
|
ts_stats_lock_(CriticalSectionWrapper::CreateCriticalSection()),
|
|
|
|
|
first_frame_arrived_(false),
|
|
|
|
|
capture_start_rtp_time_stamp_(0),
|
|
|
|
|
capture_start_ntp_time_ms_(-1),
|
2011-08-03 12:40:23 +00:00
|
|
|
_engineStatisticsPtr(NULL),
|
2012-01-31 08:45:03 +00:00
|
|
|
_outputMixerPtr(NULL),
|
|
|
|
|
_transmitMixerPtr(NULL),
|
2011-08-03 12:40:23 +00:00
|
|
|
_moduleProcessThreadPtr(NULL),
|
|
|
|
|
_audioDeviceModulePtr(NULL),
|
|
|
|
|
_voiceEngineObserverPtr(NULL),
|
|
|
|
|
_callbackCritSectPtr(NULL),
|
|
|
|
|
_transportPtr(NULL),
|
|
|
|
|
_rxVadObserverPtr(NULL),
|
|
|
|
|
_oldVadDecision(-1),
|
|
|
|
|
_sendFrameType(0),
|
2011-07-07 08:21:25 +00:00
|
|
|
_rtcpObserverPtr(NULL),
|
2011-08-03 12:40:23 +00:00
|
|
|
_externalPlayout(false),
|
2012-12-12 23:00:29 +00:00
|
|
|
_externalMixing(false),
|
2011-08-03 12:40:23 +00:00
|
|
|
_mixFileWithMicrophone(false),
|
|
|
|
|
_rtcpObserver(false),
|
2011-07-07 08:21:25 +00:00
|
|
|
_mute(false),
|
|
|
|
|
_panLeft(1.0f),
|
|
|
|
|
_panRight(1.0f),
|
|
|
|
|
_outputGain(1.0f),
|
|
|
|
|
_playOutbandDtmfEvent(false),
|
|
|
|
|
_playInbandDtmfEvent(false),
|
|
|
|
|
_lastLocalTimeStamp(0),
|
|
|
|
|
_lastPayloadType(0),
|
2011-08-03 12:40:23 +00:00
|
|
|
_includeAudioLevelIndication(false),
|
2011-07-07 08:21:25 +00:00
|
|
|
_rtpPacketTimedOut(false),
|
|
|
|
|
_rtpPacketTimeOutIsEnabled(false),
|
|
|
|
|
_rtpTimeOutSeconds(0),
|
|
|
|
|
_connectionObserver(false),
|
|
|
|
|
_connectionObserverPtr(NULL),
|
|
|
|
|
_outputSpeechType(AudioFrame::kNormalSpeech),
|
2014-03-24 10:38:25 +00:00
|
|
|
vie_network_(NULL),
|
|
|
|
|
video_channel_(-1),
|
2013-04-11 20:23:35 +00:00
|
|
|
_average_jitter_buffer_delay_us(0),
|
2013-05-22 20:39:43 +00:00
|
|
|
least_required_delay_ms_(0),
|
2011-07-07 08:21:25 +00:00
|
|
|
_previousTimestamp(0),
|
|
|
|
|
_recPacketDelayMs(20),
|
|
|
|
|
_RxVadDetection(false),
|
|
|
|
|
_rxAgcIsEnabled(false),
|
2013-09-06 13:40:11 +00:00
|
|
|
_rxNsIsEnabled(false),
|
2014-05-28 09:52:06 +00:00
|
|
|
restored_packet_in_use_(false),
|
|
|
|
|
bitrate_controller_(
|
|
|
|
|
BitrateController::CreateBitrateController(Clock::GetRealTimeClock(),
|
|
|
|
|
true)),
|
|
|
|
|
rtcp_bandwidth_observer_(
|
|
|
|
|
bitrate_controller_->CreateRtcpBandwidthObserver()),
|
|
|
|
|
send_bitrate_observer_(new VoEBitrateObserver(this))
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::Channel() - ctor");
|
|
|
|
|
_inbandDtmfQueue.ResetDtmf();
|
|
|
|
|
_inbandDtmfGenerator.Init();
|
|
|
|
|
_outputAudioLevel.Clear();
|
|
|
|
|
|
2012-05-11 11:08:54 +00:00
|
|
|
RtpRtcp::Configuration configuration;
|
|
|
|
|
configuration.id = VoEModuleId(instanceId, channelId);
|
|
|
|
|
configuration.audio = true;
|
|
|
|
|
configuration.outgoing_transport = this;
|
|
|
|
|
configuration.rtcp_feedback = this;
|
|
|
|
|
configuration.audio_messages = this;
|
2013-08-15 23:38:54 +00:00
|
|
|
configuration.receive_statistics = rtp_receive_statistics_.get();
|
2014-05-28 09:52:06 +00:00
|
|
|
configuration.bandwidth_callback = rtcp_bandwidth_observer_.get();
|
2012-05-11 11:08:54 +00:00
|
|
|
|
|
|
|
|
_rtpRtcpModule.reset(RtpRtcp::CreateRtpRtcp(configuration));
|
2013-12-19 13:26:02 +00:00
|
|
|
|
|
|
|
|
statistics_proxy_.reset(new StatisticsProxy(_rtpRtcpModule->SSRC()));
|
|
|
|
|
rtp_receive_statistics_->RegisterRtcpStatisticsCallback(
|
|
|
|
|
statistics_proxy_.get());
|
2014-04-16 11:58:18 +00:00
|
|
|
|
|
|
|
|
Config audioproc_config;
|
|
|
|
|
audioproc_config.Set<ExperimentalAgc>(new ExperimentalAgc(false));
|
|
|
|
|
rx_audioproc_.reset(AudioProcessing::Create(audioproc_config));
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Channel::~Channel()
|
|
|
|
|
{
|
2013-12-19 13:26:02 +00:00
|
|
|
rtp_receive_statistics_->RegisterRtcpStatisticsCallback(NULL);
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::~Channel() - dtor");
|
|
|
|
|
|
|
|
|
|
if (_outputExternalMedia)
|
|
|
|
|
{
|
|
|
|
|
DeRegisterExternalMediaProcessing(kPlaybackPerChannel);
|
|
|
|
|
}
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().input_external_media)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
DeRegisterExternalMediaProcessing(kRecordingPerChannel);
|
|
|
|
|
}
|
|
|
|
|
StopSend();
|
|
|
|
|
StopPlayout();
|
|
|
|
|
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_inputFilePlayerPtr)
|
|
|
|
|
{
|
|
|
|
|
_inputFilePlayerPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
_inputFilePlayerPtr->StopPlayingFile();
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_inputFilePlayerPtr);
|
|
|
|
|
_inputFilePlayerPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (_outputFilePlayerPtr)
|
|
|
|
|
{
|
|
|
|
|
_outputFilePlayerPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
_outputFilePlayerPtr->StopPlayingFile();
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_outputFilePlayerPtr);
|
|
|
|
|
_outputFilePlayerPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (_outputFileRecorderPtr)
|
|
|
|
|
{
|
|
|
|
|
_outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
_outputFileRecorderPtr->StopRecording();
|
|
|
|
|
FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
|
|
|
|
_outputFileRecorderPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterTransportCallback(NULL) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"~Channel() failed to de-register transport callback"
|
|
|
|
|
" (Audio coding module)");
|
|
|
|
|
}
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterVADCallback(NULL) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"~Channel() failed to de-register VAD callback"
|
|
|
|
|
" (Audio coding module)");
|
|
|
|
|
}
|
|
|
|
|
// De-register modules in process thread
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_moduleProcessThreadPtr->DeRegisterModule(_rtpRtcpModule.get()) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"~Channel() failed to deregister RTP/RTCP module");
|
|
|
|
|
}
|
|
|
|
|
// End of modules shutdown
|
|
|
|
|
|
|
|
|
|
// Delete other objects
|
2014-03-24 10:38:25 +00:00
|
|
|
if (vie_network_) {
|
|
|
|
|
vie_network_->Release();
|
|
|
|
|
vie_network_ = NULL;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
RtpDump::DestroyRtpDump(&_rtpDumpIn);
|
|
|
|
|
RtpDump::DestroyRtpDump(&_rtpDumpOut);
|
|
|
|
|
delete &_callbackCritSect;
|
|
|
|
|
delete &_fileCritSect;
|
2013-10-17 18:28:55 +00:00
|
|
|
delete &volume_settings_critsect_;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::Init()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::Init()");
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.Reset();
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
// --- 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)
|
|
|
|
|
|
|
|
|
|
const bool processThreadFail =
|
2012-05-11 11:08:54 +00:00
|
|
|
((_moduleProcessThreadPtr->RegisterModule(_rtpRtcpModule.get()) != 0) ||
|
2011-07-07 08:21:25 +00:00
|
|
|
false);
|
|
|
|
|
if (processThreadFail)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_CANNOT_INIT_CHANNEL, kTraceError,
|
|
|
|
|
"Channel::Init() modules not registered");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2012-01-04 15:00:12 +00:00
|
|
|
// --- ACM initialization
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-09-23 23:02:24 +00:00
|
|
|
if ((audio_coding_->InitializeReceiver() == -1) ||
|
2011-07-07 08:21:25 +00:00
|
|
|
#ifdef WEBRTC_CODEC_AVT
|
|
|
|
|
// out-of-band Dtmf tones are played out by default
|
2013-09-23 23:02:24 +00:00
|
|
|
(audio_coding_->SetDtmfPlayoutStatus(true) == -1) ||
|
2011-07-07 08:21:25 +00:00
|
|
|
#endif
|
2013-09-23 23:02:24 +00:00
|
|
|
(audio_coding_->InitializeSender() == -1))
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_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.
|
2013-08-15 23:38:54 +00:00
|
|
|
telephone_event_handler_->SetTelephoneEventForwardToDecoder(true);
|
|
|
|
|
// RTCP is enabled by default.
|
|
|
|
|
if (_rtpRtcpModule->SetRTCPStatus(kRtcpCompound) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"Channel::Init() RTP/RTCP module not initialized");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Register all permanent callbacks
|
|
|
|
|
const bool fail =
|
2013-09-23 23:02:24 +00:00
|
|
|
(audio_coding_->RegisterTransportCallback(this) == -1) ||
|
|
|
|
|
(audio_coding_->RegisterVADCallback(this) == -1);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (fail)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_CANNOT_INIT_CHANNEL, kTraceError,
|
|
|
|
|
"Channel::Init() callbacks not registered");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Register all supported codecs to the receiving side of the
|
|
|
|
|
// RTP/RTCP module
|
|
|
|
|
|
|
|
|
|
CodecInst codec;
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint8_t nSupportedCodecs = AudioCodingModule::NumberOfCodecs();
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
for (int idx = 0; idx < nSupportedCodecs; idx++)
|
|
|
|
|
{
|
|
|
|
|
// Open up the RTP/RTCP receiver for all supported codecs
|
2013-09-23 23:02:24 +00:00
|
|
|
if ((audio_coding_->Codec(idx, &codec) == -1) ||
|
2013-08-15 23:38:54 +00:00
|
|
|
(rtp_receiver_->RegisterReceivePayload(
|
|
|
|
|
codec.plname,
|
|
|
|
|
codec.pltype,
|
|
|
|
|
codec.plfreq,
|
|
|
|
|
codec.channels,
|
|
|
|
|
(codec.rate < 0) ? 0 : codec.rate) == -1))
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::Init() unable to register %s (%d/%d/%d/%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/%d/%d) has been added to "
|
|
|
|
|
"the RTP/RTCP receiver",
|
|
|
|
|
codec.plname, codec.pltype, codec.plfreq,
|
|
|
|
|
codec.channels, codec.rate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure that PCMU is used as default codec on the sending side
|
2012-06-01 09:27:35 +00:00
|
|
|
if (!STR_CASE_CMP(codec.plname, "PCMU") && (codec.channels == 1))
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
SetSendCodec(codec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Register default PT for outband 'telephone-event'
|
|
|
|
|
if (!STR_CASE_CMP(codec.plname, "telephone-event"))
|
|
|
|
|
{
|
2012-05-11 11:08:54 +00:00
|
|
|
if ((_rtpRtcpModule->RegisterSendPayload(codec) == -1) ||
|
2013-09-23 23:02:24 +00:00
|
|
|
(audio_coding_->RegisterReceiveCodec(codec) == -1))
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
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"))
|
|
|
|
|
{
|
2013-09-23 23:02:24 +00:00
|
|
|
if ((audio_coding_->RegisterSendCodec(codec) == -1) ||
|
|
|
|
|
(audio_coding_->RegisterReceiveCodec(codec) == -1) ||
|
2012-05-11 11:08:54 +00:00
|
|
|
(_rtpRtcpModule->RegisterSendPayload(codec) == -1))
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::Init() failed to register CN (%d/%d) "
|
|
|
|
|
"correctly - 1",
|
|
|
|
|
codec.pltype, codec.plfreq);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#ifdef WEBRTC_CODEC_RED
|
|
|
|
|
// Register RED to the receiving side of the ACM.
|
|
|
|
|
// We will not receive an OnInitializeDecoder() callback for RED.
|
|
|
|
|
if (!STR_CASE_CMP(codec.plname, "RED"))
|
|
|
|
|
{
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterReceiveCodec(codec) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::Init() failed to register RED (%d/%d) "
|
|
|
|
|
"correctly",
|
|
|
|
|
codec.pltype, codec.plfreq);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2013-03-13 23:20:57 +00:00
|
|
|
|
2013-10-04 17:54:09 +00:00
|
|
|
if (rx_audioproc_->noise_suppression()->set_level(kDefaultNsMode) != 0) {
|
|
|
|
|
LOG_FERR1(LS_ERROR, noise_suppression()->set_level, kDefaultNsMode);
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2013-10-04 17:54:09 +00:00
|
|
|
if (rx_audioproc_->gain_control()->set_mode(kDefaultRxAgcMode) != 0) {
|
|
|
|
|
LOG_FERR1(LS_ERROR, gain_control()->set_mode, kDefaultRxAgcMode);
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::SetEngineInformation(Statistics& engineStatistics,
|
|
|
|
|
OutputMixer& outputMixer,
|
|
|
|
|
voe::TransmitMixer& transmitMixer,
|
|
|
|
|
ProcessThread& moduleProcessThread,
|
|
|
|
|
AudioDeviceModule& audioDeviceModule,
|
|
|
|
|
VoiceEngineObserver* voiceEngineObserver,
|
|
|
|
|
CriticalSectionWrapper* callbackCritSect)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetEngineInformation()");
|
|
|
|
|
_engineStatisticsPtr = &engineStatistics;
|
|
|
|
|
_outputMixerPtr = &outputMixer;
|
|
|
|
|
_transmitMixerPtr = &transmitMixer,
|
|
|
|
|
_moduleProcessThreadPtr = &moduleProcessThread;
|
|
|
|
|
_audioDeviceModulePtr = &audioDeviceModule;
|
|
|
|
|
_voiceEngineObserverPtr = voiceEngineObserver;
|
|
|
|
|
_callbackCritSectPtr = callbackCritSect;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::UpdateLocalTimeStamp()
|
|
|
|
|
{
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
_timeStamp += _audioFrame.samples_per_channel_;
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::StartPlayout()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::StartPlayout()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-12-12 23:00:29 +00:00
|
|
|
|
|
|
|
|
if (!_externalMixing) {
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetPlaying(true);
|
2012-06-04 03:26:39 +00:00
|
|
|
if (RegisterFilePlayingToMixer() != 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::StopPlayout()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::StopPlayout()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (!channel_state_.Get().playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-12-12 23:00:29 +00:00
|
|
|
|
|
|
|
|
if (!_externalMixing) {
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetPlaying(false);
|
2011-07-07 08:21:25 +00:00
|
|
|
_outputAudioLevel.Clear();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::StartSend()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::StartSend()");
|
Merge r4374 from stable to trunk.
r4374 was mistakenly committed to stable, so this is to re-merge back to trunk.
Store the sequence number in StopSend() and resume it in StartSend().
When restarting the microphone device, we call StopSend() first, then
StartSend() later. Since we reset sequence number in StopSend(), it sometimes
causes libSRTP to complain about packets being replayed. Libjingle work around
it by caching the sequence number in WebRtcVoiceEngine.cc, and call
SetInitSequenceNumber() to resume the sequence number before StartSend().Store the sequence number in StopSend() and resume it in StartSend().
When restarting the microphone device, we call StopSend() first, then
StartSend() later. Since we reset sequence number in StopSend(), it sometimes
causes libSRTP to complain about packets being replayed. Libjingle work around
it by caching the sequence number in WebRtcVoiceEngine.cc, and call
SetInitSequenceNumber() to resume the sequence number before StartSend().
This patch fixes this problem by storing the sequence number in StopSend(), and
resume it in StartSend(). So that we can remove the workaround in libjingle.
BUG=2102
R=tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/1922004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4451 4adac7df-926f-26a2-2b94-8c16560cd09d
2013-07-31 16:30:19 +00:00
|
|
|
// Resume the previous sequence number which was reset by StopSend().
|
2014-03-18 10:32:33 +00:00
|
|
|
// This needs to be done before |sending| is set to true.
|
Merge r4374 from stable to trunk.
r4374 was mistakenly committed to stable, so this is to re-merge back to trunk.
Store the sequence number in StopSend() and resume it in StartSend().
When restarting the microphone device, we call StopSend() first, then
StartSend() later. Since we reset sequence number in StopSend(), it sometimes
causes libSRTP to complain about packets being replayed. Libjingle work around
it by caching the sequence number in WebRtcVoiceEngine.cc, and call
SetInitSequenceNumber() to resume the sequence number before StartSend().Store the sequence number in StopSend() and resume it in StartSend().
When restarting the microphone device, we call StopSend() first, then
StartSend() later. Since we reset sequence number in StopSend(), it sometimes
causes libSRTP to complain about packets being replayed. Libjingle work around
it by caching the sequence number in WebRtcVoiceEngine.cc, and call
SetInitSequenceNumber() to resume the sequence number before StartSend().
This patch fixes this problem by storing the sequence number in StopSend(), and
resume it in StartSend(). So that we can remove the workaround in libjingle.
BUG=2102
R=tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/1922004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4451 4adac7df-926f-26a2-2b94-8c16560cd09d
2013-07-31 16:30:19 +00:00
|
|
|
if (send_sequence_number_)
|
|
|
|
|
SetInitSequenceNumber(send_sequence_number_);
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().sending)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2014-03-18 10:32:33 +00:00
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetSending(true);
|
2011-11-28 16:31:28 +00:00
|
|
|
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SetSendingStatus(true) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"StartSend() RTP/RTCP failed to start sending");
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetSending(false);
|
2011-07-07 08:21:25 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-11-28 16:31:28 +00:00
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::StopSend()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::StopSend()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (!channel_state_.Get().sending)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2014-03-18 10:32:33 +00:00
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetSending(false);
|
2011-11-28 16:31:28 +00:00
|
|
|
|
Merge r4374 from stable to trunk.
r4374 was mistakenly committed to stable, so this is to re-merge back to trunk.
Store the sequence number in StopSend() and resume it in StartSend().
When restarting the microphone device, we call StopSend() first, then
StartSend() later. Since we reset sequence number in StopSend(), it sometimes
causes libSRTP to complain about packets being replayed. Libjingle work around
it by caching the sequence number in WebRtcVoiceEngine.cc, and call
SetInitSequenceNumber() to resume the sequence number before StartSend().Store the sequence number in StopSend() and resume it in StartSend().
When restarting the microphone device, we call StopSend() first, then
StartSend() later. Since we reset sequence number in StopSend(), it sometimes
causes libSRTP to complain about packets being replayed. Libjingle work around
it by caching the sequence number in WebRtcVoiceEngine.cc, and call
SetInitSequenceNumber() to resume the sequence number before StartSend().
This patch fixes this problem by storing the sequence number in StopSend(), and
resume it in StartSend(). So that we can remove the workaround in libjingle.
BUG=2102
R=tommi@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/1922004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@4451 4adac7df-926f-26a2-2b94-8c16560cd09d
2013-07-31 16:30:19 +00:00
|
|
|
// 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();
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
// Reset sending SSRC and sequence number and triggers direct transmission
|
|
|
|
|
// of RTCP BYE
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SetSendingStatus(false) == -1 ||
|
|
|
|
|
_rtpRtcpModule->ResetSendDataCountersRTP() == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceWarning,
|
|
|
|
|
"StartSend() RTP/RTCP failed to stop sending");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::StartReceiving()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::StartReceiving()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().receiving)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetReceiving(true);
|
2011-07-07 08:21:25 +00:00
|
|
|
_numberOfDiscardedPackets = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::StopReceiving()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::StopReceiving()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (!channel_state_.Get().receiving)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-03-13 23:20:57 +00:00
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetReceiving(false);
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::SetNetEQPlayoutMode(NetEqModes mode)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetNetEQPlayoutMode()");
|
|
|
|
|
AudioPlayoutMode playoutMode(voice);
|
|
|
|
|
switch (mode)
|
|
|
|
|
{
|
|
|
|
|
case kNetEqDefault:
|
|
|
|
|
playoutMode = voice;
|
|
|
|
|
break;
|
|
|
|
|
case kNetEqStreaming:
|
|
|
|
|
playoutMode = streaming;
|
|
|
|
|
break;
|
|
|
|
|
case kNetEqFax:
|
|
|
|
|
playoutMode = fax;
|
|
|
|
|
break;
|
2012-12-12 21:59:14 +00:00
|
|
|
case kNetEqOff:
|
|
|
|
|
playoutMode = off;
|
|
|
|
|
break;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->SetPlayoutMode(playoutMode) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetNetEQPlayoutMode() failed to set playout mode");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::GetNetEQPlayoutMode(NetEqModes& mode)
|
|
|
|
|
{
|
2013-09-23 23:02:24 +00:00
|
|
|
const AudioPlayoutMode playoutMode = audio_coding_->PlayoutMode();
|
2011-07-07 08:21:25 +00:00
|
|
|
switch (playoutMode)
|
|
|
|
|
{
|
|
|
|
|
case voice:
|
|
|
|
|
mode = kNetEqDefault;
|
|
|
|
|
break;
|
|
|
|
|
case streaming:
|
|
|
|
|
mode = kNetEqStreaming;
|
|
|
|
|
break;
|
|
|
|
|
case fax:
|
|
|
|
|
mode = kNetEqFax;
|
|
|
|
|
break;
|
2012-12-12 21:59:14 +00:00
|
|
|
case off:
|
|
|
|
|
mode = kNetEqOff;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetNetEQPlayoutMode() => mode=%u", mode);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::RegisterVoiceEngineObserver()");
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_voiceEngineObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceError,
|
|
|
|
|
"RegisterVoiceEngineObserver() observer already enabled");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_voiceEngineObserverPtr = &observer;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::DeRegisterVoiceEngineObserver()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::DeRegisterVoiceEngineObserver()");
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (!_voiceEngineObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceWarning,
|
|
|
|
|
"DeRegisterVoiceEngineObserver() observer already disabled");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
_voiceEngineObserverPtr = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::GetSendCodec(CodecInst& codec)
|
|
|
|
|
{
|
2013-09-23 23:02:24 +00:00
|
|
|
return (audio_coding_->SendCodec(&codec));
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::GetRecCodec(CodecInst& codec)
|
|
|
|
|
{
|
2013-09-23 23:02:24 +00:00
|
|
|
return (audio_coding_->ReceiveCodec(&codec));
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::SetSendCodec(const CodecInst& codec)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetSendCodec()");
|
|
|
|
|
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterSendCodec(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"SetSendCodec() failed to register codec to ACM");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->RegisterSendPayload(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2012-05-11 11:08:54 +00:00
|
|
|
_rtpRtcpModule->DeRegisterSendPayload(codec.pltype);
|
|
|
|
|
if (_rtpRtcpModule->RegisterSendPayload(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(
|
|
|
|
|
kTraceError, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"SetSendCodec() failed to register codec to"
|
|
|
|
|
" RTP/RTCP module");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SetAudioPacketSize(codec.pacsize) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"SetSendCodec() failed to set audio packet size");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-28 09:52:06 +00:00
|
|
|
bitrate_controller_->SetBitrateObserver(send_bitrate_observer_.get(),
|
|
|
|
|
codec.rate, 0, 0);
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-28 09:52:06 +00:00
|
|
|
void
|
|
|
|
|
Channel::OnNetworkChanged(const uint32_t bitrate_bps,
|
|
|
|
|
const uint8_t fraction_lost, // 0 - 255.
|
|
|
|
|
const uint32_t rtt) {
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::OnNetworkChanged(bitrate_bps=%d, fration_lost=%d, rtt=%d)",
|
|
|
|
|
bitrate_bps, fraction_lost, rtt);
|
|
|
|
|
// Normalizes rate to 0 - 100.
|
|
|
|
|
if (audio_coding_->SetPacketLossRate(100 * fraction_lost / 255) != 0) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(VE_AUDIO_CODING_MODULE_ERROR,
|
|
|
|
|
kTraceError, "OnNetworkChanged() failed to set packet loss rate");
|
|
|
|
|
assert(false); // This should not happen.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::SetVADStatus(bool enableVAD, ACMVADMode mode, bool disableDTX)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetVADStatus(mode=%d)", mode);
|
|
|
|
|
// To disable VAD, DTX must be disabled too
|
|
|
|
|
disableDTX = ((enableVAD == false) ? true : disableDTX);
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->SetVAD(!disableDTX, enableVAD, mode) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetVADStatus() failed to set VAD");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::GetVADStatus(bool& enabledVAD, ACMVADMode& mode, bool& disabledDTX)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetVADStatus");
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->VAD(&disabledDTX, &enabledVAD, &mode) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"GetVADStatus() failed to get VAD status");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
disabledDTX = !disabledDTX;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::SetRecPayloadType(const CodecInst& codec)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetRecPayloadType()");
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_ALREADY_PLAYING, kTraceError,
|
|
|
|
|
"SetRecPayloadType() unable to set PT while playing");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().receiving)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_ALREADY_LISTENING, kTraceError,
|
|
|
|
|
"SetRecPayloadType() unable to set PT while listening");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (codec.pltype == -1)
|
|
|
|
|
{
|
|
|
|
|
// De-register the selected codec (RTP/RTCP module and ACM)
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int8_t pltype(-1);
|
2011-07-07 08:21:25 +00:00
|
|
|
CodecInst rxCodec = codec;
|
|
|
|
|
|
|
|
|
|
// Get payload type for the given codec
|
2013-08-15 23:38:54 +00:00
|
|
|
rtp_payload_registry_->ReceivePayloadType(
|
|
|
|
|
rxCodec.plname,
|
|
|
|
|
rxCodec.plfreq,
|
|
|
|
|
rxCodec.channels,
|
|
|
|
|
(rxCodec.rate < 0) ? 0 : rxCodec.rate,
|
|
|
|
|
&pltype);
|
2011-07-07 08:21:25 +00:00
|
|
|
rxCodec.pltype = pltype;
|
|
|
|
|
|
2013-08-15 23:38:54 +00:00
|
|
|
if (rtp_receiver_->DeRegisterReceivePayload(pltype) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR,
|
|
|
|
|
kTraceError,
|
|
|
|
|
"SetRecPayloadType() RTP/RTCP-module deregistration "
|
|
|
|
|
"failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->UnregisterReceiveCodec(rxCodec.pltype) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetRecPayloadType() ACM deregistration failed - 1");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-15 23:38:54 +00:00
|
|
|
if (rtp_receiver_->RegisterReceivePayload(
|
|
|
|
|
codec.plname,
|
|
|
|
|
codec.pltype,
|
|
|
|
|
codec.plfreq,
|
|
|
|
|
codec.channels,
|
|
|
|
|
(codec.rate < 0) ? 0 : codec.rate) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
// First attempt to register failed => de-register and try again
|
2013-08-15 23:38:54 +00:00
|
|
|
rtp_receiver_->DeRegisterReceivePayload(codec.pltype);
|
|
|
|
|
if (rtp_receiver_->RegisterReceivePayload(
|
|
|
|
|
codec.plname,
|
|
|
|
|
codec.pltype,
|
|
|
|
|
codec.plfreq,
|
|
|
|
|
codec.channels,
|
|
|
|
|
(codec.rate < 0) ? 0 : codec.rate) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetRecPayloadType() RTP/RTCP-module registration failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterReceiveCodec(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2013-09-23 23:02:24 +00:00
|
|
|
audio_coding_->UnregisterReceiveCodec(codec.pltype);
|
|
|
|
|
if (audio_coding_->RegisterReceiveCodec(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetRecPayloadType() ACM registration failed - 1");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::GetRecPayloadType(CodecInst& codec)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetRecPayloadType()");
|
2013-04-09 10:09:10 +00:00
|
|
|
int8_t payloadType(-1);
|
2013-08-15 23:38:54 +00:00
|
|
|
if (rtp_payload_registry_->ReceivePayloadType(
|
|
|
|
|
codec.plname,
|
|
|
|
|
codec.plfreq,
|
|
|
|
|
codec.channels,
|
|
|
|
|
(codec.rate < 0) ? 0 : codec.rate,
|
|
|
|
|
&payloadType) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
2012-06-18 11:00:12 +00:00
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceWarning,
|
2011-07-07 08:21:25 +00:00
|
|
|
"GetRecPayloadType() failed to retrieve RX payload type");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
codec.pltype = payloadType;
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetRecPayloadType() => pltype=%u", codec.pltype);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::SetSendCNPayloadType(int type, PayloadFrequencies frequency)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetSendCNPayloadType()");
|
|
|
|
|
|
|
|
|
|
CodecInst codec;
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t samplingFreqHz(-1);
|
2012-06-01 09:27:35 +00:00
|
|
|
const int kMono = 1;
|
2011-07-07 08:21:25 +00:00
|
|
|
if (frequency == kFreq32000Hz)
|
|
|
|
|
samplingFreqHz = 32000;
|
|
|
|
|
else if (frequency == kFreq16000Hz)
|
|
|
|
|
samplingFreqHz = 16000;
|
|
|
|
|
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->Codec("CN", &codec, samplingFreqHz, kMono) == -1)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_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;
|
|
|
|
|
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterSendCodec(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetSendCNPayloadType() failed to register CN to ACM");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->RegisterSendPayload(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2012-05-11 11:08:54 +00:00
|
|
|
_rtpRtcpModule->DeRegisterSendPayload(codec.pltype);
|
|
|
|
|
if (_rtpRtcpModule->RegisterSendPayload(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetSendCNPayloadType() failed to register CN to RTP/RTCP "
|
|
|
|
|
"module");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t Channel::RegisterExternalTransport(Transport& transport)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::RegisterExternalTransport()");
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_externalTransport)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(VE_INVALID_OPERATION,
|
|
|
|
|
kTraceError,
|
|
|
|
|
"RegisterExternalTransport() external transport already enabled");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_externalTransport = true;
|
|
|
|
|
_transportPtr = &transport;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::DeRegisterExternalTransport()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::DeRegisterExternalTransport()");
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-11-25 10:58:15 +00:00
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
if (!_transportPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceWarning,
|
|
|
|
|
"DeRegisterExternalTransport() external transport already "
|
|
|
|
|
"disabled");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
_externalTransport = false;
|
|
|
|
|
_transportPtr = NULL;
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"DeRegisterExternalTransport() all transport is disabled");
|
2013-03-13 23:20:57 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-24 10:38:25 +00:00
|
|
|
int32_t Channel::ReceivedRTPPacket(const int8_t* data, int32_t length,
|
|
|
|
|
const PacketTime& packet_time) {
|
2013-04-03 15:43:57 +00:00
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::ReceivedRTPPacket()");
|
2013-03-13 23:20:57 +00:00
|
|
|
|
2013-04-03 15:43:57 +00:00
|
|
|
// Store playout timestamp for the received RTP packet
|
2013-04-11 20:23:35 +00:00
|
|
|
UpdatePlayoutTimestamp(false);
|
2013-03-13 23:20:57 +00:00
|
|
|
|
2013-04-03 15:43:57 +00:00
|
|
|
// Dump the RTP packet to a file (if RTP dump is enabled).
|
2013-04-09 10:09:10 +00:00
|
|
|
if (_rtpDumpIn.DumpPacket((const uint8_t*)data,
|
|
|
|
|
(uint16_t)length) == -1) {
|
2013-04-03 15:43:57 +00:00
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendPacket() RTP dump to input file failed");
|
|
|
|
|
}
|
2013-09-06 13:40:11 +00:00
|
|
|
const uint8_t* received_packet = reinterpret_cast<const uint8_t*>(data);
|
2013-05-29 12:12:51 +00:00
|
|
|
RTPHeader header;
|
2013-09-06 13:40:11 +00:00
|
|
|
if (!rtp_header_parser_->Parse(received_packet, length, &header)) {
|
|
|
|
|
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId,
|
|
|
|
|
"Incoming packet: invalid RTP header");
|
2013-05-29 12:12:51 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2013-08-15 23:38:54 +00:00
|
|
|
header.payload_type_frequency =
|
|
|
|
|
rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType);
|
2013-09-06 13:40:11 +00:00
|
|
|
if (header.payload_type_frequency < 0)
|
2013-08-15 23:38:54 +00:00
|
|
|
return -1;
|
2013-11-08 15:18:52 +00:00
|
|
|
bool in_order = IsPacketInOrder(header);
|
2013-09-06 13:40:11 +00:00
|
|
|
rtp_receive_statistics_->IncomingPacket(header, length,
|
2013-11-08 15:18:52 +00:00
|
|
|
IsPacketRetransmitted(header, in_order));
|
2013-09-06 13:40:11 +00:00
|
|
|
rtp_payload_registry_->SetIncomingPayloadType(header);
|
2014-03-24 10:38:25 +00:00
|
|
|
|
|
|
|
|
// Forward any packets to ViE bandwidth estimator, if enabled.
|
|
|
|
|
{
|
|
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
|
|
|
|
if (vie_network_) {
|
|
|
|
|
int64_t arrival_time_ms;
|
|
|
|
|
if (packet_time.timestamp != -1) {
|
|
|
|
|
arrival_time_ms = (packet_time.timestamp + 500) / 1000;
|
|
|
|
|
} else {
|
|
|
|
|
arrival_time_ms = TickTime::MillisecondTimestamp();
|
|
|
|
|
}
|
|
|
|
|
int payload_length = length - header.headerLength;
|
|
|
|
|
vie_network_->ReceivedBWEPacket(video_channel_, arrival_time_ms,
|
|
|
|
|
payload_length, header);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-08 15:18:52 +00:00
|
|
|
return ReceivePacket(received_packet, length, header, in_order) ? 0 : -1;
|
2013-09-06 13:40:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Channel::ReceivePacket(const uint8_t* packet,
|
|
|
|
|
int packet_length,
|
|
|
|
|
const RTPHeader& header,
|
|
|
|
|
bool in_order) {
|
|
|
|
|
if (rtp_payload_registry_->IsEncapsulated(header)) {
|
|
|
|
|
return HandleEncapsulation(packet, packet_length, header);
|
2013-08-15 23:38:54 +00:00
|
|
|
}
|
2013-09-06 13:40:11 +00:00
|
|
|
const uint8_t* payload = packet + header.headerLength;
|
|
|
|
|
int payload_length = packet_length - header.headerLength;
|
|
|
|
|
assert(payload_length >= 0);
|
2013-08-15 23:38:54 +00:00
|
|
|
PayloadUnion payload_specific;
|
|
|
|
|
if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
|
2013-09-06 13:40:11 +00:00
|
|
|
&payload_specific)) {
|
|
|
|
|
return false;
|
2013-08-15 23:38:54 +00:00
|
|
|
}
|
2013-09-06 13:40:11 +00:00
|
|
|
return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length,
|
|
|
|
|
payload_specific, in_order);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Channel::HandleEncapsulation(const uint8_t* packet,
|
|
|
|
|
int 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;
|
2013-04-03 15:43:57 +00:00
|
|
|
}
|
2013-09-06 13:40:11 +00:00
|
|
|
uint8_t* restored_packet_ptr = restored_packet_;
|
|
|
|
|
if (!rtp_payload_registry_->RestoreOriginalPacket(
|
|
|
|
|
&restored_packet_ptr, 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_ptr, 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);
|
2013-04-03 15:43:57 +00:00
|
|
|
}
|
|
|
|
|
|
2013-11-08 15:18:52 +00:00
|
|
|
bool Channel::IsPacketRetransmitted(const RTPHeader& header,
|
|
|
|
|
bool in_order) const {
|
2013-09-06 13:40:11 +00:00
|
|
|
// 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.
|
|
|
|
|
uint16_t min_rtt = 0;
|
|
|
|
|
_rtpRtcpModule->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
|
2013-11-08 15:18:52 +00:00
|
|
|
return !in_order &&
|
2013-09-06 13:40:11 +00:00
|
|
|
statistician->IsRetransmitOfOldPacket(header, min_rtt);
|
2013-08-15 23:38:54 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t Channel::ReceivedRTCPPacket(const int8_t* data, int32_t length) {
|
2013-04-03 15:43:57 +00:00
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::ReceivedRTCPPacket()");
|
|
|
|
|
// Store playout timestamp for the received RTCP packet
|
2013-04-11 20:23:35 +00:00
|
|
|
UpdatePlayoutTimestamp(true);
|
2013-04-03 15:43:57 +00:00
|
|
|
|
|
|
|
|
// Dump the RTCP packet to a file (if RTP dump is enabled).
|
2013-04-09 10:09:10 +00:00
|
|
|
if (_rtpDumpIn.DumpPacket((const uint8_t*)data,
|
|
|
|
|
(uint16_t)length) == -1) {
|
2013-04-03 15:43:57 +00:00
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SendPacket() RTCP dump to input file failed");
|
|
|
|
|
}
|
2013-03-13 23:20:57 +00:00
|
|
|
|
2013-04-03 15:43:57 +00:00
|
|
|
// Deliver RTCP packet to RTP/RTCP module for parsing
|
2013-05-29 12:12:51 +00:00
|
|
|
if (_rtpRtcpModule->IncomingRtcpPacket((const uint8_t*)data,
|
|
|
|
|
(uint16_t)length) == -1) {
|
2013-04-03 15:43:57 +00:00
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_SOCKET_TRANSPORT_MODULE_ERROR, kTraceWarning,
|
|
|
|
|
"Channel::IncomingRTPPacket() RTCP packet is invalid");
|
|
|
|
|
}
|
2014-05-20 22:55:01 +00:00
|
|
|
|
|
|
|
|
ntp_estimator_->UpdateRtcpTimestamp(rtp_receiver_->SSRC(),
|
|
|
|
|
_rtpRtcpModule.get());
|
2013-04-03 15:43:57 +00:00
|
|
|
return 0;
|
2013-03-13 23:20:57 +00:00
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
int Channel::StartPlayingFileLocally(const char* fileName,
|
2013-05-14 08:31:39 +00:00
|
|
|
bool loop,
|
|
|
|
|
FileFormats format,
|
|
|
|
|
int startPosition,
|
|
|
|
|
float volumeScaling,
|
|
|
|
|
int stopPosition,
|
2011-07-07 08:21:25 +00:00
|
|
|
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);
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().output_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_ALREADY_PLAYING, kTraceError,
|
|
|
|
|
"StartPlayingFileLocally() is already playing");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2011-10-31 23:53:04 +00:00
|
|
|
if (_outputFilePlayerPtr)
|
|
|
|
|
{
|
|
|
|
|
_outputFilePlayerPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_outputFilePlayerPtr);
|
|
|
|
|
_outputFilePlayerPtr = NULL;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2011-10-31 23:53:04 +00:00
|
|
|
_outputFilePlayerPtr = FilePlayer::CreateFilePlayer(
|
|
|
|
|
_outputFilePlayerId, (const FileFormats)format);
|
|
|
|
|
|
|
|
|
|
if (_outputFilePlayerPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
2011-11-18 19:59:32 +00:00
|
|
|
"StartPlayingFileLocally() filePlayer format is not correct");
|
2011-10-31 23:53:04 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint32_t notificationTime(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2011-10-31 23:53:04 +00:00
|
|
|
if (_outputFilePlayerPtr->StartPlayingFile(
|
|
|
|
|
fileName,
|
|
|
|
|
loop,
|
|
|
|
|
startPosition,
|
|
|
|
|
volumeScaling,
|
|
|
|
|
notificationTime,
|
|
|
|
|
stopPosition,
|
|
|
|
|
(const CodecInst*)codecInst) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_BAD_FILE, kTraceError,
|
|
|
|
|
"StartPlayingFile() failed to start file playout");
|
|
|
|
|
_outputFilePlayerPtr->StopPlayingFile();
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_outputFilePlayerPtr);
|
|
|
|
|
_outputFilePlayerPtr = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_outputFilePlayerPtr->RegisterModuleFileCallback(this);
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetOutputFilePlaying(true);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2012-06-04 03:26:39 +00:00
|
|
|
|
|
|
|
|
if (RegisterFilePlayingToMixer() != 0)
|
2011-10-28 23:15:47 +00:00
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::StartPlayingFileLocally(InStream* stream,
|
2013-05-14 08:31:39 +00:00
|
|
|
FileFormats format,
|
|
|
|
|
int startPosition,
|
|
|
|
|
float volumeScaling,
|
|
|
|
|
int stopPosition,
|
2011-07-07 08:21:25 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().output_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_ALREADY_PLAYING, kTraceError,
|
|
|
|
|
"StartPlayingFileLocally() is already playing");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-10-31 23:53:04 +00:00
|
|
|
|
|
|
|
|
// Destroy the old instance
|
|
|
|
|
if (_outputFilePlayerPtr)
|
|
|
|
|
{
|
|
|
|
|
_outputFilePlayerPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_outputFilePlayerPtr);
|
|
|
|
|
_outputFilePlayerPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the instance
|
|
|
|
|
_outputFilePlayerPtr = FilePlayer::CreateFilePlayer(
|
|
|
|
|
_outputFilePlayerId,
|
|
|
|
|
(const FileFormats)format);
|
|
|
|
|
|
|
|
|
|
if (_outputFilePlayerPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"StartPlayingFileLocally() filePlayer format isnot correct");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint32_t notificationTime(0);
|
2011-10-31 23:53:04 +00:00
|
|
|
|
|
|
|
|
if (_outputFilePlayerPtr->StartPlayingFile(*stream, startPosition,
|
|
|
|
|
volumeScaling,
|
|
|
|
|
notificationTime,
|
|
|
|
|
stopPosition, codecInst) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
|
|
|
|
|
"StartPlayingFile() failed to "
|
|
|
|
|
"start file playout");
|
|
|
|
|
_outputFilePlayerPtr->StopPlayingFile();
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_outputFilePlayerPtr);
|
|
|
|
|
_outputFilePlayerPtr = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_outputFilePlayerPtr->RegisterModuleFileCallback(this);
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetOutputFilePlaying(true);
|
2011-10-31 23:53:04 +00:00
|
|
|
}
|
2012-06-04 03:26:39 +00:00
|
|
|
|
|
|
|
|
if (RegisterFilePlayingToMixer() != 0)
|
2011-10-28 23:15:47 +00:00
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::StopPlayingFileLocally()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::StopPlayingFileLocally()");
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (!channel_state_.Get().output_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceWarning,
|
|
|
|
|
"StopPlayingFileLocally() isnot playing");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-10-31 23:53:04 +00:00
|
|
|
|
|
|
|
|
if (_outputFilePlayerPtr->StopPlayingFile() != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_STOP_RECORDING_FAILED, kTraceError,
|
|
|
|
|
"StopPlayingFile() could not stop playing");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_outputFilePlayerPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_outputFilePlayerPtr);
|
|
|
|
|
_outputFilePlayerPtr = NULL;
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetOutputFilePlaying(false);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2011-10-31 23:53:04 +00:00
|
|
|
// _fileCritSect cannot be taken while calling
|
|
|
|
|
// SetAnonymousMixibilityStatus. Refer to comments in
|
|
|
|
|
// StartPlayingFileLocally(const char* ...) for more details.
|
2011-10-28 23:15:47 +00:00
|
|
|
if (_outputMixerPtr->SetAnonymousMixabilityStatus(*this, false) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CONF_MIX_MODULE_ERROR, kTraceError,
|
2011-10-31 23:53:04 +00:00
|
|
|
"StopPlayingFile() failed to stop participant from playing as"
|
|
|
|
|
"file in the mixer");
|
2011-10-28 23:15:47 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::IsPlayingFileLocally() const
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::IsPlayingFileLocally()");
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
return channel_state_.Get().output_file_playing;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2012-06-04 03:26:39 +00:00
|
|
|
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.
|
2014-03-18 10:32:33 +00:00
|
|
|
if (!channel_state_.Get().playing ||
|
|
|
|
|
!channel_state_.Get().output_file_playing)
|
2012-06-04 03:26:39 +00:00
|
|
|
{
|
|
|
|
|
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)
|
|
|
|
|
{
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetOutputFilePlaying(false);
|
2012-06-04 03:26:39 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CONF_MIX_MODULE_ERROR, kTraceError,
|
|
|
|
|
"StartPlayingFile() failed to add participant as file to mixer");
|
|
|
|
|
_outputFilePlayerPtr->StopPlayingFile();
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_outputFilePlayerPtr);
|
|
|
|
|
_outputFilePlayerPtr = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
int Channel::StartPlayingFileAsMicrophone(const char* fileName,
|
2013-05-14 08:31:39 +00:00
|
|
|
bool loop,
|
|
|
|
|
FileFormats format,
|
|
|
|
|
int startPosition,
|
|
|
|
|
float volumeScaling,
|
|
|
|
|
int stopPosition,
|
2011-07-07 08:21:25 +00:00
|
|
|
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);
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
|
|
|
|
|
|
|
|
|
if (channel_state_.Get().input_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_ALREADY_PLAYING, kTraceWarning,
|
|
|
|
|
"StartPlayingFileAsMicrophone() filePlayer is playing");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Destroy the old instance
|
|
|
|
|
if (_inputFilePlayerPtr)
|
|
|
|
|
{
|
|
|
|
|
_inputFilePlayerPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_inputFilePlayerPtr);
|
|
|
|
|
_inputFilePlayerPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the instance
|
|
|
|
|
_inputFilePlayerPtr = FilePlayer::CreateFilePlayer(
|
|
|
|
|
_inputFilePlayerId, (const FileFormats)format);
|
|
|
|
|
|
|
|
|
|
if (_inputFilePlayerPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"StartPlayingFileAsMicrophone() filePlayer format isnot correct");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint32_t notificationTime(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_inputFilePlayerPtr->StartPlayingFile(
|
|
|
|
|
fileName,
|
|
|
|
|
loop,
|
|
|
|
|
startPosition,
|
|
|
|
|
volumeScaling,
|
|
|
|
|
notificationTime,
|
|
|
|
|
stopPosition,
|
|
|
|
|
(const CodecInst*)codecInst) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_BAD_FILE, kTraceError,
|
|
|
|
|
"StartPlayingFile() failed to start file playout");
|
|
|
|
|
_inputFilePlayerPtr->StopPlayingFile();
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_inputFilePlayerPtr);
|
|
|
|
|
_inputFilePlayerPtr = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_inputFilePlayerPtr->RegisterModuleFileCallback(this);
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetInputFilePlaying(true);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::StartPlayingFileAsMicrophone(InStream* stream,
|
2013-05-14 08:31:39 +00:00
|
|
|
FileFormats format,
|
|
|
|
|
int startPosition,
|
|
|
|
|
float volumeScaling,
|
|
|
|
|
int stopPosition,
|
2011-07-07 08:21:25 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
|
|
|
|
|
|
|
|
|
if (channel_state_.Get().input_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_ALREADY_PLAYING, kTraceWarning,
|
|
|
|
|
"StartPlayingFileAsMicrophone() is playing");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Destroy the old instance
|
|
|
|
|
if (_inputFilePlayerPtr)
|
|
|
|
|
{
|
|
|
|
|
_inputFilePlayerPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_inputFilePlayerPtr);
|
|
|
|
|
_inputFilePlayerPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the instance
|
|
|
|
|
_inputFilePlayerPtr = FilePlayer::CreateFilePlayer(
|
|
|
|
|
_inputFilePlayerId, (const FileFormats)format);
|
|
|
|
|
|
|
|
|
|
if (_inputFilePlayerPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"StartPlayingInputFile() filePlayer format isnot correct");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint32_t notificationTime(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_inputFilePlayerPtr->StartPlayingFile(*stream, startPosition,
|
|
|
|
|
volumeScaling, notificationTime,
|
|
|
|
|
stopPosition, codecInst) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
|
|
|
|
|
"StartPlayingFile() failed to start "
|
|
|
|
|
"file playout");
|
|
|
|
|
_inputFilePlayerPtr->StopPlayingFile();
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_inputFilePlayerPtr);
|
|
|
|
|
_inputFilePlayerPtr = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-01-22 04:44:30 +00:00
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
_inputFilePlayerPtr->RegisterModuleFileCallback(this);
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetInputFilePlaying(true);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::StopPlayingFileAsMicrophone()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::StopPlayingFileAsMicrophone()");
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
|
|
|
|
|
|
|
|
|
if (!channel_state_.Get().input_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceWarning,
|
|
|
|
|
"StopPlayingFileAsMicrophone() isnot playing");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_inputFilePlayerPtr->StopPlayingFile() != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_STOP_RECORDING_FAILED, kTraceError,
|
|
|
|
|
"StopPlayingFile() could not stop playing");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_inputFilePlayerPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FilePlayer::DestroyFilePlayer(_inputFilePlayerPtr);
|
|
|
|
|
_inputFilePlayerPtr = NULL;
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetInputFilePlaying(false);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::IsPlayingFileAsMicrophone() const
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::IsPlayingFileAsMicrophone()");
|
2014-03-18 10:32:33 +00:00
|
|
|
return channel_state_.Get().input_file_playing;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2012-03-01 18:34:25 +00:00
|
|
|
int Channel::StartRecordingPlayout(const char* fileName,
|
2011-07-07 08:21:25 +00:00
|
|
|
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;
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint32_t notificationTime(0); // Not supported in VoE
|
2011-07-07 08:21:25 +00:00
|
|
|
CodecInst dummyCodec={100,"L16",16000,320,1,320000};
|
|
|
|
|
|
2012-03-26 08:45:47 +00:00
|
|
|
if ((codecInst != NULL) &&
|
|
|
|
|
((codecInst->channels < 1) || (codecInst->channels > 2)))
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_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;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// Destroy the old instance
|
|
|
|
|
if (_outputFileRecorderPtr)
|
|
|
|
|
{
|
|
|
|
|
_outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
|
|
|
|
_outputFileRecorderPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
|
|
|
|
|
_outputFileRecorderId, (const FileFormats)format);
|
|
|
|
|
if (_outputFileRecorderPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"StartRecordingPlayout() fileRecorder format isnot correct");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_outputFileRecorderPtr->StartRecordingAudioFile(
|
|
|
|
|
fileName, (const CodecInst&)*codecInst, notificationTime) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_BAD_FILE, kTraceError,
|
|
|
|
|
"StartRecordingAudioFile() failed to start file recording");
|
|
|
|
|
_outputFileRecorderPtr->StopRecording();
|
|
|
|
|
FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
|
|
|
|
_outputFileRecorderPtr = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_outputFileRecorderPtr->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;
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint32_t notificationTime(0); // Not supported in VoE
|
2011-07-07 08:21:25 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// Destroy the old instance
|
|
|
|
|
if (_outputFileRecorderPtr)
|
|
|
|
|
{
|
|
|
|
|
_outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
|
|
|
|
_outputFileRecorderPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_outputFileRecorderPtr = FileRecorder::CreateFileRecorder(
|
|
|
|
|
_outputFileRecorderId, (const FileFormats)format);
|
|
|
|
|
if (_outputFileRecorderPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"StartRecordingPlayout() fileRecorder format isnot correct");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_outputFileRecorderPtr->StartRecordingAudioFile(*stream, *codecInst,
|
|
|
|
|
notificationTime) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(VE_BAD_FILE, kTraceError,
|
|
|
|
|
"StartRecordingPlayout() failed to "
|
|
|
|
|
"start file recording");
|
|
|
|
|
_outputFileRecorderPtr->StopRecording();
|
|
|
|
|
FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
|
|
|
|
_outputFileRecorderPtr = NULL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-01-22 04:44:30 +00:00
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
_outputFileRecorderPtr->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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_outputFileRecorderPtr->StopRecording() != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_STOP_RECORDING_FAILED, kTraceError,
|
|
|
|
|
"StopRecording() could not stop recording");
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
_outputFileRecorderPtr->RegisterModuleFileCallback(NULL);
|
|
|
|
|
FileRecorder::DestroyFileRecorder(_outputFileRecorderPtr);
|
|
|
|
|
_outputFileRecorderPtr = NULL;
|
|
|
|
|
_outputFileRecording = false;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Channel::SetMixWithMicStatus(bool mix)
|
|
|
|
|
{
|
2014-03-18 10:32:33 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
_mixFileWithMicrophone=mix;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2013-04-09 10:09:10 +00:00
|
|
|
Channel::GetSpeechOutputLevel(uint32_t& level) const
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2013-04-09 10:09:10 +00:00
|
|
|
int8_t currentLevel = _outputAudioLevel.Level();
|
|
|
|
|
level = static_cast<int32_t> (currentLevel);
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetSpeechOutputLevel() => level=%u", level);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2013-04-09 10:09:10 +00:00
|
|
|
Channel::GetSpeechOutputLevelFullRange(uint32_t& level) const
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2013-04-09 10:09:10 +00:00
|
|
|
int16_t currentLevel = _outputAudioLevel.LevelFullRange();
|
|
|
|
|
level = static_cast<int32_t> (currentLevel);
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetSpeechOutputLevelFullRange() => level=%u", level);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetMute(bool enable)
|
|
|
|
|
{
|
2013-10-17 18:28:55 +00:00
|
|
|
CriticalSectionScoped cs(&volume_settings_critsect_);
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetMute(enable=%d)", enable);
|
|
|
|
|
_mute = enable;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Channel::Mute() const
|
|
|
|
|
{
|
2013-10-17 18:28:55 +00:00
|
|
|
CriticalSectionScoped cs(&volume_settings_critsect_);
|
2011-07-07 08:21:25 +00:00
|
|
|
return _mute;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetOutputVolumePan(float left, float right)
|
|
|
|
|
{
|
2013-10-17 18:28:55 +00:00
|
|
|
CriticalSectionScoped cs(&volume_settings_critsect_);
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetOutputVolumePan()");
|
|
|
|
|
_panLeft = left;
|
|
|
|
|
_panRight = right;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetOutputVolumePan(float& left, float& right) const
|
|
|
|
|
{
|
2013-10-17 18:28:55 +00:00
|
|
|
CriticalSectionScoped cs(&volume_settings_critsect_);
|
2011-07-07 08:21:25 +00:00
|
|
|
left = _panLeft;
|
|
|
|
|
right = _panRight;
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetOutputVolumePan() => left=%3.2f, right=%3.2f", left, right);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetChannelOutputVolumeScaling(float scaling)
|
|
|
|
|
{
|
2013-10-17 18:28:55 +00:00
|
|
|
CriticalSectionScoped cs(&volume_settings_critsect_);
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetChannelOutputVolumeScaling()");
|
|
|
|
|
_outputGain = scaling;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetChannelOutputVolumeScaling(float& scaling) const
|
|
|
|
|
{
|
2013-10-17 18:28:55 +00:00
|
|
|
CriticalSectionScoped cs(&volume_settings_critsect_);
|
2011-07-07 08:21:25 +00:00
|
|
|
scaling = _outputGain;
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetChannelOutputVolumeScaling() => scaling=%3.2f", scaling);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::SendTelephoneEventOutband(unsigned char eventCode,
|
2013-08-15 23:38:54 +00:00
|
|
|
int lengthMs, int attenuationDb,
|
|
|
|
|
bool playDtmfEvent)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::SendTelephoneEventOutband(..., playDtmfEvent=%d)",
|
|
|
|
|
playDtmfEvent);
|
|
|
|
|
|
|
|
|
|
_playOutbandDtmfEvent = playDtmfEvent;
|
|
|
|
|
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SendTelephoneEventOutband(eventCode, lengthMs,
|
2011-07-07 08:21:25 +00:00
|
|
|
attenuationDb) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_SEND_DTMF_FAILED,
|
|
|
|
|
kTraceWarning,
|
|
|
|
|
"SendTelephoneEventOutband() failed to send event");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::SendTelephoneEventInband(unsigned char eventCode,
|
|
|
|
|
int lengthMs,
|
|
|
|
|
int attenuationDb,
|
|
|
|
|
bool playDtmfEvent)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::SendTelephoneEventInband(..., playDtmfEvent=%d)",
|
|
|
|
|
playDtmfEvent);
|
|
|
|
|
|
|
|
|
|
_playInbandDtmfEvent = playDtmfEvent;
|
|
|
|
|
_inbandDtmfQueue.AddDtmf(eventCode, lengthMs, attenuationDb);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetDtmfPlayoutStatus(bool enable)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetDtmfPlayoutStatus()");
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->SetDtmfPlayoutStatus(enable) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceWarning,
|
|
|
|
|
"SetDtmfPlayoutStatus() failed to set Dtmf playout");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Channel::DtmfPlayoutStatus() const
|
|
|
|
|
{
|
2013-09-23 23:02:24 +00:00
|
|
|
return audio_coding_->DtmfPlayoutStatus();
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetSendTelephoneEventPayloadType(unsigned char type)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetSendTelephoneEventPayloadType()");
|
2011-08-19 22:56:22 +00:00
|
|
|
if (type > 127)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"SetSendTelephoneEventPayloadType() invalid type");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-07-11 15:50:07 +00:00
|
|
|
CodecInst codec = {};
|
2011-10-13 15:19:55 +00:00
|
|
|
codec.plfreq = 8000;
|
|
|
|
|
codec.pltype = type;
|
|
|
|
|
memcpy(codec.plname, "telephone-event", 16);
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->RegisterSendPayload(codec) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2013-04-17 07:34:25 +00:00
|
|
|
_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;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
_sendTelephoneEventPayloadType = type;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetSendTelephoneEventPayloadType(unsigned char& type)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetSendTelephoneEventPayloadType()");
|
|
|
|
|
type = _sendTelephoneEventPayloadType;
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetSendTelephoneEventPayloadType() => type=%u", type);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::UpdateRxVadDetection(AudioFrame& audioFrame)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::UpdateRxVadDetection()");
|
|
|
|
|
|
|
|
|
|
int vadDecision = 1;
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
vadDecision = (audioFrame.vad_activity_ == AudioFrame::kVadActive)? 1 : 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if ((vadDecision != _oldVadDecision) && _rxVadObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
OnRxVadDetected(vadDecision);
|
|
|
|
|
_oldVadDecision = vadDecision;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::UpdateRxVadDetection() => vadDecision=%d",
|
|
|
|
|
vadDecision);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::RegisterRxVadObserver(VoERxVadCallback &observer)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::RegisterRxVadObserver()");
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_rxVadObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceError,
|
|
|
|
|
"RegisterRxVadObserver() observer already enabled");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_rxVadObserverPtr = &observer;
|
|
|
|
|
_RxVadDetection = true;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::DeRegisterRxVadObserver()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::DeRegisterRxVadObserver()");
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (!_rxVadObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceWarning,
|
|
|
|
|
"DeRegisterRxVadObserver() observer already disabled");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
_rxVadObserverPtr = NULL;
|
|
|
|
|
_RxVadDetection = false;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::VoiceActivityIndicator(int &activity)
|
|
|
|
|
{
|
|
|
|
|
activity = _sendFrameType;
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
2013-10-04 17:54:09 +00:00
|
|
|
"Channel::VoiceActivityIndicator(indicator=%d)", activity);
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef WEBRTC_VOICE_ENGINE_AGC
|
|
|
|
|
|
|
|
|
|
int
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::SetRxAgcStatus(bool enable, AgcModes mode)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetRxAgcStatus(enable=%d, mode=%d)",
|
|
|
|
|
(int)enable, (int)mode);
|
|
|
|
|
|
2013-10-04 17:54:09 +00:00
|
|
|
GainControl::Mode agcMode = kDefaultRxAgcMode;
|
2011-07-07 08:21:25 +00:00
|
|
|
switch (mode)
|
|
|
|
|
{
|
|
|
|
|
case kAgcDefault:
|
|
|
|
|
break;
|
|
|
|
|
case kAgcUnchanged:
|
2013-09-18 22:37:32 +00:00
|
|
|
agcMode = rx_audioproc_->gain_control()->mode();
|
2011-07-07 08:21:25 +00:00
|
|
|
break;
|
|
|
|
|
case kAgcFixedDigital:
|
|
|
|
|
agcMode = GainControl::kFixedDigital;
|
|
|
|
|
break;
|
|
|
|
|
case kAgcAdaptiveDigital:
|
|
|
|
|
agcMode =GainControl::kAdaptiveDigital;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"SetRxAgcStatus() invalid Agc mode");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-18 22:37:32 +00:00
|
|
|
if (rx_audioproc_->gain_control()->set_mode(agcMode) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_APM_ERROR, kTraceError,
|
|
|
|
|
"SetRxAgcStatus() failed to set Agc mode");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-09-18 22:37:32 +00:00
|
|
|
if (rx_audioproc_->gain_control()->Enable(enable) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_APM_ERROR, kTraceError,
|
|
|
|
|
"SetRxAgcStatus() failed to set Agc state");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_rxAgcIsEnabled = enable;
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetRxApmIsEnabled(_rxAgcIsEnabled || _rxNsIsEnabled);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetRxAgcStatus(bool& enabled, AgcModes& mode)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetRxAgcStatus(enable=?, mode=?)");
|
|
|
|
|
|
2013-09-18 22:37:32 +00:00
|
|
|
bool enable = rx_audioproc_->gain_control()->is_enabled();
|
2011-07-07 08:21:25 +00:00
|
|
|
GainControl::Mode agcMode =
|
2013-09-18 22:37:32 +00:00
|
|
|
rx_audioproc_->gain_control()->mode();
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
enabled = enable;
|
|
|
|
|
|
|
|
|
|
switch (agcMode)
|
|
|
|
|
{
|
|
|
|
|
case GainControl::kFixedDigital:
|
|
|
|
|
mode = kAgcFixedDigital;
|
|
|
|
|
break;
|
|
|
|
|
case GainControl::kAdaptiveDigital:
|
|
|
|
|
mode = kAgcAdaptiveDigital;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_APM_ERROR, kTraceError,
|
|
|
|
|
"GetRxAgcStatus() invalid Agc mode");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::SetRxAgcConfig(AgcConfig config)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetRxAgcConfig()");
|
|
|
|
|
|
2013-09-18 22:37:32 +00:00
|
|
|
if (rx_audioproc_->gain_control()->set_target_level_dbfs(
|
2011-07-07 08:21:25 +00:00
|
|
|
config.targetLeveldBOv) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_APM_ERROR, kTraceError,
|
|
|
|
|
"SetRxAgcConfig() failed to set target peak |level|"
|
|
|
|
|
"(or envelope) of the Agc");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-09-18 22:37:32 +00:00
|
|
|
if (rx_audioproc_->gain_control()->set_compression_gain_db(
|
2011-07-07 08:21:25 +00:00
|
|
|
config.digitalCompressionGaindB) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_APM_ERROR, kTraceError,
|
|
|
|
|
"SetRxAgcConfig() failed to set the range in |gain| the"
|
|
|
|
|
" digital compression stage may apply");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-09-18 22:37:32 +00:00
|
|
|
if (rx_audioproc_->gain_control()->enable_limiter(
|
2011-07-07 08:21:25 +00:00
|
|
|
config.limiterEnable) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_APM_ERROR, kTraceError,
|
|
|
|
|
"SetRxAgcConfig() failed to set hard limiter to the signal");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetRxAgcConfig(AgcConfig& config)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetRxAgcConfig(config=%?)");
|
|
|
|
|
|
|
|
|
|
config.targetLeveldBOv =
|
2013-09-18 22:37:32 +00:00
|
|
|
rx_audioproc_->gain_control()->target_level_dbfs();
|
2011-07-07 08:21:25 +00:00
|
|
|
config.digitalCompressionGaindB =
|
2013-09-18 22:37:32 +00:00
|
|
|
rx_audioproc_->gain_control()->compression_gain_db();
|
2011-07-07 08:21:25 +00:00
|
|
|
config.limiterEnable =
|
2013-09-18 22:37:32 +00:00
|
|
|
rx_audioproc_->gain_control()->is_limiter_enabled();
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId), "GetRxAgcConfig() => "
|
|
|
|
|
"targetLeveldBOv=%u, digitalCompressionGaindB=%u,"
|
|
|
|
|
" limiterEnable=%d",
|
|
|
|
|
config.targetLeveldBOv,
|
|
|
|
|
config.digitalCompressionGaindB,
|
|
|
|
|
config.limiterEnable);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // #ifdef WEBRTC_VOICE_ENGINE_AGC
|
|
|
|
|
|
|
|
|
|
#ifdef WEBRTC_VOICE_ENGINE_NR
|
|
|
|
|
|
|
|
|
|
int
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::SetRxNsStatus(bool enable, NsModes mode)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetRxNsStatus(enable=%d, mode=%d)",
|
|
|
|
|
(int)enable, (int)mode);
|
|
|
|
|
|
2013-10-04 17:54:09 +00:00
|
|
|
NoiseSuppression::Level nsLevel = kDefaultNsMode;
|
2011-07-07 08:21:25 +00:00
|
|
|
switch (mode)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
case kNsDefault:
|
|
|
|
|
break;
|
|
|
|
|
case kNsUnchanged:
|
2013-09-18 22:37:32 +00:00
|
|
|
nsLevel = rx_audioproc_->noise_suppression()->level();
|
2011-07-07 08:21:25 +00:00
|
|
|
break;
|
|
|
|
|
case kNsConference:
|
|
|
|
|
nsLevel = NoiseSuppression::kHigh;
|
|
|
|
|
break;
|
|
|
|
|
case kNsLowSuppression:
|
|
|
|
|
nsLevel = NoiseSuppression::kLow;
|
|
|
|
|
break;
|
|
|
|
|
case kNsModerateSuppression:
|
|
|
|
|
nsLevel = NoiseSuppression::kModerate;
|
|
|
|
|
break;
|
|
|
|
|
case kNsHighSuppression:
|
|
|
|
|
nsLevel = NoiseSuppression::kHigh;
|
|
|
|
|
break;
|
|
|
|
|
case kNsVeryHighSuppression:
|
|
|
|
|
nsLevel = NoiseSuppression::kVeryHigh;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-18 22:37:32 +00:00
|
|
|
if (rx_audioproc_->noise_suppression()->set_level(nsLevel)
|
2011-07-07 08:21:25 +00:00
|
|
|
!= 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_APM_ERROR, kTraceError,
|
2013-10-04 17:54:09 +00:00
|
|
|
"SetRxNsStatus() failed to set NS level");
|
2011-07-07 08:21:25 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
2013-09-18 22:37:32 +00:00
|
|
|
if (rx_audioproc_->noise_suppression()->Enable(enable) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_APM_ERROR, kTraceError,
|
2013-10-04 17:54:09 +00:00
|
|
|
"SetRxNsStatus() failed to set NS state");
|
2011-07-07 08:21:25 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_rxNsIsEnabled = enable;
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetRxApmIsEnabled(_rxAgcIsEnabled || _rxNsIsEnabled);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetRxNsStatus(bool& enabled, NsModes& mode)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetRxNsStatus(enable=?, mode=?)");
|
|
|
|
|
|
|
|
|
|
bool enable =
|
2013-09-18 22:37:32 +00:00
|
|
|
rx_audioproc_->noise_suppression()->is_enabled();
|
2011-07-07 08:21:25 +00:00
|
|
|
NoiseSuppression::Level ncLevel =
|
2013-09-18 22:37:32 +00:00
|
|
|
rx_audioproc_->noise_suppression()->level();
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
enabled = enable;
|
|
|
|
|
|
|
|
|
|
switch (ncLevel)
|
|
|
|
|
{
|
|
|
|
|
case NoiseSuppression::kLow:
|
|
|
|
|
mode = kNsLowSuppression;
|
|
|
|
|
break;
|
|
|
|
|
case NoiseSuppression::kModerate:
|
|
|
|
|
mode = kNsModerateSuppression;
|
|
|
|
|
break;
|
|
|
|
|
case NoiseSuppression::kHigh:
|
|
|
|
|
mode = kNsHighSuppression;
|
|
|
|
|
break;
|
|
|
|
|
case NoiseSuppression::kVeryHigh:
|
|
|
|
|
mode = kNsVeryHighSuppression;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetRxNsStatus() => enabled=%d, mode=%d", enabled, mode);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // #ifdef WEBRTC_VOICE_ENGINE_NR
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::RegisterRTCPObserver(VoERTCPObserver& observer)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::RegisterRTCPObserver()");
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_rtcpObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceError,
|
|
|
|
|
"RegisterRTCPObserver() observer already enabled");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_rtcpObserverPtr = &observer;
|
|
|
|
|
_rtcpObserver = true;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::DeRegisterRTCPObserver()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::DeRegisterRTCPObserver()");
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (!_rtcpObserverPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceWarning,
|
|
|
|
|
"DeRegisterRTCPObserver() observer already disabled");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_rtcpObserver = false;
|
|
|
|
|
_rtcpObserverPtr = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetLocalSSRC(unsigned int ssrc)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::SetLocalSSRC()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().sending)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_ALREADY_SENDING, kTraceError,
|
|
|
|
|
"SetLocalSSRC() already sending");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2014-06-05 08:25:29 +00:00
|
|
|
_rtpRtcpModule->SetSSRC(ssrc);
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetLocalSSRC(unsigned int& ssrc)
|
|
|
|
|
{
|
2012-05-11 11:08:54 +00:00
|
|
|
ssrc = _rtpRtcpModule->SSRC();
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetLocalSSRC() => ssrc=%lu", ssrc);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetRemoteSSRC(unsigned int& ssrc)
|
|
|
|
|
{
|
2013-08-15 23:38:54 +00:00
|
|
|
ssrc = rtp_receiver_->SSRC();
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetRemoteSSRC() => ssrc=%lu", ssrc);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-06 23:49:08 +00:00
|
|
|
int Channel::SetSendAudioLevelIndicationStatus(bool enable, unsigned char id) {
|
2013-09-18 22:37:32 +00:00
|
|
|
_includeAudioLevelIndication = enable;
|
2014-03-06 23:49:08 +00:00
|
|
|
return SetSendRtpHeaderExtension(enable, kRtpExtensionAudioLevel, id);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2013-09-18 22:37:32 +00:00
|
|
|
|
2014-04-24 20:33:08 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-06 23:49:08 +00:00
|
|
|
int Channel::SetSendAbsoluteSenderTimeStatus(bool enable, unsigned char id) {
|
|
|
|
|
return SetSendRtpHeaderExtension(enable, kRtpExtensionAbsoluteSendTime, id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::SetReceiveAbsoluteSenderTimeStatus(bool enable, unsigned char id) {
|
|
|
|
|
rtp_header_parser_->DeregisterRtpHeaderExtension(
|
|
|
|
|
kRtpExtensionAbsoluteSendTime);
|
2014-03-24 10:38:25 +00:00
|
|
|
if (enable && !rtp_header_parser_->RegisterRtpHeaderExtension(
|
|
|
|
|
kRtpExtensionAbsoluteSendTime, id)) {
|
|
|
|
|
return -1;
|
2014-03-06 23:49:08 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetRTCPStatus(bool enable)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetRTCPStatus()");
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SetRTCPStatus(enable ?
|
2011-07-07 08:21:25 +00:00
|
|
|
kRtcpCompound : kRtcpOff) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetRTCPStatus() failed to set RTCP status");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetRTCPStatus(bool& enabled)
|
|
|
|
|
{
|
2012-05-11 11:08:54 +00:00
|
|
|
RTCPMethod method = _rtpRtcpModule->RTCP();
|
2011-07-07 08:21:25 +00:00
|
|
|
enabled = (method != kRtcpOff);
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetRTCPStatus() => enabled=%d", enabled);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetRTCP_CNAME(const char cName[256])
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::SetRTCP_CNAME()");
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SetCNAME(cName) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetRTCP_CNAME() failed to set RTCP CNAME");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetRTCP_CNAME(char cName[256])
|
|
|
|
|
{
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->CNAME(cName) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"GetRTCP_CNAME() failed to retrieve RTCP CNAME");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRTCP_CNAME() => cName=%s", cName);
|
|
|
|
|
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;
|
|
|
|
|
}
|
2012-03-01 18:34:25 +00:00
|
|
|
char cname[RTCP_CNAME_SIZE];
|
2013-08-15 23:38:54 +00:00
|
|
|
const uint32_t remoteSSRC = rtp_receiver_->SSRC();
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->RemoteCNAME(remoteSSRC, cname) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_CANNOT_RETRIEVE_CNAME, kTraceError,
|
|
|
|
|
"GetRemoteRTCP_CNAME() failed to retrieve remote RTCP CNAME");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
strcpy(cName, cname);
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRemoteRTCP_CNAME() => cName=%s", cName);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::GetRemoteRTCPData(
|
|
|
|
|
unsigned int& NTPHigh,
|
|
|
|
|
unsigned int& NTPLow,
|
|
|
|
|
unsigned int& timestamp,
|
|
|
|
|
unsigned int& playoutTimestamp,
|
|
|
|
|
unsigned int* jitter,
|
|
|
|
|
unsigned short* fractionLost)
|
|
|
|
|
{
|
|
|
|
|
// --- Information from sender info in received Sender Reports
|
|
|
|
|
|
|
|
|
|
RTCPSenderInfo senderInfo;
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->RemoteRTCPStat(&senderInfo) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
2011-09-15 20:49:50 +00:00
|
|
|
"GetRemoteRTCPData() failed to retrieve sender info for remote "
|
2011-07-07 08:21:25 +00:00
|
|
|
"side");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We only utilize 12 out of 20 bytes in the sender info (ignores packet
|
|
|
|
|
// and octet count)
|
|
|
|
|
NTPHigh = senderInfo.NTPseconds;
|
|
|
|
|
NTPLow = senderInfo.NTPfraction;
|
|
|
|
|
timestamp = senderInfo.RTPtimeStamp;
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRemoteRTCPData() => NTPHigh=%lu, NTPLow=%lu, "
|
|
|
|
|
"timestamp=%lu",
|
|
|
|
|
NTPHigh, NTPLow, timestamp);
|
|
|
|
|
|
|
|
|
|
// --- Locally derived information
|
|
|
|
|
|
|
|
|
|
// This value is updated on each incoming RTCP packet (0 when no packet
|
|
|
|
|
// has been received)
|
2013-04-11 20:23:35 +00:00
|
|
|
playoutTimestamp = playout_timestamp_rtcp_;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRemoteRTCPData() => playoutTimestamp=%lu",
|
2013-04-11 20:23:35 +00:00
|
|
|
playout_timestamp_rtcp_);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (NULL != jitter || NULL != fractionLost)
|
|
|
|
|
{
|
2012-01-11 13:00:08 +00:00
|
|
|
// Get all RTCP receiver report blocks that have been received on this
|
|
|
|
|
// channel. If we receive RTP packets from a remote source we know the
|
|
|
|
|
// remote SSRC and use the report block from him.
|
|
|
|
|
// Otherwise use the first report block.
|
|
|
|
|
std::vector<RTCPReportBlock> remote_stats;
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->RemoteRTCPStat(&remote_stats) != 0 ||
|
2012-01-11 13:00:08 +00:00
|
|
|
remote_stats.empty()) {
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRemoteRTCPData() failed to measure statistics due"
|
|
|
|
|
" to lack of received RTP and/or RTCP packets");
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2012-01-11 13:00:08 +00:00
|
|
|
|
2013-08-15 23:38:54 +00:00
|
|
|
uint32_t remoteSSRC = rtp_receiver_->SSRC();
|
2012-01-11 13:00:08 +00:00
|
|
|
std::vector<RTCPReportBlock>::const_iterator it = remote_stats.begin();
|
|
|
|
|
for (; it != remote_stats.end(); ++it) {
|
|
|
|
|
if (it->remoteSSRC == remoteSSRC)
|
|
|
|
|
break;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2012-01-11 13:00:08 +00:00
|
|
|
|
|
|
|
|
if (it == remote_stats.end()) {
|
|
|
|
|
// If we have not received any RTCP packets from this SSRC it probably
|
|
|
|
|
// means that we have not received any RTP packets.
|
|
|
|
|
// Use the first received report block instead.
|
|
|
|
|
it = remote_stats.begin();
|
|
|
|
|
remoteSSRC = it->remoteSSRC;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2012-01-11 13:00:08 +00:00
|
|
|
|
2012-01-31 12:22:14 +00:00
|
|
|
if (jitter) {
|
|
|
|
|
*jitter = it->jitter;
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRemoteRTCPData() => jitter = %lu", *jitter);
|
|
|
|
|
}
|
2012-01-11 13:00:08 +00:00
|
|
|
|
2012-01-31 12:22:14 +00:00
|
|
|
if (fractionLost) {
|
|
|
|
|
*fractionLost = it->fractionLost;
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRemoteRTCPData() => fractionLost = %lu",
|
|
|
|
|
*fractionLost);
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::SendApplicationDefinedRTCPPacket(unsigned char subType,
|
2011-07-07 08:21:25 +00:00
|
|
|
unsigned int name,
|
|
|
|
|
const char* data,
|
|
|
|
|
unsigned short dataLengthInBytes)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::SendApplicationDefinedRTCPPacket()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (!channel_state_.Get().sending)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_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;
|
|
|
|
|
}
|
2012-05-11 11:08:54 +00:00
|
|
|
RTCPMethod status = _rtpRtcpModule->RTCP();
|
2011-07-07 08:21:25 +00:00
|
|
|
if (status == kRtcpOff)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTCP_ERROR, kTraceError,
|
|
|
|
|
"SendApplicationDefinedRTCPPacket() RTCP is disabled");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create and schedule the RTCP APP packet for transmission
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SetRTCPApplicationSpecificData(
|
2011-07-07 08:21:25 +00:00
|
|
|
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::GetRTPStatistics(
|
|
|
|
|
unsigned int& averageJitterMs,
|
|
|
|
|
unsigned int& maxJitterMs,
|
|
|
|
|
unsigned int& discardedPackets)
|
|
|
|
|
{
|
|
|
|
|
// The jitter statistics is updated for each received RTP packet and is
|
|
|
|
|
// based on received packets.
|
2013-12-19 13:26:02 +00:00
|
|
|
if (_rtpRtcpModule->RTCP() == kRtcpOff) {
|
|
|
|
|
// If RTCP is off, there is no timed thread in the RTCP module regularly
|
|
|
|
|
// generating new stats, trigger the update manually here instead.
|
|
|
|
|
StreamStatistician* statistician =
|
|
|
|
|
rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
|
|
|
|
|
if (statistician) {
|
|
|
|
|
// Don't use returned statistics, use data from proxy instead so that
|
|
|
|
|
// max jitter can be fetched atomically.
|
|
|
|
|
RtcpStatistics s;
|
|
|
|
|
statistician->GetStatistics(&s, true);
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-12-19 13:26:02 +00:00
|
|
|
ChannelStatistics stats = statistics_proxy_->GetStats();
|
2013-09-23 23:02:24 +00:00
|
|
|
const int32_t playoutFrequency = audio_coding_->PlayoutFrequency();
|
2013-12-19 13:26:02 +00:00
|
|
|
if (playoutFrequency > 0) {
|
|
|
|
|
// Scale RTP statistics given the current playout frequency
|
|
|
|
|
maxJitterMs = stats.max_jitter / (playoutFrequency / 1000);
|
|
|
|
|
averageJitterMs = stats.rtcp.jitter / (playoutFrequency / 1000);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
discardedPackets = _numberOfDiscardedPackets;
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRTPStatistics() => averageJitterMs = %lu, maxJitterMs = %lu,"
|
2011-09-15 20:49:50 +00:00
|
|
|
" discardedPackets = %lu)",
|
2011-07-07 08:21:25 +00:00
|
|
|
averageJitterMs, maxJitterMs, discardedPackets);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-22 08:53:55 +00:00
|
|
|
int Channel::GetRemoteRTCPReportBlocks(
|
|
|
|
|
std::vector<ReportBlock>* 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<RTCPReportBlock> rtcp_report_blocks;
|
|
|
|
|
if (_rtpRtcpModule->RemoteRTCPStat(&rtcp_report_blocks) != 0) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"GetRemoteRTCPReportBlocks() failed to read RTCP SR/RR report block.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rtcp_report_blocks.empty())
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
std::vector<RTCPReportBlock>::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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
int
|
|
|
|
|
Channel::GetRTPStatistics(CallStatistics& stats)
|
|
|
|
|
{
|
2014-05-19 17:39:11 +00:00
|
|
|
// --- RtcpStatistics
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// The jitter statistics is updated for each received RTP packet and is
|
|
|
|
|
// based on received packets.
|
2013-12-19 13:26:02 +00:00
|
|
|
RtcpStatistics statistics;
|
2013-08-21 20:58:21 +00:00
|
|
|
StreamStatistician* statistician =
|
|
|
|
|
rtp_receive_statistics_->GetStatistician(rtp_receiver_->SSRC());
|
|
|
|
|
if (!statistician || !statistician->GetStatistics(
|
2013-08-15 23:38:54 +00:00
|
|
|
&statistics, _rtpRtcpModule->RTCP() == kRtcpOff)) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_CANNOT_RETRIEVE_RTP_STAT, kTraceWarning,
|
|
|
|
|
"GetRTPStatistics() failed to read RTP statistics from the "
|
|
|
|
|
"RTP/RTCP module");
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-08-15 23:38:54 +00:00
|
|
|
stats.fractionLost = statistics.fraction_lost;
|
|
|
|
|
stats.cumulativeLost = statistics.cumulative_lost;
|
|
|
|
|
stats.extendedMax = statistics.extended_max_sequence_number;
|
|
|
|
|
stats.jitterSamples = statistics.jitter;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRTPStatistics() => fractionLost=%lu, cumulativeLost=%lu,"
|
2011-09-15 20:49:50 +00:00
|
|
|
" extendedMax=%lu, jitterSamples=%li)",
|
2011-07-07 08:21:25 +00:00
|
|
|
stats.fractionLost, stats.cumulativeLost, stats.extendedMax,
|
|
|
|
|
stats.jitterSamples);
|
|
|
|
|
|
2014-05-19 17:39:11 +00:00
|
|
|
// --- RTT
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
uint16_t RTT(0);
|
2012-05-11 11:08:54 +00:00
|
|
|
RTCPMethod method = _rtpRtcpModule->RTCP();
|
2011-07-07 08:21:25 +00:00
|
|
|
if (method == kRtcpOff)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
2011-09-15 20:49:50 +00:00
|
|
|
"GetRTPStatistics() RTCP is disabled => valid RTT "
|
2011-07-07 08:21:25 +00:00
|
|
|
"measurements cannot be retrieved");
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
// The remote SSRC will be zero if no RTP packet has been received.
|
2013-08-15 23:38:54 +00:00
|
|
|
uint32_t remoteSSRC = rtp_receiver_->SSRC();
|
2011-07-07 08:21:25 +00:00
|
|
|
if (remoteSSRC > 0)
|
|
|
|
|
{
|
2013-04-09 10:09:10 +00:00
|
|
|
uint16_t avgRTT(0);
|
|
|
|
|
uint16_t maxRTT(0);
|
|
|
|
|
uint16_t minRTT(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->RTT(remoteSSRC, &RTT, &avgRTT, &minRTT, &maxRTT)
|
2011-07-07 08:21:25 +00:00
|
|
|
!= 0)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
2011-09-15 20:49:50 +00:00
|
|
|
"GetRTPStatistics() failed to retrieve RTT from "
|
2011-07-07 08:21:25 +00:00
|
|
|
"the RTP/RTCP module");
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
2011-09-15 20:49:50 +00:00
|
|
|
"GetRTPStatistics() failed to measure RTT since no "
|
2011-07-07 08:21:25 +00:00
|
|
|
"RTP packets have been received yet");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stats.rttMs = static_cast<int> (RTT);
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRTPStatistics() => rttMs=%d", stats.rttMs);
|
|
|
|
|
|
2014-05-19 17:39:11 +00:00
|
|
|
// --- Data counters
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
uint32_t bytesSent(0);
|
|
|
|
|
uint32_t packetsSent(0);
|
|
|
|
|
uint32_t bytesReceived(0);
|
|
|
|
|
uint32_t packetsReceived(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-08-21 20:58:21 +00:00
|
|
|
if (statistician) {
|
|
|
|
|
statistician->GetDataCounters(&bytesReceived, &packetsReceived);
|
|
|
|
|
}
|
2013-08-15 23:38:54 +00:00
|
|
|
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->DataCountersRTP(&bytesSent,
|
2013-08-15 23:38:54 +00:00
|
|
|
&packetsSent) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRTPStatistics() failed to retrieve RTP datacounters =>"
|
2011-09-15 20:49:50 +00:00
|
|
|
" output will not be complete");
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stats.bytesSent = bytesSent;
|
|
|
|
|
stats.packetsSent = packetsSent;
|
|
|
|
|
stats.bytesReceived = bytesReceived;
|
|
|
|
|
stats.packetsReceived = packetsReceived;
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetRTPStatistics() => bytesSent=%d, packetsSent=%d,"
|
2011-09-15 20:49:50 +00:00
|
|
|
" bytesReceived=%d, packetsReceived=%d)",
|
2011-07-07 08:21:25 +00:00
|
|
|
stats.bytesSent, stats.packetsSent, stats.bytesReceived,
|
|
|
|
|
stats.packetsReceived);
|
|
|
|
|
|
2014-05-19 17:39:11 +00:00
|
|
|
// --- Timestamps
|
|
|
|
|
{
|
|
|
|
|
CriticalSectionScoped lock(ts_stats_lock_.get());
|
|
|
|
|
stats.capture_start_ntp_time_ms_ = capture_start_ntp_time_ms_;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-28 09:52:06 +00:00
|
|
|
int Channel::SetREDStatus(bool enable, int redPayloadtype) {
|
2012-12-11 02:15:12 +00:00
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
2014-05-28 09:52:06 +00:00
|
|
|
"Channel::SetREDStatus()");
|
2012-12-11 02:15:12 +00:00
|
|
|
|
2013-01-31 18:20:17 +00:00
|
|
|
if (enable) {
|
|
|
|
|
if (redPayloadtype < 0 || redPayloadtype > 127) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_PLTYPE_ERROR, kTraceError,
|
2014-05-28 09:52:06 +00:00
|
|
|
"SetREDStatus() invalid RED payload type");
|
2013-01-31 18:20:17 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SetRedPayloadType(redPayloadtype) < 0) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_CODEC_ERROR, kTraceError,
|
|
|
|
|
"SetSecondarySendCodec() Failed to register RED ACM");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2012-12-11 02:15:12 +00:00
|
|
|
}
|
2012-12-04 10:02:02 +00:00
|
|
|
|
2014-05-23 15:16:51 +00:00
|
|
|
if (audio_coding_->SetREDStatus(enable) != 0) {
|
2012-12-11 02:15:12 +00:00
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
2014-05-23 15:16:51 +00:00
|
|
|
"SetREDStatus() failed to set RED state in the ACM");
|
2012-12-11 02:15:12 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2014-05-28 09:52:06 +00:00
|
|
|
Channel::GetREDStatus(bool& enabled, int& redPayloadtype)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2014-05-23 15:16:51 +00:00
|
|
|
enabled = audio_coding_->REDStatus();
|
2011-07-07 08:21:25 +00:00
|
|
|
if (enabled)
|
|
|
|
|
{
|
2013-04-09 10:09:10 +00:00
|
|
|
int8_t payloadType(0);
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SendREDPayloadType(payloadType) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
2014-05-28 09:52:06 +00:00
|
|
|
"GetREDStatus() failed to retrieve RED PT from RTP/RTCP "
|
2011-07-07 08:21:25 +00:00
|
|
|
"module");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
2014-05-28 09:52:06 +00:00
|
|
|
"GetREDStatus() => enabled=%d, redPayloadtype=%d",
|
2011-07-07 08:21:25 +00:00
|
|
|
enabled, redPayloadtype);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
2014-05-28 09:52:06 +00:00
|
|
|
"GetREDStatus() => enabled=%d", enabled);
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-28 09:52:06 +00:00
|
|
|
int Channel::SetCodecFECStatus(bool enable) {
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::SetCodecFECStatus()");
|
|
|
|
|
|
|
|
|
|
if (audio_coding_->SetCodecFEC(enable) != 0) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetCodecFECStatus() failed to set FEC state");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Channel::GetCodecFECStatus() {
|
|
|
|
|
bool enabled = audio_coding_->CodecFEC();
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"GetCodecFECStatus() => enabled=%d", enabled);
|
|
|
|
|
return enabled;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-05 15:33:20 +00:00
|
|
|
void Channel::SetNACKStatus(bool enable, int maxNumberOfPackets) {
|
|
|
|
|
// None of these functions can fail.
|
|
|
|
|
_rtpRtcpModule->SetStorePacketsStatus(enable, maxNumberOfPackets);
|
2013-09-06 13:40:11 +00:00
|
|
|
rtp_receive_statistics_->SetMaxReorderingThreshold(maxNumberOfPackets);
|
|
|
|
|
rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff);
|
2013-06-06 21:09:01 +00:00
|
|
|
if (enable)
|
2013-09-23 23:02:24 +00:00
|
|
|
audio_coding_->EnableNack(maxNumberOfPackets);
|
2013-06-06 21:09:01 +00:00
|
|
|
else
|
2013-09-23 23:02:24 +00:00
|
|
|
audio_coding_->DisableNack();
|
2013-06-05 15:33:20 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-06 21:09:01 +00:00
|
|
|
// Called when we are missing one or more packets.
|
|
|
|
|
int Channel::ResendPackets(const uint16_t* sequence_numbers, int length) {
|
2013-06-05 15:33:20 +00:00
|
|
|
return _rtpRtcpModule->SendNACK(sequence_numbers, length);
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
int
|
|
|
|
|
Channel::StartRTPDump(const char fileNameUTF8[1024],
|
|
|
|
|
RTPDirections direction)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::StartRTPDump()");
|
|
|
|
|
if ((direction != kRtpIncoming) && (direction != kRtpOutgoing))
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"StartRTPDump() invalid RTP direction");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
RtpDump* rtpDumpPtr = (direction == kRtpIncoming) ?
|
|
|
|
|
&_rtpDumpIn : &_rtpDumpOut;
|
|
|
|
|
if (rtpDumpPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
assert(false);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (rtpDumpPtr->IsActive())
|
|
|
|
|
{
|
|
|
|
|
rtpDumpPtr->Stop();
|
|
|
|
|
}
|
|
|
|
|
if (rtpDumpPtr->Start(fileNameUTF8) != 0)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_BAD_FILE, kTraceError,
|
|
|
|
|
"StartRTPDump() failed to create file");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::StopRTPDump(RTPDirections direction)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::StopRTPDump()");
|
|
|
|
|
if ((direction != kRtpIncoming) && (direction != kRtpOutgoing))
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"StopRTPDump() invalid RTP direction");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
RtpDump* rtpDumpPtr = (direction == kRtpIncoming) ?
|
|
|
|
|
&_rtpDumpIn : &_rtpDumpOut;
|
|
|
|
|
if (rtpDumpPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
assert(false);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!rtpDumpPtr->IsActive())
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return rtpDumpPtr->Stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Channel::RTPDumpIsActive(RTPDirections direction)
|
|
|
|
|
{
|
|
|
|
|
if ((direction != kRtpIncoming) &&
|
|
|
|
|
(direction != kRtpOutgoing))
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"RTPDumpIsActive() invalid RTP direction");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
RtpDump* rtpDumpPtr = (direction == kRtpIncoming) ?
|
|
|
|
|
&_rtpDumpIn : &_rtpDumpOut;
|
|
|
|
|
return rtpDumpPtr->IsActive();
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-24 10:38:25 +00:00
|
|
|
void Channel::SetVideoEngineBWETarget(ViENetwork* vie_network,
|
|
|
|
|
int video_channel) {
|
|
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
|
|
|
|
if (vie_network_) {
|
|
|
|
|
vie_network_->Release();
|
|
|
|
|
vie_network_ = NULL;
|
|
|
|
|
}
|
|
|
|
|
video_channel_ = -1;
|
|
|
|
|
|
|
|
|
|
if (vie_network != NULL && video_channel != -1) {
|
|
|
|
|
vie_network_ = vie_network;
|
|
|
|
|
video_channel_ = video_channel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
uint32_t
|
2011-11-15 16:57:56 +00:00
|
|
|
Channel::Demultiplex(const AudioFrame& audioFrame)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
2011-11-15 16:57:56 +00:00
|
|
|
"Channel::Demultiplex()");
|
2013-01-22 04:44:30 +00:00
|
|
|
_audioFrame.CopyFrom(audioFrame);
|
2012-05-02 23:56:37 +00:00
|
|
|
_audioFrame.id_ = _channelId;
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-31 16:23:37 +00:00
|
|
|
void Channel::Demultiplex(const int16_t* audio_data,
|
2013-07-31 16:27:42 +00:00
|
|
|
int sample_rate,
|
2013-07-31 16:23:37 +00:00
|
|
|
int number_of_frames,
|
2013-07-31 16:27:42 +00:00
|
|
|
int number_of_channels) {
|
2013-07-31 16:23:37 +00:00
|
|
|
CodecInst codec;
|
|
|
|
|
GetSendCodec(codec);
|
|
|
|
|
|
Consolidate audio conversion from Channel and TransmitMixer.
Replace the two versions with a single DownConvertToCodecFormat. As
mentioned in comments, this could be further consolidated with
RemixAndResample but we should write a full audio converter class in
that case.
Along the way:
- Fix the bug present in Channel::Demultiplex with mono input and a
stereo codec.
- Remove the 32 kHz max from the OnDataAvailable path. This avoids a
48 -> 32 -> 48 conversion when VoE is passed 48 kHz audio; instead we
get a straight pass-through to ACM. The 32 kHz conversion is still
needed in the RecordedDataIsAvailable path until APM natively supports
48 kHz.
- Merge resampler improvements from ACM1 to ACM2. This allows ACM to
handle 44.1 kHz audio passed to VoE and was originally done here:
https://webrtc-codereview.appspot.com/1590004
- Reuse the RemixAndResample unit tests for DownConvertToCodecFormat.
- Remove unused functions from utility.cc.
BUG=3155,3000,b/12867572
TESTED=voe_cmd_test using both the OnDataAvailable and
RecordedDataIsAvailable paths, with a captured audio format of all
combinations of {44.1,48} kHz and {1,2} channels, running through all
codecs, and finally using both ACM1 and ACM2.
R=henrika@webrtc.org, turaj@webrtc.org, xians@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/11019005
git-svn-id: http://webrtc.googlecode.com/svn/trunk@5843 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-04-03 21:56:01 +00:00
|
|
|
if (!mono_recording_audio_.get()) {
|
|
|
|
|
// Temporary space for DownConvertToCodecFormat.
|
|
|
|
|
mono_recording_audio_.reset(new int16_t[kMaxMonoDataSizeSamples]);
|
2013-07-31 16:23:37 +00:00
|
|
|
}
|
Consolidate audio conversion from Channel and TransmitMixer.
Replace the two versions with a single DownConvertToCodecFormat. As
mentioned in comments, this could be further consolidated with
RemixAndResample but we should write a full audio converter class in
that case.
Along the way:
- Fix the bug present in Channel::Demultiplex with mono input and a
stereo codec.
- Remove the 32 kHz max from the OnDataAvailable path. This avoids a
48 -> 32 -> 48 conversion when VoE is passed 48 kHz audio; instead we
get a straight pass-through to ACM. The 32 kHz conversion is still
needed in the RecordedDataIsAvailable path until APM natively supports
48 kHz.
- Merge resampler improvements from ACM1 to ACM2. This allows ACM to
handle 44.1 kHz audio passed to VoE and was originally done here:
https://webrtc-codereview.appspot.com/1590004
- Reuse the RemixAndResample unit tests for DownConvertToCodecFormat.
- Remove unused functions from utility.cc.
BUG=3155,3000,b/12867572
TESTED=voe_cmd_test using both the OnDataAvailable and
RecordedDataIsAvailable paths, with a captured audio format of all
combinations of {44.1,48} kHz and {1,2} channels, running through all
codecs, and finally using both ACM1 and ACM2.
R=henrika@webrtc.org, turaj@webrtc.org, xians@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/11019005
git-svn-id: http://webrtc.googlecode.com/svn/trunk@5843 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-04-03 21:56:01 +00:00
|
|
|
DownConvertToCodecFormat(audio_data,
|
|
|
|
|
number_of_frames,
|
|
|
|
|
number_of_channels,
|
|
|
|
|
sample_rate,
|
|
|
|
|
codec.channels,
|
|
|
|
|
codec.plfreq,
|
|
|
|
|
mono_recording_audio_.get(),
|
|
|
|
|
&input_resampler_,
|
|
|
|
|
&_audioFrame);
|
2013-07-31 16:23:37 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
uint32_t
|
2011-08-08 08:18:44 +00:00
|
|
|
Channel::PrepareEncodeAndSend(int mixingFrequency)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::PrepareEncodeAndSend()");
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
if (_audioFrame.samples_per_channel_ == 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::PrepareEncodeAndSend() invalid audio frame");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().input_file_playing)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
MixOrReplaceAudioWithFile(mixingFrequency);
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-14 19:00:59 +00:00
|
|
|
bool is_muted = Mute(); // Cache locally as Mute() takes a lock.
|
|
|
|
|
if (is_muted) {
|
|
|
|
|
AudioFrameOperations::Mute(_audioFrame);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().input_external_media)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2012-05-02 23:56:37 +00:00
|
|
|
const bool isStereo = (_audioFrame.num_channels_ == 2);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_inputExternalMediaCallbackPtr)
|
|
|
|
|
{
|
|
|
|
|
_inputExternalMediaCallbackPtr->Process(
|
|
|
|
|
_channelId,
|
|
|
|
|
kRecordingPerChannel,
|
2013-04-09 10:09:10 +00:00
|
|
|
(int16_t*)_audioFrame.data_,
|
2012-05-02 23:56:37 +00:00
|
|
|
_audioFrame.samples_per_channel_,
|
|
|
|
|
_audioFrame.sample_rate_hz_,
|
2011-07-07 08:21:25 +00:00
|
|
|
isStereo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InsertInbandDtmfTone();
|
|
|
|
|
|
2014-01-07 17:45:09 +00:00
|
|
|
if (_includeAudioLevelIndication) {
|
2014-05-05 18:22:21 +00:00
|
|
|
int length = _audioFrame.samples_per_channel_ * _audioFrame.num_channels_;
|
2014-05-14 19:00:59 +00:00
|
|
|
if (is_muted) {
|
|
|
|
|
rms_level_.ProcessMuted(length);
|
|
|
|
|
} else {
|
|
|
|
|
rms_level_.Process(_audioFrame.data_, length);
|
|
|
|
|
}
|
2011-11-15 16:57:56 +00:00
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
uint32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::EncodeAndSend()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::EncodeAndSend()");
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
assert(_audioFrame.num_channels_ <= 2);
|
|
|
|
|
if (_audioFrame.samples_per_channel_ == 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::EncodeAndSend() invalid audio frame");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
_audioFrame.id_ = _channelId;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// --- Add 10ms of raw (PCM) audio data to the encoder @ 32kHz.
|
|
|
|
|
|
|
|
|
|
// The ACM resamples internally.
|
2012-05-02 23:56:37 +00:00
|
|
|
_audioFrame.timestamp_ = _timeStamp;
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->Add10MsData((AudioFrame&)_audioFrame) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::EncodeAndSend() ACM encoding failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
_timeStamp += _audioFrame.samples_per_channel_;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
// --- Encode if complete frame is ready
|
|
|
|
|
|
|
|
|
|
// This call will trigger AudioPacketizationCallback::SendData if encoding
|
|
|
|
|
// is done and payload is ready for packetization and transmission.
|
2013-09-23 23:02:24 +00:00
|
|
|
return audio_coding_->Process();
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::RegisterExternalMediaProcessing(
|
|
|
|
|
ProcessingTypes type,
|
|
|
|
|
VoEMediaProcess& processObject)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::RegisterExternalMediaProcessing()");
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (kPlaybackPerChannel == type)
|
|
|
|
|
{
|
|
|
|
|
if (_outputExternalMediaCallbackPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceError,
|
|
|
|
|
"Channel::RegisterExternalMediaProcessing() "
|
|
|
|
|
"output external media already enabled");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_outputExternalMediaCallbackPtr = &processObject;
|
|
|
|
|
_outputExternalMedia = true;
|
|
|
|
|
}
|
|
|
|
|
else if (kRecordingPerChannel == type)
|
|
|
|
|
{
|
|
|
|
|
if (_inputExternalMediaCallbackPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceError,
|
|
|
|
|
"Channel::RegisterExternalMediaProcessing() "
|
|
|
|
|
"output external media already enabled");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
_inputExternalMediaCallbackPtr = &processObject;
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetInputExternalMedia(true);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::DeRegisterExternalMediaProcessing(ProcessingTypes type)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::DeRegisterExternalMediaProcessing()");
|
|
|
|
|
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (kPlaybackPerChannel == type)
|
|
|
|
|
{
|
|
|
|
|
if (!_outputExternalMediaCallbackPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceWarning,
|
|
|
|
|
"Channel::DeRegisterExternalMediaProcessing() "
|
|
|
|
|
"output external media already disabled");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
_outputExternalMedia = false;
|
|
|
|
|
_outputExternalMediaCallbackPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
else if (kRecordingPerChannel == type)
|
|
|
|
|
{
|
|
|
|
|
if (!_inputExternalMediaCallbackPtr)
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceWarning,
|
|
|
|
|
"Channel::DeRegisterExternalMediaProcessing() "
|
|
|
|
|
"input external media already disabled");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-03-18 10:32:33 +00:00
|
|
|
channel_state_.SetInputExternalMedia(false);
|
2011-07-07 08:21:25 +00:00
|
|
|
_inputExternalMediaCallbackPtr = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-12 23:00:29 +00:00
|
|
|
int Channel::SetExternalMixing(bool enabled) {
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetExternalMixing(enabled=%d)", enabled);
|
|
|
|
|
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().playing)
|
2012-12-12 23:00:29 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_OPERATION, kTraceError,
|
|
|
|
|
"Channel::SetExternalMixing() "
|
|
|
|
|
"external mixing cannot be changed while playing.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_externalMixing = enabled;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
int
|
|
|
|
|
Channel::GetNetworkStatistics(NetworkStatistics& stats)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetNetworkStatistics()");
|
2013-02-21 10:27:48 +00:00
|
|
|
ACMNetworkStatistics acm_stats;
|
2013-09-23 23:02:24 +00:00
|
|
|
int return_value = audio_coding_->NetworkStatistics(&acm_stats);
|
2013-02-21 10:27:48 +00:00
|
|
|
if (return_value >= 0) {
|
|
|
|
|
memcpy(&stats, &acm_stats, sizeof(NetworkStatistics));
|
|
|
|
|
}
|
|
|
|
|
return return_value;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-12-13 19:17:43 +00:00
|
|
|
void Channel::GetDecodingCallStatistics(AudioDecodingCallStats* stats) const {
|
|
|
|
|
audio_coding_->GetDecodingCallStatistics(stats);
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
bool Channel::GetDelayEstimate(int* jitter_buffer_delay_ms,
|
|
|
|
|
int* playout_buffer_delay_ms) const {
|
|
|
|
|
if (_average_jitter_buffer_delay_us == 0) {
|
2011-07-07 08:21:25 +00:00
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
2013-04-11 20:23:35 +00:00
|
|
|
"Channel::GetDelayEstimate() no valid estimate.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*jitter_buffer_delay_ms = (_average_jitter_buffer_delay_us + 500) / 1000 +
|
|
|
|
|
_recPacketDelayMs;
|
|
|
|
|
*playout_buffer_delay_ms = playout_delay_ms_;
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetDelayEstimate()");
|
|
|
|
|
return true;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2013-02-12 21:42:18 +00:00
|
|
|
int Channel::SetInitialPlayoutDelay(int delay_ms)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetInitialPlayoutDelay()");
|
|
|
|
|
if ((delay_ms < kVoiceEngineMinMinPlayoutDelayMs) ||
|
|
|
|
|
(delay_ms > kVoiceEngineMaxMinPlayoutDelayMs))
|
|
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
|
|
|
"SetInitialPlayoutDelay() invalid min delay");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->SetInitialPlayoutDelay(delay_ms) != 0)
|
2013-02-12 21:42:18 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetInitialPlayoutDelay() failed to set min playout delay");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->SetMinimumPlayoutDelay(delayMs) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetMinimumPlayoutDelay() failed to set min playout delay");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
void Channel::UpdatePlayoutTimestamp(bool rtcp) {
|
|
|
|
|
uint32_t playout_timestamp = 0;
|
|
|
|
|
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->PlayoutTimestamp(&playout_timestamp) == -1) {
|
2013-04-11 20:23:35 +00:00
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::UpdatePlayoutTimestamp() failed to read playout"
|
|
|
|
|
" timestamp from the ACM");
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_CANNOT_RETRIEVE_VALUE, kTraceError,
|
|
|
|
|
"UpdatePlayoutTimestamp() failed to retrieve 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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-23 23:02:24 +00:00
|
|
|
int32_t playout_frequency = audio_coding_->PlayoutFrequency();
|
2013-04-11 20:23:35 +00:00
|
|
|
CodecInst current_recive_codec;
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->ReceiveCodec(¤t_recive_codec) == 0) {
|
2013-04-11 20:23:35 +00:00
|
|
|
if (STR_CASE_CMP("G722", current_recive_codec.plname) == 0) {
|
|
|
|
|
playout_frequency = 8000;
|
|
|
|
|
} else if (STR_CASE_CMP("opus", current_recive_codec.plname) == 0) {
|
|
|
|
|
playout_frequency = 48000;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2013-04-11 20:23:35 +00:00
|
|
|
}
|
|
|
|
|
|
2013-12-13 21:05:07 +00:00
|
|
|
jitter_buffer_playout_timestamp_ = playout_timestamp;
|
|
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
// Remove the playout delay.
|
|
|
|
|
playout_timestamp -= (delay_ms * (playout_frequency / 1000));
|
|
|
|
|
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::UpdatePlayoutTimestamp() => playoutTimestamp = %lu",
|
|
|
|
|
playout_timestamp);
|
|
|
|
|
|
|
|
|
|
if (rtcp) {
|
|
|
|
|
playout_timestamp_rtcp_ = playout_timestamp;
|
|
|
|
|
} else {
|
|
|
|
|
playout_timestamp_rtp_ = playout_timestamp;
|
|
|
|
|
}
|
|
|
|
|
playout_delay_ms_ = delay_ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::GetPlayoutTimestamp(unsigned int& timestamp) {
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetPlayoutTimestamp()");
|
|
|
|
|
if (playout_timestamp_rtp_ == 0) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_CANNOT_RETRIEVE_VALUE, kTraceError,
|
|
|
|
|
"GetPlayoutTimestamp() failed to retrieve timestamp");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
timestamp = playout_timestamp_rtp_;
|
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId,_channelId),
|
|
|
|
|
"GetPlayoutTimestamp() => timestamp=%u", timestamp);
|
|
|
|
|
return 0;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetInitTimestamp(unsigned int timestamp)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetInitTimestamp()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().sending)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_SENDING, kTraceError, "SetInitTimestamp() already sending");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SetStartTimestamp(timestamp) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetInitTimestamp() failed to set timestamp");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::SetInitSequenceNumber(short sequenceNumber)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::SetInitSequenceNumber()");
|
2014-03-18 10:32:33 +00:00
|
|
|
if (channel_state_.Get().sending)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_SENDING, kTraceError,
|
|
|
|
|
"SetInitSequenceNumber() already sending");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2012-05-11 11:08:54 +00:00
|
|
|
if (_rtpRtcpModule->SetSequenceNumber(sequenceNumber) != 0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetInitSequenceNumber() failed to set sequence number");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2013-08-15 23:38:54 +00:00
|
|
|
Channel::GetRtpRtcp(RtpRtcp** rtpRtcpModule, RtpReceiver** rtp_receiver) const
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::GetRtpRtcp()");
|
2013-08-15 23:38:54 +00:00
|
|
|
*rtpRtcpModule = _rtpRtcpModule.get();
|
|
|
|
|
*rtp_receiver = rtp_receiver_.get();
|
2011-07-07 08:21:25 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-08 17:12:40 +00:00
|
|
|
// TODO(andrew): refactor Mix functions here and in transmit_mixer.cc to use
|
|
|
|
|
// a shared helper.
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2013-05-14 08:31:39 +00:00
|
|
|
Channel::MixOrReplaceAudioWithFile(int mixingFrequency)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2014-04-25 23:10:28 +00:00
|
|
|
scoped_ptr<int16_t[]> fileBuffer(new int16_t[640]);
|
2012-05-08 17:12:40 +00:00
|
|
|
int fileSamples(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_inputFilePlayerPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::MixOrReplaceAudioWithFile() fileplayer"
|
|
|
|
|
" doesnt exist");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-29 10:39:44 +00:00
|
|
|
if (_inputFilePlayerPtr->Get10msAudioFromFile(fileBuffer.get(),
|
2011-07-07 08:21:25 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
assert(_audioFrame.samples_per_channel_ == fileSamples);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_mixFileWithMicrophone)
|
|
|
|
|
{
|
2012-03-29 10:39:44 +00:00
|
|
|
// Currently file stream is always mono.
|
|
|
|
|
// TODO(xians): Change the code when FilePlayer supports real stereo.
|
Consolidate audio conversion from Channel and TransmitMixer.
Replace the two versions with a single DownConvertToCodecFormat. As
mentioned in comments, this could be further consolidated with
RemixAndResample but we should write a full audio converter class in
that case.
Along the way:
- Fix the bug present in Channel::Demultiplex with mono input and a
stereo codec.
- Remove the 32 kHz max from the OnDataAvailable path. This avoids a
48 -> 32 -> 48 conversion when VoE is passed 48 kHz audio; instead we
get a straight pass-through to ACM. The 32 kHz conversion is still
needed in the RecordedDataIsAvailable path until APM natively supports
48 kHz.
- Merge resampler improvements from ACM1 to ACM2. This allows ACM to
handle 44.1 kHz audio passed to VoE and was originally done here:
https://webrtc-codereview.appspot.com/1590004
- Reuse the RemixAndResample unit tests for DownConvertToCodecFormat.
- Remove unused functions from utility.cc.
BUG=3155,3000,b/12867572
TESTED=voe_cmd_test using both the OnDataAvailable and
RecordedDataIsAvailable paths, with a captured audio format of all
combinations of {44.1,48} kHz and {1,2} channels, running through all
codecs, and finally using both ACM1 and ACM2.
R=henrika@webrtc.org, turaj@webrtc.org, xians@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/11019005
git-svn-id: http://webrtc.googlecode.com/svn/trunk@5843 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-04-03 21:56:01 +00:00
|
|
|
MixWithSat(_audioFrame.data_,
|
|
|
|
|
_audioFrame.num_channels_,
|
|
|
|
|
fileBuffer.get(),
|
|
|
|
|
1,
|
|
|
|
|
fileSamples);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2012-03-29 10:39:44 +00:00
|
|
|
// Replace ACM audio with file.
|
|
|
|
|
// Currently file stream is always mono.
|
|
|
|
|
// TODO(xians): Change the code when FilePlayer supports real stereo.
|
2011-07-07 08:21:25 +00:00
|
|
|
_audioFrame.UpdateFrame(_channelId,
|
|
|
|
|
-1,
|
2012-03-29 10:39:44 +00:00
|
|
|
fileBuffer.get(),
|
2012-05-08 17:12:40 +00:00
|
|
|
fileSamples,
|
2011-07-07 08:21:25 +00:00
|
|
|
mixingFrequency,
|
|
|
|
|
AudioFrame::kNormalSpeech,
|
|
|
|
|
AudioFrame::kVadUnknown,
|
|
|
|
|
1);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::MixAudioWithFile(AudioFrame& audioFrame,
|
2013-05-14 08:31:39 +00:00
|
|
|
int mixingFrequency)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
assert(mixingFrequency <= 32000);
|
|
|
|
|
|
2014-04-25 23:10:28 +00:00
|
|
|
scoped_ptr<int16_t[]> fileBuffer(new int16_t[640]);
|
2012-05-08 17:12:40 +00:00
|
|
|
int fileSamples(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
{
|
2012-03-07 08:12:21 +00:00
|
|
|
CriticalSectionScoped cs(&_fileCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
if (_outputFilePlayerPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::MixAudioWithFile() file mixing failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We should get the frequency we ask for.
|
2012-03-29 10:39:44 +00:00
|
|
|
if (_outputFilePlayerPtr->Get10msAudioFromFile(fileBuffer.get(),
|
2011-07-07 08:21:25 +00:00
|
|
|
fileSamples,
|
|
|
|
|
mixingFrequency) == -1)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::MixAudioWithFile() file mixing failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
if (audioFrame.samples_per_channel_ == fileSamples)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2012-03-29 10:39:44 +00:00
|
|
|
// Currently file stream is always mono.
|
|
|
|
|
// TODO(xians): Change the code when FilePlayer supports real stereo.
|
Consolidate audio conversion from Channel and TransmitMixer.
Replace the two versions with a single DownConvertToCodecFormat. As
mentioned in comments, this could be further consolidated with
RemixAndResample but we should write a full audio converter class in
that case.
Along the way:
- Fix the bug present in Channel::Demultiplex with mono input and a
stereo codec.
- Remove the 32 kHz max from the OnDataAvailable path. This avoids a
48 -> 32 -> 48 conversion when VoE is passed 48 kHz audio; instead we
get a straight pass-through to ACM. The 32 kHz conversion is still
needed in the RecordedDataIsAvailable path until APM natively supports
48 kHz.
- Merge resampler improvements from ACM1 to ACM2. This allows ACM to
handle 44.1 kHz audio passed to VoE and was originally done here:
https://webrtc-codereview.appspot.com/1590004
- Reuse the RemixAndResample unit tests for DownConvertToCodecFormat.
- Remove unused functions from utility.cc.
BUG=3155,3000,b/12867572
TESTED=voe_cmd_test using both the OnDataAvailable and
RecordedDataIsAvailable paths, with a captured audio format of all
combinations of {44.1,48} kHz and {1,2} channels, running through all
codecs, and finally using both ACM1 and ACM2.
R=henrika@webrtc.org, turaj@webrtc.org, xians@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/11019005
git-svn-id: http://webrtc.googlecode.com/svn/trunk@5843 4adac7df-926f-26a2-2b94-8c16560cd09d
2014-04-03 21:56:01 +00:00
|
|
|
MixWithSat(audioFrame.data_,
|
|
|
|
|
audioFrame.num_channels_,
|
|
|
|
|
fileBuffer.get(),
|
|
|
|
|
1,
|
|
|
|
|
fileSamples);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId,_channelId),
|
2012-05-02 23:56:37 +00:00
|
|
|
"Channel::MixAudioWithFile() samples_per_channel_(%d) != "
|
2011-07-07 08:21:25 +00:00
|
|
|
"fileSamples(%d)",
|
2012-05-02 23:56:37 +00:00
|
|
|
audioFrame.samples_per_channel_, fileSamples);
|
2011-07-07 08:21:25 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Channel::InsertInbandDtmfTone()
|
|
|
|
|
{
|
2011-11-16 12:41:36 +00:00
|
|
|
// Check if we should start a new tone.
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_inbandDtmfQueue.PendingDtmf() &&
|
|
|
|
|
!_inbandDtmfGenerator.IsAddingTone() &&
|
|
|
|
|
_inbandDtmfGenerator.DelaySinceLastTone() >
|
|
|
|
|
kMinTelephoneEventSeparationMs)
|
|
|
|
|
{
|
2013-04-09 10:09:10 +00:00
|
|
|
int8_t eventCode(0);
|
|
|
|
|
uint16_t lengthMs(0);
|
|
|
|
|
uint8_t attenuationDb(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
eventCode = _inbandDtmfQueue.NextDtmf(&lengthMs, &attenuationDb);
|
|
|
|
|
_inbandDtmfGenerator.AddTone(eventCode, lengthMs, attenuationDb);
|
|
|
|
|
if (_playInbandDtmfEvent)
|
|
|
|
|
{
|
|
|
|
|
// Add tone to output mixer using a reduced length to minimize
|
|
|
|
|
// risk of echo.
|
|
|
|
|
_outputMixerPtr->PlayDtmfTone(eventCode, lengthMs - 80,
|
|
|
|
|
attenuationDb);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_inbandDtmfGenerator.IsAddingTone())
|
|
|
|
|
{
|
2013-04-09 10:09:10 +00:00
|
|
|
uint16_t frequency(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
_inbandDtmfGenerator.GetSampleRate(frequency);
|
|
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
if (frequency != _audioFrame.sample_rate_hz_)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
// Update sample rate of Dtmf tone since the mixing frequency
|
|
|
|
|
// has changed.
|
|
|
|
|
_inbandDtmfGenerator.SetSampleRate(
|
2013-04-09 10:09:10 +00:00
|
|
|
(uint16_t) (_audioFrame.sample_rate_hz_));
|
2011-07-07 08:21:25 +00:00
|
|
|
// Reset the tone to be added taking the new sample rate into
|
|
|
|
|
// account.
|
|
|
|
|
_inbandDtmfGenerator.ResetTone();
|
|
|
|
|
}
|
2013-01-22 04:44:30 +00:00
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int16_t toneBuffer[320];
|
|
|
|
|
uint16_t toneSamples(0);
|
2011-07-07 08:21:25 +00:00
|
|
|
// Get 10ms tone segment and set time since last tone to zero
|
|
|
|
|
if (_inbandDtmfGenerator.Get10msTone(toneBuffer, toneSamples) == -1)
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceWarning, kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::EncodeAndSend() inserting Dtmf failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-16 12:41:36 +00:00
|
|
|
// Replace mixed audio with DTMF tone.
|
2013-01-22 04:44:30 +00:00
|
|
|
for (int sample = 0;
|
2012-05-02 23:56:37 +00:00
|
|
|
sample < _audioFrame.samples_per_channel_;
|
2011-11-16 12:41:36 +00:00
|
|
|
sample++)
|
|
|
|
|
{
|
2013-01-22 04:44:30 +00:00
|
|
|
for (int channel = 0;
|
|
|
|
|
channel < _audioFrame.num_channels_;
|
2011-11-16 12:41:36 +00:00
|
|
|
channel++)
|
|
|
|
|
{
|
2013-01-22 04:44:30 +00:00
|
|
|
const int index = sample * _audioFrame.num_channels_ + channel;
|
|
|
|
|
_audioFrame.data_[index] = toneBuffer[sample];
|
2011-11-16 12:41:36 +00:00
|
|
|
}
|
|
|
|
|
}
|
2013-01-22 04:44:30 +00:00
|
|
|
|
2012-05-02 23:56:37 +00:00
|
|
|
assert(_audioFrame.samples_per_channel_ == toneSamples);
|
2011-07-07 08:21:25 +00:00
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
// Add 10ms to "delay-since-last-tone" counter
|
|
|
|
|
_inbandDtmfGenerator.UpdateDelaySinceLastTone();
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-09 10:09:10 +00:00
|
|
|
int32_t
|
2011-07-07 08:21:25 +00:00
|
|
|
Channel::SendPacketRaw(const void *data, int len, bool RTCP)
|
|
|
|
|
{
|
2013-10-18 21:10:51 +00:00
|
|
|
CriticalSectionScoped cs(&_callbackCritSect);
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_transportPtr == NULL)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!RTCP)
|
|
|
|
|
{
|
|
|
|
|
return _transportPtr->SendPacket(_channelId, data, len);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return _transportPtr->SendRTCPPacket(_channelId, data, len);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
// Called for incoming RTP packets after successful RTP header parsing.
|
|
|
|
|
void Channel::UpdatePacketDelay(uint32_t rtp_timestamp,
|
|
|
|
|
uint16_t sequence_number) {
|
|
|
|
|
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::UpdatePacketDelay(timestamp=%lu, sequenceNumber=%u)",
|
|
|
|
|
rtp_timestamp, sequence_number);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
// Get frequency of last received payload
|
2013-09-23 23:02:24 +00:00
|
|
|
int rtp_receive_frequency = audio_coding_->ReceiveFrequency();
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
CodecInst current_receive_codec;
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->ReceiveCodec(¤t_receive_codec) != 0) {
|
2013-04-11 20:23:35 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-05-22 20:39:43 +00:00
|
|
|
// Update the least required delay.
|
2013-09-23 23:02:24 +00:00
|
|
|
least_required_delay_ms_ = audio_coding_->LeastRequiredDelayMs();
|
2013-05-22 20:39:43 +00:00
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
if (STR_CASE_CMP("G722", current_receive_codec.plname) == 0) {
|
|
|
|
|
// Even though the actual sampling rate for G.722 audio is
|
|
|
|
|
// 16,000 Hz, the RTP clock rate for the G722 payload format is
|
|
|
|
|
// 8,000 Hz because that value was erroneously assigned in
|
|
|
|
|
// RFC 1890 and must remain unchanged for backward compatibility.
|
|
|
|
|
rtp_receive_frequency = 8000;
|
|
|
|
|
} else if (STR_CASE_CMP("opus", current_receive_codec.plname) == 0) {
|
|
|
|
|
// We are resampling Opus internally to 32,000 Hz until all our
|
|
|
|
|
// DSP routines can operate at 48,000 Hz, but the RTP clock
|
|
|
|
|
// rate for the Opus payload format is standardized to 48,000 Hz,
|
|
|
|
|
// because that is the maximum supported decoding sampling rate.
|
|
|
|
|
rtp_receive_frequency = 48000;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-12-13 21:05:07 +00:00
|
|
|
// |jitter_buffer_playout_timestamp_| updated in UpdatePlayoutTimestamp for
|
|
|
|
|
// every incoming packet.
|
|
|
|
|
uint32_t timestamp_diff_ms = (rtp_timestamp -
|
|
|
|
|
jitter_buffer_playout_timestamp_) / (rtp_receive_frequency / 1000);
|
2014-03-20 12:04:09 +00:00
|
|
|
if (!IsNewerTimestamp(rtp_timestamp, jitter_buffer_playout_timestamp_) ||
|
|
|
|
|
timestamp_diff_ms > (2 * kVoiceEngineMaxMinPlayoutDelayMs)) {
|
|
|
|
|
// If |jitter_buffer_playout_timestamp_| is newer than the incoming RTP
|
|
|
|
|
// timestamp, the resulting difference is negative, but is set to zero.
|
|
|
|
|
// This can happen when a network glitch causes a packet to arrive late,
|
|
|
|
|
// and during long comfort noise periods with clock drift.
|
|
|
|
|
timestamp_diff_ms = 0;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
uint16_t packet_delay_ms = (rtp_timestamp - _previousTimestamp) /
|
|
|
|
|
(rtp_receive_frequency / 1000);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
_previousTimestamp = rtp_timestamp;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2013-04-11 20:23:35 +00:00
|
|
|
if (timestamp_diff_ms == 0) return;
|
|
|
|
|
|
|
|
|
|
if (packet_delay_ms >= 10 && packet_delay_ms <= 60) {
|
|
|
|
|
_recPacketDelayMs = packet_delay_ms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_average_jitter_buffer_delay_us == 0) {
|
|
|
|
|
_average_jitter_buffer_delay_us = timestamp_diff_ms * 1000;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Filter average delay value using exponential filter (alpha is
|
|
|
|
|
// 7/8). We derive 1000 *_average_jitter_buffer_delay_us here (reduces
|
|
|
|
|
// risk of rounding error) and compensate for it in GetDelayEstimate()
|
|
|
|
|
// later.
|
|
|
|
|
_average_jitter_buffer_delay_us = (_average_jitter_buffer_delay_us * 7 +
|
|
|
|
|
1000 * timestamp_diff_ms + 500) / 8;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Channel::RegisterReceiveCodecsToRTPModule()
|
|
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
|
|
|
|
|
"Channel::RegisterReceiveCodecsToRTPModule()");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CodecInst codec;
|
2013-04-09 10:09:10 +00:00
|
|
|
const uint8_t nSupportedCodecs = AudioCodingModule::NumberOfCodecs();
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
for (int idx = 0; idx < nSupportedCodecs; idx++)
|
|
|
|
|
{
|
|
|
|
|
// Open up the RTP/RTCP receiver for all supported codecs
|
2013-09-23 23:02:24 +00:00
|
|
|
if ((audio_coding_->Codec(idx, &codec) == -1) ||
|
2013-08-15 23:38:54 +00:00
|
|
|
(rtp_receiver_->RegisterReceivePayload(
|
|
|
|
|
codec.plname,
|
|
|
|
|
codec.pltype,
|
|
|
|
|
codec.plfreq,
|
|
|
|
|
codec.channels,
|
|
|
|
|
(codec.rate < 0) ? 0 : codec.rate) == -1))
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
WEBRTC_TRACE(
|
|
|
|
|
kTraceWarning,
|
|
|
|
|
kTraceVoice,
|
|
|
|
|
VoEId(_instanceId, _channelId),
|
|
|
|
|
"Channel::RegisterReceiveCodecsToRTPModule() unable"
|
|
|
|
|
" to register %s (%d/%d/%d/%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 "
|
2011-09-15 20:49:50 +00:00
|
|
|
"(%d/%d/%d/%d) has been added to the RTP/RTCP "
|
2011-07-07 08:21:25 +00:00
|
|
|
"receiver",
|
|
|
|
|
codec.plname, codec.pltype, codec.plfreq,
|
|
|
|
|
codec.channels, codec.rate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 02:15:12 +00:00
|
|
|
int Channel::SetSecondarySendCodec(const CodecInst& codec,
|
|
|
|
|
int red_payload_type) {
|
2013-01-31 18:20:17 +00:00
|
|
|
// Sanity check for payload type.
|
|
|
|
|
if (red_payload_type < 0 || red_payload_type > 127) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_PLTYPE_ERROR, kTraceError,
|
|
|
|
|
"SetRedPayloadType() invalid RED payload type");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 02:15:12 +00:00
|
|
|
if (SetRedPayloadType(red_payload_type) < 0) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetSecondarySendCodec() Failed to register RED ACM");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterSecondarySendCodec(codec) < 0) {
|
2012-12-11 02:15:12 +00:00
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetSecondarySendCodec() Failed to register secondary send codec in "
|
|
|
|
|
"ACM");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Channel::RemoveSecondarySendCodec() {
|
2013-09-23 23:02:24 +00:00
|
|
|
audio_coding_->UnregisterSecondarySendCodec();
|
2012-12-11 02:15:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Channel::GetSecondarySendCodec(CodecInst* codec) {
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->SecondarySendCodec(codec) < 0) {
|
2012-12-11 02:15:12 +00:00
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"GetSecondarySendCodec() Failed to get secondary sent codec from ACM");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-31 18:20:17 +00:00
|
|
|
// Assuming this method is called with valid payload type.
|
2012-12-11 02:15:12 +00:00
|
|
|
int Channel::SetRedPayloadType(int red_payload_type) {
|
|
|
|
|
CodecInst codec;
|
|
|
|
|
bool found_red = false;
|
|
|
|
|
|
|
|
|
|
// Get default RED settings from the ACM database
|
|
|
|
|
const int num_codecs = AudioCodingModule::NumberOfCodecs();
|
|
|
|
|
for (int idx = 0; idx < num_codecs; idx++) {
|
2013-09-23 23:02:24 +00:00
|
|
|
audio_coding_->Codec(idx, &codec);
|
2012-12-11 02:15:12 +00:00
|
|
|
if (!STR_CASE_CMP(codec.plname, "RED")) {
|
|
|
|
|
found_red = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found_red) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_CODEC_ERROR, kTraceError,
|
|
|
|
|
"SetRedPayloadType() RED is not supported");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-31 18:34:19 +00:00
|
|
|
codec.pltype = red_payload_type;
|
2013-09-23 23:02:24 +00:00
|
|
|
if (audio_coding_->RegisterSendCodec(codec) < 0) {
|
2012-12-11 02:15:12 +00:00
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetRedPayloadType() RED registration in ACM module failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_rtpRtcpModule->SetSendREDPayloadType(red_payload_type) != 0) {
|
|
|
|
|
_engineStatisticsPtr->SetLastError(
|
|
|
|
|
VE_RTP_RTCP_MODULE_ERROR, kTraceError,
|
|
|
|
|
"SetRedPayloadType() RED registration in RTP/RTCP module failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-06 23:49:08 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2014-05-28 09:52:06 +00:00
|
|
|
|
2013-07-03 15:12:26 +00:00
|
|
|
} // namespace voe
|
|
|
|
|
} // namespace webrtc
|