compress method

  1. @override
Future<VideoCompressResult> compress(
  1. String id,
  2. String path,
  3. VideoCompressConfig config,
  4. String? outputPath,
)
override

Compress path using config. id identifies the job (for progress correlation and cancellation). When outputPath is non-null the encoded file is written there; otherwise it lands in the plugin cache.

Implementation

@override
Future<VideoCompressResult> compress(
  String id,
  String path,
  VideoCompressConfig config,
  String? outputPath,
) async {
  await _ensureLoaded();
  _busy = true;
  try {
    final m = await _rawInfo(path);
    final srcW = (m['width'] as num).toInt();
    final srcH = (m['height'] as num).toInt();
    final durationMs = (m['durationMs'] as num).toInt();
    final srcKbps = (m['bitrateKbps'] as num).toInt();
    final srcBytes = (m['sizeBytes'] as num).toInt();
    final (tw, th) = _SizeMath.targetDimensions(srcW, srcH, config);
    final videoBps =
        _SizeMath.videoBitrateBps(config, durationMs, srcKbps, th);

    final cfg = <String, Object?>{
      'id': id,
      'targetWidth': tw,
      'targetHeight': th,
      'videoBitrateBps': videoBps,
      'frameRate': config.frameRate ?? 30,
      // Requested codec; the JS engine encodes HEVC when the browser supports
      // it, otherwise falls back to H.264 (and reports which was used).
      'codec': config.codec.name,
      // Hit the target closely when a size is requested; stay efficient (VBR)
      // for quality/bitrate modes.
      'bitrateMode': config.targetSizeMB != null ? 'constant' : 'variable',
      'keepOriginalIfLarger': config.keepOriginalIfLarger,
      'originalSizeBytes': srcBytes,
    }.jsify()! as JSObject;

    final onProgress = (double fraction, double outBytes) {
      _progressCtrl.add(CompressionProgress(
        id: id,
        progress: fraction,
        currentOutputBytes: outBytes > 0 ? outBytes.toInt() : null,
      ));
    }.toJS;

    final resJs = await _jsCompress(path, cfg, onProgress).toDart;
    final r = (resJs.dartify() as Map).cast<dynamic, dynamic>();
    return VideoCompressResult(
      id: id,
      outputPath: r['outputUrl'] as String,
      originalSizeBytes: srcBytes,
      compressedSizeBytes: (r['sizeBytes'] as num).toInt(),
      width: (r['width'] as num).toInt(),
      height: (r['height'] as num).toInt(),
      durationMs: (r['durationMs'] as num).toInt(),
      codec: r['codec'] as String,
      skipped: r['skipped'] == true,
    );
  } catch (e) {
    final msg = e.toString();
    if (msg.contains('cancelled')) {
      throw VideoCompressCancelledException();
    }
    throw VideoCompressException('compress_failed', msg);
  } finally {
    _busy = false;
  }
}