, _timecode (new wxStaticText (this, wxID_ANY, wxT("")))
, _play_button (new wxToggleButton (this, wxID_ANY, _("Play")))
, _coalesce_player_changes (false)
- , _pending_player_change (false)
+ , _slider_being_moved (false)
+ , _was_running_before_slider (false)
, _audio (DCPOMATIC_RTAUDIO_API)
, _audio_channels (0)
, _audio_block_size (1024)
if (_outline_content) {
_outline_content->Bind (wxEVT_CHECKBOX, boost::bind (&FilmViewer::refresh_panel, this));
}
- _left_eye->Bind (wxEVT_RADIOBUTTON, boost::bind (&FilmViewer::refresh, this));
- _right_eye->Bind (wxEVT_RADIOBUTTON, boost::bind (&FilmViewer::refresh, this));
+ _left_eye->Bind (wxEVT_RADIOBUTTON, boost::bind (&FilmViewer::slow_refresh, this));
+ _right_eye->Bind (wxEVT_RADIOBUTTON, boost::bind (&FilmViewer::slow_refresh, this));
_slider->Bind (wxEVT_SCROLL_THUMBTRACK, boost::bind (&FilmViewer::slider_moved, this, false));
- _slider->Bind (wxEVT_SCROLL_PAGEUP, boost::bind (&FilmViewer::slider_moved, this, false));
- _slider->Bind (wxEVT_SCROLL_PAGEDOWN, boost::bind (&FilmViewer::slider_moved, this, false));
- _slider->Bind (wxEVT_SCROLL_CHANGED, boost::bind (&FilmViewer::slider_moved, this, true));
+ _slider->Bind (wxEVT_SCROLL_PAGEUP, boost::bind (&FilmViewer::slider_moved, this, true));
+ _slider->Bind (wxEVT_SCROLL_PAGEDOWN, boost::bind (&FilmViewer::slider_moved, this, true));
+ _slider->Bind (wxEVT_SCROLL_THUMBRELEASE, boost::bind (&FilmViewer::slider_released, this));
_play_button->Bind (wxEVT_TOGGLEBUTTON, boost::bind (&FilmViewer::play_clicked, this));
_timer.Bind (wxEVT_TIMER, boost::bind (&FilmViewer::timer, this));
_rewind_button->Bind (wxEVT_LEFT_DOWN, boost::bind (&FilmViewer::rewind_clicked, this, _1));
_player->set_play_referenced ();
_film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1));
- _player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1));
+ _player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1, _2));
/* Keep about 1 second's worth of history samples */
_latency_history_count = _film->audio_frame_rate() / _audio_block_size;
recreate_butler ();
calculate_sizes ();
- refresh ();
+ slow_refresh ();
setup_sensitivity ();
}
map.set (i, i, 1);
}
} else {
- /* Special case: stereo output, at least 3 channel input, map L+R to L/R and
- C to both, all 3dB down.
+ /* Special case: stereo output, at least 3 channel input.
+ Map so that Lt = L(-3dB) + Ls(-3dB) + C(-6dB) + Lfe(-10dB)
+ Rt = R(-3dB) + Rs(-3dB) + C(-6dB) + Lfe(-10dB)
*/
- map.set (0, 0, 1 / sqrt(2)); // L -> L
- map.set (1, 1, 1 / sqrt(2)); // R -> R
- map.set (2, 0, 1 / sqrt(2)); // C -> L
- map.set (2, 1, 1 / sqrt(2)); // C -> R
+ map.set (dcp::LEFT, 0, 1 / sqrt(2)); // L -> Lt
+ map.set (dcp::RIGHT, 1, 1 / sqrt(2)); // R -> Rt
+ map.set (dcp::CENTRE, 0, 1 / 2.0); // C -> Lt
+ map.set (dcp::CENTRE, 1, 1 / 2.0); // C -> Rt
+ map.set (dcp::LFE, 0, 1 / sqrt(10)); // Lfe -> Lt
+ map.set (dcp::LFE, 1, 1 / sqrt(10)); // Lfe -> Rt
+ map.set (dcp::LS, 0, 1 / sqrt(2)); // Ls -> Lt
+ map.set (dcp::RS, 1, 1 / sqrt(2)); // Rs -> Rt
}
_butler.reset (new Butler (_player, _film->log(), map, _audio_channels));
{
DCPOMATIC_ASSERT (_butler);
- pair<shared_ptr<PlayerVideo>, DCPTime> video;
do {
- video = _butler->get_video ();
+ _player_video = _butler->get_video ();
} while (
_film->three_d() &&
- ((_left_eye->GetValue() && video.first->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && video.first->eyes() == EYES_LEFT))
+ ((_left_eye->GetValue() && _player_video.first->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && _player_video.first->eyes() == EYES_LEFT))
);
_butler->rethrow ();
- if (!video.first) {
+ display_player_video ();
+}
+
+void
+FilmViewer::display_player_video ()
+{
+ if (!_player_video.first) {
_frame.reset ();
refresh_panel ();
return;
}
- if (_playing && (time() - video.second) > one_video_frame()) {
+ if (_playing && (time() - _player_video.second) > one_video_frame()) {
/* Too late; just drop this frame before we try to get its image (which will be the time-consuming
part if this frame is J2K).
*/
- _video_position = video.second;
+ _video_position = _player_video.second;
++_dropped;
return;
}
* image and convert it (from whatever the user has said it is) to RGB.
*/
- _frame = video.first->image (
+ _frame = _player_video.first->image (
bind (&Log::dcp_log, _film->log().get(), _1, _2),
bind (&PlayerVideo::always_rgb, _1),
false, true
);
- ImageChanged (video.first);
+ ImageChanged (_player_video.first);
- _video_position = video.second;
- _inter_position = video.first->inter_position ();
- _inter_size = video.first->inter_size ();
+ _video_position = _player_video.second;
+ _inter_position = _player_video.first->inter_position ();
+ _inter_size = _player_video.first->inter_size ();
refresh_panel ();
}
{
wxPaintDC dc (_panel);
- if (!_frame || !_film || !_out_size.width || !_out_size.height) {
+ if (!_frame || !_film || !_out_size.width || !_out_size.height || _out_size != _frame->size()) {
dc.Clear ();
return;
}
}
}
+/** @param page true if this was a PAGEUP/PAGEDOWN event for which we won't receive a THUMBRELEASE */
void
-FilmViewer::slider_moved (bool update_slider)
+FilmViewer::slider_moved (bool page)
{
if (!_film) {
return;
}
+ if (!page && !_slider_being_moved) {
+ /* This is the first event of a drag; stop playback for the duration of the drag */
+ _was_running_before_slider = stop ();
+ _slider_being_moved = true;
+ }
DCPTime t (_slider->GetValue() * _film->length().get() / 4096);
+ t = t.round (_film->video_frame_rate());
/* Ensure that we hit the end of the film at the end of the slider */
if (t >= _film->length ()) {
t = _film->length() - one_video_frame();
}
seek (t, false);
update_position_label ();
- if (update_slider) {
- update_position_slider ();
+}
+
+void
+FilmViewer::slider_released ()
+{
+ if (_was_running_before_slider) {
+ /* Restart after a drag */
+ start ();
}
+ _slider_being_moved = false;
}
void
_panel_size.height = ev.GetSize().GetHeight();
calculate_sizes ();
- refresh ();
+ if (!quick_refresh()) {
+ slow_refresh ();
+ }
update_position_label ();
update_position_slider ();
}
void
FilmViewer::start ()
{
+ if (!_film) {
+ return;
+ }
+
if (_audio.isStreamOpen()) {
_audio.setStreamTime (_video_position.seconds());
_audio.startStream ();
_playing = true;
_dropped = 0;
timer ();
+ _play_button->SetValue (true);
}
bool
FilmViewer::stop ()
{
if (_audio.isStreamRunning()) {
- /* stop stream and discard any remainig queued samples */
+ /* stop stream and discard any remaining queued samples */
_audio.abortStream ();
}
}
DCPTime
-FilmViewer::nudge_amount (wxMouseEvent& ev)
+FilmViewer::nudge_amount (wxKeyboardState& ev)
{
DCPTime amount = one_video_frame ();
}
void
-FilmViewer::back_clicked (wxMouseEvent& ev)
+FilmViewer::back_frame ()
+{
+ if (!_film) {
+ return;
+ }
+
+ go_to (_video_position - one_video_frame());
+}
+
+void
+FilmViewer::forward_frame ()
+{
+ if (!_film) {
+ return;
+ }
+
+ go_to (_video_position + one_video_frame());
+}
+
+void
+FilmViewer::back_clicked (wxKeyboardState& ev)
{
go_to (_video_position - nudge_amount (ev));
- ev.Skip ();
}
void
-FilmViewer::forward_clicked (wxMouseEvent& ev)
+FilmViewer::forward_clicked (wxKeyboardState& ev)
{
go_to (_video_position + nudge_amount (ev));
- ev.Skip ();
}
void
-FilmViewer::player_changed (bool frequent)
+FilmViewer::player_changed (int property, bool frequent)
{
if (frequent) {
return;
}
if (_coalesce_player_changes) {
- _pending_player_change = true;
+ _pending_player_changes.push_back (property);
return;
}
calculate_sizes ();
- refresh ();
+ bool refreshed = false;
+ if (
+ property == VideoContentProperty::CROP ||
+ property == VideoContentProperty::SCALE ||
+ property == VideoContentProperty::FADE_IN ||
+ property == VideoContentProperty::FADE_OUT ||
+ property == VideoContentProperty::COLOUR_CONVERSION ||
+ property == PlayerProperty::VIDEO_CONTAINER_SIZE ||
+ property == PlayerProperty::FILM_CONTAINER
+ ) {
+ refreshed = quick_refresh ();
+ }
+
+ if (!refreshed) {
+ slow_refresh ();
+ }
update_position_label ();
update_position_slider ();
}
{
if (p == Film::CONTENT || p == Film::THREE_D) {
setup_sensitivity ();
+ } else if (p == Film::AUDIO_CHANNELS) {
+ recreate_butler ();
}
}
-/** Re-get the current frame */
+/** Re-get the current frame slowly by seeking */
void
-FilmViewer::refresh ()
+FilmViewer::slow_refresh ()
{
seek (_video_position, true);
}
+/** Try to re-get the current frame quickly by resetting the metadata
+ * in the PlayerVideo that we used last time.
+ * @return true if this was possible, false if not.
+ */
+bool
+FilmViewer::quick_refresh ()
+{
+ if (!_player_video.first) {
+ return false;
+ }
+
+ if (!_player_video.first->reset_metadata (_player->video_container_size(), _film->frame_size())) {
+ return false;
+ }
+
+ display_player_video ();
+ return true;
+}
+
void
FilmViewer::set_position (DCPTime p)
{
update_position_slider ();
}
+void
+FilmViewer::set_position (shared_ptr<Content> content, ContentTime t)
+{
+ set_position (_player->content_time_to_dcp (content, t));
+}
+
void
FilmViewer::set_coalesce_player_changes (bool c)
{
_coalesce_player_changes = c;
- if (c) {
- _pending_player_change = false;
- } else {
- if (_pending_player_change) {
- player_changed (false);
+ if (!c) {
+ BOOST_FOREACH (int i, _pending_player_changes) {
+ player_changed (i, false);
}
+ _pending_player_changes.clear ();
}
}
_audio.closeStream ();
}
- if (Config::instance()->sound()) {
+ if (Config::instance()->sound() && _audio.getDeviceCount() > 0) {
unsigned int st = 0;
if (Config::instance()->sound_output()) {
while (st < _audio.getDeviceCount()) {