Qt5 Tutorial Video Player with HTML5 QWebView and FFmpeg Converter - 2020
In this tutorial, we will learn how to play videos using QWebView.
This tutorial is the extension of the previous tutorial: FFmpeg Converter using QProcess.
The following two are the products of this tutorial.
The picture is converting mp4 to webm using ffmpeg.
The video recording below is the player built using HTML5 video tag with QWebView as its canvas. Internally, we have two containers: mp4 and ogv, and both are converted from recorded avi. The very converter of this tutorial was used for the conversion.
Since this tutorial is the extension of the previous tutorial: FFmpeg Converter using QProcess, and not much changes made to the converter itself, we'll start directly from the video player.
I'm using Qt5.1.1.
The player we're using in this tutorial is not a real standalone play but it's kind of Browser that can play a video. I'm using QWebview as a canvas where we can render an HTML5 video.
As we can see from the picture above, we have "HTML5 Play..." button on the "FFmpeg Dialog". When we click this button, it gets the mime types from the media file, in this case, from the *.mp4, and produces an html file in
// Make a new html file using video tag // after getting the mimetype from the video file. // The html file name will be put into url. void Dialog::on_playOnBrowserButton_clicked() { QString videoFilePath = ui->urlEdit->text(); QString htmlFilePath = "C:/TEST/t.html"; QFile file(htmlFilePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { return; } QString mimetype = mimeReturn(videoFilePath); QTextStream stream(&file;); stream << "<html>"<< "\n"; stream << "<video width=\"480\" height=\"270\" controls autoplay>"<< "\n"; stream << "<source src=" << "\"" << videoFilePath << "\" " << "type=\"" << mimetype << "\"> \n"; stream << "</video>"<< "\n"; stream << "</html>"<< "\n"; QString htmlUrl = "file:///" + htmlFilePath; mPlayer = new Player(htmlUrl, this); mPlayer->setWindowTitle("WebKit HTML5 Video Player"); mPlayer->show(); }
So, this generates the following html.
<html> <video width="480" height="270" controls autoplay> <source src="C:/TEST/planet_small.mp4" type="video/mp4"> </video> </html>
Then, it will create a "WebKit HTML5 Video Player" dialog:
mPlayer = new Player(htmlUrl, this);
The "htmlUrl" parameter is used a url for QWebView, and it will render the media between <video> </video> tags.
We called a function, mimeReturn(), to get the right MimeType for a given QFile. The function looks like this:
// Returning a mimetype for a given QFile QString mimeReturn(const QFile& file) { QMimeDatabase mimeDatabase; QMimeType mimeType; mimeType = mimeDatabase.mimeTypeForFile(QFileInfo(file)); // mp4 mpg4 if(mimeType.inherits("video/mp4")) return "video/mp4"; // mpeg mpg mpe else if(mimeType.inherits("video/mpeg")) return "video/mpeg"; // ogv else if(mimeType.inherits("video/ogg")) return "video/ogg"; // qt, mov else if(mimeType.inherits("video/quicktime")) return "video/quicktime"; // avi else if(mimeType.inherits("video/x-msvideo")) return "video/x-msvideo"; // flv else if (mimeType.inherits("video/x-flv")) return "video/x-flv"; // webm else if (mimeType.inherits("video/webm")) return "video/webm"; // text else if (mimeType.inherits("text/plain")) return "text"; else return ""; }
The key lines are the highlighted two lines:
For a given QFile, the QFileInfo() gets the names, and the mimeDatabase.mimeTypeForFile() returns QMimeType. This enables us to compare the known Mime type with the given file's Mime type.
Here are the source codes:
Th zip file is ffmpeg.zip.
Note: We must add webkitwidgets to our .pro file.
dialog.h:
// dialog.h #ifndef DIALOG_H #define DIALOG_H #include <QDialog> #include <QProcess> #include <QFile> #include <QTextEdit> #include "player.h" namespace Ui { class Dialog; } class Dialog : public QDialog { Q_OBJECT public: explicit Dialog(QWidget *parent = 0); ~Dialog(); public slots: public: private slots: void on_startButton_clicked(); void readyReadStandardOutput(); void processStarted(); void encodingFinished(); void on_fileOpenButton_clicked(); void on_playInputButton_clicked(); void on_playOutputButton_clicked(); void on_playOnBrowserButton_clicked(); void on_webBrowserButton_clicked(); private: Ui::Dialog *ui; QProcess *mTranscodingProcess; QProcess *mInputPlayProcess; QProcess *mOutputPlayProcess; QString mOutputString; QString playerUrl; Player *mPlayer; }; #endif // DIALOG_H
dialog.cpp:
// dialog.cpp #include "dialog.h" #include "ui_dialog.h" #include <QDebug> #include <QString> #include <QProcess> #include <QScrollBar> #include <QMessageBox> #include <QFileInfo> #include <QFileDialog> #include <QWebView> #include <QTextStream> #include <QMimeDatabase> #include <QMimeType> QString mimeReturn(const QFile&); Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); // Play button for output - initially disabled ui->playOutputButton->setEnabled(false); // Create three processes // 1.transcoding, 2.input play 3.output play mTranscodingProcess = new QProcess(this); mInputPlayProcess = new QProcess(this); mOutputPlayProcess = new QProcess(this); connect(mTranscodingProcess, SIGNAL(started()), this, SLOT(processStarted())); connect(mTranscodingProcess,SIGNAL(readyReadStandardOutput()),this,SLOT(readyReadStandardOutput())); connect(mTranscodingProcess, SIGNAL(finished(int)), this, SLOT(encodingFinished())); } Dialog::~Dialog() { delete ui; } void Dialog::processStarted() { qDebug() << "processStarted()"; } // conversion start void Dialog::on_startButton_clicked() { QString program = "C:/FFmpeg/bin/ffmpeg"; QStringList arguments; QString input = ui->fromLineEdit->text(); if(input.isEmpty()) { qDebug() << "No input"; QMessageBox::information(this, tr("ffmpeg"),tr("Input file not specified")); return; } QString output = ui->toLineEdit->text(); if(output.isEmpty()) { qDebug() << "No output"; QMessageBox::information(this, tr("ffmpeg"),tr("Output file not specified")); return; } QString fileName = ui->toLineEdit->text(); qDebug() << "output file check " << fileName; qDebug() << "QFile::exists(fileName) = " << QFile::exists(fileName); if (QFile::exists(fileName)) { if (QMessageBox::question(this, tr("ffmpeg"), tr("There already exists a file called %1 in " "the current directory. Overwrite?").arg(fileName), QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::No) return; QFile::remove(fileName); while(QFile::exists(fileName)) { qDebug() << "output file still there"; } } arguments << "-i" << input << output; qDebug() << arguments; mTranscodingProcess->setProcessChannelMode(QProcess::MergedChannels); mTranscodingProcess->start(program, arguments); } void Dialog::readyReadStandardOutput() { mOutputString.append(mTranscodingProcess->readAllStandardOutput()); ui->textEdit->setText(mOutputString); // put the slider at the bottom ui->textEdit->verticalScrollBar() ->setSliderPosition( ui->textEdit->verticalScrollBar()->maximum()); } void Dialog::encodingFinished() { // Set the encoding status by checking output file's existence QString fileName = ui->toLineEdit->text(); if (QFile::exists(fileName)) { ui->transcodingStatusLabel ->setText("Transcoding Status: Successful!"); ui->playOutputButton->setEnabled(true); } else { ui->transcodingStatusLabel ->setText("Transcoding Status: Failed!"); } } // Browse... button clicked - this is for input file void Dialog::on_fileOpenButton_clicked() { QString fileName = QFileDialog::getOpenFileName( this, tr("Open File"), "C:/TEST", tr("videoss (*.mp4 *.mov *.avi *webm)")); if (!fileName.isEmpty()) { ui->fromLineEdit->setText(fileName); } } // ffplay with input video void Dialog::on_playInputButton_clicked() { QString program = "C:/FFmpeg/bin/ffplay"; QStringList arguments; QString input = ui->fromLineEdit->text(); arguments << input; mInputPlayProcess->start(program, arguments); } // ffplay with output video void Dialog::on_playOutputButton_clicked() { QString program = "C:/FFmpeg/bin/ffplay"; QStringList arguments; QString output = ui->toLineEdit->text(); arguments << output; mInputPlayProcess->start(program, arguments); } // Make a new html file using video tag // after getting the mimetype from the video file. // The html file name will be put into url. void Dialog::on_playOnBrowserButton_clicked() { QString videoFilePath = ui->urlEdit->text(); QString htmlFilePath = "C:/TEST/t.html"; QFile file(htmlFilePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { return; } QString mimetype = mimeReturn(videoFilePath); QTextStream stream(&file;); stream << "<html>"<< "\n"; stream << "<video width=\"480\" height=\"270\" controls autoplay>"<< "\n"; stream << "<source src=" << "\"" << videoFilePath << "\" " << "type=\"" << mimetype << "\"> \n"; stream << "</video>"<< "\n"; stream << "</html>"<< "\n"; QString htmlUrl = "file:///" + htmlFilePath; mPlayer = new Player(htmlUrl, this); mPlayer->setWindowTitle("WebKit HTML5 Video Player"); mPlayer->show(); } // Browse button for HTML5 clicked. // Media file name will be put into the lineEdit void Dialog::on_webBrowserButton_clicked() { QString fileName = QFileDialog::getOpenFileName( this, tr("Open File"), "C:/TEST", tr("videoss (*.mp4 *.mov *.avi *webm)")); if (!fileName.isEmpty()) { ui->urlEdit->setText(fileName); } } // Returning a mimetype for a given QFile QString mimeReturn(const QFile& file) { QMimeDatabase mimeDatabase; QMimeType mimeType; mimeType = mimeDatabase.mimeTypeForFile(QFileInfo(file)); // mp4 mpg4 if(mimeType.inherits("video/mp4")) return "video/mp4"; // mpeg mpg mpe else if(mimeType.inherits("video/mpeg")) return "video/mpeg"; // ogv else if(mimeType.inherits("video/ogg")) return "video/ogg"; // qt, mov else if(mimeType.inherits("video/quicktime")) return "video/quicktime"; // avi else if(mimeType.inherits("video/x-msvideo")) return "video/x-msvideo"; // flv else if (mimeType.inherits("video/x-flv")) return "video/x-flv"; // webm else if (mimeType.inherits("video/webm")) return "video/webm"; // text else if (mimeType.inherits("text/plain")) return "text"; else return ""; }
player.h:
// player.h #ifndef PLAYER_H #define PLAYER_H #include <QDialog> namespace Ui { class Player; } class Player : public QDialog { Q_OBJECT public: explicit Player(QString &str;, QWidget *parent = 0); ~Player(); private slots: void on_replayButton_clicked(); private: Ui::Player *ui; QString url; }; #endif // PLAYER_H
player.cpp:
// player.cpp #include "player.h" #include "ui_player.h" Player::Player(QString &s;, QWidget *parent) : QDialog(parent), ui(new Ui::Player), url(s) { ui->setupUi(this); ui->playerEdit->setText(url); ui->webView->load(url); } Player::~Player() { delete ui; } void Player::on_replayButton_clicked() { url = ui->playerEdit->text(); ui->webView->load(url); }
ffmpeg.pro:
QT += core gui QT += webkitwidgets greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = ffmpeg TEMPLATE = app SOURCES += main.cpp\ dialog.cpp \ player.cpp HEADERS += dialog.h \ player.h FORMS += dialog.ui \ player.ui
Qt 5 Tutorial
- Hello World
- Signals and Slots
- Q_OBJECT Macro
- MainWindow and Action
- MainWindow and ImageViewer using Designer A
- MainWindow and ImageViewer using Designer B
- Layouts
- Layouts without Designer
- Grid Layouts
- Splitter
- QDir
- QFile (Basic)
- Resource Files (.qrc)
- QComboBox
- QListWidget
- QTreeWidget
- QAction and Icon Resources
- QStatusBar
- QMessageBox
- QTimer
- QList
- QListIterator
- QMutableListIterator
- QLinkedList
- QMap
- QHash
- QStringList
- QTextStream
- QMimeType and QMimeDatabase
- QFile (Serialization I)
- QFile (Serialization II - Class)
- Tool Tips in HTML Style and with Resource Images
- QPainter
- QBrush and QRect
- QPainterPath and QPolygon
- QPen and Cap Style
- QBrush and QGradient
- QPainter and Transformations
- QGraphicsView and QGraphicsScene
- Customizing Items by inheriting QGraphicsItem
- QGraphicsView Animation
- FFmpeg Converter using QProcess
- QProgress Dialog - Modal and Modeless
- QVariant and QMetaType
- QtXML - Writing to a file
- QtXML - QtXML DOM Reading
- QThreads - Introduction
- QThreads - Creating Threads
- Creating QThreads using QtConcurrent
- QThreads - Priority
- QThreads - QMutex
- QThreads - GuiThread
- QtConcurrent QProgressDialog with QFutureWatcher
- QSemaphores - Producer and Consumer
- QThreads - wait()
- MVC - ModelView with QListView and QStringListModel
- MVC - ModelView with QTreeView and QDirModel
- MVC - ModelView with QTreeView and QFileSystemModel
- MVC - ModelView with QTableView and QItemDelegate
- QHttp - Downloading Files
- QNetworkAccessManager and QNetworkRequest - Downloading Files
- Qt's Network Download Example - Reconstructed
- QNetworkAccessManager - Downloading Files with UI and QProgressDialog
- QUdpSocket
- QTcpSocket
- QTcpSocket with Signals and Slots
- QTcpServer - Client and Server
- QTcpServer - Loopback Dialog
- QTcpServer - Client and Server using MultiThreading
- QTcpServer - Client and Server using QThreadPool
- Asynchronous QTcpServer - Client and Server using QThreadPool
- Qt Quick2 QML Animation - A
- Qt Quick2 QML Animation - B
- Short note on Ubuntu Install
- OpenGL with QT5
- Qt5 Webkit : Web Browser with QtCreator using QWebView Part A
- Qt5 Webkit : Web Browser with QtCreator using QWebView Part B
- Video Player with HTML5 QWebView and FFmpeg Converter
- Qt5 Add-in and Visual Studio 2012
- Qt5.3 Installation on Ubuntu 14.04
- Qt5.5 Installation on Ubuntu 14.04
- Short note on deploying to Windows
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization