X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=generate_dvd.cpp;h=64079d28ad6c406e6579cc452bb7f1ae1e364ded;hb=12780c6ecf9ec8c227d5795e8fd1083b48891d7b;hp=ff1b63574eaf0f01bb7fae9bb78e3b8b061fcd9a;hpb=f5d9069647f70b7aab8e656f59cf42176c419461;p=videolink.git diff --git a/generate_dvd.cpp b/generate_dvd.cpp index ff1b635..64079d2 100644 --- a/generate_dvd.cpp +++ b/generate_dvd.cpp @@ -1,12 +1,17 @@ // Copyright 2005-6 Ben Hutchings . // See the file "COPYING" for licence details. +#include +#include #include +#include #include +#include #include #include #include +#include #include #include "dvd.hpp" @@ -55,14 +60,46 @@ namespace + (end_y - start_y) * (end_y - start_y); return vertical_distance / distance_squared; } -} -dvd_generator::menu::menu() - : vob_temp(new temp_file("videolink-vob-")) -{ - vob_temp->close(); + std::string temp_file_name(const temp_dir & dir, + std::string base_name, + unsigned index=0) + { + if (index != 0) + { + std::size_t index_pos = base_name.find("%3d"); + assert(index_pos != std::string::npos); + base_name[index_pos] = '0' + index / 100; + base_name[index_pos + 1] = '0' + (index / 10) % 10; + base_name[index_pos + 2] = '0' + index % 10; + } + + return Glib::build_filename(dir.get_name(), base_name); + } + + // We would like to use just a single frame for the menu but this + // seems not to be legal or compatible. The minimum length of a + // cell is 0.4 seconds but I've seen a static menu using 12 frames + // on a commercial "PAL" disc so let's use 12 frames regardless. + unsigned menu_duration_frames(const video::frame_params & params) + { + return 12; + } + double menu_duration_seconds(const video::frame_params & params) + { + return double(menu_duration_frames(params)) + * double(params.rate_numer) + / double(params.rate_denom); + } } +dvd_generator::dvd_generator(const video::frame_params & frame_params, + mpeg_encoder encoder) + : temp_dir_("videolink-"), + frame_params_(frame_params), + encoder_(encoder) +{} + dvd_generator::pgc_ref dvd_generator::add_menu() { pgc_ref next_menu(menu_pgc, menus_.size()); @@ -98,25 +135,25 @@ void dvd_generator::generate_menu_vob(unsigned index, assert(index < menus_.size()); const menu & this_menu = menus_[index]; - temp_file background_temp("videolink-back-"); - background_temp.close(); - std::cout << "saving " << background_temp.get_name() << std::endl; - background->save(background_temp.get_name(), "png"); + std::string background_name( + temp_file_name(temp_dir_, "menu-%3d-back.png", 1 + index)); + std::cout << "saving " << background_name << std::endl; + background->save(background_name, "png"); - temp_file highlights_temp("videolink-links-"); - highlights_temp.close(); - std::cout << "saving " << highlights_temp.get_name() << std::endl; - highlights->save(highlights_temp.get_name(), "png"); + std::string highlights_name( + temp_file_name(temp_dir_, "menu-%3d-links.png", 1 + index)); + std::cout << "saving " << highlights_name << std::endl; + highlights->save(highlights_name, "png"); - temp_file spumux_temp("videolink-spumux-"); - spumux_temp.close(); - std::ofstream spumux_file(spumux_temp.get_name().c_str()); + std::string spumux_name( + temp_file_name(temp_dir_, "menu-%3d.subpictures", 1 + index)); + std::ofstream spumux_file(spumux_name.c_str()); spumux_file << "\n" " \n" " \n"; + " highlight='" << highlights_name << "'\n" + " select='" << highlights_name << "'>\n"; int button_count = this_menu.entries.size(); for (int i = 0; i != button_count; ++i) { @@ -170,23 +207,34 @@ void dvd_generator::generate_menu_vob(unsigned index, throw std::runtime_error("Failed to write control file for spumux"); std::ostringstream command_stream; + unsigned frame_count(menu_duration_frames(frame_params_)); if (encoder_ == mpeg_encoder_ffmpeg) { - command_stream - << "ffmpeg" - << " -f image2 -vcodec png -i " - << background_temp.get_name() - << " -target " << frame_params_.common_name << "-dvd" - << " -vcodec mpeg2video -aspect 4:3 -an -y /dev/stdout"; + for (unsigned i = 0; i != frame_count; ++i) + { + std::string frame_name(background_name); + frame_name.push_back('-'); + frame_name.push_back('0' + i / 10); + frame_name.push_back('0' + i % 10); + if (symlink(background_name.c_str(), frame_name.c_str()) != 0) + throw std::runtime_error( + std::string("symlink: ").append(std::strerror(errno))); + } + 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_temp.get_name() - << " | ppmtoy4m -v0 -n1 -F" + << "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 @@ -203,8 +251,8 @@ void dvd_generator::generate_menu_vob(unsigned index, " | mplex -v0 -f8 -o/dev/stdout /dev/stdin"; } command_stream - << " | spumux -v0 -mdvd " << spumux_temp.get_name() - << " > " << this_menu.vob_temp->get_name(); + << " | 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 @@ -238,9 +286,8 @@ dvd_generator::pgc_ref dvd_generator::add_title(vob_list & content) void dvd_generator::generate(const std::string & output_dir) const { - temp_file temp("videolink-dvdauthor-"); - temp.close(); - std::ofstream file(temp.get_name().c_str()); + std::string name(temp_file_name(temp_dir_, "videolink.dvdauthor")); + std::ofstream file(name.c_str()); // We generate code that uses registers in the following way: // @@ -273,7 +320,7 @@ void dvd_generator::generate(const std::string & output_dir) const // This is the first (title) menu, displayed when the // disc is first played. file << - " \n" + " \n" "
\n"
 		// Set a default target location if none is set.
 		// This covers first play and use of the "top menu"
@@ -284,7 +331,7 @@ void dvd_generator::generate(const std::string & output_dir) const
 	else
 	{
 	    file <<
-		"      \n"
+		"      \n"
 		"        
\n";
 	}
 
@@ -332,7 +379,17 @@ void dvd_generator::generate(const std::string & output_dir) const
 	    // this same menu!
 	    "          g1 = 0;\n"
 	    "        
\n" - " \n"; + " \n" + // Define a cell covering the whole menu and set a still + // time at the end of that, since it seems all players + // support that but some ignore a still time set on a PGC. + " \n" + " \n"; for (unsigned button_index = 0; button_index != this_menu.entries.size(); @@ -391,11 +448,6 @@ void dvd_generator::generate(const std::string & output_dir) const } file << - // Some DVD players don't seem to obey pause='inf' so make - // them loop. - " \n" - " jump cell 1;\n" - " \n" "
\n"; } @@ -491,7 +543,7 @@ void dvd_generator::generate(const std::string & output_dir) const const char * argv[] = { "dvdauthor", "-o", output_dir.c_str(), - "-x", temp.get_name().c_str(), + "-x", name.c_str(), 0 }; int command_result;