FFmpeg一般采用SDL进行显示,如果不追求复杂的界面、交互和多线程功能,当然也可以使用OpenCV的imshow()方法进行显示了,而且实现起来比SDL更简单。方法也很简单,只需要把视频帧的BGR格式的数据(如果是RGB格式,需要转换)转存到OpenCV的Mat矩阵里。OpenCV的Mat是一个类,由两个数据部分组成: 矩阵头(包含信息有矩阵的大小,用于存储的方法,矩阵存储的地址等信息) 和一个指向存储所有像素值矩阵的指针。OpenCV的data属性是一个uchar类型的指针,它指向Mat数据矩阵的首地址;利用该属性,只需要把Mat的data指向FFmpeg的帧数据里即可,就可以用OpenCV的imshow()显示了。

下面给出两个方法,第一个是网上参考别人,并修改好的方法;第二个是鄙人再次精简,更好理解的方法

方法一:

先定义个out_buffer指针指向AVFrame帧数据,注意存储格式一定要选择与OpenCV格式类似的AV_PIX_FMT_BGR24

 int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);uint8_t *out_buffer = (uint8_t *)av_malloc(size);avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);

经过FFmpeg的sws_scale()后,只需要在OpenCV初试化时,传入out_buffer即可,如下:

 //Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//等效于下面Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);mRGB.data = out_buffer;

方法二:

前面已经说明,OpenCV的Mat是一个类,由两个数据部分组成: 矩阵头(包含信息有矩阵的大小,用于存储的方法,矩阵存储的地址等信息) 和一个指向存储所有像素值矩阵的指针。OpenCV的data属性是一个uchar类型的指针,它指向Mat数据矩阵的首地址;利用该属性,只需要把Mat的data指向FFmpeg的帧数据里即可,就可以用OpenCV的imshow()显示了。

因此,我们只需要在sws_scale后,把Mat地址直接指向AVFrame帧数据的首地址即可,不需要out_buffer,将上面的关键代码改为:

Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);
mRGB.data =(uchar*)pFrameBGR->data[0];//注意不能写为:(uchar*)pFrameBGR->data

下面是完整的代码:

#define __STDC_CONSTANT_MACROS
#include <stdio.h>
// Opencv
#include <opencv/cv.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>extern "C"
{
#include "libavutil/avutil.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"//新版里的图像转换结构需要引入的头文件
#include "libswscale/swscale.h"
};using namespace cv;char* filename = "F:/FFmpeg/testvideo/屌丝男士.mov";;int main()
{AVCodec *pCodec; //解码器指针AVCodecContext* pCodecCtx; //ffmpeg解码类的类成员AVFrame* pAvFrame; //多媒体帧,保存解码后的数据帧AVFormatContext* pFormatCtx; //保存视频流的信息av_register_all(); //注册库中所有可用的文件格式和编码器pFormatCtx = avformat_alloc_context();if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { //检查文件头部printf("Can't find the stream!\n");}if (avformat_find_stream_info(pFormatCtx, NULL)<0) { //查找流信息printf("Can't find the stream information !\n");}int videoindex = -1;for (int i = 0; i < pFormatCtx->nb_streams; ++i) //遍历各个流,找到第一个视频流,并记录该流的编码信息{if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {videoindex = i;break;}}if (videoindex == -1) {printf("Don't find a video stream !\n");return -1;}pCodecCtx = pFormatCtx->streams[videoindex]->codec; //得到一个指向视频流的上下文指针pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //到该格式的解码器if (pCodec == NULL) {printf("Cant't find the decoder !\n"); //寻找解码器return -1;}if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { //打开解码器printf("Can't open the decoder !\n");return -1;}pAvFrame = av_frame_alloc(); //分配帧存储空间AVFrame* pFrameBGR = av_frame_alloc(); //存储解码后转换的RGB数据// 保存BGR,opencv中是按BGR来保存的int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);uint8_t *out_buffer = (uint8_t *)av_malloc(size);avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);AVPacket* packet = (AVPacket*)malloc(sizeof(AVPacket));printf("-----------输出文件信息---------\n");av_dump_format(pFormatCtx, 0, filename, 0);printf("------------------------------");struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,AV_PIX_FMT_BGR24, //设置sws_scale转换格式为BGR24,这样转换后可以直接用OpenCV显示图像了SWS_BICUBIC, NULL, NULL, NULL);int ret;int got_picture;cvNamedWindow("RGB", 1);for (;;){if (av_read_frame(pFormatCtx, packet) >= 0){if (packet->stream_index == videoindex){ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);if (ret < 0){printf("Decode Error.(解码错误)\n");return -1;}if (got_picture){//YUV to RGBsws_scale(img_convert_ctx, (const uint8_t* const*)pAvFrame->data, pAvFrame->linesize, 0, pCodecCtx->height,pFrameBGR->data, pFrameBGR->linesize);//Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//(1)等效于下面//Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);//(2)//mRGB.data = out_buffer;//memcpy(pCvMat.data, out_buffer, size);Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);mRGB.data =(uchar*)pFrameBGR->data[0];//注意不能写为:(uchar*)pFrameBGR->dataimshow("RGB", mRGB);waitKey(40);}}av_free_packet(packet);}else{break;}}av_free(out_buffer);av_free(pFrameBGR);av_free(pAvFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);sws_freeContext(img_convert_ctx);cvDestroyWindow("RGB");system("pause");return 0;
}

方法三:为了方便使用,这里提供一个函数可以实现AVFrame到OpenCV Mat的转换

cv::Mat avFrame2Mat(AVFrame* pAvFrame, AVCodecContext*pCodecCtx) {AVFrame* pFrameBGR = av_frame_alloc(); //存储解码后转换的RGB数据// 保存BGR,opencv中是按BGR来保存的int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);uint8_t *out_buffer = (uint8_t *)av_malloc(size);avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_BGR24, //设置sws_scale转换格式为BGR24,这样转换后可以直接用OpenCV显示图像了SWS_BICUBIC,NULL, NULL, NULL);sws_scale(img_convert_ctx,(const uint8_t* const*)pAvFrame->data,pAvFrame->linesize,0,pCodecCtx->height,pFrameBGR->data,pFrameBGR->linesize);//Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//(1)等效于下面//Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);//(2)//mRGB.data = out_buffer;//memcpy(pCvMat.data, out_buffer, size);Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);mRGB.data = (uchar*)pFrameBGR->data[0];//注意不能写为:(uchar*)pFrameBGR->data//av_free(out_buffer);av_free(pFrameBGR);//av_free(pAvFrame);sws_freeContext(img_convert_ctx);return mRGB;
}

完整的程序如下:

#define __STDC_CONSTANT_MACROS
#include <stdio.h>
// Opencv
#include <opencv/cv.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>extern "C"
{
#include "libavutil/avutil.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
};using namespace cv;
cv::Mat avFrame2Mat(AVFrame* pAvFrame, AVCodecContext*pCodecCtx);char* filename = "F:/FFmpeg/testvideo/屌丝男士.mov";;int main()
{AVCodec *pCodec; //解码器指针AVCodecContext* pCodecCtx; //ffmpeg解码类的类成员AVFrame* pAvFrame; //多媒体帧,保存解码后的数据帧AVFormatContext* pFormatCtx; //保存视频流的信息av_register_all(); //注册库中所有可用的文件格式和编码器pFormatCtx = avformat_alloc_context();if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { //检查文件头部printf("Can't find the stream!\n");}if (avformat_find_stream_info(pFormatCtx, NULL)<0) { //查找流信息printf("Can't find the stream information !\n");}int videoindex = -1;for (int i = 0; i < pFormatCtx->nb_streams; ++i) //遍历各个流,找到第一个视频流,并记录该流的编码信息{if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {videoindex = i;break;}}if (videoindex == -1) {printf("Don't find a video stream !\n");return -1;}pCodecCtx = pFormatCtx->streams[videoindex]->codec; //得到一个指向视频流的上下文指针pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //到该格式的解码器if (pCodec == NULL) {printf("Cant't find the decoder !\n"); //寻找解码器return -1;}if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { //打开解码器printf("Can't open the decoder !\n");return -1;}pAvFrame = av_frame_alloc(); //分配帧存储空间// 保存BGR,opencv中是按BGR来保存的AVPacket* packet = (AVPacket*)malloc(sizeof(AVPacket));printf("-----------输出文件信息---------\n");av_dump_format(pFormatCtx, 0, filename, 0);printf("------------------------------");int ret;int got_picture;cvNamedWindow("RGB", 1);for (;;){if (av_read_frame(pFormatCtx, packet) >= 0){if (packet->stream_index == videoindex){ret = avcodec_decode_video2(pCodecCtx, pAvFrame, &got_picture, packet);if (ret < 0){printf("Decode Error.(解码错误)\n");return -1;}if (got_picture){cv::Mat des = avFrame2Mat(pAvFrame, pCodecCtx);imshow("RGB", des);waitKey(40);}}av_free_packet(packet);}else{break;}}//av_free(out_buffer);//av_free(pFrameBGR);av_free(pAvFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);//sws_freeContext(img_convert_ctx);cvDestroyWindow("RGB");system("pause");return 0;
}cv::Mat avFrame2Mat(AVFrame* pAvFrame, AVCodecContext*pCodecCtx) {AVFrame* pFrameBGR = av_frame_alloc(); //存储解码后转换的RGB数据// 保存BGR,opencv中是按BGR来保存的int size = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);uint8_t *out_buffer = (uint8_t *)av_malloc(size);avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,AV_PIX_FMT_BGR24, //设置sws_scale转换格式为BGR24,这样转换后可以直接用OpenCV显示图像了SWS_BICUBIC,NULL, NULL, NULL);sws_scale(img_convert_ctx,(const uint8_t* const*)pAvFrame->data,pAvFrame->linesize,0,pCodecCtx->height,pFrameBGR->data,pFrameBGR->linesize);//Mat mRGB(pCodecCtx->height, pCodecCtx->width, CV_8UC3, out_buffer);//(1)等效于下面//Mat mRGB(Size(pCodecCtx->width,pCodecCtx->height), CV_8UC3);//(2)//mRGB.data = out_buffer;//memcpy(pCvMat.data, out_buffer, size);Mat mRGB(Size(pCodecCtx->width, pCodecCtx->height), CV_8UC3);mRGB.data = (uchar*)pFrameBGR->data[0];//注意不能写为:(uchar*)pFrameBGR->data//av_free(out_buffer);av_free(pFrameBGR);//av_free(pAvFrame);sws_freeContext(img_convert_ctx);return mRGB;
}

参考资料:

【1】《利用ffmpeg和opencv进行视频的解码播放》https://www.jianshu.com/p/6ef3c18d61b0

【2】《ffmpeg中avframe的YUV格式数据到OpenCV中Mat的BGR格式转换》http://www.cnblogs.com/riddick/p/7719190.html

FFmpeg转OpenCV Mat显示相关推荐

  1. OpenCV Mat 简介

    OpenCV Mat 简介 Part I Mat是OpenCV最基本的数据结构,Mat即矩阵(Matrix)的缩写,Mat数据结构主要包含2部分:Header和Pointer.Header中主要包含矩 ...

  2. Mat 显示到MFC中Pictrue Control的问题

    时代在进步,近期忙于工作,好久没能坐在办公室写点关于技术的东西了,想想感觉毕业后工作的几年丢掉了自己的老本行,没有在程序的海洋里徜徉,而是为了应付客户和领导开始不断背离初心.终于现在又有时间写点东西了 ...

  3. OpenCV Mat类详解和用法(官网原文)

    参考文章:OpenCV Mat类详解和用法 我马克一下,日后更 官网原文链接:https://docs.opencv.org/3.2.0/d6/d6d/tutorial_mat_the_basic_i ...

  4. Dlib学习笔记:解决dlib array2d转 OpenCV Mat时颜色失真

    Dlib学习笔记:解决dlib array2d转 OpenCV Mat时颜色失真 [尊重原创,转载请注明出处] http://blog.csdn.net/guyuealian/article/deta ...

  5. qt 关闭窗口的槽函数_勇哥的VC++应用框架学习之QT(1) 信号槽、按钮控件、opencv读取显示图片...

    前言勇哥对于C语言,C++早些年有一些接触,这个系列贴子就记载一下C++应用框架的学习经验. 在写程序时,UI.基础类库.应用程序框架对于vc来讲,只能依靠MFC和QT了. 勇哥对MFC有很强的抵触, ...

  6. OpenCV Mat主要用法(1)

    Mat 为OpenCV中的核心数据结构,主要负责图像数据的保存,Mat创建方法有很多种 Mat构造函数 可以使用Mat构造函数,创建Mat,Mat构造函数有多种形式的参数,来满足要求 Mat 主要构造 ...

  7. OpenCV Mat与uchar*指针相互转换赋值

    将uchar*数组内容赋值到OpenCV Mat中时,可以利用如下操作完成转换: uchar* data = [...]; Mat src(rect_height, rect_width, CV_8U ...

  8. OpenCV Mat类详解和用法

    OpenCV Mat类详解和用法 我们有多种方法可以获得从现实世界的数字图像:数码相机.扫描仪.计算机体层摄影或磁共振成像就是其中的几种.在每种情况下我们(人类)看到了什么是图像.但是,转换图像到我们 ...

  9. Qt QImage与OpenCV Mat转换

    本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/51029382 应一个朋友的要求,整理总 ...

最新文章

  1. 图像形态学(opencv),运行后通过在屏幕上输入对应指令完成相对应的形态学方法。
  2. jenkins邮件通知功能
  3. 我的 Serverless 实战 — 云函数与触发器的创建与使用 ( 开通腾讯云 “ 云开发 “ 服务 | 创建云函数 | 创建触发器 | 测试触发器 )
  4. 【坑爹微信】总有一款接口能坑你到吐血 --- 微信开发经验录
  5. 表格中复制后出现空格_软件应用在Excel表格中怎样批量删除空格?
  6. Python精通-Python局部变量与全局变量的区别
  7. linux 列表看多个文件数量,查看linux默认能最多开启多少个文件数量
  8. 【Flink】No key set. This method should not be called outside of a keyed context.
  9. 金典 SQL笔记(2)
  10. ui分离的进程 如何查找窗口句柄_如何使用 Linux screen
  11. 测试开发岗面试,需要准备的100道题型
  12. 如何画出企业系统架构图
  13. 俄罗斯黑客太疯狂,破解赌场算法,全球捞钱……
  14. Linux:DNS域名解析服务
  15. 基于新浪微博API生成短链接的15款在线工具
  16. 计算机休眠后无法联网,电脑休眠后回来就不能上网了
  17. 25 个你可能不知道的 Linux 真相
  18. unity世界坐标与相对坐标转换
  19. Yuan先生博客-Django进阶
  20. Oracle中预警表,Oracle DB 查看预警日志

热门文章

  1. 【FPGA】相关介绍
  2. 【BZOJ】【1038】【ZJOI2008】瞭望塔
  3. You can't specify target table 'myusertable' for ......in FROM clause
  4. Installshield获取安装包版本的系统变量是IFX_PRODUCT_VERSION
  5. 得到Raster的一些基本信息(高程值,列数,列数)
  6. TEAM WORK 認清自己的角色
  7. PHP 算法之 -- 计算器设计
  8. 各大网站CSS代码初始化集合
  9. [Splay][线段树] jzoj P5662 尺树寸泓
  10. Oracle数据库之事务