haiku-website/static/legacy-docs/benewsletter/Issue5-4.html

722 lines
56 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Be Newsletters - Volume 5: 2000</title><link rel="stylesheet" href="be_newsletter.css" type="text/css" media="all" /><link rel="shortcut icon" type="image/vnd.microsoft.icon" href="./images/favicon.ico" /><!--[if IE]>
<link rel="stylesheet" type="text/css" href="be_newsletter_ie.css" />
<![endif]--><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="Be Newsletters" /><link rel="up" href="volume5.html" title="Volume 5: 2000" /><link rel="prev" href="Issue5-3.html" title="Issue 5-3, January 19, 2000" /><link rel="next" href="Issue5-5.html" title="Issue 5-5, February 2, 2000" /></head><body><div id="header"><div id="headerT"><div id="headerTL"><a accesskey="p" href="Issue5-3.html" title="Issue 5-3, January 19, 2000"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a accesskey="u" href="volume5.html" title="Volume 5: 2000"><img src="./images/navigation/up.png" alt="Up" /></a> <a accesskey="n" href="Issue5-5.html" title="Issue 5-5, February 2, 2000"><img src="./images/navigation/next.png" alt="Next" /></a></div><div id="headerTR"><div id="navigpeople"><a href="http://www.haiku-os.org"><img src="./images/People_24.png" alt="haiku-os.org" title="Visit The Haiku Website" /></a></div><div class="navighome" title="Home"><a accesskey="h" href="index.html"><img src="./images/navigation/home.png" alt="Home" /></a></div><div class="navigboxed" id="naviglang" title="English">en</div></div><div id="headerTC">Be Newsletters - Volume 5: 2000</div></div><div id="headerB">Prev: <a href="Issue5-3.html">Issue 5-3, January 19, 2000</a>  Up: <a href="volume5.html">Volume 5: 2000</a>  Next: <a href="Issue5-5.html">Issue 5-5, February 2, 2000</a></div><hr /></div><div class="article"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Issue5-4"></a>Issue 5-4, January 26, 2000</h2></div></div></div><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Engineering5-4"></a>Be Engineering Insights: An Overview of Media File Formats and Codecs</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Dominic</span> <span class="surname">Giampaolo</span></span></div></div></div><div class="blockquote"><table border="0" cellspacing="0" cellpadding="0" class="blockquote" summary="Block quote"><tr><td width="90%" valign="top"><p>
<span class="quote">The wonderful thing about standards is that there are so many to choose
from.</span></p></td><td width="5%" valign="top"> </td></tr><tr><td width="100%" colspan="2" align="right" valign="top"></td></tr></table></div><p>
I can't think of a better way to introduce an article about "standard"
media file formats. In this article I'd like to provide a pretty detailed
description of the different file formats and codecs available for
storing audio and video data. I'll start with a high-level view of what
is going on and then drill down to the details. My hope is that this will
help people better understand what is happening when they read or write a
media file.
</p><p>
Before we begin though, let me state that you don't strictly need to know
any of this information. The <code class="classname">BMediaFile</code> and
<code class="classname">BMediaTrack</code> objects handle
all the grungy details for you. However, if you do know how media file
formats work, it may help you understand why the APIs were designed the
way they were and why they behave in certain ways.
</p><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id438938"></a>File Formats First</h3></div></div></div><p>
The first thing to understand is the difference between a "file format"
and a "codec." A file format is nothing more than a container to store
data. A file format contains additional information about the movie (such
as its size, the frame rate, etc.) but the file format does not typically
specify that the audio or video must be encoded in a specific way. A
codec, on the other hand, is a way of encoding audio or video data. How a
piece of data is encoded is (typically) not dependent on the file format
it's stored in.
</p><p>
As an example, consider an <acronym class="acronym">AVI</acronym> file. An <acronym class="acronym">AVI</acronym> file can store audio and
video encoded with numerous different encoders. Another way to say it is
that an <acronym class="acronym">AVI</acronym> file is a way to store audio and video in a file, but you can
encode that audio/video data any way you wish. Encoding audio or video
data transforms it from a very high- bandwidth stream of raw data into a
stream of compressed data. While you could do anything with a stream of
compressed data, it is typically stored in a common file format such as
<acronym class="acronym">AVI</acronym> or QuickTime.
</p><p>
The following table lists the media file formats that BeOS supports:
</p><div class="informaltable"><table border="1"><colgroup><col align="right" /><col align="left" /></colgroup><thead><tr><th align="right">File Format Name</th><th align="left">Type of Data Supported</th></tr></thead><tbody><tr><td align="right"><acronym class="acronym">AVI</acronym></td><td align="left">audio, video</td></tr><tr><td align="right">QuickTime</td><td align="left">audio, video, misc.</td></tr><tr><td align="right"><acronym class="acronym">MPEG</acronym> system stream</td><td align="left">audio, video, misc.</td></tr><tr><td align="right">DV stream</td><td align="left">audio, video</td></tr><tr><td align="right">AIFF</td><td align="left">audio</td></tr><tr><td align="right">8SVX</td><td align="left">audio</td></tr><tr><td align="right"><acronym class="acronym">WAV</acronym></td><td align="left">audio</td></tr><tr><td align="right">AVR</td><td align="left">audio</td></tr><tr><td align="right">AU</td><td align="left">audio</td></tr></tbody></table></div><p>
There are many other file formats (such as FLI/FLC, ASF, etc.) but
currently the BeOS does not support them.
</p><p>
Each of the file formats listed in the table (except for <acronym class="acronym">MPEG</acronym>) has a
similar structure, although each one uses its own terminology to describe
the specifics. The basic idea is that a file is made up of "chunks" of
data and if you don't understand a chunk you can skip over it. The way
this is typically done is with a 4-byte identifier to describe the type
of chunk and a 4-byte (or sometimes 8-byte) size. A diagram helps to
illustrate:
</p><pre class="screen">
file offset data
------------+---------------------+
0 | 0x000012b8 |
------------+---------------------+
4 | 'moov' (0x6d6f6f76) |
----------------------------------+
</pre><p>
This represents the first 8 bytes of a QuickTime file. QuickTime stores
its chunks with the size first and then the identifier (and as one would
expect, <acronym class="acronym">AVI</acronym> does it the opposite way). The identifier is known as a
"four-character code" or "fourcc." A fourcc is often a mnemonic or clever
name that makes it easy to identify when looking at a hex-dump of a file.
In this example the ASCII characters 'moov' have the value <code class="literal">0x6d6f6f76</code>
when treated as a 4-byte integer (on a big-endian system).
</p><p>
The size of this "moov" chunk is <code class="literal">0x12b8</code> bytes long (4792 in decimal).
Because we know the size, even if we don't know anything about a moov
chunk we can correctly skip over it and parse the next item (of course,
if you skip the moov chunk you won't be able to do very much!).
</p><p>
The concept of a chunk'ed file with an identifier and size for each chunk
is a simple but good way to store data that needs to be parsed by many
different types of programs. Having well-identified chunks and the
ability to skip over them if they are unknown makes for a robust file
format.
</p><p>
In addition to a linear series of chunks in a file, both QuickTime and
<acronym class="acronym">AVI</acronym> allow a chunk to contain other nested chunks. That is, certain chunk
identifiers indicate that the chunk is a container that has other chunks
within it. The size of a container chunk is the sum of all the chunks
inside it. For example, an <acronym class="acronym">AVI</acronym> file may contain a 'LIST' chunk that
contains other chunks. The size of the LIST chunk would allow you to skip
over it if you wanted. An example in a QuickTime file would be the 'trak'
chunk, which contains many other chunks that describe the media track.
</p><p>
To get a better feel for the layout of a file format I suggest that you
fire up DiskProbe and scan through some media files. You'll quickly see
how they're laid out. It's also helpful to have the file format spec
handy for serious perusal (a good source of specs is
<a class="ulink" href="http://www.wotsit.org/">http://www.wotsit.org/</a>, or just fire off a search on
<a class="ulink" href="http://www.google.com/">http://www.google.com/</a>).
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id439213"></a>The Other File Format (Mpeg)</h3></div></div></div><p>
The <acronym class="acronym">MPEG</acronym> file format is actually not one but three formats. You can have
a raw video stream, a raw audio stream, or a "system" stream (which mixes
multiple channels of audio and video together). The <acronym class="acronym">MPEG</acronym> file formats are
designed to be a continuous stream that you can start receiving and begin
decoding after you find a sync token. With streaming (or broadcasting if
you prefer) as a major design goal, <acronym class="acronym">MPEG</acronym> does not have many of the
features that other media file formats have.
</p><p>
The different <acronym class="acronym">MPEG</acronym> file formats all have some common deficiencies. The
first is that none of them have a good header identifier that is even
moderately unique. Instead, <acronym class="acronym">MPEG</acronym> files have header identifiers like <code class="literal">0x1ba</code>
(system stream), <code class="literal">0x1b3</code> (video stream), and <code class="literal">0xfff</code> (audio stream). These
values are atrocious identifiers because they are far too easy to
encounter in chunks of non-<acronym class="acronym">MPEG</acronym> data.
</p><p>
Next, the structure of <acronym class="acronym">MPEG</acronym> streams is tightly bit-packed and does not
lend itself well to easy parsing. For example, to extract the clock value
from a video stream, you have to do this:
</p><pre class="programlisting c">
<code class="varname">t</code> = ((((<span class="type">bigtime_t</span>)(<code class="varname">stream</code>[0] &amp; 0x0e)) &lt;&lt; 30) |
(((<span class="type">bigtime_t</span>)(<code class="varname">stream</code>[1] &amp; 0xff)) &lt;&lt; 22) |
(((<span class="type">bigtime_t</span>)(<code class="varname">stream</code>[2] &amp; 0xfe)) &lt;&lt; 15) |
(((<span class="type">bigtime_t</span>)(<code class="varname">stream</code>[3] &amp; 0xff)) &lt;&lt; 8) |
(((<span class="type">bigtime_t</span>)(<code class="varname">stream</code>[4] &amp; 0xfe)) &lt;&lt; 0));
</pre><p>
As Dave Bort would say: Crazy.
</p><p>
Another area that poses big problems when dealing with <acronym class="acronym">MPEG</acronym> is that a lot
of <acronym class="acronym">MPEG</acronym> data is not well formatted. There's what the spec says and then
there is what's done. This is the kind of problem you come to expect when
dealing with data files generated by lots of programs, but <acronym class="acronym">MPEG</acronym> seems to
have the problem in spades. The highly varied interpretations of what the
spec says make trying to parse <acronym class="acronym">MPEG</acronym> files an interesting job.
</p><p>
Getting beyond my childlike whining, <acronym class="acronym">MPEG</acronym> does have a notion of chunks of
data, although the size of each chunk is not usually specified as a
4-byte value in the stream of data. Instead, the size is either implied
or determined by continuing to read the data until you find the start of
the next chunk.
</p><p>
The inherent streaming nature of <acronym class="acronym">MPEG</acronym> means that there are no global
indices of file positions to frames or to where the sync points are in
the file. This complicates life for BeOS because a program often wants to
know how long a media file is so it can display a properly sized slider
or progress bar. To make that work, <code class="classname">BMediaFile</code>
pre-parses the <acronym class="acronym">MPEG</acronym> video
stream so that it knows where sync points are in the file and roughly how
many frames there are. The disadvantage of doing this is that it can take
a long time to read through all the data first before decoding any of the
frames. Our current approach of pre-parsing the entire <acronym class="acronym">MPEG</acronym> stream is not
something we're happy with and we plan to rewrite it.
</p><p>
From a media programmer's standpoint the important lesson here is that
the number of frames returned from
<code class="code"><code class="classname">BMediaTrack</code>-&gt;<code class="methodname">CountFrames()</code></code>
is only an approximate number.
<code class="code"><code class="classname">BMediaTrack</code>-&gt;<code class="methodname">Duration()</code></code>
is also only an approximate
value. This may be more or it may be less due to the imprecise nature of
file formats like <acronym class="acronym">MPEG</acronym>. Therefore, when you write a piece of code to
process all the frames in a file you should write the loop this way:
</p><pre class="programlisting c">
while(1) {
<span class="type">status_t</span> <code class="varname">err</code>;
<code class="varname">err</code> = <code class="varname">track</code>-&gt;<code class="methodname">ReadFrames</code>();
if (<code class="varname">err</code> == <code class="constant">B_LAST_BUFFER_ERROR</code>) <span class="comment">// all done!</span>
break;
else if (<code class="varname">err</code> == ...) <span class="comment">// handle other errors</span>
....
else if (<code class="varname">err</code> == <code class="constant">B_NO_ERROR</code>)
render the frames...
}
</pre><p>
or some variant of that approach (a good example is what the tcode
program does). You should NEVER code a for loop that counts up to the
number of frames returned by <code class="methodname">CountFrames()</code>.
</p><p>
Another interesting twist is that in QuickTime and some <acronym class="acronym">AVI</acronym> files a
single frame may be displayed for longer than the frame rate of the file.
That is, a single frame is displayed for several seconds instead of
repeating the frame's data in the file. Therefore, if you need to display
a scroll bar or slider, the <code class="methodname">Duration()</code> of the track is a better estimate
of how long it really is. The correct thing to do is to use <code class="methodname">Duration()</code> as
an estimate and dynamically adjust scrollbars and sliders until you get a
<code class="constant">B_LAST_BUFFER_ERROR</code>.
</p><p>
It's important to remember that <code class="methodname">CountFrames()</code>
and <code class="methodname">Duration()</code> are not
guaranteed to be precise and thus should NOT be used as an upper limit in
a <code class="code">for</code> or <code class="code">while</code> loop. This imprecision is certainly frustrating for
programmers but it's a fact of life that needs to be dealt with.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id437122"></a>From Chunks To Tracks</h3></div></div></div><p>
Now that we understand the concept of chunks, it's time to talk about
what's in some of those chunks. In QuickTime and <acronym class="acronym">AVI</acronym> there is a notion of
a media track that contains audio or video data. In a QuickTime file the
'trak' chunk indicates a media track. In <acronym class="acronym">AVI</acronym> there is the 'avih' chunk,
which contains global information about the entire movie, and the 'vids'
chunk that contains details about the video track.
</p><p>
The QuickTime file format is much more flexible than <acronym class="acronym">AVI</acronym> and allows any
number of tracks, each with its own frame rate and data type. QuickTime
tracks have extensive information about the presentation of the track's
contents. Video tracks even have a 3x3 transformation matrix that can be
applied to the video before compositing it into the final presentation!
</p><p>
Audio tracks in all file formats have the necessary information for
playing the audio: sample rate; size of the samples (8 or 16 bit); mono,
stereo, or multichannel, etc.
</p><p>
The track header also specifies how the track's data is encoded. For
example, Cinepak-encoded data is identified with a fourcc of "cvid."
Indeo-5 encoded data is identified with a fourcc of 'iv50'. You can find
a very complete list of fourcc codes from <acronym class="acronym">AVI</acronym> files at:
</p><p>
<a class="ulink" href="http://www.fourcc.org/">http://www.fourcc.org/</a>
</p><p>
Audio track information usually uses a single integer to identify which
codec to use. In a <acronym class="acronym">WAV</acronym> files for example, raw audio is indicated by the
integer value 1, and <acronym class="acronym">MS-ADPCM</acronym> (Microsoft's Adaptive Pulse Code
Modulation) is identified by a 2. <acronym class="acronym">AVI</acronym> uses the same identifiers as <acronym class="acronym">WAV</acronym>.
QuickTime uses its own set of fourcc's, of course.
</p><p>
Another good reference for <acronym class="acronym">AVI</acronym>/<acronym class="acronym">WAV</acronym> file codec id's is RFC-2361:
</p><p>
<a class="ulink" href="http://www.faqs.org/rfcs/rfc2361.html">http://www.faqs.org/rfcs/rfc2361.html</a>
</p><p>
In addition to the information about a track, a file format will also
have information about where in the file the data can be found. Typically
this takes the form of a table that indexes frames and file positions.
Using this information, you can extract the stream of data that makes up
a particular track. In the abstract, most media files look something like
this:
</p><pre class="screen">
+--------+------------+------------+-----+------------+------
| header | track 1 | track 2 | ... | track 1 | ...
| info | data chunk | data chunk | | data chunk |
+--------+------------+------------+-----+------------+------
</pre><p>
What you effectively have are interleaved chunks of data for each track.
In practice, you usually have a single audio track and a single video
track. Because the data rate of audio is far less than video, you often
have a single audio chunk followed by several video chunks, followed by
another audio chunk, etc.
</p><p>
If we were to split out the data chunks for each track and put them
together, we would have a contiguous stream of data for each track.
Again, it's important to remember that the file format doesn't care what
the data is in a track or how it's encoded, it only cares about the
ordering of the chunks of data and what frame (or frames) they correspond
to.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id437267"></a>From Tracks To Your Eyes And Ears</h3></div></div></div><p>
Now that we know what a track is, let's discuss how we get from the bits
of data that belong to a track to something that we see on screen (or
hear). If you're programming on BeOS you simply use the <code class="classname">BMediaTrack</code>
object to get access to the data that makes up a track. The <code class="classname">BMediaTrack</code>
object will let you access the decoded data for the track with
<code class="methodname">ReadFrames()</code>, or you can choose to get the encoded
data with <code class="methodname">ReadChunk()</code>.
</p><p>
What's going on behind the scenes is that when you want to decode a frame
from a media file, you have to first look up in the index where that
frame's data lives on disk. Once you know that, you can seek there, read
the encoded data into memory and hand it off to a decoder. This process
(and doing it efficiently) is the bulk of the work that <code class="classname">BMediaFile</code> and
<code class="classname">BMediaTrack</code> do.
</p><p>
The index that maps frames to on-disk locations can be very simple, as it
is in <acronym class="acronym">AVI</acronym>, or much more complex, as it is in QuickTime. In QuickTime each
frame can have a different duration, can appear anywhere in the file, and
QuickTime index/mapping tables support complex arrangements of the data
(presumably to allow it to be optimized for slow devices such as
cd-roms). In most cases the QuickTime approach is massive overkill. Aside
from the ability to change the duration of individual frames, the extra
functionality that QuickTime offers is almost never used.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id437333"></a>Decoders</h3></div></div></div><p>
Above we described conceptually how we get the encoded data out of a file
format. Once we've retrieved the encoded data for a particular frame, we
hand it off to a decoder. The decoder's job is to convert that encoded
video into a decoded format that the client (the person calling
<code class="code"><code class="classname">BMediaTrack</code>-&gt;<code class="methodname">ReadFrames()</code></code>)
can deal with. Most simple video compression
formats read the chunk, decode it into the output buffer, and are done.
More complex codecs have state information that they must maintain from
frame to frame. Indeo5 and <acronym class="acronym">MPEG</acronym> are two examples of encodings that
require lots of state information to be able to decode a frame.
</p><p>
Let's now delve into a few more details about different video and audio
codecs. This is the list of encoded media formats supported by the
upcoming BeOS Release 5. There are, of course, many more encodings out
there—these are just the ones we support:
</p><p>
</p><div class="informaltable"><table border="1"><colgroup><col align="right" /><col align="left" /></colgroup><thead><tr><th align="right">Encoded Video Format</th><th align="left">Comment</th></tr></thead><tbody><tr><td align="right">Cinepak</td><td align="left">expensive to encode; fast to decode</td></tr><tr><td align="right"><acronym class="acronym">PJPEG</acronym>/<acronym class="acronym">MJPEG</acronym></td><td align="left">decent quality; common hardware capture format</td></tr><tr><td align="right"><acronym class="acronym">MPEG</acronym>-1</td><td align="left">good quality; very widespread</td></tr><tr><td align="right">Indeo-5</td><td align="left">decent quality; can encode in real time</td></tr><tr><td align="right">DV</td><td align="left">constant bitrate; FireWire/i.Link video format</td></tr><tr><td align="right">Apple Video</td><td align="left">not widely used; low quality</td></tr><tr><td align="right">MS Video</td><td align="left">not widely used; low quality</td></tr><tr><td align="right">MS RLE</td><td align="left">not widely used; low quality</td></tr><tr><td align="right">raw</td><td align="left">heavy bandwidth requirements; perfect quality</td></tr></tbody></table></div><p>
</p><p>
</p><div class="informaltable"><table border="1"><colgroup><col align="right" /><col align="left" /></colgroup><thead><tr><th align="right">Encoded Audio Format</th><th align="left">Comment</th></tr></thead><tbody><tr><td align="right"><acronym class="acronym">MS-ADPCM</acronym></td><td align="left">compress 16-bit audio into 4-bit; good quality</td></tr><tr><td align="right">CCITT-ADPCM</td><td align="left">compress 16-bit audio into 4-bit; good quality</td></tr><tr><td align="right">ima4</td><td align="left">compress 16-bit audio into 4-bit; good quality</td></tr><tr><td align="right">ulaw</td><td align="left">compress 12-bit audio into 8-bit; OK quality</td></tr><tr><td align="right">raw</td><td align="left">perfect quality</td></tr><tr><td align="right">mpeg-1 layer 1,2,3</td><td align="left">great compression (10:1); insanely popular</td></tr></tbody></table></div><p>
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id437611"></a>Video Codec Background</h3></div></div></div><p>
Before we jump into the details of each video codec, let's step back for
a second and discuss some general terminology. A video codec encodes data
using an algorithm to transform an uncompressed frame or field of video.
The algorithm may operate on each frame independently or it may require
information about previous frames. If an encoder encodes each frame
independently, then when you want decode a frame you can do it without
having to look at any other frames. In this situation each frame is
treated as a keyframe (i.e., it can be decoded completely without
requiring other data) and, therefore, you can seek to any frame in the
file (aka: a perfectly seekable file).
</p><p>
If the encoding algorithm requires information about previous frames to
encode the current frame, then when decoding the data you must first
decode the prior frames leading up the frame you want. This type of
encoder will output a "keyframe" every so often as a sync point in the
file. Remember—a keyframe can be decoded independently of any other
frames. So for example, the output of an encoder could be this: 1
keyframe, 10 delta frames, 1 keyframe, 10 delta frames, etc. That means
that you can't seek to any arbitrary frame; you have to seek to a
keyframe and then play forward to reach the frame you want.
</p><p>
Most sophisticated encoding algorithms almost always have keyframes every
10-20 frames. The algorithms take advantage of the temporal cohesion
between each frame in video data. Keyframes are the reason that you can
not seek to an arbitrary position in most video files. <code class="classname">BMediaFile</code> has
support for seeking to the closest keyframe (ahead of or behind) to the
frame you wanted. This feature did not work very well in Release 4.5 but
will work much better in Release 5. If you require seeking to the exact
frame you requested, you need to iterate calling <code class="methodname">ReadFrames()</code> until you
get to the frame you want.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id437646"></a>Video Codec Descriptions</h3></div></div></div><p>
Video codecs each have different properties that make them more or less
suited to different tasks. Cinepak, for example, is definitely not a
real-time capture type of encoder. It can take several seconds to encode
a frame. A stream of Cinepak data always starts with a keyframe and is
followed by delta frames and then usually another keyframe (then the
cycle repeats). Some Cinepak-encoded videos only have a single keyframe,
as the first frame and everything else is just deltas. Decoding Cinepak
data, though, takes very little time, making it a good playback format
(for lower-end machines, etc.).
</p><p>
<acronym class="acronym">PJPEG</acronym> (aka Photo-<acronym class="acronym">JPEG</acronym>) encodes each frame as a <acronym class="acronym">JPEG</acronym> image. This works
reasonably well but does not take advantage of the temporal coherence in
video. You can find fast (real time, even) <acronym class="acronym">PJPEG</acronym> encoders (such as the
one that ships with personalStudio). <acronym class="acronym">MJPEG</acronym> is <acronym class="acronym">JPEG</acronym> encoding of fields of
video (remember though—frames are not the same as fields with video!).
<acronym class="acronym">PJPEG</acronym> is a good format for editing, because each frame is independent
(i.e., a keyframe) so perfect seeking is possible.
</p><p>
One (simplistic) way to describe <acronym class="acronym">MPEG</acronym>-1 is to think of it as <acronym class="acronym">PJPEG</acronym> frames
interspersed with deltas between the <acronym class="acronym">PJPEG</acronym> frames. <acronym class="acronym">MPEG</acronym>-1 encodes frames
into one of three types: I-frames, P-frames, or B-frames (there are also
D frames but these are extremely rare). I-frames are keyframes, P frames
are "predicted" frames, and B frames are bi-directional frames. You can
only seek to I-frames (and if an <acronym class="acronym">MPEG</acronym>-1 file had only I-frames it would
be roughly equivalent to a <acronym class="acronym">PJPEG</acronym> file).
</p><p>
Indeo-5 encoding is similar <acronym class="acronym">MPEG</acronym> in that it uses temporal cohesion to
improve the compression rate, but there isn't a lot of information about
the specifics of Indeo-5 encoding. Indeo-5 encoding can be done in real
time if you have the right encoder (not currently on BeOS). The quality
of Indeo-5 is decent, although not as good as a good <acronym class="acronym">MPEG</acronym>-1 encoder.
Indeo-5 is a good distribution format but not the best as an editing
format.
</p><p>
<acronym class="acronym">DV</acronym> (Digital Video) encoding is the encoding used by DV cameras (duh!)
that communicate with a computer over 1394 (aka Sony i.Link, aka
Firewire). <acronym class="acronym">DV</acronym> is a good encoding: it's high quality; has constant
bit-rate (about 4 megs/sec); and it can be decoded in real time in
software. <acronym class="acronym">DV</acronym> is also a good format for editing because every frame is
independent.
</p><p>
The Apple Video, MS Video, and MS <acronym class="acronym">RLE</acronym> formats are all old video encodings
that don't have good quality compared to the newer codecs. Essentially
they're all variants of run-length encoding.
</p><p>
Raw video isn't really an "encoding" but it is an option for storing
video if you have the disk bandwidth. Typically, you can capture raw
320x240 size video to a standard <acronym class="acronym">IDE</acronym> hard disk. Capturing 640x480 video
at 16 bits per pixel requires about 17.57 megabytes/second bandwidth.
That means that you need either a super-fast single drive or a striped
disk setup. You can store raw video in a variety of pixel formats
(<acronym class="acronym">RGB</acronym>-32, <acronym class="acronym">RGB</acronym>-16, <acronym class="acronym">YUV</acronym>-422,
etc.). Both <acronym class="acronym">AVI</acronym> and QuickTime support raw
video; <acronym class="acronym">MPEG</acronym> does not. Raw video is perfectly seekable and is the best
format for editing if you can handle it.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id437810"></a>Audio Codec Descriptions</h3></div></div></div><p>
The <acronym class="acronym">MS-ADPCM</acronym>, <acronym class="acronym">CCITT-ADPCM</acronym> and
<acronym class="acronym">ima4</acronym> encoders are all variants of Adaptive
Pulse Code Modulation encoding. Essentially, they encode 16-bit audio
into 4-bit chunks in a mostly lossless manner. The encoding process isn't
terribly CPU intensive and decoding is quite cheap. You probably wouldn't
want to use these formats for editing but for final distribution they
work well.
</p><p>
<acronym class="acronym">uLaw</acronym> encoding compresses 12-bit audio into an 8-bit quantity. The quality
is usually poor (phone-line quality) but the encode and decode are
extremely cheap. Old timers may remember the original SparcStation-1 had
a <code class="filename">/dev/audio</code> that spat out (and accepted) uLaw-encoded data. And before I
get flamed, yes, the NeXT Cube did it too. BeOS only supports decoding
this format.
</p><p>
<acronym class="acronym">MPEG</acronym>-1 audio is a sophisticated encoding scheme that uses psycho-acoustic
models, <acronym class="acronym" title="Discrete Cosine Transform"><a class="ulink" href="http://en.wikipedia.org/wiki/Discrete_cosine_transform">DCT</a></acronym>
transforms, and <a class="ulink" href="http://en.wikipedia.org/wiki/Huffman_coding">Huffman encoding</a>
to compress audio, typically
around 10 to 1. <acronym class="acronym">MPEG</acronym>-1 audio has three layers, called, appropriately
enough, layers 1, 2, and 3. <acronym class="acronym">MPEG</acronym>-1 layer 3 audio is commonly known as
MP3. There is also a layer 2.5 for low bit rates but it is not terribly
common. <acronym class="acronym">MPEG</acronym>-1 audio can be encoded with a wide variety of options
(different data rates, stereo/mono, different sample sizes and sample
rates). By far the most common form of <acronym class="acronym">MPEG</acronym>-1 audio data is 128kbs data
rate 44.1 khz stereo data (it's the format that 90% of your pirated mp3's
are in). <acronym class="acronym">MPEG</acronym>-1 audio is typically quite expensive to encode. Decoding
doesn't take a lot of CPU time relative to video but it's much more than
any of the other audio encodings. <acronym class="acronym">MPEG</acronym>-1 audio is a great format to
distribute audio in because of its wide acceptance and excellent
compression ratio.
</p><p>
As with video, raw audio provides perfect quality. Raw audio can be
stored in a variety of sample rates (11,000 hz, 22050 hz, 32000 hz, 44100
hz, 48000 hz) and in a variety of sample sizes (8-bit, 16-bit, or even
32-bit). Fortunately, raw audio doesn't have the bandwidth requirements
of video, so storing it on a standard hard disk is easy. Editing audio is
always best when done with raw audio.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id437927"></a>Wrapping Up</h3></div></div></div><p>
That about covers our tour of media file formats and codecs. I know that
this is a bit short but the subject really is vast and we don't have the
space to write all that should be written about it.
</p><p>
The two most important points that I hope people take away from this
article are these:
</p><ul class="itemizedlist"><li><p>
<acronym class="acronym">MPEG</acronym> makes life difficult because it's difficult to know exactly how
many frames are in a file until you've read them all. Therefore, you
must code processing loops to end when they receive the error
<code class="constant">B_LAST_BUFFER_ERROR</code> (even if you're not planning to deal with <acronym class="acronym">MPEG</acronym>
files).
</p></li><li><p>
Most video formats (and some audio formats) use keyframes, which
makes seeking imprecise. When seeking is imprecise you may ask to seek
to frame 35 but only get frame 30.
</p></li></ul></div></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="DevWorkshop5-4"></a>Developers' Workshop: The BeOS—The Rescue OS</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Daniel</span> <span class="surname">Switkin</span></span></div></div></div><p>
I recently went hiking with my Nikon digital camera. After capturing a
few memorable shots, I headed home to examine my handiwork. I downloaded
the images to my hard drive and deleted them from the Compact Flash card.
I happened to be viewing one particular image in ArtPaint and in Retouch,
a photo manipulator I'm writing. I decided to scale it down to 640x480
from 1600x1200 to post it on the web. I then promptly overwrote my
original file by being too quick with my keyboard shortcuts.
</p><p>
So here's the setup: the image was gone from the camera, it was
overwritten on disk, undo was off in <span class="application">ArtPaint</span> (these are 8 meg images),
and my app doesn't have a save feature yet. The only place the original
image existed was in a <code class="classname">BBitmap</code> in Retouch. A good challenge. Not to be
thwarted, I did the following:
</p><pre class="programlisting cpp">
#include &lt;OS.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;File.h&gt;
#define <code class="constant">APP_SERVER_TEAM</code> 15
#define <code class="constant">APP_IN_TROUBLE</code> "Retouch"
<span class="type">int</span> main(<span class="type">int</span> <code class="parameter">argc</code>, <span class="type">char **</span><code class="parameter">argv</code>) {
<span class="type">int32</span> <code class="varname">cookie</code> = 0;
<span class="type">area_info</span> <code class="varname">info</code>;
while (get_next_area_info(<code class="constant">APP_SERVER_TEAM</code>, &amp;<code class="varname">cookie</code>,
&amp;<code class="varname">info</code>) == <code class="constant">B_OK</code>) {
if (strstr(<code class="varname">info</code>.<code class="varname">name</code>, <code class="constant">APP_IN_TROUBLE</code>) != <code class="constant">NULL</code>) {
printf("\nName is %s\nArea ID is %d\nSize is %d
or 0x%x\nAddress is 0x%x", info.name,
<code class="varname">info</code>.<code class="varname">area</code>, <code class="varname">info</code>.<code class="varname">size</code>, <code class="varname">info</code>.<code class="varname">size</code>,
<code class="varname">info</code>.<code class="varname">address</code>);
if (strstr(<code class="varname">info</code>.<code class="varname">name</code>, "RWHeap") == <code class="constant">NULL</code>)
continue;
printf("Found RWHeap for %s\n", <code class="constant">APP_IN_TROUBLE</code>);
<span class="type">int32</span> <code class="varname">address</code> = 0;
<span class="type">int32 *</span><code class="varname">address_pointer</code> = &amp;address;
<span class="type">area_id</span> <code class="varname">cloned_area</code> = clone_area("Clone",
(void **)&amp;<code class="varname">address_pointer</code>, <code class="constant">B_ANY_ADDRESS</code>,
<code class="constant">B_READ_AREA</code>, <code class="varname">info</code>.<code class="varname">area</code>);
if (<code class="varname">cloned_area</code> &lt; 0) {
printf("Clone failed: %s\n",
strerror(<code class="varname">cloned_area</code>));
return 1;
}
<span class="type">area_info</span> <code class="varname">cloned_info</code>;
if (get_area_info(<code class="varname">cloned_area</code>, &amp;<code class="varname">cloned_info</code>) !
= <code class="constant">B_OK</code>) {
printf("get_area_info failed\n");
return 1;
}
<code class="classname">BFile</code> <code class="varname">file</code>("/boot/home/src/Fun/rescue.rw",
<code class="constant">B_WRITE_ONLY</code> | <code class="constant">B_CREATE_FILE</code>);
if (<code class="varname">file</code>.<code class="methodname">InitCheck()</code> == <code class="constant">B_OK</code>) {
<span class="type">int32</span> <code class="varname">size</code> = <code class="varname">file</code>.<code class="methodname">Write</code>(<code class="varname">cloned_info</code>.<code class="varname">address</code>,
<code class="varname">cloned_info</code>.<code class="varname">size</code>);
printf("Wrote %d bytes out of %d bytes\n",
<code class="varname">size</code>, <code class="varname">cloned_info</code>.<code class="varname">size</code>);
} else printf("Could not create file\n");
delete_area(<code class="varname">cloned_area</code>);
}
}
return 0;
}
</pre><p>
This dumped the entire contents of the read-write area for Retouch out of
the App Server into a file. Running ps told me what team number to search
for. So now the raw data of my photograph was on disk, which is good, but
somewhere in a 46 megabyte file, which is bad. Hmmm.
</p><p>
I fired up Magnify, and found the RGB values of the first three pixels in
the top left corner. After converting these to hex, adding <code class="literal">0xff</code> for the
alpha channel, and writing them out as BGRA (the order of a <code class="constant">B_RGB32</code>
bitmap) I ran:
</p><pre class="screen">
hd rescue.rw | grep -1 "a3 a5 94 ff a3 a9 95 ff"
</pre><p>
to find every occurrence of the first two pixels. This only turned up a
few hits, and the third pixel narrowed it down to one location. I then
hacked up the following to write a Targa header, seek into the rescue.rw
file, and dump the image contents to my new image:
</p><pre class="programlisting cpp">
#include &lt;File.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#define <code class="constant">LOCATION</code> 0x01d4f368
#define <code class="constant">SIZE</code> 7680000
<span class="type">int</span> main(<span class="type">int</span> <code class="parameter">argc</code>, <span class="type">char **</span><code class="parameter">argv</code>) {
<code class="classname">BFile</code> <code class="varname">in</code>("/boot/home/src/Fun/rescue.rw", <code class="constant">B_READ_ONLY</code>);
if (<code class="varname">in</code>.<code class="methodname">InitCheck()</code> != <code class="constant">B_OK</code>) {
printf("Could not load file\n");
return 1;
}
<span class="type">unsigned char</span> <code class="varname">header</code>[18];
memset(<code class="varname">header</code>, 0, 18);
<code class="varname">header</code>[2] = 2;
<code class="varname">header</code>[12] = 1600 % 256;
<code class="varname">header</code>[13] = 1600 / 256;
<code class="varname">header</code>[14] = 1200 % 256;
<code class="varname">header</code>[15] = 1200 / 256;
<code class="varname">header</code>[16] = 32;
<code class="varname">header</code>[17] = 0x28;
<code class="classname">BFile</code> <code class="varname">out</code>("/boot/home/src/Fun/rescue.tga",
<code class="constant">B_WRITE_ONLY</code> | <code class="constant">B_CREATE_FILE</code>);
if (<code class="varname">out</code>.<code class="methodname">InitCheck()</code> != <code class="constant">B_OK</code> ||
<code class="varname">out</code>.<code class="methodname">Write</code>(<code class="varname">header</code>, 18) != 18) {
printf("Could not write file\n");
return 1;
}
if (<code class="varname">in</code>.<code class="methodname">Seek</code>(<code class="constant">LOCATION</code>, <code class="constant">SEEK_SET</code>) != <code class="constant">LOCATION</code>) {
printf("Could not seek to %d\n", <code class="constant">LOCATION</code>);
return 1;
}
<span class="type">int</span> <code class="varname">size</code> = 1 &lt;&lt; 16;
<span class="type">char *</span><code class="varname">buffer</code> = (<span class="type">char *</span>)malloc(<code class="varname">size</code>);
if (<code class="varname">buffer</code> == <code class="constant">NULL</code>) {
printf("Could not allocate memory\n");
return 1;
}
<span class="type">int</span> <code class="varname">total_size</code> = <code class="constant">SIZE</code>;
while (<code class="varname">total_size</code> &gt; <code class="varname">size</code>) {
<code class="varname">in</code>.<code class="methodname">Read</code>(<code class="varname">buffer</code>, <code class="varname">size</code>);
<code class="varname">out</code>.<code class="methodname">Write</code>(<code class="varname">buffer</code>, <code class="varname">size</code>);
<code class="varname">total_size</code> -= <code class="varname">size</code>;
}
if (<code class="varname">total_size</code> != 0) {
<code class="varname">in</code>.<code class="methodname">Read</code>(<code class="varname">buffer</code>, <code class="varname">total_size</code>);
<code class="varname">out</code>.<code class="methodname">Write</code>(<code class="varname">buffer</code>, <code class="varname">total_size</code>);
}
free(<code class="varname">buffer</code>);
return 0;
}
</pre><p>
Conveniently, true color Targa data is little endian, so I could dump the
data directly (this is also why taking a screenshot in BeOS writes a .tga
file).
</p></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Gassee5-4"></a>What Now?</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Jean-Louis</span> <span class="surname">Gassée</span></span></div></div></div><p>
First, two words of apology. One for being unable to answer all the
e-mail I've received in recent days at my own jlg@be.com account, or
through the info@be.com feed—I get to keep an eye on the daily flow of
queries. I hope this column will help address most questions regarding
last week's announcement. Second, the timing and manner of disclosure.
The SEC frowns upon what is called "selective disclosure," a practice by
which a subset of "select" individuals get material information before
the market at large. In other words, we have to make sure we disseminate
material information via a medium that provides timely and reasonably
broad dissemination.
</p><p>
In our case, we use a press release on Business Wire. Perish the thought,
but should an enthusiastic CEO discuss product plans in too much detail
at some industry conference, a publicly traded company would have to rush
out a press release the same day in order to put all buyers and sellers
of its stock on the same footing.
</p><p>
But enough of that. Let's go right to the heart of the matter: Why are we
shifting resources to Internet appliances, and what does it mean for what
is commonly referred to as "the desktop?" Once upon a time, four or five
years ago, if memory serves, Vint Cerf, one of the true fathers of the
Internet, was on the cover of Boardwatch magazine. The
professorial-looking Mr. Cerf proudly modeled a t-shirt bearing a simple
motto: "IP on Everything". I remembered thinking, right, Coke, with a
capital C, has gone to his head, as in pinging the proverbial soft-drink
machine in a university dormitory. What's next? IP-enabled refrigerators?
</p><p>
Cut to January 2000 football commercials where the repairman comes to
your house for your refrigerator. But it's not on the fritz. Not yet,
goes the penetrating answer. The not-so-subtle subtext here is that we've
entered the "everything connected" era where, yes, your IP-enabled fridge
will report incipient trouble and get it fixed before the contents of the
freezer spoil.
</p><p>
We agree, this is the post-PC revolution, a new phase for our industry,
when all the objects in our daily lives will be connected to the
Internet, with or without wires. At each previous phase, mainframes to
minis, minis to personal computers, we've seen tremendous growth in the
number of people and devices. We'll see a similar phase change in the
post-PC era. Last spring, Michael Dell saw two billion devices connected
to the Net by 2002 or 2003, with PCs accounting for 700 million of the
number. Since then, most industry analysts have upped the forecast and
agreed that Vint Cerf's "IP on Everything" vision was becoming a market
reality. "Everything" should probably refer to objects ranging from
watches to TVs, from cars to video recorders, and from stereos on Net
steroids to refrigerators, security systems, wireless tablets, PDAs,
telephones, and whiteboards.
</p><p>
Let's immediately qualify this by referring to the early days of
marketing a new invention, the telephone. The (urban?) legend has it that
the telephone was promoted as a means to listen to opera performances and
theater plays. The concept of a worldwide web of telephone wires allowing
anyone to call anyone any time was unimagined. How could anyone have seen
the consequences of the telephone on our lives? It didn't change our DNA,
but it is interwoven, "webbed," says the thesaurus, into our lives. Now,
we're beginning to weave a new generation of IP-enabled devices into our
culture. As we do this, we have to keep in mind the difficulties in
foreseeing the impact of the telephone and expect similar surprises with
"IP on Everything".
</p><p>
Moving on to BeOS, we have OS technology that combines many desirable
features for the new breed of applications. Unlike embedded systems
running under the hood of a car, these new appliances need strong
multimedia capabilities. BeOS offers a small footprint and a modern,
robust, modular, customizable solution for these applications. As
disclosed in several of last quarter's announcements, customers and
partners have validated our offering and we're planning a more formal
announcement of what we refer to as Stinger, a complete software solution
for Internet appliances. We'll be providing details at the upcoming
introduction event.
</p><p>
So far, we have an exciting emerging market and a product for it. Looking
at the market again, we see no 800-pound gorilla monopolizing it. Rather,
we see a very fluid situation, fast growth, and we see the opportunity to
become a mainstream player. That is why we've decided to shift our
resources to that opportunity, to the goal of establishing BeOS as the
premier OS platform for media- rich Internet devices. "Our resources," in
the previous sentence, includes the desktop BeOS. In support of our
Internet appliances effort, the desktop BeOS plays two roles, both vital.
The first role is the development system for Stinger-based products,
offering the advantages of a native environment, already well-tested,
and, if I may say so, well-liked. Then, by offering a free version of
BeOS available for download, we advertise our technology on the widest
billboard known to humankind, the Internet. The goals are to gain
visibility, market testing and feedback and to inspire developers to
create new types of Internet appliance devices using Be technology. As a
result, we'll continue to issue updates and new releases for the desktop
BeOS in support of its role in our appliances strategy. For example, as
new drivers and features are developed for Stinger-based products, BeOS
desktop will gain driver compatibility and features.
</p><p>
I realize there is concern that we'll "ditch" the desktop, and I accept
the fact that a shift in strategy always creates uncertainty. Only our
actions over time can allay those concerns.
</p><p>
Fortunately, it's not entirely up to us. We intend to work with
publishers and other partners to make commercial versions of BeOS 5
available through retail channels. This allows us to refocus the energies
we previously applied to our own retail distribution efforts. We've
received a number of calls from all over the world expressing interest in
BeOS 5. Several software developers are interested in bundling BeOS with
their applications and others want to publish and ship BeOS 5 itself.
I've even heard a comment to the effect one company wants to be the Red
Hat of the BeOS. I like the sentiment, and I'll let the legal eagles have
fun with the putative motto.
</p><p>
As I have been required to do in the past, I must inform you that many of
the statements I have made here are forward-looking in nature. That is,
statements that are not historical facts are "forward-looking
statements," including without limitation my statements regarding the
future growth of the Internet appliance market, future availability and
performance of Internet appliances and third party applications, plans
for product development and release, the future capabilities of our
products or other products mentioned herein, the market acceptance of our
products, and our ability to penetrate and capture the emerging Internet
appliance markets. Actual events or results may differ materially as a
result of risks facing Be Incorporated or actual results differing from
the assumptions underlying such statements. Such risks and assumptions
include, but are not limited to, risks related to the growth of the
market for Internet appliances, our ability to establish and maintain
strategic relationships, our ability to develop and engineer
modifications to BeOS, and the competition and market acceptance of BeOS.
All such forward-looking statements are expressly qualified in their
entirety by the "Risk Factors" and other cautionary statements included
in Be Incorporated's prospectus, filed pursuant to Rule 424(b) of the
Securities Act of 1933 on July 20, 1999 (Commission File No. 333- 77855),
and other public filings with the Securities and Exchange Commission.
</p></div></div><div id="footer"><hr /><div id="footerT">Prev: <a href="Issue5-3.html">Issue 5-3, January 19, 2000</a>  Up: <a href="volume5.html">Volume 5: 2000</a>  Next: <a href="Issue5-5.html">Issue 5-5, February 2, 2000</a> </div><div id="footerB"><div id="footerBL"><a href="Issue5-3.html" title="Issue 5-3, January 19, 2000"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a href="volume5.html" title="Volume 5: 2000"><img src="./images/navigation/up.png" alt="Up" /></a> <a href="Issue5-5.html" title="Issue 5-5, February 2, 2000"><img src="./images/navigation/next.png" alt="Next" /></a></div><div id="footerBR"><div><a href="http://www.haiku-os.org"><img src="./images/People_24.png" alt="haiku-os.org" title="Visit The Haiku Website" /></a></div><div class="navighome" title="Home"><a accesskey="h" href="index.html"><img src="./images/navigation/home.png" alt="Home" /></a></div></div><div id="footerBC"><a href="http://www.access-company.com/home.html" title="ACCESS Co."><img alt="Access Company" src="./images/access_logo.png" /></a></div></div></div><div id="licenseFooter"><div id="licenseFooterBL"><a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/" title="Creative Commons License"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by-nc-nd/3.0/88x31.png" /></a></div><div id="licenseFooterBR"><a href="./LegalNotice.html">Legal Notice</a></div><div id="licenseFooterBC"><span id="licenseText">This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Creative
Commons Attribution-Non commercial-No Derivative Works 3.0 License</a>.</span></div></div></body></html>