首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >硬件加速的Android MediaCodec解码器在本机代码中的访问冲突

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

Stack Overflow用户
提问于 2013-03-19 13:02:26
回答 2查看 3.3K关注 0票数 5

我的目标是使用Android MediaCodec解码视频流,然后使用输出图像在本机代码中进行进一步的图像处理。

平台:华硕tf700t安卓4.1.1。测试流: H.264 full HD @ 24 frm/s

随着Tegra-3 SoC在里面,我指望硬件支持的视频解码.在功能上,我的应用程序的行为与预期一样:我确实可以访问解码器图像并正确地处理它们。然而,我经历了一个非常高的解码器cpu负载。

在随后的实验中,进程/线程负载由亚行外壳中的"top -m 32 -t“测量。为了从"top“获得可靠的输出,所有4个cpu核心都通过运行几个线程来强制活动,这些线程始终以最低优先级循环。这是通过反复执行"cat /sys/设备/system/cpu/cpu0-3/online“来证实的。为了保持简单,只有视频解码,没有音频;也没有定时控制,所以解码器运行得越快越好。

第一个实验:运行应用程序,调用JNI处理函数,但是所有进一步的处理调用都被注释掉。结果:

  • 吞吐量: 25 frm/s
  • 应用程序的线程VideoDecoder负载的1%
  • 进程/系统/bin/媒体服务器线程Binder_3的24%负载

解码速度似乎是有限的CPU (一个四核CPU的25%).当启用输出处理时,解码的图像是正确的,并且应用程序工作。唯一的问题是: cpu负载太高,无法解码。

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

  • 吞吐量55 frm/s (不错!)
  • 应用程序线程VideoDecoder的2%负载
  • 进程/系统/bin/中介器线程中介器负载的1%

实际上,视频显示在提供的表面上。因为几乎没有cpu负载,这必须是硬件加速.

如果提供了一个曲面,那么de MediaCodec只会使用硬件增益吗?

到现在为止还好。我已经倾向于使用Surface作为一种工作(不是必需的,但在某些情况下甚至是一个很好的选择)。但是,如果提供了一个曲面,我就无法访问输出图像!结果是本机代码中的访问冲突。

这真让我费解!我没有看到任何访问限制的概念,也没有在文档http://developer.android.com/reference/android/media/MediaCodec.html中看到任何内容。而且,在google /O演示文稿http://www.youtube.com/watch?v=RQws6vsoav8中也没有提到这个方向。

So:如何使用硬件加密的安卓MediaCodec解码器并在本地代码中访问图像?如何避免访问冲突?任何帮助都是被认可的!还有任何解释或暗示.

我非常确信MediaExtractor和MediaCodec的使用是正确的,因为应用程序的功能很好(只要我没有提供一个Surface)。它仍然是相当试验性的,一个好的API设计在待办事项清单上;

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

初始化代码:

代码语言:javascript
复制
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);

解码器循环(在一个单独的线程中运行):

代码语言:javascript
复制
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;
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-03-30 00:12:33

如果配置输出曲面,则将解码后的数据写入图形缓冲区,该缓冲区可用作OpenGL ES纹理(通过“外部纹理”扩展)。不同的硬件可以以他们喜欢的格式传递数据,CPU不需要复制数据。

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

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

正如另一个答案所指出的,不同设备上的解码器以不同的格式输出ByteBuffer数据,因此,如果可移植性对您很重要,那么在软件中解释数据可能是不可行的。

编辑: 格拉菲卡现在有一个使用GPU进行图像处理的例子。您可以看到一个演示视频这里

票数 4
EN

Stack Overflow用户

发布于 2013-03-29 20:50:03

我在nexus 4上使用mediacodec,得到QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka.的输出颜色格式。我认为这种格式是一种硬件格式,只能通过硬件渲染来呈现。有趣的是,当我使用null和实际曲面为MediaCodec配置曲面时,输出缓冲区长度将分别更改为实际值和0。我也不知道原因。我认为你可以在不同的设备上做一些实验来获得更多的结果。关于硬件加速,您可以看到http://www.saschahlusiak.de/2012/10/hardware-acceleration-on-sgs2-with-android-4-0/

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15500290

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档