0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事は筆者オンリーのAdvent Calendar 202517日目の記事です。

以前の記事 の「INVITE/ACK/BYE 最小実装メモ」の続きで、MVPで返している SDP が実装上どう組み立てられているかを残す。対象は INVITE に対する 200 OK の SDPオファーの読み取り

SDP を返す流れ

  • session がローカル SDP を組み立て(PCMU/8000 固定)→ SessionOut::SipSend200{answer} として sip へ渡す。src/session/session.rsbuild_answer_pcmu8kSdp::pcmu(local_ip, local_port) を返す。
  • sip 側の SipCore::handle_session_outresponse_final_with_sdp を呼び出し、リクエストヘッダを引き継いだ 200 OK + SDP を生成して送信する。

200 OK を作るコード(実装抜粋)

// src/sip/builder.rs:221-
pub fn response_final_with_sdp(
    req: &SipRequest,
    code: u16,
    reason: &str,
    contact_ip: &str,
    sip_port: u16,
    answer: &Sdp,
) -> Option<SipResponse> {
    let via = req.header_value("Via")?;
    let from = req.header_value("From")?;
    let mut to = req.header_value("To")?.to_string();
    let call_id = req.header_value("Call-ID")?;
    let cseq = req.header_value("CSeq")?;

    if !to.to_ascii_lowercase().contains("tag=") {
        to = format!("{to};tag=rustbot");
    }

    let sdp = format!(
        concat!(
            "v=0\r\n",
            "o=rustbot 1 1 IN IP4 {ip}\r\n",
            "s=Rust PCMU Bot\r\n",
            "c=IN IP4 {ip}\r\n",
            "t=0 0\r\n",
            "m=audio {rtp} RTP/AVP {pt}\r\n",
            "a=rtpmap:{pt} {codec}\r\n",
            "a=sendrecv\r\n",
        ),
        ip = answer.ip,
        rtp = answer.port,
        pt = answer.payload_type,
        codec = answer.codec
    );

    Some(
        SipResponseBuilder::new(code, reason)
            .header("Via", via)
            .header("From", from)
            .header("To", to)
            .header("Call-ID", call_id)
            .header("CSeq", cseq)
            .header("Contact", format!("sip:rustbot@{contact_ip}:{sip_port}"))
            .body(sdp.as_bytes(), Some("application/sdp"))
            .build(),
    )
}

SDP の中身(固定値)

  • v=0 / o=rustbot 1 1 IN IP4 {ip} / s=Rust PCMU Bot / c=IN IP4 {ip} / t=0 0
  • m=audio {rtp} RTP/AVP {pt}(pt は Sdp.payload_type、デフォルト 0)
  • a=rtpmap:{pt} {codec}codec"PCMU/8000" 固定)
  • a=sendrecv

オファーの扱い(受信側)

  • src/sip/mod.rsparse_offer_sdpc= の IPv4m=audio のポート/pt だけを抜き出し、Sdp に詰める。
  • 失敗/欠落時は Sdp::pcmu("0.0.0.0", 0) にフォールバックする。
  • 属性(a= 行)や fmtp/ptime は読まない、pt が無い場合は 0 を仮定。

オファーパースのコード

// src/sip/mod.rs:85-
fn parse_offer_sdp(body: &[u8]) -> Option<Sdp> {
    let s = std::str::from_utf8(body).ok()?;
    let mut ip = None;
    let mut port = None;
    let mut pt = None;
    for line in s.lines() {
        let line = line.trim();
        if line.starts_with("c=IN IP4 ") {
            let v = line.trim_start_matches("c=IN IP4 ").trim();
            ip = Some(v.to_string());
        } else if line.starts_with("m=audio ") {
            let cols: Vec<&str> = line.split_whitespace().collect();
            if cols.len() >= 4 {
                port = cols[1].parse::<u16>().ok();
                pt = cols[3].parse::<u8>().ok();
            }
        }
    }
    Some(Sdp {
        ip: ip?,
        port: port?,
        payload_type: pt.unwrap_or(0),
        codec: "PCMU/8000".to_string(),
    })
}

ローカル SDP を組むコード(session 側)

// src/session/session.rs:365-
fn build_answer_pcmu8k(&self) -> Sdp {
    // PCMU/8000 でローカル SDP を組み立て
    Sdp::pcmu(self.media_cfg.local_ip.clone(), self.media_cfg.local_port)
}

SDP 構造体

// src/session/types.rs:4-
#[derive(Clone, Debug)]
pub struct Sdp {
    pub ip: String,
    pub port: u16,
    pub payload_type: u8,
    pub codec: String, // e.g. "PCMU/8000"
}

impl Sdp {
    pub fn pcmu(ip: impl Into<String>, port: u16) -> Self {
        Self {
            ip: ip.into(),
            port,
            payload_type: 0,
            codec: "PCMU/8000".to_string(),
        }
    }
}

現状の割り切り / 次の宿題

  • コーデックは PCMU のみ、a=sendrecv 固定。Contactadvertised_ip:sip_port で組み立て。
  • オファーの属性は無視しているため、モノラル/ptime などは受け流し。複数 m 行や ICE/SRTP も未対応。
  • 今後の拡張案: 複数コーデック対応、fmtp/ptime の反映、a=recvonly/sendonly の尊重、ICE/SRTP/RTCP 周りの項目を追加。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?