Move FilmViewer::get() into SimpleVideoView.
[dcpomatic.git] / src / wx / simple_video_view.cc
1 /*
2     Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "simple_video_view.h"
22 #include "film_viewer.h"
23 #include "wx_util.h"
24 #include "lib/image.h"
25 #include "lib/dcpomatic_log.h"
26 #include "lib/butler.h"
27 #include <dcp/util.h>
28 #include <wx/wx.h>
29 #include <boost/bind.hpp>
30
31 using std::max;
32 using std::string;
33 using boost::optional;
34 using namespace dcpomatic;
35
36 SimpleVideoView::SimpleVideoView (FilmViewer* viewer, wxWindow* parent)
37         : VideoView (viewer)
38 {
39         _panel = new wxPanel (parent);
40
41 #ifndef __WXOSX__
42         _panel->SetDoubleBuffered (true);
43 #endif
44
45         _panel->SetBackgroundStyle (wxBG_STYLE_PAINT);
46         _panel->SetBackgroundColour (*wxBLACK);
47
48         _panel->Bind (wxEVT_PAINT, boost::bind (&SimpleVideoView::paint, this));
49         _panel->Bind (wxEVT_SIZE, boost::bind(boost::ref(Sized)));
50
51         _timer.Bind (wxEVT_TIMER, boost::bind(&SimpleVideoView::timer, this));
52 }
53
54 void
55 SimpleVideoView::paint ()
56 {
57         _viewer->state_timer().set("paint-panel");
58         wxPaintDC dc (_panel);
59
60         dcp::Size const out_size = _viewer->out_size ();
61         wxSize const panel_size = _panel->GetSize ();
62
63 #ifdef DCPOMATIC_VARIANT_SWAROOP
64         if (_viewer->background_image()) {
65                 dc.Clear ();
66                 optional<boost::filesystem::path> bg = Config::instance()->player_background_image();
67                 if (bg) {
68                         wxImage image (std_to_wx(bg->string()));
69                         wxBitmap bitmap (image);
70                         dc.DrawBitmap (bitmap, max(0, (panel_size.GetWidth() - image.GetSize().GetWidth()) / 2), max(0, (panel_size.GetHeight() - image.GetSize().GetHeight()) / 2));
71                 }
72                 return;
73         }
74 #endif
75
76         if (!out_size.width || !out_size.height || !_image || out_size != _image->size()) {
77                 dc.Clear ();
78         } else {
79
80                 wxImage frame (out_size.width, out_size.height, _image->data()[0], true);
81                 wxBitmap frame_bitmap (frame);
82                 dc.DrawBitmap (frame_bitmap, 0, max(0, (panel_size.GetHeight() - out_size.height) / 2));
83
84 #ifdef DCPOMATIC_VARIANT_SWAROOP
85                 DCPTime const period = DCPTime::from_seconds(Config::instance()->player_watermark_period() * 60);
86                 int64_t n = _viewer->position().get() / period.get();
87                 DCPTime from(n * period.get());
88                 DCPTime to = from + DCPTime::from_seconds(Config::instance()->player_watermark_duration() / 1000.0);
89                 if (from <= _viewer->position() && _viewer->position() <= to) {
90                         if (!_in_watermark) {
91                                 _in_watermark = true;
92                                 _watermark_x = rand() % panel_size.GetWidth();
93                                 _watermark_y = rand() % panel_size.GetHeight();
94                         }
95                         dc.SetTextForeground(*wxWHITE);
96                         string wm = Config::instance()->player_watermark_theatre();
97                         boost::posix_time::ptime t = boost::posix_time::second_clock::local_time();
98                         wm += "\n" + boost::posix_time::to_iso_extended_string(t);
99                         dc.DrawText(std_to_wx(wm), _watermark_x, _watermark_y);
100                 } else {
101                         _in_watermark = false;
102                 }
103 #endif
104         }
105
106         if (out_size.width < panel_size.GetWidth()) {
107                 /* XXX: these colours are right for GNOME; may need adjusting for other OS */
108                 wxPen   p (_viewer->pad_black() ? wxColour(0, 0, 0) : wxColour(240, 240, 240));
109                 wxBrush b (_viewer->pad_black() ? wxColour(0, 0, 0) : wxColour(240, 240, 240));
110                 dc.SetPen (p);
111                 dc.SetBrush (b);
112                 dc.DrawRectangle (out_size.width, 0, panel_size.GetWidth() - out_size.width, panel_size.GetHeight());
113         }
114
115         if (out_size.height < panel_size.GetHeight()) {
116                 wxPen   p (_viewer->pad_black() ? wxColour(0, 0, 0) : wxColour(240, 240, 240));
117                 wxBrush b (_viewer->pad_black() ? wxColour(0, 0, 0) : wxColour(240, 240, 240));
118                 dc.SetPen (p);
119                 dc.SetBrush (b);
120                 int const gap = (panel_size.GetHeight() - out_size.height) / 2;
121                 dc.DrawRectangle (0, 0, panel_size.GetWidth(), gap);
122                 dc.DrawRectangle (0, gap + out_size.height + 1, panel_size.GetWidth(), gap + 1);
123         }
124
125         if (_viewer->outline_content()) {
126                 Position<int> inter_position = _viewer->inter_position ();
127                 dcp::Size inter_size = _viewer->inter_size ();
128                 wxPen p (wxColour (255, 0, 0), 2);
129                 dc.SetPen (p);
130                 dc.SetBrush (*wxTRANSPARENT_BRUSH);
131                 dc.DrawRectangle (inter_position.x, inter_position.y + (panel_size.GetHeight() - out_size.height) / 2, inter_size.width, inter_size.height);
132         }
133         _viewer->state_timer().unset();
134 }
135
136 void
137 SimpleVideoView::update ()
138 {
139         _panel->Refresh ();
140         _panel->Update ();
141 }
142
143 void
144 SimpleVideoView::timer ()
145 {
146         if (!_viewer->film() || !_viewer->playing()) {
147                 return;
148         }
149
150         get (false);
151         DCPTime const next = _viewer->position() + _viewer->one_video_frame();
152
153         if (next >= _viewer->film()->length()) {
154                 _viewer->stop ();
155                 _viewer->Finished ();
156                 return;
157         }
158
159         LOG_DEBUG_PLAYER("%1 -> %2; delay %3", next.seconds(), _viewer->time().seconds(), max((next.seconds() - _viewer->time().seconds()) * 1000, 1.0));
160         _timer.Start (max ((next.seconds() - _viewer->time().seconds()) * 1000, 1.0), wxTIMER_ONE_SHOT);
161
162         if (_viewer->_butler) {
163                 _viewer->_butler->rethrow ();
164         }
165 }
166
167 void
168 SimpleVideoView::start ()
169 {
170         timer ();
171 }
172
173 /** Try to get a frame from the butler and display it.
174  *  @param lazy true to return false quickly if no video is available quickly (i.e. we are waiting for the butler).
175  *  false to ask the butler to block until it has video (unless it is suspended).
176  *  @return true on success, false if we did nothing because it would have taken too long.
177  */
178 bool
179 SimpleVideoView::get (bool lazy)
180 {
181         DCPOMATIC_ASSERT (_viewer->_butler);
182         _viewer->_gets++;
183
184         do {
185                 Butler::Error e;
186                 _viewer->_player_video = _viewer->_butler->get_video (!lazy, &e);
187                 if (!_viewer->_player_video.first && e == Butler::AGAIN) {
188                         if (lazy) {
189                                 /* No video available; return saying we failed */
190                                 return false;
191                         } else {
192                                 /* Player was suspended; come back later */
193                                 signal_manager->when_idle (boost::bind(&SimpleVideoView::get, this, false));
194                                 return false;
195                         }
196                 }
197         } while (
198                 _viewer->_player_video.first &&
199                 _viewer->film()->three_d() &&
200                 _viewer->_eyes != _viewer->_player_video.first->eyes() &&
201                 _viewer->_player_video.first->eyes() != EYES_BOTH
202                 );
203
204         try {
205                 _viewer->_butler->rethrow ();
206         } catch (DecodeError& e) {
207                 error_dialog (get(), e.what());
208         }
209
210         _viewer->display_player_video ();
211         _viewer->PositionChanged ();
212
213         return true;
214 }