docs: async def createOffer(self):
          """
          Create an SDP offer for the purpose of starting a new WebRTC
          connection to a remote peer.
  
          :rtype: :class:`RTCSessionDescription`
          """
          # check state is valid
          self.__assertNotClosed()
  
          if not self.__sctp and not self.__transceivers:
              raise InternalError('Cannot create an offer with no media and no data channels')
  
          # offer codecs
          for transceiver in self.__transceivers:
              transceiver._codecs = filter_preferred_codecs(
                  CODECS[transceiver.kind][:],
                  transceiver._preferred_codecs,
              )
              transceiver._headerExtensions = HEADER_EXTENSIONS[transceiver.kind][:]
  
          mids = self.__seenMids.copy()
  
          # create description
          ntp_seconds = clock.current_ntp_time() >> 32
          description = sdp.SessionDescription()
          description.origin = '- %d %d IN IP4 0.0.0.0' % (ntp_seconds, ntp_seconds)
          description.msid_semantic.append(sdp.GroupDescription(
              semantic='WMS',
              items=['*']))
          description.type = 'offer'
  
          def get_media(description):
              return description.media if description else []
  
          def get_media_section(media, i):
              return media[i] if i < len(media) else None
  
          # handle existing transceivers / sctp
          local_media = get_media(self.__localDescription())
          remote_media = get_media(self.__remoteDescription())
          for i in range(max(len(local_media), len(remote_media))):
              local_m = get_media_section(local_media, i)
              remote_m = get_media_section(remote_media, i)
              media_kind = local_m.kind if local_m else remote_m.kind
              mid = local_m.rtp.muxId if local_m else remote_m.rtp.muxId
              if media_kind in ['audio', 'video']:
                  transceiver = self.__getTransceiverByMid(mid)
                  transceiver._set_mline_index(i)
                  description.media.append(create_media_description_for_transceiver(
                      transceiver,
                      cname=self.__cname,
                      direction=transceiver.direction,
                      mid=mid))
              elif media_kind == 'application':
                  self.__sctp_mline_index = i
                  description.media.append(create_media_description_for_sctp(
                      self.__sctp, legacy=self._sctpLegacySdp, mid=mid))
  
          # handle new transceivers / sctp
          def next_mline_index():
              return len(description.media)
  
          for transceiver in filter(lambda x: x.mid is None and not x.stopped, self.__transceivers):
              transceiver._set_mline_index(next_mline_index())
              description.media.append(create_media_description_for_transceiver(
                  transceiver,
                  cname=self.__cname,
                  direction=transceiver.direction,
                  mid=allocate_mid(mids)))
          if self.__sctp and self.__sctp.mid is None:
              self.__sctp_mline_index = next_mline_index()
              description.media.append(create_media_description_for_sctp(
                  self.__sctp, legacy=self._sctpLegacySdp, mid=allocate_mid(mids)))
  
          bundle = sdp.GroupDescription(semantic='BUNDLE', items=[])
          for media in description.media:
              bundle.items.append(media.rtp.muxId)
          description.group.append(bundle)
  
          return wrap_session_description(description)