我们知道,ffmpeg中的avformat有个"timeout"的opt参数,可以设置播放tcp读写超时时间,当我们使用ffmpeg的api来播放网络点播或直播url的时候,这个参数可以指定网络断开后多长时间后能返回读取失败的错误,比如设置超时时间为10s代码如下:
av_dict_set(&ffp->format_opts, "timeout", "1000000", 0);
但当我们使用rtmp协议时,设置该参数将导致无法正常连接播放,比如IJKPlayer为了规避这个问题,就做了如下处理:
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了,但带来的一个问题就是无法设置timeout参数了,只能承受默认的两分钟的超时时间,对于需要灵活处理网络不稳定时能及时返回并尝试重连的播放器来说就会是一个坑。
那为什么播放rtmp时不能设置这个参数呢?我们先来看看ffmpeg中libavformat里面的tcp.c的参数列表中这一行:
{ "timeout", "set timeout (in microseconds) of socket I/O operations", OFFSET(rw_timeout)...
这个timeout原来是设置给了TCPContext中的rw_timeout,完整结构体如下:
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;
这个rw_timeout就是我们在tcp连接后读写超时的时间,我们再来看rtmp中的设置,在rtmpproto.c中的rtmp_options里的最后一行,却把timeout赋给了listen_timeout,而当我们是作为客户端去连接rtmp服务器的时候,给listen_timeout赋值会导致连接不上rtmp服务器,我想这就是ijkplayer的作者为什么要在read_thread里面判断是rtmp协议时,将"timeout"清空的原因了吧,要修复这个问题很简单,只需要将rtmp_options里的"timeout"修改为"listen_time"即可,当然你也可以改为其它参数名,这样就避免了冲突,而且在你很确定需要设置listen的超时时间时,在外部设置"listen_time"这个参数就可以了。