parseSdpAudio function

SdpAudio? parseSdpAudio(
  1. String sdp
)

Parse the remote audio endpoint from an SDP body. Returns null if the body has no audio media line we can use.

Implementation

SdpAudio? parseSdpAudio(String sdp) {
  if (sdp.trim().isEmpty) return null;
  String? sessionConn;
  String? mediaConn;
  String? mLine;
  int? rtcpPort;
  SdpDirection? direction;
  int? telephoneEventPt;
  // fmtp parameters keyed by payload type (e.g. {101: '0-16'}).
  final fmtpByPt = <int, String>{};
  // Track which payload types appear in m= and any rtpmap entries so we
  // can pick PCMU (0) or PCMA (8) deterministically.
  final ptInMedia = <int>[];
  final rtpmapNames = <int, String>{};

  var inAudio = false;
  for (final raw in sdp.split('\n')) {
    final line = raw.trim();
    if (line.isEmpty) continue;
    if (line.startsWith('c=') && !inAudio) {
      sessionConn = line;
    } else if (line.startsWith('m=')) {
      inAudio = line.startsWith('m=audio');
      if (inAudio) {
        mLine = line;
        // m=audio <port> RTP/AVP <pt> <pt> ...
        final parts = line.split(RegExp(r'\s+'));
        for (var i = 3; i < parts.length; i++) {
          final pt = int.tryParse(parts[i]);
          if (pt != null) ptInMedia.add(pt);
        }
      }
    } else if (inAudio && line.startsWith('c=')) {
      mediaConn = line;
    } else if (inAudio && line.startsWith('a=rtpmap:')) {
      // a=rtpmap:<pt> <name>/<rate>[/channels]
      final rest = line.substring('a=rtpmap:'.length);
      final sp = rest.indexOf(' ');
      if (sp > 0) {
        final pt = int.tryParse(rest.substring(0, sp));
        if (pt != null) {
          final name = rest.substring(sp + 1).split('/').first.toUpperCase();
          rtpmapNames[pt] = name;
          if (name == 'TELEPHONE-EVENT') telephoneEventPt = pt;
        }
      }
    } else if (inAudio && line.startsWith('a=rtcp:')) {
      // a=rtcp:<port> [<nettype> <addrtype> <addr>]
      final rest = line.substring('a=rtcp:'.length).trim();
      final tok = rest.split(RegExp(r'\s+')).first;
      rtcpPort = int.tryParse(tok);
    } else if (inAudio && line.startsWith('a=fmtp:')) {
      // a=fmtp:<pt> <params>
      final rest = line.substring('a=fmtp:'.length);
      final sp = rest.indexOf(' ');
      if (sp > 0) {
        final pt = int.tryParse(rest.substring(0, sp));
        if (pt != null) fmtpByPt[pt] = rest.substring(sp + 1).trim();
      }
    } else if (inAudio) {
      switch (line) {
        case 'a=sendrecv':
          direction = SdpDirection.sendrecv;
          break;
        case 'a=sendonly':
          direction = SdpDirection.sendonly;
          break;
        case 'a=recvonly':
          direction = SdpDirection.recvonly;
          break;
        case 'a=inactive':
          direction = SdpDirection.inactive;
          break;
      }
    }
  }

  if (mLine == null) return null;
  final mParts = mLine.split(RegExp(r'\s+'));
  if (mParts.length < 4) return null;
  final port = int.tryParse(mParts[1]);
  if (port == null || port == 0) return null;

  final connLine = mediaConn ?? sessionConn;
  if (connLine == null) return null;
  // c=IN IP4 <host>
  final cParts = connLine.split(RegExp(r'\s+'));
  if (cParts.length < 3) return null;
  final host = cParts[2];

  // Pick first PT we know about (prefer PCMU then PCMA).
  G711Variant? chosen;
  for (final pt in ptInMedia) {
    final variant = G711Variant.fromPayloadType(pt);
    if (variant != null) {
      chosen = variant;
      break;
    }
  }
  // Fall back to rtpmap-by-name if the static PTs weren't 0/8.
  if (chosen == null) {
    for (final entry in rtpmapNames.entries) {
      if (entry.value == 'PCMU') {
        chosen = G711Variant.pcmu;
        break;
      }
      if (entry.value == 'PCMA') {
        chosen = G711Variant.pcma;
        break;
      }
    }
  }
  if (chosen == null) return null;

  return SdpAudio(
    host: host,
    port: port,
    codec: chosen,
    rtcpPort: rtcpPort,
    direction: direction ?? SdpDirection.sendrecv,
    telephoneEventPt: telephoneEventPt,
    telephoneEventRange: telephoneEventPt == null
        ? null
        : fmtpByPt[telephoneEventPt],
  );
}