diff options
| author | Carl Hetherington <cth@carlh.net> | 2012-07-15 00:14:28 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2012-07-15 00:14:28 +0100 |
| commit | bb767c7e338414beee132af3e96829c1448e214b (patch) | |
| tree | bec2858dcc7225a9bcc2acd8170c25508f6df6cb /src/tools | |
| parent | 66c9be6bdb1361e5681e094a0c8170d268aa9518 (diff) | |
Move things round a bit.
Diffstat (limited to 'src/tools')
| -rw-r--r-- | src/tools/alignomatic.cc | 317 | ||||
| -rw-r--r-- | src/tools/dvdomatic.cc | 328 | ||||
| -rw-r--r-- | src/tools/fixlengths.cc | 209 | ||||
| -rw-r--r-- | src/tools/makedcp.cc | 138 | ||||
| -rw-r--r-- | src/tools/playomatic.cc | 67 | ||||
| -rwxr-xr-x | src/tools/run_film_editor | 4 | ||||
| -rw-r--r-- | src/tools/servomatic.cc | 238 | ||||
| -rw-r--r-- | src/tools/servomatictest.cc | 159 | ||||
| -rw-r--r-- | src/tools/test.cc | 15 | ||||
| -rw-r--r-- | src/tools/wscript | 17 |
10 files changed, 1492 insertions, 0 deletions
diff --git a/src/tools/alignomatic.cc b/src/tools/alignomatic.cc new file mode 100644 index 000000000..9cab6c430 --- /dev/null +++ b/src/tools/alignomatic.cc @@ -0,0 +1,317 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <gtkmm.h> +#include "lib/util.h" +#include "lib/config.h" +#include "lib/screen.h" +#include "lib/format.h" +#include "gtk/gtk_util.h" +#include "gtk/alignment.h" + +using namespace std; +using namespace boost; + +static Alignment* alignment = 0; +static Gtk::ComboBoxText* format_combo = 0; +static Format const * format = 0; +static Gtk::ComboBoxText* screen_combo = 0; +static shared_ptr<Screen> screen; +static Gtk::Button* add_screen = 0; +static Gtk::Entry* screen_name = 0; +static Gtk::SpinButton* x_position = 0; +static Gtk::SpinButton* y_position = 0; +static Gtk::SpinButton* width = 0; +static Gtk::Button* calculate_width = 0; +static Gtk::SpinButton* height = 0; +static Gtk::Button* calculate_height = 0; +static Gtk::Button* save = 0; +static bool screen_dirty = false; + +enum GeometryPart { + GEOMETRY_PART_X, + GEOMETRY_PART_Y, + GEOMETRY_PART_WIDTH, + GEOMETRY_PART_HEIGHT +}; + +void +update_sensitivity () +{ + bool const dims = format && screen; + + x_position->set_sensitive (dims); + y_position->set_sensitive (dims); + width->set_sensitive (dims); + calculate_width->set_sensitive (dims); + height->set_sensitive (dims); + calculate_height->set_sensitive (dims); + + screen_name->set_sensitive (screen); + save->set_sensitive (screen_dirty); +} + +void +update_alignment () +{ + if (!screen || !format) { + return; + } + + delete alignment; + alignment = new Alignment (screen->position (format), screen->size (format)); + alignment->set_text_line (0, screen->name ()); + alignment->set_text_line (1, format->name ()); +} + +void +update_entries () +{ + if (!screen || !format) { + return; + } + + Position p = screen->position (format); + x_position->set_value (p.x); + y_position->set_value (p.y); + Size s = screen->size (format); + width->set_value (s.width); + height->set_value (s.height); + + update_sensitivity (); +} + +void +screen_changed () +{ + if (screen_combo->get_active_row_number() < 0) { + return; + } + + vector<shared_ptr<Screen> > screens = Config::instance()->screens (); + + if (screens[screen_combo->get_active_row_number()] == screen) { + return; + } + + screen = screens[screen_combo->get_active_row_number()]; + + update_entries (); + update_alignment (); + + screen_name->set_text (screen->name ()); + + screen_dirty = false; + update_sensitivity (); +} + +void +format_changed () +{ + vector<Format const *> formats = Format::all (); + + if (formats[format_combo->get_active_row_number()] == format) { + return; + } + + format = formats[format_combo->get_active_row_number()]; + + update_entries (); + update_alignment (); + update_sensitivity (); +} + +void +geometry_changed (GeometryPart p) +{ + if (p == GEOMETRY_PART_X && screen->position(format).x == x_position->get_value_as_int()) { + return; + } + + if (p == GEOMETRY_PART_Y && screen->position(format).y == y_position->get_value_as_int()) { + return; + } + + if (p == GEOMETRY_PART_WIDTH && screen->size(format).width == width->get_value_as_int()) { + return; + } + + if (p == GEOMETRY_PART_HEIGHT && screen->size(format).height == height->get_value_as_int()) { + return; + } + + screen->set_geometry ( + format, + Position (x_position->get_value_as_int(), y_position->get_value_as_int()), + Size (width->get_value_as_int(), height->get_value_as_int()) + ); + + update_alignment (); + + screen_dirty = true; + update_sensitivity (); +} + +void +save_clicked () +{ + Config::instance()->write (); + screen_dirty = false; + update_sensitivity (); +} + +void +calculate_width_clicked () +{ + width->set_value (height->get_value_as_int() * format->ratio_as_float ()); +} + +void +calculate_height_clicked () +{ + height->set_value (width->get_value_as_int() / format->ratio_as_float ()); +} + +void +update_screen_combo () +{ + screen_combo->clear (); + + vector<shared_ptr<Screen> > screens = Config::instance()->screens (); + for (vector<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) { + screen_combo->append_text ((*i)->name ()); + } +} + +void +screen_name_changed () +{ + screen->set_name (screen_name->get_text ()); + + int const r = screen_combo->get_active_row_number (); + update_screen_combo (); + screen_combo->set_active (r); + + screen_dirty = true; + update_sensitivity (); +} + +void +add_screen_clicked () +{ + shared_ptr<Screen> s (new Screen ("New Screen")); + vector<shared_ptr<Screen> > screens = Config::instance()->screens (); + screens.push_back (s); + Config::instance()->set_screens (screens); + update_screen_combo (); + screen_combo->set_active (screens.size() - 1); +} + +int +main (int argc, char* argv[]) +{ + dvdomatic_setup (); + + Gtk::Main kit (argc, argv); + + Gtk::Dialog dialog ("Align-o-matic"); + + screen_combo = Gtk::manage (new Gtk::ComboBoxText); + update_screen_combo (); + screen_combo->signal_changed().connect (sigc::ptr_fun (&screen_changed)); + + add_screen = Gtk::manage (new Gtk::Button ("Add")); + add_screen->signal_clicked().connect (sigc::ptr_fun (&add_screen_clicked)); + + screen_name = Gtk::manage (new Gtk::Entry ()); + screen_name->signal_changed().connect (sigc::ptr_fun (&screen_name_changed)); + + format_combo = Gtk::manage (new Gtk::ComboBoxText); + vector<Format const *> formats = Format::all (); + for (vector<Format const *>::iterator i = formats.begin(); i != formats.end(); ++i) { + format_combo->append_text ((*i)->name ()); + } + + format_combo->signal_changed().connect (sigc::ptr_fun (&format_changed)); + + save = Gtk::manage (new Gtk::Button ("Save")); + save->signal_clicked().connect (sigc::ptr_fun (&save_clicked)); + + x_position = Gtk::manage (new Gtk::SpinButton ()); + x_position->signal_value_changed().connect (sigc::bind (ptr_fun (&geometry_changed), GEOMETRY_PART_X)); + x_position->set_range (0, 2048); + x_position->set_increments (1, 16); + y_position = Gtk::manage (new Gtk::SpinButton ()); + y_position->signal_value_changed().connect (sigc::bind (sigc::ptr_fun (&geometry_changed), GEOMETRY_PART_Y)); + y_position->set_range (0, 1080); + y_position->set_increments (1, 16); + width = Gtk::manage (new Gtk::SpinButton ()); + width->signal_value_changed().connect (sigc::bind (sigc::ptr_fun (&geometry_changed), GEOMETRY_PART_WIDTH)); + width->set_range (0, 2048); + width->set_increments (1, 16); + height = Gtk::manage (new Gtk::SpinButton ()); + height->signal_value_changed().connect (sigc::bind (sigc::ptr_fun (&geometry_changed), GEOMETRY_PART_HEIGHT)); + height->set_range (0, 1080); + height->set_increments (1, 16); + + calculate_width = Gtk::manage (new Gtk::Button ("Calculate")); + calculate_width->signal_clicked().connect (sigc::ptr_fun (&calculate_width_clicked)); + calculate_height = Gtk::manage (new Gtk::Button ("Calculate")); + calculate_height->signal_clicked().connect (sigc::ptr_fun (&calculate_height_clicked)); + + Gtk::Table table; + table.set_row_spacings (12); + table.set_col_spacings (12); + table.set_border_width (12); + + int n = 0; + table.attach (left_aligned_label ("Screen"), 0, 1, n, n + 1); + table.attach (*screen_combo, 1, 2, n, n + 1); + table.attach (*add_screen, 2, 3, n, n + 1); + ++n; + table.attach (left_aligned_label ("Screen Name"), 0, 1, n, n + 1); + table.attach (*screen_name, 1, 2, n, n + 1); + ++n; + table.attach (left_aligned_label ("Format"), 0, 1, n, n + 1); + table.attach (*format_combo, 1, 2, n, n + 1); + ++n; + table.attach (left_aligned_label ("x"), 0, 1, n, n + 1); + table.attach (*x_position, 1, 2, n, n + 1); + ++n; + table.attach (left_aligned_label ("y"), 0, 1, n, n + 1); + table.attach (*y_position, 1, 2, n, n + 1); + ++n; + table.attach (left_aligned_label ("Width"), 0, 1, n, n + 1); + table.attach (*width, 1, 2, n, n + 1); + table.attach (*calculate_width, 2, 3, n, n + 1); + ++n; + table.attach (left_aligned_label ("Height"), 0, 1, n, n + 1); + table.attach (*height, 1, 2, n, n + 1); + table.attach (*calculate_height, 2, 3, n, n + 1); + ++n; + + dialog.get_vbox()->pack_start (table, false, false); + dialog.add_action_widget (*save, 0); + update_sensitivity (); + dialog.show_all (); + + Gtk::Main::run (dialog); + + return 0; +} diff --git a/src/tools/dvdomatic.cc b/src/tools/dvdomatic.cc new file mode 100644 index 000000000..803eec3c4 --- /dev/null +++ b/src/tools/dvdomatic.cc @@ -0,0 +1,328 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include <boost/filesystem.hpp> +#include "gtk/film_viewer.h" +#include "gtk/film_editor.h" +#include "gtk/film_player.h" +#include "gtk/job_manager_view.h" +#include "gtk/config_dialog.h" +#include "gtk/gpl.h" +#include "gtk/job_wrapper.h" +#include "lib/film.h" +#include "lib/format.h" +#include "lib/config.h" +#include "lib/filter.h" +#include "lib/util.h" +#include "lib/scaler.h" + +using namespace std; +using namespace boost; + +static Gtk::Window* window = 0; +static FilmViewer* film_viewer = 0; +static FilmEditor* film_editor = 0; +static FilmPlayer* film_player = 0; +static Film* film = 0; + +class FilmChangedDialog : public Gtk::MessageDialog +{ +public: + FilmChangedDialog () + : Gtk::MessageDialog ("", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE) + { + stringstream s; + s << "Save changes to film \"" << film->name() << "\" before closing?"; + set_message (s.str ()); + add_button ("Close _without saving", Gtk::RESPONSE_NO); + add_button ("_Cancel", Gtk::RESPONSE_CANCEL); + add_button ("_Save", Gtk::RESPONSE_YES); + } +}; + +bool +maybe_save_then_delete_film () +{ + if (!film) { + return false; + } + + if (film->dirty ()) { + FilmChangedDialog d; + switch (d.run ()) { + case Gtk::RESPONSE_CANCEL: + return true; + case Gtk::RESPONSE_YES: + film->write_metadata (); + break; + case Gtk::RESPONSE_NO: + return false; + } + } + + delete film; + film = 0; + return false; +} + +void +file_new () +{ + Gtk::FileChooserDialog c (*window, "New Film", Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER); + c.add_button ("_Cancel", Gtk::RESPONSE_CANCEL); + c.add_button ("C_reate", Gtk::RESPONSE_ACCEPT); + + int const r = c.run (); + if (r == Gtk::RESPONSE_ACCEPT) { + if (maybe_save_then_delete_film ()) { + return; + } + film = new Film (c.get_filename ()); +#if BOOST_FILESYSTEM_VERSION == 3 + film->set_name (filesystem::path (c.get_filename().c_str()).filename().generic_string()); +#else + film->set_name (filesystem::path (c.get_filename().c_str()).filename()); +#endif + film_viewer->set_film (film); + film_editor->set_film (film); + } +} + +void +file_open () +{ + Gtk::FileChooserDialog c (*window, "Open Film", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER); + c.add_button ("_Cancel", Gtk::RESPONSE_CANCEL); + c.add_button ("_Open", Gtk::RESPONSE_ACCEPT); + + int const r = c.run (); + if (r == Gtk::RESPONSE_ACCEPT) { + if (maybe_save_then_delete_film ()) { + return; + } + film = new Film (c.get_filename ()); + film_viewer->set_film (film); + film_editor->set_film (film); + } +} + +void +file_save () +{ + film->write_metadata (); +} + +void +file_quit () +{ + if (maybe_save_then_delete_film ()) { + return; + } + + Gtk::Main::quit (); +} + +void +edit_preferences () +{ + ConfigDialog d; + d.run (); + Config::instance()->write (); +} + +void +jobs_make_dcp () +{ + JobWrapper::make_dcp (film, true); +} + +void +jobs_make_dcp_from_existing_transcode () +{ + JobWrapper::make_dcp (film, false); +} + +void +jobs_copy_from_dvd () +{ + film->copy_from_dvd (); +} + +void +jobs_send_dcp_to_tms () +{ + film->send_dcp_to_tms (); +} + +void +jobs_examine_content () +{ + film->examine_content (); +} + +void +help_about () +{ + Gtk::AboutDialog d; + d.set_name ("DVD-o-matic"); + d.set_version (DVDOMATIC_VERSION); + + stringstream s; + s << "DCP generation from arbitrary formats\n\n" + << "Using " << dependency_version_summary() << "\n"; + d.set_comments (s.str ()); + + vector<string> authors; + authors.push_back ("Carl Hetherington"); + authors.push_back ("Terrence Meiczinger"); + authors.push_back ("Paul Davis"); + d.set_authors (authors); + + d.set_website ("http://carlh.net/software/dvdomatic"); + d.set_license (gpl); + + d.run (); +} + +void +setup_menu (Gtk::MenuBar& m) +{ + using namespace Gtk::Menu_Helpers; + + Gtk::Menu* file = manage (new Gtk::Menu); + MenuList& file_items (file->items ()); + file_items.push_back (MenuElem ("_New...", sigc::ptr_fun (file_new))); + file_items.push_back (MenuElem ("_Open...", sigc::ptr_fun (file_open))); + file_items.push_back (SeparatorElem ()); + file_items.push_back (MenuElem ("_Save", sigc::ptr_fun (file_save))); + file_items.push_back (SeparatorElem ()); + file_items.push_back (MenuElem ("_Quit", sigc::ptr_fun (file_quit))); + + Gtk::Menu* edit = manage (new Gtk::Menu); + MenuList& edit_items (edit->items ()); + edit_items.push_back (MenuElem ("_Preferences...", sigc::ptr_fun (edit_preferences))); + + Gtk::Menu* jobs = manage (new Gtk::Menu); + MenuList& jobs_items (jobs->items ()); + jobs_items.push_back (MenuElem ("_Make DCP", sigc::ptr_fun (jobs_make_dcp))); + jobs_items.push_back (MenuElem ("_Send DCP to TMS", sigc::ptr_fun (jobs_send_dcp_to_tms))); + jobs_items.push_back (MenuElem ("Copy from _DVD", sigc::ptr_fun (jobs_copy_from_dvd))); + jobs_items.push_back (MenuElem ("_Examine content", sigc::ptr_fun (jobs_examine_content))); + jobs_items.push_back (MenuElem ("Make DCP from _existing transcode", sigc::ptr_fun (jobs_make_dcp_from_existing_transcode))); + + Gtk::Menu* help = manage (new Gtk::Menu); + MenuList& help_items (help->items ()); + help_items.push_back (MenuElem ("_About", sigc::ptr_fun (help_about))); + + MenuList& items (m.items ()); + items.push_back (MenuElem ("_File", *file)); + items.push_back (MenuElem ("_Edit", *edit)); + items.push_back (MenuElem ("_Jobs", *jobs)); + items.push_back (MenuElem ("_Help", *help)); +} + +bool +window_closed (GdkEventAny *) +{ + if (maybe_save_then_delete_film ()) { + return true; + } + + return false; +} + +void +file_changed (string f) +{ + stringstream s; + s << "DVD-o-matic"; + if (!f.empty ()) { + s << " — " << f; + } + + window->set_title (s.str ()); +} + +int +main (int argc, char* argv[]) +{ + dvdomatic_setup (); + + Gtk::Main kit (argc, argv); + + if (argc == 2 && boost::filesystem::is_directory (argv[1])) { + film = new Film (argv[1]); + } + + window = new Gtk::Window (); + window->signal_delete_event().connect (sigc::ptr_fun (window_closed)); + + film_viewer = new FilmViewer (film); + film_editor = new FilmEditor (film); + film_player = new FilmPlayer (film); + JobManagerView jobs_view; + + window->set_title ("DVD-o-matic"); + + Gtk::VBox vbox; + + Gtk::MenuBar menu_bar; + vbox.pack_start (menu_bar, false, false); + setup_menu (menu_bar); + + Gtk::HBox hbox; + hbox.set_spacing (12); + + Gtk::VBox left_vbox; + left_vbox.set_spacing (12); + left_vbox.pack_start (film_editor->widget (), false, false); +// left_vbox.pack_start (film_player->widget (), false, false); + hbox.pack_start (left_vbox, false, false); + + Gtk::VBox right_vbox; + right_vbox.pack_start (film_viewer->widget (), true, true); + right_vbox.pack_start (jobs_view.widget(), false, false); + hbox.pack_start (right_vbox, true, true); + + vbox.pack_start (hbox, true, true); + + window->add (vbox); + window->show_all (); + + /* XXX: calling these here is a bit of a hack */ + film_editor->setup_visibility (); + film_player->setup_visibility (); + film_viewer->setup_visibility (); + + film_editor->FileChanged.connect (ptr_fun (file_changed)); + if (film) { + file_changed (film->directory ()); + } else { + file_changed (""); + } + + /* XXX this should be in JobManagerView, shouldn't it? */ + Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (jobs_view, &JobManagerView::update), true), 1000); + + window->maximize (); + Gtk::Main::run (*window); + + return 0; +} diff --git a/src/tools/fixlengths.cc b/src/tools/fixlengths.cc new file mode 100644 index 000000000..52696cd8b --- /dev/null +++ b/src/tools/fixlengths.cc @@ -0,0 +1,209 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <stdexcept> +#include <iostream> +#include <iomanip> +#include <getopt.h> +#include <sndfile.h> +#include <boost/filesystem.hpp> +#include "lib/film.h" + +using namespace std; +using namespace boost; + +void +help (string n) +{ + cerr << "Syntax: " << n << " [--help] [--chop-audio-start] [--chop-audio-end] --film <film>\n"; +} + +void +sox (vector<string> const & audio_files, string const & process) +{ + for (vector<string>::const_iterator i = audio_files.begin(); i != audio_files.end(); ++i) { + stringstream cmd; + cmd << "sox \"" << *i << "\" -t wav \"" << *i << ".tmp\" " << process; + cout << "> " << cmd.str() << "\n"; + int r = ::system (cmd.str().c_str()); + if (r == -1 || WEXITSTATUS (r) != 0) { + cerr << "fixlengths: call to sox failed.\n"; + exit (EXIT_FAILURE); + } + filesystem::rename (*i + ".tmp", *i); + } +} + +int main (int argc, char* argv[]) +{ + string film_dir; + bool chop_audio_start = false; + bool chop_audio_end = false; + bool pad_audio_end = false; + + while (1) { + static struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "chop-audio-start", no_argument, 0, 'c' }, + { "chop-audio-end", no_argument, 0, 'd' }, + { "pad-audio-end", no_argument, 0, 'p' }, + { "film", required_argument, 0, 'f' }, + { 0, 0, 0, 0 } + }; + + int option_index = 0; + int c = getopt_long (argc, argv, "hcf:", long_options, &option_index); + + if (c == -1) { + break; + } + + switch (c) { + case 'h': + help (argv[0]); + exit (EXIT_SUCCESS); + case 'c': + chop_audio_start = true; + break; + case 'd': + chop_audio_end = true; + break; + case 'p': + pad_audio_end = true; + break; + case 'f': + film_dir = optarg; + break; + } + } + + if (film_dir.empty ()) { + help (argv[0]); + exit (EXIT_FAILURE); + } + + dvdomatic_setup (); + + Film* film = 0; + try { + film = new Film (film_dir, true); + } catch (std::exception& e) { + cerr << argv[0] << ": error reading film `" << film_dir << "' (" << e.what() << ")\n"; + exit (EXIT_FAILURE); + } + + /* XXX: hack */ + int video_frames = 0; + for (filesystem::directory_iterator i = filesystem::directory_iterator (film->j2k_dir()); i != filesystem::directory_iterator(); ++i) { + ++video_frames; + } + + float const video_length = video_frames / film->frames_per_second(); + cout << "Video length: " << video_length << " (" << video_frames << " frames at " << film->frames_per_second() << " frames per second).\n"; + + vector<string> audio_files = film->audio_files (); + if (audio_files.empty ()) { + cerr << argv[0] << ": film has no audio files.\n"; + exit (EXIT_FAILURE); + } + + sf_count_t audio_frames = 0; + int audio_sample_rate = 0; + + for (vector<string>::iterator i = audio_files.begin(); i != audio_files.end(); ++i) { + SF_INFO info; + info.format = 0; + SNDFILE* sf = sf_open (i->c_str(), SFM_READ, &info); + if (sf == 0) { + cerr << argv[0] << ": could not open WAV file for reading.\n"; + exit (EXIT_FAILURE); + } + + if (audio_frames == 0) { + audio_frames = info.frames; + } + + if (audio_sample_rate == 0) { + audio_sample_rate = info.samplerate; + } + + if (audio_frames != info.frames) { + cerr << argv[0] << ": audio files have differing lengths.\n"; + exit (EXIT_FAILURE); + } + + if (audio_sample_rate != info.samplerate) { + cerr << argv[0] << ": audio files have differing sample rates.\n"; + exit (EXIT_FAILURE); + } + + sf_close (sf); + } + + float const audio_length = audio_frames / float (audio_sample_rate); + + cout << "Audio length: " << audio_length << " (" << audio_frames << " frames at " << audio_sample_rate << " frames per second).\n"; + + cout << "\n"; + + if (audio_length > video_length) { + cout << setprecision (3); + cout << "Audio " << (audio_length - video_length) << "s longer than video.\n"; + + float const delta = audio_length - video_length; + int const delta_samples = delta * audio_sample_rate; + + if (chop_audio_start) { + cout << "Chopping difference off the start of the audio.\n"; + + stringstream s; + s << "trim " << delta_samples << "s"; + sox (audio_files, s.str ()); + + } else if (chop_audio_end) { + cout << "Chopping difference off the end of the audio.\n"; + + stringstream s; + s << "reverse trim " << delta_samples << "s reverse"; + sox (audio_files, s.str ()); + + } else { + cout << "Re-run with --chop-audio-start or --chop-audio-end, perhaps.\n"; + } + + } else if (audio_length < video_length) { + cout << setprecision (3); + cout << "Audio " << (video_length - audio_length) << "s shorter than video.\n"; + + if (pad_audio_end) { + + float const delta = video_length - audio_length; + int const delta_samples = delta * audio_sample_rate; + stringstream s; + s << "pad 0 " << delta_samples << "s"; + sox (audio_files, s.str ()); + + } else { + cout << "Re-run with --pad-audio-end, perhaps.\n"; + } + } + + + return 0; +} diff --git a/src/tools/makedcp.cc b/src/tools/makedcp.cc new file mode 100644 index 000000000..76cda8202 --- /dev/null +++ b/src/tools/makedcp.cc @@ -0,0 +1,138 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include <iomanip> +#include <getopt.h> +#include "format.h" +#include "film.h" +#include "filter.h" +#include "transcode_job.h" +#include "make_mxf_job.h" +#include "make_dcp_job.h" +#include "job_manager.h" +#include "ab_transcode_job.h" +#include "util.h" +#include "scaler.h" + +using namespace std; +using namespace boost; + +static void +help (string n) +{ + cerr << "Syntax: " << n << " [--help] [--deps] [--film <film>]\n"; +} + +int +main (int argc, char* argv[]) +{ + string film_dir; + + while (1) { + static struct option long_options[] = { + { "help", no_argument, 0, 'h'}, + { "deps", no_argument, 0, 'd'}, + { "film", required_argument, 0, 'f'}, + { 0, 0, 0, 0 } + }; + + int option_index = 0; + int c = getopt_long (argc, argv, "hdf:", long_options, &option_index); + + if (c == -1) { + break; + } + + switch (c) { + case 'h': + help (argv[0]); + exit (EXIT_SUCCESS); + case 'd': + cout << dependency_version_summary () << "\n"; + exit (EXIT_SUCCESS); + case 'f': + film_dir = optarg; + break; + } + } + + if (film_dir.empty ()) { + help (argv[0]); + exit (EXIT_FAILURE); + } + + dvdomatic_setup (); + + Film* film = 0; + try { + film = new Film (film_dir, true); + } catch (std::exception& e) { + cerr << argv[0] << ": error reading film `" << film_dir << "' (" << e.what() << ")\n"; + exit (EXIT_FAILURE); + } + + cout << "\nMaking "; + if (film->dcp_ab ()) { + cout << "A/B "; + } + cout << "DCP for " << film->name() << "\n"; + cout << "Content: " << film->content() << "\n"; + pair<string, string> const f = Filter::ffmpeg_strings (film->filters ()); + cout << "Filters: " << f.first << " " << f.second << "\n"; + + film->make_dcp (true); + + list<shared_ptr<Job> > jobs = JobManager::instance()->get (); + + bool all_done = false; + bool first = true; + while (!all_done) { + + sleep (5); + + if (!first) { + cout << "\033[" << jobs.size() << "A"; + cout.flush (); + } + + first = false; + + all_done = true; + for (list<shared_ptr<Job> >::iterator i = jobs.begin(); i != jobs.end(); ++i) { + cout << (*i)->name() << ": "; + + float const p = (*i)->overall_progress (); + + if (p >= 0) { + cout << (*i)->status() << " \n"; + } else { + cout << ": Running \n"; + } + + if (!(*i)->finished ()) { + all_done = false; + } + } + } + + return 0; +} + + diff --git a/src/tools/playomatic.cc b/src/tools/playomatic.cc new file mode 100644 index 000000000..b6fcd43cd --- /dev/null +++ b/src/tools/playomatic.cc @@ -0,0 +1,67 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include "lib/util.h" +#include "gtk/film_player.h" +#include "gtk/film_list.h" + +using namespace std; + +static FilmPlayer* film_player = 0; + +void +film_changed (Film const * f) +{ + film_player->set_film (f); +} + +int +main (int argc, char* argv[]) +{ + dvdomatic_setup (); + + Gtk::Main kit (argc, argv); + + if (argc != 2) { + cerr << "Syntax: " << argv[0] << " <directory>\n"; + exit (EXIT_FAILURE); + } + + Gtk::Window* window = new Gtk::Window (); + + FilmList* film_list = new FilmList (argv[1]); + film_player = new FilmPlayer (); + + Gtk::HBox hbox; + hbox.pack_start (film_list->widget(), true, true); + hbox.pack_start (film_player->widget(), true, true); + + film_list->SelectionChanged.connect (sigc::ptr_fun (&film_changed)); + + window->set_title ("Play-o-matic"); + window->add (hbox); + window->show_all (); + + window->maximize (); + Gtk::Main::run (*window); + + return 0; +} + diff --git a/src/tools/run_film_editor b/src/tools/run_film_editor new file mode 100755 index 000000000..3a3079e92 --- /dev/null +++ b/src/tools/run_film_editor @@ -0,0 +1,4 @@ +#!/bin/sh + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:build/src +build/tools/film_editor $* diff --git a/src/tools/servomatic.cc b/src/tools/servomatic.cc new file mode 100644 index 000000000..b312af352 --- /dev/null +++ b/src/tools/servomatic.cc @@ -0,0 +1,238 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include <stdexcept> +#include <sstream> +#include <cstring> +#include <vector> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <boost/algorithm/string.hpp> +#include <boost/thread.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition.hpp> +#include "config.h" +#include "dcp_video_frame.h" +#include "exceptions.h" +#include "util.h" +#include "config.h" +#include "scaler.h" +#include "image.h" +#include "log.h" + +#define BACKLOG 8 + +using namespace std; +using namespace boost; + +static vector<thread *> worker_threads; + +static std::list<int> queue; +static mutex worker_mutex; +static condition worker_condition; +static Log log_ ("servomatic.log"); + +int +process (int fd) +{ + SocketReader reader (fd); + + char buffer[128]; + reader.read_indefinite ((uint8_t *) buffer, sizeof (buffer)); + reader.consume (strlen (buffer) + 1); + + stringstream s (buffer); + + string command; + s >> command; + if (command != "encode") { + close (fd); + return -1; + } + + Size in_size; + int pixel_format_int; + Size out_size; + int padding; + string scaler_id; + int frame; + float frames_per_second; + string post_process; + int colour_lut_index; + int j2k_bandwidth; + + s >> in_size.width >> in_size.height + >> pixel_format_int + >> out_size.width >> out_size.height + >> padding + >> scaler_id + >> frame + >> frames_per_second + >> post_process + >> colour_lut_index + >> j2k_bandwidth; + + PixelFormat pixel_format = (PixelFormat) pixel_format_int; + Scaler const * scaler = Scaler::from_id (scaler_id); + if (post_process == "none") { + post_process = ""; + } + + shared_ptr<SimpleImage> image (new SimpleImage (pixel_format, in_size)); + + for (int i = 0; i < image->components(); ++i) { + int line_size; + s >> line_size; + image->set_line_size (i, line_size); + } + + for (int i = 0; i < image->components(); ++i) { + reader.read_definite_and_consume (image->data()[i], image->line_size()[i] * image->lines(i)); + } + +#ifdef DEBUG_HASH + image->hash ("Image for encoding (as received by server)"); +#endif + + DCPVideoFrame dcp_video_frame (image, out_size, padding, scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, &log_); + shared_ptr<EncodedData> encoded = dcp_video_frame.encode_locally (); + encoded->send (fd); + +#ifdef DEBUG_HASH + encoded->hash ("Encoded image (as made by server and as sent back)"); +#endif + + + return frame; +} + +void +worker_thread () +{ + while (1) { + mutex::scoped_lock lock (worker_mutex); + while (queue.empty ()) { + worker_condition.wait (lock); + } + + int fd = queue.front (); + queue.pop_front (); + + lock.unlock (); + + int frame = -1; + + struct timeval start; + gettimeofday (&start, 0); + + try { + frame = process (fd); + } catch (std::exception& e) { + cerr << "Error: " << e.what() << "\n"; + } + + close (fd); + + lock.lock (); + + if (frame >= 0) { + struct timeval end; + gettimeofday (&end, 0); + cout << "Encoded frame " << frame << " in " << (seconds (end) - seconds (start)) << "\n"; + } + + worker_condition.notify_all (); + } +} + +int +main () +{ + Scaler::setup_scalers (); + + int const num_threads = Config::instance()->num_local_encoding_threads (); + + for (int i = 0; i < num_threads; ++i) { + worker_threads.push_back (new thread (worker_thread)); + } + + int fd = socket (AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + throw NetworkError ("could not open socket"); + } + + int const o = 1; + setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &o, sizeof (o)); + + struct timeval tv; + tv.tv_sec = 20; + tv.tv_usec = 0; + setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof (tv)); + setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof (tv)); + + struct sockaddr_in server_address; + memset (&server_address, 0, sizeof (server_address)); + server_address.sin_family = AF_INET; + server_address.sin_addr.s_addr = INADDR_ANY; + server_address.sin_port = htons (Config::instance()->server_port ()); + if (::bind (fd, (struct sockaddr *) &server_address, sizeof (server_address)) < 0) { + stringstream s; + s << "could not bind to port " << Config::instance()->server_port() << " (" << strerror (errno) << ")"; + throw NetworkError (s.str()); + } + + listen (fd, BACKLOG); + + while (1) { + struct sockaddr_in client_address; + socklen_t client_length = sizeof (client_address); + int new_fd = accept (fd, (struct sockaddr *) &client_address, &client_length); + if (new_fd < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + throw NetworkError ("accept failed"); + } + + continue; + } + + mutex::scoped_lock lock (worker_mutex); + + /* Wait until the queue has gone down a bit */ + while (int (queue.size()) >= num_threads * 2) { + worker_condition.wait (lock); + } + + struct timeval tv; + tv.tv_sec = 20; + tv.tv_usec = 0; + setsockopt (new_fd, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof (tv)); + setsockopt (new_fd, SOL_SOCKET, SO_SNDTIMEO, (void *) &tv, sizeof (tv)); + + queue.push_back (new_fd); + worker_condition.notify_all (); + } + + close (fd); + + return 0; +} diff --git a/src/tools/servomatictest.cc b/src/tools/servomatictest.cc new file mode 100644 index 000000000..0f37e73a5 --- /dev/null +++ b/src/tools/servomatictest.cc @@ -0,0 +1,159 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include <iomanip> +#include <exception> +#include <getopt.h> +#include "format.h" +#include "film.h" +#include "filter.h" +#include "util.h" +#include "scaler.h" +#include "server.h" +#include "dcp_video_frame.h" +#include "options.h" +#include "decoder.h" +#include "exceptions.h" +#include "scaler.h" +#include "log.h" +#include "decoder_factory.h" + +using namespace std; +using namespace boost; + +static Server* server; +static Log log_ ("servomatictest.log"); + +void +process_video (shared_ptr<Image> image, int frame) +{ + shared_ptr<DCPVideoFrame> local (new DCPVideoFrame (image, Size (1024, 1024), 0, Scaler::from_id ("bicubic"), frame, 24, "", 0, 250000000, &log_)); + shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (image, Size (1024, 1024), 0, Scaler::from_id ("bicubic"), frame, 24, "", 0, 250000000, &log_)); + +#if defined(DEBUG_HASH) + cout << "Frame " << frame << ":\n"; +#else + cout << "Frame " << frame << ": "; + cout.flush (); +#endif + + shared_ptr<EncodedData> local_encoded = local->encode_locally (); + shared_ptr<EncodedData> remote_encoded; + + string remote_error; + try { + remote_encoded = remote->encode_remotely (server); + } catch (NetworkError& e) { + remote_error = e.what (); + } + +#if defined(DEBUG_HASH) + cout << "Frame " << frame << ": "; + cout.flush (); +#endif + + if (!remote_error.empty ()) { + cout << "\033[0;31mnetwork problem: " << remote_error << "\033[0m\n"; + return; + } + + if (local_encoded->size() != remote_encoded->size()) { + cout << "\033[0;31msizes differ\033[0m\n"; + return; + } + + uint8_t* p = local_encoded->data(); + uint8_t* q = remote_encoded->data(); + for (int i = 0; i < local_encoded->size(); ++i) { + if (*p++ != *q++) { + cout << "\033[0;31mdata differ\033[0m at byte " << i << "\n"; + return; + } + } + + cout << "\033[0;32mgood\033[0m\n"; +} + +static void +help (string n) +{ + cerr << "Syntax: " << n << " [--help] --film <film> --server <host>\n"; + exit (EXIT_FAILURE); +} + +int +main (int argc, char* argv[]) +{ + string film_dir; + string server_host; + + while (1) { + static struct option long_options[] = { + { "help", no_argument, 0, 'h'}, + { "server", required_argument, 0, 's'}, + { "film", required_argument, 0, 'f'}, + { 0, 0, 0, 0 } + }; + + int option_index = 0; + int c = getopt_long (argc, argv, "hs:f:", long_options, &option_index); + + if (c == -1) { + break; + } + + switch (c) { + case 'h': + help (argv[0]); + exit (EXIT_SUCCESS); + case 's': + server_host = optarg; + break; + case 'f': + film_dir = optarg; + break; + } + } + + if (server_host.empty() || film_dir.empty()) { + help (argv[0]); + exit (EXIT_FAILURE); + } + + dvdomatic_setup (); + + server = new Server (server_host, 1); + Film film (film_dir, true); + + shared_ptr<Options> opt (new Options ("fred", "jim", "sheila")); + opt->out_size = Size (1024, 1024); + opt->apply_crop = false; + opt->decode_audio = false; + + shared_ptr<Decoder> decoder = decoder_factory (film.state_copy(), opt, 0, &log_); + try { + decoder->Video.connect (sigc::ptr_fun (process_video)); + decoder->go (); + } catch (std::exception& e) { + cerr << "Error: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/tools/test.cc b/src/tools/test.cc new file mode 100644 index 000000000..f81814160 --- /dev/null +++ b/src/tools/test.cc @@ -0,0 +1,15 @@ +#include <stdint.h> +#include <boost/shared_ptr.hpp> +#include "image.h" +#include "server.h" + +using namespace boost; + +int main () +{ + uint8_t* rgb = new uint8_t[256]; + shared_ptr<Image> image (new Image (rgb, 0, 32, 32, 24)); + Server* s = new Server ("localhost", 2); + image->encode_remotely (s); + return 0; +} diff --git a/src/tools/wscript b/src/tools/wscript new file mode 100644 index 000000000..919c98e3f --- /dev/null +++ b/src/tools/wscript @@ -0,0 +1,17 @@ +def build(bld): + for t in ['makedcp', 'servomatic', 'servomatictest', 'fixlengths']: + obj = bld(features = 'cxx cxxprogram') + obj.uselib = 'BOOST_THREAD' + obj.includes = ['..'] + obj.use = ['libdvdomatic'] + obj.source = '%s.cc' % t + obj.target = t + + if not bld.env.DISABLE_GUI: + for t in ['dvdomatic', 'playomatic', 'alignomatic']: + obj = bld(features = 'cxx cxxprogram') + obj.uselib = 'BOOST_THREAD GTKMM' + obj.includes = ['..'] + obj.use = ['libdvdomatic', 'libdvdomatic-gtk'] + obj.source = '%s.cc' % t + obj.target = t |
