You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何给视频添加水印?优先不使用第三方包

Flutter 视频添加水印实现方案

一、无第三方包实现思路

Flutter本身没有直接操作视频的API,需通过MethodChannel调用平台原生代码实现,核心是在原生层对视频帧添加水印后重新编码输出。

Android 端实现要点

利用Android的MediaCodec解码原视频,在每一帧渲染时通过Canvas绘制水印(文本/图片),再编码为新视频,最后用MediaMuxer封装成完整文件。核心代码片段:

override fun onOutputFrame(frame: ByteBuffer, info: MediaCodec.BufferInfo) {
    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    // 绘制原视频帧到画布
    canvas.drawBitmap(frameToBitmap(frame, width, height), 0f, 0f, null)
    // 添加文本水印
    val paint = Paint().apply {
        color = Color.WHITE
        textSize = 48f
        isAntiAlias = true
    }
    canvas.drawText("自定义水印", 50f, height - 50f, paint)
    // 将带水印的Bitmap送入编码器
    encodeBitmap(bitmap)
}

iOS 端实现要点

基于AVFoundation框架,用AVAsset读取原视频,创建AVMutableComposition,将水印封装为CALayer添加到视频合成图层,最后通过AVAssetExportSession导出。核心代码片段:

func addWatermark(to inputURL: URL, outputURL: URL, watermarkText: String) async throws {
    let asset = AVAsset(url: inputURL)
    let composition = AVMutableComposition()
    guard let videoTrack = asset.tracks(withMediaType: .video).first else { throw VideoError.invalidSource }
    let compVideoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)!
    try await compVideoTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: asset.duration), of: videoTrack, at: .zero)
    
    // 创建文本水印图层
    let watermarkLayer = CATextLayer()
    watermarkLayer.string = watermarkText
    watermarkLayer.foregroundColor = UIColor.white.cgColor
    watermarkLayer.fontSize = 40
    watermarkLayer.frame = CGRect(x: 20, y: asset.duration.value - 60, width: 200, height: 40)
    
    // 配置视频合成
    let videoComposition = AVMutableVideoComposition(asset: asset) { request in
        let frameLayer = request.renderContext.copyCurrentFrame()
        let rootLayer = CALayer()
        rootLayer.addSublayer(frameLayer)
        rootLayer.addSublayer(watermarkLayer)
        request.finish(with: rootLayer, context: nil)
    }
    
    // 导出处理后的视频
    let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality)!
    exporter.outputURL = outputURL
    exporter.outputFileType = .mp4
    exporter.videoComposition = videoComposition
    await exporter.export()
}

Flutter 端调用

通过MethodChannel与原生通信,传递参数并获取处理结果:

class VideoWatermark {
  static const MethodChannel _channel = MethodChannel('video_watermark');

  static Future<String?> addTextWatermark({
    required String inputPath,
    required String outputPath,
    required String text,
  }) async {
    return await _channel.invokeMethod('addTextWatermark', {
      'input': inputPath,
      'output': outputPath,
      'text': text,
    });
  }
}

二、第三方包实现方案(推荐)

若原生开发成本高,可使用flutter_ffmpeg基于FFmpeg命令行实现,步骤简单且兼容性强。

步骤1:添加依赖

dependencies:
  flutter_ffmpeg: ^0.5.1

步骤2:执行水印命令

  • 添加文本水印:
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';

final FlutterFFmpeg _ffmpeg = FlutterFFmpeg();

// 命令说明:保留原音频,在视频左下角添加白色文本水印
await _ffmpeg.execute(
  '-i $inputPath -vf "drawtext=text=\'我的水印\':x=20:y=H-60:fontsize=40:fontcolor=white" -codec:a copy $outputPath -y'
);
  • 添加图片水印:
// 命令说明:在视频右下角添加半透明图片水印
await _ffmpeg.execute(
  '-i $inputPath -i $watermarkImgPath -filter_complex "overlay=W-120:H-120:alpha=0.6" -codec:a copy $outputPath -y'
);

注意事项

  • 权限:Android需申请READ_MEDIA_VIDEO(Android13+)、WRITE_EXTERNAL_STORAGE(Android13以下);iOS需开启相册读写权限。
  • 性能:视频处理耗资源,建议在后台线程执行,避免阻塞UI。
  • 位置调整:通过滤镜参数(如x=W-100表示右对齐)或原生图层坐标控制水印位置。

内容的提问来源于stack exchange,提问作者황희윤

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 13:45:44