Flutter音视频处理插件ffipeg_muxer的使用

Flutter音视频处理插件ffipeg_muxer的使用

简介

ffipeg_muxer 是一个用于在 Flutter 中使用 FFmpeg FFI 来将单独的音频和视频文件合并为单个输出文件的插件。它仅负责复用(muxing),而不进行转码、重新计时或过滤等操作。


示例代码

以下是一个完整的示例代码,展示如何使用 ffipeg_muxer 插件来合并视频和音频文件:

import 'dart:ffi';
import 'dart:io';

import 'package:args/args.dart';
import 'package:ffipeg_muxer/ffipeg_muxer.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;

// 构建命令行参数解析器
ArgParser buildParser() {
  return ArgParser()
    ..addOption(
      'video',
      abbr: 'v',
      valueHelp: 'video_file',
      mandatory: true,
      help: '设置视频输入文件路径。',
    )
    ..addOption(
      'audio',
      abbr: 'a',
      valueHelp: 'audio_file',
      mandatory: true,
      help: '设置音频输入文件路径。',
    )
    ..addOption(
      'format',
      abbr: 'f',
      valueHelp: 'FORMAT',
      help: '覆盖输出格式(默认:根据文件扩展名自动检测)。',
    )
    ..addFlag(
      'overwrite',
      abbr: 'y',
      negatable: false,
      help: '覆盖输出文件(如果存在)。',
    )
    ..addFlag(
      'help',
      abbr: 'h',
      negatable: false,
      help: '打印此帮助信息。',
    )
    ..addOption(
      'log',
      abbr: 'l',
      valueHelp: 'LEVEL',
      allowed: Level.LEVELS.map((l) => l.name.toLowerCase()),
      defaultsTo: Level.SEVERE.name.toLowerCase(),
      help: '设置日志级别(详细程度)。',
    )
    ..addFlag(
      'version',
      negatable: false,
      help: '打印工具版本号。',
    );
}

// 获取脚本名称
String scriptName() {
  final executable = path.split(Platform.executable).last;
  final script = Platform.script.pathSegments.last;
  return executable == script ? script : '$executable $script';
}

// 打印使用说明
void printUsage(ArgParser argParser) {
  print('Usage: ${scriptName()} ${positionalArgs.join(' ')}');
  print(argParser.usage);
  print('\n设置 FFMPEG_PATH 环境变量以指定 FFmpeg 的路径。');
  print('(如果未设置,则会通过 `which` 命令搜索 FFmpeg。)');
}

void main(List<String> arguments) {
  final ArgParser argParser = buildParser();
  try {
    final ArgResults results = argParser.parse(arguments);

    // 处理解析后的参数
    if (results.wasParsed('help')) {
      printUsage(argParser);
      return;
    }
    if (results.wasParsed('version')) {
      print('ffipeg_muxer 版本: $packageVersion');
      return;
    }
    final logOption = results.option('log');
    final logLevel = Level.LEVELS.firstWhere(
        (l) => l.name.toLowerCase() == logOption,
        orElse: () => Level.SEVERE);

    final overwrite = results.wasParsed('overwrite');

    for (final option in argParser.options.values) {
      if (option.mandatory && !results.wasParsed(option.name)) {
        throw FormatException([
          '缺少必需选项:',
          if (option.abbr != null) ' -${option.abbr},',
          ' --${option.name}',
          if (option.valueHelp != null) '=${option.valueHelp}',
        ].join(''));
      }
    }

    final videoFile = results.option('video')!;
    final audioFile = results.option('audio')!;
    final format = results.option('format');

    if (results.rest.length < positionalArgs.length) {
      final missingArgs = positionalArgs.sublist(results.rest.length).join(' ');
      throw FormatException(
          '缺少必需的位置参数: $missingArgs');
    } else if (results.rest.length > positionalArgs.length) {
      throw FormatException('提供了过多的位置参数。');
    }

    final [outputFile] = results.rest;

    final ffmpegPath = Platform.environment['FFMPEG_PATH'] ??
        Process.runSync('which', ['ffmpeg']).stdout.toString().trim();
    if (ffmpegPath.isEmpty) {
      throw Exception(
          '未找到 FFmpeg。请确保其位于您的 PATH 中,或设置 FFMPEG_PATH 环境变量。');
    }

    Logger.root.level = logLevel;
    Logger.root.onRecord.listen((record) {
      print('${record.level.name}: ${record.message}');
    });

    Logger.root.info('从 $ffmpegPath 加载 FFmpeg ...');

    final muxer = Muxer(DynamicLibrary.open(ffmpegPath));

    if (Logger.root.level >= Level.INFO) {
      final version = muxer.getFFmpegVersion();
      Logger.root.info('FFmpeg 版本: $version');
    }

    final start = DateTime.now();

    final result = muxer.run(
      videoFile: videoFile,
      audioFile: audioFile,
      outputFile: outputFile,
      format: format,
      overwrite: overwrite,
    );

    final end = DateTime.now();
    final diff = end.difference(start);
    final diffDesc = diff.inSeconds > 300
        ? '${diff.inMinutes} 分钟'
        : diff.inMilliseconds > 1000
            ? '${diff.inSeconds} 秒'
            : '${diff.inMilliseconds} 毫秒';

    switch (result) {
      case MuxerOK():
        print('成功合并文件,耗时 $diffDesc,输出文件为: $outputFile');
        break;
      case MuxerError():
        throw result;
    }
  } on FormatException catch (e) {
    // 如果提供了无效参数,打印使用说明
    print(e.message);
    print('');
    printUsage(argParser);
    exit(1);
  } on MuxerError catch (e) {
    // 如果发生 Muxer 错误,打印错误信息
    if (Logger.root.level > Level.SEVERE) {
      print('发生 Muxer 错误: $e');
    }
    exit(1);
  } catch (e, stack) {
    print('发生错误: $e\n$stack');
    exit(1);
  }
}

安装

要使用 ffipeg_muxer,只需将其添加到项目的依赖中即可:

dart pub add ffipeg_muxer
1 回复

更多关于Flutter音视频处理插件ffipeg_muxer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


ffmpeg_muxer 是一个用于 Flutter 的音视频处理插件,它基于 FFmpeg 库,允许你在 Flutter 应用中执行音视频的混合、转码、剪切等操作。以下是如何在 Flutter 项目中使用 ffmpeg_muxer 插件的基本步骤。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 ffmpeg_muxer 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  ffmpeg_muxer: ^0.0.1  # 请使用最新版本

然后,运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 ffmpeg_muxer 插件。

import 'package:ffmpeg_muxer/ffmpeg_muxer.dart';

3. 使用插件

ffmpeg_muxer 插件提供了多种方法来处理音视频文件。以下是一些常见的操作示例。

3.1 混合音视频

你可以使用 ffmpeg_muxer 将一个音频文件和一个视频文件混合成一个新的文件。

void muxAudioVideo() async {
  String videoPath = 'path/to/video.mp4';
  String audioPath = 'path/to/audio.mp3';
  String outputPath = 'path/to/output.mp4';

  bool result = await FfmpegMuxer.mux(videoPath, audioPath, outputPath);

  if (result) {
    print('音视频混合成功');
  } else {
    print('音视频混合失败');
  }
}

3.2 转码视频

你可以使用 ffmpeg_muxer 将视频文件转码为另一种格式。

void transcodeVideo() async {
  String inputPath = 'path/to/input.mp4';
  String outputPath = 'path/to/output.avi';

  bool result = await FfmpegMuxer.transcode(inputPath, outputPath);

  if (result) {
    print('视频转码成功');
  } else {
    print('视频转码失败');
  }
}

3.3 剪切视频

你可以使用 ffmpeg_muxer 剪切视频文件的一部分。

void trimVideo() async {
  String inputPath = 'path/to/input.mp4';
  String outputPath = 'path/to/output.mp4';
  int startTime = 10; // 开始时间(秒)
  int duration = 20;  // 持续时间(秒)

  bool result = await FfmpegMuxer.trim(inputPath, outputPath, startTime, duration);

  if (result) {
    print('视频剪切成功');
  } else {
    print('视频剪切失败');
  }
}

4. 处理错误

在使用 ffmpeg_muxer 插件时,可能会遇到各种错误。你可以通过捕获异常来处理这些错误。

void muxAudioVideo() async {
  try {
    String videoPath = 'path/to/video.mp4';
    String audioPath = 'path/to/audio.mp3';
    String outputPath = 'path/to/output.mp4';

    bool result = await FfmpegMuxer.mux(videoPath, audioPath, outputPath);

    if (result) {
      print('音视频混合成功');
    } else {
      print('音视频混合失败');
    }
  } catch (e) {
    print('发生错误: $e');
  }
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!