ffmpeg/VLC连接rtmp视频断开的一个原因

问题

这几天解决了一个问题。问题是这样的,用ffmpeg向我做的rtmp server请求rtmp视频,99%的情况视频会在10秒钟内断开。ffmpeg会报一个mismatch的错误。打印是这样的:RTMP packet size mismatch %d != %d。在ffmpeg代码中是在rtmp_packet_read_one_chunk接口中,前后两包所在帧的尺寸不匹配,需要断开。

 

原因

发送的数据有一部分丢失,导致ffmpeg收到的帧有时候会不完整,ffmpeg校验非常严格,当发现有一个帧的数据不完整的时候就断开连接,就出现了连接几秒钟就断开的现象。

 

解决办法

确保发出去的每一帧都是完整的,不允许出现有一帧的部分数据没发送成功,接下来发送的数据是下一包或下一帧的数据的情况出现。网络不好的时候可以一帧一帧的丢弃数据,但绝不能一包一包的丢数据。

 

分析

1、ffmpeg的接收接口我找了很久,最后发现其实很简单,就是简单的recv()来实现,是下面这个接口。

static int tcp_read(URLContext *h, uint8_t *buf, int size)
{
    TCPContext *s = h->priv_data;
    int ret;

    if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
        ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
        if (ret)
            return ret;
    }
    ret = recv(s->fd, buf, size, 0);
    if (ret == 0)
        return AVERROR_EOF;
    return ret < 0 ? ff_neterrno() : ret;
}

2、整个rtmp的数据接收,拼装,和组合是下面这个接口实现的。在printtttttttttt那个地方我进行过打印比较,把接收到的数据和我server端发送的数据进行比较,最后发现是数据错误就是在这里发现的。为了进一步确认,还和wireshark抓包数据进行了比较,最终定位的原因。

static int rtmp_read(URLContext *s, uint8_t *buf, int size)
{
    RTMPContext *rt = s->priv_data;
    int orig_size = size;
    int ret;
	//printf("\n\n\n\n\n\n%s %d size=%d filename=%s max_packet_size=%d\n",__FUNCTION__,__LINE__,size,s->filename,s->max_packet_size);
    while (size > 0) {
        int data_left = rt->flv_size - rt->flv_off;
		//printf("%s %d %d - %d = %d\n\n",__FUNCTION__,__LINE__,rt->flv_size,rt->flv_off,data_left);
        if (data_left >= size) {
            memcpy(buf, rt->flv_data + rt->flv_off, size);
            rt->flv_off += size;
			//printf("%s %d here orig_size=%d\n",__FUNCTION__,__LINE__,orig_size);
            return orig_size;
        }
        if (data_left > 0) {
            memcpy(buf, rt->flv_data + rt->flv_off, data_left);
			//printtttttttttt
            buf  += data_left;
            size -= data_left;
            rt->flv_off = rt->flv_size;
			//printf("%s %d here data_left=%d\n",__FUNCTION__,__LINE__,data_left);
            return data_left;
        }
		//printf("%s %d here\n",__FUNCTION__,__LINE__);
        if ((ret = get_packet(s, 0)) < 0){
			//printf("%s %d here ret=%d\n",__FUNCTION__,__LINE__,ret);
			return ret;
		}
    }
    return orig_size;
}

3、为什么我这里会出现发送不完整的情况呢?为了消除各模块之间的相互影响,这里的socket必须是非阻塞状态,send后必须马上返回,再加上未知原因导致ffmpeg接收的不是特别特别的快,导致发送缓冲区经常出现满的状态,所以就出现了这个问题。如果是阻塞socket就不会有这个问题。

 

最后

仓促之间完成这篇文章,关键地方都已经指出,ffmpeg里面的具体逻辑就不说了,抓住我上面提到的2个接口就足够分析整个ffmpeg对简单rtmp协议解析的过程。ffmpeg是一个严格的工具,不是为了应用使用,而是为了发现我们逻辑问题而存在的。flash才是为了应用,它就可以兼容数据不全的问题,不影响画面。ffmpeg要求每一帧必须是完整的,一个字节都不能差。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值