Bra*_*ram 5 android access-violation native-code
我的目标是使用Android MediaCodec解码视频流,然后使用输出图像在本机代码中进行进一步的图像处理.
平台:华硕tf700t android 4.1.1.测试流:H.264全高清@ 24 frm/s
有了Tegra-3 SoC,我依靠硬件支持视频解码.在功能上,我的应用程序表现得如预期的那样:我确实可以访问解码器图像并正确处理它们.但是,我遇到了非常高的解码器CPU负载.
在以下实验中,过程/线程负载由adb shell中的"top -m 32 -t"测量.为了从"top"获得可靠的输出,所有4个cpu内核都通过运行一些永久循环以最低优先级循环的线程来强制激活.这通过重复执行"cat/sys/devices/system/cpu/cpu [0-3]/online"来确认.为了简单起见,只有视频解码,没有音频; 并且没有时序控制,因此解码器尽可能快地运行.
第一个实验:运行应用程序,调用JNI处理函数,但所有进一步的处理调用都被注释掉了.结果:
似乎解码速度受CPU限制(四核CPU的25%)......启用输出处理时,解码图像正确并且应用程序正常工作.唯一的问题:解码时cpu负载过高.
经过大量的实验,我考虑给MediaCodec一个表面来绘制它的结果.在所有其他方面,代码是相同的.结果:
实际上,视频显示在提供的Surface上.由于几乎没有任何CPU负载,这必须是硬件加速......
如果提供Surface,de MediaCodec似乎只使用硬件加速?
到现在为止还挺好.我已经倾向于使用Surface作为解决方法(不是必需的,但在某些情况下甚至是一个很好的).但是,如果提供表面,我无法访问输出图像!结果是本机代码中的访问冲突.
这真让我困惑!我没有看到的访问限制任何想法,任何或文档中http://developer.android.com/reference/android/media/MediaCodec.html.此外,谷歌I/O演示文稿http://www.youtube.com/watch?v=RQws6vsoav8中也未提及此方向.
那么:如何使用硬件加速Android MediaCodec解码器并以原生代码访问图像?如何避免访问冲突?任何帮助都会被激活!也有任何解释或提示.
我敢肯定的MediaExtractor和MediaCodec正确使用,因为该应用程序是functionaly OK(只要我不提供表面).它仍然是非常实验性的,在todo列表上有一个很好的API设计;-)
请注意,两个实验之间的唯一区别是变量mSurface:null或"mDecoder.configure(mediaFormat,mSurface,null,0)中的实际Surface";
初始化代码:
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);
Run Code Online (Sandbox Code Playgroud)
解码器循环(在单独的线程中运行):
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;
}
}
Run Code Online (Sandbox Code Playgroud)
如果配置输出 Surface,则解码数据将写入可用作 OpenGL ES 纹理的图形缓冲区(通过“外部纹理”扩展)。硬件的各个部分可以按照它们喜欢的格式传递数据,并且 CPU 不必复制数据。
如果您不配置 Surface,输出将进入java.nio.ByteBuffer. 至少有一个缓冲区副本用于将数据从 MediaCodec 分配的缓冲区获取到您的ByteByffer,并且可能还有另一个副本用于将数据返回到您的 JNI 代码中。我希望您看到的是开销成本而不是软件解码成本。
您可以通过将输出发送到 a SurfaceTexture、渲染到 FBO 或 pbuffer,然后使用glReadPixels来提取数据来改进问题。如果您从本机代码读取“直接”ByteBuffer或调用glReadPixels,则可以减少 JNI 开销。这种方法的缺点是您的数据将采用 RGB 而不是 YCbCr。(OTOH,如果您想要的转换可以在 GLES 2.0 片段着色器中表达,您可以让 GPU 代替 CPU 来完成这项工作。)
正如另一个答案中所述,不同设备上的解码器ByteBuffer以不同格式输出数据,因此如果可移植性对您来说很重要,那么在软件中解释数据可能不可行。
编辑: Grafika现在有一个使用 GPU 进行图像处理的示例。您可以在此处观看演示视频。
| 归档时间: |
|
| 查看次数: |
3111 次 |
| 最近记录: |