]> git.decadent.org.uk Git - videolink.git/commitdiff
Separated out dvd_contents and generate_dvd.
authorBen Hutchings <ben@decadent.org.uk>
Mon, 20 Feb 2006 01:30:20 +0000 (01:30 +0000)
committerBen Hutchings <ben@decadent.org.uk>
Sun, 2 Nov 2008 23:39:57 +0000 (23:39 +0000)
generate_dvd.cpp [new file with mode: 0644]
generate_dvd.hpp [new file with mode: 0644]
webdvd.cpp

diff --git a/generate_dvd.cpp b/generate_dvd.cpp
new file mode 100644 (file)
index 0000000..6144ef5
--- /dev/null
@@ -0,0 +1,234 @@
+// Copyright 2005-6 Ben Hutchings <ben@decadentplace.org.uk>.
+// See the file "COPYING" for licence details.
+
+#include <fstream>
+#include <stdexcept>
+
+#include <glibmm/spawn.h>
+
+#include "dvd.hpp"
+#include "generate_dvd.hpp"
+
+dvd_contents::menu::menu()
+       : vob_temp(new temp_file("webdvd-vob-"))
+{
+    vob_temp->close();
+}
+
+void generate_dvd(const dvd_contents & contents,
+                 const std::string & output_dir)
+{
+    temp_file temp("webdvd-dvdauthor-");
+    temp.close();
+    std::ofstream file(temp.get_name().c_str());
+
+    // We generate code that uses registers in the following way:
+    //
+    // g0:     scratch
+    // g1:     current location
+    // g12:    location that last jumped to a video
+    //
+    // All locations are divided into two bitfields: the least
+    // significant 10 bits are a page/menu number and the most
+    // significant 6 bits are a link/button number, and numbering
+    // starts at 1, not 0.  This is done for compatibility with
+    // the encoding of the s8 (button) register.
+    //
+    static const int button_mult = dvd::reg_s8_button_mult;
+    static const int menu_mask = button_mult - 1;
+    static const int button_mask = (1 << dvd::reg_bits) - button_mult;
+
+    file <<
+       "<dvdauthor>\n"
+       "  <vmgm>\n"
+       "    <menus>\n";
+           
+    for (unsigned menu_num = 0;
+        menu_num != contents.menus.size();
+        ++menu_num)
+    {
+       const dvd_contents::menu & menu = contents.menus[menu_num];
+
+       if (menu_num == 0)
+       {
+           // This is the first (title) menu, displayed when the
+           // disc is first played.
+           file <<
+               "      <pgc entry='title'>\n"
+               "        <pre>\n"
+               // Initialise the current location if it is not set
+               // (all general registers are initially 0).
+               "          if (g1 eq 0)\n"
+               "            g1 = " << 1 + button_mult << ";\n";
+       }
+       else
+       {
+           file <<
+               "      <pgc>\n"
+               "        <pre>\n";
+       }
+
+       // When a title finishes or the user presses the menu
+       // button, this always jumps to the titleset's root menu.
+       // We want to return the user to the last menu they used.
+       // So we arrange for each titleset's root menu to return
+       // to the vmgm title menu and then dispatch from there to
+       // whatever the correct menu is.  We determine the correct
+       // menu by looking at the menu part of g1.
+
+       file << "          g0 = g1 &amp; " << menu_mask << ";\n";
+
+       // There is a limit of 128 VM instructions in each PGC.
+       // Therefore in each menu's <pre> section we generate
+       // jumps to menus with numbers greater by 512, 256, 128,
+       // ..., 1 where (a) such a menu exists, (b) this menu
+       // number is divisible by twice that increment and (c) the
+       // correct menu is that or a later menu.  Thus each menu
+       // has at most 10 such conditional jumps and is reachable
+       // by at most 10 jumps from the title menu.  This chain of
+       // jumps might take too long on some players; this has yet
+       // to be investigated.
+           
+       for (std::size_t menu_incr = (menu_mask + 1) / 2;
+            menu_incr != 0;
+            menu_incr /= 2)
+       {
+           if (menu_num + menu_incr < contents.menus.size()
+               && (menu_num & (menu_incr * 2 - 1)) == 0)
+           {
+               file <<
+                   "          if (g0 ge " << 1 + menu_num + menu_incr
+                                          << ")\n"
+                   "            jump menu " << 1 + menu_num + menu_incr
+                                          << ";\n";
+           }
+       }
+
+       file <<
+           // Highlight the appropriate button.
+           "          s8 = g1 &amp; " << button_mask << ";\n"
+           "        </pre>\n"
+           "        <vob file='" << menu.vob_temp->get_name() << "'/>\n";
+
+       for (unsigned button_num = 0;
+            button_num != menu.entries.size();
+            ++button_num)
+       {
+           const dvd_contents::pgc_ref & target =
+               menu.entries[button_num];
+
+           file << "        <button> ";
+
+           if (target.type == dvd_contents::menu_pgc)
+           {
+               unsigned target_button_num;
+
+               if (target.sub_index)
+               {
+                   target_button_num = target.sub_index;
+               }
+               else
+               {
+                   // Look for a button on the new menu that links
+                   // back to this one.  If there is one, set that to
+                   // be the highlighted button; otherwise, use the
+                   // first button.
+                   const std::vector<dvd_contents::pgc_ref> &
+                       target_menu_entries =
+                       contents.menus[target.index].entries;
+                   dvd_contents::pgc_ref this_pgc(dvd_contents::menu_pgc,
+                                                  menu_num);
+                   target_button_num = target_menu_entries.size();
+                   while (target_button_num != 0
+                          && (target_menu_entries[--target_button_num]
+                              != this_pgc))
+                       ;
+                   target_button_num += 1;
+               }
+                        
+               file << "g1 = "
+                    << (1 + target.index
+                        + target_button_num * button_mult)
+                    << "; jump menu " << 1 + target.index << ";";
+           }
+           else
+           {
+               assert(target.type == dvd_contents::title_pgc);
+
+               file << "g1 = "
+                    << 1 + menu_num + (1 + button_num) * button_mult
+                    << "; jump title "
+                    << 1 + target.index;
+               if (target.sub_index)
+                   file << " chapter " << target.sub_index;
+               file << ";";
+           }
+
+           file <<  " </button>\n";
+       }
+
+       file << "      </pgc>\n";
+    }
+
+    file <<
+       "    </menus>\n"
+       "  </vmgm>\n";
+
+    // Generate a titleset for each title.  This appears to make
+    // jumping to titles a whole lot simpler (but limits us to 99
+    // titles).
+    for (unsigned title_num = 0;
+        title_num != contents.titles.size();
+        ++title_num)
+    {
+       file <<
+           "  <titleset>\n"
+           // Generate a dummy menu so that the menu button on the
+           // remote control will work.
+           "    <menus>\n"
+           "      <pgc entry='root'>\n"
+           "        <pre> jump vmgm menu; </pre>\n"
+           "      </pgc>\n"
+           "    </menus>\n"
+           "    <titles>\n"
+           "      <pgc>\n"
+           // Record calling location.
+           "        <pre> g12 = g1; </pre>\n"
+            << contents.titles[title_num].vob_list <<
+           // If the menu location has not been changed during
+           // the title, set the location to be the following
+           // button in the menu.  In any case, return to some
+           // menu.
+           "        <post> if (g1 eq g12) g1 = g1 + " << button_mult
+            << "; call menu; </post>\n"
+           "      </pgc>\n"
+           "    </titles>\n"
+           "  </titleset>\n";
+    }
+
+    file <<
+       "</dvdauthor>\n";
+
+    file.close();
+
+    {
+       const char * argv[] = {
+           "dvdauthor",
+           "-o", output_dir.c_str(),
+           "-x", temp.get_name().c_str(),
+           0
+       };
+       int command_result;
+       Glib::spawn_sync(".",
+                        Glib::ArrayHandle<std::string>(
+                            argv, sizeof(argv)/sizeof(argv[0]),
+                            Glib::OWNERSHIP_NONE),
+                        Glib::SPAWN_SEARCH_PATH
+                        | Glib::SPAWN_STDOUT_TO_DEV_NULL,
+                        SigC::Slot0<void>(),
+                        0, 0,
+                        &command_result);
+       if (command_result != 0)
+           throw std::runtime_error("dvdauthor failed");
+    }
+}
diff --git a/generate_dvd.hpp b/generate_dvd.hpp
new file mode 100644 (file)
index 0000000..8ce8f7b
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2005-6 Ben Hutchings <ben@decadentplace.org.uk>.
+// See the file "COPYING" for licence details.
+
+#ifndef INC_GENERATE_DVD_HPP
+#define INC_GENERATE_DVD_HPP
+
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+
+#include "temp_file.hpp"
+
+// Description of menus and titles to go on a DVD.
+
+struct dvd_contents
+{
+    enum pgc_type { unknown_pgc,  menu_pgc, title_pgc };
+
+    // Reference to some PGC (program chain).
+    struct pgc_ref
+    {
+       explicit pgc_ref(pgc_type type = unknown_pgc,
+                        int index = -1,
+                        int sub_index = 0)
+               : type(type), index(index), sub_index(sub_index)
+           {}
+       bool operator==(const pgc_ref & other) const
+           {
+               return type == other.type && index == other.index;
+           }
+       bool operator!=(const pgc_ref & other) const
+           {
+               return !(*this == other);
+           }
+           
+       pgc_type type;      // Menu or title reference?
+       unsigned index;     // Menu or title index (within resp. vector)
+       unsigned sub_index; // Button or chapter number (1-based; 0 if
+                           // unspecified; not compared!)
+    };
+
+    // Menu definition.
+    struct menu
+    {
+       menu();
+
+       // Temporary file in which the menu VOB should be generated.
+       // This is created as an empty file and then closed.
+       boost::shared_ptr<temp_file> vob_temp;
+
+       // References to the menus and titles that the menu buttons
+       // are meant to link to, in the same order as the buttons.
+       std::vector<pgc_ref> entries;
+    };
+
+    // Title definition.  This is currently just an XML fragment
+    // consisting of one of more <vob> elements.  It is included
+    // directly within the corresponding <pgc> element in the file
+    // passed to dvdauthor.
+    struct title
+    {
+       explicit title(const std::string & vob_list)
+               : vob_list(vob_list)
+           {}
+
+       std::string vob_list;
+    };
+
+    std::vector<menu> menus;
+    std::vector<title> titles;
+};
+
+// Use dvdauthor to generate a DVD filesystem with the given contents.
+void generate_dvd(const dvd_contents & contents,
+                 const std::string & output_dir);
+
+#endif // !INC_GENERATE_DVD_HPP
index eb2a9b2743d28a2a59bdd369af6ab072c25ae16f..8019007deb4fda978bc7533328dbbdcd28bfb169 100644 (file)
@@ -53,6 +53,7 @@
 #include "browser_widget.hpp"
 #include "child_iterator.hpp"
 #include "dvd.hpp"
+#include "generate_dvd.hpp"
 #include "link_iterator.hpp"
 #include "pixbufs.hpp"
 #include "style_sheets.hpp"
@@ -172,60 +173,6 @@ namespace
     }
 
     
-    struct dvd_contents
-    {
-       enum pgc_type { unknown_pgc,  menu_pgc, title_pgc };
-
-       struct pgc_ref
-       {
-           explicit pgc_ref(pgc_type type = unknown_pgc,
-                            int index = -1,
-                            int sub_index = 0)
-                   : type(type), index(index), sub_index(sub_index)
-               {}
-           bool operator==(const pgc_ref & other) const
-               {
-                   return type == other.type && index == other.index;
-               }
-           bool operator!=(const pgc_ref & other) const
-               {
-                   return !(*this == other);
-               }
-           
-           pgc_type type;      // Menu or title reference?
-           unsigned index;     // Menu or title index (within resp. vector)
-           unsigned sub_index; // Button or chapter number (1-based; 0 if
-                               // unspecified; not compared!)
-       };
-
-       struct menu
-       {
-           menu()
-                   : vob_temp(new temp_file("webdvd-vob-"))
-               {
-                   vob_temp->close();
-               }
-
-           boost::shared_ptr<temp_file> vob_temp;
-           std::vector<pgc_ref> entries;
-       };
-
-       struct title
-       {
-           explicit title(const std::string & vob_list)
-                   : vob_list(vob_list)
-               {}
-
-           std::string vob_list;
-       };
-
-       std::vector<menu> menus;
-       std::vector<title> titles;
-    };
-
-    void generate_dvd(const dvd_contents & contents,
-                     const std::string & output_dir);
-
     class webdvd_window : public Gtk::Window
     {
     public:
@@ -837,224 +784,6 @@ namespace
        }
     }
 
-    void generate_dvd(const dvd_contents & contents,
-                     const std::string & output_dir)
-    {
-       temp_file temp("webdvd-dvdauthor-");
-       temp.close();
-       std::ofstream file(temp.get_name().c_str());
-
-       // We generate code that uses registers in the following way:
-       //
-       // g0:     scratch
-       // g1:     current location
-       // g12:    location that last jumped to a video
-       //
-       // All locations are divided into two bitfields: the least
-       // significant 10 bits are a page/menu number and the most
-       // significant 6 bits are a link/button number, and numbering
-       // starts at 1, not 0.  This is done for compatibility with
-       // the encoding of the s8 (button) register.
-       //
-       static const int button_mult = dvd::reg_s8_button_mult;
-       static const int menu_mask = button_mult - 1;
-       static const int button_mask = (1 << dvd::reg_bits) - button_mult;
-
-       file <<
-           "<dvdauthor>\n"
-           "  <vmgm>\n"
-           "    <menus>\n";
-           
-       for (unsigned menu_num = 0;
-            menu_num != contents.menus.size();
-            ++menu_num)
-       {
-           const dvd_contents::menu & menu = contents.menus[menu_num];
-
-           if (menu_num == 0)
-           {
-               // This is the first (title) menu, displayed when the
-               // disc is first played.
-               file <<
-                   "      <pgc entry='title'>\n"
-                   "        <pre>\n"
-                   // Initialise the current location if it is not set
-                   // (all general registers are initially 0).
-                   "          if (g1 eq 0)\n"
-                   "            g1 = " << 1 + button_mult << ";\n";
-           }
-           else
-           {
-               file <<
-                   "      <pgc>\n"
-                   "        <pre>\n";
-           }
-
-           // When a title finishes or the user presses the menu
-           // button, this always jumps to the titleset's root menu.
-           // We want to return the user to the last menu they used.
-           // So we arrange for each titleset's root menu to return
-           // to the vmgm title menu and then dispatch from there to
-           // whatever the correct menu is.  We determine the correct
-           // menu by looking at the menu part of g1.
-
-           file << "          g0 = g1 &amp; " << menu_mask << ";\n";
-
-           // There is a limit of 128 VM instructions in each PGC.
-           // Therefore in each menu's <pre> section we generate
-           // jumps to menus with numbers greater by 512, 256, 128,
-           // ..., 1 where (a) such a menu exists, (b) this menu
-           // number is divisible by twice that increment and (c) the
-           // correct menu is that or a later menu.  Thus each menu
-           // has at most 10 such conditional jumps and is reachable
-           // by at most 10 jumps from the title menu.  This chain of
-           // jumps might take too long on some players; this has yet
-           // to be investigated.
-           
-           for (std::size_t menu_incr = (menu_mask + 1) / 2;
-                menu_incr != 0;
-                menu_incr /= 2)
-           {
-               if (menu_num + menu_incr < contents.menus.size()
-                   && (menu_num & (menu_incr * 2 - 1)) == 0)
-               {
-                   file <<
-                       "          if (g0 ge " << 1 + menu_num + menu_incr
-                                              << ")\n"
-                       "            jump menu " << 1 + menu_num + menu_incr
-                                              << ";\n";
-               }
-           }
-
-           file <<
-               // Highlight the appropriate button.
-               "          s8 = g1 &amp; " << button_mask << ";\n"
-               "        </pre>\n"
-               "        <vob file='" << menu.vob_temp->get_name() << "'/>\n";
-
-           for (unsigned button_num = 0;
-                button_num != menu.entries.size();
-                ++button_num)
-           {
-               const dvd_contents::pgc_ref & target =
-                   menu.entries[button_num];
-
-               file << "        <button> ";
-
-               if (target.type == dvd_contents::menu_pgc)
-               {
-                   unsigned target_button_num;
-
-                   if (target.sub_index)
-                   {
-                       target_button_num = target.sub_index;
-                   }
-                   else
-                   {
-                       // Look for a button on the new menu that links
-                       // back to this one.  If there is one, set that to
-                       // be the highlighted button; otherwise, use the
-                       // first button.
-                       const std::vector<dvd_contents::pgc_ref> &
-                           target_menu_entries =
-                           contents.menus[target.index].entries;
-                       dvd_contents::pgc_ref this_pgc(dvd_contents::menu_pgc,
-                                                      menu_num);
-                       target_button_num = target_menu_entries.size();
-                       while (target_button_num != 0
-                              && (target_menu_entries[--target_button_num]
-                                  != this_pgc))
-                           ;
-                       target_button_num += 1;
-                   }
-                        
-                   file << "g1 = "
-                        << (1 + target.index
-                            + target_button_num * button_mult)
-                        << "; jump menu " << 1 + target.index << ";";
-               }
-               else
-               {
-                   assert(target.type == dvd_contents::title_pgc);
-
-                   file << "g1 = "
-                        << 1 + menu_num + (1 + button_num) * button_mult
-                        << "; jump title "
-                        << 1 + target.index;
-                   if (target.sub_index)
-                       file << " chapter " << target.sub_index;
-                   file << ";";
-               }
-
-               file <<  " </button>\n";
-           }
-
-           file << "      </pgc>\n";
-       }
-
-       file <<
-           "    </menus>\n"
-           "  </vmgm>\n";
-
-       // Generate a titleset for each title.  This appears to make
-       // jumping to titles a whole lot simpler (but limits us to 99
-       // titles).
-       for (unsigned title_num = 0;
-            title_num != contents.titles.size();
-            ++title_num)
-       {
-           file <<
-               "  <titleset>\n"
-               // Generate a dummy menu so that the menu button on the
-               // remote control will work.
-               "    <menus>\n"
-               "      <pgc entry='root'>\n"
-               "        <pre> jump vmgm menu; </pre>\n"
-               "      </pgc>\n"
-               "    </menus>\n"
-               "    <titles>\n"
-               "      <pgc>\n"
-               // Record calling location.
-               "        <pre> g12 = g1; </pre>\n"
-                << contents.titles[title_num].vob_list <<
-               // If the menu location has not been changed during
-               // the title, set the location to be the following
-               // button in the menu.  In any case, return to some
-               // menu.
-               "        <post> if (g1 eq g12) g1 = g1 + " << button_mult
-                << "; call menu; </post>\n"
-               "      </pgc>\n"
-               "    </titles>\n"
-               "  </titleset>\n";
-       }
-
-       file <<
-           "</dvdauthor>\n";
-
-       file.close();
-
-       {
-           const char * argv[] = {
-               "dvdauthor",
-               "-o", output_dir.c_str(),
-               "-x", temp.get_name().c_str(),
-               0
-           };
-           int command_result;
-           Glib::spawn_sync(".",
-                            Glib::ArrayHandle<std::string>(
-                                argv, sizeof(argv)/sizeof(argv[0]),
-                                Glib::OWNERSHIP_NONE),
-                            Glib::SPAWN_SEARCH_PATH
-                            | Glib::SPAWN_STDOUT_TO_DEV_NULL,
-                            SigC::Slot0<void>(),
-                            0, 0,
-                            &command_result);
-           if (command_result != 0)
-               throw std::runtime_error("dvdauthor failed");
-       }
-    }
-
     const video::frame_params & lookup_frame_params(const char * str)
     {
        assert(str);