1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1040 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "CSFLog.h" 1.9 +#include "nspr.h" 1.10 + 1.11 +#ifdef HAVE_NETINET_IN_H 1.12 +#include <netinet/in.h> 1.13 +#elif defined XP_WIN 1.14 +#include <winsock2.h> 1.15 +#endif 1.16 + 1.17 +#include "AudioConduit.h" 1.18 +#include "nsCOMPtr.h" 1.19 +#include "mozilla/Services.h" 1.20 +#include "nsServiceManagerUtils.h" 1.21 +#include "nsIPrefService.h" 1.22 +#include "nsIPrefBranch.h" 1.23 +#include "nsThreadUtils.h" 1.24 +#ifdef MOZILLA_INTERNAL_API 1.25 +#include "Latency.h" 1.26 +#include "mozilla/Telemetry.h" 1.27 +#endif 1.28 + 1.29 +#include "webrtc/voice_engine/include/voe_errors.h" 1.30 +#include "webrtc/system_wrappers/interface/clock.h" 1.31 + 1.32 +#ifdef MOZ_WIDGET_ANDROID 1.33 +#include "AndroidJNIWrapper.h" 1.34 +#endif 1.35 + 1.36 +namespace mozilla { 1.37 + 1.38 +static const char* logTag ="WebrtcAudioSessionConduit"; 1.39 + 1.40 +// 32 bytes is what WebRTC CodecInst expects 1.41 +const unsigned int WebrtcAudioConduit::CODEC_PLNAME_SIZE = 32; 1.42 + 1.43 +/** 1.44 + * Factory Method for AudioConduit 1.45 + */ 1.46 +mozilla::RefPtr<AudioSessionConduit> AudioSessionConduit::Create(AudioSessionConduit *aOther) 1.47 +{ 1.48 + CSFLogDebug(logTag, "%s ", __FUNCTION__); 1.49 +#ifdef MOZILLA_INTERNAL_API 1.50 + // unit tests create their own "main thread" 1.51 + NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); 1.52 +#endif 1.53 + 1.54 + WebrtcAudioConduit* obj = new WebrtcAudioConduit(); 1.55 + if(obj->Init(static_cast<WebrtcAudioConduit*>(aOther)) != kMediaConduitNoError) 1.56 + { 1.57 + CSFLogError(logTag, "%s AudioConduit Init Failed ", __FUNCTION__); 1.58 + delete obj; 1.59 + return nullptr; 1.60 + } 1.61 + CSFLogDebug(logTag, "%s Successfully created AudioConduit ", __FUNCTION__); 1.62 + return obj; 1.63 +} 1.64 + 1.65 +/** 1.66 + * Destruction defines for our super-classes 1.67 + */ 1.68 +WebrtcAudioConduit::~WebrtcAudioConduit() 1.69 +{ 1.70 +#ifdef MOZILLA_INTERNAL_API 1.71 + // unit tests create their own "main thread" 1.72 + NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); 1.73 +#endif 1.74 + 1.75 + CSFLogDebug(logTag, "%s ", __FUNCTION__); 1.76 + for(std::vector<AudioCodecConfig*>::size_type i=0;i < mRecvCodecList.size();i++) 1.77 + { 1.78 + delete mRecvCodecList[i]; 1.79 + } 1.80 + delete mCurSendCodecConfig; 1.81 + 1.82 + // The first one of a pair to be deleted shuts down media for both 1.83 + if(mPtrVoEXmedia) 1.84 + { 1.85 + if (!mShutDown) { 1.86 + mPtrVoEXmedia->SetExternalRecordingStatus(false); 1.87 + mPtrVoEXmedia->SetExternalPlayoutStatus(false); 1.88 + } 1.89 + } 1.90 + 1.91 + //Deal with the transport 1.92 + if(mPtrVoENetwork) 1.93 + { 1.94 + if (!mShutDown) { 1.95 + mPtrVoENetwork->DeRegisterExternalTransport(mChannel); 1.96 + } 1.97 + } 1.98 + 1.99 + if(mPtrVoEBase) 1.100 + { 1.101 + if (!mShutDown) { 1.102 + mPtrVoEBase->StopPlayout(mChannel); 1.103 + mPtrVoEBase->StopSend(mChannel); 1.104 + mPtrVoEBase->StopReceive(mChannel); 1.105 + mPtrVoEBase->DeleteChannel(mChannel); 1.106 + mPtrVoEBase->Terminate(); 1.107 + } 1.108 + } 1.109 + 1.110 + if (mOtherDirection) 1.111 + { 1.112 + // mOtherDirection owns these now! 1.113 + mOtherDirection->mOtherDirection = nullptr; 1.114 + // let other side we terminated the channel 1.115 + mOtherDirection->mShutDown = true; 1.116 + mVoiceEngine = nullptr; 1.117 + } else { 1.118 + // We shouldn't delete the VoiceEngine until all these are released! 1.119 + // And we can't use a Scoped ptr, since the order is arbitrary 1.120 + mPtrVoENetwork = nullptr; 1.121 + mPtrVoEBase = nullptr; 1.122 + mPtrVoECodec = nullptr; 1.123 + mPtrVoEXmedia = nullptr; 1.124 + mPtrVoEProcessing = nullptr; 1.125 + mPtrVoEVideoSync = nullptr; 1.126 + mPtrVoERTP_RTCP = nullptr; 1.127 + mPtrRTP = nullptr; 1.128 + 1.129 + // only one opener can call Delete. Have it be the last to close. 1.130 + if(mVoiceEngine) 1.131 + { 1.132 + webrtc::VoiceEngine::Delete(mVoiceEngine); 1.133 + } 1.134 + } 1.135 +} 1.136 + 1.137 +bool WebrtcAudioConduit::GetLocalSSRC(unsigned int* ssrc) { 1.138 + return !mPtrRTP->GetLocalSSRC(mChannel, *ssrc); 1.139 +} 1.140 + 1.141 +bool WebrtcAudioConduit::GetRemoteSSRC(unsigned int* ssrc) { 1.142 + return !mPtrRTP->GetRemoteSSRC(mChannel, *ssrc); 1.143 +} 1.144 + 1.145 +bool WebrtcAudioConduit::GetAVStats(int32_t* jitterBufferDelayMs, 1.146 + int32_t* playoutBufferDelayMs, 1.147 + int32_t* avSyncOffsetMs) { 1.148 + return !mPtrVoEVideoSync->GetDelayEstimate(mChannel, 1.149 + jitterBufferDelayMs, 1.150 + playoutBufferDelayMs, 1.151 + avSyncOffsetMs); 1.152 +} 1.153 + 1.154 +bool WebrtcAudioConduit::GetRTPStats(unsigned int* jitterMs, 1.155 + unsigned int* cumulativeLost) { 1.156 + unsigned int maxJitterMs = 0; 1.157 + unsigned int discardedPackets; 1.158 + *jitterMs = 0; 1.159 + *cumulativeLost = 0; 1.160 + return !mPtrRTP->GetRTPStatistics(mChannel, *jitterMs, maxJitterMs, 1.161 + discardedPackets, *cumulativeLost); 1.162 +} 1.163 + 1.164 +DOMHighResTimeStamp 1.165 +NTPtoDOMHighResTimeStamp(uint32_t ntpHigh, uint32_t ntpLow) { 1.166 + return (uint32_t(ntpHigh - webrtc::kNtpJan1970) + 1.167 + double(ntpLow) / webrtc::kMagicNtpFractionalUnit) * 1000; 1.168 +} 1.169 + 1.170 +bool WebrtcAudioConduit::GetRTCPReceiverReport(DOMHighResTimeStamp* timestamp, 1.171 + uint32_t* jitterMs, 1.172 + uint32_t* packetsReceived, 1.173 + uint64_t* bytesReceived, 1.174 + uint32_t* cumulativeLost, 1.175 + int32_t* rttMs) { 1.176 + uint32_t ntpHigh, ntpLow; 1.177 + uint16_t fractionLost; 1.178 + bool result = !mPtrRTP->GetRemoteRTCPReceiverInfo(mChannel, ntpHigh, ntpLow, 1.179 + *packetsReceived, 1.180 + *bytesReceived, 1.181 + *jitterMs, 1.182 + fractionLost, 1.183 + *cumulativeLost, 1.184 + *rttMs); 1.185 + if (result) { 1.186 + *timestamp = NTPtoDOMHighResTimeStamp(ntpHigh, ntpLow); 1.187 + } 1.188 + return result; 1.189 +} 1.190 + 1.191 +bool WebrtcAudioConduit::GetRTCPSenderReport(DOMHighResTimeStamp* timestamp, 1.192 + unsigned int* packetsSent, 1.193 + uint64_t* bytesSent) { 1.194 + struct webrtc::SenderInfo senderInfo; 1.195 + bool result = !mPtrRTP->GetRemoteRTCPSenderInfo(mChannel, &senderInfo); 1.196 + if (result) { 1.197 + *timestamp = NTPtoDOMHighResTimeStamp(senderInfo.NTP_timestamp_high, 1.198 + senderInfo.NTP_timestamp_low); 1.199 + *packetsSent = senderInfo.sender_packet_count; 1.200 + *bytesSent = senderInfo.sender_octet_count; 1.201 + } 1.202 + return result; 1.203 +} 1.204 + 1.205 +/* 1.206 + * WebRTCAudioConduit Implementation 1.207 + */ 1.208 +MediaConduitErrorCode WebrtcAudioConduit::Init(WebrtcAudioConduit *other) 1.209 +{ 1.210 + CSFLogDebug(logTag, "%s this=%p other=%p", __FUNCTION__, this, other); 1.211 + 1.212 + if (other) { 1.213 + MOZ_ASSERT(!other->mOtherDirection); 1.214 + other->mOtherDirection = this; 1.215 + mOtherDirection = other; 1.216 + 1.217 + // only one can call ::Create()/GetVoiceEngine() 1.218 + MOZ_ASSERT(other->mVoiceEngine); 1.219 + mVoiceEngine = other->mVoiceEngine; 1.220 + } else { 1.221 +#ifdef MOZ_WIDGET_ANDROID 1.222 + jobject context = jsjni_GetGlobalContextRef(); 1.223 + 1.224 + // get the JVM 1.225 + JavaVM *jvm = jsjni_GetVM(); 1.226 + JNIEnv* jenv = jsjni_GetJNIForThread(); 1.227 + 1.228 + if (webrtc::VoiceEngine::SetAndroidObjects(jvm, jenv, (void*)context) != 0) { 1.229 + CSFLogError(logTag, "%s Unable to set Android objects", __FUNCTION__); 1.230 + return kMediaConduitSessionNotInited; 1.231 + } 1.232 +#endif 1.233 + 1.234 + // Per WebRTC APIs below function calls return nullptr on failure 1.235 + if(!(mVoiceEngine = webrtc::VoiceEngine::Create())) 1.236 + { 1.237 + CSFLogError(logTag, "%s Unable to create voice engine", __FUNCTION__); 1.238 + return kMediaConduitSessionNotInited; 1.239 + } 1.240 + 1.241 + PRLogModuleInfo *logs = GetWebRTCLogInfo(); 1.242 + if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { 1.243 + // no need to a critical section or lock here 1.244 + gWebrtcTraceLoggingOn = 1; 1.245 + 1.246 + const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); 1.247 + if (!file) { 1.248 + file = "WebRTC.log"; 1.249 + } 1.250 + CSFLogDebug(logTag, "%s Logging webrtc to %s level %d", __FUNCTION__, 1.251 + file, logs->level); 1.252 + mVoiceEngine->SetTraceFilter(logs->level); 1.253 + mVoiceEngine->SetTraceFile(file); 1.254 + } 1.255 + } 1.256 + 1.257 + if(!(mPtrVoEBase = VoEBase::GetInterface(mVoiceEngine))) 1.258 + { 1.259 + CSFLogError(logTag, "%s Unable to initialize VoEBase", __FUNCTION__); 1.260 + return kMediaConduitSessionNotInited; 1.261 + } 1.262 + 1.263 + if(!(mPtrVoENetwork = VoENetwork::GetInterface(mVoiceEngine))) 1.264 + { 1.265 + CSFLogError(logTag, "%s Unable to initialize VoENetwork", __FUNCTION__); 1.266 + return kMediaConduitSessionNotInited; 1.267 + } 1.268 + 1.269 + if(!(mPtrVoECodec = VoECodec::GetInterface(mVoiceEngine))) 1.270 + { 1.271 + CSFLogError(logTag, "%s Unable to initialize VoEBCodec", __FUNCTION__); 1.272 + return kMediaConduitSessionNotInited; 1.273 + } 1.274 + 1.275 + if(!(mPtrVoEProcessing = VoEAudioProcessing::GetInterface(mVoiceEngine))) 1.276 + { 1.277 + CSFLogError(logTag, "%s Unable to initialize VoEProcessing", __FUNCTION__); 1.278 + return kMediaConduitSessionNotInited; 1.279 + } 1.280 + if(!(mPtrVoEXmedia = VoEExternalMedia::GetInterface(mVoiceEngine))) 1.281 + { 1.282 + CSFLogError(logTag, "%s Unable to initialize VoEExternalMedia", __FUNCTION__); 1.283 + return kMediaConduitSessionNotInited; 1.284 + } 1.285 + if(!(mPtrVoERTP_RTCP = VoERTP_RTCP::GetInterface(mVoiceEngine))) 1.286 + { 1.287 + CSFLogError(logTag, "%s Unable to initialize VoERTP_RTCP", __FUNCTION__); 1.288 + return kMediaConduitSessionNotInited; 1.289 + } 1.290 + 1.291 + if(!(mPtrVoEVideoSync = VoEVideoSync::GetInterface(mVoiceEngine))) 1.292 + { 1.293 + CSFLogError(logTag, "%s Unable to initialize VoEVideoSync", __FUNCTION__); 1.294 + return kMediaConduitSessionNotInited; 1.295 + } 1.296 + if (!(mPtrRTP = webrtc::VoERTP_RTCP::GetInterface(mVoiceEngine))) 1.297 + { 1.298 + CSFLogError(logTag, "%s Unable to get audio RTP/RTCP interface ", 1.299 + __FUNCTION__); 1.300 + return kMediaConduitSessionNotInited; 1.301 + } 1.302 + 1.303 + if (other) { 1.304 + mChannel = other->mChannel; 1.305 + } else { 1.306 + // init the engine with our audio device layer 1.307 + if(mPtrVoEBase->Init() == -1) 1.308 + { 1.309 + CSFLogError(logTag, "%s VoiceEngine Base Not Initialized", __FUNCTION__); 1.310 + return kMediaConduitSessionNotInited; 1.311 + } 1.312 + 1.313 + if( (mChannel = mPtrVoEBase->CreateChannel()) == -1) 1.314 + { 1.315 + CSFLogError(logTag, "%s VoiceEngine Channel creation failed",__FUNCTION__); 1.316 + return kMediaConduitChannelError; 1.317 + } 1.318 + 1.319 + CSFLogDebug(logTag, "%s Channel Created %d ",__FUNCTION__, mChannel); 1.320 + 1.321 + if(mPtrVoENetwork->RegisterExternalTransport(mChannel, *this) == -1) 1.322 + { 1.323 + CSFLogError(logTag, "%s VoiceEngine, External Transport Failed",__FUNCTION__); 1.324 + return kMediaConduitTransportRegistrationFail; 1.325 + } 1.326 + 1.327 + if(mPtrVoEXmedia->SetExternalRecordingStatus(true) == -1) 1.328 + { 1.329 + CSFLogError(logTag, "%s SetExternalRecordingStatus Failed %d",__FUNCTION__, 1.330 + mPtrVoEBase->LastError()); 1.331 + return kMediaConduitExternalPlayoutError; 1.332 + } 1.333 + 1.334 + if(mPtrVoEXmedia->SetExternalPlayoutStatus(true) == -1) 1.335 + { 1.336 + CSFLogError(logTag, "%s SetExternalPlayoutStatus Failed %d ",__FUNCTION__, 1.337 + mPtrVoEBase->LastError()); 1.338 + return kMediaConduitExternalRecordingError; 1.339 + } 1.340 + CSFLogDebug(logTag , "%s AudioSessionConduit Initialization Done (%p)",__FUNCTION__, this); 1.341 + } 1.342 + return kMediaConduitNoError; 1.343 +} 1.344 + 1.345 +// AudioSessionConduit Implementation 1.346 +MediaConduitErrorCode 1.347 +WebrtcAudioConduit::AttachTransport(mozilla::RefPtr<TransportInterface> aTransport) 1.348 +{ 1.349 + CSFLogDebug(logTag, "%s ", __FUNCTION__); 1.350 + 1.351 + if(!aTransport) 1.352 + { 1.353 + CSFLogError(logTag, "%s NULL Transport", __FUNCTION__); 1.354 + return kMediaConduitInvalidTransport; 1.355 + } 1.356 + // set the transport 1.357 + mTransport = aTransport; 1.358 + return kMediaConduitNoError; 1.359 +} 1.360 + 1.361 +MediaConduitErrorCode 1.362 +WebrtcAudioConduit::ConfigureSendMediaCodec(const AudioCodecConfig* codecConfig) 1.363 +{ 1.364 + CSFLogDebug(logTag, "%s ", __FUNCTION__); 1.365 + MediaConduitErrorCode condError = kMediaConduitNoError; 1.366 + int error = 0;//webrtc engine errors 1.367 + webrtc::CodecInst cinst; 1.368 + 1.369 + //validate codec param 1.370 + if((condError = ValidateCodecConfig(codecConfig, true)) != kMediaConduitNoError) 1.371 + { 1.372 + return condError; 1.373 + } 1.374 + 1.375 + //are we transmitting already, stop and apply the send codec 1.376 + if(mEngineTransmitting) 1.377 + { 1.378 + CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__); 1.379 + if(mPtrVoEBase->StopSend(mChannel) == -1) 1.380 + { 1.381 + CSFLogError(logTag, "%s StopSend() Failed %d ", __FUNCTION__, 1.382 + mPtrVoEBase->LastError()); 1.383 + return kMediaConduitUnknownError; 1.384 + } 1.385 + } 1.386 + 1.387 + mEngineTransmitting = false; 1.388 + 1.389 + if(!CodecConfigToWebRTCCodec(codecConfig,cinst)) 1.390 + { 1.391 + CSFLogError(logTag,"%s CodecConfig to WebRTC Codec Failed ",__FUNCTION__); 1.392 + return kMediaConduitMalformedArgument; 1.393 + } 1.394 + 1.395 + if(mPtrVoECodec->SetSendCodec(mChannel, cinst) == -1) 1.396 + { 1.397 + error = mPtrVoEBase->LastError(); 1.398 + CSFLogError(logTag, "%s SetSendCodec - Invalid Codec %d ",__FUNCTION__, 1.399 + error); 1.400 + 1.401 + if(error == VE_CANNOT_SET_SEND_CODEC || error == VE_CODEC_ERROR) 1.402 + { 1.403 + CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__); 1.404 + return kMediaConduitInvalidSendCodec; 1.405 + } 1.406 + CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__, 1.407 + mPtrVoEBase->LastError()); 1.408 + return kMediaConduitUnknownError; 1.409 + } 1.410 + 1.411 +#ifdef MOZILLA_INTERNAL_API 1.412 + // TEMPORARY - see bug 694814 comment 2 1.413 + nsresult rv; 1.414 + nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); 1.415 + if (NS_SUCCEEDED(rv)) { 1.416 + nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); 1.417 + 1.418 + if (branch) { 1.419 + branch->GetIntPref("media.peerconnection.capture_delay", &mCaptureDelay); 1.420 + } 1.421 + } 1.422 +#endif 1.423 + 1.424 + //Let's Send Transport State-machine on the Engine 1.425 + if(mPtrVoEBase->StartSend(mChannel) == -1) 1.426 + { 1.427 + error = mPtrVoEBase->LastError(); 1.428 + CSFLogError(logTag, "%s StartSend failed %d", __FUNCTION__, error); 1.429 + return kMediaConduitUnknownError; 1.430 + } 1.431 + 1.432 + //Copy the applied config for future reference. 1.433 + delete mCurSendCodecConfig; 1.434 + 1.435 + mCurSendCodecConfig = new AudioCodecConfig(codecConfig->mType, 1.436 + codecConfig->mName, 1.437 + codecConfig->mFreq, 1.438 + codecConfig->mPacSize, 1.439 + codecConfig->mChannels, 1.440 + codecConfig->mRate, 1.441 + codecConfig->mLoadManager); 1.442 + 1.443 + mEngineTransmitting = true; 1.444 + return kMediaConduitNoError; 1.445 +} 1.446 + 1.447 +MediaConduitErrorCode 1.448 +WebrtcAudioConduit::ConfigureRecvMediaCodecs( 1.449 + const std::vector<AudioCodecConfig*>& codecConfigList) 1.450 +{ 1.451 + CSFLogDebug(logTag, "%s ", __FUNCTION__); 1.452 + MediaConduitErrorCode condError = kMediaConduitNoError; 1.453 + int error = 0; //webrtc engine errors 1.454 + bool success = false; 1.455 + 1.456 + // Are we receiving already? If so, stop receiving and playout 1.457 + // since we can't apply new recv codec when the engine is playing. 1.458 + if(mEngineReceiving) 1.459 + { 1.460 + CSFLogDebug(logTag, "%s Engine Already Receiving. Attemping to Stop ", __FUNCTION__); 1.461 + // AudioEngine doesn't fail fatally on stopping reception. Ref:voe_errors.h. 1.462 + // hence we need not be strict in failing here on errors 1.463 + mPtrVoEBase->StopReceive(mChannel); 1.464 + CSFLogDebug(logTag, "%s Attemping to Stop playout ", __FUNCTION__); 1.465 + if(mPtrVoEBase->StopPlayout(mChannel) == -1) 1.466 + { 1.467 + if( mPtrVoEBase->LastError() == VE_CANNOT_STOP_PLAYOUT) 1.468 + { 1.469 + CSFLogDebug(logTag, "%s Stop-Playout Failed %d", __FUNCTION__, mPtrVoEBase->LastError()); 1.470 + return kMediaConduitPlayoutError; 1.471 + } 1.472 + } 1.473 + } 1.474 + 1.475 + mEngineReceiving = false; 1.476 + 1.477 + if(codecConfigList.empty()) 1.478 + { 1.479 + CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__); 1.480 + return kMediaConduitMalformedArgument; 1.481 + } 1.482 + 1.483 + // Try Applying the codecs in the list. 1.484 + // We succeed if at least one codec was applied and reception was 1.485 + // started successfully. 1.486 + for(std::vector<AudioCodecConfig*>::size_type i=0 ;i<codecConfigList.size();i++) 1.487 + { 1.488 + //if the codec param is invalid or diplicate, return error 1.489 + if((condError = ValidateCodecConfig(codecConfigList[i],false)) != kMediaConduitNoError) 1.490 + { 1.491 + return condError; 1.492 + } 1.493 + 1.494 + webrtc::CodecInst cinst; 1.495 + if(!CodecConfigToWebRTCCodec(codecConfigList[i],cinst)) 1.496 + { 1.497 + CSFLogError(logTag,"%s CodecConfig to WebRTC Codec Failed ",__FUNCTION__); 1.498 + continue; 1.499 + } 1.500 + 1.501 + if(mPtrVoECodec->SetRecPayloadType(mChannel,cinst) == -1) 1.502 + { 1.503 + error = mPtrVoEBase->LastError(); 1.504 + CSFLogError(logTag, "%s SetRecvCodec Failed %d ",__FUNCTION__, error); 1.505 + continue; 1.506 + } else { 1.507 + CSFLogDebug(logTag, "%s Successfully Set RecvCodec %s", __FUNCTION__, 1.508 + codecConfigList[i]->mName.c_str()); 1.509 + //copy this to local database 1.510 + if(CopyCodecToDB(codecConfigList[i])) 1.511 + { 1.512 + success = true; 1.513 + } else { 1.514 + CSFLogError(logTag,"%s Unable to updated Codec Database", __FUNCTION__); 1.515 + return kMediaConduitUnknownError; 1.516 + } 1.517 + 1.518 + } 1.519 + 1.520 + } //end for 1.521 + 1.522 + if(!success) 1.523 + { 1.524 + CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__); 1.525 + return kMediaConduitInvalidReceiveCodec; 1.526 + } 1.527 + 1.528 + //If we are here, atleast one codec should have been set 1.529 + if(mPtrVoEBase->StartReceive(mChannel) == -1) 1.530 + { 1.531 + error = mPtrVoEBase->LastError(); 1.532 + CSFLogError(logTag , "%s StartReceive Failed %d ",__FUNCTION__, error); 1.533 + if(error == VE_RECV_SOCKET_ERROR) 1.534 + { 1.535 + return kMediaConduitSocketError; 1.536 + } 1.537 + return kMediaConduitUnknownError; 1.538 + } 1.539 + 1.540 + 1.541 + if(mPtrVoEBase->StartPlayout(mChannel) == -1) 1.542 + { 1.543 + CSFLogError(logTag, "%s Starting playout Failed", __FUNCTION__); 1.544 + return kMediaConduitPlayoutError; 1.545 + } 1.546 + //we should be good here for setting this. 1.547 + mEngineReceiving = true; 1.548 + DumpCodecDB(); 1.549 + return kMediaConduitNoError; 1.550 +} 1.551 +MediaConduitErrorCode 1.552 +WebrtcAudioConduit::EnableAudioLevelExtension(bool enabled, uint8_t id) 1.553 +{ 1.554 + CSFLogDebug(logTag, "%s %d %d ", __FUNCTION__, enabled, id); 1.555 + 1.556 + if (mPtrVoERTP_RTCP->SetRTPAudioLevelIndicationStatus(mChannel, enabled, id) == -1) 1.557 + { 1.558 + CSFLogError(logTag, "%s SetRTPAudioLevelIndicationStatus Failed", __FUNCTION__); 1.559 + return kMediaConduitUnknownError; 1.560 + } 1.561 + 1.562 + return kMediaConduitNoError; 1.563 +} 1.564 + 1.565 +MediaConduitErrorCode 1.566 +WebrtcAudioConduit::SendAudioFrame(const int16_t audio_data[], 1.567 + int32_t lengthSamples, 1.568 + int32_t samplingFreqHz, 1.569 + int32_t capture_delay) 1.570 +{ 1.571 + CSFLogDebug(logTag, "%s ", __FUNCTION__); 1.572 + // Following checks need to be performed 1.573 + // 1. Non null audio buffer pointer, 1.574 + // 2. invalid sampling frequency - less than 0 or unsupported ones 1.575 + // 3. Appropriate Sample Length for 10 ms audio-frame. This represents 1.576 + // block size the VoiceEngine feeds into encoder for passed in audio-frame 1.577 + // Ex: for 16000 sampling rate , valid block-length is 160 1.578 + // Similarly for 32000 sampling rate, valid block length is 320 1.579 + // We do the check by the verify modular operator below to be zero 1.580 + 1.581 + if(!audio_data || (lengthSamples <= 0) || 1.582 + (IsSamplingFreqSupported(samplingFreqHz) == false) || 1.583 + ((lengthSamples % (samplingFreqHz / 100) != 0)) ) 1.584 + { 1.585 + CSFLogError(logTag, "%s Invalid Parameters ",__FUNCTION__); 1.586 + MOZ_ASSERT(PR_FALSE); 1.587 + return kMediaConduitMalformedArgument; 1.588 + } 1.589 + 1.590 + //validate capture time 1.591 + if(capture_delay < 0 ) 1.592 + { 1.593 + CSFLogError(logTag,"%s Invalid Capture Delay ", __FUNCTION__); 1.594 + MOZ_ASSERT(PR_FALSE); 1.595 + return kMediaConduitMalformedArgument; 1.596 + } 1.597 + 1.598 + // if transmission is not started .. conduit cannot insert frames 1.599 + if(!mEngineTransmitting) 1.600 + { 1.601 + CSFLogError(logTag, "%s Engine not transmitting ", __FUNCTION__); 1.602 + return kMediaConduitSessionNotInited; 1.603 + } 1.604 + 1.605 +#ifdef MOZILLA_INTERNAL_API 1.606 + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { 1.607 + struct Processing insert = { TimeStamp::Now(), 0 }; 1.608 + mProcessing.AppendElement(insert); 1.609 + } 1.610 +#endif 1.611 + 1.612 + capture_delay = mCaptureDelay; 1.613 + //Insert the samples 1.614 + if(mPtrVoEXmedia->ExternalRecordingInsertData(audio_data, 1.615 + lengthSamples, 1.616 + samplingFreqHz, 1.617 + capture_delay) == -1) 1.618 + { 1.619 + int error = mPtrVoEBase->LastError(); 1.620 + CSFLogError(logTag, "%s Inserting audio data Failed %d", __FUNCTION__, error); 1.621 + if(error == VE_RUNTIME_REC_ERROR) 1.622 + { 1.623 + return kMediaConduitRecordingError; 1.624 + } 1.625 + return kMediaConduitUnknownError; 1.626 + } 1.627 + // we should be good here 1.628 + return kMediaConduitNoError; 1.629 +} 1.630 + 1.631 +MediaConduitErrorCode 1.632 +WebrtcAudioConduit::GetAudioFrame(int16_t speechData[], 1.633 + int32_t samplingFreqHz, 1.634 + int32_t capture_delay, 1.635 + int& lengthSamples) 1.636 +{ 1.637 + 1.638 + CSFLogDebug(logTag, "%s ", __FUNCTION__); 1.639 + unsigned int numSamples = 0; 1.640 + 1.641 + //validate params 1.642 + if(!speechData ) 1.643 + { 1.644 + CSFLogError(logTag,"%s Null Audio Buffer Pointer", __FUNCTION__); 1.645 + MOZ_ASSERT(PR_FALSE); 1.646 + return kMediaConduitMalformedArgument; 1.647 + } 1.648 + 1.649 + // Validate sample length 1.650 + if((numSamples = GetNum10msSamplesForFrequency(samplingFreqHz)) == 0 ) 1.651 + { 1.652 + CSFLogError(logTag,"%s Invalid Sampling Frequency ", __FUNCTION__); 1.653 + MOZ_ASSERT(PR_FALSE); 1.654 + return kMediaConduitMalformedArgument; 1.655 + } 1.656 + 1.657 + //validate capture time 1.658 + if(capture_delay < 0 ) 1.659 + { 1.660 + CSFLogError(logTag,"%s Invalid Capture Delay ", __FUNCTION__); 1.661 + MOZ_ASSERT(PR_FALSE); 1.662 + return kMediaConduitMalformedArgument; 1.663 + } 1.664 + 1.665 + //Conduit should have reception enabled before we ask for decoded 1.666 + // samples 1.667 + if(!mEngineReceiving) 1.668 + { 1.669 + CSFLogError(logTag, "%s Engine not Receiving ", __FUNCTION__); 1.670 + return kMediaConduitSessionNotInited; 1.671 + } 1.672 + 1.673 + 1.674 + lengthSamples = 0; //output paramter 1.675 + 1.676 + if(mPtrVoEXmedia->ExternalPlayoutGetData( speechData, 1.677 + samplingFreqHz, 1.678 + capture_delay, 1.679 + lengthSamples) == -1) 1.680 + { 1.681 + int error = mPtrVoEBase->LastError(); 1.682 + CSFLogError(logTag, "%s Getting audio data Failed %d", __FUNCTION__, error); 1.683 + if(error == VE_RUNTIME_PLAY_ERROR) 1.684 + { 1.685 + return kMediaConduitPlayoutError; 1.686 + } 1.687 + return kMediaConduitUnknownError; 1.688 + } 1.689 + 1.690 + // Not #ifdef DEBUG or on a log module so we can use it for about:webrtc/etc 1.691 + mSamples += lengthSamples; 1.692 + if (mSamples >= mLastSyncLog + samplingFreqHz) { 1.693 + int jitter_buffer_delay_ms; 1.694 + int playout_buffer_delay_ms; 1.695 + int avsync_offset_ms; 1.696 + if (GetAVStats(&jitter_buffer_delay_ms, 1.697 + &playout_buffer_delay_ms, 1.698 + &avsync_offset_ms)) { 1.699 +#ifdef MOZILLA_INTERNAL_API 1.700 + if (avsync_offset_ms < 0) { 1.701 + Telemetry::Accumulate(Telemetry::WEBRTC_AVSYNC_WHEN_VIDEO_LAGS_AUDIO_MS, 1.702 + -avsync_offset_ms); 1.703 + } else { 1.704 + Telemetry::Accumulate(Telemetry::WEBRTC_AVSYNC_WHEN_AUDIO_LAGS_VIDEO_MS, 1.705 + avsync_offset_ms); 1.706 + } 1.707 +#endif 1.708 + CSFLogError(logTag, 1.709 + "A/V sync: sync delta: %dms, audio jitter delay %dms, playout delay %dms", 1.710 + avsync_offset_ms, jitter_buffer_delay_ms, playout_buffer_delay_ms); 1.711 + } else { 1.712 + CSFLogError(logTag, "A/V sync: GetAVStats failed"); 1.713 + } 1.714 + mLastSyncLog = mSamples; 1.715 + } 1.716 + 1.717 +#ifdef MOZILLA_INTERNAL_API 1.718 + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { 1.719 + if (mProcessing.Length() > 0) { 1.720 + unsigned int now; 1.721 + mPtrVoEVideoSync->GetPlayoutTimestamp(mChannel, now); 1.722 + if (static_cast<uint32_t>(now) != mLastTimestamp) { 1.723 + mLastTimestamp = static_cast<uint32_t>(now); 1.724 + // Find the block that includes this timestamp in the network input 1.725 + while (mProcessing.Length() > 0) { 1.726 + // FIX! assumes 20ms @ 48000Hz 1.727 + // FIX handle wrap-around 1.728 + if (mProcessing[0].mRTPTimeStamp + 20*(48000/1000) >= now) { 1.729 + TimeDuration t = TimeStamp::Now() - mProcessing[0].mTimeStamp; 1.730 + // Wrap-around? 1.731 + int64_t delta = t.ToMilliseconds() + (now - mProcessing[0].mRTPTimeStamp)/(48000/1000); 1.732 + LogTime(AsyncLatencyLogger::AudioRecvRTP, ((uint64_t) this), delta); 1.733 + break; 1.734 + } 1.735 + mProcessing.RemoveElementAt(0); 1.736 + } 1.737 + } 1.738 + } 1.739 + } 1.740 +#endif 1.741 + CSFLogDebug(logTag,"%s GetAudioFrame:Got samples: length %d ",__FUNCTION__, 1.742 + lengthSamples); 1.743 + return kMediaConduitNoError; 1.744 +} 1.745 + 1.746 +// Transport Layer Callbacks 1.747 +MediaConduitErrorCode 1.748 +WebrtcAudioConduit::ReceivedRTPPacket(const void *data, int len) 1.749 +{ 1.750 + CSFLogDebug(logTag, "%s : channel %d", __FUNCTION__, mChannel); 1.751 + 1.752 + if(mEngineReceiving) 1.753 + { 1.754 +#ifdef MOZILLA_INTERNAL_API 1.755 + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { 1.756 + // timestamp is at 32 bits in ([1]) 1.757 + struct Processing insert = { TimeStamp::Now(), 1.758 + ntohl(static_cast<const uint32_t *>(data)[1]) }; 1.759 + mProcessing.AppendElement(insert); 1.760 + } 1.761 +#endif 1.762 + 1.763 + if(mPtrVoENetwork->ReceivedRTPPacket(mChannel,data,len) == -1) 1.764 + { 1.765 + int error = mPtrVoEBase->LastError(); 1.766 + CSFLogError(logTag, "%s RTP Processing Error %d", __FUNCTION__, error); 1.767 + if(error == VE_RTP_RTCP_MODULE_ERROR) 1.768 + { 1.769 + return kMediaConduitRTPRTCPModuleError; 1.770 + } 1.771 + return kMediaConduitUnknownError; 1.772 + } 1.773 + } else { 1.774 + CSFLogError(logTag, "Error: %s when not receiving", __FUNCTION__); 1.775 + return kMediaConduitSessionNotInited; 1.776 + } 1.777 + 1.778 + return kMediaConduitNoError; 1.779 +} 1.780 + 1.781 +MediaConduitErrorCode 1.782 +WebrtcAudioConduit::ReceivedRTCPPacket(const void *data, int len) 1.783 +{ 1.784 + CSFLogDebug(logTag, "%s : channel %d",__FUNCTION__, mChannel); 1.785 + 1.786 + if(mEngineTransmitting) 1.787 + { 1.788 + if(mPtrVoENetwork->ReceivedRTCPPacket(mChannel, data, len) == -1) 1.789 + { 1.790 + int error = mPtrVoEBase->LastError(); 1.791 + CSFLogError(logTag, "%s RTCP Processing Error %d", __FUNCTION__, error); 1.792 + if(error == VE_RTP_RTCP_MODULE_ERROR) 1.793 + { 1.794 + return kMediaConduitRTPRTCPModuleError; 1.795 + } 1.796 + return kMediaConduitUnknownError; 1.797 + } 1.798 + } else { 1.799 + CSFLogError(logTag, "Error: %s when not receiving", __FUNCTION__); 1.800 + return kMediaConduitSessionNotInited; 1.801 + } 1.802 + return kMediaConduitNoError; 1.803 +} 1.804 + 1.805 +//WebRTC::RTP Callback Implementation 1.806 +int WebrtcAudioConduit::SendPacket(int channel, const void* data, int len) 1.807 +{ 1.808 + CSFLogDebug(logTag, "%s : channel %d %s", __FUNCTION__, channel, 1.809 + (mEngineReceiving && mOtherDirection) ? "(using mOtherDirection)" : ""); 1.810 + 1.811 + if (mEngineReceiving) 1.812 + { 1.813 + if (mOtherDirection) 1.814 + { 1.815 + return mOtherDirection->SendPacket(channel, data, len); 1.816 + } 1.817 + CSFLogDebug(logTag, "%s : Asked to send RTP without an RTP sender on channel %d", 1.818 + __FUNCTION__, channel); 1.819 + return -1; 1.820 + } else { 1.821 +#ifdef MOZILLA_INTERNAL_API 1.822 + if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { 1.823 + if (mProcessing.Length() > 0) { 1.824 + TimeStamp started = mProcessing[0].mTimeStamp; 1.825 + mProcessing.RemoveElementAt(0); 1.826 + mProcessing.RemoveElementAt(0); // 20ms packetization! Could automate this by watching sizes 1.827 + TimeDuration t = TimeStamp::Now() - started; 1.828 + int64_t delta = t.ToMilliseconds(); 1.829 + LogTime(AsyncLatencyLogger::AudioSendRTP, ((uint64_t) this), delta); 1.830 + } 1.831 + } 1.832 +#endif 1.833 + if(mTransport && (mTransport->SendRtpPacket(data, len) == NS_OK)) 1.834 + { 1.835 + CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__); 1.836 + return len; 1.837 + } else { 1.838 + CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__); 1.839 + return -1; 1.840 + } 1.841 + } 1.842 +} 1.843 + 1.844 +int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len) 1.845 +{ 1.846 + CSFLogDebug(logTag, "%s : channel %d", __FUNCTION__, channel); 1.847 + 1.848 + if (mEngineTransmitting) 1.849 + { 1.850 + if (mOtherDirection) 1.851 + { 1.852 + return mOtherDirection->SendRTCPPacket(channel, data, len); 1.853 + } 1.854 + } 1.855 + 1.856 + // We come here if we have only one pipeline/conduit setup, 1.857 + // such as for unidirectional streams. 1.858 + // We also end up here if we are receiving 1.859 + if(mTransport && mTransport->SendRtcpPacket(data, len) == NS_OK) 1.860 + { 1.861 + CSFLogDebug(logTag, "%s Sent RTCP Packet ", __FUNCTION__); 1.862 + return len; 1.863 + } else { 1.864 + CSFLogError(logTag, "%s RTCP Packet Send Failed ", __FUNCTION__); 1.865 + return -1; 1.866 + } 1.867 +} 1.868 + 1.869 +/** 1.870 + * Converts between CodecConfig to WebRTC Codec Structure. 1.871 + */ 1.872 + 1.873 +bool 1.874 +WebrtcAudioConduit::CodecConfigToWebRTCCodec(const AudioCodecConfig* codecInfo, 1.875 + webrtc::CodecInst& cinst) 1.876 + { 1.877 + const unsigned int plNameLength = codecInfo->mName.length()+1; 1.878 + memset(&cinst, 0, sizeof(webrtc::CodecInst)); 1.879 + if(sizeof(cinst.plname) < plNameLength) 1.880 + { 1.881 + CSFLogError(logTag, "%s Payload name buffer capacity mismatch ", 1.882 + __FUNCTION__); 1.883 + return false; 1.884 + } 1.885 + memcpy(cinst.plname, codecInfo->mName.c_str(),codecInfo->mName.length()); 1.886 + cinst.plname[plNameLength]='\0'; 1.887 + cinst.pltype = codecInfo->mType; 1.888 + cinst.rate = codecInfo->mRate; 1.889 + cinst.pacsize = codecInfo->mPacSize; 1.890 + cinst.plfreq = codecInfo->mFreq; 1.891 + cinst.channels = codecInfo->mChannels; 1.892 + return true; 1.893 + } 1.894 + 1.895 +/** 1.896 + * Supported Sampling Frequncies. 1.897 + */ 1.898 +bool 1.899 +WebrtcAudioConduit::IsSamplingFreqSupported(int freq) const 1.900 +{ 1.901 + if(GetNum10msSamplesForFrequency(freq)) 1.902 + { 1.903 + return true; 1.904 + } else { 1.905 + return false; 1.906 + } 1.907 +} 1.908 + 1.909 +/* Return block-length of 10 ms audio frame in number of samples */ 1.910 +unsigned int 1.911 +WebrtcAudioConduit::GetNum10msSamplesForFrequency(int samplingFreqHz) const 1.912 +{ 1.913 + switch(samplingFreqHz) 1.914 + { 1.915 + case 16000: return 160; //160 samples 1.916 + case 32000: return 320; //320 samples 1.917 + case 44100: return 441; //441 samples 1.918 + case 48000: return 480; //480 samples 1.919 + default: return 0; // invalid or unsupported 1.920 + } 1.921 +} 1.922 + 1.923 +//Copy the codec passed into Conduit's database 1.924 +bool 1.925 +WebrtcAudioConduit::CopyCodecToDB(const AudioCodecConfig* codecInfo) 1.926 +{ 1.927 + 1.928 + AudioCodecConfig* cdcConfig = new AudioCodecConfig(codecInfo->mType, 1.929 + codecInfo->mName, 1.930 + codecInfo->mFreq, 1.931 + codecInfo->mPacSize, 1.932 + codecInfo->mChannels, 1.933 + codecInfo->mRate, 1.934 + codecInfo->mLoadManager); 1.935 + mRecvCodecList.push_back(cdcConfig); 1.936 + return true; 1.937 +} 1.938 + 1.939 +/** 1.940 + * Checks if 2 codec structs are same 1.941 + */ 1.942 +bool 1.943 +WebrtcAudioConduit::CheckCodecsForMatch(const AudioCodecConfig* curCodecConfig, 1.944 + const AudioCodecConfig* codecInfo) const 1.945 +{ 1.946 + if(!curCodecConfig) 1.947 + { 1.948 + return false; 1.949 + } 1.950 + 1.951 + if(curCodecConfig->mType == codecInfo->mType && 1.952 + (curCodecConfig->mName.compare(codecInfo->mName) == 0) && 1.953 + curCodecConfig->mFreq == codecInfo->mFreq && 1.954 + curCodecConfig->mPacSize == codecInfo->mPacSize && 1.955 + curCodecConfig->mChannels == codecInfo->mChannels && 1.956 + curCodecConfig->mRate == codecInfo->mRate) 1.957 + { 1.958 + return true; 1.959 + } 1.960 + 1.961 + return false; 1.962 +} 1.963 + 1.964 +/** 1.965 + * Checks if the codec is already in Conduit's database 1.966 + */ 1.967 +bool 1.968 +WebrtcAudioConduit::CheckCodecForMatch(const AudioCodecConfig* codecInfo) const 1.969 +{ 1.970 + //the db should have atleast one codec 1.971 + for(std::vector<AudioCodecConfig*>::size_type i=0;i < mRecvCodecList.size();i++) 1.972 + { 1.973 + if(CheckCodecsForMatch(mRecvCodecList[i],codecInfo)) 1.974 + { 1.975 + //match 1.976 + return true; 1.977 + } 1.978 + } 1.979 + //no match or empty local db 1.980 + return false; 1.981 +} 1.982 + 1.983 + 1.984 +/** 1.985 + * Perform validation on the codecConfig to be applied. 1.986 + * Verifies if the codec is already applied. 1.987 + */ 1.988 +MediaConduitErrorCode 1.989 +WebrtcAudioConduit::ValidateCodecConfig(const AudioCodecConfig* codecInfo, 1.990 + bool send) const 1.991 +{ 1.992 + bool codecAppliedAlready = false; 1.993 + 1.994 + if(!codecInfo) 1.995 + { 1.996 + CSFLogError(logTag, "%s Null CodecConfig ", __FUNCTION__); 1.997 + return kMediaConduitMalformedArgument; 1.998 + } 1.999 + 1.1000 + if((codecInfo->mName.empty()) || 1.1001 + (codecInfo->mName.length() >= CODEC_PLNAME_SIZE)) 1.1002 + { 1.1003 + CSFLogError(logTag, "%s Invalid Payload Name Length ", __FUNCTION__); 1.1004 + return kMediaConduitMalformedArgument; 1.1005 + } 1.1006 + 1.1007 + //Only mono or stereo channels supported 1.1008 + if( (codecInfo->mChannels != 1) && (codecInfo->mChannels != 2)) 1.1009 + { 1.1010 + CSFLogError(logTag, "%s Channel Unsupported ", __FUNCTION__); 1.1011 + return kMediaConduitMalformedArgument; 1.1012 + } 1.1013 + 1.1014 + //check if we have the same codec already applied 1.1015 + if(send) 1.1016 + { 1.1017 + codecAppliedAlready = CheckCodecsForMatch(mCurSendCodecConfig,codecInfo); 1.1018 + } else { 1.1019 + codecAppliedAlready = CheckCodecForMatch(codecInfo); 1.1020 + } 1.1021 + 1.1022 + if(codecAppliedAlready) 1.1023 + { 1.1024 + CSFLogDebug(logTag, "%s Codec %s Already Applied ", __FUNCTION__, codecInfo->mName.c_str()); 1.1025 + return kMediaConduitCodecInUse; 1.1026 + } 1.1027 + return kMediaConduitNoError; 1.1028 +} 1.1029 + 1.1030 +void 1.1031 +WebrtcAudioConduit::DumpCodecDB() const 1.1032 + { 1.1033 + for(std::vector<AudioCodecConfig*>::size_type i=0;i < mRecvCodecList.size();i++) 1.1034 + { 1.1035 + CSFLogDebug(logTag,"Payload Name: %s", mRecvCodecList[i]->mName.c_str()); 1.1036 + CSFLogDebug(logTag,"Payload Type: %d", mRecvCodecList[i]->mType); 1.1037 + CSFLogDebug(logTag,"Payload Frequency: %d", mRecvCodecList[i]->mFreq); 1.1038 + CSFLogDebug(logTag,"Payload PacketSize: %d", mRecvCodecList[i]->mPacSize); 1.1039 + CSFLogDebug(logTag,"Payload Channels: %d", mRecvCodecList[i]->mChannels); 1.1040 + CSFLogDebug(logTag,"Payload Sampling Rate: %d", mRecvCodecList[i]->mRate); 1.1041 + } 1.1042 + } 1.1043 +}// end namespace