ijkplayer是bibiliB站開源的一個三方,面向對象開發。
蘋果提供了:AVPlayer播放不了直播文件。需要自己去基於ffmpeg播放。
ijkplayer
框架是專門用來做
視頻直播 的開源框架,基於
ffmpeg
,同時支持
Android 和
iOS 平台。
對於 App 中的直播功能,集成
ijkplayer
,那么就算完成一半的工程了。接下來,只要獲取到
拉流 URL,就能實現
視頻直播
功能。

2.克隆ijkplayer到桌面
cd Desktop/ git clone https://github.com/Bilibili/ijkplayer.git ijkplayer
或者直接下載zip.解壓,切到解壓的文件夾內
(請忽略背景😂,年輕時候弄得電腦。)
打開 IJKMediaDemo,並編譯
提示: 'libavformat/avformat.h' file not found

ijkplayer
的 README.md。
libavformat
是
ffmpeg
框架中的庫,而
ijkplayer
又是基於
ffmpeg
框架的,因此需要編譯
ffmpeg
。
3.下載ffmpeg
cd ijkplayer
git checkout -B latest k0.8.8 ./init-ios.sh (下載ffmpeg)
注:下載最好使用FQ軟件,本人使用藍燈設置全局代理。下載很快,否則容易失敗。

要下載一會,下載完成之后,在ios目錄下就有了ffmpeg文件:
這時候我們的 ffmpeg 就下載好了,再次運行 IJKMediaDemo
發現還是報上面的錯誤,因為執行init-ios.sh,只是下載ffmpeg源碼,但是源碼並沒有參與編譯,需要把源碼編譯成.a文件:
4、編譯 ffmpeg 庫
進入 ios 文件的目錄中
cd ios ./compile-ffmpeg.sh clean ./compile-ffmpeg.sh all
注:1.執行 ./compile-ffmpeg.sh clean
,目的是刪除一些文件和文件夾,為編譯ffmpeg.sh做准備,在編譯ffmpeg.sh的時候,會自動創建剛剛
刪除的那些文件,為避免文件名沖突,因此在編譯ffmpeg.sh之前先刪除等會會自動創建的文件夾或者文件
2.執行 ./compile-ffmpeg.sh all
目的是編譯各個平台的ffmpeg庫,並生成所以平台的通用庫。
可能遇到的問題:
warning: optimization flag '-fomit-frame-pointer' is not supported for target 'armv7' [-Wignored-optimization-argument]
AS libavcodec/arm/aacpsdsp_neon.o
./libavutil/arm/asm.S:50:9: error: unknown directive
.arch armv7-a
^
make: *** [libavcodec/arm/aacpsdsp_neon.o] Error 1
make: *** Waiting for unfinished jobs....

不支持arm7
那么刪除這個編譯目標
解決辦法:
打開ios目錄下這個 compile-ffmpeg.sh 文件
第24行 改為: FF_ALL_ARCHS_IOS8_SDK="arm64 i386 x86_64"
第120行 改為: if [ "$FF_TARGET" = "armv7s" -o "$FF_TARGET" = "arm64" ]; then
第159行 改為: echo " compile-ffmpeg.sh arm64|i386|x86_64"
5.這時候編譯成功已經可以運行列子了
如需打包framework參考:文章三
6.優化加速
延遲優化
發現拉流存在3-6秒延遲,這個根據七牛直播雲大牛講解的一些問題點。進行優化。
丟包處理方案參考
a),有音頻流和視頻流,或者只有音頻流情況下,當audioq達到一定的duration,就丟掉前面一部分數據包,因為默認是AV_SYNC_AUDIO_MASTER,視頻會追上來。
b),只有視頻流情況,當videoq達到一定的duration,就丟掉前面一部分數據包。
開始本人修改丟失幀數過大,雖然延遲不存在了,但是同時測試局域網出現供不應求現象,就是畫面過一混會卡頓。因此還是修改緩存時間比較好。
ff_ffplay.c read_thread 線程中,在每次 av_read_frame后去判斷緩存隊列有沒有達到最大時長。這里需要把原來的realtime設置為0。
別的不說直接上代碼
通過修改源文件,因為ijkplayer實際上是基於ffplay.c實現的:
ijkmedia>ijkplayer>ff_ffplay.c這個文件
static double vp_duration(VideoState *is, Frame *vp, Frame *nextvp) { if(vp->serial == nextvp->serial) { doubleduration = nextvp->pts - vp->pts; if(isnan(duration) || duration <=0|| duration > is->max_frame_duration) return vp->duration; else return duration; }else{ return 0.0; } }
改成直接返回duration
static double vp_duration(VideoState*is,Frame*vp,Frame*nextvp) { return vp->duration; }
接着改staticintffplay_video_thread這個方法:
static int ffplay_video_thread(void*arg){ FFPlayer*ffp = arg; VideoState*is = ffp->is; AVFrame*frame =av_frame_alloc(); doublepts; doubleduration; intret; AVRationaltb = is->video_st->time_base; //注釋如下一行代碼 //AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL); //......省略部分代碼 //注釋如下一行代碼 //duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational) {frame_rate.den, frame_rate.num}) : 0); //直接這里寫出 duration=0.01; //........ }
延遲明顯降低,高分辨率開啟硬解碼,不支持的話會自動切換到軟解,就算開啟mediacodec,如果設備不支持,顯示的解碼器也是avcodec軟解
參數配合設置如下(由於c碼,安卓蘋果參數基本一樣)
修改后基本延遲很低了1秒左右。然后
直接上修改后的代碼參數配合編譯的包
//播放前的探測Size,默認是1M, 改小一點會出畫面更快 [options setFormatOptionIntValue:1024 * 16 forKey:@"probesize"]; //播放前的探測時間 [options setFormatOptionIntValue:50000 forKey:@"analyzeduration"]; //默認好像是硬解開啟軟解 [options setPlayerOptionIntValue:0 forKey:@"videotoolbox"]; //解碼參數,畫面更清晰 [options setCodecOptionIntValue:IJK_AVDISCARD_DEFAULT forKey:@"skip_loop_filter"]; //這個目前理解應該是丟幀數設置 [options setCodecOptionIntValue:IJK_AVDISCARD_DEFAULT forKey:@"skip_frame"]; [options setPlayerOptionIntValue:3000 forKey:@"max_cached_duration"]; // 最大緩存大小是3秒,可以依據自己的需求修改 [options setPlayerOptionIntValue:1 forKey:@"infbuf"]; // 無限讀 [options setPlayerOptionIntValue:0 forKey:@"packet-buffering"]; // 關閉播放器緩沖
7.如此設置已經基本可以實現0延遲播放了。
集成到自己項目見:其他一些優化本人也做了一些嘗試。詳情見下篇。
安卓端本人根據參數翻譯代碼設置如下:
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 1024 * 16); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 50000); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_frame", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 3000); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 1); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0);
另外如果是后台播放可以參考改變設置
// Param for playback
//ios
[options setPlayerOptionIntValue:0 forKey:@"max_cached_duration"]; [options setPlayerOptionIntValue:0 forKey:@"infbuf"]; [options setPlayerOptionIntValue:1 forKey:@"packet-buffering"];
//android
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", 0); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 1);
另外一些其他參數參考定義如下,跟據需要修改。
//開啟硬件解碼 [options setPlayerOptionIntValue:1 forKey:@"videotoolbox"]; // 設置音量大小,256為標准音量。(要設置成兩倍音量時則輸入512,依此類推) [options setPlayerOptionIntValue:512 forKey:@"vol"]; // 最大fps [options setPlayerOptionIntValue:30 forKey:@"max-fps"]; // 跳幀開關,如果cpu解碼能力不足,可以設置成5,否則 // 會引起音視頻不同步,也可以通過設置它來跳幀達到倍速播放 [options setPlayerOptionIntValue:0 forKey:@"framedrop"]; // 指定最大寬度,我沒試過 [options setPlayerOptionIntValue:960 forKey:@"videotoolbox-max-frame-width"]; // 自動轉屏開關,我沒試過 [options setFormatOptionIntValue:0 forKey:@"auto_convert"]; // 重連次數, 我沒試過 [options setFormatOptionIntValue:1 forKey:@"reconnect"]; // 超時時間,timeout參數只對http設置有效,若果你用rtmp設置timeout,ijkplayer內部會忽略timeout參數。rtmp的timeout參數含義和http的不一樣。 [options setFormatOptionIntValue:30 * 1000 * 1000 forKey:@"timeout"]; // 幀速率(fps) 我沒試過(可以改,確認非標准楨率會導致音畫不同步,所以只能設定為15或者29.97) [options setPlayerOptionIntValue:29.97 forKey:@"r"];
Note: 這里有個比較有用的參數,skip_loop_filter
// for codec option 'skip_loop_filter' and 'skip_frame' typedef enum IJKAVDiscard { /* We leave some space between them for extensions (drop some * keyframes for intra-only or drop just some bidir frames). */ IJK_AVDISCARD_NONE =-16, ///< discard nothing IJK_AVDISCARD_DEFAULT = 0, ///< discard useless packets like 0 size packets in avi IJK_AVDISCARD_NONREF = 8, ///< discard all non reference IJK_AVDISCARD_BIDIR = 16, ///< discard all bidirectional frames IJK_AVDISCARD_NONKEY = 32, ///< discard all frames except keyframes IJK_AVDISCARD_ALL = 48, ///< discard all } IJKAVDiscard; 前面兩個都看得懂 第三個是拋棄非參考幀(I幀) 第四個是拋棄B幀 第五個是拋棄除關鍵幀以外的,比如B,P幀 第六個是拋棄所有的幀,這我就奇怪了,之前Android默認的就是48,難道把所有幀都丟了? 那就沒有視頻幀了, 所以應該不是這么理解, 應該是skip_loop_filter和skip_frame的對象要過濾哪些幀類型。 skip_loop_filter這個是解碼的一個參數,叫環路濾波, 設置成48和0,圖像清晰度對比,0比48清楚,理解起來就是, 0是開啟了環路濾波,過濾的是大部分, 而48基本沒啟用環路濾波,所以清晰度更低,但是解碼性能開銷小 skip_loop_filter(環路濾波)簡言之: a:環路濾波器可以保證不同水平的圖像質量。 b:環路濾波器更能增加視頻流的主客觀質量,同時降低解碼器的復雜度。
具體參考:
http://blog.csdn.net/h514434485/article/details/52241778 http://www.cnblogs.com/TaigaCon/p/5500110.html skip_frame我沒完全理解意思,應該是等同上面這個類似。
另外安卓參數基本一樣
接口不同而已,注意屬性的分類
eg:OPT_CATEGORY_PLAYER
//可以打開h265硬解; ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1); //開啟mediacodec硬解 ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1); ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
下篇
ijkplayer的一些優化
參考前人經驗更新
輪子使用中
1、https://blog.csdn.net/ssy_1992/article/details/79191727 //編譯流程
或 https://www.jianshu.com/p/9a69af13835e
注意FQ下載編譯。
2、優化
覺得有用的同學點個關注,或者留言評論區,看到郵件提示消息盡快回復。