赞
踩
Qt5中通过FFmpeg拉流实现视频播放(简单的在Qt中使用FFmpeg的demo,没有做任何优化)
本地环境:
Qt5.15.2 + MSVC2019_64bit编译器
ffmpeg头文件路径:
ffmpeg库路径:
exe输出文件夹,需要ffmpeg的dll库加进来:
QT += core gui concurrent greaterThan(QT_MAJOR_VERSION, 4): QT += widgets # 输出目录 DESTDIR = $${OUT_PWD}/../output TARGET = FFmpegDemo CONFIG += c++17 # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #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 # ffmpeg头文件 INCLUDEPATH += $$PWD/../3rdparty/ffmpeg/include # ffmpeg库链接 LIBS += -L$$PWD/../3rdparty/ffmpeg/lib -lavformat -lavfilter -lavcodec -lswresample -lswscale -lavutil # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
//当前C++兼容C语言 extern "C" { //avcodec:编解码(最重要的库) #include <libavcodec/avcodec.h> //avformat:封装格式处理 #include <libavformat/avformat.h> //swscale:视频像素数据格式转换 #include <libswscale/swscale.h> //avdevice:各种设备的输入输出 #include <libavdevice/avdevice.h> //avutil:工具库(大部分库都需要这个库的支持) #include <libavutil/avutil.h> #include <libavutil/imgutils.h> #include <libavutil/time.h> }
void MainWindow::transVideoToImage(const QString &url) { //avdevice_register_all(); //硬件设备注册 avformat_network_init(); //网络注册 //创建封装格式上下文 AVFormatContext *formatContext = avformat_alloc_context(); //打开本地文件到封装格式上下文中。 QString filename = url; int avformat_open_ret = avformat_open_input(&formatContext, filename.toLatin1(), NULL, NULL); if(avformat_open_ret < 0) { char *result = new char[64]; av_strerror(avformat_open_ret,result,64); qDebug() << QString(u8"错误信息:%1").arg(result); } qDebug() << u8"打开多媒体文件成功!"; //在封装格式上下文中获取音视频流信息 int avformat_find_ret = avformat_find_stream_info(formatContext, NULL); if(avformat_find_ret < 0) { char *result = new char[64]; av_strerror(avformat_open_ret,result,64); qDebug() << QString(u8"错误信息:%1").arg(result); } qDebug() << u8"获取音视频流信息成功!"; //通过遍历封装格式上下文中所有的流信息,找到视频流并保存视频流索引。 int streamIndex = -1; for(unsigned int i = 0; i < formatContext->nb_streams; i++) { if(formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { streamIndex = i; break; } } if(streamIndex == -1) { qDebug() << u8"找不到相应的流信息!"; } //通过视频流索引,获取解码器上下文。 AVCodecContext *codecContext = avcodec_alloc_context3(NULL); avcodec_parameters_to_context(codecContext , formatContext->streams[streamIndex]->codecpar); //通过解码上下文的属性id寻找合适的解码器。 const AVCodec *codec = avcodec_find_decoder(codecContext->codec_id); if(codec == NULL) { qDebug() << u8"找不到合适的解码器!"; } //通过解码器上下文,打开寻找到的解码器。 int avcodec_open_ret = avcodec_open2(codecContext,codec,NULL); if(avcodec_open_ret < 0) { char *result = new char[64]; av_strerror(avformat_open_ret,result,64); qDebug() << QString(u8"错误信息:%1").arg(result); } qDebug() << u8"打开解码器成功!"; //对接收数据的数据包、数据帧、数据缓冲区容器动态分配内存空间。 //数据包初始化 AVPacket *packet = av_packet_alloc(); //输入视频帧初始化 AVFrame *frame = av_frame_alloc(); //输出RGB帧初始化 AVFrame *frameRGB = av_frame_alloc(); //给缓冲区动态分配内存 uint8_t *pOutbuffer = static_cast<unsigned char *>( av_malloc(static_cast<size_t>(av_image_get_buffer_size(AV_PIX_FMT_RGB32, codecContext->width, codecContext->height, 1)))); //初始化缓冲区 av_image_fill_arrays(frameRGB->data, frameRGB->linesize, pOutbuffer, AV_PIX_FMT_RGB32, codecContext->width, codecContext->height, 1); //通过解码器上下文获取图像转换上下文 SwsContext *sws = sws_getContext(codecContext->width,codecContext->height,codecContext->pix_fmt, codecContext->width,codecContext->height,AV_PIX_FMT_RGB32, SWS_BICUBIC,NULL,NULL,NULL); //从封装格式上下文读取信息到数据包,再从数据包读取到数据帧,最后将数据转为图像并显示。 //计算解码帧数 int frameNum = 0; //读取视频帧 while(av_read_frame(formatContext,packet) >= 0) { if(packet->stream_index == streamIndex) { avcodec_send_packet(codecContext,packet); int decode_video_ret = avcodec_receive_frame(codecContext,frame); if(decode_video_ret >= 0) { //计算帧数 frameNum++; qDebug() << QString(u8"正在解码播放第%1帧数据").arg(frameNum); //视频流数据转换为RGB图像数据 sws_scale(sws, (const unsigned char* const*)frame->data,frame->linesize, 0, codecContext->height,frameRGB->data,frameRGB->linesize); //数据转换为图像 QImage *tmpImg = new QImage((uchar *)pOutbuffer,codecContext->width, codecContext->height,QImage::Format_RGB32); //加载到标签中显示 // imgLabel->setPixmap(QPixmap::fromImage(img)); emit signal_devodeImage(tmpImg); av_usleep(10 * 1000); } } } }
QtConcurrent::run(this, &MainWindow::transVideoToImage, urls.at(1));
connect(this, &MainWindow::signal_devodeImage, this, [=](QImage *img) {
ui->label->setPixmap(QPixmap::fromImage(*img));
});
Mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QImage> #include <QPixmap> #include <QDebug> #include <QtConcurrent> //当前C++兼容C语言 extern "C" { //avcodec:编解码(最重要的库) #include <libavcodec/avcodec.h> //avformat:封装格式处理 #include <libavformat/avformat.h> //swscale:视频像素数据格式转换 #include <libswscale/swscale.h> //avdevice:各种设备的输入输出 #include <libavdevice/avdevice.h> //avutil:工具库(大部分库都需要这个库的支持) #include <libavutil/avutil.h> #include <libavutil/imgutils.h> #include <libavutil/time.h> } QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; void transVideoToImage(const QString &url); signals: void signal_devodeImage(QImage *); }; #endif // MAINWINDOW_H
Mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); QStringList urls; // todo 摄像头url urls << "rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp" << "rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid" << "http://vfx.mtime.cn/Video/2021/11/16/mp4/211116131456748178.mp4"; QtConcurrent::run(this, &MainWindow::transVideoToImage, urls.at(1)); connect(this, &MainWindow::signal_devodeImage, this, [=](QImage *img) { ui->label->setPixmap(QPixmap::fromImage(*img)); }); } MainWindow::~MainWindow() { delete ui; } void MainWindow::transVideoToImage(const QString &url) { //avdevice_register_all(); //硬件设备注册 avformat_network_init(); //网络注册 //创建封装格式上下文 AVFormatContext *formatContext = avformat_alloc_context(); //打开本地文件到封装格式上下文中。 QString filename = url; int avformat_open_ret = avformat_open_input(&formatContext, filename.toLatin1(), NULL, NULL); if(avformat_open_ret < 0) { char *result = new char[64]; av_strerror(avformat_open_ret,result,64); qDebug() << QString(u8"错误信息:%1").arg(result); } qDebug() << u8"打开多媒体文件成功!"; //在封装格式上下文中获取音视频流信息 int avformat_find_ret = avformat_find_stream_info(formatContext, NULL); if(avformat_find_ret < 0) { char *result = new char[64]; av_strerror(avformat_open_ret,result,64); qDebug() << QString(u8"错误信息:%1").arg(result); } qDebug() << u8"获取音视频流信息成功!"; //通过遍历封装格式上下文中所有的流信息,找到视频流并保存视频流索引。 int streamIndex = -1; for(unsigned int i = 0; i < formatContext->nb_streams; i++) { if(formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { streamIndex = i; break; } } if(streamIndex == -1) { qDebug() << u8"找不到相应的流信息!"; } //通过视频流索引,获取解码器上下文。 AVCodecContext *codecContext = avcodec_alloc_context3(NULL); avcodec_parameters_to_context(codecContext , formatContext->streams[streamIndex]->codecpar); //通过解码上下文的属性id寻找合适的解码器。 const AVCodec *codec = avcodec_find_decoder(codecContext->codec_id); if(codec == NULL) { qDebug() << u8"找不到合适的解码器!"; } //通过解码器上下文,打开寻找到的解码器。 int avcodec_open_ret = avcodec_open2(codecContext,codec,NULL); if(avcodec_open_ret < 0) { char *result = new char[64]; av_strerror(avformat_open_ret,result,64); qDebug() << QString(u8"错误信息:%1").arg(result); } qDebug() << u8"打开解码器成功!"; //对接收数据的数据包、数据帧、数据缓冲区容器动态分配内存空间。 //数据包初始化 AVPacket *packet = av_packet_alloc(); //输入视频帧初始化 AVFrame *frame = av_frame_alloc(); //输出RGB帧初始化 AVFrame *frameRGB = av_frame_alloc(); //给缓冲区动态分配内存 uint8_t *pOutbuffer = static_cast<unsigned char *>( av_malloc(static_cast<size_t>(av_image_get_buffer_size(AV_PIX_FMT_RGB32, codecContext->width, codecContext->height, 1)))); //初始化缓冲区 av_image_fill_arrays(frameRGB->data, frameRGB->linesize, pOutbuffer, AV_PIX_FMT_RGB32, codecContext->width, codecContext->height, 1); //通过解码器上下文获取图像转换上下文 SwsContext *sws = sws_getContext(codecContext->width,codecContext->height,codecContext->pix_fmt, codecContext->width,codecContext->height,AV_PIX_FMT_RGB32, SWS_BICUBIC,NULL,NULL,NULL); //从封装格式上下文读取信息到数据包,再从数据包读取到数据帧,最后将数据转为图像并显示。 //计算解码帧数 int frameNum = 0; //读取视频帧 while(av_read_frame(formatContext,packet) >= 0) { if(packet->stream_index == streamIndex) { avcodec_send_packet(codecContext,packet); int decode_video_ret = avcodec_receive_frame(codecContext,frame); if(decode_video_ret >= 0) { //计算帧数 frameNum++; qDebug() << QString(u8"正在解码播放第%1帧数据").arg(frameNum); //视频流数据转换为RGB图像数据 sws_scale(sws, (const unsigned char* const*)frame->data,frame->linesize, 0, codecContext->height,frameRGB->data,frameRGB->linesize); //数据转换为图像 QImage *tmpImg = new QImage((uchar *)pOutbuffer,codecContext->width, codecContext->height,QImage::Format_RGB32); //加载到标签中显示 // imgLabel->setPixmap(QPixmap::fromImage(img)); emit signal_devodeImage(tmpImg); av_usleep(10 * 1000); } } } }
Mainwindow.ui
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。