示例#1
0
nsresult
SdpHelper::GetMsids(const SdpMediaSection& msection,
                    std::vector<SdpMsidAttributeList::Msid>* msids)
{
  if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
    *msids = msection.GetAttributeList().GetMsid().mMsids;
  }

  // Can we find some additional msids in ssrc attributes?
  // (Chrome does not put plain-old msid attributes in its SDP)
  if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
    auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;

    for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
      if (i->attribute.find("msid:") == 0) {
        std::string streamId;
        std::string trackId;
        nsresult rv = ParseMsid(i->attribute, &streamId, &trackId);
        NS_ENSURE_SUCCESS(rv, rv);
        msids->push_back({streamId, trackId});
      }
    }
  }

  return NS_OK;
}
示例#2
0
void
SdpHelper::AddCommonExtmaps(
    const SdpMediaSection& remoteMsection,
    const std::vector<SdpExtmapAttributeList::Extmap>& localExtensions,
    SdpMediaSection* localMsection)
{
  if (!remoteMsection.GetAttributeList().HasAttribute(
        SdpAttribute::kExtmapAttribute)) {
    return;
  }

  UniquePtr<SdpExtmapAttributeList> localExtmap(new SdpExtmapAttributeList);
  auto& theirExtmap = remoteMsection.GetAttributeList().GetExtmap().mExtmaps;
  for (auto i = theirExtmap.begin(); i != theirExtmap.end(); ++i) {
    for (auto j = localExtensions.begin(); j != localExtensions.end(); ++j) {
      if (i->extensionname == j->extensionname) {
        localExtmap->mExtmaps.push_back(*i);

        // RFC 5285 says that ids >= 4096 can be used by the offerer to
        // force the answerer to pick, otherwise the value in the offer is
        // used.
        if (localExtmap->mExtmaps.back().entry >= 4096) {
          localExtmap->mExtmaps.back().entry = j->entry;
        }
      }
    }
  }

  if (!localExtmap->mExtmaps.empty()) {
    localMsection->GetAttributeList().SetAttribute(localExtmap.release());
  }
}
示例#3
0
nsresult
SdpHelper::GetIdsFromMsid(const Sdp& sdp,
                                const SdpMediaSection& msection,
                                std::string* streamId,
                                std::string* trackId)
{
  if (!sdp.GetAttributeList().HasAttribute(
        SdpAttribute::kMsidSemanticAttribute)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  auto& msidSemantics = sdp.GetAttributeList().GetMsidSemantic().mMsidSemantics;
  std::vector<SdpMsidAttributeList::Msid> allMsids;
  nsresult rv = GetMsids(msection, &allMsids);
  NS_ENSURE_SUCCESS(rv, rv);

  bool allMsidsAreWebrtc = false;
  std::set<std::string> webrtcMsids;

  for (auto i = msidSemantics.begin(); i != msidSemantics.end(); ++i) {
    if (i->semantic == "WMS") {
      for (auto j = i->msids.begin(); j != i->msids.end(); ++j) {
        if (*j == "*") {
          allMsidsAreWebrtc = true;
        } else {
          webrtcMsids.insert(*j);
        }
      }
      break;
    }
  }

  bool found = false;

  for (auto i = allMsids.begin(); i != allMsids.end(); ++i) {
    if (allMsidsAreWebrtc || webrtcMsids.count(i->identifier)) {
      if (i->appdata.empty()) {
        SDP_SET_ERROR("Invalid webrtc msid at level " << msection.GetLevel()
                       << ": Missing track id.");
        return NS_ERROR_INVALID_ARG;
      }
      if (!found) {
        *streamId = i->identifier;
        *trackId = i->appdata;
        found = true;
      } else if ((*streamId != i->identifier) || (*trackId != i->appdata)) {
        SDP_SET_ERROR("Found multiple different webrtc msids in m-section "
                       << msection.GetLevel() << ". The behavior here is "
                       "undefined.");
        return NS_ERROR_INVALID_ARG;
      }
    }
  }

  if (!found) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  return NS_OK;
}
示例#4
0
bool
SdpHelper::MsectionIsDisabled(const SdpMediaSection& msection) const
{
  return !msection.GetPort() &&
         !msection.GetAttributeList().HasAttribute(
             SdpAttribute::kBundleOnlyAttribute);
}
示例#5
0
std::string
SdpHelper::GetCNAME(const SdpMediaSection& msection) const
{
  if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
    auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;
    for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
      if (i->attribute.find("cname:") == 0) {
        return i->attribute.substr(6);
      }
    }
  }
  return "";
}
示例#6
0
void
JsepTrack::Negotiate(const SdpMediaSection& answer,
                     const SdpMediaSection& remote)
{
  PtrVector<JsepCodecDescription> negotiatedCodecs;
  negotiatedCodecs.values = GetCodecClones();

  std::map<std::string, std::string> formatChanges;
  NegotiateCodecs(remote,
                  &negotiatedCodecs.values,
                  &formatChanges);

  // Use |formatChanges| to update mPrototypeCodecs
  size_t insertPos = 0;
  for (size_t i = 0; i < mPrototypeCodecs.values.size(); ++i) {
    if (formatChanges.count(mPrototypeCodecs.values[i]->mDefaultPt)) {
      // Update the payload type to what was negotiated
      mPrototypeCodecs.values[i]->mDefaultPt =
        formatChanges[mPrototypeCodecs.values[i]->mDefaultPt];
      // Move this negotiated codec up front
      std::swap(mPrototypeCodecs.values[insertPos],
                mPrototypeCodecs.values[i]);
      ++insertPos;
    }
  }

  EnsureNoDuplicatePayloadTypes(&mPrototypeCodecs.values);

  UniquePtr<JsepTrackNegotiatedDetails> negotiatedDetails =
      MakeUnique<JsepTrackNegotiatedDetails>();

  CreateEncodings(remote, negotiatedCodecs.values, negotiatedDetails.get());

  if (answer.GetAttributeList().HasAttribute(SdpAttribute::kExtmapAttribute)) {
    for (auto& extmapAttr : answer.GetAttributeList().GetExtmap().mExtmaps) {
      negotiatedDetails->mExtmap[extmapAttr.extensionname] = extmapAttr;
    }
  }

  if (mDirection == sdp::kRecv) {
    mSsrcs.clear();
    if (remote.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
      for (auto& ssrcAttr : remote.GetAttributeList().GetSsrc().mSsrcs) {
        AddSsrc(ssrcAttr.ssrc);
      }
    }
  }

  mNegotiatedDetails = Move(negotiatedDetails);
}
示例#7
0
void
JsepTrack::GetRids(const SdpMediaSection& msection,
                   sdp::Direction direction,
                   std::vector<SdpRidAttributeList::Rid>* rids) const
{
  rids->clear();
  if (!msection.GetAttributeList().HasAttribute(
        SdpAttribute::kSimulcastAttribute)) {
    return;
  }

  const SdpSimulcastAttribute& simulcast(
      msection.GetAttributeList().GetSimulcast());

  const SdpSimulcastAttribute::Versions* versions = nullptr;
  switch (direction) {
    case sdp::kSend:
      versions = &simulcast.sendVersions;
      break;
    case sdp::kRecv:
      versions = &simulcast.recvVersions;
      break;
  }

  if (!versions->IsSet()) {
    return;
  }

  if (versions->type != SdpSimulcastAttribute::Versions::kRid) {
    // No support for PT-based simulcast, yet.
    return;
  }

  for (const SdpSimulcastAttribute::Version& version : *versions) {
    if (!version.choices.empty()) {
      // We validate that rids are present (and sane) elsewhere.
      rids->push_back(*msection.FindRid(version.choices[0]));
    }
  }
}
示例#8
0
nsresult
SdpHelper::CopyTransportParams(size_t numComponents,
                               const SdpMediaSection& oldLocal,
                               SdpMediaSection* newLocal)
{
  // Copy over m-section details
  newLocal->SetPort(oldLocal.GetPort());
  newLocal->GetConnection() = oldLocal.GetConnection();

  const SdpAttributeList& oldLocalAttrs = oldLocal.GetAttributeList();
  SdpAttributeList& newLocalAttrs = newLocal->GetAttributeList();

  // Now we copy over attributes that won't be added by the usual logic
  if (oldLocalAttrs.HasAttribute(SdpAttribute::kCandidateAttribute) &&
      numComponents) {
    UniquePtr<SdpMultiStringAttribute> candidateAttrs(
      new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
    for (const std::string& candidate : oldLocalAttrs.GetCandidate()) {
      size_t component;
      nsresult rv = GetComponent(candidate, &component);
      NS_ENSURE_SUCCESS(rv, rv);
      if (numComponents >= component) {
        candidateAttrs->mValues.push_back(candidate);
      }
    }
    if (candidateAttrs->mValues.size()) {
      newLocalAttrs.SetAttribute(candidateAttrs.release());
    }
  }

  if (numComponents == 2 &&
      oldLocalAttrs.HasAttribute(SdpAttribute::kRtcpAttribute)) {
    // copy rtcp attribute if we had one that we are using
    newLocalAttrs.SetAttribute(new SdpRtcpAttribute(oldLocalAttrs.GetRtcp()));
  }

  return NS_OK;
}
示例#9
0
void
JsepTrack::NegotiateCodecs(
    const SdpMediaSection& remote,
    std::vector<JsepCodecDescription*>* codecs,
    std::map<std::string, std::string>* formatChanges) const
{
  PtrVector<JsepCodecDescription> unnegotiatedCodecs;
  std::swap(unnegotiatedCodecs.values, *codecs);

  // Outer loop establishes the remote side's preference
  for (const std::string& fmt : remote.GetFormats()) {
    for (size_t i = 0; i < unnegotiatedCodecs.values.size(); ++i) {
      JsepCodecDescription* codec = unnegotiatedCodecs.values[i];
      if (!codec || !codec->mEnabled || !codec->Matches(fmt, remote)) {
        continue;
      }

      std::string originalFormat = codec->mDefaultPt;
      if(codec->Negotiate(fmt, remote)) {
        codecs->push_back(codec);
        unnegotiatedCodecs.values[i] = nullptr;
        if (formatChanges) {
          (*formatChanges)[originalFormat] = codec->mDefaultPt;
        }
        break;
      }
    }
  }

  // Make sure strongly preferred codecs are up front, overriding the remote
  // side's preference.
  std::stable_sort(codecs->begin(), codecs->end(), CompareCodec);

  // TODO(bug 814227): Remove this once we're ready to put multiple codecs in an
  // answer
  if (!codecs->empty()) {
    for (size_t i = 1; i < codecs->size(); ++i) {
      delete (*codecs)[i];
      (*codecs)[i] = nullptr;
    }
    codecs->resize(1);
  }
}
示例#10
0
nsresult
SdpHelper::CopyStickyParams(const SdpMediaSection& source,
                            SdpMediaSection* dest)
{
  auto& sourceAttrs = source.GetAttributeList();
  auto& destAttrs = dest->GetAttributeList();

  // There's no reason to renegotiate rtcp-mux
  if (sourceAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
    destAttrs.SetAttribute(
        new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
  }

  // mid should stay the same
  if (sourceAttrs.HasAttribute(SdpAttribute::kMidAttribute)) {
    destAttrs.SetAttribute(
        new SdpStringAttribute(SdpAttribute::kMidAttribute,
          sourceAttrs.GetMid()));
  }

  return NS_OK;
}
示例#11
0
nsresult
SdpHelper::AddCandidateToSdp(Sdp* sdp,
                             const std::string& candidateUntrimmed,
                             const std::string& mid,
                             uint16_t level)
{

  if (level >= sdp->GetMediaSectionCount()) {
    SDP_SET_ERROR("Index " << level << " out of range");
    return NS_ERROR_INVALID_ARG;
  }

  // Trim off '[a=]candidate:'
  size_t begin = candidateUntrimmed.find(':');
  if (begin == std::string::npos) {
    SDP_SET_ERROR("Invalid candidate, no ':' (" << candidateUntrimmed << ")");
    return NS_ERROR_INVALID_ARG;
  }
  ++begin;

  std::string candidate = candidateUntrimmed.substr(begin);

  // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-11#section-3.4.2.1
  // Implementations receiving an ICE Candidate object MUST use the MID if
  // present, or the m= line index, if not (as it could have come from a
  // non-JSEP endpoint). (bug 1095793)
  SdpMediaSection* msection = 0;
  if (!mid.empty()) {
    // FindMsectionByMid could return nullptr
    msection = FindMsectionByMid(*sdp, mid);

    // Check to make sure mid matches what we'd get by
    // looking up the m= line using the level. (mjf)
    std::string checkMid;
    nsresult rv = GetMidFromLevel(*sdp, level, &checkMid);
    if (NS_FAILED(rv)) {
      return rv;
    }
    if (mid != checkMid) {
      SDP_SET_ERROR("Mismatch between mid and level - \"" << mid
                     << "\" is not the mid for level " << level
                     << "; \"" << checkMid << "\" is");
      return NS_ERROR_INVALID_ARG;
    }
  }
  if (!msection) {
    msection = &(sdp->GetMediaSection(level));
  }

  SdpAttributeList& attrList = msection->GetAttributeList();

  UniquePtr<SdpMultiStringAttribute> candidates;
  if (!attrList.HasAttribute(SdpAttribute::kCandidateAttribute)) {
    // Create new
    candidates.reset(
        new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
  } else {
    // Copy existing
    candidates.reset(new SdpMultiStringAttribute(
        *static_cast<const SdpMultiStringAttribute*>(
            attrList.GetAttribute(SdpAttribute::kCandidateAttribute))));
  }
  candidates->PushEntry(candidate);
  attrList.SetAttribute(candidates.release());

  return NS_OK;
}
示例#12
0
void
JsepTrack::NegotiateCodecs(
    const SdpMediaSection& remote,
    std::vector<JsepCodecDescription*>* codecs,
    std::map<std::string, std::string>* formatChanges) const
{
  PtrVector<JsepCodecDescription> unnegotiatedCodecs;
  std::swap(unnegotiatedCodecs.values, *codecs);

  // Outer loop establishes the remote side's preference
  for (const std::string& fmt : remote.GetFormats()) {
    for (size_t i = 0; i < unnegotiatedCodecs.values.size(); ++i) {
      JsepCodecDescription* codec = unnegotiatedCodecs.values[i];
      if (!codec || !codec->mEnabled || !codec->Matches(fmt, remote)) {
        continue;
      }

      std::string originalFormat = codec->mDefaultPt;
      if(codec->Negotiate(fmt, remote)) {
        codecs->push_back(codec);
        unnegotiatedCodecs.values[i] = nullptr;
        if (formatChanges) {
          (*formatChanges)[originalFormat] = codec->mDefaultPt;
        }
        break;
      }
    }
  }

  // Find the (potential) red codec and ulpfec codec
  JsepVideoCodecDescription* red = nullptr;
  JsepVideoCodecDescription* ulpfec = nullptr;
  for (auto codec : *codecs) {
    if (codec->mName == "red") {
      red = static_cast<JsepVideoCodecDescription*>(codec);
    }
    else if (codec->mName == "ulpfec") {
      ulpfec = static_cast<JsepVideoCodecDescription*>(codec);
    }
  }
  // if we have a red codec remove redundant encodings that don't exist
  if (red) {
    // Since we could have an externally specified redundant endcodings
    // list, we shouldn't simply rebuild the redundant encodings list
    // based on the current list of codecs.
    std::vector<uint8_t> unnegotiatedEncodings;
    std::swap(unnegotiatedEncodings, red->mRedundantEncodings);
    for (auto redundantPt : unnegotiatedEncodings) {
      std::string pt = std::to_string(redundantPt);
      for (auto codec : *codecs) {
        if (pt == codec->mDefaultPt) {
          red->mRedundantEncodings.push_back(redundantPt);
          break;
        }
      }
    }
  }
  // Video FEC is indicated by the existence of the red and ulpfec
  // codecs and not an attribute on the particular video codec (like in
  // a rtcpfb attr). If we see both red and ulpfec codecs, we enable FEC
  // on all the other codecs.
  if (red && ulpfec) {
    for (auto codec : *codecs) {
      if (codec->mName != "red" && codec->mName != "ulpfec") {
        JsepVideoCodecDescription* videoCodec =
            static_cast<JsepVideoCodecDescription*>(codec);
        videoCodec->EnableFec();
      }
    }
  }

  // Make sure strongly preferred codecs are up front, overriding the remote
  // side's preference.
  std::stable_sort(codecs->begin(), codecs->end(), CompareCodec);

  // TODO(bug 814227): Remove this once we're ready to put multiple codecs in an
  // answer.  For now, remove all but the first codec unless the red codec
  // exists, and then we include the others per RFC 5109, section 14.2.
  if (!codecs->empty() && !red) {
    for (size_t i = 1; i < codecs->size(); ++i) {
      delete (*codecs)[i];
      (*codecs)[i] = nullptr;
    }
    codecs->resize(1);
  }
}