Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/advertise-passthrough-formats.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
webrtc-sys: patch
libwebrtc: patch
livekit: patch
livekit-ffi: patch
---

Advertise pre-encoded pass-through video formats even when no local encoder implements the codec, so pre-encoded H.265 publishes negotiate on machines without a hardware HEVC encoder.
22 changes: 8 additions & 14 deletions webrtc-sys/src/video_encoder_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,25 +393,19 @@ VideoEncoderFactory::InternalFactory::GetSupportedFormats() const {
supported_formats.end());
}

// The pass-through factory would otherwise advertise codecs no real
// encoder implements (e.g. H265 on desktops); a normal session
// negotiating such a codec would end up with a sender that cannot create
// an encoder. Only advertise pass-through formats for codecs some real
// encoder already supports.
const size_t real_format_count = formats.size();
// Advertise pass-through formats unconditionally: a pre-encoded publisher
// supplies its own bitstream, so it must be able to negotiate codecs no
// local encoder implements (e.g. H265 on machines without a hardware
// encoder). The pass-through backend is never selected automatically (it
// is excluded from automatic fallback and only used on an explicit publish
// request), and Create() fails loudly rather than falling back when a
// pre-encoded sender cannot be built.
for (const auto& backend_factory : factories_) {
if (backend_factory.backend != VideoEncoderBackend::PreEncoded) {
continue;
}
for (const auto& format : backend_factory.factory->GetSupportedFormats()) {
const bool codec_available = std::any_of(
formats.begin(), formats.begin() + real_format_count,
[&](const webrtc::SdpVideoFormat& existing) {
return IsSameCodecName(existing.name, format.name);
});
if (codec_available) {
formats.push_back(format);
}
formats.push_back(format);
Comment on lines 407 to +408

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Raw senders negotiate unusable codecs

GetSupportedFormats advertises pass-through-only codecs to raw senders. Untagged encoder creation excludes PreEncoded, so negotiation succeeds but the sender cannot encode.

Learn more

Encoder capabilities apply to every sender created by this peer connection factory. They do not carry the sender's later VideoEncoderBackend choice. These added formats are also untagged, while Create deliberately excludes PreEncoded from automatic fallback. The factory therefore promises support for formats that ordinary raw senders cannot instantiate.

Example: On a desktop without HEVC encoding, the factory advertises H.265. A raw sender can negotiate H.265 from that capability, but untagged Create skips the pass-through factory and returns null instead of an encoder.

Recommended fix: Preserve pass-through-only codecs without exposing them as ordinary encoder capabilities. Associate them with explicit PreEncoded selection before codec negotiation, or add sender-level capability filtering so only pre-encoded publications advertise them. Keep raw sender capabilities limited to formats accepted by untagged Create.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

}
}
return formats;
Expand Down
Loading