]> git.decadent.org.uk Git - videolink.git/blobdiff - generate_dvd.cpp
Corrected formula in menu_duration_seconds. It looks like any sufficiently large...
[videolink.git] / generate_dvd.cpp
index 398fdd15e1662d6a095ccc0eafb9a74e0e1247b1..5d357e6d39c385fe6eb76a89551bd5c115340e64 100644 (file)
@@ -1,8 +1,12 @@
 // Copyright 2005-6 Ben Hutchings <ben@decadent.org.uk>.
 // See the file "COPYING" for licence details.
 
+#include <cerrno>
+#include <cstring>
 #include <fstream>
+#include <iomanip>
 #include <iostream>
+#include <ostream>
 #include <sstream>
 #include <stdexcept>
 
@@ -76,12 +80,31 @@ namespace
     // 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 "PAL" disc so let's say 0.5 seconds rounded down.
-    const char menu_duration_string[] = "0.5";
+    // on a commercial "PAL" disc so let's use 12 frames regardless.
     unsigned menu_duration_frames(const video::frame_params & params)
     {
-       return params.rate_numer / params.rate_denom / 2;
+       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);
+    }
+
+    void throw_length_error(const char * limit_type, std::size_t limit)
+    {
+       std::ostringstream oss;
+       oss << "exceeded DVD limit: " << limit_type << " > " << limit;
+       throw std::length_error(oss.str());
+    }
+
+    // dvdauthor uses some menu numbers to represent entry points -
+    // distinct from the actual numbers of the menus assigned as those
+    // entry points - resulting in a practical limit of 119 per
+    // domain.  This seems to be an oddity of the parser that could be
+    // fixed, but for now we'll have to work with it.
+    const unsigned dvdauthor_anonymous_menus_max = dvd::domain_pgcs_max - 8;
 }
 
 dvd_generator::dvd_generator(const video::frame_params & frame_params,
@@ -95,13 +118,8 @@ dvd_generator::pgc_ref dvd_generator::add_menu()
 {
     pgc_ref next_menu(menu_pgc, menus_.size());
 
-    // Check against maximum number of menus.  It appears that no more
-    // than 128 menus are reachable through LinkPGCN instructions, and
-    // dvdauthor uses some menu numbers for special purposes, resulting
-    // in a practical limit of 119 per domain.  We can work around this
-    // later by spreading some menus across titlesets.
-    if (next_menu.index == 119)
-       throw std::runtime_error("No more than 119 menus can be used");
+    if (next_menu.index == dvdauthor_anonymous_menus_max)
+       throw_length_error("number of menus", dvdauthor_anonymous_menus_max);
 
     menus_.resize(next_menu.index + 1);
     return next_menu;
@@ -114,6 +132,10 @@ void dvd_generator::add_menu_entry(unsigned index,
     assert(index < menus_.size());
     assert(target.type == menu_pgc && target.index < menus_.size()
           || target.type == title_pgc && target.index < titles_.size());
+
+    if (menus_[index].entries.size() == dvd::menu_buttons_max)
+       throw_length_error("number of buttons", dvd::menu_buttons_max);
+
     menu_entry new_entry = { area, target };
     menus_[index].entries.push_back(new_entry);
 }
@@ -212,7 +234,10 @@ void dvd_generator::generate_menu_vob(unsigned index,
                    std::string("symlink: ").append(std::strerror(errno)));
        }
        command_stream <<
-           "ffmpeg -f image2 -vcodec png -i " << background_name << "-%02d"
+           "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";
     }
@@ -264,8 +289,8 @@ 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 == 99)
-       throw std::runtime_error("No more than 99 titles can be used");
+    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);
@@ -373,7 +398,9 @@ void dvd_generator::generate(const std::string & output_dir) const
            // 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.
-           "          <cell start='0' end='" << menu_duration_string << "'"
+           "          <cell start='0' end='"
+            << std::fixed << std::setprecision(4)
+            << menu_duration_seconds(frame_params_) << "'"
            " chapter='yes' pause='inf'/>\n"
            "        </vob>\n";