Skip to content

Video: play stereo audio as stereo - #2399

Open
dallo28 wants to merge 1 commit into
TelegramMessenger:masterfrom
dallo28:fix/video-stereo-playback
Open

dallo28 wants to merge 1 commit into
TelegramMessenger:masterfrom
dallo28:fix/video-stereo-playback

Conversation

@dallo28

@dallo28 dallo28 commented Oct 25, 2026

Copy link
Copy Markdown

Fixes https://bugs.telegram.org/c/66408: videos with stereo audio play in mono in the app, with the same signal in both ears. The same video plays in stereo in Telegram Web, on Android and desktop, and on iOS after it is saved to Photos.

Cause

The chunk player decodes audio with FFmpeg through one of two readers, and both asked for a single output channel:

  • FFMpegFileReader (the V2 reader): FFMpegAudioFrameDecoder(codecContext:, sampleRate: 48000, channelCount: 1)
  • SoftwareAudioSource (the V1 reader): the same call

So the resampler mixed left and right into one channel for every video. The decoder also tagged its output kAudioChannelLayoutTag_Mono whatever the channel count, and created each sample buffer with sampleCount: data.count / 2, which is the frame count only for 16-bit mono.

Change

  • FFMpegAudioFrameDecoder.playbackChannelCount(codecContext:): 2 for a track with two or more channels, 1 for a mono track. Both readers use it.
  • The output format gets the matching layout tag (Mono or Stereo), and each sample buffer gets data.count / (2 * channelCount) samples. That also fixes the legacy player (FFMpegMediaFrameSourceContext), which already decoded to stereo and so got buffers reporting twice their real length.
  • SoftwareAudioSource takes isPlayback. MediaDataReader passes true; the public init(path:) keeps mono output for convertOpusToAAC, whose encoder is set up for one channel.

Multichannel tracks (5.1 and so on) are downmixed to stereo by the resampler, as they were downmixed to mono before.

Testing

iOS 27 simulator, debug build, playing https://t.me/jingluoasmr/36373 in the chat's player. The simulator's audio output can't be recorded, so a temporary log in the decoder printed the RMS of each output channel and of their sum and difference once per second (removed before commit):

Build Output channels Per-second levels
This change 2 Left and right differ: e.g. L 308 / R 14, L 27 / R 96; the difference (side) is often as strong as the sum (mid)
Same build with the output forced to 1 channel (the old behaviour) 1 One channel, no side signal

Not tested on a device or with the legacy player.

The chunk player's audio readers (FFMpegFileReader for the V2 reader,
SoftwareAudioSource for V1) decoded every track to one channel, so
videos played in mono in the app: both ears got the same signal, while
the saved file played in stereo in Photos and elsewhere.

- Decode a track with two or more channels to stereo, and a mono track
  to mono (FFMpegAudioFrameDecoder.playbackChannelCount).
- Give the output the matching channel layout tag (it was always Mono)
  and a sample count of bytes / (2 * channels) (it was bytes / 2, right
  for mono only, and twice the real length for the legacy player's
  stereo output).
- Voice message conversion (convertOpusToAAC) still decodes to mono,
  which is what its encoder expects.

Fixes https://bugs.telegram.org/c/66408
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant