Android中MetaData.cpp数据存储学习

代码路径:frameworks\av\media\libstagefright\foundation\MetaData.cpp

这个是在学习NuPlayer的时候看到的,觉得有必要记录一下,积累C++数据存储的技巧;在NuPlayer的getFrameRate()函数中,用到了MetaData,所以从这里开始记录:

float NuPlayer::getFrameRate() {
    sp<MetaData> meta = mSource->getFormatMeta(false /* audio */);
    if (meta == NULL) {
        return 0;
    }
    int32_t rate;
    if (!meta->findInt32(kKeyFrameRate, &rate)) {
        // fall back to try file meta
        sp<MetaData> fileMeta = getFileMeta();
        if (fileMeta == NULL) {
            ALOGW("source has video meta but not file meta");
            return -1;
        }
        int32_t fileMetaRate;
        if (!fileMeta->findInt32(kKeyFrameRate, &fileMetaRate)) { //通过findInt32函数,查找键kKeyFrameRate对应的值
            return -1; 
        }
        return fileMetaRate;
    }
    return rate;
}

下面来看,MetaData是如何存储/获取值的:

先来看findInt32对应的存储方法MetaData::setInt32

bool MetaData::setInt32(uint32_t key, int32_t value) {
    return setData(key, TYPE_INT32, &value, sizeof(value));
}

bool MetaData::setData(
        uint32_t key, uint32_t type, const void *data, size_t size) {
    bool overwrote_existing = true;

    ssize_t i = mItems.indexOfKey(key); //检查key是否已存在,不存在的话,创建一个添加进入
    if (i < 0) {
        typed_data item;
        i = mItems.add(key, item);  //添加一个新的item

        overwrote_existing = false;
    }

    typed_data &item = mItems.editValueAt(i); //获取刚才添加好的item的引用,并进行编辑复制操作

    item.setData(type, data, size);  

    return overwrote_existing;
}

以上代码有两个知识点:

1. 从上面申明item可以知道item是typed_data类型,而typed_data是类MetaData中的一个结构体,这个结构体专门用来保存数据

2. setData()的最后一个参数是size_t,传入的是value值的字节大小;可以得知C++存储数据时,需要考虑存储数据的字节数

下面来看item.setData(type, data, size)这个方法的实现,代码是在结构体typed_data中:

 

void MetaData::typed_data::setData(uint32_t type, const void *data, size_t size) {
    clear();  //clear()的作用稍后再说明

    mType = type;     //type保存在成员变量中

    void *dst = allocateStorage(size);   //申请size大小的内存,这是明白了size的作用
    if (dst) {
        memcpy(dst, data, size);    //将data地址的数据copy size字节到dts地址中去
    }
}

以上有两个函数clear()/allocateStorage(),需要继续跟踪以明白它是如何实现的

void *MetaData::typed_data::allocateStorage(size_t size) {
    mSize = size;    //将size存储在成员变量中

    if (usesReservoir()) {     //判断是否使用已有存储池来存储数据
        return &u.reservoir;   //可以使用的话,返回u.reservoir地址
    }

    u.ext_data = malloc(mSize);  //如果不可以使用,则重新申请一块mSize大小的内存
    if (u.ext_data == NULL) {
        ALOGE("Couldn't allocate %zu bytes for item", size);
        mSize = 0;
    }
    return u.ext_data;
}

//下面来看usesReservoir():
 bool usesReservoir() const {
    return mSize <= sizeof(u.reservoir); //哦,明白了,是判断要存储的数据是否大于存储池的空间,如果小于的话,就将data存在u.reservoir中
 }

看了刚才的allocateStorage()函数的实现后,不禁会好奇u.ext_data和u.reservoir两个元素,它是结构体typed_data中定义的用一个联合体:

 union {                   //是定义在共用体中,共用体的特点是,只是使用其中一个成员,如果使用两个的话,前一个的值将会丢弃
     void *ext_data;       //void* 表示不确定类型的指针,可以接受任意类型的赋值
     float reservoir;
 } u;

我们可以存上面的allocateStorage()函数看到,如果存储的数据小于float字节数的话,就存储在u.reservoir地址中;如果存储的数据大于float字节数,则使用void*来存储;

----------------------------------------存储的过程已解释,下面来看取数据的过程--------------------------------------------

来看frameworks\av\media\libstagefright\foundation\MetaData.cpp中findInt32()函数的实现:

bool MetaData::findInt32(uint32_t key, int32_t *value) { //通过key获取对应value的值
    uint32_t type = 0;
    const void *data;   //要获取的是data的地址
    size_t size;
    if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {  //先获取key对应的type/data/size的值;然后比较type是否是TYPE_INT32类型,如果不是说明key不是通过setInt32()来存储的,直接返回false
        return false;
    }

    CHECK_EQ(size, sizeof(*value));  //比较*value和我们获取的data大小是否一致,如果一致就将data数据赋值给value

    *value = *(int32_t *)data;

    return true;
}
bool MetaData::findData(uint32_t key, uint32_t *type,
                        const void **data, size_t *size) const {
    ssize_t i = mItems.indexOfKey(key);   //获取key在mItems中的id

    if (i < 0) {
        return false;
    }

    const typed_data &item = mItems.valueAt(i);  //通过id获取存储的item的值

    item.getData(type, data, size); //获取值

    return true;
}

从mItems.indexOfKey(key)/mItems.valueAt(i)可以明白,我们存储的数据其实都是存在了mItems中去了

void MetaData::typed_data::getData(
        uint32_t *type, const void **data, size_t *size) const {
    *type = mType;       //直接使用之前存在成员变量中的数据给它们赋值
    *size = mSize;
    *data = storage();   //给一级指针赋值,这样*data的值就是storage返回的地址了
}

void *storage() {
    //从size判断是当时数据存储成了哪个类型
    return usesReservoir() ? &u.reservoir : u.ext_data;
}

这样取数据的过程就完成了

关于data的二级指针,不太明白,日后再论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值