haiku-website/static/legacy-docs/benewsletter/Issue4-17.html

496 lines
32 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 4: 1999</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="volume4.html" title="Volume 4: 1999" /><link rel="prev" href="Issue4-16.html" title="Issue 4-16, April 21, 1999" /><link rel="next" href="Issue4-18.html" title="Issue 4-18, May 5, 1999" /></head><body><div id="header"><div id="headerT"><div id="headerTL"><a accesskey="p" href="Issue4-16.html" title="Issue 4-16, April 21, 1999"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a accesskey="u" href="volume4.html" title="Volume 4: 1999"><img src="./images/navigation/up.png" alt="Up" /></a> <a accesskey="n" href="Issue4-18.html" title="Issue 4-18, May 5, 1999"><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 4: 1999</div></div><div id="headerB">Prev: <a href="Issue4-16.html">Issue 4-16, April 21, 1999</a>  Up: <a href="volume4.html">Volume 4: 1999</a>  Next: <a href="Issue4-18.html">Issue 4-18, May 5, 1999</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="Issue4-17"></a>Issue 4-17, April 28, 1999</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="Engineering4-17"></a>Be Engineering Insights: Cross Tools for x86 BeOS Development</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Fred</span> <span class="surname">Fish</span></span></div></div></div><p>
The vast majority of programmers develop applications using native
toolchains. That is, they use a set of development tools (a "toolchain")
on a single machine to compile, test, and debug their application running
on that machine.
</p><p>
A common situation, however, is where a programmer, for one reason or the
other, wants to use one machine (the host machine) to develop
applications for another machine (the target machine). A cross toolchain
is one that supports this development model.
</p><p>
One of the great features of the GNUPro tools now used in the x86 version
of BeOS is that they are designed to make it as easy as possible to
create cross toolchains in a huge variety of weird and wonderful
combinations. For example, according to the Cygnus website
<a class="ulink" href="http://www.cygwin.com/">http://www.cygwin.com/</a>, Cygwin currently supports over 125 different
configurations of native and cross- development GNUPro toolchains.
</p><p>
This article presents two cross toolchains for developing x86 BeOS R4
applications, one hosted on PPC BeOS R4 and the other hosted on RedHat
Linux 5.2. They can be found at:
</p><p>
ftp://ftp.be.com/pub/experimental/tools/gnupro-Hppc-Ti586-bin.tgz<br />
ftp://ftp.be.com/pub/experimental/tools/gnupro-Hlinux-Ti586-bin.tgz
</p><p>
To install the PPC BeOS-hosted toolchain, do something like:
</p><pre class="screen">
$ mkdir -p /boot/develop/tools
$ cd /boot/develop/tools
$ tar -xvzf gnupro-Hppc-Ti586-bin.tgz
</pre><p>
To install the Linux-hosted toolchain, do something like:
</p><pre class="screen">
$ su
Password:
# cd /usr/local
# tar -xvzf gnupro-Hlinux-Ti586-bin.tgz
</pre><p>
In either case, a directory called "gnupro" will be created and all the
relevant files will be extracted into a subtree rooted at the gnupro
directory.
</p><p>
In order to provide a concrete example of using the cross toolchain, I
have taken the BeBounce sample application from the R4 release CD and
modified it to use a <acronym class="acronym" title="GNUs Not Unix">GNU</acronym>
<span class="application">autoconf</span> generated <span class="application">configure</span> script and
associated <code class="filename">Makefile</code> template. This example can be found on the Be ftp
site at:
</p><p>
ftp://ftp.be.com/pub/experimental/tools/gnupro-example.tgz
</p><p>
When unpacked, it creates a directory "bounce" and populates it with the
sample files. To build it using the Linux- hosted cross tools you would
do something like:
</p><pre class="screen">
$ export PATH=/usr/local/gnupro/bin:$PATH
$ CXX=i586-beos-gcc configure -q
creating cache ./config.cache
updating cache ./config.cache
creating ./config.status
creating Makefile
$ make
i586-beos-g++ -c -g -O2 main.cpp
i586-beos-g++ -c -g -O2 Crunch.cpp
i586-beos-g++ -o BeBounce main.o Crunch.o -lbe
$
</pre><p>
(NOTE: for more info while configuring, don't use -q )
</p><p>
The currently supplied cross tools include a full set of x86 BeOS R4
headers and libraries, which allows you to create applications that
should run on any R4 or later release. If you are a beta tester for a
later release and want to update these files, here are the steps to
follow:
</p><p>
Create archives of the new include and lib files on an installed version
of the updated BeOS system and then transfer them to the machine where
the cross tools are installed:
</p><pre class="screen">
$ cd /boot/develop/headers
$ tar -cvzf /tmp/headers.tgz *
$ cd /boot/develop/lib/x86
$ tar -cvzhf /tmp/libs.tgz *
</pre><p>
(Note: The 'h' option to tar is very important, to ensure that symbolic
links are followed. Copy the <code class="filename">headers.tgz</code>
and <code class="filename">libs.tgz</code> files to the
machine running the cross tools.)
</p><p>
Go to the installed crosstools i586-beos directory, preserve some
installed files that are part of the toolchain itself, remove the old
includes and libs, and install the updated versions:
</p><pre class="screen">
$ cd /usr/local/gnupro/i586-beos
$ mv lib/ldscripts ldscripts-save
$ rm -rf lib include
$ mkdir lib include
$ mv ldscripts-save lib/lscripts
$ cd include
$ tar -xvzf /tmp/headers.tgz
$ cd ../lib
$ tar -xvzf /tmp/libs.tgz
</pre><p>
The source for the cross tools can be found in:
</p><p>
ftp://ftp.be.com/pub/experimental/tools/gnupro-990420-src.tgz
</p><p>
To build the tools from source, you first have to select a root directory
for the installation. For the PPC host I chose
<code class="filename">/boot/develop/tools/gnupro</code> and for the Linux host I chose
<code class="filename">/usr/local/gnupro</code>.
The following instructions assume you have chosen
<code class="filename">/usr/local/gnupro</code>, as would be a good choice on most UNIX systems. But
the actual path is completely arbitrary.
</p><p>
Next you have to install the BeOS native include and library files, in
much the same way as described earlier for updating these files in the
prepackaged toolchains. In the UNIX case, you would create
<code class="filename">/usr/local/gnupro/i586-beos</code>
and then populate an
<code class="filename">include</code> subdirectory
with the BeOS headers and a
<code class="filename">lib</code> subdirectory with the BeOS libraries;
i.e., the following need to exist and contain the headers and libraries
respectively:
</p><p>
<code class="filename">/usr/local/gnupro/i586-beos/include</code><br />
<code class="filename">/usr/local/gnupro/i586-beos/lib</code>
</p><p>
One last step before actually building the tools is to create the
toolchain's private install directory tree, which would be (again for the
Linux case):
</p><p>
<code class="filename">/usr/local/gnupro/lib/gcc-lib/i586-beos/2.9-beos-980929</code>
</p><p>
The previous step is needed for the cross compiler to be able to locate
the BeOS include files when building some of the runtime components,
since they need access to the target's include files, and these are found
relative to this path by tacking
<code class="filename">../../../../i586-beos/include</code> on to
the end of it.
</p><p>
Now you're ready to actually configure and build the cross tools. Once
you've extracted the 70 Mb or so of sources, do something like:
</p><pre class="screen">
$ cd gnupro
$ configure --prefix=/usr/local/gnupro --target=i586-beos
$ make
$ make install
</pre><p>
Of course, each of the last three steps will generate lots of output to
the terminal, but should succeed with no errors. If there are errors,
you'll have to resolve those before proceeding to the next step. After
doing <code class="command">make install</code>, your cross tools should be ready for use.
</p><p>
Regenerating the PPC-hosted cross tools is much trickier, since success
depends upon having some things from the Geek Gadgets distribution
installed. In particular, you have to use the GG <span class="application">bcc</span> front end for
mwcc, which makes mwcc behave much more like gcc. If you really feel the
need to do this, contact me directly for help, since it is not anywhere
near as straighforward as generating the Linux-hosted tools.
</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="DevWorkshop4-17"></a>Developers' Workshop: Muxamania Part 2: Better Than Haggis (The Guts of
SelectorNode)</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Owen</span> <span class="surname">Smith</span></span></div></div></div><p>
In the previous installment, I introduced a node called <code class="classname">SelectorNode</code>.
This node allows you to select one of several inputs to pass to a single
output. This week, I'd like to explore the guts of the node in a little
more detail.
</p><p>
The sample code (plus some scrubbing and general clean-up from last
week's effort) can again be found at
</p><p>
ftp://ftp.be.com/pub/samples/media_kit/Selector.zip
</p><p>
I'd like to answer two questions about timing that I encountered whilst
wrangling with the selector node for last week's diatribe. They are
</p><ul class="itemizedlist"><li><p>
When should I send a buffer that I've received?
</p></li><li><p>
How can I inform my upstream nodes when my latency changes?
</p></li></ul><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="id783629"></a>Introducing... BMediaEventLooper!</h3></div></div></div><p>
The node looks vastly different from previous nodes I've presented
because it derives from the super-nifty and newly christened
<code class="classname">BMediaEventLooper</code> (formerly known as the
<code class="classname">MotherNode</code>, and featured in
Stephen's article two weeks ago) to manage the timing of buffers and
events. Previously, timing was the trickiest part about developing nodes.
Now, it takes little more than a few method calls, and leaves you with
almost no steaming entrails. What does <code class="classname">BMediaEventLooper</code> give you?
</p><ul class="itemizedlist"><li><p>
You no longer have to write a Big Bad Service Thread to receive and
process messages on your port. The <code class="classname">BMediaEventLooper</code> creates and
maintains the service thread and control port for you (thus, very
similar to a <code class="classname">BLooper</code> in function).
</p></li><li><p>
You no longer have to worry about holding on to events until it's
time to do them (another feature of the Big Bad Service Threads of the
past). The <code class="classname">BMediaEventLooper</code> maintains
a <code class="classname">BTimedEventQueue</code>. You simply
push events onto the queue as you receive them, and the
<code class="classname">BMediaEventLooper</code> simply pops events off the queue when they become due.
</p></li><li><p>
If all your node wants is to know when Start, Stop, or Seek requests
become due, your node doesn't have to override Start, Stop, or Seek
anymore. The <code class="classname">BMediaEventLooper</code> automatically intercepts these events
and pushes them onto the queue for you.
</p></li><li><p>
Your node doesn't have to be limited to storing one Start, Stop, or
Seek at a time. The <code class="classname">BTimedEventQueue</code> allows you to stack up as many
events as your heart desires. (Can you say "automation?" I knew you
could.)
</p></li><li><p>
You can and should use <code class="methodname">BMediaEventLooper::EventLatency()</code> to store the
total latency of your node (that is, processing plus downstream
latency). <code class="classname">BMediaEventLooper</code> takes this latency into account when it
determines when to pop events off of the queue.
</p></li></ul><p>
You'll see the <code class="classname">BMediaEventLooper</code> in action when I show you how
<code class="classname">SelectorNode</code> handles buffers.
</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="id783752"></a>Handling the Buffer Binge</h3></div></div></div><p>
Thanks to <code class="classname">BMediaEventLooper</code>, most of the trickiness in getting buffers
handled at the proper time is done for you. All <code class="classname">SelectorNode</code> has to do is
filter the incoming buffers so that only the selected input's buffers get
through. What we have to determine is: when should <code class="classname">SelectorNode</code> filter
and send the buffers that it receives?
</p><p>
The easiest approach would be to filter buffers as soon as they are
received, and send them on their merry way as soon as possible, as the
following diagram illustrates:
</p><pre class="screen">
Buffer Received and Handled Buffer Is Late
| |
V V
|OOOOOO|---------------------------------------------------|
Legend:
|OO| = time spent by processing the buffer
---- = time spent by waiting
</pre><p>
For such a simple filter as Selector, this would be a passable approach.
However, a more robust node will take pains not to deal with the buffer
too early. Why? Consider the pathological case where we receive a Stop,
or are told to change the selected input, between the time we receive the
buffer (if it was sent super-early) and the time by which the buffer has
to be sent. If we have already sent the early buffer, then that buffer
will escape whatever commands we might otherwise want to apply that will
affect the buffer.
</p><p>
Another way to manage buffers would be to handle and send them as late as
possible so that they'll still arrive on time, taking only the amount of
time you need to process the buffer into account:
</p><pre class="screen">
Buffer Received Buffer Is Late
| Buffer Handled |
| | |
V V V
|---------------------------------------------------|OOOOOO|
</pre><p>
This will take care of the race condition between commands and early
buffers. However, because you've eliminated all of the slack time you
might otherwise be able to use to process buffers, your node becomes
susceptible to jitter from factors such as CPU load and disk activity,
and your buffers stand a dangerous chance of arriving late.
</p><p>
The best approach in this case is to strike a compromise. Let's take raw
audio and video into account, so that we can calculate how long our
buffers will be. In this case, we should try to handle the buffer as soon
as possible, *but not earlier than* the duration of one buffer before the
time at which the buffer must be sent:
</p><pre class="screen">
Buffer Received Buffer Handled Buffer Is Late
| | |
V V V
|-----------------------------|OOOOOO|---------------------|
&lt;----------------------------&gt;
Buffer Duration
</pre><p>
This will ensure that we properly handle commands that apply to our
buffer, but it will give us enough room to handle unpredictable delays in
processing.
</p><p>
Fortunately, with the <code class="classname">BMediaEventLooper</code>'s event queue, doing this the
right way is a snap. There are three simple steps:
</p><ul class="itemizedlist"><li><p>
We report our processing latency as being the duration of one buffer,
plus whatever our estimated scheduling jitter is for our thread.
</p></li><li><p>
We calculate our total latency by adding this processing latency to
the latency downstream.
</p></li><li><p>
Finally, we tell the <code class="classname">BMediaEventLooper</code> what our total latency is by
calling <code class="methodname">SetEventLatency()</code>, so that it pops events at the proper time.
</p></li></ul><p>
Here's what it looks like in code:
</p><pre class="programlisting cpp">
<span class="type">void</span>
<code class="classname">SelectorNode</code>::<code class="methodname">Connect</code>(..., <span class="type">media_format&amp;</span> <code class="parameter">with</code>, ...)
{
...
<span class="comment">// Calculate the processing latency.</span>
<span class="type">bigtime_t</span> <code class="varname">proc</code> = <code class="function">buffer_duration</code>(<code class="varname">with</code>);
<code class="varname">proc</code> += <code class="function">estimate_max_scheduling_latency</code>();
<span class="comment">// Calculate the downstream latency.</span>
<span class="type">bigtime_t</span> <code class="varname">downstream</code>;
<span class="type">media_node_id</span> <code class="varname">ts</code>;
<code class="methodname">FindLatencyFor</code>(<code class="varname">m output.destination</code>, &amp;<code class="varname">downstream</code>, &amp;<code class="varname">ts</code>);
<span class="type">bigtime_t</span> <code class="varname">totalLatency</code> = <code class="varname">proc</code> + <code class="varname">downstream</code>;
<span class="comment">// Tell the event queue what our new latency is.</span>
<code class="methodname">SetEventLatency</code>(<code class="varname">totalLatency</code>);
...
}
</pre><p>
Now, all we need to do is push buffers onto the queue as we receive them.
At the proper time, they'll be popped from the queue and handed to
<code class="methodname">HandleEvent()</code>, which we override to actually send the buffer.
</p><pre class="programlisting cpp">
<span class="type">void</span>
<code class="classname">SelectorNode</code>::<code class="methodname">BufferReceived</code>(<code class="classname">BBuffer</code>* <code class="varname">b</code>)
{
<span class="comment">// The B RECYCLE constant means that any buffers</span>
<span class="comment">// inadvertently left in the queue will be recycled</span>
<span class="comment">// when the queue is deleted.</span>
<code class="methodname">EventQueue()</code>-&gt;<code class="methodname">PushEvent</code>(<code class="parameter">b</code>-&gt;<code class="methodname">Header()</code>-&gt;<code class="varname">start_time</code>,
<code class="classname">BTimedEventQueue</code>::<code class="constant">B_HANDLE_BUFFER</code>, <code class="parameter">b</code>,
<code class="classname">BTimedEventQueue</code>::<code class="constant">B_RECYCLE</code>, 0);
}
<span class="type">void</span>
<code class="classname">SelectorNode</code>::<code class="methodname">HandleEvent</code>(<span class="type">bigtime_t</span> <code class="parameter">performance_time</code>,
<span class="type">int32</span> <code class="parameter">what</code>, <span class="type">const void *</span><code class="parameter">pointer</code>, <span class="type">uint32</span> <code class="parameter">cleanup</code>,
<span class="type">int64</span> <code class="parameter">data</code>)
{
switch (<code class="parameter">what</code>) {
case <code class="classname">BTimedEventQueue</code>::<code class="constant">B_HANDLE_BUFFER</code>:
{
<span class="comment">// It's time to handle this buffer.</span>
<span class="type"><code class="classname">BBuffer</code>*</span> <code class="varname">b</code> = (<span class="type"><code class="classname">BBuffer</code>*</span>) <code class="parameter">pointer</code>;
if (<code class="varname">b</code>-&gt;<code class="methodname">Header</code>()-&gt;<code class="varname">destination</code>
!= (<span class="type">uint32</span>) <code class="varname">m_selectedInput</code>)
{
<span class="comment">// This buffer doesn't belong to the</span>
<span class="comment">// selected input, so get rid of it!</span>
<code class="varname">b</code>-&gt;<code class="methodname">Recycle</code>();
break;
}
<span class="comment">// This buffer does belong to our selected</span>
<span class="comment">// input, so try to send it.</span>
if ((! <code class="methodname">IsConnected</code>()) || <code class="methodname">IsStopped</code>()
|| (! <code class="varname">m_enabled</code>) // output enabled
|| <code class="methodname">SendBuffer</code>(<code class="varname">b</code>, <code class="varname">m_output</code>.<code class="varname">destination</code>)
!= <code class="constant">B_OK</code>)
{
<span class="comment">// Either we shouldn't send the buffer</span>
<span class="comment">// or the send failed. In either case,</span>
<span class="comment">// get rid of the buffer.</span>
<code class="varname">b</code>-&gt;<code class="methodname">Recycle</code>();
}
}
break;
...
}
}
</pre><p>
This is a decent approach for the slackers out there like myself.
However, the astute observer will note one disadvantage to this approach:
your node's latency is increased to one whole buffer's duration. Stack a
few of these nodes together in a chain, and you end up with far more
latency than you really need. So, for you bean counters out there, what
you really want is to report your standard processing latency when
handling events as usual, *but* instruct the <code class="classname">BMediaEventLooper</code> to pop
buffers a buffer's duration early if it can. We're working on building
this sophistication into <code class="classname">BMediaEventLooper</code> right now, to show up in a
future release. Stay tuned, O true believers...
</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="id784325"></a>Captain Hook: Managing the Format and Connections</h3></div></div></div><p>
Another interesting part of the <code class="classname">SelectorNode</code> deals with negotiating the
format and the connections.
</p><p>
As you can see from the above, the selector node passes buffers blindly,
so it must enforce that the output connection have the same format as the
input connections. It does this by rejecting all output connections until
its first input is connected, and then uses the first input's connection
format as the non-negotiable format for all future inputs and outputs.
</p><p>
Because I'm connecting upstream nodes before downstream nodes in this
case, there's an additional complication that gets introduced. When I
begin, the node chain for my application looks something like this,
including the approximate processing latency for the downstream nodes:
</p><pre class="screen">
File Reader Selector Node Output
latency=1ms latency=50ms
</pre><p>
The first connection is made between the file reader and the selector
node. Once this is done, the file reader will see a downstream latency of
1ms, because only the selector node is currently downstream.
</p><pre class="screen">
File Reader ----------&gt; Selector Node Output
latency=1ms latency=50ms
&lt;------------------------&gt;
downstream latency=1ms
</pre><p>
The next connection is made between the file reader and the selector node:
</p><pre class="screen">
File Reader ----------&gt; Selector Node --------------&gt; Output
latency=1ms latency=50ms
&lt;-----------------------------------------------&gt;
downstream latency=51ms
</pre><p>
Because the Output is now connected, the downstream latency for the File
Reader has just increased tremendously. But there is currently no good
mechanism for the selector node to tell the File Reader that its latency
has just changed, so the File Reader still thinks its latency is only
1ms. The result? All the file reader's buffers end up arriving 50ms late!
</p><p>
We've solved this problem by adding two simple functions to the Media Kit
API for genki/5; you can see these functions in action in SelectorNode:
</p><ul class="itemizedlist"><li><p>
<code class="methodname">BBufferConsumer::SendLatencyChange()</code> informs an upstream producer
what the new downstream latency from us is.
</p></li><li><p>
<code class="methodname">BBufferProducer::LatencyChanged()</code> is the corresponding function
that's called on the upstream producer. The producer then makes
whatever adjustments are necessary to abide by the new latency.
</p></li></ul></div><p>
That's it for this week's installment. Hopefully, this week I've managed
to give you a taste of stuff we're adding to the Media Kit to make the
node writer's life easier. As always, let us know what else we can do to
pave the road for you. And have fun developing those tractor nodes!
</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="Gassee4-17"></a>Pricing Power</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>
As I learn more idioms in my adopted tongue, I'm continuously delighted
by the expressive power, the conciseness of the American lingo. There are
some atrocities, of course, such as incentivize, or unavoidable
infelicities, such as reintermediate, but the term "Pricing Power" struck
a different chord—the Internet.
</p><p>
On the radio, I heard one sage explain our current low inflation as the
result of the Internet reducing companies' Pricing Power. I thought that
was as good an explanation as any for what has come to be known as the
Internet mania. Looking a little more closely, I felt the phrase
encapsulated even more than the NPR oracle explicitly stated—but
that's how oracles are—they leave some of the hermeneutics to the
reader.
</p><p>
The seer could have said that the Internet makes comparison shopping
easier, faster, ubiquitous, universal. As a result, we have a buyer's
market, prices stay low and, voilà , low inflation. But there is a little
-- or even a lot—more. You can have low prices and low sales, but what
the frictionless movement of data also allows is faster, easier,
ubiquitous, permanent transactions. So, we have lower prices and twice
higher volumes, first because of more attractive tags, and second because
of easier purchases.
</p><p>
It looks as though this so called "Internet mania" benefits brick and
mortar companies by lowering inflation and stimulating business. In other
words, e-stocks take credit for the rise in "old" stocks. To the point
where a banker friend of mine, a bricks and mortar banker, worries that
the most dangerous situation in the stock market today isn't the
stratospheric level of e-stocks, which still represent a smallish share
of the stock market's total capitalization. Rather, it is the
less-heralded inflation in the
<acronym class="acronym" title="Price/Earnings">P/E</acronym> of conventional companies. They used
to trade at 10, now they're closer to 30, and he wondered if the benefits
of less friction in the economy could entirely justify such a rise. In
his view, the height of the fall might not look forbidding but,
considering the mass of the falling object, one might rethink one's
evaluation of potential damages.
</p><p>
But, there is perhaps more to the oracle, more than a buyer's market and
its consequences. As any good oracle, it might also pose the opposite
interpretation. How about the Internet creating a seller's market? Look
at eBay, and its recently announced acquisition of a respected
conventional auction house, Butterfield and Butterfield of San Francisco.
</p><p>
Just as the efficient circulation of information contributes to lower
prices, it seems that eBay, by efficiently matching sellers and buyers,
liberates bottled-up supply and demand, creating an apparently illogical
combination of higher prices for the contents of my attic and better
prices for people looking for that stuff. Thus the conclusion that a
conventional auction house would be synergistic with its cyber-sibling.
Butterfield's experience and reputation make higher price items easier
appraise and sell, and eBay is a great broadcast vehicle for the
information, and a great net to catch goods and sellers with. Another
great retroactively obvious Internet story. Where are the next ones?
</p></div></div><div id="footer"><hr /><div id="footerT">Prev: <a href="Issue4-16.html">Issue 4-16, April 21, 1999</a>  Up: <a href="volume4.html">Volume 4: 1999</a>  Next: <a href="Issue4-18.html">Issue 4-18, May 5, 1999</a> </div><div id="footerB"><div id="footerBL"><a href="Issue4-16.html" title="Issue 4-16, April 21, 1999"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a href="volume4.html" title="Volume 4: 1999"><img src="./images/navigation/up.png" alt="Up" /></a> <a href="Issue4-18.html" title="Issue 4-18, May 5, 1999"><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>