6#ifdef QGC_GST_RTSP_SERVER
8#include <gst/rtsp-server/rtsp-server.h>
21constexpr const char *kTestSource =
22 "videotestsrc is-live=true pattern=ball ! "
23 "video/x-raw,width=1280,height=720,framerate=30/1 ! "
26constexpr const char *kH264Encoder =
"x264enc tune=zerolatency bitrate=2000 key-int-max=30";
27constexpr const char *kH265Encoder =
"x265enc tune=zerolatency bitrate=2000";
29bool factoryExists(
const char *name)
31 GstElementFactory *factory = gst_element_factory_find(name);
33 gst_object_unref(factory);
44 if (!gst_is_initialized()) {
45 qCWarning(MockVideoStreamServerLog) <<
"GStreamer not initialized; QGC's central GStreamer init must run before serving a mock stream";
60 if (!gstInitialized()) {
66 return factoryExists(
"x264enc") && factoryExists(
"videotestsrc");
70 return factoryExists(
"x264enc") && factoryExists(
"videotestsrc") && factoryExists(
"mpegtsmux");
72 return factoryExists(
"x265enc") && factoryExists(
"videotestsrc");
74#ifdef QGC_GST_RTSP_SERVER
75 return factoryExists(
"x264enc") && factoryExists(
"videotestsrc");
91 qCWarning(MockVideoStreamServerLog) <<
"Requested stream type is not available in this GStreamer install";
96#ifdef QGC_GST_RTSP_SERVER
97 if (!_startRtsp(host, port)) {
100 _servedUri = QStringLiteral(
"rtsp://%1:%2/test").arg(host, QString::number(port));
102 qCDebug(MockVideoStreamServerLog) <<
"Serving" << _servedUri;
105 qCWarning(MockVideoStreamServerLog) <<
"RTSP requested but gst-rtsp-server is not available";
112 const QString portString = QString::number(port);
115 launch = QStringLiteral(
"%1 ! %2 ! rtph264pay config-interval=1 pt=96 ! udpsink host=%3 port=%4")
116 .arg(kTestSource, kH264Encoder, host, portString);
117 uri = QStringLiteral(
"udp://%1:%2").arg(host, portString);
120 launch = QStringLiteral(
"%1 ! %2 ! rtph265pay config-interval=1 pt=96 ! udpsink host=%3 port=%4")
121 .arg(kTestSource, kH265Encoder, host, portString);
122 uri = QStringLiteral(
"udp265://%1:%2").arg(host, portString);
125 launch = QStringLiteral(
"%1 ! %2 ! mpegtsmux ! udpsink host=%3 port=%4")
126 .arg(kTestSource, kH264Encoder, host, portString);
127 uri = QStringLiteral(
"mpegts://%1:%2").arg(host, portString);
130 launch = QStringLiteral(
"%1 ! %2 ! mpegtsmux ! tcpserversink host=%3 port=%4")
131 .arg(kTestSource, kH264Encoder, host, portString);
132 uri = QStringLiteral(
"tcp://%1:%2").arg(host, portString);
138 if (!_startPipeline(launch)) {
144 qCDebug(MockVideoStreamServerLog) <<
"Serving" << _servedUri <<
"via" << launch;
148bool MockVideoStreamServer::_startPipeline(
const QString &launch)
150 GError *
error =
nullptr;
151 GstElement *pipeline = gst_parse_launch(launch.toUtf8().constData(), &
error);
153 qCWarning(MockVideoStreamServerLog) <<
"Failed to build pipeline:" <<
error->message;
156 gst_object_unref(pipeline);
161 if (gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
162 qCWarning(MockVideoStreamServerLog) <<
"Failed to set pipeline to PLAYING";
163 gst_object_unref(pipeline);
167 _pipeline = pipeline;
174 gst_element_set_state(_pipeline, GST_STATE_NULL);
175 gst_object_unref(_pipeline);
179#ifdef QGC_GST_RTSP_SERVER
187#ifdef QGC_GST_RTSP_SERVER
188bool MockVideoStreamServer::_startRtsp(
const QString &host, quint16 port)
192 _rtspContext = g_main_context_new();
194 GstRTSPServer *server = gst_rtsp_server_new();
195 const QByteArray service = QByteArray::number(port);
196 gst_rtsp_server_set_service(server, service.constData());
198 GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);
199 GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new();
200 const QString launch = QStringLiteral(
"( %1 ! %2 ! rtph264pay name=pay0 pt=96 )")
201 .arg(kTestSource, kH264Encoder);
202 gst_rtsp_media_factory_set_launch(factory, launch.toUtf8().constData());
203 gst_rtsp_media_factory_set_shared(factory, TRUE);
204 gst_rtsp_mount_points_add_factory(mounts,
"/test", factory);
205 g_object_unref(mounts);
207 if (gst_rtsp_server_attach(server, _rtspContext) == 0) {
208 qCWarning(MockVideoStreamServerLog) <<
"Failed to attach RTSP server on port" << port;
209 gst_object_unref(server);
210 g_main_context_unref(_rtspContext);
211 _rtspContext =
nullptr;
215 _rtspServer = server;
216 _rtspLoop = g_main_loop_new(_rtspContext, FALSE);
218 _rtspThread = std::thread([
this]() {
219 g_main_context_push_thread_default(_rtspContext);
220 g_main_loop_run(_rtspLoop);
221 g_main_context_pop_thread_default(_rtspContext);
227void MockVideoStreamServer::_stopRtsp()
230 g_main_loop_quit(_rtspLoop);
232 if (_rtspThread.joinable()) {
236 g_main_loop_unref(_rtspLoop);
240 gst_object_unref(_rtspServer);
241 _rtspServer =
nullptr;
244 g_main_context_unref(_rtspContext);
245 _rtspContext =
nullptr;
struct _GstElement GstElement
#define QGC_LOGGING_CATEGORY(name, categoryStr)
@ MpegTsTcp
MPEG-TS over TCP -> tcp://.
@ RtpUdpH265
RTP/H.265 over UDP -> udp265://.
@ RtpUdpH264
RTP/H.264 over UDP -> udp://.
@ RtspH264
RTSP (H.264) -> rtsp://.
@ MpegTsUdp
MPEG-TS over UDP -> mpegts://.
void stop()
Stop the stream and release all resources. Safe to call when not running.
static bool isStreamTypeAvailable(StreamType type)
bool start(StreamType type, const QString &host, quint16 port)