摘要:最終方案也確定采用序列幀動(dòng)畫方案。所以,要想在電影或者視頻上顯示效果,首先要做的是編寫特效文件,然后再將特效文件解析成序列幀動(dòng)畫的位圖,最后將這些位圖按照特定的順序和一定的幀率進(jìn)行播放,就能看到各種特效的動(dòng)畫。
本文由云+社區(qū)發(fā)表一、 背景 1. 現(xiàn)狀作者:QQ音樂(lè)技術(shù)團(tuán)隊(duì)
歌詞瀏覽已經(jīng)成為音樂(lè)app的標(biāo)配,展示和動(dòng)畫效果也基本上大同小異,主要是單行的逐字染色的卡拉OK效果和多行的滾動(dòng)效果。當(dāng)然,我們也不例外。
2. 目標(biāo)我們的目標(biāo)十分明確,一是提升歌詞的基礎(chǔ)體驗(yàn),二是在此基礎(chǔ)上,能提供差異化的VIP特效,來(lái)吸引用戶開(kāi)通VIP。
二、探索技術(shù)方案經(jīng)過(guò)多次的需求評(píng)審和溝通討論,各方在需求的目標(biāo)和細(xì)節(jié)上也達(dá)成了初步的統(tǒng)一。 產(chǎn)品的希望 :效果炫酷,能實(shí)現(xiàn)逐字動(dòng)畫(位移,翻轉(zhuǎn),漸隱漸現(xiàn),模糊,粒子特效等),可配置等。開(kāi)發(fā)的思考: 技術(shù)架構(gòu)方案,性能挑戰(zhàn)等,接下來(lái)我們簡(jiǎn)單介紹一下確定技術(shù)方案的過(guò)程。
1. 技術(shù)方案選型這里最初的思路有兩個(gè)方向,升級(jí)現(xiàn)有歌詞組件和開(kāi)發(fā)全新歌詞組件。所謂知已知彼,百戰(zhàn)不殆, 通過(guò)對(duì)移動(dòng)端面主流競(jìng)品的技術(shù)方案和PC端類似方案的技術(shù)調(diào)研與分析。最終將技術(shù)方案鎖定在以下三種:
現(xiàn)有歌詞組件升級(jí)
Shader序列幀動(dòng)畫
ASS序列幀動(dòng)畫
2. 備選技術(shù)方案介紹下面簡(jiǎn)單介紹一下三種方案的原理和特點(diǎn),如下表所示:
總的來(lái)說(shuō),就是在原生動(dòng)畫開(kāi)發(fā)和幀動(dòng)畫方案中進(jìn)行選擇。
3. 技術(shù)方案對(duì)比以下主要是從是否實(shí)現(xiàn)特效,開(kāi)發(fā)的難度,方案的性能,實(shí)現(xiàn)的成本,跨平臺(tái)等方面對(duì)比三種方案,具體細(xì)節(jié)如下表所示:
4. 確定方案通過(guò)以上幾個(gè)維度的綜合考量:
現(xiàn)有歌詞組件基本上無(wú)法實(shí)現(xiàn)逐字動(dòng)畫。
Shader幀動(dòng)畫開(kāi)發(fā)周期長(zhǎng),實(shí)現(xiàn)成本高,逐字動(dòng)畫支持不是很好。
ASS實(shí)現(xiàn)逐字動(dòng)畫,可通過(guò)植入動(dòng)畫標(biāo)簽實(shí)現(xiàn)復(fù)雜的特效,有開(kāi)源支持,且跨平臺(tái)。
綜上所述,ASS方案性價(jià)比最高。
最終方案也確定采用ASS序列幀動(dòng)畫方案。
三、 技術(shù)架構(gòu) 1. ASS技術(shù)工作原理介紹前面簡(jiǎn)單介紹了一下什么ASS字幕和幀動(dòng)畫的原理。我們知道ASS是一種字幕文件格式,屬于高級(jí)字幕,可以制作出華麗的特效字幕。所以,要想在電影或者視頻上顯示ASS效果,首先要做的是編寫ASS特效文件,然后再將ASS特效文件解析成序列幀動(dòng)畫的位圖,最后將這些位圖按照特定的順序和一定的幀率進(jìn)行播放,就能看到各種特效的動(dòng)畫。如下圖所示:
2. 如何接入ASS方案 2.1 合成如下下圖所示:,首先,需要準(zhǔn)備展示內(nèi)容(字幕或者歌詞內(nèi)容),比如一個(gè)文本文件,有了最基本的文本文件,怎么轉(zhuǎn)換成ASS解析器能解析的ASS文件呢?答案是打K值,打K值是指給字幕文件加上時(shí)間軸屬性。而是什么K值呢,就是ASS中K拉OK的效果標(biāo)簽代碼,即每行甚至每個(gè)字的時(shí)間坐標(biāo)。有了打完K值的ASS文件,我們就可以在視頻播放器中瀏覽,也就有了最基本的逐字染色動(dòng)畫。如果要開(kāi)發(fā)更復(fù)雜的特效,就需要加入更多的特效標(biāo)簽。而這一部分,就可以通過(guò)腳本加上動(dòng)畫模板(動(dòng)效模板就是具有特定動(dòng)畫效果的ASS文件),將動(dòng)畫標(biāo)簽注入到打完K值A(chǔ)SS文件中,生成最終的ASS特效文件。至此,一個(gè)具有特效的ASS文件就誕生了。
2.2 解析解析的過(guò)程相對(duì)比較簡(jiǎn)單。解析一個(gè)ASS文件,不僅需要ASS文件本身,還需要知道ASS文件是用什么字體合成的。這里補(bǔ)充一下,前面合成的時(shí)候,其中的動(dòng)畫模板也是需要指定是使用哪種字體來(lái)合成的。因?yàn)檫@里會(huì)涉及到字體的大小,間距等,對(duì)動(dòng)畫效果和排版的影響。然后,再回到解析上來(lái),通過(guò)ASS文件加上字體庫(kù)就可以解析生成特定序列的幀動(dòng)畫位圖。
3. 技術(shù)架構(gòu)
最終方案的技術(shù)架構(gòu):功能上劃分如下,后負(fù)責(zé)存儲(chǔ)和合成;客戶端負(fù)責(zé)解析和繪制,呈現(xiàn)用戶最終的動(dòng)畫效果。
4. 通用性上面提到了這套方案的通用性和易復(fù)用的特點(diǎn)。那除了動(dòng)效歌詞之外,我們還可以做些什么呢?
首先,我們脫離業(yè)務(wù)對(duì)架構(gòu)進(jìn)行更高一層的抽象,梳理出了更通用的架構(gòu)方。這里還需要補(bǔ)充一點(diǎn),“字體庫(kù)”,從字面上理解應(yīng)該是一堆字體的容器,所以字體庫(kù)應(yīng)該是保存了一大堆的文字信息等。但其實(shí)不僅是文字也可以是圖形,所以我們的動(dòng)畫效果可以不只是針對(duì)文字的,還可以設(shè)計(jì)一些圖形動(dòng)畫效果。所以,這里可以有更多的想像空間。前面解析的過(guò)程我們提到,解析出一幀幀的圖,就拿去直接播放了,這樣我們就能實(shí)時(shí)看到動(dòng)畫效果。那如果把這些圖片保存下來(lái),根據(jù)業(yè)務(wù)需求在需要的時(shí)候再播放呢。這里就可以拆分出實(shí)時(shí)渲染和離線渲染兩種方案。
這里的渲染提供了兩種方案:
1. 實(shí)時(shí)渲染
將解析出來(lái)的位圖立即繪制到屏幕上。
適用場(chǎng)景:實(shí)時(shí)要求高的場(chǎng)景。
特點(diǎn): 對(duì)系統(tǒng)性能消耗大,需要注意當(dāng)前場(chǎng)景的性能開(kāi)銷。
2. 離線渲染
將解析出來(lái)的位圖保存到磁盤上,并可以此基礎(chǔ)上建立序列幀動(dòng)畫的資源管理。
適用場(chǎng)景:適用于異步化的場(chǎng)景。
特點(diǎn): 建議采用異步線程在后臺(tái)處理,減少對(duì)主線程消耗。
大家可以根據(jù)各自業(yè)務(wù)場(chǎng)景和特點(diǎn)靈活選擇或者組合使用這兩種方案。
以上主要是介紹動(dòng)效歌詞技術(shù)方案的實(shí)現(xiàn)原理與架構(gòu)介紹。
四、技術(shù)難點(diǎn)與挑戰(zhàn)在開(kāi)發(fā)過(guò)程中,我們遇到了兩個(gè)重要的問(wèn)題:一個(gè)是在運(yùn)行復(fù)雜的效果時(shí),動(dòng)畫效果出現(xiàn)了肉眼可見(jiàn)的卡頓;另一個(gè)則是內(nèi)存的問(wèn)題,即使是比較簡(jiǎn)單的效果播放以后也會(huì)占用大量的內(nèi)存。本文后半部分將重點(diǎn)闡述K歌是如何解決這兩個(gè)問(wèn)題的。
1. 卡頓問(wèn)題描述我們選取了一個(gè)較為復(fù)雜的效果,包含了大量的煙霧、花瓣等動(dòng)畫元素 及 位移、形變與模糊等效果,它的每一幀畫面約由1000個(gè)元素構(gòu)成。
在三星Note 3(Android 5.0,4核,ARMv7)上運(yùn)行起來(lái)平均只能達(dá)到7幀的效果。
2. 解碼與渲染的過(guò)程為了解決上述問(wèn)題,我們需要對(duì)ASS由文本文件到渲染至屏幕的整個(gè)過(guò)程有基本的認(rèn)識(shí)。這里以Android為例(Ios在渲染的處理上略有不同,而其它是一致的),先看JNI的接口:
private native int decodeFrame(long time, int[] pixels);
Java層會(huì)傳入時(shí)間戮time及名為pixels的Int數(shù)組,time代表當(dāng)前需要獲取哪個(gè)時(shí)間點(diǎn)的動(dòng)畫效果,libass接著會(huì)對(duì)與這一時(shí)間點(diǎn)有關(guān)的每一行文本進(jìn)行解析,生成一個(gè)或多個(gè)的小圖,從而得到一系列的圖片,然后合成到一個(gè)大圖里面去,最終通過(guò)像素拷貝的方式把合成后的結(jié)果輸出到pixels,回到Java以后,再把pixels設(shè)置至Bitmap,最后交給Canvas進(jìn)行渲染。
3. 過(guò)程耗時(shí)分析通過(guò)對(duì)各關(guān)鍵過(guò)程的打點(diǎn)并運(yùn)行前述復(fù)雜效果,我們得到了各過(guò)程的耗時(shí)占比:解析46%、合成37%、輸出與渲染各8%,其它1%。分解到每一幀并以毫秒計(jì)算則如下表:
接下來(lái),我們將會(huì)按解析、合成、輸出、渲染這樣的順序來(lái)逐步優(yōu)化。
4. 卡頓優(yōu)化實(shí)踐前面提到,每一行ass文本都會(huì)生成一個(gè)或多個(gè)的小圖,這是因?yàn)橐粋€(gè)文字會(huì)被拆解成文體、邊框及背景三個(gè)部分,除此之外,libass并不關(guān)心這些構(gòu)成部分的顏色及透明度。這就導(dǎo)致了這樣的一個(gè)問(wèn)題:
Dialogue: 1,0:00:00.00,0:01:00.00,Default,,0,0,0,fx,{pos(120,100)1a&HFF&lur3}全民K歌
以上ass文本所實(shí)現(xiàn)的是一個(gè)文字鏤空效果:
1a&HFF&表示文字主體是完全透明的,而這樣的一個(gè)透明的元素,libass依然會(huì)生成一個(gè)小圖對(duì)它進(jìn)行各種各樣的處理,但這是完全沒(méi)有必要的,于是我們對(duì)libass進(jìn)行了第一點(diǎn)改造:不再生成無(wú)效的透明小圖,提高ass解析效率的同時(shí)也減少了內(nèi)存的分配,對(duì)后續(xù)合成的處理也有正向的影響
在合成的處理中,需要遍歷小圖的每一個(gè)像素并拆分為ARGB4個(gè)通道進(jìn)行顏色的運(yùn)算
dstA = (255 * 255 - (255 - k) * (255 - dstA)) / 255; dstB = (k * b + (255 - k) * dstB) / 255; dstG = (k * g + (255 - k) * dstG) / 255; dstR = (k * r + (255 - k) * dstR) / 255;
與普通的圖片合成不同,在歌詞動(dòng)效的場(chǎng)景中,小圖由文字或點(diǎn)線之類的圖形構(gòu)成,往往存在著大量的透明像素及完全不透明像素,可通過(guò)判斷來(lái)減少這部分的合成運(yùn)算:
if(k == 0){ // 完全透明,跳過(guò) continue; } if(k == 255){ // 完全不透明,直接使用小圖顏色 dst = color; continue; }
測(cè)試了5個(gè)在K歌上線的動(dòng)效,合成時(shí)間減少了10%~50%。
雖然通過(guò)透明度的判斷減少了一定計(jì)算,但無(wú)法完全避免。以Alpha通道的計(jì)算為例,包含了2次乘法、1次除法和3次的減法,而除法是特別耗時(shí)的。所以,對(duì)于這些必要的計(jì)算,我們進(jìn)行了簡(jiǎn)化,先進(jìn)行等式變換:
dstA = (255 * 255 - (255 - k) * (255 - dstA)) / 255; = (255 - (255 - k) * (255 - dstA) / 255);
然后利用255 - x = ~x及x / 255 ≈ x >> 8進(jìn)行替換,得到簡(jiǎn)化后的結(jié)果:
dstA = ~((~k) * (~dstA)) >> 8);
可見(jiàn),一次計(jì)算變成了1次乘法與4次位運(yùn)算,測(cè)得合成時(shí)間減少了26%。
經(jīng)過(guò)上述幾項(xiàng)優(yōu)化,合成速度快了許多,但這還不夠。在合成的算法中,像素點(diǎn)與像素點(diǎn)間是沒(méi)有任何聯(lián)系的,所以可以通過(guò)并行計(jì)算的方式來(lái)提高合成的效率。我們采用了NEON的解決方案,利用CPU專用模塊的128位寄存器同時(shí)對(duì)多個(gè)像素點(diǎn)進(jìn)行計(jì)算,因32位色彩中ARGB各占8位,再考慮乘法處理后可能達(dá)到的16位,由此,可用128位寄存器同時(shí)處理8個(gè)像素點(diǎn)的計(jì)算,實(shí)現(xiàn)約8倍的加速效果,對(duì)CPU和幀率可起到明顯的作用。 具體實(shí)現(xiàn)如下:
至此,合成的優(yōu)化告一段落,每一幀的合成耗時(shí)由原來(lái)的52ms,降到了3ms以內(nèi)
輸出的過(guò)程實(shí)際上只是做了一次像素拷貝的操作,把合成后的大圖輸出到JNI傳入的Int數(shù)組里面去,除了耗時(shí)以后,還會(huì)產(chǎn)生額外的一次Native內(nèi)存分配,于是,我們優(yōu)化了這個(gè)過(guò)程,讓合成直接在Int數(shù)組進(jìn)行,這樣就把原來(lái)輸出的11ms完全去掉了
前面提到,數(shù)據(jù)到了Java層,還會(huì)調(diào)用Bitmap的setPixels方法把像素信息傳給Bitmap,最后才交給Canvas進(jìn)行繪制,而這里的setPixels做的事跟剛剛輸出的過(guò)程一樣,會(huì)把像素點(diǎn)全都拷貝一次。所以,我們希望把這一過(guò)程的拷貝也給取消掉,但Java并沒(méi)有提供接口給我們?nèi)カ@取Bitmap的Buffer,也就采用了反射的方案,優(yōu)化后,渲染耗時(shí)降低了65%。
我們知道,卡頓的原因在于處理一幀的耗時(shí)太久,達(dá)不到我們想要的幀率要求,那很容易會(huì)想到,我們是否可以使用多線程同時(shí)處理多幀數(shù)據(jù)呢?結(jié)果是失敗了,因?yàn)閘ibass是單例的模式,同時(shí)處理多個(gè)時(shí)間點(diǎn)的解析合成會(huì)導(dǎo)致其內(nèi)部一些狀態(tài)的錯(cuò)亂,并以crash告終。雖然解碼無(wú)法使用多線程,但渲染與libass無(wú)關(guān),還是可以拿出來(lái)放到一個(gè)多帶帶的線程去處理的。這就引入了一個(gè)新的問(wèn)題,解碼與渲染兩個(gè)線程都會(huì)操作同一塊內(nèi)存,一邊在寫、一邊在讀,數(shù)據(jù)容易出錯(cuò)。于是,我們多申請(qǐng)了一塊內(nèi)存,一個(gè)解碼用,一個(gè)渲染用,每次解碼完成時(shí)進(jìn)行交換,我們的雙緩沖異步渲染方案就這樣出現(xiàn)了
這一實(shí)現(xiàn)讓libass不需要等待渲染的完成就可以進(jìn)行下一幀數(shù)據(jù)的解碼,有效地提高了動(dòng)效的幀率
經(jīng)歷上述各項(xiàng)優(yōu)化后,前述復(fù)雜動(dòng)效在低端機(jī)Note 3上由原來(lái)的7幀達(dá)到15幀
2. 內(nèi)存問(wèn)題描述在不干預(yù)內(nèi)存的情況下,在一個(gè)3分多鐘的作品上播放了K歌線上的一個(gè)普通效果,期間內(nèi)存的變化見(jiàn)下圖:
內(nèi)存增量達(dá)到了180M,且主要是Native層的內(nèi)存,這是我們面臨的一個(gè)很嚴(yán)重的問(wèn)題,有OOM的風(fēng)險(xiǎn),系統(tǒng)也有可能因此產(chǎn)生頻繁的GC而引起卡頓
通過(guò)對(duì)libass源碼的閱讀,我們了解到了更為詳細(xì)的ASS解析過(guò)程
每一行動(dòng)效文本在libass中被定義一個(gè)事件,先是對(duì)事件中的動(dòng)畫標(biāo)簽及參數(shù)進(jìn)行解析,得到某一瞬間的所有屬性值后創(chuàng)建文字或圖形的輪廓;接著是對(duì)它進(jìn)行柵格化的處理,后續(xù)還有拼接、模糊等處理,最終生成小圖并進(jìn)行重排,就得到了卡頓問(wèn)題中所說(shuō)的一系列小圖。
在這樣的一個(gè)過(guò)程中,內(nèi)存分配主要消耗在柵格化和拼接這2個(gè)過(guò)程中,且libass內(nèi)部已經(jīng)實(shí)現(xiàn)了一套完整的緩存管理機(jī)制,只是其默認(rèn)緩存較大,分別為128M和64M,總大小達(dá)到了192M,再加上些其它的內(nèi)存分配,最大會(huì)占用超過(guò)200M的內(nèi)存才會(huì)趨于平穩(wěn)。除此之外,libass還提供了接口給我們?cè)O(shè)置緩存的大小,但只能設(shè)置總的緩存大小,不能自定義Bitmap和Composite Bitmap分別是多少,其內(nèi)部會(huì)按2:1進(jìn)行分配。
有了對(duì)libass的認(rèn)識(shí),內(nèi)存問(wèn)題也就變成了:如何尋找一個(gè)合適的緩存總大小 及 內(nèi)存的2:1分配是否適合我們的場(chǎng)景。
統(tǒng)計(jì)動(dòng)效在一次播放的過(guò)程中查詢緩存的次數(shù)M,查詢后命中的次數(shù)為N,從而得到緩存命中率N/M。下圖橫軸表示了我們給libass設(shè)置的緩存總大小,縱軸則是2類緩存的命中率
通過(guò)上面的曲線,我們可以得到2個(gè)結(jié)論:1. 隨著緩存總大小的增加,新增內(nèi)存所獲得的收益逐漸變小,對(duì)于K歌的場(chǎng)景,設(shè)置4M~16M比較合理; 2. Bitmap 與 Composite Bitmap 的分配不合理,可將更多的內(nèi)存用于Composite Bitmap。
從K歌線上的10幾個(gè)動(dòng)效中,隨機(jī)選取了5個(gè),統(tǒng)計(jì)各個(gè)動(dòng)效處理1500幀數(shù)據(jù)對(duì)2類緩存的訪求并制成了表格
通過(guò)表格的數(shù)據(jù)可以看到,Composite Bitmap需要更大的緩存,平均約為Bitmap的1.8倍,于是我們把libass內(nèi)2:1的分配規(guī)則調(diào)整為了1:1.8,最終使用8M的內(nèi)存基本上達(dá)到了原來(lái)16M的效果
設(shè)置緩存大小后,內(nèi)存增長(zhǎng)得到了控制且處于穩(wěn)定狀態(tài);而調(diào)整分配比例提高了緩存命中率,減少了CPU在內(nèi)存分配與柵格化等處理上的耗時(shí)。
小結(jié)本文主要介紹了動(dòng)效歌詞開(kāi)發(fā)的關(guān)鍵技術(shù)和優(yōu)化策略。技術(shù)方案經(jīng)歷了數(shù)次討論和預(yù)研,采用了并行計(jì)算大幅減少運(yùn)算時(shí)間,優(yōu)化了編譯策略解決了跨平臺(tái)問(wèn)題。在架構(gòu)設(shè)計(jì)上,也充分考慮性能,跨平臺(tái),可擴(kuò)展,組件化,復(fù)用性等各方面的因素。在該方案的落地實(shí)現(xiàn)過(guò)程中,團(tuán)隊(duì)的John、Harvey、Wing、 Comic,、Jerry、rey等同學(xué)通力合作,付出了不懈的努力!
此文已由騰訊云+社區(qū)在各渠道發(fā)布
獲取更多新鮮技術(shù)干貨,可以關(guān)注我們騰訊云技術(shù)社區(qū)-云加社區(qū)官方號(hào)及知乎機(jī)構(gòu)號(hào)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/53688.html
摘要:最終方案也確定采用序列幀動(dòng)畫方案。所以,要想在電影或者視頻上顯示效果,首先要做的是編寫特效文件,然后再將特效文件解析成序列幀動(dòng)畫的位圖,最后將這些位圖按照特定的順序和一定的幀率進(jìn)行播放,就能看到各種特效的動(dòng)畫。 本文由云+社區(qū)發(fā)表作者:QQ音樂(lè)技術(shù)團(tuán)隊(duì) 一、 背景 1. 現(xiàn)狀 歌詞瀏覽已經(jīng)成為音樂(lè)app的標(biāo)配,展示和動(dòng)畫效果也基本上大同小異,主要是單行的逐字染色的卡拉OK效果和多行的...
摘要:非常的龐大,而且它是完全為設(shè)計(jì)而生的動(dòng)效庫(kù)。它運(yùn)行于純粹的之上,是目前最強(qiáng)健的動(dòng)畫資源庫(kù)之一。可能是創(chuàng)建滾動(dòng)特效最好用的工具,它支持大量的瀏覽器,只要它們支持和特性。可以通過(guò)安裝吊炸天了,接近現(xiàn)實(shí)生活中的物理運(yùn)動(dòng)碰撞慣性動(dòng)畫庫(kù)。 收集日期為2019-02-28,★代表當(dāng)時(shí)的該項(xiàng)目在github的star數(shù)量 Animate.css 56401 ★ 一個(gè)跨瀏覽器的動(dòng)效基礎(chǔ)庫(kù),是許多基礎(chǔ)動(dòng)...
摘要:即將立秋的課多周刊第期我們的微信公眾號(hào),更多精彩內(nèi)容皆在微信公眾號(hào),歡迎關(guān)注。若有幫助,請(qǐng)把課多周刊推薦給你的朋友,你的支持是我們最大的動(dòng)力。課多周刊機(jī)器人運(yùn)營(yíng)中心是如何玩轉(zhuǎn)起來(lái)的分享課多周刊是如何運(yùn)營(yíng)并堅(jiān)持下來(lái)的。 即將立秋的《課多周刊》(第2期) 我們的微信公眾號(hào):fed-talk,更多精彩內(nèi)容皆在微信公眾號(hào),歡迎關(guān)注。 若有幫助,請(qǐng)把 課多周刊 推薦給你的朋友,你的支持是我們最大...
摘要:即將立秋的課多周刊第期我們的微信公眾號(hào),更多精彩內(nèi)容皆在微信公眾號(hào),歡迎關(guān)注。若有幫助,請(qǐng)把課多周刊推薦給你的朋友,你的支持是我們最大的動(dòng)力。課多周刊機(jī)器人運(yùn)營(yíng)中心是如何玩轉(zhuǎn)起來(lái)的分享課多周刊是如何運(yùn)營(yíng)并堅(jiān)持下來(lái)的。 即將立秋的《課多周刊》(第2期) 我們的微信公眾號(hào):fed-talk,更多精彩內(nèi)容皆在微信公眾號(hào),歡迎關(guān)注。 若有幫助,請(qǐng)把 課多周刊 推薦給你的朋友,你的支持是我們最大...
閱讀 3678·2021-09-27 14:02
閱讀 1794·2019-08-30 15:56
閱讀 1748·2019-08-29 18:44
閱讀 3282·2019-08-29 17:21
閱讀 491·2019-08-26 17:15
閱讀 1180·2019-08-26 13:57
閱讀 1245·2019-08-26 13:56
閱讀 2887·2019-08-26 11:30