玉兔远程控制 0.1.0-bate11
载入中...
搜索中...
未找到
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}
远程桌面接口。它由协议插件实现。
void sigUpdateRect(const QRect &r, const QImage &image)
通知视图,图像更新
virtual void slotClipBoardChanged()=0
当剪切板发生改变时调用
virtual int OnClean() override
清理
virtual int WakeUp() override
唤醒后台线程。
virtual OnInitReturnValue OnInit() override
初始化
void sigRunning()
当插件开始成功后触发。仅由插件触发
void sigFinished()
停止成功信号。仅由插件触发