IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android MediaCodec message机制分析 -> 正文阅读

[移动开发]Android MediaCodec message机制分析

我在MediaCodec中看到一段代码,感觉很蒙蔽

 if (mIsVideo) {
        // video codec needs dedicated looper
        if (mCodecLooper == NULL) {
            mCodecLooper = new ALooper;
            mCodecLooper->setName("CodecLooper");
            mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
        }
        //这里mCodec是一个ACodec对象
        mCodecLooper->registerHandler(mCodec);
    }

在MediaCodec.h中定义了

sp<ALooper> mCodecLooper;

这是一个ALooper对象,然后在MediaCodec这个类里就再也搜索不到mCodecLooper的使用了。所以就很奇怪,总不能这段代码写出来没什么用吧?
对了,mCodecLooper是一个ALooper,start调用完之后,完全不像不作为的样子啊

status_t ALooper::start(
        bool runOnCallingThread, bool canCallJava, int32_t priority) {
    ...
    Mutex::Autolock autoLock(mLock);
    ...
    // LooperThread继承自Thread类
    // 通过threadLoop触发ALooper的loop
    mThread = new LooperThread(this, canCallJava);
    // 然后这个新创建的CodecLooper就开始运行了
    status_t err = mThread->run(
            mName.empty() ? "ALooper" : mName.c_str(), priority);
    if (err != OK) {
        mThread.clear();
    }

    return err;
}

很明显,这个线程被启动了,并且有事儿干。
此时,我的目光被这个调用吸引了

mCodecLooper->registerHandler(mCodec);

ALooper调用了registerHandler方法,传递了一个ACodec对象。而ACodec使用了很多AMessage来发消息,进行异步调用。那我们先列举一下这几个类的关系
暂时无法在文档外展示此内容
接着看看registerHandler方法

ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
    return gLooperRoster.registerHandler(this, handler);
}

函数就执行了,ALooper中的gLooperRoster调用registerHandler方法。在ALooper中没有发现gLooperRoster的使用,它是一个全局变量。
在frameworks/av/media/libstagefright目录搜索发现,除了ALooper,AMessage中也有这个全局变量的身影

foundation/AMessage.cpp:39:extern ALooperRoster gLooperRoster;

除此之外就没有线索了。所以再次在frameworks/av底下就行搜索

media/libmediaplayer2/mediaplayer2.cpp:49:extern ALooperRoster gLooperRoster;
media/libmediaplayer2/mediaplayer2.cpp:352:        gLooperRoster.dump(fd, args);
media/libstagefright/foundation/AMessage.cpp:39:extern ALooperRoster gLooperRoster;
media/libstagefright/foundation/ALooper.cpp:34:ALooperRoster gLooperRoster;
media/libstagefright/foundation/ALooper.cpp:76:    gLooperRoster.unregisterStaleHandlers();
media/libstagefright/foundation/ALooper.cpp:89:    return gLooperRoster.registerHandler(this, handler);
media/libstagefright/foundation/ALooper.cpp:93:    gLooperRoster.unregisterHandler(handlerID);
media/libmediaplayerservice/MediaPlayerService.cpp:260:extern ALooperRoster gLooperRoster;
media/libmediaplayerservice/MediaPlayerService.cpp:551:        gLooperRoster.dump(fd, args);

就可以发现这个变量在mediaplayer被用到了,不过,也只是用来调用dump方法。而这个变量名起名是
g(global)Looper + Roster(名册)。难道这是一个用来做media全局记录所有ALooper的一个名册?
还是继续看看ALooperRoster先吧

ALooper::handler_id ALooperRoster::registerHandler(
        const sp<ALooper> &looper, const sp<AHandler> &handler) {
    Mutex::Autolock autoLock(mLock);
    // 如果前面传入的ACodec的id不为0
    // 则报错,每个hander只能被注册一次
    if (handler->id() != 0) {
        CHECK(!"A handler must only be registered once.");
        return INVALID_OPERATION;
    }

    HandlerInfo info;
    info.mLooper = looper; // mCodecLooper
    info.mHandler = handler; // ACodec对象
    ALooper::handler_id handlerID = mNextHandlerID++; //生成一个id
    mHandlers.add(handlerID, info); //将id和这个info放入全局变量的mHandlers
    // 调用ACodec的setID方法
    handler->setID(handlerID, looper);

    return handlerID;
}

到setID的时候,发现ACodec并没有实现setID方法。实现在这里:
struct AHandler

  inline void setID(ALooper::handler_id id, const wp<ALooper> &looper) {
        mID = id;
        mLooper = looper;
    }

也就是说ACodec调用完setID方法之后,首先它获得了一个唯一生成的id,其次ACodec的mLooper对象被赋值为mCodecLooper。
终于把联系都串起来,然而!当我去搜索mLooper对象的时候,ACodec中并没有。
别着急!经验告诉我们,那就是他的父类AHandler已经把这个变量替他用好了,他没必要再重新造轮子了。果不其然

struct AHandler
    sp<ALooper> looper() const {
        return mLooper.promote();
    }

    wp<ALooper> getLooper() const {
        return mLooper;
    }

当然,这里也有可能只是为了不想让用户不直接访问mLooper对象,所以封装成函数
搜索发现,下面这段代码,高度疑似

void AMessage::setTarget(const sp<const AHandler> &handler) {
    if (handler == NULL) {
        mTarget = 0;
        mHandler.clear();
        mLooper.clear();
    } else {
        mTarget = handler->id();
        mHandler = handler->getHandler();
        mLooper = handler->getLooper();
    }
}

再来看看ACodec中是怎么使用的吧:

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

这个函数是从MediaCodec调用过来的,msg传入的时候,携带了codecinfo和componentName信息。
进入函数之后

  1. 调用了一个setWhat,把AMessage的mWhat进行赋值。
  2. setTarget将ACodec对象送入上面我们提到的方法,那这个消息的mTarget就获得了当前ACodec对象的id。前面分析中,registerHandler中生成的那一个。
  3. mHandler赋值为handler->getHandler(),这个方法我们之前没有提过,返回的就是ACodec本身。
  4. mLooper赋值为handler->getLooper(),就是我们前面追踪的方法,还记得这里返回的mLooper是谁吗?就是mCodecLooper,那个名字叫做CodecLooper的线程。
  5. 接着setTarget方法执行完之后,就执行了AMessage的post的方法。
status_t AMessage::post(int64_t delayUs) {
    sp<ALooper> looper = mLooper.promote();
    if (looper == NULL) {
        ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
        return -ENOENT;
    }

    looper->post(this, delayUs);
    return OK;
}

此时,mLooper是谁呢?就是名字叫做CodecLooper的线程。
它的promote又是谁呢?由于ALooper继承自RefBase,所以,这里可以理解为,就是mLooper这个wp封装的指针指向的对象。简单来说,这里就还是名字叫做CodecLooper的线程。
然后就看看post方法

void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
    Mutex::Autolock autoLock(mLock);
    // 消息是否要延迟处理
    int64_t whenUs;
    if (delayUs > 0) {
        whenUs = GetNowUs() + delayUs;
    } else {
        whenUs = GetNowUs();
    }
    // 按照发送时间找位置
    List<Event>::iterator it = mEventQueue.begin();
    while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
        ++it;
    }

    Event event;
    event.mWhenUs = whenUs;
    event.mMessage = msg;
    
    if (it == mEventQueue.begin()) {
        mQueueChangedCondition.signal();
    }
    // 插入这条消息
    mEventQueue.insert(it, event);
}

刚才那一条mWhat为kWhatAllocateComponent的消息被post去了哪里? mEventQueue中,再一搜索mEventQueue,整个流程就明白了

bool ALooper::loop() {
    Event event;

    {
        Mutex::Autolock autoLock(mLock);
        if (mThread == NULL && !mRunningLocally) {
            return false;
        }
        if (mEventQueue.empty()) {
            mQueueChangedCondition.wait(mLock);
            return true;
        }
        int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
        int64_t nowUs = GetNowUs();

        if (whenUs > nowUs) {
            int64_t delayUs = whenUs - nowUs;
            mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);

            return true;
        }

        event = *mEventQueue.begin();
        mEventQueue.erase(mEventQueue.begin());
    }

    event.mMessage->deliver();

    // NOTE: It's important to note that at this point our "ALooper" object
    // may no longer exist (its final reference may have gone away while
    // delivering the message). We have made sure, however, that loop()
    // won't be called again.

    return true;
}

这个loop即是名字叫做CodecLooper的线程的loop。会不断被所属LooperThread的threadLoop不断触发(前提是得返回true,返回false就停掉了)。

  1. 名字叫做CodecLooper的线程是一个ALooper,它有一个mEventQueue。
  2. mEventQueue为空的时候,他就等着。
  3. 一旦有AMessage被post,就会触发event.mMessage->deliver();
void AMessage::deliver() {
    sp<AHandler> handler = mHandler.promote();
    ...
    handler->deliverMessage(this);
}

再次回到mHandler,我们前面说了,就是ACodec本身,实现在它父类AHandler里。

void AHandler::deliverMessage(const sp<AMessage> &msg) {
    onMessageReceived(msg);
    ...
}

重点就是调用了onMessageReceived,这个父类并没有实现,所以还是要回到ACodec自己来处理了。而ACodec自己的处理过程也很精妙,且听我细细道来。
ACodec::onMessageReceived实现如下:

// AHierarchicalStateMachine implements the message handling
virtual void onMessageReceived(const sp<AMessage> &msg) {
    handleMessage(msg);
}

注释中也写明白了,AHierarchicalStateMachine实现消息处理,因为AHierarchicalStateMachine是ACodec的父类,所以子类没实现的方法,就调用父类的

void AHierarchicalStateMachine::handleMessage(const sp<AMessage> &msg) {
    ...
    sp<AState> cur = mState;
    ...
    cur->onMessageReceived(msg)
    ...
}

而这个mState是在AHierarchicalStateMachine::changeState中设置的。在ACodec的构造函数中就调用了这个

ACodec::ACodec() {
    ...
    changeState(mUninitializedState);
}

这样的话,很巧妙的,每次调用完changeState,发出去的消息就会发给不同的类了!

bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
    case ACodec::kWhatAllocateComponent:
        {
            onAllocateComponent(msg);
    ...
}

比如说当前是mState被赋值为mUninitializedState,所以处理消息的就是UninitializedState了,这里想继续操作ACodec怎么办?再看ACodec构造函数

ACodec::ACodec() {
    ...
    mUninitializedState = new UninitializedState(this);
    mLoadedState = new LoadedState(this);
    mLoadedToIdleState = new LoadedToIdleState(this);
    mIdleToExecutingState = new IdleToExecutingState(this);
    mExecutingState = new ExecutingState(this);

    mOutputPortSettingsChangedState =
        new OutputPortSettingsChangedState(this);

    mExecutingToIdleState = new ExecutingToIdleState(this);
    mIdleToLoadedState = new IdleToLoadedState(this);
    mFlushingState = new FlushingState(this);
}

所有状态机函数都传递了一个this指针做参数
然后

ACodec::UninitializedState::UninitializedState(ACodec *codec)
    : BaseState(codec) {
}

ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState)
    : AState(parentState),
      mCodec(codec) {
}

也就是说BaseState所定义的mCodec即前面我们一直在使用的ACodec对象,所以这里就可以随便操作了。可以理解为这些状态机管理着ACodec的行为。

到这里,可以总结发现,MediaCodec变量mCodecLooper作用主要是

创建一个线程,供ACodec来使用。可以在线程上执行所有的异步流程。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-09-21 00:40:22  更:2022-09-21 00:42:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 5:24:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码