Create an SDP offer for the purpose of starting a new WebRTC
        connection to a remote peer.

        :rtype: :class:`RTCSessionDescription`

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)