At first a hint: LibGPF is still in alpha state, while this document is
shortly before LibGPF's beta state. So, please don't wonder, when the code
doesn't do what is mentioned here in many cases. Please keep that in mind.
If you feel you want to change that state, you're welcome to help out in
the development of LibGPF. Thanks.


Introduction
============

The initial motivation for LibGPF was to load still-image files and
convert between formats, without incurring the huge overhead that trivial
systems such as the pnmtools package do, because of their converting the
entire image to a common intermediate format. LibGPF includes provisions
for shallow-buffered conversion in a tile or row based method. LibGPF's
conversion sublibs (pipe) would find the shallowest buffer length needed
to perform the conversion between an input sublib and an output sublib,
while still maintaining modularity of image readers and loaders. To our
knowlege, though more advanced graphics programs such as the GIMP use
tile-based algorithms, LibGPF is the only API that has been conceived to
process format conversion in this way and retain all the benefits of a
plugin system.

It was decided early in the design stage that LibGPF should also encompass
motion image files, since many of the same concepts and API apply, rather
than engender redundancy, though a full workup of the video related
functionality in LibGPF was not at that time attempted. A recent review of
<link to "http://www.gstreamer.net/">GStreamer</link> has led to some
rethinking of the LibGPF API to include equivalent features, and to make
use of GStreamer plugins as a fallback source of io and conversion
routines.  This refactoring is not entirely complete as of yet.

LibGPF differs from GStreamer in the following intrinsic points:

1) Decisions about threading are left to the user, who is provided with
the necessary information about the real-time and flow control
characteristics and entry points of a pipeline once it is built.  Threads
are never automatically created unless they are absolutely necessary, and
libpthreads is not required in the base library. However, there is room
in LibGPF for common-sense convenience functions to aid spawning threads.

2) Rate control must not be solely provided by passing DTR/CTS signals
through the pipeline. Sources, sinks, and convertors should accept
real-time parameters requesting throttling at specific traffic bitrates,
though they will naturally be ignored by objects that have no intrinsic
real-time properties. This allows a pipeline to be driven by hardware *at
a desired rate* when any of the components of the pipeline are driving
hardware which has real-time properties.

3) A focus is placed on keeping buffers shallow and timeslices small,
unless tuned by the application to be more aggressive or jitter-tolerant.
Entry points and objects in the pipeline should provide basic real-time
informational elements.

4) Everything is in vanilla C, such that it can be easily wrapped by other
programming languages and has minimal dependencies.

5) LibGPF will include the ability to reverse direction of the stream in
addition to setting rates, when the stream supports this. Similarly LibGPF
implements seeking back and forth to positions in the stream, when it is
possible to do so (stored content).

Note we are not in "competition" with GStreamer, and in fact we expect
that when LibGPF has more meat to it, the situation will reverse itself
and LibGPF will be utilized by the GStreamer team as a back-end, as a
place to offload plugins while the GStreamer API/SDK becomes more
embellished. We expect a synergy to develop between the two libraries.
Where GStreamer has advantages over LibGPF, and they are advantages we do
not consider to be too high level for LibGPF, we will seek to improve
LibGPF's API. Specifically here are some desirable characteristics in
GStreamer:

1) GStreamer pipelines can be arbitrarily complex, which may give some
advantages over LibGPF's pipeline structure, though LibGPF does support a
certain level of nesting. It must be determined where the point of
diminishing returns is reached in modularizing conversion routines, and we
should adapt LibGPF's pipeline structure accordingly.

2) GStreamer abstracts queues (buffers) as a pipeline element, a concept
which may have a lot of potential for LibGPF.

3) GStreamer is mature enough to have developed a vocabulary describing
the pipeline ("pad", "sink", etc). To aid potential cross-development and
understanding, we should import some of this nomenclature.



The big picture
===============

The pipeline
------------

LibGPF is very modular. It consists of the base lib, target- , protocol-
and the pipe sublibs.

The base lib handles the dynamic loading of all sublibs and computes the
fastest/optimal way for the data transfer.

The targets are used to be either the source (input) or the destination
(output) of the data-stream, where the data is transfered between. They
have all information about the data; they manage, but they don't know,
where the data actually comes from or goes to physically. The targets can
also perform some format conversions of the data as a kind of
pre/post-conversion in order to optimize the pipeline more or less
agressively.

The protocol sublibs know where the data physically comes from or goes to.
They are used by the targets to transparently read/save data from/to the
memory/disk/network/internet.

The pipe sublibs are intended to convert the data coming from the input
target into a suitable form for the output target.

Now, you're surely wondering how these parts interact with each other. Have
a look at this figure to get a rough picture how things work together:


protocol1 ---> target1 ---> pipe ---> target2 ---> protocol2


Here, target1 and target2 are target sublibs, where target1 is used as the
input and target2 as the output. protocol1, protocol2, target1, target2 and
pipe can be any sort of actually available sublibs in the correspondent
directories. Each protocol sublib can be used with another one. protocol1
and protocol2 can even be the same sublib. Same for target1 and target2.

So LibGPF's pipeline can be imagined as a powerful blitting operation:
from disk to disk, from LAN to disk, from disk to screen, from screen to
screen, from screen to disk, from LAN to LAN, etc... The only _real_ limit
is your hardware (i.e. if you have a diskless machine :). Other limits
come from the sublibs.

The user opens two targets, tells them which protocols they have to use,
and specifies which target interacts as the input and the output. A target
can be everything that has detailed information about a certain form of
data as like as a picture or a video. All this can be done by <link to
"gpfOpen() in the API section">gpfOpen()</link>.

After setting up some <link to "Stream control section">parameters</link>
you can start the data-transfer with <link to "gpfStartStream() in the API
section">gpfStartStream()</link>.

Here is an example:

gpf_handle_t handle;

...

/* open handle */
handle = gpfOpen(...);

    /* setup pipeline */
    gpfPipelineSetup(handle);

/* start transfer */
gpfStartStream(handle);

/* do transfer */
while (!gpfIsStopped(handle)) {
gpfTransferNextStreamBlock(handle, NULL);
}/* while */

/* close handle */
gpfClose(handle);

See the <link to "User guide section">User guide</link> for more
information.

Now, you are surely wondering how the pipe is specified/choosed for
conversion. The most suitable pipe sublib is autodetected by the pipeline
management. It asks the targets for the exact format of the data and
searches for one or more suitable pipe sublibs. But why more than one pipe
sublibs? Isn't one enough? Well, that makes even conversions over several
pipe sublibs possible in case one suitable pipe sublib isn't available.
But how is it determined/figured out, _if_ a target should or should not
perform pre/post-conversion? How reliable is the most optimal pipeline
found? A target may offer more than one possible formats (i.e. different
colordepth of a picture)!

That's a good point. Both the targets and the pipes know how fast or slow
their algorithm is (i.e. O(1), O(n), O(log n), O(n^2), etc.) and they know,
how much memory they need. The pipeline management gets all the detailed
information of all of them. So it accumulates them up and chooses the
fastest pipeline by default. The user can choose the pipeline by setting
some flags. So, the pipeline management can choose a pipeline, that uses
the less memory, for example. The user can also give a ratio of
speed<->memory usage. Then the pipeline management chooses the pipeline
with the best ratio that fits.

For more information about this algorithm go to the <link to
"Developers guide section">Developers section</link>.


The transfer
------------

LibGPF abstracts everything as a kind of movie with timestamps to the
user. So the user only and only has to take care about timestamps and
nothing else. Not even about different kind of frames in a movie. But hey,
how can pcx-, gif- and jpeg-images be a movie? How are those sort of data
handled, that don't have timestamp information?

To answer these questions, it is mandatory to have the right picture about
what a movie _exactly_ is. A movie is a collection of frames with or
without timestamp information and with or without sound.

Images like pcx, png, etc. are movies with _exactly_ ONE frame without
sound and without timestamp information.
(Animated) images like gif, jpeg, mng, etc. are movies with at least one
frame without sound. And they _may_ have timestamp information.
Movies like AVI, MPEG, Quicktime, etc. are movies with multiple frames,
usually with sound and with timestamp information. 

Ok, so far the first question from above should be answered up to here.
Frames without timestamp information simply get timestamps with an frame
interdelay of 1 second counted from 0. So the first frame has a timestamp
of 0 second, the 2nd frame has timestamp of 1 second, the 3rd one 2
seconds, and so on and so forth.

How to do seeking forward, backward etc. is explained below in the <link
to "Timestamps & Stream control section">Timestamps & Stream control
section</link>.




User documentation
==================



Installation
------------

Getting LibGPF
--------------

All the information about latest releases can be found on the <link to
"http://www.ggi-project.org/libgpf.php">GGI Project website</link>.

The normal user is advised to get the stable release.

Third party developers are encouraged to try the development releases (or
even the CVS sources) and notify the GGI team of any bugs, or suggest
improvements, etc.


Required packages
-----------------

Core libraries and extensions
-----------------------------

The base lib requires <link to "http://www.ggi-project.org/download.php">
LibGGI</link>. What you need else depends on what you intend to do with
LibGPF.

All other packages are optional, but some might be needed by a particular
program. Keep in mind that the highlevel family extensions internally rely
on lowlevel ones, which provide common functionality found in modern
hardware. You'll need them, when you wanna use some targets like <link to
"http://www.ggi-project.org/docs/lowlevel-book.html">LibOVL</link>, <link
to "http://www.ggi-project.org/docs/lowlevel-book.html">LibBUF</link>
and/or <link to
"http://www.ggi-project.org/docs/lowlevel-book.html">LibBLT</link>. If you
want to use <link to
"http://www.ggi-project.org/docs/highlevel-book.html">LibVIDEO </link>,
then the three mentioned lowlevel libs are required, because it makes use
of those targets internally. But then again it's up to you.

Targets
-------

Whereas the source packages include the code for all available targets,
binary packages provide only the core libraries. Thus, you only need to
download and install the target you actually need. E.g. a MS-Windows? or a
FreeBSD? User won't need DirectFB libraries and doesn't care much about
it.

Anyway, unless you're very short on disk space, having more libraries than
needed won't hurt, so don't hesitate to get additional targets.



Building from sources
---------------------

UNIX systems
------------

Reading the README and INSTALL files for each package is more than
recommended. But for those in a hurry, the following ancestral incantation
should work fine for most people:

$ ./configure; make; make install

In case you don't have root access on your machine, you can specify a
prefix instead of the default /usr/local/.

$ ./configure --prefix=<absolute path, you have WRITE-access to>
$ make
$ make install

Don't forget to had the <chosen prefix>/lib directory to your
LD_LIBRARY_PATH environment variable.


Win32
-----

To compile LibGPF on WIN32, you'll need to install the cygwin environment
(http://www.cygwin.com). Download setup.exe and install at least the
following packages : cygwin, automake, autoconf, perl. bash might also
help. In addition, you'll need the DirectX SDK 6.1 or newer
(http://download.microsoft.com/download/win2000pro/dx8asdk/8.0a/W9X2KMe/EN-US/DX8a_SDK.exe).

You can then run (in a library directory):

$ ./configure
$ make
$ make install

If you got the source from CVS, you'll have to generate a configure.sh
before compiling a library. You'll need the autoconf and automake tools
for this.




User guide
==========



A simple data-transfer aka "My first hello world program"
---------------------------------------------------------

Before we go into the details, we wanna make a simple data transfer that
equals to loading an image file (ppm format) to the screen. If you have
questions about the API functions or its syntax used here, then please
have a look into <link to "The API section">The API</link> section, where
you find a detailed description about all of them. Further you'll find
detailed information about the <link to "LibGGI's API section">LibGGI's
API</link> functions used here, in case you have questions about them,
too.

At first we include two headers

#include <ggi/ggi.h>
#include <ggi/gpf.h>

and initialize LibGGI and LibGPF

ggi_visual_t vis;
gpf_handle_t handle;
    struct gpf_properties_t props;

/* Initialize LibGGI */
if (ggiInit() != 0) {
fprintf(stderr, "Unable to initialize LibGGI\n");
exit(1);
}/* if */

/* Initialize libGPF */
if (gpfInit() != 0) {
ggiPanic("Unable to initialize LibGPF\n");
}/* if */


open a default GGI visual with a suitable video mode

/* Open the default visual */
if ((vis = ggiOpen(NULL)) == NULL) {
ggiPanic("Unable to open default visual\n");
}/* if */

/* Set the default mode */
if (ggiSetSimpleMode(vis, GGI_AUTO, GGI_AUTO, GGI_AUTO, GT_AUTO) < 0) {
ggiPanic("Unable to set default mode\n");
}/* if */


Now, we create a gpf handle:

handle = gpfOpen("ppm",
"file:/home/myself/picture-collection/foo.ppm",
"libggi", "-vis=%p", vis);

This tells gpfOpen to use the ppm target as the input and the libggi
target as the output. We tell the ppm target to use the file protocol and
we tell the file protocol the location of the file on the disk. Further,
we tell the libggi target to use the visual we just opened.

Here is a one-liner to do a error handling. I know, there are other/better
ways than exiting the hard way :)

if (!handle) gpfPanic("Couldn't create handle\n");

Now you have the chance to set some properties to the pipeline, _before_
it will be finally set up. See the <link to "Stream control
section">stream control</link> for more information about this.

    gpfClearProperties(&props);			/* initialize properties */
gpfSetFlags(&props, GPF_TIMESTAMP_CONTINUE);
    gpfSetProperties(handle, &props);

Now here we come! You surely guess right what these lines do... ;-)

    gpfPipelineSetup(handle);
gpfStartStream(handle);

And here we perform the actual transfer until it's finished. This way
LibGPF can do asynchronous transfers.

while (!gpfIsStopped(handle)) {
gpfTransferNextStreamBlock(handle, NULL);
}/* while */

Finally, we flush the output to the screen and voila - you see your
favourite picture on the screen.

ggiFlush(vis);

Errr... Yes, serious programmers don't forget to "shut down" their
programs cleanly ;-)

/* Close handle */
gpfClose(handle);

/* Close visual */
ggiClose(vis);

/* Deinitialize libGPF */
gpfExit();

/* Deinitialize LibGGI */
ggiExit();


The enthustiastic reader might want to do an exercise: Draw anything on a
GGI visual and save it anywhere as a ppm image using LibGPF. You have 15
minutes! ;-)


Another simple data-transfer aka "My second hello world program"
----------------------------------------------------------------

This time, there's nothing to see on the screen. But you'll notice, that
you'll have some less bytes of free disk space on your disk! ;-)) You
wonder why? Well, when you load a ppm file (again) and save it in
the jpeg format, you'll surely have some less bytes free on your disk,
right? :) Ok, enough spoken - let's go on.

First we include one header,

#include <ggi/gpf.h>

initialize LibGPF,

gpf_handle_t handle;
    struct gpf_properties_t props;

/* Initialize libGPF */
if (gpfInit() != 0) {
ggiPanic("Unable to initialize LibGPF\n");
}/* if */


and create a gpf handle:

handle = gpfOpen("ppm",
"file:/home/myself/picture-collection/foo.ppm",
"jpeg",
"file:/home/myself/picture-collection/foo.jpg");

This tells gpfOpen to use the ppm target as the input and the jpeg target
as the output. We tell the ppm target to use the file protocol and we tell
the file protocol the location of the file on the disk. Similar the same
for the jpeg target.

Then same as above:

if (!handle) gpfPanic("Couldn't create handle\n");

    gpfClearProperties(&props);			/* initialize properties */
gpfSetFlags(&props, GPF_TIMESTAMP_CONTINUE);
    gpfSetProperties(handle, &props);

    gpfPipelineSetup(handle);
gpfStartStream(handle);

while (!gpfIsStopped(handle)) {
gpfTransferNextStreamBlock(handle, NULL);
}/* while */

/* Close handle */
gpfClose(handle);

/* Deinitialize libGPF */
gpfExit();

The enthustiastic reader (again :) might want to do an exercise (again):
Load a ppm file, display it on the screen and then save it in the jpeg
format. Hint: You need to open an another handle for this. You have 15
minutes again! ;-)


Create a video
--------------

Ok, let's get more serious now! We create a mpeg1 video from a couple of
ppm files. You'll be astonished how easy that is: Use the code lines from
the previous example and replace

handle = gpfOpen("ppm",
"file:/home/myself/picture-collection/foo.ppm",
"jpeg",
"file:/home/myself/picture-collection/foo.jpg");

by

handle = gpfOpen("ppm",
"file:/home/myself/picture-collection/foo-%i.ppm
-multiple_images -use_internal_counter",
"mpeg",
"file:/home/myself/picture-collection/foo.mpeg
-format=mpeg1");

That's it! Yes, you read correctly! That's all! Compile it! Run it!


Combine a picture and a audio stream to a video stream
------------------------------------------------------

The previous example was very amazing and amusing at once, wasn't it? But
nontheless, we believe, you missed something: Sound! So we want to
compound a picture and a audio stream to a video stream. Assume we have a
FLI video - a video format, that supports no sound by specification, and
the sound is stored in a wav audio-file. We fit them together into an
mpeg2 file. We make use of a multi pseudo-target as the input ("pseudo"
meaning, that it uses another targets in order to get tricky things
working). Here is the code:

#include <ggi/gpf.h>

int main(void)
{
gpf_handle_t handle;
    struct gpf_properties props;

/* Initialize libGPF */
if (gpfInit() != 0) {
ggiPanic("Unable to initialize LibGPF\n");
}/* if */


handle = gpfOpen("multi",
"-targets=fli(file:/home/myself/picture-collection/foo.fli):
wav(file:/home/myself/sound-collection/foo.wav)",
"mpeg",
"file:/home/myself/picture-collection/foo.mpeg
-format=mpeg2");

if (!handle) gpfPanic("Couldn't create handle\n");

    gpfClearProperties(&props);			/* initialize properties */
gpfSetFlags(&props, GPF_TIMESTAMP_CONTINUE);
    gpfSetProperties(handle, &props);

    gpfPipelineSetup(handle);
gpfStartStream(handle);

while (!gpfIsStopped(handle)) {
gpfTransferNextStreamBlock(handle, NULL);
}/* while */

/* Close handle */
gpfClose(handle);

/* Deinitialize libGPF */
gpfExit();

return 0;
}/* main */




Playing a video
---------------

You surely expect a lot from LibGPF now, right? To play a video with
LibGPF you need to do, what <link to "libvideo's doc index>LibVIDEO</link>
does internally. Yes, LibVIDEO uses LibGPF (and some more libs)
internally. So I refer to <link to "libvideo's developers
section">Developers documentation </link> of LibVIDEO here.


Timestamps & Stream control
---------------------------

When playing videos, you surely want to halt, rewind, forward in the video
stream. The family word for that is "stream manipulation" or "stream
control". Obviously, you need control over the data stream. LibGPF
provides everything you need, if stream manipulation is possible. What
does that mean? Well, when you play an AVI, Quicktime or a DVD video, then
that's possible, because those videos are stored contents. But a video
conference, for example, is an endless data-stream, so you can't do that
(except you store the video on disk and replay it from there, as it's a
stored content then).

Note: Have a look into <link to "The API section">The API</link> section,
if you have questions about the functions and/or their syntax mentioned
here.

Using Flags you can control the behaviour of the whole API. That makes the
API much more powerful than it looks like to be.

The data transfer can be stopped and restarted again and again.
gpfStartStream() starts always from the beginning, when the
GPF_TIMESTAMP_FIRST flag is set, or continues from the last frame being
transfered/played, when GPF_TIMESTAMP_CONTINUE is set (= default).

gpfStopStream() is the way to stop an transfer. If you don't do a tranfer
from a stored content, then calling gpfStopStream() is the only way to do
that.

gpfTransferNextStreamBlock() has no effect, when the transfer isn't
started. After creating a handle, you have to call gpfStartStream()
explicitely, as the transfer is stopped by default. The reason is, that
you might set some properties before. It may also skip some data, when the
GPF_TIMESTAMP_MAYSKIP flag is set and the transfer rate is set too high
set by gpfSetTransferrate.

BTW: When you want to set GPF_TIMESTAMP_MAYSKIP | GPF_PIPE_REALTIME, you
should know what you're doing, because gpfSetProperties() won't fail
anymore.

gpfTimeStamp() is THE function to do seeking in any way you want.  But
hey, LibGPF uses only and only timestamps! So how can be timestamps
controlled by timestamps? Well, that is as easy as clever: LibGPF strictly
separates the internal timestamp clock from the one in the real world.
What does that mean? That means, that 1 second that has been
transfered/played is NOT necessarily 1 second on your watch. To be more
verbose, you can play 1 second IN a movie in 5 seconds or in 1/5th second
as well. In other words: You can "spool" the movie for- and backward as
like on a cassette. In dependent on the flags you pass onto the whence
argument of gpfTimeStamp(), you can do that or even jump for- and backward.

Negative timestamps mean, that you are going to play backward. Note, that
gpfSetProperties() fails, if this is NOT possible.

So be always aware, that seeking forth and back is NOT always possible.
That is the case, when the movie comes as a streaming-content over the
network or when the movie is a bloody codec, that makes it hard or
impossible to implement by specification or something like that.




Realtime aspects
----------------

LibGPF chooses the fastest pipeline per default automatically. When the
data transfer is stopped, gpfTimeStamp() gives you the transfer rate in
timestamps, LibGPF can guarantee you absolutely. By gpfSetProperties(),
you can determine a certain transfer rate. It fails, when LibGPF can't
guarantee you the given one. If LibGPF can guarantee an even higher rate,
then it is slowed down automatically to the value specified in the
timestamp_inc member in the gpf_properties_t structure. The default
behaviour is to transfer as many timestamps as internally specified in the
movie source (= header information from a file for example). If
gpfSetProperties() fails, and you're sure, that both your hardware and
your OS can guarantee the transfer rate given in timestamps you want/need,
then please contact <link to "http://www.ggi-project.org/contact.php">the
GGI developer team</link> and we will find a solution together. TNX.


Thread safety
-------------

LibGPF doesn't support threads directly. But it is threadsafe in the
sense, so that using threads in applications won't hurt.



The API
=======


NAME
gpfInit, gpfExit - Initialize and uninitialize LibGPF

SYNOPSIS
#include <ggi/gpf.h>

int gpfInit(void);
int gpfExit(void);

DESCRIPTION
gpfInit initalizes the library. This function must be
called before using other LibGPF functions; otherwise the
results will be undefined.

gpfExit uninitializes the library (after being initalized
by gpfInit) and automatically cleanup if necessary. This
should be called after an application is finished with the
library. If any GPF functions are called after the library
has been uninitialized, the results will be undefined.

gpfInit allows multiple invocations. A reference count is
maintained, and to completely uninitialize the library,
gpfExit must be called as many times as ggiInit has been
called beforehand.

RETURN VALUE
gpfInit returns 0 for OK, otherwise an error code.

gpfExit returns:

0after successfully cleaning up,

> 0the number of 'open' gpfInit calls, if there has
been more than one call to gpfInit. As gpfInit and
gpfExit must be used in properly nested pairs, e.g.
the first gpfExit after two gpfInits will return 1.

< 0error, especially if more gpfExit calls have been
done than gpfInit calls.

EXAMPLE
INITIALIZE AND UNINITIALIZE LIBGPF
if (gpfInit() < 0) {
fprintf(stderr, "Cannot initalize LibGPF!\n");
exit(1);
}/* if */

/* Do some LibGPF stuff */

gpfExit();

---------------------------------------------------------------------------

NAME
gpfOpen, gpfOpenInput, gpfOpenOutput, gpfClose, gpfCloseInput,
gpfCloseOutput - open and close a handle

SYNOPSIS

#include <ggi/gpf.h>

gpf_handle_t gpfOpen(char *input, char *inputargf,
 char *output, char *outputargf, ...);

int gpfOpenInput(gpf_handle_t *handle, char *input, char *argf, ...);
int gpfOpenOutput(gpf_handle_t *handle, char *output, char *argf, ...);

int gpfClose(gpf_handle_t handle);

int gpfCloseInput(gpf_handle_t handle);
int gpfCloseOutput(gpf_handle_t handle);

int gpfIsOpened(gpf_handle_t handle, int type);

DESCRIPTION

gpfOpen opens a handle. The handle is specified by four
strings. The first one specifies the input target,
followed by its parameters (second string). The third
string specifies the output target, followed by its
parameters (fourth string). The parameters are absolutely
target specific, so see the target doc for more information.

The other arguments are intended to pass some non-textual
information to the targets. They are specified in the
parameters (printf style).

gpfOpenInput is likely gpfOpen, but only opens the specified
input target. When handle is NULL, then gpfOpenInput opens
a new handle additionally. Otherwise, it assumes the handle
is valid.

gpfOpenOutput is the analogue of gpfOpenInput.

gpfClose closes all targets, pipes and protocols cleanly.
The stream transfer must be stopped before, otherwise it does
absolutely nothing.

gpfCloseInput only closes the input target plus the protocol
it used. When the output is already closed, then it closes
everything, that was left open. The stream transfer must be
stopped before, otherwise it does absolutely nothing.

gpfCloseOutput is the analogue of gpfCloseInput.

RETURN VALUE
gpfOpen, gpfOpenInput, gpfOpenOutput returns the opened handle
(gpf_handle_t), or NULL for error.

gpfClose, gpfCloseInput, gpfCloseOutput returns 0 for OK,
otherwise an error code.

EXAMPLE
1. Example how to load an avi-video from an webserver through a proxy
into a libbuf object, where a gstreamer avi-plugin is used to do this:

ggiBuf_t buf = ggiBufCreateBuffer(vis, ...);
 ...
handle = gpfOpen("gstreamer",
	"www-proxy://www.avi-collection.org/intro.avi -plugin=avi",
"libbuf", "-buf=%p", buf);

2. Example how to load a gif-picture from local disk into a GGI visual:

vis = ggiOpen("memory");
 ...
handle = gpfOpen("gif",
"file:/usr/local/ftp/pub/picture-collection/GGI_logo.gif",
"libggi", "-vis=%p", vis);

3. Example how to save a jpg-picture from GGI visual
to a remote ftpserver.

handle = gpfOpen("libggi","-vis=%p",
"jpg", "ftp://ftp.picture-collection.org/pub/image.jpg",
vis);

SEE ALSO
gpfInit(3)

---------------------------------------------------------------------------

NAME
    gpfPipelineSetup - function for setting up the pipeline

SYNOPSIS
#include <ggi/gpf.h>

    int gpfPipelineSetup(gpf_handle_t handle);

DESCRIPTION
    gpfPipelineSetup creates the pipeline being used for the transfer.
    It has to be called explicitely after gpfOpen() in order to allow
    the user to set some properties for the pipeline. The gpfClose()
    function family is the only way to shutdown the pipeline cleanly.

RETURN VALUE
gpfPipelineSetup returns 0 for OK, otherwise an error code.

EXAMPLE

SEE ALSO
    gpfOpen(3), gpfSetProperties(3)

---------------------------------------------------------------------------

NAME
gpfSetProperties, gpfGetProperties, gpfClearProperties - functions for
pipline control

SYNOPSIS
#include <ggi/gpf.h>

int gpfSetProperties(gpf_handle_t handle, const struct gpf_properties_t *props);
int gpfGetProperties(gpf_handle_t handle, struct gpf_properties_t *props);
int gpfClearProperties(struct gpf_properties_t *props);

DESCRIPTION
gpfSetProperties is used to give the user control over the
    pipeline, beginning from the pipeline selection through the
    pipeline management to set the transfer rate given in time-
    stamps and other things. Note, that changing properties
    only affects, when the data stream is stopped at calling time.
    An exception is changing the timestamp_inc member in the
    gpf_properties_t structure.

gpfGetProperties returns you the current properties being
    active on a handle.

gpfClearProperties initializes the property variable and
    sets the value for the default behaviour of libgpf.

RETURN VALUE
gpfSetProperties return 0 for OK, otherwise an error code.

gpfGetProperties gpfClearProperties returns 0 for OK, otherwise
an error code.

EXAMPLE
STOP THE STREAM AND RESTART FROM THE BEGINNING
struct gpf_properties_t props;

...

    /* clear properties */
gpfClearProperties(&props);

    /* set a flag */
    gpfSetFlag(&props, GPF_TIMESTAMP_FIRST);

    /* set properties */
    gpfSetProperties(&props);

SEE ALSO
gpfSetFlags(3), gpfTimeStamp(3)

---------------------------------------------------------------------------

NAME
gpfTimeStamp - function for stream control

SYNOPSIS
#include <ggi/gpf.h>

    int gpfTimeStamp(gpf_handle_t handle, struct timeval *tv, int whence);

DESCRIPTION
gpfTimeStamp behaves completely different in dependence
    of the whence parameter. The whence parameter consists
    of two values: The operation to perform (GPF_SEEK_SET or
    GPF_SEEK_GET) and the position (GPF_SEEK_REL/CUR, GPF_SEEK_ABS,
    GPF_SEEK_LAST or GPF_SEEK_END). Combine the two values simply
    with a binary or operation.

    GPF_SEEK_SET allows you to set a timestamp relatively
    to the current position (GPF_SEEK_REL/CUR), absolutely
    (GPF_SEEK_ABS), relatively to the last available time-
    stamp (GPF_SEEK_LAST) or relatively to the end of the
    movie (GPF_SEEK_END).
    When you wanna set a timestamp, then gpfTimeStamp()
    expects the timestamp value in the tv param.

    GPF_SEEK_GET allows you the same in getting the current
    timestamp analogous to GPF_SEEK_SET. The tv param will
    contain the result, in case of success.
    Just note, that GPF_SEEK_REL/CUR and GPF_SEEK_ABS have
    the same meaning here.

RETURN VALUE
gpfTimeStamp return 0 for OK, otherwise an error code
(i.e. when the given timestamp is out of range).

EXAMPLE
struct timeval tv;

...

    /* Get current position */
gpfTimeStamp(handle, &tv, GPF_SEEK_GET | GPF_SEEK_ABS);

/* skip five seconds */
    tv.tv_sec += 5;

    /* set new absolute timestamp position */
gpfTimeStamp(handle, &tv, GPF_SEEK_SET | GPF_SEEK_ABS);

SEE ALSO
gpfSetProperties(3), gpfSetFlags(3)

---------------------------------------------------------------------------

NAME
gpfSetFlags, gpfGetFlags - Set or get flags affecting operation on
a handle

SYNOPSIS
#include <ggi/gpf.h>

enum gpf_flags gpfSetFlags(struct gpf_properties_t *props, enum gpf_flags flags);
enum gpf_flags gpfGetFlags(const struct gpf_properties_t *props);

enum gpf_flags gpfAddFlags(struct gpf_properties_t *props, enum gpf_flags flags);
enum gpf_flags gpfRemoveFlags(struct gpf_properties_t *props, enum gpf_flags flags);

DESCRIPTION
gpfSetFlags sets the specified flags (bitwise OR'd together)
in the properties of a handle. This function is used to control
    the behaviour of stream operation functions like gpfStartStream.

gpfGetFlags obtains the flags currently in effect.

gpfAddFlags and gpfRemoveFlags are macros that set or
unsets the specified flags.

RETURN VALUE
gpfSetFlags, gpfAddFlags, gpfRemoveFlags returns the flags
previously set.

gpfGetFlags returns the flags currently set.

EXAMPLE
STOP THE STREAM AND RESTART FROM THE BEGINNING
gpfStopStream(handle);

    gpfGetProperties(handle, &props);
gpfAddFlags(&props, GPF_TIMESTAMP_FIRST);
    gpfSetProperties(handle, &props);

gpfStartStream(handle);

SEE ALSO
gpfStartStream(3), gpfSetCurrFrame(3)

---------------------------------------------------------------------------

NAME
gpfStartStream, gpfStopStream, gpfTransferNextStreamBlock,
gpfIsStopped, gpfIsEOS - starts and stops the stream transfer

SYNOPSIS
#include <ggi/gpf.h>

typedef void (*gpfIndicatorCallback)(int current, int total);

int gpfStartStream(gpf_handle_t handle);
int gpfStopStream(gpf_handle_t handle);

int gpfTransferNextStreamBlock(gpf_handle_t handle,
gpfIndicatorCallback cbI);

int gpfIsStopped(gpf_handle_t handle);
int gpfIsEOS(gpf_handle_t handle);

DESCRIPTION
gpfStartStream starts the stream transfer.

gpfStopStream stops the stream transfer.

gpfTransferNextStreamBlock performs the next data-transfer,
when it was started before, otherwise this function has no effect.
The block size is internally optimized. It may change, each time
called (i.e. when the data stream goes over a network and the
transfer rate changes more or less fast). Internally, it
increments the current frame number automatically.

gpfIsStopped obtains the status currently in effect.

gpfIsEOS tests the End Of Stream indicator of a stream similar to
the feof() function of the c standard library.
WARNING: Only use it on stored contents, otherwise you are
dangered to run into an endless loop.

RETURN VALUE
gpfStartStream, gpfStopStream, gpfTransferNextStreamBlock returns
0 for OK, otherwise an error code.

gpfIsStopped returns true, if transfer is stopped.

gpfIsEOS returns true, when the End Of Stream is reached.

EXAMPLE
TRANSFER OF A STORED CONTENT
gpfStartStream(handle);

while (!gpfIsStopped(handle)) {
gpfTransferNextStreamBlock(handle, NULL);
}/* while */

/* Close handle */
gpfClose(handle);

SEE ALSO
gpfSetFlags(3), gpfTimeStamp(3)






Developer documentation
=======================


I assume here, that you already read the <link to "Introduction
section">Introduction</link> and <link to "The big picture section">the
big picture</link> section. Thus, I assume here, that you have a rough
overview how things _might_ work internally. If you didn't yet, then
please do it now. Otherwise you won't get a glue what will be talked here.


In this section, you should get a detailed picture about what's going on
when a handle is opened, how the pipeline management works and what's
actually going on, when a transfer happens. This should give you a deep
understanding how things work and why you should do what, when you (go to)
write a target, protocol or pipe sublib.


How gpfOpen() works
-------------------

This function has actually nothing to do with the transfer itself, but
nonetheless, it is the most complex one after gpfPipelineSetup().
gpfOpen() opens the two targets and the protocols. gpfOpen() fails, when
opening any sublib fails.

gpfOpen() is part of LibGPF's base lib. Its files are in the libgpf/gpf
subdirectory. You can find it in the init.c file. You will be surprised
how easily this function is realized.

First it calls vgpfOpenInput(), which opens the input target and its
protocol. Then it calls vgpfOpenOutput(), which does the same for the
output side.

vgpfOpenInput() creates a handle, if handle was a NULL pointer. That is
usually the case. If the target was already opened before, then it fails
to avoid to reopen the input target, which would cause problems. Then it
opens the target and its protocol using _gpfOpenTarget(). When
_gpfOpenTarget() fails, then vgpfOpenInput() fails as well. Then it
initializes some mutexes to make libgpf threadsafe and exits.

vgpfOpenOutput() works analogous to vgpfOpenInput(), so I don't go in
further.

In the internal.c file _gpfOpenTarget() is implemented as well as the
following functions. At first, _gpfOpenTarget() allocates enough memory
for the target handler. Then a pipeline element is created, which holds
some administration information for the target handler. This is necessary
to not overwrite/destroy these information, when pipelines gets
created/destroyed in the pipeline management and to simplify the pipeline
cloning process - but that only on the side. More about that comes <link
to "The pipeline management section">later</link>. Then
_gpfParseProtocol() filters out all information needed to open the
protocol with the right parameters. _gpfOpenDL() finally opens the target.
Then _gpfOpenProtocol() loads the protocol and _gpfOpenTarget() returns.

_gpfOpenProtocol() works very similar to the _gpfOpenTarget() function. At
first it allocates enough memory for the protocol handler. Then it creates
one pipeline element to hold the administration information. The reason is
the same as for _gpfOpenTarget(). Finally, _gpfOpenDL() opens the protocol
and _gpfOpenProtocol() exits.



The pipeline management
-----------------------

The pipeline management is part of LibGPF's base lib. Its files are in the
libgpf/gpf subdirectory. The pipeline management is in the pipe_mgr.c
file. The pipe.c file contains some helper routines.

After <link to "How does gpfOpen() work section">gpfOpen()</link> opened
_both_ the input and the output targets are successfully _inside_ of the
<link to "gpfOpen() in the API section">gpfOpen()</link> function, the
user called _gpfPipelineSetup().

gpfPipelineSetup() sets up the pipeline to make the transfer possible and
fails, when no proper pipeline was found. It tries to find the optimal
pipeline using _gpfPipelineFindOptimal(), that fits best to the <link to
"gpfSetProperties() in the API section">wishes of the user</link>. Then it
initializes the pipeline by calling the setup function pointer of each
pipeline element. This gives the target and pipe sublibs the chance to do
things, they can't do at GGIopen time because of lack of information about
the pipeline itself. After initizialization, all the pipeline
administration information get destroyed, which is of no longer use to
safe memory. _gpfPipelineSetup() fails, when _gpfPipelineFindOptimal()
fail. _gpfPipelineSetup() will return back to gpfOpen() and gpfOpen()
finally exits.

_gpfPipelineFindOptimal() searches for as many pipelines as possible at
first. It uses the _gpfPipelineFind() function for this. The found
pipelines are formed in a line using a dynamic one way linked list. Then
it selects the pipeline using _gpfPipelineSelect(), that fits best to the
<link to "gpfSetProperties() in the API section">users wishes</link>. Then
it destroys all other pipelines and returns the selected one.

_gpfPipelineFind()'s job is to find one (more) pipeline, which surely
brings the result the user wanna have _without_ taking care, whether the
pipeline itself fits to the user's wishes or not. I guess, you can think
of why!? If it creates the _first_ pipeline, then it gets the information
lists of the two targets. These lists describes all pixel/vector/video
formats, the target can handle. Each list is a dynamic one way linked
list. Why not a static linked list is used? That's simple: That
_gpfPipelineSetup() is able to remove them before returning. Then it
creates clones of the targets and links them together to a first
pre-liminary pipeline. This clone-process is necessary to avoid, that
_gpfPipelineFindOptimal() closes the targets accidentally, when destroying
the non-selected pipelines. If _gpfPipelineFind() tries to find another
pipeline, then it should obviously be made sure, that each found pipeline
is kept unique. Therefore it needs information, where to continue. For
this case, it is good, that each pipeline item contains state-information
(The "mark" field in the gpf_pipeline_item_t structure). Thus, simply
cloning the whole pipeline, that was found before, gives all needed
information. Then _gpfPipelineFind() goes through the (pre-liminary)
pipeline and checks, if each pipeline item fits with the following one.
_gpfPipelineItemFits() is used for this. If yes, then it compares the next
one with the overnext one, etc. If no, then _gpfPipelineFindSuitableItem()
searches for an appropriate pipeline element. _gpfPipelineIsUnique() makes
sure, that the pipeline is still unique. Otherwise another pipeline item
must be found. All the mentioned functions uses the state information in
order to make sure to continue the searching for another pipeline. Once
the whole pipeline is checked through, it will be returned to
_gpfPipelineFindOptimal() or NULL, when no pipeline could be found.

_gpfPipelineSelect() goes through each pipeline, found by
_gpfPipelineFind(). It accumulates the memory usage and the runtime of
each pipeline item. Then these values are compared with the values of the
other pipelines. The pipeline, which values fits best to the ratio of
speed<->memory given by the user, is the happiest one and will be returned
to _gpfPipelineFindOptimal().



The transfer (overview)
-----------------------

The transfer happens within gpfTransferNextStreamBlock() and is part of
LibGPF's base lib, too. It is in the gpf.c file.

In this stage, the pipeline is already initialized. That means, the
pipeline knows the size (width/height) of the _input_ stream (from the
input target) and each pipeline item has its own input buffer allocated -
where it reads its data from. Where the data goes to is the output buffer.
That is simply the input buffer of the next pipeline item.

gpfTransferNextStreamBlock() is the function, that performs the transfer
asyncronously to the application. It knows from the user and from the
input target how much timestamps has to be transfered. But due to the
asynchronous transfer LibGPF provides, the application can eat up some
time, that could be used to perform some transfers. This obviously can
cause some speed fluctuations, which are tried to be compensated
automatically by increasing/decreasing the timestamps as far as necessary.
The calculation is done in dependence to the time the application eated.
Then it calls the handle->pipe_index->transfernextstreamblock function
pointer, which reads the data from the input buffer, converts and writes
it to the output buffer, until the given timestamps are transfered.

During that operation, an internal transfer counter is updated and a
"callback Indicator" will be called, if non-NULL. The callback Indicator
is a function pointer set by the user. Then it gives control back to the
application.

The transfernextstreamblock callback function returns the bytes being
transfered. Negative values indicates an occured error. The
handle->pipe_index points _always_ to the current pipeline item. It is
required that that is static data in order to be able to continue the data
transfer later, when the transfer (was) stopped by any reason.

In case not all data couldn't be transfered for some reasons, the data is
cached inside each pipleline item. The cached data will be prepended to
the new data and then all the data will be transfered as usual. The
transfernextstreamblock callback function also sets the handle->pipe_index
function to the next pipeline item that has to transfer some data.

After finishing the loop, the whole transfer should be finished and the
transfer is stopped.

That is actually everything what happens in the base lib. Everything else
is handled inside the target/pipe sublib. So open questions should be
answered there. Read on.



The transfer - in a target sublib
---------------------------------

Here we have to distinguish two cases: a) when the target acts as the
input and b) when the target acts as the output.

a) In the setup routine, it allocates as much memory as needed for both
the input and the output buffer to read the data from the protocol,
convert it - if needed/required - and to write it to the output.

transfernextstreamblock reads the data from the protocol using the
handle->opio_input->read function pointer, appends the data to the cached
data, if present, and writes the data to the output buffer using the
handle->pipeline->write function pointer. Between these two io operations,
the conversion is performed, if needed/necessary. Where and how the
conversion actually happens differs from target to target. The way of
implementation is fully up to the target developer(s). Also the
recalculation how much bytes corresponds the number of timestamps to
transfer is performed by the helper function _gpf_ts_gettransfersize().
Then the handle->pipe_index is set to the next pipeline element, that has
to transfer the next data. It is _not_ necessarily the next level pipeline
item. If the current one has still data to transfer, but the transfer
limit is archieved, then the handle->pipe_index is _not_ changed.

b) In the setup routine, it can assume that the input buffer is non-NULL
and that the size is correct. Only the output buffer may be resized to be
able to write the data through the protocol - also after the conversion,
if performed.

transfernextstreamblock reads the data from the input buffer through the
handle->pipeline->read function pointer and writes the data through the
protocol using the handle->opio_output->write function pointer. Where and
how the conversion happens is the same as in a). When there's no more data
to transfer, then handle->pipe_index is set to the previous pipeline
element to get new data to read.



The transfer - in a pipe sublib
-------------------------------

In the setup routine, it can assumed that the input buffer is non-NULL and
that the size is correct. Only the output buffer may be resized to be able
to write the converted data to the output buffer. Further, everything gets
finally prepared to get the conversion working (setting up the bitshifts,
etc...).

transfernextstreamblock reads the data from the input buffer through the
handle->pipeline->read function pointer, performs the data conversion and
writes the data to the output buffer using the handle->pipeline->write
function pointer. Where and how the conversion happens is fully up to the
pipe sublib developer(s). It also uses the _gpf_gettransfersize() function
to get the bytes from the given timestamp to transfer.

When the transfer limit is archieved, but there's still data to transfer,
then handle->pipe_index is _not_ changed. Otherwise it is set to the next
pipeline item, if it wasn't the last active one before the current. In
that case the previous pipeline item is selected, in order to get new
data.



The internal data structure
---------------------------


Structure: gpf_handle

Field: props
Description: Contains the properties of the pipeline

Field: opio_input
Description: Pointer to the input target handle

Field: opio_output
Description: Pointer to the output target handle

Field: pipeline
Description: Pointer to a double linked list, which contains all pipeline
elements.

Field: pipe_index
Description: Pointer to the pipeline element, that will be processed next
time by transfernextstreamblock




Structure: gpf_handle_opio_t

Field: head
Description: Used by the dl handling. non-baselib hackers never will need
it.

Field: opproto
Description: Pointer to the protocol handle.

Field: pipe
Description: Pointer to the pipe-element. It is used to abstract the
target as a pipe element. This simplifies the pipeline management and the
pipeline transfer a lot.

Field: open
Description: Used to tell the protocol to open the
file/connection/whatever it is used for. Then the target can perform some
preparations to read/write the data from/to the protocol in the right
format later. Note, that it is not known, which format the next pipeline
element expects at that time. This function pointer has to be initialized
in the targets' GPFopen() function. It is called in the _gpfOpenTarget()
function after the target and its protocol was opened.

Field: close
Description: Used to tell the protocol to close the
file/connection/whatever it is used for. This function pointer has to be
initialized in the target' GPFopen() function, too. It is called in the
_gpfCloseTarget() function before closing the target and its protocol.
This allows the target to shutdown cleanly.

Field: timestamp
Description: This function pointer has to be initizialized in the targets'
GPFopen() function. It implements the gpfTimeStamp() API function inside
the targets. It returns GGI_OK on success, otherwise an appropriate error
code.

Field: read
Description: This function pointer has to be initialized in the target's
GPFopen() function. It is used to read the data coming from the protocol.

Field: write
Description: This function pointer has to be initialized in the target's
GPFopen() function. It is used to write the data to the protocol.

Field: doio
Description: If a target provides its own mechanism to read/write the data
from/to the protocol, then it will use the so called target protocol. This
is usually the case, when the target uses another lib. The target protocol
calls the doio function pointer, where the open, close, setcurrframe,
getcurrframe, getnumframes, read and write function pointers get
initialized. If the target uses the target protocol, then this doio
function pointer has to be initialized in the target's GPFopen() function.
Otherwise, doio should be NULL.

Field: pipe_getinfolist
Description: This function pointer has to be initizalized in the target's
GPFopen() function. It is used to create a one way linked list, which
contains all information about the formats the target can handle. It is
called by the pipeline management.




Structure: gpf_handle_opproto_t

Field: head
Description: Used by the dl handling. non-baselib hackers never will need
it.

Field: pipe
Description: Pointer to the pipe-structure. It is used to simplify the dl
handling between the target and the protocol.

Field: open
Description: This function pointer has to be initialized in the protocol's
GPFopen() function. It is used to open the file/connection/whatever the
physical thing is. This function pointer is called by its targets' open
function.

Field: close
Description: This function pointer has to be initialized in the protocol's
GPFopen() function. It is used to close the file/connection/whatever the
physical thing is. This function pointer is called by its targets' close
function.

Field: read
Description: This function pointer has to be initialized in the protocol's
GPFopen() function. It is used to read a certain size of the data-stream.
It also takes care about caching and such. This function pointer is called
by its targets' read function.

Field: write
Description: This function pointer has to be initialized in the protocol's
GPFopen() function. It is used to write a certain size to the data-stream.
It also takes care about caching and such. This function pointer is called
by its targets' write function.

Field: lseek
Description: This function pointer has to be initialized in the protocol's
GPFopen() function. It is used for the same reason as libc's lseek()
function. It has also the same behaviour and flags.

Field: flush
Description: This function pointer has to be initialized in the protocol's
GPFopen() function. It is used to force a flush of the protocol's cached
data.

Field: eos
Description: This function pointer has to be initialized in the protocol's
GPFopen() function. It works similar to the feof() libc function. It
returns true, if a End Of Stream (EOS) is determined. Note, that this
function will always fail on endless data-streams (i.e. video-streaming).

Field: protopriv
Description: Used to store protocol specific data





Structure: gpf_pipeline_item_t

Field: head
Description: Used by the dl handling. non-baselib hackers never will need
it.

Field: dlhandle
Description: Used by the dl handling. non-baselib hackers never will need
it.

Field: prev
Description: The previous pipe element

Field: next
Description: The next pipe element

Field: input_head
Description: single linked list containing the formats, that the pipe
accepts from the previous pipe element.

Field: output_head
Description: single linked list containing the formats, that the pipe
offers to the next pipeline.

Field: mark
Description: Used by the pipeline management to mark the last successful
place. The pipe sublibs' uses it to read out the information of the
selected format, which are stored in the infolists.

Field: setup
Description: This function pointer has to be initialized in the sublibs'
GPFopen() function. This function pointer is called by the pipeline
management. At that time, the sublib knows the previous/next pipe and
therefore knows, which format it gets and how the conversion has to happen
to get the right format for the next pipe.

Field: getinfolist
Description: This function pointer is unused on targets. On real pipe
sublibs, it has to be initialized in the pipe's GPFopen() function. It
creates a single linked list containing all formats the pipe sublib can
handle. It is called by the pipeline management.

Field: getformat
Description: This function pointer has to be initialized in the sublibs'
GPFopen() function (both target and pipe sublib). This function pointer
is called by the pipeline management. At that time, the pipeline is under
creation. When this sublib uses the GPF_PIPE_DF_ALLVARIANTS flag, then
it has obviously to know, which formats it has to expect from the input
and output. At the calling time, it has to copy all the information from
the previous and next sublib in the pipeline. When this sublib don't use
GPF_PIPE_DF_ALLVARIANTS at all, then this function should be empty, but
returung GGI_OK.

Field: transfernextstreamblock
Description: This function pointer has to be initialized in the sublibs'
GPFopen() function. This function is called by
gpfTransferNextStreamBlock().




Structure: gpf_pipeline_t

Field: head
Description: Used by the dl handling. non-baselib hackers never will need
it.

Field: next
Description: Pointer to the next pipeline.

Field: item_head
Description: Pointer to the first pipeline element. This has to be a
target.

Field: read
Description: This function pointer points to a default function. It reads
the data from buf[read_buf] and writes it to buf_conv. This function is
used to simplify the transfer/conversion implementation in the sublibs.

Field: write
Description: This function pointer points to a default function. It reads
the data from buf_conv and writes it to buf[write_buf]. This function is
used to simplify the transfer/conversion implementation in the sublibs.

Field: width
Description: Width of the image in pixels. 

Field: height
Description: Height of the image in pixels.

Field: transfer_timestamps
Description: how much to timestamps to transfer

Field: current_timestamp
Description: tranfer state

Field: total_timestamps
Description: only to be filled out by the input target





Developer guide
===============


I assume here, that you read the <link to "Developer Documentation
section">Developer Documentation</link>. Thus, I assume here, that you
have a detailed understanding how things _does_ work internally.


Writing a target sublib
-----------------------

All targets are in the libgpf/io directory, so change to there.

Step 1 is to build a compiling framework. At first copy the sample target
(don't forget the hidden .cvsignore file :) and rename it to your target's
name. If you don't use a library providing its own methods to access the
physical data, then you should remove the proto.c file.

Next go through all the files and replace the <sample> tag by the target's
name. Then go through all files and read the #warnings and comments in the
source and follow them.

Next, add your target's name in the libgpf/io/Makefile.am file (line
DIST_SUBDIRS). Then add your target to the libgpf/configure.in file. There
are already some sample blocks (search for "sample"). Copy and change them
appropriatly to your target. Further add some missing headers to
AC_CHECK_HEADERS and add missing AC_CHECK_LIB entries. Don't forget to
disable your target, if something (header and/or lib) is missing in the
following if-blocks of AC_CHECK_LIB. Add your own if-block(s), if you
added a header to AC_CHECK_HEADERS. Now type autogen.sh and configure on
the libgpf root directory to make your above changes visible.

Step 2 is to finish your target. Rebuild libgpf with your target enabled.
Fix the compiler warnings and errors and fill in your code in the gaps.

Step 3: Test your target and go back to Step 2 until your target works.



Writing a protocol sublib
-------------------------

All protocols are in the libgpf/proto directory, so change to there.

Step 1 is to build a compiling framework. At first copy the sample
protocol (don't forget the hidden .cvsignore file :) and rename it to your
protocol's name.

Next go through all the files and replace the <sample> tag by the
protocol's name. Then go through all files and read the #warnings and
comments in the source and follow them.

Next, add your protocol's name in the libgpf/proto/Makefile.am file (line
DIST_SUBDIRS). Then add your target to the libgpf/configure.in file. There
are already some sample blocks (search for "sample"). Copy and change them
appropriatly to your protocol. Further add some missing headers to
AC_CHECK_HEADERS and add missing AC_CHECK_LIB entries. Don't forget to
disable your protocol, if something (header and/or lib) is missing in the
following if-blocks of AC_CHECK_LIB. Add your own if-block(s), if you
added a header to AC_CHECK_HEADERS. Now type autogen.sh and configure on
the libgpf root directory to make your above changes visible.

Step 2 is to finish your protocol. Rebuild libgpf with your protocol
enabled.  Fix the compiler warnings and errors and fill in your code in
the gaps.

Step 3: Test your protocol and go back to Step 2 until your protocol
works.



Writing a pipe sublib
---------------------

All pipes are in the libgpf/pipe directory, so change to there.

Step 1 is to build a compiling framework. At first copy the sample pipe
(don't forget the hidden .cvsignore file :) and rename it to your pipe's
name.

Next go through all the files and replace the <sample> tag by the pipe's
name. Then go through all files and read the #warnings and comments in the
source and follow them.

Next, add your target's name in the libgpf/pipe/Makefile.am file (line
DIST_SUBDIRS). Then add your target to the libgpf/configure.in file. There
are already some sample blocks (search for "sample"). Copy and change them
appropriatly to your target. Further add some missing headers to
AC_CHECK_HEADERS and add missing AC_CHECK_LIB entries. Don't forget to
disable your pipe, if something (header and/or lib) is missing in the
following if-blocks of AC_CHECK_LIB. Add your own if-block(s), if you
added a header to AC_CHECK_HEADERS. Now type autogen.sh and configure on
the libgpf root directory to make your above changes visible.

Step 2 is to finish your target. Rebuild libgpf with your pipe enabled.
Fix the compiler warnings and errors and fill in your code in the gaps.

Step 3: Test your pipe and go back to Step 2 until your pipe works.





<!-- Ignore this stuff

Have a look at <link to
"ftp://ftp.inria.fr/INRIA/Projects/algo/web/seminars/sem92-93/flajolet.ps">
ftp://ftp.inria.fr/INRIA/Projects/algo/web/seminars/sem92-93/flajolet.ps
</link> and <link to
"http://mitpress.mit.edu/catalog/item/default.asp?ttype=2&tid=8570">
http://mitpress.mit.edu/catalog/item/default.asp?ttype=2&tid=8570</link>
for detailed information to get a feeling, how fast a algorithm might be.


Pipeline:
Yes, cpu/speed is tricky. The right way would be a way to autodetect the
CPU at initialization time of libgpf using libgg.

-->

