玉兔远程控制 0.1.0-bate1
载入中...
搜索中...
未找到
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
203void CBackendPlayer::slotStart()
204{
205 qDebug(log) << Q_FUNC_INFO;
206 slotEnableAudioInput(m_pParameters->GetEnableAudioInput());
207 slotEnableAudioOutput(m_pParameters->GetEnableAudioOutput());
208
209 switch (m_pParameters->GetType()) {
210 case CParameterPlayer::TYPE::Camera: {
211 if(!m_pCamera) {
212 const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
213 if(cameras.isEmpty()
214 || -1 > m_pParameters->GetCamera()
215 || m_pParameters->GetCamera() > QMediaDevices::videoInputs().size())
216 break;
217 m_pCamera = new QCamera(cameras.at(m_pParameters->GetCamera()));
218 emit sigServerName(cameras.at(m_pParameters->GetCamera()).description());
219 }
220 if(m_pCamera) {
221 m_CaptureSession.setVideoSink(&m_VideoSink);
222 m_CaptureSession.setCamera(m_pCamera);
223 m_pCamera->start();
224 }
225 break;
226 }
227 case CParameterPlayer::TYPE::Url: {
228 QString szFile = m_pParameters->GetUrl();
229 QFileInfo fi(szFile);
230 emit sigServerName(fi.fileName());
231 QUrl url(szFile);
232 if(url.isRelative())
233 url = QUrl::fromLocalFile(szFile);
234 m_Player.setSource(url);
235 m_Player.setVideoSink(&m_VideoSink);
236#if HAVE_QT6_RECORD
237 if(m_pParameters->m_Record.GetEnableAudio())
238 m_Player.setAudioBufferOutput(&m_AudioBufferOutput);
239#endif
240 m_Player.play();
241
242 m_nPosition = m_Player.position();
243 m_nDuration = m_Player.duration();
244 break;
245 }
246 default:
247 break;
248 }
249}
250
251void CBackendPlayer::slotStop()
252{
253 qDebug(log) << Q_FUNC_INFO;
254 switch (m_pParameters->GetType()) {
255 case CParameterPlayer::TYPE::Camera:
256 if(m_pCamera)
257 m_pCamera->stop();
258 m_CaptureSession.setVideoSink(nullptr);
259 break;
260 case CParameterPlayer::TYPE::Url:
261 m_Player.stop();
262 m_Player.setVideoSink(nullptr);
263 m_Player.setVideoOutput(nullptr);
264 break;
265 default:
266 break;
267 }
268
269 if(m_pCamera) {
270 delete m_pCamera;
271 m_pCamera = nullptr;
272 }
273#if HAVE_QT6_RECORD
274 slotRecord(false);
275#endif
276}
277
278#if HAVE_QT6_RECORD
279void CBackendPlayer::slotRecord(bool bRecord)
280{
281 qDebug(log) << Q_FUNC_INFO << bRecord;
282
283 if(bRecord) {
284 if(QMediaRecorder::StoppedState != m_Recorder.recorderState()) {
285 return;
286 }
287
288 auto &record = m_pParameters->m_Record;
289 switch (m_pParameters->GetType()) {
290 case CParameterPlayer::TYPE::Camera: {
291 record >> m_Recorder;
292 m_CaptureSession.setRecorder(&m_Recorder);
293 m_Recorder.record();
294 break;
295 }
296 case CParameterPlayer::TYPE::Url: {
297 record >> m_Recorder;
298 if(record.GetEnableAudio()) {
299 m_CaptureSession.setAudioBufferInput(&m_AudioBufferInput);
300 } else
301 qDebug(log) << "Record: disable audio";
302 if(record.GetEnableVideo())
303 m_CaptureSession.setVideoFrameInput(&m_VideoFrameInput);
304 else
305 qDebug(log) << "Record: disable video";
306 m_CaptureSession.setRecorder(&m_Recorder);
307 m_Recorder.record();
308 break;
309 }
310 default:
311 break;
312 }
313#ifndef HAVE_QVideoWidget
314 emit sigRecordVideo(bRecord, m_pParameters->m_Record.GetVideoFrameRate());
315#endif
316 return;
317 }
318
319 if(QMediaRecorder::StoppedState != m_Recorder.recorderState()) {
320 m_Recorder.stop();
321 m_CaptureSession.setRecorder(nullptr);
322 m_CaptureSession.setVideoFrameInput(nullptr);
323 m_CaptureSession.setAudioBufferInput(nullptr);
324 }
325}
326#endif
327
328void CBackendPlayer::slotClipBoardChanged()
329{
330}
331
332void CBackendPlayer::slotVideoFrameChanged(const QVideoFrame &frame)
333{
334#ifndef HAVE_QVideoWidget
335 if(m_Video.width() != frame.width()
336 || m_Video.height() != frame.height())
337 {
338 m_Video = QRect(0, 0, frame.width(), frame.height());
339 emit sigSetDesktopSize(m_Video.width(), m_Video.height());
340 }
341 QImage img(frame.width(), frame.height(), QImage::Format_ARGB32);
342 QPainter painter(&img);
343 const QVideoFrame::PaintOptions option;
344 QVideoFrame f = frame;
345 f.paint(&painter, m_Video, option);
346 //qDebug(log) << "QVideoSink::videoFrameChanged" << frame << img;
347 emit this->sigUpdateRect(img);
348#endif
349 //qDebug(log) << "QVideoSink::videoFrameChanged" << frame;
350 if(m_bScreenShot) {
351 m_bScreenShot = false;
352 QImage image = frame.toImage();
353 if(image.isNull()) {
354 qCritical(log) << "frame.toImage() fail";
355 } else {
356 QString szFile = m_pParameters->m_Record.GetImageFile(true);
357 if(!image.save(szFile, "PNG"))
358 {
359 qCritical(log) << "Capture image save to file fail." << szFile;
360 return;
361 }
362 qDebug(log) << "Capture image to file:" << szFile;
363 qDebug(log) << "End action:" << m_pParameters->m_Record.GetEndAction();
364 switch(m_pParameters->m_Record.GetEndAction())
365 {
366 case CParameterRecord::ENDACTION::OpenFile: {
367 bool bRet = QDesktopServices::openUrl(QUrl::fromLocalFile(szFile));
368 if(!bRet)
369 qCritical(log) << "Fail: Open capture image file" << szFile;
370 break;
371 }
372 case CParameterRecord::ENDACTION::OpenFolder: {
373 QFileInfo fi(szFile);
374 QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absolutePath()));
375 break;
376 }
377 default:
378 break;
379 }
380 }
381 }
382
383#if defined(HAVE_QT6_RECORD) && defined(HAVE_QVideoWidget)
384 if(QMediaRecorder::RecordingState == m_Recorder.recorderState()) {
385 bool bRet = m_VideoFrameInput.sendVideoFrame(frame);
386 if(!bRet) {
387 //TODO: 放入未成功发送队列,
388 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
389 qDebug(log) << "m_VideoFrameInput.sendVideoFrame fail";
390 }
391 }
392#endif
393}
394
395void CBackendPlayer::slotEnableAudioInput(bool bEnable)
396{
397 if(bEnable && -1 < m_pParameters->GetAudioInput()
398 && m_pParameters->GetAudioInput() < QMediaDevices::audioInputs().size()) {
399 m_AudioInput.setDevice(QMediaDevices::audioInputs()
400 .at(m_pParameters->GetAudioInput()));
401 m_AudioInput.setMuted(m_pParameters->GetAudioInputMuted());
402 m_AudioInput.setVolume(m_pParameters->GetAudioInputVolume());
403 m_CaptureSession.setAudioInput(&m_AudioInput);
404
405 bool check = connect(m_pParameters,
406 &CParameterPlayer::sigAudioInputMuted,
407 &m_AudioInput, &QAudioInput::setMuted);
408 Q_ASSERT(check);
409 check = connect(m_pParameters, &CParameterPlayer::sigAudioInputVolume,
410 &m_AudioInput, &QAudioInput::setVolume);
411 Q_ASSERT(check);
412 check = connect(m_pParameters, &CParameterPlayer::sigAudioInput,
413 this, [&](int nIndex) {
414 if(-1 < nIndex
415 && nIndex < QMediaDevices::audioInputs().size())
416 m_AudioInput.setDevice(
417 QMediaDevices::audioInputs().at(nIndex));
418 });
419 Q_ASSERT(check);
420 } else {
421 qDebug(log) << "m_CaptureSession: disable audio input";
422 m_CaptureSession.setAudioInput(nullptr);
423 }
424}
425
426void CBackendPlayer::slotEnableAudioOutput(bool bEnable)
427{
428 if(bEnable && (-1 < m_pParameters->GetAudioOutput()
429 && m_pParameters->GetAudioOutput()
430 < QMediaDevices::audioInputs().size()))
431 {
432 m_AudioOutput.setDevice(
433 QMediaDevices::audioOutputs()
434 .at(m_pParameters->GetAudioOutput()));
435 m_AudioOutput.setMuted(m_pParameters->GetAudioOutputMuted());
436 m_AudioOutput.setVolume(m_pParameters->GetAudioOutputVolume());
437 m_AudioOutput.disconnect();
438 bool check = connect(m_pParameters,
439 &CParameterPlayer::sigAudioOutputMuted,
440 &m_AudioOutput, &QAudioOutput::setMuted);
441 Q_ASSERT(check);
442 check = connect(m_pParameters, &CParameterPlayer::sigAudioOutputVolume,
443 &m_AudioOutput, &QAudioOutput::setVolume);
444 Q_ASSERT(check);
445 check = connect(m_pParameters, &CParameterPlayer::sigAudioOutput,
446 this, [&](int nIndex) {
447 if(-1 < nIndex
448 && nIndex < QMediaDevices::audioOutputs().size())
449 m_AudioOutput.setDevice(
450 QMediaDevices::audioOutputs().at(nIndex));
451 });
452 Q_ASSERT(check);
453 switch (m_pParameters->GetType()) {
454 case CParameterPlayer::TYPE::Camera:
455 m_CaptureSession.setAudioOutput(&m_AudioOutput);
456 break;
457 case CParameterPlayer::TYPE::Url:
458 m_Player.setAudioOutput(&m_AudioOutput);
459 break;
460 default:
461 break;
462 }
463 } else {
464 m_Player.setAudioOutput(nullptr);
465 m_CaptureSession.setAudioOutput(nullptr);
466 m_AudioOutput.disconnect();
467 }
468}
469
470void CBackendPlayer::slotPositionChanged(qint64 pos)
471{
472 m_nPosition = pos;
473
474 //qDebug(log) << "Position:" << pos;
475 qint64 currentInfo = pos / 1000;
476 qint64 duration = m_nDuration / 1000;
477 QString szStr;
478 if (currentInfo || duration) {
479 QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60,
480 (currentInfo * 1000) % 1000);
481 QTime totalTime((duration / 3600) % 60, (duration / 60) % 60, duration % 60,
482 (duration * 1000) % 1000);
483 QString format = "mm:ss";
484 if (duration > 3600)
485 format = "hh:mm:ss";
486 szStr = currentTime.toString(format) + " / " + totalTime.toString(format);
487 emit sigPositionChanged(m_nPosition, m_nDuration);
488 }
489 //emit sigInformation(tr("Progress: ") + szStr);
490}
491
492void CBackendPlayer::slotDurationChanged(qint64 duration)
493{
494 //qDebug(log) << "Duration:" << duration;
495 m_nDuration = duration;
496}
远程桌面接口。它由协议插件实现。
void sigUpdateRect(const QRect &r, const QImage &image)
通知视图,图像更新
virtual int OnClean() override
清理
virtual OnInitReturnValue OnInit() override
初始化
void sigRunning()
当插件开始成功后触发。仅由插件触发
void sigFinished()
停止成功信号。仅由插件触发