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

Love The Way You Lie 2022-01-27 14:37 1183阅读 0赞

问题

这几天解决了一个问题。问题是这样的,用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()来实现,是下面这个接口。

  1. static int tcp_read(URLContext *h, uint8_t *buf, int size)
  2. {
  3. TCPContext *s = h->priv_data;
  4. int ret;
  5. if (!(h->flags & AVIO_FLAG_NONBLOCK)) {
  6. ret = ff_network_wait_fd_timeout(s->fd, 0, h->rw_timeout, &h->interrupt_callback);
  7. if (ret)
  8. return ret;
  9. }
  10. ret = recv(s->fd, buf, size, 0);
  11. if (ret == 0)
  12. return AVERROR_EOF;
  13. return ret < 0 ? ff_neterrno() : ret;
  14. }

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

  1. static int rtmp_read(URLContext *s, uint8_t *buf, int size)
  2. {
  3. RTMPContext *rt = s->priv_data;
  4. int orig_size = size;
  5. int ret;
  6. //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);
  7. while (size > 0) {
  8. int data_left = rt->flv_size - rt->flv_off;
  9. //printf("%s %d %d - %d = %d\n\n",__FUNCTION__,__LINE__,rt->flv_size,rt->flv_off,data_left);
  10. if (data_left >= size) {
  11. memcpy(buf, rt->flv_data + rt->flv_off, size);
  12. rt->flv_off += size;
  13. //printf("%s %d here orig_size=%d\n",__FUNCTION__,__LINE__,orig_size);
  14. return orig_size;
  15. }
  16. if (data_left > 0) {
  17. memcpy(buf, rt->flv_data + rt->flv_off, data_left);
  18. //printtttttttttt
  19. buf += data_left;
  20. size -= data_left;
  21. rt->flv_off = rt->flv_size;
  22. //printf("%s %d here data_left=%d\n",__FUNCTION__,__LINE__,data_left);
  23. return data_left;
  24. }
  25. //printf("%s %d here\n",__FUNCTION__,__LINE__);
  26. if ((ret = get_packet(s, 0)) < 0){
  27. //printf("%s %d here ret=%d\n",__FUNCTION__,__LINE__,ret);
  28. return ret;
  29. }
  30. }
  31. return orig_size;
  32. }

20190527092410942.png

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

最后

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

发表评论

表情:
评论列表 (有 0 条评论,1183人围观)

还没有评论,来说两句吧...

相关阅读