MediaCodec调用编解码器

2,053 阅读3分钟

MediaCodec是Android提供的用于对音视频进行编解码的类,它通过访问底层的codec来实现编解码的功能。 MediaCodec是通过ACodec来加载openmax层。

使用MediaCodec的编解码流程

/base/media/java/android/media/MediaCodec.java

MediaCodec的基本调用流程是:

createEncoderByType/createDecoderByType/createByCodecName
configure
start
while(true) {
     dequeueInputBuffer  //从输入流队列中取数据进行编码操作 
     getInputBuffers     //获取需要编码数据的输入流队列,返回的是一个ByteBuffer数组 
     queueInputBuffer    //输入流入队列 
     dequeueOutputBuffer //从输出队列中取出编码操作之后的数据
     getOutPutBuffers    // 获取编解码之后的数据输出流队列,返回的是一个ByteBuffer数组
     releaseOutputBuffer //处理完成,释放ByteBuffer数据
}
flush:清空的输入和输出端口。
stop:终止decode/encode会话
release:释放编解码器实例使用的资源。

MediaCodec创建:

createDecoderByType/createEncoderByType:根据特定MIME类型(如"video/avc;video/hevc")创建codec。 createByCodecName:知道组件的确切名称(如OMX.google.h264.decoder;OMX.google.h264.encoder)的时候,根据组件名创建codec。使用MediaCodecList可以获取组件的名称。

通过JNI调用MediaCodec.cpp

/base/media/jni/android_media_MediaCodec.cpp

/av/media/libstagefright/MediaCodec.cpp

MediaCodec::init

在init时会利用消息来传递编解码器状态

if (mIsVideo) {
    // video codec needs dedicated looper
    if (mCodecLooper == NULL) {
        mCodecLooper = new ALooper;
        mCodecLooper->setName("CodecLooper");
        mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
    }
    ALOGI("[init]:mCodecLooper registerHandler");
    mCodecLooper->registerHandler(mCodec);
} else {
    mLooper->registerHandler(mCodec);
}

mLooper->registerHandler(this);

mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, this));

sp<AMessage> msg = new AMessage(kWhatInit, this);
msg->setString("name", name);
msg->setInt32("nameIsType", nameIsType);

if (nameIsType) {
    msg->setInt32("encoder", encoder);
}

在init中调用GetCodecBase创建ACodec/MediaFilter

sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
    // at this time only ACodec specifies a mime type.
    if (nameIsType || name.startsWithIgnoreCase("omx.")) {
        return new ACodec;
    } else if (name.startsWithIgnoreCase("android.filter.")) {
        return new MediaFilter;
    } else {
        return NULL;
    }
}

ACodec

/av/media/libstagefright/ACodec.cpp

MediaCodec调用ACodec的initiateAllocateComponent接口进编解码组件的创建,ACodec就给自己发送了个msg: kWhatAllocateComponent。这个msg的处理就是单纯的调用执行onAllocateComponent。

在onAllocateComponent接口函数中, MediaCodec通过 OMXClient,经过binder进程间通讯, 向MediaCodecService获取 OMX句柄; OMXClient 通过connect接口与 MediaCodecService建立通讯;

创建 OMXMaster 创建解析xml文件的parser

在OMXMaster中会加载vendor和google的component;

在addVendorPlugin中加载硬编解码器,例如qcom/qti/Rockchip/hisi/MTK/...

在addPlugin中加载软编解码器;

void OMXMaster::addVendorPlugin() {
    addPlugin("libstagefrighthw.so");
}

void OMXMaster::addPlugin(const char *libname) {
    mVendorLibHandle = dlopen(libname, RTLD_NOW);

    if (mVendorLibHandle == NULL) {
        return;
    }

    typedef OMXPluginBase *(*CreateOMXPluginFunc)();
    CreateOMXPluginFunc createOMXPlugin =
        (CreateOMXPluginFunc)dlsym(
                mVendorLibHandle, "createOMXPlugin");
    if (!createOMXPlugin)
        createOMXPlugin = (CreateOMXPluginFunc)dlsym(
                mVendorLibHandle, "_ZN7android15createOMXPluginEv");

    if (createOMXPlugin) {
        addPlugin((*createOMXPlugin)());
    }
}

ACodec在创建编解码组件的过程中,通过omxclient拿到omx bp代理对象,根据这个代理对象和omx进行通信。omx接收到ALLOCATE_NODE的请求后,调用allocateNode()接口进一步通知plugin创建对应的编解码组件,并返回其操作句柄,后面就是操作这个句柄来工作。这个操作句柄保存在instance(OmxNodeInstance类型)对象中,与之对应的是一个node id,是一个32位的无符号int值,两者以键值对形式存放在mNodeIDToInstance变量中。acodec可以拿到的就是这个node id和omx bp代理对象。

OMX::node_id OMX::makeNodeID_l(OMXNodeInstance *instance) {
    // mLock is already held.

    node_id prefix = node_id(getpid() << 16);
    node_id node = 0;
    do  {
        if (++mNodeCounter >= kMaxNodeInstances) {
            mNodeCounter = 0; // OK to use because we are combining with the pid
        }
        node = node_id(prefix | mNodeCounter);
    } while (mNodeIDToInstance.indexOfKey(node) >= 0);
    mNodeIDToInstance.add(node, instance);

    return node;
}