老早之前用qt集成ffmpeg 做过一个播放器 那个是基于sdl的命令行窗口
这次做成GUI的方式 直接做成一个播放器 可以输入rtsp或者rtmp流地址
效果图如下 那个长的输入框输入rtsp地址 然后点击下面的播放按钮
测试时再ubuntu下面测试的 只有视频 没有音频
由于ffmpeg是支持rtmp h265的 所以 这个也可以播放h265的rtmp流
注意 是ubuntu下面运行的

点击播放之后 按钮会变成停止模式

可以只有缩放 也会随着窗口变化而变化

双击支持全屏显示 再次双击会变回之前的模式
关闭是给出提示
代码 头文件:
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
-
- #include
-
-
- #include
- extern "C"
- {
- #include "libavcodec/avcodec.h"
- #include "libavformat/avformat.h"
- #include "libswscale/swscale.h"
- #include "libavdevice/avdevice.h"
- #include "libswresample/swresample.h"
- #include "libavutil/imgutils.h"
- }
-
-
-
- namespace Ui {
- class MainWindow;
- }
-
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
-
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
-
- int init();
- void play();
-
-
-
- private:
- Ui::MainWindow *ui;
-
- bool m_run_flag = false;
- bool m_fullscreen_flag = false;
- std::thread m_decodecThread;
- AVFormatContext *pAVFormatCtx;
- AVCodecContext *pAVCodecCtx;
- SwsContext *pSwsCtx;
- uint8_t *pRgbBuffer;
- AVPacket packet;
- AVFrame *pAVFrame = NULL;
- AVFrame *pAVFrameRGB;
- int iVideoIndex = -1;
- QImage m_image;
- bool isFinish =false;
- void decodec();
-
- signals:
- void signalDraw();
-
-
-
- protected:
- void mouseDoubleClickEvent( QMouseEvent * e ) override;
- void paintEvent(QPaintEvent *event) override;
- void closeEvent(QCloseEvent * e) override;
-
-
- private slots:
- void slotDraw();
- void slotButtonClicked();
- void onPlayClicked();
- void onExitClicked();
- void onStopClicked();
-
- };
-
- #endif // MAINWINDOW_H
cpp源文件
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include
- #include
-
- #include "button.h"
- #include
- #include
- #include
-
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
-
- m_run_flag = false;
- connect(this,&MainWindow::signalDraw,this,&MainWindow::slotDraw);
-
- connect(ui->buttonplay, SIGNAL(clicked()), this, SLOT(onPlayClicked()));
- //connect(ui->buttonstop, SIGNAL(clicked()), this, SLOT(onStopClicked()));
-
- //ui->groupVideo->installEventFilter(this);
-
- ui->inputrtsp->setText("rtsp://uer:gd123456@192.168.2.126:554/Streaming/Channels/101");
-
-
-
- ui->buttonplay->setIcon(QIcon(":/new/image/play.png"));
- ui->buttonplay->setIconSize(QSize(50,50));
- ui->buttonplay->setMinimumSize(50,50);
- ui->buttonplay->setMaximumSize(50,50);
- ui->buttonplay->setFlat(true);
-
-
-
-
- //lay->addWidget(m_pBtnSearch); //。。。。 添加按钮。。。。。。。
- //lay->setContentsMargins(0, 0, 0, 0);
- //lay->setAlignment(Qt::AlignRight);
-
- //ui->inputrtsp->setFrame(false);
- ui->inputrtsp->setStyleSheet("QLineEdit{ background-color: rgba(128,128,128,120); }QLineEdit:focus{background-color: rgb(222,222,222)}"); // 设置样式
- //ui->inputrtsp->setTextMargins(0, 0, 30, 0); // 注意这里的30 因为图一的输入框最右边有个按钮。不能让光标在此区域出现。。需要设置文字显示范围
- //searchEdit->setPlaceholderText(tr("许嵩")); // 设置默认文字
-
-
- //ui->buttonplay->setStyleSheet("background-color: yellow");
-
- //ui->buttonstop->setStyleSheet("background-color: red");
-
-
- #if 0
- MyButton *play = new MyButton(this);
- connect(play, SIGNAL(clicked()), this, SLOT(onPlayClicked()));
- play->move(100,200);
-
-
- QPushButton *test = new QPushButton(this);
- connect(test,SIGNAL(clicked()),this,SLOT(slotButtonClicked()));
- test->setText("test button");
- test->move(200,300);
-
-
-
-
- connect(ui->buttonexit, SIGNAL(clicked()), this, SLOT(onExitClicked()));
-
-
-
-
- #endif
- }
-
- void MainWindow::closeEvent(QCloseEvent *e)
- {
- QMessageBox::StandardButton result=QMessageBox::question(this, "确认", "确定要退出本程序吗?",
- QMessageBox::Yes|QMessageBox::No,
- QMessageBox::No);
-
- if (result==QMessageBox::Yes)
- e->accept();
- else
- e->ignore();
-
- }
-
- #if 0
- bool eventFilter(QObject *obj, QEvent *event)
- {
- int i = 0;
- if (obj == ui->groupVideo)//当事件发生在u1(为Qlabel型)控件上
- {
- if (event->type() == QEvent::MouseButtonPress)//当为双击事件时
- {
- i++;
- if (i % 2 == 0) //此处为双击一次全屏,再双击一次退出
- {
- ui->groupVideo->setWindowFlags(Qt::Dialog);
- ui->groupVideo->showFullScreen();//全屏显示
- }
- else
- {
- ui->groupVideo->setWindowFlags(Qt::SubWindow);
- ui->groupVideo->showNormal();//退出全屏
- };
-
- }
- return QObject::eventFilter(obj, event);
- }
-
- }
- #endif
-
- int MainWindow::init()
- {
- //std::string file = "rtsp://uer:gd123456@192.168.2.121:554/Streaming/Channels/101";
-
- QString file =ui->inputrtsp->text();
- //描述多媒体文件的构成及其基本信息
-
- pAVFormatCtx = avformat_alloc_context(); //Init handle
-
- if (avformat_open_input(&pAVFormatCtx, file.toUtf8(), NULL, NULL) != 0)
- {
- qDebug() <<"open file fail";
- avformat_free_context(pAVFormatCtx);
- return -1;
- }
-
- //读取一部分视音频数据并且获得一些相关的信息
- if (avformat_find_stream_info(pAVFormatCtx, NULL) < 0)
- {
- qDebug() <<"vformat find stream fail";
- avformat_close_input(&pAVFormatCtx);
- return -1;
- }
- // 根据解码器枚举类型找到解码器
- AVCodec *pAVCodec;
- int ret = av_find_best_stream(pAVFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &pAVCodec, 0);
- if (ret < 0) {
- qDebug()<< "av_find_best_stream faliture";
- avformat_close_input(&pAVFormatCtx);
- return -1;
- }
- iVideoIndex = ret;
-
- pAVCodec = avcodec_find_decoder(pAVFormatCtx->streams[iVideoIndex]->codecpar->codec_id);
- if (pAVCodec == NULL)
- {
- qDebug()<<"not find decoder";
- return -1;
- }
-
- qDebug()<<"avcodec_open2 pAVCodec->name:" << QString::fromStdString(pAVCodec->name);
-
-
- if(pAVFormatCtx->streams[iVideoIndex]->avg_frame_rate.den != 0) {
- float fps_ = pAVFormatCtx->streams[iVideoIndex]->avg_frame_rate.num / pAVFormatCtx->streams[iVideoIndex]->avg_frame_rate.den;
- qDebug() <<"fps:" << fps_;
- }
- int64_t video_length_sec_ = pAVFormatCtx->duration/AV_TIME_BASE;
- qDebug() <<"video_length_sec_:" << video_length_sec_;
- pAVCodecCtx = avcodec_alloc_context3(pAVCodec);
- if (pAVCodecCtx == NULL)
- {
- qDebug() <<"get pAVCodecCtx fail";
- avformat_close_input(&pAVFormatCtx);
- return -1;
- }
- ret = avcodec_parameters_to_context(pAVCodecCtx,pAVFormatCtx->streams[iVideoIndex]->codecpar);
- if (ret < 0)
- {
- qDebug() <<"avcodec_parameters_to_context fail";
- avformat_close_input(&pAVFormatCtx);
- return -1;
- }
- if (avcodec_open2(pAVCodecCtx, pAVCodec, NULL) < 0)
- {
- qDebug()<<"avcodec_open2 fail";
- return -1;
- }
- //为解码帧分配内存
- //AVFrame 存放从AVPacket中解码出来的原始数据
- pAVFrame = av_frame_alloc();
- pAVFrameRGB = av_frame_alloc();
- //用于视频图像的转换,将源数据转换为RGB32的目标数据
- pSwsCtx = sws_getContext(pAVCodecCtx->width, pAVCodecCtx->height, pAVCodecCtx->pix_fmt,
- pAVCodecCtx->width, pAVCodecCtx->height, AV_PIX_FMT_RGB32,
- SWS_BICUBIC, NULL, NULL, NULL);
-
- int m_size = av_image_get_buffer_size(AVPixelFormat(AV_PIX_FMT_RGB32), pAVCodecCtx->width, pAVCodecCtx->height, 1);
- pRgbBuffer = (uint8_t *)(av_malloc(m_size));
- //为已经分配的空间的结构体AVPicture挂上一段用于保存数据的空间
- avpicture_fill((AVPicture *)pAVFrameRGB, pRgbBuffer, AV_PIX_FMT_BGR32, pAVCodecCtx->width, pAVCodecCtx->height);
- //av_image_fill_arrays
- //AVpacket 用来存放解码数据
- av_new_packet(&packet, pAVCodecCtx->width * pAVCodecCtx->height);
- return 0;
- }
-
- void MainWindow::play()
- {
- m_decodecThread = std::thread([this]()
- {
- init();
- decodec();
- });
- m_decodecThread.detach();
- }
-
-
-
- void MainWindow::decodec()
- {
- //读取码流中视频帧
- while (m_run_flag)
- {
- int ret = av_read_frame(pAVFormatCtx, &packet);
- if(ret != 0)
- {
- qDebug()<<"file end";
- isFinish = !isFinish;
- return;
- }
- if (packet.stream_index != iVideoIndex)
- {
- av_packet_unref(&packet);
- continue;
- }
- int iGotPic = AVERROR(EAGAIN);
- // //解码一帧视频数据
- iGotPic = avcodec_send_packet(pAVCodecCtx, &packet);
- if(iGotPic!=0){
- qDebug()<<"avcodec_send_packet error";
- continue;
- }
- iGotPic = avcodec_receive_frame(pAVCodecCtx, pAVFrame);
- if(iGotPic == 0){
- //转换像素
- sws_scale(pSwsCtx, (uint8_t const * const *)pAVFrame->data, pAVFrame->linesize, 0, pAVCodecCtx->height, pAVFrameRGB->data, pAVFrameRGB->linesize);
-
- //构造QImage
- QImage img(pRgbBuffer, pAVCodecCtx->width, pAVCodecCtx->height, QImage::Format_RGB32);
- //qDebug()<<"decode img";
- m_image = img;
- emit signalDraw();
- }else {
- qDebug()<<"decode error";
- }
-
- av_packet_unref(&packet);
- //std::this_thread::sleep_for(std::chrono::milliseconds(25));
- }
- //资源回收
- av_free(pAVFrame);
- av_free(pAVFrameRGB);
- sws_freeContext(pSwsCtx);
- avcodec_close(pAVCodecCtx);
- avformat_close_input(&pAVFormatCtx);
- }
-
- void MainWindow::slotDraw()
- {
- update();
- }
-
- void MainWindow::paintEvent(QPaintEvent *event)
- {
- QPainter painter(this);
- painter.setBrush(Qt::black);
- painter.drawRect(0, 0, this->width(), this->height());
-
-
-
- int appWindowWidth = this->geometry().width();
-
- int appWindowHeight = this->geometry().height();
-
-
- int button_w = ui->buttonplay->geometry().width();
- int button_h = ui->buttonplay->geometry().height();
-
-
- int intpu_w = ui->groupInput->geometry().width();
- int input_h = ui->groupInput->geometry().height();
-
-
- auto center_x = (appWindowWidth-button_w)/2;
- auto center_y = (appWindowHeight-button_h-50);
-
- ui->buttonplay->setGeometry(center_x, center_y, button_w,button_h);
-
-
-
- center_x = (appWindowWidth-intpu_w)/2;
- center_y = (appWindowHeight-input_h-120);
-
- ui->groupInput->setGeometry(center_x, center_y, intpu_w,input_h);
-
-
- if(m_run_flag == false)
- {
- return ;
- }
-
- if (m_image.size().width() <= 0)
- return;
-
- //比例缩放
- QImage img = m_image.scaled(this->size(),Qt::KeepAspectRatio);
- int x = this->width() - img.width();
- int y = this->height() - img.height();
-
- x /= 2;
- y /= 2;
-
- //QPoint(x,y)为中心绘制图像
- painter.drawImage(QPoint(x,y),img);
- }
-
-
- void MainWindow::slotButtonClicked()
- {
- QMessageBox::information(NULL,"Click","Double click",QMessageBox::Accepted);
- }
-
-
- void MainWindow::onPlayClicked()
- {
-
- if(m_run_flag == false)
- {
-
- ui->buttonplay->setIcon(QIcon(":/new/image/stop.png"));
- ui->buttonplay->setIconSize(QSize(50,50));
- ui->buttonplay->setMinimumSize(50,50);
- ui->buttonplay->setMaximumSize(50,50);
- ui->buttonplay->setFlat(true);
- m_run_flag = true;
-
- play();
-
-
-
- }
- else
- {
- m_run_flag = false;
- ui->buttonplay->setIcon(QIcon(":/new/image/play.png"));
- ui->buttonplay->setIconSize(QSize(50,50));
- ui->buttonplay->setMinimumSize(50,50);
- ui->buttonplay->setMaximumSize(50,50);
- ui->buttonplay->setFlat(true);
- //QMessageBox::information(NULL,"Click","Allready in playing...",QMessageBox::Accepted);
- }
-
- }
-
- void MainWindow::onExitClicked()
- {
- QMessageBox::information(NULL,"Click","onexitClicked",QMessageBox::Accepted);
- //ui->buttonexit->setText("I am exit button");
- }
-
- void MainWindow::onStopClicked()
- {
- //QMessageBox::information(NULL,"Click","onStopClicked",QMessageBox::Accepted);
- m_run_flag = false;
- update();
- //qApp->exit();
- }
-
-
- void MainWindow::mouseDoubleClickEvent( QMouseEvent * e )
- {
- if ( e->button() == Qt::LeftButton )
- {
- m_fullscreen_flag = 1-m_fullscreen_flag;
- if(m_fullscreen_flag)
- {
- showFullScreen();
- }
- else
- {
- showNormal();
- }
-
- }
-
- // You may have to call the parent's method for other cases
- QMainWindow::mouseDoubleClickEvent( e );
- }
-
-
-
- MainWindow::~MainWindow()
- {
- m_run_flag = false;
- delete ui;
- }
这个时个demo很多异常处理都没有做
算是QT入个门
项目配置文件 利用ffmpeg软解码
-
- QMAKE_CXXFLAGS += -std=c++0x
-
- greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
-
- TARGET = qtplayer
- TEMPLATE = app
-
-
- SOURCES += main.cpp\
- mainwindow.cpp \
- button.cpp
-
-
-
- INCLUDEPATH += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt_so/include
- INCLUDEPATH += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubunt_rmtp_h265/include
-
-
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt_so/lib/libavformat.a
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt_so/lib/libavcodec.a
-
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt_so/lib/libavdevice.a
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt_so/lib/libavfilter.a
-
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt_so/lib/libavutil.a
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt_so/lib/libswresample.a
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/FFmpeg-n4.3.2/install_ubunt_so/lib/libswscale.a
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubuntu/lib/libSDL2.a
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubuntu/lib/libSDL2main.a
- LIBS += /home/jason/HS/Hi3531DV200_SDK_V2.0.0.3/mpp/sample/SDL2-2.0.16/install_ubuntu/lib/libSDL2_test.a
-
- LIBS += -lz
-
- HEADERS += mainwindow.h \
- button.h
-
- FORMS += mainwindow.ui
-
- RESOURCES += \
- image.qrc