NorMIT-nav  16.5
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxVideoConnection.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 #include "cxVideoConnection.h"
33 
34 #include <vtkDataSetMapper.h>
35 #include <vtkImageFlip.h>
36 
37 #include "cxTrackingService.h"
38 #include "cxBasicVideoSource.h"
39 #include "cxVideoServiceBackend.h"
40 #include "cxNullDeleter.h"
41 #include "cxImageReceiverThread.h"
42 #include "cxImage.h"
43 #include "cxLogger.h"
44 #include <QApplication>
45 #include "boost/function.hpp"
46 #include "boost/bind.hpp"
47 
48 typedef vtkSmartPointer<vtkDataSetMapper> vtkDataSetMapperPtr;
49 typedef vtkSmartPointer<vtkImageFlip> vtkImageFlipPtr;
50 
51 namespace cx
52 {
53 
55 {
56  mBackend = backend;
57  mUnusedProbeDefinitionVector.clear();
58 
59  connect(mBackend->tracking().get(), &TrackingService::stateChanged, this, &VideoConnection::connectVideoToProbe);
60  connect(mBackend->tracking().get(), SIGNAL(activeToolChanged(QString)), this, SLOT(connectVideoToProbe()));
61 }
62 
64 {
65  this->stopClient();
66  this->waitForClientFinished();
67 }
68 
69 void VideoConnection::waitForClientFinished()
70 {
71  if (mThread)
72  {
73  mThread->wait(2000);
74  // NOTE: OpenCV requires a running event loop in the main thread to quit normally.
75  // During system shutdown, this is not the case and we get this warning.
76  // Attempts to solve using qApp->processEvents() has failed due to (lots of) side effects.
77  // Seems to happen with other streamers as well.
78 // CX_LOG_WARNING() << "Video thread finished: " << mThread->isFinished();
79 // CX_LOG_WARNING() << "Video thread running: " << mThread->isRunning();
80 // if (mThread->isRunning())
81 // CX_LOG_WARNING() << "Video thread did not quit normally - ignoring.";
82  }
83 }
84 
85 void VideoConnection::fpsSlot(QString source, double fpsNumber)
86 {
87  mFPS = fpsNumber;
88  emit fps(source, fpsNumber);
89 }
90 
92 {
93  return mThread;
94 }
95 
96 StreamerServicePtr VideoConnection::getStreamerInterface()
97 {
98  return mStreamerInterface;
99 }
100 
101 
102 namespace
103 {
104 class EventProcessingThread : public QThread
105 {
106  virtual void run()
107  {
108  this->exec();
109  qApp->processEvents(); // exec() docs doesn't guarantee that the posted events are processed. - do that here.
110  }
111 };
112 }
113 
115 {
116  if (mClient)
117  {
118  // in this case we already have a working system: ignore
119  CX_LOG_INFO() << "Video client already exists - cannot start";
120  return;
121  }
122  if (mThread)
123  {
124  // in this case we have a thread but no client: probably shutting down: ignore
125  CX_LOG_INFO() << "Video thread already exists (in shutdown?) - cannot start";
126  return;
127  }
128 
129  mStreamerInterface = service;
130  mClient = new ImageReceiverThread(mStreamerInterface);
131 
132  connect(mClient.data(), &ImageReceiverThread::imageReceived, this, &VideoConnection::imageReceivedSlot); // thread-bridging connection
133  connect(mClient.data(), &ImageReceiverThread::sonixStatusReceived, this, &VideoConnection::statusReceivedSlot); // thread-bridging connection
134  connect(mClient.data(), &ImageReceiverThread::fps, this, &VideoConnection::fpsSlot); // thread-bridging connection
135 
136  mThread = new EventProcessingThread;
137  mThread->setObjectName("org.custusx.core.video.imagereceiver");
138  mClient->moveToThread(mThread);
139 
140  connect(mThread.data(), &QThread::started, this, &VideoConnection::onConnected);
141  connect(mThread.data(), &QThread::started, mClient.data(), &ImageReceiverThread::initialize);
142 
143  connect(mClient.data(), &ImageReceiverThread::finished, mThread.data(), &QThread::quit);
144  connect(mClient.data(), &ImageReceiverThread::finished, mClient.data(), &ImageReceiverThread::deleteLater);
145  connect(mThread.data(), &QThread::finished, this, &VideoConnection::onDisconnected);
146  connect(mThread.data(), &QThread::finished, mThread.data(), &QThread::deleteLater);
147 
148  mThread->start();
149 }
150 
151 void VideoConnection::imageReceivedSlot()
152 {
153  if (!mClient)
154  return;
155  this->updateImage(mClient->getLastImageMessage());
156 }
157 
158 void VideoConnection::statusReceivedSlot()
159 {
160  if (!mClient)
161  return;
162  this->updateStatus(mClient->getLastSonixStatusMessage());
163 }
164 
165 void VideoConnection::stopClient()
166 {
167  if (!mThread)
168  return;
169 
170  if (mClient)
171  {
172  disconnect(mClient.data(), &ImageReceiverThread::imageReceived, this, &VideoConnection::imageReceivedSlot); // thread-bridging connection
173  disconnect(mClient.data(), &ImageReceiverThread::sonixStatusReceived, this, &VideoConnection::statusReceivedSlot); // thread-bridging connection
174  disconnect(mClient.data(), &ImageReceiverThread::fps, this, &VideoConnection::fpsSlot); // thread-bridging connection
175 
176  QMetaObject::invokeMethod(mClient, "shutdown", Qt::QueuedConnection);
177 
178  mClient = NULL;
179  }
180 }
181 
183 {
184  this->stopClient();
185 }
186 
187 void VideoConnection::onConnected()
188 {
189  this->startAllSources();
190  emit connected(true);
191 }
192 
193 void VideoConnection::onDisconnected()
194 {
195  mClient = NULL;
196  mThread = NULL; // because this method listens to thread::finished
197 
198  this->resetProbe();
199 
200  this->stopAllSources();
201 
202  for (unsigned i=0; i<mSources.size(); ++i)
203  mSources[i]->setInput(ImagePtr());
204 
205  ToolPtr tool = mBackend->tracking()->getFirstProbe();
206  if (tool && tool->getProbe())
207  this->removeSourceFromProbe(tool);
208 
209  mSources.clear();
210  mStreamerInterface.reset();
211 
212  emit connected(false);
213  emit videoSourcesChanged();
214 }
215 
216 void VideoConnection::useUnusedProbeDefinitionSlot()
217 {
218  disconnect(mBackend->tracking().get(), &TrackingService::stateChanged, this, &VideoConnection::useUnusedProbeDefinitionSlot);
219 
220  std::vector<ProbeDefinitionPtr> unusedProbeDefinitionVector = mUnusedProbeDefinitionVector;
221  mUnusedProbeDefinitionVector.clear();
222 
223  for (unsigned i = 0; i < unusedProbeDefinitionVector.size(); ++i)
224  this->updateStatus(unusedProbeDefinitionVector[i]);
225 }
226 
227 void VideoConnection::resetProbe()
228 {
229  ToolPtr tool = mBackend->tracking()->getFirstProbe();
230  if (!tool || !tool->getProbe())
231  return;
232  ProbePtr probe = tool->getProbe();
233  if (probe)
234  {
235  ProbeDefinition data = probe->getProbeDefinition();
236  data.setUseDigitalVideo(false);
237  probe->setProbeDefinition(data);
238  }
239 }
240 
245 void VideoConnection::updateStatus(ProbeDefinitionPtr msg)
246 {
247  ToolPtr tool = mBackend->tracking()->getFirstProbe();
248  if (!tool || !tool->getProbe())
249  {
250  //Don't throw away the ProbeDefinition. Save it until it can be used
251  if (mUnusedProbeDefinitionVector.empty())
252  connect(mBackend->tracking().get(), &TrackingService::stateChanged, this, &VideoConnection::useUnusedProbeDefinitionSlot);
253  mUnusedProbeDefinitionVector.push_back(msg);
254  return;
255  }
256  ProbePtr probe = tool->getProbe();
257 
258  // start with getting a valid data object from the probe, in order to keep
259  // existing values (such as temporal calibration).
260  // Note that the 'active' data is get while the 'uid' data is set.
261  ProbeDefinition data = probe->getProbeDefinition();
262 
263  data.setUid(msg->getUid());
264  data.setType(msg->getType());
265  data.setSector(msg->getDepthStart(), msg->getDepthEnd(), msg->getWidth());
266  data.setOrigin_p(msg->getOrigin_p());
267  data.setSize(msg->getSize());
268  data.setSpacing(msg->getSpacing());
269  data.setClipRect_p(msg->getClipRect_p());
270  data.setUseDigitalVideo(true);
271 
272  probe->setProbeDefinition(data);
273  probe->setActiveStream(msg->getUid());
274 }
275 
276 void VideoConnection::startAllSources()
277 {
278  for (unsigned i=0; i<mSources.size(); ++i)
279  mSources[i]->start();
280 }
281 
282 void VideoConnection::stopAllSources()
283 {
284  for (unsigned i=0; i<mSources.size(); ++i)
285  mSources[i]->stop();
286 }
287 
288 void VideoConnection::removeSourceFromProbe(ToolPtr tool)
289 {
290  ProbePtr probe = tool->getProbe();
291  for (unsigned i=0; i<mSources.size(); ++i)
292  probe->removeRTSource(mSources[i]);
293 }
294 
295 void VideoConnection::updateImage(ImagePtr message)
296 {
297  BasicVideoSourcePtr source;
298 
299  // look for existing VideoSource
300  for (unsigned i=0; i<mSources.size(); ++i)
301  {
302  if (message && message->getUid() == mSources[i]->getUid())
303  source = mSources[i];
304  }
305 
306  bool newSource = false;
307  // no existing found: create new
308  if (!source)
309  {
310  source.reset(new BasicVideoSource());
311  mSources.push_back(source);
312  source->start();
313  newSource = true;
314  }
315  // set input.
316  source->setInput(message);
317 
318  QString info = mClient->hostDescription() + " - " + QString::number(mFPS, 'f', 1) + " fps";
319  source->setInfoString(info);
320 
321  if (newSource)
322  {
323  this->connectVideoToProbe();
324  emit videoSourcesChanged();
325  }
326 }
327 
328 std::vector<VideoSourcePtr> VideoConnection::getVideoSources()
329 {
330  std::vector<VideoSourcePtr> retval;
331  std::copy(mSources.begin(), mSources.end(), std::back_inserter(retval));
332  return retval;
333 }
334 
342 void VideoConnection::connectVideoToProbe()
343 {
344  ToolPtr tool = mBackend->tracking()->getFirstProbe();
345  if (!tool)
346  return;
347 
348  ProbePtr probe = tool->getProbe();
349  if (!probe)
350  return;
351 
352  for (unsigned i=0; i<mSources.size(); ++i)
353  probe->setRTSource(mSources[i]);
354 }
355 
356 }
bool connected(bool)
void fps(QString, double)
#define CX_LOG_INFO
Definition: cxLogger.h:111
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
VideoConnection(VideoServiceBackendPtr backend)
std::vector< VideoSourcePtr > getVideoSources()
boost::shared_ptr< class VideoServiceBackend > VideoServiceBackendPtr
boost::shared_ptr< Probe > ProbePtr
Definition: cxProbe.h:93
Base class for receiving images from a video stream.
void runDirectLinkClient(StreamerServicePtr service)
virtual bool isConnected() const
vtkSmartPointer< vtkDataSetMapper > vtkDataSetMapperPtr
vtkSmartPointer< vtkImageFlip > vtkImageFlipPtr
boost::shared_ptr< class StreamerService > StreamerServicePtr
boost::shared_ptr< class BasicVideoSource > BasicVideoSourcePtr
boost::shared_ptr< class ProbeDefinition > ProbeDefinitionPtr
void fps(QString source, int fps)
boost::shared_ptr< class Tool > ToolPtr