簡體   English   中英

SurfaceTexture的onFrameAvailable()方法總是被調用太晚了

[英]SurfaceTexture's onFrameAvailable() method always called too late

我正在嘗試使用以下MediaExtractor示例:

http://bigflake.com/mediacodec/ - ExtractMpegFramesTest.java(需要4.1,API 16)

我遇到的問題是outputSurface.awaitNewImage(); 似乎總是拋出RuntimeException(“幀等待超時”),每當mFrameSyncObject.wait(TIMEOUT_MS)調用超時時拋出它。 無論我將TIMEOUT_MS設置為什么, onFrameAvailable()總是在超時發生后立即被調用。 我嘗試了50毫秒和30000毫秒,它是相同的。

似乎在線程繁忙時無法完成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()本身應該在另一個線程上運行,所以主線程沒有被阻止。

在調用codec.configure()時4.1.2上還有一個小問題,它給出了錯誤:

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(三星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布局定義的表面紋理:

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

但它在第二行引發了一個奇怪的錯誤, getMessage()==null 我找不到任何其他方法來繪制某種視圖。 如何更改解碼器以在Surface / SurfaceView / TextureView上顯示幀而不是保存它們?

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測試的線程有一個looper。)可用幀的事件到達主UI線程。

更新(對於“編輯3”) :我有點難過忽略“大小”字段有幫助,但4.3之前很難預測設備的行為方式。

如果您只想顯示框架,請將SurfaceViewTextureView獲得的Surface傳遞給MediaCodec解碼器configure()調用。 然后你根本不必弄亂SurfaceTexture - 在解碼時會顯示幀。 有關示例,請參閱Grafika中的兩個“播放視頻”活動。

如果您真的想要通過SurfaceTexture ,則需要更改CodecOutputSurface以渲染到窗口表面而不是pbuffer。 (屏幕外渲染已完成,因此我們可以在無頭測試中使用glReadPixels() 。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM