Реализация обрезки и редактирования видео в мобильном приложении
Видеоредактор в мобильном приложении — одна из самых трудозатратных медиазадач. Не потому что API сложный, а потому что пользователь ждёт отзывчивости: таймлайн должен скролиться плавно, превью после обрезки — показываться мгновенно, а финальный экспорт — не блокировать UI на 30 секунд.
Ключевые операции и их реализация
Обрезка по времени (trimming). На iOS — AVAssetExportSession с timeRange:
exportSession.timeRange = CMTimeRange(
start: CMTime(seconds: startTime, preferredTimescale: 600),
duration: CMTime(seconds: duration, preferredTimescale: 600)
)
preferredTimescale: 600 — стандарт для видео, кратен 24/25/30/60 fps. Использование timescale: 1 даёт ошибки округления на фреймах.
На Android — MediaMuxer + MediaExtractor для frame-accurate trimming без перекодирования (только если совпадают ключевые кадры). Если нет — Transformer из media3 с явным перекодированием.
Объединение клипов. iOS: AVMutableComposition — добавляем AVMutableCompositionTrack для видео и аудио, вставляем через insertTimeRange(_:of:at:). Важно: аудиодорожки объединяются отдельно от видеодорожек, иначе рассинхронизация.
Android: media3 Transformer с Composition API (1.2.x+). Последовательность клипов — EditedMediaItemSequence.
Текстовые оверлеи. iOS: AVVideoComposition + AVVideoCompositionCoreAnimationTool. Создаём CATextLayer, добавляем анимацию появления через CABasicAnimation, передаём в animationTool. На Android — OverlayEffect в media3 Transformer или рисуем через Canvas на каждом кадре (медленно, только для короткого контента).
Таймлайн-превью
Самая заметная пользователю деталь — полоска с кадрами видео под плеером. Генерируем через AVAssetImageGenerator на iOS:
generator.generateCGImagesAsynchronously(forTimes: times) { _, image, _, _, _ in
// обновляем UI на главном потоке
}
requestedTimeToleranceBefore/After = .zero даёт точные кадры, но медленнее. Для таймлайна достаточно CMTime(seconds: 0.1, preferredTimescale: 600) — кадры ближайшие к отметке.
На Android — MediaMetadataRetriever.getFrameAtTime() в IO Dispatcher.
Экспорт
Всегда асинхронно, с прогрессом. iOS: exportSession.progress через Timer каждые 0.1 с. Android: Transformer.addListener с onProgress(progress: Float).
Формат вывода: H.264 в MP4 — максимальная совместимость. H.265 (HEVC) — меньше размер, но не все серверы принимают без перекодирования.
| Пресет iOS | Разрешение | Битрейт (прим.) |
|---|---|---|
AVAssetExportPreset640x480 |
640×480 | ~1.5 Мбит/с |
AVAssetExportPreset1280x720 |
1280×720 | ~5 Мбит/с |
AVAssetExportPreset1920x1080 |
1920×1080 | ~10 Мбит/с |
| Кастомный AVVideoSettings | любое | контролируемый |
Flutter: video_editor
video_editor (pub.dev) предоставляет UI-компоненты (таймлайн, кроппер) поверх ffmpeg_kit_flutter для реальных операций. Плюс — кросс-платформенный Flutter-код для UI. Минус — FFmpeg обрабатывает видео на CPU, что медленнее нативных AVFoundation/MediaCodec решений. Для MVP и несложного контента подходит, для production-редактора с частым использованием — предпочтительнее нативная реализация.
Сроки
Обрезка по времени + экспорт — 2–3 дня. Полноценный редактор с таймлайном, объединением клипов и текстовыми оверлеями — 5–8 дней.







