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