#include "dvd.hpp"
#include "generate_dvd.hpp"
#include "link_iterator.hpp"
+#include "null_prompt_service.hpp"
#include "pixbufs.hpp"
#include "style_sheets.hpp"
#include "temp_file.hpp"
namespace
{
+ // We can try using any of these encoders to convert PNG to MPEG.
+ enum mpeg_encoder
+ {
+ mpeg_encoder_ffmpeg, // ffmpeg
+ mpeg_encoder_mjpegtools_old, // mjpegtools before version 1.8
+ mpeg_encoder_mjpegtools_new // mjpegtools from version 1.8
+ };
+
struct rectangle
{
int left, top; // inclusive
webdvd_window(
const video::frame_params & frame_params,
const std::string & main_page_uri,
- const std::string & output_dir);
+ const std::string & output_dir,
+ mpeg_encoder encoder);
+
+ bool is_finished() const;
private:
dvd_contents::pgc_ref add_menu(const std::string & uri);
video::frame_params frame_params_;
std::string output_dir_;
+ mpeg_encoder encoder_;
browser_widget browser_widget_;
nsCOMPtr<nsIStyleSheet> stylesheet_;
std::auto_ptr<temp_file> background_temp_;
struct page_state;
std::auto_ptr<page_state> page_state_;
+
+ bool finished_;
};
webdvd_window::webdvd_window(
const video::frame_params & frame_params,
const std::string & main_page_uri,
- const std::string & output_dir)
+ const std::string & output_dir,
+ mpeg_encoder encoder)
: frame_params_(frame_params),
output_dir_(output_dir),
+ encoder_(encoder),
stylesheet_(load_css("file://" WEBDVD_LIB_DIR "/webdvd.css")),
pending_window_update_(false),
pending_req_count_(0),
- have_tweaked_page_(false)
+ have_tweaked_page_(false),
+ finished_(false)
{
set_size_request(frame_params_.width, frame_params_.height);
set_resizable(false);
load_next_page();
}
+ bool webdvd_window::is_finished() const
+ {
+ return finished_;
+ }
+
dvd_contents::pgc_ref webdvd_window::add_menu(const std::string & uri)
{
dvd_contents::pgc_ref next_menu(dvd_contents::menu_pgc,
try
{
if (!process_page())
+ {
+ finished_ = true;
Gtk::Main::quit();
+ }
}
catch (std::exception & e)
{
{
std::ostringstream command_stream;
- command_stream << "pngtopnm "
- << background_temp_->get_name()
- << " | ppmtoy4m -v0 -n1 -F"
- << frame_params_.rate_numer
- << ":" << frame_params_.rate_denom
- << " -A" << frame_params_.pixel_ratio_width
- << ":" << frame_params_.pixel_ratio_height
- << (" -Ip -S420_mpeg2"
- " | mpeg2enc -v0 -f8 -a2 -o/dev/stdout"
- " | mplex -v0 -f8 -o/dev/stdout /dev/stdin"
- " | spumux -v0 -mdvd ")
- << state->spumux_temp.get_name()
- << " > "
- << contents_.menus[menu_num].vob_temp->get_name();
+ if (encoder_ == mpeg_encoder_ffmpeg)
+ {
+ command_stream
+ << "ffmpeg"
+ << " -f image2 -vcodec png -i "
+ << background_temp_->get_name()
+ << " -target " << frame_params_.ffmpeg_name << "-dvd"
+ << " -vcodec mpeg2video -an -y /dev/stdout"
+ << " | spumux -v0 -mdvd " << state->spumux_temp.get_name()
+ << " > " << contents_.menus[menu_num].vob_temp->get_name();
+ }
+ else
+ {
+ assert(encoder_ == mpeg_encoder_mjpegtools_old
+ || encoder_ == mpeg_encoder_mjpegtools_new);
+ command_stream
+ << "pngtopnm "
+ << background_temp_->get_name()
+ << " | ppmtoy4m -v0 -n1 -F"
+ << frame_params_.rate_numer
+ << ":" << frame_params_.rate_denom
+ << " -A" << frame_params_.pixel_ratio_width
+ << ":" << frame_params_.pixel_ratio_height
+ << " -Ip ";
+ // The chroma subsampling keywords changed between
+ // versions 1.6.2 and 1.8 of mjpegtools. There is no
+ // keyword that works with both.
+ if (encoder_ == mpeg_encoder_mjpegtools_old)
+ command_stream << "-S420_mpeg2";
+ else
+ command_stream << "-S420mpeg2";
+ command_stream
+ << (" | mpeg2enc -v0 -f8 -a2 -o/dev/stdout"
+ " | mplex -v0 -f8 -o/dev/stdout /dev/stdin"
+ " | spumux -v0 -mdvd ")
+ << state->spumux_temp.get_name()
+ << " > "
+ << contents_.menus[menu_num].vob_temp->get_name();
+ }
std::string command(command_stream.str());
const char * argv[] = {
"/bin/sh", "-c", command.c_str(), 0
const video::frame_params & lookup_frame_params(const char * str)
{
assert(str);
- static const struct { const char * str; bool is_ntsc; }
- known_strings[] = {
- { "NTSC", true },
- { "ntsc", true },
- { "PAL", false },
- { "pal", false },
- // For DVD purposes, SECAM can be treated identically to PAL.
- { "SECAM", false },
- { "secam", false }
+ static const char * const known_strings[] = {
+ "525", "625",
+ "525/60", "625/50",
+ "NTSC", "PAL",
+ "ntsc", "pal"
};
for (std::size_t i = 0;
i != sizeof(known_strings)/sizeof(known_strings[0]);
++i)
- if (std::strcmp(str, known_strings[i].str) == 0)
- return known_strings[i].is_ntsc ?
- video::ntsc_params : video::pal_params;
+ if (std::strcmp(str, known_strings[i]) == 0)
+ return (i & 1)
+ ? video::frame_params_625
+ : video::frame_params_525;
throw std::runtime_error(
std::string("Invalid video standard: ").append(str));
}
void print_usage(std::ostream & stream, const char * command_name)
{
- stream << "Usage: " << command_name
- << (" [gtk-options] [--video-std std-name]"
- " [--preview] menu-url [output-dir]\n");
+ stream <<
+ "Usage: " << command_name << " [gtk-options] [--preview]\n"
+ " [--video-std {525|525/60|NTSC|ntsc"
+ " | 625|625/50|PAL|pal}]\n"
+ " [--encoder {mjpegtools|mjpegtools-old}]\n"
+ " menu-url [output-dir]\n";
}
void set_browser_preferences()
} // namespace
+void fatal_error(const std::string & message)
+{
+ std::cerr << "Fatal error: " << message << "\n";
+ Gtk::Main::quit();
+}
+
int main(int argc, char ** argv)
{
try
{
- video::frame_params frame_params = video::pal_params;
+ video::frame_params frame_params = video::frame_params_625;
bool preview_mode = false;
std::string menu_url;
std::string output_dir;
+ mpeg_encoder encoder = mpeg_encoder_ffmpeg;
// Do initial option parsing. We have to do this before
// letting Gtk parse the arguments since we may need to spawn
{
argi += 2;
}
+ else if (std::strcmp(argv[argi], "--save-temps") == 0)
+ {
+ temp_file::keep_all(true);
+ argi += 1;
+ }
+ else if (std::strcmp(argv[argi], "--encoder") == 0)
+ {
+ if (argi + 1 == argc)
+ {
+ std::cerr << "Missing argument to --encoder\n";
+ print_usage(std::cerr, argv[0]);
+ return EXIT_FAILURE;
+ }
+ if (std::strcmp(argv[argi + 1], "ffmpeg") == 0)
+ {
+ encoder = mpeg_encoder_ffmpeg;
+ }
+ else if (std::strcmp(argv[argi + 1], "mjpegtools-old") == 0)
+ {
+ encoder = mpeg_encoder_mjpegtools_old;
+ }
+ else if (std::strcmp(argv[argi + 1], "mjpegtools") == 0
+ || std::strcmp(argv[argi + 1], "mjpegtools-new") == 0)
+ {
+ encoder = mpeg_encoder_mjpegtools_new;
+ }
+ else
+ {
+ std::cerr << "Invalid argument to --encoder\n";
+ print_usage(std::cerr, argv[0]);
+ return EXIT_FAILURE;
+ }
+ argi += 2;
+ }
else if (argv[argi][0] == '-')
{
std::cerr << "Invalid option: " << argv[argi] << "\n";
// Initialise Mozilla
browser_widget::initialiser browser_init;
set_browser_preferences();
+ if (!preview_mode)
+ null_prompt_service::install();
// Run the browser/converter
- webdvd_window window(frame_params, menu_url, output_dir);
+ webdvd_window window(frame_params, menu_url, output_dir, encoder);
Gtk::Main::run(window);
+
+ return ((preview_mode || window.is_finished())
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE);
}
catch (std::exception & e)
{
std::cerr << "Fatal error: " << e.what() << "\n";
return EXIT_FAILURE;
}
-
- return EXIT_SUCCESS;
}