Flutter SFTP视频播放插件sftp_video_player的使用

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

Flutter SFTP视频播放插件sftp_video_player的使用

简介

这是一个基于官方Flutter包video_player构建的简单视频播放器。该插件实现了从SFTP文件中播放视频的功能。我创建这个包的主要原因是练习,并且当我尝试使用SFTP协议从服务器流式传输视频时,我发现使用默认的Flutter video_player包存在一些困难。希望这样的包能为其他人带来便利。

功能

通过DartSSH2包实现从SFTP文件播放视频。基本功能基于video_player包,因此也可以与Chewie一起使用。

开始使用

要求

video_player包的要求相同,确保在Android设备上,AndroidManifest文件中授予了互联网权限,并且允许明文流量,除非你选择使用带有SSL证书的安全连接。该插件使用shelf来创建HTTP服务器,并通过使用SecurityContext类可以绕过此限制。

对于iOS设备,需要在Info.plist文件中添加关于NSAppTransportSecurity键的条目,包括字典条目(NSAllowsLocalNetworking和NSAllowsArbitraryLoads)。

对于Web平台,限制条件与dartssh2包使用的原生套接字相同。

为了帮助你满足这些前提条件,请遵循示例文件夹中的测试版本。

使用方法

使用SftpVideoPlayerControllerSftpFile源实例化一个video_player。由于该类扩展了VideoPlayerController,因此可以与VideoPlayer小部件或ChewieController一起使用。

final SftpFile file;
SftpVideoPlayerController? _controller;

[@override](/user/override)
void initState(){
    super.initState();
    _controller = SftpVideoPlayerController(file,
    videoFormat: StreamVideoFormat.mp4,
    serverConfig: VideoServerConfig(hostname: '127.0.0.1', port: 8080, ssl: false));
    _controller!.initialize().then((value) {
        _initialized = true;
        _controller!.play();
        setState(() {});
    });
}

[@override](/user/override)
Widget build(BuildContext context) {
    return VideoPlayer(_controller!));
}

完整示例

以下是一个完整的示例,展示了如何使用SftpVideoPlayerController来播放视频。

import 'package:dartssh2/dartssh2.dart';
import 'package:flutter/material.dart';
import 'package:sftp_video_player/sftp_video_player.dart';
import 'test_conf.dart' as test_config;

Future<void> main() async {
  final file = await test_config.getTestFile();
  runApp(MyApp(file: file));
}

class MyApp extends StatefulWidget {
  final SftpFile file;
  const MyApp({super.key, required this.file});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  SftpVideoPlayerController? _controller;
  bool _initialized = false;

  [@override](/user/override)
  void initState() {
    super.initState();
    debugPrint('Started playing: ${widget.file}');
    () async {
      final fileStat = await widget.file.stat();
      debugPrint(fileStat.toString());
    }();
    _controller = SftpVideoPlayerController(widget.file,
        videoFormat: test_config.videoFormat,
        serverConfig: test_config.serverConfig);
    _controller!.addListener(() {
      setState(() {});
    });
    _controller!.initialize().then((value) {
      _initialized = true;
      _controller!.play();
      setState(() {});
    });
  }

  [@override](/user/override)
  void dispose() {
    super.dispose();
    _controller?.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    if (_initialized) {
      return MaterialApp(
        home: Scaffold(
          backgroundColor: Colors.black,
          body: SizedBox.expand(
              child: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                AspectRatio(
                    aspectRatio: 16 / 9, child: VideoPlayer(_controller!)),
                VideoProgressIndicator(
                  _controller!,
                  allowScrubbing: true,
                ),
                Row(
                  children: [
                    IconButton(
                        onPressed: () {
                          if (_controller!.value.isPlaying) {
                            _controller!.pause();
                          } else {
                            _controller!.play();
                          }

                          setState(() {});
                        },
                        color: Colors.white,
                        icon: Icon(_controller!.value.isPlaying
                            ? Icons.pause
                            : Icons.play_arrow)),
                    IconButton(
                        color: Colors.white,
                        onPressed: () {
                          _controller!.seekTo(const Duration(seconds: 0));

                          setState(() {});
                        },
                        icon: const Icon(Icons.refresh))
                  ],
                )
              ],
            ),
          )),
        ),
      );
    } else {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }
  }
}

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

1 回复

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


关于在Flutter中使用sftp_video_player插件来播放SFTP服务器上的视频,虽然直接提供一个完整的、现成的SFTP视频播放插件可能并不现实(因为通常Flutter社区中的插件可能没有直接支持这种特定用例的),但我可以提供一个概念性的指导,以及如何使用Flutter结合Dart的SSH库来实现从SFTP服务器下载视频并在Flutter应用中播放的大致流程。

请注意,这个过程将涉及几个步骤,包括连接到SFTP服务器、下载视频文件、以及使用Flutter的视频播放插件来播放下载的视频。

步骤 1: 添加依赖

首先,你需要在pubspec.yaml文件中添加必要的依赖。由于Flutter本身没有直接的SFTP库,你可以使用Dart的package:ssh库来与SFTP服务器交互,以及一个视频播放插件如chewievideo_player来播放视频。

dependencies:
  flutter:
    sdk: flutter
  video_player: ^2.2.10 # 请检查最新版本
  chewie: ^1.2.2 # 请检查最新版本
  ssh: ^0.29.0 # 请检查最新版本,注意这个库可能不支持SFTP直接操作,需要额外处理

步骤 2: 连接到SFTP服务器并下载视频

由于package:ssh库主要用于SSH连接,对于SFTP操作,你可能需要使用package:dart_ssh2或其他支持SFTP的库。这里是一个简化的示例,展示如何使用dart_ssh2连接到SFTP服务器并下载文件(注意,dart_ssh2可能不是Flutter官方支持的库,你可能需要在VM环境下运行这部分代码,或者寻找其他Flutter兼容的SFTP解决方案)。

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

Future<void> downloadVideoFromSFTP(String sftpHost, int sftpPort, String sftpUsername, String sftpPassword, String remoteFilePath, String localFilePath) async {
  var client = await startClient(sftpHost, sftpPort,
      username: sftpUsername,
      password: sftpPassword,
      hostKeyVerifier: (host, port, key) => true // 注意:在生产环境中,你应该验证主机密钥
  );

  var sftp = client.startSftpSession();
  await sftp.get(remoteFilePath, localFilePath);
  await sftp.quit();
  await client.disconnect();
}

步骤 3: 使用Flutter的视频播放插件播放视频

一旦视频文件被下载到本地存储,你就可以使用video_playerchewie来播放它。

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

class VideoPlayerScreen extends StatefulWidget {
  @override
  _VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  VideoPlayerController _controller;
  ChewieController _chewieController;

  @override
  void initState() {
    super.initState();
    _initializeVideoPlayer().then((_) {
      setState(() {});
    });
  }

  Future<void> _initializeVideoPlayer() async {
    var directory = await getApplicationDocumentsDirectory();
    String localVideoPath = directory.path + '/downloaded_video.mp4'; // 确保这是你的下载路径

    _controller = VideoPlayerController.file(File(localVideoPath))
      ..initialize().then((_) {
        setState(() {});
      });

    _chewieController = ChewieController(
      videoPlayerController: _controller,
      aspectRatio: _controller.value.aspectRatio,
      autoPlay: false,
      looping: false,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Video Player'),
      ),
      body: Center(
        child: _controller.value.isInitialized
            ? Chewie(controller: _chewieController)
            : Container(
                child: CircularProgressIndicator(),
              ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _controller.value.isPlaying
                ? _controller.pause()
                : _controller.play();
          });
        },
        child: Icon(
          _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
        ),
      ),
    );
  }

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

注意

  1. SFTP连接:上述SFTP连接代码可能需要在VM环境下运行,因为Flutter对某些原生库的支持有限。你可能需要寻找一个完全支持Flutter的SFTP库,或者在Flutter应用中调用一个后台服务来处理SFTP操作。

  2. 视频缓存:对于大型视频文件,考虑实现下载进度显示和缓存管理,以提高用户体验。

  3. 错误处理:在生产代码中,添加适当的错误处理,包括网络错误、文件读写错误等。

  4. 权限:确保应用有权限访问存储,特别是在iOS上,你可能需要在Info.plist中添加相应的权限声明。

这个示例提供了一个基本框架,你可能需要根据自己的具体需求进行调整和扩展。

回到顶部