15#include <QtCore/QDateTime>
17#include <QtQuick/QQuickItem>
30 (void) connect(&_watchdogTimer, &QTimer::timeout,
this, &GstVideoReceiver::_watchdog);
31 _watchdogTimer.start(1000);
44 if (_needDispatch()) {
50 qCDebug(GstVideoReceiverLog) <<
"Already running!" <<
_uri;
56 qCDebug(GstVideoReceiverLog) <<
"Failed because URI is not specified";
64 qCDebug(GstVideoReceiverLog) <<
"Starting" <<
_uri <<
", lowLatency" <<
lowLatency() <<
", timeout" <<
_timeout;
69 bool pipelineUp =
false;
75 _tee = gst_element_factory_make(
"tee",
nullptr);
77 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('tee') failed";
81 GstPad *pad = gst_element_get_static_pad(_tee,
"sink");
83 qCCritical(GstVideoReceiverLog) <<
"gst_element_get_static_pad() failed";
89 _teeProbeId = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, _teeProbe,
this,
nullptr);
90 gst_clear_object(&pad);
92 decoderQueue = gst_element_factory_make(
"queue",
nullptr);
94 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('queue') failed";
98 _decoderValve = gst_element_factory_make(
"valve",
nullptr);
100 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('valve') failed";
104 g_object_set(_decoderValve,
108 recorderQueue = gst_element_factory_make(
"queue",
nullptr);
109 if (!recorderQueue) {
110 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('queue') failed";
114 _recorderValve = gst_element_factory_make(
"valve",
nullptr);
115 if (!_recorderValve) {
116 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('valve') failed";
120 g_object_set(_recorderValve,
124 _pipeline = gst_pipeline_new(
"receiver");
126 qCCritical(GstVideoReceiverLog) <<
"gst_pipeline_new() failed";
130 g_object_set(_pipeline,
131 "message-forward", TRUE,
134 _source = _makeSource(
_uri);
136 qCCritical(GstVideoReceiverLog) <<
"_makeSource() failed";
140 gst_bin_add_many(GST_BIN(_pipeline), _source, _tee, decoderQueue, _decoderValve, recorderQueue, _recorderValve,
nullptr);
144 GstPad *srcPad =
nullptr;
145 GstIterator *it = gst_element_iterate_src_pads(_source);
146 GValue vpad = G_VALUE_INIT;
147 switch (gst_iterator_next(it, &vpad)) {
148 case GST_ITERATOR_OK:
149 srcPad = GST_PAD(g_value_get_object(&vpad));
150 (void) gst_object_ref(srcPad);
151 (void) g_value_reset(&vpad);
153 case GST_ITERATOR_RESYNC:
154 gst_iterator_resync(it);
159 g_value_unset(&vpad);
160 gst_iterator_free(it);
163 _onNewSourcePad(srcPad);
164 gst_clear_object(&srcPad);
166 (void) g_signal_connect(_source,
"pad-added", G_CALLBACK(_onNewPad),
this);
169 if (!gst_element_link_many(_tee, decoderQueue, _decoderValve,
nullptr)) {
170 qCCritical(GstVideoReceiverLog) <<
"Unable to link decoder queue";
174 if (!gst_element_link_many(_tee, recorderQueue, _recorderValve,
nullptr)) {
175 qCCritical(GstVideoReceiverLog) <<
"Unable to link recorder queue";
179 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline));
181 gst_bus_enable_sync_message_emission(bus);
182 (void) g_signal_connect(bus,
"sync-message", G_CALLBACK(_onBusMessage),
this);
183 gst_clear_object(&bus);
186 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-initial");
187 running = (gst_element_set_state(_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE);
191 qCCritical(GstVideoReceiverLog) <<
"Failed";
194 (void) gst_element_set_state(_pipeline, GST_STATE_NULL);
195 gst_clear_object(&_pipeline);
199 gst_clear_object(&_recorderValve);
200 gst_clear_object(&recorderQueue);
201 gst_clear_object(&_decoderValve);
202 gst_clear_object(&decoderQueue);
203 gst_clear_object(&_tee);
204 gst_clear_object(&_source);
211 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-started");
212 qCDebug(GstVideoReceiverLog) <<
"Started" <<
_uri;
220 if (_needDispatch()) {
225 if (
_uri.isEmpty()) {
226 qCWarning(GstVideoReceiverLog) <<
"Stop called on empty URI";
230 qCDebug(GstVideoReceiverLog) <<
"Stopping" <<
_uri;
232 if (_teeProbeId != 0) {
234 GstPad *sinkpad = gst_element_get_static_pad(_tee,
"sink");
236 gst_pad_remove_probe(sinkpad, _teeProbeId);
237 gst_clear_object(&sinkpad);
244 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline));
246 gst_bus_disable_sync_message_emission(bus);
247 (void) g_signal_handlers_disconnect_by_data(bus,
this);
249 gboolean recordingValveClosed = TRUE;
250 g_object_get(_recorderValve,
"drop", &recordingValveClosed,
nullptr);
252 if (!recordingValveClosed) {
253 (void) gst_element_send_event(_pipeline, gst_event_new_eos());
255 GstMessage *msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_EOS | GST_MESSAGE_ERROR));
257 switch (GST_MESSAGE_TYPE(msg)) {
258 case GST_MESSAGE_EOS:
259 qCDebug(GstVideoReceiverLog) <<
"End of stream received!";
261 case GST_MESSAGE_ERROR:
262 qCCritical(GstVideoReceiverLog) <<
"Error stopping pipeline!";
268 gst_clear_message(&msg);
270 qCCritical(GstVideoReceiverLog) <<
"gst_bus_timed_pop_filtered() failed";
274 gst_clear_object(&bus);
276 qCCritical(GstVideoReceiverLog) <<
"gst_pipeline_get_bus() failed";
279 (void) gst_element_set_state(_pipeline, GST_STATE_NULL);
283 _shutdownRecordingBranch();
287 _shutdownDecodingBranch();
290 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-stopped");
292 gst_clear_object(&_pipeline);
295 _recorderValve =
nullptr;
296 _decoderValve =
nullptr;
304 qCDebug(GstVideoReceiverLog) <<
"Streaming stopped" <<
_uri;
307 qCDebug(GstVideoReceiverLog) <<
"Streaming did not start" <<
_uri;
311 qCDebug(GstVideoReceiverLog) <<
"Stopped" <<
_uri;
319 qCCritical(GstVideoReceiverLog) <<
"VideoSink is NULL" <<
_uri;
323 if (_needDispatch()) {
328 qCDebug(GstVideoReceiverLog) <<
"Starting decoding" <<
_uri;
331 qCDebug(GstVideoReceiverLog) <<
"Video Widget is NULL" <<
_uri;
337 gst_clear_object(&_videoSink);
341 qCDebug(GstVideoReceiverLog) <<
"Already decoding!" <<
_uri;
347 GstPad *pad = gst_element_get_static_pad(videoSink,
"sink");
349 qCCritical(GstVideoReceiverLog) <<
"Unable to find sink pad of video sink" <<
_uri;
357 _videoSinkProbeId = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_BUFFER, _videoSinkProbe,
this,
nullptr);
358 gst_clear_object(&pad);
360 _videoSink = videoSink;
361 gst_object_ref(_videoSink);
370 if (!_addDecoder(_decoderValve)) {
371 qCCritical(GstVideoReceiverLog) <<
"_addDecoder() failed" <<
_uri;
376 g_object_set(_decoderValve,
380 qCDebug(GstVideoReceiverLog) <<
"Decoding started" <<
_uri;
387 if (_needDispatch()) {
392 qCDebug(GstVideoReceiverLog) <<
"Stopping decoding" <<
_uri;
395 qCDebug(GstVideoReceiverLog) <<
"Not decoding!" <<
_uri;
400 g_object_set(_decoderValve,
406 const bool ret = _unlinkBranch(_decoderValve);
415 if (_needDispatch()) {
416 const QString cachedVideoFile = videoFile;
421 qCDebug(GstVideoReceiverLog) <<
"Starting recording" <<
_uri;
424 qCDebug(GstVideoReceiverLog) <<
"Streaming is not active!" <<
_uri;
430 qCDebug(GstVideoReceiverLog) <<
"Already recording!" <<
_uri;
435 qCDebug(GstVideoReceiverLog) <<
"New video file:" << videoFile <<
_uri;
437 _fileSink = _makeFileSink(videoFile, format);
439 qCCritical(GstVideoReceiverLog) <<
"_makeFileSink() failed" <<
_uri;
446 (void) gst_object_ref(_fileSink);
448 gst_bin_add(GST_BIN(_pipeline), _fileSink);
450 if (!gst_element_link(_recorderValve, _fileSink)) {
451 qCCritical(GstVideoReceiverLog) <<
"Failed to link valve and file sink" <<
_uri;
456 (void) gst_element_sync_state_with_parent(_fileSink);
458 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-with-filesink");
463 GstPad *probepad = gst_element_get_static_pad(_recorderValve,
"src");
465 qCCritical(GstVideoReceiverLog) <<
"gst_element_get_static_pad() failed" <<
_uri;
470 (void) gst_pad_add_probe(probepad, GST_PAD_PROBE_TYPE_BUFFER, _keyframeWatch,
this,
nullptr);
471 gst_clear_object(&probepad);
473 g_object_set(_recorderValve,
479 qCDebug(GstVideoReceiverLog) <<
"Recording started" <<
_uri;
480 _dispatchSignal([
this]() {
488 if (_needDispatch()) {
493 qCDebug(GstVideoReceiverLog) <<
"Stopping recording" <<
_uri;
496 qCDebug(GstVideoReceiverLog) <<
"Not recording!" <<
_uri;
501 g_object_set(_recorderValve,
507 const bool ret = _unlinkBranch(_recorderValve);
516 if (_needDispatch()) {
517 const QString cachedImageFile = imageFile;
522 qCDebug(GstVideoReceiverLog) <<
"taking screenshot" <<
_uri;
528void GstVideoReceiver::_watchdog()
535 const qint64 now = QDateTime::currentSecsSinceEpoch();
542 qCDebug(GstVideoReceiverLog) <<
"Stream timeout, no frames for" << elapsed <<
_uri;
543 _dispatchSignal([
this]() { emit
timeout(); });
554 qCDebug(GstVideoReceiverLog) <<
"Video decoder timeout, no frames for" << elapsed <<
_uri;
555 _dispatchSignal([
this]() { emit
timeout(); });
562void GstVideoReceiver::_handleEOS()
571 _shutdownDecodingBranch();
573 _shutdownRecordingBranch();
580gboolean GstVideoReceiver::_filterParserCaps(
GstElement *bin, GstPad *pad,
GstElement *element, GstQuery *query, gpointer data)
582 Q_UNUSED(bin); Q_UNUSED(pad); Q_UNUSED(element); Q_UNUSED(data)
584 if (GST_QUERY_TYPE(query) != GST_QUERY_CAPS) {
589 gst_query_parse_caps(query, &srcCaps);
590 if (!srcCaps || gst_caps_is_any(srcCaps)) {
594 GstCaps *sinkCaps =
nullptr;
595 GstCaps *filter =
nullptr;
596 GstStructure *structure = gst_caps_get_structure(srcCaps, 0);
597 if (gst_structure_has_name(structure,
"video/x-h265")) {
598 filter = gst_caps_from_string(
"video/x-h265");
599 if (gst_caps_can_intersect(srcCaps, filter)) {
600 sinkCaps = gst_caps_from_string(
"video/x-h265,stream-format=hvc1");
602 gst_clear_caps(&filter);
603 }
else if (gst_structure_has_name(structure,
"video/x-h264")) {
604 filter = gst_caps_from_string(
"video/x-h264");
605 if (gst_caps_can_intersect(srcCaps, filter)) {
606 sinkCaps = gst_caps_from_string(
"video/x-h264,stream-format=avc");
608 gst_clear_caps(&filter);
612 gst_query_set_caps_result(query, sinkCaps);
613 gst_clear_caps(&sinkCaps);
620GstElement *GstVideoReceiver::_makeSource(
const QString &input)
622 if (input.isEmpty()) {
623 qCCritical(GstVideoReceiverLog) <<
"Failed because URI is not specified";
627 const QUrl sourceUrl(input);
629 const bool isRtsp = sourceUrl.scheme().startsWith(
"rtsp", Qt::CaseInsensitive);
630 const bool isUdp264 = input.contains(
"udp://", Qt::CaseInsensitive);
631 const bool isUdp265 = input.contains(
"udp265://", Qt::CaseInsensitive);
632 const bool isUdpMPEGTS = input.contains(
"mpegts://", Qt::CaseInsensitive);
633 const bool isTcpMPEGTS = input.contains(
"tcp://", Qt::CaseInsensitive);
645 qCCritical(GstVideoReceiverLog) <<
"Invalid RTSP URI:" << input;
649 source = gst_element_factory_make(
"rtspsrc",
"source");
651 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('rtspsrc') failed";
656 "location", input.toUtf8().constData(),
659 }
else if (isTcpMPEGTS) {
660 source = gst_element_factory_make(
"tcpclientsrc",
"source");
662 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('tcpclientsrc') failed";
666 const QString host = sourceUrl.host();
667 const quint16 port = sourceUrl.port();
669 "host", host.toUtf8().constData(),
672 }
else if (isUdp264 || isUdp265 || isUdpMPEGTS) {
673 source = gst_element_factory_make(
"udpsrc",
"source");
675 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('udpsrc') failed";
679 const QString
uri = QStringLiteral(
"udp://%1:%2").arg(sourceUrl.host(), QString::number(sourceUrl.port()));
681 "uri",
uri.toUtf8().constData(),
684 GstCaps *caps =
nullptr;
686 caps = gst_caps_from_string(
"application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264");
688 qCCritical(GstVideoReceiverLog) <<
"gst_caps_from_string() failed";
691 }
else if (isUdp265) {
692 caps = gst_caps_from_string(
"application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H265");
694 qCCritical(GstVideoReceiverLog) <<
"gst_caps_from_string() failed";
703 gst_clear_caps(&caps);
706 qCDebug(GstVideoReceiverLog) <<
"URI is not recognized";
710 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make() for data source failed";
714 bin = gst_bin_new(
"sourcebin");
716 qCCritical(GstVideoReceiverLog) <<
"gst_bin_new('sourcebin') failed";
720 parser = gst_element_factory_make(
"parsebin",
"parser");
722 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('parsebin') failed";
726 (void) g_signal_connect(parser,
"autoplug-query", G_CALLBACK(_filterParserCaps),
nullptr);
728 gst_bin_add_many(GST_BIN(bin), source, parser,
nullptr);
732 if (isTcpMPEGTS || isUdpMPEGTS) {
733 tsdemux = gst_element_factory_make(
"tsdemux",
nullptr);
735 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('tsdemux') failed";
739 (void) gst_bin_add(GST_BIN(bin), tsdemux);
741 if (!gst_element_link(source, tsdemux)) {
742 qCCritical(GstVideoReceiverLog) <<
"gst_element_link() failed";
751 (void) gst_element_foreach_src_pad(source, _padProbe, &probeRes);
754 if ((probeRes & 2) && (
_buffer >= 0)) {
755 buffer = gst_element_factory_make(
"rtpjitterbuffer",
nullptr);
757 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('rtpjitterbuffer') failed";
761 (void) gst_bin_add(GST_BIN(bin), buffer);
763 if (!gst_element_link_many(source, buffer, parser,
nullptr)) {
764 qCCritical(GstVideoReceiverLog) <<
"gst_element_link() failed";
768 if (!gst_element_link(source, parser)) {
769 qCCritical(GstVideoReceiverLog) <<
"gst_element_link() failed";
774 (void) g_signal_connect(source,
"pad-added", G_CALLBACK(_linkPad), parser);
777 (void) g_signal_connect(parser,
"pad-added", G_CALLBACK(_wrapWithGhostPad),
nullptr);
779 source = tsdemux = buffer = parser =
nullptr;
785 gst_clear_object(&bin);
786 gst_clear_object(&parser);
787 gst_clear_object(&tsdemux);
788 gst_clear_object(&buffer);
789 gst_clear_object(&source);
796 Q_UNUSED(caps); Q_UNUSED(videoSink)
798 GstElement *decoder = gst_element_factory_make(
"decodebin3",
nullptr);
800 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('decodebin3') failed";
806GstElement *GstVideoReceiver::_makeFileSink(
const QString &videoFile, FILE_FORMAT format)
812 bool releaseElements =
true;
816 qCCritical(GstVideoReceiverLog) <<
"Unsupported file format";
820 mux = gst_element_factory_make(_kFileMux[format],
nullptr);
822 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('" << _kFileMux[format] <<
"') failed";
826 sink = gst_element_factory_make(
"filesink",
nullptr);
828 qCCritical(GstVideoReceiverLog) <<
"gst_element_factory_make('filesink') failed";
833 "location", qPrintable(videoFile),
836 bin = gst_bin_new(
"sinkbin");
838 qCCritical(GstVideoReceiverLog) <<
"gst_bin_new('sinkbin') failed";
842 GstPadTemplate *padTemplate = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(mux),
"video_%u");
844 qCCritical(GstVideoReceiverLog) <<
"gst_element_class_get_pad_template(mux) failed";
849 GstPad *pad = gst_element_request_pad(mux, padTemplate,
nullptr,
nullptr);
851 qCCritical(GstVideoReceiverLog) <<
"gst_element_request_pad(mux) failed";
855 gst_bin_add_many(GST_BIN(bin), mux,
sink,
nullptr);
857 releaseElements =
false;
859 GstPad *ghostpad = gst_ghost_pad_new(
"sink", pad);
860 (void) gst_element_add_pad(bin, ghostpad);
861 gst_clear_object(&pad);
863 if (!gst_element_link(mux,
sink)) {
864 qCCritical(GstVideoReceiverLog) <<
"gst_element_link() failed";
872 if (releaseElements) {
873 gst_clear_object(&
sink);
874 gst_clear_object(&mux);
877 gst_clear_object(&bin);
881void GstVideoReceiver::_onNewSourcePad(GstPad *pad)
884 if (!gst_element_link(_source, _tee)) {
885 qCCritical(GstVideoReceiverLog) <<
"Unable to link source";
891 qCDebug(GstVideoReceiverLog) <<
"Streaming started" <<
_uri;
895 (void) gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, _eosProbe,
this,
nullptr);
900 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-with-new-source-pad");
902 if (!_addDecoder(_decoderValve)) {
903 qCCritical(GstVideoReceiverLog) <<
"_addDecoder() failed";
907 g_object_set(_decoderValve,
911 qCDebug(GstVideoReceiverLog) <<
"Decoding started" <<
_uri;
914void GstVideoReceiver::_logDecodebin3SelectedCodec(
GstElement *decodebin3)
916 GValue value = G_VALUE_INIT;
917 GstIterator *iter = gst_bin_iterate_elements(GST_BIN(decodebin3));
920 while (gst_iterator_next(iter, &value) == GST_ITERATOR_OK) {
921 child = GST_ELEMENT(g_value_get_object(&value));
922 GstElementFactory *factory = gst_element_get_factory(child);
925 gboolean is_decoder = gst_element_factory_list_is_type(factory, GST_ELEMENT_FACTORY_TYPE_DECODER);
927 const gchar *decoderKlass = gst_element_factory_get_klass(factory);
928 GstPluginFeature *feature = GST_PLUGIN_FEATURE(factory);
929 const gchar *featureName = gst_plugin_feature_get_name(feature);
930 const guint rank = gst_plugin_feature_get_rank(feature);
933 QString pluginName = featureName;
934 GstPlugin *plugin = gst_plugin_feature_get_plugin(feature);
936 pluginName = gst_plugin_get_name(plugin);
937 gst_object_unref(plugin);
939 qCDebug(GstVideoReceiverLog) <<
"Decodebin3 selected codec:rank -" << pluginName <<
"/" << featureName <<
"-" << decoderKlass << (isHardwareDecoder ?
"(HW)" :
"(SW)") <<
":" << rank;
942 g_value_reset(&value);
944 g_value_unset(&value);
945 gst_iterator_free(iter);
949void GstVideoReceiver::_onNewDecoderPad(GstPad *pad)
951 qCDebug(GstVideoReceiverLog) <<
"_onNewDecoderPad" <<
_uri;
953 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-with-new-decoder-pad");
956 _logDecodebin3SelectedCodec(_decoder);
958 if (!_addVideoSink(pad)) {
959 qCCritical(GstVideoReceiverLog) <<
"_addVideoSink() failed";
963bool GstVideoReceiver::_addDecoder(
GstElement *src)
965 GstPad *srcpad = gst_element_get_static_pad(src,
"src");
967 qCCritical(GstVideoReceiverLog) <<
"gst_element_get_static_pad() failed";
971 GstCaps *caps = gst_pad_query_caps(srcpad,
nullptr);
973 qCCritical(GstVideoReceiverLog) <<
"gst_pad_query_caps() failed";
974 gst_clear_object(&srcpad);
978 gst_clear_object(&srcpad);
980 _decoder = _makeDecoder();
982 qCCritical(GstVideoReceiverLog) <<
"_makeDecoder() failed";
983 gst_clear_caps(&caps);
987 (void) gst_object_ref(_decoder);
989 gst_clear_caps(&caps);
991 (void) gst_bin_add(GST_BIN(_pipeline), _decoder);
992 (void) gst_element_sync_state_with_parent(_decoder);
994 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-with-decoder");
996 if (!gst_element_link(src, _decoder)) {
997 qCCritical(GstVideoReceiverLog) <<
"Unable to link decoder";
1001 GstPad *srcPad =
nullptr;
1002 GstIterator *it = gst_element_iterate_src_pads(_decoder);
1003 GValue vpad = G_VALUE_INIT;
1004 switch (gst_iterator_next(it, &vpad)) {
1005 case GST_ITERATOR_OK:
1006 srcPad = GST_PAD(g_value_get_object(&vpad));
1007 (void) gst_object_ref(srcPad);
1008 (void) g_value_reset(&vpad);
1010 case GST_ITERATOR_RESYNC:
1011 gst_iterator_resync(it);
1016 g_value_unset(&vpad);
1017 gst_iterator_free(it);
1020 _onNewDecoderPad(srcPad);
1022 (void) g_signal_connect(_decoder,
"pad-added", G_CALLBACK(_onNewPad),
this);
1025 gst_clear_object(&srcPad);
1029bool GstVideoReceiver::_addVideoSink(GstPad *pad)
1031 GstCaps *caps = gst_pad_query_caps(pad,
nullptr);
1033 (void) gst_object_ref(_videoSink);
1034 (void) gst_bin_add(GST_BIN(_pipeline), _videoSink);
1036 if (!gst_element_link(_decoder, _videoSink)) {
1037 (void) gst_bin_remove(GST_BIN(_pipeline), _videoSink);
1038 qCCritical(GstVideoReceiverLog) <<
"Unable to link video sink";
1039 gst_clear_caps(&caps);
1043 g_object_set(_videoSink,
1048 (void) gst_element_sync_state_with_parent(_videoSink);
1050 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-with-videosink");
1055 if (!_decoderValve) {
1056 qCCritical(GstVideoReceiverLog) <<
"Unable to determine video size - _decoderValve is NULL" <<
_uri;
1060 GstPad *valveSrcPad = gst_element_get_static_pad(_decoderValve,
"src");
1062 qCCritical(GstVideoReceiverLog) <<
"gst_element_get_static_pad() failed";
1066 GstCaps *valveSrcPadCaps = gst_pad_query_caps(valveSrcPad,
nullptr);
1067 if (!valveSrcPadCaps) {
1068 qCCritical(GstVideoReceiverLog) <<
"gst_pad_query_caps() failed";
1069 gst_clear_object(&valveSrcPad);
1073 const GstStructure *structure = gst_caps_get_structure(valveSrcPadCaps, 0);
1075 qCCritical(GstVideoReceiverLog) <<
"Unable to determine video size - structure is NULL" <<
_uri;
1076 gst_clear_object(&valveSrcPad);
1082 (void) gst_structure_get_int(structure,
"width", &width);
1083 (void) gst_structure_get_int(structure,
"height", &height);
1084 videoSize.setWidth(width);
1085 videoSize.setHeight(height);
1087 gst_clear_caps(&valveSrcPadCaps);
1088 gst_clear_object(&valveSrcPad);
1090 _dispatchSignal([
this, videoSize]() { emit
videoSizeChanged(videoSize); });
1092 gst_clear_caps(&caps);
1096void GstVideoReceiver::_noteTeeFrame()
1101void GstVideoReceiver::_noteVideoSinkFrame()
1106 qCDebug(GstVideoReceiverLog) <<
"Decoding started";
1111void GstVideoReceiver::_noteEndOfStream()
1116bool GstVideoReceiver::_unlinkBranch(
GstElement *from)
1118 GstPad *src = gst_element_get_static_pad(from,
"src");
1120 qCCritical(GstVideoReceiverLog) <<
"gst_element_get_static_pad() failed";
1124 GstPad *
sink = gst_pad_get_peer(src);
1126 gst_clear_object(&src);
1127 qCCritical(GstVideoReceiverLog) <<
"gst_pad_get_peer() failed";
1131 if (!gst_pad_unlink(src,
sink)) {
1132 gst_clear_object(&src);
1133 gst_clear_object(&
sink);
1134 qCCritical(GstVideoReceiverLog) <<
"gst_pad_unlink() failed";
1138 gst_clear_object(&src);
1141 const gboolean ret = gst_pad_send_event(
sink, gst_event_new_eos());
1143 gst_clear_object(&
sink);
1146 qCCritical(GstVideoReceiverLog) <<
"Branch EOS was NOT sent";
1150 qCDebug(GstVideoReceiverLog) <<
"Branch EOS was sent";
1155void GstVideoReceiver::_shutdownDecodingBranch()
1158 GstObject *parent = gst_element_get_parent(_decoder);
1160 (void) gst_bin_remove(GST_BIN(_pipeline), _decoder);
1161 (void) gst_element_set_state(_decoder, GST_STATE_NULL);
1162 gst_clear_object(&parent);
1165 gst_clear_object(&_decoder);
1168 if (_videoSinkProbeId != 0) {
1169 GstPad *sinkpad = gst_element_get_static_pad(_videoSink,
"sink");
1171 gst_pad_remove_probe(sinkpad, _videoSinkProbeId);
1172 gst_clear_object(&sinkpad);
1174 _videoSinkProbeId = 0;
1179 GstObject *parent = gst_element_get_parent(_videoSink);
1181 (void) gst_bin_remove(GST_BIN(_pipeline), _videoSink);
1182 (void) gst_element_set_state(_videoSink, GST_STATE_NULL);
1183 gst_clear_object(&parent);
1186 gst_clear_object(&_videoSink);
1192 qCDebug(GstVideoReceiverLog) <<
"Decoding stopped";
1196 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-decoding-stopped");
1199void GstVideoReceiver::_shutdownRecordingBranch()
1201 gst_bin_remove(GST_BIN(_pipeline), _fileSink);
1202 gst_element_set_state(_fileSink, GST_STATE_NULL);
1203 gst_clear_object(&_fileSink);
1209 qCDebug(GstVideoReceiverLog) <<
"Recording stopped";
1213 GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"pipeline-recording-stopped");
1216bool GstVideoReceiver::_needDispatch()
1221void GstVideoReceiver::_dispatchSignal(
Task emitter)
1235gboolean GstVideoReceiver::_onBusMessage(GstBus * , GstMessage *msg, gpointer data)
1237 if (!msg || !data) {
1238 qCCritical(GstVideoReceiverLog) <<
"Invalid parameters in _onBusMessage: msg=" << msg <<
"data=" << data;
1244 switch (GST_MESSAGE_TYPE(msg)) {
1245 case GST_MESSAGE_ERROR: {
1248 gst_message_parse_error(msg, &
error, &debug);
1251 qCDebug(GstVideoReceiverLog) <<
"GStreamer debug:" << debug;
1252 g_clear_pointer(&debug, g_free);
1256 qCCritical(GstVideoReceiverLog) <<
"GStreamer error:" <<
error->message;
1257 g_clear_error(&
error);
1260 pThis->_worker->
dispatch([pThis]() {
1261 qCDebug(GstVideoReceiverLog) <<
"Stopping because of error";
1266 case GST_MESSAGE_EOS:
1267 pThis->_worker->
dispatch([pThis]() {
1268 qCDebug(GstVideoReceiverLog) <<
"Received EOS";
1269 pThis->_handleEOS();
1272 case GST_MESSAGE_ELEMENT: {
1273 const GstStructure *structure = gst_message_get_structure(msg);
1274 if (!gst_structure_has_name(structure,
"GstBinForwarded")) {
1278 GstMessage *forward_msg =
nullptr;
1279 gst_structure_get(structure,
"message", GST_TYPE_MESSAGE, &forward_msg, NULL);
1284 if (GST_MESSAGE_TYPE(forward_msg) == GST_MESSAGE_EOS) {
1285 pThis->_worker->
dispatch([pThis]() {
1286 qCDebug(GstVideoReceiverLog) <<
"Received branch EOS";
1287 pThis->_handleEOS();
1291 gst_clear_message(&forward_msg);
1301void GstVideoReceiver::_onNewPad(
GstElement *element, GstPad *pad, gpointer data)
1305 if (element == self->_source) {
1306 self->_onNewSourcePad(pad);
1307 }
else if (element == self->_decoder) {
1308 self->_onNewDecoderPad(pad);
1310 qCDebug(GstVideoReceiverLog) <<
"Unexpected call!";
1314void GstVideoReceiver::_wrapWithGhostPad(
GstElement *element, GstPad *pad, gpointer data)
1318 gchar *
name = gst_pad_get_name(pad);
1320 qCCritical(GstVideoReceiverLog) <<
"gst_pad_get_name() failed";
1324 GstPad *ghostpad = gst_ghost_pad_new(
name, pad);
1326 qCCritical(GstVideoReceiverLog) <<
"gst_ghost_pad_new() failed";
1327 g_clear_pointer(&
name, g_free);
1331 g_clear_pointer(&
name, g_free);
1333 (void) gst_pad_set_active(ghostpad, TRUE);
1335 if (!gst_element_add_pad(GST_ELEMENT_PARENT(element), ghostpad)) {
1336 qCCritical(GstVideoReceiverLog) <<
"gst_element_add_pad() failed";
1340void GstVideoReceiver::_linkPad(
GstElement *element, GstPad *pad, gpointer data)
1342 gchar *
name = gst_pad_get_name(pad);
1344 qCCritical(GstVideoReceiverLog) <<
"gst_pad_get_name() failed";
1348 if (!gst_element_link_pads(element,
name, GST_ELEMENT(data),
"sink")) {
1349 qCCritical(GstVideoReceiverLog) <<
"gst_element_link_pads() failed";
1352 g_clear_pointer(&
name, g_free);
1355gboolean GstVideoReceiver::_padProbe(
GstElement *element, GstPad *pad, gpointer user_data)
1359 int *probeRes =
static_cast<int*
>(user_data);
1362 GstCaps *filter = gst_caps_from_string(
"application/x-rtp");
1364 GstCaps *caps = gst_pad_query_caps(pad,
nullptr);
1366 if (!gst_caps_is_any(caps) && gst_caps_can_intersect(caps, filter)) {
1370 gst_clear_caps(&caps);
1373 gst_clear_caps(&filter);
1379GstPadProbeReturn GstVideoReceiver::_teeProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
1381 Q_UNUSED(pad); Q_UNUSED(info)
1385 pThis->_noteTeeFrame();
1388 return GST_PAD_PROBE_OK;
1391GstPadProbeReturn GstVideoReceiver::_videoSinkProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
1393 Q_UNUSED(pad); Q_UNUSED(info)
1402 gst_pad_send_event(pad, gst_event_new_flush_start());
1403 gst_pad_send_event(pad, gst_event_new_flush_stop(TRUE));
1407 if ((buf = gst_pad_probe_info_get_buffer(info)) !=
nullptr) {
1410 if ((seg = gst_segment_new()) !=
nullptr) {
1411 gst_segment_init(seg, GST_FORMAT_TIME);
1413 seg->start = buf->pts;
1415 gst_pad_send_event(pad, gst_event_new_segment(seg));
1417 gst_segment_free(seg);
1421 gst_pad_set_offset(pad, -
static_cast<gint64
>(buf->pts));
1426 pThis->_noteVideoSinkFrame();
1429 return GST_PAD_PROBE_OK;
1432GstPadProbeReturn GstVideoReceiver::_eosProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
1435 Q_ASSERT(user_data);
1438 const GstEvent *
event = gst_pad_probe_info_get_event(info);
1439 if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
1441 pThis->_noteEndOfStream();
1445 return GST_PAD_PROBE_OK;
1448GstPadProbeReturn GstVideoReceiver::_keyframeWatch(GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
1450 if (!info || !user_data) {
1451 qCCritical(GstVideoReceiverLog) <<
"Invalid arguments";
1452 return GST_PAD_PROBE_DROP;
1455 GstBuffer *buf = gst_pad_probe_info_get_buffer(info);
1456 if (GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
1458 return GST_PAD_PROBE_DROP;
1462 gst_pad_set_offset(pad, -
static_cast<gint64
>(buf->pts));
1464 qCDebug(GstVideoReceiverLog) <<
"Got keyframe, stop dropping buffers";
1469 return GST_PAD_PROBE_REMOVE;
1485 return (QThread::currentThread() !=
this);
1490 QMutexLocker lock(&_taskQueueSync);
1491 _taskQueue.enqueue(task);
1492 _taskQueueUpdate.wakeOne();
1498 dispatch([
this]() { _shutdown =
true; });
1499 (void) QThread::wait(2000);
1505void GstVideoWorker::run()
1507 while (!_shutdown) {
1508 _taskQueueSync.lock();
1510 while (_taskQueue.isEmpty()) {
1511 _taskQueueUpdate.wait(&_taskQueueSync);
1514 const Task task = _taskQueue.dequeue();
1516 _taskQueueSync.unlock();
std::function< void()> Task
struct _GstElement GstElement
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void stopDecoding() override
void takeScreenshot(const QString &imageFile) override
void start(uint32_t timeout) override
void stopRecording() override
void startRecording(const QString &videoFile, FILE_FORMAT format) override
void startDecoding(void *sink) override
bool needDispatch() const
GstVideoWorker(QObject *parent=nullptr)
void recordingStarted(const QString &filename)
void videoSizeChanged(QSize size)
void streamingChanged(bool active)
qint64 _lastSourceFrameTime
qint64 _lastVideoFrameTime
void recordingChanged(bool active)
void onStartRecordingComplete(STATUS status)
virtual void start(uint32_t timeout)=0
void onTakeScreenshotComplete(STATUS status)
void decodingChanged(bool active)
void onStartComplete(STATUS status)
static bool isValidFileFormat(FILE_FORMAT format)
void onStopDecodingComplete(STATUS status)
void onStopRecordingComplete(STATUS status)
QString recordingOutput() const
void onStartDecodingComplete(STATUS status)
void onStopComplete(STATUS status)
gboolean is_valid_rtsp_uri(const gchar *uri_str)
bool is_hardware_decoder_factory(GstElementFactory *factory)