video_compress_kit_android

pub package License: MIT

The Android implementation of video_compress_kit.

Usage

You should not depend on this package directly. Add the app-facing package instead:

dependencies:
  video_compress_kit: ^0.0.1

The Android implementation is automatically endorsed and registered.


How It Works

Video Compression Pipeline

Uses Android's built-in MediaCodec hardware video encoder with MediaExtractor for demuxing and MediaMuxer for muxing. No bundled native libraries — the entire implementation rides on top of OS APIs, adding ~0 MB to your APK size.

Input MP4
  → MediaExtractor (demux video + audio tracks)
  → MediaCodec decoder (hardware H.264 decode)
  → Surface transfer
  → MediaCodec encoder (hardware H.264 encode)
      ├─ H.264 Profile: Baseline / Main / High (API 23+)
      ├─ Bitrate mode: VBR / CBR / CQ (API 21+)
      ├─ CQ quality: KEY_QUALITY (0–100)
      └─ Color standard: BT.601 / BT.709 (API 24+)
  → MediaMuxer + audio passthrough
  → Mp4FastStart (if faststart=true): relocate moov atom
  → Output MP4

Image Compression Pipeline

Uses Android's built-in BitmapFactory for decoding and Bitmap.compress() for re-encoding. No third-party image libraries.

Input image (JPEG / PNG / WebP / etc.)
  → BitmapFactory.decodeFile (with inSampleSize for memory efficiency)
  → Scale to maxWidth / maxHeight (proportional)
  → Bitmap.compress(format, quality, outputStream)
      ├─ JPEG: CompressFormat.JPEG
      ├─ PNG:  CompressFormat.PNG
      └─ WebP: CompressFormat.WEBP / WEBP_LOSSY (API 30+)
  → ExifInterface: copy EXIF tags (if keepExif = true)
  → Output file

Per-Session Architecture

Each compressVideo() call creates a VideoCompressor instance stored in a ConcurrentHashMap<String, VideoCompressor> keyed by sessionId. This enables:

  • Concurrent compressions — multiple videos processed in parallel
  • Per-session progress — each compressor reports progress tagged with its session ID
  • Per-session cancellation — cancel a specific session without affecting others
  • Cancel all — iterate the map and cancel every active session
┌──────────────────────────────────┐
│  VideoCompressKitAndroidPlugin   │
│                                  │
│  compressors: ConcurrentHashMap  │
│  ┌────────────┬────────────┐     │
│  │ "job-1"    │ "job-2"    │     │
│  │ Compressor │ Compressor │     │
│  └────────────┴────────────┘     │
│                                  │
│  cancelCompression("job-1")      │
│   → compressors["job-1"].cancel()│
│                                  │
│  cancelCompression(null)         │
│   → cancel ALL                   │
└──────────────────────────────────┘

Video Feature Support Matrix

Feature Support Notes
H.264 encoding Hardware-accelerated via MediaCodec
Resolution scaling Aspect-ratio preserved
Custom bitrate Overrides preset default
Custom frame rate
Audio passthrough Copies audio track without re-encoding
H.264 Baseline profile KEY_PROFILE, API 23+
H.264 Main profile KEY_PROFILE, API 23+
H.264 High profile KEY_PROFILE, API 23+
VBR bitrate mode BITRATE_MODE_VBR (default)
CBR bitrate mode BITRATE_MODE_CBR
CQ bitrate mode ✅* BITRATE_MODE_CQ + KEY_QUALITY (API 21+). Falls back to VBR if hardware doesn't support CQ
BT.601 color standard KEY_COLOR_STANDARD (API 24+)
BT.709 color standard KEY_COLOR_STANDARD (API 24+)
Faststart (moov atom) Custom Mp4FastStart post-processor — pure Kotlin, zero dependencies. Patches stco/co64 offsets and rewrites as ftyp + moov + mdat. ~1–2 s I/O overhead. Non-fatal on failure.
Per-session progress Event channel with {sessionId, progress}
Per-session cancel ConcurrentHashMap keyed by sessionId
Delete original file

CQ fallback: Not all Android hardware encoders support BITRATE_MODE_CQ. The implementation wraps CQ configuration in a try/catch — if the encoder rejects CQ mode, it automatically falls back to VBR with the preset bitrate.

Image Feature Support Matrix

Feature Support Notes
JPEG output Bitmap.CompressFormat.JPEG
PNG output Bitmap.CompressFormat.PNG (lossless, quality ignored)
WebP output WEBP_LOSSY on API 30+, WEBP on older
Quality control 1–100 for JPEG/WebP
Resize (maxWidth / maxHeight) Proportional scaling with inSampleSize for memory efficiency
EXIF metadata copy ExifInterface — copies orientation, GPS, camera, and date tags

Requirements

Requirement Value
Min SDK 24 (Android 7.0)
Compile SDK 36
Kotlin 2.x
Java 17
Dependencies kotlinx-coroutines-android:1.7.3

Native APIs Used

API Purpose Min API
MediaExtractor Demux input video/audio tracks 16
MediaCodec Hardware H.264 decode/encode 16
MediaMuxer Mux encoded video + audio into MP4 18
MediaFormat Configure encoder (profile, bitrate mode, CQ, color) 16+
BitmapFactory Decode images with inSampleSize 1
Bitmap.compress Re-encode images to JPEG/PNG/WebP 1
ExifInterface Read/write EXIF metadata 24
MediaMetadataRetriever Get video metadata, extract frames 10
Mp4FastStart (custom) Post-process MP4 to relocate moov atom before mdat N/A (pure Kotlin)

License

MIT — see LICENSE.