avformat_open_input阻塞源码分析

原因:在进行RTP拉流时,会出现avforamt_open_input阻塞问题,故通过源码进行分析

概况:avforamt_open_input默认为阻塞模式,可以通过三种方式进行设置阻塞返回,但是不同的阻塞环节,不同协议方式也不全部可行。下面三种方式的伪代码,接下来通过源码进行分析并给出解决方案。

	
    int callback_cb(void *p)
    {
	  return 1;
    }

    AVFormatContext *pFormatCtx = nullptr;

	pFormatCtx = avformat_alloc_context();

	pFormatCtx->interrupt_callback.opaque = nullptr;
	pFormatCtx->interrupt_callback.callback = callback_cb;//返回1,则退出阻塞

	AVDictionary *pDictionary=nullptr;

	av_dict_set_int(&pDictionary, "timeout", 6, 0);

	pFormatCtx->flags |= AVIO_FLAG_NONBLOCK;

    int ir = avformat_open_input(&pFormatCtx, url, nullptr, &pDictionary);

以rtp拉流为例,伪代码如下:可以简单理解为init_input方法根据输入的url进行判断当前协议是否支持,如果是文件的话则进行内部打开,由于是rtp协议,故阻塞发生在read_header中.

int avformat_open_input(AVFormatContext **ps, const char *filename,
                        AVInputFormat *fmt, AVDictionary **options)
{
    if ((ret = init_input(s, filename, &tmp)) < 0)
        goto fail;

    f (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
        if ((ret = s->iformat->read_header(s)) < 0)
            goto fail;

}

接下来read_header的简答实现伪代码如下:通过下面的实现代码可以看出内部只是进行了一个read操作,如果读取失败则会进行跳出循环,故真实的阻塞发生在read中.

static int rtp_read_header(AVFormatContext *s)
{

while (1) {
        ret = ffurl_read(in, recvbuf, sizeof(recvbuf));
        if (ret == AVERROR(EAGAIN))
            continue;
        if (ret < 0)
            goto fail;
        if (ret < 12) {
            av_log(s, AV_LOG_WARNING, "Received too short packet\n");
            continue;
        }

        if ((recvbuf[0] & 0xc0) != 0x80) {
            av_log(s, AV_LOG_WARNING, "Unsupported RTP version packet "
                                      "received\n");
            continue;
        }

        if (RTP_PT_IS_RTCP(recvbuf[1]))
            continue;

        payload_type = recvbuf[1] & 0x7f;
        break;
    }

}

重点分析下面的read内部流程代码实现如下:通过代码可以看出跳出循环有三种条件,第一:URLContext中的interrupt_callback设置回调.第二:URLContext中flag设置为AVIO_FLAG_NONBLOCK属性,第三:URLContext中的rw_timeout设置阻塞时长.

tatic inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,
                                         int size, int size_min,
                                         int (*transfer_func)(URLContext *h,
                                                              uint8_t *buf,
                                                              int size))
{
    int ret, len;
    int fast_retries = 5;
    int64_t wait_since = 0;

    len = 0;
    while (len < size_min) {
        if (ff_check_interrupt(&h->interrupt_callback))
            return AVERROR_EXIT;
        ret = transfer_func(h, buf + len, size - len);
        if (ret == AVERROR(EINTR))
            continue;
        if (h->flags & AVIO_FLAG_NONBLOCK)
            return ret;
        if (ret == AVERROR(EAGAIN)) {
            ret = 0;
            if (fast_retries) {
                fast_retries--;
            } else {
                if (h->rw_timeout) {
                    if (!wait_since)
                        wait_since = av_gettime_relative();
                    else if (av_gettime_relative() > wait_since + h->rw_timeout)
                        return AVERROR(EIO);
                }
                av_usleep(1000);
            }
        } else if (ret < 1)
            return (ret < 0 && ret != AVERROR_EOF) ? ret : len;
        if (ret) {
            fast_retries = FFMAX(fast_retries, 2);
            wait_since = 0;
        }
        len += ret;
    }
    return len;
}

那么如何通过设置AVFomatContex属性,将阻塞配置赋值给URLContext对象,通过下面的伪代码可以清楚的看出目前只有interrupt_callback被进行了设置,故如果需要不同的跳出方式可以通过修改rtp源码进行添加即可。

static int rtp_read_header(AVFormatContext *s)
{

ret = ffurl_open_whitelist(&in, s->filename, AVIO_FLAG_READ,
                     &s->interrupt_callback, NULL, s->protocol_whitelist, s-    
                  >protocol_blacklist, NULL);

  while(1)
 {

 }
}

总结:通过RTP拉流分析,在RTP模式下跳出avforamt_open_input阻塞的方式为通过设置AVIOInterruptCB interrupt_callback.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值