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;
}
