基于TCP网络包调用av_parser_parse2()构建AVpacket解码AVframe遇问题
问题解决思路与步骤
1. av_parser_parse2() 的 pts/dts 参数处理
av_parser_parse2() 不需要手动传入 pts/dts,这个函数会从码流中自动解析并填充这些时间戳到AVPacket。你只需要初始化空AVPacket(调用av_packet_alloc()),然后把TCP接收的字节流喂给解析器即可。正确调用流程:
- 初始化解析器:
av_parser_init(codec_id) - 循环接收TCP数据,调用
av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size, buf, buf_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0) - 最后三个参数(pts、dts、pos)直接传
AV_NOPTS_VALUE,解析器会自行处理时间戳。
2. avcodec_send_packet() 报错排查
常见问题及修复方式:
- 序列头缺失:像H.264这类编码格式,必须先传递SPS/PPS等序列头到接收端CodecContext。发送端要先把
avcodec_parameters_from_context()导出的extradata发给接收端,接收端将其复制到自己的CodecContext,否则解码会直接失败。 - AVPacket未重置:每次调用解析器前,必须用
av_packet_unref()重置AVPacket,避免重复使用导致数据混乱。 - TCP粘包/拆包:发送端要给每个AVPacket加长度前缀(比如4字节uint32_t表示包大小),接收端先读长度,再读取对应字节数的完整数据包,再喂给解析器。不完整的码流会直接导致
avcodec_send_packet()报错。
3. AVPacket 是否自带上下文信息?
AVPacket 不携带CodecContext的完整配置,它只包含编码后的码流、时间戳、流索引等元数据。CodecContext的分辨率、编码格式、extradata等配置必须由发送端单独传递,不能依赖AVPacket。硬编码属性的方式可行,但更可靠的做法是:
- 发送端启动时,把
AVCodecParameters(通过avcodec_parameters_from_context()从本地CodecContext导出)序列化成字节流发送。 - 接收端拿到后,用
avcodec_parameters_to_context()导入自己的CodecContext,再调用avcodec_open2()打开解码器。
接收端关键代码片段
// 初始化解析器与解码器 AVCodecParserContext* parser = av_parser_init(AV_CODEC_ID_H264); AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); AVCodecContext* codec_ctx = avcodec_alloc_context3(codec); // 导入从发送端获取的配置(extradata、width、height等) avcodec_open2(codec_ctx, codec, nullptr); AVPacket* pkt = av_packet_alloc(); uint8_t recv_buf[4096]; int recv_len; while ((recv_len = recv(tcp_fd, recv_buf, sizeof(recv_buf), 0)) > 0) { uint8_t* data = recv_buf; int size = recv_len; while (size > 0) { int parsed_size = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size, data, size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); data += parsed_size; size -= parsed_size; if (pkt->size > 0) { int ret = avcodec_send_packet(codec_ctx, pkt); if (ret == AVERROR(EAGAIN)) { // 先取出已解码帧 AVFrame* frame = av_frame_alloc(); if (avcodec_receive_frame(codec_ctx, frame) == 0) { cv::Mat mat(frame->height, frame->width, CV_8UC3, frame->data[0], frame->linesize[0]); // 处理解码后的帧 av_frame_unref(frame); } av_frame_free(&frame); continue; } else if (ret < 0) { char err_buf[1024]; av_strerror(ret, err_buf, sizeof(err_buf)); fprintf(stderr, "解码失败: %s\n", err_buf); break; } // 批量取出解码帧 AVFrame* frame = av_frame_alloc(); while (avcodec_receive_frame(codec_ctx, frame) == 0) { cv::Mat mat(frame->height, frame->width, CV_8UC3, frame->data[0], frame->linesize[0]); // 帧处理逻辑 av_frame_unref(frame); } av_frame_free(&frame); } av_packet_unref(pkt); } } // 资源释放 av_packet_free(&pkt); avcodec_free_context(&codec_ctx); av_parser_close(parser);
内容的提问来源于stack exchange,提问作者Batuhan Zorlu
相关产品推荐
相关产品推荐

