Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/webrtc/sdp/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 43 kB image not shown  

Quelle  SipccSdpAttributeList.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/SipccSdpAttributeList.h"

#include <ostream>
#include "mozilla/Assertions.h"

extern "C" {
#include "sdp_private.h"
}

namespace mozilla {

using InternalResults = SdpParser::InternalResults;

/* static */
MOZ_RUNINIT const std::string SipccSdpAttributeList::kEmptyString = "";

SipccSdpAttributeList::SipccSdpAttributeList(
    const SipccSdpAttributeList* sessionLevel)
    : mSessionLevel(sessionLevel) {
  memset(&mAttributes, 0, sizeof(mAttributes));
}

SipccSdpAttributeList::SipccSdpAttributeList(
    const SipccSdpAttributeList& aOrig,
    const SipccSdpAttributeList* sessionLevel)
    : SipccSdpAttributeList(sessionLevel) {
  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
    if (aOrig.mAttributes[i]) {
      mAttributes[i] = aOrig.mAttributes[i]->Clone();
    }
  }
}

SipccSdpAttributeList::~SipccSdpAttributeList() {
  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
    delete mAttributes[i];
  }
}

bool SipccSdpAttributeList::HasAttribute(AttributeType type,
                                         bool sessionFallback) const {
  return !!GetAttribute(type, sessionFallback);
}

const SdpAttribute* SipccSdpAttributeList::GetAttribute(
    AttributeType type, bool sessionFallback) const {
  const SdpAttribute* value = mAttributes[static_cast<size_t>(type)];
  // Only do fallback when the attribute can appear at both the media and
  // session level
  if (!value && !AtSessionLevel() && sessionFallback &&
      SdpAttribute::IsAllowedAtSessionLevel(type) &&
      SdpAttribute::IsAllowedAtMediaLevel(type)) {
    return mSessionLevel->GetAttribute(type, false);
  }
  return value;
}

void SipccSdpAttributeList::RemoveAttribute(AttributeType type) {
  delete mAttributes[static_cast<size_t>(type)];
  mAttributes[static_cast<size_t>(type)] = nullptr;
}

void SipccSdpAttributeList::Clear() {
  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
    RemoveAttribute(static_cast<AttributeType>(i));
  }
}

uint32_t SipccSdpAttributeList::Count() const {
  uint32_t count = 0;
  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
    if (mAttributes[i]) {
      count++;
    }
  }
  return count;
}

void SipccSdpAttributeList::SetAttribute(SdpAttribute* attr) {
  if (!IsAllowedHere(attr->GetType())) {
    MOZ_ASSERT(false"This type of attribute is not allowed here");
    return;
  }
  RemoveAttribute(attr->GetType());
  mAttributes[attr->GetType()] = attr;
}

void SipccSdpAttributeList::LoadSimpleString(sdp_t* sdp, uint16_t level,
                                             sdp_attr_e attr,
                                             AttributeType targetType,
                                             InternalResults& results) {
  const char* value = sdp_attr_get_simple_string(sdp, attr, level, 0, 1);
  if (value) {
    if (!IsAllowedHere(targetType)) {
      uint32_t lineNumber = sdp_attr_line_number(sdp, attr, level, 0, 1);
      WarnAboutMisplacedAttribute(targetType, lineNumber, results);
    } else {
      SetAttribute(new SdpStringAttribute(targetType, std::string(value)));
    }
  }
}

void SipccSdpAttributeList::LoadSimpleStrings(sdp_t* sdp, uint16_t level,
                                              InternalResults& results) {
  LoadSimpleString(sdp, level, SDP_ATTR_MID, SdpAttribute::kMidAttribute,
                   results);
  LoadSimpleString(sdp, level, SDP_ATTR_LABEL, SdpAttribute::kLabelAttribute,
                   results);
}

void SipccSdpAttributeList::LoadSimpleNumber(sdp_t* sdp, uint16_t level,
                                             sdp_attr_e attr,
                                             AttributeType targetType,
                                             InternalResults& results) {
  if (sdp_attr_valid(sdp, attr, level, 0, 1)) {
    if (!IsAllowedHere(targetType)) {
      uint32_t lineNumber = sdp_attr_line_number(sdp, attr, level, 0, 1);
      WarnAboutMisplacedAttribute(targetType, lineNumber, results);
    } else {
      uint32_t value = sdp_attr_get_simple_u32(sdp, attr, level, 0, 1);
      SetAttribute(new SdpNumberAttribute(targetType, value));
    }
  }
}

void SipccSdpAttributeList::LoadSimpleNumbers(sdp_t* sdp, uint16_t level,
                                              InternalResults& results) {
  LoadSimpleNumber(sdp, level, SDP_ATTR_PTIME, SdpAttribute::kPtimeAttribute,
                   results);
  LoadSimpleNumber(sdp, level, SDP_ATTR_MAXPTIME,
                   SdpAttribute::kMaxptimeAttribute, results);
  LoadSimpleNumber(sdp, level, SDP_ATTR_SCTPPORT,
                   SdpAttribute::kSctpPortAttribute, results);
  LoadSimpleNumber(sdp, level, SDP_ATTR_MAXMESSAGESIZE,
                   SdpAttribute::kMaxMessageSizeAttribute, results);
}

void SipccSdpAttributeList::LoadFlags(sdp_t* sdp, uint16_t level) {
  if (AtSessionLevel()) {
    if (sdp_attr_valid(sdp, SDP_ATTR_ICE_LITE, level, 0, 1)) {
      SetAttribute(new SdpFlagAttribute(SdpAttribute::kIceLiteAttribute));
    }
  } else {  // media-level
    if (sdp_attr_valid(sdp, SDP_ATTR_RTCP_MUX, level, 0, 1)) {
      SetAttribute(new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
    }
    if (sdp_attr_valid(sdp, SDP_ATTR_END_OF_CANDIDATES, level, 0, 1)) {
      SetAttribute(
          new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
    }
    if (sdp_attr_valid(sdp, SDP_ATTR_BUNDLE_ONLY, level, 0, 1)) {
      SetAttribute(new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
    }
    if (sdp_attr_valid(sdp, SDP_ATTR_RTCP_RSIZE, level, 0, 1))
      SetAttribute(new SdpFlagAttribute(SdpAttribute::kRtcpRsizeAttribute));
  }
}

static void ConvertDirection(sdp_direction_e sipcc_direction,
                             SdpDirectionAttribute::Direction* dir_outparam) {
  switch (sipcc_direction) {
    case SDP_DIRECTION_SENDRECV:
      *dir_outparam = SdpDirectionAttribute::kSendrecv;
      return;
    case SDP_DIRECTION_SENDONLY:
      *dir_outparam = SdpDirectionAttribute::kSendonly;
      return;
    case SDP_DIRECTION_RECVONLY:
      *dir_outparam = SdpDirectionAttribute::kRecvonly;
      return;
    case SDP_DIRECTION_INACTIVE:
      *dir_outparam = SdpDirectionAttribute::kInactive;
      return;
    case SDP_MAX_QOS_DIRECTIONS:
      // Nothing actually sets this value.
      // Fall through to MOZ_CRASH below.
      {}
  }

  MOZ_CRASH("Invalid direction from sipcc; this is probably corruption");
}

void SipccSdpAttributeList::LoadDirection(sdp_t* sdp, uint16_t level,
                                          InternalResults& results) {
  SdpDirectionAttribute::Direction dir;
  ConvertDirection(sdp_get_media_direction(sdp, level, 0), &dir);
  SetAttribute(new SdpDirectionAttribute(dir));
}

void SipccSdpAttributeList::LoadIceAttributes(sdp_t* sdp, uint16_t level) {
  char* value;
  sdp_result_e sdpres =
      sdp_attr_get_ice_attribute(sdp, level, 0, SDP_ATTR_ICE_UFRAG, 1, &value);
  if (sdpres == SDP_SUCCESS) {
    SetAttribute(new SdpStringAttribute(SdpAttribute::kIceUfragAttribute,
                                        std::string(value)));
  }
  sdpres =
      sdp_attr_get_ice_attribute(sdp, level, 0, SDP_ATTR_ICE_PWD, 1, &value);
  if (sdpres == SDP_SUCCESS) {
    SetAttribute(new SdpStringAttribute(SdpAttribute::kIcePwdAttribute,
                                        std::string(value)));
  }

  const char* iceOptVal =
      sdp_attr_get_simple_string(sdp, SDP_ATTR_ICE_OPTIONS, level, 0, 1);
  if (iceOptVal) {
    auto* iceOptions =
        new SdpOptionsAttribute(SdpAttribute::kIceOptionsAttribute);
    iceOptions->Load(iceOptVal);
    SetAttribute(iceOptions);
  }
}

bool SipccSdpAttributeList::LoadFingerprint(sdp_t* sdp, uint16_t level,
                                            InternalResults& results) {
  char* value;
  UniquePtr<SdpFingerprintAttributeList> fingerprintAttrs;

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    sdp_result_e result = sdp_attr_get_dtls_fingerprint_attribute(
        sdp, level, 0, SDP_ATTR_DTLS_FINGERPRINT, i, &value);

    if (result != SDP_SUCCESS) {
      break;
    }

    std::string fingerprintAttr(value);
    uint32_t lineNumber =
        sdp_attr_line_number(sdp, SDP_ATTR_DTLS_FINGERPRINT, level, 0, i);

    // sipcc does not expose parse code for this
    size_t start = fingerprintAttr.find_first_not_of(" \t");
    if (start == std::string::npos) {
      results.AddParseError(lineNumber, "Empty fingerprint attribute");
      return false;
    }

    size_t end = fingerprintAttr.find_first_of(" \t", start);
    if (end == std::string::npos) {
      // One token, no trailing ws
      results.AddParseError(lineNumber,
                            "Only one token in fingerprint attribute");
      return false;
    }

    std::string algorithmToken(fingerprintAttr.substr(start, end - start));

    start = fingerprintAttr.find_first_not_of(" \t", end);
    if (start == std::string::npos) {
      // One token, trailing ws
      results.AddParseError(lineNumber,
                            "Only one token in fingerprint attribute");
      return false;
    }

    std::string fingerprintToken(fingerprintAttr.substr(start));

    std::vector<uint8_t> fingerprint =
        SdpFingerprintAttributeList::ParseFingerprint(fingerprintToken);
    if (fingerprint.empty()) {
      results.AddParseError(lineNumber, "Malformed fingerprint token");
      return false;
    }

    if (!fingerprintAttrs) {
      fingerprintAttrs.reset(new SdpFingerprintAttributeList);
    }

    // Don't assert on unknown algorithm, just skip
    fingerprintAttrs->PushEntry(algorithmToken, fingerprint, false);
  }

  if (fingerprintAttrs) {
    SetAttribute(fingerprintAttrs.release());
  }

  return true;
}

void SipccSdpAttributeList::LoadCandidate(sdp_t* sdp, uint16_t level) {
  char* value;
  auto candidates =
      MakeUnique<SdpMultiStringAttribute>(SdpAttribute::kCandidateAttribute);

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    sdp_result_e result = sdp_attr_get_ice_attribute(
        sdp, level, 0, SDP_ATTR_ICE_CANDIDATE, i, &value);

    if (result != SDP_SUCCESS) {
      break;
    }

    candidates->mValues.push_back(value);
  }

  if (!candidates->mValues.empty()) {
    SetAttribute(candidates.release());
  }
}

bool SipccSdpAttributeList::LoadSctpmap(sdp_t* sdp, uint16_t level,
                                        InternalResults& results) {
  auto sctpmap = MakeUnique<SdpSctpmapAttributeList>();
  for (uint16_t i = 0; i < UINT16_MAX; ++i) {
    sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_SCTPMAP, i + 1);

    if (!attr) {
      break;
    }

    // Yeah, this is a little weird, but for now we'll just store this as a
    // payload type.
    uint16_t payloadType = attr->attr.sctpmap.port;
    uint16_t streams = attr->attr.sctpmap.streams;
    const char* name = attr->attr.sctpmap.protocol;

    std::ostringstream osPayloadType;
    osPayloadType << payloadType;
    sctpmap->PushEntry(osPayloadType.str(), name, streams);
  }

  if (!sctpmap->mSctpmaps.empty()) {
    SetAttribute(sctpmap.release());
  }

  return true;
}

SdpRtpmapAttributeList::CodecType SipccSdpAttributeList::GetCodecType(
    rtp_ptype type) {
  switch (type) {
    case RTP_PCMU:
      return SdpRtpmapAttributeList::kPCMU;
    case RTP_PCMA:
      return SdpRtpmapAttributeList::kPCMA;
    case RTP_G722:
      return SdpRtpmapAttributeList::kG722;
    case RTP_H264_P0:
    case RTP_H264_P1:
      return SdpRtpmapAttributeList::kH264;
    case RTP_AV1:
      return SdpRtpmapAttributeList::kAV1;
    case RTP_OPUS:
      return SdpRtpmapAttributeList::kOpus;
    case RTP_VP8:
      return SdpRtpmapAttributeList::kVP8;
    case RTP_VP9:
      return SdpRtpmapAttributeList::kVP9;
    case RTP_RED:
      return SdpRtpmapAttributeList::kRed;
    case RTP_ULPFEC:
      return SdpRtpmapAttributeList::kUlpfec;
    case RTP_RTX:
      return SdpRtpmapAttributeList::kRtx;
    case RTP_TELEPHONE_EVENT:
      return SdpRtpmapAttributeList::kTelephoneEvent;
    case RTP_NONE:
    // Happens when sipcc doesn't know how to translate to the enum
    case RTP_CELP:
    case RTP_G726:
    case RTP_GSM:
    case RTP_G723:
    case RTP_DVI4:
    case RTP_DVI4_II:
    case RTP_LPC:
    case RTP_G728:
    case RTP_G729:
    case RTP_JPEG:
    case RTP_NV:
    case RTP_H261:
    case RTP_L16:
    case RTP_H263:
    case RTP_ILBC:
    case RTP_I420:
      return SdpRtpmapAttributeList::kOtherCodec;
  }
  MOZ_CRASH("Invalid codec type from sipcc. Probably corruption.");
}

bool SipccSdpAttributeList::LoadRtpmap(sdp_t* sdp, uint16_t level,
                                       InternalResults& results) {
  auto rtpmap = MakeUnique<SdpRtpmapAttributeList>();
  uint16_t count;
  sdp_result_e result =
      sdp_attr_num_instances(sdp, level, 0, SDP_ATTR_RTPMAP, &count);
  if (result != SDP_SUCCESS) {
    MOZ_ASSERT(false"Unable to get rtpmap size");
    results.AddParseError(sdp_get_media_line_number(sdp, level),
                          "Unable to get rtpmap size");
    return false;
  }
  for (uint16_t i = 0; i < count; ++i) {
    uint16_t pt = sdp_attr_get_rtpmap_payload_type(sdp, level, 0, i + 1);
    const char* ccName = sdp_attr_get_rtpmap_encname(sdp, level, 0, i + 1);

    if (!ccName) {
      // Probably no rtpmap attribute for a pt in an m-line
      results.AddParseError(sdp_get_media_line_number(sdp, level),
                            "No rtpmap attribute for payload type");
      continue;
    }

    std::string name(ccName);

    SdpRtpmapAttributeList::CodecType codec =
        GetCodecType(sdp_get_known_payload_type(sdp, level, pt));

    uint32_t clock = sdp_attr_get_rtpmap_clockrate(sdp, level, 0, i + 1);
    uint16_t channels = 0;

    // sipcc gives us a channels value of "1" for video
    if (sdp_get_media_type(sdp, level) == SDP_MEDIA_AUDIO) {
      channels = sdp_attr_get_rtpmap_num_chan(sdp, level, 0, i + 1);
    }

    std::ostringstream osPayloadType;
    osPayloadType << pt;
    rtpmap->PushEntry(osPayloadType.str(), codec, name, clock, channels);
  }

  if (!rtpmap->mRtpmaps.empty()) {
    SetAttribute(rtpmap.release());
  }

  return true;
}

void SipccSdpAttributeList::LoadSetup(sdp_t* sdp, uint16_t level) {
  sdp_setup_type_e setupType;
  auto sdpres = sdp_attr_get_setup_attribute(sdp, level, 0, 1, &setupType);

  if (sdpres != SDP_SUCCESS) {
    return;
  }

  switch (setupType) {
    case SDP_SETUP_ACTIVE:
      SetAttribute(new SdpSetupAttribute(SdpSetupAttribute::kActive));
      return;
    case SDP_SETUP_PASSIVE:
      SetAttribute(new SdpSetupAttribute(SdpSetupAttribute::kPassive));
      return;
    case SDP_SETUP_ACTPASS:
      SetAttribute(new SdpSetupAttribute(SdpSetupAttribute::kActpass));
      return;
    case SDP_SETUP_HOLDCONN:
      SetAttribute(new SdpSetupAttribute(SdpSetupAttribute::kHoldconn));
      return;
    case SDP_SETUP_UNKNOWN:
      return;
    case SDP_SETUP_NOT_FOUND:
    case SDP_MAX_SETUP:
      // There is no code that will set these.
      // Fall through to MOZ_CRASH() below.
      {}
  }

  MOZ_CRASH("Invalid setup type from sipcc. This is probably corruption.");
}

void SipccSdpAttributeList::LoadSsrc(sdp_t* sdp, uint16_t level) {
  auto ssrcs = MakeUnique<SdpSsrcAttributeList>();

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_SSRC, i);

    if (!attr) {
      break;
    }

    sdp_ssrc_t* ssrc = &(attr->attr.ssrc);
    ssrcs->PushEntry(ssrc->ssrc, ssrc->attribute);
  }

  if (!ssrcs->mSsrcs.empty()) {
    SetAttribute(ssrcs.release());
  }
}

void SipccSdpAttributeList::LoadSsrcGroup(sdp_t* sdp, uint16_t level) {
  auto ssrcGroups = MakeUnique<SdpSsrcGroupAttributeList>();

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_SSRC_GROUP, i);

    if (!attr) {
      break;
    }

    sdp_ssrc_group_t* ssrc_group = &(attr->attr.ssrc_group);

    SdpSsrcGroupAttributeList::Semantics semantic;
    switch (ssrc_group->semantic) {
      case SDP_SSRC_GROUP_ATTR_FEC:
        semantic = SdpSsrcGroupAttributeList::kFec;
        break;
      case SDP_SSRC_GROUP_ATTR_FID:
        semantic = SdpSsrcGroupAttributeList::kFid;
        break;
      case SDP_SSRC_GROUP_ATTR_FECFR:
        semantic = SdpSsrcGroupAttributeList::kFecFr;
        break;
      case SDP_SSRC_GROUP_ATTR_DUP:
        semantic = SdpSsrcGroupAttributeList::kDup;
        break;
      case SDP_SSRC_GROUP_ATTR_SIM:
        semantic = SdpSsrcGroupAttributeList::kSim;
        break;
      case SDP_MAX_SSRC_GROUP_ATTR_VAL:
        continue;
      case SDP_SSRC_GROUP_ATTR_UNSUPPORTED:
        continue;
    }

    std::vector<uint32_t> ssrcs;
    ssrcs.reserve(ssrc_group->num_ssrcs);
    for (int i = 0; i < ssrc_group->num_ssrcs; ++i) {
      ssrcs.push_back(ssrc_group->ssrcs[i]);
    }

    ssrcGroups->PushEntry(semantic, ssrcs);
  }

  if (!ssrcGroups->mSsrcGroups.empty()) {
    SetAttribute(ssrcGroups.release());
  }
}

bool SipccSdpAttributeList::LoadImageattr(sdp_t* sdp, uint16_t level,
                                          InternalResults& results) {
  UniquePtr<SdpImageattrAttributeList> imageattrs(
      new SdpImageattrAttributeList);

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    const char* imageattrRaw =
        sdp_attr_get_simple_string(sdp, SDP_ATTR_IMAGEATTR, level, 0, i);
    if (!imageattrRaw) {
      break;
    }

    std::string error;
    size_t errorPos;
    if (!imageattrs->PushEntry(imageattrRaw, &error, &errorPos)) {
      std::ostringstream fullError;
      fullError << error << " at column " << errorPos;
      results.AddParseError(
          sdp_attr_line_number(sdp, SDP_ATTR_IMAGEATTR, level, 0, i),
          fullError.str());
      return false;
    }
  }

  if (!imageattrs->mImageattrs.empty()) {
    SetAttribute(imageattrs.release());
  }
  return true;
}

bool SipccSdpAttributeList::LoadSimulcast(sdp_t* sdp, uint16_t level,
                                          InternalResults& results) {
  const char* simulcastRaw =
      sdp_attr_get_simple_string(sdp, SDP_ATTR_SIMULCAST, level, 0, 1);
  if (!simulcastRaw) {
    return true;
  }

  UniquePtr<SdpSimulcastAttribute> simulcast(new SdpSimulcastAttribute);

  std::istringstream is(simulcastRaw);
  std::string error;
  if (!simulcast->Parse(is, &error)) {
    std::ostringstream fullError;
    fullError << error << " at column " << is.tellg();
    results.AddParseError(
        sdp_attr_line_number(sdp, SDP_ATTR_SIMULCAST, level, 0, 1),
        fullError.str());
    return false;
  }

  SetAttribute(simulcast.release());
  return true;
}

bool SipccSdpAttributeList::LoadGroups(sdp_t* sdp, uint16_t level,
                                       InternalResults& results) {
  uint16_t attrCount = 0;
  if (sdp_attr_num_instances(sdp, level, 0, SDP_ATTR_GROUP, &attrCount) !=
      SDP_SUCCESS) {
    MOZ_ASSERT(false"Could not get count of group attributes");
    results.AddParseError(0, "Could not get count of group attributes");
    return false;
  }

  UniquePtr<SdpGroupAttributeList> groups = MakeUnique<SdpGroupAttributeList>();
  for (uint16_t attr = 1; attr <= attrCount; ++attr) {
    SdpGroupAttributeList::Semantics semantics;
    std::vector<std::string> tags;

    switch (sdp_get_group_attr(sdp, level, 0, attr)) {
      case SDP_GROUP_ATTR_FID:
        semantics = SdpGroupAttributeList::kFid;
        break;
      case SDP_GROUP_ATTR_LS:
        semantics = SdpGroupAttributeList::kLs;
        break;
      case SDP_GROUP_ATTR_ANAT:
        semantics = SdpGroupAttributeList::kAnat;
        break;
      case SDP_GROUP_ATTR_BUNDLE:
        semantics = SdpGroupAttributeList::kBundle;
        break;
      default:
        continue;
    }

    uint16_t idCount = sdp_get_group_num_id(sdp, level, 0, attr);
    for (uint16_t id = 1; id <= idCount; ++id) {
      const char* idStr = sdp_get_group_id(sdp, level, 0, attr, id);
      if (!idStr) {
        std::ostringstream os;
        os << "bad a=group identifier at " << (attr - 1) << ", " << (id - 1);
        results.AddParseError(0, os.str());
        return false;
      }
      tags.push_back(std::string(idStr));
    }
    groups->PushEntry(semantics, tags);
  }

  if (!groups->mGroups.empty()) {
    SetAttribute(groups.release());
  }

  return true;
}

bool SipccSdpAttributeList::LoadMsidSemantics(sdp_t* sdp, uint16_t level,
                                              InternalResults& results) {
  auto msidSemantics = MakeUnique<SdpMsidSemanticAttributeList>();

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_MSID_SEMANTIC, i);

    if (!attr) {
      break;
    }

    sdp_msid_semantic_t* msid_semantic = &(attr->attr.msid_semantic);
    std::vector<std::string> msids;
    for (size_t i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) {
      if (!msid_semantic->msids[i]) {
        break;
      }

      msids.push_back(msid_semantic->msids[i]);
    }

    msidSemantics->PushEntry(msid_semantic->semantic, msids);
  }

  if (!msidSemantics->mMsidSemantics.empty()) {
    SetAttribute(msidSemantics.release());
  }
  return true;
}

void SipccSdpAttributeList::LoadIdentity(sdp_t* sdp, uint16_t level) {
  const char* val =
      sdp_attr_get_long_string(sdp, SDP_ATTR_IDENTITY, level, 0, 1);
  if (val) {
    SetAttribute(new SdpStringAttribute(SdpAttribute::kIdentityAttribute,
                                        std::string(val)));
  }
}

void SipccSdpAttributeList::LoadDtlsMessage(sdp_t* sdp, uint16_t level) {
  const char* val =
      sdp_attr_get_long_string(sdp, SDP_ATTR_DTLS_MESSAGE, level, 0, 1);
  if (val) {
    // sipcc does not expose parse code for this, so we use a SDParta-provided
    // parser
    std::string strval(val);
    SetAttribute(new SdpDtlsMessageAttribute(strval));
  }
}

void SipccSdpAttributeList::LoadFmtp(sdp_t* sdp, uint16_t level) {
  auto fmtps = MakeUnique<SdpFmtpAttributeList>();

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_FMTP, i);

    if (!attr) {
      break;
    }

    sdp_fmtp_t* fmtp = &(attr->attr.fmtp);

    // Get the payload type
    std::stringstream osPayloadType;
    // payload_num is the number in the fmtp attribute, verbatim
    osPayloadType << fmtp->payload_num;

    // Get parsed form of parameters, if supported
    UniquePtr<SdpFmtpAttributeList::Parameters> parameters;

    rtp_ptype codec = sdp_get_known_payload_type(sdp, level, fmtp->payload_num);

    switch (codec) {
      case RTP_H264_P0:
      case RTP_H264_P1: {
        SdpFmtpAttributeList::H264Parameters* h264Parameters(
            new SdpFmtpAttributeList::H264Parameters);

        sstrncpy(h264Parameters->sprop_parameter_sets, fmtp->parameter_sets,
                 sizeof(h264Parameters->sprop_parameter_sets));

        h264Parameters->level_asymmetry_allowed =
            !!(fmtp->level_asymmetry_allowed);

        h264Parameters->packetization_mode = fmtp->packetization_mode;
        sscanf(fmtp->profile_level_id, "%x", &h264Parameters->profile_level_id);
        h264Parameters->max_mbps = fmtp->max_mbps;
        h264Parameters->max_fs = fmtp->max_fs;
        h264Parameters->max_cpb = fmtp->max_cpb;
        h264Parameters->max_dpb = fmtp->max_dpb;
        h264Parameters->max_br = fmtp->max_br;

        parameters.reset(h264Parameters);
      } break;
      case RTP_AV1: {
        SdpFmtpAttributeList::Av1Parameters* av1Parameters(
            new SdpFmtpAttributeList::Av1Parameters());
        if (fmtp->profile > 0 && fmtp->profile <= UINT8_MAX) {
          av1Parameters->profile = Some(static_cast<uint8_t>(fmtp->profile));
        }
        if (fmtp->av1_has_level_idx) {
          av1Parameters->profile = Some(fmtp->av1_level_idx);
        }
        if (fmtp->av1_has_tier) {
          av1Parameters->tier = Some(fmtp->av1_tier);
        }
        parameters.reset(av1Parameters);
      } break;
      case RTP_VP9: {
        SdpFmtpAttributeList::VP8Parameters* vp9Parameters(
            new SdpFmtpAttributeList::VP8Parameters(
                SdpRtpmapAttributeList::kVP9));

        vp9Parameters->max_fs = fmtp->max_fs;
        vp9Parameters->max_fr = fmtp->max_fr;

        parameters.reset(vp9Parameters);
      } break;
      case RTP_VP8: {
        SdpFmtpAttributeList::VP8Parameters* vp8Parameters(
            new SdpFmtpAttributeList::VP8Parameters(
                SdpRtpmapAttributeList::kVP8));

        vp8Parameters->max_fs = fmtp->max_fs;
        vp8Parameters->max_fr = fmtp->max_fr;

        parameters.reset(vp8Parameters);
      } break;
      case RTP_RED: {
        SdpFmtpAttributeList::RedParameters* redParameters(
            new SdpFmtpAttributeList::RedParameters);
        for (int i = 0; i < SDP_FMTP_MAX_REDUNDANT_ENCODINGS &&
                        fmtp->redundant_encodings[i];
             ++i) {
          redParameters->encodings.push_back(fmtp->redundant_encodings[i]);
        }

        parameters.reset(redParameters);
      } break;
      case RTP_OPUS: {
        SdpFmtpAttributeList::OpusParameters* opusParameters(
            new SdpFmtpAttributeList::OpusParameters);
        opusParameters->maxplaybackrate = fmtp->maxplaybackrate;
        opusParameters->stereo = fmtp->stereo;
        opusParameters->useInBandFec = fmtp->useinbandfec;
        opusParameters->maxAverageBitrate = fmtp->maxaveragebitrate;
        opusParameters->useDTX = fmtp->usedtx;
        parameters.reset(opusParameters);
      } break;
      case RTP_TELEPHONE_EVENT: {
        SdpFmtpAttributeList::TelephoneEventParameters* teParameters(
            new SdpFmtpAttributeList::TelephoneEventParameters);
        if (strlen(fmtp->dtmf_tones) > 0) {
          teParameters->dtmfTones = fmtp->dtmf_tones;
        }
        parameters.reset(teParameters);
      } break;
      case RTP_RTX: {
        SdpFmtpAttributeList::RtxParameters* rtxParameters(
            new SdpFmtpAttributeList::RtxParameters);
        rtxParameters->apt = fmtp->apt;
        if (fmtp->has_rtx_time == TRUE) {
          rtxParameters->rtx_time = Some(fmtp->rtx_time);
        }
        parameters.reset(rtxParameters);
      } break;
      default: {
      }
    }

    if (parameters) {
      fmtps->PushEntry(osPayloadType.str(), *parameters);
    }
  }

  if (!fmtps->mFmtps.empty()) {
    SetAttribute(fmtps.release());
  }
}

void SipccSdpAttributeList::LoadMsids(sdp_t* sdp, uint16_t level,
                                      InternalResults& results) {
  uint16_t attrCount = 0;
  if (sdp_attr_num_instances(sdp, level, 0, SDP_ATTR_MSID, &attrCount) !=
      SDP_SUCCESS) {
    MOZ_ASSERT(false"Unable to get count of msid attributes");
    results.AddParseError(0, "Unable to get count of msid attributes");
    return;
  }
  auto msids = MakeUnique<SdpMsidAttributeList>();
  for (uint16_t i = 1; i <= attrCount; ++i) {
    uint32_t lineNumber = sdp_attr_line_number(sdp, SDP_ATTR_MSID, level, 0, i);

    const char* identifier = sdp_attr_get_msid_identifier(sdp, level, 0, i);
    if (!identifier) {
      results.AddParseError(lineNumber, "msid attribute with bad identity");
      continue;
    }

    const char* appdata = sdp_attr_get_msid_appdata(sdp, level, 0, i);
    if (!appdata) {
      results.AddParseError(lineNumber, "msid attribute with bad appdata");
      continue;
    }

    msids->PushEntry(identifier, appdata);
  }

  if (!msids->mMsids.empty()) {
    SetAttribute(msids.release());
  }
}

bool SipccSdpAttributeList::LoadRid(sdp_t* sdp, uint16_t level,
                                    InternalResults& results) {
  UniquePtr<SdpRidAttributeList> rids(new SdpRidAttributeList);

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    const char* ridRaw =
        sdp_attr_get_simple_string(sdp, SDP_ATTR_RID, level, 0, i);
    if (!ridRaw) {
      break;
    }

    std::string error;
    size_t errorPos;
    if (!rids->PushEntry(ridRaw, &error, &errorPos)) {
      std::ostringstream fullError;
      fullError << error << " at column " << errorPos;
      results.AddParseError(
          sdp_attr_line_number(sdp, SDP_ATTR_RID, level, 0, i),
          fullError.str());
      return false;
    }
  }

  if (!rids->mRids.empty()) {
    SetAttribute(rids.release());
  }
  return true;
}

void SipccSdpAttributeList::LoadExtmap(sdp_t* sdp, uint16_t level,
                                       InternalResults& results) {
  auto extmaps = MakeUnique<SdpExtmapAttributeList>();

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_EXTMAP, i);

    if (!attr) {
      break;
    }

    sdp_extmap_t* extmap = &(attr->attr.extmap);

    SdpDirectionAttribute::Direction dir = SdpDirectionAttribute::kSendrecv;

    if (extmap->media_direction_specified) {
      ConvertDirection(extmap->media_direction, &dir);
    }

    extmaps->PushEntry(extmap->id, dir, extmap->media_direction_specified,
                       extmap->uri, extmap->extension_attributes);
  }

  if (!extmaps->mExtmaps.empty()) {
    if (!AtSessionLevel() &&
        mSessionLevel->HasAttribute(SdpAttribute::kExtmapAttribute)) {
      uint32_t lineNumber =
          sdp_attr_line_number(sdp, SDP_ATTR_EXTMAP, level, 0, 1);
      results.AddParseError(
          lineNumber, "extmap attributes in both session and media level");
    }
    SetAttribute(extmaps.release());
  }
}

void SipccSdpAttributeList::LoadRtcpFb(sdp_t* sdp, uint16_t level,
                                       InternalResults& results) {
  auto rtcpfbs = MakeUnique<SdpRtcpFbAttributeList>();

  for (uint16_t i = 1; i < UINT16_MAX; ++i) {
    sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_RTCP_FB, i);

    if (!attr) {
      break;
    }

    sdp_fmtp_fb_t* rtcpfb = &attr->attr.rtcp_fb;

    SdpRtcpFbAttributeList::Type type;
    std::string parameter;

    // Set type and parameter
    switch (rtcpfb->feedback_type) {
      case SDP_RTCP_FB_ACK:
        type = SdpRtcpFbAttributeList::kAck;
        switch (rtcpfb->param.ack) {
          // TODO: sipcc doesn't seem to support ack with no following token.
          // Issue 189.
          case SDP_RTCP_FB_ACK_RPSI:
            parameter = SdpRtcpFbAttributeList::rpsi;
            break;
          case SDP_RTCP_FB_ACK_APP:
            parameter = SdpRtcpFbAttributeList::app;
            break;
          default:
            // Type we don't care about, ignore.
            continue;
        }
        break;
      case SDP_RTCP_FB_CCM:
        type = SdpRtcpFbAttributeList::kCcm;
        switch (rtcpfb->param.ccm) {
          case SDP_RTCP_FB_CCM_FIR:
            parameter = SdpRtcpFbAttributeList::fir;
            break;
          case SDP_RTCP_FB_CCM_TMMBR:
            parameter = SdpRtcpFbAttributeList::tmmbr;
            break;
          case SDP_RTCP_FB_CCM_TSTR:
            parameter = SdpRtcpFbAttributeList::tstr;
            break;
          case SDP_RTCP_FB_CCM_VBCM:
            parameter = SdpRtcpFbAttributeList::vbcm;
            break;
          default:
            // Type we don't care about, ignore.
            continue;
        }
        break;
      case SDP_RTCP_FB_NACK:
        type = SdpRtcpFbAttributeList::kNack;
        switch (rtcpfb->param.nack) {
          case SDP_RTCP_FB_NACK_BASIC:
            break;
          case SDP_RTCP_FB_NACK_SLI:
            parameter = SdpRtcpFbAttributeList::sli;
            break;
          case SDP_RTCP_FB_NACK_PLI:
            parameter = SdpRtcpFbAttributeList::pli;
            break;
          case SDP_RTCP_FB_NACK_RPSI:
            parameter = SdpRtcpFbAttributeList::rpsi;
            break;
          case SDP_RTCP_FB_NACK_APP:
            parameter = SdpRtcpFbAttributeList::app;
            break;
          default:
            // Type we don't care about, ignore.
            continue;
        }
        break;
      case SDP_RTCP_FB_TRR_INT: {
        type = SdpRtcpFbAttributeList::kTrrInt;
        std::ostringstream os;
        os << rtcpfb->param.trr_int;
        parameter = os.str();
      } break;
      case SDP_RTCP_FB_REMB: {
        type = SdpRtcpFbAttributeList::kRemb;
      } break;
      case SDP_RTCP_FB_TRANSPORT_CC: {
        type = SdpRtcpFbAttributeList::kTransportCC;
      } break;
      default:
        // Type we don't care about, ignore.
        continue;
    }

    std::stringstream osPayloadType;
    if (rtcpfb->payload_num == UINT16_MAX) {
      osPayloadType << "*";
    } else {
      osPayloadType << rtcpfb->payload_num;
    }

    std::string pt(osPayloadType.str());
    std::string extra(rtcpfb->extra);

    rtcpfbs->PushEntry(pt, type, parameter, extra);
  }

  if (!rtcpfbs->mFeedbacks.empty()) {
    SetAttribute(rtcpfbs.release());
  }
}

void SipccSdpAttributeList::LoadRtcp(sdp_t* sdp, uint16_t level,
                                     InternalResults& results) {
  sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_RTCP, 1);

  if (!attr) {
    return;
  }

  sdp_rtcp_t* rtcp = &attr->attr.rtcp;

  if (rtcp->nettype != SDP_NT_INTERNET) {
    return;
  }

  if (rtcp->addrtype != SDP_AT_IP4 && rtcp->addrtype != SDP_AT_IP6) {
    return;
  }

  if (!strlen(rtcp->addr)) {
    SetAttribute(new SdpRtcpAttribute(rtcp->port));
  } else {
    SetAttribute(new SdpRtcpAttribute(
        rtcp->port, sdp::kInternet,
        rtcp->addrtype == SDP_AT_IP4 ? sdp::kIPv4 : sdp::kIPv6, rtcp->addr));
  }
}

bool SipccSdpAttributeList::Load(sdp_t* sdp, uint16_t level,
                                 InternalResults& results) {
  LoadSimpleStrings(sdp, level, results);
  LoadSimpleNumbers(sdp, level, results);
  LoadFlags(sdp, level);
  LoadDirection(sdp, level, results);

  if (AtSessionLevel()) {
    if (!LoadGroups(sdp, level, results)) {
      return false;
    }

    if (!LoadMsidSemantics(sdp, level, results)) {
      return false;
    }

    LoadIdentity(sdp, level);
    LoadDtlsMessage(sdp, level);
  } else {
    sdp_media_e mtype = sdp_get_media_type(sdp, level);
    if (mtype == SDP_MEDIA_APPLICATION) {
      LoadSctpmap(sdp, level, results);
    } else {
      if (!LoadRtpmap(sdp, level, results)) {
        return false;
      }
    }
    LoadCandidate(sdp, level);
    LoadFmtp(sdp, level);
    LoadMsids(sdp, level, results);
    LoadRtcpFb(sdp, level, results);
    LoadRtcp(sdp, level, results);
    LoadSsrc(sdp, level);
    LoadSsrcGroup(sdp, level);
    if (!LoadImageattr(sdp, level, results)) {
      return false;
    }
    if (!LoadSimulcast(sdp, level, results)) {
      return false;
    }
    if (!LoadRid(sdp, level, results)) {
      return false;
    }
  }

  LoadIceAttributes(sdp, level);
  if (!LoadFingerprint(sdp, level, results)) {
    return false;
  }
  LoadSetup(sdp, level);
  LoadExtmap(sdp, level, results);

  return true;
}

bool SipccSdpAttributeList::IsAllowedHere(
    SdpAttribute::AttributeType type) const {
  if (AtSessionLevel() && !SdpAttribute::IsAllowedAtSessionLevel(type)) {
    return false;
  }

  if (!AtSessionLevel() && !SdpAttribute::IsAllowedAtMediaLevel(type)) {
    return false;
  }

  return true;
}

void SipccSdpAttributeList::WarnAboutMisplacedAttribute(
    SdpAttribute::AttributeType type, uint32_t lineNumber,
    InternalResults& results) {
  std::string warning = SdpAttribute::GetAttributeTypeString(type) +
                        (AtSessionLevel() ? " at session level. Ignoring."
                                          : " at media level. Ignoring.");
  results.AddParseError(lineNumber, warning);
}

const std::vector<std::string>& SipccSdpAttributeList::GetCandidate() const {
  if (!HasAttribute(SdpAttribute::kCandidateAttribute)) {
    MOZ_CRASH();
  }

  return static_cast<const SdpMultiStringAttribute*>(
             GetAttribute(SdpAttribute::kCandidateAttribute))
      ->mValues;
}

const SdpConnectionAttribute& SipccSdpAttributeList::GetConnection() const {
  if (!HasAttribute(SdpAttribute::kConnectionAttribute)) {
    MOZ_CRASH();
  }

  return *static_cast<const SdpConnectionAttribute*>(
      GetAttribute(SdpAttribute::kConnectionAttribute));
}

SdpDirectionAttribute::Direction SipccSdpAttributeList::GetDirection() const {
  if (!HasAttribute(SdpAttribute::kDirectionAttribute)) {
    MOZ_CRASH();
  }

  const SdpAttribute* attr = GetAttribute(SdpAttribute::kDirectionAttribute);
  return static_cast<const SdpDirectionAttribute*>(attr)->mValue;
}

const SdpDtlsMessageAttribute& SipccSdpAttributeList::GetDtlsMessage() const {
  if (!HasAttribute(SdpAttribute::kDtlsMessageAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kDtlsMessageAttribute);
  return *static_cast<const SdpDtlsMessageAttribute*>(attr);
}

const SdpExtmapAttributeList& SipccSdpAttributeList::GetExtmap() const {
  if (!HasAttribute(SdpAttribute::kExtmapAttribute)) {
    MOZ_CRASH();
  }

  return *static_cast<const SdpExtmapAttributeList*>(
      GetAttribute(SdpAttribute::kExtmapAttribute));
}

const SdpFingerprintAttributeList& SipccSdpAttributeList::GetFingerprint()
    const {
  if (!HasAttribute(SdpAttribute::kFingerprintAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kFingerprintAttribute);
  return *static_cast<const SdpFingerprintAttributeList*>(attr);
}

const SdpFmtpAttributeList& SipccSdpAttributeList::GetFmtp() const {
  if (!HasAttribute(SdpAttribute::kFmtpAttribute)) {
    MOZ_CRASH();
  }

  return *static_cast<const SdpFmtpAttributeList*>(
      GetAttribute(SdpAttribute::kFmtpAttribute));
}

const SdpGroupAttributeList& SipccSdpAttributeList::GetGroup() const {
  if (!HasAttribute(SdpAttribute::kGroupAttribute)) {
    MOZ_CRASH();
  }

  return *static_cast<const SdpGroupAttributeList*>(
      GetAttribute(SdpAttribute::kGroupAttribute));
}

const SdpOptionsAttribute& SipccSdpAttributeList::GetIceOptions() const {
  if (!HasAttribute(SdpAttribute::kIceOptionsAttribute)) {
    MOZ_CRASH();
  }

  const SdpAttribute* attr = GetAttribute(SdpAttribute::kIceOptionsAttribute);
  return *static_cast<const SdpOptionsAttribute*>(attr);
}

const std::string& SipccSdpAttributeList::GetIcePwd() const {
  if (!HasAttribute(SdpAttribute::kIcePwdAttribute)) {
    return kEmptyString;
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kIcePwdAttribute);
  return static_cast<const SdpStringAttribute*>(attr)->mValue;
}

const std::string& SipccSdpAttributeList::GetIceUfrag() const {
  if (!HasAttribute(SdpAttribute::kIceUfragAttribute)) {
    return kEmptyString;
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kIceUfragAttribute);
  return static_cast<const SdpStringAttribute*>(attr)->mValue;
}

const std::string& SipccSdpAttributeList::GetIdentity() const {
  if (!HasAttribute(SdpAttribute::kIdentityAttribute)) {
    return kEmptyString;
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kIdentityAttribute);
  return static_cast<const SdpStringAttribute*>(attr)->mValue;
}

const SdpImageattrAttributeList& SipccSdpAttributeList::GetImageattr() const {
  if (!HasAttribute(SdpAttribute::kImageattrAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kImageattrAttribute);
  return *static_cast<const SdpImageattrAttributeList*>(attr);
}

const SdpSimulcastAttribute& SipccSdpAttributeList::GetSimulcast() const {
  if (!HasAttribute(SdpAttribute::kSimulcastAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSimulcastAttribute);
  return *static_cast<const SdpSimulcastAttribute*>(attr);
}

const std::string& SipccSdpAttributeList::GetLabel() const {
  if (!HasAttribute(SdpAttribute::kLabelAttribute)) {
    return kEmptyString;
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kLabelAttribute);
  return static_cast<const SdpStringAttribute*>(attr)->mValue;
}

uint32_t SipccSdpAttributeList::GetMaxptime() const {
  if (!HasAttribute(SdpAttribute::kMaxptimeAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMaxptimeAttribute);
  return static_cast<const SdpNumberAttribute*>(attr)->mValue;
}

const std::string& SipccSdpAttributeList::GetMid() const {
  if (!HasAttribute(SdpAttribute::kMidAttribute)) {
    return kEmptyString;
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMidAttribute);
  return static_cast<const SdpStringAttribute*>(attr)->mValue;
}

const SdpMsidAttributeList& SipccSdpAttributeList::GetMsid() const {
  if (!HasAttribute(SdpAttribute::kMsidAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidAttribute);
  return *static_cast<const SdpMsidAttributeList*>(attr);
}

const SdpMsidSemanticAttributeList& SipccSdpAttributeList::GetMsidSemantic()
    const {
  if (!HasAttribute(SdpAttribute::kMsidSemanticAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidSemanticAttribute);
  return *static_cast<const SdpMsidSemanticAttributeList*>(attr);
}

const SdpRidAttributeList& SipccSdpAttributeList::GetRid() const {
  if (!HasAttribute(SdpAttribute::kRidAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kRidAttribute);
  return *static_cast<const SdpRidAttributeList*>(attr);
}

uint32_t SipccSdpAttributeList::GetPtime() const {
  if (!HasAttribute(SdpAttribute::kPtimeAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kPtimeAttribute);
  return static_cast<const SdpNumberAttribute*>(attr)->mValue;
}

const SdpRtcpAttribute& SipccSdpAttributeList::GetRtcp() const {
  if (!HasAttribute(SdpAttribute::kRtcpAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtcpAttribute);
  return *static_cast<const SdpRtcpAttribute*>(attr);
}

const SdpRtcpFbAttributeList& SipccSdpAttributeList::GetRtcpFb() const {
  if (!HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtcpFbAttribute);
  return *static_cast<const SdpRtcpFbAttributeList*>(attr);
}

const SdpRemoteCandidatesAttribute& SipccSdpAttributeList::GetRemoteCandidates()
    const {
  MOZ_CRASH("Not yet implemented");
}

const SdpRtpmapAttributeList& SipccSdpAttributeList::GetRtpmap() const {
  if (!HasAttribute(SdpAttribute::kRtpmapAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtpmapAttribute);
  return *static_cast<const SdpRtpmapAttributeList*>(attr);
}

const SdpSctpmapAttributeList& SipccSdpAttributeList::GetSctpmap() const {
  if (!HasAttribute(SdpAttribute::kSctpmapAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSctpmapAttribute);
  return *static_cast<const SdpSctpmapAttributeList*>(attr);
}

uint32_t SipccSdpAttributeList::GetSctpPort() const {
  if (!HasAttribute(SdpAttribute::kSctpPortAttribute)) {
    MOZ_CRASH();
  }

  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSctpPortAttribute);
  return static_cast<const SdpNumberAttribute*>(attr)->mValue;
}

uint32_t SipccSdpAttributeList::GetMaxMessageSize() const {
  if (!HasAttribute(SdpAttribute::kMaxMessageSizeAttribute)) {
    MOZ_CRASH();
  }

  const SdpAttribute* attr =
      GetAttribute(SdpAttribute::kMaxMessageSizeAttribute);
  return static_cast<const SdpNumberAttribute*>(attr)->mValue;
}

const SdpSetupAttribute& SipccSdpAttributeList::GetSetup() const {
  if (!HasAttribute(SdpAttribute::kSetupAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSetupAttribute);
  return *static_cast<const SdpSetupAttribute*>(attr);
}

const SdpSsrcAttributeList& SipccSdpAttributeList::GetSsrc() const {
  if (!HasAttribute(SdpAttribute::kSsrcAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSsrcAttribute);
  return *static_cast<const SdpSsrcAttributeList*>(attr);
}

const SdpSsrcGroupAttributeList& SipccSdpAttributeList::GetSsrcGroup() const {
  if (!HasAttribute(SdpAttribute::kSsrcGroupAttribute)) {
    MOZ_CRASH();
  }
  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSsrcGroupAttribute);
  return *static_cast<const SdpSsrcGroupAttributeList*>(attr);
}

void SipccSdpAttributeList::Serialize(std::ostream& os) const {
  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
    if (mAttributes[i]) {
      os << *mAttributes[i];
    }
  }
}

}  // namespace mozilla

Messung V0.5
C=96 H=94 G=94

¤ Dauer der Verarbeitung: 0.20 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.