nsresult SrtpFlow::ProtectRtp(void *in, int in_len, int max_len, int *out_len) { nsresult res = CheckInputs(true, in, in_len, max_len, out_len); if (NS_FAILED(res)) return res; int len = in_len; srtp_err_status_t r = srtp_protect(session_, in, &len); if (r != srtp_err_status_ok) { CSFLogError(LOGTAG, "Error protecting SRTP packet"); return NS_ERROR_FAILURE; } MOZ_ASSERT(len <= max_len); *out_len = len; CSFLogDebug(LOGTAG, "Successfully protected an SRTP packet of len %d", *out_len); return NS_OK; }
//WebRTC::RTP Callback Implementation int WebrtcAudioConduit::SendPacket(int channel, const void* data, int len) { CSFLogDebug(logTag, "%s : channel %d %s", __FUNCTION__, channel, (mEngineReceiving && mOtherDirection) ? "(using mOtherDirection)" : ""); if (mEngineReceiving) { if (mOtherDirection) { return mOtherDirection->SendPacket(channel, data, len); } CSFLogDebug(logTag, "%s : Asked to send RTP without an RTP sender on channel %d", __FUNCTION__, channel); return -1; } else { #ifdef MOZILLA_INTERNAL_API if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { if (mProcessing.Length() > 0) { TimeStamp started = mProcessing[0].mTimeStamp; mProcessing.RemoveElementAt(0); mProcessing.RemoveElementAt(0); // 20ms packetization! Could automate this by watching sizes TimeDuration t = TimeStamp::Now() - started; int64_t delta = t.ToMilliseconds(); LogTime(AsyncLatencyLogger::AudioSendRTP, ((uint64_t) this), delta); } } #endif if(mTransport && (mTransport->SendRtpPacket(data, len) == NS_OK)) { CSFLogDebug(logTag, "%s Sent RTP Packet ", __FUNCTION__); return len; } else { CSFLogError(logTag, "%s RTP Packet Send Failed ", __FUNCTION__); return -1; } } }
int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len) { CSFLogDebug(logTag, "%s : channel %d", __FUNCTION__, channel); if (mEngineTransmitting) { if (mOtherDirection) { return mOtherDirection->SendRTCPPacket(channel, data, len); } } // We come here if we have only one pipeline/conduit setup, // such as for unidirectional streams. // We also end up here if we are receiving if(mTransport && mTransport->SendRtcpPacket(data, len) == NS_OK) { CSFLogDebug(logTag, "%s Sent RTCP Packet ", __FUNCTION__); return len; } else { CSFLogError(logTag, "%s RTCP Packet Send Failed ", __FUNCTION__); return -1; } }
void RemoteSourceStreamInfo::StorePipeline(int aTrack, bool aIsVideo, mozilla::RefPtr<mozilla::MediaPipeline> aPipeline) { MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end()); if (mPipelines.find(aTrack) != mPipelines.end()) { CSFLogError(logTag, "%s: Request to store duplicate track %d", __FUNCTION__, aTrack); return; } CSFLogDebug(logTag, "%s track %d %s = %p", __FUNCTION__, aTrack, aIsVideo ? "video" : "audio", aPipeline.get()); // See if we have both audio and video here, and if so cross the streams and sync them // XXX Needs to be adjusted when we support multiple streams of the same type for (std::map<int, bool>::iterator it = mTypes.begin(); it != mTypes.end(); ++it) { if (it->second != aIsVideo) { // Ok, we have one video, one non-video - cross the streams! mozilla::WebrtcAudioConduit *audio_conduit = static_cast<mozilla::WebrtcAudioConduit*> (aIsVideo ? mPipelines[it->first]->Conduit() : aPipeline->Conduit()); mozilla::WebrtcVideoConduit *video_conduit = static_cast<mozilla::WebrtcVideoConduit*> (aIsVideo ? aPipeline->Conduit() : mPipelines[it->first]->Conduit()); video_conduit->SyncTo(audio_conduit); CSFLogDebug(logTag, "Syncing %p to %p, %d to %d", video_conduit, audio_conduit, aTrack, it->first); } } //TODO: Revisit once we start supporting multiple streams or multiple tracks // of same type mPipelines[aTrack] = aPipeline; //TODO: move to attribute on Pipeline mTypes[aTrack] = aIsVideo; }
int WebrtcAudioConduit::SendRTCPPacket(int channel, const void* data, int len) { CSFLogDebug(logTag, "%s : channel %d", __FUNCTION__, channel); if (mEngineTransmitting) { if (mOtherDirection) { return mOtherDirection->SendRTCPPacket(channel, data, len); } CSFLogDebug(logTag, "%s : Asked to send RTCP without an RTP receiver on channel %d", __FUNCTION__, channel); return -1; } else { if(mTransport && mTransport->SendRtcpPacket(data, len) == NS_OK) { CSFLogDebug(logTag, "%s Sent RTCP Packet ", __FUNCTION__); return len; } else { CSFLogError(logTag, "%s RTCP Packet Send Failed ", __FUNCTION__); return -1; } } }
MediaConduitErrorCode WebrtcAudioConduit::ConfigureSendMediaCodec(const AudioCodecConfig* codecConfig) { CSFLogDebug(logTag, "%s ", __FUNCTION__); MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0;//webrtc engine errors webrtc::CodecInst cinst; //validate codec param if((condError = ValidateCodecConfig(codecConfig, true)) != kMediaConduitNoError) { return condError; } //are we transmitting already, stop and apply the send codec if(mEngineTransmitting) { CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__); if(mPtrVoEBase->StopSend(mChannel) == -1) { CSFLogError(logTag, "%s StopSend() Failed %d ", __FUNCTION__, mPtrVoEBase->LastError()); return kMediaConduitUnknownError; } } mEngineTransmitting = false; if(!CodecConfigToWebRTCCodec(codecConfig,cinst)) { CSFLogError(logTag,"%s CodecConfig to WebRTC Codec Failed ",__FUNCTION__); return kMediaConduitMalformedArgument; } if(mPtrVoECodec->SetSendCodec(mChannel, cinst) == -1) { error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s SetSendCodec - Invalid Codec %d ",__FUNCTION__, error); if(error == VE_CANNOT_SET_SEND_CODEC || error == VE_CODEC_ERROR) { CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__); return kMediaConduitInvalidSendCodec; } CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__, mPtrVoEBase->LastError()); return kMediaConduitUnknownError; } #ifdef MOZILLA_INTERNAL_API // TEMPORARY - see bug 694814 comment 2 nsresult rv; nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); if (branch) { int32_t aec = 0; // 0 == unchanged bool aec_on = false; branch->GetBoolPref("media.peerconnection.aec_enabled", &aec_on); branch->GetIntPref("media.peerconnection.aec", &aec); CSFLogDebug(logTag,"Audio config: aec: %d", aec_on ? aec : -1); mEchoOn = aec_on; if (static_cast<webrtc::EcModes>(aec) != webrtc::kEcUnchanged) mEchoCancel = static_cast<webrtc::EcModes>(aec); branch->GetIntPref("media.peerconnection.capture_delay", &mCaptureDelay); } } #endif if (0 != (error = mPtrVoEProcessing->SetEcStatus(mEchoOn, mEchoCancel))) { CSFLogError(logTag,"%s Error setting EVStatus: %d ",__FUNCTION__, error); return kMediaConduitUnknownError; } //Let's Send Transport State-machine on the Engine if(mPtrVoEBase->StartSend(mChannel) == -1) { error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s StartSend failed %d", __FUNCTION__, error); return kMediaConduitUnknownError; } //Copy the applied config for future reference. delete mCurSendCodecConfig; mCurSendCodecConfig = new AudioCodecConfig(codecConfig->mType, codecConfig->mName, codecConfig->mFreq, codecConfig->mPacSize, codecConfig->mChannels, codecConfig->mRate, codecConfig->mLoadManager); mEngineTransmitting = true; return kMediaConduitNoError; }
/* * WebRTCAudioConduit Implementation */ MediaConduitErrorCode WebrtcAudioConduit::Init(WebrtcAudioConduit *other) { CSFLogDebug(logTag, "%s this=%p other=%p", __FUNCTION__, this, other); if (other) { MOZ_ASSERT(!other->mOtherDirection); other->mOtherDirection = this; mOtherDirection = other; // only one can call ::Create()/GetVoiceEngine() MOZ_ASSERT(other->mVoiceEngine); mVoiceEngine = other->mVoiceEngine; } else { #ifdef MOZ_WIDGET_ANDROID jobject context = jsjni_GetGlobalContextRef(); // get the JVM JavaVM *jvm = jsjni_GetVM(); JNIEnv* jenv = jsjni_GetJNIForThread(); if (webrtc::VoiceEngine::SetAndroidObjects(jvm, jenv, (void*)context) != 0) { CSFLogError(logTag, "%s Unable to set Android objects", __FUNCTION__); return kMediaConduitSessionNotInited; } #endif // Per WebRTC APIs below function calls return nullptr on failure if(!(mVoiceEngine = webrtc::VoiceEngine::Create())) { CSFLogError(logTag, "%s Unable to create voice engine", __FUNCTION__); return kMediaConduitSessionNotInited; } PRLogModuleInfo *logs = GetWebRTCLogInfo(); if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { // no need to a critical section or lock here gWebrtcTraceLoggingOn = 1; const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); if (!file) { file = "WebRTC.log"; } CSFLogDebug(logTag, "%s Logging webrtc to %s level %d", __FUNCTION__, file, logs->level); mVoiceEngine->SetTraceFilter(logs->level); mVoiceEngine->SetTraceFile(file); } } if(!(mPtrVoEBase = VoEBase::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEBase", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoENetwork = VoENetwork::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoENetwork", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoECodec = VoECodec::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEBCodec", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoEProcessing = VoEAudioProcessing::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEProcessing", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoEXmedia = VoEExternalMedia::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEExternalMedia", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoERTP_RTCP = VoERTP_RTCP::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoERTP_RTCP", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoEVideoSync = VoEVideoSync::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEVideoSync", __FUNCTION__); return kMediaConduitSessionNotInited; } if (!(mPtrRTP = webrtc::VoERTP_RTCP::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to get audio RTP/RTCP interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if (other) { mChannel = other->mChannel; } else { // init the engine with our audio device layer if(mPtrVoEBase->Init() == -1) { CSFLogError(logTag, "%s VoiceEngine Base Not Initialized", __FUNCTION__); return kMediaConduitSessionNotInited; } if( (mChannel = mPtrVoEBase->CreateChannel()) == -1) { CSFLogError(logTag, "%s VoiceEngine Channel creation failed",__FUNCTION__); return kMediaConduitChannelError; } CSFLogDebug(logTag, "%s Channel Created %d ",__FUNCTION__, mChannel); if(mPtrVoENetwork->RegisterExternalTransport(mChannel, *this) == -1) { CSFLogError(logTag, "%s VoiceEngine, External Transport Failed",__FUNCTION__); return kMediaConduitTransportRegistrationFail; } if(mPtrVoEXmedia->SetExternalRecordingStatus(true) == -1) { CSFLogError(logTag, "%s SetExternalRecordingStatus Failed %d",__FUNCTION__, mPtrVoEBase->LastError()); return kMediaConduitExternalPlayoutError; } if(mPtrVoEXmedia->SetExternalPlayoutStatus(true) == -1) { CSFLogError(logTag, "%s SetExternalPlayoutStatus Failed %d ",__FUNCTION__, mPtrVoEBase->LastError()); return kMediaConduitExternalRecordingError; } CSFLogDebug(logTag , "%s AudioSessionConduit Initialization Done (%p)",__FUNCTION__, this); } return kMediaConduitNoError; }
nsresult PeerConnectionMedia::InitProxy() { #if !defined(MOZILLA_EXTERNAL_LINKAGE) // Allow mochitests to disable this, since mochitest configures a fake proxy // that serves up content. bool disable = Preferences::GetBool("media.peerconnection.disable_http_proxy", false); if (disable) { mProxyResolveCompleted = true; return NS_OK; } #endif nsresult rv; nsCOMPtr<nsIProtocolProxyService> pps = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to get proxy service: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } // We use the following URL to find the "default" proxy address for all HTTPS // connections. We will only attempt one HTTP(S) CONNECT per peer connection. // "example.com" is guaranteed to be unallocated and should return the best default. nsCOMPtr<nsIURI> fakeHttpsLocation; rv = NS_NewURI(getter_AddRefs(fakeHttpsLocation), "https://example.com"); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to set URI: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } nsCOMPtr<nsIScriptSecurityManager> secMan( do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to get IOService: %d", __FUNCTION__, (int)rv); CSFLogError(logTag, "%s: Failed to get securityManager: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } nsCOMPtr<nsIPrincipal> systemPrincipal; rv = secMan->GetSystemPrincipal(getter_AddRefs(systemPrincipal)); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to get systemPrincipal: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } nsCOMPtr<nsIChannel> channel; rv = NS_NewChannel(getter_AddRefs(channel), fakeHttpsLocation, systemPrincipal, nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, nsIContentPolicy::TYPE_OTHER); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to get channel from URI: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } RefPtr<ProtocolProxyQueryHandler> handler = new ProtocolProxyQueryHandler(this); rv = pps->AsyncResolve(channel, nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY | nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL, handler, getter_AddRefs(mProxyRequest)); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to resolve protocol proxy: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } return NS_OK; }
/** * Peforms intialization of the MANDATORY components of the Video Engine */ MediaConduitErrorCode WebrtcVideoConduit::Init() { CSFLogDebug(logTag, "%s ", __FUNCTION__); if( !(mVideoEngine = webrtc::VideoEngine::Create()) ) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } #if 0 // TRACING mVideoEngine->SetTraceFilter(webrtc::kTraceAll); mVideoEngine->SetTraceFile( "Vievideotrace.out" ); #endif if( !(mPtrViEBase = ViEBase::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViECapture = ViECapture::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViECodec = ViECodec::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViENetwork = ViENetwork::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViERender = ViERender::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } CSFLogDebug(logTag, "%sEngine Created: Init'ng the interfaces ",__FUNCTION__); if(mPtrViEBase->Init() == -1) { CSFLogError(logTag, " %s Video Engine Init Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitSessionNotInited; } if(mPtrViEBase->CreateChannel(mChannel) == -1) { CSFLogError(logTag, " %s Channel creation Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitChannelError; } if(mPtrViENetwork->RegisterSendTransport(mChannel, *this) == -1) { CSFLogError(logTag, "%s ViENetwork Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitTransportRegistrationFail; } mPtrExtCapture = 0; if(mPtrViECapture->AllocateExternalCaptureDevice(mCapId, mPtrExtCapture) == -1) { CSFLogError(logTag, "%s Unable to Allocate capture module: %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitCaptureError; } if(mPtrViECapture->ConnectCaptureDevice(mCapId,mChannel) == -1) { CSFLogError(logTag, "%s Unable to Connect capture module: %d ", __FUNCTION__,mPtrViEBase->LastError()); return kMediaConduitCaptureError; } if(mPtrViERender->AddRenderer(mChannel, webrtc::kVideoI420, (webrtc::ExternalRenderer*) this) == -1) { CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__); return kMediaConduitInvalidRenderer; } CSFLogError(logTag, "Initialization Done"); return kMediaConduitNoError; }
/* * WebRTCAudioConduit Implementation */ MediaConduitErrorCode WebrtcAudioConduit::Init() { CSFLogDebug(logTag, "%s this=%p", __FUNCTION__, this); #ifdef MOZ_WIDGET_ANDROID jobject context = jsjni_GetGlobalContextRef(); // get the JVM JavaVM *jvm = jsjni_GetVM(); JNIEnv* jenv = jsjni_GetJNIForThread(); if (webrtc::VoiceEngine::SetAndroidObjects(jvm, jenv, (void*)context) != 0) { CSFLogError(logTag, "%s Unable to set Android objects", __FUNCTION__); return kMediaConduitSessionNotInited; } #endif // Per WebRTC APIs below function calls return nullptr on failure if(!(mVoiceEngine = webrtc::VoiceEngine::Create())) { CSFLogError(logTag, "%s Unable to create voice engine", __FUNCTION__); return kMediaConduitSessionNotInited; } EnableWebRtcLog(); if(!(mPtrVoEBase = VoEBase::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEBase", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoENetwork = VoENetwork::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoENetwork", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoECodec = VoECodec::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEBCodec", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoEProcessing = VoEAudioProcessing::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEProcessing", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoEXmedia = VoEExternalMedia::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEExternalMedia", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoERTP_RTCP = VoERTP_RTCP::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoERTP_RTCP", __FUNCTION__); return kMediaConduitSessionNotInited; } if(!(mPtrVoEVideoSync = VoEVideoSync::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to initialize VoEVideoSync", __FUNCTION__); return kMediaConduitSessionNotInited; } if (!(mPtrRTP = webrtc::VoERTP_RTCP::GetInterface(mVoiceEngine))) { CSFLogError(logTag, "%s Unable to get audio RTP/RTCP interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } // init the engine with our audio device layer if(mPtrVoEBase->Init() == -1) { CSFLogError(logTag, "%s VoiceEngine Base Not Initialized", __FUNCTION__); return kMediaConduitSessionNotInited; } if( (mChannel = mPtrVoEBase->CreateChannel()) == -1) { CSFLogError(logTag, "%s VoiceEngine Channel creation failed",__FUNCTION__); return kMediaConduitChannelError; } CSFLogDebug(logTag, "%s Channel Created %d ",__FUNCTION__, mChannel); if(mPtrVoENetwork->RegisterExternalTransport(mChannel, *this) == -1) { CSFLogError(logTag, "%s VoiceEngine, External Transport Failed",__FUNCTION__); return kMediaConduitTransportRegistrationFail; } if(mPtrVoEXmedia->SetExternalRecordingStatus(true) == -1) { CSFLogError(logTag, "%s SetExternalRecordingStatus Failed %d",__FUNCTION__, mPtrVoEBase->LastError()); return kMediaConduitExternalPlayoutError; } if(mPtrVoEXmedia->SetExternalPlayoutStatus(true) == -1) { CSFLogError(logTag, "%s SetExternalPlayoutStatus Failed %d ",__FUNCTION__, mPtrVoEBase->LastError()); return kMediaConduitExternalRecordingError; } CSFLogDebug(logTag , "%s AudioSessionConduit Initialization Done (%p)",__FUNCTION__, this); return kMediaConduitNoError; }
/** * Note: Setting the send-codec on the Video Engine will restart the encoder, * sets up new SSRC and reset RTP_RTCP module with the new codec setting. * * Note: this is called from MainThread, and the codec settings are read on * videoframe delivery threads (i.e in SendVideoFrame(). With * renegotiation/reconfiguration, this now needs a lock! Alternatively * changes could be queued until the next frame is delivered using an * Atomic pointer and swaps. */ MediaConduitErrorCode WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig) { CSFLogDebug(logTag, "%s for %s", __FUNCTION__, codecConfig ? codecConfig->mName.c_str() : "<null>"); bool codecFound = false; MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0; //webrtc engine errors webrtc::VideoCodec video_codec; std::string payloadName; memset(&video_codec, 0, sizeof(video_codec)); { //validate basic params if((condError = ValidateCodecConfig(codecConfig,true)) != kMediaConduitNoError) { return condError; } } condError = StopTransmitting(); if (condError != kMediaConduitNoError) { return condError; } if (mExternalSendCodec && codecConfig->mType == mExternalSendCodec->mType) { CSFLogError(logTag, "%s Configuring External H264 Send Codec", __FUNCTION__); // width/height will be overridden on the first frame video_codec.width = 320; video_codec.height = 240; #ifdef MOZ_WEBRTC_OMX if (codecConfig->mType == webrtc::kVideoCodecH264) { video_codec.resolution_divisor = 16; } else { video_codec.resolution_divisor = 1; // We could try using it to handle odd resolutions } #else video_codec.resolution_divisor = 1; // We could try using it to handle odd resolutions #endif video_codec.qpMax = 56; video_codec.numberOfSimulcastStreams = 1; video_codec.mode = webrtc::kRealtimeVideo; codecFound = true; } else { // we should be good here to set the new codec. for(int idx=0; idx < mPtrViECodec->NumberOfCodecs(); idx++) { if(0 == mPtrViECodec->GetCodec(idx, video_codec)) { payloadName = video_codec.plName; if(codecConfig->mName.compare(payloadName) == 0) { // Note: side-effect of this is that video_codec is filled in // by GetCodec() codecFound = true; break; } } }//for } if(codecFound == false) { CSFLogError(logTag, "%s Codec Mismatch ", __FUNCTION__); return kMediaConduitInvalidSendCodec; } // Note: only for overriding parameters from GetCodec()! CodecConfigToWebRTCCodec(codecConfig, video_codec); if(mPtrViECodec->SetSendCodec(mChannel, video_codec) == -1) { error = mPtrViEBase->LastError(); if(error == kViECodecInvalidCodec) { CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__); return kMediaConduitInvalidSendCodec; } CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } if (!mVideoCodecStat) { mVideoCodecStat = new VideoCodecStatistics(mChannel, mPtrViECodec); } mVideoCodecStat->Register(true); mSendingWidth = 0; mSendingHeight = 0; mSendingFramerate = video_codec.maxFramerate; if(codecConfig->RtcpFbNackIsSet("")) { CSFLogDebug(logTag, "Enabling NACK (send) for video stream\n"); if (mPtrRTP->SetNACKStatus(mChannel, true) != 0) { CSFLogError(logTag, "%s NACKStatus Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitNACKStatusError; } } condError = StartTransmitting(); if (condError != kMediaConduitNoError) { return condError; } { MutexAutoLock lock(mCodecMutex); //Copy the applied config for future reference. mCurSendCodecConfig = new VideoCodecConfig(*codecConfig); } mPtrRTP->SetRembStatus(mChannel, true, false); return kMediaConduitNoError; }
nsresult PeerConnectionMedia::UpdateTransportFlow( size_t aLevel, bool aIsRtcp, const JsepTransport& aTransport) { if (aIsRtcp && aTransport.mComponents < 2) { RemoveTransportFlow(aLevel, aIsRtcp); return NS_OK; } if (!aIsRtcp && !aTransport.mComponents) { RemoveTransportFlow(aLevel, aIsRtcp); return NS_OK; } nsresult rv; RefPtr<TransportFlow> flow = GetTransportFlow(aLevel, aIsRtcp); if (flow) { if (IsIceRestarting()) { CSFLogInfo(LOGTAG, "Flow[%s]: detected ICE restart - level: %u rtcp: %d", flow->id().c_str(), (unsigned)aLevel, aIsRtcp); RefPtr<PeerConnectionMedia> pcMedia(this); rv = GetSTSThread()->Dispatch( WrapRunnableNM(AddNewIceStreamForRestart_s, pcMedia, flow, aLevel, aIsRtcp), NS_DISPATCH_NORMAL); if (NS_FAILED(rv)) { CSFLogError(LOGTAG, "Failed to dispatch AddNewIceStreamForRestart_s"); return rv; } } return NS_OK; } std::ostringstream osId; osId << mParentHandle << ":" << aLevel << "," << (aIsRtcp ? "rtcp" : "rtp"); flow = new TransportFlow(osId.str()); // The media streams are made on STS so we need to defer setup. auto ice = MakeUnique<TransportLayerIce>(); auto dtls = MakeUnique<TransportLayerDtls>(); dtls->SetRole(aTransport.mDtls->GetRole() == JsepDtlsTransport::kJsepDtlsClient ? TransportLayerDtls::CLIENT : TransportLayerDtls::SERVER); RefPtr<DtlsIdentity> pcid = mParent->Identity(); if (!pcid) { CSFLogError(LOGTAG, "Failed to get DTLS identity."); return NS_ERROR_FAILURE; } dtls->SetIdentity(pcid); const SdpFingerprintAttributeList& fingerprints = aTransport.mDtls->GetFingerprints(); for (const auto& fingerprint : fingerprints.mFingerprints) { std::ostringstream ss; ss << fingerprint.hashFunc; rv = dtls->SetVerificationDigest(ss.str(), &fingerprint.fingerprint[0], fingerprint.fingerprint.size()); if (NS_FAILED(rv)) { CSFLogError(LOGTAG, "Could not set fingerprint"); return rv; } } std::vector<uint16_t> srtpCiphers; srtpCiphers.push_back(SRTP_AES128_CM_HMAC_SHA1_80); srtpCiphers.push_back(SRTP_AES128_CM_HMAC_SHA1_32); rv = dtls->SetSrtpCiphers(srtpCiphers); if (NS_FAILED(rv)) { CSFLogError(LOGTAG, "Couldn't set SRTP ciphers"); return rv; } // Always permits negotiation of the confidential mode. // Only allow non-confidential (which is an allowed default), // if we aren't confidential. std::set<std::string> alpn; std::string alpnDefault = ""; alpn.insert("c-webrtc"); if (!mParent->PrivacyRequested()) { alpnDefault = "webrtc"; alpn.insert(alpnDefault); } rv = dtls->SetAlpn(alpn, alpnDefault); if (NS_FAILED(rv)) { CSFLogError(LOGTAG, "Couldn't set ALPN"); return rv; } nsAutoPtr<PtrVector<TransportLayer> > layers(new PtrVector<TransportLayer>); layers->values.push_back(ice.release()); layers->values.push_back(dtls.release()); RefPtr<PeerConnectionMedia> pcMedia(this); rv = GetSTSThread()->Dispatch( WrapRunnableNM(FinalizeTransportFlow_s, pcMedia, flow, aLevel, aIsRtcp, layers), NS_DISPATCH_NORMAL); if (NS_FAILED(rv)) { CSFLogError(LOGTAG, "Failed to dispatch FinalizeTransportFlow_s"); return rv; } AddTransportFlow(aLevel, aIsRtcp, flow); return NS_OK; }
MediaConduitErrorCode WebrtcAudioConduit::GetAudioFrame(int16_t speechData[], int32_t samplingFreqHz, int32_t capture_delay, int& lengthSamples) { CSFLogDebug(logTag, "%s ", __FUNCTION__); unsigned int numSamples = 0; //validate params if(!speechData ) { CSFLogError(logTag,"%s Null Audio Buffer Pointer", __FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } // Validate sample length if((numSamples = GetNum10msSamplesForFrequency(samplingFreqHz)) == 0 ) { CSFLogError(logTag,"%s Invalid Sampling Frequency ", __FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } //validate capture time if(capture_delay < 0 ) { CSFLogError(logTag,"%s Invalid Capture Delay ", __FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } //Conduit should have reception enabled before we ask for decoded // samples if(!mEngineReceiving) { CSFLogError(logTag, "%s Engine not Receiving ", __FUNCTION__); return kMediaConduitSessionNotInited; } lengthSamples = 0; //output paramter if(mPtrVoEXmedia->ExternalPlayoutGetData( speechData, samplingFreqHz, capture_delay, lengthSamples) == -1) { int error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s Getting audio data Failed %d", __FUNCTION__, error); if(error == VE_RUNTIME_PLAY_ERROR) { return kMediaConduitPlayoutError; } return kMediaConduitUnknownError; } // Not #ifdef DEBUG or on a log module so we can use it for about:webrtc/etc mSamples += lengthSamples; if (mSamples >= mLastSyncLog + samplingFreqHz) { int jitter_buffer_delay_ms; int playout_buffer_delay_ms; int avsync_offset_ms; if (GetAVStats(&jitter_buffer_delay_ms, &playout_buffer_delay_ms, &avsync_offset_ms)) { #ifdef MOZILLA_INTERNAL_API if (avsync_offset_ms < 0) { Telemetry::Accumulate(Telemetry::WEBRTC_AVSYNC_WHEN_VIDEO_LAGS_AUDIO_MS, -avsync_offset_ms); } else { Telemetry::Accumulate(Telemetry::WEBRTC_AVSYNC_WHEN_AUDIO_LAGS_VIDEO_MS, avsync_offset_ms); } #endif CSFLogError(logTag, "A/V sync: sync delta: %dms, audio jitter delay %dms, playout delay %dms", avsync_offset_ms, jitter_buffer_delay_ms, playout_buffer_delay_ms); } else { CSFLogError(logTag, "A/V sync: GetAVStats failed"); } mLastSyncLog = mSamples; } #ifdef MOZILLA_INTERNAL_API if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { if (mProcessing.Length() > 0) { unsigned int now; mPtrVoEVideoSync->GetPlayoutTimestamp(mChannel, now); if (static_cast<uint32_t>(now) != mLastTimestamp) { mLastTimestamp = static_cast<uint32_t>(now); // Find the block that includes this timestamp in the network input while (mProcessing.Length() > 0) { // FIX! assumes 20ms @ 48000Hz // FIX handle wrap-around if (mProcessing[0].mRTPTimeStamp + 20*(48000/1000) >= now) { TimeDuration t = TimeStamp::Now() - mProcessing[0].mTimeStamp; // Wrap-around? int64_t delta = t.ToMilliseconds() + (now - mProcessing[0].mRTPTimeStamp)/(48000/1000); LogTime(AsyncLatencyLogger::AudioRecvRTP, ((uint64_t) this), delta); break; } mProcessing.RemoveElementAt(0); } } } } #endif CSFLogDebug(logTag,"%s GetAudioFrame:Got samples: length %d ",__FUNCTION__, lengthSamples); return kMediaConduitNoError; }
/** * Peforms intialization of the MANDATORY components of the Video Engine */ MediaConduitErrorCode WebrtcVideoConduit::Init() { CSFLogDebug(logTag, "%s ", __FUNCTION__); if( !(mVideoEngine = webrtc::VideoEngine::Create()) ) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } PRLogModuleInfo *logs = GetWebRTCLogInfo(); if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { // no need to a critical section or lock here gWebrtcTraceLoggingOn = 1; const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); if (!file) { file = "WebRTC.log"; } CSFLogDebug(logTag, "%s Logging webrtc to %s level %d", __FUNCTION__, file, logs->level); mVideoEngine->SetTraceFilter(logs->level); mVideoEngine->SetTraceFile(file); } if( !(mPtrViEBase = ViEBase::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video base interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViECapture = ViECapture::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video capture interface", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViECodec = ViECodec::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video codec interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViENetwork = ViENetwork::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video network interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViERender = ViERender::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video render interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrRTP = webrtc::ViERTP_RTCP::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video RTCP interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } CSFLogDebug(logTag, "%s Engine Created: Init'ng the interfaces ",__FUNCTION__); if(mPtrViEBase->Init() == -1) { CSFLogError(logTag, " %s Video Engine Init Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitSessionNotInited; } if(mPtrViEBase->CreateChannel(mChannel) == -1) { CSFLogError(logTag, " %s Channel creation Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitChannelError; } if(mPtrViENetwork->RegisterSendTransport(mChannel, *this) == -1) { CSFLogError(logTag, "%s ViENetwork Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitTransportRegistrationFail; } mPtrExtCapture = 0; if(mPtrViECapture->AllocateExternalCaptureDevice(mCapId, mPtrExtCapture) == -1) { CSFLogError(logTag, "%s Unable to Allocate capture module: %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitCaptureError; } if(mPtrViECapture->ConnectCaptureDevice(mCapId,mChannel) == -1) { CSFLogError(logTag, "%s Unable to Connect capture module: %d ", __FUNCTION__,mPtrViEBase->LastError()); return kMediaConduitCaptureError; } if(mPtrViERender->AddRenderer(mChannel, webrtc::kVideoI420, (webrtc::ExternalRenderer*) this) == -1) { CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__); return kMediaConduitInvalidRenderer; } // Set up some parameters, per juberti. Set MTU. if(mPtrViENetwork->SetMTU(mChannel, 1200) != 0) { CSFLogError(logTag, "%s MTU Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitMTUError; } // Turn on RTCP and loss feedback reporting. if(mPtrRTP->SetRTCPStatus(mChannel, webrtc::kRtcpCompound_RFC4585) != 0) { CSFLogError(logTag, "%s RTCPStatus Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitRTCPStatusError; } // Enable pli as key frame request method. if(mPtrRTP->SetKeyFrameRequestMethod(mChannel, webrtc::kViEKeyFrameRequestPliRtcp) != 0) { CSFLogError(logTag, "%s KeyFrameRequest Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitKeyFrameRequestError; } // Enable lossless transport if (mPtrRTP->SetNACKStatus(mChannel, true) != 0) { CSFLogError(logTag, "%s NACKStatus Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitNACKStatusError; } CSFLogError(logTag, "%s Initialization Done", __FUNCTION__); return kMediaConduitNoError; }
MediaConduitErrorCode WebrtcAudioConduit::GetAudioFrame(int16_t speechData[], int32_t samplingFreqHz, int32_t capture_delay, int& lengthSamples) { CSFLogDebug(logTag, "%s ", __FUNCTION__); unsigned int numSamples = 0; //validate params if(!speechData ) { CSFLogError(logTag,"%s Null Audio Buffer Pointer", __FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } // Validate sample length if((numSamples = GetNum10msSamplesForFrequency(samplingFreqHz)) == 0 ) { CSFLogError(logTag,"%s Invalid Sampling Frequency ", __FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } //validate capture time if(capture_delay < 0 ) { CSFLogError(logTag,"%s Invalid Capture Delay ", __FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } //Conduit should have reception enabled before we ask for decoded // samples if(!mEngineReceiving) { CSFLogError(logTag, "%s Engine not Receiving ", __FUNCTION__); return kMediaConduitSessionNotInited; } lengthSamples = 0; //output paramter if(mPtrVoEXmedia->ExternalPlayoutGetData( speechData, samplingFreqHz, capture_delay, lengthSamples) == -1) { int error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s Getting audio data Failed %d", __FUNCTION__, error); if(error == VE_RUNTIME_PLAY_ERROR) { return kMediaConduitPlayoutError; } return kMediaConduitUnknownError; } CSFLogDebug(logTag,"%s GetAudioFrame:Got samples: length %d ",__FUNCTION__, lengthSamples); return kMediaConduitNoError; }
MediaConduitErrorCode WebrtcVideoConduit::ConfigureRecvMediaCodecs( const std::vector<VideoCodecConfig* >& codecConfigList) { CSFLogDebug(logTag, "%s ", __FUNCTION__); MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0; //webrtc engine errors bool success = false; std::string payloadName; // are we receiving already? If so, stop receiving and playout // since we can't apply new recv codec when the engine is playing. if(mEngineReceiving) { CSFLogDebug(logTag, "%s Engine Already Receiving . Attemping to Stop ", __FUNCTION__); if(mPtrViEBase->StopReceive(mChannel) == -1) { error = mPtrViEBase->LastError(); if(error == kViEBaseUnknownError) { CSFLogDebug(logTag, "%s StopReceive() Success ", __FUNCTION__); mEngineReceiving = false; } else { CSFLogError(logTag, "%s StopReceive() Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } } } mEngineReceiving = false; if(codecConfigList.empty()) { CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__); return kMediaConduitMalformedArgument; } webrtc::ViEKeyFrameRequestMethod kf_request = webrtc::kViEKeyFrameRequestNone; bool use_nack_basic = false; //Try Applying the codecs in the list // we treat as success if atleast one codec was applied and reception was // started successfully. for(std::vector<VideoCodecConfig*>::size_type i=0;i < codecConfigList.size();i++) { //if the codec param is invalid or diplicate, return error if((condError = ValidateCodecConfig(codecConfigList[i],false)) != kMediaConduitNoError) { return condError; } // Check for the keyframe request type: PLI is preferred // over FIR, and FIR is preferred over none. if (codecConfigList[i]->RtcpFbIsSet(SDP_RTCP_FB_NACK_PLI)) { kf_request = webrtc::kViEKeyFrameRequestPliRtcp; } else if(kf_request == webrtc::kViEKeyFrameRequestNone && codecConfigList[i]->RtcpFbIsSet(SDP_RTCP_FB_CCM_FIR)) { kf_request = webrtc::kViEKeyFrameRequestFirRtcp; } // Check whether NACK is requested if(codecConfigList[i]->RtcpFbIsSet(SDP_RTCP_FB_NACK_BASIC)) { use_nack_basic = true; } webrtc::VideoCodec video_codec; mEngineReceiving = false; memset(&video_codec, 0, sizeof(webrtc::VideoCodec)); //Retrieve pre-populated codec structure for our codec. for(int idx=0; idx < mPtrViECodec->NumberOfCodecs(); idx++) { if(mPtrViECodec->GetCodec(idx, video_codec) == 0) { payloadName = video_codec.plName; if(codecConfigList[i]->mName.compare(payloadName) == 0) { CodecConfigToWebRTCCodec(codecConfigList[i], video_codec); if(mPtrViECodec->SetReceiveCodec(mChannel,video_codec) == -1) { CSFLogError(logTag, "%s Invalid Receive Codec %d ", __FUNCTION__, mPtrViEBase->LastError()); } else { CSFLogError(logTag, "%s Successfully Set the codec %s", __FUNCTION__, codecConfigList[i]->mName.c_str()); if(CopyCodecToDB(codecConfigList[i])) { success = true; } else { CSFLogError(logTag,"%s Unable to updated Codec Database", __FUNCTION__); return kMediaConduitUnknownError; } } break; //we found a match } } }//end for codeclist }//end for if(!success) { CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__); return kMediaConduitInvalidReceiveCodec; } // XXX Currently, we gather up all of the feedback types that the remote // party indicated it supports for all video codecs and configure the entire // conduit based on those capabilities. This is technically out of spec, // as these values should be configured on a per-codec basis. However, // the video engine only provides this API on a per-conduit basis, so that's // how we have to do it. The approach of considering the remote capablities // for the entire conduit to be a union of all remote codec capabilities // (rather than the more conservative approach of using an intersection) // is made to provide as many feedback mechanisms as are likely to be // processed by the remote party (and should be relatively safe, since the // remote party is required to ignore feedback types that it does not // understand). // // Note that our configuration uses this union of remote capabilites as // input to the configuration. It is not isomorphic to the configuration. // For example, it only makes sense to have one frame request mechanism // active at a time; so, if the remote party indicates more than one // supported mechanism, we're only configuring the one we most prefer. // // See http://code.google.com/p/webrtc/issues/detail?id=2331 if (kf_request != webrtc::kViEKeyFrameRequestNone) { CSFLogDebug(logTag, "Enabling %s frame requests for video stream\n", (kf_request == webrtc::kViEKeyFrameRequestPliRtcp ? "PLI" : "FIR")); if(mPtrRTP->SetKeyFrameRequestMethod(mChannel, kf_request) != 0) { CSFLogError(logTag, "%s KeyFrameRequest Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitKeyFrameRequestError; } } switch (kf_request) { case webrtc::kViEKeyFrameRequestNone: mFrameRequestMethod = FrameRequestNone; break; case webrtc::kViEKeyFrameRequestPliRtcp: mFrameRequestMethod = FrameRequestPli; break; case webrtc::kViEKeyFrameRequestFirRtcp: mFrameRequestMethod = FrameRequestFir; break; default: MOZ_ASSERT(PR_FALSE); mFrameRequestMethod = FrameRequestUnknown; } if(use_nack_basic) { CSFLogDebug(logTag, "Enabling NACK (recv) for video stream\n"); if (mPtrRTP->SetNACKStatus(mChannel, true) != 0) { CSFLogError(logTag, "%s NACKStatus Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitNACKStatusError; } } mUsingNackBasic = use_nack_basic; //Start Receive on the video engine if(mPtrViEBase->StartReceive(mChannel) == -1) { error = mPtrViEBase->LastError(); CSFLogError(logTag, "%s Start Receive Error %d ", __FUNCTION__, error); return kMediaConduitUnknownError; } #ifdef MOZILLA_INTERNAL_API if (NS_IsMainThread()) { nsresult rv; nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); if (branch) { branch->GetBoolPref("media.video.test_latency", &mVideoLatencyTestEnable); } } } #endif // by now we should be successfully started the reception mPtrRTP->SetRembStatus(mChannel, false, true); mEngineReceiving = true; DumpCodecDB(); return kMediaConduitNoError; }
/** * Note: Setting the send-codec on the Video Engine will restart the encoder, * sets up new SSRC and reset RTP_RTCP module with the new codec setting. */ MediaConduitErrorCode WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig) { CSFLogDebug(logTag, "%s ", __FUNCTION__); bool codecFound = false; MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0; //webrtc engine errors webrtc::VideoCodec video_codec; std::string payloadName; //validate basic params if((condError = ValidateCodecConfig(codecConfig,true)) != kMediaConduitNoError) { return condError; } //Check if we have same codec already applied if(CheckCodecsForMatch(mCurSendCodecConfig, codecConfig)) { CSFLogDebug(logTag, "%s Codec has been applied already ", __FUNCTION__); return kMediaConduitCodecInUse; } //transmitting already ? if(mEngineTransmitting) { CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__); if(mPtrViEBase->StopSend(mChannel) == -1) { CSFLogError(logTag, "%s StopSend() Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } } mEngineTransmitting = false; if (codecConfig->mLoadManager) { mPtrViEBase->RegisterCpuOveruseObserver(mChannel, codecConfig->mLoadManager); mPtrViEBase->SetLoadManager(codecConfig->mLoadManager); } // we should be good here to set the new codec. for(int idx=0; idx < mPtrViECodec->NumberOfCodecs(); idx++) { if(0 == mPtrViECodec->GetCodec(idx, video_codec)) { payloadName = video_codec.plName; if(codecConfig->mName.compare(payloadName) == 0) { CodecConfigToWebRTCCodec(codecConfig, video_codec); codecFound = true; break; } } }//for if(codecFound == false) { CSFLogError(logTag, "%s Codec Mismatch ", __FUNCTION__); return kMediaConduitInvalidSendCodec; } if(mPtrViECodec->SetSendCodec(mChannel, video_codec) == -1) { error = mPtrViEBase->LastError(); if(error == kViECodecInvalidCodec) { CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__); return kMediaConduitInvalidSendCodec; } CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } mSendingWidth = 0; mSendingHeight = 0; if(codecConfig->RtcpFbIsSet(SDP_RTCP_FB_NACK_BASIC)) { CSFLogDebug(logTag, "Enabling NACK (send) for video stream\n"); if (mPtrRTP->SetNACKStatus(mChannel, true) != 0) { CSFLogError(logTag, "%s NACKStatus Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitNACKStatusError; } } if(mPtrViEBase->StartSend(mChannel) == -1) { CSFLogError(logTag, "%s Start Send Error %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } //Copy the applied config for future reference. delete mCurSendCodecConfig; mCurSendCodecConfig = new VideoCodecConfig(*codecConfig); mPtrRTP->SetRembStatus(mChannel, true, false); // by now we should be successfully started the transmission mEngineTransmitting = true; return kMediaConduitNoError; }
/** * Peforms intialization of the MANDATORY components of the Video Engine */ MediaConduitErrorCode WebrtcVideoConduit::Init(WebrtcVideoConduit *other) { CSFLogDebug(logTag, "%s this=%p other=%p", __FUNCTION__, this, other); if (other) { MOZ_ASSERT(!other->mOtherDirection); other->mOtherDirection = this; mOtherDirection = other; // only one can call ::Create()/GetVideoEngine() MOZ_ASSERT(other->mVideoEngine); mVideoEngine = other->mVideoEngine; } else { #ifdef MOZ_WIDGET_ANDROID jobject context = jsjni_GetGlobalContextRef(); // get the JVM JavaVM *jvm = jsjni_GetVM(); if (webrtc::VideoEngine::SetAndroidObjects(jvm, (void*)context) != 0) { CSFLogError(logTag, "%s: could not set Android objects", __FUNCTION__); return kMediaConduitSessionNotInited; } #endif // Per WebRTC APIs below function calls return nullptr on failure if( !(mVideoEngine = webrtc::VideoEngine::Create()) ) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } PRLogModuleInfo *logs = GetWebRTCLogInfo(); if (!gWebrtcTraceLoggingOn && logs && logs->level > 0) { // no need to a critical section or lock here gWebrtcTraceLoggingOn = 1; const char *file = PR_GetEnv("WEBRTC_TRACE_FILE"); if (!file) { file = "WebRTC.log"; } CSFLogDebug(logTag, "%s Logging webrtc to %s level %d", __FUNCTION__, file, logs->level); mVideoEngine->SetTraceFilter(logs->level); mVideoEngine->SetTraceFile(file); } } if( !(mPtrViEBase = ViEBase::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video base interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViECapture = ViECapture::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video capture interface", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViECodec = ViECodec::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video codec interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViENetwork = ViENetwork::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video network interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViERender = ViERender::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video render interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrRTP = webrtc::ViERTP_RTCP::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video RTCP interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if ( !(mPtrExtCodec = webrtc::ViEExternalCodec::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get external codec interface %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitSessionNotInited; } if (other) { mChannel = other->mChannel; mPtrExtCapture = other->mPtrExtCapture; mCapId = other->mCapId; } else { CSFLogDebug(logTag, "%s Engine Created: Init'ng the interfaces ",__FUNCTION__); if(mPtrViEBase->Init() == -1) { CSFLogError(logTag, " %s Video Engine Init Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitSessionNotInited; } if(mPtrViEBase->CreateChannel(mChannel) == -1) { CSFLogError(logTag, " %s Channel creation Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitChannelError; } if(mPtrViENetwork->RegisterSendTransport(mChannel, *this) == -1) { CSFLogError(logTag, "%s ViENetwork Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitTransportRegistrationFail; } if(mPtrViECapture->AllocateExternalCaptureDevice(mCapId, mPtrExtCapture) == -1) { CSFLogError(logTag, "%s Unable to Allocate capture module: %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitCaptureError; } if(mPtrViECapture->ConnectCaptureDevice(mCapId,mChannel) == -1) { CSFLogError(logTag, "%s Unable to Connect capture module: %d ", __FUNCTION__,mPtrViEBase->LastError()); return kMediaConduitCaptureError; } if(mPtrViERender->AddRenderer(mChannel, webrtc::kVideoI420, (webrtc::ExternalRenderer*) this) == -1) { CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__); return kMediaConduitInvalidRenderer; } // Set up some parameters, per juberti. Set MTU. if(mPtrViENetwork->SetMTU(mChannel, 1200) != 0) { CSFLogError(logTag, "%s MTU Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitMTUError; } // Turn on RTCP and loss feedback reporting. if(mPtrRTP->SetRTCPStatus(mChannel, webrtc::kRtcpCompound_RFC4585) != 0) { CSFLogError(logTag, "%s RTCPStatus Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitRTCPStatusError; } } CSFLogError(logTag, "%s Initialization Done", __FUNCTION__); return kMediaConduitNoError; }
MediaConduitErrorCode WebrtcAudioConduit::ConfigureRecvMediaCodecs( const std::vector<AudioCodecConfig*>& codecConfigList) { CSFLogDebug(logTag, "%s ", __FUNCTION__); MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0; //webrtc engine errors bool success = false; // Are we receiving already? If so, stop receiving and playout // since we can't apply new recv codec when the engine is playing. if(mEngineReceiving) { CSFLogDebug(logTag, "%s Engine Already Receiving. Attemping to Stop ", __FUNCTION__); // AudioEngine doesn't fail fatally on stopping reception. Ref:voe_errors.h. // hence we need not be strict in failing here on errors mPtrVoEBase->StopReceive(mChannel); CSFLogDebug(logTag, "%s Attemping to Stop playout ", __FUNCTION__); if(mPtrVoEBase->StopPlayout(mChannel) == -1) { if( mPtrVoEBase->LastError() == VE_CANNOT_STOP_PLAYOUT) { CSFLogDebug(logTag, "%s Stop-Playout Failed %d", __FUNCTION__, mPtrVoEBase->LastError()); return kMediaConduitPlayoutError; } } } mEngineReceiving = false; if(codecConfigList.empty()) { CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__); return kMediaConduitMalformedArgument; } // Try Applying the codecs in the list. // We succeed if at least one codec was applied and reception was // started successfully. for(std::vector<AudioCodecConfig*>::size_type i=0 ;i<codecConfigList.size();i++) { //if the codec param is invalid or diplicate, return error if((condError = ValidateCodecConfig(codecConfigList[i],false)) != kMediaConduitNoError) { return condError; } webrtc::CodecInst cinst; if(!CodecConfigToWebRTCCodec(codecConfigList[i],cinst)) { CSFLogError(logTag,"%s CodecConfig to WebRTC Codec Failed ",__FUNCTION__); continue; } if(mPtrVoECodec->SetRecPayloadType(mChannel,cinst) == -1) { error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s SetRecvCodec Failed %d ",__FUNCTION__, error); continue; } else { CSFLogDebug(logTag, "%s Successfully Set RecvCodec %s", __FUNCTION__, codecConfigList[i]->mName.c_str()); //copy this to local database if(CopyCodecToDB(codecConfigList[i])) { success = true; } else { CSFLogError(logTag,"%s Unable to updated Codec Database", __FUNCTION__); return kMediaConduitUnknownError; } } } //end for if(!success) { CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__); return kMediaConduitInvalidReceiveCodec; } //If we are here, atleast one codec should have been set if(mPtrVoEBase->StartReceive(mChannel) == -1) { error = mPtrVoEBase->LastError(); CSFLogError(logTag , "%s StartReceive Failed %d ",__FUNCTION__, error); if(error == VE_RECV_SOCKET_ERROR) { return kMediaConduitSocketError; } return kMediaConduitUnknownError; } if(mPtrVoEBase->StartPlayout(mChannel) == -1) { CSFLogError(logTag, "%s Starting playout Failed", __FUNCTION__); return kMediaConduitPlayoutError; } //we should be good here for setting this. mEngineReceiving = true; DumpCodecDB(); return kMediaConduitNoError; }
MediaConduitErrorCode WebrtcAudioConduit::ConfigureRecvMediaCodecs( const std::vector<AudioCodecConfig*>& codecConfigList) { CSFLogDebug(logTag, "%s ", __FUNCTION__); MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0; //webrtc engine errors bool success = false; // Are we receiving already? If so, stop receiving and playout // since we can't apply new recv codec when the engine is playing. condError = StopReceiving(); if (condError != kMediaConduitNoError) { return condError; } if(codecConfigList.empty()) { CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__); return kMediaConduitMalformedArgument; } // Try Applying the codecs in the list. // We succeed if at least one codec was applied and reception was // started successfully. for(std::vector<AudioCodecConfig*>::size_type i=0 ;i<codecConfigList.size();i++) { //if the codec param is invalid or diplicate, return error if((condError = ValidateCodecConfig(codecConfigList[i],false)) != kMediaConduitNoError) { return condError; } webrtc::CodecInst cinst; if(!CodecConfigToWebRTCCodec(codecConfigList[i],cinst)) { CSFLogError(logTag,"%s CodecConfig to WebRTC Codec Failed ",__FUNCTION__); continue; } if(mPtrVoECodec->SetRecPayloadType(mChannel,cinst) == -1) { error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s SetRecvCodec Failed %d ",__FUNCTION__, error); continue; } else { CSFLogDebug(logTag, "%s Successfully Set RecvCodec %s", __FUNCTION__, codecConfigList[i]->mName.c_str()); //copy this to local database if(CopyCodecToDB(codecConfigList[i])) { success = true; } else { CSFLogError(logTag,"%s Unable to updated Codec Database", __FUNCTION__); return kMediaConduitUnknownError; } } } //end for if(!success) { CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__); return kMediaConduitInvalidReceiveCodec; } //If we are here, atleast one codec should have been set condError = StartReceiving(); if (condError != kMediaConduitNoError) { return condError; } DumpCodecDB(); return kMediaConduitNoError; }
MediaConduitErrorCode WebrtcAudioConduit::SendAudioFrame(const int16_t audio_data[], int32_t lengthSamples, int32_t samplingFreqHz, int32_t capture_delay) { CSFLogDebug(logTag, "%s ", __FUNCTION__); // Following checks need to be performed // 1. Non null audio buffer pointer, // 2. invalid sampling frequency - less than 0 or unsupported ones // 3. Appropriate Sample Length for 10 ms audio-frame. This represents // block size the VoiceEngine feeds into encoder for passed in audio-frame // Ex: for 16000 sampling rate , valid block-length is 160 // Similarly for 32000 sampling rate, valid block length is 320 // We do the check by the verify modular operator below to be zero if(!audio_data || (lengthSamples <= 0) || (IsSamplingFreqSupported(samplingFreqHz) == false) || ((lengthSamples % (samplingFreqHz / 100) != 0)) ) { CSFLogError(logTag, "%s Invalid Parameters ",__FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } //validate capture time if(capture_delay < 0 ) { CSFLogError(logTag,"%s Invalid Capture Delay ", __FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } // if transmission is not started .. conduit cannot insert frames if(!mEngineTransmitting) { CSFLogError(logTag, "%s Engine not transmitting ", __FUNCTION__); return kMediaConduitSessionNotInited; } #ifdef MOZILLA_INTERNAL_API if (PR_LOG_TEST(GetLatencyLog(), PR_LOG_DEBUG)) { struct Processing insert = { TimeStamp::Now(), 0 }; mProcessing.AppendElement(insert); } #endif capture_delay = mCaptureDelay; //Insert the samples if(mPtrVoEXmedia->ExternalRecordingInsertData(audio_data, lengthSamples, samplingFreqHz, capture_delay) == -1) { int error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s Inserting audio data Failed %d", __FUNCTION__, error); if(error == VE_RUNTIME_REC_ERROR) { return kMediaConduitRecordingError; } return kMediaConduitUnknownError; } // we should be good here return kMediaConduitNoError; }
nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers, const std::vector<NrIceTurnServer>& turn_servers, NrIceCtx::Policy policy) { nsresult rv; nsCOMPtr<nsIProtocolProxyService> pps = do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to get proxy service: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } // We use the following URL to find the "default" proxy address for all HTTPS // connections. We will only attempt one HTTP(S) CONNECT per peer connection. // "example.com" is guaranteed to be unallocated and should return the best default. nsCOMPtr<nsIURI> fakeHttpsLocation; rv = NS_NewURI(getter_AddRefs(fakeHttpsLocation), "https://example.com"); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to set URI: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } nsCOMPtr<nsIScriptSecurityManager> secMan( do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv)); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to get IOService: %d", __FUNCTION__, (int)rv); CSFLogError(logTag, "%s: Failed to get securityManager: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } nsCOMPtr<nsIPrincipal> systemPrincipal; rv = secMan->GetSystemPrincipal(getter_AddRefs(systemPrincipal)); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to get systemPrincipal: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } nsCOMPtr<nsIChannel> channel; rv = NS_NewChannel(getter_AddRefs(channel), fakeHttpsLocation, systemPrincipal, nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, nsIContentPolicy::TYPE_OTHER); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to get channel from URI: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } RefPtr<ProtocolProxyQueryHandler> handler = new ProtocolProxyQueryHandler(this); rv = pps->AsyncResolve(channel, nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY | nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL, handler, getter_AddRefs(mProxyRequest)); if (NS_FAILED(rv)) { CSFLogError(logTag, "%s: Failed to resolve protocol proxy: %d", __FUNCTION__, (int)rv); return NS_ERROR_FAILURE; } #if !defined(MOZILLA_EXTERNAL_LINKAGE) bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false); if (!XRE_IsParentProcess()) { CSFLogError(logTag, "%s: ICE TCP not support on e10s", __FUNCTION__); ice_tcp = false; } bool default_address_only = Preferences::GetBool( "media.peerconnection.ice.default_address_only", false); #else bool ice_tcp = false; bool default_address_only = false; #endif // TODO([email protected]): need some way to set not offerer later // Looks like a bug in the NrIceCtx API. mIceCtx = NrIceCtx::Create("PC:" + mParentName, true, // Offerer mParent->GetAllowIceLoopback(), ice_tcp, mParent->GetAllowIceLinkLocal(), default_address_only, policy); if(!mIceCtx) { CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__); return NS_ERROR_FAILURE; } if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) { CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__); return rv; } // Give us a way to globally turn off TURN support #if !defined(MOZILLA_EXTERNAL_LINKAGE) bool disabled = Preferences::GetBool("media.peerconnection.turn.disable", false); #else bool disabled = false; #endif if (!disabled) { if (NS_FAILED(rv = mIceCtx->SetTurnServers(turn_servers))) { CSFLogError(logTag, "%s: Failed to set turn servers", __FUNCTION__); return rv; } } else if (turn_servers.size() != 0) { CSFLogError(logTag, "%s: Setting turn servers disabled", __FUNCTION__); } if (NS_FAILED(rv = mDNSResolver->Init())) { CSFLogError(logTag, "%s: Failed to initialize dns resolver", __FUNCTION__); return rv; } if (NS_FAILED(rv = mIceCtx->SetResolver(mDNSResolver->AllocateResolver()))) { CSFLogError(logTag, "%s: Failed to get dns resolver", __FUNCTION__); return rv; } mIceCtx->SignalGatheringStateChange.connect( this, &PeerConnectionMedia::IceGatheringStateChange_s); mIceCtx->SignalConnectionStateChange.connect( this, &PeerConnectionMedia::IceConnectionStateChange_s); return NS_OK; }
nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers) { // TODO([email protected]): need some way to set not offerer later // Looks like a bug in the NrIceCtx API. mIceCtx = NrIceCtx::Create("PC:" + mParent->GetHandle(), true); if(!mIceCtx) { CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__); return NS_ERROR_FAILURE; } nsresult rv; if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) { CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__); return rv; } if (NS_FAILED(rv = mDNSResolver->Init())) { CSFLogError(logTag, "%s: Failed to initialize dns resolver", __FUNCTION__); return rv; } if (NS_FAILED(rv = mIceCtx->SetResolver(mDNSResolver->AllocateResolver()))) { CSFLogError(logTag, "%s: Failed to get dns resolver", __FUNCTION__); return rv; } mIceCtx->SignalGatheringCompleted.connect(this, &PeerConnectionMedia::IceGatheringCompleted); mIceCtx->SignalCompleted.connect(this, &PeerConnectionMedia::IceCompleted); // Create three streams to start with. // One each for audio, video and DataChannel // TODO: this will be re-visited RefPtr<NrIceMediaStream> audioStream = mIceCtx->CreateStream("stream1", 2); RefPtr<NrIceMediaStream> videoStream = mIceCtx->CreateStream("stream2", 2); RefPtr<NrIceMediaStream> dcStream = mIceCtx->CreateStream("stream3", 2); if (!audioStream) { CSFLogError(logTag, "%s: audio stream is NULL", __FUNCTION__); return NS_ERROR_FAILURE; } else { mIceStreams.push_back(audioStream); } if (!videoStream) { CSFLogError(logTag, "%s: video stream is NULL", __FUNCTION__); return NS_ERROR_FAILURE; } else { mIceStreams.push_back(videoStream); } if (!dcStream) { CSFLogError(logTag, "%s: datachannel stream is NULL", __FUNCTION__); return NS_ERROR_FAILURE; } else { mIceStreams.push_back(dcStream); } // TODO([email protected]): This is not connected to the PCCimpl. // Will need to do that later. for (std::size_t i=0; i<mIceStreams.size(); i++) { mIceStreams[i]->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady); } // Start gathering nsresult res; mIceCtx->thread()->Dispatch(WrapRunnableRet( mIceCtx, &NrIceCtx::StartGathering, &res), NS_DISPATCH_SYNC ); if (NS_FAILED(res)) { CSFLogError(logTag, "%s: StartGathering failed: %u", __FUNCTION__, static_cast<uint32_t>(res)); return res; } return NS_OK; }
MediaConduitErrorCode WebrtcAudioConduit::ConfigureSendMediaCodec(const AudioCodecConfig* codecConfig) { CSFLogDebug(logTag, "%s ", __FUNCTION__); MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0;//webrtc engine errors webrtc::CodecInst cinst; //validate codec param if((condError = ValidateCodecConfig(codecConfig, true)) != kMediaConduitNoError) { return condError; } condError = StopTransmitting(); if (condError != kMediaConduitNoError) { return condError; } if(!CodecConfigToWebRTCCodec(codecConfig,cinst)) { CSFLogError(logTag,"%s CodecConfig to WebRTC Codec Failed ",__FUNCTION__); return kMediaConduitMalformedArgument; } if(mPtrVoECodec->SetSendCodec(mChannel, cinst) == -1) { error = mPtrVoEBase->LastError(); CSFLogError(logTag, "%s SetSendCodec - Invalid Codec %d ",__FUNCTION__, error); if(error == VE_CANNOT_SET_SEND_CODEC || error == VE_CODEC_ERROR) { CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__); return kMediaConduitInvalidSendCodec; } CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__, mPtrVoEBase->LastError()); return kMediaConduitUnknownError; } #if !defined(MOZILLA_EXTERNAL_LINKAGE) // TEMPORARY - see bug 694814 comment 2 nsresult rv; nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); if (branch) { branch->GetIntPref("media.peerconnection.capture_delay", &mCaptureDelay); } } #endif condError = StartTransmitting(); if (condError != kMediaConduitNoError) { return condError; } //Copy the applied config for future reference. delete mCurSendCodecConfig; mCurSendCodecConfig = new AudioCodecConfig(codecConfig->mType, codecConfig->mName, codecConfig->mFreq, codecConfig->mPacSize, codecConfig->mChannels, codecConfig->mRate); return kMediaConduitNoError; }
RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite, bool inbound, const void *key, size_t key_len) { nsresult res = Init(); if (!NS_SUCCEEDED(res)) return nullptr; RefPtr<SrtpFlow> flow = new SrtpFlow(); if (!key) { CSFLogError(LOGTAG, "Null SRTP key specified"); return nullptr; } if (key_len != SRTP_TOTAL_KEY_LENGTH) { CSFLogError(LOGTAG, "Invalid SRTP key length"); return nullptr; } srtp_policy_t policy; memset(&policy, 0, sizeof(srtp_policy_t)); // Note that we set the same cipher suite for RTP and RTCP // since any flow can only have one cipher suite with DTLS-SRTP switch (cipher_suite) { case SRTP_AES128_CM_HMAC_SHA1_80: CSFLogDebug(LOGTAG, "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80"); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); break; case SRTP_AES128_CM_HMAC_SHA1_32: CSFLogDebug(LOGTAG, "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_32"); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // 80-bit per RFC 5764 break; // S 4.1.2. default: CSFLogError(LOGTAG, "Request to set unknown SRTP cipher suite"); return nullptr; } // This key is copied into the srtp_t object, so we don't // need to keep it. policy.key = const_cast<unsigned char *>( static_cast<const unsigned char *>(key)); policy.ssrc.type = inbound ? ssrc_any_inbound : ssrc_any_outbound; policy.ssrc.value = 0; policy.ekt = nullptr; policy.window_size = 1024; // Use the Chrome value. Needs to be revisited. Default is 128 policy.allow_repeat_tx = 1; // Use Chrome value; needed for NACK mode to work policy.next = nullptr; // Now make the session srtp_err_status_t r = srtp_create(&flow->session_, &policy); if (r != srtp_err_status_ok) { CSFLogError(LOGTAG, "Error creating srtp session"); return nullptr; } return flow; }
/** * Note: Setting the send-codec on the Video Engine will restart the encoder, * sets up new SSRC and reset RTP_RTCP module with the new codec setting. */ MediaConduitErrorCode WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig) { CSFLogDebug(logTag, "%s ", __FUNCTION__); bool codecFound = false; MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0; //webrtc engine errors webrtc::VideoCodec video_codec; std::string payloadName; //validate basic params if((condError = ValidateCodecConfig(codecConfig,true)) != kMediaConduitNoError) { return condError; } //Check if we have same codec already applied if(CheckCodecsForMatch(mCurSendCodecConfig, codecConfig)) { CSFLogDebug(logTag, "%s Codec has been applied already ", __FUNCTION__); return kMediaConduitCodecInUse; } //transmitting already ? if(mEngineTransmitting) { CSFLogDebug(logTag, "%s Engine Already Sending. Attemping to Stop ", __FUNCTION__); if(mPtrViEBase->StopSend(mChannel) == -1) { CSFLogError(logTag, "%s StopSend() Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } } //reset the flag mEngineTransmitting = false; // we should be good here to set the new codec. for(int idx=0; idx < mPtrViECodec->NumberOfCodecs(); idx++) { if(0 == mPtrViECodec->GetCodec(idx, video_codec)) { payloadName = video_codec.plName; if(codecConfig->mName.compare(payloadName) == 0) { CodecConfigToWebRTCCodec(codecConfig, video_codec); codecFound = true; break; } } }//for if(codecFound == false) { CSFLogError(logTag, "%s Codec Mismatch ", __FUNCTION__); return kMediaConduitInvalidSendCodec; } if(mPtrViECodec->SetSendCodec(mChannel, video_codec) == -1) { error = mPtrViEBase->LastError(); if(error == kViECodecInvalidCodec) { CSFLogError(logTag, "%s Invalid Send Codec", __FUNCTION__); return kMediaConduitInvalidSendCodec; } CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } if(mPtrViEBase->StartSend(mChannel) == -1) { CSFLogError(logTag, "%s Start Send Error %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } //Copy the applied codec for future reference delete mCurSendCodecConfig; mCurSendCodecConfig = new VideoCodecConfig(codecConfig->mType, codecConfig->mName, codecConfig->mWidth, codecConfig->mHeight); // by now we should be successfully started the transmission mEngineTransmitting = true; return kMediaConduitNoError; }
/** * Performs initialization of the MANDATORY components of the Video Engine */ MediaConduitErrorCode WebrtcVideoConduit::Init() { CSFLogDebug(logTag, "%s this=%p", __FUNCTION__, this); #ifdef MOZILLA_INTERNAL_API // already know we must be on MainThread barring unit test weirdness MOZ_ASSERT(NS_IsMainThread()); nsresult rv; nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv); if (!NS_WARN_IF(NS_FAILED(rv))) { nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); if (branch) { int32_t temp; (void) NS_WARN_IF(NS_FAILED(branch->GetBoolPref("media.video.test_latency", &mVideoLatencyTestEnable))); (void) NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.min_bitrate", &temp))); if (temp >= 0) { mMinBitrate = temp; } (void) NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.start_bitrate", &temp))); if (temp >= 0) { mStartBitrate = temp; } (void) NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.max_bitrate", &temp))); if (temp >= 0) { mMaxBitrate = temp; } bool use_loadmanager = false; (void) NS_WARN_IF(NS_FAILED(branch->GetBoolPref("media.navigator.load_adapt", &use_loadmanager))); if (use_loadmanager) { mLoadManager = LoadManagerBuild(); } } } #endif #ifdef MOZ_WIDGET_ANDROID // get the JVM JavaVM *jvm = jsjni_GetVM(); if (webrtc::VideoEngine::SetAndroidObjects(jvm) != 0) { CSFLogError(logTag, "%s: could not set Android objects", __FUNCTION__); return kMediaConduitSessionNotInited; } #endif // Per WebRTC APIs below function calls return nullptr on failure mVideoEngine = webrtc::VideoEngine::Create(); if(!mVideoEngine) { CSFLogError(logTag, "%s Unable to create video engine ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViEBase = ViEBase::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video base interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViECapture = ViECapture::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video capture interface", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViECodec = ViECodec::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video codec interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViENetwork = ViENetwork::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video network interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if( !(mPtrViERender = ViERender::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video render interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } mPtrExtCodec = webrtc::ViEExternalCodec::GetInterface(mVideoEngine); if (!mPtrExtCodec) { CSFLogError(logTag, "%s Unable to get external codec interface: %d ", __FUNCTION__,mPtrViEBase->LastError()); return kMediaConduitSessionNotInited; } if( !(mPtrRTP = webrtc::ViERTP_RTCP::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get video RTCP interface ", __FUNCTION__); return kMediaConduitSessionNotInited; } if ( !(mPtrExtCodec = webrtc::ViEExternalCodec::GetInterface(mVideoEngine))) { CSFLogError(logTag, "%s Unable to get external codec interface %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitSessionNotInited; } CSFLogDebug(logTag, "%s Engine Created: Init'ng the interfaces ",__FUNCTION__); if(mPtrViEBase->Init() == -1) { CSFLogError(logTag, " %s Video Engine Init Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitSessionNotInited; } if(mPtrViEBase->CreateChannel(mChannel) == -1) { CSFLogError(logTag, " %s Channel creation Failed %d ",__FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitChannelError; } if(mPtrViENetwork->RegisterSendTransport(mChannel, *this) == -1) { CSFLogError(logTag, "%s ViENetwork Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitTransportRegistrationFail; } if(mPtrViECapture->AllocateExternalCaptureDevice(mCapId, mPtrExtCapture) == -1) { CSFLogError(logTag, "%s Unable to Allocate capture module: %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitCaptureError; } if(mPtrViECapture->ConnectCaptureDevice(mCapId,mChannel) == -1) { CSFLogError(logTag, "%s Unable to Connect capture module: %d ", __FUNCTION__,mPtrViEBase->LastError()); return kMediaConduitCaptureError; } // Set up some parameters, per juberti. Set MTU. if(mPtrViENetwork->SetMTU(mChannel, 1200) != 0) { CSFLogError(logTag, "%s MTU Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitMTUError; } // Turn on RTCP and loss feedback reporting. if(mPtrRTP->SetRTCPStatus(mChannel, webrtc::kRtcpCompound_RFC4585) != 0) { CSFLogError(logTag, "%s RTCPStatus Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitRTCPStatusError; } if (mPtrViERender->AddRenderer(mChannel, webrtc::kVideoI420, (webrtc::ExternalRenderer*) this) == -1) { CSFLogError(logTag, "%s Failed to added external renderer ", __FUNCTION__); return kMediaConduitInvalidRenderer; } if (mLoadManager) { mPtrViEBase->RegisterCpuOveruseObserver(mChannel, mLoadManager); mPtrViEBase->SetLoadManager(mLoadManager); } CSFLogError(logTag, "%s Initialization Done", __FUNCTION__); return kMediaConduitNoError; }
MediaConduitErrorCode WebrtcVideoConduit::ConfigureRecvMediaCodecs( const std::vector<VideoCodecConfig* >& codecConfigList) { CSFLogDebug(logTag, "%s ", __FUNCTION__); MediaConduitErrorCode condError = kMediaConduitNoError; int error = 0; //webrtc engine errors bool success = false; std::string payloadName; if(mEngineReceiving) { CSFLogDebug(logTag, "%s Engine Already Receiving . Attemping to Stop ", __FUNCTION__); if(mPtrViEBase->StopReceive(mChannel) == -1) { error = mPtrViEBase->LastError(); if(error == kViEBaseUnknownError) { CSFLogDebug(logTag, "%s StopReceive() Success ", __FUNCTION__); mEngineReceiving = false; } else { CSFLogError(logTag, "%s StopReceive() Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitUnknownError; } } } if(codecConfigList.empty()) { CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__); return kMediaConduitMalformedArgument; } //Try Applying the codecs in the list // we treat as success if atleast one codec was applied and reception was // started successfully. for(std::vector<VideoCodecConfig*>::size_type i=0;i < codecConfigList.size();i++) { //if the codec param is invalid or diplicate, return error if((condError = ValidateCodecConfig(codecConfigList[i],false)) != kMediaConduitNoError) { return condError; } webrtc::VideoCodec video_codec; mEngineReceiving = false; memset(&video_codec, 0, sizeof(webrtc::VideoCodec)); //Retrieve pre-populated codec structure for our codec. for(int idx=0; idx < mPtrViECodec->NumberOfCodecs(); idx++) { if(mPtrViECodec->GetCodec(idx, video_codec) == 0) { payloadName = video_codec.plName; if(codecConfigList[i]->mName.compare(payloadName) == 0) { CodecConfigToWebRTCCodec(codecConfigList[i], video_codec); if(mPtrViECodec->SetReceiveCodec(mChannel,video_codec) == -1) { CSFLogError(logTag, "%s Invalid Receive Codec %d ", __FUNCTION__, mPtrViEBase->LastError()); } else { CSFLogError(logTag, "%s Successfully Set the codec %s", __FUNCTION__, codecConfigList[i]->mName.c_str()); if(CopyCodecToDB(codecConfigList[i])) { success = true; } else { CSFLogError(logTag,"%s Unable to updated Codec Database", __FUNCTION__); return kMediaConduitUnknownError; } } break; //we found a match } } }//end for codeclist }//end for if(!success) { CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__); return kMediaConduitInvalidReceiveCodec; } //Start Receive on the video engine if(mPtrViEBase->StartReceive(mChannel) == -1) { error = mPtrViEBase->LastError(); CSFLogError(logTag, "%s Start Receive Error %d ", __FUNCTION__, error); return kMediaConduitUnknownError; } // by now we should be successfully started the reception mEngineReceiving = true; DumpCodecDB(); return kMediaConduitNoError; }
MediaConduitErrorCode WebrtcVideoConduit::ConfigureRecvMediaCodecs( const std::vector<VideoCodecConfig* >& codecConfigList) { CSFLogDebug(logTag, "%s ", __FUNCTION__); MediaConduitErrorCode condError = kMediaConduitNoError; bool success = false; std::string payloadName; condError = StopReceiving(); if (condError != kMediaConduitNoError) { return condError; } if(codecConfigList.empty()) { CSFLogError(logTag, "%s Zero number of codecs to configure", __FUNCTION__); return kMediaConduitMalformedArgument; } webrtc::ViEKeyFrameRequestMethod kf_request = webrtc::kViEKeyFrameRequestNone; bool use_nack_basic = false; //Try Applying the codecs in the list // we treat as success if atleast one codec was applied and reception was // started successfully. for(std::vector<VideoCodecConfig*>::size_type i=0;i < codecConfigList.size();i++) { //if the codec param is invalid or diplicate, return error if((condError = ValidateCodecConfig(codecConfigList[i],false)) != kMediaConduitNoError) { return condError; } // Check for the keyframe request type: PLI is preferred // over FIR, and FIR is preferred over none. if (codecConfigList[i]->RtcpFbNackIsSet("pli")) { kf_request = webrtc::kViEKeyFrameRequestPliRtcp; } else if(kf_request == webrtc::kViEKeyFrameRequestNone && codecConfigList[i]->RtcpFbCcmIsSet("fir")) { kf_request = webrtc::kViEKeyFrameRequestFirRtcp; } // Check whether NACK is requested if(codecConfigList[i]->RtcpFbNackIsSet("")) { use_nack_basic = true; } webrtc::VideoCodec video_codec; memset(&video_codec, 0, sizeof(webrtc::VideoCodec)); if (mExternalRecvCodec && codecConfigList[i]->mType == mExternalRecvCodec->mType) { CSFLogError(logTag, "%s Configuring External H264 Receive Codec", __FUNCTION__); // XXX Do we need a separate setting for receive maxbitrate? Is it // different for hardware codecs? For now assume symmetry. CodecConfigToWebRTCCodec(codecConfigList[i], video_codec); // values SetReceiveCodec() cares about are name, type, maxbitrate if(mPtrViECodec->SetReceiveCodec(mChannel,video_codec) == -1) { CSFLogError(logTag, "%s Invalid Receive Codec %d ", __FUNCTION__, mPtrViEBase->LastError()); } else { CSFLogError(logTag, "%s Successfully Set the codec %s", __FUNCTION__, codecConfigList[i]->mName.c_str()); if(CopyCodecToDB(codecConfigList[i])) { success = true; } else { CSFLogError(logTag,"%s Unable to update Codec Database", __FUNCTION__); return kMediaConduitUnknownError; } } } else { //Retrieve pre-populated codec structure for our codec. for(int idx=0; idx < mPtrViECodec->NumberOfCodecs(); idx++) { if(mPtrViECodec->GetCodec(idx, video_codec) == 0) { payloadName = video_codec.plName; if(codecConfigList[i]->mName.compare(payloadName) == 0) { CodecConfigToWebRTCCodec(codecConfigList[i], video_codec); if(mPtrViECodec->SetReceiveCodec(mChannel,video_codec) == -1) { CSFLogError(logTag, "%s Invalid Receive Codec %d ", __FUNCTION__, mPtrViEBase->LastError()); } else { CSFLogError(logTag, "%s Successfully Set the codec %s", __FUNCTION__, codecConfigList[i]->mName.c_str()); if(CopyCodecToDB(codecConfigList[i])) { success = true; } else { CSFLogError(logTag,"%s Unable to update Codec Database", __FUNCTION__); return kMediaConduitUnknownError; } } break; //we found a match } } }//end for codeclist } }//end for if(!success) { CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__); return kMediaConduitInvalidReceiveCodec; } if (!mVideoCodecStat) { mVideoCodecStat = new VideoCodecStatistics(mChannel, mPtrViECodec); } mVideoCodecStat->Register(false); // XXX Currently, we gather up all of the feedback types that the remote // party indicated it supports for all video codecs and configure the entire // conduit based on those capabilities. This is technically out of spec, // as these values should be configured on a per-codec basis. However, // the video engine only provides this API on a per-conduit basis, so that's // how we have to do it. The approach of considering the remote capablities // for the entire conduit to be a union of all remote codec capabilities // (rather than the more conservative approach of using an intersection) // is made to provide as many feedback mechanisms as are likely to be // processed by the remote party (and should be relatively safe, since the // remote party is required to ignore feedback types that it does not // understand). // // Note that our configuration uses this union of remote capabilites as // input to the configuration. It is not isomorphic to the configuration. // For example, it only makes sense to have one frame request mechanism // active at a time; so, if the remote party indicates more than one // supported mechanism, we're only configuring the one we most prefer. // // See http://code.google.com/p/webrtc/issues/detail?id=2331 if (kf_request != webrtc::kViEKeyFrameRequestNone) { CSFLogDebug(logTag, "Enabling %s frame requests for video stream\n", (kf_request == webrtc::kViEKeyFrameRequestPliRtcp ? "PLI" : "FIR")); if(mPtrRTP->SetKeyFrameRequestMethod(mChannel, kf_request) != 0) { CSFLogError(logTag, "%s KeyFrameRequest Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitKeyFrameRequestError; } } switch (kf_request) { case webrtc::kViEKeyFrameRequestNone: mFrameRequestMethod = FrameRequestNone; break; case webrtc::kViEKeyFrameRequestPliRtcp: mFrameRequestMethod = FrameRequestPli; break; case webrtc::kViEKeyFrameRequestFirRtcp: mFrameRequestMethod = FrameRequestFir; break; default: MOZ_ASSERT(PR_FALSE); mFrameRequestMethod = FrameRequestUnknown; } if(use_nack_basic) { CSFLogDebug(logTag, "Enabling NACK (recv) for video stream\n"); if (mPtrRTP->SetNACKStatus(mChannel, true) != 0) { CSFLogError(logTag, "%s NACKStatus Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitNACKStatusError; } } mUsingNackBasic = use_nack_basic; condError = StartReceiving(); if (condError != kMediaConduitNoError) { return condError; } // by now we should be successfully started the reception mPtrRTP->SetRembStatus(mChannel, false, true); DumpCodecDB(); return kMediaConduitNoError; }
MediaConduitErrorCode WebrtcVideoConduit::SendVideoFrame(unsigned char* video_frame, unsigned int video_frame_length, unsigned short width, unsigned short height, VideoType video_type, uint64_t capture_time) { CSFLogDebug(logTag, "%s ", __FUNCTION__); //check for the parameters sanity if(!video_frame || video_frame_length == 0 || width == 0 || height == 0) { CSFLogError(logTag, "%s Invalid Parameters ",__FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } webrtc::RawVideoType type; switch (video_type) { case kVideoI420: type = webrtc::kVideoI420; break; case kVideoNV21: type = webrtc::kVideoNV21; break; default: CSFLogError(logTag, "%s VideoType Invalid. Only 1420 and NV21 Supported",__FUNCTION__); MOZ_ASSERT(PR_FALSE); return kMediaConduitMalformedArgument; } //Transmission should be enabled before we insert any frames. if(!mEngineTransmitting) { CSFLogError(logTag, "%s Engine not transmitting ", __FUNCTION__); return kMediaConduitSessionNotInited; } // enforce even width/height (paranoia) MOZ_ASSERT(!(width & 1)); MOZ_ASSERT(!(height & 1)); if (!SelectSendResolution(width, height)) { return kMediaConduitCaptureError; } //insert the frame to video engine in I420 format only if(mPtrExtCapture->IncomingFrame(video_frame, video_frame_length, width, height, type, (unsigned long long)capture_time) == -1) { CSFLogError(logTag, "%s IncomingFrame Failed %d ", __FUNCTION__, mPtrViEBase->LastError()); return kMediaConduitCaptureError; } CSFLogError(logTag, "%s Inserted A Frame", __FUNCTION__); return kMediaConduitNoError; }