最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

記錄一下第一次啃源碼的過程

2021-01-11 01:29 作者:スレーブ_スレイヤー  | 我要投稿

對(duì)于一直以來(lái)都是面向百度,使用Crlt A C V編程的我來(lái)說(shuō),讀源碼的過程就好像在做閱讀理解。然而這次的問題有些特殊,網(wǎng)上不可能有答案。好在,縱使英文-1級(jí),靠著谷歌翻譯還有不停下斷點(diǎn),最后還是成功猜出作者當(dāng)時(shí)的想法。整個(gè)過程耗時(shí)一星期?;诉@么多時(shí)間,就想著把中間學(xué)到的東西總結(jié)一下(沒有有價(jià)值的內(nèi)容,大概也就是一些被說(shuō)爛了的東西),沒錢租服務(wù)器搭博客,就先把這個(gè)過程寫成專欄吧。

首先,我是想要開發(fā)一個(gè)音樂app。那么問題來(lái)了,安卓自帶的播放音視頻的API有MediaPlayer和SoundPool,因?yàn)樯婕暗皆诰€播放的功能,這兩個(gè)肯定是不夠用的。

然后是第三方的庫(kù),找到最后,決定B站的IjkPlayer和谷歌的ExoPlayer二選一。IjkPlayer沒提供Java層的調(diào)用,要用這個(gè)就要手動(dòng)編譯so文件,另外這玩意也沒個(gè)使用文檔啥的??吹紺的代碼就頭暈,又瞅了一眼隔壁ExoPlayer的文檔,好像三行Java代碼就能搞定,果斷投靠谷歌。

OK,然后問題就來(lái)了。因?yàn)槟承┰颍嬖诜?wù)器上的視頻是分片的文件。比如一個(gè)300M的MP4格式視頻,被切成了三個(gè)100M的文件。這個(gè)切割是針對(duì)文件而言的,和把文件剪輯成三份不一樣,這樣直接切割導(dǎo)致的結(jié)果就是只有第一個(gè)文件能夠被播放器識(shí)別。

但是很神奇的一點(diǎn)是,同樣的切割方式,把一個(gè)MP3格式的文件切成三份就能正常播放。

那么,接下來(lái)要做的就是讓播放器能夠正常播放這些被切割的文件。

通過谷歌知道了,MP4把所有的信息(第幾個(gè)字節(jié)到第幾個(gè)字節(jié)對(duì)應(yīng)視頻的第幾幀到第幾幀之類的吧)都儲(chǔ)存在頭部,切割以后第一個(gè)以外的文件都不包含那部分信息所以無(wú)法播放。這樣很容易想到,只要把后面那些文件缺失的頭部給加上就可以正常播放了。于是又花了好長(zhǎng)時(shí)間看分析MP4編碼的文章,最后實(shí)在看不懂放棄了。

ExoPlayer的官方文檔說(shuō)支持HLS和DASH兩種流媒體格式,說(shuō)實(shí)話不用這個(gè)還真不知道有流媒體格式這種東西存在,一直以為網(wǎng)站上的視頻就是MP4格式。既然有Web專用的視頻格式,那么直接把視頻轉(zhuǎn)成這兩個(gè)格式就行了......手動(dòng)轉(zhuǎn)換肯定是不可能的,然而好像有個(gè)叫ffmpeg的工具可以做到。用ffmpeg轉(zhuǎn)格式算是最后的殺手锏了,在那之前先試著不改變?cè)瓉?lái)的文件讓播放器正常播放:

// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSourceFactory();
// Create a DASH media source pointing to a DASH manifest uri.
MediaSource mediaSource =
 ? ?new DashMediaSource.Factory(dataSourceFactory)
 ? ? ? ?.createMediaSource(MediaItem.fromUri(dashUri));
// Create a player instance.
SimpleExoPlayer player = new SimpleExoPlayer.Builder(context).build();
// Set the media source to be played.
player.setMediaSource(mediaSource);
// Prepare the player.
player.prepare();

繼續(xù)看官方文檔,有一個(gè)叫DefaultHttpDataSourceFactory的東西。啊,這個(gè)我知道,工廠模式,雖然根本不知道為什么非要把對(duì)象的創(chuàng)建搞得這么麻煩(解耦?我所有功能寫在一個(gè)類里面的時(shí)候也沒感覺耦合啊)。那肯定就有DefaultHttpDataSource,根據(jù)名字來(lái)看,這個(gè)類應(yīng)該是處理來(lái)自Http請(qǐng)求的數(shù)據(jù)源的。既然有HttpDataSource,那肯定也有其他的DataSource,說(shuō)不定有能滿足需求的,這樣就不用多寫代碼了。

好吧,因?yàn)橛⑽奶?,看不太懂注釋,只好每個(gè)都用一下。一輪測(cè)試下來(lái)還是沒一個(gè)能用的。

沒辦法,只能對(duì)這個(gè)類動(dòng)手腳,先讓它播放第一個(gè)分片。在它讀取數(shù)據(jù)的時(shí)候做判斷,如果需要的數(shù)據(jù)不在第一個(gè)分片,就去后面的分片里讀取。

很幸運(yùn),找到成員變量里面有一個(gè)HttpUrlConnection。這個(gè)類是用來(lái)處理網(wǎng)絡(luò)請(qǐng)求的,只要對(duì)這個(gè)對(duì)象動(dòng)些手腳,應(yīng)該就能實(shí)現(xiàn)想要的效果了。

看一下哪個(gè)方法用到了httpUrlConnection,最終找到了一個(gè)open方法。懶得看內(nèi)容,直接下個(gè)斷點(diǎn)運(yùn)行……拖動(dòng)進(jìn)度條的時(shí)候open方法被調(diào)用了,open內(nèi)部又調(diào)用了makeConnection,在這個(gè)方法內(nèi)部會(huì)給http請(qǐng)求添加一個(gè)Range頭,作用是指定需要獲取的文件的字節(jié)范圍。

好吧,現(xiàn)在明白為什么mp4格式也能在線播放了,其實(shí)就是添加了一個(gè)Range請(qǐng)求頭。

雖然文件被分割了,但播放器顯示的視頻長(zhǎng)度仍然是分割之前的。此時(shí)如果拖動(dòng)進(jìn)度條到末尾,會(huì)拋出一個(gè)異常,說(shuō)服務(wù)器的返回碼是416(正常情況下應(yīng)該是206)。

下斷點(diǎn),發(fā)現(xiàn)把進(jìn)度條拖到末尾時(shí),makeConnection方法依舊很耿直地添加了一個(gè)超出文件大小的Range。也就是說(shuō)就算文件不完整,播放器依舊知道拖動(dòng)進(jìn)度條時(shí)應(yīng)該去讀哪一部分的內(nèi)容。

到這里事情就簡(jiǎn)單了,直接在添加Range時(shí)判斷,如果大小超過當(dāng)前文件,就給httpUrlConnection重新賦值為后面的文件的鏈接,然后設(shè)置相應(yīng)的Range。

好吧,當(dāng)時(shí)也不知道可不可行,不過這樣做以后居然奇跡般的正常運(yùn)行了。再仔細(xì)想想好像也沒什么奇怪的,DataSource類的職責(zé)就只是提供數(shù)據(jù)供播放器解析,至于這些數(shù)據(jù)從哪來(lái),上層并不關(guān)心。

于是寫了一個(gè)類繼承DefaultHttpDataSource。然后問題來(lái)了,我要改動(dòng)的屬性是父類的私有屬性,方法也是私有方法……試了一下反射,可能是用的kotlin的緣故,this.getClass().getSuperClass得到的不是DefaultHttpDataSource而是Object。搞不懂,網(wǎng)上也沒有解決方案……最后只好直接把源碼copy過來(lái)了。

雖然現(xiàn)在說(shuō)出來(lái)很輕松,但當(dāng)時(shí)真的腦子一片漿糊。無(wú)頭蒼蠅一樣,好多類的源碼都翻了一遍看得腦淤血,最后也就改動(dòng)了一個(gè)地方的代碼。

總結(jié):

看不懂源碼就別先別看,先下斷點(diǎn)跑兩步把整個(gè)結(jié)構(gòu)捋清楚。最重要的是,別急,別看不懂就覺得“我是不是弱智啊?”然后就放棄了。第一次就能看懂別人的代碼是不可能的(雖然花了一個(gè)星期也就搞明白MP4的在線播放是利用Range這種理所當(dāng)然的事)。

解決同一個(gè)問題的方法多種多樣,譬如如果我對(duì)視頻編碼足夠了解,就可以在讀取數(shù)據(jù)時(shí)給后面沒有頭的分片加上頭部了。而能夠想到多少種方法,取決于知識(shí)的廣度,譬如這次我能解決這個(gè)問題,是因?yàn)槲抑皩?duì)http請(qǐng)求有了解,否則根本無(wú)從下手。

MP4只是一種容器格式,和具體的視頻編碼無(wú)關(guān)(之前一直有誤會(huì))。MP3格式的文件,就算被“分尸”也依舊可以正常播放。

英語(yǔ)很重要,能決定一個(gè)程序員的上限。













記錄一下第一次啃源碼的過程的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
永年县| 临西县| 扎囊县| 奉新县| 宣汉县| 泰来县| 阳山县| 蒲江县| 利辛县| 扎囊县| 海兴县| 龙南县| 会宁县| 广饶县| 怀安县| 开鲁县| 云浮市| 阜平县| 石泉县| 南昌县| 旬阳县| 保山市| 余干县| 麻栗坡县| 老河口市| 沈阳市| 汤原县| 柘城县| 夏邑县| 敦煌市| 五家渠市| 元氏县| 隆化县| 夹江县| 色达县| 精河县| 鹰潭市| 盐亭县| 从江县| 区。| 屏东市|