ffmpeg与opencv联合的解码图像并显示问题。
该段代码来自:(2条消息) 3、FFMPEG拉流转OpenCV并推流& Opencv 读视频转FFMPEG并推流_sxj731533730的博客-CSDN博客
该作者的其他文章也十分具有工程的借鉴意义,不知道作者本人是何方圣神?
笔者抄袭了该段代码,添加了一些注释并修改了一些不合适的地方。
ffmpeg使用起来不是很顺畅,也许是该库开放了太多api,给予了开发者更多的操作空间,对于希望开发更上层应用的编程人员来讲是件好事!
#include <stdio.h> #include <string> #include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavutil/avutil.h" #include "libavutil/imgutils.h" }; using namespace std; int main(int argc, char *argv[]) { const char *path = "cv2_curve.mp4";//不能识别UTF8和其他汉语,请尽量使用全英文 AVFormatContext *pFormat = NULL;//输出上下文,用于联系数据和输出的中间结构体。 int ret = avformat_open_input(&pFormat, path, NULL, NULL); if (ret < 0) { perror("avformat_open_input"); avformat_free_context(pFormat); return -1; } // av_dict_free(&optional); printf("avformat_open_input successfully\n"); ret = avformat_find_stream_info(pFormat, NULL); if (ret < 0) { perror("avformat_find_stream_info\n"); return -1; } printf("avformat_find_stream_info successfully\n"); int time = pFormat->duration; int mbittime = (time / 100000) / 60; int mminttime = (time / 100000) % 60; printf("video time: %d'm %d's\n", mbittime, mminttime); av_dump_format(pFormat, 0, path, 0); int videoindex = -1; for (int i = 0; i < pFormat->nb_streams; i++) { if (pFormat->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoindex = i; break; } } if (videoindex == -1) { printf("don't find video stream\n"); return -1; } AVCodecParameters *codecParameters = pFormat->streams[videoindex]->codecpar; printf("video width %d\n", codecParameters->width); printf("video height %d\n", codecParameters->height); const AVCodec *pCodec = avcodec_find_decoder((AVCodecID)27);//设置解码器 解码协议及格式 AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec); //为解码器上下文分配内存空间 avcodec_parameters_to_context(pCodecCtx, codecParameters); //解码器相关参数拷贝到上下文 ret = avcodec_open2(pCodecCtx, pCodec, NULL);//用解码器初始化上下文 /*if (ret < 0) {//打开解码器 printf("Could not open codec.\n"); return -1; }*/ AVFrame *pFrame = av_frame_alloc(); pFrame->width = codecParameters->width; pFrame->height = codecParameters->height; pFrame->format = AV_PIX_FMT_YUV420P; ret = av_frame_get_buffer(pFrame, 1); if (ret < 0) { printf("av_frame_get_buffer error\n"); return -1; } AVFrame *pFrameRGB = av_frame_alloc(); pFrameRGB->width = codecParameters->width; pFrameRGB->height = codecParameters->height; pFrameRGB->format = AV_PIX_FMT_RGB24; ret = av_frame_get_buffer(pFrameRGB, 1); if (ret < 0) { printf("av_frame_get_buffer error\n"); return -1; } int picture_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, codecParameters->width, codecParameters->height, 1);//计算这个格式的图片,需要多少字节来存储 uint8_t *out_buff = (uint8_t *)av_malloc(picture_size * sizeof(uint8_t)); av_image_fill_arrays(pFrame->data, pFrame->linesize, out_buff, AV_PIX_FMT_YUV420P, codecParameters->width, codecParameters->height, 1); //这个函数 是缓存转换格式,可以不用 以为上面已经设置了AV_PIX_FMT_YUV420P SwsContext *img_convert_ctx = sws_getContext(codecParameters->width, codecParameters->height, AV_PIX_FMT_YUV420P, codecParameters->width, codecParameters->height, AV_PIX_FMT_RGB24, 4, NULL, NULL, NULL); AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket)); while (av_read_frame(pFormat, packet) >= 0) { if (packet->stream_index == videoindex) { ret = avcodec_send_packet(pCodecCtx, packet);//提供原始数据包给解码器作为输入 if (ret < 0) { printf("avcodec_send_packet error\n"); continue; } av_packet_unref(packet);//刷新一下packet int got_picture = avcodec_receive_frame(pCodecCtx, pFrame);//输出 if (got_picture < 0) { printf("avcodec_receive_frame error\n"); continue; } sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, codecParameters->height, pFrameRGB->data, pFrameRGB->linesize); cv::Mat mRGB(cv::Size(codecParameters->width, codecParameters->height), CV_8UC3); mRGB.data = (unsigned char *)pFrameRGB->data[0]; cv::Mat mBGR; cv::cvtColor(mRGB, mBGR, cv::COLOR_RGB2BGR); cv::imshow("demo", mRGB); cv::waitKey(10); } } av_frame_free(&pFrame); av_frame_free(&pFrameRGB); avformat_free_context(pFormat); return 0; }
笔者认为有以下几点需要注意:
1.AVFormatContext记述了输入文件、数据流信息等等重要的信息。AVFormatContext下的孙成员codecpar是比较重要的,该成员记述了编码协议、图像长宽高等等重要的东西。
2.sws_scale这个操作类似于python中的reshape函数,因为Mat的data数据是逐行的,所以必须要squeeze一下数据。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理