Flutter视频水印添加插件video_watermark的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter视频水印添加插件video_watermark的使用

video_watermark 是一个简单的Flutter包,用于在视频中添加图像覆盖,并提供视频剪辑选项。

特性

  • 在视频中添加标志
  • 以各种参数调整标志
  • 简单方式裁剪视频
  • 从多种来源添加图像(文件、资产和网络)
  • 跨平台支持

使用方法

初始化 VideoWatermark 实例

VideoWatermark videoWatermark = VideoWatermark(
    sourceVideoPath: videoPath,
    watermark: Watermark(image: WatermarkSource.file(imagepath)),
    onSave: (path) {
        // 获取输出文件路径
    },
    progress: (value) {
        // 获取视频生成进度
    },
);

生成带水印的视频

videoWatermark.generateVideo();

视频裁剪

VideoWatermark videoWatermark = VideoWatermark(
    sourceVideoPath: videoPath,
    videoTrim: VideoTrim(start: startTime, end: endTime),
    onSave: (path) {
        // 获取输出文件路径
    },
);

设置水印位置、大小和透明度

Watermark watermark = Watermark(
    image: WatermarkSource.file(imagepath),
    watermarkAlignment: WatermarkAlignment.topCenter,
    watermarkSize: WatermarkSize(150, 200),
    opacity: 0.8   // 0.0 - 1.0
);

从不同来源加载图像

WatermarkSource.file(imagepath)
WatermarkSource.asset(assetpath)
WatermarkSource.network(imageUrl)

完整示例 Demo

以下是一个完整的示例代码,展示了如何使用 video_watermark 插件:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_player/video_player.dart';
import 'package:video_watermark/video_watermark.dart';

void main() {
  runApp(const MyApp());
}

double width = 0;
double height = 0;

enum Pages { Watermark, Alignment, Padding, Opacity, Resize, Trim }

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      width = constraints.maxWidth;
      height = constraints.maxHeight;
      return MaterialApp(
        title: 'Video Watermark Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const MyHomePage(),
      );
    });
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

List<WatermarkAlignment> alignmentList = [
  WatermarkAlignment.center,
  WatermarkAlignment.topCenter,
  WatermarkAlignment.bottomCenter,
  WatermarkAlignment.leftCenter,
  WatermarkAlignment.rightCenter,
  WatermarkAlignment.topLeft,
  WatermarkAlignment.topRight,
  WatermarkAlignment.bottomLeft,
  WatermarkAlignment.botomRight,
];

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  late VideoPlayerController videoPlayerController;
  late TabController tabController;
  late final ScrollController scrollController;
  WatermarkAlignment? watermarkAlignment;
  int opacity = 100;
  late final List<TextEditingController> paddingControllers;
  late final TextEditingController widthController;
  late final TextEditingController heightController;
  late final TextEditingController opacityController;
  late Duration videoDuration;
  late Duration startTime;
  late Duration endTime;
  double? _progress;
  String? videoPath;
  WatermarkSource? imagePath;
  bool loading = false;
  bool lockAspectRatio = false;
  bool addWatermark = false;
  int currentPage = 0;

  @override
  void initState() {
    scrollController = ScrollController();
    tabController = TabController(length: Pages.values.length, vsync: this);
    paddingControllers = List.generate(4, (index) => TextEditingController());
    opacityController = TextEditingController();
    widthController = TextEditingController();
    heightController = TextEditingController();
    startTime = const Duration();
    super.initState();
  }

  @override
  void dispose() {
    scrollController.dispose();
    opacityController.dispose();
    videoPlayerController.dispose();
    widthController.dispose();
    heightController.dispose();
    tabController.dispose();
    for (var item in paddingControllers) {
      item.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        title: const Text("Video Watermark Demo"),
      ),
      body: loading
          ? Center(
              child: CircularProgressIndicator(
                value: _progress,
              ),
            )
          : videoPath == null || imagePath == null
              ? Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      ElevatedButton(
                        onPressed: () async {
                          final value = await ImagePicker().pickVideo(source: ImageSource.gallery);
                          if (value != null) {
                            setState(() {
                              loading = true;
                            });
                            videoPlayerController = VideoPlayerController.file(File(value.path));
                            await videoPlayerController.initialize().then((_) {
                              setState(() {
                                videoPath = value.path;
                                videoDuration = videoPlayerController.value.duration;
                                endTime = videoDuration;
                                loading = false;
                              });
                            });
                          }
                        },
                        child: const Text("Select Video"),
                      ),
                      ElevatedButton(
                        onPressed: () async {
                          final value = await ImagePicker().pickImage(source: ImageSource.gallery);
                          if (value?.path != null) {
                            setState(() {
                              imagePath = WatermarkSource.file(value!.path);
                            });
                          }
                        },
                        child: const Text("Select Watermark"),
                      ),
                    ],
                  ),
                )
              : Builder(builder: (context) {
                  videoPlayerController.play();
                  videoPlayerController.setLooping(true);

                  return Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Expanded(
                        flex: 4,
                        child: AspectRatio(
                          aspectRatio: videoPlayerController.value.aspectRatio,
                          child: InkWell(
                            onTap: () {
                              videoPlayback();
                            },
                            child: VideoPlayer(videoPlayerController),
                          ),
                        ),
                      ),
                      SizedBox(
                        height: height * 0.05,
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceAround,
                          children: [
                            IconButton(
                              onPressed: () {
                                if (currentPage > 0) {
                                  currentPage--;
                                  changeOption();
                                }
                              },
                              icon: const Icon(Icons.arrow_back_ios),
                            ),
                            IconButton(
                              onPressed: () {
                                if (currentPage < Pages.values.length - 1) {
                                  currentPage++;
                                  changeOption();
                                }
                              },
                              icon: const Icon(Icons.arrow_forward_ios),
                            ),
                          ],
                        ),
                      ),
                      Expanded(
                        flex: 2,
                        child: TabBarView(
                          controller: tabController,
                          children: List.generate(Pages.values.length, (index) {
                            return SizedBox(
                              width: width,
                              child: Column(
                                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                                children: [
                                  if (index == 1 || index == 2)
                                    DropdownButton<WatermarkAlignment>(
                                      value: watermarkAlignment,
                                      hint: const Text("Select Alignment"),
                                      items: List.generate(
                                        alignmentList.length,
                                        (index) => DropdownMenuItem(
                                          child: Text(alignmentList[index].toString()),
                                          value: alignmentList[index],
                                        ),
                                      ),
                                      onChanged: (alignment) {
                                        setState(() {
                                          watermarkAlignment = alignment;
                                        });
                                      },
                                    ),
                                  if (index == 2)
                                    SizedBox(
                                      width: width,
                                      child: Row(
                                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                                        children: [
                                          for (var i = 0; i < paddingControllers.length; i++)
                                            SizedBox(
                                              width: width * 0.2,
                                              child: TextField(
                                                controller: paddingControllers[i],
                                                decoration: InputDecoration(
                                                  label: Text(i == 0 ? "Left" : i == 1 ? "Right" : i == 2 ? "Top" : "Bottom"),
                                                  counterText: "",
                                                ),
                                                keyboardType: TextInputType.number,
                                                maxLength: 3,
                                              ),
                                            ),
                                        ],
                                      ),
                                    ),
                                  if (index == 3)
                                    Row(
                                      mainAxisAlignment: MainAxisAlignment.center,
                                      children: [
                                        IconButton(
                                          onPressed: (() {
                                            if (opacity > 0) {
                                              setState(() {
                                                opacity -= 10;
                                              });
                                            }
                                          }),
                                          icon: const Icon(Icons.remove),
                                        ),
                                        SizedBox(
                                          width: width * 0.3,
                                          child: Text(
                                            "$opacity",
                                            textAlign: TextAlign.center,
                                          ),
                                        ),
                                        IconButton(
                                          onPressed: (() {
                                            if (opacity < 100) {
                                              setState(() {
                                                opacity += 10;
                                              });
                                            }
                                          }),
                                          icon: const Icon(Icons.add),
                                        ),
                                      ],
                                    ),
                                  if (index == 4)
                                    Column(
                                      children: [
                                        Row(
                                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                                          children: [
                                            SizedBox(
                                              width: width * 0.3,
                                              child: TextField(
                                                controller: widthController,
                                                decoration: const InputDecoration(
                                                  label: Text("Width"),
                                                  counterText: "",
                                                ),
                                              ),
                                            ),
                                            if (!lockAspectRatio)
                                              SizedBox(
                                                width: width * 0.3,
                                                child: TextField(
                                                  controller: heightController,
                                                  decoration: const InputDecoration(
                                                    label: Text("Height"),
                                                    counterText: "",
                                                  ),
                                                ),
                                              ),
                                          ],
                                        ),
                                        CheckboxListTile(
                                          value: lockAspectRatio,
                                          title: const Text("Lock aspect ratio"),
                                          onChanged: (value) {
                                            setState(() {
                                              lockAspectRatio = value ?? true;
                                            });
                                          },
                                        ),
                                      ],
                                    ),
                                  if (index == 5)
                                    Column(
                                      crossAxisAlignment: CrossAxisAlignment.start,
                                      children: [
                                        const Text("Start"),
                                        Row(
                                          mainAxisAlignment: MainAxisAlignment.start,
                                          children: [
                                            IconButton(
                                              onPressed: (() {
                                                if (startTime > const Duration()) {
                                                  setState(() {
                                                    startTime -= const Duration(seconds: 1);
                                                  });
                                                }
                                              }),
                                              icon: const Icon(Icons.remove),
                                            ),
                                            SizedBox(
                                              width: width * 0.3,
                                              child: Text(
                                                startTime.toString().split(".").first,
                                                textAlign: TextAlign.center,
                                              ),
                                            ),
                                            IconButton(
                                              onPressed: (() {
                                                if (startTime < endTime) {
                                                  setState(() {
                                                    startTime += const Duration(seconds: 1);
                                                  });
                                                }
                                              }),
                                              icon: const Icon(Icons.add),
                                            ),
                                          ],
                                        ),
                                        const Text("\nEnd"),
                                        Row(
                                          mainAxisAlignment: MainAxisAlignment.start,
                                          children: [
                                            IconButton(
                                              onPressed: (() {
                                                if (endTime > startTime) {
                                                  setState(() {
                                                    endTime -= const Duration(seconds: 1);
                                                  });
                                                }
                                              }),
                                              icon: const Icon(Icons.remove),
                                            ),
                                            SizedBox(
                                              width: width * 0.3,
                                              child: Text(
                                                endTime.toString().split(".").first,
                                                textAlign: TextAlign.center,
                                              ),
                                            ),
                                            IconButton(
                                              onPressed: (() {
                                                if (endTime < videoDuration) {
                                                  setState(() {
                                                    endTime += const Duration(seconds: 1);
                                                  });
                                                }
                                              }),
                                              icon: const Icon(Icons.add),
                                            ),
                                            Row(
                                              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                                              children: [
                                                Checkbox(
                                                  value: addWatermark,
                                                  onChanged: (value) {
                                                    setState(() {
                                                      addWatermark = value ?? true;
                                                    });
                                                  },
                                                ),
                                                const Text("\t\tWatermark"),
                                              ],
                                            ),
                                          ],
                                        ),
                                      ],
                                    ),
                                  ElevatedButton(
                                    onPressed: () async {
                                      if (index == 2) {
                                        for (var element in paddingControllers) {
                                          if (element.text.isEmpty) {
                                            element.text = "0";
                                          }
                                        }
                                        if (watermarkAlignment != null) {
                                          watermarkAlignment!.padding = EdgeInsets.only(
                                            left: double.parse(paddingControllers[0].text),
                                            right: double.parse(paddingControllers[1].text),
                                            top: double.parse(paddingControllers[2].text),
                                            bottom: double.parse(paddingControllers[3].text),
                                          );
                                        }
                                      }
                                      String? path = await getDownloadsDirectory().then((value) => value?.path);
                                      print('PATH: ${path}');

                                      VideoWatermark videoWatermark;

                                      switch (Pages.values[index]) {
                                        case Pages.Watermark:
                                          videoWatermark = VideoWatermark(
                                            sourceVideoPath: videoPath!,
                                            savePath: path,
                                            watermark: Watermark(
                                              image: imagePath!,
                                            ),
                                            progress: progress,
                                            onSave: onSave,
                                          );
                                          break;
                                        case Pages.Alignment:
                                          videoWatermark = VideoWatermark(
                                            sourceVideoPath: videoPath!,
                                            watermark: Watermark(
                                              image: imagePath!,
                                              watermarkAlignment: watermarkAlignment,
                                            ),
                                            progress: progress,
                                            onSave: onSave,
                                          );
                                          break;
                                        case Pages.Padding:
                                          videoWatermark = VideoWatermark(
                                            sourceVideoPath: videoPath!,
                                            watermark: Watermark(
                                              image: imagePath!,
                                              watermarkAlignment: watermarkAlignment,
                                            ),
                                            progress: progress,
                                            onSave: onSave,
                                          );
                                          break;
                                        case Pages.Opacity:
                                          videoWatermark = VideoWatermark(
                                            sourceVideoPath: videoPath!,
                                            watermark: Watermark(
                                              image: imagePath!,
                                              opacity: opacity / 100,
                                            ),
                                            progress: progress,
                                            onSave: onSave,
                                          );
                                          break;
                                        case Pages.Resize:
                                          videoWatermark = VideoWatermark(
                                            sourceVideoPath: videoPath!,
                                            watermark: Watermark(
                                              image: imagePath!,
                                              watermarkSize: lockAspectRatio
                                                  ? WatermarkSize.symmertric(double.tryParse(widthController.text) ?? 0)
                                                  : WatermarkSize(double.tryParse(widthController.text) ?? 0, double.tryParse(heightController.text) ?? 0),
                                            ),
                                            progress: progress,
                                            onSave: onSave,
                                          );
                                          break;
                                        case Pages.Trim:
                                          videoWatermark = VideoWatermark(
                                            sourceVideoPath: videoPath!,
                                            videoTrim: VideoTrim(start: startTime, end: endTime),
                                            watermark: addWatermark ? Watermark(image: imagePath!) : null,
                                            onSave: onSave,
                                            progress: progress,
                                          );
                                          break;
                                      }

                                      generateVideo(videoWatermark);
                                    },
                                    child: Text(Pages.values[index].name),
                                  ),
                                ],
                              ),
                            );
                          }),
                        ),
                      ),
                    ],
                  );
                }),
    );
  }

  Future<void> generateVideo(VideoWatermark videoWatermark) async {
    videoPlayback();
    setState(() {
      loading = true;
    });

    await videoWatermark.generateVideo();
  }

  void progress(double percent) {
    setState(() {
      if (percent <= 1) {
        _progress = percent;
      } else {
        _progress = null;
      }
    });
  }

  void onSave(String? file) {
    print('FILE: ${file}');
    if (file != null) {
      videoPlayerController = VideoPlayerController.file(File(file))
        ..initialize().then((value) {
          setState(() {
            loading = false;
          });
        });
    } else {
      setState(() {
        loading = false;
      });
    }
  }

  void changeOption() async {
    tabController.animateTo(currentPage, duration: const Duration(milliseconds: 200), curve: Curves.ease);
  }

  Future<void> videoPlayback() async {
    if (videoPlayerController.value.isPlaying) {
      await videoPlayerController.pause();
    } else {
      await videoPlayerController.play();
    }
  }
}

这个示例展示了如何选择视频和水印图像,并通过不同的选项来设置水印的位置、大小和透明度。同时提供了视频裁剪的功能。


更多关于Flutter视频水印添加插件video_watermark的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter视频水印添加插件video_watermark的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter应用中使用video_watermark插件来为视频添加水印的示例代码。这个插件允许你在视频上叠加图片或文字水印。

首先,确保你已经在pubspec.yaml文件中添加了video_watermark依赖:

dependencies:
  flutter:
    sdk: flutter
  video_watermark: ^最新版本号  # 请替换为实际的最新版本号

然后运行flutter pub get来安装依赖。

接下来,在你的Flutter项目中,你可以使用以下代码来添加水印:

import 'package:flutter/material.dart';
import 'package:video_watermark/video_watermark.dart';
import 'dart:io';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _outputPath = '';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Video Watermark Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ElevatedButton(
                onPressed: () async {
                  // 输入视频文件路径
                  String inputVideoPath = 'path/to/your/input/video.mp4';
                  // 水印图片文件路径(可选,如果添加文字水印则不需要)
                  String watermarkImagePath = 'path/to/your/watermark/image.png';
                  // 水印文字(可选,如果添加图片水印则不需要)
                  String watermarkText = 'Watermark Text';

                  // 创建水印配置
                  VideoWatermarkConfig config = VideoWatermarkConfig(
                    inputVideoPath: inputVideoPath,
                    outputVideoPath: (await getApplicationDocumentsDirectory()).path + '/output_video.mp4',
                    watermarkImagePath: watermarkImagePath, // 如果使用图片水印,请提供路径
                    watermarkText: watermarkText, // 如果使用文字水印,请提供文字
                    startTime: 0, // 水印开始时间(秒)
                    endTime: 10, // 水印结束时间(秒),如果为null则水印显示到视频结束
                    fontSize: 36, // 文字水印字体大小(仅当使用文字水印时有效)
                    color: Colors.red.value, // 文字水印颜色(仅当使用文字水印时有效)
                    position: WatermarkPosition.bottomRight, // 水印位置
                    opacity: 0.5, // 水印透明度
                  );

                  // 添加水印
                  bool result = await VideoWatermark.addWatermark(config);

                  if (result) {
                    setState(() {
                      _outputPath = config.outputVideoPath!;
                    });
                    print('Watermark added successfully. Output video path: $_outputPath');
                  } else {
                    print('Failed to add watermark.');
                  }
                },
                child: Text('Add Watermark'),
              ),
              if (_outputPath.isNotEmpty)
                Text('Output Video Path: $_outputPath'),
            ],
          ),
        ),
      ),
    );
  }
}

在这个示例中:

  1. 依赖添加:确保在pubspec.yaml中添加了video_watermark依赖。
  2. UI布局:创建了一个简单的Flutter应用,包含一个按钮用于触发水印添加操作。
  3. 水印配置:定义了水印的配置,包括输入视频路径、输出视频路径、水印图片路径(或水印文字)、水印开始和结束时间、字体大小(仅文字水印)、颜色(仅文字水印)、位置以及透明度。
  4. 水印添加:调用VideoWatermark.addWatermark方法添加水印,并根据结果更新UI。

请确保将inputVideoPathwatermarkImagePath替换为实际的文件路径。如果你使用的是文字水印,则不需要提供watermarkImagePath,只需提供watermarkText即可。

注意:实际开发中,你可能需要处理更多的异常情况,例如文件不存在、权限问题等。此外,由于video_watermark插件的具体实现和API可能会随着版本更新而变化,请参考最新的插件文档以获取最准确的信息。

回到顶部