Rabbit Remote Control 0.0.30
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::playbackStateChanged,
124 pConnecter, &CConnecterPlayer::slotPlaybackStateChanged);
125 Q_ASSERT(check);
126 check = connect(this, SIGNAL(sigPositionChanged(qint64,qint64)),
127 pConnecter, SLOT(slotPositionChanged(qint64,qint64)));
128 Q_ASSERT(check);
129#if HAVE_QT6_RECORD
130 check = connect(&m_Recorder, &QMediaRecorder::recorderStateChanged,
131 pConnecter, &CConnecterPlayer::slotRecordStateChanged);
132 Q_ASSERT(check);
133#endif // #if HAVE_QT6_RECORD
134#endif // #if HAVE_QVideoWidget
135
136 check = connect(pConnecter, &CConnecterPlayer::sigScreenShot,
137 this, [&](){
138 m_bScreenShot = true;
139 });
140 Q_ASSERT(check);
141
142}
143
144CConnectPlayer::~CConnectPlayer()
145{
146 qDebug(log) << Q_FUNC_INFO;
147}
148
149CConnect::OnInitReturnValue CConnectPlayer::OnInit()
150{
151 qDebug(log) << "CConnectPlayer::OnInit()";
152 emit sigConnected();
153 return OnInitReturnValue::NotUseOnProcess;
154}
155
157{
158 qDebug(log) << "CConnectPlayer::OnClean()";
159 slotStop();
160 emit sigDisconnected();
161 return 0;
162}
163
164void CConnectPlayer::slotStart()
165{
166 qDebug(log) << Q_FUNC_INFO;
167 slotEnableAudioInput(m_pParameters->GetEnableAudioInput());
168 slotEnableAudioOutput(m_pParameters->GetEnableAudioOutput());
169
170 switch (m_pParameters->GetType()) {
171 case CParameterPlayer::TYPE::Camera: {
172 if(!m_pCamera) {
173 const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
174 if(cameras.isEmpty()
175 || -1 > m_pParameters->GetCamera()
176 || m_pParameters->GetCamera() > QMediaDevices::videoInputs().size())
177 break;
178 m_pCamera = new QCamera(cameras.at(m_pParameters->GetCamera()));
179 emit sigServerName(cameras.at(m_pParameters->GetCamera()).description());
180 }
181 if(m_pCamera) {
182 m_CaptureSession.setVideoSink(&m_VideoSink);
183 m_CaptureSession.setCamera(m_pCamera);
184 m_pCamera->start();
185 }
186 break;
187 }
188 case CParameterPlayer::TYPE::Url: {
189 QString szFile = m_pParameters->GetUrl();
190 QFileInfo fi(szFile);
191 emit sigServerName(fi.fileName());
192 QUrl url(szFile);
193 if(url.isRelative())
194 url = QUrl::fromLocalFile(szFile);
195 m_Player.setSource(url);
196 m_Player.setVideoSink(&m_VideoSink);
197#if HAVE_QT6_RECORD
198 if(m_pParameters->m_Record.GetEnableAudio())
199 m_Player.setAudioBufferOutput(&m_AudioBufferOutput);
200#endif
201 m_Player.play();
202 m_nPosition = m_Player.position();
203 m_nDuration = m_Player.duration();
204 break;
205 }
206 default:
207 break;
208 }
209}
210
211void CConnectPlayer::slotStop()
212{
213 qDebug(log) << Q_FUNC_INFO;
214 switch (m_pParameters->GetType()) {
215 case CParameterPlayer::TYPE::Camera:
216 if(m_pCamera)
217 m_pCamera->stop();
218 m_CaptureSession.setVideoSink(nullptr);
219 break;
220 case CParameterPlayer::TYPE::Url:
221 m_Player.stop();
222 m_Player.setVideoSink(nullptr);
223 m_Player.setVideoOutput(nullptr);
224 break;
225 default:
226 break;
227 }
228
229 if(m_pCamera) {
230 delete m_pCamera;
231 m_pCamera = nullptr;
232 }
233#if HAVE_QT6_RECORD
234 slotRecord(false);
235#endif
236}
237
238#if HAVE_QT6_RECORD
239void CConnectPlayer::slotRecord(bool bRecord)
240{
241 qDebug(log) << Q_FUNC_INFO << bRecord;
242
243 if(bRecord) {
244 if(QMediaRecorder::StoppedState != m_Recorder.recorderState()) {
245 return;
246 }
247
248 auto &record = m_pParameters->m_Record;
249 switch (m_pParameters->GetType()) {
250 case CParameterPlayer::TYPE::Camera: {
251 record >> m_Recorder;
252 m_CaptureSession.setRecorder(&m_Recorder);
253 m_Recorder.record();
254 break;
255 }
256 case CParameterPlayer::TYPE::Url: {
257 record >> m_Recorder;
258 if(record.GetEnableAudio()) {
259 m_CaptureSession.setAudioBufferInput(&m_AudioBufferInput);
260 } else
261 qDebug(log) << "Record: disable audio";
262 if(record.GetEnableVideo())
263 m_CaptureSession.setVideoFrameInput(&m_VideoFrameInput);
264 else
265 qDebug(log) << "Record: disable video";
266 m_CaptureSession.setRecorder(&m_Recorder);
267 m_Recorder.record();
268 break;
269 }
270 default:
271 break;
272 }
273#ifndef HAVE_QVideoWidget
274 emit sigRecordVideo(bRecord);
275#endif
276 return;
277 }
278
279 if(QMediaRecorder::StoppedState != m_Recorder.recorderState()) {
280 m_Recorder.stop();
281 m_CaptureSession.setRecorder(nullptr);
282 m_CaptureSession.setVideoFrameInput(nullptr);
283 m_CaptureSession.setAudioBufferInput(nullptr);
284 }
285}
286#endif
287
288void CConnectPlayer::slotClipBoardChanged()
289{
290}
291
292void CConnectPlayer::slotVideoFrameChanged(const QVideoFrame &frame)
293{
294#ifndef HAVE_QVideoWidget
295 if(m_Video.width() != frame.width()
296 || m_Video.height() != frame.height())
297 {
298 m_Video = QRect(0, 0, frame.width(), frame.height());
299 emit sigSetDesktopSize(m_Video.width(), m_Video.height());
300 }
301 QImage img(frame.width(), frame.height(), QImage::Format_ARGB32);
302 QPainter painter(&img);
303 const QVideoFrame::PaintOptions option;
304 QVideoFrame f = frame;
305 f.paint(&painter, m_Video, option);
306 //qDebug(log) << "QVideoSink::videoFrameChanged" << frame << img;
307 emit this->sigUpdateRect(img);
308#endif
309 //qDebug(log) << "QVideoSink::videoFrameChanged" << frame;
310 if(m_bScreenShot) {
311 m_bScreenShot = false;
312 QImage image = frame.toImage();
313 if(image.isNull()) {
314 qCritical(log) << "frame.toImage() fail";
315 } else {
316 QString szFile = m_pParameters->m_Record.GetImageFile(true);
317 if(!image.save(szFile, "PNG"))
318 {
319 qCritical(log) << "Capture image save to file fail." << szFile;
320 return;
321 }
322 qDebug(log) << "Capture image to file:" << szFile;
323 qDebug(log) << "End action:" << m_pParameters->m_Record.GetEndAction();
324 switch(m_pParameters->m_Record.GetEndAction())
325 {
326 case CParameterRecord::ENDACTION::OpenFile: {
327 bool bRet = QDesktopServices::openUrl(QUrl::fromLocalFile(szFile));
328 if(!bRet)
329 qCritical(log) << "Fail: Open capture image file" << szFile;
330 break;
331 }
332 case CParameterRecord::ENDACTION::OpenFolder: {
333 QFileInfo fi(szFile);
334 QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absolutePath()));
335 break;
336 }
337 default:
338 break;
339 }
340 }
341 }
342
343#if defined(HAVE_QT6_RECORD) && defined(HAVE_QVideoWidget)
344 if(QMediaRecorder::RecordingState == m_Recorder.recorderState()) {
345 bool bRet = m_VideoFrameInput.sendVideoFrame(frame);
346 if(!bRet) {
347 //TODO: 放入未成功发送队列,
348 // 当 QVideoFrameInput::readyToSendVideoFrame() 时,再发送
349 qDebug(log) << "m_VideoFrameInput.sendVideoFrame fail";
350 }
351 }
352#endif
353}
354
355void CConnectPlayer::slotEnableAudioInput(bool bEnable)
356{
357 if(bEnable && -1 < m_pParameters->GetAudioInput()
358 && m_pParameters->GetAudioInput() < QMediaDevices::audioInputs().size()) {
359 m_AudioInput.setDevice(QMediaDevices::audioInputs()
360 .at(m_pParameters->GetAudioInput()));
361 m_AudioInput.setMuted(m_pParameters->GetAudioInputMuted());
362 m_AudioInput.setVolume(m_pParameters->GetAudioInputVolume());
363 m_CaptureSession.setAudioInput(&m_AudioInput);
364
365 bool check = connect(m_pParameters,
366 &CParameterPlayer::sigAudioInputMuted,
367 &m_AudioInput, &QAudioInput::setMuted);
368 Q_ASSERT(check);
369 check = connect(m_pParameters, &CParameterPlayer::sigAudioInputVolume,
370 &m_AudioInput, &QAudioInput::setVolume);
371 Q_ASSERT(check);
372 check = connect(m_pParameters, &CParameterPlayer::sigAudioInput,
373 this, [&](int nIndex) {
374 if(-1 < nIndex
375 && nIndex < QMediaDevices::audioInputs().size())
376 m_AudioInput.setDevice(
377 QMediaDevices::audioInputs().at(nIndex));
378 });
379 Q_ASSERT(check);
380 } else {
381 qDebug(log) << "m_CaptureSession: disable audio input";
382 m_CaptureSession.setAudioInput(nullptr);
383 }
384}
385
386void CConnectPlayer::slotEnableAudioOutput(bool bEnable)
387{
388 if(bEnable && (-1 < m_pParameters->GetAudioOutput()
389 && m_pParameters->GetAudioOutput()
390 < QMediaDevices::audioInputs().size()))
391 {
392 m_AudioOutput.setDevice(
393 QMediaDevices::audioOutputs()
394 .at(m_pParameters->GetAudioOutput()));
395 m_AudioOutput.setMuted(m_pParameters->GetAudioOutputMuted());
396 m_AudioOutput.setVolume(m_pParameters->GetAudioOutputVolume());
397 m_AudioOutput.disconnect();
398 bool check = connect(m_pParameters,
399 &CParameterPlayer::sigAudioOutputMuted,
400 &m_AudioOutput, &QAudioOutput::setMuted);
401 Q_ASSERT(check);
402 check = connect(m_pParameters, &CParameterPlayer::sigAudioOutputVolume,
403 &m_AudioOutput, &QAudioOutput::setVolume);
404 Q_ASSERT(check);
405 check = connect(m_pParameters, &CParameterPlayer::sigAudioOutput,
406 this, [&](int nIndex) {
407 if(-1 < nIndex
408 && nIndex < QMediaDevices::audioOutputs().size())
409 m_AudioOutput.setDevice(
410 QMediaDevices::audioOutputs().at(nIndex));
411 });
412 Q_ASSERT(check);
413 switch (m_pParameters->GetType()) {
414 case CParameterPlayer::TYPE::Camera:
415 m_CaptureSession.setAudioOutput(&m_AudioOutput);
416 break;
417 case CParameterPlayer::TYPE::Url:
418 m_Player.setAudioOutput(&m_AudioOutput);
419 break;
420 default:
421 break;
422 }
423 } else {
424 m_Player.setAudioOutput(nullptr);
425 m_CaptureSession.setAudioOutput(nullptr);
426 m_AudioOutput.disconnect();
427 }
428}
429
430void CConnectPlayer::slotPositionChanged(qint64 pos)
431{
432 m_nPosition = pos;
433
434 //qDebug(log) << "Position:" << pos;
435 qint64 currentInfo = pos / 1000;
436 qint64 duration = m_nDuration / 1000;
437 QString szStr;
438 if (currentInfo || duration) {
439 QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, currentInfo % 60,
440 (currentInfo * 1000) % 1000);
441 QTime totalTime((duration / 3600) % 60, (duration / 60) % 60, duration % 60,
442 (duration * 1000) % 1000);
443 QString format = "mm:ss";
444 if (duration > 3600)
445 format = "hh:mm:ss";
446 szStr = currentTime.toString(format) + " / " + totalTime.toString(format);
447 emit sigPositionChanged(m_nPosition, m_nDuration);
448 }
449 //emit sigInformation(tr("Progress: ") + szStr);
450}
451
452void CConnectPlayer::slotDurationChanged(qint64 duration)
453{
454 //qDebug(log) << "Duration:" << duration;
455 m_nDuration = duration;
456}
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.