#include "videoplayer.h" #include #include #include #include #include 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(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 ("glimagesink", 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 ("glimagesink", 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 ("glimagesink", 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..."; }