下面代码调用FFMPEG库,读取摄像头的一帧数据,转换为RGB888,加载到QImage,再显示到标签控件上。
开发环境:
操作系统: ubuntu18.04 64位
QT版本: QT5.12
代码包已经上传到CSDN,需要的可以去下载。
https://download.csdn.net/download/xiaolong1126626497/12233526
mainwindow.cpp文件代码:
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); FFMPEG_Init_Config(); } MainWindow::~MainWindow() { delete ui; } int MainWindow::FFMPEG_Init_Config() { AVInputFormat *ifmt; AVFormatContext *pFormatCtx; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVDictionary *options=nullptr; AVPacket *packet; AVFrame *pFrame,*pFrameYUV; int videoindex; int i,ret,got_picture; /*1. FFMPEG初始化*/ av_register_all(); avcodec_register_all(); avdevice_register_all(); //注册多媒体设备交互的类库 /*2. 查找用于输入的设备*/ ifmt=av_find_input_format("video4linux2"); pFormatCtx=avformat_alloc_context(); av_dict_set(&options,"video_size","640x480",0); //设置摄像头输出的分辨率 //av_dict_set(&options,"framerate","30",0); //设置摄像头帧率. 每秒为单位,这里设置每秒30帧. //一般帧率不用设置,默认为最高,帧率和输出的图像尺寸有关系 if(avformat_open_input(&pFormatCtx,"/dev/video0",ifmt,&options)!=0) { qDebug("输入设备打开失败: /dev/video0\n"); return -1; } if(avformat_find_stream_info(pFormatCtx,nullptr)<0) { qDebug("查找输入流失败.\n"); return -2; } videoindex=-1; for(i=0;i<pFormatCtx->nb_streams;i++) { if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) { videoindex=i; break; } } if(videoindex==-1) { qDebug("视频流查找失败.\n"); return -3; } pCodecCtx=pFormatCtx->streams[videoindex]->codec; qDebug("摄像头尺寸(WxH): %d x %d \n",pCodecCtx->width, pCodecCtx->height); pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==nullptr) { qDebug("找不到编解码器。\n"); return -4; } if(avcodec_open2(pCodecCtx, pCodec,nullptr)<0) { qDebug("无法打开编解码器。\n"); return -5; } packet=(AVPacket *)av_malloc(sizeof(AVPacket)); pFrame=av_frame_alloc(); pFrameYUV=av_frame_alloc(); unsigned char *out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,16)); // avpicture_get_size av_image_fill_arrays(pFrameYUV->data,pFrameYUV->linesize,out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,16); struct SwsContext *img_convert_ctx; img_convert_ctx=sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //读取一帧数据 if(av_read_frame(pFormatCtx, packet)>=0) { //输出图像的大小 qDebug("数据大小=%d\n",packet->size); //判断是否是视频流 if(packet->stream_index==videoindex) { //解码从摄像头获取的数据,pframe结构 ret=avcodec_decode_video2(pCodecCtx, pFrame,&got_picture,packet); if(ret<0) { qDebug("解码Error.\n"); return -6; } if(got_picture) { size_t y_size=pCodecCtx->width*pCodecCtx->height; sws_scale(img_convert_ctx,(const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); //根据前面配置的缩放参数,进行图像格式转换以及缩放等操作 unsigned char *p=new unsigned char[y_size]; //申请空间 unsigned char *rgb24_p=new unsigned char[pCodecCtx->width*pCodecCtx->height*3]; //将YUV数据拷贝到缓冲区 memcpy(p,pFrameYUV->data[0],y_size); memcpy(p+y_size,pFrameYUV->data[1],y_size/4); memcpy(p+y_size+y_size/4,pFrameYUV->data[2],y_size/4); //将YUV数据转为RGB格式 YUV420P_to_RGB24(p,rgb24_p,pCodecCtx->width,pCodecCtx->height); //加载到QIMAGE显示到QT控件 QImage image(rgb24_p,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB888); QPixmap my_pixmap; my_pixmap.convertFromImage(image); ui->label_DisplayImage->setPixmap(my_pixmap); delete[] p; //释放空间 delete[] rgb24_p; //释放空间 } } } sws_freeContext(img_convert_ctx); av_free(out_buffer); av_free(pFrameYUV); avcodec_close(pCodecCtx); //关闭编码器 avformat_close_input(&pFormatCtx); //关闭输入设备 } /** * YUV420P转RGB24 * @param data * @param rgb * @param width * @param height */ void MainWindow::YUV420P_to_RGB24(unsigned char *data, unsigned char *rgb, int width, int height) { int index = 0; unsigned char *ybase = data; unsigned char *ubase = &data[width * height]; unsigned char *vbase = &data[width * height * 5 / 4]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { //YYYYYYYYUUVV u_char Y = ybase[x + y * width]; u_char U = ubase[y / 2 * width / 2 + (x / 2)]; u_char V = vbase[y / 2 * width / 2 + (x / 2)]; rgb[index++] = Y + 1.402 * (V - 128); //R rgb[index++] = Y - 0.34413 * (U - 128) - 0.71414 * (V - 128); //G rgb[index++] = Y + 1.772 * (U - 128); //B } } } //FFMPEG推流线程执行起始点 void Thread_ffmpgVideo::run() { int ret,got_picture; while(1) { sleep(1); } }
mainwindos.h代码:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QImage> #include <QThread> extern "C"{ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <math.h> #include <libavutil/avassert.h> #include <libavutil/channel_layout.h> #include <libavutil/opt.h> #include <libavutil/mathematics.h> #include <libavutil/timestamp.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> #include <libswresample/swresample.h> #include "libavdevice/avdevice.h" #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/imgutils.h" /* FFmpeg多媒体设备交互的类库:Libavdevice。 使用这个库可以读取电脑(或者其他设备上)的多媒体设备的数据或者输出数据到指定的多媒体设备上。 */ } class MainWindow; //线程类的子类化 class Thread_ffmpgVideo : public QThread { public: MainWindow *mianwindow; //指向widget的指针 protected: void run(); //线程执行的函数 }; QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); int FFMPEG_Init_Config(); void YUV420P_to_RGB24(unsigned char *data, unsigned char *rgb, int width, int height); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
main.cpp:
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
pro 工程文件
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 # The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ mainwindow.cpp HEADERS += \ mainwindow.h FORMS += \ mainwindow.ui # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target #指定库的路径 unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lavcodec unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lavfilter unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lavutil unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lavdevice unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lavformat unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lpostproc unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lswscale unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lswresample unix:LIBS += -L$$PWD/ffmpeg_x264_lib/lib -lx264 #制定头文件的路径 INCLUDEPATH+=$$PWD/ffmpeg_x264_lib/include