Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  SdpAttribute.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "sdp/SdpAttribute.h"
#include "sdp/SdpHelper.h"
#include <iomanip>
#include <bitset>

#ifdef CRLF
#  undef CRLF
#endif
#define CRLF "\r\n"

namespace mozilla {

static unsigned char PeekChar(std::istream& is, std::string* error) {
  int next = is.peek();
  if (next == EOF) {
    *error = "Truncated";
    return 0;
  }

  return next;
}

static std::string ParseToken(std::istream& is, const std::string& delims,
                              std::string* error) {
  std::string token;
  while (is) {
    unsigned char c = PeekChar(is, error);
    if (!c || (delims.find(c) != std::string::npos)) {
      break;
    }
    token.push_back(std::tolower(is.get()));
  }
  return token;
}

static bool SkipChar(std::istream& is, unsigned char c, std::string* error) {
  if (PeekChar(is, error) != c) {
    *error = "Expected \'";
    error->push_back(c);
    error->push_back('\'');
    return false;
  }

  is.get();
  return true;
}

void SdpConnectionAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mValue << CRLF;
}

void SdpDirectionAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mValue << CRLF;
}

void SdpDtlsMessageAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mRole << " " << mValue << CRLF;
}

bool SdpDtlsMessageAttribute::Parse(std::istream& is, std::string* error) {
  std::string roleToken = ParseToken(is, " ", error);
  if (roleToken == "server") {
    mRole = kServer;
  } else if (roleToken == "client") {
    mRole = kClient;
  } else {
    *error = "Invalid dtls-message role; must be either client or server";
    return false;
  }

  is >> std::ws;

  std::string s(std::istreambuf_iterator<char>(is), {});
  mValue = s;

  return true;
}

void SdpExtmapAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mExtmaps.begin(); i != mExtmaps.end(); ++i) {
    os << "a=" << mType << ":" << i->entry;
    if (i->direction_specified) {
      os << "/" << i->direction;
    }
    os << " " << i->extensionname;
    if (i->extensionattributes.length()) {
      os << " " << i->extensionattributes;
    }
    os << CRLF;
  }
}

void SdpFingerprintAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mFingerprints.begin(); i != mFingerprints.end(); ++i) {
    os << "a=" << mType << ":" << i->hashFunc << " "
       << FormatFingerprint(i->fingerprint) << CRLF;
  }
}

// Format the fingerprint in RFC 4572 Section 5 attribute format
std::string SdpFingerprintAttributeList::FormatFingerprint(
    const std::vector<uint8_t>& fp) {
  if (fp.empty()) {
    MOZ_ASSERT(false"Cannot format an empty fingerprint.");
    return "";
  }

  std::ostringstream os;
  for (auto i = fp.begin(); i != fp.end(); ++i) {
    os << ":" << std::hex << std::uppercase << std::setw(2) << std::setfill('0')
       << static_cast<uint32_t>(*i);
  }
  return os.str().substr(1);
}

static uint8_t FromUppercaseHex(char ch) {
  if ((ch >= '0') && (ch <= '9')) {
    return ch - '0';
  }
  if ((ch >= 'A') && (ch <= 'F')) {
    return ch - 'A' + 10;
  }
  return 16;  // invalid
}

// Parse the fingerprint from RFC 4572 Section 5 attribute format
std::vector<uint8_t> SdpFingerprintAttributeList::ParseFingerprint(
    const std::string& str) {
  size_t targetSize = (str.length() + 1) / 3;
  std::vector<uint8_t> fp(targetSize);
  size_t fpIndex = 0;

  if (str.length() % 3 != 2) {
    fp.clear();
    return fp;
  }

  for (size_t i = 0; i < str.length(); i += 3) {
    uint8_t high = FromUppercaseHex(str[i]);
    uint8_t low = FromUppercaseHex(str[i + 1]);
    if (high > 0xf || low > 0xf ||
        (i + 2 < str.length() && str[i + 2] != ':')) {
      fp.clear();  // error
      return fp;
    }
    fp[fpIndex++] = high << 4 | low;
  }
  return fp;
}

bool SdpFmtpAttributeList::operator==(const SdpFmtpAttributeList& other) const {
  return mFmtps == other.mFmtps;
}

void SdpFmtpAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mFmtps.begin(); i != mFmtps.end(); ++i) {
    if (i->parameters && i->parameters->ShouldSerialize()) {
      os << "a=" << mType << ":" << i->format << " ";
      i->parameters->Serialize(os);
      os << CRLF;
    }
  }
}

void SdpGroupAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mGroups.begin(); i != mGroups.end(); ++i) {
    os << "a=" << mType << ":" << i->semantics;
    for (auto j = i->tags.begin(); j != i->tags.end(); ++j) {
      os << " " << (*j);
    }
    os << CRLF;
  }
}

// We're just using an SdpStringAttribute for this right now
#if 0
void SdpIdentityAttribute::Serialize(std::ostream& os) const
{
  os << "a=" << mType << ":" << mAssertion;
  for (auto i = mExtensions.begin(); i != mExtensions.end(); i++) {
    os << (i == mExtensions.begin() ? " " : ";") << (*i);
  }
  os << CRLF;
}
#endif

// Class to help with omitting a leading delimiter for the first item in a list
class SkipFirstDelimiter {
 public:
  explicit SkipFirstDelimiter(const std::string& delim)
      : mDelim(delim), mFirst(true) {}

  std::ostream& print(std::ostream& os) {
    if (!mFirst) {
      os << mDelim;
    }
    mFirst = false;
    return os;
  }

 private:
  std::string mDelim;
  bool mFirst;
};

static std::ostream& operator<<(std::ostream& os, SkipFirstDelimiter& delim) {
  return delim.print(os);
}

void SdpImageattrAttributeList::XYRange::Serialize(std::ostream& os) const {
  if (discreteValues.empty()) {
    os << "[" << min << ":";
    if (step != 1) {
      os << step << ":";
    }
    os << max << "]";
  } else if (discreteValues.size() == 1) {
    os << discreteValues.front();
  } else {
    os << "[";
    SkipFirstDelimiter comma(",");
    for (auto value : discreteValues) {
      os << comma << value;
    }
    os << "]";
  }
}

template <typename T>
bool GetUnsigned(std::istream& is, T min, T max, T* value, std::string* error) {
  if (PeekChar(is, error) == '-') {
    *error = "Value is less than 0";
    return false;
  }

  is >> std::noskipws >> *value;

  if (is.fail()) {
    *error = "Malformed";
    return false;
  }

  if (*value < min) {
    *error = "Value too small";
    return false;
  }

  if (*value > max) {
    *error = "Value too large";
    return false;
  }

  return true;
}

static bool GetXYValue(std::istream& is, uint32_t* value, std::string* error) {
  return GetUnsigned<uint32_t>(is, 1, 999999, value, error);
}

bool SdpImageattrAttributeList::XYRange::ParseDiscreteValues(
    std::istream& is, std::string* error) {
  do {
    uint32_t value;
    if (!GetXYValue(is, &value, error)) {
      return false;
    }
    discreteValues.push_back(value);
  } while (SkipChar(is, ',', error));

  return SkipChar(is, ']', error);
}

bool SdpImageattrAttributeList::XYRange::ParseAfterMin(std::istream& is,
                                                       std::string* error) {
  // We have already parsed "[320:", and now expect another uint
  uint32_t value;
  if (!GetXYValue(is, &value, error)) {
    return false;
  }

  if (SkipChar(is, ':', error)) {
    // Range with step eg [320:16:640]
    step = value;
    // Now |value| should be the max
    if (!GetXYValue(is, &value, error)) {
      return false;
    }
  }

  max = value;
  if (min >= max) {
    *error = "Min is not smaller than max";
    return false;
  }

  return SkipChar(is, ']', error);
}

bool SdpImageattrAttributeList::XYRange::ParseAfterBracket(std::istream& is,
                                                           std::string* error) {
  // Either a range, or a list of discrete values
  // [320:640], [320:16:640], or [320,640]
  uint32_t value;
  if (!GetXYValue(is, &value, error)) {
    return false;
  }

  if (SkipChar(is, ':', error)) {
    // Range - [640:480] or [640:16:480]
    min = value;
    return ParseAfterMin(is, error);
  }

  if (SkipChar(is, ',', error)) {
    discreteValues.push_back(value);
    return ParseDiscreteValues(is, error);
  }

  *error = "Expected \':\' or \',\'";
  return false;
}

bool SdpImageattrAttributeList::XYRange::Parse(std::istream& is,
                                               std::string* error) {
  if (SkipChar(is, '[', error)) {
    return ParseAfterBracket(is, error);
  }

  // Single discrete value
  uint32_t value;
  if (!GetXYValue(is, &value, error)) {
    return false;
  }
  discreteValues.push_back(value);

  return true;
}

static bool GetSPValue(std::istream& is, float* value, std::string* error) {
  return GetUnsigned<float>(is, 0.1f, 9.9999f, value, error);
}

static bool GetQValue(std::istream& is, float* value, std::string* error) {
  return GetUnsigned<float>(is, 0.0f, 1.0f, value, error);
}

bool SdpImageattrAttributeList::SRange::ParseDiscreteValues(
    std::istream& is, std::string* error) {
  do {
    float value;
    if (!GetSPValue(is, &value, error)) {
      return false;
    }
    discreteValues.push_back(value);
  } while (SkipChar(is, ',', error));

  return SkipChar(is, ']', error);
}

bool SdpImageattrAttributeList::SRange::ParseAfterMin(std::istream& is,
                                                      std::string* error) {
  if (!GetSPValue(is, &max, error)) {
    return false;
  }

  if (min >= max) {
    *error = "Min is not smaller than max";
    return false;
  }

  return SkipChar(is, ']', error);
}

bool SdpImageattrAttributeList::SRange::ParseAfterBracket(std::istream& is,
                                                          std::string* error) {
  // Either a range, or a list of discrete values
  float value;
  if (!GetSPValue(is, &value, error)) {
    return false;
  }

  if (SkipChar(is, '-', error)) {
    min = value;
    return ParseAfterMin(is, error);
  }

  if (SkipChar(is, ',', error)) {
    discreteValues.push_back(value);
    return ParseDiscreteValues(is, error);
  }

  *error = "Expected either \'-\' or \',\'";
  return false;
}

bool SdpImageattrAttributeList::SRange::Parse(std::istream& is,
                                              std::string* error) {
  if (SkipChar(is, '[', error)) {
    return ParseAfterBracket(is, error);
  }

  // Single discrete value
  float value;
  if (!GetSPValue(is, &value, error)) {
    return false;
  }
  discreteValues.push_back(value);
  return true;
}

bool SdpImageattrAttributeList::PRange::Parse(std::istream& is,
                                              std::string* error) {
  if (!SkipChar(is, '[', error)) {
    return false;
  }

  if (!GetSPValue(is, &min, error)) {
    return false;
  }

  if (!SkipChar(is, '-', error)) {
    return false;
  }

  if (!GetSPValue(is, &max, error)) {
    return false;
  }

  if (min >= max) {
    *error = "min must be smaller than max";
    return false;
  }

  if (!SkipChar(is, ']', error)) {
    return false;
  }
  return true;
}

void SdpImageattrAttributeList::SRange::Serialize(std::ostream& os) const {
  os << std::setprecision(4) << std::fixed;
  if (discreteValues.empty()) {
    os << "[" << min << "-" << max << "]";
  } else if (discreteValues.size() == 1) {
    os << discreteValues.front();
  } else {
    os << "[";
    SkipFirstDelimiter comma(",");
    for (auto value : discreteValues) {
      os << comma << value;
    }
    os << "]";
  }
}

void SdpImageattrAttributeList::PRange::Serialize(std::ostream& os) const {
  os << std::setprecision(4) << std::fixed;
  os << "[" << min << "-" << max << "]";
}

static std::string ParseKey(std::istream& is, std::string* error) {
  std::string token = ParseToken(is, "=", error);
  if (!SkipChar(is, '=', error)) {
    return "";
  }
  return token;
}

static bool SkipBraces(std::istream& is, std::string* error) {
  if (PeekChar(is, error) != '[') {
    *error = "Expected \'[\'";
    return false;
  }

  size_t braceCount = 0;
  do {
    switch (PeekChar(is, error)) {
      case '[':
        ++braceCount;
        break;
      case ']':
        --braceCount;
        break;
      default:
        break;
    }
    is.get();
  } while (braceCount && is);

  if (!is) {
    *error = "Expected closing brace";
    return false;
  }

  return true;
}

// Assumptions:
// 1. If the value contains '[' or ']', they are balanced.
// 2. The value contains no ',' outside of brackets.
static bool SkipValue(std::istream& is, std::string* error) {
  while (is) {
    switch (PeekChar(is, error)) {
      case ',':
      case ']':
        return true;
      case '[':
        if (!SkipBraces(is, error)) {
          return false;
        }
        break;
      default:
        is.get();
    }
  }

  *error = "No closing \']\' on set";
  return false;
}

bool SdpImageattrAttributeList::Set::Parse(std::istream& is,
                                           std::string* error) {
  if (!SkipChar(is, '[', error)) {
    return false;
  }

  if (ParseKey(is, error) != "x") {
    *error = "Expected x=";
    return false;
  }

  if (!xRange.Parse(is, error)) {
    return false;
  }

  if (!SkipChar(is, ',', error)) {
    return false;
  }

  if (ParseKey(is, error) != "y") {
    *error = "Expected y=";
    return false;
  }

  if (!yRange.Parse(is, error)) {
    return false;
  }

  qValue = 0.5f;  // default

  bool gotSar = false;
  bool gotPar = false;
  bool gotQ = false;

  while (SkipChar(is, ',', error)) {
    std::string key = ParseKey(is, error);
    if (key.empty()) {
      *error = "Expected key-value";
      return false;
    }

    if (key == "sar") {
      if (gotSar) {
        *error = "Extra sar parameter";
        return false;
      }
      gotSar = true;
      if (!sRange.Parse(is, error)) {
        return false;
      }
    } else if (key == "par") {
      if (gotPar) {
        *error = "Extra par parameter";
        return false;
      }
      gotPar = true;
      if (!pRange.Parse(is, error)) {
        return false;
      }
    } else if (key == "q") {
      if (gotQ) {
        *error = "Extra q parameter";
        return false;
      }
      gotQ = true;
      if (!GetQValue(is, &qValue, error)) {
        return false;
      }
    } else {
      if (!SkipValue(is, error)) {
        return false;
      }
    }
  }

  return SkipChar(is, ']', error);
}

void SdpImageattrAttributeList::Set::Serialize(std::ostream& os) const {
  os << "[x=";
  xRange.Serialize(os);
  os << ",y=";
  yRange.Serialize(os);
  if (sRange.IsSet()) {
    os << ",sar=";
    sRange.Serialize(os);
  }
  if (pRange.IsSet()) {
    os << ",par=";
    pRange.Serialize(os);
  }
  if (qValue >= 0) {
    os << std::setprecision(2) << std::fixed << ",q=" << qValue;
  }
  os << "]";
}

bool SdpImageattrAttributeList::Imageattr::ParseSets(std::istream& is,
                                                     std::string* error) {
  std::string type = ParseToken(is, " \t", error);

  bool* isAll = nullptr;
  std::vector<Set>* sets = nullptr;

  if (type == "send") {
    isAll = &sendAll;
    sets = &sendSets;
  } else if (type == "recv") {
    isAll = &recvAll;
    sets = &recvSets;
  } else {
    *error = "Unknown type, must be either send or recv";
    return false;
  }

  if (*isAll || !sets->empty()) {
    *error = "Multiple send or recv set lists";
    return false;
  }

  is >> std::ws;
  if (SkipChar(is, '*', error)) {
    *isAll = true;
    return true;
  }

  do {
    Set set;
    if (!set.Parse(is, error)) {
      return false;
    }

    sets->push_back(set);
    is >> std::ws;
  } while (PeekChar(is, error) == '[');

  return true;
}

bool SdpImageattrAttributeList::Imageattr::Parse(std::istream& is,
                                                 std::string* error) {
  if (!SkipChar(is, '*', error)) {
    uint16_t value;
    if (!GetUnsigned<uint16_t>(is, 0, UINT16_MAX, &value, error)) {
      return false;
    }
    pt = Some(value);
  }

  is >> std::ws;
  if (!ParseSets(is, error)) {
    return false;
  }

  // There might be a second one
  is >> std::ws;
  if (is.eof()) {
    return true;
  }

  if (!ParseSets(is, error)) {
    return false;
  }

  is >> std::ws;
  if (!is.eof()) {
    *error = "Trailing characters";
    return false;
  }

  return true;
}

void SdpImageattrAttributeList::Imageattr::Serialize(std::ostream& os) const {
  if (pt.isSome()) {
    os << *pt;
  } else {
    os << "*";
  }

  if (sendAll) {
    os << " send *";
  } else if (!sendSets.empty()) {
    os << " send";
    for (auto& set : sendSets) {
      os << " ";
      set.Serialize(os);
    }
  }

  if (recvAll) {
    os << " recv *";
  } else if (!recvSets.empty()) {
    os << " recv";
    for (auto& set : recvSets) {
      os << " ";
      set.Serialize(os);
    }
  }
}

void SdpImageattrAttributeList::Serialize(std::ostream& os) const {
  for (auto& imageattr : mImageattrs) {
    os << "a=" << mType << ":";
    imageattr.Serialize(os);
    os << CRLF;
  }
}

bool SdpImageattrAttributeList::PushEntry(const std::string& raw,
                                          std::string* error,
                                          size_t* errorPos) {
  std::istringstream is(raw);

  Imageattr imageattr;
  if (!imageattr.Parse(is, error)) {
    is.clear();
    *errorPos = is.tellg();
    return false;
  }

  mImageattrs.push_back(imageattr);
  return true;
}

void SdpMsidAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mMsids.begin(); i != mMsids.end(); ++i) {
    os << "a=" << mType << ":" << i->identifier;
    if (i->appdata.length()) {
      os << " " << i->appdata;
    }
    os << CRLF;
  }
}

void SdpMsidSemanticAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mMsidSemantics.begin(); i != mMsidSemantics.end(); ++i) {
    os << "a=" << mType << ":" << i->semantic;
    for (auto j = i->msids.begin(); j != i->msids.end(); ++j) {
      os << " " << *j;
    }
    os << CRLF;
  }
}

void SdpRemoteCandidatesAttribute::Serialize(std::ostream& os) const {
  if (mCandidates.empty()) {
    return;
  }

  os << "a=" << mType;
  for (auto i = mCandidates.begin(); i != mCandidates.end(); i++) {
    os << (i == mCandidates.begin() ? ":" : " ") << i->id << " " << i->address
       << " " << i->port;
  }
  os << CRLF;
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::Rid::ParseParameters(std::istream& is,
                                               std::string* error) {
  if (!PeekChar(is, error)) {
    // No parameters
    return true;
  }

  do {
    is >> std::ws;
    std::string key = ParseKey(is, error);
    if (key.empty()) {
      return false;  // Illegal trailing cruft
    }

    // This allows pt= to appear anywhere, instead of only at the beginning, but
    // this ends up being significantly less code.
    if (key == "pt") {
      if (!ParseFormats(is, error)) {
        return false;
      }
    } else if (key == "max-width") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxWidth,
                                 error)) {
        return false;
      }
    } else if (key == "max-height") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxHeight,
                                 error)) {
        return false;
      }
    } else if (key == "max-fps") {
      uint32_t maxFps;
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxFps, error)) {
        return false;
      }
      constraints.maxFps = Some(maxFps);
    } else if (key == "max-fs") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxFs,
                                 error)) {
        return false;
      }
    } else if (key == "max-br") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxBr,
                                 error)) {
        return false;
      }
    } else if (key == "max-pps") {
      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxPps,
                                 error)) {
        return false;
      }
    } else if (key == "depend") {
      if (!ParseDepend(is, error)) {
        return false;
      }
    } else {
      (void)ParseToken(is, ";", error);
    }
  } while (SkipChar(is, ';', error));
  return true;
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::Rid::ParseDepend(std::istream& is,
                                           std::string* error) {
  do {
    std::string id = ParseToken(is, ",;", error);
    if (id.empty()) {
      return false;
    }
    dependIds.push_back(id);
  } while (SkipChar(is, ',', error));

  return true;
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::Rid::ParseFormats(std::istream& is,
                                            std::string* error) {
  do {
    uint16_t fmt;
    if (!GetUnsigned<uint16_t>(is, 0, 127, &fmt, error)) {
      return false;
    }
    formats.push_back(fmt);
  } while (SkipChar(is, ',', error));

  return true;
}

void SdpRidAttributeList::Rid::SerializeParameters(std::ostream& os) const {
  if (!HasParameters()) {
    return;
  }

  os << " ";

  SkipFirstDelimiter semic(";");

  if (!formats.empty()) {
    os << semic << "pt=";
    SkipFirstDelimiter comma(",");
    for (uint16_t fmt : formats) {
      os << comma << fmt;
    }
  }

  if (constraints.maxWidth) {
    os << semic << "max-width=" << constraints.maxWidth;
  }

  if (constraints.maxHeight) {
    os << semic << "max-height=" << constraints.maxHeight;
  }

  if (constraints.maxFps) {
    os << semic << "max-fps=" << constraints.maxFps;
  }

  if (constraints.maxFs) {
    os << semic << "max-fs=" << constraints.maxFs;
  }

  if (constraints.maxBr) {
    os << semic << "max-br=" << constraints.maxBr;
  }

  if (constraints.maxPps) {
    os << semic << "max-pps=" << constraints.maxPps;
  }

  if (!dependIds.empty()) {
    os << semic << "depend=";
    SkipFirstDelimiter comma(",");
    for (const std::string& id : dependIds) {
      os << comma << id;
    }
  }
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::Rid::Parse(std::istream& is, std::string* error) {
  id = ParseToken(is, " ", error);
  if (!CheckRidValidity(id, error)) {
    return false;
  }

  is >> std::ws;
  std::string directionToken = ParseToken(is, " ", error);
  if (directionToken == "send") {
    direction = sdp::kSend;
  } else if (directionToken == "recv") {
    direction = sdp::kRecv;
  } else {
    *error = "Invalid direction, must be either send or recv";
    return false;
  }

  return ParseParameters(is, error);
}

static std::bitset<256> GetAllowedRidCharacters() {
  // From RFC 8851:
  // rid-id            = 1*(alpha-numeric / "-" / "_")
  std::bitset<256> result;
  for (unsigned char c = 'a'; c <= 'z'; ++c) {
    result.set(c);
  }
  for (unsigned char c = 'A'; c <= 'Z'; ++c) {
    result.set(c);
  }
  for (unsigned char c = '0'; c <= '9'; ++c) {
    result.set(c);
  }
  // NOTE: RFC 8851 says these are allowed, but RFC 8852 says they are not
  // https://www.rfc-editor.org/errata/eid7132
  // result.set('-');
  // result.set('_');
  return result;
}

/* static */
bool SdpRidAttributeList::CheckRidValidity(const std::string& aRid,
                                           std::string* aError) {
  if (aRid.empty()) {
    *aError = "Rid must be non-empty (according to RFC 8851)";
    return false;
  }

  // We need to check against a maximum length, but that is nowhere
  // specified in webrtc-pc right now.
  if (aRid.size() > 255) {
    *aError = "Rid can be at most 255 characters long (according to RFC 8852)";
    return false;
  }

  // TODO: Right now, if the rid is longer than kMaxRidLength, we don't treat it
  // as a parse error, since the grammar does not have this restriction.
  // Instead, our JSEP code ignores rids that exceed this limit. However, there
  // is a possibility that the IETF grammar (in RFC 8852) will change the limit
  // from 255 to 16, in which case we will need to revise this code.

  static const std::bitset<256> allowed = GetAllowedRidCharacters();
  for (unsigned char c : aRid) {
    if (!allowed[c]) {
      *aError =
          "Rid can contain only alphanumeric characters (according to RFC "
          "8852)";
      return false;
    }
  }

  return true;
}

// This can be overridden if necessary
size_t SdpRidAttributeList::kMaxRidLength = 255;

void SdpRidAttributeList::Rid::Serialize(std::ostream& os) const {
  os << id << " " << direction;
  SerializeParameters(os);
}

bool SdpRidAttributeList::Rid::HasFormat(const std::string& format) const {
  if (formats.empty()) {
    return true;
  }

  uint16_t formatAsInt;
  if (!SdpHelper::GetPtAsInt(format, &formatAsInt)) {
    return false;
  }

  return (std::find(formats.begin(), formats.end(), formatAsInt) !=
          formats.end());
}

void SdpRidAttributeList::Serialize(std::ostream& os) const {
  for (const Rid& rid : mRids) {
    os << "a=" << mType << ":";
    rid.Serialize(os);
    os << CRLF;
  }
}

// Remove this function. See Bug 1469702
bool SdpRidAttributeList::PushEntry(const std::string& raw, std::string* error,
                                    size_t* errorPos) {
  std::istringstream is(raw);

  Rid rid;
  if (!rid.Parse(is, error)) {
    is.clear();
    *errorPos = is.tellg();
    return false;
  }

  mRids.push_back(rid);
  return true;
}

void SdpRidAttributeList::PushEntry(const std::string& id, sdp::Direction dir,
                                    const std::vector<uint16_t>& formats,
                                    const EncodingConstraints& constraints,
                                    const std::vector<std::string>& dependIds) {
  SdpRidAttributeList::Rid rid;

  rid.id = id;
  rid.direction = dir;
  rid.formats = formats;
  rid.constraints = constraints;
  rid.dependIds = dependIds;

  mRids.push_back(std::move(rid));
}

void SdpRtcpAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mPort;
  if (!mAddress.empty()) {
    os << " " << mNetType << " " << mAddrType << " " << mAddress;
  }
  os << CRLF;
}

const char* SdpRtcpFbAttributeList::pli = "pli";
const char* SdpRtcpFbAttributeList::sli = "sli";
const char* SdpRtcpFbAttributeList::rpsi = "rpsi";
const char* SdpRtcpFbAttributeList::app = "app";

const char* SdpRtcpFbAttributeList::fir = "fir";
const char* SdpRtcpFbAttributeList::tmmbr = "tmmbr";
const char* SdpRtcpFbAttributeList::tstr = "tstr";
const char* SdpRtcpFbAttributeList::vbcm = "vbcm";

void SdpRtcpFbAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mFeedbacks.begin(); i != mFeedbacks.end(); ++i) {
    os << "a=" << mType << ":" << i->pt << " " << i->type;
    if (i->parameter.length()) {
      os << " " << i->parameter;
      if (i->extra.length()) {
        os << " " << i->extra;
      }
    }
    os << CRLF;
  }
}

static bool ShouldSerializeChannels(SdpRtpmapAttributeList::CodecType type) {
  switch (type) {
    case SdpRtpmapAttributeList::kOpus:
    case SdpRtpmapAttributeList::kG722:
      return true;
    case SdpRtpmapAttributeList::kPCMU:
    case SdpRtpmapAttributeList::kPCMA:
    case SdpRtpmapAttributeList::kVP8:
    case SdpRtpmapAttributeList::kVP9:
    case SdpRtpmapAttributeList::kiLBC:
    case SdpRtpmapAttributeList::kiSAC:
    case SdpRtpmapAttributeList::kH264:
    case SdpRtpmapAttributeList::kAV1:
    case SdpRtpmapAttributeList::kRed:
    case SdpRtpmapAttributeList::kUlpfec:
    case SdpRtpmapAttributeList::kTelephoneEvent:
    case SdpRtpmapAttributeList::kRtx:
      return false;
    case SdpRtpmapAttributeList::kOtherCodec:
      return true;
  }
  MOZ_CRASH();
}

void SdpRtpmapAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mRtpmaps.begin(); i != mRtpmaps.end(); ++i) {
    os << "a=" << mType << ":" << i->pt << " " << i->name << "/" << i->clock;
    if (i->channels && ShouldSerializeChannels(i->codec)) {
      os << "/" << i->channels;
    }
    os << CRLF;
  }
}

void SdpSctpmapAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mSctpmaps.begin(); i != mSctpmaps.end(); ++i) {
    os << "a=" << mType << ":" << i->pt << " " << i->name << " " << i->streams
       << CRLF;
  }
}

void SdpSetupAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mRole << CRLF;
}

void SdpSimulcastAttribute::Version::Serialize(std::ostream& os) const {
  SkipFirstDelimiter comma(",");
  for (const Encoding& choice : choices) {
    os << comma;
    if (choice.paused) {
      os << '~';
    }
    os << choice.rid;
  }
}

bool SdpSimulcastAttribute::Version::Parse(std::istream& is,
                                           std::string* error) {
  do {
    bool paused = SkipChar(is, '~', error);
    std::string value = ParseToken(is, ",; ", error);
    if (value.empty()) {
      *error = "Missing rid";
      return false;
    }
    if (!SdpRidAttributeList::CheckRidValidity(value, error)) {
      return false;
    }
    choices.push_back(Encoding(value, paused));
  } while (SkipChar(is, ',', error));

  return true;
}

void SdpSimulcastAttribute::Versions::Serialize(std::ostream& os) const {
  SkipFirstDelimiter semic(";");
  for (const Version& version : *this) {
    if (!version.IsSet()) {
      continue;
    }
    os << semic;
    version.Serialize(os);
  }
}

bool SdpSimulcastAttribute::Versions::Parse(std::istream& is,
                                            std::string* error) {
  do {
    Version version;
    if (!version.Parse(is, error)) {
      return false;
    }
    push_back(version);
  } while (SkipChar(is, ';', error));

  return true;
}

void SdpSimulcastAttribute::Serialize(std::ostream& os) const {
  MOZ_ASSERT(sendVersions.IsSet() || recvVersions.IsSet());

  os << "a=" << mType << ":";

  if (sendVersions.IsSet()) {
    os << "send ";
    sendVersions.Serialize(os);
  }

  if (recvVersions.IsSet()) {
    if (sendVersions.IsSet()) {
      os << " ";
    }
    os << "recv ";
    recvVersions.Serialize(os);
  }

  os << CRLF;
}

bool SdpSimulcastAttribute::Parse(std::istream& is, std::string* error) {
  bool gotRecv = false;
  bool gotSend = false;

  while (true) {
    is >> std::ws;
    std::string token = ParseToken(is, " \t", error);
    if (token.empty()) {
      break;
    }

    if (token == "send") {
      if (gotSend) {
        *error = "Already got a send list";
        return false;
      }
      gotSend = true;

      is >> std::ws;
      if (!sendVersions.Parse(is, error)) {
        return false;
      }
    } else if (token == "recv") {
      if (gotRecv) {
        *error = "Already got a recv list";
        return false;
      }
      gotRecv = true;

      is >> std::ws;
      if (!recvVersions.Parse(is, error)) {
        return false;
      }
    } else {
      *error = "Type must be either 'send' or 'recv'";
      return false;
    }
  }

  if (!gotSend && !gotRecv) {
    *error = "Empty simulcast attribute";
    return false;
  }

  return true;
}

void SdpSsrcAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mSsrcs.begin(); i != mSsrcs.end(); ++i) {
    os << "a=" << mType << ":" << i->ssrc << " " << i->attribute << CRLF;
  }
}

void SdpSsrcGroupAttributeList::Serialize(std::ostream& os) const {
  for (auto i = mSsrcGroups.begin(); i != mSsrcGroups.end(); ++i) {
    os << "a=" << mType << ":" << i->semantics;
    for (auto j = i->ssrcs.begin(); j != i->ssrcs.end(); ++j) {
      os << " " << (*j);
    }
    os << CRLF;
  }
}

void SdpMultiStringAttribute::Serialize(std::ostream& os) const {
  for (auto i = mValues.begin(); i != mValues.end(); ++i) {
    os << "a=" << mType << ":" << *i << CRLF;
  }
}

void SdpOptionsAttribute::Serialize(std::ostream& os) const {
  if (mValues.empty()) {
    return;
  }

  os << "a=" << mType << ":";

  for (auto i = mValues.begin(); i != mValues.end(); ++i) {
    if (i != mValues.begin()) {
      os << " ";
    }
    os << *i;
  }
  os << CRLF;
}

void SdpOptionsAttribute::Load(const std::string& value) {
  size_t start = 0;
  size_t end = value.find(' ');
  while (end != std::string::npos) {
    PushEntry(value.substr(start, end));
    start = end + 1;
    end = value.find(' ', start);
  }
  PushEntry(value.substr(start));
}

void SdpFlagAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << CRLF;
}

void SdpStringAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mValue << CRLF;
}

void SdpNumberAttribute::Serialize(std::ostream& os) const {
  os << "a=" << mType << ":" << mValue << CRLF;
}

bool SdpAttribute::IsAllowedAtMediaLevel(AttributeType type) {
  switch (type) {
    case kBundleOnlyAttribute:
      return true;
    case kCandidateAttribute:
      return true;
    case kConnectionAttribute:
      return true;
    case kDirectionAttribute:
      return true;
    case kDtlsMessageAttribute:
      return false;
    case kEndOfCandidatesAttribute:
      return true;
    case kExtmapAttribute:
      return true;
    case kFingerprintAttribute:
      return true;
    case kFmtpAttribute:
      return true;
    case kGroupAttribute:
      return false;
    case kIceLiteAttribute:
      return false;
    case kIceMismatchAttribute:
      return true;
    // RFC 5245 says this is session-level only, but
    // draft-ietf-mmusic-ice-sip-sdp-03 updates this to allow at the media
    // level.
    case kIceOptionsAttribute:
      return true;
    case kIcePwdAttribute:
      return true;
    case kIceUfragAttribute:
      return true;
    case kIdentityAttribute:
      return false;
    case kImageattrAttribute:
      return true;
    case kLabelAttribute:
      return true;
    case kMaxptimeAttribute:
      return true;
    case kMidAttribute:
      return true;
    case kMsidAttribute:
      return true;
    case kMsidSemanticAttribute:
      return false;
    case kPtimeAttribute:
      return true;
    case kRemoteCandidatesAttribute:
      return true;
    case kRidAttribute:
      return true;
    case kRtcpAttribute:
      return true;
    case kRtcpFbAttribute:
      return true;
    case kRtcpMuxAttribute:
      return true;
    case kRtcpRsizeAttribute:
      return true;
    case kRtpmapAttribute:
      return true;
    case kSctpmapAttribute:
      return true;
    case kSetupAttribute:
      return true;
    case kSimulcastAttribute:
      return true;
    case kSsrcAttribute:
      return true;
    case kSsrcGroupAttribute:
      return true;
    case kSctpPortAttribute:
      return true;
    case kMaxMessageSizeAttribute:
      return true;
  }
  MOZ_CRASH("Unknown attribute type");
}

bool SdpAttribute::IsAllowedAtSessionLevel(AttributeType type) {
  switch (type) {
    case kBundleOnlyAttribute:
      return false;
    case kCandidateAttribute:
      return false;
    case kConnectionAttribute:
      return true;
    case kDirectionAttribute:
      return true;
    case kDtlsMessageAttribute:
      return true;
    case kEndOfCandidatesAttribute:
      return true;
    case kExtmapAttribute:
      return true;
    case kFingerprintAttribute:
      return true;
    case kFmtpAttribute:
      return false;
    case kGroupAttribute:
      return true;
    case kIceLiteAttribute:
      return true;
    case kIceMismatchAttribute:
      return false;
    case kIceOptionsAttribute:
      return true;
    case kIcePwdAttribute:
      return true;
    case kIceUfragAttribute:
      return true;
    case kIdentityAttribute:
      return true;
    case kImageattrAttribute:
      return false;
    case kLabelAttribute:
      return false;
    case kMaxptimeAttribute:
      return false;
    case kMidAttribute:
      return false;
    case kMsidSemanticAttribute:
      return true;
    case kMsidAttribute:
      return false;
    case kPtimeAttribute:
      return false;
    case kRemoteCandidatesAttribute:
      return false;
    case kRidAttribute:
      return false;
    case kRtcpAttribute:
      return false;
    case kRtcpFbAttribute:
      return false;
    case kRtcpMuxAttribute:
      return false;
    case kRtcpRsizeAttribute:
      return false;
    case kRtpmapAttribute:
      return false;
    case kSctpmapAttribute:
      return false;
    case kSetupAttribute:
      return true;
    case kSimulcastAttribute:
      return false;
    case kSsrcAttribute:
      return false;
    case kSsrcGroupAttribute:
      return false;
    case kSctpPortAttribute:
      return false;
    case kMaxMessageSizeAttribute:
      return false;
  }
  MOZ_CRASH("Unknown attribute type");
}

const std::string SdpAttribute::GetAttributeTypeString(AttributeType type) {
  switch (type) {
    case kBundleOnlyAttribute:
      return "bundle-only";
    case kCandidateAttribute:
      return "candidate";
    case kConnectionAttribute:
      return "connection";
    case kDtlsMessageAttribute:
      return "dtls-message";
    case kEndOfCandidatesAttribute:
      return "end-of-candidates";
    case kExtmapAttribute:
      return "extmap";
    case kFingerprintAttribute:
      return "fingerprint";
    case kFmtpAttribute:
      return "fmtp";
    case kGroupAttribute:
      return "group";
    case kIceLiteAttribute:
      return "ice-lite";
    case kIceMismatchAttribute:
      return "ice-mismatch";
    case kIceOptionsAttribute:
      return "ice-options";
    case kIcePwdAttribute:
      return "ice-pwd";
    case kIceUfragAttribute:
      return "ice-ufrag";
    case kIdentityAttribute:
      return "identity";
    case kImageattrAttribute:
      return "imageattr";
    case kLabelAttribute:
      return "label";
    case kMaxptimeAttribute:
      return "maxptime";
    case kMidAttribute:
      return "mid";
    case kMsidAttribute:
      return "msid";
    case kMsidSemanticAttribute:
      return "msid-semantic";
    case kPtimeAttribute:
      return "ptime";
    case kRemoteCandidatesAttribute:
      return "remote-candidates";
    case kRidAttribute:
      return "rid";
    case kRtcpAttribute:
      return "rtcp";
    case kRtcpFbAttribute:
      return "rtcp-fb";
    case kRtcpMuxAttribute:
      return "rtcp-mux";
    case kRtcpRsizeAttribute:
      return "rtcp-rsize";
    case kRtpmapAttribute:
      return "rtpmap";
    case kSctpmapAttribute:
      return "sctpmap";
    case kSetupAttribute:
      return "setup";
    case kSimulcastAttribute:
      return "simulcast";
    case kSsrcAttribute:
      return "ssrc";
    case kSsrcGroupAttribute:
      return "ssrc-group";
    case kSctpPortAttribute:
      return "sctp-port";
    case kMaxMessageSizeAttribute:
      return "max-message-size";
    case kDirectionAttribute:
      MOZ_CRASH("kDirectionAttribute not valid here");
  }
  MOZ_CRASH("Unknown attribute type");
}

}  // namespace mozilla

Messung V0.5
C=95 H=93 G=93

¤ Dauer der Verarbeitung: 0.6 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge