blob: dfd095bbbf98341ae07f427a279302ad33474c84 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001/*
2 * Copyright (C) 2019-2020 by Savoir-faire Linux
3 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
4 * Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "rendermanager.h"
21
22#include <QtConcurrent/QtConcurrent>
23
24#include <stdexcept>
25
26using namespace lrc::api;
27
28FrameWrapper::FrameWrapper(AVModel &avModel, const QString &id)
29 : avModel_(avModel)
30 , id_(id)
31 , isRendering_(false)
32{}
33
34FrameWrapper::~FrameWrapper()
35{
36 if (id_ == video::PREVIEW_RENDERER_ID) {
37 avModel_.stopPreview();
38 }
39}
40
41void
42FrameWrapper::connectStartRendering()
43{
44 QObject::disconnect(renderConnections_.started);
45 renderConnections_.started = QObject::connect(&avModel_,
46 &AVModel::rendererStarted,
47 this,
48 &FrameWrapper::slotRenderingStarted);
49}
50
51bool
52FrameWrapper::startRendering()
53{
54 try {
55 renderer_ = const_cast<video::Renderer *>(&avModel_.getRenderer(id_));
56 } catch (std::out_of_range &e) {
57 qWarning() << e.what();
58 return false;
59 }
60
61 QObject::disconnect(renderConnections_.updated);
62 QObject::disconnect(renderConnections_.stopped);
63
64 renderConnections_.updated = QObject::connect(&avModel_,
65 &AVModel::frameUpdated,
66 this,
67 &FrameWrapper::slotFrameUpdated);
68
69 renderConnections_.stopped = QObject::connect(&avModel_,
70 &AVModel::rendererStopped,
71 this,
72 &FrameWrapper::slotRenderingStopped);
73
74 return true;
75}
76
77QImage *
78FrameWrapper::getFrame()
79{
80 return image_.get();
81}
82
83bool
84FrameWrapper::isRendering()
85{
86 return isRendering_;
87}
88
89void
90FrameWrapper::slotRenderingStarted(const QString &id)
91{
92 if (id != id_) {
93 return;
94 }
95
96 if (!startRendering()) {
97 qWarning() << "Couldn't start rendering for id: " << id_;
98 return;
99 }
100
101 isRendering_ = true;
102
103 emit renderingStarted(id);
104}
105
106void
107FrameWrapper::slotFrameUpdated(const QString &id)
108{
109 if (id != id_) {
110 return;
111 }
112
113 if (!renderer_ || !renderer_->isRendering()) {
114 return;
115 }
116
117 {
118 QMutexLocker lock(&mutex_);
119
120 frame_ = renderer_->currentFrame();
121
122 unsigned int width = renderer_->size().width();
123 unsigned int height = renderer_->size().height();
124
125#ifndef Q_OS_LINUX
126 unsigned int size = frame_.storage.size();
127 /*
128 * If the frame is empty or not the expected size,
129 * do nothing and keep the last rendered QImage.
130 */
131 if (size != 0 && size == width * height * 4) {
132 buffer_ = std::move(frame_.storage);
133 image_.reset(new QImage((uchar *) buffer_.data(),
134 width,
135 height,
136 QImage::Format_ARGB32_Premultiplied));
137#else
138 if (frame_.ptr) {
139 image_.reset(new QImage(frame_.ptr, width, height, QImage::Format_ARGB32));
140#endif
141 }
142 }
143
144 emit frameUpdated(id);
145}
146
147void
148FrameWrapper::slotRenderingStopped(const QString &id)
149{
150 if (id != id_) {
151 return;
152 }
153
154 QObject::disconnect(renderConnections_.updated);
155 QObject::disconnect(renderConnections_.stopped);
156 renderer_ = nullptr;
157
158 /*
159 * The object's QImage pointer is reset before renderingStopped
160 * is emitted, allowing the listener to invoke specific behavior
161 * like clearing the widget or changing the UI entirely.
162 */
163 image_.reset();
164
165 isRendering_ = false;
166
167 emit renderingStopped(id);
168}
169
170RenderManager::RenderManager(AVModel &avModel)
171 : avModel_(avModel)
172{
173 deviceListSize_ = avModel_.getDevices().size();
174 connect(&avModel_, &lrc::api::AVModel::deviceEvent, this, &RenderManager::slotDeviceEvent);
175
176 previewFrameWrapper_ = std::make_unique<FrameWrapper>(avModel_);
177
178 QObject::connect(previewFrameWrapper_.get(),
179 &FrameWrapper::renderingStarted,
180 [this](const QString &id) {
181 Q_UNUSED(id);
182 emit previewRenderingStarted();
183 });
184 QObject::connect(previewFrameWrapper_.get(),
185 &FrameWrapper::frameUpdated,
186 [this](const QString &id) {
187 Q_UNUSED(id);
188 emit previewFrameUpdated();
189 });
190 QObject::connect(previewFrameWrapper_.get(),
191 &FrameWrapper::renderingStopped,
192 [this](const QString &id) {
193 Q_UNUSED(id);
194 emit previewRenderingStopped();
195 });
196
197 previewFrameWrapper_->connectStartRendering();
198}
199
200RenderManager::~RenderManager()
201{
202 previewFrameWrapper_.reset();
203
204 for (auto &dfw : distantFrameWrapperMap_) {
205 dfw.second.reset();
206 }
207}
208
209bool
210RenderManager::isPreviewing()
211{
212 return previewFrameWrapper_->isRendering();
213}
214
215QImage *
216RenderManager::getPreviewFrame()
217{
218 return previewFrameWrapper_->getFrame();
219}
220
221void
222RenderManager::stopPreviewing(bool async)
223{
224 if (!previewFrameWrapper_->isRendering()) {
225 return;
226 }
227
228 if (async) {
229 QtConcurrent::run([this] { avModel_.stopPreview(); });
230 } else {
231 avModel_.stopPreview();
232 }
233}
234
235void
236RenderManager::startPreviewing(bool force, bool async)
237{
238 if (previewFrameWrapper_->isRendering() && !force) {
239 return;
240 }
241
242 auto restart = [this] {
243 if (previewFrameWrapper_->isRendering()) {
244 avModel_.stopPreview();
245 }
246 avModel_.startPreview();
247 };
248 if (async) {
249 QtConcurrent::run(restart);
250 } else {
251 restart();
252 }
253}
254
255QImage *
256RenderManager::getFrame(const QString &id)
257{
258 auto dfwIt = distantFrameWrapperMap_.find(id);
259 if (dfwIt != distantFrameWrapperMap_.end()) {
260 return dfwIt->second->getFrame();
261 }
262 return nullptr;
263}
264
265void
266RenderManager::addDistantRenderer(const QString &id)
267{
268 /*
269 * Check if a FrameWrapper with this id exists.
270 */
271 auto dfwIt = distantFrameWrapperMap_.find(id);
272 if (dfwIt != distantFrameWrapperMap_.end()) {
273 if (!dfwIt->second->startRendering()) {
274 qWarning() << "Couldn't start rendering for id: " << id;
275 }
276 } else {
277 auto dfw = std::make_unique<FrameWrapper>(avModel_, id);
278
279 /*
280 * Connect this to the FrameWrapper.
281 */
282 distantConnectionMap_[id].started = QObject::connect(dfw.get(),
283 &FrameWrapper::renderingStarted,
284 [this](const QString &id) {
285 emit distantRenderingStarted(id);
286 });
287 distantConnectionMap_[id].updated = QObject::connect(dfw.get(),
288 &FrameWrapper::frameUpdated,
289 [this](const QString &id) {
290 emit distantFrameUpdated(id);
291 });
292 distantConnectionMap_[id].stopped = QObject::connect(dfw.get(),
293 &FrameWrapper::renderingStopped,
294 [this](const QString &id) {
295 emit distantRenderingStopped(id);
296 });
297
298 /*
299 * Connect FrameWrapper to avmodel.
300 */
301 dfw->connectStartRendering();
302
303 /*
304 * Add to map.
305 */
306 distantFrameWrapperMap_.insert(std::make_pair(id, std::move(dfw)));
307 }
308}
309
310void
311RenderManager::removeDistantRenderer(const QString &id)
312{
313 auto dfwIt = distantFrameWrapperMap_.find(id);
314 if (dfwIt != distantFrameWrapperMap_.end()) {
315 /*
316 * Disconnect FrameWrapper from this.
317 */
318 auto dcIt = distantConnectionMap_.find(id);
319 if (dcIt != distantConnectionMap_.end()) {
320 QObject::disconnect(dcIt->second.started);
321 QObject::disconnect(dcIt->second.updated);
322 QObject::disconnect(dcIt->second.stopped);
323 }
324
325 /*
326 * Erase.
327 */
328 distantFrameWrapperMap_.erase(dfwIt);
329 }
330}
331
332void
333RenderManager::slotDeviceEvent()
334{
335 auto defaultDevice = avModel_.getDefaultDevice();
336 auto currentCaptureDevice = avModel_.getCurrentVideoCaptureDevice();
337 /*
338 * Decide whether a device has plugged, unplugged, or nothing has changed.
339 */
340 auto deviceList = avModel_.getDevices();
341 auto currentDeviceListSize = deviceList.size();
342
343 DeviceEvent deviceEvent{DeviceEvent::None};
344 if (currentDeviceListSize > deviceListSize_) {
345 deviceEvent = DeviceEvent::Added;
346 } else if (currentDeviceListSize < deviceListSize_) {
347 /*
348 * Check if the currentCaptureDevice is still in the device list.
349 */
350 if (std::find(std::begin(deviceList), std::end(deviceList), currentCaptureDevice)
351 == std::end(deviceList)) {
352 deviceEvent = DeviceEvent::RemovedCurrent;
353 }
354 }
355
356 if (previewFrameWrapper_->isRendering()) {
357 if (currentDeviceListSize == 0) {
358 avModel_.clearCurrentVideoCaptureDevice();
359 avModel_.switchInputTo({});
360 stopPreviewing();
361 } else if (deviceEvent == DeviceEvent::RemovedCurrent && currentDeviceListSize > 0) {
362 avModel_.setCurrentVideoCaptureDevice(defaultDevice);
363 startPreviewing(true);
364 } else {
365 startPreviewing();
366 }
367 } else if (deviceEvent == DeviceEvent::Added && currentDeviceListSize == 1) {
368 avModel_.setCurrentVideoCaptureDevice(defaultDevice);
369 }
370
371 emit videoDeviceListChanged();
372
373 deviceListSize_ = currentDeviceListSize;
374}