Zum Hauptinhalt springen
YouTube Security VideosIBM Technology: AI Agents Need More Than Code. Here's Why.(18.09.2026 um 18:00 Uhr)
YouTube Security VideosGoogle Chrome: Batman Day is here. Choose your Chrome Theme.(18.09.2026 um 18:04 Uhr)
YouTube Security VideosGolemDE: Ist das die Zukunft des Lenkrades?(18.09.2026 um 18:01 Uhr)
YouTube Security VideosIBM Technology: AI Agents Need More Than Code. Here's Why.(18.09.2026 um 18:00 Uhr)
YouTube Security VideosGoogle Chrome: Batman Day is here. Choose your Chrome Theme.(18.09.2026 um 18:04 Uhr)
YouTube Security VideosGolemDE: Ist das die Zukunft des Lenkrades?(18.09.2026 um 18:01 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Fixing iPhone HEVC for Telegram Video Avatars With ffmpeg

Why iPhone Videos Silently Fail as Telegram Avatars

Set an iPhone video as your Telegram video profile picture and you get... nothing. No error. Telegram accepts the upload, spins, then the avatar doesn't change. The video plays fine on your phone. It looks sharp. Telegram just quietly rejects it.

The culprit is HEVC (H.265). iPhones have recorded in HEVC by default since iOS 11. Telegram's video avatar spec requires H.264. Telegram doesn't surface a codec error, so users have no idea what went wrong. They assume the feature is broken.

I ran into this while building a small side-project. The fix is about 80 lines of Python wrapping ffmpeg.

What Telegram's Video Avatar Spec Requires

The Bot API documents this, but it's scattered across a few pages:

  • Codec: H.264 (libx264). Not H.265, VP9, or AV1.
  • Resolution: exactly 800x800 pixels.
  • Duration: 10 seconds maximum.
  • File size: 2 MB maximum.
  • Audio: must be absent. Telegram strips it server-side, but removing it saves bytes.
  • Container: MP4 with moov atom at the front (faststart flag).
  • Pixel format: yuv420p for broad decoder compatibility.

The 800x800 square crop is the hardest constraint. Phone videos are 16:9 or 9:16. You can't just scale, you need to crop to a square first.

The ffmpeg Pipeline

ffmpeg handles everything: crop to square, scale to 800x800, convert pixel format, encode H.264, strip audio, enforce duration.

For video files (including HEVC from iPhone):

ffmpeg -y -i input.mov \
  -vf "crop=min(iw\,ih):min(iw\,ih):0:(ih-iw)/2,scale=800:800,format=yuv420p" \
  -c:v libx264 -crf 28 -preset fast \
  -movflags +faststart \
  -an -t 10 \
  output.mp4

crop=min(iw,ih):min(iw,ih) takes the largest centered square. For a 1080x1920 portrait video, that's a 1080x1080 crop from center. -an removes the audio track entirely. -t 10 hard-stops at 10 seconds.

For GIFs, which lack a proper frame rate, you need one extra filter:

ffmpeg -y -i input.gif \
  -vf "fps=15,crop=min(iw\,ih):min(iw\,ih):0:0,scale=800:800,format=yuv420p" \
  -c:v libx264 -crf 28 -preset fast \
  -movflags +faststart \
  -an \
  output.mp4

fps=15 normalizes GIF frame timing before cropping. Without it, variable-framerate GIFs encode at wrong speeds.

CRF 28 gives acceptable quality and usually lands under 2 MB for a 10-second 800x800 clip. If it doesn't, retry logic tries CRF 32, then CRF 36, before giving up and telling the user to send a shorter clip.

The aiogram 3 Handler

The bot uses aiogram 3.x in webhook mode. The handler accepts video, animation (GIF), and document messages, then runs ffmpeg asynchronously via asyncio.create_subprocess_exec so the event loop stays unblocked.

from aiogram import Router, F
from aiogram.types import Message, BufferedInputFile
import asyncio, tempfile, os

router = Router()

@router.message(F.video | F.document | F.animation)
async def handle_video(message: Message):
    await message.answer("Converting, give me a second...")

    file_id = (
        message.video.file_id if message.video
        else message.animation.file_id if message.animation
        else message.document.file_id
    )

    with tempfile.TemporaryDirectory() as tmp:
        src = os.path.join(tmp, "input")
        dst = os.path.join(tmp, "avatar.mp4")

        await message.bot.download(file_id, destination=src)

        proc = await asyncio.create_subprocess_exec(
            "ffmpeg", "-y", "-i", src,
            "-vf", "crop=min(iw\\,ih):min(iw\\,ih),scale=800:800,format=yuv420p",
            "-c:v", "libx264", "-crf", "28", "-preset", "fast",
            "-movflags", "+faststart", "-an", "-t", "10",
            dst,
            stderr=asyncio.subprocess.PIPE,
        )
        _, stderr = await proc.communicate()

        if proc.returncode != 0:
            await message.answer("ffmpeg failed. Is this a valid video file?")
            return

        if os.path.getsize(dst) > 2 * 1024 * 1024:
            await message.answer("Over 2 MB after conversion. Try a shorter clip.")
            return

        with open(dst, "rb") as f:
            await message.answer_video(
                BufferedInputFile(f.read(), filename="avatar.mp4"),
                caption="Done. Set this as your Telegram video avatar in profile settings.",
            )

For 104 users, running ffmpeg jobs directly in the event loop is fine. At higher concurrency you'd want a worker pool with a job cap.

Shipping It and the Edge Cases I Didn't Expect

I packaged this as @liveavabot, running on a Hetzner VPS with systemd and nginx. Stateless per-request, no database needed for the core conversion flow.

A few things surprised me along the way.

Videos sent as video vs. document. When you send a video normally in Telegram, Telegram's servers transcode it before your bot ever sees it. HEVC becomes H.264 automatically. Problem solved? Not quite. Telegram's transcoding scales to around 640px wide, not 800x800. The bot then has to upscale, which hurts quality. If you send the file as a document, it arrives untouched. I added a note in /start: send as a file for best quality.

Portrait vs. landscape. The center square crop works for both orientations, but portrait videos (9:16) lose the sides and landscape (16:9) loses the top and bottom. For videos with important content near the edges, users get surprised. There's no great solution without a trim UI, which I haven't built yet.

GIFs with solid borders. Some meme GIFs have white or black letterbox borders baked in. The simple center crop handles this poorly. I experimented with ffmpeg's cropdetect filter to find the content bounds automatically, but it added around 500ms latency per file. I kept the simple crop and called it a known limit in the docs.

The 2 MB wall. A dense 10-second animated GIF at 800x800 can exceed 2 MB even at CRF 28. The retry logic at CRF 32 and CRF 36 catches most of these. If it still doesn't fit, I tell the user to send a clip under 6 seconds. That covers the remaining cases.

The bot is live at https://t.me/LiveAvaBot?start=devto_article_20260603. Currently 104 users, all organic.

Built by me: @liveavabot

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Fixing iPhone HEVC for Telegram Video Avatars With ffmpeg

Thematisch verwandte Begriffe: Fixing, iPhone, HEVC, Telegram · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-61591 | djust provides Phoenix LiveView-style reactive server-side rendering for…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Rechts: Artikel Ziehen Links: RSS
Hoch: nächster Artikel Runter: zurück / schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Rechts: Original Links: RSS-Ansicht
↗ Original-Quelle
Social Reaktionen Stimme abgeben (+5 Karma)
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick