1. avformat_open_input默认是阻塞操作,如果不加控制,等待时间可能会达到30s以上,对于有些情况,等待30s的体验是无法接受的。
2. 我设定
AVDictionary* opts = NULL;
av_dict_set(&opts, "timeout", "6000", 0);
avformat_open_input(&m_pFormatCtx,pcURL,NULL,NULL)!=0
这样做的结果,我感觉像是把timeout的时间设置为了0,不论URL值是否正确,avformat_open_input函数都返回错误值。
3. 突发奇想,启动另一个线程调用avformat_open_input,待取流超时后,杀掉线程。但事实证明,这是一个天真的想法,因为pthread_cancel不是一个靠的住的函数。虽然知道了问题所在,但却束手无策。但最终还是在网上找到了解决方案。
4. 该方案具体如下:创建AVFormatContext成功后,设置中断回调(数据成员interrupt_callback.opaque(自定义指针),和interrupt_callback.callback(回调函数)),设置一个全局变量(类成员变量)last_packet_timestamp,调用avformat_open_input前初始化为time(nullptr),在回调函数里计算当前时间和last_packet_timestame的差值,当差值超过某一阈值时,比如说10s时,则认为取流超时,返回一个小于0的值,告诉调用者取流失败,其他情况下返回0。
但是用该方案一定要注意,成功取到流,每次调用av_read_frame成功后,一定要更新last_packet_timestamp。否则,av_read_frame还会超值,因为设置的中断回调会被循环调用,如果last_packet_timestamp不更新,很快就会超时。
ffmpeg支持interrupt_callback机制,可以对输入(或输出)的AVFormatContext的interrupt_callback成员设置,然后再回调函数中做控制。
// 回调函数的参数,用了时间
typedef struct {
time_t lasttime;
bool connected;
}Runner;
// 回调函数
static int interrupt_callback(void *p)
{
Runner *r = (Runner *)p;
if(r->lasttime > 0)
{
if(time(NULL) - r->lasttime > 2 && !input_runner.connected)
{
return 1;
}
}
return 0;
}
// usage
Runner input_runner = {0};
AVFormatContext *ifmt_ctx = avformat_alloc_context();
ifmt_ctx->interrupt_callback.callback = interrupt_callback;
ifmt_ctx->interrupt_callback.opaque = &input_runner;
input_runner.lasttime = time(NULL);
// 调用之前初始化时间
ret = avformat_open_input(&ifmt_ctx, url, NULL, NULL);
if(ret < 0) {
// error
}
input_runner.lasttime = time(NULL);
ret = av_read_frame(input_fmt_ctx,&pkt);
参考: https://ffmpeg.org/doxygen/3.2/structAVFormatContext.html