Android MultiMedia框架——MediaCodec编码(上)

Posted VNanyesheshou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android MultiMedia框架——MediaCodec编码(上)相关的知识,希望对你有一定的参考价值。

Android MultiMedia框架——OMX服务启动
Android MultiMedia框架——OMXPlugin
Android MultiMedia框架——MediaCodec编码(上)
Android MultiMedia框架——MediaCodec编码(下)
Android MultiMedia框架——ACodec加载OMX
基于Android 9.0源码分析

分析MediaCodec编码流程,以OMX.google.aac.encoder编码器为例;

MeidaCodec 使用方法可以参考之前的总结文档

初始化MediaCodec

public static MediaCodec createByCodecName(@NonNull String name)
        throws IOException 
    return new MediaCodec(
            name, false /* nameIsType */, false /* unused */);


private MediaCodec(
        @NonNull String name, boolean nameIsType, boolean encoder) 
    Looper looper;
    if ((looper = Looper.myLooper()) != null) 
        mEventHandler = new EventHandler(this, looper);
     else if ((looper = Looper.getMainLooper()) != null) 
        mEventHandler = new EventHandler(this, looper);
     else 
        mEventHandler = null;
    
    mCallbackHandler = mEventHandler;
    mOnFrameRenderedHandler = mEventHandler;

    mBufferLock = new Object();

    native_setup(name, nameIsType, encoder);

MeidaCodec构造器私有的,需要通过静态方法创建对象。

  1. 创建Handler;
  2. 创建对象锁;
  3. 调用native层方法;

接着看native_setup绑定的jni方法

frameworks/base/media/jni/android_media_MediaCodec.cpp

static void android_media_MediaCodec_native_setup(
        JNIEnv *env, jobject thiz,
        jstring name, jboolean nameIsType, jboolean encoder) 
    // ... 
    sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);

    const status_t err = codec->initCheck();
    // ...
    codec->registerSelf();
    setMediaCodec(env,thiz, codec);

  1. 创建JMediaCodec类型指针;
  2. JMediaCodec检查初始化状态;
  3. 保存codec指针;
JMediaCodec::JMediaCodec(
        JNIEnv *env, jobject thiz,
        const char *name, bool nameIsType, bool encoder)
    : mClass(NULL),
      mObject(NULL) 
     //.....
    mLooper = new ALooper;
    mLooper->setName("MediaCodec_looper");
    mLooper->start(
            false,      // runOnCallingThread
            true,       // canCallJava
            ANDROID_PRIORITY_VIDEO);

    if (nameIsType) 
        mCodec = MediaCodec::CreateByType(mLooper, name, encoder, &mInitStatus);
     else 
        mCodec = MediaCodec::CreateByComponentName(mLooper, name, &mInitStatus);
    
    CHECK((mCodec != NULL) != (mInitStatus != OK));

  1. 创建ALooper对象,并start,开启一个线程运行;
  2. nameIsType为false,调用CreateByComponentName;

接着看MediaCodec::CreateByComponentName

路径frameworks/av/media/libstagefright/MediaCodec.cpp

// static
sp<MediaCodec> MediaCodec::CreateByComponentName(
        const sp<ALooper> &looper, const AString &name, status_t *err, pid_t pid, uid_t uid) 
    sp<MediaCodec> codec = new MediaCodec(looper, pid, uid);

    const status_t ret = codec->init(name);
    if (err != NULL) 
        *err = ret;
    
    return ret == OK ? codec : NULL; // NULL deallocates codec.

  1. 创建native层 MeiaCodec对象,其构造器主要是一些遍历初始化;
  2. 调用init方法;
status_t MediaCodec::init(const AString &name) 
	//判断是否是安全编解码、是不是vidoe编解码器
	//........
	mCodec = GetCodecBase(name, mCodecInfo->getOwnerName());
	
    if (mIsVideo) 
        // video codec needs dedicated looper
        //........
     else 
        mLooper->registerHandler(mCodec);
    

    mLooper->registerHandler(this);
    
	sp<AMessage> msg = new AMessage(kWhatInit, this);
    msg->setObject("codecInfo", mCodecInfo);
    // name may be different from mCodecInfo->getCodecName() if we stripped
    // ".secure"
    msg->setString("name", name);
    
    for (int i = 0; i <= kMaxRetry; ++i) 
        if (i > 0) 
            // Don't try to reclaim resource for the first time.
            if (!mResourceManagerService->reclaimResource(resources)) 
                break;
            
        

        sp<AMessage> response;
        err = PostAndAwaitResponse(msg, &response);
        if (!isResourceError(err)) 
            break;
        
    
    return err;


sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, const char *owner) 
    if (owner) 
        if (strncmp(owner, "default", 8) == 0) 
            return new ACodec;
         else if (strncmp(owner, "codec2", 7) == 0) 
            return CreateCCodec();
        
    

    if (name.startsWithIgnoreCase("c2.")) 
        return CreateCCodec();
     else if (name.startsWithIgnoreCase("omx.")) 
        // at this time only ACodec specifies a mime type.
        return new ACodec;
     else if (name.startsWithIgnoreCase("android.filter.")) 
        return new MediaFilter;
     else 
        return NULL;
    

  1. 调用GetCodecBase创建 ACodec对象;
  2. mLooper注册handler, mCodec和MediaCodec本身;
  3. 发送 kWhatInit msg消息;

接着查看msg 回调

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) 
    switch (msg->what()) 
    case kWhatInit:
		
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));
            if (mState != UNINITIALIZED) 
                PostReplyWithError(replyID, INVALID_OPERATION);
                break;
            
            mReplyID = replyID;
            setState(INITIALIZING);

            sp<RefBase> codecInfo;
            CHECK(msg->findObject("codecInfo", &codecInfo));
            AString name;
            CHECK(msg->findString("name", &name));

            sp<AMessage> format = new AMessage;
            format->setObject("codecInfo", codecInfo);
            format->setString("componentName", name);

            mCodec->initiateAllocateComponent(format);
            break;
		
      

调用ACodec initiateAllocateComponent函数

void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) 
    msg->setWhat(kWhatAllocateComponent);
    msg->setTarget(this);
    msg->post();


bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) 
    bool handled = false;
    switch (msg->what()) 
        case ACodec::kWhatAllocateComponent:
            onAllocateComponent(msg);
            handled = true;
            break;
        
    

接着看onAllocateComponent函数

bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) 
    //.......
    sp<CodecObserver> observer = new CodecObserver;
    sp<IOMX> omx;
    sp<IOMXNode> omxNode;

    status_t err = NAME_NOT_FOUND;
    OMXClient client;
    if (client.connect(owner.c_str()) != OK) 
        mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
        return false;
    
    omx = client.interface();

    err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
	//......
    mCodec->mOMX = omx;
    mCodec->mOMXNode = omxNode;
    mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());
    mCodec->changeState(mCodec->mLoadedState);
    return true;

  1. OMXClient connect 与media.codec 服务建立连接;
  2. 获取句柄 IOMX;
  3. 分配Node;
  4. 保存omx和omxNode;
  5. 将状态修改为LoadedState;

配置MeidaFormat

codec.configure(format, …)

由于代码太多,分析下大致流程:

MediaCodec.java  
    configure(MediaFormat format,Surface surface, @Nullable MediaCrypto crypto,
           			 int flags)
    native native_configure()
android_medai_MediaCodec.cpp         
    android_media_MediaCodec_native_configure()
    JMediaCodec::configure()
 MediaCodec.cpp
    configure()
    onMessageReceived()  case kWhatConfigure:
ACodec.cpp
    void ACodec::initiateConfigureComponent(const sp<AMessage> &msg)
    ACodec::LoadedState::onMessageReceived() case ACodec::kWhatConfigureComponent
    ACodec::LoadedState::onConfigureComponent()
    ACodec::configureCodec(const char *mime, const sp<AMessage> &msg)

ACodec::configureCodec函数代码太长,大概有600多行,就不贴出来了,主要就是通过之前保存的IOMXNode,设置编解码的相关参数,

最终会设置到编码其中,如:SoftAACEncoder2 的internalSetParameter函数

开始

codec.start()

MediaCodec.java  
    start()
    native native_start()
android_medai_MediaCodec.cpp         
    android_media_MediaCodec_start()
    JMediaCodec::start()
 MediaCodec.cpp
    start()
    onMessageReceived()  case kWhatStart:
ACodec.cpp
    void ACodec::initiateStart()
    ACodec::LoadedState::onMessageReceived() case ACodec::kWhatStart
    ACodec::LoadedState::onStart()
    
void ACodec::LoadedState::onStart() 
    ALOGV("onStart");

    status_t err = mCodec->mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
    if (err != OK) 
        mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
     else 
        mCodec->changeState(mCodec->mLoadedToIdleState);
    

mOMNode 发送command, omx处理状态。

ACodec 状态改为mLoadedToIdleState,allocateBuffers分配buffer;

void ACodec::LoadedToIdleState::stateEntered() 
    status_t err;
    if ((err = allocateBuffers()) != OK) 
        //。。。。。。
    

OMX 处理信息 :OMX_CommandStateSet ,OMX_StateIdle,会发送event通知ACodec,看看ACodec是如何处理的:

bool ACodec::LoadedToIdleState::onOMXEvent(
        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) 
    switch (event) 
        case OMX_EventCmdComplete:
        
            //....

            if (err == OK) 
                err = mCodec->mOMXNode->sendCommand(
                    OMX_CommandStateSet, OMX_StateExecuting);
            

            if (err != OK) 
                mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
             else 
                mCodec->changeState(mCodec->mIdleToExecutingState);
            

            return true;
        

    

LoadedToIdleState接收到event:OMX_EventCmdComplete后,向OMX 发送command OMX_StateExecuting, 并且ACodec 将状态切换为IdleToExecutingState。

继续。。。最终ACodec state切换为ExecutingState。

未完待续。。。。。

欢迎大家交流,指正问题。

以上是关于Android MultiMedia框架——MediaCodec编码(上)的主要内容,如果未能解决你的问题,请参考以下文章

Android MultiMedia框架——OMXPlugin

Android MultiMedia框架——ACodec加载OMX

Android MultiMedia框架——ALooper AHandler AMessage

Android MultiMedia框架——MediaCodec编码(下)

Android MultiMedia框架——mediaserver启动

Android MultiMedia框架——mediaserver启动