歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android http progressive streaming分析

Android http progressive streaming分析

日期:2017/3/1 10:11:02   编辑:Linux編程

1. 數據源設置DataSource
對於http progressive download模式的數據源,分為兩步完成:

1. 客戶端調用setDataSource(const char*uri, …)後,AwesomePlayer保存了uri的值,其實沒有做什麼實質的事情,也沒有發起連接。真正的連接網絡並sniff的過程是在prepare的時候才進行的。

2. 客戶端調用prepare,AwesomePlayerpost一個onPrepareAsyncEvent事件,在其回調函數中調用finishSetDataSource_l函數,發起連接獲取源的文件頭,讀取文件頭的MIMETYPE信息,再根據MIMETYPE信息創建相應的MediaExtractor。最後這個MediaExtractor才是AwesomePlayer的數據源。讀取原始數據源的dataSource將委托給MediaExtractor來調用。AwesomePlayer只從MediaExtractor中讀取已經extract過的數據。

在finishSetDataSource_l中,用到了幾個DataSource

1. NuCachedSource2,帶緩存的DataSource,不包含媒體信息,只管理緩存以及調用底層的DataSource讀取和緩存數據。可以獲取緩存的信息以及操作緩存。

2. HTTPBase,基於http連接的DataSource,具有帶寬管理功能,沒有實現http連接功能

3. ChromiumHTTPDataSource,HTTPBase的子類,真正操作和管理http連接及狀態。ChromiumHTTPDataSource本身也不會直接操作socket,而是采用SfDelegate作為http協議棧來進行http連接的發起和關閉,本身通過注冊回調函數(onConnectionEstablished, onDisconnectComplete等)來獲取連接信息。ChromiumHTTPDataSource不進行緩存管理,如果調用readAt,將直接調用SfDelegate,如果需要連接就發起連接,可見如果沒有緩存管理每次直接操作ChromiumHTTPDataSource將有可能不斷發起網絡連接造成性能低下。

finishSetDataSource_l函數中設置數據源的真正操作如下
1】 首先設置連接源
a. 如果是Widevine 協議的DRM,由於沒有緩存,直接使用HTTPBase
b. 其它形式的Http progressive download使用NuCachedSource2
對於視頻源,首先下載至少192KB(192x1024)字節的文件緩存用於Sniff文件頭,
2】 Sniff到文件頭信息後,獲取MIMETYPE,並創建相應的MediaExtractor,再次調用setDataSource_l(extractor);至此DataSource正式建立完成。

2. PageCache 緩存數據結構
PageCache包含兩個page list: active pages和 free pages:


· List<Page*> mActivePages; 保存了有效數據的緩存,其中的連續的list構成了一段連續的媒體內容

· List<Page*> mFreePages; 可用緩存,可能是有數據但是已經無效的內存,或者是空內存

PageCache初始化時就確定了每個Page的最大size:

PageCache::PageCache(size_t pageSize)

: mPageSize(pageSize),

mTotalSize(0) {

}

· 獲取一個可用的page: acquirePage
Iterator從free pages中獲取第一個page,並從free pages中刪除該page。如果沒有則直接malloc一個。

· 獲取到page後,通過page data指針往裡面填充數據,再調用appendPage將page加入到active pages中。

· 釋放緩存: releasePage
releasePage時並不free指針,也不從active pages中移除。只是清空size信息並將page放到freePages中以便下次重用。由此可見整個cache實際占用內存的大小應該是非遞減的,等於運行過程中占用內存的最大值。(WHY?)由於page cache中的list代表的是連續媒體,因此不能release list中間的一個page,只能release list頭部或尾部的page。

· 整個cache中數據量保存在PageCache的mTotalSize中,每次appendPage時累加當前page中的實際數據量(mSize)。

3.NuCachedSource2讀取網絡

NuCachedSource2被創建(new)時就立即post了一個kWhatFetchMore消息,onFetch調用最後語句又post了一個kWhatFetchMore,並且此前沒有return語句,所以可見在NuCachedSource2的生存期內,kWhatFetchMore是個一直存在的循環驅動事件,但是事件post的delay時間不一樣,如果是重試,delay 3s, 如果是空閒狀態,delay 100ms。

· HighwaterThresholdBytes
當cache數據大於mHighwaterThresholdBytes時,停止fetch,並根據設置決定是否disconnect(). 默認20KB

· LowwaterThresholdBytes
restartPrefetcher中判斷當cache的數據量大於阈值mLowwaterThresholdBytes時,就不再prefetch了。默認4KB.

4.數據獲取

NuCachedSource2保持底層數據連接有兩種形式:

keepAlive
應該是針對有session的http連接,通過ALooper::GetNowUs計時,每隔mKeepAliveIntervalUs時間將調用fetchIntenal()保持與server的連接。默認間隔設置為15s。

預取數據:restartPrefetcherIfNecessary_l
幾個變量:

· Lastaccess position (mLastAccessPos)
表示數據調用者最後一次讀取的位置,

· Cacheoffset (mCacheOffset)
Cache數據在整個源數據文件中的偏移位置。

void NuCachedSource2::restartPrefetcherIfNecessary_l(boolignoreLowWaterThreshold, bool force)

Prefetch操作相當於隨著播放的進行cache也會自動往前進行,每次prefetch釋放當前page cache中已經播放過的的active pages,並更新cache offset至合適的位置。

· ignoreLowWaterThreshold:

當cache的數據量大於阈值mLowwaterThresholdBytes時,就不再prefetch了。

· force

last access與Cache偏移位置之間可以保留一個kGrayArea大小的數據,這部分數據是已經訪問過的數據,理論上可以被忽略並釋放,但考慮到seek或其它操作(如帶Bframe的媒體?)中會再次用到,如果釋放的話會引起再次重連網絡獲取影響性能,因此在保留不釋放。force變量用於控制釋放cache時,是否保留gray area.

5.讀取數據源readAt

readAt(off64_toffset, void *data, size_t size)

NuCachedSource2會首先在cache中查找offset和size是否能被滿足(再次注意,cache中的多個page代表的是連續的媒體內容),如果滿足,直接從cache中拷貝到目標指針並返回,否則post一個kWhatRead消息,調用線程被阻塞(condition.wait())。

onRead()回調

調用readInternal,返回-EAGAIN時,延遲50ms繼續發送kWhatRead消息。直到成功或者返回IO錯誤時,broadcast解除阻塞中的調用線程。

readInternal

調用restartPrefetcherIfNecessary_l進行數據預取,如果不滿足read條件(offset,size)則返回-EAGAIN,成功則拷貝目標數據。

Copyright © Linux教程網 All Rights Reserved