video_compress_kit 0.0.3
video_compress_kit: ^0.0.3 copied to clipboard
Hardware-accelerated video compression for Flutter using native platform APIs. Zero binary bloat — uses MediaCodec on Android and VideoToolbox on iOS.
video_compress_kit #
Hardware-accelerated video & image compression for Flutter — zero binary bloat, no bundled FFmpeg.
Built by ikolvi.com.
Features #
Video #
- Compress videos with five quality presets (or custom bitrate/resolution)
- H.264 profile selection — Baseline, Main, or High
- Bitrate modes — VBR, CBR, or Constant Quality (CRF-like)
- BT.709 color standard for HD / 4K content
- Faststart — moov atom at front for faster streaming (both platforms)
- Per-session progress & cancellation — run multiple compressions concurrently
- Audio passthrough — copies audio track without re-encoding
- Estimate file size — predict output size before compressing
Image #
- Compress images — JPEG, PNG, WebP output
- Resize — constrain to max width / height with proportional scaling
- EXIF metadata — strip or preserve orientation, GPS, camera info
General #
- Get media info — resolution, duration, bitrate, file size
- Extract thumbnails — single-frame JPEG at any timestamp
- Zero binary bloat — uses only native OS APIs
Installation #
dependencies:
video_compress_kit: ^0.0.1
No extra setup — platform implementations are auto-endorsed.
Video Compression #
Basic usage #
import 'package:video_compress_kit/video_compress_kit.dart';
final kit = VideoCompressKit();
final result = await kit.compressVideo(
'/path/to/video.mp4',
sessionId: 'upload-1', // unique per concurrent operation
config: const CompressionConfig(quality: VideoQuality.medium),
);
print('Output: ${result.outputPath} (${result.fileSize} bytes)');
Per-session progress #
Each compression is identified by a sessionId. Progress events include both
the session ID and progress value (0.0–1.0):
kit.compressionProgress.listen((event) {
final id = event['sessionId'] as String;
final pct = (event['progress'] as double) * 100;
print('$id: ${pct.toStringAsFixed(1)}%');
});
Filter progress for a specific session:
kit.compressionProgress
.where((e) => e['sessionId'] == 'upload-1')
.listen((e) => print('${e['progress']}'));
Per-session cancellation #
// Cancel a specific session
await kit.cancelCompression(sessionId: 'upload-1');
// Cancel ALL active sessions
await kit.cancelCompression();
Concurrent compressions #
final job1 = kit.compressVideo('/path/a.mp4', sessionId: 'job-1');
final job2 = kit.compressVideo('/path/b.mp4', sessionId: 'job-2');
// Both run in parallel with independent progress and cancellation
final results = await Future.wait([job1, job2]);
Quality presets #
| Preset | Max Resolution | Typical Bitrate (1080p@30fps) | Approx. 60 s file |
|---|---|---|---|
veryLow |
360p | ~0.6 Mbps | ~4 MB |
low |
480p | ~1.3 Mbps | ~10 MB |
medium |
720p | ~2.8 Mbps | ~21 MB |
high |
1080p | ~9.3 Mbps | ~70 MB |
veryHigh |
Original | ~12.4 Mbps | ~93 MB |
Values are estimates from the built-in bits-per-pixel model. Actual sizes vary with content complexity and frame rate.
Estimate file size before compressing #
final info = await kit.getMediaInfo(path);
final estimate = kit.estimateFileSize(info: info, quality: VideoQuality.medium);
print('≈ ${(estimate / 1024 / 1024).toStringAsFixed(1)} MB');
Custom configuration #
const config = CompressionConfig(
quality: VideoQuality.medium, // base preset
bitrate: 3000000, // override: 3 Mbps
width: 1280, // override: 1280 px
height: 720, // override: 720 px
frameRate: 30,
includeAudio: true,
deleteOrigin: false,
outputPath: '/custom/output.mp4',
);
Advanced codec options #
Fine-tune the H.264 encoder beyond simple presets:
final result = await kit.compressVideo(
'/path/to/video.mp4',
sessionId: 'hq-job',
config: CompressionConfig(
quality: VideoQuality.high,
h264Profile: H264Profile.high, // best compression efficiency
bitrateMode: BitrateMode.cq, // constant quality (CRF-like)
cqQuality: 70, // 0–100 (higher = better quality)
colorStandard: ColorStandard.bt709, // HD color space
faststart: true, // moov atom at front (both platforms)
),
);
H.264 Profile
| Profile | Description | Best for |
|---|---|---|
H264Profile.baseline |
No B-frames, no CABAC | Maximum compatibility, WebRTC, older devices |
H264Profile.main |
B-frames, CABAC | Broadcast, streaming |
H264Profile.high |
8×8 transforms, custom quant matrices | Best compression (default on modern devices) |
Android: Requires API 23+. If
null, the platform default is used.
iOS: All profiles supported on modern devices.
Bitrate Mode
| Mode | Behaviour | File size |
|---|---|---|
BitrateMode.vbr |
Variable bitrate — adjusts per-frame | Variable (platform default) |
BitrateMode.cbr |
Constant bitrate — fixed rate | Predictable |
BitrateMode.cq |
Constant Quality — quality-based encoding | Variable, quality-driven |
CQ mode is the closest equivalent to CRF (x264). Use the
cqQualityparameter (0–100) to control quality. On Android, usesBITRATE_MODE_CQ(falls back to VBR if hardware doesn't support it). On iOS, usesAVVideoQualityKey.
Color Standard
| Standard | Use case |
|---|---|
ColorStandard.bt601 |
Standard-definition content |
ColorStandard.bt709 |
HD / 4K content (recommended) |
Android: Requires API 24+.
iOS: Applied via pixel buffer attributes.
Faststart
faststart: true moves the moov atom to the beginning of the MP4 file,
enabling progressive download and faster streaming start — equivalent to
ffmpeg -movflags +faststart.
| Platform | Support |
|---|---|
| iOS | ✅ Native shouldOptimizeForNetworkUse |
| Android | ✅ Custom Mp4FastStart post-processor — parses MP4 boxes, patches stco/co64 offsets, rewrites as ftyp + moov + mdat. Zero dependencies, ~1–2 s I/O overhead. |
Image Compression #
Basic usage #
final result = await kit.compressImage(
'/path/to/photo.jpg',
config: ImageCompressionConfig(
quality: 80,
maxWidth: 1920,
maxHeight: 1080,
format: ImageFormat.jpeg,
),
);
print('${result.outputPath} — ${result.fileSize} bytes');
print('Dimensions: ${result.width}×${result.height}');
Output formats #
| Format | Quality range | Notes |
|---|---|---|
ImageFormat.jpeg |
1–100 (default: 80) | Lossy, smallest files, universal |
ImageFormat.png |
Ignored (lossless) | Lossless, transparency support |
ImageFormat.webp |
1–100 | Best compression ratio. iOS 14+ only |
All configuration options #
| Parameter | Type | Default | Description |
|---|---|---|---|
quality |
int |
80 |
Output quality (1–100). Ignored for PNG |
maxWidth |
int? |
null |
Max output width. Scales proportionally |
maxHeight |
int? |
null |
Max output height. Scales proportionally |
format |
ImageFormat |
jpeg |
Output format: jpeg, png, webp |
keepExif |
bool |
false |
Preserve EXIF metadata (orientation, GPS, camera) |
outputPath |
String? |
null |
Custom output path (auto-generated if null) |
WebP example #
final result = await kit.compressImage(
'/path/to/photo.jpg',
config: ImageCompressionConfig(
quality: 75,
format: ImageFormat.webp,
keepExif: false,
),
);
EXIF preservation #
// Keep original EXIF data (orientation, GPS, camera info)
final result = await kit.compressImage(
'/path/to/photo.jpg',
config: ImageCompressionConfig(keepExif: true),
);
Android: Uses
ExifInterfaceto copy EXIF tags.
iOS: Uses ImageIO (CGImageSource/CGImageDestination) to copy metadata.
Other APIs #
Get media info #
final info = await kit.getMediaInfo('/path/to/video.mp4');
print('Resolution: ${info.width}×${info.height}');
print('Duration: ${info.duration}s');
print('Bitrate: ${info.bitrate} bps');
print('File size: ${info.fileSize} bytes');
Extract thumbnail #
// Get a JPEG frame at 2 seconds with 90% quality
final bytes = await kit.getThumbnail(
'/path/to/video.mp4',
quality: 90,
position: 2000, // milliseconds
);
Complete API Reference #
VideoCompressKit methods #
| Method | Returns | Description |
|---|---|---|
compressVideo(path, {required sessionId, config}) |
Future<CompressionResult> |
Compress a video with per-session tracking |
compressImage(path, {config}) |
Future<ImageCompressionResult> |
Compress an image (JPEG/PNG/WebP) |
getMediaInfo(path) |
Future<MediaInfo> |
Get video metadata |
getThumbnail(path, {quality, position}) |
Future<Uint8List?> |
Extract a single JPEG frame |
cancelCompression({sessionId}) |
Future<void> |
Cancel one session or all |
compressionProgress |
Stream<Map<String, dynamic>> |
Per-session progress stream |
estimateFileSize({info, quality}) |
int |
Predict compressed file size in bytes |
CompressionConfig fields #
| Field | Type | Default | Description |
|---|---|---|---|
quality |
VideoQuality |
medium |
Quality preset |
bitrate |
int? |
null |
Override bitrate in bps |
width |
int? |
null |
Override output width |
height |
int? |
null |
Override output height |
frameRate |
int? |
null |
Override frame rate |
includeAudio |
bool |
true |
Include audio track |
deleteOrigin |
bool |
false |
Delete original file |
outputPath |
String? |
null |
Custom output path |
h264Profile |
H264Profile? |
null |
H.264 profile (baseline/main/high) |
bitrateMode |
BitrateMode? |
null |
Bitrate mode (vbr/cbr/cq) |
cqQuality |
int? |
null |
CQ quality (0–100). Used with BitrateMode.cq |
colorStandard |
ColorStandard? |
null |
Color space (bt601/bt709) |
faststart |
bool |
false |
Moov atom at front (both platforms) |
Exported types #
| Type | Description |
|---|---|
CompressionConfig |
Video compression configuration |
CompressionResult |
Result of video compression |
ImageCompressionConfig |
Image compression configuration |
ImageCompressionResult |
Result of image compression |
MediaInfo |
Video metadata |
VideoQuality |
Preset enum: veryLow, low, medium, high, veryHigh |
H264Profile |
Profile enum: baseline, main, high |
BitrateMode |
Mode enum: vbr, cbr, cq |
ColorStandard |
Standard enum: bt601, bt709 |
ImageFormat |
Format enum: jpeg, png, webp |
Supported Platforms #
| Platform | Implementation | Video API | Image API |
|---|---|---|---|
| Android | video_compress_kit_android |
MediaCodec + MediaMuxer | BitmapFactory + Bitmap.compress |
| iOS | video_compress_kit_ios |
AVFoundation / VideoToolbox | UIImage / ImageIO |
Architecture #
This is a federated Flutter plugin. App developers only import this package; platform implementations are resolved automatically at build time.
video_compress_kit ← you import this
└─ video_compress_kit_platform_interface
├─ video_compress_kit_android
└─ video_compress_kit_ios
License #
MIT — see LICENSE.