我的目標是使用Android MediaCodec解碼視頻流,然後使用輸出圖像進行本機代碼中的進一步圖像處理。使用硬件加速的本機代碼訪問衝突Android MediaCodec解碼器
平臺:華碩tf700t android 4.1.1。 測試流:H.264全高清@ 24 frm/s
隨着Tegra-3 SoC的內部,我計算硬件支持視頻解碼。在功能上,我的應用程序的行爲如預期:我確實可以訪問解碼器圖像 並正確處理它們。但是,我經歷了非常高的解碼器CPU負載。
在下面的實驗中,過程/螺紋加載通過adb shell中的「top -m 32 -t」來測量。爲了從「頂部」獲得可靠的輸出,通過運行幾個線程永久以最低優先級循環,所有4個cpu內核被強制激活。這通過反覆執行「cat/sys/devices/system/cpu/cpu [0-3]/online」來確認。爲了簡單起見,只有視頻解碼,沒有音頻;並且沒有時序控制,所以解碼器運行得儘可能快。
第一個實驗:運行應用程序,調用JNI處理函數,但所有進一步處理調用都被註釋掉。結果:
- 吞吐量:25 FRM /秒
- 1%應用過程/系統/ bin中的螺紋Binder_3的
- 24%負載/媒體服務器
它的螺紋VideoDecoder的負載似乎解碼速度是CPU限制的(四核CPU的25%)... 啓用輸出處理時,解碼圖像是正確的,並且應用程序工作。唯一的問題:用於解碼的CPU負載過高。
經過大量的實驗後,我考慮給MediaCodec一個表面來繪製其結果。在所有其他方面,代碼是相同的。結果:
- 吞吐量55 FRM /秒(很好!!)的應用程序的線程的VideoDecoder
- 2%負載 過程/系統/ bin中/媒體服務器的螺紋媒體服務器的
- 1%負載
確實,視頻顯示在提供的Surface上。由於幾乎沒有任何CPU負載,這必須是硬件加速...
看來,MediaCodec是隻使用硬件加速,如果提供表面?
到目前爲止,這麼好。我已經傾向於將Surface用作解決方案(不是必需的,但在某些情況下甚至是非常好的)。但是,如果提供表面,我無法訪問輸出圖像!結果是本機代碼中的訪問衝突。
這真令我困惑!我沒有看到訪問限制的任何概念,或文檔http://developer.android.com/reference/android/media/MediaCodec.html中的任何內容。 也沒有在這方面提到在谷歌I/O介紹http://www.youtube.com/watch?v=RQws6vsoav8。
所以:如何使用硬件加速的Android MediaCodec解碼器和訪問本地代碼中的圖像?如何避免訪問違規?任何幫助都附帶了!還有任何解釋或提示。
我很肯定MediaExtractor和MediaCodec使用得當,因爲應用程序 功能正常(只要我沒有提供Surface)。 它仍然是相當實驗性,和良好的API設計是待辦事項列表;-)
注意兩個實驗之間的唯一區別是可變的mSurface上:空或「mDecoder.configure的實際表面 (mediaFormat ,mSurface,null,0);「
初始化代碼:
mExtractor = new MediaExtractor();
mExtractor.setDataSource(mPath);
// Locate first video stream
for (int i = 0; i < mExtractor.getTrackCount(); i++) {
mediaFormat = mExtractor.getTrackFormat(i);
String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
Log.i(TAG, String.format("Stream %d/%d %s", i, mExtractor.getTrackCount(), mime));
if (streamId == -1 && mime.startsWith("video/")) {
streamId = i;
}
}
if (streamId == -1) {
Log.e(TAG, "Can't find video info in " + mPath);
return;
}
mExtractor.selectTrack(streamId);
mediaFormat = mExtractor.getTrackFormat(streamId);
mDecoder = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));
mDecoder.configure(mediaFormat, mSurface, null, 0);
width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
Log.i(TAG, String.format("Image size: %dx%d format: %s", width, height, mediaFormat.toString()));
JniGlue.decoutStart(width, height);
解碼器環路(在單獨的線程中運行):
ByteBuffer[] inputBuffers = mDecoder.getInputBuffers();
ByteBuffer[] outputBuffers = mDecoder.getOutputBuffers();
while (!isEOS && !Thread.interrupted()) {
int inIndex = mDecoder.dequeueInputBuffer(10000);
if (inIndex >= 0) {
// Valid buffer returned
int sampleSize = mExtractor.readSampleData(inputBuffers[inIndex], 0);
if (sampleSize < 0) {
Log.i(TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM");
mDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
mDecoder.queueInputBuffer(inIndex, 0, sampleSize, mExtractor.getSampleTime(), 0);
mExtractor.advance();
}
}
int outIndex = mDecoder.dequeueOutputBuffer(info, 10000);
if (outIndex >= 0) {
// Valid buffer returned
ByteBuffer buffer = outputBuffers[outIndex];
JniGlue.decoutFrame(buffer, info.offset, info.size);
mDecoder.releaseOutputBuffer(outIndex, true);
} else {
// Some INFO_* value returned
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.i(TAG, "RunDecoder: INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = mDecoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.i(TAG, "RunDecoder: New format " + mDecoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
// Timeout - simply ignore
break;
default:
// Some other value, simply ignore
break;
}
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "RunDecoder: OutputBuffer BUFFER_FLAG_END_OF_STREAM");
isEOS = true;
}
}
仍然沒有解決方案。任何建議仍然受歡迎。此外還提供實驗以增加理解的建議。任何人使用MediaCodec獲得硬件解碼工作?也許在其他平臺上? – Bram 2013-03-25 10:53:51
布拉姆,我試圖解決完全相同的問題。看起來這種放緩不是解碼緩衝區的多個副本。當解碼數據意圖呈現給本地表面時,它看起來有一些直接的數據路徑,它使用TILER(平鋪渲染)。當你需要訪問完整的YUV幀(例如你想訪問已解碼的緩衝區)時,解碼器需要完成一些額外的任務,比如將所有數據渲染到內存緩衝區並複製,這會使得緩慢。我從字面上浪費了一生的時間來解決這個問題,但似乎沒有什麼可以解決的。 – Pavel 2013-06-23 21:20:05
更重要的是,在我的情況下,我有一個720p @ 30fps,我無法實時解碼,而本地播放器沒有問題付費。 – Pavel 2013-06-23 21:21:33