下面代码调用FFMPEG库,读取摄像头的一帧数据,转换为RGB888,加载到QImage,再显示到标签控件上。
开发环境:
操作系统: ubuntu18.04 64位
QT版本: QT5.12
代码包已经上传到CSDN,需要的可以去下载。
https://download.csdn.net/download/xiaolong1126626497/12233526
mainwindow.cpp文件代码:
代码语言:javascript复制#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/video0n");
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("数据大小=%dn",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代码:
代码语言:javascript复制#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:
代码语言:javascript复制#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
.pro 工程文件
代码语言:javascript复制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