使用硬件加速的Android MediaCodec解码器在本机代码中访问冲突

使用硬件加速的Android MediaCodec解码器在本机代码中访问冲突,第1张

概述我的目标是使用 Android MediaCodec解码视频流,然后使用输出图像在本机代码中进行进一步的图像处理. 平台:华硕tf700t android 4.1.1. 测试流:H.264全高清@ 24 frm / s 有了Tegra-3 SoC,我依靠硬件支持视频解码.从功能上讲,我的应用程序表现得如预期的那样:我确实可以访问解码器图像 并正确处理它们.但是,我遇到了非常高的解码器CPU负载. 我的目标是使用 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处理函数,但所有进一步的处理调用都被注释掉了.结果:

>吞吐量:25 frm / s
> 1%加载应用程序的线程VIDeoDecoder
> 24%负载进程/系统/ bin / mediaserver的Binder_3线程

似乎解码速度是cpu限制的(四核cpu的25%)……
启用输出处理时,解码图像正确并且应用程序正常工作.唯一的问题:解码时cpu负载过高.

经过大量的实验,我考虑给MediaCodec一个表面来绘制它的结果.在所有其他方面,代码是相同的.结果:

>吞吐量55 frm / s(不错!!)
> 2%加载应用程序的线程VIDeoDecoder
> 1%加载进程/ system / bin / mediaserver的线程mediaserver

实际上,视频显示在提供的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是否正确使用,因为应用程序
功能正常(只要我不提供Surface).
它仍然是非常实验性的,在todo列表上有一个很好的API设计;-)

请注意,两个实验之间的唯一区别是变量mSurface:null或实际Surface
在“mDecoder.configure(mediaFormat,mSurface,null,0);”

初始化代码:

mExtractor = new MediaExtractor();mExtractor.setDataSource(mPath);// Locate first vIDeo streamfor (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,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,MediaCodec.BUFFER_FLAG_END_OF_STREAM);            iSEOS = true;        } else {            mDecoder.queueinputBuffer(inIndex,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;    }}
解决方法 如果配置输出Surface,则解码数据将写入可用作OpenGL ES纹理的图形缓冲区(通过“外部纹理”扩展).各种硬件都以他们喜欢的格式处理数据,而cpu不必复制数据.

如果未配置Surface,则输出将进入java.nio.ByteBuffer.至少有一个缓冲区副本用于将数据从MediaCodec分配的缓冲区获取到ByteByffer,并且可能是另一个副本,以将数据返回到您的JNI代码中.我希望你所看到的是开销成本而不是软件解码成本.

您可以通过将输出发送到SurfaceTexture,转换为FBO或pbuffer,然后使用glreadPixels提取数据来改善问题.如果您读入“直接”ByteBuffer或从本机代码调用glreadPixels,则可以减少JNI开销.这种方法的缺点是你的数据将是RGB而不是ycbcr. (OTOH,如果你想要的转换可以用GLES 2.0片段着色器表示,你可以让GPU来完成工作而不是cpu.)

如另一个答案所述,不同设备上的解码器以不同格式输出ByteBuffer数据,因此如果可移植性对您很重要,则用软件解释数据可能不可行.

编辑:Grafika现在有一个使用GPU进行图像处理的例子.您可以看到演示视频here.

总结

以上是内存溢出为你收集整理的使用硬件加速的Android MediaCodec解码器在本机代码中访问冲突全部内容,希望文章能够帮你解决使用硬件加速的Android MediaCodec解码器在本机代码中访问冲突所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1140788.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存