当前位置: 首页 > news >正文

avcodec_send_packet函数阻塞

用ffmpeg4.1.4开发一个播放器,解码过程如下,在每个函数前设置标志,测试发现程序阻塞在avcodec_send_packet函数。

while(true){av_read_frameavcodec_send_packetavcodec_receive_frameav_packet_unref
}

解释如下:

avcodec_send_packetavcodec_receive_frame 的工作原理是,avcodec_send_packet 向解码器发送压缩数据包,而 avcodec_receive_frame 从解码器接收解码后的帧。解码器内部有一个缓冲区,用于存储解码过程中间的数据。如果缓冲区已满(即没有足够的空间来存储新的数据包),avcodec_send_packet 就会阻塞,直到有足够的空间。

我用的是处理器是rk3288,后台打印信息如下:

[h264_rkmpp @ 0xa5795e20] Received a frame.
[h264_rkmpp @ 0xa5795e20] Wrote 1615 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Received a frame.
[h264_rkmpp @ 0xa5795e20] Wrote 2107 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Received a frame.
[h264_rkmpp @ 0xa5795e20] Wrote 2064 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Received a frame.
[h264_rkmpp @ 0xa5795e20] Wrote 1504 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Received a frame.
[h264_rkmpp @ 0xa5795e20] Wrote 2588 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Received a frame.
[h264_rkmpp @ 0xa5795e20] Wrote 1642 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Received a frame.
[h264_rkmpp @ 0xa5795e20] Wrote 1721 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Received a frame.
[h264_rkmpp @ 0xa5795e20] Wrote 1437 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 2307 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1427 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1638 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 2303 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1342 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 2193 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1567 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 2247 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1396 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 2375 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1520 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1663 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 2273 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1288 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 2340 bytes to decoder
[h264_rkmpp @ 0xa5795e20] Wrote 1399 bytes to decoder

avcodec_send_packe函数为什么会“get a frame”呢?源码如下,猜测是阻塞到这里

int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
{AVCodecInternal *avci = avctx->internal;int ret;if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec))return AVERROR(EINVAL);if (avctx->internal->draining)return AVERROR_EOF;if (avpkt && !avpkt->size && avpkt->data)return AVERROR(EINVAL);av_packet_unref(avci->buffer_pkt);if (avpkt && (avpkt->data || avpkt->side_data_elems)) {ret = av_packet_ref(avci->buffer_pkt, avpkt);if (ret < 0)return ret;}ret = av_bsf_send_packet(avci->filter.bsfs[0], avci->buffer_pkt);if (ret < 0) {av_packet_unref(avci->buffer_pkt);return ret;}if (!avci->buffer_frame->buf[0]) {ret = decode_receive_frame_internal(avctx, avci->buffer_frame);if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)return ret;}return 0;
}static int decode_simple_receive_frame(AVCodecContext *avctx, AVFrame *frame)
{int ret;while (!frame->buf[0]) {ret = decode_simple_internal(avctx, frame);if (ret < 0)return ret;}return 0;
}

解决方法:

1、在调用 avcodec_send_packet 之前,可以先调用 avcodec_receive_frame,即使你不确定是否有帧可以接收。这有助于清理缓冲区。

2、在while循环中等待avcodec_receive_frame

    ret = avcodec_send_packet(codec_ctx, packet); while (ret >= 0) {AVFrame *frame = av_frame_alloc();if (!frame) {// 处理内存分配错误return AVERROR(ENOMEM);}ret = avcodec_receive_frame(codec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {// 没有更多帧可以接收,退出循环av_frame_free(&frame);break;} else if (ret < 0) {// 处理接收帧错误av_frame_free(&frame);return ret;}}

3、正常情况下向解码器发送一包数据会收到一个解码后的帧,因此增加一个检测机制,当多次发送数据包但没有收到解码帧后则重置解码器。

void reset_decoder(AVCodecContext* codecContext) {// 刷新解码器缓冲区avcodec_flush_buffers(codecContext);// 关闭解码器avcodec_close(codecContext);// 重新打开解码器if (avcodec_open2(codecContext, codecContext->codec, nullptr) < 0) {std::cerr << "Could not re-open codec" << std::endl;}
}

http://www.lryc.cn/news/394877.html

相关文章:

  • 一个parquet-go例子
  • 扩散模型笔记
  • 上海-LM科技(面经)
  • 用 Echarts 画折线图
  • C++的map / multimap容器
  • 双向链表 -- 详细理解和实现
  • WebGIS面试题
  • 代码随想录算法训练营:21/60
  • 数据结构——二叉树之c语言实现堆与堆排序
  • #数据结构 链表
  • 单片机软件架构连载(4)-结构体
  • 工厂方法模式在金融业务中的应用及其框架实现
  • python库(6):Pygments库
  • 金斗云 HKMP智慧商业软件 任意用户创建漏洞复现
  • 前端JS特效第24集:jquery css3实现瀑布流照片墙特效
  • 区块链论文速读A会-ISSTA 2023(2/2)如何检测DeFi协议中的价格操纵漏洞
  • 权力之望怎么下载客户端 权力之望一键下载
  • Oracle PL/SQL 循环批量执行存储过程
  • kafka 生产者
  • Powershell 获取电脑保存的所有wifi密码
  • golang结合neo4j实现权限功能设计
  • java 参数传递(尤其注意参数是对象的情况)
  • 拼音字符串相似度
  • 如何创建一个基本的Mojolicious Web应用:探索Perl的现代Web框架
  • FPGA/数字IC复习八股
  • Android 简单快速实现 下弧形刻度尺(滑动事件)
  • 【Go】常见的变量与常量
  • Qt使用sqlite数据库及项目实战
  • 开源模型应用落地-FastAPI-助力模型交互-进阶篇(一)
  • 精准选择广告工具,提升推广效果