]> git.decadent.org.uk Git - videolink.git/blobdiff - webdvd.cpp
Added --help option and arranged to print usage information when this is used and...
[videolink.git] / webdvd.cpp
index a6d456a8f0de6ed9ee20dba636c7b0d14d9c1a4b..94ab121f890c3f043cca63277ecc7928e76933e1 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,
@@ -786,23 +867,40 @@ namespace
            std::string("Invalid video standard: ").append(str));
     }
 
+    void print_usage(std::ostream & stream, const char * command_name)
+    {
+       stream << "Usage: " << command_name
+              << (" [gtk-options] [--video-std std-name]"
+                  " front-page-url output-dir\n");
+    }
+    
 } // namespace
 
 int main(int argc, char ** argv)
 {
     try
     {
-       // Determine video frame parameters.
+       // Do initial argument parsing.  We have to do this before
+       // letting Gtk parse the arguments since we need to spawn Xvfb
+       // first and that's currently dependent on the frame
+       // parameters (though we could just make it large enough for
+       // any frame) and also an unnecessary expense in some cases.
        video::frame_params frame_params = video::pal_params;
        for (int argi = 1; argi != argc; ++argi)
        {
            if (std::strcmp(argv[argi], "--") == 0)
                break;
+           if (std::strcmp(argv[argi], "--help") == 0)
+           {
+               print_usage(std::cout, argv[0]);
+               return EXIT_SUCCESS;
+           }
            if (std::strcmp(argv[argi], "--video-std") == 0)
            {
                if (argi + 1 == argc)
                {
                    std::cerr << "Missing argument to --video-std\n";
+                   print_usage(std::cerr, argv[0]);
                    return EXIT_FAILURE;
                }
                frame_params = lookup_frame_params(argv[argi + 1]);
@@ -819,10 +917,8 @@ 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.
+
+       // Complete argument parsing with Gtk's options out of the way.
        int argi = 1;
        while (argi != argc)
        {
@@ -838,23 +934,22 @@ int main(int argc, char ** argv)
            else if (argv[argi][0] == '-')
            {
                std::cerr << "Invalid option: " << argv[argi] << "\n";
+               print_usage(std::cerr, argv[0]);
                return EXIT_FAILURE;
            }
            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");
+           print_usage(std::cerr, argv[0]);
            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)