23

次の MediaExtractor の例を機能させようとしています:

http://bigflake.com/mediacodec/ - ExtractMpegFramesTest.java (4.1、API 16 が必要)

私が抱えている問題は、outputSurface.awaitNewImage(); です。mFrameSyncObject.wait(TIMEOUT_MS)呼び出しがタイムアウトするたびにスローされる RuntimeException("frame wait timed out") を常にスローするようです。何を設定TIMEOUT_MSしても、タイムアウトが発生した直後にonFrameAvailable()常に呼び出されます。50msと30000msで試してみましたが同じです。

onFrameAvailable()スレッドがビジー状態の間は呼び出しを実行できないようです。タイムアウトが発生してスレッド コードの実行が終了すると、呼び出しを解析できますonFrameAvailable()

この例を機能させることができた人はいますか、または MediaExtractor が GL テクスチャでどのように機能するかを知っていますか?

編集: API 4.4 および 4.1.1 を搭載したデバイスでこれを試したところ、両方で同じことが起こりました。

編集2:

fadden のおかげで 4.4 で動作するようになりました。問題は、ExtractMpegFramesWrapper.runTest()呼び出されたメソッドth.join();がメイン スレッドをブロックし、onFrameAvailable()呼び出しが処理されないことでした。コメントth.join();すると、4.4で動作します。ExtractMpegFramesWrapper.runTest()メインスレッドがブロックされないように、それ自体がさらに別のスレッドで実行されるはずだったのかもしれません。

を呼び出すときに 4.1.2 にも小さな問題がありcodec.configure()、エラーが発生しました。

A/ACodec(2566): frameworks/av/media/libstagefright/ACodec.cpp:1041 CHECK(def.nBufferSize >= size) failed.
A/libc(2566): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1), thread 2625 (CodecLooper)

呼び出しの前に次を追加することで解決しました:

format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);

ただし、4.1.1 (Galaxy S2 GT-I9100) と 4.1.2 (Samsung Galaxy Tab GT-P3110) の両方で現在発生している問題は、すべてのフレームで常に info.size を 0 に設定していることです。ログ出力は次のとおりです。

loop
input buffer not available
no output from decoder available
loop
input buffer not available
no output from decoder available
loop
input buffer not available
no output from decoder available
loop
input buffer not available
no output from decoder available
loop
submitted frame 0 to dec, size=20562
no output from decoder available
loop
submitted frame 1 to dec, size=7193
no output from decoder available
loop
[... skipped 18 lines ...]
submitted frame 8 to dec, size=6531
no output from decoder available
loop
submitted frame 9 to dec, size=5639
decoder output format changed: {height=240, what=1869968451, color-format=19, slice-height=240, crop-left=0, width=320, crop-bottom=239, crop-top=0, mime=video/raw, stride=320, crop-right=319}
loop
submitted frame 10 to dec, size=6272
surface decoder given buffer 0 (size=0)
loop
[... skipped 1211 lines ...]
submitted frame 409 to dec, size=456
surface decoder given buffer 1 (size=0)
loop
sent input EOS
surface decoder given buffer 0 (size=0)
loop
surface decoder given buffer 1 (size=0)
loop
surface decoder given buffer 0 (size=0)
loop
surface decoder given buffer 1 (size=0)
loop
[... skipped 27 lines all with size=0 ...]
surface decoder given buffer 1 (size=0)
loop
surface decoder given buffer 0 (size=0)
output EOS
Saving 0 frames took ? us per frame // edited to avoid division-by-zero error

したがって、画像は保存されません。ただし、同じコードとビデオは 4.3 でも機能します。私が使用しているビデオは、「H264 - MPEG-4 AVC (avc1)」ビデオ コーデックと「MPEG AAAC Audio (mp4a)」オーディオ コーデックの .mp4 ファイルです。

他のビデオ形式も試してみましたが、4.1.x ではもっと早く死んでいるように見えますが、4.3 ではどちらも動作します。

編集3:

あなたが提案したとおりにしましたが、フレーム画像が正しく保存されているようです。ありがとうございました。

KEY_MAX_INPUT_SIZEについては、設定しないか、0, 20, 200, ... 200000000に設定してみましたが、いずれもinfo.size=0と同じ結果になりました。

レイアウトでレンダリングを SurfaceView または TextureView に設定できなくなりました。この行を置き換えてみました:

mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());

これで、surfaceTexture私のxml-layoutで定義されたSurfaceTextureはどこにありますか:

mSurfaceTexture = textureView.getSurfaceTexture();
mSurfaceTexture.attachToGLContext(mTextureRender.getTextureId());

getMessage()==nullしかし、2行目で奇妙なエラーがスローされます。ある種のビューに描画する他の方法を見つけることができませんでした。フレームを保存する代わりに、Surface/SurfaceView/TextureView にフレームを表示するようにデコーダを変更するにはどうすればよいですか?

4

1 に答える 1

16

このSurfaceTexture仕組みにより、これを正しく行うのは少し難しくなります。

ドキュメントには、フレームで利用可能なコールバックが「任意のスレッドで呼び出される」と書かれています。このSurfaceTextureクラスには、初期化時に次のことを行うコードが少しあります (行 318 )。

if (this thread has a looper) {
    handle events on this thread
} else if (there's a "main" looper) {
    handle events on the main UI thread
} else {
    no events for you
}

Looperフレームを使用できるイベントは、通常の/Handlerメカニズムを通じてアプリに配信されます。そのメカニズムは単なるメッセージ キューです。つまり、スレッドはLooperイベント ループに留まり、メッセージの到着を待機する必要があります。問題は、で寝ている場合、キューawaitNewImage()を見ていないことです。Looperそのため、イベントが到着しますが、誰もそれを見ません。最終的awaitNewImage()にタイムアウトになり、スレッドはイベント キューの監視に戻り、保留中の「新しいフレーム」メッセージをすぐに検出します。

そのため、フレームで使用可能なイベントが、 にあるスレッドとは別のスレッドに到着するようにするのがコツですawaitNewImage()。このExtractMpegFramesTest例では、これは新しく作成されたスレッド (ExtractMpegFramesWrapperクラスを参照) でテストを実行することによって行われますLooper。(何らかの理由で、CTS テストを実行するスレッドにはルーパーがあります。) フレームを使用できるイベントは、メインの UI スレッドに到着します。

更新 (「編集 3」の場合) : 「サイズ」フィールドを無視したことが役に立ったのは少し残念ですが、4.3 より前では、デバイスがどのように動作するかを予測するのは困難です。

フレームを表示したいだけの場合は、またはSurfaceから取得した をデコーダー呼び出しに渡します。そうすれば、いじる必要はまったくありません。フレームは、デコードすると表示されます。例については、 Grafikaの 2 つの「ビデオの再生」アクティビティを参照してください。SurfaceViewTextureViewMediaCodecconfigure()SurfaceTexture

本当に を通過したい場合はSurfaceTexture、CodecOutputSurface を pbuffer ではなくウィンドウ サーフェスにレンダリングするように変更する必要があります。glReadPixels()(ヘッドレス テストで使用できるように、オフスクリーン レンダリングが行われます。)

于 2014-03-17T17:19:11.917 に答える