Реалізація обрізання та редагування відео в мобільному застосунку
Відеоредактор у мобільному застосунку — одна з найбільш трудовитратних медіазавдань. Не тому що 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 для обрізання з точністю до кадру без перекодування (тільки якщо збігаються ключові кадри). Якщо ні — 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 днів.







