【问题标题】:Getting MediaCodec decoder provide video frames in RGBA获取 MediaCodec 解码器提供 RGBA 中的视频帧
【发布时间】:2018-05-17 13:48:02
【问题描述】:

我正在尝试使用 MediaCodec 解码器(通过 NDK API)从 .mp4 文件中获取视频帧(用于进一步处理)。以下是设置解码器以渲染到表面(由 ImageReader 拥有)的示例代码:

// Omitting most error handling for clarity

AMediaExtractor* ex = AMediaExtractor_new();
media_status_t err = AMediaExtractor_setDataSourceFd(ex, fd /*opened previously*/, outStart, outLen);
close(fd);

int numtracks = AMediaExtractor_getTrackCount(ex);
AMediaCodec* decoder = NULL;

for (int i = 0; i < numtracks; i++) {
    AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
    const char *s = AMediaFormat_toString(format);
    LOGV("track %d format: %s", i, s);
    const char *mime;
    if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
        LOGV("no mime type");
        return JNI_FALSE;
    } else if (!strncmp(mime, "video/", 6)) {
        AMediaExtractor_selectTrack(ex, i);
        decoder = AMediaCodec_createDecoderByType(mime);

        AImageReader* imageReader;
        ANativeWindow* surface;

        // This setting doesn’t works
        media_status_t status = AImageReader_new(480, 360, AIMAGE_FORMAT_RGBA_8888, 1, &imageReader);

        // This setting works
        //media_status_t status = AImageReader_new(480, 360, AIMAGE_FORMAT_YUV_420_888, 1, &imageReader);

        status = AImageReader_getWindow(imageReader, &surface);

        // Configure the decoder to render to a surface
        AMediaCodec_configure(codec, format, surface, NULL, 0);

        AMediaCodec_start(codec);
    }

    AMediaFormat_delete(format);
}

在其他地方,下面是我为 ImageReader 设置回调的方式:

AImageReader_ImageListener* imageListener = new AImageReader_ImageListener();
imageListener->onImageAvailable = &imageCallback;
AImageReader_setImageListener(imageReader, imageListener);

最后,下面是回调的样子:

void imageCallback(void *context, AImageReader *reader) {

    int32_t format;
    media_status_t status = AImageReader_getFormat (reader, &format);

    AImage* image;
    status = AImageReader_acquireLatestImage(reader, &image);

    status = AImage_getFormat(image, &format);

    // further processing to follow
    ...
}    

我一直面临的问题是,如果我将 ImageReader 配置为 RGBA 格式,则回调中的 image 总是为 NULL:

// Always NULL for ImageReader configured with RGBA
// OK for ImageReader configured with YUV_420_888
AImage* image;
status = AImageReader_acquireLatestImage(reader, &image);

我在这里正确使用了 NDK API 吗?我想提一提的是,RGBA 没有出现在通过以下 API 获取的解码器功能列表中(不是通过 NDK 提供的,在 Java 层中尝试过):

 getCodecInfo().getCapabilitiesForType(…).colorFormats

【问题讨论】:

    标签: android video android-ndk android-mediacodec


    【解决方案1】:

    视频解码器通常不支持以 RGB 格式输出(正如您在 colorformats 编解码器信息中注意到的那样),所以这就是它不起作用的原因。

    如果您使用表面纹理作为解码器的输出,则视频解码器输出可以透明地转换为 RGB - 然后解码的视频数据在 OpenGL 上下文中可用。如果您只是对表面纹理进行简单的 1:1 复制/渲染,并设置 OpenGL 上下文以渲染到 ImageReader 中,我希望您能够获得所需的 RGB 数据。

    这有点迂回(而且我不确定所有 API 在本机代码上下文中是否易于访问),但据我所知应该是可行的。

    【讨论】:

    • OpenGL 纹理非常适合以 RGB 格式显示输出。但是如果想要接收 RGB,Android 上最有效的方法是使用 Renderscript(参见例如software.intel.com/en-us/articles/…
    • 感谢@mstorsjo,亚历克斯为您的 cmets !渲染到 SurfaceTexture 是否只是为了获取 RGBA 中的像素而过度杀伤(至少在代码方面)?我不需要显示任何东西。计算着色器能否成为可行的替代方案?
    猜你喜欢
    • 1970-01-01
    • 2013-11-14
    • 1970-01-01
    • 2014-06-12
    • 1970-01-01
    • 2018-01-13
    • 2023-03-22
    • 1970-01-01
    • 2019-06-21
    相关资源
    最近更新 更多