Note that newer libsub version is required.
[dcpomatic.git] / src / wx / film_viewer.cc
index b5b2ca972e57aa345856cea8ce28037db1788283..b6f8096e85dda7dcfb07a44a6e19fafb9781fc82 100644 (file)
@@ -86,7 +86,11 @@ rtaudio_callback (void* out, void *, unsigned int frames, double, RtAudioStreamS
 
 
 FilmViewer::FilmViewer (wxWindow* p)
+#if (RTAUDIO_VERSION_MAJOR >= 6)
+       : _audio(DCPOMATIC_RTAUDIO_API, boost::bind(&FilmViewer::rtaudio_error_callback, this, _2))
+#else
        : _audio (DCPOMATIC_RTAUDIO_API)
+#endif
        , _closed_captions_dialog (new ClosedCaptionsDialog(p, this))
 {
 #if wxCHECK_VERSION(3, 1, 0)
@@ -160,15 +164,17 @@ FilmViewer::set_film (shared_ptr<Film> film)
        _video_view->clear ();
        _closed_captions_dialog->clear ();
 
+       destroy_butler();
+
        if (!_film) {
-               _player.reset ();
-               recreate_butler ();
+               _player = boost::none;
+               resume();
                _video_view->update ();
                return;
        }
 
        try {
-               _player = make_shared<Player>(_film, _optimise_for_j2k ? Image::Alignment::COMPACT : Image::Alignment::PADDED);
+               _player.emplace(_film, _optimise_for_j2k ? Image::Alignment::COMPACT : Image::Alignment::PADDED);
                _player->set_fast ();
                if (_dcp_decode_reduction) {
                        _player->set_dcp_decode_reduction (_dcp_decode_reduction);
@@ -176,6 +182,7 @@ FilmViewer::set_film (shared_ptr<Film> film)
        } catch (bad_alloc &) {
                error_dialog (_video_view->get(), _("There is not enough free memory to do that."));
                _film.reset ();
+               resume();
                return;
        }
 
@@ -186,8 +193,8 @@ FilmViewer::set_film (shared_ptr<Film> film)
        _film->LengthChange.connect (boost::bind(&FilmViewer::film_length_change, this));
        _player->Change.connect (boost::bind (&FilmViewer::player_change, this, _1, _2, _3));
 
-       film_change (ChangeType::DONE, Film::Property::VIDEO_FRAME_RATE);
-       film_change (ChangeType::DONE, Film::Property::THREE_D);
+       film_change(ChangeType::DONE, FilmProperty::VIDEO_FRAME_RATE);
+       film_change(ChangeType::DONE, FilmProperty::THREE_D);
        film_length_change ();
 
        /* Keep about 1 second's worth of history samples */
@@ -195,7 +202,7 @@ FilmViewer::set_film (shared_ptr<Film> film)
 
        _closed_captions_dialog->update_tracks (_film);
 
-       recreate_butler ();
+       create_butler();
 
        calculate_sizes ();
        slow_refresh ();
@@ -203,25 +210,41 @@ FilmViewer::set_film (shared_ptr<Film> film)
 
 
 void
-FilmViewer::recreate_butler ()
+FilmViewer::destroy_butler()
 {
        suspend ();
        _butler.reset ();
+}
+
+
+void
+FilmViewer::destroy_and_maybe_create_butler()
+{
+       destroy_butler();
 
        if (!_film) {
                resume ();
                return;
        }
 
+       create_butler();
+}
+
+
+void
+FilmViewer::create_butler()
+{
 #if wxCHECK_VERSION(3, 1, 0)
        auto const j2k_gl_optimised = dynamic_pointer_cast<GLVideoView>(_video_view) && _optimise_for_j2k;
 #else
        auto const j2k_gl_optimised = false;
 #endif
 
+       DCPOMATIC_ASSERT(_player);
+
        _butler = std::make_shared<Butler>(
                _film,
-               _player,
+               *_player,
                Config::instance()->audio_mapping(_audio_channels),
                _audio_channels,
                boost::bind(&PlayerVideo::force, AV_PIX_FMT_RGB24),
@@ -303,6 +326,16 @@ FilmViewer::calculate_sizes ()
        out_size.width = max (64, out_size.width);
        out_size.height = max (64, out_size.height);
 
+       /* Make sure the video container sizes are always a multiple of 2 so that
+        * we don't get gaps with subsampled sources (e.g. YUV420)
+        */
+       if (out_size.width % 2) {
+               out_size.width++;
+       }
+       if (out_size.height % 2) {
+               out_size.height++;
+       }
+
        _player->set_video_container_size (out_size);
 }
 
@@ -322,6 +355,15 @@ FilmViewer::start_audio_stream_if_open ()
 {
        if (_audio.isStreamOpen()) {
                _audio.setStreamTime (_video_view->position().seconds());
+#if (RTAUDIO_VERSION_MAJOR >= 6)
+               if (_audio.startStream() != RTAUDIO_NO_ERROR) {
+                       _audio_channels = 0;
+                       error_dialog(
+                               _video_view->get(),
+                               _("There was a problem starting audio playback.  Please try another audio output device in Preferences."), std_to_wx(last_rtaudio_error())
+                               );
+               }
+#else
                try {
                        _audio.startStream ();
                } catch (RtAudioError& e) {
@@ -331,6 +373,7 @@ FilmViewer::start_audio_stream_if_open ()
                                _("There was a problem starting audio playback.  Please try another audio output device in Preferences."), std_to_wx(e.what())
                                );
                }
+#endif
        }
 }
 
@@ -457,19 +500,19 @@ FilmViewer::player_change (vector<int> properties)
 
 
 void
-FilmViewer::film_change (ChangeType type, Film::Property p)
+FilmViewer::film_change(ChangeType type, FilmProperty p)
 {
        if (type != ChangeType::DONE) {
                return;
        }
 
-       if (p == Film::Property::AUDIO_CHANNELS) {
-               recreate_butler ();
-       } else if (p == Film::Property::VIDEO_FRAME_RATE) {
+       if (p == FilmProperty::AUDIO_CHANNELS) {
+               destroy_and_maybe_create_butler();
+       } else if (p == FilmProperty::VIDEO_FRAME_RATE) {
                _video_view->set_video_frame_rate (_film->video_frame_rate());
-       } else if (p == Film::Property::THREE_D) {
+       } else if (p == FilmProperty::THREE_D) {
                _video_view->set_three_d (_film->three_d());
-       } else if (p == Film::Property::CONTENT) {
+       } else if (p == FilmProperty::CONTENT) {
                _closed_captions_dialog->update_tracks (_film);
        }
 }
@@ -507,6 +550,7 @@ FilmViewer::quick_refresh ()
 void
 FilmViewer::seek (shared_ptr<Content> content, ContentTime t, bool accurate)
 {
+       DCPOMATIC_ASSERT(_player);
        auto dt = _player->content_time_to_dcp (content, t);
        if (dt) {
                seek (*dt, accurate);
@@ -566,7 +610,7 @@ void
 FilmViewer::config_changed (Config::Property p)
 {
        if (p == Config::AUDIO_MAPPING) {
-               recreate_butler ();
+               destroy_and_maybe_create_butler();
                return;
        }
 
@@ -579,6 +623,33 @@ FilmViewer::config_changed (Config::Property p)
        }
 
        if (Config::instance()->sound() && _audio.getDeviceCount() > 0) {
+               optional<unsigned int> chosen_device_id;
+#if (RTAUDIO_VERSION_MAJOR >= 6)
+               if (Config::instance()->sound_output()) {
+                       for (auto device_id: _audio.getDeviceIds()) {
+                               if (_audio.getDeviceInfo(device_id).name == Config::instance()->sound_output().get()) {
+                                       chosen_device_id = device_id;
+                                       break;
+                               }
+                       }
+               }
+
+               if (!chosen_device_id) {
+                       chosen_device_id = _audio.getDefaultOutputDevice();
+               }
+               _audio_channels = _audio.getDeviceInfo(*chosen_device_id).outputChannels;
+               RtAudio::StreamParameters sp;
+               sp.deviceId = *chosen_device_id;
+               sp.nChannels = _audio_channels;
+               sp.firstChannel = 0;
+               if (_audio.openStream(&sp, 0, RTAUDIO_FLOAT32, 48000, &_audio_block_size, &rtaudio_callback, this) != RTAUDIO_NO_ERROR) {
+                       _audio_channels = 0;
+                       error_dialog(
+                               _video_view->get(),
+                               _("Could not set up audio output.  There will be no audio during the preview."), std_to_wx(last_rtaudio_error())
+                               );
+               }
+#else
                unsigned int st = 0;
                if (Config::instance()->sound_output()) {
                        while (st < _audio.getDeviceCount()) {
@@ -592,10 +663,18 @@ FilmViewer::config_changed (Config::Property p)
                                ++st;
                        }
                        if (st == _audio.getDeviceCount()) {
-                               st = _audio.getDefaultOutputDevice();
+                               try {
+                                       st = _audio.getDefaultOutputDevice();
+                               } catch (RtAudioError&) {
+                                       /* Something went wrong with that device so we don't want to use it anyway */
+                               }
                        }
                } else {
-                       st = _audio.getDefaultOutputDevice();
+                       try {
+                               st = _audio.getDefaultOutputDevice();
+                       } catch (RtAudioError&) {
+                               /* Something went wrong with that device so we don't want to use it anyway */
+                       }
                }
 
                try {
@@ -612,11 +691,12 @@ FilmViewer::config_changed (Config::Property p)
                                _("Could not set up audio output.  There will be no audio during the preview."), std_to_wx(e.what())
                                );
                }
-               recreate_butler ();
+#endif
+               destroy_and_maybe_create_butler();
 
        } else {
                _audio_channels = 0;
-               recreate_butler ();
+               destroy_and_maybe_create_butler();
        }
 }
 
@@ -712,6 +792,7 @@ FilmViewer::dcp_decode_reduction () const
 optional<ContentTime>
 FilmViewer::position_in_content (shared_ptr<const Content> content) const
 {
+       DCPOMATIC_ASSERT(_player);
        return _player->dcp_to_content_time (content, position());
 }
 
@@ -817,3 +898,21 @@ FilmViewer::unset_crop_guess ()
        _video_view->update ();
 }
 
+
+#if (RTAUDIO_VERSION_MAJOR >= 6)
+void
+FilmViewer::rtaudio_error_callback(string const& error)
+{
+       boost::mutex::scoped_lock lm(_last_rtaudio_error_mutex);
+       _last_rtaudio_error = error;
+}
+
+
+string
+FilmViewer::last_rtaudio_error() const
+{
+       boost::mutex::scoped_lock lm(_last_rtaudio_error_mutex);
+       return _last_rtaudio_error;
+}
+#endif
+