void NrIceMediaStream::Ready() { MOZ_MTLOG(ML_DEBUG, "Marking stream ready '" << name_ << "'"); state_ = ICE_OPEN; SignalReady(this); }
RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name, bool offerer, bool set_interface_priorities, bool allow_loopback) { RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer); // Initialize the crypto callbacks and logging stuff if (!initialized) { NR_reg_init(NR_REG_MODE_LOCAL); RLogRingBuffer::CreateInstance(); nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl; initialized = true; // Set the priorites for candidate type preferences. // These numbers come from RFC 5245 S. 4.1.2.2 NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_SRV_RFLX, 100); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_PEER_RFLX, 110); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_HOST, 126); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_RELAYED, 5); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_RELAYED_TCP, 0); if (set_interface_priorities) { NR_reg_set_uchar((char *)"ice.pref.interface.rl0", 255); NR_reg_set_uchar((char *)"ice.pref.interface.wi0", 254); NR_reg_set_uchar((char *)"ice.pref.interface.lo0", 253); NR_reg_set_uchar((char *)"ice.pref.interface.en1", 252); NR_reg_set_uchar((char *)"ice.pref.interface.en0", 251); NR_reg_set_uchar((char *)"ice.pref.interface.eth0", 252); NR_reg_set_uchar((char *)"ice.pref.interface.eth1", 251); NR_reg_set_uchar((char *)"ice.pref.interface.eth2", 249); NR_reg_set_uchar((char *)"ice.pref.interface.ppp", 250); NR_reg_set_uchar((char *)"ice.pref.interface.ppp0", 249); NR_reg_set_uchar((char *)"ice.pref.interface.en2", 248); NR_reg_set_uchar((char *)"ice.pref.interface.en3", 247); NR_reg_set_uchar((char *)"ice.pref.interface.em0", 251); NR_reg_set_uchar((char *)"ice.pref.interface.em1", 252); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet0", 240); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet1", 241); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet3", 239); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet4", 238); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet5", 237); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet6", 236); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet7", 235); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet8", 234); NR_reg_set_uchar((char *)"ice.pref.interface.virbr0", 233); NR_reg_set_uchar((char *)"ice.pref.interface.wlan0", 232); } NR_reg_set_uint4((char *)"stun.client.maximum_transmits",7); NR_reg_set_uint4((char *)NR_ICE_REG_TRICKLE_GRACE_PERIOD, 5000); if (allow_loopback) { NR_reg_set_char((char *)NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, 1); } } // Create the ICE context int r; UINT4 flags = offerer ? NR_ICE_CTX_FLAGS_OFFERER: NR_ICE_CTX_FLAGS_ANSWERER; flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION; r = nr_ice_ctx_create(const_cast<char *>(name.c_str()), flags, &ctx->ctx_); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name << "'"); return nullptr; } #ifdef USE_INTERFACE_PRIORITIZER nr_interface_prioritizer *prioritizer = CreateInterfacePrioritizer(); if (!prioritizer) { MOZ_MTLOG(PR_LOG_ERROR, "Couldn't create interface prioritizer."); return nullptr; } r = nr_ice_ctx_set_interface_prioritizer(ctx->ctx_, prioritizer); if (r) { MOZ_MTLOG(PR_LOG_ERROR, "Couldn't set interface prioritizer."); return nullptr; } #endif // USE_INTERFACE_PRIORITIZER if (ctx->generating_trickle()) { r = nr_ice_ctx_set_trickle_cb(ctx->ctx_, &NrIceCtx::trickle_cb, ctx); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name << "'"); return nullptr; } } // Create the handler objects ctx->ice_handler_vtbl_ = new nr_ice_handler_vtbl(); ctx->ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair; ctx->ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready; ctx->ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed; ctx->ice_handler_vtbl_->ice_completed = &NrIceCtx::ice_completed; ctx->ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd; ctx->ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking; ctx->ice_handler_ = new nr_ice_handler(); ctx->ice_handler_->vtbl = ctx->ice_handler_vtbl_; ctx->ice_handler_->obj = ctx; // Create the peer ctx. Because we do not support parallel forking, we // only have one peer ctx. std::string peer_name = name + ":default"; r = nr_ice_peer_ctx_create(ctx->ctx_, ctx->ice_handler_, const_cast<char *>(peer_name.c_str()), &ctx->peer_); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << name << "'"); return nullptr; } nsresult rv; ctx->sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (!NS_SUCCEEDED(rv)) return nullptr; return ctx; }
void TransportLayerLogging::StateChange(TransportLayer *layer, State state) { MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Received StateChange to " << state); SetState(state); }
/* * Add external H.264 video codec. */ MediaConduitErrorCode MediaPipelineFactory::EnsureExternalCodec(VideoSessionConduit& aConduit, VideoCodecConfig* aConfig, bool aIsSend) { if (aConfig->mName == "VP8") { #ifdef MOZ_WEBRTC_MEDIACODEC if (aIsSend) { #ifdef MOZILLA_INTERNAL_API bool enabled = mozilla::Preferences::GetBool("media.navigator.hardware.vp8_encode.acceleration_enabled", false); #else bool enabled = false; #endif if (enabled) { nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); if (gfxInfo) { int32_t status; if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE, &status))) { if (status != nsIGfxInfo::FEATURE_STATUS_OK) { NS_WARNING("VP8 encoder hardware is not whitelisted: disabling.\n"); } else { VideoEncoder* encoder = nullptr; encoder = MediaCodecVideoCodec::CreateEncoder(MediaCodecVideoCodec::CodecType::CODEC_VP8); if (encoder) { return aConduit.SetExternalSendCodec(aConfig, encoder); } else { return kMediaConduitNoError; } } } } } } else { #ifdef MOZILLA_INTERNAL_API bool enabled = mozilla::Preferences::GetBool("media.navigator.hardware.vp8_decode.acceleration_enabled", false); #else bool enabled = false; #endif if (enabled) { nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); if (gfxInfo) { int32_t status; if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE, &status))) { if (status != nsIGfxInfo::FEATURE_STATUS_OK) { NS_WARNING("VP8 decoder hardware is not whitelisted: disabling.\n"); } else { VideoDecoder* decoder; decoder = MediaCodecVideoCodec::CreateDecoder(MediaCodecVideoCodec::CodecType::CODEC_VP8); if (decoder) { return aConduit.SetExternalRecvCodec(aConfig, decoder); } else { return kMediaConduitNoError; } } } } } } #endif return kMediaConduitNoError; } else if (aConfig->mName == "VP9") { return kMediaConduitNoError; } else if (aConfig->mName == "H264") { if (aConduit.CodecPluginID() != 0) { return kMediaConduitNoError; } // Register H.264 codec. if (aIsSend) { VideoEncoder* encoder = nullptr; #ifdef MOZ_WEBRTC_OMX encoder = OMXVideoCodec::CreateEncoder(OMXVideoCodec::CodecType::CODEC_H264); #elif !defined(MOZILLA_XPCOMRT_API) encoder = GmpVideoCodec::CreateEncoder(); #endif if (encoder) { return aConduit.SetExternalSendCodec(aConfig, encoder); } else { return kMediaConduitInvalidSendCodec; } } else { VideoDecoder* decoder = nullptr; #ifdef MOZ_WEBRTC_OMX decoder = OMXVideoCodec::CreateDecoder(OMXVideoCodec::CodecType::CODEC_H264); #elif !defined(MOZILLA_XPCOMRT_API) decoder = GmpVideoCodec::CreateDecoder(); #endif if (decoder) { return aConduit.SetExternalRecvCodec(aConfig, decoder); } else { return kMediaConduitInvalidReceiveCodec; } } NS_NOTREACHED("Shouldn't get here!"); } else { MOZ_MTLOG(ML_ERROR, "Invalid video codec configured: " << aConfig->mName.c_str()); return aIsSend ? kMediaConduitInvalidSendCodec : kMediaConduitInvalidReceiveCodec; } NS_NOTREACHED("Shouldn't get here!"); }
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) { MOZ_MTLOG(ML_ERROR, "Null SRTP key specified"); return nullptr; } if (key_len != SRTP_TOTAL_KEY_LENGTH) { MOZ_MTLOG(ML_ERROR, "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: MOZ_MTLOG(ML_DEBUG, "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80"); crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); break; case SRTP_AES128_CM_HMAC_SHA1_32: MOZ_MTLOG(ML_DEBUG, "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_32"); crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // 80-bit per RFC 5764 break; // S 4.1.2. default: MOZ_MTLOG(ML_ERROR, "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 err_status_t r = srtp_create(&flow->session_, &policy); if (r != err_status_ok) { MOZ_MTLOG(ML_ERROR, "Error creating srtp session"); return nullptr; } return flow; }
RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name, bool offerer, bool allow_loopback, bool tcp_enabled, bool allow_link_local, bool hide_non_default, Policy policy) { RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer, policy); // Initialize the crypto callbacks and logging stuff if (!initialized) { NR_reg_init(NR_REG_MODE_LOCAL); RLogRingBuffer::CreateInstance(); nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl; initialized = true; // Set the priorites for candidate type preferences. // These numbers come from RFC 5245 S. 4.1.2.2 NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_SRV_RFLX, 100); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_PEER_RFLX, 110); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_HOST, 126); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_RELAYED, 5); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_SRV_RFLX_TCP, 99); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_PEER_RFLX_TCP, 109); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_HOST_TCP, 125); NR_reg_set_uchar((char *)NR_ICE_REG_PREF_TYPE_RELAYED_TCP, 0); int32_t stun_client_maximum_transmits = 7; int32_t ice_trickle_grace_period = 5000; int32_t ice_tcp_so_sock_count = 3; int32_t ice_tcp_listen_backlog = 10; nsAutoCString force_net_interface; #ifndef MOZILLA_XPCOMRT_API nsresult res; nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &res); if (NS_SUCCEEDED(res)) { nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs); if (branch) { branch->GetIntPref( "media.peerconnection.ice.stun_client_maximum_transmits", &stun_client_maximum_transmits); branch->GetIntPref( "media.peerconnection.ice.trickle_grace_period", &ice_trickle_grace_period); branch->GetIntPref( "media.peerconnection.ice.tcp_so_sock_count", &ice_tcp_so_sock_count); branch->GetIntPref( "media.peerconnection.ice.tcp_listen_backlog", &ice_tcp_listen_backlog); branch->GetCharPref( "media.peerconnection.ice.force_interface", getter_Copies(force_net_interface)); } } #endif NR_reg_set_uint4((char *)"stun.client.maximum_transmits", stun_client_maximum_transmits); NR_reg_set_uint4((char *)NR_ICE_REG_TRICKLE_GRACE_PERIOD, ice_trickle_grace_period); NR_reg_set_int4((char *)NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT, ice_tcp_so_sock_count); NR_reg_set_int4((char *)NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG, ice_tcp_listen_backlog); NR_reg_set_char((char *)NR_ICE_REG_ICE_TCP_DISABLE, !tcp_enabled); if (allow_loopback) { NR_reg_set_char((char *)NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, 1); } if (allow_link_local) { NR_reg_set_char((char *)NR_STUN_REG_PREF_ALLOW_LINK_LOCAL_ADDRS, 1); } if (force_net_interface.Length() > 0) { // Stupid cast.... but needed const nsCString& flat = PromiseFlatCString(static_cast<nsACString&>(force_net_interface)); NR_reg_set_string((char *)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME, const_cast<char*>(flat.get())); } } // Create the ICE context int r; UINT4 flags = offerer ? NR_ICE_CTX_FLAGS_OFFERER: NR_ICE_CTX_FLAGS_ANSWERER; flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION; if (policy == ICE_POLICY_RELAY) { flags |= NR_ICE_CTX_FLAGS_RELAY_ONLY; } if (hide_non_default) flags |= NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS; r = nr_ice_ctx_create(const_cast<char *>(name.c_str()), flags, &ctx->ctx_); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name << "'"); return nullptr; } nr_interface_prioritizer *prioritizer = CreateInterfacePrioritizer(); if (!prioritizer) { MOZ_MTLOG(LogLevel::Error, "Couldn't create interface prioritizer."); return nullptr; } r = nr_ice_ctx_set_interface_prioritizer(ctx->ctx_, prioritizer); if (r) { MOZ_MTLOG(LogLevel::Error, "Couldn't set interface prioritizer."); return nullptr; } if (ctx->generating_trickle()) { r = nr_ice_ctx_set_trickle_cb(ctx->ctx_, &NrIceCtx::trickle_cb, ctx); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name << "'"); return nullptr; } } // Create the handler objects ctx->ice_handler_vtbl_ = new nr_ice_handler_vtbl(); ctx->ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair; ctx->ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready; ctx->ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed; ctx->ice_handler_vtbl_->ice_completed = &NrIceCtx::ice_completed; ctx->ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd; ctx->ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking; ctx->ice_handler_ = new nr_ice_handler(); ctx->ice_handler_->vtbl = ctx->ice_handler_vtbl_; ctx->ice_handler_->obj = ctx; // Create the peer ctx. Because we do not support parallel forking, we // only have one peer ctx. std::string peer_name = name + ":default"; r = nr_ice_peer_ctx_create(ctx->ctx_, ctx->ice_handler_, const_cast<char *>(peer_name.c_str()), &ctx->peer_); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << name << "'"); return nullptr; } nsresult rv; ctx->sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (!NS_SUCCEEDED(rv)) return nullptr; return ctx; }
nsresult MediaPipelineFactory::GetOrCreateVideoConduit( const JsepTrackPair& aTrackPair, const JsepTrack& aTrack, RefPtr<MediaSessionConduit>* aConduitp) { if (!aTrack.GetNegotiatedDetails()) { MOZ_ASSERT(false, "Track is missing negotiated details"); return NS_ERROR_INVALID_ARG; } bool receiving = aTrack.GetDirection() == sdp::kRecv; RefPtr<VideoSessionConduit> conduit = mPCMedia->GetVideoConduit(aTrackPair.mLevel); if (!conduit) { conduit = VideoSessionConduit::Create(); if (!conduit) { MOZ_MTLOG(ML_ERROR, "Could not create video conduit"); return NS_ERROR_FAILURE; } mPCMedia->AddVideoConduit(aTrackPair.mLevel, conduit); } if (!GetBestCodec(*aTrack.GetNegotiatedDetails())) { MOZ_MTLOG(ML_ERROR, "Can't set up a conduit with 0 codecs"); return NS_ERROR_FAILURE; } size_t numCodecs = aTrack.GetNegotiatedDetails()->GetCodecCount(); bool configuredH264 = false; if (receiving) { PtrVector<VideoCodecConfig> configs; for (size_t i = 0; i < numCodecs; i++) { const JsepCodecDescription* cdesc = aTrack.GetNegotiatedDetails()->GetCodec(i); // We can only handle configuring one recv H264 codec if (configuredH264 && (cdesc->mName == "H264")) { continue; } VideoCodecConfig* configRaw; nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw); if (NS_FAILED(rv)) return rv; UniquePtr<VideoCodecConfig> config(configRaw); if (EnsureExternalCodec(*conduit, config.get(), false)) { continue; } if (cdesc->mName == "H264") { configuredH264 = true; } configs.values.push_back(config.release()); } auto error = conduit->ConfigureRecvMediaCodecs(configs.values); if (error) { MOZ_MTLOG(ML_ERROR, "ConfigureRecvMediaCodecs failed: " << error); return NS_ERROR_FAILURE; } if (!aTrackPair.mSending) { // No send track, but we still need to configure an SSRC for receiver // reports. if (!conduit->SetLocalSSRC(aTrackPair.mRecvonlySsrc)) { MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed"); return NS_ERROR_FAILURE; } } } else { // For now we only expect to have one ssrc per local track. auto ssrcs = aTrack.GetSsrcs(); if (!ssrcs.empty()) { if (!conduit->SetLocalSSRC(ssrcs.front())) { MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed"); return NS_ERROR_FAILURE; } } conduit->SetLocalCNAME(aTrack.GetCNAME().c_str()); const JsepCodecDescription* cdesc = GetBestCodec(*aTrack.GetNegotiatedDetails()); VideoCodecConfig* configRaw; nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw); if (NS_FAILED(rv)) return rv; rv = ConfigureVideoCodecMode(aTrack,*conduit); if (NS_FAILED(rv)) { return rv; } // Take possession of this pointer ScopedDeletePtr<VideoCodecConfig> config(configRaw); if (EnsureExternalCodec(*conduit, config, true)) { MOZ_MTLOG(ML_ERROR, "External codec not available"); return NS_ERROR_FAILURE; } auto error = conduit->ConfigureSendMediaCodec(config); if (error) { MOZ_MTLOG(ML_ERROR, "ConfigureSendMediaCodec failed: " << error); return NS_ERROR_FAILURE; } } *aConduitp = conduit; return NS_OK; }
nsresult MediaPipelineFactory::CreateOrUpdateMediaPipeline( const JsepTrackPair& aTrackPair, const JsepTrack& aTrack) { #if !defined(MOZILLA_EXTERNAL_LINKAGE) // The GMP code is all the way on the other side of webrtc.org, and it is not // feasible to plumb this information all the way through. So, we set it (for // the duration of this call) in a global variable. This allows the GMP code // to report errors to the PC. WebrtcGmpPCHandleSetter setter(mPC->GetHandle()); #endif MOZ_ASSERT(aTrackPair.mRtpTransport); bool receiving = aTrack.GetDirection() == sdp::kRecv; size_t level; RefPtr<TransportFlow> rtpFlow; RefPtr<TransportFlow> rtcpFlow; nsAutoPtr<MediaPipelineFilter> filter; nsresult rv = GetTransportParameters(aTrackPair, aTrack, &level, &rtpFlow, &rtcpFlow, &filter); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "Failed to get transport parameters for pipeline, rv=" << static_cast<unsigned>(rv)); return rv; } if (aTrack.GetMediaType() == SdpMediaSection::kApplication) { // GetTransportParameters has already done everything we need for // datachannel. return NS_OK; } // Find the stream we need SourceStreamInfo* stream; if (receiving) { stream = mPCMedia->GetRemoteStreamById(aTrack.GetStreamId()); } else { stream = mPCMedia->GetLocalStreamById(aTrack.GetStreamId()); } if (!stream) { MOZ_MTLOG(ML_ERROR, "Negotiated " << (receiving ? "recv" : "send") << " stream id " << aTrack.GetStreamId() << " was never added"); MOZ_ASSERT(false); return NS_ERROR_FAILURE; } if (!stream->HasTrack(aTrack.GetTrackId())) { MOZ_MTLOG(ML_ERROR, "Negotiated " << (receiving ? "recv" : "send") << " track id " << aTrack.GetTrackId() << " was never added"); MOZ_ASSERT(false); return NS_ERROR_FAILURE; } RefPtr<MediaSessionConduit> conduit; if (aTrack.GetMediaType() == SdpMediaSection::kAudio) { rv = GetOrCreateAudioConduit(aTrackPair, aTrack, &conduit); if (NS_FAILED(rv)) return rv; } else if (aTrack.GetMediaType() == SdpMediaSection::kVideo) { rv = GetOrCreateVideoConduit(aTrackPair, aTrack, &conduit); if (NS_FAILED(rv)) return rv; } else { // We've created the TransportFlow, nothing else to do here. return NS_OK; } RefPtr<MediaPipeline> pipeline = stream->GetPipelineByTrackId_m(aTrack.GetTrackId()); if (pipeline && pipeline->level() != static_cast<int>(level)) { MOZ_MTLOG(ML_WARNING, "Track " << aTrack.GetTrackId() << " has moved from level " << pipeline->level() << " to level " << level << ". This requires re-creating the MediaPipeline."); // Since we do not support changing the conduit on a pre-existing // MediaPipeline pipeline = nullptr; stream->RemoveTrack(aTrack.GetTrackId()); stream->AddTrack(aTrack.GetTrackId()); } if (pipeline) { pipeline->UpdateTransport_m(level, rtpFlow, rtcpFlow, filter); return NS_OK; } MOZ_MTLOG(ML_DEBUG, "Creating media pipeline" << " m-line index=" << aTrackPair.mLevel << " type=" << aTrack.GetMediaType() << " direction=" << aTrack.GetDirection()); if (receiving) { rv = CreateMediaPipelineReceiving(aTrackPair, aTrack, level, rtpFlow, rtcpFlow, filter, conduit); if (NS_FAILED(rv)) return rv; } else { rv = CreateMediaPipelineSending(aTrackPair, aTrack, level, rtpFlow, rtcpFlow, filter, conduit); if (NS_FAILED(rv)) return rv; } return NS_OK; }
nsresult MediaPipelineFactory::CreateMediaPipelineReceiving( const JsepTrackPair& aTrackPair, const JsepTrack& aTrack, size_t aLevel, RefPtr<TransportFlow> aRtpFlow, RefPtr<TransportFlow> aRtcpFlow, nsAutoPtr<MediaPipelineFilter> aFilter, const RefPtr<MediaSessionConduit>& aConduit) { // We will error out earlier if this isn't here. RefPtr<RemoteSourceStreamInfo> stream = mPCMedia->GetRemoteStreamById(aTrack.GetStreamId()); RefPtr<MediaPipelineReceive> pipeline; TrackID numericTrackId = stream->GetNumericTrackId(aTrack.GetTrackId()); MOZ_ASSERT(numericTrackId != TRACK_INVALID); bool queue_track = stream->ShouldQueueTracks(); MOZ_MTLOG(ML_DEBUG, __FUNCTION__ << ": Creating pipeline for " << numericTrackId << " -> " << aTrack.GetTrackId()); if (aTrack.GetMediaType() == SdpMediaSection::kAudio) { pipeline = new MediaPipelineReceiveAudio( mPC->GetHandle(), mPC->GetMainThread().get(), mPC->GetSTSThread(), stream->GetMediaStream()->GetInputStream(), aTrack.GetTrackId(), numericTrackId, aLevel, static_cast<AudioSessionConduit*>(aConduit.get()), // Ugly downcast. aRtpFlow, aRtcpFlow, aFilter, queue_track); } else if (aTrack.GetMediaType() == SdpMediaSection::kVideo) { pipeline = new MediaPipelineReceiveVideo( mPC->GetHandle(), mPC->GetMainThread().get(), mPC->GetSTSThread(), stream->GetMediaStream()->GetInputStream(), aTrack.GetTrackId(), numericTrackId, aLevel, static_cast<VideoSessionConduit*>(aConduit.get()), // Ugly downcast. aRtpFlow, aRtcpFlow, aFilter, queue_track); } else { MOZ_ASSERT(false); MOZ_MTLOG(ML_ERROR, "Invalid media type in CreateMediaPipelineReceiving"); return NS_ERROR_FAILURE; } nsresult rv = pipeline->Init(); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "Couldn't initialize receiving pipeline"); return rv; } rv = stream->StorePipeline(aTrack.GetTrackId(), RefPtr<MediaPipeline>(pipeline)); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "Couldn't store receiving pipeline " << static_cast<unsigned>(rv)); return rv; } stream->SyncPipeline(pipeline); return NS_OK; }
bool NrIceCtx::Initialize(const std::string& ufrag, const std::string& pwd) { MOZ_ASSERT(!ufrag.empty()); MOZ_ASSERT(!pwd.empty()); if (ufrag.empty() || pwd.empty()) { return false; } // Create the ICE context int r; UINT4 flags = offerer_ ? NR_ICE_CTX_FLAGS_OFFERER: NR_ICE_CTX_FLAGS_ANSWERER; flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION; switch (policy_) { case ICE_POLICY_RELAY: flags |= NR_ICE_CTX_FLAGS_RELAY_ONLY; break; case ICE_POLICY_NO_HOST: flags |= NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES; break; case ICE_POLICY_ALL: break; } r = nr_ice_ctx_create_with_credentials(const_cast<char *>(name_.c_str()), flags, const_cast<char *>(ufrag.c_str()), const_cast<char *>(pwd.c_str()), &ctx_); MOZ_ASSERT(ufrag == ctx_->ufrag); MOZ_ASSERT(pwd == ctx_->pwd); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name_ << "'"); return false; } nr_interface_prioritizer *prioritizer = CreateInterfacePrioritizer(); if (!prioritizer) { MOZ_MTLOG(LogLevel::Error, "Couldn't create interface prioritizer."); return false; } r = nr_ice_ctx_set_interface_prioritizer(ctx_, prioritizer); if (r) { MOZ_MTLOG(LogLevel::Error, "Couldn't set interface prioritizer."); return false; } if (generating_trickle()) { r = nr_ice_ctx_set_trickle_cb(ctx_, &NrIceCtx::trickle_cb, this); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name_ << "'"); return false; } } nsCString mapping_type; nsCString filtering_type; bool block_udp = false; nsresult rv; nsCOMPtr<nsIPrefService> pref_service = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIPrefBranch> pref_branch; rv = pref_service->GetBranch(nullptr, getter_AddRefs(pref_branch)); if (NS_SUCCEEDED(rv)) { rv = pref_branch->GetCharPref( "media.peerconnection.nat_simulator.mapping_type", getter_Copies(mapping_type)); rv = pref_branch->GetCharPref( "media.peerconnection.nat_simulator.filtering_type", getter_Copies(filtering_type)); rv = pref_branch->GetBoolPref( "media.peerconnection.nat_simulator.block_udp", &block_udp); } } if (!mapping_type.IsEmpty() && !filtering_type.IsEmpty()) { MOZ_MTLOG(ML_DEBUG, "NAT filtering type: " << filtering_type.get()); MOZ_MTLOG(ML_DEBUG, "NAT mapping type: " << mapping_type.get()); TestNat* test_nat = new TestNat; test_nat->filtering_type_ = TestNat::ToNatBehavior(filtering_type.get()); test_nat->mapping_type_ = TestNat::ToNatBehavior(mapping_type.get()); test_nat->block_udp_ = block_udp; test_nat->enabled_ = true; SetNat(test_nat); } // Create the handler objects ice_handler_vtbl_ = new nr_ice_handler_vtbl(); ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair; ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready; ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed; ice_handler_vtbl_->ice_connected = &NrIceCtx::ice_connected; ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd; ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking; ice_handler_vtbl_->ice_disconnected = &NrIceCtx::ice_disconnected; ice_handler_ = new nr_ice_handler(); ice_handler_->vtbl = ice_handler_vtbl_; ice_handler_->obj = this; // Create the peer ctx. Because we do not support parallel forking, we // only have one peer ctx. std::string peer_name = name_ + ":default"; r = nr_ice_peer_ctx_create(ctx_, ice_handler_, const_cast<char *>(peer_name.c_str()), &peer_); if (r) { MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << name_ << "'"); return false; } sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (!NS_SUCCEEDED(rv)) return false; return true; }
nsresult MediaPipelineFactory::CreateOrGetTransportFlow( size_t aLevel, bool aIsRtcp, const JsepTransport& aTransport, RefPtr<TransportFlow>* aFlowOutparam) { nsresult rv; RefPtr<TransportFlow> flow; flow = mPCMedia->GetTransportFlow(aLevel, aIsRtcp); if (flow) { *aFlowOutparam = flow; return NS_OK; } std::ostringstream osId; osId << mPC->GetHandle() << ":" << 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>(mPC->GetHandle()); auto dtls = MakeUnique<TransportLayerDtls>(); dtls->SetRole(aTransport.mDtls->GetRole() == JsepDtlsTransport::kJsepDtlsClient ? TransportLayerDtls::CLIENT : TransportLayerDtls::SERVER); RefPtr<DtlsIdentity> pcid = mPC->Identity(); if (!pcid) { MOZ_MTLOG(ML_ERROR, "Failed to get DTLS identity."); return NS_ERROR_FAILURE; } dtls->SetIdentity(pcid); const SdpFingerprintAttributeList& fingerprints = aTransport.mDtls->GetFingerprints(); for (auto fp = fingerprints.mFingerprints.begin(); fp != fingerprints.mFingerprints.end(); ++fp) { std::ostringstream ss; ss << fp->hashFunc; rv = dtls->SetVerificationDigest(ss.str(), &fp->fingerprint[0], fp->fingerprint.size()); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "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)) { MOZ_MTLOG(ML_ERROR, "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 (!mPC->PrivacyRequested()) { alpnDefault = "webrtc"; alpn.insert(alpnDefault); } rv = dtls->SetAlpn(alpn, alpnDefault); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "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()); rv = mPCMedia->GetSTSThread()->Dispatch( WrapRunnableNM(FinalizeTransportFlow_s, mPCMedia, flow, aLevel, aIsRtcp, layers), NS_DISPATCH_NORMAL); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "Failed to dispatch FinalizeTransportFlow_s"); return rv; } mPCMedia->AddTransportFlow(aLevel, aIsRtcp, flow); *aFlowOutparam = flow; return NS_OK; }
// TODO: make sure this is called from STS. Otherwise // we have thread safety issues bool TransportLayerDtls::Setup() { CheckThread(); SECStatus rv; if (!downward_) { MOZ_MTLOG(ML_ERROR, "DTLS layer with nothing below. This is useless"); return false; } nspr_io_adapter_ = new TransportLayerNSPRAdapter(downward_); if (!identity_) { MOZ_MTLOG(ML_ERROR, "Can't start DTLS without an identity"); return false; } if (verification_mode_ == VERIFY_UNSET) { MOZ_MTLOG(ML_ERROR, "Can't start DTLS without specifying a verification mode"); return false; } if (transport_layer_identity == PR_INVALID_IO_LAYER) { transport_layer_identity = PR_GetUniqueIdentity("nssstreamadapter"); } ScopedPRFileDesc pr_fd(PR_CreateIOLayerStub(transport_layer_identity, &TransportLayerMethods)); MOZ_ASSERT(pr_fd != nullptr); if (!pr_fd) return false; pr_fd->secret = reinterpret_cast<PRFilePrivate *>(nspr_io_adapter_.get()); ScopedPRFileDesc ssl_fd(DTLS_ImportFD(nullptr, pr_fd)); MOZ_ASSERT(ssl_fd != nullptr); // This should never happen if (!ssl_fd) { return false; } pr_fd.forget(); // ownership transfered to ssl_fd; if (role_ == CLIENT) { MOZ_MTLOG(ML_DEBUG, "Setting up DTLS as client"); rv = SSL_GetClientAuthDataHook(ssl_fd, GetClientAuthDataHook, this); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); return false; } } else { MOZ_MTLOG(ML_DEBUG, "Setting up DTLS as server"); // Server side rv = SSL_ConfigSecureServer(ssl_fd, identity_->cert(), identity_->privkey(), kt_rsa); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); return false; } // Insist on a certificate from the client rv = SSL_OptionSet(ssl_fd, SSL_REQUEST_CERTIFICATE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't request certificate"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_REQUIRE_CERTIFICATE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't require certificate"); return false; } } // Require TLS 1.1 or 1.2. Perhaps some day in the future we will allow TLS // 1.0 for stream modes. SSLVersionRange version_range = { SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_2 }; rv = SSL_VersionRangeSet(ssl_fd, &version_range); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Can't disable SSLv3"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_SESSION_TICKETS, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable session tickets"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_NO_CACHE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable session caching"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_DEFLATE, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable deflate"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable renegotiation"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_FALSE_START, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable false start"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_NO_LOCKS, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable locks"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable ECDHE key reuse"); return false; } if (!SetupCipherSuites(ssl_fd)) { return false; } // Certificate validation rv = SSL_AuthCertificateHook(ssl_fd, AuthCertificateHook, reinterpret_cast<void *>(this)); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set certificate validation hook"); return false; } // Now start the handshake rv = SSL_ResetHandshake(ssl_fd, role_ == SERVER ? PR_TRUE : PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't reset handshake"); return false; } ssl_fd_ = ssl_fd.forget(); // Finally, get ready to receive data downward_->SignalStateChange.connect(this, &TransportLayerDtls::StateChange); downward_->SignalPacketReceived.connect(this, &TransportLayerDtls::PacketReceived); if (downward_->state() == TS_OPEN) { Handshake(); } return true; }
nsresult MediaPipelineFactory::CreateOrUpdateMediaPipeline( const JsepTrackPair& aTrackPair, const JsepTrack& aTrack) { MOZ_ASSERT(aTrackPair.mRtpTransport); bool receiving = aTrack.GetDirection() == JsepTrack::Direction::kJsepTrackReceiving; size_t level; RefPtr<TransportFlow> rtpFlow; RefPtr<TransportFlow> rtcpFlow; nsAutoPtr<MediaPipelineFilter> filter; nsresult rv = GetTransportParameters(aTrackPair, aTrack, &level, &rtpFlow, &rtcpFlow, &filter); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "Failed to get transport parameters for pipeline, rv=" << static_cast<unsigned>(rv)); return rv; } if (aTrack.GetMediaType() == SdpMediaSection::kApplication) { // GetTransportParameters has already done everything we need for // datachannel. return NS_OK; } // Find the stream we need SourceStreamInfo* stream; if (receiving) { stream = mPCMedia->GetRemoteStreamById(aTrack.GetStreamId()); } else { stream = mPCMedia->GetLocalStreamById(aTrack.GetStreamId()); } if (!stream) { MOZ_MTLOG(ML_ERROR, "Negotiated " << (receiving ? "recv" : "send") << " stream id " << aTrack.GetStreamId() << " was never added"); MOZ_ASSERT(false); return NS_ERROR_FAILURE; } if (!stream->HasTrack(aTrack.GetTrackId())) { MOZ_MTLOG(ML_ERROR, "Negotiated " << (receiving ? "recv" : "send") << " track id " << aTrack.GetTrackId() << " was never added"); MOZ_ASSERT(false); return NS_ERROR_FAILURE; } RefPtr<MediaSessionConduit> conduit; if (aTrack.GetMediaType() == SdpMediaSection::kAudio) { rv = GetOrCreateAudioConduit(aTrackPair, aTrack, &conduit); if (NS_FAILED(rv)) return rv; } else if (aTrack.GetMediaType() == SdpMediaSection::kVideo) { rv = GetOrCreateVideoConduit(aTrackPair, aTrack, &conduit); if (NS_FAILED(rv)) return rv; } else { // We've created the TransportFlow, nothing else to do here. return NS_OK; } RefPtr<MediaPipeline> pipeline = stream->GetPipelineByTrackId_m(aTrack.GetTrackId()); if (pipeline && pipeline->level() != static_cast<int>(level)) { MOZ_MTLOG(ML_WARNING, "Track " << aTrack.GetTrackId() << " has moved from level " << pipeline->level() << " to level " << level << ". This requires re-creating the MediaPipeline."); // Since we do not support changing the conduit on a pre-existing // MediaPipeline pipeline = nullptr; stream->RemoveTrack(aTrack.GetTrackId()); stream->AddTrack(aTrack.GetTrackId()); } if (pipeline) { pipeline->UpdateTransport_m(level, rtpFlow, rtcpFlow, filter); return NS_OK; } MOZ_MTLOG(ML_DEBUG, "Creating media pipeline" << " m-line index=" << aTrackPair.mLevel << " type=" << aTrack.GetMediaType() << " direction=" << aTrack.GetDirection()); if (receiving) { rv = CreateMediaPipelineReceiving(aTrackPair, aTrack, level, rtpFlow, rtcpFlow, filter, conduit); if (NS_FAILED(rv)) return rv; } else { rv = CreateMediaPipelineSending(aTrackPair, aTrack, level, rtpFlow, rtcpFlow, filter, conduit); if (NS_FAILED(rv)) return rv; } return NS_OK; }
// This is all-or-nothing. nsresult TransportFlow::PushLayers(nsAutoPtr<std::queue<TransportLayer *> > layers) { MOZ_ASSERT(!layers->empty()); if (layers->empty()) { MOZ_MTLOG(PR_LOG_ERROR, id_ << ": Can't call PushLayers with empty layers"); return NS_ERROR_INVALID_ARG; } // Don't allow pushes once we are in error state. if (state_ == TransportLayer::TS_ERROR) { MOZ_MTLOG(PR_LOG_ERROR, id_ << ": Can't call PushLayers in error state for flow "); return NS_ERROR_FAILURE; } nsresult rv = NS_OK; // Disconnect all the old signals. disconnect_all(); TransportLayer *layer; while (!layers->empty()) { TransportLayer *old_layer = layers_.empty() ? nullptr : layers_.front(); layer = layers->front(); rv = layer->Init(); if (NS_FAILED(rv)) { MOZ_MTLOG(PR_LOG_ERROR, id_ << ": Layer initialization failed; invalidating flow "); break; } // Push the layer onto the queue. layers_.push_front(layer); layers->pop(); layer->Inserted(this, old_layer); } if (NS_FAILED(rv)) { // Destroy any layers we could not push. while (!layers->empty()) { delete layers->front(); layers->pop(); } // Now destroy the rest of the flow, because it's no longer // in an acceptable state. while (!layers_.empty()) { delete layers_.front(); layers_.pop_front(); } // Set ourselves to have failed. StateChangeInt(TransportLayer::TS_ERROR); // Return failure. return rv; } // Finally, attach ourselves to the top layer. layer->SignalStateChange.connect(this, &TransportFlow::StateChange); layer->SignalPacketReceived.connect(this, &TransportFlow::PacketReceived); StateChangeInt(layer->state()); // Signals if the state changes. return NS_OK; }
void NrIceMediaStream::Close() { MOZ_MTLOG(ML_DEBUG, "Marking stream closed '" << name_ << "'"); state_ = ICE_CLOSED; stream_ = nullptr; }
nsresult MediaPipelineFactory::GetOrCreateAudioConduit( const JsepTrackPair& aTrackPair, const JsepTrack& aTrack, RefPtr<MediaSessionConduit>* aConduitp) { if (!aTrack.GetNegotiatedDetails()) { MOZ_ASSERT(false, "Track is missing negotiated details"); return NS_ERROR_INVALID_ARG; } bool receiving = aTrack.GetDirection() == sdp::kRecv; RefPtr<AudioSessionConduit> conduit = mPCMedia->GetAudioConduit(aTrackPair.mLevel); if (!conduit) { conduit = AudioSessionConduit::Create(); if (!conduit) { MOZ_MTLOG(ML_ERROR, "Could not create audio conduit"); return NS_ERROR_FAILURE; } mPCMedia->AddAudioConduit(aTrackPair.mLevel, conduit); } if (!GetBestCodec(*aTrack.GetNegotiatedDetails())) { MOZ_MTLOG(ML_ERROR, "Can't set up a conduit with 0 codecs"); return NS_ERROR_FAILURE; } size_t numCodecs = aTrack.GetNegotiatedDetails()->GetCodecCount(); if (receiving) { PtrVector<AudioCodecConfig> configs; for (size_t i = 0; i < numCodecs; i++) { const JsepCodecDescription* cdesc = aTrack.GetNegotiatedDetails()->GetCodec(i); AudioCodecConfig* configRaw; nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw); if (NS_FAILED(rv)) return rv; configs.values.push_back(configRaw); } auto error = conduit->ConfigureRecvMediaCodecs(configs.values); if (error) { MOZ_MTLOG(ML_ERROR, "ConfigureRecvMediaCodecs failed: " << error); return NS_ERROR_FAILURE; } if (!aTrackPair.mSending) { // No send track, but we still need to configure an SSRC for receiver // reports. if (!conduit->SetLocalSSRC(aTrackPair.mRecvonlySsrc)) { MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed"); return NS_ERROR_FAILURE; } } } else { // For now we only expect to have one ssrc per local track. auto ssrcs = aTrack.GetSsrcs(); if (!ssrcs.empty()) { if (!conduit->SetLocalSSRC(ssrcs.front())) { MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed"); return NS_ERROR_FAILURE; } } conduit->SetLocalCNAME(aTrack.GetCNAME().c_str()); const JsepCodecDescription* cdesc = GetBestCodec(*aTrack.GetNegotiatedDetails()); AudioCodecConfig* configRaw; nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw); if (NS_FAILED(rv)) return rv; ScopedDeletePtr<AudioCodecConfig> config(configRaw); auto error = conduit->ConfigureSendMediaCodec(config.get()); if (error) { MOZ_MTLOG(ML_ERROR, "ConfigureSendMediaCodec failed: " << error); return NS_ERROR_FAILURE; } const SdpExtmapAttributeList::Extmap* audioLevelExt = aTrack.GetNegotiatedDetails()->GetExt( "urn:ietf:params:rtp-hdrext:ssrc-audio-level"); if (audioLevelExt) { MOZ_MTLOG(ML_DEBUG, "Calling EnableAudioLevelExtension"); error = conduit->EnableAudioLevelExtension(true, audioLevelExt->entry); if (error) { MOZ_MTLOG(ML_ERROR, "EnableAudioLevelExtension failed: " << error); return NS_ERROR_FAILURE; } } } *aConduitp = conduit; return NS_OK; }
RefPtr<NrIceCtx> NrIceCtx::Create(const std::string& name, bool offerer, bool set_interface_priorities) { RefPtr<NrIceCtx> ctx = new NrIceCtx(name, offerer); // Initialize the crypto callbacks if (!initialized) { NR_reg_init(NR_REG_MODE_LOCAL); nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl; initialized = true; // Set the priorites for candidate type preferences NR_reg_set_uchar((char *)"ice.pref.type.srv_rflx",100); NR_reg_set_uchar((char *)"ice.pref.type.peer_rflx",105); NR_reg_set_uchar((char *)"ice.pref.type.prflx",99); NR_reg_set_uchar((char *)"ice.pref.type.host",125); NR_reg_set_uchar((char *)"ice.pref.type.relayed",126); if (set_interface_priorities) { NR_reg_set_uchar((char *)"ice.pref.interface.rl0", 255); NR_reg_set_uchar((char *)"ice.pref.interface.wi0", 254); NR_reg_set_uchar((char *)"ice.pref.interface.lo0", 253); NR_reg_set_uchar((char *)"ice.pref.interface.en1", 252); NR_reg_set_uchar((char *)"ice.pref.interface.en0", 251); NR_reg_set_uchar((char *)"ice.pref.interface.eth0", 252); NR_reg_set_uchar((char *)"ice.pref.interface.eth1", 251); NR_reg_set_uchar((char *)"ice.pref.interface.eth2", 249); NR_reg_set_uchar((char *)"ice.pref.interface.ppp", 250); NR_reg_set_uchar((char *)"ice.pref.interface.ppp0", 249); NR_reg_set_uchar((char *)"ice.pref.interface.en2", 248); NR_reg_set_uchar((char *)"ice.pref.interface.en3", 247); NR_reg_set_uchar((char *)"ice.pref.interface.em0", 251); NR_reg_set_uchar((char *)"ice.pref.interface.em1", 252); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet0", 240); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet1", 241); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet3", 239); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet4", 238); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet5", 237); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet6", 236); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet7", 235); NR_reg_set_uchar((char *)"ice.pref.interface.vmnet8", 234); NR_reg_set_uchar((char *)"ice.pref.interface.virbr0", 233); NR_reg_set_uchar((char *)"ice.pref.interface.wlan0", 232); } NR_reg_set_uint4((char *)"stun.client.maximum_transmits",4); } // Create the ICE context int r; UINT4 flags = offerer ? NR_ICE_CTX_FLAGS_OFFERER: NR_ICE_CTX_FLAGS_ANSWERER; flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION; r = nr_ice_ctx_create(const_cast<char *>(name.c_str()), flags, &ctx->ctx_); if (r) { MOZ_MTLOG(PR_LOG_ERROR, "Couldn't create ICE ctx for '" << name << "'"); return nullptr; } // Create the handler objects ctx->ice_handler_vtbl_ = new nr_ice_handler_vtbl(); ctx->ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair; ctx->ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready; ctx->ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed; ctx->ice_handler_vtbl_->ice_completed = &NrIceCtx::ice_completed; ctx->ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd; ctx->ice_handler_ = new nr_ice_handler(); ctx->ice_handler_->vtbl = ctx->ice_handler_vtbl_; ctx->ice_handler_->obj = ctx; // Create the peer ctx. Because we do not support parallel forking, we // only have one peer ctx. std::string peer_name = name + ":default"; r = nr_ice_peer_ctx_create(ctx->ctx_, ctx->ice_handler_, const_cast<char *>(peer_name.c_str()), &ctx->peer_); if (r) { MOZ_MTLOG(PR_LOG_ERROR, "Couldn't create ICE peer ctx for '" << name << "'"); return nullptr; } nsresult rv; ctx->sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (!NS_SUCCEEDED(rv)) return nullptr; return ctx; }
nsresult MediaPipelineFactory::GetOrCreateVideoConduit( const JsepTrackPair& aTrackPair, const JsepTrack& aTrack, RefPtr<MediaSessionConduit>* aConduitp) { if (!aTrack.GetNegotiatedDetails()) { MOZ_ASSERT(false, "Track is missing negotiated details"); return NS_ERROR_INVALID_ARG; } bool receiving = aTrack.GetDirection() == sdp::kRecv; RefPtr<VideoSessionConduit> conduit = mPCMedia->GetVideoConduit(aTrackPair.mLevel); if (!conduit) { conduit = VideoSessionConduit::Create(); if (!conduit) { MOZ_MTLOG(ML_ERROR, "Could not create video conduit"); return NS_ERROR_FAILURE; } mPCMedia->AddVideoConduit(aTrackPair.mLevel, conduit); } PtrVector<VideoCodecConfig> configs; nsresult rv = NegotiatedDetailsToVideoCodecConfigs( *aTrack.GetNegotiatedDetails(), &configs); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "Failed to convert JsepCodecDescriptions to " "VideoCodecConfigs."); return rv; } if (configs.values.empty()) { MOZ_MTLOG(ML_ERROR, "Can't set up a conduit with 0 codecs"); return NS_ERROR_FAILURE; } if (receiving) { // Prune out stuff we cannot actually do. We should work to eliminate the // need for this. bool configuredH264 = false; for (size_t i = 0; i < configs.values.size();) { // TODO(bug 1200768): We can only handle configuring one recv H264 codec if (configuredH264 && (configs.values[i]->mName == "H264")) { delete configs.values[i]; configs.values.erase(configs.values.begin() + i); continue; } // TODO(bug 1018791): This really should be checked sooner if (EnsureExternalCodec(*conduit, configs.values[i], false)) { delete configs.values[i]; configs.values.erase(configs.values.begin() + i); continue; } if (configs.values[i]->mName == "H264") { configuredH264 = true; } ++i; } auto error = conduit->ConfigureRecvMediaCodecs(configs.values); if (error) { MOZ_MTLOG(ML_ERROR, "ConfigureRecvMediaCodecs failed: " << error); return NS_ERROR_FAILURE; } if (!aTrackPair.mSending) { // No send track, but we still need to configure an SSRC for receiver // reports. if (!conduit->SetLocalSSRC(aTrackPair.mRecvonlySsrc)) { MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed"); return NS_ERROR_FAILURE; } } } else { // For now we only expect to have one ssrc per local track. auto ssrcs = aTrack.GetSsrcs(); if (!ssrcs.empty()) { if (!conduit->SetLocalSSRC(ssrcs.front())) { MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed"); return NS_ERROR_FAILURE; } } conduit->SetLocalCNAME(aTrack.GetCNAME().c_str()); rv = ConfigureVideoCodecMode(aTrack,*conduit); if (NS_FAILED(rv)) { return rv; } // TODO(bug 1018791): This really should be checked sooner if (EnsureExternalCodec(*conduit, configs.values[0], true)) { MOZ_MTLOG(ML_ERROR, "External codec not available"); return NS_ERROR_FAILURE; } auto error = conduit->ConfigureSendMediaCodec(configs.values[0]); if (error) { MOZ_MTLOG(ML_ERROR, "ConfigureSendMediaCodec failed: " << error); return NS_ERROR_FAILURE; } } *aConduitp = conduit; return NS_OK; }