Skip to content

VP8 publishing silently produces no frames on Samsung Exynos devices (c2.exynos.vp8.encoder is allocated but never starts) #1029

Description

@vjoomens

Summary

On Samsung devices with Samsung's own SoC, publishing the default VP8 video
track results in zero frames reaching other participants. The call
connects, the camera opens, the track is published and stays published, and
the encoder is allocated — but it never starts, and no output buffer is ever
produced. Nothing fails, nothing throws: the publisher simply cannot be seen.

The Qualcomm build of the same phone model works.

Environment

  • io.livekit:livekit-android 2.26.1 (webrtc-sdk android-prefixed 144.7559.05)
  • Devices: Galaxy S24+ (e2s) and Galaxy S24 (e1s), both Exynos, Android 16 (API 36)
  • Counter-example: Galaxy S26+ (m2q, Qualcomm), Android 16 — works
  • All three are physical devices in Firebase Test Lab, same APK, same test, same room

What we see

Publishing succeeds:

LK joined room <redacted>
LK setCameraEnabled(true) ok, publication TR_VC…

The encoder is then allocated and configured, and stops there:

I CCodec  : allocate(c2.exynos.vp8.encoder)
I CCodec  : Created component [c2.exynos.vp8.encoder]
I CCodec  : [c2.exynos.vp8.encoder] state->set(ALLOCATED)
I CCodec  : [c2.exynos.vp8.encoder] config->initialize() : 0
D CCodec  : [c2.exynos.vp8.encoder] buffers are bound to CCodec for this session

There is no state->set(STARTING), no state->set(RUNNING), and no
CCodecBufferChannel input/output buffer line — compare an ordinary codec in
the same logcat, which goes ALLOCATED → STARTING → RUNNING with buffers.
The remote peer (a browser) receives 0 frames for the entire call.

On the receiving side we have measured the mirror image of this on a Galaxy
S26+ with Exynos (SM-S947B): subscribed VP8, packetsReceived 1483,
framesReceived 221, framesDecoded 0, decoderImplementation null.
The same chip family, both directions.

Why we think WebRTC picks that encoder at all

HardwareVideoEncoderFactory.isHardwareSupportedInCurrentSdkVp8() still only
accepts the OMX names:

return (name.startsWith(QCOM_PREFIX)  && SDK_INT >= KITKAT)
    || (name.startsWith(EXYNOS_PREFIX) && SDK_INT >= M)
    || (name.startsWith(INTEL_PREFIX)  && SDK_INT >= LOLLIPOP && enableIntelVp8Encoder);

No c2. prefix, and no MediaCodecInfo.isHardwareAccelerated() — unchanged
in upstream HEAD, while Android moved to Codec2 in 12. Our hypothesis is that
Samsung still exposes an OMX.Exynos.… alias for the Codec2 component, so a
check written for the OMX encoders of 2016 ends up selecting a codec it was
never validated against. We could not confirm the alias from logcat, so this
part is inference — the behaviour above is not.

Workaround

CustomVideoEncoderFactory already has exactly the right escape hatch, and
its default already distrusts hardware VP9:

private var forceSWCodecs: List<String> = listOf("VP9")

Passing listOf("VP8", "VP9") through LiveKitOverrides(videoEncoderFactory = …)
resolves it for us. (Note the factory has to be built lazily: its constructor
reaches into libwebrtc, which is only loaded while the room is built.)

Suggestion

Either add VP8 to that default on affected chipsets, or gate hardware VP8 on
something that reflects the Codec2 era (isHardwareAccelerated(), or a name
check that does not rely on OMX prefixes). At the very least it may be worth
documenting setForceSWCodecList as the remedy for "publisher is connected
but nobody can see them" on Samsung Exynos.

Happy to run any patch or extra logging on these exact devices — we have them
reproducible in Test Lab.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions