video_playback.cpp
Go to the documentation of this file.
1 //=================================================================================================
2 // Copyright (c) 2013, Johannes Meyer, TU Darmstadt
3 // All rights reserved.
4 
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright
10 // notice, this list of conditions and the following disclaimer in the
11 // documentation and/or other materials provided with the distribution.
12 // * Neither the name of the Flight Systems and Automatic Control group,
13 // TU Darmstadt, nor the names of its contributors may be used to
14 // endorse or promote products derived from this software without
15 // specific prior written permission.
16 
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //=================================================================================================
28 
31 
32 #include <QFileDialog>
33 
34 namespace rqt_video_playback {
35 
37  : rqt_gui_cpp::Plugin()
38  , widget_(0)
39  , step_time_(100)
40  , time_tolerance_(100)
41 {
42  setObjectName("VideoPlayback");
43 }
44 
46 {
47 }
48 
50 {
51  widget_ = new QWidget();
52  ui_.setupUi(widget_);
53 
54  if (context.serialNumber() > 1)
55  {
56  widget_->setWindowTitle(widget_->windowTitle() + " (" + QString::number(context.serialNumber()) + ")");
57  }
58  context.addWidget(widget_);
59 
60  // connect UI elements
61  connect(ui_.open, SIGNAL(clicked()), this, SLOT(openClicked()));
62  connect(ui_.play, SIGNAL(clicked()), this, SLOT(playClicked()));
63  connect(ui_.previous, SIGNAL(clicked()), this, SLOT(previousClicked()));
64  connect(ui_.next, SIGNAL(clicked()), this, SLOT(nextClicked()));
65  connect(ui_.slider, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
66  connect(ui_.sync, SIGNAL(clicked()), this, SLOT(sync()));
67 
68  // configure video player
69  mediaObject()->setTickInterval(100);
70  connect(mediaObject(), SIGNAL(stateChanged(Phonon::State, Phonon::State)), this, SLOT(stateChanged(Phonon::State, Phonon::State)));
71  connect(mediaObject(), SIGNAL(currentSourceChanged(Phonon::MediaSource)), this, SLOT(currentSourceChanged(Phonon::MediaSource)));
72  connect(mediaObject(), SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64)));
73 
74  // connect clock update
75  connect(this, SIGNAL(clockUpdated()), this, SLOT(sync()));
76  subscriber_ = getNodeHandle().subscribe<rosgraph_msgs::Clock>("/clock", 1, boost::bind(&VideoPlayback::callbackClock, this, _1));
77 }
78 
79 bool VideoPlayback::eventFilter(QObject* watched, QEvent* event)
80 {
81 // if (watched == ui_.image_frame && event->type() == QEvent::Paint)
82 // {
83 // QPainter painter(ui_.image_frame);
84 // if (!qimage_.isNull())
85 // {
86 // // TODO: check if full draw is really necessary
87 // //QPaintEvent* paint_event = dynamic_cast<QPaintEvent*>(event);
88 // //painter.drawImage(paint_event->rect(), qimage_);
89 // qimage_mutex_.lock();
90 // painter.drawImage(ui_.image_frame->contentsRect(), qimage_);
91 // qimage_mutex_.unlock();
92 // } else {
93 // // default image with gradient
94 // QLinearGradient gradient(0, 0, ui_.image_frame->frameRect().width(), ui_.image_frame->frameRect().height());
95 // gradient.setColorAt(0, Qt::white);
96 // gradient.setColorAt(1, Qt::black);
97 // painter.setBrush(gradient);
98 // painter.drawRect(0, 0, ui_.image_frame->frameRect().width() + 1, ui_.image_frame->frameRect().height() + 1);
99 // }
100 // return false;
101 // }
102 
103  return QObject::eventFilter(watched, event);
104 }
105 
107 {
109  ui_.videoPlayer->stop();
110  ui_.videoPlayer->close();
111 }
112 
113 void VideoPlayback::saveSettings(qt_gui_cpp::Settings& plugin_settings, qt_gui_cpp::Settings& instance_settings) const
114 {
115 // QString topic = ui_.topics_combo_box->currentText();
116 // //qDebug("ImageView::saveSettings() topic '%s'", topic.toStdString().c_str());
117 // instance_settings.setValue("topic", topic);
118 // instance_settings.setValue("zoom1", ui_.zoom_1_push_button->isChecked());
119 // instance_settings.setValue("dynamic_range", ui_.dynamic_range_check_box->isChecked());
120 // instance_settings.setValue("max_range", ui_.max_range_double_spin_box->value());
121 }
122 
123 void VideoPlayback::restoreSettings(const qt_gui_cpp::Settings& plugin_settings, const qt_gui_cpp::Settings& instance_settings)
124 {
125 // bool zoom1_checked = instance_settings.value("zoom1", false).toBool();
126 // ui_.zoom_1_push_button->setChecked(zoom1_checked);
127 
128 // bool dynamic_range_checked = instance_settings.value("dynamic_range", false).toBool();
129 // ui_.dynamic_range_check_box->setChecked(dynamic_range_checked);
130 
131 // double max_range = instance_settings.value("max_range", ui_.max_range_double_spin_box->value()).toDouble();
132 // ui_.max_range_double_spin_box->setValue(max_range);
133 
134 // QString topic = instance_settings.value("topic", "").toString();
135 // //qDebug("ImageView::restoreSettings() topic '%s'", topic.toStdString().c_str());
136 // selectTopic(topic);
137 }
138 
140 {
141  QString filter;
142 
143  QString fileName = QFileDialog::getOpenFileName(widget_, tr("Open Movie"),
144  QDir::homePath(), filter);
145 
146  if (fileName.isEmpty()) return;
147 
148  ui_.videoPlayer->load(fileName);
149  ui_.sync->setChecked(false);
150  sync();
151 }
152 
154 {
155  switch(mediaObject()->state()) {
156  case Phonon::StoppedState:
157  ui_.videoPlayer->play();
158  break;
159  case Phonon::PausedState:
160  ui_.videoPlayer->play();
161  break;
162  case Phonon::PlayingState:
163  ui_.videoPlayer->pause();
164  break;
165  default:
166  qDebug() << "Unhandled Phonon state: " << mediaObject()->state();
167  break;
168  }
169 }
170 
171 //void VideoPlayer::movieStateChanged(QMovie::MovieState state)
172 //{
173 // switch(state) {
174 // case QMovie::NotRunning:
175 // case QMovie::Paused:
176 // playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
177 // break;
178 // case QMovie::Running:
179 // playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
180 // break;
181 // }
182 //}
183 
184 //void VideoPlayer::frameChanged(int frame)
185 //{
186 // if (!presentImage(movie.currentImage())) {
187 // movie.stop();
188 // playButton->setEnabled(false);
189 // positionSlider->setMaximum(0);
190 // } else {
191 // positionSlider->setValue(frame);
192 // }
193 //}
194 
196 {
197  qint64 new_time = ui_.videoPlayer->currentTime() - step_time_;
198  if (new_time > 0)
199  ui_.videoPlayer->seek(new_time);
200 }
201 
203 {
204  qint64 new_time = ui_.videoPlayer->currentTime() + step_time_;
205  if (new_time < ui_.videoPlayer->totalTime())
206  ui_.videoPlayer->seek(new_time);
207 }
208 
210 {
211  if (ui_.videoPlayer->currentTime() != value)
212  ui_.videoPlayer->seek(value);
213 }
214 
216 {
217  uint32_t sec, nsec;
218  sec = time / 1000ll;
219  nsec = (time - sec * 1000ll) * 1000000;
220  return ros::Duration(sec, nsec);
221 }
222 
223 qint64 VideoPlayback::fromRos(const ros::Duration& duration)
224 {
225  return (qint64(duration.sec) * 1000ll + qint64(duration.nsec) / 1000000ll);
226 }
227 
229 {
230  tick(ui_.videoPlayer->mediaObject()->currentTime());
231 }
232 
234 {
235  ui_.slider->setSliderPosition(tick);
236 
237  qint64 currentVideo = tick;
238  qint64 totalTime = ui_.videoPlayer->totalTime();
239 
240  // is a video source loaded?
241  if (ui_.videoPlayer->mediaObject()->currentSource().type() == Phonon::MediaSource::Empty) {
242  ui_.controls->setEnabled(false);
243  return;
244  }
245 
246  // adjust start time if Sync is not checked
247  if (!ui_.sync->isChecked()) {
248  ui_.controls->setEnabled(true);
249 
250  if (!clock_.isZero()) start_ = clock_ - toRos(currentVideo);
251  return;
252  }
253 
254  // sync video with ROS clock time
255  ui_.controls->setEnabled(false);
256 
257  // wait until clock is available
258  if (clock_.isZero()) {
259  ui_.videoPlayer->pause();
260  return;
261  }
262 
263  qint64 currentRos = fromRos(clock_ - start_);
264 
265  std::cout << "video: " << currentVideo << "/" << totalTime << ", ROS: " << currentRos << " => diff: " << (currentVideo - currentRos) << std::endl;
266 
267  if (currentVideo < currentRos - time_tolerance_) {
268  if (currentRos < totalTime) {
269  ui_.videoPlayer->seek(currentRos);
270  ui_.videoPlayer->play();
271  } else {
272  ui_.videoPlayer->pause();
273  }
274  }
275 
276  if (currentVideo > currentRos + time_tolerance_) {
277  if (currentRos >= 0) {
278  ui_.videoPlayer->seek(currentRos);
279  ui_.videoPlayer->play();
280  } else {
281  ui_.videoPlayer->pause();
282  }
283  }
284 }
285 
286 void VideoPlayback::stateChanged(Phonon::State newstate, Phonon::State oldstate)
287 {
288  switch(newstate) {
289  case Phonon::PlayingState:
290  ui_.play->setIcon(QIcon::fromTheme("media-playback-pause"));
291  break;
292 
293  case Phonon::StoppedState:
294  ui_.play->setIcon(QIcon::fromTheme("media-playback-start"));
295  break;
296 
297  case Phonon::PausedState:
298  ui_.play->setIcon(QIcon::fromTheme("media-playback-start"));
299  break;
300  }
301 }
302 
304 {
305  ui_.slider->setMaximum(total);
306 }
307 
308 void VideoPlayback::currentSourceChanged(Phonon::MediaSource)
309 {
310  sync();
311 }
312 
313 void VideoPlayback::callbackClock(const rosgraph_msgs::Clock::ConstPtr& clock)
314 {
315  clock_ = clock->clock;
316  clockUpdated();
317 }
318 
319 } // namespace rqt_video_playback
320 
virtual void callbackClock(const rosgraph_msgs::Clock::ConstPtr &msg)
virtual void initPlugin(qt_gui_cpp::PluginContext &context)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
ros::Duration toRos(qint64 time)
void stateChanged(Phonon::State newstate, Phonon::State oldstate)
void addWidget(QWidget *widget)
virtual void restoreSettings(const qt_gui_cpp::Settings &plugin_settings, const qt_gui_cpp::Settings &instance_settings)
virtual bool eventFilter(QObject *watched, QEvent *event)
qint64 fromRos(const ros::Duration &duration)
void currentSourceChanged(Phonon::MediaSource)
ros::NodeHandle & getNodeHandle() const
Phonon::MediaObject * mediaObject() const
virtual void saveSettings(qt_gui_cpp::Settings &plugin_settings, qt_gui_cpp::Settings &instance_settings) const
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)


rqt_video_playback
Author(s): Johannes Meyer
autogenerated on Mon Jun 10 2019 13:36:43