2 ******************************************************************************
4 * @file videowidget.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2017.
7 * @see The GNU Public License (GPL) Version 3
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
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"
31 #include "pipelineevent.h"
32 // #include "devicemonitor.h"
35 #include <gst/video/videooverlay.h>
41 #include <QTextDocument>
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
{
52 GstOverlayImpl(GstVideoOverlay
*gst_overlay
) :
53 gst_overlay(gst_overlay
)
58 gst_video_overlay_expose(gst_overlay
);
62 GstVideoOverlay
*gst_overlay
;
65 class BusSyncHandler
{
67 BusSyncHandler(VideoWidget
*widget
, WId wid
) :
68 widget(widget
), wId(wid
)
70 bool handleMessage(GstMessage
*msg
);
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();
92 // make the widget native so it gets its own native window id that we will pass to gstreamer
93 setAttribute(Qt::WA_NativeWindow
);
95 // WA_DontCreateNativeAncestors is needed on mac
96 setAttribute(Qt::WA_DontCreateNativeAncestors
);
99 // set black background
100 QPalette
pal(palette());
101 pal
.setColor(backgroundRole(), Qt::black
);
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...
107 qDebug() << "VideoWidget::VideoWidget - video winId :" << (gulong
)wid
;
108 handler
= new BusSyncHandler(this, wid
);
110 // init widget state (see setOverlay() for more information)
112 setAutoFillBackground(true);
113 setAttribute(Qt::WA_OpaquePaintEvent
, false);
114 setAttribute(Qt::WA_PaintOnScreen
, false);
120 VideoWidget::~VideoWidget()
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
;
145 this->m_pipelineDesc
= pipelineDesc
;
148 void VideoWidget::start()
150 qDebug() << "VideoWidget::start -" << m_pipelineDesc
;
154 gst_element_set_state(GST_ELEMENT(pipeline
), GST_STATE_PLAYING
);
158 void VideoWidget::pause()
160 qDebug() << "VideoWidget::pause -" << m_pipelineDesc
;
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
;
178 // emit fake state change event. this is needed by the UI...
179 emit
stateChanged(Pipeline::Null
, Pipeline::Null
, Pipeline::VoidPending
);
184 void VideoWidget::init()
187 // if pipeline is already created, reset some state and return
188 qDebug() << "VideoWidget::init - reseting pipeline state :" << m_pipelineDesc
;
197 qDebug() << "VideoWidget::init - initializing pipeline :" << m_pipelineDesc
;
198 pipeline
= createPipelineFromDesc(m_pipelineDesc
.toStdString().c_str(), lastError
);
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
);
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
;
219 gst_element_set_state(pipeline
, GST_STATE_NULL
);
220 gst_object_unref(pipeline
);
225 void VideoWidget::paintEvent(QPaintEvent
*event
)
230 QWidget::paintEvent(event
);
235 void VideoWidget::paintStatus(QPaintEvent
*event
)
240 doc
.setDefaultStyleSheet("* { color:red; }");
242 QString html
= "<p align=center><font size=+2>" + getStatusMessage() + "</font></p>";
244 QRect widgetRect
= QWidget::rect();
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
);
253 doc
.setTextWidth(rect
.width());
255 QPainter
painter(this);
257 painter
.translate(rect
.topLeft());
258 doc
.drawContents(&painter
, rect
.translated(-rect
.topLeft()));
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
268 // QFont font = QApplication::font();
269 // font.setPixelSize( rect.height() );
270 // painter.setFont( font );
273 QString
VideoWidget::getStatus()
275 if (!lastError
.isEmpty()) {
277 } else if (!pipeline
&& m_pipelineDesc
.isEmpty()) {
278 return "NO PIPELINE";
283 QString
VideoWidget::getStatusMessage()
285 if (!lastError
.isEmpty()) {
287 } else if (!pipeline
&& m_pipelineDesc
.isEmpty()) {
288 return "No pipeline";
293 void VideoWidget::mouseDoubleClickEvent(QMouseEvent
*event
)
298 void VideoWidget::resizeEvent(QResizeEvent
*event
)
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
)
316 case GST_STATE_VOID_PENDING
:
317 return Pipeline::VoidPending
;
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
)
337 case Pipeline::VoidPending
:
338 return "VoidPending";
343 case Pipeline::Ready
:
346 case Pipeline::Paused
:
349 case Pipeline::Playing
:
355 // static StreamStatusEvent::StreamStatusType cvt(GstStreamStatusType 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;
379 // return StreamStatusEvent::Null;
382 static ProgressEvent::ProgressType
cvt(GstProgressType 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
);
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
) {
421 toDotFile("pipeline");
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
);
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
);
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
);
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);
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
);
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
));
464 qWarning() << "Failed to recalculate latency";
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
);
475 toDotFile("pipeline_qos");
479 } else if (event
->type() == PipelineEvent::Eos
) {
480 QString msg
= QString("Eos: element %0 sent EOS event");
481 emitEventMessage(msg
);
484 toDotFile("pipeline_eos");
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(
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());
499 // TODO record subsequent errors separately
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(
506 emitEventMessage(msg
);
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(
512 emitEventMessage(msg
);
515 return QWidget::event(event
);
518 void VideoWidget::emitEventMessage(QString msg
)
520 // qDebug() << "VideoWidget::event -" << msg;
524 void VideoWidget::setOverlay(Overlay
*overlay
)
526 if (this->overlay
!= overlay
) {
527 Overlay
*oldOverlay
= this->overlay
;
528 this->overlay
= overlay
;
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
)
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
);
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
;
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
;
578 // pipeline and error...
579 // report error to user?
581 QString msg
= QString("Created pipeline with error: %0").arg(error
->message
);
582 qWarning() << "VideoWidget::createPipelineFromDesc -" << msg
;
584 // qDebug() << gst_bin_get_by_name(GST_BIN(pipeline), "videotestsrc0");
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
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
)));
627 case GST_MESSAGE_STREAM_STATUS
:
629 GstStreamStatusType type
;
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));
638 case GST_MESSAGE_NEW_CLOCK
:
640 if (GST_IS_PIPELINE(message
->src
)) {
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
));
651 case GST_MESSAGE_CLOCK_PROVIDE
:
653 if (GST_IS_PIPELINE(message
->src
)) {
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
));
665 case GST_MESSAGE_CLOCK_LOST
:
667 if (GST_IS_PIPELINE(message
->src
)) {
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
));
678 case GST_MESSAGE_PROGRESS
:
680 GstProgressType type
;
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
)));
692 case GST_MESSAGE_SEGMENT_START
:
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), ""));
703 case GST_MESSAGE_SEGMENT_DONE
:
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
), ""));
714 case GST_MESSAGE_LATENCY
:
716 QString
src(GST_OBJECT_NAME(message
->src
));
717 QCoreApplication::postEvent(widget
, new LatencyEvent(src
));
720 case GST_MESSAGE_BUFFERING
:
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
), ""));
730 case GST_MESSAGE_QOS
:
734 guint64 running_time
;
738 gst_message_parse_qos(message
, &live
, &running_time
, &stream_time
, ×tamp
, &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
;
748 gst_message_parse_qos_values(message
, &jitter
, &proportion
, &quality
);
749 data
.jitter
= jitter
;
750 data
.proportion
= proportion
;
751 data
.quality
= quality
;
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
));
764 case GST_MESSAGE_EOS
:
767 // g_main_loop_quit (loop);
769 QString
src(GST_OBJECT_NAME(message
->src
));
770 QCoreApplication::postEvent(widget
, new EosEvent(src
));
774 case GST_MESSAGE_ERROR
:
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
)));
787 case GST_MESSAGE_WARNING
:
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
)));
800 case GST_MESSAGE_INFO
:
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
)));
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
);
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
))));
842 static GstBusSyncReply
gst_bus_sync_handler(GstBus
*bus
, GstMessage
*message
, BusSyncHandler
*handler
)
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
);