nsresult MediaPipelineFactory::CreateMediaPipelineSending( const JsepTrackPair& aTrackPair, const JsepTrack& aTrack, size_t aLevel, RefPtr<TransportFlow> aRtpFlow, RefPtr<TransportFlow> aRtcpFlow, nsAutoPtr<MediaPipelineFilter> aFilter, const RefPtr<MediaSessionConduit>& aConduit) { nsresult rv; // This is checked earlier RefPtr<LocalSourceStreamInfo> stream = mPCMedia->GetLocalStreamById(aTrack.GetStreamId()); // Now we have all the pieces, create the pipeline RefPtr<MediaPipelineTransmit> pipeline = new MediaPipelineTransmit( mPC->GetHandle(), mPC->GetMainThread().get(), mPC->GetSTSThread(), stream->GetMediaStream(), aTrack.GetTrackId(), aLevel, aTrack.GetMediaType() == SdpMediaSection::kVideo, aConduit, aRtpFlow, aRtcpFlow, aFilter); #if !defined(MOZILLA_EXTERNAL_LINKAGE) // implement checking for peerIdentity (where failure == black/silence) nsIDocument* doc = mPC->GetWindow()->GetExtantDoc(); if (doc) { pipeline->UpdateSinkIdentity_m(doc->NodePrincipal(), mPC->GetPeerIdentity()); } else { MOZ_MTLOG(ML_ERROR, "Cannot initialize pipeline without attached doc"); return NS_ERROR_FAILURE; // Don't remove this till we know it's safe. } #endif rv = pipeline->Init(); if (NS_FAILED(rv)) { MOZ_MTLOG(ML_ERROR, "Couldn't initialize sending 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; } return NS_OK; }
nsresult MediaPipelineFactory::GetTransportParameters( const JsepTrackPair& aTrackPair, const JsepTrack& aTrack, size_t* aLevelOut, RefPtr<TransportFlow>* aRtpOut, RefPtr<TransportFlow>* aRtcpOut, nsAutoPtr<MediaPipelineFilter>* aFilterOut) { *aLevelOut = aTrackPair.mLevel; size_t transportLevel = aTrackPair.mBundleLevel.isSome() ? *aTrackPair.mBundleLevel : aTrackPair.mLevel; nsresult rv = CreateOrGetTransportFlow( transportLevel, false, *aTrackPair.mRtpTransport, aRtpOut); if (NS_FAILED(rv)) { return rv; } MOZ_ASSERT(aRtpOut); if (aTrackPair.mRtcpTransport) { rv = CreateOrGetTransportFlow( transportLevel, true, *aTrackPair.mRtcpTransport, aRtcpOut); if (NS_FAILED(rv)) { return rv; } MOZ_ASSERT(aRtcpOut); } if (aTrackPair.mBundleLevel.isSome()) { bool receiving = aTrack.GetDirection() == sdp::kRecv; *aFilterOut = new MediaPipelineFilter; if (receiving) { // Add remote SSRCs so we can distinguish which RTP packets actually // belong to this pipeline (also RTCP sender reports). for (auto i = aTrack.GetSsrcs().begin(); i != aTrack.GetSsrcs().end(); ++i) { (*aFilterOut)->AddRemoteSSRC(*i); } // TODO(bug 1105005): Tell the filter about the mid for this track // Add unique payload types as a last-ditch fallback auto uniquePts = aTrack.GetNegotiatedDetails()->GetUniquePayloadTypes(); for (auto i = uniquePts.begin(); i != uniquePts.end(); ++i) { (*aFilterOut)->AddUniquePT(*i); } } } return NS_OK; }
nsresult MediaPipelineFactory::ConfigureVideoCodecMode(const JsepTrack& aTrack, VideoSessionConduit& aConduit) { #if !defined(MOZILLA_EXTERNAL_LINKAGE) RefPtr<LocalSourceStreamInfo> stream = mPCMedia->GetLocalStreamById(aTrack.GetStreamId()); //get video track RefPtr<mozilla::dom::VideoStreamTrack> videotrack = stream->GetVideoTrackByTrackId(aTrack.GetTrackId()); if (!videotrack) { MOZ_MTLOG(ML_ERROR, "video track not available"); return NS_ERROR_FAILURE; } //get video source type RefPtr<DOMMediaStream> mediastream = mPCMedia->GetLocalStreamById(aTrack.GetStreamId())->GetMediaStream(); DOMLocalMediaStream* domLocalStream = mediastream->AsDOMLocalMediaStream(); if (!domLocalStream) { return NS_OK; } MediaEngineSource *engine = domLocalStream->GetMediaEngine(videotrack->GetTrackID()); dom::MediaSourceEnum source = engine->GetMediaSource(); webrtc::VideoCodecMode mode = webrtc::kRealtimeVideo; switch (source) { case dom::MediaSourceEnum::Browser: case dom::MediaSourceEnum::Screen: case dom::MediaSourceEnum::Application: case dom::MediaSourceEnum::Window: mode = webrtc::kScreensharing; break; case dom::MediaSourceEnum::Camera: default: mode = webrtc::kRealtimeVideo; break; } auto error = aConduit.ConfigureCodecMode(mode); if (error) { MOZ_MTLOG(ML_ERROR, "ConfigureCodecMode failed: " << error); return NS_ERROR_FAILURE; } #endif return NS_OK; }
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::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; }
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; }
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::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; }
void SanityCheckTracks(const JsepTrack& a, const JsepTrack& b) const { if (!a.GetNegotiatedDetails()) { ASSERT_FALSE(!!b.GetNegotiatedDetails()); return; } ASSERT_TRUE(!!a.GetNegotiatedDetails()); ASSERT_TRUE(!!b.GetNegotiatedDetails()); ASSERT_EQ(a.GetMediaType(), b.GetMediaType()); ASSERT_EQ(a.GetStreamId(), b.GetStreamId()); ASSERT_EQ(a.GetTrackId(), b.GetTrackId()); ASSERT_EQ(a.GetCNAME(), b.GetCNAME()); ASSERT_NE(a.GetDirection(), b.GetDirection()); ASSERT_EQ(a.GetSsrcs().size(), b.GetSsrcs().size()); for (size_t i = 0; i < a.GetSsrcs().size(); ++i) { ASSERT_EQ(a.GetSsrcs()[i], b.GetSsrcs()[i]); } SanityCheckNegotiatedDetails(*a.GetNegotiatedDetails(), *b.GetNegotiatedDetails()); }
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; }