0

私の Android アプリでは、タイムラプスでビデオを録画したいと考えています。InputSurface -> MediaCodec (エンコーダー) -> MediaMuxer があります。

しかし、ビデオの速度を上げたい場合 (例: 3 倍)、非常に高いフレームレートのビデオが得られます。例: 通常の速度で 30 fps のビデオを取得します。スピードアップ (x3) すると、ビデオは 90 fps になります。

ビデオのフレームレートが高いため、私の携帯電話のビデオ プレーヤーはビデオを正常に再生できません (コンピュータのビデオ プレーヤーは問題なくビデオを再生します)。そのため、フレームレートを 60fps より低く保つために、いくつかのフレームをドロップする必要があると思います。

しかし、フレームをドロップする方法がわかりません。AVC ストリームには I、B、P フレームがあり、それらは他のフレームに依存している可能性があるため、任意にドロップすることはできません。誰でも私を助けることができますか?

4

1 に答える 1

2

ストリームをデコードして再エンコードし、途中でフレームをドロップする必要があります。60 fps ビデオのタイム スタンプを単純に半分にすると、120 fps ビデオになります。

生の H.264 ビデオ ストリームには、タイムスタンプが埋め込まれていないことに注意してください。MediaExtractor によって解析され、MediaMuxer によって追加された .mp4 ラッパーは、タイミング情報を保持します。MediaCodec インターフェースは、プレゼンテーション タイム スタンプを受け入れて生成するように見えますが、ほとんどの場合、正しいフレームに関連付けられたタイムスタンプを維持するためにタイム スタンプを渡すだけです。フレームはエンコーダーによって並べ替えることができます。(一部のエンコーダーは、タイムスタンプを調べてビット レートの目標を達成しようとするため、偽の値を渡すことはできません。)

DecodeEditEncodeの例のようなことができます。デコーダーが を呼び出すとき、releaseOutputBuffer()1 フレームおきに render 引数に「false」を渡すだけです。

画面記録用の仮想ディスプレイなど、他のソースからビデオ フレームを受け入れる場合、エンコーダの Surface をディスプレイに直接渡すことはできません。SurfaceTexture を作成し、そこからSurfaceを作成し、到着したフレームを処理する必要があります。DecodeEditEncode の例はまさにこれを行い、各フレームを GLES シェーダーで変更します。

ただし、画面の記録にはさらに問題があります。仮想ディスプレイからのフレームは、固定フレーム レートではなく、生成されたときに到着し、可変フレーム レート ビデオを生成します。たとえば、次のような一連のフレームがあるとします。

[1] [2] <10 seconds pass> [3] [4] [5] ...

ほとんどのフレームは 16.7 ミリ秒間隔 (60 fps) で到着していますが、表示が更新されていないときはギャップがあります。記録が 1 フレームおきに取得される場合、次のようになります。

[1] <10+ seconds pass> [3] [5] ...

間違ったフレームで 10 秒間一時停止してしまいます。これは、 12の間に多くの動きがあった場合に目立ちます。これを正しく機能させるには、フレーム ドロップにある程度の知性が必要です。たとえば、固定フレーム レートの 30fps ビデオを生成するために、必要に応じて前のフレームを繰り返します。

于 2015-06-22T16:00:43.507 に答える