]> git.decadent.org.uk Git - videolink.git/blobdiff - generate_dvd.cpp
Increased number of frames in menu, defined a cell covering each menu, and moved...
[videolink.git] / generate_dvd.cpp
index ff1b63574eaf0f01bb7fae9bb78e3b8b061fcd9a..398fdd15e1662d6a095ccc0eafb9a74e0e1247b1 100644 (file)
@@ -7,6 +7,7 @@
 #include <stdexcept>
 
 #include <gdkmm/pixbuf.h>
+#include <glibmm/miscutils.h>
 #include <glibmm/spawn.h>
 
 #include "dvd.hpp"
@@ -55,14 +56,41 @@ 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 "PAL" disc so let's say 0.5 seconds rounded down.
+    const char menu_duration_string[] = "0.5";
+    unsigned menu_duration_frames(const video::frame_params & params)
+    {
+       return params.rate_numer / params.rate_denom / 2;
+    }
 }
 
+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 +126,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 <<
        "<subpictures>\n"
        "  <stream>\n"
        "    <spu force='yes' start='00:00:00.00'\n"
-       "        highlight='" << highlights_temp.get_name() << "'\n"
-       "        select='" << highlights_temp.get_name() << "'>\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 +198,31 @@ 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 -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 +239,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 +274,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 +308,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 <<
-               "      <pgc entry='title' pause='inf'>\n"
+               "      <pgc entry='title'>\n"
                "        <pre>\n"
                // Set a default target location if none is set.
                // This covers first play and use of the "top menu"
@@ -284,7 +319,7 @@ void dvd_generator::generate(const std::string & output_dir) const
        else
        {
            file <<
-               "      <pgc pause='inf'>\n"
+               "      <pgc>\n"
                "        <pre>\n";
        }
 
@@ -332,7 +367,15 @@ void dvd_generator::generate(const std::string & output_dir) const
            // this same menu!
            "          g1 = 0;\n"
            "        </pre>\n"
-           "        <vob file='" << this_menu.vob_temp->get_name() << "'/>\n";
+           "        <vob file='"
+            << temp_file_name(temp_dir_, "menu-%3d.mpeg", 1 + menu_index)
+            << "'>\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.
+           "          <cell start='0' end='" << menu_duration_string << "'"
+           " chapter='yes' pause='inf'/>\n"
+           "        </vob>\n";
 
        for (unsigned button_index = 0;
             button_index != this_menu.entries.size();
@@ -391,11 +434,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.
-           "        <post>\n"
-           "          jump cell 1;\n"
-           "        </post>\n"
            "      </pgc>\n";
     }
 
@@ -491,7 +529,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;