Contact information:

   Paul Rajlich

Welcome to the NCSA Pixel Blaster! NPB, not to be confused with
the "National Pork Board", is an easy to use tiled wall movieplayer
that works with Chromium or WireGL 1.2.1. It is able to optimize
movie playback by distributing and caching textures across the cluster.

To enable texture caching with Chromium make sure that you add
a dist_texture spu before each render spu. This spu is based on
my distributed texture modifications that are part of WireGL 1.2.1.
When compiling NPB, make sure that the USE_CHROMIUM flag is set.

To enable texture caching with WireGL make sure that you are using
WireGL 1.2.1. After installing WireGL 1.2.1, copy the texture.c file
in the wgl directory to the pipe_server directory in the WireGL source
tree and recompile. When running the pipeservers, make sure that
distributed textures are enabled:

% pipeserver -dtex

By enabling texture caching, NPB will be able to cache textures on the
disks across the cluster, allowing fast playback. NPB can still be used
in the "simple mode" using regular OpenGL.

What's new in NPB 1.2
- GUI (optional, implemented with FLTK)
- Large image support (pan and zoom images that exceed the display resolution)
- Can specify tile resolution (code changes contributed by Frank Summers)
- Centering option for letterboxing (e.g. npb -AC ...)
- video-in support from capture card (npb -V /dev/video0)

Getting started
Edit the Makefile. Set the "WALL_COLS" and "WALL_ROWS" flags to
match the dimensions of your wall and "TILE_RES_X" and "TILE_RES_Y"
to match the resolution of your tiles.

Compile the source (type "make").

Displaying a single image
display on client only:
  npb intro.jpg

display on entire wall:

  wgl npb intro.jpg

NOTE: NPB uses the PPM image format. If an image is not a PPM, NPB
  will attempt to convert it on the fly to a PPM using the "convert"
  program. NPB can also handle compressed PPM images of the form
  .ppm.gz or .ppm.Z

Playing movies
Playing movies is very simple. Here are the basic steps:

1) Play your movie in "simple mode" to see if the frames are good.
   Try the example:

     wgl npb example/small/%04d.jpg 1 1701 2

   You can also play the movie locally without WireGL:

     npb example/small/%04d.jpg 1 1701 2

   In this mode, frames are loaded on the client and displayed across
   the wall. However, given that large frames have to be loaded from disk,
   broken up, and sent across the network as textures, this mode typically
   has poor performance and is not scalable.

2) If the movie looks ok and you want to optimize playback, play it
   in "write mode" to distribute it across the cluster:

   First, create the directory /usr/local/frames/example/small across
   the cluster. If you have cexec, that would be:

     cexec -c "mkdir /usr/local/frames/example/small"

   NOTE: the wiregl pipeserver on each cluster node should be running from
   /usr/local/frames. The WireGL texture mods DO NOT allow absolute paths for
   security reasons (WireGL pipeservers are typically run by root).

   NOTE: The Chromium dist_texture spu DOES allow absolute paths (Chromium
   servers are typically not run by root).

   Now you are ready to distribute the movie:

     wgl npb -W example/small/%04d.jpg 1 1701 2

   This mode is similar to the "simple mode" except that as the movie
   is playing, frame "chunks" are being cached to disk across the cluster. 
   In future playbacks (step 4), the movie will already be distributed
   across the cluster, resulting in much better performance than the
   "simple mode".  This solution is also scalable.

   During this mode, the frame "chunks" may be seperated by blank spaces.
   This is normal. In fact, this signals that a further optimization is
   possible and is indeed happening. During final playback (step 3), these
   gaps will disappear.

3) Now play the movie in "distributed mode" at full speed!

     wgl npb -D example/small/%04d.jpg 1 1701 2

   if it plays too fast :-), you can adjust the rate (fps):

     wgl npb -D -r 24 example/small/%04d.jpg 1 1701 2

   NOTE: npb reads the first full frame on the head node in order to
   determine the size of the original frames. Because of this, you need 
   to keep the first frame from each movie. To get around this (and to
   decrease startup time), use the -X option:

     wgl npb -D -X 2048x1536 -r 24 example/small/%04d.jpg 1 1701 2

4) That's it! From now on, your movie is ready to play in distributed
   mode at full speed (step 3)! You may want to make a script for each
   movie so that you don't need to remember the commandline arguments. 

Displaying Very Large Images
To enable panning and zooming on very large images, use the -P option.
For this feature to work properly, each tile will have to cache all of
the image data. To accomplish this, make sure that the Chromium or WireGL
tilesort is in "broadcast" mode when you do the WRITE step. An alternative
is to play the image(s) to a single display node in write mode and then
copy the files from that display node to the other display nodes. Of course,
make sure that you turn off the "broadcast" flag when running in the

NPB can display frames of any resolution. However, if the resolution of
the frames is higher than the wall resolution, you will want to resize
the frames for improved performance.

Aspect Ratio
By default, NPB scales the frame (by texturing) to fit the entire wall.
To preserve the aspect ratio of the frames, use the -A option:

  wgl npb -A intro.jpg

Of course, this may introduce "letterboxing". Another option is to
use a subset of your wall (don't run a wiregl pipesever on each node).

If movies are not being optimized (distributed) properly. Here are
a couple of things to consider:

1) Are your wall dimensions correctly specified? Type "npb" and at the
   bottom of the output it will tell you what the compiled dimensions
   are. You can override this with the -S commandline option or you can
   edit the Makefile and recompile.

2) Do the pipeservers across the cluster have write permission to the
   directory where you are writing (caching) frame chunks? Does the
   directory exist across the cluster?! You may want to run the
   pipeservers as root. 
   NOTE: Each pipeserver caches frame chunks locally based on the
   fileSpec that was specified on the npb commandline on the client.
   For security reasons, only relative paths are allowed.  As a result,
   it is good to run the pipeservers from a specific location
   (ie. /usr/local/frames/).

Advanced Topics
During playback, you can hit the 'w' key to see wireframe mode. This
will show you how frame "chunks" are being distributed. In general,
during the optimized modes (write and distributed) NPB tries to break
up your frames into chunks such that there is one chunk per wall tile
(up to a limit of 1024x1024 sized chunks).

However, you can override this by specifying the frame "chunk" size
explicitly using the -T option. In general, you want the "chunk" size
to be such that no chunk spans across tiles. If that is the case, NPB
can do a further optimization such that each "chunk" will be sent to
only one tile. If this is the case, then during the write mode you will
see that chunks are "shrunken" such that there are gaps between them.
This means that this optimization is happening. During playback, the
chunks will be shown at regular size and there will be no gaps.

During distributed playback, another optimization is the use of a 
so-called "prefetch daemon". This daemon was written by Stuart Levy.
The daemon is a seperate process running on each node that is reading
ahead a frame or two. As a result, frame chunks are cached in the system
disk-read buffer.  Using the prefetch daemon typically does not improve
framerate, but does make the framerate more stable and reduces jitter.

Prefetch daemons may already be running across the cluster. NPB 
communicates with these daemons based on the setting in the NPB_PREFETCH
environment variable.  However, you can override this setting using the
-F commandline option.

For a full list of commandline options and their usage, run npb with no
commandline arguments.

Playlists allow you to play multiple movies without interruption. To make
a playlist, create a text file. Each movie is specified with two lines of
text. The first line is the title and the second line is the npb commandline. 
An example playlist is provided (PLAYLIST-example.txt). 

NOTE: playlist files must have a .txt filename extension!

WireGL modifications
In case you are curious, here is a brief description of the changes
made to WireGL to support NPB:

In order to support distributed textures, I made some modifications to
WireGL. The idea for this came from an email conversation with Ian Buck
of the WireGL team. Essentially, I added two new features to glTexImage2D
to support caching of textures across the cluster. 

The spec for glTexImage2D is: 

void glTexImage2D( GLenum target,
                         GLint level,
                         GLint internalformat,
                         GLsizei width,
                         GLsizei height,
                         GLint border,
                         GLenum format,
                         GLenum type,
                         const GLvoid *pixels )

Normally, type is one of GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, etc. 

I added two more options for type that are normally invalid. The two
options are GL_TRUE and GL_FALSE. 

If the type is set to GL_TRUE then the pixels data is interpreted as a
filename followed by the texture data. On the unpacking side, the filename
is extracted and the texture data is saved to local disk. 

If the type is set to GL_FALSE then the pixels data is interpreted as
a filename only. On the unpacking side, the filename is extracted and
the texture is loaded from local disk. 

With these two simple modifications to WireGL, I was able to write a
distributed movieplayer with good performance. Since the new calls to
glTexImage2D are invalid in regular OpenGL, they do not interfere with
other users of the system. However, I made sure to have modes so that
the movieplayer can also be used with regular WireGL or OpenGL.