TorchCodec: PyTorch собрал видео и звук в одну библиотеку

TorchCodec: PyTorch собрал видео и звук в одну библиотеку

Пятого октября команда PyTorch Team опубликовала разбор того, как за два года изменился медиа-стек фреймворка. Короткая версия: чтение и запись картинок, видео и аудио переехали в отдельную библиотеку TorchCodec, старые медиа-API из TorchVision и TorchAudio отправились на пенсию, и это тот редкий рефакторинг, который заканчивается не новыми слоями абстракций, а удалённым кодом. Если вам когда-нибудь приходилось пересобирать PyTorch из исходников только ради того, чтобы прочитать mp4 в даталоадере, у вас сегодня хороший день.

Раньше медиа-стек PyTorch был зоопарком из двух библиотек и полудюжины бэкендов, где никто до конца не понимал, какой декодер считать каноническим. Теперь роли разделены жёстко: у каждого типа задач есть ровно один адрес, и это меняет жизнь каждого проекта, где даталоадер читает видео или звук.

TorchCodec это библиотека чтения и записи медиа: она превращает файлы и байты в тензоры и обратно, покрывая изображения, видео и аудио, на CPU и на GPU. TorchVision и TorchAudio остались в стеке, но занимаются только трансформациями: ресайзом, кропом, мел-спектрограммами и всем остальным, что происходит с данными после декодирования.

Как выглядел старый медиа-стек

До консолидации всё было размазано. В TorchVision за чтение видео отвечали сразу две точки входа, io.read_video() и io.VideoReader(), и три бэкенда: PyAV на Python, C++-реализация на FFmpeg и CUDA-бэкенд на NVCUVID. Из коробки работал только PyAV, остальные приходилось собирать из исходников, причём каждый был привязан к конкретной версии FFmpeg. В TorchAudio жили StreamReader и StreamWriter плюс набор утилит декодирования с бэкендами на FFmpeg, libsoundfile и libsox. Декодирование картинок при этом оставалось в TorchVision.

Пользователю это давало классическую развилку: непонятно, какой API считать правильным и почему один файл читается, а другой молча падает. Разработчикам было не проще: когда декодеров пять, невозможно решить, какой из них ускорять. Усилия распылялись, а проблемы решались заново в каждом проекте.

И выигрыш от бэкенда зависел от машины: один читал файл на CPU, другой пытался задействовать GPU, третий требовал собранный из исходников FFmpeg. Один и тот же код показывал разную скорость на разных системах, и это считалось нормальным положением дел.

Почему это была настоящая боль

Причина, по которой всё это жило так долго, прячется в зависимостях. Медиа-I/O это C и C++ библиотеки: к моменту консолидации только FFmpeg существовал в шести мажорных версиях, с четвёртой по девятую, рядом NVIDIA codec SDK для декодирования на видеокартах и по отдельной библиотеке на каждый формат картинок, от libjpeg до libpng. У каждой свои правила лицензирования и свои ограничения на то, что можно включать в релиз.

Сборка, CI и релизы требовали постоянных усилий: каждая из этих зависимостей поставляется для десятка платформ, а каждая новая версия FFmpeg умножает матрицу проверок. Именно поэтому третьей причиной консолидации команда называет не скорость и не удобство, а обслуживание: держать всю сложность в одном пакете дешевле, чем поддерживать три копии одних и тех же решений.

На практике это выглядело так: сборки, собранные против одной версии FFmpeg, отказывались работать на машине с другой, а для GPU-декодирования приходилось вручную собирать PyTorch с нужными флагами и надеяться, что версии codec SDK и CUDA совпадут. Каждое обновление окружения превращалось в разминирование, а фраза «поставь правильный FFmpeg» стала частью фольклора.

И третий фактор, самый современный: декодирование перестало быть служебной задачей. Vision-language модели и видеодиффузия прогоняют через даталоадеры миллионы кадров, и узким местом всё чаще становится не счёт на GPU, а распаковка файлов, пока ускоритель ждёт данные. Каждый процент скорости декодера конвертируется в проценты использования железа, а значит экономить на этой части пайплайна больше нельзя.

Теперь за I/O отвечает TorchCodec

Путь занял два года: сначала появился сам TorchCodec, затем старые API пометили устаревшими, часть удалили, и только теперь всё дошло до ABI-стабильности. Опора здесь не на вкус, а на статистику использования: трансформации оказались самой востребованной частью обеих библиотек, поэтому в фокусе остались именно они.

Новая схема описывается одной фразой: нужно прочитать или записать медиа, идите в TorchCodec; нужно преобразовать тензоры, оставайтесь в TorchVision и TorchAudio. Вся сложность с FFmpeg и codec SDK теперь спрятана в одном пакете.

Как это выглядит в коде для видео. Декодер открывает файл и отдаёт кадры срезом, как элементы списка:

import torch
from torchcodec.decoders import VideoDecoder
from torchvision.transforms import v2

clip = VideoDecoder("video.mp4")[10:20]  # FrameBatch, uint8, (10, C, H, W)

transform = v2.Compose([
    v2.RandomResizedCrop(224),
    v2.ToDtype(torch.float32, scale=True),
])
batch = transform(clip.data)

Кадры приходят готовым тензором формы (кадры, каналы, высота, ширина) в uint8, и трансформации из torchvision.transforms.v2 работают с ним как с любым другим тензором. Декодер сам решает, что и когда распаковывать: возиться с буферами и позициями в файле больше не нужно.

Для аудио схема та же, только вместо кадров семплы:

from torchcodec.decoders import AudioDecoder
from torchaudio.transforms import MelSpectrogram

samples = AudioDecoder("audio.mp3").get_all_samples()  # AudioSamples

mel = MelSpectrogram(sample_rate=samples.sample_rate)(samples.data)

Картинки устроены аналогично: decode_image и родственные функции возвращают обычные тензоры для v2-трансформаций. В обратную сторону работают VideoEncoder, AudioEncoder, JpegEncoder и PngEncoder: генеративная модель пишет результат тем же пакетом, которым остальные читают обучающие данные.

Отдельный эффект централизации: у медиа-I/O появился владелец. Раньше вопрос «почему оно тормозит» растекался между двумя библиотеками и полудюжиной бэкендов, и починить проблему можно было не там, где её искали. Теперь есть один репозиторий, одна команда и один адрес для багрепортов и оптимизаций.

Кого это касается в первую очередь: всех, кто обучает модели на видео и звуке. Претрейн vision-language моделей, обучение видео-диффузии, речевые пайплайны: декодирование в таких проектах работает постоянно, и его скорость складывается в суммарное время обучения. Теперь у оптимизаций есть адрес: раньше неизвестно было, какой из бэкендов станет каноническим, и непонятно, куда вкладывать усилия, а сейчас всё, что ускоряют в TorchCodec, ускоряется у всех пользователей сразу. По словам команды, пакет в целом быстрее старых реализаций, а самый заметный выигрыш там, где он нужнее всего: в декодировании видео на CUDA.

TorchVision и TorchAudio: меньше, но честнее

Обе библиотеки сузили фокус до трансформаций, потому что именно они остаются самой используемой частью сообщества. Модели, датасеты и готовые пайплайны уходят в режим без активной разработки: их давно заменяют библиотеки экосистемы, в первую очередь семейство HuggingFace, и поддерживать собственные копии стало бессмысленно.

Дороже всего перестройка далась TorchAudio. Оттуда удалили большой пласт API, и это вызвало предсказуемое недовольство. Команда пошла навстречу: план скорректировали, а часть популярных интерфейсов, которые собирались выпилить, оставили по итогам обратной связи. В посте это описано без бравады: переход был тяжёлым, но меньшая библиотека это та, которую реально поддерживать, а значит на неё можно продолжать полагаться.

Полезно проговорить и то, чего в новой картине не будет. Новые модели и датасеты внутри TorchVision больше не появятся: эта работа уходит в профильные проекты экосистемы. Зато трансформации продолжают развиваться, и именно они остаются причиной держать обе библиотеки в зависимостях.

ABI-стабильность: версии больше не связаны

Все три библиотеки стали ABI-стабильными. Практический смысл: конкретная версия TorchCodec, TorchVision или TorchAudio больше не привязана к одной версии PyTorch. Библиотека продолжает работать с последующими релизами фреймворка, её не нужно пересобирать под каждый выпуск, а собственный релизный цикл может расходиться с циклом PyTorch. В посте это отдельно проговаривают: так и должно быть.

Раньше матрица совместимости напоминала таблицу умножения: версию torchvision приходилось подбирать под версию torch, а несовпадение лечилось пересборкой или откатом. Для проектов, которые просто хотят обновлять зависимости, это было постоянным налогом. ABI-стабильность не добавляет фич, зато убирает целый класс поломок, которые раньше прилетали при каждом апгрейде.

Как переехать на TorchCodec

Если ваш код читает медиа через старые API, миграция сводится к нескольким шагам. Найдите в проекте torchvision.io.read_video() и VideoReader(), а также StreamReader из TorchAudio: это и есть точки переезда. Дальше сверьтесь с официальным migration guide в репозитории TorchCodec: типовые замены ложатся один в один, а нарезка кадров через срез закрывает большинство сценариев, ради которых раньше писались ручные циклы чтения.

Раньше можно было не спешить, теперь у ожидания есть цена: старые интерфейсы не получают ни оптимизаций, ни гарантий поддержки, и каждый релиз может забрать ещё один вызов. Если для ваших форматов чего-то не хватает, команда просит открывать issue на GitHub. Судя по истории TorchAudio, это не формальность: обратная связь сообщества там действительно меняла решения. Переезд стоит планировать как обычную задачу по техническому долгу, а не как срочный пожар: интерфейсы ещё работают, но их час уже известен.

Что это значит на длинной дистанции

За консолидацией медиа-стека видно общую линию: PyTorch аккуратно очерчивает свои границы. Ядро, то есть тензоры, трансформации и медиа-I/O, остаётся внутри, модели и датасеты отдаются экосистеме, а тяжёлые нативные зависимости стягиваются в один контролируемый пакет. Для тех, кто обучает мультимодальные модели, выгода измеримая: меньше кода в поддержке, меньше несовместимых сборок и один владелец у проблем с декодированием.

Есть в этой истории и прагматичный урок для соседних экосистем: консолидация выигрывает не когда выглядит красиво, а когда убирает конкретную боль. Декодеры не стали хуже от того, что их было два набора, но стали поддерживаемее, когда набор остался один. Так выглядит взросление инфраструктуры: не новые слои поверх старых, а снос дублей и честные границы.

Команда обещает продолжение: следующий материал будет про то, что нового появилось в каждой из трёх библиотек и как выжать максимум из полного конвейера декодирования и трансформаций.

Вопросы и ответы

Полностью ли TorchCodec заменяет TorchVision и TorchAudio?

Нет. TorchCodec отвечает только за чтение и запись медиа, то есть за превращение файлов в тензоры и обратно. Трансформации остаются в TorchVision и TorchAudio: там живут v2-преобразования для картинок и видео и обработка звука вроде мел-спектрограмм.

Мой код использует torchvision.io.read_video(). Что делать?

Эти API помечены как устаревшие, а часть уже удалена, поэтому миграция это вопрос времени. Штатная замена: VideoDecoder из TorchCodec, который открывает файл и отдаёт кадры индексированием. Пошаговые соответствия собраны в официальном migration guide, и обычно переезд занимает меньше времени, чем его откладывание.

Останется ли TorchAudio для обработки звука?

Да, TorchAudio никуда не исчезает: его трансформации и обработка аудио это одна из двух рабочих зон библиотеки. Ушла только та часть, которая дублировала чтение и запись файлов, теперь этим занимается TorchCodec.

TorchCodec умеет декодировать видео на GPU?

Да, CUDA-декодирование это часть публичного API. Раньше NVCUVID-путь требовал ручной сборки из исходников, теперь он живёт в обычном пакете и ставится вместе с TorchCodec.

Зачем мне ABI-стабильность, если я просто ставлю пакеты через pip?

Она экономит самый неприятный вид отладки, разбор несовместимых версий. Раньше внутренние связи между PyTorch и его библиотеками требовали, чтобы версии совпадали, и обновление фреймворка тянуло за собой пересборки. Теперь TorchCodec, TorchVision и TorchAudio совместимы с последующими версиями PyTorch, а не только с той, под которую были собраны.

Итог

Два года рефакторинга закончились простым договором: файлы читает и пишет TorchCodec, тензоры преобразуют TorchVision и TorchAudio, а сложность FFmpeg и codec SDK спрятана за одним стабильным интерфейсом. Откройте свой даталоадер и посмотрите, как он читает медиа. Если там всё ещё живут старые I/O-вызовы, у TorchCodec есть гайд по миграции, а лучшее время для переезда это момент, пока старые API ещё существуют.

← Все записи