av_interleaved_write_frame(fmt_ctx, &enc_pkt);返回-22

在使用ffmpeg进行音视频编码时遇到av_interleaved_write_frame返回-22的问题,主要原因是enc_pkt.stream_index设置错误。解决办法是在编码音频帧时确保enc_pkt.stream_index等于音频流的索引。通过检查和修正这个值,成功解决了编码过程中的错误。

 av_interleaved_write_frame(fmt_ctx, &enc_pkt);返回-22,

原因之一:

各处寻找答案,都说是:pts和dts的问题。

我跟踪了pts和dts, 发现这两个值第一次是随机一个相等数,比如pts = dts = 486400. 然后第二次就是pts=dts = 487424, 可以看出,这是递增了1024.

所以,并不是pts和dts出了问题。毕竟官网的 音频编码和视频编码 的例子,最后也没有处理pts和dts。

原因之二:

enc_pkt.stream_index的值,设置错了。

是因为enc_pkt.stream_index 默认的是0。
而我合成音视频后,音频的索引是1,
所以,需要给enc_pkt.stream_index = stream_index;  需要重新赋值一下。

参考链接:

https://blog.csdn.net/shizheng163/article/details/80476489

正确代码如下:

// 做录制音视频项目
// 此函数是:保存最后几帧音频数据
static int flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index)
{
	int ret = 0;
	int got_frame = 0;
	AVPacket enc_pkt;
	av_init_packet(&enc_pkt);
	if(!(pFormatCtx_Out->streams[stream_index]->codec->codec->
#include "VideoCodec.h" #include <stdio.h> #include <iostream> #include <qeventloop.h> #include <QTime> #include <QCoreApplication> #include <QCryptographicHash> using namespace std; extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavutil/pixfmt.h" #include "libswscale/swscale.h" #include "libavutil/imgutils.h" } //-------zty 封装为mp4------- AVFormatContext* fmt_ctx_visual = nullptr; AVCodecContext* enc_ctx_visual = nullptr; AVStream* stream_visual = nullptr; int64_t pts_counter_visual = 0; AVFormatContext* fmt_ctx_infrared = nullptr; AVCodecContext* enc_ctx_infrared = nullptr; AVStream* stream_infrared = nullptr; int64_t pts_counter_infrared = 0; AVFormatContext* fmt_ctx_depth = nullptr; AVCodecContext* enc_ctx_depth = nullptr; AVStream* stream_depth = nullptr; int64_t pts_counter_depth = 0; bool SucOfVisualStore = true; bool SucOfInfraredStore = true; bool SucOfDepthStore = true; void init_mp4_writer(const char* filename1, const char* filename2,const char* filename3,int index,int width, int height) { switch (index) { case 1: { if(SucOfVisualStore) { // 创建输出上下文 avformat_alloc_output_context2(&fmt_ctx_visual, nullptr, "mp4", filename1); // 查找编码器(H.264) const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); enc_ctx_visual = avcodec_alloc_context3(codec); // 配置编码器参数 enc_ctx_visual->bit_rate = 400000; enc_ctx_visual->width = width; enc_ctx_visual->height = height; enc_ctx_visual->time_base = (AVRational){1, 30}; // 帧率25fps enc_ctx_visual->framerate = (AVRational){30, 1}; enc_ctx_visual->gop_size = 10; enc_ctx_visual->max_b_frames = 1; enc_ctx_visual->pix_fmt = AV_PIX_FMT_YUV420P; // 打开编码器 avcodec_open2(enc_ctx_visual, codec, nullptr); // 创建视频流 stream_visual = avformat_new_stream(fmt_ctx_visual, nullptr); avcodec_parameters_from_context(stream_visual->codecpar, enc_ctx_visual); // 打开输出文件 avio_open(&fmt_ctx_visual->pb, filename1, AVIO_FLAG_WRITE); avformat_write_header(fmt_ctx_visual, nullptr); SucOfVisualStore = false; } } break; case 2: { if(SucOfInfraredStore) { // 创建输出上下文 avformat_alloc_output_context2(&fmt_ctx_visual, nullptr, "mp4", filename2); // 查找编码器(H.264) const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); enc_ctx_infrared = avcodec_alloc_context3(codec); // 配置编码器参数 enc_ctx_infrared->bit_rate = 400000; enc_ctx_infrared->width = width; enc_ctx_infrared->height = height; enc_ctx_infrared->time_base = (AVRational){1, 30}; // 帧率25fps enc_ctx_infrared->framerate = (AVRational){30, 1}; enc_ctx_infrared->gop_size = 10; enc_ctx_infrared->max_b_frames = 1; enc_ctx_infrared->pix_fmt = AV_PIX_FMT_YUV420P; // 打开编码器 avcodec_open2(enc_ctx_infrared, codec, nullptr); // 创建视频流 stream_infrared = avformat_new_stream(fmt_ctx_infrared, nullptr); avcodec_parameters_from_context(stream_infrared->codecpar, enc_ctx_infrared); // 打开输出文件 avio_open(&fmt_ctx_infrared->pb, filename2, AVIO_FLAG_WRITE); avformat_write_header(fmt_ctx_infrared, nullptr); SucOfInfraredStore = false; } } break; case 3: { if(SucOfDepthStore) { // 创建输出上下文 avformat_alloc_output_context2(&fmt_ctx_depth, nullptr, "mp4", filename3); // 查找编码器(H.264) const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); enc_ctx_depth = avcodec_alloc_context3(codec); // 配置编码器参数 enc_ctx_depth->bit_rate = 400000; enc_ctx_depth->width = width; enc_ctx_depth->height = height; enc_ctx_depth->time_base = (AVRational){1, 30}; // 帧率25fps enc_ctx_depth->framerate = (AVRational){30, 1}; enc_ctx_depth->gop_size = 10; enc_ctx_depth->max_b_frames = 1; enc_ctx_depth->pix_fmt = AV_PIX_FMT_YUV420P; // 打开编码器 avcodec_open2(enc_ctx_depth, codec, nullptr); // 创建视频流 stream_depth = avformat_new_stream(fmt_ctx_depth, nullptr); avcodec_parameters_from_context(stream_depth->codecpar, enc_ctx_depth); // 打开输出文件 avio_open(&fmt_ctx_depth->pb, filename3, AVIO_FLAG_WRITE); avformat_write_header(fmt_ctx_depth, nullptr); SucOfDepthStore = false; } } break; default: break; } } void saveFrameToMP4(AVFrame* frame,int index) { switch (index) { case 1: { // 设置帧时间戳 frame->pts = pts_counter_visual++; // 发送帧到编码器 avcodec_send_frame(enc_ctx_visual, frame); AVPacket* pkt = av_packet_alloc(); while (avcodec_receive_packet(enc_ctx_visual, pkt) == 0) { // 设置包时间戳 av_packet_rescale_ts(pkt, enc_ctx_visual->time_base, stream_visual->time_base); pkt->stream_index = stream_visual->index; // 写入文件 av_interleaved_write_frame(fmt_ctx_visual, pkt); av_packet_unref(pkt); } av_packet_free(&pkt); } break; case 2: { // 设置帧时间戳 frame->pts = pts_counter_infrared++; // 发送帧到编码器 avcodec_send_frame(enc_ctx_infrared, frame); AVPacket* pkt = av_packet_alloc(); while (avcodec_receive_packet(enc_ctx_infrared, pkt) == 0) { // 设置包时间戳 av_packet_rescale_ts(pkt, enc_ctx_infrared->time_base, stream_infrared->time_base); pkt->stream_index = stream_infrared->index; // 写入文件 av_interleaved_write_frame(fmt_ctx_infrared, pkt); av_packet_unref(pkt); } av_packet_free(&pkt); } break; case 3: { // 设置帧时间戳 frame->pts = pts_counter_depth++; // 发送帧到编码器 avcodec_send_frame(enc_ctx_depth, frame); AVPacket* pkt = av_packet_alloc(); while (avcodec_receive_packet(enc_ctx_depth, pkt) == 0) { // 设置包时间戳 av_packet_rescale_ts(pkt, enc_ctx_depth->time_base, stream_depth->time_base); pkt->stream_index = stream_depth->index; // 写入文件 av_interleaved_write_frame(fmt_ctx_depth, pkt); av_packet_unref(pkt); } av_packet_free(&pkt); } break; default: break; } } void finalize_mp4_writer(int index) { switch (index) { case 1: { if (enc_ctx_visual || enc_ctx_visual->codec) { // 刷新编码器缓冲区 avcodec_send_frame(enc_ctx_visual, nullptr); // 发送空帧刷新 AVPacket* pkt = av_packet_alloc(); while (avcodec_receive_packet(enc_ctx_visual, pkt) == 0) { av_packet_rescale_ts(pkt, enc_ctx_visual->time_base, stream_visual->time_base); pkt->stream_index = stream_visual->index; av_interleaved_write_frame(fmt_ctx_visual, pkt); av_packet_unref(pkt); } av_packet_free(&pkt); // 写入文件尾 av_write_trailer(fmt_ctx_visual); // 释放资源 avcodec_free_context(&enc_ctx_visual); avio_closep(&fmt_ctx_visual->pb); avformat_free_context(fmt_ctx_visual); } } break; case 2: { // 刷新编码器缓冲区 avcodec_send_frame(enc_ctx_infrared, nullptr); // 发送空帧刷新 AVPacket* pkt = av_packet_alloc(); while (avcodec_receive_packet(enc_ctx_infrared, pkt) == 0) { av_packet_rescale_ts(pkt, enc_ctx_infrared->time_base, stream_infrared->time_base); pkt->stream_index = stream_infrared->index; av_interleaved_write_frame(fmt_ctx_infrared, pkt); av_packet_unref(pkt); } av_packet_free(&pkt); // 写入文件尾 av_write_trailer(fmt_ctx_infrared); // 释放资源 avcodec_free_context(&enc_ctx_infrared); avio_closep(&fmt_ctx_infrared->pb); avformat_free_context(fmt_ctx_infrared); } break; case 3: { // 刷新编码器缓冲区 avcodec_send_frame(enc_ctx_depth, nullptr); // 发送空帧刷新 AVPacket* pkt = av_packet_alloc(); while (avcodec_receive_packet(enc_ctx_depth, pkt) == 0) { av_packet_rescale_ts(pkt, enc_ctx_depth->time_base, stream_depth->time_base); pkt->stream_index = stream_depth->index; av_interleaved_write_frame(fmt_ctx_depth, pkt); av_packet_unref(pkt); } av_packet_free(&pkt); // 写入文件尾 av_write_trailer(fmt_ctx_depth); // 释放资源 avcodec_free_context(&enc_ctx_depth); avio_closep(&fmt_ctx_depth->pb); avformat_free_context(fmt_ctx_depth); } break; default: break; } } void VideoCodec::run() { int videoIndex = -1; // 初始化环境 if (!Init(videoIndex)) { return; } // 分配包空间 AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket)); // 逐帧解析数据 while (av_read_frame(m_pFormatCtx, packet) == 0) { //如果是视频数据 if (packet->stream_index == videoIndex) { //解码一帧视频数据 int got_picture = 0; int ret = avcodec_decode_video2(m_pCodecCtx, m_pFrame, &got_picture, packet); if (ret < 0) { printf("Decode Error.\n"); return; } if (got_picture) { sws_scale(m_pImgConvertCtx, (const unsigned char* const*)m_pFrame->data, m_pFrame->linesize, 0, m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize); QImage img((uchar*)m_pFrameRGB->data[0], m_pCodecCtx->width, m_pCodecCtx->height, QImage::Format_RGB32); // QImage image = img.copy(); //img=img.scaled(275,230);//zty 把这句话注释了,放在了主线程 emit sig_GetOneFrame(img,VideoStyle);//zty 加了一个emit if(storeornot) { QByteArray latin1Data_visual = path_visual.toLatin1(); QByteArray latin1Data_infrared = path_infrared.toLatin1(); QByteArray latin1Data_depth = path_depth.toLatin1(); init_mp4_writer(latin1Data_visual.constData(),latin1Data_infrared.constData(), latin1Data_depth.constData(),VideoStyle,1920, 1080); AVFrame* frame = m_pFrame; saveFrameToMP4(frame,VideoStyle); } Delay(10); } } // 释放包空间 av_free_packet(packet); } // 清理数据 CleanUp(); } bool VideoCodec::Init(int& videoIndex) { unsigned char* out_buffer; avformat_network_init(); AVDictionary* avdic = NULL; QByteArray ba = UdpORtcp.toLatin1(); char* tcpudp = ba.data(); av_dict_set(&avdic, "rtsp_transport", tcpudp, 0); // av_dict_set(&avdic, "stimeout", "3000000", 0);原生的ffmpeg参数在打开RTSP流时,若连接不上,会出现卡死在打开函数的情况,在有些情况下这是很不好的,可以通过设置超时来改变卡死的情况 av_dict_set(&avdic, "max_delay", "10", 0);//rtsp情况下的最大延时 // 缓冲区大小16K av_dict_set(&avdic, "buffer_size", "16777216", 0); //原生的ffmpeg参数在对1920x1080的RTSP流进行播放时,花屏现象很严重,根据网上查的资料,可以通过增大“buffer_size”参数来提高画质,减少花屏现象,当你rtsp流码率很大的时候,设置“buffer_size”很有必要,否则内存泄漏,并出现花屏。 av_dict_set(&avdic, "reorder_queue_size", "5000", 0); // rtsp流网址 //std::string strUrl = "rtsp://172.16.0.240:8554/live/stream";//Url.toStdString();//lcj std::string strUrl = Url.toStdString();//lcj const char* urlpath = strUrl.c_str(); std::string strFilepath = m_FilePath.toStdString(); const char* filepath = strFilepath.c_str(); // 初始化编解码库 av_register_all();//创建AVFormatContext对象,与码流相关的结构。 m_pFormatCtx = avformat_alloc_context(); // 初始化pFormatCtx结构打开视频文件,从视频流中解析出部分信息,填充到pFormatCtx中,包含视频的分辨率,时间戳等信息,而且包含了相应的解码器信息 if (/*isurlpath_filepath*/true) { if (avformat_open_input(&m_pFormatCtx, urlpath, NULL, &avdic) != 0) { printf("Couldn't open input stream.\n"); return false; } } else { if (avformat_open_input(&m_pFormatCtx, filepath, NULL, NULL) != 0) { printf("Couldn't open the file.\n"); return false; } } // 获取音视频流数据信息 if (avformat_find_stream_info(m_pFormatCtx, NULL) < 0) { printf("Couldn't find stream information.\n"); return false; } // nb_streams视音频流的个数,这里当查找到视频流时就中断了。 for (int i = 0; i < m_pFormatCtx->nb_streams; i++) { if (m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { videoIndex = i; break; } } // 判断是否找到视频流 if (videoIndex == -1) { printf("Didn't find a video stream.\n"); return false; } // 获取视频流编码结构 m_pCodecCtx = m_pFormatCtx->streams[videoIndex]->codec; // 查找解码器 AVCodec* pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id); if (pCodec == NULL) { printf("Codec not found.\n"); return false; } // 用于初始化pCodecCtx结构 if (avcodec_open2(m_pCodecCtx, pCodec, NULL) < 0) { printf("Could not open codec.\n"); return false; } // 创建帧结构,此函数仅分配基本结构空间,图像数据空间需通过av_malloc分配 m_pFrame = av_frame_alloc(); m_pFrameRGB = av_frame_alloc(); // 创建动态内存,创建存储图像数据的空间 // av_image_get_buffer_size获取一帧图像需要的大小 out_buffer = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB32, m_pCodecCtx->width, m_pCodecCtx->height, 1)); av_image_fill_arrays(m_pFrameRGB->data, m_pFrameRGB->linesize, out_buffer, AV_PIX_FMT_RGB32, m_pCodecCtx->width, m_pCodecCtx->height, 1); // 此函数打印输入或输出的详细信息,非视频文件会报错 // av_dump_format(pFormatCtx, 0, filepath, 0); // 初始化img_convert_ctx结构 m_pImgConvertCtx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt, m_pCodecCtx->width, m_pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL); // 标识当前环境处于运行状态,未被清理过 m_bCleaned = false; return true; } void VideoCodec::CleanUp() { if (m_bCleaned) { return; } // 清理环境 sws_freeContext(m_pImgConvertCtx); av_frame_free(&m_pFrameRGB); av_frame_free(&m_pFrame); avcodec_close(m_pCodecCtx); avformat_close_input(&m_pFormatCtx); // 标识视频已播放完毕或已停止,环境已被清理 m_bCleaned = true; } void VideoCodec::slot_exeisover() { finalize_mp4_writer(VideoStyle); } 在这段代码中,当关闭程序时在析构函数调用slot_exeisover函数,但是avcodec_send_frame(enc_ctx_visual, nullptr); 这句代码报错导致存储的mp4文件无法打开
09-06
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值