[PR #238] [CLOSED] Use libdatachannel RTP packetizer for OPUS in WHIP stream #240

Closed
opened 2026-03-02 04:00:53 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/eerimoq/moblin/pull/238
Author: @Copilot
Created: 2/14/2026
Status: Closed

Base: mainHead: copilot/use-rtp-packetizer-for-opus


📝 Commits (2)

  • f20f097 Initial plan
  • 38b58d8 Use libdatachannel RTP packetizer for OPUS in WHIP stream with NACK and SR handling

📊 Changes

1 file changed (+37 additions, -91 deletions)

View changed files

📝 Moblin/Media/HaishinKit/Whip/WhipStream.swift (+37 -91)

📄 Description

Opus audio in the WHIP stream was using a custom OpusPacketizer that manually constructed RTP packets, with no NACK or Sender Report support. Video already used libdatachannel's built-in packetizer with both.

  • Replace custom packetizer with rtcSetOpusPacketizer — delegates RTP header construction, sequence numbering, and timestamp management to libdatachannel (48kHz clock rate)
  • Register rtcChainRtcpSrReporter and rtcChainRtcpNackResponder on the audio track — enables lost packet retransmission and sender reports, matching video track behavior
  • Send raw Opus frames via setTimestamp + send(packet:), same pattern as H264
  • Remove OpusPacketizer, RtpPacket, convertTimestamp — no longer needed
// Before: packetizer/NACK/SR only for video
if config.isVideo() {
    try checkOk(rtcSetH264Packetizer(trackId, &packetizerInit))
    try checkOk(rtcChainRtcpSrReporter(trackId))
    try checkOk(rtcChainRtcpNackResponder(trackId, nackMaxStoredPacketCount))
}

// After: packetizer per codec, NACK/SR for all tracks
if config.isVideo() {
    packetizerInit.clockRate = 90000
    try checkOk(rtcSetH264Packetizer(trackId, &packetizerInit))
} else {
    packetizerInit.clockRate = 48000
    try checkOk(rtcSetOpusPacketizer(trackId, &packetizerInit))
}
try checkOk(rtcChainRtcpSrReporter(trackId))
try checkOk(rtcChainRtcpNackResponder(trackId, nackMaxStoredPacketCount))

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/eerimoq/moblin/pull/238 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 2/14/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `copilot/use-rtp-packetizer-for-opus` --- ### 📝 Commits (2) - [`f20f097`](https://github.com/eerimoq/moblin/commit/f20f0979ca30521e6529f25a1b424d31ab8567ea) Initial plan - [`38b58d8`](https://github.com/eerimoq/moblin/commit/38b58d8ad0b24bbcd438e4d807a8a9416248e599) Use libdatachannel RTP packetizer for OPUS in WHIP stream with NACK and SR handling ### 📊 Changes **1 file changed** (+37 additions, -91 deletions) <details> <summary>View changed files</summary> 📝 `Moblin/Media/HaishinKit/Whip/WhipStream.swift` (+37 -91) </details> ### 📄 Description Opus audio in the WHIP stream was using a custom `OpusPacketizer` that manually constructed RTP packets, with no NACK or Sender Report support. Video already used libdatachannel's built-in packetizer with both. - **Replace custom packetizer with `rtcSetOpusPacketizer`** — delegates RTP header construction, sequence numbering, and timestamp management to libdatachannel (48kHz clock rate) - **Register `rtcChainRtcpSrReporter` and `rtcChainRtcpNackResponder` on the audio track** — enables lost packet retransmission and sender reports, matching video track behavior - **Send raw Opus frames** via `setTimestamp` + `send(packet:)`, same pattern as H264 - **Remove `OpusPacketizer`, `RtpPacket`, `convertTimestamp`** — no longer needed ```swift // Before: packetizer/NACK/SR only for video if config.isVideo() { try checkOk(rtcSetH264Packetizer(trackId, &packetizerInit)) try checkOk(rtcChainRtcpSrReporter(trackId)) try checkOk(rtcChainRtcpNackResponder(trackId, nackMaxStoredPacketCount)) } // After: packetizer per codec, NACK/SR for all tracks if config.isVideo() { packetizerInit.clockRate = 90000 try checkOk(rtcSetH264Packetizer(trackId, &packetizerInit)) } else { packetizerInit.clockRate = 48000 try checkOk(rtcSetOpusPacketizer(trackId, &packetizerInit)) } try checkOk(rtcChainRtcpSrReporter(trackId)) try checkOk(rtcChainRtcpNackResponder(trackId, nackMaxStoredPacketCount)) ``` <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem closed this issue 2026-03-02 04:00:53 +03:00
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/moblin#240
No description provided.