composeVideo method

  1. @override
Future<VideoTask> composeVideo({
  1. required String inputVideoPath,
  2. String? outputVideoPath,
  3. Uint8List? watermarkImage,
  4. String? text,
  5. String anchor = 'bottomRight',
  6. double margin = 16.0,
  7. String marginUnit = 'px',
  8. double offsetX = 0.0,
  9. double offsetY = 0.0,
  10. String offsetUnit = 'px',
  11. double widthPercent = 0.18,
  12. double opacity = 0.6,
  13. String codec = 'h264',
  14. int? bitrateBps,
  15. double? maxFps,
  16. int? maxLongSide,
})
override

Starts composing a watermark over a video file on iOS. Returns a VideoTask that exposes a progress stream and completion future.

Implementation

@override
Future<VideoTask> composeVideo({
  required String inputVideoPath,
  String? outputVideoPath,
  Uint8List? watermarkImage,
  String? text,
  String anchor = 'bottomRight',
  double margin = 16.0,
  String marginUnit = 'px',
  double offsetX = 0.0,
  double offsetY = 0.0,
  String offsetUnit = 'px',
  double widthPercent = 0.18,
  double opacity = 0.6,
  String codec = 'h264',
  int? bitrateBps,
  double? maxFps,
  int? maxLongSide,
}) async {
  _ensureCallbacksRegistered();
  final taskId = _genTaskId();
  final ctrl = StreamController<double>.broadcast();
  final completer = Completer<VideoResult>();
  _tasks[taskId] = _VideoTaskState(ctrl, completer);

  pigeon.ComposeVideoRequest req = pigeon.ComposeVideoRequest(
    taskId: taskId,
    inputVideoPath: inputVideoPath,
    outputVideoPath: outputVideoPath,
    watermarkImage: watermarkImage,
    text: text,
    anchor: _anchorFromString(anchor),
    margin: margin,
    marginUnit: _unitFromString(marginUnit),
    offsetX: offsetX,
    offsetY: offsetY,
    offsetUnit: _unitFromString(offsetUnit),
    widthPercent: widthPercent,
    opacity: opacity,
    codec: (codec == 'hevc') ? pigeon.VideoCodec.hevc : pigeon.VideoCodec.h264,
    bitrateBps: bitrateBps,
    maxFps: maxFps,
    maxLongSide: maxLongSide,
  );

  // Fire-and-forget; completion will also complete the future
  unawaited(pigeon.WatermarkApi().composeVideo(req).then((res) {
    // Fallback completion in case onVideoCompleted wasn't received
    final st = _tasks[res.taskId];
    if (st != null && !st.completer.isCompleted) {
      st.ctrl.close();
      st.completer.complete(VideoResult(
        path: res.outputVideoPath,
        width: res.width,
        height: res.height,
        durationMs: res.durationMs,
        codec: res.codec == pigeon.VideoCodec.hevc ? 'hevc' : 'h264',
      ));
      _tasks.remove(res.taskId);
    }
  }).catchError((e, st) {
    // If error surfaces via returned Future
    final s = _tasks.remove(taskId);
    if (s != null && !s.completer.isCompleted) {
      s.ctrl.addError(e, st);
      s.ctrl.close();
      s.completer.completeError(e, st);
    }
  }));

  return VideoTask(
    taskId: taskId,
    progress: ctrl.stream,
    done: completer.future,
    cancel: () async {
      await pigeon.WatermarkApi().cancel(taskId);
    },
  );
}