Add option to save a player frame to a file (#2133).
authorCarl Hetherington <cth@carlh.net>
Sat, 18 Dec 2021 22:54:54 +0000 (23:54 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 23 Dec 2021 11:59:56 +0000 (12:59 +0100)
src/tools/dcpomatic_player.cc

index d52ffd1ec4260473f1599a4303e87d1d235a3733..24f2d73ad194b923a1e20534594b922b996eae06 100644 (file)
 
 */
 
-#include "wx/wx_signal_manager.h"
-#include "wx/wx_util.h"
 #include "wx/about_dialog.h"
-#include "wx/report_problem_dialog.h"
 #include "wx/film_viewer.h"
-#include "wx/player_information.h"
-#include "wx/update_dialog.h"
+#include "wx/nag_dialog.h"
 #include "wx/player_config_dialog.h"
-#include "wx/verify_dcp_dialog.h"
-#include "wx/standard_controls.h"
+#include "wx/player_information.h"
+#include "wx/player_stress_tester.h"
 #include "wx/playlist_controls.h"
-#include "wx/timer_display.h"
+#include "wx/report_problem_dialog.h"
+#include "wx/standard_controls.h"
 #include "wx/system_information_dialog.h"
-#include "wx/player_stress_tester.h"
+#include "wx/timer_display.h"
+#include "wx/update_dialog.h"
+#include "wx/verify_dcp_dialog.h"
 #include "wx/verify_dcp_progress_dialog.h"
 #include "wx/wx_signal_manager.h"
 #include "wx/wx_util.h"
 #include "lib/file_log.h"
 #include "lib/film.h"
 #include "lib/image.h"
+#include "lib/image_jpeg.h"
 #include "lib/image_png.h"
 #include "lib/internet.h"
 #include "lib/job.h"
 #include "lib/job_manager.h"
 #include "lib/null_log.h"
+#include "lib/player.h"
+#include "lib/player_video.h"
 #include "lib/ratio.h"
 #include "lib/scoped_temporary.h"
 #include "lib/server.h"
 #include "lib/video_content.h"
 #include <dcp/cpl.h>
 #include <dcp/dcp.h>
-#include <dcp/raw_convert.h>
 #include <dcp/exceptions.h>
-#include <wx/wx.h>
-#include <wx/stdpaths.h>
-#include <wx/splash.h>
+#include <dcp/raw_convert.h>
 #include <wx/cmdline.h>
+#include <wx/display.h>
 #include <wx/preferences.h>
 #include <wx/progdlg.h>
-#include <wx/display.h>
+#include <wx/splash.h>
+#include <wx/stdpaths.h>
+#include <wx/wx.h>
 #ifdef __WXGTK__
 #include <X11/Xlib.h>
 #endif
@@ -107,6 +109,7 @@ enum {
        ID_file_open = 1,
        ID_file_add_ov,
        ID_file_add_kdm,
+       ID_file_save_frame,
        ID_file_history,
        /* Allow spare IDs after _history for the recent files list */
        ID_file_close = 100,
@@ -168,6 +171,7 @@ public:
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_open, this), ID_file_open);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_add_ov, this), ID_file_add_ov);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_add_kdm, this), ID_file_add_kdm);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_save_frame, this), ID_file_save_frame);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_history, this, _1), ID_file_history, ID_file_history + HISTORY_SIZE);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_close, this), ID_file_close);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_exit, this), wxID_EXIT);
@@ -491,6 +495,8 @@ private:
                _file_menu->Append (ID_file_open, _("&Open...\tCtrl-O"));
                _file_add_ov = _file_menu->Append (ID_file_add_ov, _("&Add OV..."));
                _file_add_kdm = _file_menu->Append (ID_file_add_kdm, _("Add &KDM..."));
+               _file_menu->AppendSeparator ();
+               _file_save_frame = _file_menu->Append (ID_file_save_frame, _("&Save frame to file...\tCtrl-S"));
 
                _history_position = _file_menu->GetMenuItems().GetCount();
 
@@ -652,6 +658,42 @@ private:
                _info->triggered_update ();
        }
 
+       void file_save_frame ()
+       {
+               wxFileDialog dialog (this, _("Save frame to file"), "", "", "PNG files (*.png)|*.png|JPEG files (*.jpg,*.jpeg)|*.jpg,*.jpeg", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
+               if (dialog.ShowModal() == wxID_CANCEL) {
+                       return;
+               }
+
+               auto path = boost::filesystem::path (wx_to_std(dialog.GetPath()));
+
+               auto player = make_shared<Player>(_film, Image::Alignment::PADDED);
+               player->seek (_viewer->position(), true);
+
+               bool done = false;
+               player->Video.connect ([path, &done, this](shared_ptr<PlayerVideo> video, DCPTime) {
+                       auto ext = boost::algorithm::to_lower_copy(path.extension().string());
+                       if (ext == ".png") {
+                               auto image = video->image(boost::bind(PlayerVideo::force, AV_PIX_FMT_RGBA), VideoRange::FULL, false);
+                               image_as_png(image).write(path);
+                       } else if (ext == ".jpg" || ext == ".jpeg") {
+                               auto image = video->image(boost::bind(PlayerVideo::force, AV_PIX_FMT_RGB24), VideoRange::FULL, false);
+                               image_as_jpeg(image, 80).write(path);
+                       } else {
+                               error_dialog (this, _(wxString::Format("Unrecognised file extension %s (use .jpg, .jpeg or .png)", std_to_wx(ext))));
+                       }
+                       done = true;
+               });
+
+               int tries_left = 50;
+               while (!done && tries_left >= 0) {
+                       player->pass();
+                       --tries_left;
+               }
+
+               DCPOMATIC_ASSERT (tries_left >= 0);
+       }
+
        void file_history (wxCommandEvent& event)
        {
                auto history = Config::instance()->player_history ();
@@ -946,6 +988,7 @@ private:
                _tools_verify->Enable (static_cast<bool>(_film));
                _file_add_ov->Enable (static_cast<bool>(_film));
                _file_add_kdm->Enable (static_cast<bool>(_film));
+               _file_save_frame->Enable (static_cast<bool>(_film));
                _view_cpl->Enable (static_cast<bool>(_film));
        }
 
@@ -1003,6 +1046,7 @@ private:
        boost::signals2::scoped_connection _examine_job_connection;
        wxMenuItem* _file_add_ov = nullptr;
        wxMenuItem* _file_add_kdm = nullptr;
+       wxMenuItem* _file_save_frame = nullptr;
        wxMenuItem* _tools_verify = nullptr;
        wxMenuItem* _view_full_screen = nullptr;
        wxMenuItem* _view_dual_screen = nullptr;