Tag: Work

Hard subs with mencoder, or, annotating screencasts for free.

December 2, 2009 » Geek

I was working on a screen cast for a project and I could not get anything to work the way I wanted to. I didn’t have the desire to purchase software, so I sought a way to annotate it for free. I tried a half dozen free and open source video editors, with no real luck. Either they choked on the format I captured (from CamStudio) or they didn’t have a readily available inline text tool.

What I ended up using was plain old subtitles, plus the handyman’s secret weapon, mplayer/mencoder.

Here’s what I did. I fired up the video in VLC and found the points where I wanted to put my subtitle help text. Subtitle files are usually pretty straight forward. I chose the .srt format, which is plain text. You can edit by hand, or you can use a tool, I used the aptly named subtitleeditor. Which, by the way, barfed on my video file.

Here’s a snippet of the resulting .srt file:

1
00:00:00,000 --> 00:00:13,000
Welcome to the product gallery walk through.

2
00:00:13,000 --> 00:00:23,000
Adding a product: Select image.

3
00:00:23,000 --> 00:00:32,000
Insert title and description.

Next I needed to turn those “soft” subs into “hard” ones. This is where mplayer/mencoder come in. To add soft subs to a video in mplayer, you use the -sub option. Running this in mplayer first is a good way to see how it will look at tweak it according to the many options you have. I went with the defaults, it looks pretty good that way.

Lastly, I needed a good mencoder recipe to pull it all together. After lots of searching I found a great MPEG4 one here.

It’s a two pass system, and the crucial piece is in calculating the bitrate:

bitrate = 50 * 25 * width_of_video * height_of_video / 256

My bitrate was 2540600. After you get that, you just plug it into the two passes below. This includes my subtitle options.

mencoder -o /dev/null -ovc lavc -lavcopts vcodec=msmpeg4v2:vpass=1:vbitrate=2540600:mbd=2:keyint=132:vqblur=1.0:cmp=2:subcmp=2:dia=2:mv0:last_pred=3 -nosound -sub MySubTitles.srt -subfont-text-scale 3 RawScreenCast.avi
mencoder -o FinishedScreenCast.avi -ovc lavc -lavcopts vcodec=msmpeg4v2:vpass=2:vbitrate=2540600:mbd=2:keyint=132:vqblur=1.0:cmp=2:subcmp=2:dia=2:mv0:last_pred=3 -nosound -sub MySubTitles.srt -subfont-text-scale 3 RawScreenCast.avi

It takes a while but not too long. When all was said and done I had a perfect hard-subbed version and it shrank my file size from ~550MB to 7.6MB. That is a great encoding recipe.

Average video still.
An average frame, with hard subs.

Fun Holiday Message

December 15, 2008 » Geek

I made a cool holiday message today for my new job at REBEL INTERACTIVE, it’s a fridge with magnets that you can use to make and send messages. I think it’s rad.

Check it out: http://rebel-interactive.com/fridge/

Tags: , ,

Gtkmm/Glibmm Thread Example

September 30, 2008 » Geek

Heads Up!

This post uses a deprecated API. You can learn more here: https://developer.gnome.org/glibmm/stable/group__Threads.html

After dealing with threads in Qt and seeing how nice and simple it was, I decided to try again with the Gtkmm/Glibmm threading system. I think the crucial piece for me with the Qt threads was the excellent documentation, so I’m going to show you my full example here and document it in detail. As a quick disclaimer, I do not know if this is the 100% “right” way to do threads, but it matches up with everything I’ve read and, more importantly, it works.

Where to start…
I’m going to start with a brief description of what is going to happen. We will develop a threaded worker class, a trivial Gtk::Window class and a trivial main function. Let’s get the first two out of the way early, since they are the easy ones.

int main ()

int main (int argc , char ** argv) {

  if(!Glib::thread_supported()) Glib::thread_init();

  Gtk::Main kit (argc, argv);
  MyWindow win;
  Gtk::Main::run (win);
  return 0;
}

This is a pretty standard main function, with one notable nod to Glibmm, and that is line 84.

if(!Glib::thread_supported()) Glib::thread_init();

You only ever call Glib::thread_init(); once, otherwise it will abort with an error. So what we are doing is asking Glib “Are you already handling threads?” and if it isn’t we initialize the threading system. This has the additional perk of aborting the program if threads are not supported at all.

If the rest of this function doesn’t make sense to you, walk away from this tutorial and go read the Gtkmm one.

MyWindow
Next we are going to implement that Gtk::Window class that we use in main, ever so imaginatively called MyWindow. There is nothing much special going on, so I’ll show you the full listing then pick out the important bits.

class MyWindow : public Gtk::Window {

  public:
    MyWindow () : thrashHive("Go!") {
      bee = NULL;
      thrashHive.signal_clicked().connect(sigc::mem_fun(*this, &MyWindow::go));
      add(thrashHive);
      show_all();
    }

    void go () {
      if(bee != NULL)
        return;

      bee = new Worker();
      bee->sig_done.connect(sigc::mem_fun(*this, &MyWindow::beeDone));
      bee->start();

      thrashHive.set_sensitive(false);
      thrashHive.set_label("Working");
    }

    void beeDone () {
      delete bee;
      bee = NULL;
      thrashHive.set_sensitive(true);
      thrashHive.set_label("Go!");
    }

  private:
    Gtk::Button thrashHive;
    Worker * bee;

};

As you can tell, this is a simple Gtk::Window with just a single button (thrashHive) in it. In the constructor we connect this button to a method go() at line 52. go() is important to us because it is where we create and launch our thread, which is represented as the class Worker of which we have a pointer declared at line 78 called bee. Let’s run through go() line by line.

      if(bee != NULL)
        return;

In lines 58 and 59 we are protecting our memory from trying to create multiple instances of a Worker thread, a good idea to stay safe in an asynchronous environment.

      bee = new Worker();
      bee->sig_done.connect(sigc::mem_fun(*this, &MyWindow::beeDone));
      bee->start();

At line 61 we instantiate a new Worker class. Next at line 62 we connect the signal sig_done to our beeDone() method, which essentially just undoes everything we do in go(). We do this because this is a non-blocking, asynchronous working thread and we need to know when it is finished running. When we get to the implementation of the Worker class, you’ll see that this “signal” is not a given, but rather something we implement ourselves, and we can have as many “signals” for events as needed. Lastly we start our thread at line 63.

      thrashHive.set_sensitive(false);
      thrashHive.set_label("Working");

Here we disable our button and change it’s text. Why do this here and not before thread creation? In most cases that would be a good idea, but here we want to demonstrate that the main thread is still working, so we do updates to the GUI after we start the worker thread.

Worker
The Worker class is actually fairly simple. It has a basic set of steps:

  1. Create and wait to be started.
  2. Get started, create thread and run.
  3. Do work, emit signals, check for being aborted.
  4. Return when done, then wait around until cleaned up.

Just breeze over the full listing then we’ll go over each of those points one at a time.

class Worker {
  public:
 
    Worker() : thread(0), stop(false) {}
 
    // Called to start the processing on the thread
    void start () {
      thread = Glib::Thread::create(sigc::mem_fun(*this, &Worker::run), true);
    }
 
    // When shutting down, we need to stop the thread
    ~Worker() {
      {
        Glib::Mutex::Lock lock (mutex);
        stop = true;
      }
      if (thread)
        thread->join(); // Here we block to truly wait for the thread to complete
    }
 
    Glib::Dispatcher sig_done;
 
  protected:
    // This is where the real work happens
    void run () {
 
      while(true) {
        {
          Glib::Mutex::Lock lock (mutex);
          if (stop) break;
        }
        sleep(5);
        std::cout << "Thread write!" << std::endl;
        sig_done();
        break;
      }
    }
 
    Glib::Thread * thread;
    Glib::Mutex mutex;
    bool stop;
};

We don't have anything to deal with for the first point. In the constructor we just initialize the Glib::Thread to 0 and our sentinel boolean, stop to false.

For the second point, we need to look at start() and run().

start() is our public method, and it creates and runs the actual Glib::Thread. Doing that is fairly straight forward:

    void start () {
      thread = Glib::Thread::create(sigc::mem_fun(*this, &Worker::run), true);
    }

The run() method is where our blocking operations go so that we don't have to worry about gumming up the UI, this is where the work actually happens (i.e. step 3).

    void run () {
 
      while(true) {
        {
          Glib::Mutex::Lock lock (mutex);
          if (stop) break;
        }
        sleep(5);
        std::cout << "Thread write!" << std::endl;
        sig_done();
        break;
      }
    }

In my run() I just wait five seconds and write to stdout, but if you are doing a lengthy process, you should occasionally check the stop variable for an abort condition by using lines 30 - 33. Always put your Glib::Mutex::Lock inside of their own block, it is easier to handle them that way as the destructor does the cleanup work for you.

On line 37 we see a "signal" being emitted. This is actually a Glib::Dispatcher, which uses pipes for asynchronous communication between threads. In practical application just treat it like a sigc::signal . If you need to communicate data, emit the Glib::Dispatcher and have the receiving thread do method calls to get the information from stored values in the sending thread.

One easy to miss caveat when using gthreads, you have to throw in some extra libraries with pkg-config --libs gthread-2.0, here is the command line to compile this demo application.

g++ -Wall -o bee gthread.cpp `pkg-config gtkmm-2.4 --cflags --libs` `pkg-config --libs gthread-2.0`

That is the guts of a simple Gtkmm/Glibmm worker thread, I hope that I was clear if not concise. Here is the complete listing of the program for your reading/borrowing/compiling pleasure.

#include 
#include 

class Worker {
  public:

    Worker() : thread(0), stop(false) {}

    // Called to start the processing on the thread
    void start () {
      thread = Glib::Thread::create(sigc::mem_fun(*this, &Worker::run), true);
    }

    // When shutting down, we need to stop the thread
    ~Worker() {
      {
        Glib::Mutex::Lock lock (mutex);
        stop = true;
      }
      if (thread)
        thread->join(); // Here we block to truly wait for the thread to complete
    }

    Glib::Dispatcher sig_done;

  protected:
    // This is where the real work happens
    void run () {

      while(true) {
        {
          Glib::Mutex::Lock lock (mutex);
          if (stop) break;
        }
        sleep(5);
        std::cout << "Thread write!" << std::endl;
        sig_done();
        break;
      }
    }

    Glib::Thread * thread;
    Glib::Mutex mutex;
    bool stop;
};

class MyWindow : public Gtk::Window {

  public:
    MyWindow () : thrashHive("Go!") {
      bee = NULL;
      thrashHive.signal_clicked().connect(sigc::mem_fun(*this, &MyWindow::go));
      add(thrashHive);
      show_all();
    }

    void go () {
      if(bee != NULL)
        return;

      bee = new Worker();
      bee->sig_done.connect(sigc::mem_fun(*this, &MyWindow::beeDone));
      bee->start();

      thrashHive.set_sensitive(false);
      thrashHive.set_label("Working");
    }

    void beeDone () {
      delete bee;
      bee = NULL;
      thrashHive.set_sensitive(true);
      thrashHive.set_label("Go!");
    }

  private:
    Gtk::Button thrashHive;
    Worker * bee;

};

int main (int argc , char ** argv) {

  if(!Glib::thread_supported()) Glib::thread_init();

  Gtk::Main kit (argc, argv);
  MyWindow win;
  Gtk::Main::run (win);
  return 0;
}

Tags: , , , ,

Command Line Package Installation in OpenSuSE

October 1, 2007 » Geek

One of the things I love the most about Debian is apt. It’s a great and speedy package manager. Being able to apt-get install from the command line and not have to wait for a heavy UI to come up is a major plus for me. Thats why I was frustrated with OpenSuSE, which we use at work.

First thing first, I think that the OpenSuSE package system is a pig. Yast is a pig. Zypper is a pig. I did, however, find the fastest route to installing via the command line, which I’ll share here.

Unless you happen to know the exact name of the package you want to install, you’ll need to look it up. The fastest way I’ve found is using Webpin, a nice online package searcher. Be careful that you are reading from the right repo though. For example, a search on “magick++” returns many packages, including “libMagick++-devel (6.3.5.10)” which, to my Debian eye, looks like the perfect package. It is from an odd repo though, “Results from http://download.opensuse.org/repositories/home:/dipe/openSUSE_10.2” and the one I really want, and have access to mind you, is “ImageMagick-Magick++-devel (6.3.0.0)” which is in the main repo “Results from http://download.opensuse.org/distribution/10.2/repo/oss/suse”. It’s already too complicated, but we soldier on.

With my new, exact, package name in hand, I open up a root command line. The syntax for a zypper install is zypper install [package name] so I do zypper install ImageMagick-Magick++-devel and let it rip. After a ridiculous amount of parsing, it figures out the dependencies and asks me to continue, which I do. You have to babysit it for key managing, I havent figured out how to force “yes” on it. And that is how you use zypper to install on OpenSuSE.

root:~$ zypper install ImageMagick-Magick++-devel

Restoring system sources...

Parsing metadata for 20070918-142944...

Parsing metadata for 20070927-100843...

Parsing metadata for 20070927-100709...

Parsing metadata for 20070918-055437...

Parsing metadata for SUSE-Linux-10.2-Updates...

Parsing RPM database...

Summary:

   [S3:0][package]libxml2-devel-2.6.26-27.pm.1.i586

   [S4:1][package]liblcms-devel-1.15-30.i586

   [S4:1][package]libwmf-gnome-0.2.8.4-24.i586

   [S4:1][package]libwmf-devel-0.2.8.4-24.i586

   [S4:1][package]readline-devel-5.1-55.i586

   [S5:0][package]ImageMagick-Magick++-devel-6.3.0.0-27.6.i586

   [S5:0][package]ImageMagick-devel-6.3.0.0-27.6.i586

Continue? [y/n] y

Downloading: [S4:1][package]liblcms-devel-1.15-30.i586, 141.5 K(490.1 K unpacked)

Installing: [S4:1][package]liblcms-devel-1.15-30.i586

Downloading: [S4:1][package]libwmf-gnome-0.2.8.4-24.i586, 7.7 K(9.6 K unpacked)

Installing: [S4:1][package]libwmf-gnome-0.2.8.4-24.i586

Downloading: [S4:1][package]readline-devel-5.1-55.i586, 137.2 K(376.7 K unpacked)

Installing: [S4:1][package]readline-devel-5.1-55.i586

Downloading: [S4:1][package]libwmf-devel-0.2.8.4-24.i586, 414.1 K(2.7 M unpacked)

Installing: [S4:1][package]libwmf-devel-0.2.8.4-24.i586

Downloading: [S3:0][package]libxml2-devel-2.6.26-27.pm.1.i586, 2.5 M(11.6 M unpacked)

Installing: [S3:0][package]libxml2-devel-2.6.26-27.pm.1.i586

Downloading: [S5:0][package]ImageMagick-devel-6.3.0.0-27.6.i586, 1.5 M(5.6 M unpacked)

Installing: [S5:0][package]ImageMagick-devel-6.3.0.0-27.6.i586

Downloading: [S5:0][package]ImageMagick-Magick++-devel-6.3.0.0-27.6.i586, 193.2 K(939.8 K unpacked)

Installing: [S5:0][package]ImageMagick-Magick++-devel-6.3.0.0-27.6.i586

root:~$ 

GTK Tooltips On Notebook Tab Labels

August 22, 2007 » Geek

Here’s a non-obvious (or to me at least) trick to get tooltips onto a Gtk::Notebook tab. It took some searching, but essentially, you just add the Gtk::Label to a Gtk::EventBox and add that to the tab instead. Then you attach the tool tip to the Gtk::EventBox instead of the Gtk::Label.

Here’s an example. I couldn’t get my example to compile, the linker was going crazy, but I’m 99% sure that it’s fine. I’m probably just not seeing one glaring error. Let me know if you find it. The important stuff is all there, even if it won’t build.

main.cpp

#include 
#include "nbtt.h"

using namespace std;

int main (int argc, char *argv[]) {

  Gtk::Main kit (argc, argv);
  Nbtt notebookWindow;
  Gtk::Main::run(notebookWindow);
	
  return 0;
}

nbtt.cpp

#include 
#include "nbtt.h"

using namespace std;

Nbtt::Nbtt() {

  set_title("Notebook Tabs With Labels!");
  set_border_width(10);
  set_default_size(400, 200);

  lblTabOne.set_text("Tab 1");
  lblTabTwo.set_text("Tab 2");
  lblTabThree.set_text("Tab 3");

  ebTabOne.add(lblTabOne);
  ebTabTwo.add(lblTabTwo);
  ebTabThree.add(lblTabThree);
	
  toolTips.set_tip(ebTabOne,"Tab to page one.");
  toolTips.set_tip(ebTabTwo,"Tab to page two.");
  toolTips.set_tip(ebTabThree,"Tab to page three.");

  exNotebook.append_page(pageOne, ebTabOne);
  exNotebook.append_page(pageTwo, "Second");

  show_all();
}

nbtt.h

#ifndef NBTT_H
#define NBTT_H

#include 
#include 
#include 
#include 
#include 

using namespace std;

class Nbtt;

class Nbtt : public Gtk::Window {

  public:
    Nbtt();
	
  private:
    Gtk::Notebook exNotebook;
		
    Gtk::EventBox ebTabOne;
    Gtk::EventBox ebTabTwo;
    Gtk::EventBox ebTabThree;

    Gtk::Label lblTabOne;
    Gtk::Label lblTabTwo;
    Gtk::Label lblTabThree;

    Gtk::Tooltips toolTips;

    Gtk::Label pageOne;
    Gtk::Label pageTwo;
    Gtk::Label pageThree;
	
};

#endif // NBTT_H

I found this trick in the [email protected] history. The thread starts here if you want to read the exchange.

Tags: , , ,