Flutter字幕显示插件subtitle的使用

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

Flutter字幕显示插件subtitle的使用

Flutter 开发中,字幕显示是一个常见的需求,例如在视频播放、语言学习等场景。subtitle 插件提供了一种简单且高效的方式来处理多种字幕文件格式,并支持高度定制化。本文将详细介绍如何使用 subtitle 插件,并提供完整的示例代码。

介绍

subtitle 是一个用于处理多种字幕文件格式的库,具有以下特点:

  • 支持多种字幕格式:WebVTT、SRT(SubRip)、TTML(Timed Text Markup Language)、DFXP(Distribution Format Exchange Profile)和自定义字幕格式。
  • 高度可定制化(90%),支持 Sound Null Safety
  • 创建者:Muhammad Hasan Alasady,遵循 MIT LICENSE

使用方法

1. 网络字幕

网络字幕可以通过两种方式加载:使用 SubtitleController 或者 SubtitleProviderSubtitleParser

使用 SubtitleController

import 'package:subtitle/subtitle.dart';

void main() async {
  var url = Uri.parse(
      'https://brenopolanski.github.io/html5-video-webvtt-example/MIB2-subtitles-pt-BR.vtt');
  var controller = SubtitleController(
    provider: SubtitleProvider.fromNetwork(url),
  );

  await controller.initial();
  
  // 打印所有字幕
  printResult(controller.subtitles);
}

void printResult(List<Subtitle> subtitles) {
  subtitles.sort((s1, s2) => s1.compareTo(s2));
  for (var result in subtitles) {
    print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
  }
}

使用 SubtitleProviderSubtitleParser

import 'package:subtitle/subtitle.dart';

void main() async {
  var url = Uri.parse(
      'https://brenopolanski.github.io/html5-video-webvtt-example/MIB2-subtitles-pt-BR.vtt');

  SubtitleProvider provider = NetworkSubtitle(url);
  SubtitleObject object = await provider.getSubtitle();
  SubtitleParser parser = SubtitleParser(object);

  // 打印解析后的字幕
  printResult(parser.parsing());
}

void printResult(List<Subtitle> subtitles) {
  subtitles.sort((s1, s2) => s1.compareTo(s2));
  for (var result in subtitles) {
    print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
  }
}

2. 文件字幕

文件字幕的加载方式与网络字幕类似,只需要替换 SubtitleProviderFileSubtitleSubtitleProvider.fromFile()

import 'package:subtitle/subtitle.dart';
import 'dart:io';

void main() async {
  var file = File('example/data.vtt');
  var controller = SubtitleController(
    provider: FileSubtitle(file),
  );

  await controller.initial();
  
  // 打印所有字幕
  printResult(controller.subtitles);
}

void printResult(List<Subtitle> subtitles) {
  subtitles.sort((s1, s2) => s1.compareTo(s2));
  for (var result in subtitles) {
    print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
  }
}

3. 字符串字幕

字符串字幕可以直接从字符串加载,使用 StringSubtitleSubtitleProvider.fromString()

import 'package:subtitle/subtitle.dart';

const vttData = '''WEBVTT FILE

5
00:00:19.000 --> 00:00:24.000
Which is why we are bringing TV, internet and phone together in <c.highlight>one</c> super package

3
00:00:11.000 --> 00:00:14.000 A:end
Phone conversations where people truly <c.highlight>connect</c>

1
00:00:03.500 --> 00:00:05.000 D:vertical A:start
Everyone wants the most from life

2
00:00:06.000 --> 00:00:09.000 A:start
Like internet experiences that are rich <b>and</b> entertaining


4
00:00:14.500 --> 00:00:18.000
Your favourite TV programmes ready to watch at the touch of a button

6
00:00:24.500 --> 00:00:26.000
<c.highlight>One</c> simple way to get everything

7
00:00:26.500 --> 00:00:27.500 L:12%
UPC

8
00:00:28.000 --> 00:00:30.000 L:75%
Simply for <u>everyone</u>
''';

void main() async {
  var controller = SubtitleController(
    provider: SubtitleProvider.fromString(
      data: vttData,
      type: SubtitleType.vtt,
    ),
  );

  await controller.initial();
  
  // 打印所有字幕
  printResult(controller.subtitles);
}

void printResult(List<Subtitle> subtitles) {
  subtitles.sort((s1, s2) => s1.compareTo(s2));
  for (var result in subtitles) {
    print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
  }
}

4. Flutter 资源文件

对于 Flutter 项目中的资源文件,可以通过扩展 SubtitleProvider 并使用 rootBundle 来加载字幕文件。

import 'package:subtitle/subtitle.dart';
import 'package:flutter/services.dart' show rootBundle;

class AssetSubtitle extends SubtitleProvider {
  final String path;
  final SubtitleType? type;

  const AssetSubtitle(this.path, {this.type});

  @override
  Future<SubtitleObject> getSubtitle() async {
    final data = await rootBundle.loadString(path);
    final ext = extension(path);
    final subtitleType = this.type ?? getSubtitleType(ext);

    return SubtitleObject(data: data, type: subtitleType);
  }
}

void main() async {
  var path = 'files/subtitles/1.vtt';
  var controller = SubtitleController(
    provider: AssetSubtitle(path),
  );

  await controller.initial();
  
  // 打印所有字幕
  printResult(controller.subtitles);
}

void printResult(List<Subtitle> subtitles) {
  subtitles.sort((s1, s2) => s1.compareTo(s2));
  for (var result in subtitles) {
    print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
  }
}

组件介绍

  • SubtitleProvider: 简化了从多个来源获取字幕文件数据的过程。
  • SubtitleRepository: 直接与平台交互以获取或下载所需数据并提交给提供者。
  • SubtitleParser: 用于分析和转换字幕文件,使其成为可查看和使用的软件对象。
  • CustomSubtitleParser: 可自定义的字幕解析器,支持自定义正则表达式。
  • SubtitleRegexObject: 提供解码字幕文件内容所需的正则表达式,包括 WebVTT、SubRip、TTML/DFXP 和自定义正则表达式。
  • SubtitleObject: 存储字幕文件数据及其格式类型。
  • SubtitleController: 控制字幕的准备、解码、排序和搜索。

示例

以下是处理 TTML 格式的完整示例:

import 'package:subtitle/subtitle.dart';

const ttmlText = '''
<?xml version="1.0" encoding="UTF-8"?>
<tt xmlns="http://www.w3.org/ns/ttml">
  <head>
    <metadata xmlns:ttm="http://www.w3.org/ns/ttml#metadata">
      <ttm:title>Timed Text TTML Example</ttm:title>
      <ttm:copyright>The Authors (c) 2006</ttm:copyright>
    </metadata>
    <styling xmlns:tts="http://www.w3.org/ns/ttml#styling">
      <style xml:id="s1"
        tts:color="white"
        tts:fontFamily="proportionalSansSerif"
        tts:fontSize="22px"
        tts:textAlign="center"
      />
      <style xml:id="s2" style="s1" tts:color="yellow"/>
      <style xml:id="s1Right" style="s1" tts:textAlign="end" />
      <style xml:id="s2Left" style="s2" tts:textAlign="start" />
    </styling>
    <layout xmlns:tts="http://www.w3.org/ns/ttml#styling">
      <region xml:id="subtitleArea"
        style="s1"
        tts:extent="560px 62px"
        tts:padding="5px 3px"
        tts:backgroundColor="black"
        tts:displayAlign="after"
      />
    </layout> 
  </head>
  <body region="subtitleArea">
    <div>
      <p xml:id="subtitle1" begin="0.76s" end="3.45s">
        It seems a paradox, does it not,
      </p>
      <p xml:id="subtitle2" begin="5.0s" end="10.0s">
        that the image formed on<br/>
        the Retina should be inverted?
      </p>
      <p xml:id="subtitle3" begin="10.0s" end="16.0s" style="s2">
        It is puzzling, why is it<br/>
        we do not see things upside-down?
      </p>
      <p xml:id="subtitle4" begin="17.2s" end="23.0s">
        You have never heard the Theory,<br/>then, that the Brain also is inverted?
      </p>
      <p xml:id="subtitle5" begin="23.0s" end="27.0s" style="s2">
        No indeed! What a beautiful fact!
      </p>
    </div>
  </body>
</tt>
''';

Future<void> main() async {
  var sp = SubtitleProvider.fromString(
    data: ttmlText,
    type: SubtitleType.ttml,
  );
  var sc = SubtitleController(provider: sp);
  await sc.initial();
  var s = sc.durationSearch(Duration(seconds: 12));
  print(s?.data ?? 'No subtitle in range of provided duration.');
}

void printResult(List<Subtitle> subtitles) {
  subtitles.sort((s1, s2) => s1.compareTo(s2));
  for (var result in subtitles) {
    print('(${result.index}) Start: ${result.start}, end: ${result.end} [${result.data}]');
  }
}

通过以上示例,您可以根据需要选择不同的字幕来源,并轻松地集成到您的 Flutter 应用程序中。希望这些信息对您有所帮助!


更多关于Flutter字幕显示插件subtitle的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter字幕显示插件subtitle的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用subtitle插件来显示字幕的示例代码。subtitle插件通常用于在视频播放时显示同步字幕。虽然没有一个广泛认可的、名为subtitle的官方Flutter插件,但我们可以基于常见的字幕显示需求,使用video_player插件配合自定义字幕显示逻辑来实现类似功能。

首先,确保你已经添加了video_playerchewie(一个基于video_player的封装,提供了更丰富的播放控制UI)插件到你的pubspec.yaml文件中:

dependencies:
  flutter:
    sdk: flutter
  video_player: ^2.2.10  # 请检查最新版本
  chewie: ^1.2.2  # 请检查最新版本,用于视频播放UI

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

接下来是一个完整的示例代码,展示了如何在Flutter应用中播放视频并显示同步字幕:

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Video Player with Subtitles'),
        ),
        body: Center(
          child: VideoPlayerWithSubtitles(),
        ),
      ),
    );
  }
}

class VideoPlayerWithSubtitles extends StatefulWidget {
  @override
  _VideoPlayerWithSubtitlesState createState() => _VideoPlayerWithSubtitlesState();
}

class _VideoPlayerWithSubtitlesState extends State<VideoPlayerWithSubtitles> {
  late VideoPlayerController _controller;
  late ChewieController _chewieController;
  late List<String> subtitles;
  late List<Duration> subtitleTimes;

  @override
  void initState() {
    super.initState();
    // 初始化视频控制器
    _controller = VideoPlayerController.network(
      'https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_20mb.mp4',
    )
    ..initialize().then((_) {
      // 初始化字幕和时间点
      setState(() {
        subtitles = [
          'Hello, this is the first subtitle.',
          'This is the second subtitle appearing later.',
          'Final subtitle here.',
        ];
        subtitleTimes = [
          Duration(seconds: 5),
          Duration(seconds: 15),
          Duration(seconds: 30),
        ];
      });
    });

    // 初始化Chewie控制器
    _chewieController = ChewieController(
      _controller,
      aspectRatio: _controller.value.aspectRatio,
      autoPlay: false,
      looping: false,
      customSource: null,
    );

    // 监听视频播放进度
    _controller.addListener(() {
      if (_controller.value.position >= _getCurrentSubtitleTime()) {
        showSubtitle();
      }
    });
  }

  // 获取当前应该显示的字幕时间
  Duration _getCurrentSubtitleTime() {
    for (int i = 0; i < subtitleTimes.length; i++) {
      if (_controller.value.position >= subtitleTimes[i] &&
          (i == subtitleTimes.length - 1 ||
              _controller.value.position < subtitleTimes[i + 1])) {
        return subtitleTimes[i];
      }
    }
    return Duration.zero;
  }

  // 显示字幕的方法(这里简单使用Snackbar,实际项目中可能需要更复杂的UI)
  void showSubtitle() {
    final subtitleText = subtitles[subtitleTimes.indexOf(_getCurrentSubtitleTime())];
    ScaffoldMessenger.of(context).showSnackbar(
      SnackBar(
        content: Text(subtitleText),
        duration: Duration(seconds: 5),  // 根据需要调整显示时间
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return _controller.value.isInitialized
        ? Chewie(
            controller: _chewieController,
          )
        : Center(child: CircularProgressIndicator());
  }

  @override
  void dispose() {
    _controller.dispose();
    _chewieController.dispose();
    super.dispose();
  }
}

在这个示例中,我们做了以下几件事:

  1. 使用VideoPlayerController来加载和播放视频。
  2. 使用ChewieController来提供视频播放的UI控制。
  3. 初始化了一个包含字幕文本和对应显示时间的列表。
  4. 通过监听视频播放进度,在达到指定时间点时显示相应的字幕。

请注意,这里的字幕显示使用了Snackbar,这在实际应用中可能不是最佳的用户体验。你可能需要创建一个自定义的UI组件来更优雅地显示字幕。此外,这个例子假设字幕和时间是预先定义好的,实际应用中你可能需要从字幕文件(如SRT或SSA格式)中解析这些数据。

回到顶部