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

Flutter中如何实现视频文件的音频转录功能?

解决Flutter视频音频转录问题

你目前使用的speech_to_text包仅能实时捕获设备麦克风的音频输入,无法直接读取本地视频文件中的音频流,因此才会出现转录麦克风声音而非视频音频的情况。要实现视频音频转录,需要分两步处理:先提取视频中的独立音频文件,再对音频进行转写。

解决方案步骤

  1. 从视频文件中提取出单独的音频文件(推荐转成WAV格式,适配多数语音识别服务)
  2. 使用离线/在线语音转文字工具处理提取出的音频

推荐依赖包

  • 音频提取:ffmpeg_kit_flutter_full_gpl(专业的音视频处理工具,支持转码、提取等操作)
  • 离线转写:vosk_flutter(轻量级离线语音识别库,支持多语言,无需网络)
  • 在线转写:可选用Google Cloud Speech-to-Text、Azure Speech等服务(需网络和API密钥)

具体代码实现

1. 添加依赖到pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  video_player: ^2.8.1
  image_picker: ^1.0.4
  ffmpeg_kit_flutter_full_gpl: ^5.1.0
  vosk_flutter: ^0.3.0

assets:
  - assets/vosk-model-small-cn-0.22/ # 替换为你下载的Vosk模型路径

2. 实现视频音频提取函数

import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart';

Future<File?> extractAudioFromVideo(File videoFile) async {
  final tempDir = await getTemporaryDirectory();
  final audioPath = '${tempDir.path}/extracted_audio.wav';
  
  // 用FFmpeg提取音频,转成16kHz单声道WAV(Vosk要求的标准格式)
  final session = await FFmpegKit.execute(
    '-i "${videoFile.path}" -vn -acodec pcm_s16le -ar 16000 -ac 1 "$audioPath"',
  );
  
  final returnCode = await session.getReturnCode();
  if (returnCode?.isValueSuccess() == true) {
    return File(audioPath);
  } else {
    final log = await session.getAllLogsAsString();
    print('音频提取失败: $log');
    return null;
  }
}

3. 修改页面逻辑实现完整功能

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:image_picker/image_picker.dart';
import 'package:vosk_flutter/vosk_flutter.dart';

class VideoTranscriptionApp extends StatefulWidget {
  @override
  _VideoTranscriptionAppState createState() => _VideoTranscriptionAppState();
}

class _VideoTranscriptionAppState extends State<VideoTranscriptionApp> {
  VideoPlayerController? _videoPlayerController;
  List<Map<String, dynamic>> _transcriptionData = [];
  bool _isTranscribing = false;

  @override
  void initState() {
    super.initState();
    _initVoskModel();
  }

  // 初始化Vosk语音识别模型
  Future<void> _initVoskModel() async {
    await VoskFlutter.instance.loadModel('assets/vosk-model-small-cn-0.22/');
  }

  Future<void> _pickVideoAndTranscribe() async {
    final pickedFile = await ImagePicker().pickVideo(source: ImageSource.gallery);
    if (pickedFile == null) return;

    setState(() => _isTranscribing = true);
    final videoFile = File(pickedFile.path);

    // 初始化视频播放器
    _videoPlayerController = VideoPlayerController.file(videoFile)
      ..initialize().then((_) => setState(() {}));

    // 提取音频
    final audioFile = await extractAudioFromVideo(videoFile);
    if (audioFile == null) {
      setState(() => _isTranscribing = false);
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('音频提取失败')),
      );
      return;
    }

    // 转录音频
    await _transcribeAudio(audioFile);
    setState(() => _isTranscribing = false);
  }

  // 用Vosk处理音频转写,包含时间戳
  Future<void> _transcribeAudio(File audioFile) async {
    final recognizer = await VoskFlutter.instance.createRecognizer();
    _transcriptionData.clear();

    await recognizer.processFile(audioFile.path, (result) {
      if (result.finalResult) {
        // 获取带精确时间戳的单词数据
        result.wordTimestamps?.forEach((timestamp) {
          _transcriptionData.add({
            'word': timestamp.word,
            'start': timestamp.start.toDouble(),
            'end': timestamp.end.toDouble(),
          });
        });
        setState(() {});
      }
    });

    await recognizer.close();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('视频音频转录')),
      body: Column(
        children: [
          if (_videoPlayerController?.value.isInitialized == true)
            AspectRatio(
              aspectRatio: _videoPlayerController!.value.aspectRatio,
              child: VideoPlayer(_videoPlayerController!),
            ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: _isTranscribing ? null : _pickVideoAndTranscribe,
            child: Text(_isTranscribing ? '转录中...' : '选择视频并转录'),
          ),
          const SizedBox(height: 20),
          Expanded(
            child: ListView.builder(
              itemCount: _transcriptionData.length,
              itemBuilder: (context, index) {
                final data = _transcriptionData[index];
                return ListTile(
                  title: Text(data['word']),
                  subtitle: Text('开始: ${data['start'].toStringAsFixed(2)}s | 结束: ${data['end'].toStringAsFixed(2)}s'),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _videoPlayerController?.dispose();
    super.dispose();
  }
}

注意事项

  • Vosk模型需自行下载(可从Vosk官方仓库获取对应语言的轻量模型),并放置到项目assets目录
  • FFmpeg包需要配置平台权限:
    • Android:在AndroidManifest.xml添加android.permission.READ_EXTERNAL_STORAGE、android.permission.WRITE_EXTERNAL_STORAGE
    • iOS:在Info.plist添加NSPhotoLibraryUsageDescription权限描述
  • 如果使用在线转写服务,可替换_transcribeAudio函数为上传音频并调用对应API的逻辑

内容的提问来源于stack exchange,提问作者Mohammed Bekele

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:06:00