Skip to content

Add GCC-managed encoded Opus audio track#160

Open
lkang-nuro wants to merge 1 commit into
mainfrom
feat/gcc-managed-opus-audio
Open

Add GCC-managed encoded Opus audio track#160
lkang-nuro wants to merge 1 commit into
mainfrom
feat/gcc-managed-opus-audio

Conversation

@lkang-nuro

Copy link
Copy Markdown
Contributor

Context

The audio stream is effectively fixed bitrate: audio is pre-encoded outside the sender and pushed as a passive track via AddAudioTrack, so the GCC bandwidth estimator — which dynamically drives video bitrate — never touches it. When the network degrades, video scales down but audio holds its bandwidth.

This makes audio a first-class, GCC-managed encoded Opus track inside the sender, so the same control loop (updateBitrateupdateEncoderBitrate) that drives VP8 also drives Opus, sharing the GCC bitrate pool. Callers push raw PCM instead of pre-encoded Opus.

The change is additive: the legacy passive AddAudioTrack path is left untouched.

Changes

  • Generalize EncodedTrack to carry audio (isAudio, audioTrack, audioSource); the existing local track and encodedReader/bitrateTracker/mimeType are shared by both kinds.
  • AddEncodedAudioTrack(trackID, opus.Params) — builds the Opus codec selector, AudioBuffer source, mediadevices AudioTrack, and encoded reader; registers the track in s.tracks so it flows through GCC allocation.
  • SendAudioFrame(trackID, pcm, sampleRate, channels) — pushes raw interleaved PCM.
  • New sender/audio_buffer.go (AudioBuffer) — the audio counterpart to FrameBuffer: same initialized-gate non-blocking Read and drop-oldest bounded queue.
  • updateEncoderBitrate gains a codec.BitRateController branch for Opus, clamped to [8000, 32000] bps.
  • processEncodedFrames uses a 20 ms duration for audio and skips the VP8 keyframe sniff; ForceKeyFrame/recreateEncoder/Close guard on isAudio.
  • Extracted attachTrackToPeerConnection helper (de-dupes AddVideoTrack).

Testing

go build ./..., go vet ./..., golangci-lint run ./... (0 issues), and go test ./... all pass. New tests in sender/audio_buffer_test.go cover buffer behavior, track creation/validation, a full PCM→Opus encode path, and the GCC bitrate-driven path.

Note

Downstream consumer wiring (cgo PushAudioPCM export, C++ raw-PCM push, Bazel .so re-pin) lives in a separate repo and is out of scope here. For GCC to genuinely drive audio, the encoded audio track must be AddTrack'd on the PeerConnection this sender owns so transport-cc feedback reaches the estimator.

🤖 Generated with Claude Code

Move Opus audio encoding into the sender so the GCC bandwidth estimator
drives audio bitrate alongside video, sharing the bitrate pool. Audio
was previously pre-encoded and pushed as a passive track that GCC could
not touch.

- Generalize EncodedTrack to carry audio (isAudio, audioTrack,
  audioSource); the existing local track and encodedReader/bitrateTracker
  are shared by both kinds.
- New AddEncodedAudioTrack(trackID, opus.Params): builds the Opus codec
  selector, AudioBuffer source, mediadevices AudioTrack, and encoded
  reader, and registers the track in s.tracks so it flows through GCC
  allocation.
- New SendAudioFrame to push raw interleaved PCM.
- New sender/audio_buffer.go (AudioBuffer): the audio counterpart to
  FrameBuffer, with the same initialized-gate non-blocking Read and
  drop-oldest bounded queue.
- updateEncoderBitrate gains a codec.BitRateController branch for Opus,
  clamped to [8000, 32000] bps.
- processEncodedFrames uses a 20ms duration for audio and skips the
  VP8 keyframe sniff; ForceKeyFrame/recreateEncoder/Close guard on
  isAudio.
- Extract attachTrackToPeerConnection helper (de-dupes AddVideoTrack).

The legacy passive AddAudioTrack path is left untouched (additive).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant