Changed WebDvdWindow to generate temporary files in the proper way, spawn external...
authorBen Hutchings <ben@decadent.org.uk>
Sun, 27 Nov 2005 21:16:05 +0000 (21:16 +0000)
committerBen Hutchings <ben@decadent.org.uk>
Sun, 2 Nov 2008 23:19:45 +0000 (23:19 +0000)
Makefile
README
TODO
debian/control
webdvd.cpp

index 92f82ae..be6565e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -25,14 +25,15 @@ endif
 
 cxxsources := \
     auto_proc.cpp browserwidget.cpp childiterator.cpp framebuffer.cpp \
-    linkiterator.cpp pixbufs.cpp stylesheets.cpp webdvd.cpp xpcom_support.cpp
+    linkiterator.cpp pixbufs.cpp stylesheets.cpp temp_file.cpp video.cpp \
+    webdvd.cpp xpcom_support.cpp
 csources := jquant2.c
 
 webdvd : $(cxxsources:.cpp=.o) $(csources:.c=.o)
        $(CXX) $(LDFLAGS) -o $@ $^
 
 clean :
-       rm -f webdvd *.d *.o *~ .\#* *.orig *.rej svn-commit*.tmp *.dvdauthor *.png *.vob *.spumux
+       rm -f webdvd *.d *.o *~ .\#* *.orig *.rej svn-commit*.tmp
 
 distclean : clean
        rm -rf .svn
@@ -48,7 +49,7 @@ browserwidget.% : CPPFLAGS += -DMOZ_LIB_DIR='"$(moz_lib_dir)"'
 
 webdvd.% : CPPFLAGS += -DWEBDVD_LIB_DIR='"$(webdvd_lib_dir)"'
 
-browserwidget.% pixbufs.% webdvd.% \
+browserwidget.% pixbufs.% temp_file.% webdvd.% \
     : CPPFLAGS += $(shell pkg-config --cflags gtkmm-2.0)
 
 browserwidget.% childiterator.o linkiterator.% stylesheets.% webdvd.% \
@@ -69,12 +70,3 @@ linkiterator.% stylesheets.% webdvd.%                                        \
 ifneq ($(MAKECMDGOALS),clean)
     include $(cxxsources:.cpp=.d) $(csources:.c=.d)
 endif
-
-# Temporary rule for testing output files.
-
-%.vob : %_back.png %.spumux %_links.png
-       pngtopnm $*_back.png                                   \
-       | ppmtoy4m -v0 -n 1 -F 25:1 -A 59:54 -I p -S 420_mpeg2 \
-       | mpeg2enc -v0 -f 8 -a 2 -o /dev/stdout                \
-       | mplex -v0 -f 8 -o /dev/stdout /dev/stdin             \
-       | spumux -v0 -m dvd $*.spumux > $@
diff --git a/README b/README
index ec6534a..9e3b82b 100644 (file)
--- a/README
+++ b/README
@@ -5,26 +5,23 @@ WebDVD is intended to provide a simple way of producing DVDs with
 attractive and usable menus.  It converts HTML pages into DVD menus by
 rendering them in Mozilla and reproducing their link structure.  This
 allows you to design DVDs using familiar HTML editing tools or your
-favourite text editor.  This very early version produces a set of
-files suitable for passing to the "dvdauthor" and "spumux" programs,
-but in future it is intended to run the necessary external programs
-automatically.
+favourite text editor.
 
 Requirements
 ------------
 
 WebDVD depends on the following software:
 
+- dvdauthor
 - Gtkmm 2.0
+- mjpegtools
 - Mozilla 1.7.x (later versions may work but are untested)
+- netpbm
 - Xvfb (from XFree86 or X.org)
 
 To build a complete DVD image you will also need:
 
-- dvdauthor
-- mjpegtools
 - mkisofs
-- netpbm
 
 You will also need a program such as ffmpeg or mencoder for producing
 DVD-suitable MPEG-1 or MPEG-2 video files.
@@ -32,47 +29,23 @@ DVD-suitable MPEG-1 or MPEG-2 video files.
 Usage
 -----
 
-Run "webdvd URL" where URL is the URL for the page that is to be the
-top menu of the DVD.  It will automatically follow links to other
-pages and to video files, rendering each page.  You must be careful
-not to link to pages that you do not want to appear on the disc, such
-as normal web sites.
-
-By default, webdvd uses a frame size of 720x576, which is suitable for
-PAL DVDs.  If you wish to produce NTSC DVDs you must override this
-by adding the option "-geometry 720x480".
-
-This will create the following files (with NNNNNN replaced with each
-successive page number):
-
-- webdvd.dvdauthor: This is an XML file to be passed to dvdauthor.
-- page_NNNNNN.spumux: These are XML files to be passed to spumux.
-- page_NNNNNN_back.png: This is a static image of the page, which
-  becomes the menu background.
-- page_NNNNNN_links.png: This is an image of the change in appearance
-  of each link when the pointer is over it.
-
-Currently, you must run commands along the following lines to produce
-a complete DVD image:
-
-for spumux in page_??????.spumux; do
-    page=$(basename $spumux .spumux)
-    pngtopnm ${page}_back.png                              \
-    | ppmtoy4m -v0 -n 1 -F 25:1 -A 59:54 -I p -S 420_mpeg2 \
-    | mpeg2enc -v0 -f 8 -a 2 -o /dev/stdout                \
-    | mplex -v0 -f 8 -o /dev/stdout /dev/stdin             \
-    | spumux -v0 -m dvd $spumux > ${page}.vob
-done
-rm -rf dvd-temp
-dvdauthor -o dvd-temp -x webdvd.dvdauthor
-mkisofs -dvd-video dvd-temp >dvd.iso
-rm -rf dvd-temp
-
-Adjust the name of the temporary directory (here "dvd-temp") and the
-output file ("dvd.iso") as you please.
-
-If you are using NTSC video, you will need to change the ppmtoy4m
-parameters.  Use "-F 30000:1001 -A 10:11" instead of "-F 25:1 -A 59:54".
+Run "webdvd URL DIR" where URL is the URL for the page that is to be
+the top menu of the DVD and DIR is the directory in which to create
+the DVD filesystem (which should be missing or empty).  It will
+automatically follow links to other pages and to video files,
+rendering each page.  You must be careful not to link to pages that
+you do not want to appear on the disc, such as normal web sites.
+
+By default, webdvd generates PAL/SECAM video.  If you wish to produce
+NTSC DVDs you can override this by adding the option "--video-std ntsc".
+
+If webdvd runs successfully you can use mkisofs to create a DVD image:
+
+    mkisofs -dvd-video DIR > IMAGE
+    rm -rf DIR
+
+Alternately you can write the filesystem directly to a writable DVD
+with growisofs or mkisofs plus a suitable version of cdrecord.
 
 Limitations
 -----------
diff --git a/TODO b/TODO
index f57b53d..edce53e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,6 +1,4 @@
-Call spumux.
 Disable scrollbars (not sure how how to do this).
-Call dvdauthor.
 May need to override default menu navigation actions because dvdauthor
 seems to cause rather undesirable behaviour when buttons are not
 arranged in a grid.
index 284ffce..921e18e 100644 (file)
@@ -2,19 +2,16 @@ Source: webdvd
 Maintainer: Ben Hutchings <ben@decadentplace.org.uk>
 Section: graphics
 Priority: extra
-Build-Depends: debhelper (>=4), libgtkmm2.0-dev, mozilla-dev
+Build-Depends: debhelper (>=4), libboost-dev, libgtkmm2.0-dev, mozilla-dev
 Standards-Version: 3.6.2
 
 Package: webdvd
 Architecture: any
-Depends: xvfb, xfonts-base, ${shlibs:Depends}, ${mozilla:Depends}
-Recommends: dvdauthor, mjpegtools, mkisofs, netpbm
+Depends: xvfb, xfonts-base, dvdauthor, mjpegtools, netpbm, ${shlibs:Depends}, ${mozilla:Depends}
+Recommends: mkisofs
 Description: Converts HTML pages into DVD menus.
  WebDVD is intended to provide a simple way of producing DVDs with
  attractive and usable menus.  It converts HTML pages into DVD menus by
  rendering them in Mozilla and reproducing their link structure.  This
  allows you to design DVDs using familiar HTML editing tools or your
- favourite text editor.  This very early version produces a set of
- files suitable for passing to the "dvdauthor" and "spumux" programs,
- but in future it is intended to run the necessary external programs
- automatically.
+ favourite text editor.
index a6d456a..a6064b2 100644 (file)
@@ -9,12 +9,17 @@
 #include <memory>
 #include <queue>
 #include <set>
+#include <sstream>
 #include <string>
 
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <boost/shared_ptr.hpp>
+
 #include <gdkmm/pixbuf.h>
+#include <glibmm/convert.h>
+#include <glibmm/spawn.h>
 #include <gtkmm/main.h>
 #include <gtkmm/window.h>
 
@@ -48,6 +53,7 @@
 #include "linkiterator.hpp"
 #include "pixbufs.hpp"
 #include "stylesheets.hpp"
+#include "temp_file.hpp"
 #include "video.hpp"
 #include "xpcom_support.hpp"
 
@@ -135,7 +141,8 @@ namespace
     public:
        WebDvdWindow(
            const video::frame_params & frame_params,
-           const std::string & main_page_uri);
+           const std::string & main_page_uri,
+           const std::string & output_dir);
 
     private:
        void add_page(const std::string & uri);
@@ -146,11 +153,12 @@ namespace
        void process_links(nsIPresShell * pres_shell,
                           nsIPresContext * pres_context,
                           nsIDOMWindow * dom_window);
-       void generate_dvdauthor_file();
+       void generate_dvd();
 
        enum ResourceType { page_resource, video_resource };
        typedef std::pair<ResourceType, int> ResourceEntry;
        video::frame_params frame_params_;
+       std::string output_dir_;
        BrowserWidget browser_widget_;
        nsCOMPtr<nsIStyleSheet> stylesheet_;
        std::queue<std::string> page_queue_;
@@ -159,14 +167,18 @@ namespace
        std::vector<std::string> video_paths_;
        bool loading_;
        int pending_req_count_;
+       std::auto_ptr<temp_file> background_temp_;
        struct link_state;
        std::auto_ptr<link_state> link_state_;
+       std::vector<boost::shared_ptr<temp_file> > page_temp_files_;
     };
 
     WebDvdWindow::WebDvdWindow(
        const video::frame_params & frame_params,
-       const std::string & main_page_uri)
+       const std::string & main_page_uri,
+       const std::string & output_dir)
            : frame_params_(frame_params),
+             output_dir_(output_dir),
              stylesheet_(load_css("file://" WEBDVD_LIB_DIR "/webdvd.css")),
              loading_(false),
              pending_req_count_(0)
@@ -282,7 +294,7 @@ namespace
                    page_queue_.pop();
                    if (page_queue_.empty())
                    {
-                       generate_dvdauthor_file();
+                       generate_dvd();
                        Gtk::Main::quit();
                    }
                    else
@@ -303,25 +315,29 @@ namespace
 
     void WebDvdWindow::save_screenshot()
     {
-       char filename[25];
-       std::sprintf(filename, "page_%06d_back.png", page_links_.size());
        Glib::RefPtr<Gdk::Window> window(get_window());
        assert(window);
        window->process_updates(true);
-       std::cout << "saving " << filename << std::endl;
+
+       background_temp_.reset(new temp_file("webdvd-back-"));
+       background_temp_->close();
+       std::cout << "saving " << background_temp_->get_name() << std::endl;
        Gdk::Pixbuf::create(Glib::RefPtr<Gdk::Drawable>(window),
                            window->get_colormap(),
                            0, 0, 0, 0,
                            frame_params_.width, frame_params_.height)
-           ->save(filename, "png");
+           ->save(background_temp_->get_name(), "png");
     }
 
     struct WebDvdWindow::link_state
     {
        Glib::RefPtr<Gdk::Pixbuf> diff_pixbuf;
 
+       std::auto_ptr<temp_file> spumux_temp;
        std::ofstream spumux_file;
 
+       std::auto_ptr<temp_file> links_temp;
+
        int link_num;
        LinkIterator links_it, links_end;
 
@@ -363,20 +379,21 @@ namespace
                true, 8, // has_alpha, bits_per_sample
                frame_params_.width, frame_params_.height);
 
-           char spumux_filename[20];
-           std::sprintf(spumux_filename,
-                        "page_%06d.spumux", page_links_.size());
-           state->spumux_file.open(spumux_filename);
+           state->spumux_temp.reset(new temp_file("webdvd-spumux-"));
+           state->spumux_temp->close();
+
+           state->links_temp.reset(new temp_file("webdvd-links-"));
+           state->links_temp->close();
+           
+           state->spumux_file.open(state->spumux_temp->get_name().c_str());
            state->spumux_file <<
                "<subpictures>\n"
                "  <stream>\n"
                "    <spu force='yes' start='00:00:00.00'\n"
-               "        highlight='page_" << std::setfill('0')
-                              << std::setw(6) << page_links_.size()
-                              << "_links.png'\n"
-               "        select='page_" << std::setfill('0')
-                              << std::setw(6) << page_links_.size()
-                              << "_links.png'>\n";
+               "        highlight='" << state->links_temp->get_name()
+                              << "'\n"
+               "        select='" << state->links_temp->get_name()
+                              << "'>\n";
 
            state->link_num = 0;
            state->links_it = LinkIterator(basic_doc);
@@ -547,23 +564,65 @@ namespace
 
        quantise_rgba_pixbuf(state->diff_pixbuf, dvd::button_n_colours);
 
-       char filename[25];
-       std::sprintf(filename, "page_%06d_links.png", page_links_.size());
-       std::cout << "saving " << filename << std::endl;
-       state->diff_pixbuf->save(filename, "png");
+       std::cout << "saving " << state->links_temp->get_name()
+                 << std::endl;
+       state->diff_pixbuf->save(state->links_temp->get_name(), "png");
 
        state->spumux_file <<
            "    </spu>\n"
            "  </stream>\n"
            "</subpictures>\n";
+
+       state->spumux_file.close();
+
+       // TODO: if (!state->spumux_file) throw ...
+
+       {
+           boost::shared_ptr<temp_file> vob_temp(
+               new temp_file("webdvd-vob-"));
+           std::ostringstream command_stream;
+           command_stream << "pngtopnm "
+                          << background_temp_->get_name()
+                          << " | ppmtoy4m -v0 -n1 -F"
+                          << frame_params_.rate_numer
+                          << ":" << frame_params_.rate_denom
+                          << " -A" << frame_params_.pixel_ratio_width
+                          << ":" << frame_params_.pixel_ratio_height
+                          << (" -Ip -S420_mpeg2"
+                              " | mpeg2enc -v0 -f8 -a2 -o/dev/stdout"
+                              " | mplex -v0 -f8 -o/dev/stdout /dev/stdin"
+                              " | spumux -v0 -mdvd ")
+                          << state->spumux_temp->get_name()
+                          << " > " << vob_temp->get_name();
+           std::string command(command_stream.str());
+           const char * argv[] = {
+               "/bin/sh", "-c", command.c_str(), 0
+           };
+           std::cout << "running " << argv[2] << 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::Slot0<void>(),
+                            0, 0,
+                            &command_result);
+           if (command_result != 0)
+               throw std::runtime_error("spumux pipeline failed");
+
+           page_temp_files_.push_back(vob_temp);
+       }
     }
 
     void generate_page_dispatch(std::ostream &, int indent,
                                int first_page, int last_page);
 
-    void WebDvdWindow::generate_dvdauthor_file()
+    void WebDvdWindow::generate_dvd()
     {
-       std::ofstream file("webdvd.dvdauthor");
+       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:
        //
@@ -657,9 +716,8 @@ namespace
                // appropriate link/button.
                "          g0 = 0; s8 = g1 &amp; " << link_mask << ";\n"
                "        </pre>\n"
-               "        <vob file='page_"
-                << std::setfill('0') << std::setw(6) << page_num
-                << ".vob'/>\n";
+               "        <vob file='"
+                << page_temp_files_[page_num - 1]->get_name() << "'/>\n";
 
            for (std::size_t link_num = 1;
                 link_num <= page_links.size();
@@ -725,6 +783,29 @@ namespace
 
        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");
+       }
     }
 
     void generate_page_dispatch(std::ostream & file, int indent,
@@ -819,7 +900,7 @@ int main(int argc, char ** argv)
 
        // Initialise Gtk
        Gtk::Main kit(argc, argv);
-           
+
        // Check we have the right number of arguments.  We can't
        // do this earlier because we need to let Gtk read and remove
        // any of the many options it understands.
@@ -843,18 +924,18 @@ int main(int argc, char ** argv)
            else
                break;
        }
-       if (argi != argc - 1)
+       if (argi != argc - 2)
        {
            std::cerr << "Usage: " << argv[0]
                      << (" [gtk-options] [--video-std std-name]"
-                         "front-page-url\n");
+                         "front-page-url output-dir\n");
            return EXIT_FAILURE;
        }
 
        // Initialise Mozilla
        BrowserWidget::init();
 
-       WebDvdWindow window(frame_params, argv[argi]);
+       WebDvdWindow window(frame_params, argv[argi], argv[argi + 1]);
        Gtk::Main::run(window);
     }
     catch (std::exception & e)