summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2021-12-18 23:54:54 +0100
committerCarl Hetherington <cth@carlh.net>2021-12-23 12:59:56 +0100
commit607a824efbcb0ae9d5b664e919ae983224e712a0 (patch)
treed89aef6e6738890f7f2b4c6e097d58ec83539c69 /src/tools
parentc0733033923d369cce3d76b318e969bf89a130b9 (diff)
Add option to save a player frame to a file (#2133).
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/dcpomatic_player.cc72
1 files changed, 58 insertions, 14 deletions
diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc
index d52ffd1ec..24f2d73ad 100644
--- a/src/tools/dcpomatic_player.cc
+++ b/src/tools/dcpomatic_player.cc
@@ -18,20 +18,19 @@
*/
-#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"
@@ -47,11 +46,14 @@
#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"
@@ -62,15 +64,15 @@
#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;