/*===================================================================== QGroundControl Open Source Ground Control Station (c) 2009, 2015 QGROUNDCONTROL PROJECT This file is part of the QGROUNDCONTROL project QGROUNDCONTROL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QGROUNDCONTROL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with QGROUNDCONTROL. If not, see . ======================================================================*/ /** * @file * @brief QGC Video Receiver * @author Gus Grubba */ #include "VideoReceiver.h" #include VideoReceiver::VideoReceiver(QObject* parent) : QObject(parent) #if defined(QGC_GST_STREAMING) , _pipeline(NULL) , _videoSink(NULL) #endif { } VideoReceiver::~VideoReceiver() { #if defined(QGC_GST_STREAMING) stop(); setVideoSink(NULL); #endif } #if defined(QGC_GST_STREAMING) void VideoReceiver::setVideoSink(GstElement* sink) { if (_videoSink) { gst_object_unref(_videoSink); _videoSink = NULL; } if (sink) { _videoSink = sink; gst_object_ref_sink(_videoSink); } } #endif void VideoReceiver::start() { #if defined(QGC_GST_STREAMING) if (_uri.isEmpty()) { qCritical() << "VideoReceiver::start() failed because URI is not specified"; return; } if (_videoSink == NULL) { qCritical() << "VideoReceiver::start() failed because video sink is not set"; return; } stop(); bool running = false; GstElement* dataSource = NULL; GstCaps* caps = NULL; GstElement* demux = NULL; GstElement* parser = NULL; GstElement* decoder = NULL; do { if ((_pipeline = gst_pipeline_new("receiver")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_pipeline_new()"; break; } if ((dataSource = gst_element_factory_make("udpsrc", "udp-source")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('udpsrc')"; break; } if ((caps = gst_caps_from_string("application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_caps_from_string()"; break; } g_object_set(G_OBJECT(dataSource), "uri", qPrintable(_uri), "caps", caps, NULL); if ((demux = gst_element_factory_make("rtph264depay", "rtp-h264-depacketizer")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('rtph264depay')"; break; } if ((parser = gst_element_factory_make("h264parse", "h264-parser")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('h264parse')"; break; } if ((decoder = gst_element_factory_make("avdec_h264", "h264-decoder")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('avdec_h264')"; break; } gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, decoder, _videoSink, NULL); if (gst_element_link_many(dataSource, demux, parser, decoder, _videoSink, NULL) != (gboolean)TRUE) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_link_many()"; break; } dataSource = demux = parser = decoder = NULL; GstBus* bus = NULL; if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { gst_bus_add_watch(bus, _onBusMessage, this); gst_object_unref(bus); bus = NULL; } running = gst_element_set_state(_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE; } while(0); if (caps != NULL) { gst_caps_unref(caps); caps = NULL; } if (!running) { qCritical() << "VideoReceiver::start() failed"; if (decoder != NULL) { gst_object_unref(decoder); decoder = NULL; } if (parser != NULL) { gst_object_unref(parser); parser = NULL; } if (demux != NULL) { gst_object_unref(demux); demux = NULL; } if (dataSource != NULL) { gst_object_unref(dataSource); dataSource = NULL; } if (_pipeline != NULL) { gst_object_unref(_pipeline); _pipeline = NULL; } } #endif } void VideoReceiver::stop() { #if defined(QGC_GST_STREAMING) if (_pipeline != NULL) { gst_element_set_state(_pipeline, GST_STATE_NULL); gst_object_unref(_pipeline); _pipeline = NULL; } #endif } void VideoReceiver::setUri(const QString & uri) { stop(); _uri = uri; } #if defined(QGC_GST_STREAMING) void VideoReceiver::_onBusMessage(GstMessage* msg) { switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_EOS: stop(); break; case GST_MESSAGE_ERROR: do { gchar* debug; GError* error; gst_message_parse_error(msg, &error, &debug); g_free(debug); qCritical() << error->message; g_error_free(error); } while(0); stop(); break; default: break; } } #endif #if defined(QGC_GST_STREAMING) gboolean VideoReceiver::_onBusMessage(GstBus* bus, GstMessage* msg, gpointer data) { Q_UNUSED(bus) Q_ASSERT(msg != NULL && data != NULL); VideoReceiver* pThis = (VideoReceiver*)data; pThis->_onBusMessage(msg); return TRUE; } #endif