Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / ground / gcs / src / libs / gstreamer / videowidget.cpp
blob74488f972bad747c5dadaa8806a53ce42700d139
1 /**
2 ******************************************************************************
4 * @file videowidget.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017.
6 * @brief
7 * @see The GNU Public License (GPL) Version 3
8 * @defgroup
9 * @{
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "videowidget.h"
29 #include "gst_util.h"
30 #include "overlay.h"
31 #include "pipelineevent.h"
32 // #include "devicemonitor.h"
34 #include <gst/gst.h>
35 #include <gst/video/videooverlay.h>
37 #include <QtCore>
38 #include <QPainter>
39 #include <QDebug>
40 #include <QRect>
41 #include <QTextDocument>
43 #include <string>
45 // TODO find a better way and move away from this file
46 static Pipeline::State cvt(GstState state);
47 static const char *name(Pipeline::State state);
48 static ProgressEvent::ProgressType cvt(GstProgressType type);
50 class GstOverlayImpl : public Overlay {
51 public:
52 GstOverlayImpl(GstVideoOverlay *gst_overlay) :
53 gst_overlay(gst_overlay)
55 void expose()
57 if (gst_overlay) {
58 gst_video_overlay_expose(gst_overlay);
61 private:
62 GstVideoOverlay *gst_overlay;
65 class BusSyncHandler {
66 public:
67 BusSyncHandler(VideoWidget *widget, WId wid) :
68 widget(widget), wId(wid)
70 bool handleMessage(GstMessage *msg);
71 private:
72 VideoWidget *widget;
73 WId wId;
76 static GstElement *createPipelineFromDesc(const char *, QString &lastError);
78 static GstBusSyncReply gst_bus_sync_handler(GstBus *, GstMessage *, BusSyncHandler *);
80 VideoWidget::VideoWidget(QWidget *parent) :
81 QWidget(parent), pipeline(NULL), overlay(NULL)
83 qDebug() << "VideoWidget::VideoWidget";
85 // initialize gstreamer
86 gst::init(NULL, NULL);
88 // foreach(Device d, m.devices()) {
89 // qDebug() << d.displayName();
90 // }
92 // make the widget native so it gets its own native window id that we will pass to gstreamer
93 setAttribute(Qt::WA_NativeWindow);
94 #ifdef Q_OS_MAC
95 // WA_DontCreateNativeAncestors is needed on mac
96 setAttribute(Qt::WA_DontCreateNativeAncestors);
97 #endif
99 // set black background
100 QPalette pal(palette());
101 pal.setColor(backgroundRole(), Qt::black);
102 setPalette(pal);
104 // calling winId() will realize the window if it is not yet realized
105 // so we need to call winId() here and not later from a gstreamer thread...
106 WId wid = winId();
107 qDebug() << "VideoWidget::VideoWidget - video winId :" << (gulong)wid;
108 handler = new BusSyncHandler(this, wid);
110 // init widget state (see setOverlay() for more information)
111 // setOverlay(NULL);
112 setAutoFillBackground(true);
113 setAttribute(Qt::WA_OpaquePaintEvent, false);
114 setAttribute(Qt::WA_PaintOnScreen, false);
116 // init state
117 lastError = "";
120 VideoWidget::~VideoWidget()
122 if (pipeline) {
123 dispose();
125 if (handler) {
126 delete handler;
127 handler = NULL;
131 bool VideoWidget::isPlaying()
133 return pipeline && (GST_STATE(pipeline) == GST_STATE_PLAYING);
136 QString VideoWidget::pipelineDesc()
138 return m_pipelineDesc;
141 void VideoWidget::setPipelineDesc(QString pipelineDesc)
143 qDebug() << "VideoWidget::setPipelineDesc -" << pipelineDesc;
144 stop();
145 this->m_pipelineDesc = pipelineDesc;
148 void VideoWidget::start()
150 qDebug() << "VideoWidget::start -" << m_pipelineDesc;
151 init();
152 update();
153 if (pipeline) {
154 gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
158 void VideoWidget::pause()
160 qDebug() << "VideoWidget::pause -" << m_pipelineDesc;
161 init();
162 update();
163 if (pipeline) {
164 if (GST_STATE(pipeline) == GST_STATE_PAUSED) {
165 gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
166 } else if (GST_STATE(pipeline) == GST_STATE_PLAYING) {
167 gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PAUSED);
172 void VideoWidget::stop()
174 qDebug() << "VideoWidget::stop -" << m_pipelineDesc;
175 if (pipeline) {
176 dispose();
177 } else {
178 // emit fake state change event. this is needed by the UI...
179 emit stateChanged(Pipeline::Null, Pipeline::Null, Pipeline::VoidPending);
181 update();
184 void VideoWidget::init()
186 if (pipeline) {
187 // if pipeline is already created, reset some state and return
188 qDebug() << "VideoWidget::init - reseting pipeline state :" << m_pipelineDesc;
189 lastError = "";
190 return;
193 // reset state
194 lastError = "";
196 // create pipeline
197 qDebug() << "VideoWidget::init - initializing pipeline :" << m_pipelineDesc;
198 pipeline = createPipelineFromDesc(m_pipelineDesc.toStdString().c_str(), lastError);
199 if (pipeline) {
200 gst_pipeline_set_auto_flush_bus(GST_PIPELINE(pipeline), true);
202 // register bus synchronous handler
203 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
204 gst_bus_set_sync_handler(bus, (GstBusSyncHandler)gst_bus_sync_handler, handler, NULL);
205 gst_object_unref(bus);
206 } else {
207 // emit fake state change event. this is needed by the UI...
208 emit stateChanged(Pipeline::Null, Pipeline::Null, Pipeline::VoidPending);
212 void VideoWidget::dispose()
214 qDebug() << "VideoWidget::dispose -" << m_pipelineDesc;
216 setOverlay(NULL);
218 if (pipeline) {
219 gst_element_set_state(pipeline, GST_STATE_NULL);
220 gst_object_unref(pipeline);
221 pipeline = NULL;
225 void VideoWidget::paintEvent(QPaintEvent *event)
227 if (overlay) {
228 overlay->expose();
229 } else {
230 QWidget::paintEvent(event);
231 paintStatus(event);
235 void VideoWidget::paintStatus(QPaintEvent *event)
237 Q_UNUSED(event);
239 QTextDocument doc;
240 doc.setDefaultStyleSheet("* { color:red; }");
242 QString html = "<p align=center><font size=+2>" + getStatusMessage() + "</font></p>";
244 QRect widgetRect = QWidget::rect();
245 int x = 0;
246 int w = widgetRect.width();
247 int hh = widgetRect.height() / 4;
248 int y = (widgetRect.height() - hh) / 2;
249 int h = widgetRect.height() - y;
250 QRect rect = QRect(x, y, w, h);
252 doc.setHtml(html);
253 doc.setTextWidth(rect.width());
255 QPainter painter(this);
256 painter.save();
257 painter.translate(rect.topLeft());
258 doc.drawContents(&painter, rect.translated(-rect.topLeft()));
259 painter.restore();
260 // painter.drawRect(rect);
262 // QBrush brush( Qt::yellow );
263 // painter.setBrush( brush ); // set the yellow brush
264 // painter.setPen( Qt::NoPen ); // do not draw outline
265 // painter.drawRect(0, 0, width(), height()); // draw filled rectangle
266 // painter.end();
268 // QFont font = QApplication::font();
269 // font.setPixelSize( rect.height() );
270 // painter.setFont( font );
273 QString VideoWidget::getStatus()
275 if (!lastError.isEmpty()) {
276 return "ERROR";
277 } else if (!pipeline && m_pipelineDesc.isEmpty()) {
278 return "NO PIPELINE";
280 return "";
283 QString VideoWidget::getStatusMessage()
285 if (!lastError.isEmpty()) {
286 return lastError;
287 } else if (!pipeline && m_pipelineDesc.isEmpty()) {
288 return "No pipeline";
290 return "";
293 void VideoWidget::mouseDoubleClickEvent(QMouseEvent *event)
295 Q_UNUSED(event);
298 void VideoWidget::resizeEvent(QResizeEvent *event)
300 if (overlay) {
301 overlay->expose();
302 } else {
303 QWidget::resizeEvent(event);
307 QPaintEngine *VideoWidget::paintEngine() const
309 // bypass double buffering, see setOverlay() for explanation
310 return overlay ? NULL : QWidget::paintEngine();
313 static Pipeline::State cvt(GstState state)
315 switch (state) {
316 case GST_STATE_VOID_PENDING:
317 return Pipeline::VoidPending;
319 case GST_STATE_NULL:
320 return Pipeline::Null;
322 case GST_STATE_READY:
323 return Pipeline::Ready;
325 case GST_STATE_PAUSED:
326 return Pipeline::Paused;
328 case GST_STATE_PLAYING:
329 return Pipeline::Playing;
331 return Pipeline::Null;
334 static const char *name(Pipeline::State state)
336 switch (state) {
337 case Pipeline::VoidPending:
338 return "VoidPending";
340 case Pipeline::Null:
341 return "Null";
343 case Pipeline::Ready:
344 return "Ready";
346 case Pipeline::Paused:
347 return "Paused";
349 case Pipeline::Playing:
350 return "Playing";
352 return "<unknown>";
355 // static StreamStatusEvent::StreamStatusType cvt(GstStreamStatusType type)
356 // {
357 // switch (type) {
358 // case GST_STREAM_STATUS_TYPE_CREATE:
359 // return StreamStatusEvent::Create;
361 // case GST_STREAM_STATUS_TYPE_ENTER:
362 // return StreamStatusEvent::Enter;
364 // case GST_STREAM_STATUS_TYPE_LEAVE:
365 // return StreamStatusEvent::Leave;
367 // case GST_STREAM_STATUS_TYPE_DESTROY:
368 // return StreamStatusEvent::Destroy;
370 // case GST_STREAM_STATUS_TYPE_START:
371 // return StreamStatusEvent::Start;
373 // case GST_STREAM_STATUS_TYPE_PAUSE:
374 // return StreamStatusEvent::Pause;
376 // case GST_STREAM_STATUS_TYPE_STOP:
377 // return StreamStatusEvent::Stop;
378 // }
379 // return StreamStatusEvent::Null;
380 // }
382 static ProgressEvent::ProgressType cvt(GstProgressType type)
384 switch (type) {
385 case GST_PROGRESS_TYPE_START:
386 return ProgressEvent::Start;
388 case GST_PROGRESS_TYPE_CONTINUE:
389 return ProgressEvent::Continue;
391 case GST_PROGRESS_TYPE_COMPLETE:
392 return ProgressEvent::Complete;
394 case GST_PROGRESS_TYPE_CANCELED:
395 return ProgressEvent::Cancelled;
397 case GST_PROGRESS_TYPE_ERROR:
398 return ProgressEvent::Error;
400 return ProgressEvent::Error;
403 bool VideoWidget::event(QEvent *event)
405 if (event->type() == PipelineEvent::PrepareWindowId) {
406 PrepareWindowIdEvent *pe = static_cast<PrepareWindowIdEvent *>(event);
407 // we take ownership of the overlay object
408 setOverlay(pe->getOverlay());
409 QString msg = QString("PrepareWindowId: element %0 prepare window id").arg(pe->src);
410 emitEventMessage(msg);
411 return true;
412 } else if (event->type() == PipelineEvent::StateChange) {
413 StateChangedEvent *sce = static_cast<StateChangedEvent *>(event);
414 QString msg = QString("StateChange: element %0 changed state from %1 to %2")
415 .arg(sce->src).arg(name(sce->getOldState())).arg(name(sce->getNewState()));
416 emitEventMessage(msg);
417 emit stateChanged(sce->getOldState(), sce->getNewState(), sce->getPendingState());
419 if (sce->getNewState() == Pipeline::Playing) {
420 if (pipeline) {
421 toDotFile("pipeline");
425 return true;
426 } else if (event->type() == PipelineEvent::StreamStatus) {
427 StreamStatusEvent *sse = static_cast<StreamStatusEvent *>(event);
428 QString msg = QString("StreamStatus: %0 %1 (%2)").arg(sse->src).arg(sse->getStatusName()).arg(sse->getOwner());
429 emitEventMessage(msg);
430 return true;
431 } else if (event->type() == PipelineEvent::NewClock) {
432 NewClockEvent *nce = static_cast<NewClockEvent *>(event);
433 QString msg = QString("NewClock : element %0 has new clock %1").arg(nce->src).arg(nce->getName());
434 emitEventMessage(msg);
435 return true;
436 } else if (event->type() == PipelineEvent::ClockProvide) {
437 ClockProvideEvent *cpe = static_cast<ClockProvideEvent *>(event);
438 QString msg = QString("ClockProvide: element %0 clock provide %1 ready=%2").arg(cpe->src).arg(cpe->getName()).arg(cpe->isReady());
439 emitEventMessage(msg);
440 return true;
441 } else if (event->type() == PipelineEvent::ClockLost) {
442 ClockLostEvent *cle = static_cast<ClockLostEvent *>(event);
443 QString msg = QString("ClockLost: element %0 lost clock %1").arg(cle->src).arg(cle->getName());
444 emitEventMessage(msg);
446 // PRINT ("Clock lost, selecting a new one\n");
447 // gst_element_set_state (pipeline, GST_STATE_PAUSED);
448 // gst_element_set_state (pipeline, GST_STATE_PLAYING);
450 return true;
451 } else if (event->type() == PipelineEvent::Progress) {
452 ProgressEvent *pe = static_cast<ProgressEvent *>(event);
453 QString msg = QString("Progress: element %0 sent progress event: %1 %2 (%3)").arg(pe->src).arg(pe->getProgressType()).arg(
454 pe->getCode()).arg(pe->getText());
455 emitEventMessage(msg);
456 return true;
457 } else if (event->type() == PipelineEvent::Latency) {
458 LatencyEvent *le = static_cast<LatencyEvent *>(event);
459 QString msg = QString("Latency: element %0 sent latency event").arg(le->src);
460 emitEventMessage(msg);
462 bool success = gst_bin_recalculate_latency(GST_BIN(pipeline));
463 if (!success) {
464 qWarning() << "Failed to recalculate latency";
467 return true;
468 } else if (event->type() == PipelineEvent::Qos) {
469 QosEvent *qe = static_cast<QosEvent *>(event);
470 QString msg = QString("Qos: element %0 sent QOS event: %1 %2 %3").arg(qe->src).arg(qe->getData().timestamps()).arg(
471 qe->getData().values()).arg(qe->getData().stats());
472 emitEventMessage(msg);
474 if (pipeline) {
475 toDotFile("pipeline_qos");
478 return true;
479 } else if (event->type() == PipelineEvent::Eos) {
480 QString msg = QString("Eos: element %0 sent EOS event");
481 emitEventMessage(msg);
483 if (pipeline) {
484 toDotFile("pipeline_eos");
487 return true;
488 } else if (event->type() == PipelineEvent::Error) {
489 ErrorEvent *ee = static_cast<ErrorEvent *>(event);
490 QString msg = QString("Error: element %0 sent error event: %1 (%2)").arg(ee->src).arg(ee->getMessage()).arg(
491 ee->getDebug());
492 emitEventMessage(msg);
493 if (lastError.isEmpty()) {
494 // remember first error only (usually the most useful)
495 lastError = QString("Pipeline error: %0").arg(ee->getMessage());
496 // stop pipeline...
497 stop();
498 } else {
499 // TODO record subsequent errors separately
501 return true;
502 } else if (event->type() == PipelineEvent::Warning) {
503 WarningEvent *we = static_cast<WarningEvent *>(event);
504 QString msg = QString("Warning: element %0 sent warning event: %1 (%2)").arg(we->src).arg(we->getMessage()).arg(
505 we->getDebug());
506 emitEventMessage(msg);
507 return true;
508 } else if (event->type() == PipelineEvent::Info) {
509 InfoEvent *ie = static_cast<InfoEvent *>(event);
510 QString msg = QString("Info: element %0 sent info event: %1 (%2)").arg(ie->src).arg(ie->getMessage()).arg(
511 ie->getDebug());
512 emitEventMessage(msg);
513 return true;
515 return QWidget::event(event);
518 void VideoWidget::emitEventMessage(QString msg)
520 // qDebug() << "VideoWidget::event -" << msg;
521 emit message(msg);
524 void VideoWidget::setOverlay(Overlay *overlay)
526 if (this->overlay != overlay) {
527 Overlay *oldOverlay = this->overlay;
528 this->overlay = overlay;
529 if (oldOverlay) {
530 delete oldOverlay;
534 bool hasOverlay = overlay ? true : false;
536 setAutoFillBackground(!hasOverlay);
538 // disable background painting to avoid flickering when resizing
539 setAttribute(Qt::WA_OpaquePaintEvent, hasOverlay);
540 // setAttribute(Qt::WA_NoSystemBackground, hasOverlay); // not sure it is needed
542 // disable double buffering to avoid flickering when resizing
543 // for this to work we also need to override paintEngine() and make it return NULL.
544 // see http://qt-project.org/faq/answer/how_does_qtwa_paintonscreen_relate_to_the_backing_store_widget_composition_
545 // drawback is that this widget won't participate in composition...
546 setAttribute(Qt::WA_PaintOnScreen, hasOverlay);
550 void VideoWidget::toDotFile(QString name)
552 if (!pipeline) {
553 return;
555 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_VERBOSE, name.toStdString().c_str());
558 static GstElement *createPipelineFromDesc(const char *desc, QString &lastError)
560 qDebug() << "VideoWidget::createPipelineFromDesc - creating pipeline :" << desc;
561 GError *error = NULL;
562 GstElement *pipeline = gst_parse_launch_full(desc, NULL, GST_PARSE_FLAG_FATAL_ERRORS, &error);
563 if (!pipeline) {
564 if (error) {
565 // no pipeline and error...
566 // report error to user
567 QString msg = QString("Failed to create pipeline: %0").arg(error->message);
568 qCritical() << "VideoWidget::createPipelineFromDesc -" << msg;
569 lastError = msg;
570 } else {
571 // no pipeline and no error...
572 // report generic error
573 QString msg = QString("Failed to create pipeline (no error reported!)");
574 qCritical() << "VideoWidget::createPipelineFromDesc -" << msg;
575 lastError = msg;
577 } else if (error) {
578 // pipeline and error...
579 // report error to user?
580 // warning?
581 QString msg = QString("Created pipeline with error: %0").arg(error->message);
582 qWarning() << "VideoWidget::createPipelineFromDesc -" << msg;
583 } else {
584 // qDebug() << gst_bin_get_by_name(GST_BIN(pipeline), "videotestsrc0");
586 if (error) {
587 g_error_free(error);
589 return pipeline;
592 bool BusSyncHandler::handleMessage(GstMessage *message)
594 // this method is called by gstreamer as a callback
595 // and as such is not necessarily called on the QT event handling thread
597 bool handled = false;
599 switch (GST_MESSAGE_TYPE(message)) {
600 case GST_MESSAGE_ELEMENT:
602 if (gst_is_video_overlay_prepare_window_handle_message(message)) {
603 qDebug().noquote() << QString("VideoWidget::handleMessage - element %0 prepare window with id #%1").arg(GST_OBJECT_NAME(message->src)).arg((gulong)wId);
604 // prepare-xwindow-id must be handled synchronously in order to have gstreamer use our window
605 GstVideoOverlay *gst_video_overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message));
606 gst_video_overlay_set_window_handle(gst_video_overlay, (gulong)wId);
607 // and now post event asynchronously
608 Overlay *overlay = new GstOverlayImpl(gst_video_overlay);
609 QString src(GST_OBJECT_NAME(message->src));
610 QCoreApplication::postEvent(widget, new PrepareWindowIdEvent(src, overlay));
611 // notify that the message was handled
612 handled = true;
614 break;
616 case GST_MESSAGE_STATE_CHANGED:
618 if (GST_IS_PIPELINE(message->src)) {
619 GstState old_state, new_state, pending_state;
620 gst_message_parse_state_changed(message, &old_state, &new_state, &pending_state);
622 QString src(GST_OBJECT_NAME(message->src));
623 QCoreApplication::postEvent(widget, new StateChangedEvent(src, cvt(old_state), cvt(new_state), cvt(pending_state)));
625 break;
627 case GST_MESSAGE_STREAM_STATUS:
629 GstStreamStatusType type;
630 GstElement *owner;
631 gst_message_parse_stream_status(message, &type, &owner);
633 // QString src(GST_OBJECT_NAME(message->src));
634 // QString name(GST_OBJECT_NAME(owner));
635 // QCoreApplication::postEvent(widget, new StreamStatusEvent(src, cvt(type), name));
636 break;
638 case GST_MESSAGE_NEW_CLOCK:
640 if (GST_IS_PIPELINE(message->src)) {
641 GstClock *clock;
643 gst_message_parse_new_clock(message, &clock);
645 QString src(GST_OBJECT_NAME(message->src));
646 QString name(GST_OBJECT_NAME(clock));
647 QCoreApplication::postEvent(widget, new NewClockEvent(src, name));
649 break;
651 case GST_MESSAGE_CLOCK_PROVIDE:
653 if (GST_IS_PIPELINE(message->src)) {
654 GstClock *clock;
655 gboolean ready;
657 gst_message_parse_clock_provide(message, &clock, &ready);
659 QString src(GST_OBJECT_NAME(message->src));
660 QString name(GST_OBJECT_NAME(clock));
661 QCoreApplication::postEvent(widget, new ClockProvideEvent(src, name, ready));
663 break;
665 case GST_MESSAGE_CLOCK_LOST:
667 if (GST_IS_PIPELINE(message->src)) {
668 GstClock *clock;
670 gst_message_parse_clock_lost(message, &clock);
672 QString src(GST_OBJECT_NAME(message->src));
673 QString name(GST_OBJECT_NAME(clock));
674 QCoreApplication::postEvent(widget, new ClockLostEvent(src, name));
676 break;
678 case GST_MESSAGE_PROGRESS:
680 GstProgressType type;
681 gchar *code;
682 gchar *text;
683 gst_message_parse_progress(message, &type, &code, &text);
685 QString src(GST_OBJECT_NAME(message->src));
686 QCoreApplication::postEvent(widget, new ProgressEvent(src, cvt(type), QString(code), QString(text)));
688 g_free(code);
689 g_free(text);
690 break;
692 case GST_MESSAGE_SEGMENT_START:
694 GstFormat format;
695 gint64 position;
696 gst_message_parse_segment_start(message, &format, &position);
698 // QString src(GST_OBJECT_NAME(message->src));
699 // QCoreApplication::postEvent(widget, new InfoEvent(src, QString("Segment start %0").arg(position), ""));
701 break;
703 case GST_MESSAGE_SEGMENT_DONE:
705 GstFormat format;
706 gint64 position;
707 gst_message_parse_segment_done(message, &format, &position);
709 QString src(GST_OBJECT_NAME(message->src));
710 QCoreApplication::postEvent(widget, new InfoEvent(src, QString("Segment done %0").arg(position), ""));
712 break;
714 case GST_MESSAGE_LATENCY:
716 QString src(GST_OBJECT_NAME(message->src));
717 QCoreApplication::postEvent(widget, new LatencyEvent(src));
718 break;
720 case GST_MESSAGE_BUFFERING:
722 gint percent;
723 gst_message_parse_buffering(message, &percent);
725 QString src(GST_OBJECT_NAME(message->src));
726 QCoreApplication::postEvent(widget, new InfoEvent(src, QString("%0%").arg(percent), ""));
728 break;
730 case GST_MESSAGE_QOS:
732 QosData data;
733 gboolean live;
734 guint64 running_time;
735 guint64 stream_time;
736 guint64 timestamp;
737 guint64 duration;
738 gst_message_parse_qos(message, &live, &running_time, &stream_time, &timestamp, &duration);
739 data.live = (live == true);
740 data.running_time = running_time;
741 data.stream_time = stream_time;
742 data.timestamp = timestamp;
743 data.duration = duration;
745 gint64 jitter;
746 gdouble proportion;
747 gint quality;
748 gst_message_parse_qos_values(message, &jitter, &proportion, &quality);
749 data.jitter = jitter;
750 data.proportion = proportion;
751 data.quality = quality;
753 guint64 processed;
754 guint64 dropped;
755 gst_message_parse_qos_stats(message, NULL, &processed, &dropped);
756 data.processed = processed;
757 data.dropped = dropped;
759 QString src(GST_OBJECT_NAME(message->src));
760 QCoreApplication::postEvent(widget, new QosEvent(src, data));
762 break;
764 case GST_MESSAGE_EOS:
766 /* end-of-stream */
767 // g_main_loop_quit (loop);
769 QString src(GST_OBJECT_NAME(message->src));
770 QCoreApplication::postEvent(widget, new EosEvent(src));
772 break;
774 case GST_MESSAGE_ERROR:
776 GError *err;
777 gchar *debug;
778 gst_message_parse_error(message, &err, &debug);
780 QString src(GST_OBJECT_NAME(message->src));
781 QCoreApplication::postEvent(widget, new ErrorEvent(src, QString(err->message), QString(debug)));
783 g_error_free(err);
784 g_free(debug);
785 break;
787 case GST_MESSAGE_WARNING:
789 GError *err;
790 gchar *debug;
791 gst_message_parse_warning(message, &err, &debug);
793 QString src(GST_OBJECT_NAME(message->src));
794 QCoreApplication::postEvent(widget, new WarningEvent(src, QString(err->message), QString(debug)));
796 g_error_free(err);
797 g_free(debug);
798 break;
800 case GST_MESSAGE_INFO:
802 GError *err;
803 gchar *debug;
804 gst_message_parse_info(message, &err, &debug);
806 QString src(GST_OBJECT_NAME(message->src));
807 QCoreApplication::postEvent(widget, new InfoEvent(src, QString(err->message), QString(debug)));
809 g_error_free(err);
810 g_free(debug);
811 break;
813 case GST_MESSAGE_TAG:
815 GstTagList *tags = NULL;
816 gst_message_parse_tag(message, &tags);
818 // QString src(GST_OBJECT_NAME(message->src));
819 // QCoreApplication::postEvent(widget, new InfoEvent(src, QString(err->message), QString(debug)));
821 gst_tag_list_unref(tags);
822 break;
824 default:
826 // const GstStructure *s;
827 // const gchar *name;
829 // s = gst_message_get_structure (message);
831 // name = gst_structure_get_name(s);
833 QString src(GST_OBJECT_NAME(message->src));
834 QCoreApplication::postEvent(widget, new InfoEvent(src, "Unhandled message", QString("%0").arg(GST_MESSAGE_TYPE_NAME(message))));
836 break;
839 return handled;
842 static GstBusSyncReply gst_bus_sync_handler(GstBus *bus, GstMessage *message, BusSyncHandler *handler)
844 Q_UNUSED(bus);
845 // qDebug().noquote() << QString("VideoWidget::gst_bus_sync_handler (%0) : %1 : %2")
846 // .arg((long)QThread::currentThreadId())
847 // .arg(GST_MESSAGE_SRC_NAME(message))
848 // .arg(GST_MESSAGE_TYPE_NAME(message));
849 if (handler->handleMessage(message)) {
850 gst_message_unref(message);
851 return GST_BUS_DROP;
853 return GST_BUS_PASS;