? ? ?我們知道,ffmpeg中的avformat有個(gè)"timeout"的opt參數(shù),可以設(shè)置播放tcp讀寫超時(shí)時(shí)間,當(dāng)我們使用ffmpeg的api來(lái)播放網(wǎng)絡(luò)點(diǎn)播或直播url的時(shí)候,這個(gè)參數(shù)可以指定網(wǎng)絡(luò)斷開(kāi)后多長(zhǎng)時(shí)間后能返回讀取失敗的錯(cuò)誤,比如設(shè)置超時(shí)時(shí)間為10s代碼如下:
av_dict_set(&ffp->format_opts, "timeout", "1000000", 0);
但當(dāng)我們使用rtmp協(xié)議時(shí),設(shè)置該參數(shù)將導(dǎo)致無(wú)法正常連接播放,比如IJKPlayer為了規(guī)避這個(gè)問(wèn)題,就做了如下處理:
if(av_stristart(is->filename,"rtmp",NULL) ||
av_stristart(is->filename,"rtsp",NULL)) {
// There is total different meaning for 'timeout' option in rtmp
av_log(ffp,AV_LOG_WARNING,"remove 'timeout' option for rtmp.\n");
av_dict_set(&ffp->format_opts, "timeout", NULL, 0);
}
這樣雖然能播放rtmp了,但帶來(lái)的一個(gè)問(wèn)題就是無(wú)法設(shè)置timeout參數(shù)了,只能承受默認(rèn)的兩分鐘的超時(shí)時(shí)間,對(duì)于需要靈活處理網(wǎng)絡(luò)不穩(wěn)定時(shí)能及時(shí)返回并嘗試重連的播放器來(lái)說(shuō)就會(huì)是一個(gè)坑。
那為什么播放rtmp時(shí)不能設(shè)置這個(gè)參數(shù)呢?我們先來(lái)看看ffmpeg中l(wèi)ibavformat里面的tcp.c的參數(shù)列表中這一行:
{ "timeout",? ? "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout)...
這個(gè)timeout原來(lái)是設(shè)置給了TCPContext中的rw_timeout,完整結(jié)構(gòu)體如下:
typedef struct TCPContext {
const AVClass *class;
int fd;
int listen;
int open_timeout;
int rw_timeout;
int listen_timeout;
int recv_buffer_size;
int send_buffer_size;
int64_t app_ctx_intptr;
int addrinfo_one_by_one;
int addrinfo_timeout;
AVApplicationContext *app_ctx;
} TCPContext;
這個(gè)rw_timeout就是我們?cè)趖cp連接后讀寫超時(shí)的時(shí)間,我們?cè)賮?lái)看rtmp中的設(shè)置,在rtmpproto.c中的rtmp_options里的最后一行,卻把timeout賦給了listen_timeout,而當(dāng)我們是作為客戶端去連接rtmp服務(wù)器的時(shí)候,給listen_timeout賦值會(huì)導(dǎo)致連接不上rtmp服務(wù)器,我想這就是ijkplayer的作者為什么要在read_thread里面判斷是rtmp協(xié)議時(shí),將"timeout"清空的原因了吧,要修復(fù)這個(gè)問(wèn)題很簡(jiǎn)單,只需要將rtmp_options里的"timeout"修改為"listen_time"即可,當(dāng)然你也可以改為其它參數(shù)名,這樣就避免了沖突,而且在你很確定需要設(shè)置listen的超時(shí)時(shí)間時(shí),在外部設(shè)置"listen_time"這個(gè)參數(shù)就可以了。