我的目标是使用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