Android中MetaData.cpp數據存儲學習
代碼路徑:frameworksavmedialibstagefrightfoundationMetaData.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*來存儲;
----------------------------------------存儲的過程已解釋,下面來看取數據的過程--------------------------------------------
來看frameworksavmedialibstagefrightfoundationMetaData.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的二級指針,不太明白,日後再論
※Windows 下雙 Python 開發環境配置
※mysql視圖
TAG:程序員小新人學習 |