Saturday, February 13, 2010

Moving away from blogger

Blogger are deprecating their ftp publishing support and are recommending users to switch to their custom domain feature where they host the blog files for you. I prefer to keep my posts on my own server so will be switching weblog systems in the near future.

This will break existing links to Atom feeds for specific labels (eg. the feed for Mozilla specific posts) as they are on a blogger controlled domain. So if you're reading this through one of those Atom feeds and want to continue with it then you might want to switch to:
http://bluishcoder.co.nz/tags/[tagname]/atom.xml
For example, the Mozilla specific posts will be in http://bluishcoder.co.nz/tags/mozilla/atom.xml.

My existing posts will remain at their current URL's.

Labels:

Saturday, June 27, 2009

Playing Ogg files with audio and video in sync

My last post in this series had Vorbis audio playing but with Theora video out of sync. This post will go through an approach to keeping the video in sync with the audio.

To get video in sync with the audio we need a timer incrementing from when we start playback. We can't use the system clock for this as it is not necessarily keeping the same time as the audio or video being played. The system clock can drift slightly and over time this audio and video to get out of sync.

The audio library I'm using, libsydneyaudio, has an API call that allows getting the playback position of the sound sample being played by the audio system. This is a value in bytes. Since we know the sample rate and number of channels of the audio stream we can compute a time value from this. Synchronisation becomes a matter of continuously feeding the audio to libsydneybackend, querying the current position, converting it to a time value, and displaying the frame for that time.

The time for a particular frame is returned by the call to th_decode_packetin. The last parameter is a pointer to hold the 'granulepos' of the decoded frame. The Theora spec explains that the granulepos can be used to compute the time that this frame should be displayed up to. That is, when this time is exceeded this frame should no longer be displayed. It also enables computing the location of the keyframe that this frame depends on - I'll cover what this means when I write about how to do seeking.

The libtheora API th_granule_time converts a 'granulepos' to an absolute time in seconds. So decoding a frame gives us 'granulepos'. We store this so we know when to stop displaying the frame. We track the audio position, convert it to a time. If it exceeds this value we decode the next frame and display that. Here's a breakdown of the steps:

  1. Read the headers from the Ogg file. Stop when we hit the first data packet.
  2. Read packets from the audio stream in the Ogg file. For each audio packet:
    1. Decode the audio data and write it to the audio hardware.
    2. Get the current playback position of the audio and convert it to an absolute time value.
    3. Convert the last granulepos read (defaulting to zero if none have been read) to an absolute time value using the libtheora API.
    4. If the audio time is greater than the video time:
      1. Read a packet from the Theora stream.
      2. Decode that packet and display it
      3. Store the granulepos from that decoded frame so we know when to display the next frame.

Notice that the structure of the program is different to the last few articles. We no longer read all packets from the stream, processing them as we get them. Instead we specifically process the audio packets and only handle the video when it's time to display them. Since we are driving our a/v sync off the audio clock we must continously feed the audio data. I think it tends to be a better user experience to have flawless audio with video frame skipping rather than skipping audio but smooth video. Worse is to have both skipping of course.

The example code for this article is in the 'part4_avsync' branch on github.

This example takes a slightly different approach to reading headers. I use ogg_stream_packetpeek to peek ahead in the bitstream for a packet and do the header processing on the peeked packet. If it is a header I then consume the packet. This is done so I don't consume the first data packet when reading the headers. I want the data packets to be consumed in a particular order (audio, followed by video when needed).

// Process all available header packets in the stream. When we hit
// the first data stream we don't decode it, instead we
// return. The caller can then choose to process whatever data
// streams it wants to deal with.
ogg_packet packet;
while (!headersDone &&
(ret = ogg_stream_packetpeek(&stream->mState, &packet)) != 0) {
assert(ret == 1);

// A packet is available. If it is not a header packet we exit.
// If it is a header packet, process it as normal.
headersDone = headersDone || handle_theora_header(stream, &packet);
headersDone = headersDone || handle_vorbis_header(stream, &packet);
if (!headersDone) {
// Consume the packet
ret = ogg_stream_packetout(&stream->mState, &packet);
assert(ret == 1);
}

To read packets for a particular stream I use a 'read_packet' function that operates on a stream passed as a parameter:

bool OggDecoder::read_packet(istream& is, 
ogg_sync_state* state,
OggStream* stream,
ogg_packet* packet) {
int ret = 0;

while ((ret = ogg_stream_packetout(&stream->mState, packet)) != 1) {
ogg_page page;
if (!read_page(is, state, &page))
return false;

int serial = ogg_page_serialno(&page);
assert(mStreams.find(serial) != mStreams.end());
OggStream* pageStream = mStreams[serial];

// Drop data for streams we're not interested in.
if (stream->mActive) {
ret = ogg_stream_pagein(&pageStream->mState, &page);
assert(ret == 0);
}
}
return true;
}

If we need to read a new page (to be able to get more packets) we check the stream for the read page and if it is not for the stream we want we store the packet in the bitstream for that page so it can be retrieved later. I've added an 'active' flag to the streams so we can ignore streams that we aren't intersted in. We don't want to continuously buffer data for alternative audio tracks we aren't playing for example. The streams are marked inactive when the headers are finished reading.

The code that does the checking to see if it's time to display a frame is:


// At this point we've written some audio data to the sound
// system. Now we check to see if it's time to display a video
// frame.
//
// The granule position of a video frame represents the time
// that that frame should be displayed up to. So we get the
// current time, compare it to the last granule position read.
// If the time is greater than that it's time to display a new
// video frame.
//
// The time is obtained from the audio system - this represents
// the time of the audio data that the user is currently
// listening to. In this way the video frame should be synced up
// to the audio the user is hearing.
//
ogg_int64_t position = 0;
int ret = sa_stream_get_position(mAudio, SA_POSITION_WRITE_SOFTWARE, &position);
assert(ret == SA_SUCCESS);
float audio_time =
float(position) /
float(audio->mVorbis.mInfo.rate) /
float(audio->mVorbis.mInfo.channels) /
sizeof(short);

float video_time = th_granule_time(video->mTheora.mCtx, mGranulepos);
if (audio_time > video_time) {
// Decode one frame and display it. If no frame is available we
// don't do anything.
ogg_packet packet;
if (read_packet(is, &state, video, &packet)) {
handle_theora_data(video, &packet);
video_time = th_granule_time(video->mTheora.mCtx, mGranulepos);
}
}

The code for decoding and display the Theora video is similar to the Theora decoding article. The main difference is we store the granulepos in mGranulepos so we know when to stop displaying the frame.

This version of 'plogg' should play Ogg files with a Theora and Vorbis track in sync. You can test it on the transformers trailer for example. It does not play Theora files with no audio track - we can't synchronise to the audio clock if there is no audio. This can be worked around by falling back to delaying for the required framerate as the previous Theora example did.

The a/v sync is not perfect however. If the video is large and decoding keyframes takes a while then we can fall behind in displaying the video and go out of sync. This is because we only play one frame when we check the time. One approach to fixing this is to decode, but not display, all frames up until the audio time rather than just the next time.

The other issue is that the API call we are using to write to the audio hardware is blocking. This is using up valuable time that we could be using to decode a frame. When the write to the sound hardware returns we have very little time to decode a frame before glitches start appearing in the audio due to buffer underruns. Try playing a larger video (like the Ghostbusters HD Trailer and the audio and video will skip (depending on the speed of your hardware). This isn't a pleasant experience. Because of the blocking audio writes we can't skip more than one frame due to the frame decoding time taking too long causing audio skip.

The fixes for these aren't too complex and I'll go through it in my next article. The basic approach is to move to an asynchronous method of writing the audio, skip displaying frames when needed (to reduce the cost of the YUV decoding), skip decoding frames if possible (depending on location of keyframes we can do this), and to check how much audio data we have queued before decoding to always ensure we won't drop audio while decoding.

With these fixes in place I can play the 1080p Ogg version of Big Buck Bunny on a Macbook laptop (running Arch Linux) with no audio interruption and with a/v syncing correctly. There is a fair amount of frame skipping however but it's a lot more watchable than if you try playing it without these modifications in place. And better than watching with the video lagging further and further behind the longer you watch it. Further improvements can be made to reduce the frame skipping by utilising threads to take advantage of extra core's on the PC.

After the followup article on improving the a/v sync I'll look at covering seeking.


Categories: , , ,

Labels:

Thursday, June 25, 2009

Decoding Vorbis files with libvorbis

Decoding Vorbis streams require a very similar approach to that used when decoding Theora streams. The public interface to the libvorbis library is very similar to that used by libtheora. Unfortunately the libvorbis documentation doesn't contain an API reference that I could find so I'm following the approached used by the example programs.

Assuming we have already obtained an ogg_packet, the general steps to follow to decode and play Vorbis streams are:

  1. Call vorbis_synthesis_headerin to see if the packet is a Vorbis header packet. This is passed a vorbis_info and vorbis_comment object to hold the information read from those header packets. The return value of this function is zero if the packet is a Vorbis header packet. Unfortunately it doesn't return a value to see that it's a Vorbis data packet. To check for this you need to check if the stream is a Vorbis stream (by knowing you've previously read Vorbis headers from it) and the return value is OV_ENOTVORBIS.
  2. Once all the header packets are read create a vorbis_dsp_state and vorbis_block object. Initialize these with vorbis_synthesis_init and vorbis_block_init respectively. These objects hold the state of the Vorbis decoding. vorbis_synthesis_init is passed the vorbis_info object that was filled in during the reading of the header packets.
  3. For each data packet:
    1. Call vorbis_synthesis passing it the vorbis_block created above and the ogg_packet containing the packet data. If this succeeds (by returning zero), call vorbis_synthesis_blockin passing it the vorbis_dsp_state and vorbis_block objects. This call copies the data from the packet into the Vorbis objects ready for decoding.
    2. Call vorbis_synthesis_pcmout to get an pointer to an array of floating point values for the sound samples. This will return the number of samples in the array. The array is indexed by channel number, followed by sample number. Once obtained this sound data can be sent to the sound hardware to play the audio.
    3. Call vorbis_synthesis_read, passing it the vorbis_dsp_state object and the number of sound samples consumed. This allows you to consume less data than vorbis_synthesis_pcmout returned. This is useful if you can't write all the data to the sound hardware without blocking.

In the example code in the github repository I create a VorbisDecode object that holds the objects needed for decoding. This is similar to the TheoraDecode object mentioned in my Theora post:

class VorbisDecode {
...
vorbis_info mInfo;
vorbis_comment mComment;
vorbis_dsp_state mDsp;
vorbis_block mBlock;
...

VorbisDecode()
{
vorbis_info_init(&mInfo);
vorbis_comment_init(&mComment);
}
};

I added a TYPE_VORBIS value to the StreamType enum and the stream is set to this type when a Vorbis header is successfully decoded:

  int ret = vorbis_synthesis_headerin(&stream->mVorbis.mInfo,
&stream->mVorbis.mComment,
packet);
if (stream->mType == TYPE_VORBIS && ret == OV_ENOTVORBIS) {
// First data packet
ret = vorbis_synthesis_init(&stream->mVorbis.mDsp, &stream->mVorbis.mInfo);
assert(ret == 0);
ret = vorbis_block_init(&stream->mVorbis.mDsp, &stream->mVorbis.mBlock);
assert(ret == 0);
stream->mHeadersRead = true;
handle_vorbis_data(stream, packet);
}
else if (ret == 0) {
stream->mType = TYPE_VORBIS;
}
}

The example program uses libsydneyaudio for audio output. This requires sound samples to be written as signed short values. When I get the floating point data from Vorbis I convert this to signed short and send it to libsydneyaudio:

  int ret = 0;

if (vorbis_synthesis(&stream->mVorbis.mBlock, packet) == 0) {
ret = vorbis_synthesis_blockin(&stream->mVorbis.mDsp, &stream->mVorbis.mBlock);
assert(ret == 0);
}

float** pcm = 0;
int samples = 0;
while ((samples = vorbis_synthesis_pcmout(&stream->mVorbis.mDsp, &pcm)) > 0) {
if (!mAudio) {
ret = sa_stream_create_pcm(&mAudio,
NULL,
SA_MODE_WRONLY,
SA_PCM_FORMAT_S16_NE,
stream->mVorbis.mInfo.rate,
stream->mVorbis.mInfo.channels);
assert(ret == SA_SUCCESS);

ret = sa_stream_open(mAudio);
assert(ret == SA_SUCCESS);
}

if (mAudio) {
short buffer[samples * stream->mVorbis.mInfo.channels];
short* p = buffer;
for (int i=0;i < samples; ++i) {
for(int j=0; j < stream->mVorbis.mInfo.channels; ++j) {
int v = static_cast<int<(floorf(0.5 + pcm[j][i]*32767.0));
if (v > 32767) v = 32767;
if (v <-32768) v = -32768;
*p++ = v;
}
}

ret = sa_stream_write(mAudio, buffer, sizeof(buffer));
assert(ret == SA_SUCCESS);
}

ret = vorbis_synthesis_read(&stream->mVorbis.mDsp, samples);
assert(ret == 0);
}
}

A couple of minor changes were also made to the example program:

  • Continue processing pages and packets when the 'end of file' is reached. Otherwise a few packets that are buffered after we've reached the end of the file will be missed.
  • After reading a page don't just try to read one packet, call ogg_stream_packetout until it returns a result saying there are no more packets. This means we process all the packets from the page immediately and prevents a build up of buffered data.

The code for this example is in the 'part3_vorbis' branch of the github repository. This also includes the Theora code but does not do any a/v synchronisation. Files containing Theora streams will show the video data but it will not play smoothly and will not be synchronised with the audio. Fixing that is the topic of the next post in this series.


Categories: , , ,

Labels:

Decoding Theora files using libtheora

My last post covered read Ogg files using libogg. The resulting program didn't do much but it covered the basic steps needed to get an ogg_packet which we need to decode the data in the stream. The thing step I want to cover is decoding Theora streams using libtheora.

In the previous post I stored a count of the number of packets in the OggStream object. For theora decoding we need a number of different objects to be stored. I encapsulate this in a TheoraDecode structure:

class TheoraDecode { 
...
th_info mInfo;
th_comment mComment;
th_setup_info *mSetup;
th_dec_ctx* mCtx;
...
};

th_info, th_comment and th_setup_info contain data read from the Theora headers. The Theora stream contains three headers packets. These are the info, comment and setup headers. There is one object for holding each of these as we read the headers. The th_dec_ctx object holds information that the decoder requires to keep track of the decoding process.

th_info and th_comment need to be initialized using th_info_init and th_comment_init. Notice that th_setup_info is a pointer. This needs to be free'd when we're finished with it using th_setup_free. The decoder context object also needs to be free'd. Use th_decode_free. A convenient place to do this is in the TheoraDecode constructor and destructor:

class TheoraDecode {
...
TheoraDecode() :
mSetup(0),
mCtx(0)
{
th_info_init(&mInfo);
th_comment_init(&mComment);
}

~TheoraDecode() {
th_setup_free(mSetup);
th_decode_free(mCtx);
}
...

The TheoraDecode object is stored in the OggStream structure. The OggStream stucture also gets a field holding the type of the stream (Theora, Vorbis, Unknown, etc) and a boolean indicating whether the headers have been read:

class OggStream
{
...
int mSerial;
ogg_stream_state mState;
StreamType mType;
bool mHeadersRead;
TheoraDecode mTheora;
...
};

Once we get the ogg_packet from an Ogg stream we need to find out if it is a Theora stream. The approach I'm using to do this is to attempt to extract a Theora header from it. If this succeeds, it's a Theora stream. th_decode_headerin will attempt to decode a header packet. A return value of '0' indicates that we got a Theora data packet (presumably the headers have been read already). This function gets passed the info, comment, and setup objects and it will populate them with data as it reads the headers:

ogg_packet* packet = ...got this previously...;
int ret = th_decode_headerin(&stream->mTheora.mInfo,
&stream->mTheora.mComment,
&stream->mTheora.mSetup,
packet);
if (ret == TH_ENOTFORMAT)
return; // Not a theora header

if (ret > 0) {
// This is a theora header packet
stream->mType = TYPE_THEORA;
return;
}

assert(ret == 0);
// This is not a header packet. It is the first
// video data packet.
stream->mTheora.mCtx =
th_decode_alloc(&stream->mTheora.mInfo,
stream->mTheora.mSetup);
assert(stream->mTheora.mCtx != NULL);
stream->mHeadersRead = true;
...decode data packet...

In this example code we attempt to decode the header. If it fails it bails out, possibly to try decoding the packet using libvorbis or some other means. If it succeeds the stream is marked as type TYPE_THEORA so we can handle it specially later.

If all headers packets are read and we got the first data packet then we call th_decode_alloc to get a decode context to decode the data.

Once the headers are all read, the next step is to decode each Theora data packet. To do this we first call th_decode_packetin. This adds the packet to the decoder. A return value of '0' means we can get a decoded frame as a result of adding the packet. A call to th_decode_ycbcr_out gets the decoded YUV data, stored in a th_ycbcr_buffer object. This is basically an array of the YUV data.

ogg_int64_t granulepos = -1;
int ret = th_decode_packetin(stream->mTheora.mCtx,
packet,
&granulepos);
assert(ret == 0);

th_ycbcr_buffer buffer;
ret = th_decode_ycbcr_out(stream->mTheora.mCtx, buffer);
assert(ret == 0);
...copy yuv data to SDL YUV overlay...
...display overlay...
...sleep for 1 frame...

The 'granulepos' returned by the th_decode_packetin call holds information regarding the presentation time of this frame, and what frame contains the keyframe that is needed for this frame if it is not a keyframe. I'll write more about this in a future post when I cover synchronising the audio and video. For now it's going to be ignored.

Once we have the YUV data I use SDL to create a surface, and a YUV overlay. This allows SDL to do the YUV to RGB conversion for me. I won't copy the code for this since it's not particularly relevant to using the libtheora API - you can see it in the github repository.

Once the YUV data is blit to the screen the final step is to sleep for the period of one frame so the video can playback at approximately the right framerate. The framerate of the video is stored in the th_info object that we got from the headers. It is represented as the fraction of two numbers:

float framerate = 
float(stream->mTheora.mInfo.fps_numerator) /
float(stream->mTheora.mInfo.fps_denominator);
SDL_Delay((1.0/framerate)*1000);

With all that in place, running the program with an Ogg file containing a Theora stream should play the video at the right framerate. Adding Vorbis playback is almost as easy - the main difficulty is synchronising the audio and video. I'll cover these topics in a later post.


Categories: , ,

Labels:

Wednesday, June 24, 2009

Reading Ogg files using libogg

Reading data from an Ogg file is relatively simple. The file format is well documented in RFC 3533. I showed how to read the format using JavaScript in a previous post.

For C and C++ programs it's easier to use the xiph.org libraries. There are libraries for decoding specific formats (libvorbis, libtheora) and there is a library for reading data from Ogg files (libogg).

I'm prototyping some approaches to improve the performance of the Firefox Ogg video playback and while I'm at it I'll write some posts on using these libraries to decode/play Ogg files. Hopefully it'll prove useful to others using them and I can get some feedback on usage.

All the code for this is in the plogg git repository on github. The 'master' branch contains the work in progress player that I'll describe in a series of posts, and there are branches specific to the examples in each post.

The libogg documentation describes the API that I'll be using in this post. All that this example will do is read an Ogg file, read each stream in the file and count the number of packets for that stream. It prints the number of packets. It doesn't decode the data or do anything really useful. That'll come later.

You can think of an Ogg file as containing logical streams of data. Each stream has a serial number that is unique within the file to identify it. A file containing Vorbis and Theora data will have two streams. A Vorbis stream and a Theora stream.

Each stream is split up into packets. The packets contain the raw data for the stream. The process of decoding a stream involves getting a packet from it, decoding that data, doing something with it, and repeating.

That describes the logical format. The physical format of the Ogg file is split into pages of data. Each physical page contains some part of the data for one stream.

The process of reading and decoding an Ogg file is to read pages from the file, associating them with the streams they belong to. At some point we then go through the pages held in the stream and obtain the packets from it. This is the process the code in this example follows.

The first thing we need to do when reading an Ogg file is find the first page of data. We use a ogg_sync_state structure to keep track of search for the page data. This needs to be initialized with ogg_sync_init and later cleaned up with ogg_sync_clear:

ifstream file("foo.ogg", ios::in | ios::binary);
ogg_sync_state state;
int ret = ogg_sync_init(&state);
assert(ret==0);
...look for page...

Note that the libogg functions return an error code which should be checked, A result of '0' generally indicates success. We want to obtain a complete page of Ogg data. This is held in an ogg_page structure. The process of obtaining this structure is to do the following steps:

  1. Call ogg_sync_pageout. This will take any data current stored in the ogg_sync_state object and store it in the ogg_page. It will return a result indicating when the entire pages data has been read and the ogg_page can be used. It needs to be called first to initialize buffers. It gets called repeatedly as we read data from the file.
  2. Call ogg_sync_buffer to obtain an area of memory we can reading data from the file into. We pass the size of the buffer. This buffer is reused on each call and will be resized if needed if a larger buffer size is asked for later.
  3. Read data from the file into the buffer obtained above.
  4. Call ogg_sync_wrote to tell libogg how much data we copied into the buffer.
  5. Resume from the first step, calling ogg_sync_buffer. This will copy the data from the buffer into the page, and return '1' if a full page of data is available.

Here's the code to do this:

ogg_page page;
while(ogg_sync_pageout(&state, &page) != 1) {
char* buffer = ogg_sync_buffer(oy, 4096);
assert(buffer);

file.read(buffer, 4096);
int bytes = stream.gcount();
if (bytes == 0) {
// End of file
break;
}

int ret = ogg_sync_wrote(&state, bytes);
assert(ret == 0);
}

We need to keep track of the logical streams within the file. These are identified by serial number and this number is obtained from the page. I create a C++ map to associate the serial number with an OggStream object which holds information I want associated with the stream. In later examples this will hold data needed for the Theora and Vorbis decoding process.

class OggStream
{
...
int mSerial;
ogg_stream_state mState;
int mPacketCount;
...
};

typedef map StreamMap;

Each stream has an ogg_stream_state object that is used to keep track of the data read that belongs to the stream. We're storing this in the OggStream object that we associated with the stream serial number. Once we've read a page as described above we need to tell libogg to add this page of data to the stream.

StreamMap streams;
ogg_page page = ...obtained previously...;
int serial = ogg_page_serialno(&page);
OggStream* stream = 0;
if (ogg_page_bos(&page) {
stream = new OggStream(serial);
int ret = ogg_stream_init(&stream->mState, serial);
assert(ret == 0);
streams[serial] = stream;
}
else
stream = streams[serial];

int ret = ogg_stream_pagein(&stream->mState, &page);
assert(ret == 0);

This code uses ogg_page_serialnoto get the serial number of the page we just read. If it is the beginning of the stream (ogg_page_bos) then we create a new OggStream object, initialize the stream's state with ogg_stream_init, and store it in out streams map. If it's not the beginning of the stream we just get our existing entry in the map. The final call to ogg_stream_pagein inserts the page of data into the streams state object. Once this is done we can start looking for completed packets of data and decode them.

To decode the data from a stream we need to retrieve a packet from it. The steps for doing this are:

  1. Call ogg_stream_packetout. This will return a value indicating if a packet of data is available in the stream. If it is not then we need to read another page (following the same steps previously) and add it to the stream, calling ogg_stream_packetout again until it tells us a packet is available. The packet's data is stored in an ogg_packet object.
  2. Do something with the packet data. This usually involves calling libvorbis or libtheora routines to decode the data. In this example we're just counting the packets.
  3. Repeat until all packets in all streams are consumed.
while (..read a page...) {
...put page in stream...
ogg_packet packet;
int ret = ogg_stream_packetout(&stream->mState, &packet);
if (ret == 0) {
// Need more data to be able to complete the packet
continue;
}
else if (ret == -1) {
// We are out of sync and there is a gap in the data.
// We lost a page somewhere.
break;
}

// A packet is available, this is what we pass to the vorbis or
// theora libraries to decode.
stream->mPacketCount++;
}

That's all there is to reading an Ogg file. There are more libogg functions to get data out of the stream, identify end of stream, and various other useful functions but this covers the basics. Try out the example program in the github repository for more information.

Note that the libogg functions don't require reading from a file. You can use these routines with any data you've obtained. From a socket, from memory, etc.

In the next post about reading Ogg files I'll go through using libtheora to decode the video data and display it.


Categories: , ,

Labels:

Friday, June 05, 2009

Reading Ogg files with JavaScript

On tinyvid.tv I do quite a bit of server side reading of Ogg files to get things like duration and bitrate information when serving information about the media. I wondered if it would be possible to do this sort of thing using JavaScript running in the browser.

The format of the Ogg container is defined in RFC 3533. The difficulty comes in reading binary data from JavaScript. The XMLHttpRequest object can be used to retrieve data via a URL from JavaScript in a page but processing the binary data in the Ogg file is problematic. The response returned by XMLHttpRequest assumes text or XML (in Firefox at least).

One way of handling binary data is described in this Mozilla Developer article. Trying this method out works in Firefox and I can download and read the data in the Ogg file.

Ideally I don't want to download the entire file. It might be a large video. I thought by handling the 'progress' event or ready state 3 (data received) I'd be able to look at the data currently retrieved. This does work but on each call to the 'responseText' attribute in these events Firefox copies its internal copy of the downloaded data into a JavaScript array. Doing this every time a portion of the file is downloaded results in major memory use and slow downs proving impractical for even small files.

I think the only reliable way to process the file in chunks is to use byte range requests and do multiple requests. Is there a more reliable way to do binary file reading via JavaScript using XMLHttpRequest? I'd like to be able to process the file in chunks using an Iteratee style approach.

I put up a rough quick demo of loading the first 100Kb of a video and displaying information from each Ogg packet. This probably works in Firefox only due to the workaround needed to read binary data. Click on the 'Go' button in the demo page. This will load transformers320.ogg and display the contents of the first Ogg physical page.

I decode the header packets for Theora and Vorbis. So the first page shown will show it is for a Theora stream with a given size and framerate. Clicking 'Next' will move on to the Next page. This is a Vorbis header with the rate and channel information. Clicking 'Next' again gets the comment header for the Theora stream. The demo reads the comments and displays them. The same for the Vorbis comment records. As you 'Next' through the file it displays the meaning of the granulepos for each page. It shows whether the Theora data is for a keyframe, what time position it is, etc.

Something like this could be used to read metadata from Ogg files, read subtitle information, show duration, etc. More interesting would be to implement a Theora and/or Vorbis decoder in JavaScript and see how it performs.

The main issues with doing this from JavaScript seem to be:
  • Handling binary data using XMLHttpRequest in a cross browser manner
  • Processing the file in chunks so the entire file does not need to be kept in memory
  • Files need to be hosted on the same domain as the page. tinyvid.tv adds the W3C Access Control headers so they can be accessed cross domain but it also hosts some files on Amazon S3 where these headers can't be added. As a result even tinyvid itself can't use XMLHttpRequest to read these files.


Categories: , , ,

Labels:

Tuesday, June 02, 2009

Video for Everybody - HTML 5 video fallback

Kroc Camen has made available Video for Everybody, an HTML snippet that uses HTML 5 video if it's available in the browser, otherwise falling back to different video playback options.

What's interesting about 'Video for Everybody' is it doesn't use scripting at all. It uses the fallback mechanism built into HTML. The video playback mechanism used, in order of availability in the browser, is:
  1. HTML 5 <video>
  2. Adobe Flash
  3. Quicktime
  4. Windows Media Player
  5. Text explaining how to get video support if none of the above is available
The fallback options work across multiple browsers and even works on the iPhone. The 'Video For Everybody' page also goes through how to encode videos in Ogg and MP4 format.

Categories: ,

Labels:

Monday, May 11, 2009

Simple Nanojit Example

I've been playing around with Nanojit, thinking about using it in a toy project of mine.

Nanojit is a library for generating machine code with backends for x86 and ARM. It's used by Tamarin and TraceMonkey for their just in time compilers.

The Mozilla documentation contains some documentation which gives an overview of what's there:Currently Nanojit seems to be directly included in both the Tamarin and Mozilla repositories. To make it easy to reuse in my various test projects I split out the nanojit code and put it in a github repository. The build is a simple makefile and is only setup for x86 since that's that platform I'm trying it on for now.

In the 'example' directory of the nanojit github repository is the example from the Mozilla documentation. This is what I'm basing my usage of it from.

As a simple test of using it I wrote an expression evaluator. You enter simple mathematical expressions using addition, subtraction, multiplication and division at the command line and it compiles the expression into machine code using nanojit and calls it to get the result.

I started with an interpreter first and added the compiling once that was working. To parse the expressions I used Ian Piumarta's Parsing Expression Grammar library. I tweaked it a bit to be able to use it from C++. The grammar is simple and produces a basic Abstract Syntax tree:
Expr      = lhs:Product (  "+" rhs:Product  { lhs = new Add(lhs, rhs); }
| "-" rhs:Product { lhs = new Subtract(lhs, rhs); }
)* { $$ = lhs }
Product = lhs:Value ( "*" rhs:Value { lhs = new Multiply(lhs, rhs); }
| "/" rhs:Value { lhs = new Divide(lhs, rhs); }
)* _ { $$ =lhs }
Value = Number _
Number = < [0-9]+ ('.' [0-9]+)?> { $$ = new Number(atof(yytext)); }
Running this through the 'leg' program produces C code that can be included in the project to compile the expression entered by the user. The interpreter just walks over the AST, calling an 'eval' method, to produce the result. Code looks like:
float Number::eval()  { return value; }
float Add:eval() { return lhs->eval() + rhs->eval(); }
float Subtract:eval() { return lhs->eval() - rhs->eval(); }
float Multiply:eval() { return lhs->eval() * rhs->eval(); }
float Divide:eval() { return lhs->eval() / rhs->eval(); }
int main() {
Object* result = parse();
cout << "Result: " << result->eval() << endl;
return 0;
}
Converting this to generate machine code and call it is pretty easy. Instead of an 'eval' method I created a 'compile' method. 'compile' takes an object as an argument that I call methods on to generate Nanojit intermediate representation (LIR). It returns the resulting generated LIR instruction. The code for the Number AST object looks like:
LIns* Number::compile(LirWriter* writer) {
return writer->insImmf(value);
}
The LirWriter object contains methods to generate LIR code. For Number we generate an immediate float value and return a pointer to the generated instruction.

The operators are implemented in a similar manner. I'll just show the code for Add:
LIns* Add::compile(LirWriter* writer)
{
LIns* a = lhs->compile(writer);
LIns* b = rhs->compile(writer);
return writer->ins2(LIR_fadd, a, b);
}
Here we compile the left and right hand sides of the expression, saving pointers to the generated instructions. Then we generate a 'float add' LIR instruction. This is passed the instructions that were generated for the left and right parts of the expression. By calling compile on the topmost AST object we end up generating the code for the entire expression.

The code required to set up Nanojit and create the LirWriter is a bit more complicated. Most of what I did was copy and pasted from the example code. Basically I create a Fragmento object and a LirBuffer. Then each time an expression is entered I create a Fragment object which will contain the final generated code and a LirBufferWriter which is the base LirWriter object used by the 'compile' methods.

LirWriter's can be added to a LirBufferWriter to form a pipeline that instructions go through to do different things like optimisation or output debugging code before ending up in the LirBuffer. After this I generate a 'start' instruction, call the 'compile' method to do the rest, and generate a required epilogue instruction:
Fragmento *fragmento = new (gc) Fragmento(&core, 20);
LirBuffer *buf = new (gc) LirBuffer(fragmento, NULL);
...
Object* result = parse();
...
Fragment *f = new (gc) Fragment(NULL);
LirBufWriter writer(buf);
...
writer.ins0(LIR_start);
writer.ins1(LIR_fret, result->compile(&writer));
...
f->lastIns = writer.insGuard(LIR_loop, writer.insImm(1), rec_ins);

// Compile the fragment.
compile(fragmento->assm(), f);

// Call the compiled function.
typedef JS_FASTCALL float (*ExprFunction)();
ExprFunction fn = reinterpret_cast(f->code());
cout << "Result: " << fn() << endl;
The expression evaluator interpreter and compiler example can be downloaded and played with by cloning my 'play' repository on github. Once cloned you'll need to initialize and update the submodules to pull in the nanojit and peg third party libraries that it uses:
$ git clone git://github.com/doublec/play.git
$ cd play
$ git submodule init
$ git submodule clone
Run 'make' in the 'expr-interpreter' and 'expr-compiler' directories and run the 'expr' program that results. If you compile both nanojit and expr-compiler with DEBUG defined you get some debug output. It show's the LIR and the assembler code generated. For example, the LIR for: 2+3+4*2 is:
start
#40000000:0 /* 2 */
#40080000:0 /* 3 */
fadd1 = fadd #0x40000000:0, #0x40080000:0
#40100000:0 /* 4 */
#40000000:0 /* 2 */
fmul1 = fmul #0x40100000:0, #0x40000000:0
fadd2 = fadd fadd1, fmul1
ret fadd2
The assembly code generated for this is:
mov -8(ebp),0                  
mov -4(ebp),1073741824
mov -16(ebp),0
mov -12(ebp),1074266112
fldq -16(ebp)
fadd -8(ebp) f0(#0x40080000:0)
fstpq -8(ebp) f0(fadd1)
mov -16(ebp),0
mov -12(ebp),1074790400
mov -24(ebp),0
mov -20(ebp),1073741824
fldq -24(ebp)
fmul -16(ebp) f0(#0x40000000:0)
fadd -8(ebp) f0(fmul1)
It's easy to change the code to use integer math instead of floating point. Use LIR_ret instead of LIR_fret for the return, and use LIR_add, LIR_sub, etc. This is the assembly code generated for '2+3+4*2' using integer math:
mov eax,2                      
add eax,3 eax(2)
mov ebx,4 eax(add1)
mov ecx,2 eax(add1) ebx(4)
mul ebx,ecx eax(add1) ecx(2) ebx(4)
add eax,ebx eax(add1) ebx(mul1)


Categories:

Labels:

Friday, May 08, 2009

Theora encoder improvements

A lot of work has been going on improving the Theora video encoder. Given a better encoder we end up with better quality Theora videos without the need to change the decoder in existing players. The project name for the Theora encoder improvements is 'Thusnelda'.

Periodic updates have been released by Xiph showing the progress of the new encoder. The latest update makes interesting reading and show's graphs comparing it against other Theora builds, and against x264 (an H.264 encoder).

The update show's that some great improvements have been made, and in some cases is comparible to the results from the x264 encoder. Greg Maxwell gives some more information in this reddit comment about the results.

Remember that the intent of this is not to compare Theora vs x264, or to say one is better than the other in the general case. It's to show the improvement of Theora vs older Theora versions. The fact that it compares favourably against x264 in this one case, using one particular test of quality, is a good result though.

Categories: ,

Labels:

Friday, April 03, 2009

The Linux Link Tech Show on HTML 5 Audio and Video

Yesterday I was a guest on The Linux Link Tech Show talking about Firefox and HTML 5 audio/video support. It was a great show with lots of interesting questions and discussions about video in the browser, the choice of Theora for the video codec, and different ways video can be used on the web.

The show is archived and you can download and listen to episode 292 recorded on the 1st April 2009. Vorbis and MP3 versions are available.

Categories: ,

Labels:

Thursday, March 26, 2009

oggplayer: A simple Ogg player using liboggplay

To help track down performance issues, and fix some a/v synchronisation bugs, I wrote a simple Ogg player in C++ using the same libraries and a similar approach to what I use in the Firefox video implementation.

The source to 'oggplayer' is available on github.

I've setup the repository to facilitate my experimenting with different approaches and different library versions. What this means is I've taken the, probably unusual, approach of having a 'thirdparty' directory containing git submodules for each of the Ogg based libraries I'm using. I configure and build these and link statically to them. This lets me use experimental versions of the Ogg libraries without dealing with 'shared library hell'.

This is all explained in the README, but just quickly these are the steps to clone oggplayer, and get the third party libraries from git:
$ git clone git://github.com/doublec/oggplayer.git
$ cd oggplayer
$ git submodules init
$ git submodules update
Once that's done you need to build the third party libraries:
$ cd thirdparty
$ sh build.sh
Run 'make' to build oggplayer itself. The third party libraries are built and installed in a 'local' subdirectory.

You'll notice the unix-centric leaning of these commands. I've only built on Linux. I'm using cross platform libraries so it should build with minor changes on Mac OS X. Mainly changing the audio library to link against in the Makefile. For Windows you'll probably need to use Cygwin or MSYS.

To run, just pass the filename on the command line:
$ ./oggplayer foo.ogg
Press ESC to exit the playback, and space bar to toggle full screen on supported platforms (those that SDL supports full screen).

These are the third party libraries I use, and include as submodules:
  • libogg
  • libvorbis
  • libtheora
  • liboggz
  • libfishsound
  • liboggplay
  • libsydneyaudio
You'll need the following system libraries to build:
  • Boost C++ libraries
  • SDL
There's not much error checking, I mostly use 'assert' to bail on an error. I don't want to spend time creating an awesome player, rather something I can use to try test out the way I'm using the third party libraries.

Interestingly with this example player I can play back the problematic HD video I mentioned previously. Part of this is the newer YUV conversion routines in liboggplay and the more lower level optimized SDL libraries for blitting the video data. I'm also not seeing audio buffer underruns which I see with some videos in Firefox. Time to do some investigating!

Categories: ,

Labels:

Monday, March 23, 2009

Very nice web technology demo

This 'glimpse of future web technologies', all in one document is pretty nifty. Lots of different web stuff in there, including rotated videos, fancy CSS, and DFXP captions for videos.

Thanks to Sam Ruby for pointing to Philippe Le Hegaret's work.

Categories:

Labels:

Friday, March 13, 2009

Replacing <video> controls using a bookmarklet

[Update: Chris Blizzard has a neat screencast showing the bookmarklet and player replacement in action.]

There's been some interesting demo's of <video> usage and manipulation discussed in the TinyVid Feedback comments section.

One user, hansschmucker, has done some nice demonstrations of what <video> can offer.

For example, this bookmarklet can take a TinyVid video and replace the player using the built in controls with one written in JavaScript provided by the bookmarklet. The new interface includes seeking, play, pause and volume control. It looks very nice.

To use the bookmarket, visit the bookmarklet page, click on the button that appears there and bookmark the resulting page. Next visit a TinyVid video and visit the bookmarklet. The built in player will be replaced by the new one. This will also work on many pages that use video using the built in controls, for example this transparency camp video.

This example show's it's possible for users to create and customize their own video players and use it on sites of their choice which is pretty nice.

hansschmucker also provided some demo's of using <video> with <canvas> that allow you to view a video in 3D using red/cyan glasses. I've copied his comment from the feedback page below:
=== Using VIDEO as data source for 3D ===
I just wrote a little demo to demonstrate how powerful VIDEO elements can be as data source: This little script (it's 20 lines for god's sake) uses a video (which was also produced via JS by the way, thanks to Radiohead's choice of using simple CSV for the data, the power of Canvas and a bit of base64 decoding) to offset pixels in a randomly generated noise map, saving the original map as red and the altered image as red/blue.

You just have to get out your red/cyan glasses and you'll be able to enjoy the video in 3D. Lots more could be done with proper optimization (in fact, the whole thing may be done as a set of SVG filters) and just a little more attention to detail (random noise is possibly the worst thing you can use for a map), but it's still amazing how easily you can create something amazong with VIDEO.
...
Two more variants: uses a prerendered noise (looks much better) and warps the source image itself which probably makes most sense here.

Categories: , ,

Labels:

Thursday, February 26, 2009

Fallback options for HTML5 video

The HTML5 <video> element only works on a few, often bleeding edge, browsers. For tinyvid I've been only supporting this subset of browsers. Recently I put in support for a simple fallback option for other browsers.

In browsers that don't support <video> the element is ignored. The contents of the element are still processed however. This means any HTML elements within <video>...</video> will display to the user on those browsers. The most basic fallback option is to tell the user that their browser doesn't support video:
<video>
<p>pSorry, your browser doesn't support the video element<p>
</video>
In my blog in the past I've used the video element to display the Theora video, and fallback to a YouTube hosted video if the browser doesn't support it. This was done with code like:
<video src="/video_bling.ogg" controls>
<object width="425" height="350">
<param name="movie"
value="http://www.youtube.com/v/vvtdkxCIKC8"></param>
<embed src="http://www.youtube.com/v/vvtdkxCIKC8"
type="application/x-shockwave-flash"
width="425"
height="350"> </embed>
</object>>/video>
On <video> enabled browsers this uses built in support. On others it uses the Flash player to play the YouTube video.

It's possible to play Theora videos using a Java Applet called Cortado. This can be used as a fallback for users that have Java support but no HTML5 video support:
<video src="test.ogg" controls>
<applet code="com.fluendo.player.Cortado.class"
archive="cortado.jar"
width="320"
height="240">
<param name="url" value="test.ogg"></param>
<param name="duration" value="229.015"></param>
</applet>
</video>
The Cortado applet cannot be used for video's hosted on another domain however. They must be on the same domain as that serving the cortado applet.

These fallback options don't allow creating your own controls with JavaScript and using the nice HTML5 media API. They work best when you only want a single fallback option and use the built-in controls.

There are JavaScript libraries that can be used to emulate the HTML5 media API. Or you can write a simple wrapper to do this yourself. The basic approach is to use <video> in your page and insert a JavaScript file at the top. When loaded this fill searches the document for usages of <video>. It detects what capabilities the browser has (native video support, Flash, Cortado, mplayer/vlc plugin, etc) and replaces the <video> element with the correct HTML code to instantiate the fallback. The JavaScript can then create a wrapper object that emulates the HTML5 API and calls the underlying plugin methods, etc if possible.

Examples of this type of library can be seen in use:I've added very simple fallback to tinyvid using the Cortado applet. If the browser doesn't support <video> it displays a link the user can click to go to a page that uses the applet. The server decides whether to use native video or the applet depending on a URL parameter. Here's a link to a native video player, and the applet version:For the applet version the server queries the Ogg file for the width, height and duration of the video so it can embed that data in the applet parameters. In some cases I can't get the width and height. This is due to some of the videos being stored on backup storage (via Amazon S3) and I can't locally analyze it to get the data. In that case the applet dimensions are set to 320x240.

Categories:

Labels:

Wednesday, January 28, 2009

Daily Motion, OLPC and Theora

A while back it was announced that Daily Motion, an online video site, had opened an OLPC channel for sharing videos encoded using Theora for playback on OLPC's.

The channel, http://olpc.dailymotion.com, contains theora videos aimed at the OLPC audience. What's nice is that the videos playback in Firefox 3.1 using the native Theora support and don't require a plugin, for example this video.

Looking at the Daily Motion page it seems they use the <object> element to playback the Ogg Theora file, which uses the internal decoder and player user interface. This is a nice result from the adding of support for direct loading of Ogg files that Robert worked on.

Categories: , ,

Labels:

Thursday, January 22, 2009

liboggplay playback performance

I made a tweak to tinyvid.tv yesterday to transcode youtube high definition videos if the HD version is available. This results in bigger videos and therefore stresses the performance of the video implementation in Firefox.

I'm not tweaking any parameters when transcoding so it's possible that I could produce a Theora file with better playback characteristics. In particular I don't have the bandwidth to stream a file of this size. Instead I have to wait until a large portion is downloaded before playing it back. But even then playback performance is terrible.

With the file fully buffered on my dual core multi-gigabyte, multi-gigahertz laptop the sound stutters and the playback is slow. Not a great experience.

I tried playback of the ogg file with the example player from liboggplay. The playback performance is exactly the same as within Firefox. No surprise there since I use liboggplay in the implementation.

It's not a limitation with libtheora as the playback using libtheora's example player is very good. Low CPU usage, full framerate, great sound. So it looks to me like it's either a liboggplay issue, or an issue with the way I'm using liboggplay. I've raised a trac ticket with the liboggplay developers to see if they can offer any advice.

I've also raised bug 474540 in the Mozilla bugtracking system to track the fix to apply for Firefox.

Non-HD videos play fine for me, it's when they get to about 720p that things fall apart. The fact that libtheora plays these well makes me confident that we can get the performance for these files much much better.

Categories: , ,

Labels:

Wednesday, January 14, 2009

Open Media Stack Video Specification

Rob Glidden posts that a draft of the open media stack video specification is now available for comment. The specification defines a video decoder and associated bitstream syntax for decoding and playing video streams. From the specification:
The OMS video compression specification is based on legacy royalty-free technology and avoids the recently patented technology.

While this approach sacrifices the performance gains of recently patented techniques, the OMS video compression specification includes innovations of its own that at least partially compensate for these losses.

In addition, the OMS video compression specification takes advantage of contemporary semiconductor technology both to avoid the patented technology that was directed toward specific implementations, and to allow more sophisticated processing.

Where appropriate, patenting the innovations associated with the OMS video compression specification will provide a defensive portfolio of IPR to help maintain OMS as a mainstream royalty-free compression solution.
The specification can be downloaded here.

Categories:

Labels:

Thursday, December 04, 2008

Firefogg: an extension to encode and upload ogg videos

I got an email today about a great new extension that is being worked on to help with sites supporting Ogg Theora videos. Firefogg is a Firefox extension to allow selecting a video, encoding it into Ogg format, and uploading it to a server. Sites supporting this extension can receive videos transcoded to Ogg with all the work to do the transcoding done by the client.

The site has details on the client side JavaScript API as well as source for some server side examples using PHP and Django. The extension itself is open source. I plan to add support for this to tinyvid.tv to enable video uploads in Theora format.

Note that the extension requires a nightly build of Firefox 3.1.

Categories: , ,

Labels:

SRT Subtitles with HTML5 video

j^, the author of many cool open video related projects (including the Theora transcoder ffmpeg2theora) has written a jQuery based library to extract and show subtitles while a <video> is being played.

The jquery.srt page has details and a demo. Embedding code looks like:
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.srt.js"></script>

<video src="http://example.com/video.ogv" id="video" controls>
<div class="srt"
data-video="video"
data-srt="http://example.com/video.srt" />
SRT is a common subtitle format and with this solution you can easily add subtitles to your HTML 5 videos.

Categories: , ,

Labels:

Changes to git mirror of Firefox repository

I've made changes to the way I manage the git repository holding the import of the Firefox source. Since the bulk of the HTML 5 <video> implementation is now in the main Firefox source I no longer maintain the video branch. I'll be removing that and the Firefox mirror soon.

The main Mozilla repositories have been split into two now. They are mozilla-central, which holds the current trunk code and represents Firefox 3.2 (or whatever the next version will be called), and mozilla-1.9.1, which is what will be Firefox 3.1. The details of that split are described in this post.

I now import these mercurial repositories directly into git using git's fast import tool. This gives me complete history and makes things like bisect and blame a bit easier to work with. The history of mozilla-1.9.1 being based on mozilla-central is also retained, as best as I could manage. These imported repositories can be browsed at http://gitweb.bluishcoder.co.nz. They can be retrieved via git:
git clone git://bluishcoder.co.nz/git/mozilla-central.git
git clone git://bluishcoder.co.nz/git/mozilla-1.9.1.git
The way I work with these mirrors is to create a git repository that has these added as remotes. This way I can switch between the two easily, cherry pick patches between them, and do logs and diffs between them. This is how I set that up:
$ mkdir firefox
$ cd firefox
$ git init
$ git remote add mozilla-central git://bluishcoder.co.nz/git/mozilla-central.git
$ git remote add mozilla-1.9.1 git://bluishcoder.co.nz/git/mozilla-1.9.1.git
$ git fetch mozilla-central
$ git fetch mozilla-1.9.1
When working on a bug I create a branch for that bug, based on mozilla-central/master:
$ git checkout -b bug123456 mozilla-central/master
...make changes...
$ git commit -a -m "my changes"
$ git diff -U8 mozilla-central/master >bug123456.patch
...attach bug123456.patch to bugzilla for review...
Once reviewed the patch is then applied to mercurial and pushed. If I want to apply the same patch to test on the mozilla-1.9.1 branch I just use 'git cherry-pick'. To make things like 'git status' a bit less clutted I have the following .gitignore:
$ cat .gitignore
obj-*
*~
.mozconfig
config.cache
config.log
*.pyc
CVS
.mozconfig*
.svn
cvsco.log
cvsco.log.old
NONE
configure
gitchangelog
gitchangelog.bak
.hg
.nss.checkout
*.swp
.gitignore

Categories: ,

Labels:

Wednesday, November 19, 2008

HTML5 video and audio spec changes

Some changes have occurred in the HTML 5 specification related to video and audio (the media portion of the spec). These are nicely outlined on the WHATWG blog.

After my blog post about cross domain restriction of audio and video there was a fairly lengthy discussion on the WHATWG mailing list about whether this should be done. The WHATWG blog entry mentioned previously goes into detail about this. The end result is that, for now, there will be no restriction. The W3C bug on the issue has been updated to reflect this.

Categories: ,

Labels:

tinyvid.tv updates

I did some minor tweaks to tinyvid.tv recently. I added the ability to add comments to video pages, and add feedback on the site. This uses Intense Debate, a third party commenting engine.

On the video front I added a page to return a random video from those available. The video pages themselves show the duration, download progress and current playback position if the browser supports the relevant events. These should work in the latest Firefox nightlies, maybe with the exception of 'download progress'. The latter works well with the patch from bug 464376 applied. That bug should be resolved soon - it just needs tests written and handle the case of local files (those not served over HTTP).

File uploads are not yet supported. For uploads I imagine I will need to work out a way of moderating/screening the uploaded videos. Even without this functionality the site still has almost 200 videos available for testing the HTML 5 <video> element.

Categories: , ,

Labels:

Radio New Zealand's Open Source Web Based Audio Player

Radio New Zealand have released their first open source project. It's an audio player for websites, written in JavaScript. The source is hosted on github. The player uses the <audio> element if the browser supports it.
The first module (available now) is an audio player plugin based on the jQuery JavaScript library, and version 0.1 already has some interesting features.

It can play Ogg files natively in Firefox 3.1 using the audio tag. It can also play MP3s using the same javascript API - you just load a different filename and the player works out what to do. The project includes a basic flash-based MP3 player, and some example code to get you started.


Categories: ,

Labels:

Saturday, November 08, 2008

Video, Audio and Cross Domain Usage

[ Update 19/Nov/2008: This is currently under discussion in the WHATWG and the W3C bug has been marked "won't fix". For now, cross domain usage is fine. ]

This is a heads up for a change to the HTML5 media elements that may break existing usage. If you use <video> or <audio> then you may need to make changes to the way you serve your media resources.

It looks likely the that <video> and <audio> elements will be restricted so that requests for a media resource from a domain other than the page containing the element will be disallowed. There is a W3C bug (<video> and <audio> should prevent cross-origin media loads) about this.

Once the browsers implement this then default usage of cross domain media resources will fail. This includes requests for resources on subdomains, or different ports. So if you have a page on example.com, this will work in the Firefox implementation now, but will break when the change is made:
<video src='http://media.example.com/foo.ogg'></video>
Cross domain usage can be enabled by sending headers along with the file being requested. The header explicitly says 'this resource can be used cross domain'. The protocol for doing this is described in this Access Control document.

If you are hosting your media resource on a third party hosting system you'll need to ensure that they support these headers if you want to display video and audio on a different domain. Programs such as Icecast, used to stream video and audio, will also need to be compatible with the Access Control specification if you want to use the streams with <video> or <audio> from a server on a different host or port.

This unfortunately breaks some common patterns for building sites that allow uploadable media, or separate media files into subdomains, until they (or their software/hosting provider) supports access control. Now would seem to be a good time to request access control support if you're in this situation.

A discussion on this has started on the Theora mailing list. Jonas Sicking has a good summary of the reasons for the cross domain restriction.

Categories: ,

Labels:

Wednesday, October 29, 2008

Recent video updates

Over the last couple of weeks some fixes for video bugs have landed that should improve the playing experience. In particular playback and sound should work on Pulse Audio based Linux distributions. A quick summary of the changes are:
The list of audio and video bugs is available in the Mozilla bugzilla database. Try out <video> usage, exercise the JavaScript API, use sites like tinyvid.tv to test the playback and please report any problems you find.

A couple of additional backends have seen some visible progress as well. Work in progress patches for the WAV backend for <audio> (for all platforms) and a DirectShow backend for Windows are available.

Categories: ,

Labels:

Tuesday, October 21, 2008

HTML 5 Video Element Examples

If you're looking for some sites to play <video> and <audio> using the new elements, I've listed some below. Please add any you know of in the comments:Categories: , ,

Labels:

Monday, October 20, 2008

Video Element Update

Some updates to the video element have landed and are now in the Firefox nightly builds. Bug 449159 was landed which refactors the Ogg backend to improve stability and add seeking support. Some of the changes include:
  • Improved stability. Deadlocks and crashes involving shutdown have been fixed.
  • Improved performance. CPU usage of video playback is better. There were problems that would result in the CPU usage pegging at 100% at times, especially after playback. These have been resolved.
  • After a video has completed playing, calling the play() method, or using the built in controls, will now restart playback.
  • The totalBytes DOM attribute is implemented.
  • Seeking is supported in certain cases. If the playback is from a local file, or a server that supports HTTP byte ranges, then seeking should work. There is no UI yet, but setting the currentTime attribute to a floating point value representing the number of seconds to seek should work. A 'seeking' event is raised when seek starts, and a 'seeked' event is raised when seeking completes.
  • Playback of video and audio would miss a portion of the end of the file. This was especially noticeable on small audio samples. Now files should play back completely.
My video test page has byte range support if you want to test seeking out. Since there's no user interface I recommend using the JavaScript Shell bookmarklet and interact with the video from JavaScript. Here's an example interaction:
var v = document.getElementsByTagName("video")[0];
v.play()
v.addEventListener("seeked", function() { print('seek completed!'); }, true);
v.currentTime=10
Categories: , ,

Labels:

Saturday, September 27, 2008

New Zealand Open Source Awards

The New Zealand Open Source Awards were held in Wellington on Wednesday night. Congratulations to Robert O'Callahan who was nominated as a finalist for the 'Open Source Contributor' category, and won the award on the night!

Also winning an award was Radio New Zealand for 'Open Source use in Government'. Radio New Zealand are doing some great work distributing their content in Ogg Vorbis format as an alternative to MP3. For example, see their 'ninetonoon' page.

Another reason to love Radio New Zealand they are experimenting with the new <audio> functionality currently in the Firefox 3.1 nightly builds. Richard Hulse blogs about the Oggulate script he is writing that converts the links to Vorbis files on the page to buttons that play the Vorbis audio file using the audio element. So if you're looking for some content to give Firefox 3.1 a workout, give Radio NZ's Vorbis support a try.

Categories: ,

Labels:

Thursday, September 11, 2008

JavaScript Space Invaders Emulator

Ajaxian recently posted about a fun JavaScript implementation of PacMan. After spending way too much time on it I wondered how well an emulation of the old arcade game hardware would go in JavaScript.

I've written a few 8080 arcade game emulations before in different languages so I had a go at implementing it in JavaScript. You can try the work in progress at my JavaScript 8080 Emulation page. It runs surprisingly well on modern JavaScript engines.

The page first loads with the arcade game Space Invaders loaded. You can run a set number of instructions, or step through one at a time. It displays the disassembled output. Pressing 'Animate' will run the game in a timer and it can be played. It is a general 8080 arcade game emulator, for the games that use similar hardware to Space Invaders. The buttons at the top load the code for Space Invaders, Lunar Rescue or Balloon Bomber.

If you have a bleeding edge version of Firefox 3.1 with Ogg Vorbis <audio> support, pressing the 'Enable Audio' button will enable sound. The sound support uses <audio> to play the samples when requested by the emulator. This turns out to make a good test case for my audio support and it may need the fixes from bug 449159 and 454364 to work.

If you're interested in the other emulators I've done:The implementation does not get traced with the TraceMonkey tracing JIT yet. I'll look into the reasons why and as TraceMonkey and my implementation improves it'll get faster I'm sure. Even so, it runs very close to full speed.

This implementation uses Canvas, audio for sound and should work on browsers with a fast JS engine and these technologies.

For the emulator loop I run a set number of instructions during a timer that is run via 'setInterval' to prevent the 'script is running too long' message. One thought that Robert O'Callahan suggested was to run the emulator in a worker thread and have communication for input/output via messages to the browser. I'll play with this idea and see how it goes - it'll give me a chance to try out Firefox 3.1 worker threads implementation.

The emulator can be run (without the GUI) from a JavaScript shell for testing purposes. I used the shell to test the implementation by running my Factor version and logging all the state of the emulated CPU, doing the same with the JavaScript version, and making sure the output was the same.

Although it's not quite perfect, it's currently playable, and shows that the types of games that are written as Java or Flash applets can be done in standard HTML and JavaScript in the latest browsers.

Categories: ,

Labels:

Thursday, July 31, 2008

Theora Video Backend for Firefox Landed

It was announced at the Firefix Plus summit today that Firefox will include native Theora and Vorbis support for the HTML 5 media elements. So <video> and <audio> will support those codecs built into Firefox itself. Chris Blizzard posted about this earlier.

The backend has been committed to the main Mozilla source code and is enabled by default. You can download nightly builds and test it out. An example of a live site that uses <video> is the Wikimedia video archive.

This original commit is a work in progress. There are unimplemented bits, bugs, etc that need to be sorted out. But it's a start towards using a common codec across all platforms and will improve as we get towards the 3.1 release.

In other news, getting out of Whistler, where the summit is being held, is somewhat of an issue at the moment...

Categories: ,

Labels:

Thursday, July 10, 2008

Video Bling

Robert O'Callahan has been posting about his 'bling branch' which contains some very nice effects. See his blog posts for more detail:As soon as I saw these I had to try the effects with a playing video. The video patches apply fine to the svg-integration branch.

The screencast below is from this special build, displaying a video played using <video>. There is a reflection below it using the tricks from Robert's posts. Shortly after that I change to a video playing that uses an SVG filter for edge detection when I mouse over the video. Finally there is a video with both effects combined.

You can download the video_bling.ogg file to play, or if you have a <video> enabled browser you can see it below. I've also uploaded it to YouTube.



Categories: ,

Labels:

Wednesday, July 09, 2008

The Video and Audio element patch has landed

The patches in bug 382267 to add support for the WHATWG video and audio elements have been applied to the Firefox mozilla-central repository.

This means you can get the source for Firefox and build it with support for <video> and <audio> by using the configure flag '--enable-media'. Currently the media support is disabled by default so it won't appear in the nightly builds. At some point this will be changed and it will be enabled by default.

The patch that has landed does not yet include a backend decoder. It won't play any videos as a result. That will be fixed when the various backends are landed:
In the meantime you can apply the patches from those bugs to the mozilla-central source to get a video build that decodes video.

Categories: , , ,

Labels:

Tuesday, May 27, 2008

Firefox HTML5 video and audio update

A week or so ago I updated the linux build of the gstreamer based HTML5 video implemention with some fixes that make it work nicely with the public sites using <video>. This includes wikimedia and metavid. Video's on those sites with that build show a much better user experience than previous builds.

Two new backends are in progress by other Kiwi Mozilla team members. A DirectShow backend for Windows being developed by Chris Pearce and a QuickTime backend for Mac OS X being developed by Matthew Gregan.

The git repository has been updated to include the start of an <audio> element implementation. Currently only the gstreamer backend has that support. Audio plays but there is no support for the 'controls' attribute and therefore no user interface yet. You can build your own with JavaScript though.

The git repository is based on regular imports of the Mozilla CVS repository. This repository tracks Firefox 3 which is close to being released so updates to CVS are few and far between. The video/audio work will not be in Firefox 3. The plan is to have them in a release soon after, scheduled for around the end of the year. If all goes well this will have backends for gstreamer, DirectShow and QuickTime for the relevant platforms.

The Mozilla repository for this release is currently a Mercurial repository called mozilla-central. I've not yet migrated my work over to this repository but will do so soon. My Firefox git repository now has a branch containing a regular import of the mozilla-central mercurial repository. Moving over to mozilla-central should just be a simple case of merging that branch into the video work.

The bugzilla bugs with the patches for this work will be updated in the next day or so. I wanted to get the refactoring for the <audio> element done before updating them for review.

Categories:

Labels:

Wednesday, May 21, 2008

Tamarin Documentation

There have been some great weblog posts recently about the internals of Tamarin. If you're interested in more details about Tamarin, try the following:
For the brave there is an implementation of the Tamarin engine that runs as a scripting engine within Internet Explorer. Called 'Screaming Monkey' it is actually working and an alpha release is available. Screaming Monkey is based on the Tamarin Central engine, rather than the Tamarin Tracing engine.

Categories:

Labels:

Extending Tamarin Tracing with Forth

My previous article on extending Tamarin Tracing with native methods described how to implement the native methods in C. It's also possible to implement native methods in Forth.

Methods implemented in JavaScript are compiled to ABC bytecode by a compiler (currently the asc.jar provided by the Flex SDK). These are compiled to the basic Forth instructions by the Tamarin Tracing engine and those Forth instructions are run by the interpreter. 'Hot' areas of the Forth instructions are traced and compiled to native machine code as needed.

Methods implemented in Forth don't need to be compiled from ABC to Forth. They are immediately available for interpreting and JITing via the tracing mechanism. I'm a little unsure of the exact pros and cons of implementing things in Forth vs C vs JavaScript and would be interested in comments if anyone can help.

As an example of a method in Forth I'm going to use the same fibonacci function in my previous article. A Forth method is marked 'native' just like a method implemented in C. But it has some metadata associated with it to say it is implemented in Forth, and to give the name of the Forth word (in Forth terminology a 'word' is a 'function'):
package testing {
public function fib(n) {
if( n <= 1)
return 1;
else
return fib(n-1)+fib(n-2);
}

public native function fib2(n:int):int;

[forth(word="forth_fib3")]
public native function fib3(n:int):int;
}
Notice the 'forth(word="forth_fib3")' metadata annotation. This tells the asc.jar compiler that the following native function is implemented in Forth by the word 'forth_fib3' rather than in JavaScript or C. I placed this code in 'fib.as' in the 'shell' subdirectory and modified 'shell.py' to build it in exactly the same manner as my previous article.

The 'forth_fib3' word needs to be written. The Tamarin Tracing Forth compiler is implemented in 'utils/fc.py'. It is a 'whole program' compiler in that it needs to have all Forth files listed on the command line so it can analyse and compile everything. The invocation of this compiler is done in 'core/builtin.py'. This means any Forth extensions really need to be added to the 'core' subdirectory and build files. I added a 'core/fib3.fs' as follows:
: fib3 ( n -- n )
DUP 1 <= IF DROP 1 ELSE DUP 1 - fib3 SWAP 2 - fib3 + THEN ;

EXTERN: forth_fib3 ( obj n argc=1 -- int )
DROP NIP fib3 ibox ;
The 'forth_fib3' word is implemented using EXTERN:. This marks it as a word that is an available entry point by external code. The arguments it receives on the stack will be ( obj arg1 arg2 argn argc=n -- result ). In the fibonacci case there is one argument, the number passed to fib. The argc argument is the count of the number of arguments provided, in this case 1. The bottom argument on the stack is the object the method is called on. Since our fib function is 'global' and not part of an object this is not used hence the NIP to remove it.

Note that the stack effect names (the obj, n, argc=1, etc) are for documentation purposes and are not used by the compiler at all - just like most other Forth systems).

So 'forth_fib3' removes the argc and obj arguments and uses just 'n'. It calls a helper function 'fib' which does the actual fibonacci calcuation, leaving the result of that on the stack. The call to 'ibox' tags the final result as an integer number.

'fib' is a pretty standard Forth implementation of fibonacci. It uses IF/ELSE/THEN to do the testing of the number. IF/ELSE/THEN is implemented by the Forth compiler directly (fc.py) since the Tamarin Tracing Forth system doesn't have parsing words.

'core/builtin.py' needs to be modified to include 'fib3.fs' as an argument to the compiler:
 os.system("../utils/fc.py -c vm_fpu prim.fs fpu.fs vm.fs e4x.fs fib3.fs")
There are multiple invocations of the compiler for different variants of the virtual machine (without fpu, minimal VM, full VM, etc). Each of these should be changed.

Running 'core/builtin.py' will compile the Forth code and generate the necessary code. Follow this up with running 'shell/shell.py' to compile the fib.as and other code and build Tamarin Tracing as per my previous article.

Some simple test code:
import testing.*;
print("fib3 30 = " + fib3(30));
With equivalent test functions for the other implementations of fib you can compare the different runtimes:
$ time shell/avmshell fib.abc
fib 30 = 1346269

real 0m0.298s
user 0m0.252s
sys 0m0.032s

$ time shell/avmshell fib2.abc
fib2 30 = 1346269

real 0m0.063s
user 0m0.024s
sys 0m0.028s

$ time shell/avmshell fib3.abc
fib3 30 = 1346269

real 0m0.192s
user 0m0.144s
sys 0m0.024s
As can be seen in the times the C implementation smokes the other two with the Forth code being faster than the JavaScript code.

The Forth implementation has features I haven't explored in this article. This includes different ways of declaring words to take advantage of various optimisations and automatic generation of superwords. It is a 'static' Forth compiler in that it doesn't allow the execution of Forth words at parse or compile time so features like parsing words, CREATE DOES>, interactive development, etc are not available. This makes the implementation of Forth words a bit more verbose than in more full featured Forth implementations.

If you have any tips on using Forth in Tamarin Tracing please leave a comment. I'm keen to see more features of the Forth system is use.

Categories: ,

Labels:

Sunday, April 20, 2008

SVG Animation Update

I've made a couple of quick fixes to the SVG Animation patch I mentioned earlier. The fixes are:
  • Implements the <set> element
  • Gets the 'discrete' calcMode working
  • Gets setCurrentTime working
This should allow more SMIL Animation examples to work. These changes also happen to make the SMIL parts of Acid 3 work. Well, sort of. Tests 75 works, but test 76 fails intermittently. If I do a full refresh of the Acid 3 page it works. I've no doubt got something wrong in the setCurrentTime implementation. Builds:This is probably about all the time I can spend on looking at SMIL for now, apart from minor tweaks, but the original author of the patch commented in my last post that they were interested in resuming work on it. I hope that's the case - It'd be great to see this completed.

Categories: ,

Labels:

Saturday, April 19, 2008

Firefox SVG Animation Patch

I wanted to try out an SVG Animation example that someone sent me but Firefox currently doesn't have support for this. Bug 216462 contains a patch with a work in progress implementation of SVG Animation so I looked into trying it out.

Unfortunately it has suffered some bitrot recently so I made the changes to get it to apply on the current CVS. There were some problems causing crashes which I fixed up and I've updated the bug with the adjusted patch.

Jeff Schiller commented previously in the bug about wanting to try it out but not being able to build so I made my builds available for him to play with. His latest blog post mentions the results of trying the build out. Thanks for giving it the run through Jeff!

If you want to have a play with a build with the SMIL patch applied, you can try my experimental builds:These also have the video patch applied.

The original author of the patch has a status page and a page of tests that can be used to try it out. I don't know how up to date those are with regards to the current version of the patch though.

On the video front, I've updated some of the binary video builds to fix some bugs exposed by the excellent Metavid site - a <video> early adopter.

Categories: , ,

Labels:

Monday, April 14, 2008

Seaside with XUL

Pavel Krivanek posted to the Seaside mailing list about generating XUL from Seaside. XUL is the user interface markup language that Firefox and applications based on XULRunner use for the user interface. Seaside is a continuation based web framework written in Smalltalk.

Using this combination they get native look and feel for the platform for Smalltalk applications.

Categories: , ,

Labels:

Friday, April 04, 2008

HTML5 Video on the N810

The Nokia Internet Tablet's use GStreamer for the multimedia functionality. Chris Blizzard built Mozilla on the Nokia N810 with the GStreamer video backend patch applied.

He has a video on his weblog showing the N810 playing video inside Mozilla. Nice!

Categories: , ,

Labels:

Thursday, April 03, 2008

Firefox HTML5 Video with GStreamer

I wrote earlier about refactoring the HTML5 video patch to allow different backends. I've now added a patch for GStreamer support to bug 422540.

To build a Firefox with this support, get the latest trunk CVS source. Apply the patch from bug 382267, followed by the patch from bug 422540. Build with the --enable-gstreamer configure option. Or follow these steps to build from the git repository.

I've made a prebuilt binary for Linux which you can download from firefox-3.0pre-video.en-US.linux-i686.tar.bz2.

With this build, when you visit a page that uses <video> it will use the GStreamer codecs available on your system to decode and play them. The Theora examples on my test page continue to work, and I've also tested it decoding H.264 videos in .mp4 and .mov files, using the Fluendo plugins.

This is a first cut at support and still needs some work but it plays quite well so far. Seeking works within files but there is no built in controls for it yet. You can write JavaScript that changes the 'currentTime' property of the video element DOM object to move forward and back in the video.

Categories: , ,

Labels:

Friday, March 14, 2008

Building the Firefox video element backends

The git repository has multiple branches which means a little more git usage is needed to get at the Ogg (and soon, GStreamer) backends.

When you clone or pull from the repository you'll automatically pick up the commits for the new branches. You can see these branches with 'git branch -r':
$ git clone git://double.co.nz/git/video.git
$ cd video
$ git branch -r
origin/HEAD
origin/master
origin/ogg
By default the 'master' branch has a checked out working tree. Building this produces <video> element support without any decoder. One approach to building the Ogg backend is to create a local branch with the commits from the remote branch origin/ogg and build that:
$ git checkout -b ogg origin/ogg
Switched to a new branch "ogg"
$ cd mozilla
$ cat >.mozconfig << "EOF"
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
mk_add_options MOZ_CO_PROJECT=browser
ac_add_options --enable-video
ac_add_options --enable-ogg
EOF
$ autoconf2.13
$ make -f client.mk build
When the gstreamer branch is added you can do similar:
$ git checkout -b gstreamer origin/gstreamer
Switched to a new branch "gstreamer"
$ cd mozilla
$ cat >.mozconfig << "EOF"
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
mk_add_options MOZ_CO_PROJECT=browser
ac_add_options --enable-video
ac_add_options --enable-gstreamer
EOF
$ autoconf2.13
$ make -f client.mk
You can do a build which contains both backends by merging. You'll need to fix any merge conflicts and change the ordering of the codec instantiation so that one is preferred over the other when they can both handle the same codec. A merge looks like this:
$ git checkout -b gst_and_ogg origin/master
$ git merge origin/ogg
$ git merge origin/gstreamer
$ cd mozilla
$ cat >.mozconfig << "EOF"
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
mk_add_options MOZ_CO_PROJECT=browser
ac_add_options --enable-video
ac_add_options --enable-ogg
ac_add_options --enable-gstreamer
EOF
$ autoconf2.13
$ make -f client.mk
To update your branches with the latest code from the repository, fetch and merge:
$ git fetch origin
$ git checkout ogg
$ git merge origin/ogg
$ git checkout gstreamer
$ git merge origin/gstreamer
$ git checkout gst_and_ogg
$ git merge origin/ogg
$ git merge origin/gstreamer
Once you've done a successful build you can run it from the obj-dir, or make an installer:
$ cd mozilla
$ make -f client.mk build
$ export NSPR_LOG_MODULE=nsVideoDecoder:5
$ export MOZ_NO_REMOTE=1
$ ./obj-i686-pc-linux-gnu/dist/bin/firefox -ProfileManager
$ make -C browser/installer
When the installer is built there will be a file in the 'dist' subdirectory of the object directory that contains the build. It will be a .tar.gz, .zip or .dmg, depending on the platform. The environment variables set in the above example enable logging of the video debug output and running Firefox even if an existing copy is already running.

Categories: , ,

Labels:

Thursday, March 13, 2008

Refactoring of Firefox HTML video element patch

I've making some changes to the patch implementing the HTML <video> element for Firefox.

Previously the patch attached to the bug contained the implementation of the video element and a Theora backend implemented using liboggplay.

This required pulling in a large amount of third party library code making it more difficult to get reviews for the patch and to get feedback on the changes required for the video element itself. To hopefully fix this I'm changing bug 382267 to only include the video element implementation with no video decoders.

Applying the patch from that bug to the Firefox trunk source and building with the --enable-video option will provide support for <video> but all videos will fail to load due to not having a supported decoder. This is obviously not very useful but will hopefully make the patch easier to get reviewed and landed.

I've added bug 422538 which will contain support for decoding Theora videos. This is the code taken out of the original bug. Applying the patch from this bug on top of the video element patch and building with --enable-ogg will give <video> element support that can play Theora videos.

As part of the refactoring I've made it easier for different video decoder backends to be implemented. There is an abstract class with pure virtual methods that can be inherited from. I have a 'work in progress' implementation of a GStreamer backend that I'll write about later and attach a patch to bug 422540. This backend allows playing any video format supported by GStreamer. I've tested it with the Fluendo H.264 decoder and it plays fine. Keep an eye on the bug if you're interested in this.

An advantage of this refactoring is that embedders of Gecko can choose a video backend that suits their purposes (for legal or other reasons they may choose not to have support for a particular format), or implement their own.

Currently the interface for implementing video decoder backends is a C++ abstract class. I'm considering using XPCOM so that implementations can be built and dynamically loaded. This could allow extensions to be written containing video backends that will work with the <video> element. One of the questions asked of me at the W3C video on the web workshop was whether third party providers of DRM enabled codecs could leverage the <video> element. This could provide a pathway to doing that.

The git repository will be restructured so that the 'master' branch contains the <video> element support only. The 'ogg' and 'gstreamer' branches will contain the implementations of the backends.

Categories: , ,

Labels:

Wednesday, February 27, 2008

Building the ActionScript compiler for Tamarin

Adobe have released the Flex SDK as open source. The trunk of the SDK repository contains the source to the ActionScript compiler and the GlobalOptimizer component that can be used for generating code used in Tamarin Tracing. To build the asc.jar required by Tamarin, first get a copy of the current source, change to the 'modules/asc/build/java/' directory and run 'ant' to build 'asc.jar':
$ svn checkout http://opensource.adobe.com/svn/opensource/flex/sdk/trunk flex-sdk
$ cd flex-sdk/modules/asc/build/java
$ ant clean main
The generated 'asc.jar' file will be in '/modules/asc/lib/asc.jar' and should be copied to the 'utils' directory of Tamarin Tracing to be picked up by the build tools.

Categories:

Labels:

Wednesday, February 13, 2008

Implementing Native Methods in Tamarin Tracing

[ 2008-05-20: Minor update to get things working with latest Tamarin Tracing code, and updated times for test runs ]

Tamarin Tracing can be extended by creating native methods. These are methods of a class where the implementation is in C rather than JavaScript.

For this example I'll use a native implementation of the fibonacci function and compare it to the JavaScript version in my previous post.

A JavaScript function that is implemented in C using the 'native' modifier in the JavaScript source. For example, a natively implemented 'fib' function would be declared in JavaScript as:
public native function fib(n:int):int;
Notice that this includes the type of the arguments and the return type. This is so the compiler can produce the correct C types in the C stub code it generates.

The native method must be implemented in C and linked into the final executable. The name of the function is in the following form:
[class]_[visibility]_[name]
In this fib example there is no class, so 'null' is used for that part of the name and visibility is public so that part of the name is left out. The end result is a native C function called null_fib needs to be implemented.

As part of the compilation process the compiler generates a C structure that will be accessed by the native implementation to extract the arguments passed to it. This structure looks like:
struct null_fib_args
{
public: ScriptObjectp /*global1*/ self; private: int32_t self_pad;
public: int32_t n; private: int32_t n_pad;
public: StatusOut* status_out;
};
The 'n' field of the structure is the argument passed from JavaScript callers. The native implementation, which we need to write, looks like this:
  int32_t native_fib(int32_t n) {
if(n <= 1)
return 1;
else
return native_fib(n-1)+native_fib(n-2);
}

AVMPLUS_NATIVE_METHOD(int32_t, null_fib)
{
return native_fib(args->n);
}
First there is the native_fib C function that we want to call from JavaScript. The AVMPLUS_NATIVE_METHOD macro is used to declare the wrapper function that implements the 'native function fib' we declared in the JavaScript file. This receives an 'args' object that is an instance of the null_fib_args C structure mentioned previously. This is used in our example to extract the passed integer value and call the native C function and return the result.

Native function implementations must be linked into the tamarin tracing executable. It's not possible to compile a JavaScript file containing a native declaration and run it using the tamarin tracing 'avmshell' program. To integrate the fib code into 'avmshell' I modify the shell code to compile and link in the native implementation. We can then write JavaScript code that calls it and run it with 'avmshell'.

The first thing to do is write the JavaScript side of the 'fib' code. In a 'fib.as' file in the 'shell' directory of tamarin tracing I have the following code:
package testing {
public function fib(n) {
if(n <= 1)
return 1;
else
return fib(n-1) + fib(n-2);
}

public native function fib2(n:int):int;
}
This provides a JavaScript implementation of fibonacci and one called 'fib2', intended to be implemented with C code so I can compare the speed.

This file needs to be compiled to abc bytecode and have the args structure generated in a C header file. There is a script, shell.py, in the 'shell' subdirectory that does this for the other avmshell classes. Changing the line following the comment 'compile builtins' so it includes the 'fib.as' file just created will result in it being included in the build.

What this line in shell.py does is compile the JavaScript files using the Flex SDK compiler (See later about where to get this and where to put it). The command it runs is something like:
java -jar asc.jar -import builtin_full.abc ... fib.as
This produces the abc bytecode for our fibonacci code, as outlined in my previous post.

The next command run by 'shell.py' is the Flex Global Optimizer. This takes all the abc bytecode files for the shell, optimizes them, and produces a C header and implementation file. It is these C files that contain the generated arguments structure, and the implementation file actually contains a C array of the optimized bytecode. The output of this step will be compiled by a C compiler and linked into the 'avmshell' executable.

The native C implementation of the 'fib2' function should be placed in a file in the 'shell' subdirectory and that file added to the 'manifest.mk' makefile. The contents of this file for this example is:
#include "avmshell.h"
#include <stdlib.h>

namespace avmplus
{
int32_t native_fib(int32_t n) {
if(n <= 1)
return 1;
else
return native_fib(n-1)+native_fib(n-2);
}

AVMPLUS_NATIVE_METHOD(int32_t, null_fib2)
{
return native_fib(args->n);
}
}
I called this 'fibimpl.cpp' and added it to manifest.mk. You'll see in the 'shell' subdirectory various implementations of native methods in [foo]Class.cpp files, where [foo] is the JavaScript class being implemented. There are also [foo].as files which have the JavaScript side of the implementation.

To build our new 'avmshell' which is able to call our native fibonacci implementation, run 'shell.py', and do the configure and make steps as outlined previously:
$ mkdir mybuild
$ cd mybuild
$ ../tamarin-tracing/configure --enable-shell
$ make
I wrote two simple test files to test the 'fib' and 'fib2' functions:
$ cat fib.as
import testing.*;
print("fib 30 = " + fib(30));
$ cat fib2.as
import testing.*;
print("fib 30 = " + fib2(30));
Here are some simple timings on my machine with the tracing jit enabled and disabled:
$ time ./shell/avmshell fib.abc
fib 30 = 1346269

real 0m0.417s
user 0m0.384s
sys 0m0.020s
$ time ./shell/avmshell fib2.abc
fib 30 = 1346269

real 0m0.092s
user 0m0.060s
sys 0m0.020s

$ time ./shell/avmshell -interp fib.abc
fib 30 = 1346269

real 0m7.496s
user 0m7.448s
sys 0m0.004s
$ time ./shell/avmshell -interp fib2.abc
fib 30 = 1346269

real 0m0.070s
user 0m0.060s
sys 0m0.004s
Another way of extending tamarin tracing is via forth. I'll cover that in a later post.

I mentioned earlier about needing the Flex ActionScript compiler and global optimizer from their asc.jar file. Unfortunately tamarin tracing needs a bleeding edge version of this to generate the correct C code. A recent version can be obtained from Mozilla public ftp. This should be placed in the 'utils' subdirectory to be picked up by the scripts. Even more unfortunately this version is out of date for the latest mercurial repository code. Hopefully this situation will be rectified soon, but in the meantime you can go back to changeset 302 from the mercurial repository. I tested the current asc.jar against that.

There are some interesting things from the Tamarin summit about the generated arguments structure. You'll notice it has some padding fields in it. When the native implementation function is called from Forth, the layout of the Forth stack looks like (in Forth stack format):
( obj arg1 ... argn status -- )
Each value on the Forth stack is a 64 bit value. The generated structure type exactly matches the Forth stack layout.

This means that when the Forth stack is ready for the native call, the argument object is actually a pointer to a location on the stack. There is no intermediate argument object actually allocated. The padding fields are to enable exactly matching up with the items on the stack.

Interestingly, if I recall correctly from the Tamarin summit, calling native methods from the tracing jit is actually less efficient than calling it from the interpreter. This is because the interpreter uses the stack layout trick for the arguments object above. But for the tracing jit the argument values are often stored in registers or other memory locations. These must be copied into an arguments object and then the native function called. This is a slight overhead.

Please feel free to leave a comment or email me if you have any questions or corrections to the above. It represents my understanding from attending the summit and playing with the code and may not necessarily be the best way of doing things, or may be incorrect in places.

Categories: ,

Labels:

Sunday, February 03, 2008

A Quick Introduction to Tamarin Tracing

[ 2008-05-20: Fixed some breakage due to changes to the latest Tamarin Tracing source, and updated more recent timing ]

I attended the Tamarin Tech summit at Adobe on Friday. My main interest for attending was to learn more about the tamarin-tracing project. The goal of Tamarin is to produce a high performance ECMAScript 4 implementation.

'Tamarin Tracing' is an implementation that uses a 'tracing jit'. This type of 'just in time compiler' traces code executing during hotspots and compiles it so when those hotspots are entered again the compiled code is run instead. It traces each statement executed, including within other function calls, and this entire execution path is compiled. This is different from compiling individual functions. You can gain more information for the optimizer to operate on, and remove some of the overhead of the calls. Anytime the compiled code makes a call to code that has not been jitted, the interpreter is called to continue.

Apparently the JIT for Lua is also being written using a tracing jit method and a post by Mike Pall describes the approach they are taking in some detail and lists references. A followup post provides more information and mentions Tamarin Tracing.

'Tamarin Tracing' is open source and can be obtained from the mercurial repository:
$ hg clone http://hg.mozilla.org/tamarin-tracing/
To build the source you create a directory to hold the build files, change to it, and run the configure script:
$ $ mkdir mybuild
$ cd mybuild
$ ../tamarin-tracing/configure --enable-shell
$ make
The 'enable-shell' option is required to produce the 'avmshell' binary that executes the bytecode. At the end of the build you'll see the avmshell binary in the shell subdirectory:

$ shell/avmshell
avmplus shell 1.0 build cyclone

usage: avmplus [options] scripts [--] script args
-Dtimeout enforce maximum 15 seconds
execution
-error crash opens debug dialog,
instead of dumping
-suppress_stdout don't emit anything to
stdout (debug messages only)
-interp disable the trace optimizer
and nanojit
-Dnoloops disable loop invariant hoisting
-Dnocse disable common subexpression
elimination
-Dnosse disable SSE2 instructions
-log send verbose output to
<script>.log
'avmshell' operates on files containing bytecode not JavaScript. To use it you'll need to have a front end that compiles JavaScript to the 'abc' bytecode format it uses. The bytecode is the ActionScript bytecode. You'll need a compiler that generates this. This can be obtained from the Flex SDK. This is a free download from Adobe. You can also use any other tool that generates the correct bytecode.

Included with Tamarin Tracing is the source for 'esc'. This is a work-in-progress implementation of an ECMAScript 4 compiler written in ECMAScript. It generates the 'abc' bytecode but is (I think) not quite ready for prime time. In this post I'm using the 'asc' compiler from the Flex 2 SDK on Linux. This compiler is written in Java and is in the 'lib/asc.jar' file in the SDK.

A quick test that the avmshell program works:
$ echo "print('hello world!');" >>hello.as
$ java -jar asc.jar hello.as
hello.abc, 86 bytes written
$ shell/avmshell hello.abc
hello world!
'avmshell' has a number of debugging options that are only available when configuring the build with '--enable-debugger'. This allows you to get some information about the trace jit. Here's the build process with a debug enabled build and the available options:
$ mkdir mybuild
$ cd mybuild
$ ../tamarin-tracing/configure --enable-shell --enable-debugger
$ make
$ shell/avmshell
avmplus shell 1.0 build cyclone

usage: avmplus [options] scripts [--] script args
-d enter debugger on start
-Dnogc don't collect
-Dgcstats generate statistics on gc
-Dnoincgc don't use incremental collection
-Dastrace N display AS execution information,
where N is [1..4]
-Dverbose trace every instruction (verbose!)
-Dverbose_init trace builtins too
-Dverbose_opt_exits trace optimizer exit instructions
-Dverbose_opt_detail extreme optimizer verbosity
-Dquiet_opt disable verbosity for optimizer
-Dstats display various optimizer
statistics
-Dsuperwords dump basic block usage to stderr
(use with -interp;
2> to save to file, then
superwords.py)
-Dtimeout enforce maximum 15 seconds
execution
-error crash opens debug dialog, instead of
dumping
-suppress_stdout don't emit anything to stdout
(debug messages only)
-interp disable the trace optimizer and
nanojit
-Dnoloops disable loop invariant hoisting
-Dnocse disable common subexpression
elimination
-Dnosse disable SSE2 instructions
-log send verbose output to
<script>.log
To demonstrate some of the output I'll use a simple fibonacci benchmark. This is the contents of fib.as:
function fib(n) {
if(n <= 1)
return 1;
else
return fib(n-1) + fib(n-2);
}

print("fib 30 = " + fib(30));
A comparison of times with and without the tracing jit enabled:
$ time ./shell/avmshell -interp fib.abc
fib 30 = 1346269

real 0m7.550s
user 0m7.504s
sys 0m0.004s
$ time ./shell/avmshell fib.abc
fib 30 = 1346269

real 0m0.391s
user 0m0.360s
sys 0m0.016s
A complete verbose log is very large and shows the execution of the program, the trace and the assembly code generated:

$ shell/avmshell -Dverbose fib.abc
...
interp global$init()
0:getlocal0
1:pushscope ( global@20c1e61 )
2:newfunction method_id=0
4:getglobalscope
5:swap ( Function-0 global@20c1e61 )
6:setslot 1 ( global@20c1e61 Function-0 )
...
interp ()
0:getlocal1
1:pushbyte 1
3:ifnle 10 ( 30 1 )
10:getglobalscope
11:nop
12:getlocal1
13:pushbyte 1
15:subtract ( 30 1 )
16:callproperty {public,fib.as$0}::fib 1 ( global@20c1e61 29 )
...
10:getglobalscope
11:nop
12:getlocal1
13:pushbyte 1
15:subtract ( 28 1 )
16:callproperty {public,fib.as$0}::fib 1 ( global@20c1e61 27 )
interp ()
SOT pc 107D148 ip D9DD5 sp 10100FC rp 10082E4
trace 4314 (10DA000)
1 in ecx
3 int #20D8940
4 arg 3
5 arg 1
6 call fragenter
reference to rp
7 imm #16
8 ld 7(1)
...
GG: pc 107D148 ip D9DD5 sp 101010C rp 100832C
assembling pass 1 from 4311:62
1 in ecx
010DF786 mov ecx,-4(ebp) ecx(1)
3 int #20D8940
4 arg 3
010DF789 mov edx,34441536 ecx(1)
5 arg 1
6-call fragenter
010DF78E call 2E96E:fragenter
010DF793 mov ecx,-4(ebp) ecx(1)
7 imm #16
8-ld 7(1)
010DF796 mov edi,16(ecx) ecx(1)
010DF799 mov -12(ebp),edi ecx(1) edi(8)
...
There's a lot of other interesting stuff in the Tamarin Tracing source that I hope to dive into. For example:
  • the interpreter is written in Forth. There are .fs files in the 'core' subdirectory that contains the Forth source code. Each 'abc' bytecode is implemented in lower level instructions which are implemented in Forth. The tracing jit operates on these lower level instructions. The system can be extended with Forth code to call native C functions. The compiler from Forth to C++ is written in Python and is in 'utils/fc.py'
  • The jit has two backends. One for Intel x86 32 bit, and the other for ARM. See the 'nanojit' subdirectory.
  • The complete interpreter source can be rebuilt from the Forth using 'core/builtin.py'. This requires 'asc.jar' to be placed in the 'utils' subdirectory of Tamarin Tracing.
At the summit there was an in-depth session of the internals of the Forth code and how to extend it. I'll write more about that later when/if I get a chance to dig into it.

Categories: ,

Labels:

Friday, February 01, 2008

Linux support for video element patch updated

I've updated the git repository to improve the support for Linux. I've uploaded a new build, which can be obtained from the test page or downloaded here.

This is the first build using the ALSA backend that I posted about previously. The sound playback is much improved over previous Linux builds, and so is the a/v sync - as long as you have a fast enough connection that the download doesn't starve. There is still no initial buffering in this build.

I tweaked the git repository import so that the details for each git commit includes the CVS commit messages for the CVS commits contained within it. That should help track down regressions or problems to their original CVS commit.

Categories:

Labels:

Monday, January 28, 2008

Face Painting, Flying and Flat Tires

Flat Tire
I flew into San Francisco this morning for a Mozilla 'work week'. I'm here until the 6th of February. The flight was good, and the weather wasn't too bad. Until it decided to rain just as my rental car got a flat tire. The week can only improve!

Apart from that little mishap, this weekend was very good. I was down in Wellington on Saturday for some brilliant weather and a birthday party for my nephew Jesse who has turned three. Bouncy castles, face painters, barbecue, and Tricky the Clown. Rick/Tricky did very well to keep the kids entertained with some great magic tricks and best of all (I may be biased...) Tricky plays the ukulele. I may have to be a bit more careful at answering the challenge to be face painted...

Categories:

Labels:

Friday, January 11, 2008

Video Element Progress

I thought it might be time for an update on the progress of the <video> element support for Firefox.

Unfortunately on Christmas Eve my Macbook hard drive crashed losing everything on it. Fortunately I push my repository to a remote server regularly. Until I get a new drive and the Macbook set up again I'm using another laptop dual booting Linux and Windows. This has given me the chance to work on getting the Linux version of <video> support a lot better.

The main problem with the Linux version was with the sound implementation. The Linux backend of sydney audio uses OSS and has problems with locking /dev/dsp, is unable to return information I need for a/v sync, and didn't have any volume control support. There is a partial implementation of ALSA support for sydney audio included with liboggplay but it was nowhere near complete, and used an older API interface.

I've implemented a new ALSA backend for the library and now have sound playing much better on Linux. Volume control works and sound playback is generally better. I'm adding support for returning the information required for a/v sync now. Once this is done Linux playback should be as good as that on the Mac and Windows builds.

After that I'll be updating the Windows audio backend so volume control and a/v sync information works with that as well.

I'm still using an older libtheora library. During my work on the theora playback a new version of the library was released. This has many improvements but does not have optimized assembler routines for the Microsoft Visual C++ compiler, which is what we use to build Firefox on Windows. Nils Pipenbrinck has submitted a patch to theora which adds MSVC compatible optimized assembler routines. Once that hits the main codebase I'll be moving to the new version and see how it goes.

I'll have a patch pushed to the git repository with the recent Linux changes as soon as I confirm it still builds and runs on the other platforms.

Categories: ,

Labels:

Tuesday, January 08, 2008

Building Firefox from the git repository

I've made a small change to the git repository containing the Firefox source that I've been maintaining. I've removed the 'configure' file from the git repository. This will need to be regenerated before you can build the source using autoconf2.13. The steps to build Firefox from git become something like:
git clone git://double.co.nz/git/firefox.git
cd firefox/mozilla
...create .mozconfig file...
autoconf2.13
make -f client.mk build
A simple .mozconfig that works under Linux and Windows is:
. $topsrcdir/browser/config/mozconfig

mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
mk_add_options MOZ_MAKE_FLAGS=-j3
mk_add_options MOZ_CO_PROJECT=browser
ac_add_options --enable-debug
This does a debug build. If building the version with <video> element support add this line:
ac_add_options --enable-ogg
To build on Mac OS X, add the line:
ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.4u.sdk
I removed 'configure' to stop the merge conflicts that keep occurring when merging the firefox tree into the video element tree, and to prevent it from being included in patches when I generate a patch to attach to the bugzilla entry.

Categories:

Labels:

Wednesday, December 12, 2007

Video Element and Ogg Theora

There has been a lot of attention lately around the WHATWG <video> specification recommending Theora and Vorbis as baseline codecs.

The issue seems to have gained some attention since the position paper Nokia submitted to the W3C Video on the Web workshop which made it clear they didn't want Ogg included.

The reference to Theora and Vorbis has since been removed from the WHATWG specification and replaced with the wording:
It would be helpful for interoperability if all browsers could support the same codecs. However, there are no known codecs that satisfy all the current players: we need a codec that is known to not require per-unit or per-distributor licensing, that is compatible with the open source development model, that is of sufficient quality as to be usable, and that is not an additional submarine patent risk for large companies. This is an ongoing issue and this section will be updated once more information is available.
This has also caused quite a bit of discussion around the web.

From what I can see the main objection to Theora is the submarine patent issue. Theora is not 'patent-free'. Rather all known patents relating to it have been released to the public. Submarine patents are those which are unknown. They refer to the practice of keeping quiet about a patent on a technology until some company with a large amount of money implements it. Then the patent holder surfaces and attempts to get large amounts of money for it.

The problem of submarine patents is not specific to Theora. All forms of software technology face the risk. H.264 seems to be the current popular technology for those that oppose the use of Theora in the specification. H.264 also has a risk of submarine patents (like any software) but companies have already exposed themselves to this risk because they ship H.264 based systems. By having to implement Theora they then open themselves up to a second avenue of risk, one which they didn't face before.

One could argue that implementing anything new contains this element of risk, so following that logic these companies won't be doing any new implementations of anything. That's not the case obviously, or nothing would be done. They weigh up the risk of patent issues vs the reward of implementing things. With the current perceived low usage of Theora they probably don't see any advantage to them for implementing it vs the risk. So that appears to be why Theora is not a desired choice but some groups.

I've seen questions asked in the discussions asking why don't we settle on H.264. The big companies are already using it. There is the open source x264 encoder and ffmpeg decoder. While these projects are open source my understanding is that any user of the product must pay the required license fees to the patent holders on the techology they use. In the case of H.264, this is the MPEG-LA. The summary of the terms lists the fees. There is a cap on the total amount a company should pay so why not just pay this amount and ship H.264 support?

For an open source project this creates some difficulty. If the source for an H.264 decoder is included then anyone who downloads and builds from source, forks the project, or otherwise distributes a build would seem to have to deal with the licensing issues seperately. That is, the cap wouldn't cover all usage of the source.

This would immediately limit the distribution of the browser and the ability to embed the engine in other products without removing the H.264 capability. With the capability removed you're back to the problem of what codec should be supported with <video>. To effectively implement the HTML <video> specification in a way that can play compatible video streams with other (closed source) browsers, you'd have to front up with that fee.

It might seem that the best approach is to not specify a baseline codec for <video>. This also has problems. The big advantage of a baseline codec is a content producer can provide video in a format they they know all conforming browsers on any platform can display. Without a baseline codec, content producers will upload video in formats specific to particular platforms and we have little advantage over the existing object/embed elements.

The issue of support for DRM as outlined in Nokia's position paper isn't that big of an issue. Even with a royalty-free baseline codec, implementors are still able to have other codecs supported. They can support whatever DRM specific codec is required by 'big media'. But it is important that a free baseline is available for those that just want to supply video in a convenient manner without having to pay any money. Robert O'Callahan mentions this in his recent blog posting.

The W3C Video on the Web workshop starts tomorrow (Wednesday). The subject of HTML 5 and codecs is going to be discussed there. I'll be there talking about Mozilla's position and taking part in the discussion of the codec issue.

For more good commentary on the issue you might like to read The HTML 5 Wars (and why you should avoid them). If you are passionate about the use of Ogg Theora and <video> one of the best things you can do is start using it.

Do compelling demos. Release video in Theora format. It may be easy to use a service that provides video for you in exchange for giving them certain rights but if you want your format to succeed, then increased usage is the way.

Categories:

Labels:

Thursday, December 06, 2007

New <video> enabled experimental Firefox builds

I've uploaded new builds of Firefox with experimental support for the WHATWG <video> element. They are (compiled from this git commit):The git repository has been updated with the code for this build.

Note that the code in these builds, and the git repository, is advanced from that in the patch attached to the video element bugzilla entry. That code can be accessed from git via the patch11 tag.

The usual disclaimer with the builds I provide applies.
Be aware that these are builds from a random point in the Mozilla CVS tree, with the Video patch applied. I don't guarantee they'll work for much more than demonstrating video support and it's very likely to contain bugs. That said, I run these builds often.
Two main additions with this build. The first is fixing the annoying bug whereby leaving a page that is playing video would leave the sound running in the background. This was due to the 'bfcache' keeping the page around in case the user hits the back or forward buttons. The video is effectively paused now when the page is in the bfcache.

The second is preliminary support for the <source> element, which can be used to provide a list of media resources, with mime types, and the browser selects out of the list the media resource to play based on what it supports. This initial implementation only supports a 'type' attribute set to 'video/ogg'. What this will let you do though, is add a source element for an MP4 encoding, and one for an Ogg encoding. When played on Firefox the Ogg version will play, and a browser that currently doesn't support Ogg but supports MP4 can choose to play that instead.

Issues I have partial fixes for but haven't yet made it into the main branch of the git repository, and not in these builds are:
  1. Sound support on Linux is patchy. I'm using sydney audio's OSS support which has issues when multiple devices open /dev/dsp. This can occasionally affect synchronisation of the video.
  2. There is no buffering for network loads. The video plays immediately so on a slow pipe or large video the playback will stutter. You can pause the video to let the download catch up but there is no progress indication yet. This can make performance look worse than it is in bandwidth constrained environments.
  3. Seeking forward and back in the stream. There is some support, but it's a work in progress and quite broken.
You might notice that full page zoom works in this build. And it works with videos. You can zoom a page and any videos on the page are zoomed. You can test this with Ctrl+ (Apple-+ on the Mac).

Test videos are available at my test page, or Opera's page.

There are sites using the <video> right now.

The Metavid project has support for the <video> element. If you look at the videos in a <video> enabled browser, the player is the interface provided with the 'controls' attribute set. For example, Senate Proceeding 11-16-07. Nice job, Metavid, and thanks for using <video>!

The Wikimedia video's seems to have experimental support for it. Unfortunately the test to see if the video element is supported appears to be done last, so usually the Java applet, etc is found first. You can get around this by setting a cookie. When the Wikimedia video page open, enter this in the URL bar:
javascript:wgOggPlayer.setCookie('ogg_player', 'videoElement', 7*86400*1000, false, false, false, false)
Remove it by deleting the 'commons.wikimedia.org' cookie if you want to go back to the default ordering of selections.

You'll notice some issues, which is probably why it's currently right at the end of the chain. Clicking on the video picture and pressing play works fine. But clicking the play button on the main page listing all the videos does not correctly display the video. I'll look into what's causing this.

If you know of any other <video> supported sites, please let me know in the comments.

I'll be attending the W3C Video on the Web Workshop in San Jose on the 12th and 13th of December. I fly out from Auckland on the 9th and leave San Francisco on the 16th. If you're in the area and want to catch up, let me know.

Categories: firefox,

Labels:

Thursday, November 08, 2007

Opera has a new Video enabled build

Opera is making a call for video on the web, releasing an experimental build with video support modelled on the latest WHATWG specification.

Their post has some examples to try out and instructions on how to use the <video> element.

Their examples work quite well in the latest video enabled build of Firefox too. Thanks to help from Robert O'Callahan it now has support for the 'controls' attribute. There's no more need for JavaScript buttons to activate video. I've also implemented some of the events. This code is in the git repository.

Some very experimental binary builds if you want to try things out:
Be aware that these are builds from a random point in the Mozilla CVS tree, with the Video patch applied. I don't guarantee they'll work for much more than demonstrating video support and it's very likely to contain bugs. That said, I run these builds often.

Try out the demo's that Opera have done, or the one's on my test page.

It's very cool to see video support using patent free formats running on more than one browser, with simple HTML that can be embedded by anyone. Thanks Opera!

Categories: firefox,

Labels:

Friday, November 02, 2007

ECMAScript 4 Reference Implementation Updated

The reference implementation for ECMAScript 4 has been updated to M1 and is available for download. The source is available (written in SML), as well as binaries for Windows, OS X, and Linux.

Categories:

Labels:

Wednesday, October 31, 2007

Javascript Packrat Parser

I've made some changes to my parser combinator library written in Javascript that I wrote about previously.

I was extending the example code from that post to parse more of the ECMAScript 3 grammar to test it out and was hitting performance issues. I ended up modifying the combinator library to be a Packrat style parser. The Parsing Expression Grammar Wikipedia page has a description of PEGs and what a Packrat parser does. Basically results of each parse step are memoized to prevent reparsing after backtracing, sacrificing memory for speed:
Any parsing expression grammar can be converted directly into a recursive descent parser[citation needed]. Due to the unlimited lookahead capability that the grammar formalism provides, however, the resulting parser could exhibit exponential time performance in the worst case.

By memoizing the results of intermediate parsing steps and ensuring that each parsing function is only invoked at most once at a given input position, however, it is possible to convert any parsing expression grammar into a packrat parser, which always runs in linear time at the cost of substantially greater storage space requirements.

A packrat parser[1] is a form of parser similar to a recursive descent parser in construction, except that during the parsing process it memoizes the intermediate results of all invocations of the mutually recursive parsing functions. Because of this memoization, a packrat parser has the ability to parse many context-free grammars and any parsing expression grammar (including some that do not represent context-free languages) in linear time.
I changed a few other things in the library to more closely map to the vocabulary of PEGs. 'alternative' is now called 'choice' for example. There are still quite a few loose ends to tidy up and documentation of course.

The updated library can be retrieved from my git repository:
git clone git://double.co.nz/git/jsparse.git
It can run in a browser but I've mostly been testing it using Mozilla Rhino.

I've included three basic examples. They all operate on the example expression grammar from the wikipedia article:
Value   := [0-9]+ / '(' Expr ')'
Product := Value (('*' / '/') Value)*
Sum := Product (('+' / '-') Product)*
Expr := Sum
The first, example1.js, is a direct translation of that grammer. It produces a pretty ugly default Abstract Syntax Tree however:
var Value = choice(repeat1(range('0','9')), Expr);
var Product = sequence(Value, repeat0(sequence(choice('*', '/'), Value)));
var Sum = sequence(Product, repeat0(sequence(choice('+', '-'), Product)));
var Expr = Sum;
The Expr parser can be called by passing it a string to be parsed wrapped in a 'ParserState' object. This object is used to keep track of the current parse position and the memoized results. A helper function, 'ps', can be used to construct it:
var result = Expr(ps("1+2*3"));
The second example, example2.js adds to this to produce a better AST. It also uses the 'chainl' parser combinator to handle grouping correctly. A quick online page demonstrating this example is here. Enter an expression matching the grammar (there is no error checking yet), and press the button to see the AST in JSON format.

The third example, example3.js, evaluates the expression as it parses instead of generating an AST. This is also available online to try.

I've also included in the repository the work in progress of the ECMAScript 3 grammar. It is not complete or correct yet but I use it for testing the library.

Based on what I've learnt from doing this I plan to revisit the way I did Parser Combinators in Factor.

Categories:

Labels:

Tuesday, October 23, 2007

ECMAScript 4 Overview Paper

An overview paper describing the ECMAScript 4 language features was announced on the es4-discuss mailing list:
I'm pleased to present you with an overview paper describing ES4 as the language currently stands. TG1 is no longer accepting proposals, we're working on the ES4 reference implementation, and we're expecting the standard to be finished in October 2008.
...
This paper is not a spec, it is *just* a detailed overview. Some features may be cut, others may be changed, and numerous details remain to be worked out, but by and large this is what TG1 expects the language to look like. Your comments on the paper are very much welcome. Please send bug reports directly to me, everything else to the list.
The document is available on the ECMAScript 4 language site in overview.pdf.

Categories:

Labels:

Monday, October 01, 2007

Standard ML to Javascript compiler

smltojs is a compiler from Standard ML to Javascript. According to the page it has support for all of Standard ML.

Since the reference implementation of Ecmascript 4 is written in Standard ML it would be interesting to see if it can be built using this compiler. That would provide an es4 implementation that runs in the browser based off the reference implementation.

Categories:

Labels:

Firefox Video Element Patch Version 5

Version 5 of the patch to add Video element support to Firefox is up. This version rewrites a lot of the code to match the pseudocode in the WHATWG specification.

As well as bug fixes it contains support for the 'autoplay' attribute and loads the first frame of the video when the element is first displayed. This version also runs the SVG demo I made a video of in an earlier post. I tested the demo under Windows, Linux and Mac OS X and it seemed to work fine. That demo, and others, is available on my video patch test page. Binaries are also available on that page.

Don't forget you can track progress using the git repository if you don't want to wait for the patches:
git clone git://double.co.nz/git/video.git


Categories: ,

Labels:

Saturday, August 18, 2007

SVG Video Demo

[ Update 2007-10-01: Patch 5 for the video element support can run this SVG demo. Binaries and SVG source available here. ]

Vladimir Vukićević ported a Silverlight demo to SVG. The photos.svg file runs in Firefox and you can move, resize and rotate the photo's using a nice interface. It demonstrates that the types of things that Silverlight is being used for can also be done using open standard technologies like SVG.

I took Vladimir's work and modified it to work with the HTML video element support I'm adding to Firefox. With this version you can resize, rotate and move video files while they are playing. The topmost video in the z-order has the audio track played. Performance is pretty reasonable considering I haven't done any optimisation of it.

The magic of including the HTML video element inside SVG is done using <foreignObject>. Something like
<foreignObject>
<div xmlns="http://www.w3.org/1999/xhtml">
<video src="myvideo.ogg"/>
</div>
</foreignObject>
. A screen cast of this running is available (in various file formats):The videos being played are:

To get this working I had to make a few changes to the video element code, some of which are in the git repository already. The rest will be pushed soon, along with the source to the SVG demo which I'll add to the list of test/demo files I use.

If you have a browser with support for <video> you should see the option to play it below.




Categories: ,

Labels:

Dave Winer Visits Mozilla

Dave Winer visited Mozilla yesterday to give a talk about RSS during the all hands meetings. I've been a reader of Dave's blog for awhile and was previously a Radio Userland user for a couple of years so it was good to meet him in person.

He outlined his views on the 'river of news' format of aggregating feeds that Radio Userland uses. This seems to be Dave's preferred way of dealing with RSS. The advantage of this approach is that the new news items appear at the top of the list so you can quickly scan the new information and discard the old if you don't have time. I don't think he was too impressed with the way Firefox handles RSS feeds with the 'live bookmarks' and the way things are handed off to external readers.

I briefly talked to Dave afterwards about the new Video and Audio elements that are being specified, and about the Video implementation in Firefox. This came up in the context of if it was possible to build a podcast player in a web browser. With the video/audio elements I think this is more than possible and could make for a great user experience - all in standards (or at least, draft standard) based HTML.

On his blog he wrote about following up on his ideas on integrating RSS and podcasting into the web browser. I'm interested in seeing what he has to say on this and how the new functionality we are implementing could help with this.

Categories:

Labels:

Wednesday, August 08, 2007

Firefox Video Element Patch Version 4

Version 4 of the Firefox Video Element Patch is up. It has a number of bug fixes and contains changes suggested by the reviewers.

Before applying the patch to the Mozilla Firefox CVS trunk, you should first apply the third party modules patch. Another option is to just use the git repository I've been maintaining.

One major change is I'm no longer using PortAudio. Instead I'm using the Sydney Audio library included with liboggplay. This works well and now sound works across Windows, Linux and Mac OS X for both debug and optimized builds. I abstracted out the sound library implementation behind an interface. If it is decided to go to another library it's fairly simple to implement that interface.

I've done binary builds for Windows, Linux (x86) and Mac OS X (Intel). They are available at the test page. I put together. Remember this is alpha code from the CVS trunk with the patch applied - it may crash or do nasty things so run it in a Firefox profile specific for testing this (using the -P or -ProfileManager command line option for example).

If you try it out, let me know of any bugs you come across, or if the Git repository doesn't build on your platform. I'm off to the Mozilla All Hands meeting in Mountain View next week, so if you're in the area and want to meet up, let me know.

Categories: ,

Labels:

Wednesday, July 18, 2007

Mozilla based browser for the N800

The N800 is a small tablet type device produced by Nokia.

It has a 320Mhz ARM CPU, 128MB of RAM and 256MB of flash storage. While the latter sounds small it also has two card slots allowing plugging in lots of extra storage. The display is a nice 800x480 and about 4 inches. It has both 802.11b/g and Bluetooth 2.0. A built in camera is also included.

Recently a Skype client became available for it and now it seems a browser based on Gecko, the engine behind Firefox.

The new browser seems to be well received based on posts to this thread and there's a nifty web site that explains a bit about it.

The browser is open source. Links to the source and other stuff here.

I don't have one, but being a gadget person I have to say I'm now quite tempted! Time to stop reading about it...

Categories: ,

Labels:

Friday, July 13, 2007

Firefox Video Element Patch Version 2

I've attached a new version of the Firefox Video Element Patch to bugzilla. As outlined in the bugzilla entry, this patch fixes/updates:
  • Ogg codec support can be enabled/disabled with configure flag --disable-ogg. Currently if the Ogg codec is disabled then the video element is disabled too. In the future if/when other codecs are supported this can change.
  • Fix build problems when doing libxul enabled builds
  • Fix link error on windows when doing a --disable-libxul build
  • Fix colour playback issues on Linux and Windows
  • Handle no audio device being present
  • Adjust element size when video size information is read from the Ogg file
  • No longer use channel across threads
  • Various refactorings based on email feedback
A couple of issues still to track down:
  1. Sound not working on Linux
  2. Sound not working on Mac OS X optimized builds
I've also updated the third party modules patch which you need to apply first: third_party_modules.patch.gz

I've been regularly updating the Git repository and it contains all these changes as well.

Categories: ,

Labels:

Wednesday, July 11, 2007

Javascript on the Server

Running Javascript on the server seems to be gaining in popularity. Last year I wrote a small framework to test writing web applications in Javascript. Tony Garnock-Jones later extended this to support building continuation based web applications.

More recently, Peter Michaux has taken my original code and made a nicer web framework out of it.

John Resig has written a browser DOM emulation layer that allows jQuery and some other frameworks to be run server side. His example code looks very neat. Combining this with a decent Javascript based web framework would make for a interesting environment.

Steve Yegge has apparently ported Rails to Javascript. Hopefully this code will be released as open source someday.

Sun have a server side Javascript web application environment, Phobos.

Helma looks to be a mature Javascript web system too.

I wonder if Javascript usage outside the browser is reaching a 'tipping point' and is about to grow dramatically. It's an under-appreciated language and is very much like a protoype OO based Scheme with a Java-ish syntax. Ecmascript 4 is looking to be even more interesting.

Categories:

Labels:

Saturday, July 07, 2007

SuperHappyDevHouse Aotearoa

I'm at the SuperHappyDevHouse Aotearoa hackathon in Wellington at The Cross. Free food, free coffee, lots of people and lots of hacking going on. Hopefully by the end of today I'll have the video tag implementation a bit further along.

There's a Flickr stream of photo's under the tag shdhnz.

Categories:

Labels:

Building a Video element enabled Firefox

I've had a few comments and emails about problems encountered building Firefox with the video element patch, or from the git repository.

Once you've applied the patch, or retrieved the git repository, you'll need to add a .mozconfig file in the 'mozilla' directory. The Mozilla Build Documentation has the details on what can go in this file. A simple .mozconfig which will get a debug build working is:
. $topsrcdir/browser/config/mozconfig

mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
mk_add_options MOZ_CO_PROJECT=browser
ac_add_options --disable-tests
ac_add_options --enable-debug
ac_add_options --with-macosx-sdk=/Developer/SDKs/MacOSX10.4u.sdk
The last line is only needed for Mac OS X builds.

There was a problem with the patch in that this default build on some platforms would give a link error due to not finding the Alsa sound libraries. This is fixed in the git repository. The problem only occurred in 'libxul' enabled builds, which is now the default. You can disable libxul in the build process by adding this line to .mozconfig:
ac_add_options --disable-libxul
Disabling this is quite useful for development as the builds are faster and you can selectively build portions to cut down compile times. Once you have a .mozconfig file you can start the build using make:
make -f client.mk build
Note the use of the 'client.mk' makefile with the 'build' target.

In the .mozconfig we specified a MOZ_OBJDIR. This is a directory that will contain the build files and the final executable. When the build completes you can run Firefox with something like:
obj-a-b-c-d/dist/bin/firefox -ProfileManager
Change 'obj-a-b-c-d' with the name of the object directory that was created. On Mac OS X a '.App' is created and you will need to run Firefox with:
obj-a-b-d/dist/MinefieldDebug.app/Contents/MacOS/firefox -ProfileManager
The .app will be called 'Minefield' in an optimized build and 'MinefieldDebug' in a debug build.

To build something you can install on another system (a .dmg file on Mac OS X, tar file on Linux, etc) you must have a 'libxul' enabled build and run the following after a successfull build:
make -C obj-a-b-c-d/browser/installer
The packaged installer will be in the obj-a-b-c-d/dist directory.

Categories: ,

Labels:

Thursday, July 05, 2007

Patch for Video element support in Firefox

I've uploaded a patch which is the first pass at implementing support for the <video> element in Firefox. The patch is attached to bug 382267.

There are quite a few known issues with the patch at the moment, and I'm working through them, but I've made it available at this time so others can at least try it out. A page with some example usage is here: http://www.double.co.nz/video_test.

To build you'll need the latest Firefox CVS, and apply the following:I've also made a git repository available that tracks my work on video. This is basically Firefox CVS with the patch applied, and is the repository I used to generate the patch in the first place. I'll be updating that regularly with my changes between patch generation for the bugzilla entry. You can track this repository with one of the following commands:
git clone http://double.co.nz/git/video.git
git clone git://double.co.nz/git/video.git
The main issues at the moment are problems with optimized builds, so I suggest trying it out with a debug build. I'm also not using channels correctly for multithreaded code which is the cause for a bit of instability. Read the bug for further details.

Categories: ,

Labels:

Friday, June 15, 2007

Viewing Firefox CVS changes via gitweb

Following on from my previous post about using git to track the Firefox CVS repository, I've installed 'gitweb' to allow browsing the repository. The URL to access it is: http://www.double.co.nz/cgi-bin/gitweb.cgi

This is quite a nice way to see diffs for what has changed recently in the CVS tree for the time period between when my git import script runs. Unfortunately you don't get the original CVS comments for the commit.

You can also now clone or pull from the repository using the 'http' protocol as well as the 'git' protocol:
# Git protocol 
git clone git://double.co.nz/git/firefox.git

# HTTP protocol
git clone http://double.co.nz/git/firefox.git
In the comments to the last post, Alex asked for advice on how to get git on Mac OS X and MozillaBuild on Windows users.

On Mac OS X I installed 'git-core' using macports:
sudo port install git-core
Windows is a bit trickier as most people who have been following git know. The best approach at the moment is to use cygwin. Git is available in cygwin or you can install it from source. Make sure you get version 1.5.1 or greater. What I do is run all my git commands from cygwin and use the msys based MozillaBuild shell for building and other things. It's not ideal but it has worked reasonably well for me. Another option might be git-mingw but I've not tried it and don't know how stable it is.

Also in the comments, jmdesp asked about using the git repository to track regressions. I have a script that updates the repository about every 6 hours. You could probably use this to track down the six hour window where the problem occurred, and use git to see the files that were changed in the commit. You can use git to move forward and back commits and try builds. Here's an example workflow:
# Get the repository
git clone git://double.co.nz/git/firefox.git regression
cd regression/mozilla
...create .mozconfig...
make -f client.mk build
...test...

# Go back one commit
git checkout HEAD~1
make -f client.mk clean
make -f client.mk build
...test...

# Go back another commit
git checkout HEAD~1
make -f client.mk clean
make -f client.mk build
...test...

# Go back 3 more commits
git checkout HEAD~3
make -f client.mk clean
make -f client.mk build
...test...

# Restore back to the most recent commit
git checkout HEAD
The number following the tilde in HEAD~1 is the number of commits to go back. You can also use 'git log' to see the commit id of each commit and jump directly to that:
git log
...Commit c2148196de6a112982e273b5c469a9f763a1fa8b...

git checkout c2148196de6a112982e273b5c469a9f763a1fa8b


Categories:

Labels:

Using Git to track the Firefox CVS repository

Git is a distributed version control system that was written for managing the Linux kernel. I've been using it recently to make it easier to manage my code changes against a CVS repository for which I don't have write access.

The CVS repository is the codebase Firefox. Usually I'd check out a copy of the CVS repository, make my local changes, generate a patch and attach it to a Bugzilla entry for review. When it passes this then gets committed by someone with write access.

What becames hard to manage is dealing with checkouts that require a number of patches, keeping track of what has been applied, keeping patches around to be able to reverse them out and handling multiple copies of trees.

An even bigger issue is how to create patches that add directories or files when you don't have write access to CVS. 'cvs diff' only includes new files if they have been added via 'cvs add'. This requires write access to CVS. You can work around this by manually editing the local CVS files, or using a utility like 'cvsdo'. Unfortunately this still doesn't work for directories. The only way to add a directory to CVS, to have it picked up with 'cvs diff', is with 'cvs add' and write access to the repository. This issue and workarounds is described in the Mozilla documentation.

This problem will go away as Mozilla migrates to a new version control system, but in the meantime I decided to try tracking things with Git after reading an article by Diane Trout who is doing something similar.

I did a standard checkout of the sources for Firefox, following the Mozilla Build instructions. Within the checkout directory I created a git repository containing all the files, with relevant .gitignore instructions to not include CVS directories, etc. Something like this did the trick:
cvs -d ... checkout mozilla/client.mk
cd mozilla
make -f client.mk checkout MOZ_CO_PROJECT=browser
cd ..
git init
git add .
git commit -m "Initial import"
I keep this repository as the main repository linked to CVS. When I work on a bug or some functionality I can clone this repository and use git's branch management functionality to work on different things.

Periodically I update the main git repository with the latest changes from CVS. I have a script that I run that does something like this:
 cvs update mozilla/client.mk
cd mozilla
make -f client.mk checkout
git add .
git commit -a -m "Update to CVS"
This does a 'bulk commit'. It doesn't import the individual CVS checkins with comments but for my purposes I don't need this. I just need something quick that works.

Now that the main repository is updated I can merge in the changes with my 'patches in progress' easily. Here is a commented example of some workflow:

# Clone the firefox repository for working on a bug
git clone /git/firefox.git bugfixes
cd bugfixes

# I like to leave the 'master' branch created by the clone
# as being a pure copy of the original repository and work
# on a branch. The following creates a branch to work on.
git checkout -b bug_123

# Now I make changes, add files, remove files, etc. I commit
# to the branch frequently with:
git add some_file_i_added.txt
git commit -a -m "Some message"

# At some point I want to update the 'master' branch with the
# recent CVS updates.
git checkout master
git pull /git/firefox.git

# When in the bug_123 branch I can merge in the changes I
# got from the latest CVS that I keep in 'master'
git checkout bug_123
git pull . master
This workflow works quite well. At any time I can create additional branches very cheaply to try things out, reverting back to the original branch if I change my mind, or to pull in the changes if I want to keep them:
# In branch bug_123 at the moment
git checkout -b try_something

# Make changes
git commit -a -m "My changes"

# Go to the original branch and pull in those changes to keep them
git checkout bug_123
git pull . try_something
To generate a patch that can be applied to the original CVS checkout, I can generate a 'git diff' in my branch with committed changes, against the 'master' branch containing the up to date CVS:
# In branch bug_123
git diff master >~/bug_123.patch
I can also generate a patch against a specific git commit:
git diff d889a66ff046f3737b87bd3e4098dcb156b9836f >~/bug_123.patch
Some miscellaneous commands that are also helpfull:
# What has been changed since last commit
git status

# A diff of my recent edits
git diff

# A log of commits
git log

# Push my bug_123 changes to the 'master' branch in a remote repository
git push user@server.com/git/changes.git bug_123:master
I've published my regularly updated git repository of the Firefox CVS tree on my server. It can be retrieved using the 'git' or 'http' protocols using:
# Git protocol 
git clone git://double.co.nz/git/firefox.git

# HTTP protocol
git clone http://double.co.nz/git/firefox.git
Note that you can't browse the repository using the HTTP URL due to me having directory browsing turned off, but 'git clone' or 'git pull', etc works. You can browse the repository using gitweb via http://www.double.co.nz/cgi-bin/gitweb.cgi. This is actually an excellent way to view recent changes made to the Firefox CVS.

By adding a .mozconfig file in the 'mozilla' directory and doing 'make -f client.mk build' you can build your own trunk version of Firefox. A simple .mozconfig that will work on most platforms is:
. $topsrcdir/browser/config/mozconfig

mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-@CONFIG_GUESS@
mk_add_options MOZ_CO_PROJECT=browser
ac_add_options --enable-debug
For Mac OS X users, add the following line:
ac_add_options --with-macosx-sdk=/Developer/SDKs/MacOSX10.4u.sdk
You can run the new build on Linux or Windows with:
obj-dir/dist/bin/firefox
Or on Mac OS X:
obj-dir/dist/MinefieldDebug.app/Contents/MacOS/firefox
Replace 'obj-dir' with the object directory created during the build, and I recommend using -ProfileManager or something similar to do your testing a profile separate from your main Firefox profile.

If you're keen to try playing around with the code, I wrote a short post on how to add a new DOM element a while back that may give a bit of a start.

If you have any additional git tips or suggestions, please post a comment.

Categories:

Labels:

Saturday, June 09, 2007

ECMAScript Edition 4 Reference Implementation

Brendan Eich has posted about the availability of a reference implementation of ECMAScript Edition 4.

ECMAScript Edition 4 is the new version of Javascript being designed. The reference implementation is written in SML (Standard ML). More information is at the Lambda The Ultimate posting about it.

Categories:

Labels:

HTML 5 Video Javascript Wrapper

The Metavid blog has a post about the HTML 5 Video element and how it can be supported in browsers that don't have native support for <video>.

When their Javascript code, mv_embed, is included in a page it goes through the DOM and replaces all <video> elements with whatever playback method the client browser supports. If it supports HTML 5 <video> then it does nothing.

The replaced element is represented by a Javascript object with as much of the HTML 5 video Javascript API implemented as supported by the particular playback method. In this way code can be written to the standard API which works across all browsers. Very nice!

I'm making good progress on the Firefox implementation of native Ogg Theora playback. Today I got audio working by decoding the Vorbis stream within the video and using the portaudio library to control the sound hardware. I had multiple videos playing on a single page, each with sound, mixing well.

Categories: ,

Labels:

Thursday, May 31, 2007

New Javascript features in Firefox nightly build

John Resig has a post going into detail about the new Javascript language features in the latest nightly builds of Firefox. They include:
  • Expression Closures
  • Generator Expressions
  • Array.reduce

Read John's post for all the details.

Categories: ,

Labels:

Sunday, May 27, 2007

Support for HTML Video element in Firefox

The WHATWG HTML 5 working draft has a specification for a 'video' element.

I briefly mentioned in a previous post that I was working on implementing this tag natively in Firefox. The intent is to display Ogg Theora encoded video without needing any plugins, similar to the recent demonstration of Theora playback using a video element by Opera. Opera have a post about it on their labs page.

I've made steady progress and can currently display Ogg Theora encoded videos using the video element. I've done a screencast of a simple page with a Theora video to give an idea of how it works. The video looks choppy in the screencast due to the way the screencasting software captures the screens. It didn't look like that when I played it, honest :-). The screencast is also quite large, at about 7MB. It's a pity we don't already have browsers with <video/> support so I don't have to embed large flash files for screencasts!

The example in the screencast shows a simple video element usage:
<video style="width:320; height: 240;" id="v" src="test.ogg"></video>
The DOM object created by this element has a Javascript API that can control playback. The button on the page uses this to start the video:
<button onclick="document.getElementById('v').play()">Play</button>
I'm not finished with the implementation, and have yet to implement the full WHATWG specification, but it's coming along nicely. I'll post more on my weblog as I get closer to finishing it.

The snippet of video in the screencast is of James Hill playing 'Allegro Con Brio' on the ukulele. You can see this and more in the original WMV format on the video page of his website. James is an amazing ukulele player. His musics CD's are great and he will apparently be in Auckland, New Zealand in November 2007 to attend the Ukulele Festival of 2007.

Categories: ,

Labels:

Sunday, April 01, 2007

A mix of many things

I haven't been blogging much about what I've been doing lately so here's my attempt to put lots of little things in one post.

Work at Mozilla is going well. My patches for Firefox related to offline event notifications were reviewed and committed to CVS. Offline support is now in the nightly builds and can be used.

I finished the work I'd been doing on Zimbra. I can now edit emails offline, save them and they are sent automatically when I go back online. This is using the support for offline web applications that was recently added to the nightly builds. At some stage I'll do a screencast and go into detail about it.

Now I'm working on implementing a <video> tag in HTML that will provide a user interface similar to Flash video's and native support for Ogg Theora streaming videos. At least, that's the current plan, and it's based on a proposal from Opera posted to the whatwg mailing list. This proposal is being discussed fairly heavily on the list at the moment with an additional proposal from Apple so what I end up implementing may change as it gets nailed down.

While my head is in the Ogg space I added some Ogg support to Factor in my free hacking time as I mentioned in a previous blog post. I expanded on that this weekend and added support for Theora. From the latest code in the repository you can play Theora videos:
"apps/ogg-player" require
"test.ogg" play-theora-file
It has some issues (well, quite a few) at the moment. The YUV to RGB conversion is done in software, written in Factor, and is a bit too slow at the moment. A 320x240 video frame takes about 70ms on my laptop to convert. As a result it skips quite a few frames and is not synchronised with the audio.

I'll work on this over time and Slava has mentioned that he'll look into speeding the compiler up to make it faster for this sort of thing. I can also look into doing the conversion in hardware via an OpenGL shader. I would like to see it possible to be done in Factor though.

I'm still playing online poker regularly. Last year I won a few multi table tournaments which probably made my expectations on how much I could earn a bit optimistic.

Not long after that I hit a tournament dry spell, coinciding with the US law changes preventing US players, which made the tournaments less interesting to play. I switched to playing at a different site, signed up for rakeback (which gives you a percentage of the money you pay in rake to the site back to you), and played low stakes ring games. This is the $0.25-$0.50 and $0.50-$1 levels.

This has been profitable for me this year so far, although March was looking touch and go for awhile. I'll probably go back to playing tournaments at some point as I really enjoy them, but the ring games so far have been a better hourly rate. Of course, playing at these low limits I'm not making a fortune, but I'm not risking one either - and I prefer the enjoyment of the game. As my bankroll increases I'll look at moving up the limits.

But my time in Auckland hasn't all been poker playing, Factor programming and Firefox. I've been working more on my family history research, explored a few of the places and cemeteries where my ancestors are buried in Auckland. I found a few new avenues to research and explore and located the graves of great, great, great, great grandparents.

Next week I'm off to Mountain View, California, for the Mozilla "All Hands" meeting. This will be my second trip ever to the US and to California so I'm really looking forward to it. I leave NZ on Monday 9th April, and leave San Francisco airport on Saturday 14th.

Categories: , , ,

Labels:

Saturday, February 17, 2007

Another Offline Web Application Example

Mark Finkle has put together a simple demonstration application that uses the offline APIs in Firefox, including source.

In other offline application news, John Resig has written about how the offline/online notifications work.

Categories:

Labels:

Wednesday, February 14, 2007

More on Firefox Offline Web App Support

Robert O'Callahan posts with more details on the offline support in Firefox. In a later post I'll go into more technical detail on how I used the features to add the offline support to Zimbra, along with the issues I faced. At the moment I'm working on adding the ability to edit drafts and have it updated on the server when you go back online.

Categories: ,

Labels:

Offline Zimbra with Firefox

In my new job at Mozilla I've been working on adding offline support to Zimbra as a proof of concept of the new Firefox offline capabilities.

Zimbra is heavily ajax based and gives a good idea of how difficult or easy it is to convert an existing application to use the offline support. For this first cut I made Zimbra work when the browser is in offline mode and provide the ability to browse the email folders, and view messages while offline that are held in the Inbox and Drafts folders.

The Firefox features used to enable offline operation were:
  • DOM Storage for storing emails and folder information. DOM Storage is an implementation of a WHATWG specification.
  • Offline Cache. A patch is available for Firefox that adds an offline cache specifically for web applications to store data (like images, pages, etc) that must be available while offline. Items are loaded in the cache using a rel="offline-resource" element on a <link> element.
  • JAR file Protocol. Firefox accepts JAR protocol URLs. These reference individual items in a JAR file and allow convenient bundling and caching of resources. For example, to reference the file /core/AjxCore.js in the ajax.jar file, the URL would be: jar:/zimbra/js/ajax.jar!/core/AjxCore.js
  • Offline Events. The browser has "offline" and "online" events that are triggered when the user chooses to go offline via the menu, or when network connectivity is lost. I modified Zimbra to listen for these events and display a status indicator showing whether it is in offline or online mode.
A screencast shows me logging into Zimbra, viewing some emails, going offline, the offline/online indicator and the availability of the emails while offline. It's available here: http://www.bluishcoder.co.nz/offlinezimbra.

It's the first screencast I've out together so hopefully it comes out ok. I used the excellent 'wink' tool.

Offline Zimbra
Categories: ,

Labels: