一.背景說明
用戶反映某課程視頻(mp4格式)從打開播放器到第一幀畫面顯示劝堪,加載時間過長纱兑,影響觀看體驗。
二.時間埋點
1.定位耗時問題拇惋,最直接的方法是在關(guān)鍵節(jié)點埋點,打印時間戳抹剩,找出耗時操作撑帖。項目使用的是IJKPlayer
,需要注意的是埋點前需要關(guān)閉ffmpeg
的打印日志吧兔,打印大量的日志會很耗性能(ffmpeg
在給mp4文件創(chuàng)建索引表時會打印大量ctts磷仰,sttz等信息):
[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];
//改成Silent模式
[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_SILENT];
埋點代碼如下(精確到毫秒):
struct tm *ptm;
struct timeb stTimeb;
ftime(&stTimeb);
ptm = localtime(&stTimeb.time);
printf("0:Local time is:%02d-%02d %02d:%02d:%02d.%03d\n",
ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, stTimeb.millitm);
2.在ffplay
初始化到第一幀畫面顯示之間埋點。幾個重要函數(shù)如下:
stream_open
:ffplay
入口函數(shù)境蔼。
avformat_open_input
:用于打開多媒體數(shù)據(jù)并且獲得相關(guān)信息灶平。
av_read_frame
:從文件中讀取出AVPacket
。
decode_video_internal
:將AVPacket
視頻包送入VideoToolBox
中解碼箍土。
VTDecoderCallback
:VideoToolBox
解碼完成的回調(diào)函數(shù)逢享,此函數(shù)將解碼后的YUV數(shù)據(jù)送入OpenGL ES中渲染出畫面。
3.分別在本地模式和在線模式吴藻,打印時間戳:
縱向?qū)Ρ龋涸诰€模式下瞒爬,首幀顯示總耗時為4.967秒,速度較慢沟堡。其中
avformat_open_input
函數(shù)占總耗時的94%左右侧但。本地模式下,首幀顯示總耗時為0.257秒航罗,速度較快禀横。其中avformat_open_input
函數(shù)占總耗時的34%左右。橫向?qū)Ρ龋涸诰€模式下粥血,
avformat_open_input
函數(shù)的耗時為4.668秒柏锄,本地模式下為0.088秒酿箭,相差4.58秒。
三趾娃、分析avformat_open_input
函數(shù)
1.avformat_open_input
函數(shù)是ffmpeg
框架中libavformat
庫的核心函數(shù)缭嫡,作用是打開一個輸入流并且讀它的頭部信息。函數(shù)內(nèi)部做了3件事:
a.分配一個
AVFormatContext
的實例抬闷。b.調(diào)用
init_input
函數(shù)初始化輸入流的信息妇蛀。這里會初始化AVInputFormat
。c.根據(jù)上一步初始化好的
AVInputFormat
的類型饶氏,調(diào)用它的read_header
方法讥耗,讀取文件頭。
2. 從以上流程看疹启,a.c的邏輯在本地播放與在線播放一致古程,所以不是耗時操作。而b中初始化流信息喊崖,需要根據(jù)探測到的視頻格式向服務(wù)器端請求視頻頭部信息挣磨。
四、分析視頻結(jié)構(gòu)
1.請求視頻頭部信息荤懂。在mp4格式下即為請求moov數(shù)據(jù)茁裙。使用Media Parser
解析問題視頻如下:
ftyp(32個字節(jié)):描述的文件的版本、兼容協(xié)議等
free(8個字節(jié)):未知box节仿。
mdat(220572275個字節(jié)):實際媒體數(shù)據(jù)晤锥。解碼播放的數(shù)據(jù)都在這里面。
moov(2310446個字節(jié)):container box.不包含具體媒體數(shù)據(jù)廊宪,但包含本文件中所有媒體數(shù)據(jù)的宏觀描述信息矾瘾。
2.從圖中可以看出,moov在視頻文件尾部箭启。本地播放時壕翩,通過指針可快速定位到moov;在線播放則需要先加載一部分頭部數(shù)據(jù)傅寡,如fytp放妈,free,madat等荐操,計算出moov的偏移量芜抒,再通過Http Range Bytes請求moov數(shù)據(jù).
2.moov數(shù)據(jù)有2310446個字節(jié),約2.2M托启。在線播放時avformat_open_input
需請求全部的moov數(shù)據(jù)宅倒,建立索引表后,再解析音視頻數(shù)據(jù)驾中。本地播放無需網(wǎng)絡(luò)請求唉堪。
五、結(jié)論及解決方案
1.mp4的結(jié)構(gòu)特性是導(dǎo)致長視頻首幀顯示過慢的主要原因肩民。
播放mp4音視頻數(shù)據(jù)前需要先加載moov數(shù)據(jù)唠亚,moov的大小和視頻長度成正比(如問題視頻為長度為46分01秒,moov數(shù)據(jù)約2.2M)持痰。在網(wǎng)絡(luò)較差的情況下灶搜,加載moov數(shù)據(jù)會耗費較長時間。
解決方案:避免加載體積過大的視頻頭工窍。
a.拆分長視頻成數(shù)個短視頻割卖。如46分鐘左右的視頻可拆分為3個15分鐘左右的短視頻。減少moov的長度患雏。
ffmpeg -ss 00:00:00 -t 00:15:00 -i test.mp4 -vcodec copy -acodec copy output.mp4
b.使用輕量級格式鹏溯,如分段FLV(愛奇藝,優(yōu)酷)淹仑,DASH(YouTube丙挽,B站)等方案。
2.moov
在視頻文件尾部匀借,多了一次seek請求操作颜阐。相對于第一點,影響較小吓肋。
解決方案:服務(wù)器端將moov
從文件尾部移到ftyp
后面凳怨,ffmpeg命令行如下:
ffmpeg -i input.mp4 -c:a copy -c:v copy -movflags +faststart output.mp4
3.預(yù)加載,在適當(dāng)時機(jī)提前加載視頻頭部數(shù)據(jù)是鬼,寫入本地文件肤舞,播放器從本地讀取數(shù)據(jù),快速構(gòu)建索引表屑咳,進(jìn)入首幀解碼萨赁。
可參考KTVHTTPCache緩存方案。需要注意的是:
a.moov box在頭部位置,否則加載不到兆龙。
b.moov box數(shù)據(jù)量不能過大(長視頻)杖爽,比如moov box 數(shù)據(jù)量有好幾M,預(yù)加載1M紫皇,意義不大慰安。