December 28, 2009

EtherPad and co-ment

I was sitting in a train for a few hours, with the source code of EtherPad (Apache License 2.0) and co-ment (GNU Affero GPL 3) on my MacBook. I had just done the initial checkout before the trip - I was very excited to look how these two open source code editors work. EtherPad was just being acquired by Google, apparantly to get more professional people to work on the Wave. Real-time collaborative editing seems to be the new paradigm for web user interfaces.

Both projects, naturally, use JavaScript for transacting with the DOM. EtherPad handles page updates with Comet techniques. Neither come with verbose description of their inner workings, but the EtherPad authors have written a nice post (in the source code repository) that reveals the essential magic ingredient:

"The crazy idea here, which seems to have originated with Dutch programmer Marijn Haverbeke, is to take advantage of a browser feature called "design mode" (or "content editable"), a mode which allows the user to directly edit an HTML document. This feature has quietly been added to all major browsers over time. In fact, it's what GMail uses to let users compose rich-text e-mail. The advantages of basing an editor on a design-mode buffer are that such a buffer has a full DOM (document model), which allows arbitrary styling and swapping of parts of the document, and that native editing operations (selection, copy/paste) are mapped by the browser onto operations on the DOM.
...
The key is to treat the DOM as a hugely complicated I/O device between you and the browser, and carefully make rules to constrain it. The plus side is that once you've systematically beat design mode into submission, you can have an unmatched degree of scalability and nativity."
The server-side is handled by Scala, Java and JavaScript. Huh?
"This enabled us to be more productive by writing all of EtherPad in the same language, and shuttle data between the client, server, and database all using JavaScript objects."
The data persistance model is not very conventional either:
"EtherPad stores all its data in the AppJet Database, which automatically scales and caches itself in memory as necessary. This makes it fast to implement EtherPad features, fast to change storage models, and fast to serve requests in production."
The code looks very professional and clean, albeit difficult to grok to develop new features. As an interesting note, the included documents describe the changeset to look something like:
Z:5g>1|5=2p=v*4*5+1$x
"This changeset, together with the pool, represents inserting a bold letter "x" into the middle of a line."
This is achieved by JavaScript:
function handleUserChanges() {
  ...
   var userChangesData = editor.prepareUserChangeset();
   if (userChangesData.changeset) {
     lastCommitTime = t;
     state = "COMMITTING";
     stateMessage = {type:"USER_CHANGES", baseRev:rev,
                     changeset:userChangesData.changeset,
                     apool: userChangesData.apool };
     stateMessageSocketId = socketId;
     sendMessage(stateMessage);
     sentMessage = true;
     callbacks.onInternalAction("commitPerformed");
   }
 ...
 }
...and the changeset can be traced back to function "compose()":
Changeset.compose = function(cs1, cs2, pool) {
   var unpacked1 = Changeset.unpack(cs1);
   var unpacked2 = Changeset.unpack(cs2);
   ...
   var bankAssem = Changeset.stringAssembler();

   var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function(op1, op2, opOut) {

     var op1code = op1.opcode;
     var op2code = op2.opcode;
     if (op1code == '+' && op2code == '-') {
       bankIter1.skip(Math.min(op1.chars, op2.chars));
     }
     Changeset._slicerZipperFunc(op1, op2, opOut, pool);
     if (opOut.opcode == '+') {
       if (op2code == '+') {
           bankAssem.append(bankIter2.take(opOut.chars));
       }
       else {
           bankAssem.append(bankIter1.take(opOut.chars));
       }
     }

   });

   return Changeset.pack(len1, len3, newOps, bankAssem.toString());
 };

So overall, EtherPad looks very promising, but difficult to tailor for specific needs.

Co-ment, in the other hand, is a simpler solution. "co-ment® makes it possible for you to write or upload your own texts, submit them for comments and process the comments."

It also uses JavaScript on client-side and Python with Django on the server. The magic is not as advanced; it uses a DOM manipulation API (Beautiful Soup), and happily ignores standard design patterns by mixing views and request/response mechanism control structures with inline SQL. In other words: it is written with more traditional technology, but in a way that makes it difficult to maintain.

The source codes of either project are definitely beneficial to read and learn from. Unfortunately the lack of architectural documentation makes this a tedious task.

UPDATE: LWN comments on EtherPad

November 17, 2009

Recent advances in WWW technologies and the new generation of user interfaces

Many new technologies for the web have emerged during the year 2009. Google Wave, HTML5 and the increasing importance of JavaScript are the driving on the internet, but the mobile device is a growing market where Qt and Android look to provide the best SDKs for iPhone competition. JavaScript engines have been making significant improvements in performance, and V8 makes the Chrome browser a playground of innovation.

I have been silently learning Python with PyQt4 that along with QtDesigner make a good combination of tools for cross-platform GUI development. PyQt4 on the other hand did not agree on new licensing with Nokia, and now there is a new Python Qt4 library emerging: PySide. PySide is an important effort and Python -the language- is looking very promising for a number of reasons.

This exciting iframe below presents Your World of Text, a co-editor which is a prime example of what is possible with modern technology, but this is just a sneak preview of the possibilities that are opening up for developers. Not just on the web but also on the desktop. This demo is produced by Andrew Badr using jQuery, a few lines of custom JavaScript and Django, the Python framework.

In 10 years we may have an Ajax X-server.

The Google Wave videos (check also the playlist) are also inspiring. Google Wave provides a same kind of technology as the demo above, but the codebase is not the same.

The "Wave" that Google presented is under the hood essentially an XML document. The Google Wave Federation Protocol defines the operational transformation layer for sharing the document and mitigating live updates between all wave providers. This works not unlike Git, and all geeks agree how fabulous Git is. As an analogue, Wave provides to non-techie fellow humans (communicate, share) what Git does for code hackers (code, share). Just in a very cool way that code hackers too can appreciate. The protocol is open to contributions by the broader community with the goal to continue to improve how humans share information, together. Even across language barriers!

So in the very near future, we can have live co-editing in multimedia documents with indesign publication / forking / remixing mechanism. The environment, then .. will it be only the web?

The trio: HTML5, CSS3 and JavaScript, has matured and provides a solid base for new generation of dynamic user interfaces. The Amiga Workbench Emulator is a showcase of JavaScript UI and Qt Labs demonstrates the power of HTML5+CSS3 that only works on WebKit browsers (Safari, Konqueror, Chrome). There is no particular Javascript code that switches the items, everything is done by CSS.

Any defences for the plain old desktop apps? Other than terminals, text/video/etc editors, games, mission-critical tasks, CPU/GPU-intensive 3D apps, ... Oh yes. I for one find it somehow silly to overload a web browser with all of this. One of the reasons why web apps got widly popular at some stage is that they are not locked into one physical computer and that the user can change from operating system to another. Reality hits back in the form of browser inconsistencies and especially by the well-known fact that the single most used browser is completely incapacitated by todays standards (IE6 - 23,4% of all browsers in October 2009).

When I started using QtDesigner and Python I was fairly delighted how well they work together. Nice IDE to design the GUI and the UI widgets can be imported and instantiated in my Python code. This is a win for the Qt app. Qt implements the concept of signals and slots, which provide a way to connect asynchronous events (signal) to call a method (slot). For a web app, this would be possible with JavaScript XHR, but there is no "API", since this concept is a bit foreign in the web world. This is a win for the Qt app. The layout of a QFrame is very rigid, unlike HTML+CSS which can be extremely fluid. This is a win for the web app. It also is quite sensible to use accessor objects for inserting data onto a table, but I have to say it feels a bit silly to write 10-20 lines of boilerblate code instead of just iterating over an array and setting a few CSS classes to ones liking? This is a win for the web app. Σ: Qt web = 2. Both have unique strengths.

Indeed, the web hosts many exotic UIs without overall consistency, whereas the desktop look and feel is pretty much standard with a limited set of widgets, uniform in appearance and function across all applications. That is user-friendly. In some sense it is nice to provide the administrator a single install file and the user an icon on the desktop (or Dock) to click on to access the application. A desktop application may also have access to the hardware and external devices such as USB cameras. An everyday internet browser, enhanced for secure anonymous browsing, should never have these privileges.

Mozilla Prism is a small independent browser window and it already is my favourite way to read GMail and Facebook, but the full potential of desktop integration is not used. Prism uses the Gecko engine, whereas Qt4 provides a QtWebKit module that is capable of loading and rendering web pages with the help of the WebKit browser engine.

I will continue in part #2 on my ideas how desktop and web technologies could build on top of each other's strengths.

November 15, 2009

Chrome Experiments paint the canvas with the future

This is one of my favourites from the Chrome Experiments. Colorscube by Dean McNamee. Best run on the Chrome browser.

It reminds me of the movie Cube. In this demo you can't actually zoom into the cube, but the controls make possible to rotate the cube on the canvas element.

The user interface on this demo, for example, show the power of HTML5 and modern JavaScript technologies. The user experience in the Chrome Experiments is good also on modern touchpads.

This will open up new technologies for developers also on the desktop - and today the most promising tech looks to be Qt. The desktop is a tremendous playground, not just the browser where the drag is one de-facto breed of browsers.

July 9, 2009

Oracle 10g on Ubuntu 9.04 x86_64

I installed Oracle 10g for testing a particular piece of code. My development environment is:
$ uname -om ; lsb_release -a|grep ^De
x86_64 GNU/Linux
Description:    Ubuntu 9.04
and before installing I read some horror stories of people trying to run Oracle on Debian and/or x86_64. These were old posts, so I went on to try it out. In the end I had no hitches and it just works. :) After downloading the deb package, I installed some core libraries someone recommended. I have no idea if all of these are required (why gcc/make when this is a binary package?) but for the reference, this is what I installed before the oracle deb. BTW, that blog has good tuning tips.
sudo apt-get install gcc libaio1 lesstif2 lesstif2-dev make libc6 libc6-i386 libc6-dev-i386 libstdc++5 lib32stdc++6 lib32z1 ia32-libs
Oracle was a breeze to install:
$ dpkg -i --force-architecture oracle-xe-universal_10.2.0.1-1.0_i386.deb
dpkg - warning, overriding problem because --force enabled:
 package architecture (i386) does not match system (amd64)
(Reading database ... 234278 files and directories currently installed.)
Unpacking oracle-xe-universal (from oracle-xe-universal_10.2.0.1-1.0_i386.deb) ...
Setting up oracle-xe-universal (10.2.0.1-1.0) ...
update-rc.d: warning: /etc/init.d/oracle-xe missing LSB information
update-rc.d: see 
Executing Post-install steps...
-e You must run '/etc/init.d/oracle-xe configure' as the root user to configure the database.


 $ /etc/init.d/oracle-xe configure

Oracle Database 10g Express Edition Configuration
-------------------------------------------------
This will configure on-boot properties of Oracle Database 10g Express
Edition.  The following questions will determine whether the database should
be starting upon system boot, the ports it will use, and the passwords that
will be used for database accounts.  Press  to accept the defaults.
Ctrl-C will abort.

Specify the HTTP port that will be used for Oracle Application Express [8080]:8888

Specify a port that will be used for the database listener [1521]:

Specify a password to be used for database accounts.  Note that the same
password will be used for SYS and SYSTEM.  Oracle recommends the use of
different passwords for each database account.  This can be done after
initial configuration:
Confirm the password:

Do you want Oracle Database 10g Express Edition to be started on boot (y/n) [y]:

Starting Oracle Net Listener...Done
Configuring Database...Done
Starting Oracle Database 10g Express Edition Instance...Done
Installation Completed Successfully.
To access the Database Home Page go to "http://127.0.0.1:8888/apex"
Apex works so I am happy, but how about a command line console? Ah, there's SQL*Plus. I needed to set up some environment variables (note the unorthodox install location of the binary), which I based on the packaged shell script "/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/bin/oracle_env.sh" I put this in /etc/environment:
export ORACLE_HOME="/usr/lib/oracle/xe/app/oracle/product/10.2.0/server"
PATH="${PATH}:${ORACLE_HOME}/bin"
export ORACLE_SID=XE
Testing the admin account, and checking the platform. Oracle is running in 32-bit mode, while the platform is 64-bit.
$ sqlplus SYS AS SYSDBA

SQL*Plus: Release 10.2.0.1.0 - Production on Thu Jul 9 15:10:54 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Enter password:

Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

SQL> select platform_name from v$database;

PLATFORM_NAME
--------------------------------------------------------------------------------
Linux IA (32-bit)
The SQL*Plus help files need to be manually loaded:
cd /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/sqlplus/admin/help
  sqlplus SYS AS SYSDBA @helpdrop.sql
  sqlplus SYS AS SYSDBA @hlpbld.sql helpus.sql
In conclusion, I was happy to get it running without running a virtual Centos server. =)

May 7, 2009

.vimrc + Python indentation

I had two separate projects (vcpynet-client and MXManager) which belong together. MXManager is a GUI in PyQt4 for vcpynet-client (which is now renamed to mx-download). That is worth another post. I needed to re-indent the two codes to have the same indentation style. In Ruby I tend to use 2 spaces. In Python it feels a bit more natural to use 4 spaces. These files used both styles which is ok as long as the indentation is strict in each separate file. Re-indenting in Vim should be simple. I defined these in my .vimrc:
set tabstop=4
set softtabstop=4
set shiftwidth=4
set shiftround
set expandtab
set autoindent
set paste
Vim re-indents the file in normal mode by ”gg=G”. Whoops, didn't work on my files. Guess I need to be more strict. I break some lines in a bit unorthodox fashion but Python 2.5 seems to accept it. Vim doesn't. Sed accomplised it, so for the record:
sed -i 's/\ \ /\ \ \ \ /g' 

May 4, 2009

NSLU2 + UTF8 ♥

NSLU2 aka ”slug” is a small consumer-grade embedded ARM device. It is not very fast but quite handy and completely silent. It consumes only ~5 W with an USB flash drive attached. However the Debian image I use does not come with all the needed locales but it's easy to generate them.

Include whatever locales you like to /etc/locale.gen and generate them:

 # locale-gen
Generating locales (this might take a while)...
  en_GB.ISO-8859-1... done
  en_GB.ISO-8859-15... done
  en_GB.UTF-8... done
...
Generation complete.
The next step is to configure the proper keyboard layout.
 # apt-get install console-data console-tools debconf
 # dpkg-reconfigure console-data
Finally, define the default system-wide locale:
 # echo 'LC_ALL="en_GB.UTF-8"' > /etc/default/locale

April 18, 2009

Sofar and Roswell

Roswell was a place where an UFO crashed at 1947. Then the US government covered it up with a story of a crashed weather balloon. The X-files was a TV series based on this urban legend that then grow out of proportion. The series is very enjoyable and interesting to watch. The ”factual” story behind Roswell is quite as amazing, as told by a brilliant physicist and Berkeley professor Richard A. Muller. The Berkeley university has made a wonderful policy of uploading complete courses online available under a Creative Commons license! I love especially the Muller lectures, some of which are also available on YouTube. He makes each lecture very interesting and educational and can explain the physics in an easily understandable way. He doesn't go into the deep mathematics, but rather explains qualitative facts of nature and makes all subjects connect to real life. The Sofar bomb, actually, is a mind-blower. This item was used in WW2 as an SOS device for the pilots who flew over the Pacific Ocean. They threw an hollow sphere into the ocean and the navy could approximate their location and come to rescue. Those who have seen the Hunt for Red October know about the sound channel. In this channel the sound waves bend and reach high ranges because the channel reduces the scattering. The same phenomenon appears also in the atmosphere. This is what the Roswell weather balloon was measuring. Cold war signals from USSR. So when the balloon crashed, the ”flying discs” they had collected were in fact circular microphones. What was the saucer then?

April 2, 2009

Streaming from webcam

A little while ago I purchased a cheap webcam: Logitech QuickCam E3500. It works in Linux with the in-kernel 'uvcvideo' driver (plug-and-play) and I was interested to tinker with live video stream. It is very simple to view (a) or record (b) a video stream;
(a)  mplayer tv://
 (b)  mencoder -quiet tv:// -tv noaudio -ovc lavc -o "webcam-$(date "+%Y%m%d %H:%M").avi"
Streaming live video to the internet is a bit more complicated. At first I wanted to use FlowPlayer, a neat Flash program that has elaborate JavaScript controls, to view the stream using RTMP stream, which is a proprietary protocol. Setting up a RTMP server on the other hand seemed to be impossoble to setup in the time that I was going to invest. There seemed to be only few options; the Apple Darwin Streaming Server and Red5. In short, Darwin did not compile and Red5 was way too much work to configure. Away with RTMP - behold, VLC to the rescue!
vlc -I dummy --no-audio --no-sout-audio v4l2:///dev/video0:width=320:height=240 \
    --sout='#transcode{venc=ffmpeg,vcodec=x264,vb=256,vt=128}:std{access=mmsh,mux=asfh,dst=:8080/stream.asf}'
MMS, WTF?! A deprecated proprietary protocol and ASF container! Oh well, this was the first (and worst) simple streaming method I came across, and thought it would be worth a try. This seems to be a popular way of doing HTML embedded video. My internet connection is slow so I opted for the x264 codec, which by the way, is not supported by IE or WMP, which kind of cancels out the theoretical advantage of MMS that is works on the regular Windows user. With Firefox + VLC Mozilla plugin this seems to actually work. IE users can display the stream in VLC.
<OBJECT ID="MediaPlayer" WIDTH="640" HEIGHT="480"
    CLASSID="CLSID:22D6f312-B0F6-11D0-94AB-0080C74C7E95"
    STANDBY="Loading Windows Media Player components..."
    TYPE="application/x-oleobject"
    CODEBASE="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,7,1112">
    <PARAM name="autoStart" value="True">
    <PARAM name='showControls' value="False">
    <PARAM name="filename" value="mms://:8080/stream.asf">
    <EMBED TYPE="application/x-mplayer2"
        SRC="mmsh://:8080/stream.asf"
        NAME="MediaPlayer"
        WIDTH=640
        HEIGHT=480>
    </EMBED>
  </OBJECT>

March 25, 2009

Hacking MFserver

The Maximum T-8000 PVR is an interesting device; it is a consumer-grade product and comes with a custom Linux OS. Marusÿs is the proprietor of the server's source code (which I believe they can, as it uses libupnp, which is licensed under BSD), but they offer it for download. The server (version 0.0.1) needed some patching to compile on GCC 4. The fixes were relatively simple. The major problem was that VLC-0.9 does no longer accept EXTVLCOPTS in playlists, and so far MFserver had relied on this. Fortunately VLC has a powerful way to chain up the needed transcode and networking modules from a single command, so I got it working by defining a few class variables and using sprintf() to contruct the VLC command. This means I can open the MFclient on the television screen, browse the server's video collection with the remote, and select a title to play on the TV. The PVR receives MPEG2 TS stream, such as in DVB broadcasts. I had a couple of test videos; a) DVB recordings from the PVR itself (ts) and from Kaffeine (m2t) b) Youtube videos (flv) c) YLE videos (wmv) d) DVD container units (?) (vob) e) random AVIs from the internet Videos that were recorded on the PVR, transferred to a PC, and streamed back without transcoding provided expectedly the best result, in almost no skipping in audio or video. There should be no loss in the video or audio quality. The Youtube videos seem to generally work quite reasonably. Some videos have low bitrates and it is obvious on the TV screen. Some of the random AVIs pulled from the internet worked very well, others did not work at all. I haven't yet got any picture from Kaffeine recordings. After some experimenting I figured the DVB recordings should not be transcoded. FFmpeg does seem to do ”the wrong thing” when it is asked to transcode from MPEG2 to MPEG2, and actually does demux and re-encode! The transcode options should be set by MFserver and be based on the video file suffix. In Ruby this would be as simple as:
transcode = 
    case filename[/\.([^\.]+)$/,1]
    when 'ts'
      ''
    else
      'transcode{vcodec=mp2v,acodec=mpga,.....'
    end
However, MFserver is written in C (although the file suffix is .cpp). I haven't touched C in years. Since then I have learned to do some programming on Java, Bash, Perl, Ruby, JavaScript and Python (in that chronological order). These are high level, (mostly) object oriented languages. Now, I had to face char[], *char and the paradigm shift from higher level abstractions to byte-level processing of an array, trying to remember how pointers work and what happens when I return a local variable and so on. There was an option to switch to C++ and use the String class without including any additional headers but I was intrigued by solving this in C. After three hours of intense reading and experimenting with a simple testing script, I had a function that parsed the suffix from the filename *char.
void VLCMgr::parseSuffix(char *filename)
{
       char _suffix[5] = "";
       int len = strlen(filename);
       int i=0;
       int seek = len-6;
       int dotreached=0;
       // get suffix
       for ( ; seek <= len ; seek++) {
               // separator . not reached
               if ((filename[seek]=='.') && strlen(_suffix)==0) 
               {
                       dotreached=1;
                       _suffix[i] = filename[seek]; // this will be overwritten
               }
               else if ((dotreached==1) && strlen(_suffix)!=0)
               {
                       // then do not add dots
                       if (filename[seek]!='.')
                       {
                               _suffix[i] = filename[seek];
                               i++;
                       }
               }
       }
       filename[i-1]='\0'; // mark the end of suffix string
       strncpy(filename,_suffix,5);
}
That's a lot of work just to get such a simple task done. Remember the Ruby equivalent was
filename[/\.([^\.]+)$/,1]
It makes me even more amazed how people can program whole operating systems and desktop environments with this language. I also dug up example sources of socket communication and merged them in to send remote control commands to VLC's remote control interface that was opened on a TCP socket. This provided to be somewhat tricky but now the commands pause, faster and slower are sent to the VLC socket. However the speeding up function does not seem to work while transcoding. This is an ongoing project and I'll post updates and the patches later, when I get a reasonable set of functionality finished.

March 23, 2009

Notes on using multiple ALSA cards

The last few weeks have been quite exciting. I studied how to configure two ALSA chipsets to operate simultaneously. Then I worked on to bring a stable version of lportal-1.1, and hacked MFserver to stream video from a PC over the network to a PPC-based PVR device (Maximum T-8000). And learning the basics of git in the meanwhile! :) Lots of fun. My testbench was Sabayon-4 with Linux kernel 2.6.27, and had to switch from kernel ALSA to a newer version to get it working properly. After learning a bit about ALSA, it boiled down to configuring /etc/modprobe.d/alsa to define the card indices and their driver. I have a setup with integrated Intel audio which has altogether 5 analog 3.5" stereo audio plugs and a S/PDIF connector .. on the motherboard. I just today got a connector card for this, but I already had an M-AUDIO Audiophile 2496 that has a coax S/PDIF output available. I connected this with a cheap 1 m single RCA coax cable to Pioneer stereo system. After all the work of compiling ALSA headers for the correct kernel and hassling with the kernel modules, I got both cards working simultaneously. I could play the same audio file in two different processes and send the output to analog and digital simultaneously. Both input sources are selected by the "CD" switch on the amplifier, and a separate analog/digital button is available to select the source. Cool! To my ears it seems that the digital is crisp and clear, but suffers from constant static cracles during some tracks. Analog source is a bit muddier, but the dynamic range is somehow more pleasant for me, so I left the analog device the default output. I decided to use ~/.asoundrc to set the default output device: To set the Audiophile's digital output:
pcm.!default iec958:M2496
Or the integrated Intel analog:
pcm.!default front:Intel
Speaker-test is a good testing tool:
$ speaker-test -c2 -Diec958:CARD=M2496,DEV=0
 $ speaker-test -c2 -Dfront:CARD=Intel
I use Amarok 2 to play music, using Phonon and KDE-4.2.1. Works fine.