sample_slow_sound_movie

プロジェクトの概要

このFlutterアプリでは、iOSの AVPlayer を活用して動画再生を行い、FlutterのUIから再生速度を制御できます。

Code

実装のポイント

  • iOS側でAVPlayerを活用し、Flutterのみでは困難なカスタマイズを実現。
  • Flutter側でスロー再生速度を指定し、AVPlayerに反映。
  • 動画を中央表示するため、Flutter側から動画サイズを制御。
  • AVPlayerLayerを利用したカスタムプレイヤーの構築

iOSネイティブ実装

FlutterからiOSのAVPlayerを制御するため、 MethodChannel を用いて実装します。

AppDelegate.swift のコード

import UIKit
import Flutter
import AVFoundation

@main
@objc class AppDelegate: FlutterAppDelegate {
    var videoPlayer: AVPlayer?
    var playerLayer: AVPlayerLayer?
    var videoWindow: UIWindow?
    
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        let controller = window?.rootViewController as! FlutterViewController
        let channel = FlutterMethodChannel(name: "audio_processor", binaryMessenger: controller.binaryMessenger)

        channel.setMethodCallHandler { [weak self] (call, result) in
            guard let self = self else { return }
            if call.method == "Slow" || call.method == "NotSlow" {
                guard let args = call.arguments as? [String: Any],
                      let videoPath = args["videoPath"] as? String,
                      let speed = args["speed"] as? Float,
                      let x = args["x"] as? CGFloat,
                      let y = args["y"] as? CGFloat,
                      let width = args["width"] as? CGFloat,
                      let height = args["height"] as? CGFloat else {
                    result(FlutterError(code: "INVALID_ARGUMENTS", message: "Invalid arguments", details: nil))
                    return
                }
                self.playVideoWithLayer(mode: call.method, videoPath: videoPath, speed: speed, x: x, y: y, width: width, height: height, result: result)
            } else if call.method == "Stop" {
                self.stopVideo(result: result)
            } else {
                result(FlutterMethodNotImplemented)
            }
        }
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

動画の停止処理

func stopVideo(result: @escaping FlutterResult) {
    DispatchQueue.main.async {
        self.videoPlayer?.pause()
        self.videoPlayer = nil
        self.playerLayer?.removeFromSuperlayer()
        self.playerLayer = nil
        self.videoWindow?.isHidden = true
        self.videoWindow = nil
        result("停止完了")
    }
}

オーディオミックスの設定

このアプリでは、再生速度に応じた適切なオーディオ処理を行うために、 AVMutableAudioMix を使用しています。速度変更時の音声の自然な変化を確保するため、audioTimePitchAlgorithm を適用し、適切なアルゴリズムを選択します。

private func configureAudioMix(for player: AVPlayer, speed: Float) {
    guard let currentItem = player.currentItem,
          let audioTrack = currentItem.asset.tracks(withMediaType: .audio).first else { return }
    
    let audioMix = AVMutableAudioMix()
    let audioParameters = AVMutableAudioMixInputParameters(track: audioTrack)
    
    // 音量の設定(デフォルトは1.0)
    audioParameters.setVolume(1.0, at: .zero)
    
    // 音声のピッチ調整アルゴリズムを適用
    if speed < 1.0 {
        audioParameters.audioTimePitchAlgorithm = .varispeed  // 低速時にピッチを自然に調整
    } else {
        audioParameters.audioTimePitchAlgorithm = .lowQualityZeroLatency  // 通常速度時は低遅延優先
    }
    
    audioMix.inputParameters = [audioParameters]
    
    DispatchQueue.main.async {
        currentItem.audioMix = audioMix
    }
}

この実装により、スロー再生時には音声の高さが変わらないように調整し、通常速度では低遅延のオーディオ処理を優先することができます。

まとめ

FlutterでiOSのAVPlayerを活用し、動画のカスタマイズを行う方法について解説しました。FlutterのUIから動画のスピードや位置を制御し、iOSネイティブのAVPlayerで動画を再生することで、柔軟なカスタマイズが可能になります。

🚨 Androidは動画と音声を別々に設定する必要があるため、現時点では未対応です。

貴重なお時間をいただきありがとうございました。