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;
}
    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);
  }

  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::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;
}