[読書会]ビデオの再生と一時停止

 動画再生はアプリ開発ではよくある作業で、Flutterアプリも例外ではありません。
動画を再生する為に、Flutterチームはvideo_player プラグインを提供しています。
video_player プラグインを使って、ファイルシステム、アセット、またはインターネットから保存された動画を再生することができます。

制限事項
 現時点では、video_player プラグインは Linux と Windows では動作しません
 詳しくは、video_player パッケージをご覧ください。

 iOSでは、video_playerプラグインは AVPlayer を使って再生を処理します。Androidでは ExoPlayer を使用します。

 ここでは、video_player パッケージを使用して、基本的な再生と一時停止のコントロールを使ってインターネットからビデオをストリーミングする方法を示します。

  1. video_player パッケージの依存関係追加
  2. アプリへの権限追加
  3. VideoPlayerController による初期化
  4. ビデオプレイヤーの表示
  5. ビデオの再生と一時停止

1.video_player パッケージの依存関係追加

2.アプリへの権限追加

(pubspec.yaml)video_player パッケージの依存関係追加:
flutter pub add video_player

2-1.Android

2-2.iOS

2-3.macOS

2-4.Web

3.VideoPlayerController による初期化

 VideoPlayerController を作成し、初期化するプロセスについて触れています。

 VideoPlayerController は、ビデオ再生に必要なコントローラです。これにより、ビデオファイルを再生、停止、シーク(特定の位置に移動)などが可能になります。動画を再生するためには、コントローラの初期化が必要です。
初期化プロセスでは、動画の読み込みが行われ、コントローラビデオの操作準備を整えます。

3-1.手順

  1. StatefulWidget を作成
     状態管理が必要なため、StatefulWidget を使用します。
  2. State クラスにコントローラと Future 変数を追加
     VideoPlayerController とその初期化処理を保持する Future を定義します。
  3. initState でコントローラを作成し初期化
     initState メソッド内で VideoPlayerController を初期化し、動画の再生準備を行います。
  4. dispose メソッドでリソース解放
     使用したコントローラを適切に破棄して、リソースを解放します。

3-2.サンプルコード

VideoPlayerController による初期化(例):
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart'; // ビデオプレイヤーのパッケージをインポート

class VideoPlayerScreen extends StatefulWidget {
  const VideoPlayerScreen({super.key});

  @override
  State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  // VideoPlayerController インスタンスを保持
  late VideoPlayerController _controller;
  
  // 動画の初期化状態を追跡するための Future
  late Future<void> _initializeVideoPlayerFuture;

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

    // VideoPlayerController を作成して、ネットワーク上の動画を再生
    _controller = VideoPlayerController.networkUrl(
      Uri.parse(
        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
      ),
    );

    // コントローラの初期化を Future に保存
    _initializeVideoPlayerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    // 使用後、リソースを解放するためにコントローラを破棄
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // ビルドメソッドで動画を表示
    return FutureBuilder(
      future: _initializeVideoPlayerFuture,  // 初期化状態を確認
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          // 動画が初期化されていれば VideoPlayer ウィジェットを表示
          return AspectRatio(
            aspectRatio: _controller.value.aspectRatio,
            child: VideoPlayer(_controller),
          );
        } else {
          // 初期化中はローディングインジケータを表示
          return const Center(child: CircularProgressIndicator());
        }
      },
    );
  }
}

4.ビデオプレイヤーの表示

 ビデオを表示するためには、Flutter の video_player パッケージが提供する VideoPlayer ウィジェットを使用します。このウィジェットは、事前に VideoPlayerController によって初期化されたビデオを表示します。しかし、ビデオは通常、固定されたアスペクト比(例えば、16:9 や 4:3)で表示されるため、適切な比率で表示する必要があります。そのために、AspectRatio ウィジェットを使って、正しいアスペクト比でビデオが表示されるようにします。

 また、コントローラの初期化が完了するまでに時間がかかるため、FutureBuilder を使って初期化が完了するまでローディングスピナー(CircularProgressIndicator)を表示します。この方法で、初期化中にユーザに何らかのフィードバックを提供し、初期化が終わったらビデオを再生できるようにします。

Dart
FutureBuilder(
  //コントローラの初期化が完了するまで待つ
  future: _initializeVideoPlayerFuture,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.done) {
      //コントローラの初期化が完了した場合、ビデオのアスペクト比に合わせて表示
      return AspectRatio(
        //動画のアスペクト比に合わせる
        aspectRatio: _controller.value.aspectRatio,
        //VideoPlayer ウィジェットでビデオを表示
        child: VideoPlayer(_controller),
      );
    } else {
      //コントローラの初期化中はローディングスピナーを表示
      return const Center(
        child: CircularProgressIndicator(),
      );
    }
  },
)
  1. FutureBuilder の使用
     VideoPlayerController の初期化は非同期で行われるため、FutureBuilder を使って初期化が完了するまで UI にローディングスピナーを表示します。これにより、初期化完了後にビデオをスムーズに表示できます。
  2. AspectRatio ウィジェットの使用
     VideoPlayer ウィジェットはデフォルトでは可能な限り多くのスペースを使用しますが、動画は固定されたアスペクト比で表示する必要があるため、AspectRatio ウィジェットを使って、正しいプロポーションでビデオを表示します。
  3. 初期化が完了していないときの処理
     snapshot.connectionState が ConnectionState.done でない場合、コントローラがまだ初期化されていないことを意味するため、その間はローディングスピナーを表示してユーザーに待機状態を伝えます。

 このコードによって、ビデオが正しく初期化され、適切なアスペクト比で表示されることが保証されます。また、非同期処理による初期化の遅延があっても、ローディングインジケータを使ってユーザーに良好な体験を提供できます。

5.ビデオの再生と一時停止

 ここでは、ビデオの再生と一時停止実装します。
VideoPlayerControllerplay() メソッドを呼び出すと再生が始まり、pause() メソッドを呼び出すとビデオが一時停止します。

Dart
FloatingActionButton(
  onPressed: () {
    //`setState` の中で再生または一時停止を切り替える。
    setState(() {
      if (_controller.value.isPlaying) {
        //ビデオが再生中なら、一時停止
        _controller.pause();
      } else {
        //ビデオが一時停止中なら、再生開始
        _controller.play();
      }
    });
  },
  //ビデオの状態に応じてアイコンを切り替える。
  child: Icon(
    //再生中なら pause アイコン、一時停止中なら play アイコンを表示
    _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
  ),
)
  1. 再生と一時停止のトグル
    • setState() を使って、ビデオの状態(再生中か一時停止中か)に応じて UI を更新します。
      _controller.value.isPlaying を使って、ビデオが再生中かどうかを確認します。isPlaying が true の場合はビデオを一時停止し、false の場合はビデオを再生します。
  2. アイコンの変更
    • ビデオの再生状態に基づいて、FloatingActionButton のアイコンを切り替えます。再生中なら pause アイコンが表示され、一時停止中なら play_arrow アイコンが表示されます。
  3. ユーザー操作に応じた再生/一時停止の切り替え
    • ユーザーがボタンをタップするたびに、ビデオの再生と一時停止を切り替えることができます。これは、シンプルかつ直感的なビデオプレーヤー操作を提供します。

 このコードは、FloatingActionButton を使ってビデオの再生と一時停止を制御する仕組みを実装しています。ユーザーがボタンをタップすると、現在のビデオの状態に応じて再生または一時停止が行われ、アイコンもそれに応じて更新されます。

6.サンプルコード

Dart
import 'dart:async';
import 'package:flutter/material.dart';
//ビデオ再生のためのパッケージ
import 'package:video_player/video_player.dart';

//アプリのエントリーポイント、VideoPlayerApp ウィジェットを実行する。
void main() => runApp(const VideoPlayerApp());

class VideoPlayerApp extends StatelessWidget {
  const VideoPlayerApp({super.key});

  @override
  Widget build(BuildContext context) {
    //MaterialApp ウィジェットでアプリ全体の設定を行い、
    //VideoPlayerScreen を表示する
    return const MaterialApp(
      title: 'Video Player Demo',
      home: VideoPlayerScreen(),
    );
  }
}

//動画を再生するための StatefulWidget
class VideoPlayerScreen extends StatefulWidget {
  const VideoPlayerScreen({super.key});

  @override
  State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  //ビデオプレーヤーを制御するコントローラ
  late VideoPlayerController _controller;
  //コントローラの初期化が完了するのを待つための Future
  late Future<void> _initializeVideoPlayerFuture;

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

    //ビデオプレーヤーコントローラを作成しインターネット動画再生の設定をする。
    //インターネットからビデオを再生するために 
    //VideoPlayerController.networkUrl を使用しています。
    //他にも、ローカルのアセットやファイルからビデオを再生する方法もあります。
    _controller = VideoPlayerController.networkUrl(
      Uri.parse(
        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',
      ),
    );

    //ビデオプレーヤーの初期化を実行し、その結果の Future を保存する
    //initialize() メソッドを使ってビデオの準備を行い、
    //Future を使って初期化が完了するまで待機します。
    _initializeVideoPlayerFuture = _controller.initialize();

    //動画をループ再生に設定する
    _controller.setLooping(true);
  }

  @override
  void dispose() {
    //コントローラを dispose してリソースを解放する。
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Butterfly Video'),  //アプリバーのタイトル
      ),
      //FutureBuilder によりビデオ初期化完了までローディングスピナを表示する
      body: FutureBuilder(
        future: _initializeVideoPlayerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            //ビデオプレーヤーの初期化が完了した場合
            return AspectRatio(
              //ビデオのアスペクト比を調整する
              aspectRatio: _controller.value.aspectRatio,
              //VideoPlayer ウィジェットを使ってビデオを表示する
              child: VideoPlayer(_controller),
            );
          } else {
            //初期化中はローディングスピナーを表示
            return const Center(
              //ローディングスピナー
              child: CircularProgressIndicator(),
            );
          }
        },
      ),
      //再生中かどうかに応じて、FloatingActionButton 
      //のアイコンが再生 (play_arrow) または一時停止 (pause) 
      //に切り替わります。
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          //ボタンが押された際、ビデオの再生/一時停止を切り替える
          setState(() {
            if (_controller.value.isPlaying) {
              //再生中なら一時停止
              _controller.pause();
            } else {
              //一時停止中なら再生
              _controller.play();
            }
          });
        },
        //ビデオの再生状態に応じてアイコンを切り替える
        child: Icon(
          _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
        ),
      ),
    );
  }
}

コメントを残す