DrKLO/Telegram · error · OpusDecoderException
Opus decoder does not support secure decode
Error message
Opus decoder does not support secure decode
What it means
Thrown by the OpusDecoder constructor when a non-null CryptoConfig is supplied (i.e. the stream is encrypted/DRM-protected) but the loaded native libopus does not advertise secure-decode support. The check is OpusLibrary.opusIsSecureDecodeSupported(), a JNI call into the bundled native library; it only returns true if that native build was compiled with the secure-decode entry point. The decoder refuses to start because it cannot decrypt samples without it.
Source
Thrown at TMessagesProj/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java:89
* May be null and can be ignored if decoder does not handle encrypted content.
* @param outputFloat Forces the decoder to output float PCM samples when set
* @throws OpusDecoderException Thrown if an exception occurs when initializing the decoder.
*/
public OpusDecoder(
int numInputBuffers,
int numOutputBuffers,
int initialInputBufferSize,
List<byte[]> initializationData,
@Nullable CryptoConfig cryptoConfig,
boolean outputFloat)
throws OpusDecoderException {
super(new DecoderInputBuffer[numInputBuffers], new SimpleDecoderOutputBuffer[numOutputBuffers]);
if (!OpusLibrary.isAvailable()) {
throw new OpusDecoderException("Failed to load decoder native libraries");
}
this.cryptoConfig = cryptoConfig;
if (cryptoConfig != null && !OpusLibrary.opusIsSecureDecodeSupported()) {
throw new OpusDecoderException("Opus decoder does not support secure decode");
}
int initializationDataSize = initializationData.size();
if (initializationDataSize != 1 && initializationDataSize != 3) {
throw new OpusDecoderException("Invalid initialization data size");
}
if (initializationDataSize == 3
&& (initializationData.get(1).length != 8 || initializationData.get(2).length != 8)) {
throw new OpusDecoderException("Invalid pre-skip or seek pre-roll");
}
preSkipSamples = getPreSkipSamples(initializationData);
seekPreRollSamples = getSeekPreRollSamples(initializationData);
skipSamples = preSkipSamples;
byte[] headerBytes = initializationData.get(0);
if (headerBytes.length < 19) {
throw new OpusDecoderException("Invalid header length");
}
channelCount = getChannelCount(headerBytes);View on GitHub (pinned to 45ab8f4308)
Solutions
- Use a libopus native build compiled with the secure-decode JNI entry point so OpusLibrary.opusIsSecureDecodeSupported() returns true.
- If the content is not actually encrypted, ensure cryptoConfig passed to the decoder is null (do not forward a CryptoConfig for clear content).
- Fall back to another audio renderer/decoder whose native build supports the required C.CryptoType; gate playback on OpusLibrary.supportsCryptoType(format.cryptoType).
Example fix
// before
OpusDecoder decoder = new OpusDecoder(
NUM_BUFFERS, NUM_BUFFERS, initialInputBufferSize,
format.initializationData, cryptoConfig, outputFloat);
// after
if (cryptoConfig != null && !OpusLibrary.opusIsSecureDecodeSupported()) {
throw new OpusDecoderException("Secure decode unavailable; pick another renderer");
}
OpusDecoder decoder = new OpusDecoder(
NUM_BUFFERS, NUM_BUFFERS, initialInputBufferSize,
format.initializationData, cryptoConfig, outputFloat); Defensive patterns
Strategy: validation
Validate before calling
// Before constructing the OpusDecoder for encrypted content
CryptoConfig cfg = (formatIsEncrypted) ? cryptoConfig : null;
if (cfg != null
&& !(OpusLibrary.isAvailable() && OpusLibrary.opusIsSecureDecodeSupported())) {
// Do not pass a CryptoConfig: route to a renderer whose native lib supports it.
throw new OpusDecoderException("Secure Opus decode not available in this native build");
}
new OpusDecoder(NUM_BUFFERS, NUM_BUFFERS, initSize, format.initializationData, cfg, outputFloat); Try / catch
try {
decoder = new OpusDecoder(..., cryptoConfig, outputFloat);
} catch (OpusDecoderException e) {
if (e.getMessage().contains("secure decode")) {
// switch renderer / report DRM unsupported for Opus
} else { throw e; }
} Prevention
- Gate encrypted Opus playback on OpusLibrary.supportsCryptoType(format.cryptoType).
- Ship a libopus native build compiled with secure decode when DRM Opus playback is required.
- Only forward a non-null CryptoConfig when the content is actually encrypted.
When it happens
Trigger: LibopusAudioRenderer.createDecoder (LibopusAudioRenderer.java:110) forwards the Format's cryptoConfig into `new OpusDecoder(...)`. When the stream is DRM-protected (Widevine/ClearKey) cryptoConfig is non-null and the constructor at OpusDecoder.java:88 aborts if the native opusIsSecureDecodeSupported() returns false.
Common situations: Shipping the standard non-DRM libopus extension build and then attempting to play ClearKey/Widevine-protected Opus content; swapping in a different decoder extension variant that dropped secure decode; DRM scheme mismatch where OpusLibrary.cryptoType was never configured via OpusLibrary.setLibraries(...).
Related errors
- Failed to initialize decoder
- Invalid initialization data size
- Invalid pre-skip or seek pre-roll
- Invalid header length
- Invalid channel count: {channelCount}
AI-assisted analysis of DrKLO/Telegram@45ab8f4308 (2026-08-14).
Data as JSON: /api/errors/11ab05af30cfb9dc.
Report an issue: GitHub.