Android 13 - Media框架(21)- ACodec(三)

2023-12-13 18:10:12

这一节我们一起来了解 ACodec 是如何通过 configureCodec 方法配置 OMX 组件的,因为 configureCodec 代码比较长,所以我们会把代码进行拆分来了解。
ps:这部分的代码我们先跳过 encoder 的流程。

先来看函数入参,第一个参数 mime,第二个参数为 AMessage,不过看 onConfigureComponent 代码我们就可以知道 mime 是来自于 msg 的,所以这里边的信息会有重复。

status_t ACodec::configureCodec(const char *mime, const sp<AMessage> &msg) 

进入函数体内,首先干了3件事情:

    int32_t encoder;
    if (!msg->findInt32("encoder", &encoder)) {
        encoder = false;
    }
	// 创建空白的input / output format message
    sp<AMessage> inputFormat = new AMessage;
    sp<AMessage> outputFormat = new AMessage;
    mConfigFormat = msg;

    mIsEncoder = encoder;
    mIsVideo = !strncasecmp(mime, "video/", 6);
    mIsImage = !strncasecmp(mime, "image/", 6);
	// 初始化 port mode
    mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;
    mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;
  1. 判断当前的组件是 encoder 还是 decoder;
  2. 判断组件是 audio 还是 video;
  3. 初始化 input 和 ouput format 以及 port mode;

在这里 input format 和传入参数 msg 中的内容有一些区别,msg 中的信息都是由 extractor 解析出来的,input format 中除了有这些信息外,还存储有用于配置 OMX 组件的一些信息;output format 中存储的是 OMX 组件回传给上层的输出格式信息。

接下来有一个很重要的内容就是 Port Mode,每个组件会有两个端口(input / output Port),这里的 mode 指的就是对这两个端口的定义,影响的是在端口中传输的 buffer 的类型。

PortMode 定义位于 frameworks/av/media/libmedia/include/media/IOMX.h
之所以把 PortMode 放在这个文件中,是因为它只用于 ACodec 和 OMXNode 当中,OMXNode 收到 ACodec 设定下来的 mode 之后会做相关处理之后再传送给 OMX 组件。

PortMode 分为两组:

    enum PortMode {
        kPortModePresetStart = 0,
        kPortModePresetByteBuffer,
        kPortModePresetANWBuffer,
        kPortModePresetSecureBuffer,
        kPortModePresetEnd,

        kPortModeDynamicStart = 100,
        kPortModeDynamicANWBuffer,      // uses metadata mode kMetadataBufferTypeANWBuffer
                                        // or kMetadataBufferTypeGrallocSource
        kPortModeDynamicNativeHandle,   // uses metadata mode kMetadataBufferTypeNativeHandleSource
        kPortModeDynamicEnd,
    };

一组以 Preset 前缀,另一组以 Dynamic 作为前缀。他们两个的区别在于,Preset 表示端口内的 buffer 在启动前已经被预先设定好了,在编解码组件运行过程中这些 buffer 不会发生变化;Dynamic 则表示动态,意思就是组件运行过程中,我们使用的 buffer 可能会发生动态变化,有一些 buffer 可能被弃用,也有一些 buffer 会被新加入使用;这里对部分 PortMode 类型进行描述:

  • kPortModePresetByteBuffer:使用最普通的 buffer,我们可以很轻松访问到 buffer 中的内容;
  • kPortModePresetANWBuffer:使用预先设定的 Native Window Buffer,我们将无法直接访问这块 buffer 中的数据;
  • kPortModePresetSecureBuffer:使用预先设定的 Secure Buffer,我们无法直接访问 buffer 中的内容;
  • kPortModeDynamicANWBuffer:使用动态的 Native Window Buffer,我们将无法直接访问这块 buffer 中的数据;
  • kPortModeDynamicNativeHandle:使用动态的 Native Buffer Handle,buffer 以 handle 的形式回传上来,我们将无法直接访问 buffer 中的数据;

具体这些 Mode 会在什么情况下使用,我们在后面会看到。

status_t ACodec::setComponentRole(
        bool isEncoder, const char *mime) {
    const char *role = GetComponentRole(isEncoder, mime);
    if (role == NULL) {
        return BAD_VALUE;
    }
    status_t err = SetComponentRole(mOMXNode, role);
    if (err != OK) {
        ALOGW("[%s] Failed to set standard component role '%s'.",
             mComponentName.c_str(), role);
    }
    return err;
}

接下来会调用 setComponentRole 方法,首先来讲我理解的为什么要调用这个方法:我们实现的 OMX 组件可能共享的是一套流程,也就是各个组件 lib 可能是链接到同一个lib当中,那这里就会有一个问题,每当我们调用 getHandle 创建一个句柄时,组件并不知道我们要对什么格式的数据进行处理,也不知道是做编码还是做解码,所以上层需要设定相关参数通知 OMX 组件它需要走什么流程。

这个方法还有一个作用,它内部有个 GetComponentRole 方法,会根据传进来的 mime type 获取对应的 Role,如果这里没有找到对应的 role,则会发生 error,因此如果要支持某个格式的播放,则必须要修改 GetComponentRole 中使用到的一个数组 kMimeToRole,我们这里就不再展开了。

这里 SetComponentRole 是如何将参数设定下去的也不做过多的解释,主要流程是创建一个参数,初始化参数,设定参数内容,调用 setParameter 传递参数。

status_t SetComponentRole(const sp<IOMXNode> &omxNode, const char *role) {
    OMX_PARAM_COMPONENTROLETYPE roleParams;
    InitOMXParams(&roleParams);

    strncpy((char *)roleParams.cRole,
            role, OMX_MAX_STRINGNAME_SIZE - 1);

    roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';

    return omxNode->setParameter(
            OMX_IndexParamStandardComponentRole,
            &roleParams, sizeof(roleParams));
}

我们这里先跳过 encoder 的流程,所以接下来会跳过一部分代码。

    int32_t lowLatency = 0;
    if (msg->findInt32("low-latency", &lowLatency)) {
        err = setLowLatency(lowLatency);
        if (err != OK) {
            return err;
        }
    }

判断并设定 OMX 组件是否打开低延迟的模式,我理解的 low-latency 可能是 OMX 组件低缓冲快速解码。

	// 查找是否有 native window(surface)设定下来
    sp<RefBase> obj;
    bool haveNativeWindow = msg->findObject("native-window", &obj)
            && obj != NULL && mIsVideo && !encoder;
    mUsingNativeWindow = haveNativeWindow;
    if (mIsVideo && !encoder) {
        inputFormat->setInt32("adaptive-playback", false);

        int32_t usageProtected;
       	// 如果 format 中有设定 protect 信息
        if (msg->findInt32("protected", &usageProtected) && usageProtected) {
            if (!haveNativeWindow) {
                ALOGE("protected output buffers must be sent to an ANativeWindow");
                return PERMISSION_DENIED;
            }
            // 设定相关flag
            mFlags |= kFlagIsGrallocUsageProtected;
            mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
        }
    }
    // 如果是 secure 组件
    if (mFlags & kFlagIsSecure) {
        // use native_handles for secure input buffers
        err = setPortMode(kPortIndexInput, IOMX::kPortModePresetSecureBuffer);

        if (err != OK) {
            ALOGI("falling back to non-native_handles");
            setPortMode(kPortIndexInput, IOMX::kPortModePresetByteBuffer);
            err = OK; // ignore error for now
        }

        OMX_INDEXTYPE index;
        if (mOMXNode->getExtensionIndex(
                "OMX.google.android.index.preregisterMetadataBuffers", &index) == OK) {
            OMX_CONFIG_BOOLEANTYPE param;
            InitOMXParams(&param);
            param.bEnabled = OMX_FALSE;
            if (mOMXNode->getParameter(index, &param, sizeof(param)) == OK) {
                if (param.bEnabled == OMX_TRUE) {
                    mFlags |= kFlagPreregisterMetadataBuffers;
                }
            }
        }
    }

文章来源:https://blog.csdn.net/qq_41828351/article/details/134916969
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。