Rabbit Remote Control 0.1.0-bate14
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#if HAVE_QT6_RECORD
123 if(QMediaPlayer::PlaybackState::PlayingState == state) {/*
124 qDebug(log) << "Video tracks:" << m_Player.videoTracks()
125 << "Audio tracks:" << m_Player.audioTracks()
126 << "Subtitle tracks:" << m_Player.subtitleTracks()
127 << "Video:" << m_Player.activeVideoTrack()
128 << "Audio:" << m_Player.activeAudioTrack()
129 << "Subtitle:" << m_Player.activeSubtitleTrack();//*/
130 if(-1 == m_Player.activeAudioTrack()
131 && m_Player.audioTracks().size() > 0) {
132 m_Player.setActiveAudioTrack(0);
133 }
134 if(-1 == m_Player.activeVideoTrack()
135 && m_Player.videoTracks().size() > 0) {
136 m_Player.setActiveVideoTrack(0);
137 }
138 if(m_pParameters->GetSubtitle()) {
139 if(m_Player.subtitleTracks().size() > 0) {
140 int nIndex = 0;
141 for(int i = 0; i < m_Player.subtitleTracks().size(); i++) {
142 auto m = m_Player.subtitleTracks().at(i);
143 QVariant v = m.value(QMediaMetaData::Language);
144 if(!v.isValid()) continue;
145 if(v.toLocale().language() == QLocale::AnyLanguage
146 || v.toLocale().language() == QLocale::system().language()) {
147 nIndex = i;
148 break;
149 }
150 }
151 m_Player.setActiveSubtitleTrack(nIndex);
152 }
153 }
154 }
155#endif
156 });
157 Q_ASSERT(check);
158#if HAVE_QVideoWidget
159 check = connect(&m_Player, &QMediaPlayer::errorOccurred,
160 pOperate, &COperatePlayer::slotPlaybackError);
161 Q_ASSERT(check);
162 check = connect(&m_Player, &QMediaPlayer::playbackStateChanged,
163 pOperate, &COperatePlayer::slotPlaybackStateChanged);
164 Q_ASSERT(check);
165 check = connect(this, SIGNAL(sigPositionChanged(qint64,qint64)),
166 pOperate, SLOT(slotPositionChanged(qint64,qint64)));
167 Q_ASSERT(check);
168#if HAVE_QT6_RECORD
169 check = connect(&m_Recorder, &QMediaRecorder::recorderStateChanged,
170 pOperate, &COperatePlayer::slotRecordStateChanged);
171 Q_ASSERT(check);
172#endif // #if HAVE_QT6_RECORD
173#endif // #if HAVE_QVideoWidget
174
175 check = connect(pOperate, &COperatePlayer::sigScreenShot,
176 this, [&](){
177 m_bScreenShot = true;
178 });
179 Q_ASSERT(check);
180
181}
182
183CBackendPlayer::~CBackendPlayer()
184{
185 qDebug(log) << Q_FUNC_INFO;
186}
187
188CBackend::OnInitReturnValue CBackendPlayer::OnInit()
189{
190 qDebug(log) << Q_FUNC_INFO;
191 emit sigRunning();
192 return OnInitReturnValue::NotUseOnProcess;
193}
194
196{
197 qDebug(log) << Q_FUNC_INFO;
198 slotStop();
199 emit sigFinished();
200 return 0;
201}
202
204{
205 return 0;
206}
207
208void CBackendPlayer::slotStart()
209{
210 qDebug(log) << Q_FUNC_INFO;
211 slotEnableAudioInput(m_pParameters->GetEnableAudioInput());
212 slotEnableAudioOutput(m_pParameters->GetEnableAudioOutput());
213
214 switch (m_pParameters->GetType()) {
215 case CParameterPlayer::TYPE::Camera: {
216 if(!m_pCamera) {
217 const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
218 if(cameras.isEmpty()
219 || -1 > m_pParameters->GetCamera()
220 || m_pParameters->GetCamera() > QMediaDevices::videoInputs().size())
221 break;
222 m_pCamera = new QCamera(cameras.at(m_pParameters->GetCamera()));
223 emit sigServerName(cameras.at(m_pParameters->GetCamera()).description());
224 }
225 if(m_pCamera) {
226 m_CaptureSession.setVideoSink(&m_VideoSink);
227 m_CaptureSession.setCamera(m_pCamera);
228 m_pCamera->start();
229 }
230 break;
231 }
232 case CParameterPlayer::TYPE::Url: {
233 QString szFile = m_pParameters->GetUrl();
234 QFileInfo fi(szFile);
235 emit sigServerName(fi.fileName());
236 QUrl url(szFile);
237 if(url.isRelative())
238 url = QUrl::fromLocalFile(szFile);
239 m_Player.setSource(url);
240 m_Player.setVideoSink(&m_VideoSink);
241#if HAVE_QT6_RECORD
242 if(m_pParameters->m_Record.GetEnableAudio())
243 m_Player.setAudioBufferOutput(&m_AudioBufferOutput);
244#endif
245 m_Player.play();
246
247 m_nPosition = m_Player.position();
248 m_nDuration = m_Player.duration();
249 break;
250 }
251 default:
252 break;
253 }
254}
255
256void CBackendPlayer::slotStop()
257{
258 qDebug(log) << Q_FUNC_INFO;
259 switch (m_pParameters->GetType()) {
260 case CParameterPlayer::TYPE::Camera:
261 if(m_pCamera)
262 m_pCamera->stop();
263 m_CaptureSession.setVideoSink(nullptr);
264 break;
265 case CParameterPlayer::TYPE::Url:
266 m_Player.stop();
267 m_Player.setVideoSink(nullptr);
268 m_Player.setVideoOutput(nullptr);
269 break;
270 default:
271 break;
272 }
273
274 if(m_pCamera) {
275 delete m_pCamera;
276 m_pCamera = nullptr;
277 }
278#if HAVE_QT6_RECORD
279 slotRecord(false);
280#endif
281}
282
283#if HAVE_QT6_RECORD
284void CBackendPlayer::slotRecord(bool bRecord)
285{
286 qDebug(log) << Q_FUNC_INFO << bRecord;
287
288 if(bRecord) {
289 if(QMediaRecorder::StoppedState != m_Recorder.recorderState()) {
290 return;
291 }
292
293 auto &record = m_pParameters->m_Record;
294 switch (m_pParameters->GetType()) {
295 case CParameterPlayer::TYPE::Camera: {
296 record >> m_Recorder;
297 m_CaptureSession.setRecorder(&m_Recorder);
298 m_Recorder.record();
299 break;
300 }
301 case CParameterPlayer::TYPE::Url: {
302 record >> m_Recorder;
303 if(record.GetEnableAudio()) {
304 m_CaptureSession.setAudioBufferInput(&m_AudioBufferInput);
305 } else
306 qDebug(log) << "Record: disable audio";
307 if(record.GetEnableVideo())
308 m_CaptureSession.setVideoFrameInput(&m_VideoFrameInput);
309 else
310 qDebug(log) << "Record: disable video";
311 m_CaptureSession.setRecorder(&m_Recorder);
312 m_Recorder.record();
313 break;
314 }
315 default:
316 break;
317 }
318#ifndef HAVE_QVideoWidget
319 emit sigRecordVideo(bRecord, m_pParameters->m_Record.GetVideoFrameRate());
320#endif
321 return;
322 }
323
324 if(QMediaRecorder::StoppedState != m_Recorder.recorderState()) {
325 m_Recorder.stop();
326 m_CaptureSession.setRecorder(nullptr);
327 m_CaptureSession.setVideoFrameInput(nullptr);
328 m_CaptureSession.setAudioBufferInput(nullptr);
329 }
330}
331#endif
332
334{
335}
336
337void CBackendPlayer::slotVideoFrameChanged(const QVideoFrame &frame)
338{
339#ifndef HAVE_QVideoWidget
340 if(m_Video.width() != frame.width()
341 || m_Video.height() != frame.height())
342 {
343 m_Video = QRect(0, 0, frame.width(), frame.height());
344 emit sigSetDesktopSize(m_Video.width(), m_Video.height());
345 }
346 QImage img(frame.width(), frame.height(), QImage::Format_ARGB32);
347 QPainter painter(&img);
348 const QVideoFrame::PaintOptions option;
349 QVideoFrame f = frame;
350 f.paint(&painter, m_Video, option);
351 //qDebug(log) << "QVideoSink::videoFrameChanged" << frame << img;
352 emit this->sigUpdateRect(img);
353#endif
354 //qDebug(log) << "QVideoSink::videoFrameChanged" << frame;
355 if(m_bScreenShot) {
356 m_bScreenShot = false;
357 QImage image = frame.toImage();
358 if(image.isNull()) {
359 qCritical(log) << "frame.toImage() fail";
360 } else {
361 QString szFile = m_pParameters->m_Record.GetImageFile(true);
362 if(!image.save(szFile, "PNG"))
363 {
364 qCritical(log) << "Capture image save to file fail." << szFile;
365 return;
366 }
367 qDebug(log) << "Capture image to file:" << szFile;
368 qDebug(log) << "End action:" << m_pParameters->m_Record.GetEndAction();
369 switch(m_pParameters->m_Record.GetEndAction())
370 {
371 case CParameterRecord::ENDACTION::OpenFile: {
372 bool bRet = QDesktopServices::openUrl(QUrl::fromLocalFile(szFile));
373 if(!bRet)
374 qCritical(log) << "Fail: Open capture image file" << szFile;
375 break;
376 }
377 case CParameterRecord::ENDACTION::OpenFolder: {
378 QFileInfo fi(szFile);
379 QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absolutePath()));
380 break;
381 }
382 default:
383 break;
384 }
385 }
386 }
387
388#if defined(HAVE_QT6_RECORD) && defined(HAVE_QVideoWidget)
389 if(QMediaRecorder::RecordingState == m_Recorder.recorderState()) {
390 bool bRet = m_VideoFrameInput.sendVideoFrame(frame);
391 if(!bRet) {
392 //TODO: 放入未成功发送队列,
393 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
394 qDebug(log) << "m_VideoFrameInput.sendVideoFrame fail";
395 }
396 }
397#endif
398}
399
400void CBackendPlayer::slotEnableAudioInput(bool bEnable)
401{
402 if(bEnable && -1 < m_pParameters->GetAudioInput()
403 && m_pParameters->GetAudioInput() < QMediaDevices::audioInputs().size()) {
404 m_AudioInput.setDevice(QMediaDevices::audioInputs()
405 .at(m_pParameters->GetAudioInput()));
406 m_AudioInput.setMuted(m_pParameters->GetAudioInputMuted());
407 m_AudioInput.setVolume(m_pParameters->GetAudioInputVolume());
408 m_CaptureSession.setAudioInput(&m_AudioInput);
409
410 bool check = connect(m_pParameters,
411 &CParameterPlayer::sigAudioInputMuted,
412 &m_AudioInput, &QAudioInput::setMuted);
413 Q_ASSERT(check);
414 check = connect(m_pParameters, &CParameterPlayer::sigAudioInputVolume,
415 &m_AudioInput, &QAudioInput::setVolume);
416 Q_ASSERT(check);
417 check = connect(m_pParameters, &CParameterPlayer::sigAudioInput,
418 this, [&](int nIndex) {
419 if(-1 < nIndex
420 && nIndex < QMediaDevices::audioInputs().size())
421 m_AudioInput.setDevice(
422 QMediaDevices::audioInputs().at(nIndex));
423 });
424 Q_ASSERT(check);
425 } else {
426 qDebug(log) << "m_CaptureSession: disable audio input";
427 m_CaptureSession.setAudioInput(nullptr);
428 }
429}
430
431void CBackendPlayer::slotEnableAudioOutput(bool bEnable)
432{
433 if(bEnable && (-1 < m_pParameters->GetAudioOutput()
434 && m_pParameters->GetAudioOutput()
435 < QMediaDevices::audioInputs().size()))
436 {
437 m_AudioOutput.setDevice(
438 QMediaDevices::audioOutputs()
439 .at(m_pParameters->GetAudioOutput()));
440 m_AudioOutput.setMuted(m_pParameters->GetAudioOutputMuted());
441 m_AudioOutput.setVolume(m_pParameters->GetAudioOutputVolume());
442 m_AudioOutput.disconnect();
443 bool check = connect(m_pParameters,
444 &CParameterPlayer::sigAudioOutputMuted,
445 &m_AudioOutput, &QAudioOutput::setMuted);
446 Q_ASSERT(check);
447 check = connect(m_pParameters, &CParameterPlayer::sigAudioOutputVolume,
448 &m_AudioOutput, &QAudioOutput::setVolume);
449 Q_ASSERT(check);
450 check = connect(m_pParameters, &CParameterPlayer::sigAudioOutput,
451 this, [&](int nIndex) {
452 if(-1 < nIndex
453 && nIndex < QMediaDevices::audioOutputs().size())
454 m_AudioOutput.setDevice(
455 QMediaDevices::audioOutputs().at(nIndex));
456 });
457 Q_ASSERT(check);
458 switch (m_pParameters->GetType()) {
459 case CParameterPlayer::TYPE::Camera:
460 m_CaptureSession.setAudioOutput(&m_AudioOutput);
461 break;
462 case CParameterPlayer::TYPE::Url:
463 m_Player.setAudioOutput(&m_AudioOutput);
464 break;
465 default:
466 break;
467 }
468 } else {
469 m_Player.setAudioOutput(nullptr);
470 m_CaptureSession.setAudioOutput(nullptr);
471 m_AudioOutput.disconnect();
472 }
473}
474
475void CBackendPlayer::slotPositionChanged(qint64 pos)
476{
477 m_nPosition = pos;
478
479 //qDebug(log) << "Position:" << pos;
480 qint64 currentInfo = pos / 1000;
481 qint64 duration = m_nDuration / 1000;
482 QString szStr;
483 if (currentInfo || duration) {
484 QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60,
485 (currentInfo * 1000) % 1000);
486 QTime totalTime((duration / 3600) % 60, (duration / 60) % 60, duration % 60,
487 (duration * 1000) % 1000);
488 QString format = "mm:ss";
489 if (duration > 3600)
490 format = "hh:mm:ss";
491 szStr = currentTime.toString(format) + " / " + totalTime.toString(format);
492 emit sigPositionChanged(m_nPosition, m_nDuration);
493 }
494 //emit sigInformation(tr("Progress: ") + szStr);
495}
496
497void CBackendPlayer::slotDurationChanged(qint64 duration)
498{
499 //qDebug(log) << "Duration:" << duration;
500 m_nDuration = duration;
501}
502
503void CBackendPlayer::mousePressEvent(QMouseEvent *event)
504{
505 Q_UNUSED(event)
506}
507
508void CBackendPlayer::mouseReleaseEvent(QMouseEvent *event)
509{
510 Q_UNUSED(event)
511}
512
513void CBackendPlayer::mouseMoveEvent(QMouseEvent *event)
514{
515 Q_UNUSED(event)
516}
517
518void CBackendPlayer::wheelEvent(QWheelEvent *event)
519{
520 Q_UNUSED(event)
521}
522
523void CBackendPlayer::keyPressEvent(QKeyEvent *event)
524{
525 Q_UNUSED(event)
526}
527
528void CBackendPlayer::keyReleaseEvent(QKeyEvent *event)
529{
530 Q_UNUSED(event)
531}
532
533void CBackendPlayer::InputMethodEvent(QInputMethodEvent *event)
534{
535 Q_UNUSED(event)
536}
Remote desktop interface.
void sigUpdateRect(const QRect &r, const QImage &image)
Notify the CFrmView update image.
virtual void slotClipBoardChanged()=0
Be called when the clip board change.
virtual int OnClean() override
Clean.
virtual int WakeUp() override
Wake up backend thread(background thread).
virtual OnInitReturnValue OnInit() override
Initialization.
void sigRunning()
Emitted when the plugin is successfully started.
void sigFinished()
Successful stopped signal.