Initial commit
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
*.o
|
||||||
|
moc_*
|
||||||
|
.qmake.stash
|
||||||
|
Makefile
|
||||||
|
main
|
||||||
|
main.pro.user
|
||||||
|
*.mp4
|
||||||
23
main.cpp
Normal file
23
main.cpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include "videoplayer.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QCommandLineOption>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
QCoreApplication::setApplicationName("XTrack-NG core POC");
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription("XTrack-NG core POC");
|
||||||
|
parser.addHelpOption();
|
||||||
|
parser.process(app);
|
||||||
|
|
||||||
|
VideoPlayer player;
|
||||||
|
|
||||||
|
player.show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
14
main.pro
Normal file
14
main.pro
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
TEMPLATE = app
|
||||||
|
load(ccache)
|
||||||
|
|
||||||
|
QT += widgets
|
||||||
|
|
||||||
|
HEADERS += videoplayer.h
|
||||||
|
|
||||||
|
SOURCES += main.cpp \
|
||||||
|
videoplayer.cpp
|
||||||
|
|
||||||
|
unix {
|
||||||
|
CONFIG += link_pkgconfig
|
||||||
|
PKGCONFIG += gstreamer-1.0 gstreamer-video-1.0
|
||||||
|
}
|
||||||
346
videoplayer.cpp
Normal file
346
videoplayer.cpp
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
#include "videoplayer.h"
|
||||||
|
|
||||||
|
#include <QtWidgets>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/rtsp/gstrtsptransport.h>
|
||||||
|
#include <gst/video/videooverlay.h>
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
pad_added_cb (GstElement *src, GstPad *srcpad, GstElement *peer)
|
||||||
|
{
|
||||||
|
g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (srcpad), GST_ELEMENT_NAME (src));
|
||||||
|
|
||||||
|
(void)src; // remove unused variable warning
|
||||||
|
GstPad *sinkpad = gst_element_get_static_pad (peer, "sink");
|
||||||
|
gst_pad_link (srcpad, sinkpad);
|
||||||
|
gst_object_unref (sinkpad);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void VideoPlayer::print_status_of_all()
|
||||||
|
{
|
||||||
|
auto it = gst_bin_iterate_elements(GST_BIN(pipeline));
|
||||||
|
GValue value = G_VALUE_INIT;
|
||||||
|
for (GstIteratorResult r = gst_iterator_next(it, &value);
|
||||||
|
r != GST_ITERATOR_DONE;
|
||||||
|
r = gst_iterator_next(it, &value))
|
||||||
|
{
|
||||||
|
if (r == GST_ITERATOR_OK)
|
||||||
|
{
|
||||||
|
GstElement *e = static_cast<GstElement*>(g_value_peek_pointer(&value));
|
||||||
|
GstState current, pending;
|
||||||
|
gst_element_get_state(e, ¤t, &pending, 100000);
|
||||||
|
g_print("%s(%s), status = %s, pending = %s\n", G_VALUE_TYPE_NAME(&value), gst_element_get_name(e), gst_element_state_get_name(current), gst_element_state_get_name(pending));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer::VideoPlayer(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
QAbstractButton *debugButton = new QPushButton(tr("?"));
|
||||||
|
connect(debugButton, SIGNAL(clicked()), this, SLOT(debugSlot()));
|
||||||
|
|
||||||
|
m_stopButton = new QPushButton(this);
|
||||||
|
m_stopButton->setIcon(style()->standardIcon(QStyle::SP_MediaStop));
|
||||||
|
m_stopButton->setEnabled(false);
|
||||||
|
connect(m_stopButton, SIGNAL(clicked()), this, SLOT(stop()));
|
||||||
|
|
||||||
|
m_playButton = new QPushButton(this);
|
||||||
|
m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
|
||||||
|
connect(m_playButton, &QAbstractButton::clicked, this, &VideoPlayer::playPause);
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// Options
|
||||||
|
QCheckBox *tcpCheckBox = new QCheckBox("Use TCP protocol", this);
|
||||||
|
tcpCheckBox->setChecked(use_tcp);
|
||||||
|
connect(tcpCheckBox, SIGNAL(toggled(bool)), this, SLOT(useTcp(bool)));
|
||||||
|
|
||||||
|
QLineEdit *rtspUrlEdit = new QLineEdit(this);
|
||||||
|
connect (rtspUrlEdit, SIGNAL(textEdited(QString)), this, SLOT(setRtspUrl(QString)));
|
||||||
|
//rtspUrlEdit->setText("rtsp://127.0.0.1:8554/test");
|
||||||
|
rtspUrlEdit->setText(rtspUrl);
|
||||||
|
|
||||||
|
QFormLayout *optionsLayout = new QFormLayout();
|
||||||
|
optionsLayout->addWidget(tcpCheckBox);
|
||||||
|
optionsLayout->addRow("Rtsp URL", rtspUrlEdit);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// Player controls
|
||||||
|
QBoxLayout *controlLayout = new QHBoxLayout;
|
||||||
|
controlLayout->setMargin(0);
|
||||||
|
controlLayout->addWidget(debugButton);
|
||||||
|
controlLayout->addWidget(m_stopButton);
|
||||||
|
controlLayout->addWidget(m_playButton);
|
||||||
|
controlLayout->addStretch();
|
||||||
|
|
||||||
|
playerWidget = new QWidget(this);
|
||||||
|
|
||||||
|
QBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
layout->addLayout(optionsLayout);
|
||||||
|
layout->addWidget(playerWidget);
|
||||||
|
layout->addLayout(controlLayout);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//void cb_message (GstBus *bus, GstMessage *msg, CustomData *data)
|
||||||
|
void cb_message (GstBus *bus, GstMessage *msg, GstElement *pipeline)
|
||||||
|
{
|
||||||
|
(void)bus; // remove unused variable warning
|
||||||
|
int buffering_level = 0;
|
||||||
|
|
||||||
|
switch (GST_MESSAGE_TYPE (msg)) {
|
||||||
|
case GST_MESSAGE_BUFFERING:
|
||||||
|
gst_message_parse_buffering (msg, &buffering_level);
|
||||||
|
qDebug() << "Buffering level: " << buffering_level;
|
||||||
|
/* Wait until buffering is complete before start/resume playing */
|
||||||
|
if (buffering_level < 100) {
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||||
|
} else {
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Unhandled message */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoPlayer::~VideoPlayer()
|
||||||
|
{
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||||
|
gst_object_unref (pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::initGst () {
|
||||||
|
// prepare the pipeline
|
||||||
|
gst_init (NULL, NULL);
|
||||||
|
pipeline = gst_pipeline_new ("plop");
|
||||||
|
|
||||||
|
GstBus *bus = gst_element_get_bus (pipeline);
|
||||||
|
gst_bus_add_signal_watch (bus);
|
||||||
|
g_signal_connect (bus, "message", G_CALLBACK (cb_message), pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::setGstTestVideo () {
|
||||||
|
setGstTestVideo (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::setGstTestVideo (int pattern)
|
||||||
|
{
|
||||||
|
initGst();
|
||||||
|
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||||
|
|
||||||
|
GstElement *src = gst_element_factory_make ("videotestsrc", NULL);
|
||||||
|
GstElement *time = gst_element_factory_make ("timeoverlay", NULL);
|
||||||
|
GstElement *sink = gst_element_factory_make ("xvimagesink", NULL);
|
||||||
|
gst_bin_add_many (GST_BIN (pipeline), src, time, sink, NULL);
|
||||||
|
gst_element_link_many (src, time, sink, NULL);
|
||||||
|
g_object_set (src, "pattern", pattern, NULL);
|
||||||
|
g_object_set (time, "font-desc", "Sans, 24", NULL);
|
||||||
|
|
||||||
|
WId xwinid = playerWidget->winId();
|
||||||
|
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (sink), xwinid);
|
||||||
|
|
||||||
|
// Need to set newly added elements in the same state as the pipeline
|
||||||
|
gst_element_set_state (src, GST_STATE_PAUSED);
|
||||||
|
gst_element_set_state (time, GST_STATE_PAUSED);
|
||||||
|
gst_element_set_state (sink, GST_STATE_PAUSED);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void VideoPlayer::setGstFileVideo ()
|
||||||
|
{
|
||||||
|
qDebug() << "[setGstFileVideo] Entering function";
|
||||||
|
initGst();
|
||||||
|
|
||||||
|
GstElement *src = gst_element_factory_make ("filesrc", NULL);
|
||||||
|
GstElement *demux = gst_element_factory_make ("qtdemux", NULL);
|
||||||
|
GstElement *vdec = gst_element_factory_make ("avdec_h264", NULL);
|
||||||
|
GstElement *vqueue = gst_element_factory_make ("queue2", NULL);
|
||||||
|
GstElement *vconv = gst_element_factory_make ("videoconvert", NULL);
|
||||||
|
GstElement *time = gst_element_factory_make ("timeoverlay", NULL);
|
||||||
|
GstElement *sink = gst_element_factory_make ("xvimagesink", NULL);
|
||||||
|
video_sink = sink;
|
||||||
|
|
||||||
|
// qDebug () << "[setGstFileVideo] rtspUrl = " << rtspUrl;
|
||||||
|
g_object_set (src, "location", "sintel-trailer.mp4", NULL);
|
||||||
|
g_object_set (time, "font-desc", "Sans, 24", NULL);
|
||||||
|
//g_object_set (vqueue, "ring-buffer-max-size", (guint64)40000000, NULL);
|
||||||
|
g_object_set (vqueue,
|
||||||
|
"max-size-bytes", (guint64)128 * 1024 * 1024, // 128 MB buffer
|
||||||
|
//"ring-buffer-max-size", (guint64)128 * 1024, // 128 KB buffer
|
||||||
|
"use-buffering", true,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
// if (!rate_control) {
|
||||||
|
// g_object_set (src, "onvif-rate-control", FALSE, NULL);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (use_tcp) {
|
||||||
|
// g_object_set (src, "protocols", GST_RTSP_LOWER_TRANS_TCP, NULL);
|
||||||
|
// }
|
||||||
|
|
||||||
|
gst_bin_add_many (GST_BIN (pipeline), src, demux, vdec, vqueue, vconv, time, sink, NULL);
|
||||||
|
|
||||||
|
gst_element_link_many (src, demux, NULL);
|
||||||
|
// link demux to vdec in a cb, when pad is created
|
||||||
|
g_signal_connect (demux, "pad-added", G_CALLBACK (pad_added_cb), vdec);
|
||||||
|
// link rest of the pipeline
|
||||||
|
gst_element_link_many (vdec, vqueue, vconv, time, sink, NULL);
|
||||||
|
|
||||||
|
WId xwinid = playerWidget->winId();
|
||||||
|
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (sink), xwinid);
|
||||||
|
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||||
|
qDebug() << "[setGstFileVideo] All done, pipeline is in PAUSED state";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void VideoPlayer::setGstRtspVideo ()
|
||||||
|
{
|
||||||
|
initGst();
|
||||||
|
|
||||||
|
GstElement *src = gst_element_factory_make ("rtspsrc", NULL);
|
||||||
|
GstElement *vdepay = gst_element_factory_make ("rtph264depay", NULL);
|
||||||
|
GstElement *vdec = gst_element_factory_make ("avdec_h264", NULL);
|
||||||
|
GstElement *vqueue = gst_element_factory_make ("queue2", NULL);
|
||||||
|
GstElement *vconv = gst_element_factory_make ("videoconvert", NULL);
|
||||||
|
GstElement *time = gst_element_factory_make ("timeoverlay", NULL);
|
||||||
|
GstElement *sink = gst_element_factory_make ("xvimagesink", NULL);
|
||||||
|
|
||||||
|
//g_object_set (src, "location", "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov", NULL);
|
||||||
|
qDebug () << "[setGstRtspVideo] rtspUrl = " << rtspUrl;
|
||||||
|
g_object_set (src, "location", rtspUrl.toStdString().c_str(), NULL);
|
||||||
|
g_object_set (time, "font-desc", "Sans, 24", NULL);
|
||||||
|
g_object_set (vqueue, "max-size-bytes", (guint64)40000000, NULL);
|
||||||
|
|
||||||
|
if (use_tcp) {
|
||||||
|
g_object_set (src, "protocols", GST_RTSP_LOWER_TRANS_TCP, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_bin_add_many (GST_BIN (pipeline), src, vdepay, vdec, vqueue, vconv, time, sink, NULL);
|
||||||
|
// link src to vdepay in a cb, when pad is created
|
||||||
|
g_signal_connect (src, "pad-added", G_CALLBACK (pad_added_cb), vdepay);
|
||||||
|
// link rest of the pipeline
|
||||||
|
gst_element_link_many (vdepay, vdec, vqueue, vconv, time, sink, NULL);
|
||||||
|
|
||||||
|
WId xwinid = playerWidget->winId();
|
||||||
|
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (sink), xwinid);
|
||||||
|
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void VideoPlayer::play()
|
||||||
|
{
|
||||||
|
if (!pipeline) {
|
||||||
|
setGstRtspVideo();
|
||||||
|
//setGstFileVideo();
|
||||||
|
//setGstTestVideo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the pipeline
|
||||||
|
GstStateChangeReturn sret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
||||||
|
if (sret == GST_STATE_CHANGE_FAILURE) {
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||||
|
gst_object_unref (pipeline);
|
||||||
|
// Exit application
|
||||||
|
QTimer::singleShot(0, QApplication::activeWindow(), SLOT(quit()));
|
||||||
|
}
|
||||||
|
m_stopButton->setEnabled(true);
|
||||||
|
playing = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::pause()
|
||||||
|
{
|
||||||
|
qDebug() << "PAUSE called";
|
||||||
|
//GST_DEBUG_BIN_TO_DOT_FILE((GstBin *)pipeline, GST_DEBUG_GRAPH_SHOW_ALL, "pipeline");
|
||||||
|
GstStateChangeReturn sret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
|
||||||
|
if (sret == GST_STATE_CHANGE_FAILURE) {
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||||
|
gst_object_unref (pipeline);
|
||||||
|
// Exit application
|
||||||
|
QTimer::singleShot(0, QApplication::activeWindow(), SLOT(quit()));
|
||||||
|
}
|
||||||
|
playing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::playPause()
|
||||||
|
{
|
||||||
|
if (playing) {
|
||||||
|
qDebug() << "toggle: play --> pause";
|
||||||
|
m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
|
||||||
|
VideoPlayer::pause();
|
||||||
|
} else {
|
||||||
|
qDebug() << "toggle: pause --> play";
|
||||||
|
m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
|
||||||
|
//m_playButton->setEnabled(false);
|
||||||
|
VideoPlayer::play();
|
||||||
|
}
|
||||||
|
// playing boolean is set in play and pause methods
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::stop()
|
||||||
|
{
|
||||||
|
qDebug() << "[STOP function] called";
|
||||||
|
|
||||||
|
if (pipeline) {
|
||||||
|
qDebug() << "[STOP function] detected a running pipeline";
|
||||||
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||||
|
gst_object_unref (pipeline);
|
||||||
|
pipeline = nullptr;
|
||||||
|
qDebug() << "[STOP function] stopped and dropped pipeline";
|
||||||
|
} else {
|
||||||
|
qDebug() << "[STOP function] no running pipeline";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinit interface
|
||||||
|
playing = false;
|
||||||
|
m_playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
|
||||||
|
playerWidget->repaint();
|
||||||
|
m_stopButton->setEnabled(false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void VideoPlayer::debugSlot()
|
||||||
|
{
|
||||||
|
qDebug() << "DEBUG";
|
||||||
|
print_status_of_all();
|
||||||
|
qDebug() << "Use TCP? " << use_tcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::useTcp(bool value)
|
||||||
|
{
|
||||||
|
use_tcp = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::setRtspUrl(QString url)
|
||||||
|
{
|
||||||
|
qDebug() << "setRtspUrl: " << url;
|
||||||
|
rtspUrl = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoPlayer::benSlot()
|
||||||
|
{
|
||||||
|
qDebug() << " ben slot...";
|
||||||
|
}
|
||||||
58
videoplayer.h
Normal file
58
videoplayer.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#ifndef VIDEOPLAYER_H
|
||||||
|
#define VIDEOPLAYER_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QLayout>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QAbstractButton;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class VideoPlayer : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
VideoPlayer(QWidget *parent = nullptr);
|
||||||
|
~VideoPlayer();
|
||||||
|
|
||||||
|
void load(const QUrl &url);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void play();
|
||||||
|
void pause();
|
||||||
|
void playPause();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void debugSlot();
|
||||||
|
void useTcp(bool value);
|
||||||
|
void setRtspUrl(QString url);
|
||||||
|
void benSlot();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<QWidget *> videoDisplays = {};
|
||||||
|
QWidget *playerWidget = nullptr;
|
||||||
|
|
||||||
|
QAbstractButton *m_stopButton = nullptr;
|
||||||
|
QAbstractButton *m_playButton = nullptr;
|
||||||
|
QAbstractButton *m_seekBackwardButton = nullptr;
|
||||||
|
QAbstractButton *m_seekForwardButton = nullptr;
|
||||||
|
bool playing = false;
|
||||||
|
GstElement *pipeline = nullptr;
|
||||||
|
GstElement *video_sink = nullptr;
|
||||||
|
|
||||||
|
bool use_tcp = true;
|
||||||
|
QString rtspUrl = "rtsp://127.0.0.1:8554/test";
|
||||||
|
|
||||||
|
void print_status_of_all();
|
||||||
|
|
||||||
|
void initGst();
|
||||||
|
void setGstTestVideo();
|
||||||
|
void setGstTestVideo(int pattern);
|
||||||
|
void setGstFileVideo();
|
||||||
|
void setGstRtspVideo();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user