+ command_stream <<
+ "ffmpeg -f image2 -vcodec png"
+ " -r " << frame_params_.rate_numer <<
+ "/" << frame_params_.rate_denom <<
+ " -i " << background_name << "-%02d"
+ " -target " << frame_params_.common_name << "-dvd"
+ " -vcodec mpeg2video -aspect 4:3 -an -y /dev/stdout";
+ }
+ else
+ {
+ assert(encoder_ == mpeg_encoder_mjpegtools_old
+ || encoder_ == mpeg_encoder_mjpegtools_new);
+ command_stream
+ << "pngtopnm " << background_name
+ << " | ppmtoy4m -v0 -n" << frame_count << " -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";
+ }
+ command_stream
+ << " | spumux -v0 -mdvd " << spumux_name
+ << " > " << temp_file_name(temp_dir_, "menu-%3d.mpeg", 1 + index);
+ std::string command(command_stream.str());
+ const char * argv[] = {
+ "/bin/sh", "-c", command.c_str(), 0
+ };
+ std::cout << "INFO: Running " << command << std::endl;
+ int command_result;
+ Glib::spawn_sync(".",
+ Glib::ArrayHandle<std::string>(
+ argv, sizeof(argv)/sizeof(argv[0]),
+ Glib::OWNERSHIP_NONE),
+ Glib::SPAWN_STDOUT_TO_DEV_NULL,
+ sigc::slot<void>(),
+ 0, 0,
+ &command_result);
+ if (command_result != 0)
+ throw std::runtime_error("spumux pipeline failed");
+}
+
+dvd_generator::pgc_ref dvd_generator::add_title(vob_list & content)
+{
+ pgc_ref next_title(title_pgc, titles_.size());
+
+ // Check against maximum number of titles.
+ if (next_title.index == dvd::titles_max)
+ throw_length_error("number of titles", dvd::titles_max);
+
+ titles_.resize(next_title.index + 1);
+ titles_[next_title.index].swap(content);
+ return next_title;
+}
+
+void dvd_generator::generate(const std::string & output_dir) const
+{
+ // This function uses a mixture of 0-based and 1-based numbering,
+ // due to the differing conventions of the language and the DVD
+ // format. Variable names ending in "_index" indicate 0-based
+ // indices and variable names ending in "_num" indicate 1-based
+ // numbers.
+
+ std::string name(temp_file_name(temp_dir_, "videolink.dvdauthor"));
+ std::ofstream file(name.c_str());
+ file << "<dvdauthor>\n";
+
+ // We generate code that uses registers in the following way:
+ //
+ // g0: Scratch.
+ // g1: Target location when jumping between menus. Top 6 bits are
+ // the button number (like s8) and bottom 10 bits are the menu
+ // number. This is used for selecting the appropriate button
+ // when entering a menu, for completing indirect jumps between
+ // domains, and for jumping to the correct menu after exiting a
+ // title. This is set to 0 in the pre-routine of the target
+ // menu.
+ // g2: Current location in menus. This is used for jumping to the
+ // correct menu when the player exits a title.
+ // g3: Target chapter number plus 1 when jumping to a title.
+ // This is used to jump to the correct chapter and to
+ // distinguish between indirect jumps to menus and titles.
+ // This is set to 0 in the pre-routine of the target title.
+ // g4: Source menu location used to jump to a title. This is
+ // compared with g2 to determine whether to increment the
+ // button number if the title is played to the end.
+
+ static const unsigned button_mult = dvd::reg_s8_button_mult;
+ static const unsigned menu_mask = button_mult - 1;
+ static const unsigned button_mask = (1U << dvd::reg_bits) - button_mult;