QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QtMultimediaReceiver.cc
Go to the documentation of this file.
3
4#include <QtMultimedia/QMediaCaptureSession>
5#include <QtMultimedia/QMediaFormat>
6#include <QtMultimedia/QMediaMetaData>
7#include <QtMultimedia/QMediaPlayer>
8#include <QtMultimedia/QMediaRecorder>
9#include <QtMultimedia/QVideoFrame>
10#include <QtMultimedia/QVideoSink>
11#include <QtMultimediaQuick/private/qquickvideooutput_p.h>
12#include <QtQuick/QQuickItem>
13#include <QtQuick/QQuickItemGrabResult>
14
15QGC_LOGGING_CATEGORY(QtMultimediaReceiverLog, "Video.QtMultimediaReceiver")
16
18 : VideoReceiver(parent)
19 , _mediaPlayer(new QMediaPlayer(this))
20 , _captureSession(new QMediaCaptureSession(this))
21 , _mediaRecorder(new QMediaRecorder(this))
22{
23 // qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO << this;
24
25 _captureSession->setRecorder(_mediaRecorder);
26
27 (void) connect(_mediaPlayer, &QMediaPlayer::playingChanged, this, &QtMultimediaReceiver::streamingChanged);
28 (void) connect(_mediaPlayer, &QMediaPlayer::hasVideoChanged, this, &QtMultimediaReceiver::decodingChanged);
29 (void) connect(_mediaPlayer, &QMediaPlayer::playbackStateChanged, this, [this](QMediaPlayer::PlaybackState newState) {
30 if (newState == QMediaPlayer::PlaybackState::PlayingState) {
31 _frameTimer.start();
32 } else if (newState == QMediaPlayer::PlaybackState::StoppedState) {
33 _frameTimer.stop();
34 }
35 });
36 (void) connect(_mediaPlayer, &QMediaPlayer::mediaStatusChanged, this, [this](QMediaPlayer::MediaStatus status) {
37 switch (status) {
38 case QMediaPlayer::MediaStatus::LoadingMedia:
39 _streamDevice = _mediaPlayer->sourceDevice();
40 break;
41 default:
42 break;
43 }
44 });
45 (void) connect(_mediaPlayer, &QMediaPlayer::metaDataChanged, this, []() {
46 /*const QMediaMetaData metaData = _mediaPlayer->metaData();
47 const QVariant resolution = metaData.value(QMediaMetaData::Key::Resolution);
48 const QSize videoSize = resolution.toSize();*/
49 });
50 (void) connect(_mediaPlayer, &QMediaPlayer::bufferProgressChanged, this, [](float filled) {
51 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO << "Buffer Progress:" << filled;
52 });
53 (void) connect(_mediaPlayer, &QMediaPlayer::errorOccurred, this, [](QMediaPlayer::Error error, const QString &errorString) {
54 switch (error) {
55 case QMediaPlayer::Error::NetworkError:
56 break;
57 default:
58 break;
59 }
60
61 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO << errorString;
62 });
63
64 // _mediaRecorder->setEncodingMode(QMediaRecorder::EncodingMode::AverageBitRateEncoding);
65 // _mediaRecorder->setQuality(QMediaRecorder::Quality::HighQuality);
66 // _mediaRecorder->setVideoBitRate()
67 _mediaRecorder->setVideoFrameRate(0);
68 _mediaRecorder->setVideoResolution(QSize());
69 (void) connect(_mediaRecorder, &QMediaRecorder::recorderStateChanged, this, [this](QMediaRecorder::RecorderState state) {
70 if (state == QMediaRecorder::RecorderState::RecordingState) {
71 emit recordingStarted(_mediaRecorder->actualLocation().toString());
72 }
73 emit recordingChanged(_mediaRecorder->recorderState() == QMediaRecorder::RecorderState::RecordingState);
74 });
75 (void) connect(_mediaRecorder, &QMediaRecorder::errorOccurred, this, [](QMediaRecorder::Error error, const QString &errorString) {
76 switch (error) {
77 case QMediaRecorder::Error::OutOfSpaceError:
78 break;
79 default:
80 break;
81 }
82
83 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO << errorString;
84 });
85
86 _frameTimer.setSingleShot(true);
87 _frameTimer.setTimerType(Qt::PreciseTimer);
88 (void) connect(&_frameTimer, &QTimer::timeout, this, &QtMultimediaReceiver::timeout);
89}
90
92{
93 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO << this;
94}
95
97{
98#ifdef QGC_QT_STREAMING
99 return true;
100#else
101 return false;
102#endif
103}
104
105void *QtMultimediaReceiver::createVideoSink(QQuickItem *widget, QObject *parent)
106{
107 Q_UNUSED(parent);
108
109 QVideoSink *videoSink = nullptr;
110 if (widget) {
111 QQuickVideoOutput *const videoOutput = reinterpret_cast<QQuickVideoOutput*>(widget);
112 videoSink = videoOutput->videoSink();
113 }
114
115 return videoSink;
116}
117
119{
120 /*if (!sink) {
121 return;
122 }
123
124 QVideoSink* const videoSink = reinterpret_cast<QVideoSink*>(sink);
125 videoSink->deleteLater();*/
126}
127
129{
130 Q_UNUSED(parent);
131 return new QtMultimediaReceiver(nullptr);
132}
133
134void QtMultimediaReceiver::start(uint32_t timeout)
135{
136 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO;
137
138 if (_mediaPlayer->isPlaying()) {
139 qCDebug(QtMultimediaReceiverLog) << "Already running!";
141 return;
142 }
143
144 if (_uri.isEmpty()) {
145 qCDebug(QtMultimediaReceiverLog) << "Failed because URI is not specified";
147 return;
148 }
149 _mediaPlayer->setSource(QUrl::fromUserInput(_uri));
150
151 _frameTimer.setInterval(timeout);
152
153 // QAbstractVideoBuffer *buffer = _videoSink->videoFrame()->videoBuffer();
154
155 /*if (!_mediaPlayer->hasVideo()) {
156 emit onStartComplete(STATUS_FAIL);
157 }*/
158
159 _mediaPlayer->play();
160
161 qCDebug(QtMultimediaReceiverLog) << "Starting";
162
164}
165
167{
168 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO;
169
170 if (!_mediaPlayer->isPlaying()) {
171 qCDebug(QtMultimediaReceiverLog) << "Already stopped!";
173 return;
174 }
175
176 if (_mediaPlayer->source().isEmpty()) {
177 qCWarning(QtMultimediaReceiverLog) << "Stop called on empty URI";
179 return;
180 }
181
182 _mediaPlayer->stop();
183
184 qCDebug(QtMultimediaReceiverLog) << "Stopped";
185
187}
188
190{
191 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO;
192
193 if (!sink) {
194 qCCritical(QtMultimediaReceiverLog) << "VideoSink is NULL";
196 return;
197 }
198
199 if (_videoSink) {
200 qCWarning(QtMultimediaReceiverLog) << "VideoSink is already set";
201 }
202
203 if (_videoSizeUpdater) {
204 qCWarning(QtMultimediaReceiverLog) << "VideoSizeConnection is already set";
205 }
206
207 _videoSink = reinterpret_cast<QVideoSink*>(sink);
208 _videoSizeUpdater = connect(_videoSink, &QVideoSink::videoSizeChanged, this, [this]() {
209 emit videoSizeChanged(_videoSink->videoSize());
210 });
211 _videoFrameUpdater = connect(_videoSink, &QVideoSink::videoFrameChanged, this, [this](const QVideoFrame &frame) {
212 if (frame.isValid()) {
213 _frameTimer.start();
214 }
215 });
216 _rhi = _videoSink->rhi();
217 _videoSink->setSubtitleText("");
218
219 _mediaPlayer->setVideoSink(_videoSink);
220
221 qCDebug(QtMultimediaReceiverLog) << "Decoding";
222
224}
225
227{
228 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO;
229
230 if (!_videoSink) {
231 qCWarning(QtMultimediaReceiverLog) << "VideoSink is NULL";
233 return;
234 }
235
236 (void) disconnect(_videoSizeUpdater);
237 _mediaPlayer->setVideoSink(nullptr);
238 _videoSink = nullptr;
239
240 qCDebug(QtMultimediaReceiverLog) << "Stopped Decoding";
241
243}
244
245void QtMultimediaReceiver::startRecording(const QString &videoFile, FILE_FORMAT format)
246{
247 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO;
248
249 if (!_mediaRecorder->isAvailable()) {
250 qCWarning(QtMultimediaReceiverLog) << "Recording Unavailable";
252 return;
253 }
254
255 switch (format) {
256 case FILE_FORMAT_MKV:
257 _mediaRecorder->setMediaFormat(QMediaFormat::FileFormat::Matroska);
258 break;
259 case FILE_FORMAT_MOV:
260 _mediaRecorder->setMediaFormat(QMediaFormat::FileFormat::QuickTime);
261 break;
262 case FILE_FORMAT_MP4:
263 _mediaRecorder->setMediaFormat(QMediaFormat::FileFormat::MPEG4);
264 break;
265 default:
266 // QMediaFormat::AVI, WMV, Ogg, WebM
267 _mediaRecorder->setMediaFormat(QMediaFormat::FileFormat::UnspecifiedFormat);
268 break;
269 }
270
271 _mediaRecorder->setOutputLocation(QUrl::fromLocalFile(videoFile));
272 _recordingOutput = _mediaRecorder->outputLocation().toLocalFile();
273 _mediaRecorder->record();
274
275 qCDebug(QtMultimediaReceiverLog) << "Recording";
276
278}
279
281{
282 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO;
283
284 _mediaRecorder->stop();
285
286 qCDebug(QtMultimediaReceiverLog) << "Stopped Recording";
287
289}
290
291void QtMultimediaReceiver::takeScreenshot(const QString &imageFile)
292{
293 qCDebug(QtMultimediaReceiverLog) << Q_FUNC_INFO;
294
295 if (!_videoSink) {
296 qCWarning(QtMultimediaReceiverLog) << "Video Sink is NULL";
298 return;
299 }
300
301 const QVideoFrame frame = _videoSink->videoFrame();
302 if (!frame.isValid() || !frame.isReadable()) {
303 qCWarning(QtMultimediaReceiverLog) << "Screenshot Frame is Invalid";
305 return;
306 }
307
308 // const QVideoFrameFormat frameFormat = frame.surfaceFormat();
309 // const QImage frameImage = frame.toImage();
310
311 _videoOutput = reinterpret_cast<QQuickVideoOutput*>(_mediaPlayer->videoOutput());
312 const QSize targetSize = _mediaRecorder->videoResolution();
313 QSharedPointer<QQuickItemGrabResult> screenshot = _videoOutput->grabToImage(targetSize);
314 // (void) connect(&screenshot, &QQuickItemGrabResult::ready, this, [screenshot, imageFile]() {
315 // screenshot->saveToFile(imageFile);
316 // }
317 screenshot->saveToFile(imageFile);
318
319 qCDebug(QtMultimediaReceiverLog) << "Screenshot";
320
322}
QString errorString
Error error
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static VideoReceiver * createVideoReceiver(QObject *parent)
QMetaObject::Connection _videoSizeUpdater
QMetaObject::Connection _videoFrameUpdater
void takeScreenshot(const QString &imageFile) override
QQuickVideoOutput * _videoOutput
void startDecoding(void *sink) override
void start(uint32_t timeout) override
static void releaseVideoSink(void *sink)
void startRecording(const QString &videoFile, VideoReceiver::FILE_FORMAT format) override
QMediaRecorder * _mediaRecorder
static void * createVideoSink(QQuickItem *widget, QObject *parent=nullptr)
void videoSizeChanged(QSize size)
void streamingChanged(bool active)
QQuickItem * widget()
QString _recordingOutput
void onStartRecordingComplete(STATUS status)
void onTakeScreenshotComplete(STATUS status)
void decodingChanged(bool active)
void onStartComplete(STATUS status)
void onStopDecodingComplete(STATUS status)
void onStopRecordingComplete(STATUS status)
void onStartDecodingComplete(STATUS status)
void onStopComplete(STATUS status)