CustusX  15.3.4-beta
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 
46 typedef vtkSmartPointer<vtkDataSetMapper> vtkDataSetMapperPtr;
47 typedef vtkSmartPointer<vtkImageFlip> vtkImageFlipPtr;
48 
49 namespace cx
50 {
51 
53 {
54  mBackend = backend;
55  mConnected = false;
56  mUnsusedProbeDataVector.clear();
57 
58  connect(mBackend->getToolManager().get(), &TrackingService::stateChanged, this, &VideoConnection::connectVideoToProbe);
59  connect(mBackend->getToolManager().get(), SIGNAL(activeToolChanged(QString)), this, SLOT(connectVideoToProbe()));
60 }
61 
63 {
64  this->stopClient();
65 }
66 
67 void VideoConnection::fpsSlot(QString source, double fpsNumber)
68 {
69  mFPS = fpsNumber;
70  emit fps(source, fpsNumber);
71 }
72 
74 {
75  return mClient && mConnected;
76 }
77 
78 void VideoConnection::connectedSlot(bool on)
79 {
80  mConnected = on;
81 
82  if (on)
83  this->startAllSources();
84  else
85  this->disconnectServer();
86 
87  std::cout << "EMIT CONNECTED " << on << std::endl;
88  emit connected(on);
89 }
90 
91 StreamerServicePtr VideoConnection::getStreamerInterface()
92 {
93  return mStreamerInterface;
94 }
95 
97 {
98  mStreamerInterface.reset(service, null_deleter());//Can't allow boost to delete service
99  ImageReceiverThreadPtr imageReceiverThread(new ImageReceiverThread(mStreamerInterface));
100 
101  this->runClient(imageReceiverThread);
102 }
103 
104 namespace
105 {
106 class EventProcessingThread : public QThread
107 {
108  virtual void run()
109  {
110  this->exec();
111  qApp->processEvents(); // exec() docs doesn't guarantee that the posted events are processed. - do that here.
112  }
113 };
114 }
115 
116 void VideoConnection::runClient(ImageReceiverThreadPtr client)
117 {
118  if (mClient)
119  {
120  std::cout << "client already exist - returning" << std::endl;
121  return;
122  }
123  mClient = client;
124  connect(mClient.get(), SIGNAL(imageReceived()), this, SLOT(imageReceivedSlot())); // thread-bridging connection
125  connect(mClient.get(), SIGNAL(sonixStatusReceived()), this, SLOT(statusReceivedSlot())); // thread-bridging connection
126  connect(mClient.get(), SIGNAL(fps(QString, double)), this, SLOT(fpsSlot(QString, double))); // thread-bridging connection
127  connect(mClient.get(), SIGNAL(connected(bool)), this, SLOT(connectedSlot(bool)));
128 
129  mThread.reset(new EventProcessingThread);
130  mThread->setObjectName("org.custusx.core.video.imagereceiver");
131  mClient->moveToThread(mThread.get());
132  connect(mClient.get(), &ImageReceiverThread::failedToStart, mThread.get(), &QThread::quit);
133  connect(mThread.get(), &QThread::finished, this, &VideoConnection::clientFinishedSlot);
134  QMetaObject::invokeMethod(mClient.get(), "initialize", Qt::QueuedConnection);
135 
136  mThread->start();
137 }
138 
139 void VideoConnection::imageReceivedSlot()
140 {
141  if (!mClient)
142  return;
143  this->updateImage(mClient->getLastImageMessage());
144 }
145 
146 void VideoConnection::statusReceivedSlot()
147 {
148  if (!mClient)
149  return;
150  this->updateStatus(mClient->getLastSonixStatusMessage());
151 }
152 
153 void VideoConnection::stopClient()
154 {
155  if (!mThread)
156  return;
157  QThreadPtr thread = mThread;
158  mThread.reset(); // avoid multiple entries to this method during event processing.
159 
160  QMetaObject::invokeMethod(mClient.get(), "shutdown", Qt::QueuedConnection);
161  QString hostdescription = mClient->hostDescription();
162 
163  thread->quit();
164  int timeout = 2000;
165  int interval = 25;
166  for (unsigned i=0; i<timeout/interval; ++i)
167  {
168  if (!thread->isRunning())
169  break;
170  thread->wait(interval); // forever or until dead thread
171  qApp->processEvents();
172  }
173  if (thread->isRunning())
174  {
175  reportWarning(QString("Video Client [%1] did not quit normally - attempting to terminate.").arg(hostdescription));
176  thread->terminate();
177  thread->wait(); // forever or until dead thread
178  reportWarning(QString("Video Client [%1] did not quit normally - terminated.").arg(hostdescription));
179  }
180  thread.reset();
181 
182  mClient.reset();
183 }
184 
186 {
187  this->stopClient();
188  this->cleanupAfterDisconnectServer();
189 }
190 
191 void VideoConnection::cleanupAfterDisconnectServer()
192 {
193 // this->stopClient();
194  mClient.reset();
195 
196  this->resetProbe();
197 
198  this->stopAllSources();
199 
200  for (unsigned i=0; i<mSources.size(); ++i)
201  mSources[i]->setInput(ImagePtr());
202 
203  ToolPtr tool = mBackend->getToolManager()->getFirstProbe();
204  if (tool && tool->getProbe())
205  this->removeSourceFromProbe(tool);
206 
207  mSources.clear();
208  mStreamerInterface.reset();
209  emit videoSourcesChanged();
210 }
211 
212 void VideoConnection::clientFinishedSlot()
213 {
214  if (!mClient)
215  return;
216 // this->disconnectServer();
217  this->cleanupAfterDisconnectServer();
218 }
219 
220 void VideoConnection::useUnusedProbeDataSlot()
221 {
222  disconnect(mBackend->getToolManager().get(), &TrackingService::stateChanged, this, &VideoConnection::useUnusedProbeDataSlot);
223  for (std::vector<ProbeDefinitionPtr>::const_iterator citer = mUnsusedProbeDataVector.begin(); citer != mUnsusedProbeDataVector.end(); ++citer)
224  this->updateStatus(*citer);
225  mUnsusedProbeDataVector.clear();
226 }
227 
228 void VideoConnection::resetProbe()
229 {
230  ToolPtr tool = mBackend->getToolManager()->getFirstProbe();
231  if (!tool || !tool->getProbe())
232  return;
233  ProbePtr probe = tool->getProbe();
234  if (probe)
235  {
236  ProbeDefinition data = probe->getProbeData();
237  data.setUseDigitalVideo(false);
238  probe->setProbeSector(data);
239  }
240 }
241 
246 void VideoConnection::updateStatus(ProbeDefinitionPtr msg)
247 {
248  ToolPtr tool = mBackend->getToolManager()->getFirstProbe();
249  if (!tool || !tool->getProbe())
250  {
251  //Don't throw away the ProbeData. Save it until it can be used
252  if (mUnsusedProbeDataVector.empty())
253  connect(mBackend->getToolManager().get(), &TrackingService::stateChanged, this, &VideoConnection::useUnusedProbeDataSlot);
254  mUnsusedProbeDataVector.push_back(msg);
255  return;
256  }
257  ProbePtr probe = tool->getProbe();
258 
259  // start with getting a valid data object from the probe, in order to keep
260  // existing values (such as temporal calibration).
261  // Note that the 'active' data is get while the 'uid' data is set.
262  ProbeDefinition data = probe->getProbeData();
263 
264  data.setUid(msg->getUid());
265  data.setType(msg->getType());
266  data.setSector(msg->getDepthStart(), msg->getDepthEnd(), msg->getWidth());
267  data.setOrigin_p(msg->getOrigin_p());
268  data.setSize(msg->getSize());
269  data.setSpacing(msg->getSpacing());
270  data.setClipRect_p(msg->getClipRect_p());
271  data.setUseDigitalVideo(true);
272 
273  probe->setProbeSector(data);
274  probe->setActiveStream(msg->getUid());
275 }
276 
277 void VideoConnection::startAllSources()
278 {
279  for (unsigned i=0; i<mSources.size(); ++i)
280  mSources[i]->start();
281 }
282 
283 void VideoConnection::stopAllSources()
284 {
285  for (unsigned i=0; i<mSources.size(); ++i)
286  mSources[i]->stop();
287 }
288 
289 void VideoConnection::removeSourceFromProbe(ToolPtr tool)
290 {
291  ProbePtr probe = tool->getProbe();
292  for (unsigned i=0; i<mSources.size(); ++i)
293  probe->removeRTSource(mSources[i]);
294 }
295 
296 void VideoConnection::updateImage(ImagePtr message)
297 {
298  BasicVideoSourcePtr source;
299 
300  // look for existing VideoSource
301  for (unsigned i=0; i<mSources.size(); ++i)
302  {
303  if (message && message->getUid() == mSources[i]->getUid())
304  source = mSources[i];
305  }
306 
307  bool newSource = false;
308  // no existing found: create new
309  if (!source)
310  {
311  source.reset(new BasicVideoSource());
312  mSources.push_back(source);
313  source->start();
314  newSource = true;
315  }
316  // set input.
317  source->setInput(message);
318 
319  QString info = mClient->hostDescription() + " - " + QString::number(mFPS, 'f', 1) + " fps";
320  source->setInfoString(info);
321 
322  if (newSource)
323  {
324  this->connectVideoToProbe();
325  emit videoSourcesChanged();
326  }
327 }
328 
329 std::vector<VideoSourcePtr> VideoConnection::getVideoSources()
330 {
331  std::vector<VideoSourcePtr> retval;
332  std::copy(mSources.begin(), mSources.end(), std::back_inserter(retval));
333  return retval;
334 }
335 
343 void VideoConnection::connectVideoToProbe()
344 {
345  ToolPtr tool = mBackend->getToolManager()->getFirstProbe();
346  if (!tool)
347  return;
348 
349  ProbePtr probe = tool->getProbe();
350  if (!probe)
351  return;
352 
353  for (unsigned i=0; i<mSources.size(); ++i)
354  probe->setRTSource(mSources[i]);
355 }
356 
357 }
bool connected(bool)
boost::shared_ptr< BasicVideoSource > BasicVideoSourcePtr
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
boost::shared_ptr< QThread > QThreadPtr
void reportWarning(QString msg)
Definition: cxLogger.cpp:91
Base class for receiving images from a video stream.
virtual bool isConnected() const
vtkSmartPointer< vtkDataSetMapper > vtkDataSetMapperPtr
vtkSmartPointer< vtkImageFlip > vtkImageFlipPtr
boost::shared_ptr< class StreamerService > StreamerServicePtr
void runDirectLinkClient(StreamerService *service)
Abstract class. Interface to Streamers.
boost::shared_ptr< class ImageReceiverThread > ImageReceiverThreadPtr
boost::shared_ptr< class ProbeDefinition > ProbeDefinitionPtr
void fps(QString source, int fps)
boost::shared_ptr< class Tool > ToolPtr