+
+
+void
+FilmViewer::slider_moved (wxScrollEvent &)
+{
+ if (!_film || !_player) {
+ return;
+ }
+
+ if (_player->seek (_slider->GetValue() * _film->video_length() / (4096 * _film->video_frame_rate()))) {
+ return;
+ }
+
+ get_frame ();
+ _panel->Refresh ();
+ _panel->Update ();
+}
+
+void
+FilmViewer::panel_sized (wxSizeEvent& ev)
+{
+ _panel_size.width = ev.GetSize().GetWidth();
+ _panel_size.height = ev.GetSize().GetHeight();
+ calculate_sizes ();
+ update_from_raw ();
+}
+
+void
+FilmViewer::update_from_raw ()
+{
+ if (!_raw_frame) {
+ return;
+ }
+
+ raw_to_display ();
+
+ _panel->Refresh ();
+ _panel->Update ();
+}
+
+void
+FilmViewer::raw_to_display ()
+{
+ if (!_raw_frame || _out_size.width < 64 || _out_size.height < 64 || !_film) {
+ return;
+ }
+
+ shared_ptr<Image> input = _raw_frame;
+
+ pair<string, string> const s = Filter::ffmpeg_strings (_film->filters());
+ if (!s.second.empty ()) {
+ input = input->post_process (s.second, true);
+ }
+
+ /* Get a compacted image as we have to feed it to wxWidgets */
+ _display_frame = input->scale_and_convert_to_rgb (_film_size, 0, _film->scaler(), false);
+
+ if (_raw_sub) {
+
+ /* Our output is already cropped by the decoder, so we need to account for that
+ when working out the scale that we are applying.
+ */
+
+ Size const cropped_size = _film->cropped_size (_film->video_size ());
+
+ Rect tx = subtitle_transformed_area (
+ float (_film_size.width) / cropped_size.width,
+ float (_film_size.height) / cropped_size.height,
+ _raw_sub->area(), _film->subtitle_offset(), _film->subtitle_scale()
+ );
+
+ _display_sub.reset (new RGBPlusAlphaImage (_raw_sub->image()->scale (tx.size(), _film->scaler(), false)));
+ _display_sub_position = tx.position();
+ _display_sub_position.x += _display_frame_x;
+ } else {
+ _display_sub.reset ();
+ }
+}
+
+void
+FilmViewer::calculate_sizes ()
+{
+ if (!_film || !_player) {
+ return;
+ }
+
+ Format const * format = _film->format ();
+
+ float const panel_ratio = static_cast<float> (_panel_size.width) / _panel_size.height;
+ float const film_ratio = format ? format->container_ratio_as_float () : 1.78;
+
+ if (panel_ratio < film_ratio) {
+ /* panel is less widscreen than the film; clamp width */
+ _out_size.width = _panel_size.width;
+ _out_size.height = _out_size.width / film_ratio;
+ } else {
+ /* panel is more widescreen than the film; clamp height */
+ _out_size.height = _panel_size.height;
+ _out_size.width = _out_size.height * film_ratio;
+ }
+
+ /* Work out how much padding there is in terms of our display; this will be the x position
+ of our _display_frame.
+ */
+ _display_frame_x = 0;
+ if (format) {
+ _display_frame_x = static_cast<float> (format->dcp_padding (_film)) * _out_size.width / format->dcp_size().width;
+ }
+
+ _film_size = _out_size;
+ _film_size.width -= _display_frame_x * 2;
+
+ /* Catch silly values */
+ if (_out_size.width < 64) {
+ _out_size.width = 64;
+ }
+}
+
+void
+FilmViewer::play_clicked (wxCommandEvent &)
+{
+ check_play_state ();
+}
+
+void
+FilmViewer::check_play_state ()
+{
+ if (!_film) {
+ return;
+ }
+
+ if (_play_button->GetValue()) {
+ _timer.Start (1000 / _film->video_frame_rate());
+ } else {
+ _timer.Stop ();
+ }
+}
+
+void
+FilmViewer::process_video (shared_ptr<Image> image, bool, shared_ptr<Subtitle> sub)
+{
+ _raw_frame = image;
+ _raw_sub = sub;
+
+ raw_to_display ();
+
+ _got_frame = true;
+}
+
+/** Get a new _raw_frame from the decoder and then do
+ * raw_to_display ().
+ */
+void
+FilmViewer::get_frame ()
+{
+ /* Clear our raw frame in case we don't get a new one */
+ _raw_frame.reset ();
+
+ if (!_player) {
+ _display_frame.reset ();
+ return;
+ }
+
+ try {
+ _got_frame = false;
+ while (!_got_frame) {
+ if (_player->pass ()) {
+ /* We didn't get a frame before the decoder gave up,
+ so clear our display frame.
+ */
+ _display_frame.reset ();
+ break;
+ }
+ }
+ } catch (DecodeError& e) {
+ _play_button->SetValue (false);
+ check_play_state ();
+ error_dialog (this, wxString::Format (_("Could not decode video for view (%s)"), std_to_wx(e.what()).data()));
+ }
+}
+
+void
+FilmViewer::active_jobs_changed (bool a)
+{
+ if (a) {
+ list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
+ list<shared_ptr<Job> >::iterator i = jobs.begin ();
+ while (i != jobs.end() && boost::dynamic_pointer_cast<ExamineContentJob> (*i) == 0) {
+ ++i;
+ }
+
+ if (i == jobs.end() || (*i)->finished()) {
+ /* no examine content job running, so we're ok to use the viewer */
+ a = false;
+ }
+ }
+
+ _slider->Enable (!a);
+ _play_button->Enable (!a);
+}
+