CustusX  15.4.0-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 #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  mUnsusedProbeDataVector.clear();
58 
59  connect(mBackend->getToolManager().get(), &TrackingService::stateChanged, this, &VideoConnection::connectVideoToProbe);
60  connect(mBackend->getToolManager().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.reset(service, null_deleter());//Can't allow boost to delete service
130  mClient = new ImageReceiverThread(mStreamerInterface);
131 
132  connect(mClient, SIGNAL(imageReceived()), this, SLOT(imageReceivedSlot())); // thread-bridging connection
133  connect(mClient, SIGNAL(sonixStatusReceived()), this, SLOT(statusReceivedSlot())); // thread-bridging connection
134  connect(mClient, SIGNAL(fps(QString, double)), this, SLOT(fpsSlot(QString, double))); // 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, SIGNAL(imageReceived()), this, SLOT(imageReceivedSlot())); // thread-bridging connection
173  disconnect(mClient, SIGNAL(sonixStatusReceived()), this, SLOT(statusReceivedSlot())); // thread-bridging connection
174  disconnect(mClient, SIGNAL(fps(QString, double)), this, SLOT(fpsSlot(QString, double))); // 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->getToolManager()->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::useUnusedProbeDataSlot()
217 {
218  disconnect(mBackend->getToolManager().get(), &TrackingService::stateChanged, this, &VideoConnection::useUnusedProbeDataSlot);
219  for (std::vector<ProbeDefinitionPtr>::const_iterator citer = mUnsusedProbeDataVector.begin(); citer != mUnsusedProbeDataVector.end(); ++citer)
220  this->updateStatus(*citer);
221  mUnsusedProbeDataVector.clear();
222 }
223 
224 void VideoConnection::resetProbe()
225 {
226  ToolPtr tool = mBackend->getToolManager()->getFirstProbe();
227  if (!tool || !tool->getProbe())
228  return;
229  ProbePtr probe = tool->getProbe();
230  if (probe)
231  {
232  ProbeDefinition data = probe->getProbeData();
233  data.setUseDigitalVideo(false);
234  probe->setProbeSector(data);
235  }
236 }
237 
242 void VideoConnection::updateStatus(ProbeDefinitionPtr msg)
243 {
244  ToolPtr tool = mBackend->getToolManager()->getFirstProbe();
245  if (!tool || !tool->getProbe())
246  {
247  //Don't throw away the ProbeData. Save it until it can be used
248  if (mUnsusedProbeDataVector.empty())
249  connect(mBackend->getToolManager().get(), &TrackingService::stateChanged, this, &VideoConnection::useUnusedProbeDataSlot);
250  mUnsusedProbeDataVector.push_back(msg);
251  return;
252  }
253  ProbePtr probe = tool->getProbe();
254 
255  // start with getting a valid data object from the probe, in order to keep
256  // existing values (such as temporal calibration).
257  // Note that the 'active' data is get while the 'uid' data is set.
258  ProbeDefinition data = probe->getProbeData();
259 
260  data.setUid(msg->getUid());
261  data.setType(msg->getType());
262  data.setSector(msg->getDepthStart(), msg->getDepthEnd(), msg->getWidth());
263  data.setOrigin_p(msg->getOrigin_p());
264  data.setSize(msg->getSize());
265  data.setSpacing(msg->getSpacing());
266  data.setClipRect_p(msg->getClipRect_p());
267  data.setUseDigitalVideo(true);
268 
269  probe->setProbeSector(data);
270  probe->setActiveStream(msg->getUid());
271 }
272 
273 void VideoConnection::startAllSources()
274 {
275  for (unsigned i=0; i<mSources.size(); ++i)
276  mSources[i]->start();
277 }
278 
279 void VideoConnection::stopAllSources()
280 {
281  for (unsigned i=0; i<mSources.size(); ++i)
282  mSources[i]->stop();
283 }
284 
285 void VideoConnection::removeSourceFromProbe(ToolPtr tool)
286 {
287  ProbePtr probe = tool->getProbe();
288  for (unsigned i=0; i<mSources.size(); ++i)
289  probe->removeRTSource(mSources[i]);
290 }
291 
292 void VideoConnection::updateImage(ImagePtr message)
293 {
294  BasicVideoSourcePtr source;
295 
296  // look for existing VideoSource
297  for (unsigned i=0; i<mSources.size(); ++i)
298  {
299  if (message && message->getUid() == mSources[i]->getUid())
300  source = mSources[i];
301  }
302 
303  bool newSource = false;
304  // no existing found: create new
305  if (!source)
306  {
307  source.reset(new BasicVideoSource());
308  mSources.push_back(source);
309  source->start();
310  newSource = true;
311  }
312  // set input.
313  source->setInput(message);
314 
315  QString info = mClient->hostDescription() + " - " + QString::number(mFPS, 'f', 1) + " fps";
316  source->setInfoString(info);
317 
318  if (newSource)
319  {
320  this->connectVideoToProbe();
321  emit videoSourcesChanged();
322  }
323 }
324 
325 std::vector<VideoSourcePtr> VideoConnection::getVideoSources()
326 {
327  std::vector<VideoSourcePtr> retval;
328  std::copy(mSources.begin(), mSources.end(), std::back_inserter(retval));
329  return retval;
330 }
331 
339 void VideoConnection::connectVideoToProbe()
340 {
341  ToolPtr tool = mBackend->getToolManager()->getFirstProbe();
342  if (!tool)
343  return;
344 
345  ProbePtr probe = tool->getProbe();
346  if (!probe)
347  return;
348 
349  for (unsigned i=0; i<mSources.size(); ++i)
350  probe->setRTSource(mSources[i]);
351 }
352 
353 }
bool connected(bool)
boost::shared_ptr< BasicVideoSource > BasicVideoSourcePtr
#define CX_LOG_INFO
Definition: cxLogger.h:110
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.
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 ProbeDefinition > ProbeDefinitionPtr
void fps(QString source, int fps)
boost::shared_ptr< class Tool > ToolPtr