再生中に動画/映画がフリーズする

皆さん、こんにちは。いつか早い段階で解決したいと本当に願っている問題があります。

RPG Makerのゲーム制作において、カットシーン用にムービーを再生しています。残念ながら稀ですが、動画が完全にフリーズすることがあります。ダイアログを進められない、メニューに入れない、何も操作できません。完全に固まってしまい、ゲーム全体を終了させるしかありません。

これを軽減する方法はありますか?稀にしか発生しないとしても、私の好みとしてはまだ多すぎます。

プロジェクトはかなり野心的であることを認めます。これらは小さな動画ではなく、高品質な1280x720のカットシーンです。もしこれが原因であれば絶望しますが、動画の高品質さを維持しつつ、フリーズの可能性を完全に防ぎたいと思っています。

プラグイン、調整方法、アドバイスなど、どのような情報でも助かります。ありがとうございます!

私の理解では、MVの「映画を再生」コマンドは、動画が終了したことを報告するまでイベントを待機状態にします。動画が途中でフリーズした場合、イベントはただ待ち続けるため、イベント自体が完了しない限り、メニューやダイアログも進行できません。これがロックアップの原因になっているのではないかと考えています。

発生時、動画の途中画面で止まるのか、それとも動画が終了した後にゲーム全体が固まるのでしょうか? また、常に同じ動画で発生するのか、他のPCでのデプロイ済みビルドでも同様の現象が起きるのかも知りたいです。プレイテスト中にキャッチできる場合は、F8キーでコンソールを開き、表示される赤色のエラーメッセージが原因特定の手がかりになる可能性があります。

もし動画の途中でのフリーズであれば、問題のある動画の1つを1280x720の解像度を維持しつつ、ビットレートを下げて再エンコードしてみることをお勧めします。解像度を諦める必要はなく、ファイル自体がストールの原因かどうかを確認できるかもしれません。また、LTN GamesのSkip Videoのようなスキッププラグインを導入すれば、プレイヤーが脱出できる安全策として機能するかもしれませんが、ストールした動画での動作は未検証です。

動画が終了した後に画面が凍結する場合は、ランダムなグラフィックフリーズを引き起こす既知のMVバグが存在します。Tamschi氏にはその修正パッチがあり、Freeze Fix pageでは現在MAC_High_Hz_Fixesを使用することが推奨されています。

返信ありがとうございます!

  1. はい、動画の途中で止まります。通常は再生開始から1〜2秒後です。

  2. 一貫性はなく、再生するすべての動画で発生する可能性があります。私には約16本の短いイントロカットシーンクリップがあり、これらはほぼ連続して再生されます。各NPCとの短い会話の後に再生されます。

  3. はい、説明された動作が正しいです。背景音楽や効果音が流れている場合、それらは再生を続けます。動画だけがフリーズします。

  4. 他のPCでのデプロイでも発生することを確認しました。

  5. F8メニューを表示するために再度遭遇しましたが、表示されている赤いメッセージはこれだけです:

    私の知る限り、プロジェクト内にそのようなファイルはどこにもありません。また、フリーズせずに再テストプレイした際にも発生していたため、これは関連していないと思われますか?

  6. この修正のために動画の品質を少しでも下げるのは避けたいのですが、もしそれが唯一の方法ならどうすればよいでしょうか?そのサイズの動画に対して推奨されるビットレートは何ですか?

  7. LTN Gamesの「Skip Video」プラグインを使用していますが、フリーズ中に動画をスキープットしようとすると機能しないことを確認しました。何もポップアップしません。

  8. MAC_High_Hz_Fixesを実行しながら試みましたが、フリーズに遭遇しました。残念ながら、これが私が求めている解決策ではないようです…

とりあえず低いビットレートで試してみます。アドバイスありがとうごさいました。これでさらに明確になることがあればお知らせください。

動画の再生中に1〜2秒間停止しますが、音楽は継続して流れているため、動画自体がフリーズしており、「映画を再生」機能が永遠にそれを待っている可能性はないかと考えています。次にプレイテストでフリーズした際、F8キーを押して以下のコマンドを1つずつ試してみてください:

Graphics._video.currentTime
Graphics._video.currentTime += 0.1
Graphics._onVideoEnd()

最初の行を数秒間隔で2回実行してください。もし値が変わらない場合、動画が固まっています。2番目の行は動画を少しだけ進めます。これで再生が再開するようであれば、カットシーンをスキップせずに自動的に同じ処理を行うプラグインを作成できるかもしれません。最後の行は、MVで動画が終了した際に実行される処理です。これを実行後にゲームが再開する場合、固まった動画を強制終了する小さなプラグインをセーフティネットとして機能させることができるでしょう。

triacontane氏が同様のランダムな動画フリーズが発生するプロジェクトを調査した際、Chromeでは正常に再生されていたものの、ゲーム内ではフリーズしていました。これはMVのNW.jsが原因である可能性を示唆しています(GitHub issue)。MVには古いバージョンのNW.jsが含まれており、それを更新するためのガイドがあります。私はこのフリーズ現象に対して実際にその方法を試したことはありませんが、まずガイドに従ってバックアップ手順を実施することをお勧めします。

ビットレートについては確実な数値はありません。YouTubeのアップロードガイドによると、720p・30fpsの場合、約5Mbpsとされています。まずは1つの動画でこの値から始めて、何度か再生してみてください。フリーズしないようであれば、ビットレートを徐々に上げていき、どこからフリーズが始まるかを確認できます。

Actor1 1.pngのエラーは、おそらく関連性がないと思われます。なぜなら、フリーズしない実行時にもこのエラーが表示されるからです。

フリーズを押すと、currentTime の値はそのままで動画が止まったので、このコマンドを試してみたら成功しました!動画が再開し、その後はゲームも通常通り続きました。これはプラグインとして試す価値があると思います。

この修正を施したプラグインはどのように動作するのでしょうか?どのような見た目になるのでしょうか?

このプラグインは、コンソールで行った操作を自分自身で実行します。映画が再生されている間、0.25秒ごとに動画の時間をチェックします。もし1秒間時間が進んでいなければ、コマンドと同様に0.1秒分だけ前方にシフトします。もしそれでも5回連続で固まったままの場合、MVが動画終了時に実行するのと同じ方法で動画を停止し、イベントがロックすることなく続行できるようにします。

これを js/plugins フォルダ内に VideoStallFix.js として保存してください(ファイル名は完全に一致させる必要があります)。その後、プラグインマネージャーで有効にします:

/*:
 * @plugindesc Play Movieコマンドでゲームがフリーズしないよう、固まった動画を少し進めます。
 * @author Baz
 *
 * @param Stall Time
 * @desc 動画が同じフレームで止まり、補正されるまでのミリ秒数。
 * @type number
 * @min 250
 * @default 1000
 *
 * @param Nudge Amount
 * @desc 各補正で動画を進める秒数。
 * @type number
 * @decimals 2
 * @min 0.01
 * @default 0.1
 *
 * @param Max Nudges
 * @desc イベントが継続できるよう動画を停止する前に試みる補正の最大回数。0の場合は停止しません。
 * @type number
 * @min 0
 * @default 5
 *
 * @help
 * Play Movieから再生中の動画の間、0.25秒ごとに動画の時間が進行しているか確認します。
 * もし固まった場合、プラグインが少し前方に進めます。Max Nudgesで指定した回数試しても
 * 固まったままの場合、イベントが継続できるよう動画を停止します。
 */

(function() {
    var params = PluginManager.parameters('VideoStallFix');
    var stallTime = Number(params['Stall Time'] || 1000);
    var nudgeAmount = Number(params['Nudge Amount'] || 0.1);
    var maxNudges = Number(params['Max Nudges'] || 5);

    var lastTime = -1;
    var lastChange = 0;
    var nudges = 0;

    setInterval(function() {
        var video = Graphics._video;
        if (!video || !Graphics._isVideoVisible() || video.paused || video.ended) {
            lastTime = -1;
            nudges = 0;
            return;
        }
        var now = performance.now();
        if (video.currentTime !== lastTime) {
            lastTime = video.currentTime;
            lastChange = now;
            nudges = 0;
            return;
        }
        if (now - lastChange < stallTime) return;
        if (maxNudges > 0 && nudges >= maxNudges) {
            video.pause();
            Graphics._onVideoEnd();
            return;
        }
        video.currentTime += nudgeAmount;
        lastTime = video.currentTime;
        lastChange = now;
        nudges++;
    }, 250);
})();

調整したい場合は、これら3つの数値はすべてプラグインパラメータとして設定可能です。

うまくいったようです!変数はそのままにして再実行しました。すぐに映像がフリーズしましたが、すぐに元に戻りました!もう少し変数をいじって、より早く検知してシームレスに見えるようにしてみます。

これほど感謝していることはありません。本当にこのプロジェクトを救うことになったかもしれません。本当にありがとうございます!