Rabbit Remote Control 0.0.37
Loading...
Searching...
No Matches
BackendPlayer.cpp
1// Author: Kang Lin <kl222@126.com>
2
3#include <QLoggingCategory>
4#include <QMediaDevices>
5#include <QPainter>
6#include <QAudioDevice>
7#include <QDesktopServices>
8#include <QImage>
9#include <QTime>
10#include <QFileInfo>
11#include "BackendPlayer.h"
12
13static Q_LOGGING_CATEGORY(log, "Player.Backend")
14
16 : CBackendDesktop(pOperate)
17 , m_pCamera(nullptr)
18 , m_bScreenShot(false)
19 , m_nPosition(0)
20 , m_nDuration(0)
21{
22 bool check = false;
23 qDebug(log) << Q_FUNC_INFO;
24
25 m_pParameters = qobject_cast<CParameterPlayer*>(pOperate->GetParameter());
26
27#if HAVE_QVideoWidget
28 check = connect(&m_VideoSink, &QVideoSink::videoFrameChanged,
29 pOperate->GetVideoSink(), &QVideoSink::videoFrameChanged);
30 Q_ASSERT(check);
31#endif
32
33 check = connect(
34 &m_VideoSink, SIGNAL(videoFrameChanged(const QVideoFrame&)),
35 this, SLOT(slotVideoFrameChanged(QVideoFrame)));
36 Q_ASSERT(check);
37
38#if HAVE_QT6_RECORD
39 check = connect(
40 &m_AudioBufferOutput, &QAudioBufferOutput::audioBufferReceived,
41 this, [&](const QAudioBuffer &buffer){
42 //qDebug(log) << "Audio buffer output";
43 if(QMediaRecorder::RecordingState != m_Recorder.recorderState())
44 return;
45 bool bRet = m_AudioBufferInput.sendAudioBuffer(buffer);
46 if(!bRet) {
47 //TODO: 放入未成功发送队列,
48 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
49 qDebug(log) << "m_AudioBufferInput.sendAudioBuffer fail";
50 }
51 });
52 Q_ASSERT(check);
53#endif
54
55 check = connect(pOperate, &COperatePlayer::sigStart,
56 this, [&](bool bStart){
57 if(bStart)
58 slotStart();
59 else
60 slotStop();
61 });
62 Q_ASSERT(check);
63 check = connect(
64 pOperate, &COperatePlayer::sigPause,
65 this, [&](bool bPause){
66 switch (m_pParameters->GetType()) {
67 case CParameterPlayer::TYPE::Camera:
68 if(bPause) {
69 if(m_pCamera->isActive())
70 m_pCamera->stop();
71 }
72 else {
73 if(!m_pCamera->isActive())
74 m_pCamera->start();
75 }
76 break;
77 case CParameterPlayer::TYPE::Url:
78 if(bPause) {
79 if(QMediaPlayer::PlayingState == m_Player.playbackState())
80 m_Player.pause();
81 }
82 else {
83 if(QMediaPlayer::PausedState == m_Player.playbackState())
84 m_Player.play();
85 }
86 break;
87 }
88 });
89 Q_ASSERT(check);
90
91 check = connect(
92 &m_Player, SIGNAL(positionChanged(qint64)),
93 this, SLOT(slotPositionChanged(qint64)));
94 Q_ASSERT(check);
95 check = connect(
96 &m_Player, SIGNAL(durationChanged(qint64)),
97 this, SLOT(slotDurationChanged(qint64)));
98 Q_ASSERT(check);
99 check = connect(pOperate, SIGNAL(sigChangePosition(qint64)),
100 &m_Player, SLOT(setPosition(qint64)));
101 Q_ASSERT(check);
102 check = connect(m_pParameters, &CParameterPlayer::sigEnableAudioInput,
103 this, &CBackendPlayer::slotEnableAudioInput);
104 Q_ASSERT(check);
105 check = connect(m_pParameters, &CParameterPlayer::sigEnableAudioOutput,
106 this, &CBackendPlayer::slotEnableAudioOutput);
107 Q_ASSERT(check);
108
109 check = connect(
110 &m_Player, &QMediaPlayer::errorOccurred,
111 this, [&](QMediaPlayer::Error error, const QString &errorString){
112 qCritical(log) << "Player error occurred:" << error << errorString
113 << m_Player.source();
114 slotStop();
115 emit sigError(error, errorString);
116 });
117 Q_ASSERT(check);
118 check = connect(&m_Player, &QMediaPlayer::playbackStateChanged,
119 this, [&](QMediaPlayer::PlaybackState state){
120 qDebug(log) << "Player state changed:" << state
121 << m_Player.source();
122 });
123 Q_ASSERT(check);
124#if HAVE_QVideoWidget
125 check = connect(&m_Player, &QMediaPlayer::errorOccurred,
126 pOperate, &COperatePlayer::slotPlaybackError);
127 Q_ASSERT(check);
128 check = connect(&m_Player, &QMediaPlayer::playbackStateChanged,
129 pOperate, &COperatePlayer::slotPlaybackStateChanged);
130 Q_ASSERT(check);
131 check = connect(this, SIGNAL(sigPositionChanged(qint64,qint64)),
132 pOperate, SLOT(slotPositionChanged(qint64,qint64)));
133 Q_ASSERT(check);
134#if HAVE_QT6_RECORD
135 check = connect(&m_Recorder, &QMediaRecorder::recorderStateChanged,
136 pOperate, &COperatePlayer::slotRecordStateChanged);
137 Q_ASSERT(check);
138#endif // #if HAVE_QT6_RECORD
139#endif // #if HAVE_QVideoWidget
140
141 check = connect(pOperate, &COperatePlayer::sigScreenShot,
142 this, [&](){
143 m_bScreenShot = true;
144 });
145 Q_ASSERT(check);
146
147}
148
149CBackendPlayer::~CBackendPlayer()
150{
151 qDebug(log) << Q_FUNC_INFO;
152}
153
154CBackend::OnInitReturnValue CBackendPlayer::OnInit()
155{
156 qDebug(log) << Q_FUNC_INFO;
157 emit sigRunning();
158 return OnInitReturnValue::NotUseOnProcess;
159}
160
162{
163 qDebug(log) << Q_FUNC_INFO;
164 slotStop();
165 emit sigFinished();
166 return 0;
167}
168
169void CBackendPlayer::slotStart()
170{
171 qDebug(log) << Q_FUNC_INFO;
172 slotEnableAudioInput(m_pParameters->GetEnableAudioInput());
173 slotEnableAudioOutput(m_pParameters->GetEnableAudioOutput());
174
175 switch (m_pParameters->GetType()) {
176 case CParameterPlayer::TYPE::Camera: {
177 if(!m_pCamera) {
178 const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
179 if(cameras.isEmpty()
180 || -1 > m_pParameters->GetCamera()
181 || m_pParameters->GetCamera() > QMediaDevices::videoInputs().size())
182 break;
183 m_pCamera = new QCamera(cameras.at(m_pParameters->GetCamera()));
184 emit sigServerName(cameras.at(m_pParameters->GetCamera()).description());
185 }
186 if(m_pCamera) {
187 m_CaptureSession.setVideoSink(&m_VideoSink);
188 m_CaptureSession.setCamera(m_pCamera);
189 m_pCamera->start();
190 }
191 break;
192 }
193 case CParameterPlayer::TYPE::Url: {
194 QString szFile = m_pParameters->GetUrl();
195 QFileInfo fi(szFile);
196 emit sigServerName(fi.fileName());
197 QUrl url(szFile);
198 if(url.isRelative())
199 url = QUrl::fromLocalFile(szFile);
200 m_Player.setSource(url);
201 m_Player.setVideoSink(&m_VideoSink);
202#if HAVE_QT6_RECORD
203 if(m_pParameters->m_Record.GetEnableAudio())
204 m_Player.setAudioBufferOutput(&m_AudioBufferOutput);
205#endif
206 m_Player.play();
207 m_nPosition = m_Player.position();
208 m_nDuration = m_Player.duration();
209 break;
210 }
211 default:
212 break;
213 }
214}
215
216void CBackendPlayer::slotStop()
217{
218 qDebug(log) << Q_FUNC_INFO;
219 switch (m_pParameters->GetType()) {
220 case CParameterPlayer::TYPE::Camera:
221 if(m_pCamera)
222 m_pCamera->stop();
223 m_CaptureSession.setVideoSink(nullptr);
224 break;
225 case CParameterPlayer::TYPE::Url:
226 m_Player.stop();
227 m_Player.setVideoSink(nullptr);
228 m_Player.setVideoOutput(nullptr);
229 break;
230 default:
231 break;
232 }
233
234 if(m_pCamera) {
235 delete m_pCamera;
236 m_pCamera = nullptr;
237 }
238#if HAVE_QT6_RECORD
239 slotRecord(false);
240#endif
241}
242
243#if HAVE_QT6_RECORD
244void CBackendPlayer::slotRecord(bool bRecord)
245{
246 qDebug(log) << Q_FUNC_INFO << bRecord;
247
248 if(bRecord) {
249 if(QMediaRecorder::StoppedState != m_Recorder.recorderState()) {
250 return;
251 }
252
253 auto &record = m_pParameters->m_Record;
254 switch (m_pParameters->GetType()) {
255 case CParameterPlayer::TYPE::Camera: {
256 record >> m_Recorder;
257 m_CaptureSession.setRecorder(&m_Recorder);
258 m_Recorder.record();
259 break;
260 }
261 case CParameterPlayer::TYPE::Url: {
262 record >> m_Recorder;
263 if(record.GetEnableAudio()) {
264 m_CaptureSession.setAudioBufferInput(&m_AudioBufferInput);
265 } else
266 qDebug(log) << "Record: disable audio";
267 if(record.GetEnableVideo())
268 m_CaptureSession.setVideoFrameInput(&m_VideoFrameInput);
269 else
270 qDebug(log) << "Record: disable video";
271 m_CaptureSession.setRecorder(&m_Recorder);
272 m_Recorder.record();
273 break;
274 }
275 default:
276 break;
277 }
278#ifndef HAVE_QVideoWidget
279 emit sigRecordVideo(bRecord, m_pParameters->m_Record.GetVideoFrameRate());
280#endif
281 return;
282 }
283
284 if(QMediaRecorder::StoppedState != m_Recorder.recorderState()) {
285 m_Recorder.stop();
286 m_CaptureSession.setRecorder(nullptr);
287 m_CaptureSession.setVideoFrameInput(nullptr);
288 m_CaptureSession.setAudioBufferInput(nullptr);
289 }
290}
291#endif
292
293void CBackendPlayer::slotClipBoardChanged()
294{
295}
296
297void CBackendPlayer::slotVideoFrameChanged(const QVideoFrame &frame)
298{
299#ifndef HAVE_QVideoWidget
300 if(m_Video.width() != frame.width()
301 || m_Video.height() != frame.height())
302 {
303 m_Video = QRect(0, 0, frame.width(), frame.height());
304 emit sigSetDesktopSize(m_Video.width(), m_Video.height());
305 }
306 QImage img(frame.width(), frame.height(), QImage::Format_ARGB32);
307 QPainter painter(&img);
308 const QVideoFrame::PaintOptions option;
309 QVideoFrame f = frame;
310 f.paint(&painter, m_Video, option);
311 //qDebug(log) << "QVideoSink::videoFrameChanged" << frame << img;
312 emit this->sigUpdateRect(img);
313#endif
314 //qDebug(log) << "QVideoSink::videoFrameChanged" << frame;
315 if(m_bScreenShot) {
316 m_bScreenShot = false;
317 QImage image = frame.toImage();
318 if(image.isNull()) {
319 qCritical(log) << "frame.toImage() fail";
320 } else {
321 QString szFile = m_pParameters->m_Record.GetImageFile(true);
322 if(!image.save(szFile, "PNG"))
323 {
324 qCritical(log) << "Capture image save to file fail." << szFile;
325 return;
326 }
327 qDebug(log) << "Capture image to file:" << szFile;
328 qDebug(log) << "End action:" << m_pParameters->m_Record.GetEndAction();
329 switch(m_pParameters->m_Record.GetEndAction())
330 {
331 case CParameterRecord::ENDACTION::OpenFile: {
332 bool bRet = QDesktopServices::openUrl(QUrl::fromLocalFile(szFile));
333 if(!bRet)
334 qCritical(log) << "Fail: Open capture image file" << szFile;
335 break;
336 }
337 case CParameterRecord::ENDACTION::OpenFolder: {
338 QFileInfo fi(szFile);
339 QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absolutePath()));
340 break;
341 }
342 default:
343 break;
344 }
345 }
346 }
347
348#if defined(HAVE_QT6_RECORD) && defined(HAVE_QVideoWidget)
349 if(QMediaRecorder::RecordingState == m_Recorder.recorderState()) {
350 bool bRet = m_VideoFrameInput.sendVideoFrame(frame);
351 if(!bRet) {
352 //TODO: 放入未成功发送队列,
353 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
354 qDebug(log) << "m_VideoFrameInput.sendVideoFrame fail";
355 }
356 }
357#endif
358}
359
360void CBackendPlayer::slotEnableAudioInput(bool bEnable)
361{
362 if(bEnable && -1 < m_pParameters->GetAudioInput()
363 && m_pParameters->GetAudioInput() < QMediaDevices::audioInputs().size()) {
364 m_AudioInput.setDevice(QMediaDevices::audioInputs()
365 .at(m_pParameters->GetAudioInput()));
366 m_AudioInput.setMuted(m_pParameters->GetAudioInputMuted());
367 m_AudioInput.setVolume(m_pParameters->GetAudioInputVolume());
368 m_CaptureSession.setAudioInput(&m_AudioInput);
369
370 bool check = connect(m_pParameters,
371 &CParameterPlayer::sigAudioInputMuted,
372 &m_AudioInput, &QAudioInput::setMuted);
373 Q_ASSERT(check);
374 check = connect(m_pParameters, &CParameterPlayer::sigAudioInputVolume,
375 &m_AudioInput, &QAudioInput::setVolume);
376 Q_ASSERT(check);
377 check = connect(m_pParameters, &CParameterPlayer::sigAudioInput,
378 this, [&](int nIndex) {
379 if(-1 < nIndex
380 && nIndex < QMediaDevices::audioInputs().size())
381 m_AudioInput.setDevice(
382 QMediaDevices::audioInputs().at(nIndex));
383 });
384 Q_ASSERT(check);
385 } else {
386 qDebug(log) << "m_CaptureSession: disable audio input";
387 m_CaptureSession.setAudioInput(nullptr);
388 }
389}
390
391void CBackendPlayer::slotEnableAudioOutput(bool bEnable)
392{
393 if(bEnable && (-1 < m_pParameters->GetAudioOutput()
394 && m_pParameters->GetAudioOutput()
395 < QMediaDevices::audioInputs().size()))
396 {
397 m_AudioOutput.setDevice(
398 QMediaDevices::audioOutputs()
399 .at(m_pParameters->GetAudioOutput()));
400 m_AudioOutput.setMuted(m_pParameters->GetAudioOutputMuted());
401 m_AudioOutput.setVolume(m_pParameters->GetAudioOutputVolume());
402 m_AudioOutput.disconnect();
403 bool check = connect(m_pParameters,
404 &CParameterPlayer::sigAudioOutputMuted,
405 &m_AudioOutput, &QAudioOutput::setMuted);
406 Q_ASSERT(check);
407 check = connect(m_pParameters, &CParameterPlayer::sigAudioOutputVolume,
408 &m_AudioOutput, &QAudioOutput::setVolume);
409 Q_ASSERT(check);
410 check = connect(m_pParameters, &CParameterPlayer::sigAudioOutput,
411 this, [&](int nIndex) {
412 if(-1 < nIndex
413 && nIndex < QMediaDevices::audioOutputs().size())
414 m_AudioOutput.setDevice(
415 QMediaDevices::audioOutputs().at(nIndex));
416 });
417 Q_ASSERT(check);
418 switch (m_pParameters->GetType()) {
419 case CParameterPlayer::TYPE::Camera:
420 m_CaptureSession.setAudioOutput(&m_AudioOutput);
421 break;
422 case CParameterPlayer::TYPE::Url:
423 m_Player.setAudioOutput(&m_AudioOutput);
424 break;
425 default:
426 break;
427 }
428 } else {
429 m_Player.setAudioOutput(nullptr);
430 m_CaptureSession.setAudioOutput(nullptr);
431 m_AudioOutput.disconnect();
432 }
433}
434
435void CBackendPlayer::slotPositionChanged(qint64 pos)
436{
437 m_nPosition = pos;
438
439 //qDebug(log) << "Position:" << pos;
440 qint64 currentInfo = pos / 1000;
441 qint64 duration = m_nDuration / 1000;
442 QString szStr;
443 if (currentInfo || duration) {
444 QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60,
445 (currentInfo * 1000) % 1000);
446 QTime totalTime((duration / 3600) % 60, (duration / 60) % 60, duration % 60,
447 (duration * 1000) % 1000);
448 QString format = "mm:ss";
449 if (duration > 3600)
450 format = "hh:mm:ss";
451 szStr = currentTime.toString(format) + " / " + totalTime.toString(format);
452 emit sigPositionChanged(m_nPosition, m_nDuration);
453 }
454 //emit sigInformation(tr("Progress: ") + szStr);
455}
456
457void CBackendPlayer::slotDurationChanged(qint64 duration)
458{
459 //qDebug(log) << "Duration:" << duration;
460 m_nDuration = duration;
461}
Remote desktop interface.
void sigUpdateRect(const QRect &r, const QImage &image)
Notify the CFrmView update image.
virtual int OnClean() override
Clean.
virtual OnInitReturnValue OnInit() override
Initialization.
void sigRunning()
Emitted when the plugin is successfully started.
void sigFinished()
Successful stopped signal.