]> git.sesse.net Git - vlc/commitdiff
Initial revision
authorMichel Kaempf <maxx@videolan.org>
Sun, 8 Aug 1999 12:42:54 +0000 (12:42 +0000)
committerMichel Kaempf <maxx@videolan.org>
Sun, 8 Aug 1999 12:42:54 +0000 (12:42 +0000)
77 files changed:
Makefile [new file with mode: 0644]
Makefile.dep [new file with mode: 0644]
doc/bugs [new file with mode: 0644]
doc/common.tex [new file with mode: 0644]
doc/conventions [new file with mode: 0644]
doc/headers [new file with mode: 0644]
doc/main.tex [new file with mode: 0644]
doc/organization.fig [new file with mode: 0644]
doc/portage [new file with mode: 0644]
doc/threads.tex [new file with mode: 0644]
doc/todo [new file with mode: 0644]
doc/vlan-server [new file with mode: 0644]
include/all.h [new file with mode: 0644]
include/audio_constants.h [new file with mode: 0644]
include/audio_decoder.h [new file with mode: 0644]
include/audio_dsp.h [new file with mode: 0644]
include/audio_output.h [new file with mode: 0644]
include/common.h [new file with mode: 0644]
include/config.h [new file with mode: 0644]
include/control.h [new file with mode: 0644]
include/debug.h [new file with mode: 0644]
include/decoder_fifo.h [new file with mode: 0644]
include/generic_decoder.h [new file with mode: 0644]
include/input.h [new file with mode: 0644]
include/input_ctrl.h [new file with mode: 0644]
include/input_file.h [new file with mode: 0644]
include/input_netlist.h [new file with mode: 0644]
include/input_network.h [new file with mode: 0644]
include/input_pcr.h [new file with mode: 0644]
include/input_psi.h [new file with mode: 0644]
include/input_vlan.h [new file with mode: 0644]
include/interface.h [new file with mode: 0644]
include/intf_cmd.h [new file with mode: 0644]
include/intf_ctrl.h [new file with mode: 0644]
include/intf_msg.h [new file with mode: 0644]
include/mtime.h [new file with mode: 0644]
include/netutils.h [new file with mode: 0644]
include/pgm_data.h [new file with mode: 0644]
include/rsc_files.h [new file with mode: 0644]
include/thread.h [new file with mode: 0644]
include/video.h [new file with mode: 0644]
include/video_decoder.h [new file with mode: 0644]
include/video_graphics.h [new file with mode: 0644]
include/video_output.h [new file with mode: 0644]
include/video_x11.h [new file with mode: 0644]
include/xconsole.h [new file with mode: 0644]
include/xutils.h [new file with mode: 0644]
lib/background.xpm [new file with mode: 0644]
lib/s16_0_44100.raw [new file with mode: 0644]
lib/s16_1_32000.raw [new file with mode: 0644]
src/audio_decoder/audio_decoder.c [new file with mode: 0644]
src/audio_output/audio_dsp.c [new file with mode: 0644]
src/audio_output/audio_output.c [new file with mode: 0644]
src/generic_decoder/generic_decoder.c [new file with mode: 0644]
src/input/input.c [new file with mode: 0644]
src/input/input_ctrl.c [new file with mode: 0644]
src/input/input_file.c [new file with mode: 0644]
src/input/input_netlist.c [new file with mode: 0644]
src/input/input_network.c [new file with mode: 0644]
src/input/input_pcr.c [new file with mode: 0644]
src/input/input_psi.c [new file with mode: 0644]
src/input/input_vlan.c [new file with mode: 0644]
src/interface/control.c [new file with mode: 0644]
src/interface/interface.c [new file with mode: 0644]
src/interface/intf_cmd.c [new file with mode: 0644]
src/interface/intf_ctrl.c [new file with mode: 0644]
src/interface/intf_msg.c [new file with mode: 0644]
src/interface/main.c [new file with mode: 0644]
src/interface/xconsole.c [new file with mode: 0644]
src/misc/mtime.c [new file with mode: 0644]
src/misc/netutils.c [new file with mode: 0644]
src/misc/rsc_files.c [new file with mode: 0644]
src/misc/xutils.c [new file with mode: 0644]
src/video_decoder/video_decoder.c [new file with mode: 0644]
src/video_output/video_graphics.c [new file with mode: 0644]
src/video_output/video_output.c [new file with mode: 0644]
src/video_output/video_x11.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..bd4cf19
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,215 @@
+################################################################################
+# vlc (VideoLAN Client) main makefile
+# (c)1998 VideoLAN
+################################################################################
+# This makefile is the main makefile for the VideoLAN client.
+################################################################################
+
+################################################################################
+# Configuration
+################################################################################
+
+#CC = gcc
+#SHELL = /bin/sh
+
+################################################################################
+# Settings and other variables
+################################################################################
+
+#
+# C headers directories
+#
+INCLUDE += -Iinclude
+INCLUDE += -I/usr/X11R6/include/X11
+
+#
+# Libraries
+#
+LIB += -L/usr/X11R6/lib
+LIB += -lX11
+LIB += -lXext 
+LIB += -lpthread
+LIB += -lXpm
+
+#
+# C compiler flags: compilation
+#
+CCFLAGS += $(INCLUDE)
+CCFLAGS += -Wall
+CCFLAGS += -D_REENTRANT
+CCFLAGS += -D_GNU_SOURCE
+
+# Optimizations : don't compile debug versions with them
+#CCFLAGS += -O8
+#CCFLAGS += -s -fargument-noalias-global -fexpensive-optimizations -ffast-math -funroll-loops -fomit-frame-pointer #-march=pentiumpro
+#(Uncomment -march=pentiumpro if it applies)
+
+
+#
+# C compiler flags: dependancies
+#
+DCFLAGS += $(INCLUDE)
+DCFLAGS += -MM
+
+#
+# C compiler flags: linking
+#
+LCFLAGS += $(LIB)
+LCFLAGS += -Wall
+
+#
+# C compiler flags: functions flow
+#
+FCFLAGS += $(INCLUDE)
+FCFLAGS += -A
+FCFLAGS += -P
+FCFLAGS += -v
+FCFLAGS        += -a
+FCFLAGS += -X errno.h
+FCFLAGS += -X fcntl.h
+FCFLAGS += -X signal.h
+FCFLAGS += -X stdio.h
+FCFLAGS += -X stdlib.h
+FCFLAGS += -X string.h
+FCFLAGS += -X unistd.h
+FCFLAGS += -X sys/ioctl.h
+FCFLAGS += -X sys/stat.h
+FCFLAGS += -X X11/Xlib.h
+FFILTER = grep -v "intf_.*Msg.*\.\.\."
+
+#
+# C compiler flags: common flags
+#
+# CFLAGS
+
+#
+# Additionnal debugging flags
+#
+# Debugging settings: electric fence, debuging symbols and profiling support. 
+# Note that electric fence and accurate profiling are quite uncompatible.
+CCFLAGS += -g
+#CCFLAGS += -pg
+#LIB += -ldmalloc
+#LIB += -lefence
+
+#################################################################################
+# Objects and files
+#################################################################################
+
+#
+# Objects
+# 
+interface_obj =                interface/main.o \
+                                               interface/interface.o \
+                                               interface/intf_msg.o \
+                                               interface/intf_cmd.o \
+                                               interface/intf_ctrl.o \
+                                               interface/control.o \
+                                               interface/xconsole.o 
+
+input_obj =                    input/input_vlan.o \
+                                               input/input_file.o \
+                                               input/input_netlist.o \
+                                               input/input_network.o \
+                                               input/input_ctrl.o \
+                                               input/input_pcr.o \
+                                               input/input_psi.o \
+                                               input/input.o
+
+audio_output_obj =             audio_output/audio_output.o \
+                                               audio_output/audio_dsp.o
+
+video_output_obj =             video_output/video_output.o \
+                                               video_output/video_x11.o \
+                                               video_output/video_graphics.o 
+
+audio_decoder_obj =            audio_decoder/audio_decoder.o
+
+generic_decoder_obj =          generic_decoder/generic_decoder.o
+
+video_decoder_obj =            video_decoder/video_decoder.o
+
+misc_obj =                     misc/mtime.o \
+                                               misc/xutils.o \
+                                               misc/rsc_files.o \
+                                               misc/netutils.o
+
+OBJ =  $(interface_obj) \
+               $(input_obj) \
+               $(audio_output_obj) \
+               $(video_output_obj) \
+               $(audio_decoder_obj) \
+               $(generic_decoder_obj) \
+               $(video_decoder_obj) \
+               $(vlan_obj) \
+               $(misc_obj)
+
+
+#
+# Other lists of files
+#
+sources := $(OBJ:%.o=%.c)
+dependancies := $(sources:%.c=dep/%.d)
+
+# All symbols must be exported
+export
+
+################################################################################
+# Targets
+################################################################################
+
+#
+# Virtual targets
+#
+all: vlc
+
+clean:
+       rm -f $(OBJ)
+
+distclean: clean
+       rm -f **/*.o **/*~ *.log
+       rm -f vlc gmon.out core Documentation/cflow
+       rm -rf dep
+
+FORCE:
+
+#
+# Real targets
+#
+vlc: $(OBJ)
+       $(CC) $(LCFLAGS) $(CFLAGS) -o $@ $(OBJ)
+
+Documentation/cflow: $(sources)
+       cflow $(FCFLAGS) $(CFLAGS) $(sources) | $(FFILTER) > $@
+
+#
+# Generic rules (see below)
+#
+$(dependancies): %.d: FORCE
+       @make -s --no-print-directory -f Makefile.dep $@
+
+$(OBJ): %.o: dep/%.d
+$(OBJ): %.o: %.c
+       $(CC) $(CCFLAGS) $(CFLAGS) -c -o $@ $<
+
+################################################################################
+# Note on generic rules and dependancies
+################################################################################
+
+# Note on dependancies: each .c file is associated with a .d file, which
+# depends of it. The .o file associated with a .c file depends of the .d, of the 
+# .c itself, and of Makefile. The .d files are stored in a separate dep/ 
+# directory.
+# The dep directory should be ignored by CVS.
+
+# Note on inclusions: depending of the target, the dependancies files must 
+# or must not be included. The problem is that if we ask make to include a file,
+# and this file does not exist, it is made before it can be included. In a 
+# general way, a .d file should be included if and only if the corresponding .o 
+# needs to be re-made.
+
+# Two makefiles are used: the main one (this one) has regular generic rules,
+# except for .o files, for which it calls the object Makefile. Dependancies
+# are not included in this file.
+# The object Makefile known how to make a .o from a .c, and includes dependancies
+# for the target, but only those required.
diff --git a/Makefile.dep b/Makefile.dep
new file mode 100644 (file)
index 0000000..0a57b2e
--- /dev/null
@@ -0,0 +1,37 @@
+################################################################################
+# vlc (VideoLAN Client) dependancies makefile
+# (c)1998 VideoLAN
+################################################################################
+# This Makefile is dedicated to build of .d files. It should not be called
+# directly by user, but only through main Makefile.
+# See notes at the end of the main makefile for explanations.
+################################################################################
+
+# All settings and options are passed through main Makefile
+
+################################################################################
+# Default target
+################################################################################
+
+default:
+       @echo "This Makefile should not be called directly - see notes at end of"
+       @echo "main Makefile."
+
+################################################################################
+# Dependancies creation
+################################################################################
+
+# A dependancies file needs to be rebuilt if the .c  changed or if one of the 
+# dependancies files have been changed. In other words, it depends from the
+# .c and from itself.
+
+-include $(MAKECMDGOALS)
+$(dependancies): dep/%.d: %.c
+       @test -d dep/$(dir $*) || mkdir -p dep/$(dir $*)
+       @echo "generating dependancies for $*.c"
+       @$(SHELL) -ec '$(CC) $(DCFLAGS) $(CFLAGS) $< \
+               | sed '\''s/$(subst .,\.,$(notdir $*))\.o[ :]*/$(subst /,\/,$*).o \
+               dep\/$(subst /,\/,$*).d : /g'\'' > $@; \
+               [ -s $@ ] || rm -f $@'
+
+
diff --git a/doc/bugs b/doc/bugs
new file mode 100644 (file)
index 0000000..67d2074
--- /dev/null
+++ b/doc/bugs
@@ -0,0 +1,33 @@
+* interface :
+  Il semble que si l'on envoie 2 messages de logs l'un immediatement apres
+  l'autre, le 2e ne soit jamais affiche (cf debut du DemuxPES)
+
+* video output/X11 :
+  quand une XShmImage n'a jamais Ã©té affichée, la fermer provoque une erreur
+  (fatale ?) du serveur -> plantage de vout_thread.
+
+* video output/X11 :
+  erreur lors de la fermeture 'brutale' de la fenêtre
+    Ptyx: en cours de correction - j'ai trouvé comment signaler au WM qu'une
+    fenêtre est capable de se fermer propremenet sur un ClientMessage, mais
+    pas encore comment intercepter ce ClientMessage :-\
+
+* interface :
+  l'envoi repeté de commandes dans la console provoque un
+  segfault
+    Ptyx: ce bug est du Ã  la xconsole, qu'il faudra de toute manière
+    reprogrammer avec un widget quelconque (je recommande wx, qui est portable,
+    mais moche il est vrai), donc je vais pas me casser Ã  le corriger.
+    Ptyx: vérifier qu'il est bien du Ã  xconsole et non Ã  intf_ExecCommand :-\
+
+* input :
+  = segfault au `quit' lorsqu'un thread input et un thread décodeur sont
+    spawnés (MaXX)
+    - Meuuh et MaXX: le thread input demande Ã  ses décodeurs de se tuer
+      et se tue immédiatement, sans attendre leur mort effective, alors
+      que ces décodeurs accèdent encore Ã  sa structure
+  = le flag b_random_access est activé dans tous les paquets PES (MaXX)
+  = le flag b_data_alignment n'est activé dans aucun paquet PES (MaXX)
+
+* input :
+  = lorsque le pid 120 est sélectionné le vlc segfaulte (MaXX)
diff --git a/doc/common.tex b/doc/common.tex
new file mode 100644 (file)
index 0000000..57b37bb
--- /dev/null
@@ -0,0 +1,15 @@
+%
+% common.tex: common definitions for LaTeX files.
+% (c)1999 VideoLAN
+%
+
+% Included packages
+\usepackage{alltt}
+
+% C-related commands
+\newcommand{\csymbol}[1]{\texttt{#1}}
+
+% C-related environments
+\newenvironment{csource}
+    {\begin{alltt}}
+    {\end{alltt}}
diff --git a/doc/conventions b/doc/conventions
new file mode 100644 (file)
index 0000000..fb43f5d
--- /dev/null
@@ -0,0 +1,15 @@
+- Pas de tabulations dans les sources
+
+- Pas de include dans les headers sauf all.h
+
+- Utiliser systématiquement NULL pour les pointeurs nuls, et non pas 0
+
+- Eviter le if( (a=toto()) != b ), preferer l'ecriture sur 2 lignes, en
+  particulier pour les malloc(s):
+    a = toto();
+    if( a != b )
+
+- A propos des mallocs, plus une remarque qu'une convention: il n'est
+  spécifié nul part que errno est mis Ã  jour si malloc renvoie NULL.
+  Préférez donc strerror(ENOMEM) Ã  strerror(errno) !
+
diff --git a/doc/headers b/doc/headers
new file mode 100644 (file)
index 0000000..0fa4413
--- /dev/null
@@ -0,0 +1,96 @@
+Schéma de dépendance des headers et règles d'écriture
+=====================================================
+
+Ce document décrit les dépendances entre les différents headers utilisés
+par le projet, ainsi que l'ordre dans lequel ils devraient Ãªtre appelés.
+La totalité de ces headers, dans l'ordre, est regroupé dans le fichier
+"all.h", pour faciliter la mise en place de nouvelles structures.
+
+Les headers inscrits entre parenthèses sont nécessaires indirectement.
+
+Merci de maintenir ce fichier Ã  jour.
+
+Headers systèmes (appelés par ordre de repertoire et alphabetique)
+------------------------------------------------------------------
+
+#include <pthread.h>
+#include <netinet/in.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+#include <X11/xpm.h>
+
+Headers communs
+---------------
+
+#include "config.h"
+#include "common.h"         -> may depend of "config.h"
+#include "mtime.h"
+#include "netutils.h"       -> <netinet/in.h>
+#include "xutils.h"         -> <X11/Xlib.h>
+
+
+Input
+-----
+
+#include "input.h"          -> <pthread.h>, <sys/uio.h>, "config.h", "common.h"
+#include "input_vlan.h"     -> <pthread>, <netinet/in.h>
+#include "decoder_fifo.h"   -> <pthread.h>, (<sys/uio.h>), ("config.h"), ("common.h"), "input.h"
+#include "netlist.h"        -> <prhtread.h>, (<sys/uio.h>), "config.h", ("common.h"), "input.h" 
+#include "pcr.h"            -> (<pthread.h>), (<sys/uio.h>), ("config.h"), ("common.h"), "input.h"
+#include "psi.h"            -> (<pthread.h>), (<sys/uio.h>), ("config.h"), ("common.h"), "input.h"
+
+Audio
+-----
+
+#include "audio_decoder.h"  -> *le néant :)*
+#include "audio_output.h"   -> <pthread.h>, <sys/soundcard.h>, "config.h", "common.h", "mtime.h"
+#include "audio_dsp.h"      -> (<pthread.h>), (<sys/soundcard.h>), ("config.h"), ("common.h"), 
+                                ("mtime.h"), "audio_output.h"
+
+Video
+-----
+
+#include "video.h"          -> ("config.h"), "common.h", "mtime.h"
+#include "video_graphics.h" -> ("config.h"), ("common.h"), ("mtime.h"), "video.h"
+#include "video_output.h"   -> <pthread.h>, "config.h", "common.h", ("mtime.h"), "video.h"
+#include "video_x11.h"      -> (<pthread.h>) <X11/Xlib.h>, <X11/extensions/XShm.h>,
+                                ("config.h"), ("common.h"), ("mtime.h"), "video.h", "video_output.h"
+#include "video_decoder.h"  -> <pthread.h>, (<sys/uio.h>), ("config.h"), "common.h", ("mtime.h"), 
+                                "input.h", "decoder_fifo.h", ("video.h"), "video_output.h"
+
+Interface
+---------
+
+#include "xconsole.h"       -> <X11/Xlib.h>, "config.h"
+#include "interface.h"      -> (<pthread.h>), (<sys/uio.h>), (<X11/Xlib.h>), 
+                                (<X11/extensions/XShm.h>), "config.h", "common.h", ("mtime.h"),
+                                "input.h", ("video.h"), "video_output.h", "xconsole.h"
+#include "intf_msg.h"       -> <pthread.h>, "config.h", "common.h", "mtime.h"
+#include "intf_cmd.h"
+#include "control.h"        -> ??
+#include "intf_ctrl.h"      -> "intf_cmd.h"
+
+Ressources partagées
+--------------------
+
+#include "pgm_data.h"       -> (<pthread.h>), (<netinet/in.h>), (<sys/soundcard.h>), (<sys/uio.h>),  
+                                (<X11/Xlib.h>),  
+                                (<X11/extensions/XShm.h>), ("config.h"), "common.h", ("mtime.h"), 
+                                "input.h", "input_vlan.h", "audio_output.h" , "video.h", 
+                                ("video_output.h"), "xconsole.h", "interface.h", "intf_msg.h"
+
+Autres headers, qui sont condamnés Ã  disparaitre ou ne sont pas utilisés
+------------------------------------------------------------------------
+
+#include "network.h"    illisible, uniquement utilisé dans network.c
+#include "rsc_files.h"
+xconsole will also change
+
+
+
+
+
+
+
diff --git a/doc/main.tex b/doc/main.tex
new file mode 100644 (file)
index 0000000..d64d7c2
--- /dev/null
@@ -0,0 +1,60 @@
+%
+% main.tex: structure of the vlc LaTeX documentation
+% (c)1999 VideoLAN
+%
+\documentclass[a4paper]{report}
+
+%
+% Packages and definitions
+%
+\usepackage{graphicx}
+
+\include{common}
+
+\newcommand{\VideoLAN}{\textit{Video}\textsc{lan}}
+
+%
+% Document
+%
+\begin{document}
+
+% Title page and table of contents
+\begin{titlepage}
+
+\VideoLAN project
+
+\begin{center}
+\resizebox{\textwidth}{!}{vlc: the \VideoLAN\ client}
+\bigskip
+a real-time MPEG-2 software decoder and player
+\end{center}
+
+\end{titlepage}
+
+\tableofcontents
+
+% General description of the project
+\part{General description}
+
+\chapter{The goal}
+
+\chapter{??}
+
+\chapter{vlc: the \VideoLAN\ client}
+
+\chapter{Continuation of the project}
+
+% Project organization
+\part{Project organization}
+
+\chapter{Team and communication}
+
+\chapter{Source control}
+
+\chapter{Coding conventions}
+\include{threads}
+
+% Code description
+\part{Code description}
+
+\end{document}
diff --git a/doc/organization.fig b/doc/organization.fig
new file mode 100644 (file)
index 0000000..ff2ef5a
--- /dev/null
@@ -0,0 +1,312 @@
+#FIG 3.2
+Landscape
+Center
+Metric
+A4      
+100.00
+Single
+-2
+1200 2
+6 8820 9180 11970 10260
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        8820 9360 9720 9360
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        8820 9630 9720 9630
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        8820 9900 9720 9900
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        8820 10170 9720 10170
+4 0 0 100 0 0 12 0.0000 4 135 1155 10170 9360 store reference\001
+4 0 0 100 0 0 12 0.0000 4 135 585 10170 9630 manage\001
+4 0 0 100 0 0 12 0.0000 4 135 330 10170 10170 feed\001
+4 0 0 100 0 0 12 0.0000 4 180 1785 10170 9900 create/initialize/destroy\001
+-6
+6 900 900 4950 6030
+6 900 900 4950 6030
+6 1080 1620 2880 2070
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        1080 2070 2880 2070 2880 1620 1080 1620 1080 2070
+4 0 0 100 0 2 12 0.0000 4 135 735 1170 1800 interface\001
+4 0 0 100 0 0 12 0.0000 4 180 1635 1170 1980 management and loop\001
+-6
+6 1080 2520 2970 3150
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        1080 3150 2970 3150 2970 2520 1080 2520 1080 3150
+4 0 0 100 0 2 12 0.0000 4 180 975 1170 2700 intf_console\001
+4 0 0 100 0 0 12 0.0000 4 135 1755 1170 2880 command-line oriented\001
+4 0 0 100 0 0 12 0.0000 4 135 1050 1170 3060 user interface\001
+-6
+6 1080 3600 2520 4050
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        1080 4050 2520 4050 2520 3600 1080 3600 1080 4050
+4 0 0 100 0 2 12 0.0000 4 180 660 1170 3780 intf_cmd\001
+4 0 0 100 0 0 12 0.0000 4 180 1245 1170 3960 command parser\001
+-6
+6 1080 4500 3060 4950
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        1080 4950 3060 4950 3060 4500 1080 4500 1080 4950
+4 0 0 100 0 2 12 0.0000 4 180 615 1170 4680 intf_ctrl\001
+4 0 0 100 0 0 12 0.0000 4 135 1785 1170 4860 command line functions\001
+-6
+6 1080 5400 3150 5850
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        1080 5850 3150 5850 3150 5400 1080 5400 1080 5850
+4 0 0 100 0 2 12 0.0000 4 135 570 1170 5580 control\001
+4 0 0 100 0 0 12 0.0000 4 180 1935 1170 5760 program control functions\001
+-6
+6 3330 2520 4770 2970
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        3330 2970 4770 2970 4770 2520 3330 2520 3330 2970
+4 0 0 100 0 2 12 0.0000 4 180 660 3420 2700 intf_msg\001
+4 0 0 100 0 0 12 0.0000 4 150 1290 3420 2880 messages output\001
+-6
+2 4 0 1 0 7 100 0 -1 0.000 0 0 7 0 0 5
+        4950 900 4950 6030 900 6030 900 900 4950 900
+4 0 0 100 0 2 16 0.0000 4 165 930 1080 1170 interface\001
+4 0 0 100 0 0 12 0.0000 4 180 1920 1080 1350 Manage threads and user\001
+-6
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1530 2070 1530 2520
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1530 3150 1530 3600
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1530 4050 1530 4500
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1530 4950 1530 5400
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        2880 1890 3780 1890 3780 2520
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1620 2070 1620 2520
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        2880 1800 3870 1800 3870 2520
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1710 2070 1710 2520
+-6
+6 900 7200 7380 10350
+6 3240 8910 4860 9360
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        3240 9360 4860 9360 4860 8910 3240 8910 3240 9360
+4 0 0 100 0 0 12 0.0000 4 180 1440 3330 9270 files input methods\001
+4 0 0 100 0 2 12 0.0000 4 180 720 3330 9090 input_file\001
+-6
+6 5220 8910 7200 9360
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        5220 9360 7200 9360 7200 8910 5220 8910 5220 9360
+4 0 0 100 0 2 12 0.0000 4 180 1140 5310 9090 input_network\001
+4 0 0 100 0 0 12 0.0000 4 180 1815 5310 9270 networks input methods\001
+-6
+6 5670 9720 7200 10170
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        5670 10170 7200 10170 7200 9720 5670 9720 5670 10170
+4 0 0 100 0 2 12 0.0000 4 180 795 5760 9900 input_vlan\001
+4 0 0 100 0 0 12 0.0000 4 180 1410 5760 10080 vlans management\001
+-6
+6 1080 8910 2880 9360
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        1080 9360 2880 9360 2880 8910 1080 8910 1080 9360
+4 0 0 100 0 2 12 0.0000 4 135 495 1170 9090 netlist\001
+4 0 0 100 0 0 12 0.0000 4 180 1605 1170 9270 packets management\001
+-6
+6 1080 9720 2070 10170
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        1080 10170 2070 10170 2070 9720 1080 9720 1080 10170
+4 0 0 100 0 2 12 0.0000 4 180 690 1170 9900 input_psi\001
+4 0 0 100 0 0 12 0.0000 4 180 795 1170 10080 PSI parser\001
+-6
+6 2430 9720 3510 10170
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        2430 10170 3510 10170 3510 9720 2430 9720 2430 10170
+4 0 0 100 0 2 12 0.0000 4 180 720 2520 9900 input_pcr\001
+4 0 0 100 0 0 12 0.0000 4 180 870 2520 10080 PCR parser\001
+-6
+6 1080 7830 2610 8460
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        1080 8460 2610 8460 2610 7830 1080 7830 1080 8460
+4 0 0 100 0 2 12 0.0000 4 180 375 1170 8010 input\001
+4 0 0 100 0 0 12 0.0000 4 180 1365 1170 8190 stream parser and\001
+4 0 0 100 0 0 12 0.0000 4 180 1020 1170 8370 demultiplexer\001
+-6
+2 4 0 1 0 7 100 0 -1 0.000 0 0 7 0 0 5
+        7380 7200 7380 10350 900 10350 900 7200 7380 7200
+4 0 0 100 0 2 16 0.0000 4 225 570 1080 7470 input\001
+-6
+6 6750 180 8100 630
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        6750 630 8100 630 8100 180 6750 180 6750 630
+4 0 0 100 0 2 12 0.0000 4 135 375 6840 360 main\001
+4 0 0 100 0 0 12 0.0000 4 180 1185 6840 540 program control\001
+-6
+6 6300 900 8190 3240
+6 6300 900 8190 3240
+6 6480 1620 8010 2250
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        6480 2250 8010 2250 8010 1620 6480 1620 6480 2250
+4 0 0 100 0 2 12 0.0000 4 180 1020 6570 1800 video_output\001
+4 0 0 100 0 0 12 0.0000 4 180 1365 6570 1980 pictures rendering\001
+4 0 0 100 0 0 12 0.0000 4 180 1080 6570 2160 and displaying\001
+-6
+6 6480 2610 8010 3060
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        6480 3060 8010 3060 8010 2610 6480 2610 6480 3060
+4 0 0 100 0 2 12 0.0000 4 180 795 6570 2790 video_x11\001
+4 0 0 100 0 0 12 0.0000 4 180 1320 6570 2970 X11 output driver\001
+-6
+2 4 0 1 0 7 100 0 -1 0.000 0 0 7 0 0 5
+        8190 900 8190 3240 6300 3240 6300 900 8190 900
+4 0 0 100 0 2 16 0.0000 4 225 1380 6480 1170 video_output\001
+4 0 0 100 0 0 12 0.0000 4 180 1425 6480 1350 pictures displaying\001
+-6
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        6930 2250 6930 2610
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7020 2250 7020 2610
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        7110 2250 7110 2610
+-6
+6 9450 900 11430 3240
+6 9450 900 11430 3240
+6 9630 1620 11250 2250
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        9630 2250 11250 2250 11250 1620 9630 1620 9630 2250
+4 0 0 100 0 2 12 0.0000 4 180 1020 9720 1800 audio_output\001
+4 0 0 100 0 0 12 0.0000 4 135 1440 9720 1980 audio frames mixer\001
+4 0 0 100 0 0 12 0.0000 4 180 780 9720 2160 and player\001
+-6
+6 9630 2610 11070 3060
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        9630 3060 11070 3060 11070 2610 9630 2610 9630 3060
+4 0 0 100 0 2 12 0.0000 4 180 795 9720 2790 audio_dsp\001
+4 0 0 100 0 0 12 0.0000 4 180 1275 9720 2970 dsp output driver\001
+-6
+2 4 0 1 0 7 100 0 -1 0.000 0 0 7 0 0 5
+        11430 900 11430 3240 9450 3240 9450 900 11430 900
+4 0 0 100 0 2 16 0.0000 4 225 1410 9630 1170 audio_output\001
+-6
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        10080 2250 10080 2610
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        10170 2250 10170 2610
+-6
+6 6300 4050 8640 5400
+6 6480 4770 8460 5220
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        6480 5220 8460 5220 8460 4770 6480 4770 6480 5220
+4 0 0 100 0 2 12 0.0000 4 180 1185 6570 4950 video_decoder\001
+4 0 0 100 0 0 12 0.0000 4 180 1815 6570 5130 PES parser and decoder\001
+-6
+2 4 0 1 0 7 100 0 -1 0.000 0 0 7 0 0 5
+        8640 4050 8640 5400 6300 5400 6300 4050 8640 4050
+4 0 0 100 0 2 16 0.0000 4 225 1485 6480 4320 video_decoder\001
+-6
+6 9450 4050 11790 5400
+6 9630 4770 11610 5220
+2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5
+        9630 5220 11610 5220 11610 4770 9630 4770 9630 5220
+4 0 0 100 0 2 12 0.0000 4 180 1185 9720 4950 audio_decoder\001
+4 0 0 100 0 0 12 0.0000 4 180 1815 9720 5130 PES parser and decoder\001
+-6
+2 4 0 1 0 7 100 0 -1 0.000 0 0 7 0 0 5
+        11790 4050 11790 5400 9450 5400 9450 4050 11790 4050
+4 0 0 100 0 2 16 0.0000 4 225 1515 9630 4320 audio_decoder\001
+-6
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        1080 1800 450 1800 450 8100 1080 8100
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2880 1710 6480 1710
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1350 8460 1350 8910
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        2610 8100 3690 8100 3690 8910
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        3240 9180 2880 9180
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        5400 9360 5400 9540 2700 9540 2700 9360
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        2610 8010 5400 8010 5400 8910
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        2610 8190 3510 8190 3510 8910
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        2610 7920 5670 7920 5670 8910
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        8100 450 9990 450 9990 1620
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        6750 360 2250 360 2250 1620
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 3
+       1 1 1.00 60.00 120.00
+        8100 360 10080 360 10080 1620
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        6480 4950 5850 4950 5850 1890 6480 1890
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        6480 4860 5940 4860 5940 1980 6480 1980
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        1530 5850 1530 7830
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        9630 4950 9090 4950 9090 2070 9630 2070
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        9630 5040 9000 5040 9000 1980 9630 1980
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        2250 7830 2250 6750 10350 6750 10350 5220
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        1800 7830 1800 6300 6750 6300 6750 5220
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        1890 7830 1890 6390 6840 6390 6840 5220
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        2340 7830 2340 6840 10440 6840 10440 5220
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        1980 7830 1980 6480 6930 6480 6930 5220
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        2430 7830 2430 6930 10530 6930 10530 5220
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 4
+       1 1 1.00 60.00 120.00
+        3150 5490 5400 5490 5400 1800 6480 1800
+2 1 0 1 2 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11700 7650 12600 7650
+2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11700 7830 12600 7830
+2 1 0 1 4 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11700 8010 12600 8010
+2 1 0 1 1 7 100 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        11700 8190 12600 8190
diff --git a/doc/portage b/doc/portage
new file mode 100644 (file)
index 0000000..ca4746c
--- /dev/null
@@ -0,0 +1,7 @@
+FreeBSD:
+    -#ifdef __FreeBSD__
+    -remplacer les sys/soundcard.h par machine/soundcard.c
+    -problème avec getopt
+
+LinuxPPC:
+    -problème avec la libpthreads ? (ou avec le serveur X ?)
diff --git a/doc/threads.tex b/doc/threads.tex
new file mode 100644 (file)
index 0000000..5c67953
--- /dev/null
@@ -0,0 +1,255 @@
+%
+% threads.tex: description of threads interface for VideoLAN client
+% (c)1999 VideoLAN
+%
+\section{A common thread interface}
+
+This document describes how the different threads in the VideoLAN client are
+organized, their API and functionnment.
+
+%
+% Thread properties
+%
+\subsection{Thread properties}
+
+A thread is described by a \csymbol{X\_thread\_t} structure (i.e. 
+\csymbol{vout\_thread\_t}), which is used to reference the thread in calls to 
+its API. This structure includes beside following thread-specific data the 
+following fields:
+
+\begin{csource}
+typedef struct X_thread_s \{
+    pthread_t   thread_id;          /* thread id for pthreads */
+    boolean_t   b_die;              /* `die' flag */
+    boolean_t   b_run;              /* `run' flag */
+    boolean_t   b_error;            /* `error' flag */
+    boolean_t   b_active;           /* `active' flag */
+
+    ... other fields ...
+\} X_thread_t;
+\end{csource}
+
+%
+% Meaning of common flags
+%
+\subsection{Meaning of common flags}
+
+\begin{description}
+\item[\csymbol{die}]:
+The \csymbol{die} flag means that the thread received a destruction request 
+from  another thread. It must terminate as soon as possible. This field is 
+written (set to 1) by other threads and read by the thread itself. It cannot 
+be reset to 0 once it has been set.
+
+Note that when the \csymbol{die} flag is set, no other thread should feed the 
+dying one, and all shared structures should have been freed (i.e. the images 
+and video streams for the video output thread).
+
+\item[\csymbol{run}]:
+The \csymbol{run} flag tells the other threads that the concerned thread is 
+ready to receive data. It is set to 1 by the thread itself when the second 
+phase of the initialization has succeeded, and set to 0 again by the thread 
+once it exited.
+
+\item[\csymbol{error}]:
+The \csymbol{error} flag tells the other threads that a fatal error occured in
+the concerned thread. It can be set by all threads, and is read by the thread
+itself and the controlling thread.
+
+When a thread is in \csymbol{error} state, it runs in a special loop, 
+accepting feed from other threads, but trashing eveything, waiting for a 
+\csymbol{die} signal.
+
+Therefore, the controlling thread should check periodically if a thread has an
+\csymbol{error} set and, if yes, set its \csymbol{die} flag after having 
+destroyed all depending threads.
+
+This flag is optionnal, but recommanded if an error can be envisaged in a later
+extension.
+
+\item[\csymbol{active}]:
+This flag's purpose is to avoid using useless resources. An in-\csymbol{active}
+thread must accept input as if it was inactive, but can treat its input 
+differently.
+In example: the video output thread will set itself as in-\csymbol{active} 
+when it is unmapped, and continue to sort and trash images, but will not 
+render or display them to avoid consumming useless CPU. When a video decoder 
+thread will detect that its related output thread is inactive, it will set 
+itself inactive and trash everything except I images.
+
+The \csymbol{active} flag can be set and read by anyone. Precautions should be
+taken to avoid too long wake-up times.
+
+This flag is optionnal, but recommanded if its use can be envisaged in a later
+extension.
+\end{description}
+
+%
+% API
+%
+\subsection{API}
+
+This API is only a recommandation.
+
+% Creation
+\subsubsection{Creation}
+
+\begin{csource}
+X_thread_t * X_CreateThread( X_cfg_t *p_cfg )
+\end{csource}
+
+This function will allocate thread descriptor, perform basic (and fast)
+initialization steps, and create the thread itself using 
+\csymbol{pthread\_create}.
+
+Once it has been called, all flags are set to 0. It will return the thread 
+descriptor or \csymbol{NULL} on failure.
+
+% Termination
+\subsubsection{Termination}
+
+\begin{csource}
+void X_TerminateThread( X_thread_t * p_X );
+\end{csource}
+
+This function will set the \csymbol{die} flag of the thread and and return 
+immediately.
+
+% Destruction
+\subsubsection{Destruction}
+
+\begin{csource}
+int X_DestroyThread( X_thread_t *p_X );
+\end{csource}
+
+This function will try to destroy the thread descriptor, if it is possible
+(if the \csymbol{run} flag is not set). It will return 0 if it succeeded, 
+and non 0 if the thread was still active.
+
+%
+% Local functions names
+%
+\subsection{Local functions names}
+
+The following functions names are recommanded to implement the different
+parts of the thread creation and destruction:
+
+\begin{csource}
+int InitThread();       /* second phase of initialization */
+int RunThread();        /* main loop */
+int RunError();         /* error loop */
+int DestroyThread();    /* thread destruction */
+\end{csource}
+
+\csymbol{X\_CreateThread()} will spawn a thread using \csymbol{RunThread()} 
+function, which will call \csymbol{InitThread()}, enter its main loop, 
+eventually call \csymbol{RunError()} and finally calls \csymbol{DestroyThread}
+when \csymbol{die} is received.
+
+%
+% Order of operations
+%
+\subsection{Order of operations}
+
+% Creation
+\subsubsection{Creation}
+
+\begin{tabular}{l|l}
+Controlling thread                              &   Thread \\
+\hline
+
+\csymbol{p\_X = X\_CreateThread( p\_cfg )}:     & \\
+    descriptor allocation and initialization    & \\
+    all flags are set to 0                      & \\
+    base structures initialization              & \\
+If \csymbol{p\_X == NULL}: error                & \\
+    \csymbol{X\_DestroyThread( p\_X )}:         & \\
+        destruction of the descriptor           & \\
+    end...
+Else, continuation....                          & \csymbol{pthread\_create()} \\
+                                                & Second step of initialization \\
+                                                & On error: \\
+                                                &   \csymbol{b\_error = 1} \\
+                                                &   destruction of structures \\
+                                                & Else: \\
+                                                &   \csymbol{b\_run = 1} \\
+                                                &   beginning of main loop \\
+
+\hline
+
+Wait for \csymbol{b\_run} or \csymbol{b\_error}...& main loop... \\
+If \csymbol{b\_error}:                          & \\
+    \csymbol{X\_DestroyThread( p\_X )}          & \\
+    end...                                      & \\
+Else (\csymbol{b\_run == 1}):                   & \\
+    the thread is ready and can be feeded...    & \\
+
+\hline
+\end{tabular}
+
+Notes:
+\begin{enumerate}
+\item The configuration structure can have been destroyed just after 
+    \csymbol{X\_CreateThread()}. Therefore, it should not be used during second 
+    initialization step.
+
+\item When an error occurs during second initialization step, the allocated structures
+    are automatically destroyed (except the thread descriptor). Therefore, a call to
+    \csymbol{X\_TerminateThread} is not required.
+\end{enumerate}
+
+% Main loop
+\subsubsection{Main loop}
+
+\begin{tabular}{l|l}
+Controlling thread                              &   Thread \\
+\hline
+
+Periodically check for \csymbol{b\_error}        & Periodically check for \\
+If set, then:                                   & \csymbol{b\_error} and \csymbol{b\_die}\\
+    terminate all dependant threads             & \\
+    destroy all dependant threads               & \\
+    terminate and destroy thread                & \\
+
+\hline
+\end{tabular}
+% Destruction
+\subsubsection{Destruction}
+
+\begin{tabular}{l|l}
+Controlling thread                              &   Thread \\
+\hline
+
+\csymbol{X\_TerminateThread( p\_X )}:           & \\
+    set \csymbol{b\_die}                         & \\
+    all flags are set to 0                      & If \csymbol{DEBUG}, check if  \\
+                                                & all shared structures are ok. \\
+                                                & Destroy and close everything, but  \\
+                                                & keep descriptor. \\
+                                                & Set \csymbol{b\_run} to 0. \\
+                                                & Exit thread. \\
+
+\hline
+
+Loop until \csymbol{X\_DestroyThread} is 0:      & \\
+    check if \csymbol{b\_run == 0}                & \\
+    if yes:                                     & \\
+        destroy descriptor                      & \\
+        return 0                                & \\
+    else:                                       & \\
+        return 1                                & \\
+
+\hline
+\end{tabular}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/todo b/doc/todo
new file mode 100644 (file)
index 0000000..bd8dfe6
--- /dev/null
+++ b/doc/todo
@@ -0,0 +1,113 @@
+-----------------------------------------------------------------------------
+* marque les points prioritaires
++ les points Ã  ne pas oublier avant la fin de l'année
+- les trucs qu'il faudra faire, mais qui ne sont pas urgent
+? les idées Ã  discuter
+-----------------------------------------------------------------------------
+
+Input
+=====
+    ? utilisation de la NAT pour convoyer des infos sur les vlans
+    * gestion du décodeur générique/par défaut
+    - passage en b_run    
+    + trouver des flux de test bien tordus pour tester l'update des
+       PSI, les discontinuites, la gestion des PCR, etc...
+
+Input: vlan method
+==================
+    + faire une libvlan rapide (benny est dessus)
+    - reconcevoir l'API et les dialogues du vlan serveur, faire une
+        libvlan avancée, l'encapsuler proprement dans input_vlan, 
+        refaire le vlan server
+
+Main
+====
+    - lire un fichier de config en plus de la ligne de commande
+
+Audio output
+============
+    - detection automatique du materiel
+    ? penser Ã  une nouvelle manière de remplir le buffer de sortie, tout en 
+        conservant l'algorithme de rééchantillonage...
+
+Audio decoder
+=============
+    * tout tout tout
+
+Video output
+============
+    + nettoyer la boucle principale
+        + tenir compte du fait que lorsque b_die est mis, normalement tous
+            les flux et images doivent déjà Ãªtre detruits -> afficher une
+            erreur plutôt que de les supprimer
+        + gestion de b_active
+        + arbre de décisions avec goto pour les passes
+    * convertion et affichage des images
+    * gestion des couleurs X11
+    ? interpolation pour des display ratios < 1
+    ? utiliser XDGA pour accelerer 
+
+Video graphics
+==============
+    + tout reste Ã  implementer
+    + rajouter les convertions rgb->pixel
+
+Video decoder
+=============
+    * tout tout tout
+    + sur p_vout->b_active == 0, trasher tout sauf I
+    * parser/trasher de PES
+
+Generic decoder
+===============
+    * identification des paquets
+    * sauvegarde d'un flux multiplexé
+    * sauvegarde de flux demultiplexés
+    
+Interface
+=========
+    - incrustation d'infos sur les buffers
+    + réagir aux signaux
+
+Interface: commandes
+====================
+    - Plein de commandes Ã  implémenter
+
+Interface: console
+==================
+    - Utiliser un widget quelconque (portable de préférence) et renommer
+      en intf_console si la portabilité est atteinte (sinon, x11console).
+    - Utilisation des pixmaps pour egayer un peu en début de ligne
+    - Gestion des fontes et couleurs
+    - Choix d'une couleur de bg en l'absende de pixmap
+    - Ascenceur pour la zone de texte
+    - Unicité des enregistrements de l'history - sauvegarde de l'history
+    - Gestion de la souris (copier/coller)
+
+General: sources
+================
+    + déclarer en extern inline les fonctions de mtime et netlist...
+    - Eliminer les headers superflus des sources, bien indiquer les dépendances
+    - conserver la cohérence des sources: input_psi plutot que psi par exemple.
+    + vérifier que des messages sont bien Ã©mis partout (faire une recherche sur
+      les 'returns'... En particulier dans video_output).
+
+General: processus
+==================
+    * définir l'ordre de fermeture et les accès
+    - identifier les threads (pour ps)
+    ? penser au mode mono-thread
+    + utiliser les messages b_die, b_error, b_active et b_run
+
+General: X11
+============
+    - detection de fermeture des fenêtres
+    - déclaration des icones pour les window managers
+    - implémentation de X{En|Dis}ableScreenSaver dans xutils
+
+Documentation
+=============
+    - documentation des interfaces de programmation dès qu'elles sont
+        au point. En LaTeX ? (pour latex2html).
+
+
diff --git a/doc/vlan-server b/doc/vlan-server
new file mode 100644 (file)
index 0000000..7cbd96e
--- /dev/null
@@ -0,0 +1,116 @@
+Vlan server protocol and library *draft*
+========================================
+
+telnet-based protocol. Client commands are text, server responses are numbers followed by
+an explicative text (like irc). Common messages number should probalbly be grouped.
+
+Two different notions: session and connexion
+    session has a long timeout (several hours), and should not use server-side resources
+    connexion has a short timeout (a few minutes) and can use server-side resources (thread)
+
+A vlan resource (switch+port) is identified by a unique id. Changes can obviously be done
+with this id, but also, for administrative facilities, with other identifiers.
+
+Client commands:
+
+LOGIN <login> [<application>]
+    -> open connexion, required before any communication with server. Login and password should
+    correspond to groups (admin, users...) rather than individual ids. Application is for stats.
+    answers:    100: ok
+                101: rejected, invalid login
+                102: rejected, too many connexions
+                103: rejected, forbidden source 
+                104: rejected, already logged in
+                109: rejected, other reason    
+                password required
+
+PASS <password>
+
+LOGOUT -> close connexion
+    answer:     200: ok, session closed
+
+OPEN <client-session-id>
+    -> open session. required before any write operations
+    answers:    300: <server-session-id>
+                301: rejected, not authorized
+                302: rejected, too many sessions
+                399: rejected, other reason
+
+RECOVER <client-session-id> <server-session-id>
+    -> recover session. Both id are required for security.
+    answer:     400: ok
+                401: rejected, not authorized (invalid login or something else)
+                402: rejected, invalid ids
+                403: rejected, session already active on another connexion
+                499: rejected, other reason
+
+CLOSE [<server-session-id>]
+    -> close session. Session must be active for normal users.
+    answer:     500: ok, session closed
+                501: error, not loged in
+                502: error: active session
+                503: error: not authorized
+
+STATUS ME
+STATUS MAC <mac>
+STATUS IP <ip>
+STATUS ID <rsc-id>
+    give informations about something
+    answer:     600: mark beginning of status info
+                601: mark end of status info
+                602: error, not authorized
+                603: error, unknown
+                604: error, request failed             
+                610: <rsc-id>
+                611: <vlan>
+                612: <ip>
+                613: <mac>
+                614: <switch-ip>
+                615: <switch-port>
+                616: <number of interfaces dependant of this resource>
+                617: <lock info>
+                699: error, other reason
+
+LOCK ME
+LOCK MAC <mac>
+LOCK IP <ip>
+LOCK ID <rsc-id>
+    lock a resource id (session required)
+    answers:
+     
+UNLOCK ME
+UNLOCK MAC <mac>
+UNLOCK IP <ip>
+UNLOCK ID <id>
+    unlock a resource id (session required)
+    answers:
+
+CHANGE ME <vlan>
+CHANGE MAC <mac> <vlan>
+CHANGE IP <ip> <vlan>
+CHANGE ID <id> <vlan>
+    change a vlan (session required)
+
+Administrative commands:
+
+HELP
+    print list of commands
+    answer:     
+
+KICK
+
+RESET
+
+LIST IDS
+LIST MACS
+LIST IPS
+LIST SESSIONS
+LIST CONNECTIONS
+LIST LOGINS
+
+QUIT
+
+CONFIRM
+
+
+
diff --git a/include/all.h b/include/all.h
new file mode 100644 (file)
index 0000000..a00e6c9
--- /dev/null
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * all.h: all headers
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * This header includes all vlc .h headers and depending headers. A source file
+ * including it would also be able to use any of the structures of the project.
+ * Note that functions or system headers specific to the file itself are not
+ * included.
+ *******************************************************************************
+ * required headers:
+ *  none
+ *******************************************************************************/
+
+/* System headers */
+#include <pthread.h>
+#include <netinet/in.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+/* Common headers */
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+
+/* Input */
+#include "input.h"
+#include "input_vlan.h"
+#include "decoder_fifo.h"
+
+/* Audio */
+#include "audio_output.h"
+#include "audio_decoder.h"
+
+/* Video */
+#include "video.h"
+#include "video_output.h"
+#include "video_decoder.h"
+
+/* Interface */
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+
+/* Shared resources */
+#include "pgm_data.h"
+
+
+
diff --git a/include/audio_constants.h b/include/audio_constants.h
new file mode 100644 (file)
index 0000000..8b650a7
--- /dev/null
@@ -0,0 +1,1438 @@
+/******************************************************************************
+ * audio_constants.h : defines the MPEG1 Layer I-II audio constants and tables
+ * (c)1999 VideoLAN
+ ******************************************************************************/
+
+
+/******************************************************************************
+ * 32 bits (4 bytes) audio frame header definitions
+ ******************************************************************************/
+/*
+ * syncword == `1111 1111 1111'
+ */
+#define ADEC_HEADER_SYNCWORD_MASK               0xFFF00000
+#define ADEC_HEADER_SYNCWORD_SHIFT              20
+
+/* ID :
+ *
+ * `0' == reserved
+ * `1' == ISO/CEI 11172-3
+ */
+#define ADEC_HEADER_ID_MASK                     0x00080000
+#define ADEC_HEADER_ID_SHIFT                    19
+
+/*
+ * Layer :
+ *
+ * - `00' == reserved
+ * - `01' == Layer III
+ * - `10' == Layer II
+ * - `11' == Layer I
+ */
+#define ADEC_HEADER_LAYER_MASK                  0x00060000
+#define ADEC_HEADER_LAYER_SHIFT                 17
+
+#define ADEC_HEADER_LAYER_1                     0x00060000
+#define ADEC_HEADER_LAYER_2                     0x00040000
+#define ADEC_HEADER_LAYER_3                     0x00020000
+
+/* protection_bit */
+#define ADEC_HEADER_PROTECTION_BIT_MASK         0x00010000
+#define ADEC_HEADER_PROTECTION_BIT_SHIFT        16
+
+/* bitrate_index */
+#define ADEC_HEADER_BITRATE_INDEX_MASK          0x0000F000
+#define ADEC_HEADER_BITRATE_INDEX_SHIFT         12
+
+/*
+ * sampling_frequency :
+ *
+ * - `00' == 44100 Hz
+ * - `01' == 48000 Hz
+ * - `10' == 32000 Hz
+ * - `11' == reserved
+ */
+#define ADEC_HEADER_SAMPLING_FREQUENCY_MASK     0x00000C00
+#define ADEC_HEADER_SAMPLING_FREQUENCY_SHIFT    10
+
+/* padding_bit */
+#define ADEC_HEADER_PADDING_BIT_MASK            0x00000200
+#define ADEC_HEADER_PADDING_BIT_SHIFT           9
+
+/* private_bit */
+#define ADEC_HEADER_PRIVATE_BIT_MASK            0x00000100
+#define ADEC_HEADER_PRIVATE_BIT_SHIFT           8
+
+/*
+ * mode :
+ *
+ * - `00' == stereo (stereo mode)
+ * - `01' == combined stereo (stereo mode)
+ * - `10' == two channels (stereo mode)
+ * - `11' == one channel (mono mode)
+ */
+#define ADEC_HEADER_MODE_MASK                   0x000000C0
+#define ADEC_HEADER_MODE_SHIFT                  6
+
+/* mode_extension */
+#define ADEC_HEADER_MODE_EXTENSION_MASK         0x00000030
+#define ADEC_HEADER_MODE_EXTENSION_SHIFT        4
+
+/* copyright */
+#define ADEC_HEADER_COPYRIGHT_MASK              0x00000008
+#define ADEC_HEADER_COPYRIGHT_SHIFT             3
+
+/* original/copy */
+#define ADEC_HEADER_ORIGINAL_COPY_MASK          0x00000004
+#define ADEC_HEADER_ORIGINAL_COPY_SHIFT         2
+
+/* emphasis */
+#define ADEC_HEADER_EMPHASIS_MASK               0x00000003
+#define ADEC_HEADER_EMPHASIS_SHIFT              0
+
+
+/******************************************************************************
+ * frame sizes = f( layer, padding_bit, sampling_frequency, bitrate_index )
+ ******************************************************************************
+ * ISO/IEC 11172-3 2.4.3.1 explains how to find out the number of bytes between
+ * two consecutive syncwords. In order to work out the body size of the frame,
+ * we just have to substract 4 bytes for the header size.
+ *
+ * = Layer I : (slot_size == 4 bytes)
+ *   - padding_bit == 0 :
+ *     frame_size = ( floor(  12 * bitrate(layer, bitrate_index) / sampling_frequency ) * 4 ) - 4
+ *   - padding_bit == 1 :
+ *     frame_size = (  ceil(  12 * bitrate(layer, bitrate_index) / sampling_frequency ) * 4 ) - 4
+ *
+ * = Layer II : (slot_size == 1 byte)
+ *   - padding_bit == 0 :
+ *     frame_size = ( floor( 144 * bitrate(layer, bitrate_index) / sampling_frequency ) * 1 ) - 4
+ *   - padding_bit == 1 :
+ *     frame_size = (  ceil( 144 * bitrate(layer, bitrate_index) / sampling_frequency ) * 1 ) - 4
+ *
+ * The frame sizes are stored in the following array :
+ * frame_size = ADEC_FRAME_SIZE[ 128*layer + 64*padding_bit + 16*sampling_frequency + bitrate_index ]
+ ******************************************************************************/
+#define ADEC_FRAME_SIZE \
+{ \
+    /* Layer == `00' (reserved) */ \
+\
+        /* padding_bit == `0' */ \
+\
+            /* sampling_frequency == `00' (44100 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `01' (48000 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `10' (32000 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `11' (reserved) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+\
+        /* padding_bit == `1' */ \
+\
+            /* sampling_frequency == `00' (44100 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `01' (48000 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `10' (32000 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `11' (reserved) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+\
+\
+    /* Layer == `01' (III) */ \
+\
+        /* padding_bit == `0' */ \
+\
+            /* sampling_frequency == `00' (44100 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `01' (48000 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `10' (32000 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `11' (reserved) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+\
+        /* padding_bit == `1' */ \
+\
+            /* sampling_frequency == `00' (44100 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `01' (48000 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `10' (32000 Hz) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+            /* sampling_frequency == `11' (reserved) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+\
+\
+    /* Layer == `10' (II) */ \
+\
+        /* padding_bit == `0' */ \
+\
+            /* sampling_frequency == `00' (44100 Hz) */ \
+                0,  100,  152,  178,  204,  257,  309,  361,  413,  518,  622,  727,  831, 1040, 1249,    0, \
+            /* sampling_frequency == `01' (48000 Hz) */ \
+                0,   92,  140,  164,  188,  236,  284,  332,  380,  476,  572,  668,  764,  956, 1148,    0, \
+            /* sampling_frequency == `10' (32000 Hz) */ \
+                0,  140,  212,  248,  284,  356,  428,  500,  572,  716,  860, 1004, 1148, 1436, 1724,    0, \
+            /* sampling_frequency == `11' (reserved) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+\
+        /* padding_bit == `1' */ \
+\
+            /* sampling_frequency == `00' (44100 Hz) */ \
+                0,  101,  153,  179,  205,  258,  310,  362,  414,  519,  623,  728,  832, 1041, 1250,    0, \
+            /* sampling_frequency == `01' (48000 Hz) */ \
+                0,   92,  140,  164,  188,  236,  284,  332,  380,  476,  572,  668,  764,  956, 1148,    0, \
+            /* sampling_frequency == `10' (32000 Hz) */ \
+                0,  140,  212,  248,  284,  356,  428,  500,  572,  716,  860, 1004, 1148, 1436, 1724,    0, \
+            /* sampling_frequency == `11' (reserved) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+\
+\
+    /* Layer == `11' (I) */ \
+\
+        /* padding_bit == `0' */ \
+\
+            /* sampling_frequency == `00' (44100 Hz) */ \
+                0,   28,   64,  100,  132,  168,  204,  236,  272,  308,  344,  376,  412,  448,  480,    0, \
+            /* sampling_frequency == `01' (48000 Hz) */ \
+                0,   28,   60,   92,  124,  156,  188,  220,  252,  284,  316,  348,  380,  412,  444,    0, \
+            /* sampling_frequency == `10' (32000 Hz) */ \
+                0,   44,   92,  140,  188,  236,  284,  332,  380,  428,  476,  524,  572,  620,  668,    0, \
+            /* sampling_frequency == `11' (reserved) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0, \
+\
+        /* padding_bit == `1' */ \
+\
+            /* sampling_frequency == `00' (44100 Hz) */ \
+                0,   32,   68,  104,  136,  172,  208,  240,  276,  312,  348,  380,  416,  452,  484,    0, \
+            /* sampling_frequency == `01' (48000 Hz) */ \
+                0,   28,   60,   92,  124,  156,  188,  220,  252,  284,  316,  348,  380,  412,  444,    0, \
+            /* sampling_frequency == `10' (32000 Hz) */ \
+                0,   44,   92,  140,  188,  236,  284,  332,  380,  428,  476,  524,  572,  620,  668,    0, \
+            /* sampling_frequency == `11' (reserved) */ \
+                0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0 \
+}
+
+
+/******************************************************************************
+ * scale factors = f( scalefactor ) (Layer I & II, see ISO/IEC 11172-3 2.4.1)
+ ******************************************************************************
+ * Theses values are 2^(1 - index/3) (see ISO/IEC 11172-3 2.4.2.5)
+ ******************************************************************************/
+#define ADEC_SCALE_FACTOR \
+{ \
+    /* 0*/ 2.00000000000000, /* 1*/ 1.58740105196820, /* 2*/ 1.25992104989487, \
+    /* 3*/ 1.00000000000000, /* 4*/ 0.79370052598410, /* 5*/ 0.62996052494744, \
+    /* 6*/ 0.50000000000000, /* 7*/ 0.39685026299205, /* 8*/ 0.31498026247372, \
+    /* 9*/ 0.25000000000000, /*10*/ 0.19842513149602, /*11*/ 0.15749013123686, \
+    /*12*/ 0.12500000000000, /*13*/ 0.09921256574801, /*14*/ 0.07874506561843, \
+    /*15*/ 0.06250000000000, /*16*/ 0.04960628287401, /*17*/ 0.03937253280921, \
+    /*18*/ 0.03125000000000, /*19*/ 0.02480314143700, /*20*/ 0.01968626640461, \
+    /*21*/ 0.01562500000000, /*22*/ 0.01240157071850, /*23*/ 0.00984313320230, \
+    /*24*/ 0.00781250000000, /*25*/ 0.00620078535925, /*26*/ 0.00492156660115, \
+    /*27*/ 0.00390625000000, /*28*/ 0.00310039267963, /*29*/ 0.00246078330058, \
+    /*30*/ 0.00195312500000, /*31*/ 0.00155019633981, /*32*/ 0.00123039165029, \
+    /*33*/ 0.00097656250000, /*34*/ 0.00077509816991, /*35*/ 0.00061519582514, \
+    /*36*/ 0.00048828125000, /*37*/ 0.00038754908495, /*38*/ 0.00030759791257, \
+    /*39*/ 0.00024414062500, /*40*/ 0.00019377454248, /*41*/ 0.00015379895629, \
+    /*42*/ 0.00012207031250, /*43*/ 0.00009688727124, /*44*/ 0.00007689947814, \
+    /*45*/ 0.00006103515625, /*46*/ 0.00004844363562, /*47*/ 0.00003844973907, \
+    /*48*/ 0.00003051757813, /*49*/ 0.00002422181781, /*50*/ 0.00001922486954, \
+    /*51*/ 0.00001525878906, /*52*/ 0.00001211090890, /*53*/ 0.00000961243477, \
+    /*54*/ 0.00000762939453, /*55*/ 0.00000605545445, /*56*/ 0.00000480621738, \
+    /*57*/ 0.00000381469727, /*58*/ 0.00000302772723, /*59*/ 0.00000240310869, \
+    /*60*/ 0.00000190734863, /*61*/ 0.00000151386361, /*62*/ 0.00000120155435, \
+    /*63*/ 0.0 /* ?? invalid scale factor ?? */ \
+}
+
+
+/******************************************************************************
+ * Layer I definitions
+ ******************************************************************************/
+
+/*
+ * slope table = f( allocation[ch][sb] (see ISO/IEC 11171-3 2.4.1) )
+ */
+#define ADEC_LAYER1_SLOPE \
+{ \
+    /* 0*/ 0.0, /* no sample */ \
+    /* 1*/ 2.0/3, \
+    /* 2*/ 2.0/7, \
+    /* 3*/ 2.0/15, \
+    /* 4*/ 2.0/31, \
+    /* 5*/ 2.0/63, \
+    /* 6*/ 2.0/127, \
+    /* 7*/ 2.0/255, \
+    /* 8*/ 2.0/511, \
+    /* 9*/ 2.0/1023, \
+    /*10*/ 2.0/2047, \
+    /*11*/ 2.0/4095, \
+    /*12*/ 2.0/8191, \
+    /*13*/ 2.0/16383, \
+    /*14*/ 2.0/32767, \
+    /*15*/ 0.0 /* invalid bit allocation */ \
+}
+
+/*
+ * offset table = f( allocation[ch][sb] (see ISO/IEC 11172-3 2.4.1) )
+ */
+#define ADEC_LAYER1_OFFSET \
+{ \
+    /* 0*/      0.0, /* no sample */ \
+    /* 1*/     -2.0/3, \
+    /* 2*/     -6.0/7, \
+    /* 3*/    -14.0/15, \
+    /* 4*/    -30.0/31, \
+    /* 5*/    -62.0/63, \
+    /* 6*/   -126.0/127, \
+    /* 7*/   -254.0/255, \
+    /* 8*/   -510.0/511, \
+    /* 9*/  -1022.0/1023, \
+    /*10*/  -2046.0/2047, \
+    /*11*/  -4094.0/4095, \
+    /*12*/  -8190.0/8191, \
+    /*13*/ -16382.0/16383, \
+    /*14*/ -32766.0/32767, \
+    /*15*/      0.0 /* invalid bit allocation */ \
+}
+
+
+/******************************************************************************
+ * Layer II definitions
+ ******************************************************************************/
+
+/*
+ * Bitrate PER CHANNEL index = f( mode, bitrate_index )
+ * (see ISO/IEC 11172-3 2.4.2.3)
+ *
+ * - This index is used in the ADEC_LAYER2_SBLIMIT and ADEC_LAYER2_NBAL tables.
+ *
+ * -  0 == forbidden mode/bitrate_index combination
+ * -  1 ==  32 kbits/s per channel
+ * -  2 ==  48 kbits/s per channel
+ * -  3 ==  56 kbits/s per channel
+ * -  4 ==  64 kbits/s per channel
+ * -  5 ==  80 kbits/s per channel
+ * -  6 ==  96 kbits/s per channel
+ * -  7 == 112 kbits/s per channel
+ * -  8 == 128 kbits/s per channel
+ * -  9 == 160 kbits/s per channel
+ * - 10 == 192 kbits/s per channel
+ */
+#define ADEC_LAYER2_BITRATE_PER_CHANNEL_INDEX \
+{ \
+    /* mode == `00' (stereo) */ \
+    {  0,  0,  0,  0,  1,  0,  2,  3,  4,  5,  6,  7,  8,  9, 10 }, \
+    /* mode == `01' (combined stereo) */ \
+    {  0,  0,  0,  0,  1,  0,  2,  3,  4,  5,  6,  7,  8,  9, 10 }, \
+    /* mode == `10' (two channels) */ \
+    {  0,  0,  0,  0,  1,  0,  2,  3,  4,  5,  6,  7,  8,  9, 10 }, \
+    /* mode == `11' (one channel) */ \
+    {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,  0,  0,  0,  0 } \
+}
+
+/*
+ * Number of subbands = f( sampling_frequency, bitrate_per_channel_index )
+ * (see ISO/IEC 11172-3 Annex B.2)
+ */
+#define ADEC_LAYER2_SBLIMIT \
+{ \
+    /* sampling_frequency == `00' (44100 Hz) */ \
+    {  0,  8,  8, 27, 27, 27, 30, 30, 30, 30, 30}, \
+    /* sampling_frequency == `01' (48000 Hz) */ \
+    {  0,  8,  8, 27, 27, 27, 27, 27, 27, 27, 27}, \
+    /* sampling_frequency == `10' (32000 Hz) */ \
+    {  0, 12, 12, 27, 27, 27, 30, 30, 30, 30, 30} \
+}
+
+/*
+ * Number of bits allocated = f( bitrate_per_channel_index, subband )
+ * (see ISO/IEC 11172-3 Annex B.2)
+ */
+#define ADEC_LAYER2_NBAL \
+{ \
+    /* bitrate_per_channel_index <= 2 */ \
+    { 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, \
+    /* bitrate_per_channel_index > 2 */ \
+    { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 0, 0} \
+}
+
+/*
+ * = When 3 samples are grouped in one codeword, we have to ungroup them in
+ *   order to dequantize the samples (see ISO/IEC 11172-3 2.4.3.3.4) :
+ *   1) Ungrouping
+ *          for ( i = 0; i < 3; i++ )
+ *          {
+ *              s[i] = c % nlevels;
+ *              c = c / nlevels;
+ *          }
+ *   2) Requantization
+ *
+ * = We pre-calculated all this, and stored the results in the following
+ *   ungroup`nlevels' tables. ISO/IEC 11172-3 Annex B.4 tells us that the
+ *   samples are grouped only when nlevels == 3, 5, or 9.
+ *
+ * = ADEC_LAYER2_UNGROUPn = f(3 * n*n*n)
+ */
+#define ADEC_LAYER2_UNGROUP3 \
+{ \
+    -2.0/3, -2.0/3, -2.0/3, \
+      .0  , -2.0/3, -2.0/3, \
+     2.0/3, -2.0/3, -2.0/3, \
+    -2.0/3,   .0  , -2.0/3, \
+      .0  ,   .0  , -2.0/3, \
+     2.0/3,   .0  , -2.0/3, \
+    -2.0/3,  2.0/3, -2.0/3, \
+      .0  ,  2.0/3, -2.0/3, \
+     2.0/3,  2.0/3, -2.0/3, \
+    -2.0/3, -2.0/3,   .0  , \
+      .0  , -2.0/3,   .0  , \
+     2.0/3, -2.0/3,   .0  , \
+    -2.0/3,   .0  ,   .0  , \
+      .0  ,   .0  ,   .0  , \
+     2.0/3,   .0  ,   .0  , \
+    -2.0/3,  2.0/3,   .0  , \
+      .0  ,  2.0/3,   .0  , \
+     2.0/3,  2.0/3,   .0  , \
+    -2.0/3, -2.0/3,  2.0/3, \
+      .0  , -2.0/3,  2.0/3, \
+     2.0/3, -2.0/3,  2.0/3, \
+    -2.0/3,   .0  ,  2.0/3, \
+      .0  ,   .0  ,  2.0/3, \
+     2.0/3,   .0  ,  2.0/3, \
+    -2.0/3,  2.0/3,  2.0/3, \
+      .0  ,  2.0/3,  2.0/3, \
+     2.0/3,  2.0/3,  2.0/3 \
+}
+
+#define ADEC_LAYER2_UNGROUP5 \
+{ \
+    -.8, -.8, -.8, \
+    -.4, -.8, -.8, \
+     .0, -.8, -.8, \
+     .4, -.8, -.8, \
+     .8, -.8, -.8, \
+    -.8, -.4, -.8, \
+    -.4, -.4, -.8, \
+     .0, -.4, -.8, \
+     .4, -.4, -.8, \
+     .8, -.4, -.8, \
+    -.8,  .0, -.8, \
+    -.4,  .0, -.8, \
+     .0,  .0, -.8, \
+     .4,  .0, -.8, \
+     .8,  .0, -.8, \
+    -.8,  .4, -.8, \
+    -.4,  .4, -.8, \
+     .0,  .4, -.8, \
+     .4,  .4, -.8, \
+     .8,  .4, -.8, \
+    -.8,  .8, -.8, \
+    -.4,  .8, -.8, \
+     .0,  .8, -.8, \
+     .4,  .8, -.8, \
+     .8,  .8, -.8, \
+    -.8, -.8, -.4, \
+    -.4, -.8, -.4, \
+     .0, -.8, -.4, \
+     .4, -.8, -.4, \
+     .8, -.8, -.4, \
+    -.8, -.4, -.4, \
+    -.4, -.4, -.4, \
+     .0, -.4, -.4, \
+     .4, -.4, -.4, \
+     .8, -.4, -.4, \
+    -.8,  .0, -.4, \
+    -.4,  .0, -.4, \
+     .0,  .0, -.4, \
+     .4,  .0, -.4, \
+     .8,  .0, -.4, \
+    -.8,  .4, -.4, \
+    -.4,  .4, -.4, \
+     .0,  .4, -.4, \
+     .4,  .4, -.4, \
+     .8,  .4, -.4, \
+    -.8,  .8, -.4, \
+    -.4,  .8, -.4, \
+     .0,  .8, -.4, \
+     .4,  .8, -.4, \
+     .8,  .8, -.4, \
+    -.8, -.8,  .0, \
+    -.4, -.8,  .0, \
+     .0, -.8,  .0, \
+     .4, -.8,  .0, \
+     .8, -.8,  .0, \
+    -.8, -.4,  .0, \
+    -.4, -.4,  .0, \
+     .0, -.4,  .0, \
+     .4, -.4,  .0, \
+     .8, -.4,  .0, \
+    -.8,  .0,  .0, \
+    -.4,  .0,  .0, \
+     .0,  .0,  .0, \
+     .4,  .0,  .0, \
+     .8,  .0,  .0, \
+    -.8,  .4,  .0, \
+    -.4,  .4,  .0, \
+     .0,  .4,  .0, \
+     .4,  .4,  .0, \
+     .8,  .4,  .0, \
+    -.8,  .8,  .0, \
+    -.4,  .8,  .0, \
+     .0,  .8,  .0, \
+     .4,  .8,  .0, \
+     .8,  .8,  .0, \
+    -.8, -.8,  .4, \
+    -.4, -.8,  .4, \
+     .0, -.8,  .4, \
+     .4, -.8,  .4, \
+     .8, -.8,  .4, \
+    -.8, -.4,  .4, \
+    -.4, -.4,  .4, \
+     .0, -.4,  .4, \
+     .4, -.4,  .4, \
+     .8, -.4,  .4, \
+    -.8,  .0,  .4, \
+    -.4,  .0,  .4, \
+     .0,  .0,  .4, \
+     .4,  .0,  .4, \
+     .8,  .0,  .4, \
+    -.8,  .4,  .4, \
+    -.4,  .4,  .4, \
+     .0,  .4,  .4, \
+     .4,  .4,  .4, \
+     .8,  .4,  .4, \
+    -.8,  .8,  .4, \
+    -.4,  .8,  .4, \
+     .0,  .8,  .4, \
+     .4,  .8,  .4, \
+     .8,  .8,  .4, \
+    -.8, -.8,  .8, \
+    -.4, -.8,  .8, \
+     .0, -.8,  .8, \
+     .4, -.8,  .8, \
+     .8, -.8,  .8, \
+    -.8, -.4,  .8, \
+    -.4, -.4,  .8, \
+     .0, -.4,  .8, \
+     .4, -.4,  .8, \
+     .8, -.4,  .8, \
+    -.8,  .0,  .8, \
+    -.4,  .0,  .8, \
+     .0,  .0,  .8, \
+     .4,  .0,  .8, \
+     .8,  .0,  .8, \
+    -.8,  .4,  .8, \
+    -.4,  .4,  .8, \
+     .0,  .4,  .8, \
+     .4,  .4,  .8, \
+     .8,  .4,  .8, \
+    -.8,  .8,  .8, \
+    -.4,  .8,  .8, \
+     .0,  .8,  .8, \
+     .4,  .8,  .8, \
+     .8,  .8,  .8 \
+}
+
+#define ADEC_LAYER2_UNGROUP9 \
+{ \
+    -8.0/9, -8.0/9, -8.0/9, \
+    -6.0/9, -8.0/9, -8.0/9, \
+    -4.0/9, -8.0/9, -8.0/9, \
+    -2.0/9, -8.0/9, -8.0/9, \
+      .0  , -8.0/9, -8.0/9, \
+     2.0/9, -8.0/9, -8.0/9, \
+     4.0/9, -8.0/9, -8.0/9, \
+     6.0/9, -8.0/9, -8.0/9, \
+     8.0/9, -8.0/9, -8.0/9, \
+    -8.0/9, -6.0/9, -8.0/9, \
+    -6.0/9, -6.0/9, -8.0/9, \
+    -4.0/9, -6.0/9, -8.0/9, \
+    -2.0/9, -6.0/9, -8.0/9, \
+      .0  , -6.0/9, -8.0/9, \
+     2.0/9, -6.0/9, -8.0/9, \
+     4.0/9, -6.0/9, -8.0/9, \
+     6.0/9, -6.0/9, -8.0/9, \
+     8.0/9, -6.0/9, -8.0/9, \
+    -8.0/9, -4.0/9, -8.0/9, \
+    -6.0/9, -4.0/9, -8.0/9, \
+    -4.0/9, -4.0/9, -8.0/9, \
+    -2.0/9, -4.0/9, -8.0/9, \
+      .0  , -4.0/9, -8.0/9, \
+     2.0/9, -4.0/9, -8.0/9, \
+     4.0/9, -4.0/9, -8.0/9, \
+     6.0/9, -4.0/9, -8.0/9, \
+     8.0/9, -4.0/9, -8.0/9, \
+    -8.0/9, -2.0/9, -8.0/9, \
+    -6.0/9, -2.0/9, -8.0/9, \
+    -4.0/9, -2.0/9, -8.0/9, \
+    -2.0/9, -2.0/9, -8.0/9, \
+      .0  , -2.0/9, -8.0/9, \
+     2.0/9, -2.0/9, -8.0/9, \
+     4.0/9, -2.0/9, -8.0/9, \
+     6.0/9, -2.0/9, -8.0/9, \
+     8.0/9, -2.0/9, -8.0/9, \
+    -8.0/9,   .0  , -8.0/9, \
+    -6.0/9,   .0  , -8.0/9, \
+    -4.0/9,   .0  , -8.0/9, \
+    -2.0/9,   .0  , -8.0/9, \
+      .0  ,   .0  , -8.0/9, \
+     2.0/9,   .0  , -8.0/9, \
+     4.0/9,   .0  , -8.0/9, \
+     6.0/9,   .0  , -8.0/9, \
+     8.0/9,   .0  , -8.0/9, \
+    -8.0/9,  2.0/9, -8.0/9, \
+    -6.0/9,  2.0/9, -8.0/9, \
+    -4.0/9,  2.0/9, -8.0/9, \
+    -2.0/9,  2.0/9, -8.0/9, \
+      .0  ,  2.0/9, -8.0/9, \
+     2.0/9,  2.0/9, -8.0/9, \
+     4.0/9,  2.0/9, -8.0/9, \
+     6.0/9,  2.0/9, -8.0/9, \
+     8.0/9,  2.0/9, -8.0/9, \
+    -8.0/9,  4.0/9, -8.0/9, \
+    -6.0/9,  4.0/9, -8.0/9, \
+    -4.0/9,  4.0/9, -8.0/9, \
+    -2.0/9,  4.0/9, -8.0/9, \
+      .0  ,  4.0/9, -8.0/9, \
+     2.0/9,  4.0/9, -8.0/9, \
+     4.0/9,  4.0/9, -8.0/9, \
+     6.0/9,  4.0/9, -8.0/9, \
+     8.0/9,  4.0/9, -8.0/9, \
+    -8.0/9,  6.0/9, -8.0/9, \
+    -6.0/9,  6.0/9, -8.0/9, \
+    -4.0/9,  6.0/9, -8.0/9, \
+    -2.0/9,  6.0/9, -8.0/9, \
+      .0  ,  6.0/9, -8.0/9, \
+     2.0/9,  6.0/9, -8.0/9, \
+     4.0/9,  6.0/9, -8.0/9, \
+     6.0/9,  6.0/9, -8.0/9, \
+     8.0/9,  6.0/9, -8.0/9, \
+    -8.0/9,  8.0/9, -8.0/9, \
+    -6.0/9,  8.0/9, -8.0/9, \
+    -4.0/9,  8.0/9, -8.0/9, \
+    -2.0/9,  8.0/9, -8.0/9, \
+      .0  ,  8.0/9, -8.0/9, \
+     2.0/9,  8.0/9, -8.0/9, \
+     4.0/9,  8.0/9, -8.0/9, \
+     6.0/9,  8.0/9, -8.0/9, \
+     8.0/9,  8.0/9, -8.0/9, \
+    -8.0/9, -8.0/9, -6.0/9, \
+    -6.0/9, -8.0/9, -6.0/9, \
+    -4.0/9, -8.0/9, -6.0/9, \
+    -2.0/9, -8.0/9, -6.0/9, \
+      .0  , -8.0/9, -6.0/9, \
+     2.0/9, -8.0/9, -6.0/9, \
+     4.0/9, -8.0/9, -6.0/9, \
+     6.0/9, -8.0/9, -6.0/9, \
+     8.0/9, -8.0/9, -6.0/9, \
+    -8.0/9, -6.0/9, -6.0/9, \
+    -6.0/9, -6.0/9, -6.0/9, \
+    -4.0/9, -6.0/9, -6.0/9, \
+    -2.0/9, -6.0/9, -6.0/9, \
+      .0  , -6.0/9, -6.0/9, \
+     2.0/9, -6.0/9, -6.0/9, \
+     4.0/9, -6.0/9, -6.0/9, \
+     6.0/9, -6.0/9, -6.0/9, \
+     8.0/9, -6.0/9, -6.0/9, \
+    -8.0/9, -4.0/9, -6.0/9, \
+    -6.0/9, -4.0/9, -6.0/9, \
+    -4.0/9, -4.0/9, -6.0/9, \
+    -2.0/9, -4.0/9, -6.0/9, \
+      .0  , -4.0/9, -6.0/9, \
+     2.0/9, -4.0/9, -6.0/9, \
+     4.0/9, -4.0/9, -6.0/9, \
+     6.0/9, -4.0/9, -6.0/9, \
+     8.0/9, -4.0/9, -6.0/9, \
+    -8.0/9, -2.0/9, -6.0/9, \
+    -6.0/9, -2.0/9, -6.0/9, \
+    -4.0/9, -2.0/9, -6.0/9, \
+    -2.0/9, -2.0/9, -6.0/9, \
+      .0  , -2.0/9, -6.0/9, \
+     2.0/9, -2.0/9, -6.0/9, \
+     4.0/9, -2.0/9, -6.0/9, \
+     6.0/9, -2.0/9, -6.0/9, \
+     8.0/9, -2.0/9, -6.0/9, \
+    -8.0/9,   .0  , -6.0/9, \
+    -6.0/9,   .0  , -6.0/9, \
+    -4.0/9,   .0  , -6.0/9, \
+    -2.0/9,   .0  , -6.0/9, \
+      .0  ,   .0  , -6.0/9, \
+     2.0/9,   .0  , -6.0/9, \
+     4.0/9,   .0  , -6.0/9, \
+     6.0/9,   .0  , -6.0/9, \
+     8.0/9,   .0  , -6.0/9, \
+    -8.0/9,  2.0/9, -6.0/9, \
+    -6.0/9,  2.0/9, -6.0/9, \
+    -4.0/9,  2.0/9, -6.0/9, \
+    -2.0/9,  2.0/9, -6.0/9, \
+      .0  ,  2.0/9, -6.0/9, \
+     2.0/9,  2.0/9, -6.0/9, \
+     4.0/9,  2.0/9, -6.0/9, \
+     6.0/9,  2.0/9, -6.0/9, \
+     8.0/9,  2.0/9, -6.0/9, \
+    -8.0/9,  4.0/9, -6.0/9, \
+    -6.0/9,  4.0/9, -6.0/9, \
+    -4.0/9,  4.0/9, -6.0/9, \
+    -2.0/9,  4.0/9, -6.0/9, \
+      .0  ,  4.0/9, -6.0/9, \
+     2.0/9,  4.0/9, -6.0/9, \
+     4.0/9,  4.0/9, -6.0/9, \
+     6.0/9,  4.0/9, -6.0/9, \
+     8.0/9,  4.0/9, -6.0/9, \
+    -8.0/9,  6.0/9, -6.0/9, \
+    -6.0/9,  6.0/9, -6.0/9, \
+    -4.0/9,  6.0/9, -6.0/9, \
+    -2.0/9,  6.0/9, -6.0/9, \
+      .0  ,  6.0/9, -6.0/9, \
+     2.0/9,  6.0/9, -6.0/9, \
+     4.0/9,  6.0/9, -6.0/9, \
+     6.0/9,  6.0/9, -6.0/9, \
+     8.0/9,  6.0/9, -6.0/9, \
+    -8.0/9,  8.0/9, -6.0/9, \
+    -6.0/9,  8.0/9, -6.0/9, \
+    -4.0/9,  8.0/9, -6.0/9, \
+    -2.0/9,  8.0/9, -6.0/9, \
+      .0  ,  8.0/9, -6.0/9, \
+     2.0/9,  8.0/9, -6.0/9, \
+     4.0/9,  8.0/9, -6.0/9, \
+     6.0/9,  8.0/9, -6.0/9, \
+     8.0/9,  8.0/9, -6.0/9, \
+    -8.0/9, -8.0/9, -4.0/9, \
+    -6.0/9, -8.0/9, -4.0/9, \
+    -4.0/9, -8.0/9, -4.0/9, \
+    -2.0/9, -8.0/9, -4.0/9, \
+      .0  , -8.0/9, -4.0/9, \
+     2.0/9, -8.0/9, -4.0/9, \
+     4.0/9, -8.0/9, -4.0/9, \
+     6.0/9, -8.0/9, -4.0/9, \
+     8.0/9, -8.0/9, -4.0/9, \
+    -8.0/9, -6.0/9, -4.0/9, \
+    -6.0/9, -6.0/9, -4.0/9, \
+    -4.0/9, -6.0/9, -4.0/9, \
+    -2.0/9, -6.0/9, -4.0/9, \
+      .0  , -6.0/9, -4.0/9, \
+     2.0/9, -6.0/9, -4.0/9, \
+     4.0/9, -6.0/9, -4.0/9, \
+     6.0/9, -6.0/9, -4.0/9, \
+     8.0/9, -6.0/9, -4.0/9, \
+    -8.0/9, -4.0/9, -4.0/9, \
+    -6.0/9, -4.0/9, -4.0/9, \
+    -4.0/9, -4.0/9, -4.0/9, \
+    -2.0/9, -4.0/9, -4.0/9, \
+      .0  , -4.0/9, -4.0/9, \
+     2.0/9, -4.0/9, -4.0/9, \
+     4.0/9, -4.0/9, -4.0/9, \
+     6.0/9, -4.0/9, -4.0/9, \
+     8.0/9, -4.0/9, -4.0/9, \
+    -8.0/9, -2.0/9, -4.0/9, \
+    -6.0/9, -2.0/9, -4.0/9, \
+    -4.0/9, -2.0/9, -4.0/9, \
+    -2.0/9, -2.0/9, -4.0/9, \
+      .0  , -2.0/9, -4.0/9, \
+     2.0/9, -2.0/9, -4.0/9, \
+     4.0/9, -2.0/9, -4.0/9, \
+     6.0/9, -2.0/9, -4.0/9, \
+     8.0/9, -2.0/9, -4.0/9, \
+    -8.0/9,   .0  , -4.0/9, \
+    -6.0/9,   .0  , -4.0/9, \
+    -4.0/9,   .0  , -4.0/9, \
+    -2.0/9,   .0  , -4.0/9, \
+      .0  ,   .0  , -4.0/9, \
+     2.0/9,   .0  , -4.0/9, \
+     4.0/9,   .0  , -4.0/9, \
+     6.0/9,   .0  , -4.0/9, \
+     8.0/9,   .0  , -4.0/9, \
+    -8.0/9,  2.0/9, -4.0/9, \
+    -6.0/9,  2.0/9, -4.0/9, \
+    -4.0/9,  2.0/9, -4.0/9, \
+    -2.0/9,  2.0/9, -4.0/9, \
+      .0  ,  2.0/9, -4.0/9, \
+     2.0/9,  2.0/9, -4.0/9, \
+     4.0/9,  2.0/9, -4.0/9, \
+     6.0/9,  2.0/9, -4.0/9, \
+     8.0/9,  2.0/9, -4.0/9, \
+    -8.0/9,  4.0/9, -4.0/9, \
+    -6.0/9,  4.0/9, -4.0/9, \
+    -4.0/9,  4.0/9, -4.0/9, \
+    -2.0/9,  4.0/9, -4.0/9, \
+      .0  ,  4.0/9, -4.0/9, \
+     2.0/9,  4.0/9, -4.0/9, \
+     4.0/9,  4.0/9, -4.0/9, \
+     6.0/9,  4.0/9, -4.0/9, \
+     8.0/9,  4.0/9, -4.0/9, \
+    -8.0/9,  6.0/9, -4.0/9, \
+    -6.0/9,  6.0/9, -4.0/9, \
+    -4.0/9,  6.0/9, -4.0/9, \
+    -2.0/9,  6.0/9, -4.0/9, \
+      .0  ,  6.0/9, -4.0/9, \
+     2.0/9,  6.0/9, -4.0/9, \
+     4.0/9,  6.0/9, -4.0/9, \
+     6.0/9,  6.0/9, -4.0/9, \
+     8.0/9,  6.0/9, -4.0/9, \
+    -8.0/9,  8.0/9, -4.0/9, \
+    -6.0/9,  8.0/9, -4.0/9, \
+    -4.0/9,  8.0/9, -4.0/9, \
+    -2.0/9,  8.0/9, -4.0/9, \
+      .0  ,  8.0/9, -4.0/9, \
+     2.0/9,  8.0/9, -4.0/9, \
+     4.0/9,  8.0/9, -4.0/9, \
+     6.0/9,  8.0/9, -4.0/9, \
+     8.0/9,  8.0/9, -4.0/9, \
+    -8.0/9, -8.0/9, -2.0/9, \
+    -6.0/9, -8.0/9, -2.0/9, \
+    -4.0/9, -8.0/9, -2.0/9, \
+    -2.0/9, -8.0/9, -2.0/9, \
+      .0  , -8.0/9, -2.0/9, \
+     2.0/9, -8.0/9, -2.0/9, \
+     4.0/9, -8.0/9, -2.0/9, \
+     6.0/9, -8.0/9, -2.0/9, \
+     8.0/9, -8.0/9, -2.0/9, \
+    -8.0/9, -6.0/9, -2.0/9, \
+    -6.0/9, -6.0/9, -2.0/9, \
+    -4.0/9, -6.0/9, -2.0/9, \
+    -2.0/9, -6.0/9, -2.0/9, \
+      .0  , -6.0/9, -2.0/9, \
+     2.0/9, -6.0/9, -2.0/9, \
+     4.0/9, -6.0/9, -2.0/9, \
+     6.0/9, -6.0/9, -2.0/9, \
+     8.0/9, -6.0/9, -2.0/9, \
+    -8.0/9, -4.0/9, -2.0/9, \
+    -6.0/9, -4.0/9, -2.0/9, \
+    -4.0/9, -4.0/9, -2.0/9, \
+    -2.0/9, -4.0/9, -2.0/9, \
+      .0  , -4.0/9, -2.0/9, \
+     2.0/9, -4.0/9, -2.0/9, \
+     4.0/9, -4.0/9, -2.0/9, \
+     6.0/9, -4.0/9, -2.0/9, \
+     8.0/9, -4.0/9, -2.0/9, \
+    -8.0/9, -2.0/9, -2.0/9, \
+    -6.0/9, -2.0/9, -2.0/9, \
+    -4.0/9, -2.0/9, -2.0/9, \
+    -2.0/9, -2.0/9, -2.0/9, \
+      .0  , -2.0/9, -2.0/9, \
+     2.0/9, -2.0/9, -2.0/9, \
+     4.0/9, -2.0/9, -2.0/9, \
+     6.0/9, -2.0/9, -2.0/9, \
+     8.0/9, -2.0/9, -2.0/9, \
+    -8.0/9,   .0  , -2.0/9, \
+    -6.0/9,   .0  , -2.0/9, \
+    -4.0/9,   .0  , -2.0/9, \
+    -2.0/9,   .0  , -2.0/9, \
+      .0  ,   .0  , -2.0/9, \
+     2.0/9,   .0  , -2.0/9, \
+     4.0/9,   .0  , -2.0/9, \
+     6.0/9,   .0  , -2.0/9, \
+     8.0/9,   .0  , -2.0/9, \
+    -8.0/9,  2.0/9, -2.0/9, \
+    -6.0/9,  2.0/9, -2.0/9, \
+    -4.0/9,  2.0/9, -2.0/9, \
+    -2.0/9,  2.0/9, -2.0/9, \
+      .0  ,  2.0/9, -2.0/9, \
+     2.0/9,  2.0/9, -2.0/9, \
+     4.0/9,  2.0/9, -2.0/9, \
+     6.0/9,  2.0/9, -2.0/9, \
+     8.0/9,  2.0/9, -2.0/9, \
+    -8.0/9,  4.0/9, -2.0/9, \
+    -6.0/9,  4.0/9, -2.0/9, \
+    -4.0/9,  4.0/9, -2.0/9, \
+    -2.0/9,  4.0/9, -2.0/9, \
+      .0  ,  4.0/9, -2.0/9, \
+     2.0/9,  4.0/9, -2.0/9, \
+     4.0/9,  4.0/9, -2.0/9, \
+     6.0/9,  4.0/9, -2.0/9, \
+     8.0/9,  4.0/9, -2.0/9, \
+    -8.0/9,  6.0/9, -2.0/9, \
+    -6.0/9,  6.0/9, -2.0/9, \
+    -4.0/9,  6.0/9, -2.0/9, \
+    -2.0/9,  6.0/9, -2.0/9, \
+      .0  ,  6.0/9, -2.0/9, \
+     2.0/9,  6.0/9, -2.0/9, \
+     4.0/9,  6.0/9, -2.0/9, \
+     6.0/9,  6.0/9, -2.0/9, \
+     8.0/9,  6.0/9, -2.0/9, \
+    -8.0/9,  8.0/9, -2.0/9, \
+    -6.0/9,  8.0/9, -2.0/9, \
+    -4.0/9,  8.0/9, -2.0/9, \
+    -2.0/9,  8.0/9, -2.0/9, \
+      .0  ,  8.0/9, -2.0/9, \
+     2.0/9,  8.0/9, -2.0/9, \
+     4.0/9,  8.0/9, -2.0/9, \
+     6.0/9,  8.0/9, -2.0/9, \
+     8.0/9,  8.0/9, -2.0/9, \
+    -8.0/9, -8.0/9,   .0  , \
+    -6.0/9, -8.0/9,   .0  , \
+    -4.0/9, -8.0/9,   .0  , \
+    -2.0/9, -8.0/9,   .0  , \
+      .0  , -8.0/9,   .0  , \
+     2.0/9, -8.0/9,   .0  , \
+     4.0/9, -8.0/9,   .0  , \
+     6.0/9, -8.0/9,   .0  , \
+     8.0/9, -8.0/9,   .0  , \
+    -8.0/9, -6.0/9,   .0  , \
+    -6.0/9, -6.0/9,   .0  , \
+    -4.0/9, -6.0/9,   .0  , \
+    -2.0/9, -6.0/9,   .0  , \
+      .0  , -6.0/9,   .0  , \
+     2.0/9, -6.0/9,   .0  , \
+     4.0/9, -6.0/9,   .0  , \
+     6.0/9, -6.0/9,   .0  , \
+     8.0/9, -6.0/9,   .0  , \
+    -8.0/9, -4.0/9,   .0  , \
+    -6.0/9, -4.0/9,   .0  , \
+    -4.0/9, -4.0/9,   .0  , \
+    -2.0/9, -4.0/9,   .0  , \
+      .0  , -4.0/9,   .0  , \
+     2.0/9, -4.0/9,   .0  , \
+     4.0/9, -4.0/9,   .0  , \
+     6.0/9, -4.0/9,   .0  , \
+     8.0/9, -4.0/9,   .0  , \
+    -8.0/9, -2.0/9,   .0  , \
+    -6.0/9, -2.0/9,   .0  , \
+    -4.0/9, -2.0/9,   .0  , \
+    -2.0/9, -2.0/9,   .0  , \
+      .0  , -2.0/9,   .0  , \
+     2.0/9, -2.0/9,   .0  , \
+     4.0/9, -2.0/9,   .0  , \
+     6.0/9, -2.0/9,   .0  , \
+     8.0/9, -2.0/9,   .0  , \
+    -8.0/9,   .0  ,   .0  , \
+    -6.0/9,   .0  ,   .0  , \
+    -4.0/9,   .0  ,   .0  , \
+    -2.0/9,   .0  ,   .0  , \
+      .0  ,   .0  ,   .0  , \
+     2.0/9,   .0  ,   .0  , \
+     4.0/9,   .0  ,   .0  , \
+     6.0/9,   .0  ,   .0  , \
+     8.0/9,   .0  ,   .0  , \
+    -8.0/9,  2.0/9,   .0  , \
+    -6.0/9,  2.0/9,   .0  , \
+    -4.0/9,  2.0/9,   .0  , \
+    -2.0/9,  2.0/9,   .0  , \
+      .0  ,  2.0/9,   .0  , \
+     2.0/9,  2.0/9,   .0  , \
+     4.0/9,  2.0/9,   .0  , \
+     6.0/9,  2.0/9,   .0  , \
+     8.0/9,  2.0/9,   .0  , \
+    -8.0/9,  4.0/9,   .0  , \
+    -6.0/9,  4.0/9,   .0  , \
+    -4.0/9,  4.0/9,   .0  , \
+    -2.0/9,  4.0/9,   .0  , \
+      .0  ,  4.0/9,   .0  , \
+     2.0/9,  4.0/9,   .0  , \
+     4.0/9,  4.0/9,   .0  , \
+     6.0/9,  4.0/9,   .0  , \
+     8.0/9,  4.0/9,   .0  , \
+    -8.0/9,  6.0/9,   .0  , \
+    -6.0/9,  6.0/9,   .0  , \
+    -4.0/9,  6.0/9,   .0  , \
+    -2.0/9,  6.0/9,   .0  , \
+      .0  ,  6.0/9,   .0  , \
+     2.0/9,  6.0/9,   .0  , \
+     4.0/9,  6.0/9,   .0  , \
+     6.0/9,  6.0/9,   .0  , \
+     8.0/9,  6.0/9,   .0  , \
+    -8.0/9,  8.0/9,   .0  , \
+    -6.0/9,  8.0/9,   .0  , \
+    -4.0/9,  8.0/9,   .0  , \
+    -2.0/9,  8.0/9,   .0  , \
+      .0  ,  8.0/9,   .0  , \
+     2.0/9,  8.0/9,   .0  , \
+     4.0/9,  8.0/9,   .0  , \
+     6.0/9,  8.0/9,   .0  , \
+     8.0/9,  8.0/9,   .0  , \
+    -8.0/9, -8.0/9,  2.0/9, \
+    -6.0/9, -8.0/9,  2.0/9, \
+    -4.0/9, -8.0/9,  2.0/9, \
+    -2.0/9, -8.0/9,  2.0/9, \
+      .0  , -8.0/9,  2.0/9, \
+     2.0/9, -8.0/9,  2.0/9, \
+     4.0/9, -8.0/9,  2.0/9, \
+     6.0/9, -8.0/9,  2.0/9, \
+     8.0/9, -8.0/9,  2.0/9, \
+    -8.0/9, -6.0/9,  2.0/9, \
+    -6.0/9, -6.0/9,  2.0/9, \
+    -4.0/9, -6.0/9,  2.0/9, \
+    -2.0/9, -6.0/9,  2.0/9, \
+      .0  , -6.0/9,  2.0/9, \
+     2.0/9, -6.0/9,  2.0/9, \
+     4.0/9, -6.0/9,  2.0/9, \
+     6.0/9, -6.0/9,  2.0/9, \
+     8.0/9, -6.0/9,  2.0/9, \
+    -8.0/9, -4.0/9,  2.0/9, \
+    -6.0/9, -4.0/9,  2.0/9, \
+    -4.0/9, -4.0/9,  2.0/9, \
+    -2.0/9, -4.0/9,  2.0/9, \
+      .0  , -4.0/9,  2.0/9, \
+     2.0/9, -4.0/9,  2.0/9, \
+     4.0/9, -4.0/9,  2.0/9, \
+     6.0/9, -4.0/9,  2.0/9, \
+     8.0/9, -4.0/9,  2.0/9, \
+    -8.0/9, -2.0/9,  2.0/9, \
+    -6.0/9, -2.0/9,  2.0/9, \
+    -4.0/9, -2.0/9,  2.0/9, \
+    -2.0/9, -2.0/9,  2.0/9, \
+      .0  , -2.0/9,  2.0/9, \
+     2.0/9, -2.0/9,  2.0/9, \
+     4.0/9, -2.0/9,  2.0/9, \
+     6.0/9, -2.0/9,  2.0/9, \
+     8.0/9, -2.0/9,  2.0/9, \
+    -8.0/9,   .0  ,  2.0/9, \
+    -6.0/9,   .0  ,  2.0/9, \
+    -4.0/9,   .0  ,  2.0/9, \
+    -2.0/9,   .0  ,  2.0/9, \
+      .0  ,   .0  ,  2.0/9, \
+     2.0/9,   .0  ,  2.0/9, \
+     4.0/9,   .0  ,  2.0/9, \
+     6.0/9,   .0  ,  2.0/9, \
+     8.0/9,   .0  ,  2.0/9, \
+    -8.0/9,  2.0/9,  2.0/9, \
+    -6.0/9,  2.0/9,  2.0/9, \
+    -4.0/9,  2.0/9,  2.0/9, \
+    -2.0/9,  2.0/9,  2.0/9, \
+      .0  ,  2.0/9,  2.0/9, \
+     2.0/9,  2.0/9,  2.0/9, \
+     4.0/9,  2.0/9,  2.0/9, \
+     6.0/9,  2.0/9,  2.0/9, \
+     8.0/9,  2.0/9,  2.0/9, \
+    -8.0/9,  4.0/9,  2.0/9, \
+    -6.0/9,  4.0/9,  2.0/9, \
+    -4.0/9,  4.0/9,  2.0/9, \
+    -2.0/9,  4.0/9,  2.0/9, \
+      .0  ,  4.0/9,  2.0/9, \
+     2.0/9,  4.0/9,  2.0/9, \
+     4.0/9,  4.0/9,  2.0/9, \
+     6.0/9,  4.0/9,  2.0/9, \
+     8.0/9,  4.0/9,  2.0/9, \
+    -8.0/9,  6.0/9,  2.0/9, \
+    -6.0/9,  6.0/9,  2.0/9, \
+    -4.0/9,  6.0/9,  2.0/9, \
+    -2.0/9,  6.0/9,  2.0/9, \
+      .0  ,  6.0/9,  2.0/9, \
+     2.0/9,  6.0/9,  2.0/9, \
+     4.0/9,  6.0/9,  2.0/9, \
+     6.0/9,  6.0/9,  2.0/9, \
+     8.0/9,  6.0/9,  2.0/9, \
+    -8.0/9,  8.0/9,  2.0/9, \
+    -6.0/9,  8.0/9,  2.0/9, \
+    -4.0/9,  8.0/9,  2.0/9, \
+    -2.0/9,  8.0/9,  2.0/9, \
+      .0  ,  8.0/9,  2.0/9, \
+     2.0/9,  8.0/9,  2.0/9, \
+     4.0/9,  8.0/9,  2.0/9, \
+     6.0/9,  8.0/9,  2.0/9, \
+     8.0/9,  8.0/9,  2.0/9, \
+    -8.0/9, -8.0/9,  4.0/9, \
+    -6.0/9, -8.0/9,  4.0/9, \
+    -4.0/9, -8.0/9,  4.0/9, \
+    -2.0/9, -8.0/9,  4.0/9, \
+      .0  , -8.0/9,  4.0/9, \
+     2.0/9, -8.0/9,  4.0/9, \
+     4.0/9, -8.0/9,  4.0/9, \
+     6.0/9, -8.0/9,  4.0/9, \
+     8.0/9, -8.0/9,  4.0/9, \
+    -8.0/9, -6.0/9,  4.0/9, \
+    -6.0/9, -6.0/9,  4.0/9, \
+    -4.0/9, -6.0/9,  4.0/9, \
+    -2.0/9, -6.0/9,  4.0/9, \
+      .0  , -6.0/9,  4.0/9, \
+     2.0/9, -6.0/9,  4.0/9, \
+     4.0/9, -6.0/9,  4.0/9, \
+     6.0/9, -6.0/9,  4.0/9, \
+     8.0/9, -6.0/9,  4.0/9, \
+    -8.0/9, -4.0/9,  4.0/9, \
+    -6.0/9, -4.0/9,  4.0/9, \
+    -4.0/9, -4.0/9,  4.0/9, \
+    -2.0/9, -4.0/9,  4.0/9, \
+      .0  , -4.0/9,  4.0/9, \
+     2.0/9, -4.0/9,  4.0/9, \
+     4.0/9, -4.0/9,  4.0/9, \
+     6.0/9, -4.0/9,  4.0/9, \
+     8.0/9, -4.0/9,  4.0/9, \
+    -8.0/9, -2.0/9,  4.0/9, \
+    -6.0/9, -2.0/9,  4.0/9, \
+    -4.0/9, -2.0/9,  4.0/9, \
+    -2.0/9, -2.0/9,  4.0/9, \
+      .0  , -2.0/9,  4.0/9, \
+     2.0/9, -2.0/9,  4.0/9, \
+     4.0/9, -2.0/9,  4.0/9, \
+     6.0/9, -2.0/9,  4.0/9, \
+     8.0/9, -2.0/9,  4.0/9, \
+    -8.0/9,   .0  ,  4.0/9, \
+    -6.0/9,   .0  ,  4.0/9, \
+    -4.0/9,   .0  ,  4.0/9, \
+    -2.0/9,   .0  ,  4.0/9, \
+      .0  ,   .0  ,  4.0/9, \
+     2.0/9,   .0  ,  4.0/9, \
+     4.0/9,   .0  ,  4.0/9, \
+     6.0/9,   .0  ,  4.0/9, \
+     8.0/9,   .0  ,  4.0/9, \
+    -8.0/9,  2.0/9,  4.0/9, \
+    -6.0/9,  2.0/9,  4.0/9, \
+    -4.0/9,  2.0/9,  4.0/9, \
+    -2.0/9,  2.0/9,  4.0/9, \
+      .0  ,  2.0/9,  4.0/9, \
+     2.0/9,  2.0/9,  4.0/9, \
+     4.0/9,  2.0/9,  4.0/9, \
+     6.0/9,  2.0/9,  4.0/9, \
+     8.0/9,  2.0/9,  4.0/9, \
+    -8.0/9,  4.0/9,  4.0/9, \
+    -6.0/9,  4.0/9,  4.0/9, \
+    -4.0/9,  4.0/9,  4.0/9, \
+    -2.0/9,  4.0/9,  4.0/9, \
+      .0  ,  4.0/9,  4.0/9, \
+     2.0/9,  4.0/9,  4.0/9, \
+     4.0/9,  4.0/9,  4.0/9, \
+     6.0/9,  4.0/9,  4.0/9, \
+     8.0/9,  4.0/9,  4.0/9, \
+    -8.0/9,  6.0/9,  4.0/9, \
+    -6.0/9,  6.0/9,  4.0/9, \
+    -4.0/9,  6.0/9,  4.0/9, \
+    -2.0/9,  6.0/9,  4.0/9, \
+      .0  ,  6.0/9,  4.0/9, \
+     2.0/9,  6.0/9,  4.0/9, \
+     4.0/9,  6.0/9,  4.0/9, \
+     6.0/9,  6.0/9,  4.0/9, \
+     8.0/9,  6.0/9,  4.0/9, \
+    -8.0/9,  8.0/9,  4.0/9, \
+    -6.0/9,  8.0/9,  4.0/9, \
+    -4.0/9,  8.0/9,  4.0/9, \
+    -2.0/9,  8.0/9,  4.0/9, \
+      .0  ,  8.0/9,  4.0/9, \
+     2.0/9,  8.0/9,  4.0/9, \
+     4.0/9,  8.0/9,  4.0/9, \
+     6.0/9,  8.0/9,  4.0/9, \
+     8.0/9,  8.0/9,  4.0/9, \
+    -8.0/9, -8.0/9,  6.0/9, \
+    -6.0/9, -8.0/9,  6.0/9, \
+    -4.0/9, -8.0/9,  6.0/9, \
+    -2.0/9, -8.0/9,  6.0/9, \
+      .0  , -8.0/9,  6.0/9, \
+     2.0/9, -8.0/9,  6.0/9, \
+     4.0/9, -8.0/9,  6.0/9, \
+     6.0/9, -8.0/9,  6.0/9, \
+     8.0/9, -8.0/9,  6.0/9, \
+    -8.0/9, -6.0/9,  6.0/9, \
+    -6.0/9, -6.0/9,  6.0/9, \
+    -4.0/9, -6.0/9,  6.0/9, \
+    -2.0/9, -6.0/9,  6.0/9, \
+      .0  , -6.0/9,  6.0/9, \
+     2.0/9, -6.0/9,  6.0/9, \
+     4.0/9, -6.0/9,  6.0/9, \
+     6.0/9, -6.0/9,  6.0/9, \
+     8.0/9, -6.0/9,  6.0/9, \
+    -8.0/9, -4.0/9,  6.0/9, \
+    -6.0/9, -4.0/9,  6.0/9, \
+    -4.0/9, -4.0/9,  6.0/9, \
+    -2.0/9, -4.0/9,  6.0/9, \
+      .0  , -4.0/9,  6.0/9, \
+     2.0/9, -4.0/9,  6.0/9, \
+     4.0/9, -4.0/9,  6.0/9, \
+     6.0/9, -4.0/9,  6.0/9, \
+     8.0/9, -4.0/9,  6.0/9, \
+    -8.0/9, -2.0/9,  6.0/9, \
+    -6.0/9, -2.0/9,  6.0/9, \
+    -4.0/9, -2.0/9,  6.0/9, \
+    -2.0/9, -2.0/9,  6.0/9, \
+      .0  , -2.0/9,  6.0/9, \
+     2.0/9, -2.0/9,  6.0/9, \
+     4.0/9, -2.0/9,  6.0/9, \
+     6.0/9, -2.0/9,  6.0/9, \
+     8.0/9, -2.0/9,  6.0/9, \
+    -8.0/9,   .0  ,  6.0/9, \
+    -6.0/9,   .0  ,  6.0/9, \
+    -4.0/9,   .0  ,  6.0/9, \
+    -2.0/9,   .0  ,  6.0/9, \
+      .0  ,   .0  ,  6.0/9, \
+     2.0/9,   .0  ,  6.0/9, \
+     4.0/9,   .0  ,  6.0/9, \
+     6.0/9,   .0  ,  6.0/9, \
+     8.0/9,   .0  ,  6.0/9, \
+    -8.0/9,  2.0/9,  6.0/9, \
+    -6.0/9,  2.0/9,  6.0/9, \
+    -4.0/9,  2.0/9,  6.0/9, \
+    -2.0/9,  2.0/9,  6.0/9, \
+      .0  ,  2.0/9,  6.0/9, \
+     2.0/9,  2.0/9,  6.0/9, \
+     4.0/9,  2.0/9,  6.0/9, \
+     6.0/9,  2.0/9,  6.0/9, \
+     8.0/9,  2.0/9,  6.0/9, \
+    -8.0/9,  4.0/9,  6.0/9, \
+    -6.0/9,  4.0/9,  6.0/9, \
+    -4.0/9,  4.0/9,  6.0/9, \
+    -2.0/9,  4.0/9,  6.0/9, \
+      .0  ,  4.0/9,  6.0/9, \
+     2.0/9,  4.0/9,  6.0/9, \
+     4.0/9,  4.0/9,  6.0/9, \
+     6.0/9,  4.0/9,  6.0/9, \
+     8.0/9,  4.0/9,  6.0/9, \
+    -8.0/9,  6.0/9,  6.0/9, \
+    -6.0/9,  6.0/9,  6.0/9, \
+    -4.0/9,  6.0/9,  6.0/9, \
+    -2.0/9,  6.0/9,  6.0/9, \
+      .0  ,  6.0/9,  6.0/9, \
+     2.0/9,  6.0/9,  6.0/9, \
+     4.0/9,  6.0/9,  6.0/9, \
+     6.0/9,  6.0/9,  6.0/9, \
+     8.0/9,  6.0/9,  6.0/9, \
+    -8.0/9,  8.0/9,  6.0/9, \
+    -6.0/9,  8.0/9,  6.0/9, \
+    -4.0/9,  8.0/9,  6.0/9, \
+    -2.0/9,  8.0/9,  6.0/9, \
+      .0  ,  8.0/9,  6.0/9, \
+     2.0/9,  8.0/9,  6.0/9, \
+     4.0/9,  8.0/9,  6.0/9, \
+     6.0/9,  8.0/9,  6.0/9, \
+     8.0/9,  8.0/9,  6.0/9, \
+    -8.0/9, -8.0/9,  8.0/9, \
+    -6.0/9, -8.0/9,  8.0/9, \
+    -4.0/9, -8.0/9,  8.0/9, \
+    -2.0/9, -8.0/9,  8.0/9, \
+      .0  , -8.0/9,  8.0/9, \
+     2.0/9, -8.0/9,  8.0/9, \
+     4.0/9, -8.0/9,  8.0/9, \
+     6.0/9, -8.0/9,  8.0/9, \
+     8.0/9, -8.0/9,  8.0/9, \
+    -8.0/9, -6.0/9,  8.0/9, \
+    -6.0/9, -6.0/9,  8.0/9, \
+    -4.0/9, -6.0/9,  8.0/9, \
+    -2.0/9, -6.0/9,  8.0/9, \
+      .0  , -6.0/9,  8.0/9, \
+     2.0/9, -6.0/9,  8.0/9, \
+     4.0/9, -6.0/9,  8.0/9, \
+     6.0/9, -6.0/9,  8.0/9, \
+     8.0/9, -6.0/9,  8.0/9, \
+    -8.0/9, -4.0/9,  8.0/9, \
+    -6.0/9, -4.0/9,  8.0/9, \
+    -4.0/9, -4.0/9,  8.0/9, \
+    -2.0/9, -4.0/9,  8.0/9, \
+      .0  , -4.0/9,  8.0/9, \
+     2.0/9, -4.0/9,  8.0/9, \
+     4.0/9, -4.0/9,  8.0/9, \
+     6.0/9, -4.0/9,  8.0/9, \
+     8.0/9, -4.0/9,  8.0/9, \
+    -8.0/9, -2.0/9,  8.0/9, \
+    -6.0/9, -2.0/9,  8.0/9, \
+    -4.0/9, -2.0/9,  8.0/9, \
+    -2.0/9, -2.0/9,  8.0/9, \
+      .0  , -2.0/9,  8.0/9, \
+     2.0/9, -2.0/9,  8.0/9, \
+     4.0/9, -2.0/9,  8.0/9, \
+     6.0/9, -2.0/9,  8.0/9, \
+     8.0/9, -2.0/9,  8.0/9, \
+    -8.0/9,   .0  ,  8.0/9, \
+    -6.0/9,   .0  ,  8.0/9, \
+    -4.0/9,   .0  ,  8.0/9, \
+    -2.0/9,   .0  ,  8.0/9, \
+      .0  ,   .0  ,  8.0/9, \
+     2.0/9,   .0  ,  8.0/9, \
+     4.0/9,   .0  ,  8.0/9, \
+     6.0/9,   .0  ,  8.0/9, \
+     8.0/9,   .0  ,  8.0/9, \
+    -8.0/9,  2.0/9,  8.0/9, \
+    -6.0/9,  2.0/9,  8.0/9, \
+    -4.0/9,  2.0/9,  8.0/9, \
+    -2.0/9,  2.0/9,  8.0/9, \
+      .0  ,  2.0/9,  8.0/9, \
+     2.0/9,  2.0/9,  8.0/9, \
+     4.0/9,  2.0/9,  8.0/9, \
+     6.0/9,  2.0/9,  8.0/9, \
+     8.0/9,  2.0/9,  8.0/9, \
+    -8.0/9,  4.0/9,  8.0/9, \
+    -6.0/9,  4.0/9,  8.0/9, \
+    -4.0/9,  4.0/9,  8.0/9, \
+    -2.0/9,  4.0/9,  8.0/9, \
+      .0  ,  4.0/9,  8.0/9, \
+     2.0/9,  4.0/9,  8.0/9, \
+     4.0/9,  4.0/9,  8.0/9, \
+     6.0/9,  4.0/9,  8.0/9, \
+     8.0/9,  4.0/9,  8.0/9, \
+    -8.0/9,  6.0/9,  8.0/9, \
+    -6.0/9,  6.0/9,  8.0/9, \
+    -4.0/9,  6.0/9,  8.0/9, \
+    -2.0/9,  6.0/9,  8.0/9, \
+      .0  ,  6.0/9,  8.0/9, \
+     2.0/9,  6.0/9,  8.0/9, \
+     4.0/9,  6.0/9,  8.0/9, \
+     6.0/9,  6.0/9,  8.0/9, \
+     8.0/9,  6.0/9,  8.0/9, \
+    -8.0/9,  8.0/9,  8.0/9, \
+    -6.0/9,  8.0/9,  8.0/9, \
+    -4.0/9,  8.0/9,  8.0/9, \
+    -2.0/9,  8.0/9,  8.0/9, \
+      .0  ,  8.0/9,  8.0/9, \
+     2.0/9,  8.0/9,  8.0/9, \
+     4.0/9,  8.0/9,  8.0/9, \
+     6.0/9,  8.0/9,  8.0/9, \
+     8.0/9,  8.0/9,  8.0/9 \
+}
+
+/*
+ * Requantization tables : see ISO/IEC 11172-3 Annex B.2 and B.4
+ *
+ * = We store the requantization information in the following structures :
+ *     typedef struct requantization_s
+ *     {
+ *         byte_t               i_bits_per_codeword;
+ *         const float *        pf_ungroup;
+ *         float                f_slope;
+ *         float                f_offset;
+ *     } requantization_t;
+ *
+ * = Theses values depend on the bitrate per channel, on the subband number
+ *   and on the allocation[ch][sb] (see ISO/IEC 11172-3 2.4.2.6 and Annex B.2).
+ *
+ * = But, in order to avoid data redundancy, we use the following properties :
+ *
+ *   - When bitrate_per_channel == 32 or 48 kbits/s (ie when
+ *     bitrate_per_channel_index == 1 or 2), the requantization values depend
+ *     only on allocation[ch][sb] (see ISO/IEC 11172-3 Annex B.2c and B.2d).
+ *     That's why ADEC_LAYER2_REQUANTIZATION_CD = f(allocation).
+ *
+ *   - In the other cases (see ISO/IEC 11172-3 Annex B.2a and B.2b), we can
+ *     divide the tables in 4 subtables :
+ *       + ADEC_LAYER2_REQUANTIZATION_AB1 for sb in [0..2]
+ *       + ADEC_LAYER2_REQUANTIZATION_AB2 for sb in [3..10]
+ *       + ADEC_LAYER2_REQUANTIZATION_AB3 for sb in [11..22]
+ *       + ADEC_LAYER2_REQUANTIZATION_AB4 for sb in [23..29]
+ *     That's why ADEC_LAYER2_REQUANTIZATION_AB = f(sb, allocation)
+ *
+ * = When these tables are used, pf_ungroup3, pf_ungroup5, pf_ungroup9,
+ *   requantization_ab1, requantization_ab2, requantization_ab3 and
+ *   requantization_ab4 must already have been defined.
+ */
+#define ADEC_LAYER2_REQUANTIZATION_CD \
+{ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  0 */ \
+    { 5, pf_ungroup3, .0               ,  .0           }, /* allocation ==  1 */ \
+    { 7, pf_ungroup5, .0               ,  .0           }, /* allocation ==  2 */ \
+    {10, pf_ungroup9, .0               ,  .0           }, /* allocation ==  3 */ \
+    { 4,        NULL, .133333333332    , -.933333333328}, /* allocation ==  4 */ \
+    { 5,        NULL, .0645161290325   , -.967741935488}, /* allocation ==  5 */ \
+    { 6,        NULL, .0317460317459   , -.984126984124}, /* allocation ==  6 */ \
+    { 7,        NULL, .0157480314961   , -.992125984254}, /* allocation ==  7 */ \
+    { 8,        NULL, .00784313725492  , -.996078431375}, /* allocation ==  8 */ \
+    { 9,        NULL, .00391389432484  , -.998043052835}, /* allocation ==  9 */ \
+    {10,        NULL, .00195503421311  , -.999022482897}, /* allocation == 10 */ \
+    {11,        NULL, .000977039570107 , -.99951148022 }, /* allocation == 11 */ \
+    {12,        NULL, .000488400488398 , -.999755799752}, /* allocation == 12 */ \
+    {13,        NULL, .000244170430962 , -.999877914789}, /* allocation == 13 */ \
+    {14,        NULL, .000122077763535 , -.999938961117}, /* allocation == 14 */ \
+    {15,        NULL, .000061037018952 , -.999969481491}  /* allocation == 15 */ \
+}
+
+#define ADEC_LAYER2_REQUANTIZATION_AB1 \
+{ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  0 */ \
+    { 5, pf_ungroup3, .0               ,  .0           }, /* allocation ==  1 */ \
+    { 3,        NULL, .285714285715    , -.857142857145}, /* allocation ==  2 */ \
+    { 4,        NULL, .133333333332    , -.933333333328}, /* allocation ==  3 */ \
+    { 5,        NULL, .0645161290325   , -.967741935488}, /* allocation ==  4 */ \
+    { 6,        NULL, .0317460317459   , -.984126984124}, /* allocation ==  5 */ \
+    { 7,        NULL, .0157480314961   , -.992125984254}, /* allocation ==  6 */ \
+    { 8,        NULL, .00784313725492  , -.996078431375}, /* allocation ==  7 */ \
+    { 9,        NULL, .00391389432484  , -.998043052835}, /* allocation ==  8 */ \
+    {10,        NULL, .00195503421311  , -.999022482897}, /* allocation ==  9 */ \
+    {11,        NULL, .000977039570107 , -.99951148022 }, /* allocation == 10 */ \
+    {12,        NULL, .000488400488398 , -.999755799752}, /* allocation == 11 */ \
+    {13,        NULL, .000244170430962 , -.999877914789}, /* allocation == 12 */ \
+    {14,        NULL, .000122077763535 , -.999938961117}, /* allocation == 13 */ \
+    {15,        NULL, .000061037018952 , -.999969481491}, /* allocation == 14 */ \
+    {16,        NULL, .0000305180437933, -.999984740976}  /* allocation == 15 */ \
+}
+
+#define ADEC_LAYER2_REQUANTIZATION_AB2 \
+{ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  0 */ \
+    { 5, pf_ungroup3, .0               ,  .0           }, /* allocation ==  1 */ \
+    { 7, pf_ungroup5, .0               ,  .0           }, /* allocation ==  2 */ \
+    { 3,        NULL, .285714285715    , -.857142857145}, /* allocation ==  3 */ \
+    {10, pf_ungroup9, .0               ,  .0           }, /* allocation ==  4 */ \
+    { 4,        NULL, .133333333332    , -.933333333328}, /* allocation ==  5 */ \
+    { 5,        NULL, .0645161290325   , -.967741935488}, /* allocation ==  6 */ \
+    { 6,        NULL, .0317460317459   , -.984126984124}, /* allocation ==  7 */ \
+    { 7,        NULL, .0157480314961   , -.992125984254}, /* allocation ==  8 */ \
+    { 8,        NULL, .00784313725492  , -.996078431375}, /* allocation ==  9 */ \
+    { 9,        NULL, .00391389432484  , -.998043052835}, /* allocation == 10 */ \
+    {10,        NULL, .00195503421311  , -.999022482897}, /* allocation == 11 */ \
+    {11,        NULL, .000977039570107 , -.99951148022 }, /* allocation == 12 */ \
+    {12,        NULL, .000488400488398 , -.999755799752}, /* allocation == 13 */ \
+    {13,        NULL, .000244170430962 , -.999877914789}, /* allocation == 14 */ \
+    {16,        NULL, .0000305180437933, -.999984740976}  /* allocation == 15 */ \
+}
+
+#define ADEC_LAYER2_REQUANTIZATION_AB3 \
+{ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  0 */ \
+    { 5, pf_ungroup3, .0               ,  .0           }, /* allocation ==  1 */ \
+    { 7, pf_ungroup5, .0               ,  .0           }, /* allocation ==  2 */ \
+    { 3,        NULL, .285714285715    , -.857142857145}, /* allocation ==  3 */ \
+    {10, pf_ungroup9, .0               ,  .0           }, /* allocation ==  4 */ \
+    { 4,        NULL, .133333333332    , -.933333333328}, /* allocation ==  5 */ \
+    { 5,        NULL, .0645161290325   , -.967741935488}, /* allocation ==  6 */ \
+    {16,        NULL, .0000305180437933, -.999984740976}, /* allocation ==  7 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  8 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  9 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 10 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 11 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 12 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 13 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 14 */ \
+    { 0,        NULL, .0               ,  .0           }  /* allocation == 15 */ \
+}
+
+#define ADEC_LAYER2_REQUANTIZATION_AB4 \
+{ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  0 */ \
+    { 5, pf_ungroup3, .0               ,  .0           }, /* allocation ==  1 */ \
+    { 7, pf_ungroup5, .0               ,  .0           }, /* allocation ==  2 */ \
+    {16,        NULL, .0000305180437933, -.999984740976}, /* allocation ==  3 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  4 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  5 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  6 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  7 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  8 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation ==  9 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 10 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 11 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 12 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 13 */ \
+    { 0,        NULL, .0               ,  .0           }, /* allocation == 14 */ \
+    { 0,        NULL, .0               ,  .0           }  /* allocation == 15 */ \
+}
+
+#define ADEC_LAYER2_REQUANTIZATION_AB \
+{ \
+    p_requantization_ab1, /* subband ==  0 */ \
+    p_requantization_ab1, /* subband ==  1 */ \
+    p_requantization_ab1, /* subband ==  2 */ \
+    p_requantization_ab2, /* subband ==  3 */ \
+    p_requantization_ab2, /* subband ==  4 */ \
+    p_requantization_ab2, /* subband ==  5 */ \
+    p_requantization_ab2, /* subband ==  6 */ \
+    p_requantization_ab2, /* subband ==  7 */ \
+    p_requantization_ab2, /* subband ==  8 */ \
+    p_requantization_ab2, /* subband ==  9 */ \
+    p_requantization_ab2, /* subband == 10 */ \
+    p_requantization_ab3, /* subband == 11 */ \
+    p_requantization_ab3, /* subband == 12 */ \
+    p_requantization_ab3, /* subband == 13 */ \
+    p_requantization_ab3, /* subband == 14 */ \
+    p_requantization_ab3, /* subband == 15 */ \
+    p_requantization_ab3, /* subband == 16 */ \
+    p_requantization_ab3, /* subband == 17 */ \
+    p_requantization_ab3, /* subband == 18 */ \
+    p_requantization_ab3, /* subband == 19 */ \
+    p_requantization_ab3, /* subband == 20 */ \
+    p_requantization_ab3, /* subband == 21 */ \
+    p_requantization_ab3, /* subband == 22 */ \
+    p_requantization_ab4, /* subband == 23 */ \
+    p_requantization_ab4, /* subband == 24 */ \
+    p_requantization_ab4, /* subband == 25 */ \
+    p_requantization_ab4, /* subband == 26 */ \
+    p_requantization_ab4, /* subband == 27 */ \
+    p_requantization_ab4, /* subband == 28 */ \
+    p_requantization_ab4  /* subband == 29 */ \
+}
diff --git a/include/audio_decoder.h b/include/audio_decoder.h
new file mode 100644 (file)
index 0000000..0091c98
--- /dev/null
@@ -0,0 +1,1038 @@
+/******************************************************************************
+ * audio_decoder.h : audio decoder thread interface
+ * (c)1999 VideoLAN
+ ******************************************************************************
+ * = Prototyped functions are implemented in audio_decoder/audio_decoder.c
+ *
+ * = Required headers :
+ *   - <pthread.h>                                                ( pthread_t )
+ *   - "common.h"                                    ( u32, byte_t, boolean_t )
+ *   - "input.h"                                ( ts_packet_t, input_thread_t )
+ *   - "decoder_fifo.h"                                      ( decoder_fifo_t )
+ *   - "audio_output.h"                          ( aout_fifo_t, aout_thread_t )
+ *
+ * = - LSb = Least Significant bit
+ *   - LSB = Least Significant Byte
+ *
+ * = - MSb = Most Significant bit
+ *   - MSB = Most Significant Byte
+ ******************************************************************************/
+
+/*
+ * TODO :
+ * - Etudier /usr/include/asm/bitops.h d'un peu plus près, bien qu'il ne me
+ *   semble pas Ãªtre possible d'utiliser ces fonctions ici
+ * - extern inline ? #define ?
+ * - N'y aurait-t-il pas moyen de se passer d'un buffer de bits, en travaillant
+ *   directement sur le flux PES ?
+ */
+
+/******************************************************************************
+ * bit_fifo_t : bit fifo descriptor
+ ******************************************************************************
+ * This type describes a bit fifo used to store bits while working with the
+ * input stream at the bit level.
+ ******************************************************************************/
+typedef struct bit_fifo_s
+{
+    /* This unsigned integer allows us to work at the bit level. This buffer
+     * can contain 32 bits, and the used space can be found on the MSb's side
+     * and the available space on the LSb's side. */
+    u32                 buffer;
+
+    /* Number of bits available in the bit buffer */
+    int                 i_available;
+
+} bit_fifo_t;
+
+/******************************************************************************
+ * bit_stream_t : bit stream descriptor
+ ******************************************************************************
+ * This type, based on a PES stream, includes all the structures needed to
+ * handle the input stream like a bit stream.
+ ******************************************************************************/
+typedef struct bit_stream_s
+{
+    /*
+     * Input structures
+     */
+    /* The input thread feeds the stream with fresh PES packets */
+    input_thread_t *    p_input;
+    /* The decoder fifo contains the data of the PES stream */
+    decoder_fifo_t *    p_decoder_fifo;
+
+    /*
+     * Byte structures
+     */
+    /* Current TS packet (in the current PES packet of the PES stream) */
+    ts_packet_t *       p_ts;
+    /* Index of the next byte that is to be read (in the current TS packet) */
+    unsigned int        i_byte;
+
+    /*
+     * Bit structures
+     */
+    bit_fifo_t          fifo;
+
+} bit_stream_t;
+
+/******************************************************************************
+ * adec_bank_t
+ ******************************************************************************/
+typedef struct adec_bank_s
+{
+    float               v1[512];
+    float               v2[512];
+    float *             actual;
+    int                 pos;
+
+} adec_bank_t;
+
+/******************************************************************************
+ * adec_thread_t : audio decoder thread descriptor
+ ******************************************************************************
+ * This type describes an audio decoder thread
+ ******************************************************************************/
+typedef struct adec_thread_s
+{
+    /*
+     * Thread properties
+     */
+    pthread_t           thread_id;                /* id for pthread functions */
+    boolean_t           b_die;                                  /* `die' flag */
+    boolean_t           b_error;                              /* `error' flag */
+
+    /*
+     * Input properties
+     */
+    decoder_fifo_t      fifo;                   /* stores the PES stream data */
+    /* The bit stream structure handles the PES stream at the bit level */
+    bit_stream_t        bit_stream;
+
+    /*
+     * Decoder properties
+     */
+    adec_bank_t         bank_0;
+    adec_bank_t         bank_1;
+
+    /*
+     * Output properties
+     */
+    aout_fifo_t *       p_aout_fifo;  /* stores the decompressed audio frames */
+    aout_thread_t *     p_aout;            /* needed to create the audio fifo */
+
+} adec_thread_t;
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+adec_thread_t * adec_CreateThread       ( input_thread_t * p_input /* !! , aout_thread_t * p_aout !! */ );
+void            adec_DestroyThread      ( adec_thread_t * p_adec );
+
+/* Fast 32 points Discrete Cosine Transform (289 additions and multiplications)
+   F(u)=alpha(u)*SUM(x=0, x<N) f(x)*cos((2x+1)u*pi/2N)
+   where alpha(u) = sqrt(2)/N if u=0, 2/N otherwise.
+   See fastdct.ps, and fast.tar.gz for a (Fortran :) implementation. */
+static /* __inline__ */ void DCT32(float *x, adec_bank_t *b)
+{
+    /* cosine coefficients */
+    static const float c2  =  .70710678118655;
+    static const float c3  =  .54119610014620;
+    static const float c4  = -1.3065629648764;
+    static const float c5  =  .50979557910416;
+    static const float c6  =  .89997622313642;
+    static const float c7  = -2.5629154477415;
+    static const float c8  = -.60134488693505;
+    static const float c9  =  .50241928618816;
+    static const float c10 =  .56694403481636;
+    static const float c11 =  .78815462345125;
+    static const float c12 =  1.7224470982383;
+    static const float c13 = -5.1011486186892;
+    static const float c14 = -1.0606776859903;
+    static const float c15 = -.64682178335999;
+    static const float c16 = -.52249861493969;
+    static const float c17 =  .50060299823520;
+    static const float c18 =  .51544730992262;
+    static const float c19 =  .55310389603444;
+    static const float c20 =  .62250412303566;
+    static const float c21 =  .74453627100230;
+    static const float c22 =  .97256823786196;
+    static const float c23 =  1.4841646163142;
+    static const float c24 =  3.4076084184687;
+    static const float c25 = -10.190008123548;
+    static const float c26 = -2.0577810099534;
+    static const float c27 = -1.1694399334329;
+    static const float c28 = -.83934964541553;
+    static const float c29 = -.67480834145501;
+    static const float c30 = -.58293496820613;
+    static const float c31 = -.53104259108978;
+    static const float c32 = -.50547095989754;
+
+    /* temporary variables */
+    float  t1  , t2  , t3  , t4  , t5  , t6  , t7  , t8  ,
+           t9  , t10 , t11 , t12 , t13 , t14 , t15 , t16 ,
+           t17 , t18 , t19 , t20 , t21 , t22 , t23 , t24 ,
+           t25 , t26 , t27 , t28 , t29 , t30 , t31 , t32 ,
+           tt1 , tt2 , tt3 , tt4 , tt5 , tt6 , tt7 , tt8 ,
+           tt9 , tt10, tt11, tt12, tt13, tt14, tt15, tt16,
+           tt17, tt18, tt19, tt20, tt21, tt22, tt23, tt24,
+           tt25, tt26, tt27, tt28, tt29, tt30, tt31, tt32, *y;
+    
+    /* We unrolled the loops */
+    /* Odd-even ordering is integrated before the 1st stage */
+    t17 = c17 * (x[0] - x[31]);
+    t1  = x[0] + x[31];
+    t18 = c18 * (x[2] - x[29]);
+    t2  = x[2] + x[29];
+    t19 = c19 * (x[4] - x[27]);
+    t3  = x[4] + x[27];
+    t20 = c20 * (x[6] - x[25]);
+    t4  = x[6] + x[25];
+    t21 = c21 * (x[8] - x[23]);
+    t5  = x[8] + x[23];
+    t22 = c22 * (x[10] - x[21]);
+    t6  = x[10] + x[21];
+    t23 = c23 * (x[12] - x[19]);
+    t7  = x[12] + x[19];
+    t24 = c24 * (x[14] - x[17]);
+    t8  = x[14] + x[17];
+    t25 = c25 * (x[16] - x[15]);
+    t9  = x[16] + x[15];
+    t26 = c26 * (x[18] - x[13]);
+    t10 = x[18] + x[13];
+    t27 = c27 * (x[20] - x[11]);
+    t11 = x[20] + x[11];
+    t28 = c28 * (x[22] - x[9]);
+    t12 = x[22] + x[9];
+    t29 = c29 * (x[24] - x[7]);
+    t13 = x[24] + x[7];
+    t30 = c30 * (x[26] - x[5]);
+    t14 = x[26] + x[5];
+    t31 = c31 * (x[28] - x[3]);
+    t15 = x[28] + x[3];
+    t32 = c32 * (x[30] - x[1]);
+    t16 = x[30] + x[1];
+    /* 2nd stage */
+    tt9  = c9  * (t1  - t9 );
+    tt1  = t1  + t9;
+    tt10 = c10 * (t2  - t10);
+    tt2  = t2  + t10;
+    tt11 = c11 * (t3  - t11);
+    tt3  = t3  + t11;
+    tt12 = c12 * (t4  - t12);
+    tt4  = t4  + t12;
+    tt13 = c13 * (t5  - t13);
+    tt5  = t5  + t13;
+    tt14 = c14 * (t6  - t14);
+    tt6  = t6  + t14;
+    tt15 = c15 * (t7  - t15);
+    tt7  = t7  + t15;
+    tt16 = c16 * (t8  - t16);
+    tt8  = t8  + t16;
+    tt25 = c9  * (t17 - t25);
+    tt17 = t17 + t25;
+    tt26 = c10 * (t18 - t26);
+    tt18 = t18 + t26;
+    tt27 = c11 * (t19 - t27);
+    tt19 = t19 + t27;
+    tt28 = c12 * (t20 - t28);
+    tt20 = t20 + t28;
+    tt29 = c13 * (t21 - t29);
+    tt21 = t21 + t29;
+    tt30 = c14 * (t22 - t30);
+    tt22 = t22 + t30;
+    tt31 = c15 * (t23 - t31);
+    tt23 = t23 + t31;
+    tt32 = c16 * (t24 - t32);
+    tt24 = t24 + t32;
+    /* 3rd stage */
+    t5  = c5 * (tt1  - tt5 );
+    t1  = tt1  + tt5;
+    t6  = c6 * (tt2  - tt6 );
+    t2  = tt2  + tt6;
+    t7  = c7 * (tt3  - tt7 );
+    t3  = tt3  + tt7;
+    t8  = c8 * (tt4  - tt8 );
+    t4  = tt4  + tt8;
+    t13 = c5 * (tt9  - tt13);
+    t9  = tt9  + tt13;
+    t14 = c6 * (tt10 - tt14);
+    t10 = tt10 + tt14;
+    t15 = c7 * (tt11 - tt15);
+    t11 = tt11 + tt15;
+    t16 = c8 * (tt12 - tt16);
+    t12 = tt12 + tt16;
+    t21 = c5 * (tt17 - tt21);
+    t17 = tt17 + tt21;
+    t22 = c6 * (tt18 - tt22);
+    t18 = tt18 + tt22;
+    t23 = c7 * (tt19 - tt23);
+    t19 = tt19 + tt23;
+    t24 = c8 * (tt20 - tt24);
+    t20 = tt20 + tt24;
+    t29 = c5 * (tt25 - tt29);
+    t25 = tt25 + tt29;
+    t30 = c6 * (tt26 - tt30);
+    t26 = tt26 + tt30;
+    t31 = c7 * (tt27 - tt31);
+    t27 = tt27 + tt31;
+    t32 = c8 * (tt28 - tt32);
+    t28 = tt28 + tt32;
+    /* 4th stage */
+    tt3  = c3 * (t1  - t3 );
+    tt1  = t1  + t3;
+    tt4  = c4 * (t2  - t4 );
+    tt2  = t2  + t4;
+    tt7  = c3 * (t5  - t7 );
+    tt5  = t5  + t7;
+    tt8  = c4 * (t6  - t8 );
+    tt6  = t6  + t8;
+    tt11 = c3 * (t9  - t11);
+    tt9  = t9  + t11;
+    tt12 = c4 * (t10 - t12);
+    tt10 = t10 + t12;
+    tt15 = c3 * (t13 - t15);
+    tt13 = t13 + t15;
+    tt16 = c4 * (t14 - t16);
+    tt14 = t14 + t16;
+    tt19 = c3 * (t17 - t19);
+    tt17 = t17 + t19;
+    tt20 = c4 * (t18 - t20);
+    tt18 = t18 + t20;
+    tt23 = c3 * (t21 - t23);
+    tt21 = t21 + t23;
+    tt24 = c4 * (t22 - t24);
+    tt22 = t22 + t24;
+    tt27 = c3 * (t25 - t27);
+    tt25 = t25 + t27;
+    tt28 = c4 * (t26 - t28);
+    tt26 = t26 + t28;
+    tt31 = c3 * (t29 - t31);
+    tt29 = t29 + t31;
+    tt32 = c4 * (t30 - t32);
+    tt30 = t30 + t32;
+    /* Bit-reverse ordering is integrated after the 5th stage */
+    /* Begin to split the result of the DCT (t1 to t32) in the filter bank */
+    x = b->actual + b->pos;
+    y = (b->actual == b->v1 ? b->v2 : b->v1) + b->pos;
+    x[0] = -(y[0] = c2 * (tt1  - tt2 )); /* t17 */
+    x[256] = 0; y[256] = tt1  + tt2; /* t1  */
+    t25 = c2 * (tt3  - tt4 );
+    t9  = tt3  + tt4;
+    t21 = c2 * (tt5  - tt6 );
+    t5  = tt5  + tt6;
+    t29 = c2 * (tt7  - tt8 );
+    t13 = tt7  + tt8;
+    t19 = c2 * (tt9  - tt10);
+    t3  = tt9  + tt10;
+    t27 = c2 * (tt11 - tt12);
+    t11 = tt11 + tt12;
+    t23 = c2 * (tt13 - tt14);
+    t7  = tt13 + tt14;
+    t31 = c2 * (tt15 - tt16);
+    t15 = tt15 + tt16;
+    t18 = c2 * (tt17 - tt18);
+    t2  = tt17 + tt18;
+    t26 = c2 * (tt19 - tt20);
+    t10 = tt19 + tt20;
+    t22 = c2 * (tt21 - tt22);
+    t6  = tt21 + tt22;
+    t30 = c2 * (tt23 - tt24);
+    t14 = tt23 + tt24;
+    t20 = c2 * (tt25 - tt26);
+    t4  = tt25 + tt26;
+    t28 = c2 * (tt27 - tt28);
+    t12 = tt27 + tt28;
+    t24 = c2 * (tt29 - tt30);
+    t8  = tt29 + tt30;
+    t32 = c2 * (tt31 - tt32);
+    t16 = tt31 + tt32;
+    /* Do the sums */
+    /* Keep on splitting the result */
+    y[384] = y[128] = t9 - (x[128] = -(x[384] = t25)); /* t25, t9  */
+    t10 += t26;
+    t11 += t27;
+    t12 += t28;
+    t13 += t29;
+    t14 += t30;
+    t15 += t31;
+    t16 += t32;
+    y[320] = y[192] = t5 + t13; /* t5  */
+    y[448] = y[64] = t13 + t21; /* t13 */
+    x[64] = -(x[448] = t21 - (x[192] = -(x[320] = t29))); /* t29, t21 */
+    t6  += t14;
+    t14 += t22;
+    t22 += t30;
+    t7  += t15;
+    t15 += t23;
+    t23 += t31;
+    t8  += t16;
+    t16 += t24;
+    t24 += t32;
+    y[288] = y[224] = t3 + t7; /* t3  */
+    y[352] = y[160] = t7 + t11; /* t7  */
+    y[416] = y[96] = t11 + t15; /* t11 */
+    y[480] = y[32] = t15 + t19; /* t15 */
+    x[32] = -(x[480] = t19 + t23); /* t19 */
+    x[96] = -(x[416] = t23 + t27); /* t23 */
+    x[160] = -(x[352] = t27 - (x[224] = -(x[288] = t31))); /* t31, t27 */
+    t4  += t8 ;
+    t8  += t12;
+    t12 += t16;
+    t16 += t20;
+    t20 += t24;
+    t24 += t28;
+    t28 += t32;
+    y[272] = y[240] = t2 + t4; /* t2  */
+    y[304] = y[208] = t4 + t6; /* t4  */
+    y[336] = y[176] = t6 + t8; /* t6  */
+    y[368] = y[144] = t8 + t10; /* t8  */
+    y[400] = y[112] = t10 + t12; /* t10 */
+    y[432] = y[80] = t12 + t14; /* t12 */
+    y[464] = y[48] = t14 + t16; /* t14 */
+    y[496] = y[16] = t16 + t18; /* t16 */
+    x[16] = -(x[496] = t18 + t20); /* t18 */
+    x[48] = -(x[464] = t20 + t22); /* t20 */
+    x[80] = -(x[432] = t22 + t24); /* t22 */
+    x[112] = -(x[400] = t24 + t26); /* t24 */
+    x[144] = -(x[368] = t26 + t28); /* t26 */
+    x[176] = -(x[336] = t28 + t30); /* t28 */
+    x[208] = -(x[304] = t30 - (x[240] = -(x[272] = t32))); /* t32, t30 */
+    /* Note that to be really complete, the DCT should multiply t1 by sqrt(2)/N
+       and t2 to t32 by 2/N, and would take 321 additions and multiplications.
+       But that's unuseful in this application. */
+}
+
+/* Compute 32 PCM (Pulse Code Modulation) samples with a convolution product */
+static /* __inline__ */ void PCM(adec_bank_t *b, s16 **pcm, int jump)
+{
+    /* scale factor */
+#define F 32768
+    /* These values are not in the same order as in Annex 3-B.3 of the ISO/IEC
+       DIS 11172-3 */
+    static const float c[512] = {
+       0.000000000 * F, -0.000442505 * F,  0.003250122 * F, -0.007003784 * F,
+       0.031082153 * F, -0.078628540 * F,  0.100311279 * F, -0.572036743 * F,
+       1.144989014 * F,  0.572036743 * F,  0.100311279 * F,  0.078628540 * F,
+       0.031082153 * F,  0.007003784 * F,  0.003250122 * F,  0.000442505 * F,
+       -0.000015259 * F, -0.000473022 * F,  0.003326416 * F, -0.007919312 * F,
+       0.030517578 * F, -0.084182739 * F,  0.090927124 * F, -0.600219727 * F,
+       1.144287109 * F,  0.543823242 * F,  0.108856201 * F,  0.073059082 * F,
+       0.031478882 * F,  0.006118774 * F,  0.003173828 * F,  0.000396729 * F,
+       -0.000015259 * F, -0.000534058 * F,  0.003387451 * F, -0.008865356 * F,
+       0.029785156 * F, -0.089706421 * F,  0.080688477 * F, -0.628295898 * F,
+       1.142211914 * F,  0.515609741 * F,  0.116577148 * F,  0.067520142 * F,
+       0.031738281 * F,  0.005294800 * F,  0.003082275 * F,  0.000366211 * F,
+       -0.000015259 * F, -0.000579834 * F,  0.003433228 * F, -0.009841919 * F,
+       0.028884888 * F, -0.095169067 * F,  0.069595337 * F, -0.656219482 * F,
+       1.138763428 * F,  0.487472534 * F,  0.123474121 * F,  0.061996460 * F,
+       0.031845093 * F,  0.004486084 * F,  0.002990723 * F,  0.000320435 * F,
+       -0.000015259 * F, -0.000625610 * F,  0.003463745 * F, -0.010848999 * F,
+       0.027801514 * F, -0.100540161 * F,  0.057617188 * F, -0.683914185 * F,
+       1.133926392 * F,  0.459472656 * F,  0.129577637 * F,  0.056533813 * F,
+       0.031814575 * F,  0.003723145 * F,  0.002899170 * F,  0.000289917 * F,
+       -0.000015259 * F, -0.000686646 * F,  0.003479004 * F, -0.011886597 * F,
+       0.026535034 * F, -0.105819702 * F,  0.044784546 * F, -0.711318970 * F,
+       1.127746582 * F,  0.431655884 * F,  0.134887695 * F,  0.051132202 * F,
+       0.031661987 * F,  0.003005981 * F,  0.002792358 * F,  0.000259399 * F,
+       -0.000015259 * F, -0.000747681 * F,  0.003479004 * F, -0.012939453 * F,
+       0.025085449 * F, -0.110946655 * F,  0.031082153 * F, -0.738372803 * F,
+       1.120223999 * F,  0.404083252 * F,  0.139450073 * F,  0.045837402 * F,
+       0.031387329 * F,  0.002334595 * F,  0.002685547 * F,  0.000244141 * F,
+       -0.000030518 * F, -0.000808716 * F,  0.003463745 * F, -0.014022827 * F,
+       0.023422241 * F, -0.115921021 * F,  0.016510010 * F, -0.765029907 * F,
+       1.111373901 * F,  0.376800537 * F,  0.143264771 * F,  0.040634155 * F,
+       0.031005859 * F,  0.001693726 * F,  0.002578735 * F,  0.000213623 * F,
+       -0.000030518 * F, -0.000885010 * F,  0.003417969 * F, -0.015121460 * F,
+       0.021575928 * F, -0.120697021 * F,  0.001068115 * F, -0.791213989 * F,
+       1.101211548 * F,  0.349868774 * F,  0.146362305 * F,  0.035552979 * F,
+       0.030532837 * F,  0.001098633 * F,  0.002456665 * F,  0.000198364 * F,
+       -0.000030518 * F, -0.000961304 * F,  0.003372192 * F, -0.016235352 * F,
+       0.019531250 * F, -0.125259399 * F, -0.015228271 * F, -0.816864014 * F,
+       1.089782715 * F,  0.323318481 * F,  0.148773193 * F,  0.030609131 * F,
+       0.029937744 * F,  0.000549316 * F,  0.002349854 * F,  0.000167847 * F,
+       -0.000030518 * F, -0.001037598 * F,  0.003280640 * F, -0.017349243 * F,
+       0.017257690 * F, -0.129562378 * F, -0.032379150 * F, -0.841949463 * F,
+       1.077117920 * F,  0.297210693 * F,  0.150497437 * F,  0.025817871 * F,
+       0.029281616 * F,  0.000030518 * F,  0.002243042 * F,  0.000152588 * F,
+       -0.000045776 * F, -0.001113892 * F,  0.003173828 * F, -0.018463135 * F,
+       0.014801025 * F, -0.133590698 * F, -0.050354004 * F, -0.866363525 * F,
+       1.063217163 * F,  0.271591187 * F,  0.151596069 * F,  0.021179199 * F,
+       0.028533936 * F, -0.000442505 * F,  0.002120972 * F,  0.000137329 * F,
+       -0.000045776 * F, -0.001205444 * F,  0.003051758 * F, -0.019577026 * F,
+       0.012115479 * F, -0.137298584 * F, -0.069168091 * F, -0.890090942 * F,
+       1.048156738 * F,  0.246505737 * F,  0.152069092 * F,  0.016708374 * F,
+       0.027725220 * F, -0.000869751 * F,  0.002014160 * F,  0.000122070 * F,
+       -0.000061035 * F, -0.001296997 * F,  0.002883911 * F, -0.020690918 * F,
+       0.009231567 * F, -0.140670776 * F, -0.088775635 * F, -0.913055420 * F,
+       1.031936646 * F,  0.221984863 * F,  0.151962280 * F,  0.012420654 * F,
+       0.026840210 * F, -0.001266479 * F,  0.001907349 * F,  0.000106812 * F,
+       -0.000061035 * F, -0.001388550 * F,  0.002700806 * F, -0.021789551 * F,
+       0.006134033 * F, -0.143676758 * F, -0.109161377 * F, -0.935195923 * F,
+       1.014617920 * F,  0.198059082 * F,  0.151306152 * F,  0.008316040 * F,
+       0.025909424 * F, -0.001617432 * F,  0.001785278 * F,  0.000106812 * F,
+       -0.000076294 * F, -0.001480103 * F,  0.002487183 * F, -0.022857666 * F,
+       0.002822876 * F, -0.146255493 * F, -0.130310059 * F, -0.956481934 * F,
+       0.996246338 * F,  0.174789429 * F,  0.150115967 * F,  0.004394531 * F,
+       0.024932861 * F, -0.001937866 * F,  0.001693726 * F,  0.000091553 * F,
+       -0.000076294 * F, -0.001586914 * F,  0.002227783 * F, -0.023910522 * F,
+       -0.000686646 * F, -0.148422241 * F, -0.152206421 * F, -0.976852417 * F,
+       0.976852417 * F,  0.152206421 * F,  0.148422241 * F,  0.000686646 * F,
+       0.023910522 * F, -0.002227783 * F,  0.001586914 * F,  0.000076294 * F,
+       -0.000091553 * F, -0.001693726 * F,  0.001937866 * F, -0.024932861 * F,
+       -0.004394531 * F, -0.150115967 * F, -0.174789429 * F, -0.996246338 * F,
+       0.956481934 * F,  0.130310059 * F,  0.146255493 * F, -0.002822876 * F,
+       0.022857666 * F, -0.002487183 * F,  0.001480103 * F,  0.000076294 * F,
+       -0.000106812 * F, -0.001785278 * F,  0.001617432 * F, -0.025909424 * F,
+       -0.008316040 * F, -0.151306152 * F, -0.198059082 * F, -1.014617920 * F,
+       0.935195923 * F,  0.109161377 * F,  0.143676758 * F, -0.006134033 * F,
+       0.021789551 * F, -0.002700806 * F,  0.001388550 * F,  0.000061035 * F,
+       -0.000106812 * F, -0.001907349 * F,  0.001266479 * F, -0.026840210 * F,
+       -0.012420654 * F, -0.151962280 * F, -0.221984863 * F, -1.031936646 * F,
+       0.913055420 * F,  0.088775635 * F,  0.140670776 * F, -0.009231567 * F,
+       0.020690918 * F, -0.002883911 * F,  0.001296997 * F,  0.000061035 * F,
+       -0.000122070 * F, -0.002014160 * F,  0.000869751 * F, -0.027725220 * F,
+       -0.016708374 * F, -0.152069092 * F, -0.246505737 * F, -1.048156738 * F,
+       0.890090942 * F,  0.069168091 * F,  0.137298584 * F, -0.012115479 * F,
+       0.019577026 * F, -0.003051758 * F,  0.001205444 * F,  0.000045776 * F,
+       -0.000137329 * F, -0.002120972 * F,  0.000442505 * F, -0.028533936 * F,
+       -0.021179199 * F, -0.151596069 * F, -0.271591187 * F, -1.063217163 * F,
+       0.866363525 * F,  0.050354004 * F,  0.133590698 * F, -0.014801025 * F,
+       0.018463135 * F, -0.003173828 * F,  0.001113892 * F,  0.000045776 * F,
+       -0.000152588 * F, -0.002243042 * F, -0.000030518 * F, -0.029281616 * F,
+       -0.025817871 * F, -0.150497437 * F, -0.297210693 * F, -1.077117920 * F,
+       0.841949463 * F,  0.032379150 * F,  0.129562378 * F, -0.017257690 * F,
+       0.017349243 * F, -0.003280640 * F,  0.001037598 * F,  0.000030518 * F,
+       -0.000167847 * F, -0.002349854 * F, -0.000549316 * F, -0.029937744 * F,
+       -0.030609131 * F, -0.148773193 * F, -0.323318481 * F, -1.089782715 * F,
+       0.816864014 * F,  0.015228271 * F,  0.125259399 * F, -0.019531250 * F,
+       0.016235352 * F, -0.003372192 * F,  0.000961304 * F,  0.000030518 * F,
+       -0.000198364 * F, -0.002456665 * F, -0.001098633 * F, -0.030532837 * F,
+       -0.035552979 * F, -0.146362305 * F, -0.349868774 * F, -1.101211548 * F,
+       0.791213989 * F, -0.001068115 * F,  0.120697021 * F, -0.021575928 * F,
+       0.015121460 * F, -0.003417969 * F,  0.000885010 * F,  0.000030518 * F,
+       -0.000213623 * F, -0.002578735 * F, -0.001693726 * F, -0.031005859 * F,
+       -0.040634155 * F, -0.143264771 * F, -0.376800537 * F, -1.111373901 * F,
+       0.765029907 * F, -0.016510010 * F,  0.115921021 * F, -0.023422241 * F,
+       0.014022827 * F, -0.003463745 * F,  0.000808716 * F,  0.000030518 * F,
+       -0.000244141 * F, -0.002685547 * F, -0.002334595 * F, -0.031387329 * F,
+       -0.045837402 * F, -0.139450073 * F, -0.404083252 * F, -1.120223999 * F,
+       0.738372803 * F, -0.031082153 * F,  0.110946655 * F, -0.025085449 * F,
+       0.012939453 * F, -0.003479004 * F,  0.000747681 * F,  0.000015259 * F,
+       -0.000259399 * F, -0.002792358 * F, -0.003005981 * F, -0.031661987 * F,
+       -0.051132202 * F, -0.134887695 * F, -0.431655884 * F, -1.127746582 * F,
+       0.711318970 * F, -0.044784546 * F,  0.105819702 * F, -0.026535034 * F,
+       0.011886597 * F, -0.003479004 * F,  0.000686646 * F,  0.000015259 * F,
+       -0.000289917 * F, -0.002899170 * F, -0.003723145 * F, -0.031814575 * F,
+       -0.056533813 * F, -0.129577637 * F, -0.459472656 * F, -1.133926392 * F,
+       0.683914185 * F, -0.057617188 * F,  0.100540161 * F, -0.027801514 * F,
+       0.010848999 * F, -0.003463745 * F,  0.000625610 * F,  0.000015259 * F,
+       -0.000320435 * F, -0.002990723 * F, -0.004486084 * F, -0.031845093 * F,
+       -0.061996460 * F, -0.123474121 * F, -0.487472534 * F, -1.138763428 * F,
+       0.656219482 * F, -0.069595337 * F,  0.095169067 * F, -0.028884888 * F,
+       0.009841919 * F, -0.003433228 * F,  0.000579834 * F,  0.000015259 * F,
+       -0.000366211 * F, -0.003082275 * F, -0.005294800 * F, -0.031738281 * F,
+       -0.067520142 * F, -0.116577148 * F, -0.515609741 * F, -1.142211914 * F,
+       0.628295898 * F, -0.080688477 * F,  0.089706421 * F, -0.029785156 * F,
+       0.008865356 * F, -0.003387451 * F,  0.000534058 * F,  0.000015259 * F,
+       -0.000396729 * F, -0.003173828 * F, -0.006118774 * F, -0.031478882 * F,
+       -0.073059082 * F, -0.108856201 * F, -0.543823242 * F, -1.144287109 * F,
+       0.600219727 * F, -0.090927124 * F,  0.084182739 * F, -0.030517578 * F,
+       0.007919312 * F, -0.003326416 * F,  0.000473022 * F,  0.000015259 * F
+       };
+#undef F
+    int i;
+    float tmp, *v;
+    const float *f;
+    
+    f = c;
+    
+    switch(b->pos) {
+       case 0:
+           v = b->actual;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   /* ceiling saturation */
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       /* floor saturation */
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 1:
+           v = b->actual + 1;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 2:
+           v = b->actual + 2;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 3:
+           v = b->actual + 3;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 4:
+           v = b->actual + 4;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 5:
+           v = b->actual + 5;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 6:
+           v = b->actual + 6;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 7:
+           v = b->actual + 7;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 8:
+           v = b->actual + 8;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 9:
+           v = b->actual + 9;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 10:
+           v = b->actual + 10;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 11:
+           v = b->actual + 11;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 12:
+           v = b->actual + 12;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 13:
+           v = b->actual + 13;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 14:
+           v = b->actual + 14;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v;
+               v += 15;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 15;
+           }
+           break;
+       case 15:
+           v = b->actual + 15;
+           for(i=0; i<32; i++) {
+               tmp = *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               tmp += *f++ * *v--;
+               if((tmp += *f++ * *v) > 32767)
+                   **pcm = 0x7FFF;
+               else
+                   if(tmp < -32768)
+                       **pcm = 0x8000;
+                   else
+                       **pcm = (s16)tmp;
+               *pcm += jump;
+               v += 31;
+           }
+           break;
+    }
+
+    /* Set the next position in the filter bank */
+    b->pos++;
+    b->pos &= 15;
+    b->actual = (b->actual == b->v1 ? b->v2 : b->v1);
+}
diff --git a/include/audio_dsp.h b/include/audio_dsp.h
new file mode 100644 (file)
index 0000000..9b668c9
--- /dev/null
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * audio_dsp.h : header of the dsp functions library
+ * (c)1999 VideoLAN
+ ******************************************************************************
+ * Required headers:
+ * - "common.h" ( byte_t )
+ * - "audio_output.h" ( aout_dsp_t )
+ ******************************************************************************/
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int  aout_dspOpen       ( aout_dsp_t *p_dsp );
+int  aout_dspReset      ( aout_dsp_t *p_dsp );
+int  aout_dspSetFormat  ( aout_dsp_t *p_dsp );
+int  aout_dspSetChannels( aout_dsp_t *p_dsp );
+int  aout_dspSetRate    ( aout_dsp_t *p_dsp );
+void aout_dspGetBufInfo ( aout_dsp_t *p_dsp );
+void aout_dspPlaySamples( aout_dsp_t *p_dsp, byte_t *buffer, int i_size );
+void aout_dspClose      ( aout_dsp_t *p_dsp );
diff --git a/include/audio_output.h b/include/audio_output.h
new file mode 100644 (file)
index 0000000..46b316e
--- /dev/null
@@ -0,0 +1,206 @@
+/******************************************************************************
+ * audio_output.h : audio output thread interface
+ * (c)1999 VideoLAN
+ ******************************************************************************
+ * Required headers:
+ * - <pthread.h>                                                  ( pthread_t )
+ * - <sys/soundcard.h>                                       ( audio_buf_info )
+ * - "common.h"                                                   ( boolean_t )
+ * - "mtime.h"                                                      ( mtime_t )
+ ******************************************************************************/
+
+/* TODO :
+ *
+ * - Créer un flag destroy dans les fifos audio pour indiquer au thread audio
+ *   qu'il peut libérer la mémoire occupée par le buffer de la fifo lorsqu'il
+ *   le désire (fin du son ou fin du thread)
+ * - Redéplacer les #define dans config.h
+ *
+ */
+
+/*
+ * Defines => "config.h"
+ */
+
+/* Default output device. You probably should not change this. */
+#define AOUT_DEFAULT_DEVICE     "/dev/dsp"
+
+/* Default audio output format (AFMT_S16_NE = Native Endianess) */
+#define AOUT_DEFAULT_FORMAT     AFMT_S16_NE
+
+/* Default stereo mode (0 stands for mono, 1 for stereo) */
+#define AOUT_DEFAULT_STEREO     1
+
+/* Audio output rate, in Hz */
+#define AOUT_MIN_RATE           22050 /* ?? */
+#define AOUT_DEFAULT_RATE       44100
+#define AOUT_MAX_RATE           48000
+
+/* Number of audio samples (s16 integers) contained in an audio output frame...
+ * - Layer I        : a decoded frame contains 384 samples
+ * - Layer II & III : a decoded frame contains 1192 = 3*384 samples */
+#define AOUT_FRAME_SIZE         384
+
+/* Number of audio output frames contained in an audio output fifo.
+ * (AOUT_FIFO_SIZE + 1) must be a power of 2, in order to optimise the
+ * %(AOUT_FIFO_SIZE + 1) operation with an &AOUT_FIFO_SIZE.
+ * With 511 we have at least 511*384/2/48000=2 seconds of sound */
+#define AOUT_FIFO_SIZE          511
+
+/* Maximum number of audio fifos. The value of AOUT_MAX_FIFOS should be a power
+ * of two, in order to optimize the '/AOUT_MAX_FIFOS' and '*AOUT_MAX_FIFOS'
+ * operations with '>>' and '<<' (gcc changes this at compilation-time) */
+#define AOUT_MAX_FIFOS          4
+
+/* Duration (in microseconds) of an audio output buffer should be :
+ * - short, in order to be able to play a new song very quickly (especially a
+ *   song from the interface)
+ * - long, in order to perform the buffer calculations as few as possible */
+#define AOUT_BUFFER_DURATION    100000
+
+/*
+ * Macros
+ */
+#define AOUT_FIFO_ISEMPTY( fifo )       ( (fifo).l_end_frame == (fifo).i_start_frame )
+#define AOUT_FIFO_ISFULL( fifo )        ( ((((fifo).l_end_frame + 1) - (fifo).l_start_frame) & AOUT_FIFO_SIZE) == 0 )
+
+/******************************************************************************
+ * aout_dsp_t
+ ******************************************************************************/
+typedef struct
+{
+    /* Path to the audio output device (default is set to "/dev/dsp") */
+    char *              psz_device;
+    int                 i_fd;
+
+    /* Format of the audio output samples (see <sys/soundcard.h>) */
+    int                 i_format;
+    /* Following boolean is set to 0 if output sound is mono, 1 if stereo */
+    boolean_t           b_stereo;
+    /* Rate of the audio output sound (in Hz) */
+    long                l_rate;
+
+    /* Buffer information structure, used by aout_dspGetBufInfo() to store the
+     * current state of the internal sound card buffer */
+    audio_buf_info      buf_info;
+
+} aout_dsp_t;
+
+/******************************************************************************
+ * aout_increment_t
+ ******************************************************************************
+ * This structure is used to keep the progression of an index up-to-date, in
+ * order to avoid rounding problems and heavy computations, as the function
+ * that handles this structure only uses additions.
+ ******************************************************************************/
+typedef struct
+{
+    /* The remainder is used to keep track of the fractional part of the
+     * index. */
+    long                l_remainder;
+
+    /*
+     * The increment structure is initialized with the result of an euclidean
+     * division :
+     *
+     *  l_euclidean_numerator                           l_euclidean_remainder
+     * ----------------------- = l_euclidean_integer + -----------------------
+     * l_euclidean_denominator                         l_euclidean_denominator
+     *
+     */
+    long                l_euclidean_integer;
+    long                l_euclidean_remainder;
+    long                l_euclidean_denominator;
+
+} aout_increment_t;
+
+/******************************************************************************
+ * aout_frame_t
+ ******************************************************************************/
+typedef s16 aout_frame_t[ AOUT_FRAME_SIZE ];
+
+/******************************************************************************
+ * aout_fifo_t
+ ******************************************************************************/
+typedef struct
+{
+    /* See the fifo types below */
+    int                 i_type;
+    boolean_t           b_die;
+
+    boolean_t           b_stereo;
+    long                l_rate;
+
+    pthread_mutex_t     data_lock;
+    pthread_cond_t      data_wait;
+
+    void *              buffer;
+    mtime_t *           date;
+    /* The start frame is the first frame in the buffer that contains decoded
+     * audio data. It it also the first frame in the current timestamped frame
+     * area, ie the first dated frame in the decoded part of the buffer. :-p */
+    long                l_start_frame;
+    boolean_t           b_start_frame;
+    /* The next frame is the end frame of the current timestamped frame area,
+     * ie the first dated frame after the start frame. */
+    long                l_next_frame;
+    boolean_t           b_next_frame;
+    /* The end frame is the first frame, after the start frame, that doesn't
+     * contain decoded audio data. That's why the end frame is the first frame
+     * where the audio decoder can store its decoded audio frames. */
+    long                l_end_frame;
+
+    long                l_unit;
+    aout_increment_t    unit_increment;
+    /* The following variable is used to store the number of remaining audio
+     * units in the current timestamped frame area. */
+    long                l_units;
+
+} aout_fifo_t;
+
+#define AOUT_EMPTY_FIFO         0
+#define AOUT_INTF_MONO_FIFO     1
+#define AOUT_INTF_STEREO_FIFO   2
+#define AOUT_ADEC_MONO_FIFO     3
+#define AOUT_ADEC_STEREO_FIFO   4
+
+/******************************************************************************
+ * aout_thread_t
+ ******************************************************************************/
+typedef struct aout_thread_s
+{
+    pthread_t           thread_id;
+    boolean_t           b_die;
+
+    aout_dsp_t          dsp;
+
+    pthread_mutex_t     fifos_lock;
+    aout_fifo_t         fifo[ AOUT_MAX_FIFOS ];
+
+    void *              buffer;
+    /* The s32 buffer is used to mix all the audio fifos together before
+     * converting them and storing them in the audio output buffer */
+    s32 *               s32_buffer;
+
+    /* The size of the audio output buffer is kept in audio units, as this is
+     * the only unit that is common with every audio decoder and audio fifo */
+    long                l_units;
+
+    mtime_t             date;
+    /* date is the moment where the first audio unit of the output buffer
+     * should be played and is kept up-to-date with the following incremental
+     * structure */
+    aout_increment_t    date_increment;
+
+} aout_thread_t;
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int             aout_Open               ( aout_thread_t *p_aout );
+int             aout_SpawnThread        ( aout_thread_t *p_aout );
+void            aout_CancelThread       ( aout_thread_t *p_aout );
+void            aout_Close              ( aout_thread_t *p_aout );
+
+aout_fifo_t *   aout_CreateFifo         ( aout_thread_t *p_aout, aout_fifo_t *p_fifo );
+void            aout_DestroyFifo        ( aout_fifo_t *p_fifo );
diff --git a/include/common.h b/include/common.h
new file mode 100644 (file)
index 0000000..a27085c
--- /dev/null
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * common.h: common definitions
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * Collection of usefull common types and macros definitions
+ *******************************************************************************
+ * required headers:
+ *  config.h
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Types definitions
+ *******************************************************************************/
+
+/* Basic types definitions */
+typedef signed char         s8;
+typedef signed short        s16;
+typedef signed int          s32;
+typedef signed long long    s64;
+typedef unsigned char       u8;
+typedef unsigned short      u16;
+typedef unsigned int        u32;
+typedef unsigned long long  u64;
+
+typedef u8                  byte_t;
+/* Boolean type */
+typedef int                 boolean_t;
+
+/* Counter for statistics and profiling */
+typedef unsigned long       count_t;
+
+/*******************************************************************************
+ * Macros and inline functions
+ *******************************************************************************/
+
+/* CEIL: division with round to nearest greater integer */
+#define CEIL(n, d)  ( ((n) / (d)) + ( ((n) % (d)) ? 1 : 0) )
+
+/* PAD: PAD(n, d) = CEIL(n ,d) * d */
+#define PAD(n, d)   ( ((n) % (d)) ? ((((n) / (d)) + 1) * (d)) : (n) )
+
+/* MAX and MIN: self explanatory */
+#define MAX(a, b)   ( ((a) > (b)) ? (a) : (b) )
+#define MIN(a, b)   ( ((a) < (b)) ? (a) : (b) )
+
+/* MSB (big endian)/LSB (little endian) convertions - network order is always 
+ * MSB, and should be used for both network communications and files. Note that
+ * byte orders other than little and big endians are not supported, but only
+ * the VAX seems to have such exotic properties - note that these 'functions'
+ * needs <netinet/in.h> or the local equivalent. */
+/* ?? hton64 should be declared as an extern inline function to avoid border
+ * effects (see byteorder.h) */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define hton16      htons
+#define hton32      htonl
+#define hton64(i)   ( ((u64)(htonl((i) & 0xffffffff)) << 32) | htonl(((i) >> 32) & 0xffffffff ) )
+#define ntoh16      ntohs                              
+#define ntoh32      ntohl
+#define ntoh64      hton64
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define hton16      htons
+#define hton32      htonl
+#define hton64(i)   ( i )                            
+#define ntoh16      ntohs
+#define ntoh32      ntohl
+#define ntoh64(i)   ( i )
+#else
+/* ?? cause a compilation error */
+#endif
+
+/* Macros used by input to access the TS buffer */
+#define U32_AT(p)   ( ntohl ( *( (u32 *)(p) ) ) )
+#define U16_AT(p)   ( ntohs ( *( (u16 *)(p) ) ) )
diff --git a/include/config.h b/include/config.h
new file mode 100644 (file)
index 0000000..e1855a8
--- /dev/null
@@ -0,0 +1,314 @@
+/*******************************************************************************
+ * config.h: limits and configuration
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Defines all compilation-time configuration constants and size limits
+ *******************************************************************************
+ * required headers:
+ *  none
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Program information
+ *******************************************************************************/
+
+/* Program version and copyright message */
+#define PROGRAM_VERSION                "0.0.x"
+#define COPYRIGHT_MESSAGE      "VideoLAN Client v" PROGRAM_VERSION " (" __DATE__ ") - (c)1999 VideoLAN\n"
+
+/*******************************************************************************
+ * General compilation options
+ *******************************************************************************/
+
+/* Define for DVB support - Note that some extensions or restrictions may be
+ * incompatible with native MPEG2 streams */
+//#define DVB_EXTENSIONS
+//#define DVB_RESTRICTIONS
+
+/* Define for profiling support */
+#define STATS
+
+/* Define for unthreaded version of the program - ?? not yet implemented */
+//#define NO_THREAD
+
+/*******************************************************************************
+ * Debugging options - define or undefine symbols
+ *******************************************************************************/
+
+/* General debugging support */
+#define DEBUG
+
+/* Modules specific debugging */
+#define DEBUG_INTF
+#define DEBUG_INPUT
+#define DEBUG_AUDIO
+#define DEBUG_VIDEO
+
+/* Debugging log file - if defined, a file can be used to store all messages. If
+ * DEBUG_LOG_ONLY is defined, debug messages will only be printed to the log and
+ * will not appear on the screen */
+//#define DEBUG_LOG       "vlc-debug.log"
+//#define DEBUG_LOG_ONLY  
+
+/* ?? VOUT_DEBUG and co have changed ! */
+
+/*******************************************************************************
+ * Common settings
+ *******************************************************************************/
+
+/* ?? */
+#define THREAD_SLEEP    100000
+
+/*
+ * X11/XLib settings
+ */
+
+/* Default font used when a wished font could not be loaded - note that this
+ * font should be universal, else the program will exit when it can't find
+ * a font */
+#define X11_DEFAULT_FONT                "fixed"
+
+/*
+ * Decoders FIFO configuration
+ */
+
+/* Size of the FIFO. FIFO_SIZE+1 must be a multiple of 2 */
+#define FIFO_SIZE                       511
+
+/*******************************************************************************
+ * Input thread configuration
+ *******************************************************************************/
+
+/* ?? */
+#define INPUT_IDLE_SLEEP                100000
+
+/*
+ * General limitations
+ */
+
+/* Broadcast address, in case of a broadcasted stream */
+#define INPUT_BCAST_ADDR                "138.195.143.255"
+
+/* Maximum number of input threads - this value is used exclusively by
+ * interface, and is in fact an interface limitation */
+#define INPUT_MAX_THREADS               10
+
+/* Maximum number of programs definitions in a TS stream */
+#define INPUT_MAX_PGRM                  10
+
+/* Maximum number of ES definitions in a TS stream */
+#define INPUT_MAX_ES                    10
+
+/* Maximum number of ES in a single program */
+#define INPUT_MAX_PROGRAM_ES            10
+
+/* Maximum number of selected ES in an input thread */
+#define INPUT_MAX_SELECTED_ES           10
+
+/* Maximum number of TS packets in the client at any time
+ * INPUT_MAX_TS + 1 must be a power of 2, to optimize the %(INPUT_MAX_TS+1)
+ * operation with a &INPUT_MAX_TS in the case of a fifo netlist.
+ * It should be > number of fifos * FIFO_SIZE to avoid input deadlock. */
+#define INPUT_MAX_TS                    16383      /* INPUT_MAX_TS + 1 = 2^14 */
+
+/* Same thing with PES packets */
+#define INPUT_MAX_PES                   16383
+
+/* Maximum number of TS packets we read from the socket in one readv().
+ * Since you can't put more than 7 TS packets in an Ethernet frame,
+ * the maximum value is 7. This number should also limit the stream server,
+ * otherwise any supplementary packet is lost. */
+#define INPUT_TS_READ_ONCE              7
+
+/* Use a LIFO or FIFO for TS netlist ? */
+#undef INPUT_LIFO_TS_NETLIST
+
+/* Use a LIFO or FIFO for PES netlist ? */
+#undef INPUT_LIFO_PES_NETLIST
+
+/* Maximum length of a hostname */
+#define INPUT_MAX_HOSTNAME_LENGTH       100
+
+
+/* Default input method */
+#define INPUT_DEFAULT_METHOD            20 /* unicast (debug) */
+
+/* Default remote server */
+#define VIDEOLAN_DEFAULT_SERVER         "vod.via.ecp.fr"
+
+/* Default videolan port */
+#define VIDEOLAN_DEFAULT_PORT           1234
+
+/* Default videolan VLAN */
+#define VIDEOLAN_DEFAULT_VLAN           3
+
+/*
+ * Vlan method 
+ */ 
+
+/* Default VLAN server */
+#define VLAN_DEFAULT_SERVER             "vlanserver.via.ecp.fr"
+#define VLAN_DEFAULT_SERVER_PORT        6010
+
+/*******************************************************************************
+ * Audio output thread configuration
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Video output thread configuration
+ *******************************************************************************/
+
+/*
+ * Default settings for video output threads
+ */
+
+/* Title of the window */
+#define VOUT_TITLE                      "VideoLAN Client: output"
+
+/* Default use of XShm extension */
+#define VOUT_SHM_EXT                    1
+
+/* Dimensions for display window */
+#define VOUT_WIDTH                      544
+#define VOUT_HEIGHT                     576
+
+/* Default heap size */
+#define VOUT_HEAP_SIZE                  100
+
+/*
+ * Limitations
+ */
+
+/* Maximum number of video output threads - this value is used exclusively by
+ * interface, and is in fact an interface limitation */
+#define VOUT_MAX_THREADS                10
+
+/* Maximum number of video streams per video output thread */
+#define VOUT_MAX_STREAMS                10
+
+/* Maximum number of pictures which can be rendered in one loop, plus one */
+#define VOUT_MAX_PICTURES               10
+
+/*
+ * Other settings
+ */
+
+/* Time during which the thread will sleep if it has nothing to 
+ * display (in micro-seconds) */
+/* ?? this constant will probably evolve to a calculated value */
+#define VOUT_IDLE_SLEEP                 50000
+
+/* Maximum lap of time allowed between the beginning of rendering and
+ * display. If, compared to the current date, the next image is too
+ * late, the thread will perform an idle loop. This time should be
+ * at least VOUT_IDLE_SLEEP plus the time required to render a few
+ * images, to avoid trashing of decoded images */
+/* ?? this constant will probably evolve to a calculated value */
+#define VOUT_DISPLAY_DELAY              150000
+
+/* Maximum lap of time during which images are rendered in the same 
+ * time. It should be greater than the maximum time between two succesive
+ * images to avoid useless renderings and calls to the display driver,
+ * but not to high to avoid desynchronization */
+/* ?? this constant will probably evolve to a calculated value */
+#define VOUT_DISPLAY_TOLERANCE          150000
+
+/*******************************************************************************
+ * Video decoder configuration
+ *******************************************************************************/
+
+#define VDEC_IDLE_SLEEP                 100000
+
+/*******************************************************************************
+ * Generic decoder configuration
+ *******************************************************************************/
+
+#define GDEC_IDLE_SLEEP                 100000
+
+/*******************************************************************************
+ * Interface (main) thread configuration
+ *******************************************************************************/
+
+/*
+ * Interface configuration
+ */
+
+/* Base delay in micro second for interface sleeps ?? */
+#define INTF_IDLE_SLEEP                 100000
+
+/* Maximal number of arguments on a command line, including the function name */
+#define INTF_MAX_ARGS                   20
+
+/* Maximal size of a command line in a script */
+#define INTF_MAX_CMD_SIZE               240
+
+/*
+ * Messages functions
+ */
+
+/* Maximal size of the message queue - in case of overflow, all messages in the
+ * queue are printed by the calling thread */
+#define INTF_MSG_QSIZE                  32
+
+/* Define to enable messages queues - disabling messages queue can be usefull
+ * when debugging, since it allows messages which would not otherwise be printed,
+ * due to a crash, to be printed anyway */
+/*#define INTF_MSG_QUEUE*/
+
+/* Format of the header for debug messages. The arguments following this header
+ * are the file (char *), the function (char *) and the line (int) in which the
+ * message function was called */
+#define INTF_MSG_DBG_FORMAT "## %s:%s(),%i: "
+
+/* Filename to log message
+ * Note that messages are only logged when debugging */
+#define INTF_MSG_LOGFILE "vlc.log"
+
+/*
+ * X11 console properties
+ */
+
+/* Title of the X11 console interface window */
+#define INTF_XCONSOLE_TITLE             "VideoLAN Client: console"
+
+/* Welcome message: this message is always displayed when a new console is
+ * openned */
+#define INTF_XCONSOLE_WELCOME_MSG       COPYRIGHT_MESSAGE "try `help' to have a list of available commands"
+
+/* Background pixmap - if not defined, no pixmap is used */
+#define INTF_XCONSOLE_BACKGROUND_PIXMAP "Resources/background.xpm"
+
+/* Default X11 console interface window geometry. It should at least give a
+ * default size */
+#define INTF_XCONSOLE_GEOMETRY          "400x100"
+
+/* Font used in console. If first font is not found, the fallback font is
+ * used. Therefore, the fallback font should be a universal one. */
+#define INTF_XCONSOLE_FONT              "-*-helvetica-medium-r-normal-*-18-*-*-*-*-*-iso8859-1"
+
+/* Number of memorized lines in X11 console window text zone */
+#define INTF_XCONSOLE_MAX_LINES         100
+
+/* Maximal number of commands which can be saved in history list */
+#define INTF_XCONSOLE_HISTORY_SIZE      20
+
+/* Maximum width of a line in an X11 console window. If a larger line is
+ * printed, it will be wrapped. */
+#define INTF_XCONSOLE_MAX_LINE_WIDTH    120
+
+
+/*******************************************************************************
+ * Network and VLAN management
+ *******************************************************************************/
+/* Default network interface to use */
+#define NET_DFLT_IF                    "eth0"
+
+/* Default VLANserver address */
+#define VLAN_DFLT_VLANSRV              "vlanserver"
+
+/* Default VLANserver port */
+#define VLAN_DFLT_VLANPORT             "6010"
+
+/* Client identification */
+#define VLAN_LOGIN                     "guest"
+#define VLAN_PASSWD                    "none"
diff --git a/include/control.h b/include/control.h
new file mode 100644 (file)
index 0000000..63372af
--- /dev/null
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * control.h: user control functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Library of functions common to all interfaces, allowing access to various
+ * structures and settings. Interfaces should only use those functions
+ * to read or write informations from other threads.
+ *******************************************************************************
+ * Required headers:
+ *  <pthread.h>
+ *  <sys/uio.h>
+ *  <X11/Xlib.h>
+ *  <X11/extensions/XShm.h>
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ *  "input.h"
+ *  "video.h"
+ *  "video_output.h"
+ *  "xconsole.h"
+ *  "interface.h"
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+int     intf_CreateVoutThread   ( intf_thread_t *p_intf, char *psz_title, 
+                                  int i_width, int i_height );
+void    intf_DestroyVoutThread  ( intf_thread_t *p_intf, int i_thread );
+int     intf_CreateInputThread  ( intf_thread_t *p_intf, input_cfg_t* p_cfg );
+void    intf_DestroyInputThread ( intf_thread_t *p_intf, int i_thread );
+
+int     intf_SelectAudioStream  ( intf_thread_t *p_intf, int i_input, int i_id );
+void    intf_DeselectAudioStream( intf_thread_t *p_intf, int i_input, int i_id );
+int     intf_SelectVideoStream  ( intf_thread_t *p_intf, int i_input, 
+                                  int i_vout, int i_id );
+void    intf_DeselectVideoStream( intf_thread_t *p_intf, int i_input, int i_id );
+
+
+
diff --git a/include/debug.h b/include/debug.h
new file mode 100644 (file)
index 0000000..cc02f82
--- /dev/null
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * debug.h: vlc debug macros
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Stand alone file
+ *******************************************************************************
+ * Required headers:
+ * - <string.h>
+ * - intf_msg.h
+ *******************************************************************************/
+
+
+/*******************************************************************************
+ * ASSERT
+ *******************************************************************************
+ * This macro is used to test that a pointer is not nul. It insert the needed
+ * code when the program is compiled with the debug option, but does nothing
+ * in release program.
+ *******************************************************************************/
+#ifdef DEBUG
+#define ASSERT(p_Mem)                                                          \
+if (!(p_Mem))                                                                  \
+    intf_ErrMsg("Void pointer error: %s line %d (variable %s at address %p)\n",        \
+                 __FILE__, __LINE__, #p_Mem, &p_Mem);
+
+#else
+#define ASSERT(p_Mem)
+
+#endif
+    
+
+/*******************************************************************************
+ * RZERO
+ *******************************************************************************
+ * This macro is used to initialise a variable to 0. It is very useful when
+ * used with the ASSERT macro. It also only insert the needed code when the
+ * program is compiled with the debug option.
+ *******************************************************************************/
+#ifdef DEBUG
+#define RZERO(r_Var)                                                           \
+bzero(&(r_Var), sizeof((r_Var)));
+
+#else
+#define RZERO(r_Var)
+
+#endif
+
+
+/*******************************************************************************
+ * PZERO
+ *******************************************************************************
+ * This macro is used to initiase the memory pointed out by a pointer to 0.
+ * It has the same purpose than RZERO, but for pointers.
+ *******************************************************************************/
+#ifdef DEBUG
+#define PZERO(p_Mem)                                                           \
+bzero((p_Mem), sizeof(*(p_Mem)));
+
+#else
+#define PZERO(p_Mem)
+
+#endif
+
+
+/*******************************************************************************
+ * AZERO
+ *******************************************************************************
+ * This macro is used to initiase an array of variables to 0.
+ * It has the same purpose than RZERO or PZERO, but for array
+ *******************************************************************************/
+#ifdef DEBUG
+#define AZERO(p_Array, i_Size)                                                 \
+bzero((p_Array), (i_Size)*sizeof(*(p_Array)));
+
+#else
+#define ZERO(p_Array, i_Size)
+
+#endif
diff --git a/include/decoder_fifo.h b/include/decoder_fifo.h
new file mode 100644 (file)
index 0000000..ff323a4
--- /dev/null
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * decoder_fifo.h: interface for decoders PES fifo
+ * (c)1999 VideoLAN
+ ******************************************************************************
+ * Required headers:
+ * - <pthread.h>
+ * - "config.h"
+ * - "common.h"
+ * - "input.h"
+ ******************************************************************************/
+
+/******************************************************************************
+ * Macros
+ ******************************************************************************/
+
+/* ?? move to inline functions */
+#define DECODER_FIFO_ISEMPTY( fifo )    ( (fifo).i_start == (fifo).i_end )
+#define DECODER_FIFO_ISFULL( fifo )     ( ( ( (fifo).i_end + 1 - (fifo).i_start ) \
+                                          & FIFO_SIZE ) == 0 )
+#define DECODER_FIFO_START( fifo )      ( (fifo).buffer[ (fifo).i_start ] )
+#define DECODER_FIFO_INCSTART( fifo )   ( (fifo).i_start = ((fifo).i_start + 1) \
+                                                           & FIFO_SIZE ) 
+#define DECODER_FIFO_END( fifo )        ( (fifo).buffer[ (fifo).i_end ] )
+#define DECODER_FIFO_INCEND( fifo )     ( (fifo).i_end = ((fifo).i_end + 1) \
+                                                         & FIFO_SIZE )
+
+/******************************************************************************
+ * decoder_fifo_t
+ ******************************************************************************
+ * This rotative FIFO contains PES packets that are to be decoded...
+ ******************************************************************************/
+typedef struct
+{
+    pthread_mutex_t     data_lock;                          /* fifo data lock */
+    pthread_cond_t      data_wait;          /* fifo data conditional variable */
+
+    /* buffer is an array of PES packets pointers */
+    pes_packet_t *      buffer[FIFO_SIZE + 1];
+    int                 i_start;
+    int                 i_end;
+
+} decoder_fifo_t;
diff --git a/include/generic_decoder.h b/include/generic_decoder.h
new file mode 100644 (file)
index 0000000..ab2133a
--- /dev/null
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * generic_decoder.h : generic decoder thread
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Requires:
+ *  <pthread.h>
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ *  "input.h"
+ *  "decoder_fifo.h"
+ * ??
+ *******************************************************************************/
+
+/*******************************************************************************
+ * gdec_cfg_t: generic decoder configuration structure
+ *******************************************************************************
+ * This structure is passed as an initializer when a generic decoder thread is
+ * created.
+ *******************************************************************************/
+typedef struct gdec_cfg_s
+{
+    u64         i_properties;
+
+    int         i_actions;                                  /* decoder actions */
+    char *      psz_base_filename;                     /* base demux file name */
+} gdec_cfg_t;
+
+/* Properties flags */
+#define GDEC_CFG_ACTIONS    (1 << 0)
+#define GDEC_CFG_FILENAME   (1 << 1)
+
+/*******************************************************************************
+ * gdec_thread_t: generic decoder thread descriptor
+ *******************************************************************************
+ * This type describes a generic decoder thread.
+ *******************************************************************************/
+typedef struct gdec_thread_s
+{
+    /* Thread properties and locks */
+    boolean_t           b_die;                                   /* `die' flag */
+    boolean_t           b_error;                               /* `error' flag */
+    boolean_t           b_active;                             /* `active' flag */
+    pthread_t           thread_id;                 /* id for pthread functions */
+
+    /* Thread configuration */
+    int                 i_actions;                          /* decoder actions */
+    
+    /* Input properties */
+    input_thread_t *    p_input;                               /* input thread */
+    decoder_fifo_t      fifo;                                /* PES input fifo */
+
+    /* ?? status info */
+    int *               pi_status;
+    
+
+    /* Files array - these files are used to extract ES streams from a 
+     * demultiplexed stream */
+    /* ?? */
+
+#ifdef STATS
+    /* Statistics */
+    count_t         c_loops;                               /* number of loops */
+    count_t         c_idle_loops;                     /* number of idle loops */
+    count_t         c_pes;                      /* number of PES packets read */
+#endif
+} gdec_thread_t;
+
+/* Decoder actions - this flags select which actions the decoder will perform
+ * when it receives a PES packet */
+#define GDEC_IDENTIFY   (1 << 0)                   /* update input's ES tables */
+#define GDEC_SAVE       (1 << 1)                /* save all PES to a same file */
+#define GDEC_SAVE_DEMUX (1 << 2)             /* save PES to files by stream id */
+#define GDEC_PRINT      (1 << 3)                     /* print PES informations */
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+
+/* Thread management functions */
+gdec_thread_t * gdec_CreateThread       ( gdec_cfg_t *p_cfg, 
+                                          input_thread_t *p_input, int *pi_status );
+void            gdec_DestroyThread      ( gdec_thread_t *p_gdec, int *pi_status );
+
+/* Time management functions */
+/* ?? */
+
+/* Dynamic thread settings */
+/* ?? */
diff --git a/include/input.h b/include/input.h
new file mode 100644 (file)
index 0000000..2881487
--- /dev/null
@@ -0,0 +1,410 @@
+/*******************************************************************************
+ * input.h: input thread interface
+ * (c)1999 VideoLAN
+ *******************************************************************************/
+
+/* needs : "netlist.h", "config.h" */
+
+/* ?? missing: 
+ *              tables version control */
+
+/*******************************************************************************
+ * External structures
+ *******************************************************************************
+ * These structures, required here only as pointers destinations, are declared 
+ * in other headers.
+ *******************************************************************************/
+struct video_cfg_s;                          /* video configuration descriptor */
+struct vout_thread_s;                                   /* video output thread */
+struct stream_descriptor_s;                                      /* PSI tables */
+
+/*******************************************************************************
+ * Constants related to input
+ *******************************************************************************/
+#define TS_PACKET_SIZE      188                         /* size of a TS packet */
+#define PES_HEADER_SIZE     14       /* size of the first part of a PES header */
+#define PSI_SECTION_SIZE    4096              /* Maximum size of a PSI section */
+
+/*******************************************************************************
+ * ts_packet_t
+ *******************************************************************************
+ * Describe a TS packet.
+ *******************************************************************************/
+typedef struct ts_packet_struct
+{
+    /* Nothing before this line, the code relies on that */
+    byte_t                  buffer[TS_PACKET_SIZE];      /* raw TS data packet */
+
+    /* Decoders information */
+    unsigned int            i_payload_start;
+                                    /* start of the PES payload in this packet */
+    unsigned int            i_payload_end;                      /* guess ? :-) */
+
+    /* Used to chain the TS packets that carry data for a same PES or PSI */
+    struct ts_packet_struct *  p_prev_ts;
+    struct ts_packet_struct *  p_next_ts;
+} ts_packet_t;
+
+
+/*******************************************************************************
+ * pes_packet_t
+ *******************************************************************************
+ * Describes an PES packet, with its properties, and pointers to the TS packets
+ * containing it.
+ *******************************************************************************/
+typedef struct
+{
+    /* PES properties */
+    boolean_t               b_data_loss;    /* The previous (at least) PES packet
+             * has been lost. The decoders will have to find a way to recover. */
+    boolean_t               b_data_alignment;  /* used to find the beginning of a
+                                                * video or audio unit          */
+    boolean_t               b_has_pts;         /* is the following field set ? */
+    u32                     i_pts;   /* the PTS for this packet (if set above) */
+    boolean_t               b_random_access;
+              /* if TRUE, in the payload of this packet, there is the first byte 
+               * of a video sequence header, or the first byte of an audio frame.
+               */
+    u8                      i_stream_id;                /* payload type and id */
+    int                     i_pes_size;      /* size of the current PES packet */
+    int                     i_ts_packets;  /* number of TS packets in this PES */
+
+    /* Demultiplexer environment */
+    boolean_t               b_discard_payload;    /* is the packet messed up ? */
+    byte_t *                p_pes_header;         /* pointer to the PES header */
+    byte_t *                p_pes_header_save;             /* temporary buffer */
+
+    /* Pointers to TS packets (TS packets are then linked by the p_prev_ts and 
+       p_next_ts fields of the ts_packet_t struct) */
+    ts_packet_t *           p_first_ts;    /* The first TS packet containing this
+                                            * PES (used by decoders). */
+    ts_packet_t *           p_last_ts;  /* The last TS packet gathered at present
+                                         * (used by the demultiplexer). */
+} pes_packet_t;
+
+
+/*******************************************************************************
+ * psi_section_t
+ *******************************************************************************
+ * Describes a PSI section. Beware, it doesn't contain pointers to the TS
+ * packets that contain it as for a PES, but the data themselves
+ *******************************************************************************/
+typedef struct
+{
+    byte_t        buffer[PSI_SECTION_SIZE];
+  
+    boolean_t     b_running_section;     /* Is there a section being decoded ? */
+
+    u16 i_length;
+    u16 i_current_position;
+} psi_section_t;
+
+
+/*******************************************************************************
+ * es_descriptor_t: elementary stream descriptor
+ *******************************************************************************
+ * Describes an elementary stream, and includes fields required to handle and
+ * demultiplex this elementary stream.
+ *******************************************************************************/
+typedef struct
+{   
+    u16                     i_id;             /* stream ID, PID for TS streams */
+    u8                      i_type;                             /* stream type */
+
+    boolean_t               b_pcr;          /* does the stream include a PCR ? */
+    /* ?? b_pcr will be replaced by something else: since a PCR can't be shared
+     * between several ES, we will probably store the PCR fields directly here,
+     * and one of those fields will probably (again) be used as a test of the
+     * PCR presence */
+    boolean_t               b_psi;   /* does the stream have to be handled by the
+                                                                 PSI decoder ? */
+    /* Markers */
+    int                     i_continuity_counter;
+    boolean_t               b_discontinuity;
+    boolean_t               b_random;
+
+    /* PES packets */
+    pes_packet_t *          p_pes_packet;
+                                        /* current PES packet we are gathering */
+
+    /* PSI packets */
+    psi_section_t *         p_psi_section;            /* idem for a PSI stream */
+
+    /* Decoder informations */
+    void *                  p_dec;      /* p_dec is void *, since we don't know a
+                                         * priori whether it is adec_thread_t or
+                                         * vdec_thread_t. We will use explicit
+                                         * casts. */
+
+    /* ?? video stream descriptor ? */
+    /* ?? audio stream descriptor ? */
+    /* ?? hierarchy descriptor ? */
+    /* ?? target background grid descriptor ? */
+    /* ?? video window descriptor ? */
+    /* ?? ISO 639 language descriptor ? */
+
+#ifdef STATS
+    /* Stats */
+    count_t                 c_bytes;                       /* total bytes read */
+    count_t                 c_payload_bytes; /* total of payload usefull bytes */
+    count_t                 c_packets;                   /* total packets read */
+    count_t                 c_invalid_packets;         /* invalid packets read */
+    /* ?? ... other stats */
+#endif
+} es_descriptor_t;
+
+/* Special PID values - note that the PID is only on 13 bits, and that values
+ * greater than 0x1fff have no meaning in a stream */
+#define PROGRAM_ASSOCIATION_TABLE_PID   0x0000 
+#define CONDITIONNAL_ACCESS_TABLE_PID   0x0001                     /* not used */
+#define EMPTY_PID                       0xffff      /* empty record in a table */
+
+/* ES streams types - see ISO/IEC 13818-1 table 2-29 numbers */
+#define MPEG1_VIDEO_ES          0x01
+#define MPEG2_VIDEO_ES          0x02
+#define MPEG1_AUDIO_ES          0x03
+#define MPEG2_AUDIO_ES          0x04
+
+
+/*******************************************************************************
+ * program_descriptor_t
+ *******************************************************************************
+ * Describes a program and list associated elementary streams. It is build by
+ * the PSI decoder upon the informations carried in program map sections 
+ *******************************************************************************/
+typedef struct
+{
+    /* Program characteristics */
+    u16                     i_number;                        /* program number */
+    u8                      i_version;                       /* version number */ 
+    boolean_t               b_is_ok;         /* Is the description up to date ?*/
+    u16                     i_pcr_pid;                               /* PCR ES */
+
+    int i_es_number;
+    es_descriptor_t **      ap_es;                  /* array of pointers to ES */
+
+#ifdef DVB_EXTENSIONS
+    /* Service Descriptor (program name) */
+    u8                      i_srv_type;
+    char*                   psz_srv_name;
+#endif
+
+    /* ?? target background grid descriptor ? */
+    /* ?? video window descriptor ? */
+    /* ?? ISO 639 language descriptor ? */
+
+#ifdef STATS
+    /* Stats */
+    /* ?? ...stats */
+#endif
+} pgrm_descriptor_t;
+
+/*******************************************************************************
+ * pcr_descriptor_t
+ *******************************************************************************
+ * Contains informations used to synchronise the decoder with the server
+ * Only input_PcrDecode() is allowed to modify it
+ *******************************************************************************/
+
+typedef struct pcr_descriptor_struct
+{
+    pthread_mutex_t         lock;                     /* pcr modification lock */
+
+    s64                     delta_clock;
+                            /* represents decoder_time - pcr_time in usecondes */
+    count_t                 c_average;
+                             /* counter used to compute dynamic average values */
+#ifdef STATS
+    /* Stats */
+    count_t     c_average_jitter;
+    s64         max_jitter;                    /* the evalueted maximum jitter */
+    s64         average_jitter;                /* the evalueted average jitter */
+    count_t     c_pcr;            /* the number of PCR which have been decoded */
+#endif    
+} pcr_descriptor_t;
+
+/*******************************************************************************
+ * stream_descriptor_t
+ *******************************************************************************
+ * Describes a transport stream and list its associated programs. Build upon
+ * the informations carried in program association sections
+ *******************************************************************************/
+typedef struct
+{
+    u16                     i_stream_id;                                 /* stream id */
+
+    /* Program Association Table status */
+    u8                      i_PAT_version;                     /* version number */ 
+    boolean_t               b_is_PAT_complete;           /* Is the PAT complete ?*/
+    u8                      i_known_PAT_sections;   /* Number of section we received so far */
+    byte_t                  a_known_PAT_sections[32]; /* Already received sections */
+
+    /* Program Map Table status */
+    boolean_t               b_is_PMT_complete;           /* Is the PMT complete ?*/
+    u8                      i_known_PMT_sections;   /* Number of section we received so far */
+    byte_t                  a_known_PMT_sections[32]; /* Already received sections */
+
+    /* Service Description Table status */
+    u8                      i_SDT_version;                     /* version number */ 
+    boolean_t               b_is_SDT_complete;           /* Is the SDT complete ?*/
+    u8                      i_known_SDT_sections;   /* Number of section we received so far */
+    byte_t                  a_known_SDT_sections[32]; /* Already received sections */
+
+    /* Programs description */
+    int i_pgrm_number;                     /* Number of program number we have */
+    pgrm_descriptor_t **    ap_programs;             /* Array of pointers to pgrm */
+
+#ifdef STATS
+    /* Stats */
+    /* ?? ...stats */
+#endif
+} stream_descriptor_t;
+
+/*******************************************************************************
+ * input_netlist_t
+ *******************************************************************************/
+typedef struct
+{
+    pthread_mutex_t         lock;                  /* netlist modification lock */
+    struct iovec            p_ts_free[INPUT_MAX_TS + INPUT_TS_READ_ONCE];
+                                             /* FIFO or LIFO of free TS packets */
+    ts_packet_t *           p_ts_packets;
+                                 /* pointer to the first TS packet we allocated */
+
+    pes_packet_t *          p_pes_free[INPUT_MAX_PES + 1];
+                                            /* FIFO or LIFO of free PES packets */
+    pes_packet_t *          p_pes_packets;
+                                /* pointer to the first PES packet we allocated */
+
+    /* To use the efficiency of the scatter/gather IO operations. We implemented it
+     * in 2 ways, as we don't know yet which one is better : as a FIFO (code
+     * simplier) or as a LIFO stack (when we doesn't care of the ordering, this
+     * allow to drastically improve the cache performance) */
+#ifdef INPUT_LIFO_TS_NETLIST
+    int                     i_ts_index;
+#else
+    int                     i_ts_start, i_ts_end;
+#endif
+#ifdef INPUT_LIFO_PES_NETLIST
+    int                     i_pes_index;
+#else
+    int                     i_pes_start, i_pes_end;
+#endif
+} input_netlist_t;
+
+/*******************************************************************************
+ * input_thread_t
+ *******************************************************************************
+ * This structure includes all the local static variables of an input thread,
+ * including the netlist and the ES descriptors
+ * Note that p_es must be defined as a static table, otherwise we would have to
+ * update all reference to it each time the table would be reallocated 
+ *******************************************************************************/
+
+/* function pointers */
+struct input_thread_struct;
+struct input_cfg_struct;
+typedef int (*f_open_t)( struct input_thread_struct *, struct input_cfg_struct *);
+typedef int (*f_read_t)( struct input_thread_struct *, const struct iovec *,
+                         size_t );
+typedef void (*f_clean_t)( struct input_thread_struct * );
+
+typedef struct input_thread_struct
+{
+    /* Thread properties and locks */
+    boolean_t               b_die;                               /* 'die' flag */
+    boolean_t               b_error;                               /* deadlock */
+    pthread_t               thread_id;             /* id for pthread functions */
+    pthread_mutex_t         programs_lock;       /* programs modification lock */
+    pthread_mutex_t         es_lock;                   /* es modification lock */
+
+    /* Input method description */
+    int                     i_method;                          /* input method */
+    int                     i_handle;                /* file/socket descriptor */
+    int                     i_vlan_id;                   /* id for vlan method */
+    f_open_t                p_open;     /* pointer to the opener of the method */
+    f_read_t                p_read;         /* pointer to the reading function */
+    f_clean_t               p_clean;     /* pointer to the destroying function */
+
+    /* General stream description */
+    stream_descriptor_t *   p_stream;                            /* PAT tables */
+    es_descriptor_t         p_es[INPUT_MAX_ES];  /* carried elementary streams */
+    pcr_descriptor_t *      p_pcr;      /* PCR struct used for synchronisation */
+
+    /* List of streams to demux */
+    es_descriptor_t *       pp_selected_es[INPUT_MAX_SELECTED_ES];
+    
+    /* Netlists */
+    input_netlist_t         netlist;                              /* see above */
+
+    /* ?? default settings for new decoders */
+    struct aout_thread_s *      p_aout;      /* audio output thread structure */
+
+#ifdef STATS
+    /* Stats */
+    count_t                 c_loops;                        /* number of loops */
+    count_t                 c_bytes;                    /* total of bytes read */
+    count_t                 c_payload_bytes;  /* total of payload useful bytes */
+    count_t                 c_ts_packets_read;        /* total of packets read */
+    count_t                 c_ts_packets_trashed;  /* total of trashed packets */
+    /* ?? ... other stats */
+#endif
+} input_thread_t;
+
+/* Input methods */
+#define INPUT_METHOD_NONE           0              /* input thread is inactive */
+#define INPUT_METHOD_TS_FILE       10         /* TS stream is read from a file */
+#define INPUT_METHOD_TS_UCAST      20                        /* TS UDP unicast */
+#define INPUT_METHOD_TS_MCAST      21                      /* TS UDP multicast */
+#define INPUT_METHOD_TS_BCAST      22                      /* TS UDP broadcast */
+#define INPUT_METHOD_TS_VLAN_BCAST 32           /* TS UDP broadcast with VLANs */
+
+/*******************************************************************************
+ * input_cfg_t: input thread configuration structure
+ *******************************************************************************
+ * This structure is passed as a parameter to input_CreateTtread(). It includes
+ * several fields describing potential properties of a new object. 
+ * The 'i_properties' field allow to set only a subset of the required 
+ * properties, asking the called function to use default settings for
+ * the other ones.
+ *******************************************************************************/
+typedef struct input_cfg_struct
+{
+    u64     i_properties;
+
+    /* Input method properties */
+    int     i_method;                                          /* input method */
+    char *  psz_filename;                                          /* filename */
+    char *  psz_hostname;                                   /* server hostname */
+    char *  psz_ip;                                               /* server IP */
+    int     i_port;                                                    /* port */
+    int     i_vlan;                                             /* vlan number */
+
+    /* ??... default settings for new decoders */
+    struct aout_thread_s *      p_aout;      /* audio output thread structure */
+
+} input_cfg_t;
+
+/* Properties flags */
+#define INPUT_CFG_METHOD    (1 << 0)
+#define INPUT_CFG_FILENAME  (1 << 4)
+#define INPUT_CFG_HOSTNAME  (1 << 8)
+#define INPUT_CFG_IP        (1 << 9)
+#define INPUT_CFG_PORT      (1 << 10)
+#define INPUT_CFG_VLAN      (1 << 11)
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+input_thread_t *input_CreateThread      ( input_cfg_t *p_cfg );
+void            input_DestroyThread     ( input_thread_t *p_input );
+
+int             input_OpenAudioStream   ( input_thread_t *p_input, int i_pid
+                                          /* ?? , struct audio_cfg_s * p_cfg */ );
+void            input_CloseAudioStream  ( input_thread_t *p_input, int i_pid );
+int             input_OpenVideoStream   ( input_thread_t *p_input, 
+                                          struct vout_thread_s *p_vout, struct video_cfg_s * p_cfg );
+void            input_CloseVideoStream  ( input_thread_t *p_input, int i_pid );
+
+/* ?? settings functions */
+/* ?? info functions */
diff --git a/include/input_ctrl.h b/include/input_ctrl.h
new file mode 100644 (file)
index 0000000..1bf1eac
--- /dev/null
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * input_ctrl.h: Decodeur control
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Requires:
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ ******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int input_AddPgrmElem( input_thread_t *p_input, int i_current_pid );
+int input_DelPgrmElem( input_thread_t *p_input, int i_current_pid );
+boolean_t input_IsElemRecv( input_thread_t *p_input, int i_pid );
diff --git a/include/input_file.h b/include/input_file.h
new file mode 100644 (file)
index 0000000..0794c54
--- /dev/null
@@ -0,0 +1,13 @@
+/*******************************************************************************
+ * file.h: file streams functions interface
+ * (c)1999 VideoLAN
+ *******************************************************************************/
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int     input_FileCreateMethod( input_thread_t *p_input ,
+                                input_cfg_t *p_cfg );
+int     input_FileRead( input_thread_t *p_input, const struct iovec *p_vector,
+                        size_t i_count );
+void    input_FileDestroyMethod( input_thread_t *p_input );
diff --git a/include/input_netlist.h b/include/input_netlist.h
new file mode 100644 (file)
index 0000000..ce4779f
--- /dev/null
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * input_netlist.h: netlist interface
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * The netlists are an essential part of the input structure. We maintain a
+ * list of free TS packets and free PES packets to avoid continuous malloc
+ * and free.
+ *******************************************************************************/
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int input_NetlistOpen( input_thread_t *p_input );
+void input_NetlistClean( input_thread_t *p_input );
+/* ?? implement also a "normal" (non inline, non static) function in input_netlist.c,
+   which will be used when inline is disabled */
+/* ?? test */ static __inline__ void input_NetlistFreePES( input_thread_t *p_input,
+                                  pes_packet_t *p_pes_packet );
+static __inline__ void input_NetlistFreeTS( input_thread_t *p_input,
+                                 ts_packet_t *p_ts_packet );
+static __inline__ pes_packet_t* input_NetlistGetPES( input_thread_t *p_input );
+
+/*******************************************************************************
+ * input_NetlistFreePES: add a PES packet to the netlist
+ *******************************************************************************
+ * Add a PES packet to the PES netlist, so that the packet can immediately be
+ * reused by the demultiplexer. We put this function directly in the .h file,
+ * because it is very frequently called.
+ *******************************************************************************/
+static __inline__ void input_NetlistFreePES( input_thread_t *p_input,
+                                  pes_packet_t *p_pes_packet )
+{
+    int             i_dummy;
+    ts_packet_t *   p_ts_packet;
+
+    ASSERT(p_pes_packet);
+
+    /* We will be playing with indexes, so we take a lock. */
+    pthread_mutex_lock( &p_input->netlist.lock );
+
+    /* Free all TS packets in this PES structure. */
+    p_ts_packet = p_pes_packet->p_first_ts;
+    for( i_dummy = 0; i_dummy < p_pes_packet->i_ts_packets; i_dummy++ )
+    {
+       ASSERT(p_ts_packet);
+
+#ifdef INPUT_LIFO_TS_NETLIST
+        p_input->netlist.i_ts_index--;
+        p_input->netlist.p_ts_free[p_input->netlist.i_ts_index].iov_base
+                             = p_ts_packet;
+#else /* FIFO */
+        p_input->netlist.p_ts_free[p_input->netlist.i_ts_end].iov_base
+                             = p_ts_packet;
+        p_input->netlist.i_ts_end++;
+        p_input->netlist.i_ts_end &= INPUT_MAX_TS; /* loop */
+#endif
+        p_ts_packet = p_ts_packet->p_next_ts;
+    }
+    
+    /* Free the PES structure. */
+#ifdef INPUT_LIFO_PES_NETLIST
+    p_input->netlist.i_pes_index--;
+    p_input->netlist.p_pes_free[p_input->netlist.i_pes_index] = p_pes_packet;
+#else /* FIFO */
+    p_input->netlist.p_pes_free[p_input->netlist.i_pes_end] = p_pes_packet;
+    p_input->netlist.i_pes_end++;
+    p_input->netlist.i_pes_end &= INPUT_MAX_PES; /* loop */
+#endif
+
+    pthread_mutex_unlock( &p_input->netlist.lock );
+}
+
+/*******************************************************************************
+ * input_NetlistFreeTS: add a TS packet to the netlist
+ *******************************************************************************
+ * Add a TS packet to the TS netlist, so that the packet can immediately be
+ * reused by the demultiplexer. Shouldn't be called by other threads (they
+ * should only use input_FreePES.
+ *******************************************************************************/
+static __inline__ void input_NetlistFreeTS( input_thread_t *p_input,
+                                            ts_packet_t *p_ts_packet )
+{
+    ASSERT(p_ts_packet);
+
+    /* We will be playing with indexes, so we take a lock. */
+    pthread_mutex_lock( &p_input->netlist.lock );
+
+    /* Free the TS structure. */
+#ifdef INPUT_LIFO_TS_NETLIST
+    p_input->netlist.i_ts_index--;
+    p_input->netlist.p_ts_free[p_input->netlist.i_ts_index].iov_base = p_ts_packet;
+#else /* FIFO */
+    p_input->netlist.p_ts_free[p_input->netlist.i_ts_end].iov_base = p_ts_packet;
+    p_input->netlist.i_ts_end++;
+    p_input->netlist.i_ts_end &= INPUT_MAX_TS; /* loop */
+#endif
+
+    pthread_mutex_unlock( &p_input->netlist.lock );
+}
+
+/*******************************************************************************
+ * input_NetlistGetPES: remove a PES packet from the netlist
+ *******************************************************************************
+ * Add a TS packet to the TS netlist, so that the packet can immediately be
+ * reused by the demultiplexer. Shouldn't be called by other threads (they
+ * should only use input_FreePES.
+ *******************************************************************************/
+static __inline__ pes_packet_t* input_NetlistGetPES( input_thread_t *p_input )
+{
+    pes_packet_t *          p_pes_packet;
+
+#ifdef INPUT_LIFO_PES_NETLIST
+    /* i_pes_index might be accessed by a decoder thread to give back a 
+     * packet. */
+    pthread_mutex_lock( &p_input->netlist.lock );
+
+    /* Verify that we still have PES packet in the netlist */
+    if( (INPUT_MAX_PES - p_input->netlist.i_pes_index ) <= 1 )
+    {
+        intf_ErrMsg("input error: PES netlist is empty !\n");
+        return( NULL );
+    }
+
+    /* Fetch a new PES packet */
+    p_pes_packet = p_input->netlist.p_pes_free[p_input->netlist.i_pes_index];
+    p_input->netlist.i_pes_index++;
+    pthread_mutex_unlock( &p_input->netlist.lock );
+
+#else /* FIFO */
+    /* No need to lock, since we are the only ones accessing i_pes_start. */
+
+    /* Verify that we still have PES packet in the netlist */
+    if( ((p_input->netlist.i_pes_end -1 - p_input->netlist.i_pes_start) & INPUT_MAX_PES) <= 1 )
+    {
+        intf_ErrMsg("input error: PES netlist is empty !\n");
+        return( NULL );
+    }
+
+    p_pes_packet = p_input->netlist.p_pes_free[p_input->netlist.i_pes_start];
+    p_input->netlist.i_pes_start++;
+    p_input->netlist.i_pes_start &= INPUT_MAX_PES; /* loop */
+#endif /* netlist type */
+
+    /* Initialize PES flags. */
+    p_pes_packet->b_data_loss = 0;
+    p_pes_packet->b_data_alignment = 0;
+    p_pes_packet->b_has_pts = 0;
+    p_pes_packet->b_random_access = 0;
+    p_pes_packet->b_discard_payload = 0;
+    p_pes_packet->i_pes_size = 0;
+    p_pes_packet->i_ts_packets = 0;
+    p_pes_packet->p_first_ts = NULL;
+    p_pes_packet->p_last_ts = NULL;
+
+    return( p_pes_packet );
+}
diff --git a/include/input_network.h b/include/input_network.h
new file mode 100644 (file)
index 0000000..d43ea97
--- /dev/null
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * network.h: network functions interface
+ * (c)1999 VideoLAN
+ *******************************************************************************/
+
+/*
+  needs :
+   - <sys/uio.h>
+   - <sys/socket.h>
+   - <unistd.h>
+   - <netinet/in.h>
+   - <netdb.h>
+   - <arpa/inet.h>
+   - <stdio.h>
+   - <sys/ioctl.h>
+   - <net/if.h>
+*/
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int input_NetworkCreateMethod( input_thread_t *p_input,
+                               input_cfg_t *p_cfg );
+int input_NetworkRead( input_thread_t *p_input, const struct iovec *p_vector,
+                       size_t i_count );
+void input_NetworkDestroyMethod( input_thread_t *p_input );
diff --git a/include/input_pcr.h b/include/input_pcr.h
new file mode 100644 (file)
index 0000000..c1d7ccf
--- /dev/null
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * input_pcr.h: PCR management interface
+ * (c)1999 VideoLAN
+ *******************************************************************************/
+
+/* Maximum number of samples used to compute the dynamic average value,
+ * it is also the maximum of c_average in the pcr_descriptor_struct.
+ * We use the following formula :
+ * new_average = (old_average * c_average + new_sample_value) / (c_average +1) */
+#define PCR_MAX_AVERAGE_COUNTER 40
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int            input_PcrInit        ( input_thread_t *p_input );
+void           input_PcrDecode      ( input_thread_t *p_input, es_descriptor_t* p_es, 
+                                       u8* p_pcr_data );
+void           input_PcrClean       ( input_thread_t *p_input );
diff --git a/include/input_psi.h b/include/input_psi.h
new file mode 100644 (file)
index 0000000..31c71b8
--- /dev/null
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * psi.h: PSI management interface
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Requires:
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ ******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int input_PsiInit( input_thread_t *p_input );
+void input_PsiDecode( input_thread_t *p_input, psi_section_t* p_psi_section );
+void input_PsiRead( input_thread_t *p_input /* ??? */ );
+int input_PsiClean( input_thread_t *p_input );
diff --git a/include/input_vlan.h b/include/input_vlan.h
new file mode 100644 (file)
index 0000000..b4beeb5
--- /dev/null
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * input_vlan.h: vlan input method
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * ?? 
+ *******************************************************************************
+ * Required headers:
+ * <netinet/in.h>
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Vlan server related constants
+ *******************************************************************************/
+
+#define VLAN_SERVER_MSG_LENGTH  256             /* maximum length of a message */
+#define VLAN_CLIENT_VERSION     "1.3.0"                 /* vlan client version */
+
+/* Messages codes */
+#define VLAN_LOGIN_REQUEST      98        /* login: <version> <login> <passwd> */
+#define VLAN_LOGIN_ANSWER       97                    /* login accepted: [msg] */
+#define VLAN_LOGIN_REJECTED     96                    /* login rejected: [msg] */
+
+#define VLAN_LOGOUT             99                                   /* logout */
+
+#define VLAN_INFO_REQUEST       31                        /* info: no argument */
+#define VLAN_INFO_ANSWER        32/* info ok: <switch> <port> <vlan> <sharers> */
+#define VLAN_INFO_REJECTED      33                     /* info rejected: [msg] */
+
+#define VLAN_CHANGE_REQUEST     21/* change: <mac> [ip] <vlan dest> [vlan src] */
+#define VLAN_CHANGE_ANSWER      22                         /* change ok: [msg] */
+#define VLAN_CHANGE_REJECTED    23                     /* change failed: [msg] */
+
+/*******************************************************************************
+ * Macros to build/extract vlan_ids
+ *******************************************************************************/
+#define VLAN_ID_IFACE( vlan_id )    ( ((vlan_id) >> 8) & 0xff ) 
+#define VLAN_ID_VLAN( vlan_id )     ( (vlan_id) & 0xff )
+#define VLAN_ID( iface, vlan )      ( ((iface) << 8) | (vlan) )
+
+/*******************************************************************************
+ * input_vlan_server_t: vlan server
+ *******************************************************************************
+ * This structure describes a vlan server.
+ *******************************************************************************/
+typedef struct
+{
+    struct sockaddr_in  sa_in;                               /* server address */
+    int                 i_socket;                         /* socket descriptor */
+
+    /* Login informations */
+    char *              psz_login;                             /* server login */
+    char *              psz_passwd;                         /* server password */
+} input_vlan_server_t;
+
+/*******************************************************************************
+ * input_vlan_iface_t: vlan-capable network interface
+ *******************************************************************************
+ * This structure describes the abilities of a network interface capable of
+ * vlan management. Note that an interface could have several IP adresses, but
+ * since only the MAC address is used to change vlan, only one needs to be
+ * retrieved.
+ * ?? it could be interesting to send a port id on vlan request, to know if two
+ * interfaces are dependant regarding vlan changes.
+ *******************************************************************************/
+typedef struct
+{
+    char *                  psz_name;                        /* interface name */
+    struct sockaddr_in      sa_in;                             /* interface IP */
+    char                    psz_mac[20];                      /* interface MAC */
+
+    /* Hardware properties */
+    int                     i_master;                /* master interface index */
+    int                     i_switch;                         /* switch number */
+    int                     i_port;                             /* port number */
+    int                     i_sharers;          /* number of MACs on this port */
+    
+    /* Vlan properties - these are only used if i_master is negative */
+    int                     i_refcount;                       /* locks counter */
+    int                     i_vlan;                            /* current vlan */
+    int                     i_default_vlan;                    /* default vlan */
+} input_vlan_iface_t;
+
+/*******************************************************************************
+ * vlan_method_data_t
+ *******************************************************************************
+ * Store global vlan library data.
+ *******************************************************************************/
+typedef struct
+{    
+    pthread_mutex_t         lock;                              /* library lock */
+
+    /* Server */
+    input_vlan_server_t     server;                             /* vlan server */
+    /* Network interfaces */
+    int                     i_ifaces;   /* number of vlan-compliant interfaces */
+    input_vlan_iface_t *    p_iface;                             /* interfaces */
+} input_vlan_method_t;
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+int     input_VlanMethodInit    ( input_vlan_method_t *p_method,
+                                  char *psz_server, int i_port);
+void    input_VlanMethodFree    ( input_vlan_method_t *p_method );
+
+int     input_VlanId            ( char *psz_iface, int i_vlan );
+int     input_VlanJoin          ( int i_vlan_id );
+void    input_VlanLeave         ( int i_vlan_id );
+int     input_VlanRequest       ( char *psz_iface );
+int     input_VlanSynchronize   ( void );
+
+
+
diff --git a/include/interface.h b/include/interface.h
new file mode 100644 (file)
index 0000000..d9e444b
--- /dev/null
@@ -0,0 +1,48 @@
+/******************************************************************************
+ * interface.h: interface access for other threads
+ * (c)1999 VideoLAN
+ ******************************************************************************
+ * This library provides basic functions for threads to interact with user
+ * interface, such as message output.
+ ******************************************************************************
+ * Required headers:
+ *  <pthread.h>
+ *  <sys/uio.h>
+ *  <X11/Xlib.h>
+ *  <X11/extensions/XShm.h>
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ *  "input.h"
+ *  "video.h"
+ *  "video_output.h"
+ *  "audio_output.h"
+ *  "xconsole.h"
+ ******************************************************************************/
+
+/******************************************************************************
+ * intf_thread_t: describe an interface thread
+ ******************************************************************************
+ * This structe describes all interface-specific data of the main (interface)
+ * thread.
+ ******************************************************************************/
+typedef struct
+{
+    boolean_t           b_die;                                  /* `die' flag */
+
+    /* Threads control */
+    input_thread_t *    pp_input[INPUT_MAX_THREADS];         /* input threads */
+    vout_thread_t *     pp_vout[VOUT_MAX_THREADS];            /* vout threads */
+    aout_thread_t *     p_aout;                                /* aout thread */
+
+    int                 i_input;                      /* default input thread */
+    int                 i_vout;                      /* default output thread */
+
+    /* Specific interfaces */     
+    xconsole_t          xconsole;                              /* X11 console */
+} intf_thread_t;
+
+/******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+int intf_Run( intf_thread_t * p_intf );
diff --git a/include/intf_cmd.h b/include/intf_cmd.h
new file mode 100644 (file)
index 0000000..8f835c5
--- /dev/null
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * intf_cmd.h: interface commands parsing and executions functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This file implements the interface commands execution functions. It is used
+ * by command-line oriented interfaces and scripts. The commands themselves are
+ * implemented in intf_ctrl.
+ *******************************************************************************
+ * Required headers:
+ *  none
+ *******************************************************************************/
+
+/*******************************************************************************
+ * intf_arg_t: control fonction argument descriptor
+ *******************************************************************************
+ * This structure is used to control an argument type and to transmit
+ * arguments to control functions. It is also used to parse format string and
+ * build an easier to use array of arguments.
+ *******************************************************************************/
+typedef struct
+{
+    /* Argument type */
+    int         i_flags;                            /* argument type and flags */
+    int         i_index;                     /* index of mask in format string */
+
+    /* Converted arguments value */
+    char *      psz_str;                                       /* string value */
+    char *      ps_name;                /* name, can be '\0' or '=' terminated */
+    long        i_num;                                        /* integer value */
+    float       f_num;                                          /* float value */
+} intf_arg_t;
+
+/* Arguments flags */
+#define INTF_STR_ARG            1                           /* string argument */
+#define INTF_INT_ARG            2                          /* integer argument */
+#define INTF_FLOAT_ARG          4                            /* float argument */
+#define INTF_NAMED_ARG          16                           /* named argument */
+#define INTF_OPT_ARG            256                      /* optionnal argument */
+#define INTF_REP_ARG            512                /* argument can be repeated */
+#define INTF_PRESENT_ARG        1024          /* argument has been encountered */
+
+/*******************************************************************************
+ * intf_command_t: control command descriptor
+ *******************************************************************************
+ * This structure describes a control commands. It stores informations needed
+ * for argument type checking, command execution but also a short inline help.
+ * See control.c for more informations about fields.
+ *******************************************************************************/
+typedef struct
+{
+    /* Function control */
+    char *          psz_name;                                  /* command name */
+    int (* function)( int i_argc, intf_arg_t *p_argv );            /* function */
+    char *          psz_format;                            /* arguments format */
+
+    /* Function informations */
+    char *          psz_summary;                                  /* info text */
+    char *          psz_usage;                                   /* usage text */
+    char *          psz_help;                                     /* help text */
+} intf_command_t;
+
+/*******************************************************************************
+ * Error constants
+ *******************************************************************************
+ * These errors should be used as return values for control functions (see
+ * control.c). The intf_ExecCommand function as different behaviour depending
+ * of the error it received. Other errors numbers can be used, but their valued
+ * should be positive to avoid conflict with future error codes.
+ *******************************************************************************/
+
+#define INTF_NO_ERROR       0                                       /* success */
+#define INTF_FATAL_ERROR    -1            /* fatal error: the program will end */
+#define INTF_CRITICAL_ERROR -2        /* critical error: the program will exit */
+#define INTF_USAGE_ERROR    -3 /* usage error: command usage will be displayed */
+#define INTF_OTHER_ERROR    -4  /* other error: command prints its own message */
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+int intf_ExecCommand    ( char *psz_cmd );
+int intf_ExecScript     ( char *psz_filename );
diff --git a/include/intf_ctrl.h b/include/intf_ctrl.h
new file mode 100644 (file)
index 0000000..a18be92
--- /dev/null
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * intf_ctrl.h: interface commands access to control functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Library of functions common to all interfaces, allowing access to various
+ * structures and settings. Interfaces should only use those functions
+ * to read or write informations from other threads.
+ *******************************************************************************
+ * Required headers:
+ *  none
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Extern variables
+ *******************************************************************************/
+extern const intf_command_t control_command[];
+
diff --git a/include/intf_msg.h b/include/intf_msg.h
new file mode 100644 (file)
index 0000000..273bb0d
--- /dev/null
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * intf_msg.h: messages interface
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This library provides basic functions for threads to interact with user
+ * interface, such as message output. If INTF_MSG_QUEUE is defined (which is the
+ * defaul), messages are not printed directly by threads, to bypass console 
+ * limitations and slow printf() calls, but sent to a queue and printed later by
+ * interface thread. 
+ * If INTF_MSG_QUEUE is not defined, output is directly performed on stderr.
+ *******************************************************************************
+ * required headers:
+ *  <pthread.h>
+ *  config.h
+ *  mtime.h
+ *******************************************************************************/
+
+/*******************************************************************************
+ * interface_message_t                                             
+ *******************************************************************************
+ * Store a single message. Messages have a maximal size of INTF_MSG_MSGSIZE.
+ * If DEBUG is defined, messages have a date field and debug messages are
+ * printed with a date to allow more precise profiling.
+ *******************************************************************************/
+typedef struct
+{
+    int     i_type;                                 /* message type, see below */
+    char *  psz_msg;                                     /* the message itself */
+
+#ifdef DEBUG
+    /* Debugging informations - in DEBUG mode, all messages are dated and debug
+     * messages have calling location informations printed */
+    mtime_t date;                        /* date of the message (all messages) */
+    char *  psz_file;                 /* file in which the function was called */
+    char *  psz_function;       /* function from which the function was called */
+    int     i_line;                   /* line at which the function was called */
+#endif
+} interface_msg_message_t;
+
+/* Message types */
+#define INTF_MSG_STD    0                                  /* standard message */
+#define INTF_MSG_ERR    1                                     /* error message */
+#define INTF_MSG_INTF   2                                 /* interface message */
+#define INTF_MSG_DBG    3                                     /* debug message */
+
+/*******************************************************************************
+ * interface_msg_t                                                    
+ *******************************************************************************
+ * Store all data requiered by messages interfaces. It has a singe instance in
+ * program_data.
+ *******************************************************************************/
+typedef struct
+{
+#ifdef INTF_MSG_QUEUE
+    /* Message queue */
+    pthread_mutex_t         lock;                        /* message queue lock */
+    int                     i_count;              /* number of messages stored */
+    interface_msg_message_t msg[INTF_MSG_QSIZE];              /* message queue */
+#endif
+
+#ifdef DEBUG_LOG
+    /* Log file */
+    FILE *                  p_log_file;                            /* log file */
+#endif
+
+#ifndef INTF_MSG_QUEUE
+#ifndef DEBUG_LOG
+    /* If neither messages queue, neither log file is used, then the structure
+     * is empty. However, empty structures are not allowed in C. Therefore, a
+     * dummy integer is used to fill it. */
+    int                     i_dummy;                          /* unused filler */
+#endif
+#endif
+} interface_msg_t;
+
+/*******************************************************************************
+ * intf_DbgMsg macros and functions
+ *******************************************************************************
+ * The intf_DbgMsg* functions are defined as macro to be able to use the 
+ * compiler extensions and print the file, the function and the line number
+ * from which they have been called. They call _intf_DbgMsg*() functions after
+ * having added debugging informations.
+ * Outside DEBUG mode, intf_DbgMsg* functions do nothing.
+ *******************************************************************************/
+#ifdef DEBUG
+
+/* DEBUG mode */
+void    _intf_DbgMsg        ( char *psz_file, char *psz_function, int i_line, 
+                              char *psz_format, ... );
+void    _intf_DbgMsgImm     ( char *psz_file, char *psz_function, int i_line,
+                              char *psz_format, ... );
+
+#define intf_DbgMsg( format, args... ) \
+    _intf_DbgMsg( __FILE__, __FUNCTION__, __LINE__, format, ## args )
+#define intf_DbgMsgImm( format, args... ) \
+    _intf_DbgMsg( __FILE__, __FUNCTION__, __LINE__, format, ## args )
+
+#else
+
+/* Non-DEBUG mode */
+#define intf_DbgMsg( format, args... )      ;
+#define intf_DbgMsgImm( format, args...)    ;
+
+#endif
+
+/*******************************************************************************
+ * intf_FlushMsg macro and function
+ *******************************************************************************
+ * intf_FlushMsg is a function which flushs message queue and print all messages
+ * remaining. It is only usefull if INTF_MSG_QUEUE is defined. In this case, it
+ * is really a function. In the other case, it is a macro doing nothing.
+ *******************************************************************************/
+#ifdef INTF_MSG_QUEUE
+
+/* Message queue mode */
+void    intf_FlushMsg       ( void );
+
+#else
+
+/* Direct mode */
+#define intf_FlushMsg()     ;
+
+#endif
+
+/*******************************************************************************
+ * Prototypes                                                      
+ *******************************************************************************/
+int     intf_InitMsg        ( interface_msg_t *p_intf_msg );
+void    intf_TerminateMsg   ( interface_msg_t *p_intf_msg );
+
+void    intf_Msg            ( char *psz_format, ... );
+void    intf_ErrMsg         ( char *psz_format, ... );
+void    intf_IntfMsg        ( char *psz_format, ... );
+
+void    intf_MsgImm         ( char *psz_format, ... );
+void    intf_ErrMsgImm      ( char *psz_format, ... );
+
+
diff --git a/include/mtime.h b/include/mtime.h
new file mode 100644 (file)
index 0000000..b4ea319
--- /dev/null
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * mtime.h: high rezolution time management functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This header provides portable high precision time management functions,
+ * which should be the only ones used in other segments of the program, since
+ * functions like gettimeofday() and ftime() are not always supported.
+ * Most functions are declared as inline or as macros since they are only
+ * interfaces to system calls and have to be called frequently.
+ * 'm' stands for 'micro', since maximum resolution is the microsecond.
+ * Functions prototyped are implemented in interface/mtime.c.
+ *******************************************************************************
+ * Required headers:
+ *  none
+ * this header includes inline functions
+ *******************************************************************************/
+
+/*******************************************************************************
+ * mtime_t: high precision date or time interval
+ *******************************************************************************
+ * Store an high precision date or time interval. The maximum precision is the
+ * micro-second, and a 64 bits integer is used to avoid any overflow (maximum
+ * time interval is then 584542 years, which should be length enough for any
+ * video). Date are stored as a time interval since a common date.
+ * Note than date and time intervals can be manipulated using regular arithmetic
+ * operators, and that no special functions are required.
+ *******************************************************************************/
+typedef u64 mtime_t;
+
+/*******************************************************************************
+ * LAST_MDATE: date which will never happen
+ *******************************************************************************
+ * This date can be used as a 'never' date, to mark missing events in a function
+ * supposed to return a date, such as nothing to display in a function
+ * returning the date of the first image to be displayed. It can be used in
+ * comparaison with other values: all existing dates will be earlier.
+ *******************************************************************************/
+#define LAST_MDATE          ((mtime_t) -1)
+
+/*******************************************************************************
+ * MSTRTIME_MAX_SIZE: maximum possible size of mstrtime
+ *******************************************************************************
+ * This values is the maximal possible size of the string returned by the
+ * mstrtime() function, including the final '\0'. It should be used to allocate
+ * the buffer.
+ *******************************************************************************/
+#define MSTRTIME_MAX_SIZE   20
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+char *  mstrtime ( char *psz_buffer, mtime_t date );
+mtime_t mdate    ( void );
+void    mwait    ( mtime_t date );
+void    msleep   ( mtime_t delay );
diff --git a/include/netutils.h b/include/netutils.h
new file mode 100644 (file)
index 0000000..626f859
--- /dev/null
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * netutils.h: various network functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This header describe miscellanous utility functions shared between several
+ * modules.
+ *******************************************************************************
+ * Required headers:
+ *  <netinet/in.h>
+ *******************************************************************************/
+
+
+/*******************************************************************************
+ * if_descr_t: describes a network interface.
+ *******************************************************************************
+ * Note that if the interface is a point to point one, the broadcast address is
+ * set to the destination address of that interface
+ *******************************************************************************/
+typedef struct
+{
+    /* Interface device name (e.g. "eth0") */
+    char* psz_ifname;
+    /* Interface physical address */
+    struct sockaddr sa_phys_addr;  
+    /* Interface network address */
+    struct sockaddr_in sa_net_addr;
+    /* Interface broadcast address */
+    struct sockaddr_in sa_bcast_addr;
+    /* Interface flags: see if.h for their description) */
+    u16 i_flags;
+} if_descr_t;
+
+
+/*******************************************************************************
+ * net_descr_t: describes all the interfaces of the computer
+ *******************************************************************************
+ * Nothing special to say :)
+ *******************************************************************************/
+typedef struct
+{
+    /* Number of networks interfaces described below */
+    int i_if_number;
+    /* Table of if_descr_t describing each interface */
+    if_descr_t* a_if;
+} net_descr_t;
+
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+int ReadIfConf      (int i_sockfd, if_descr_t* p_ifdescr, char* psz_name);
+int ReadNetConf     (int i_sockfd, net_descr_t* p_net_descr);
+int BuildInetAddr   ( struct sockaddr_in *p_sa_in, char *psz_in_addr, int i_port );
+int ServerPort      ( char *psz_addr );
+
diff --git a/include/pgm_data.h b/include/pgm_data.h
new file mode 100644 (file)
index 0000000..83d745b
--- /dev/null
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * pgm_data.h: access to all program variables
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This header provides structures to access to all program variables. It should
+ * only be used by interface.
+ *******************************************************************************
+ * Required headers:
+ *  <pthread.h>
+ *  <netinet/in.h>
+ *  <sys/soundcard.h>
+ *  <sys/uio.h>
+ *  <X11/Xlib.h>
+ *  <X11/extensions/XShm.h>
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ *  "input.h"
+ *  "input_vlan.h"
+ *  "audio_output.h"
+ *  "video.h"
+ *  "video_output.h"
+ *  "xconsole.h"
+ *  "interface.h"
+ *  "intf_msg.h"
+ *******************************************************************************/
+
+/*******************************************************************************
+ * main_config_t
+ *******************************************************************************
+ * Store the main configuration (non thread-dependant configuration), such as
+ * parameters read from command line and name of configuration file
+ *******************************************************************************/
+typedef struct
+{
+    boolean_t               b_audio;              /* is audio output allowed ? */
+    boolean_t               b_video;              /* is video output allowed ? */
+    boolean_t               b_vlans;                  /* are vlans supported ? */
+    
+    /* Vlan input method configuration */
+    char *                  psz_input_vlan_server;              /* vlan server */
+    int                     i_input_vlan_server_port;      /* vlan server port */    
+} main_config_t;
+
+/*******************************************************************************
+ * program_data_t, p_program_data (global variable)
+ *******************************************************************************
+ * This structure has an unique instance, declared in main and pointed by the
+ * only global variable of the program. It should allow access to any variable
+ * of the program, for user-interface purposes or more easier call of interface
+ * and common functions (example: the intf_*Msg functions). Please avoid using
+ * it when you can access the members you need in an other way. In fact, it
+ * should only be used by interface thread.
+ *******************************************************************************/
+typedef struct
+{
+    /* Global properties */
+    int                     i_argc;            /* command line arguments count */
+    char **                 ppsz_argv;               /* command line arguments */
+    char **                 ppsz_env;                 /* environment variables */
+    
+    /* Configurations */
+    main_config_t           cfg;                      /* general configuration */
+    video_cfg_t             vout_cfg;            /* video output configuration */
+
+    /* Threads */
+    aout_thread_t           aout_thread;                /* audio output thread */
+    intf_thread_t           intf_thread;                   /* interface thread */    
+
+    /* Shared data - these structures are accessed directly from p_program_data
+     * by several libraries */
+    interface_msg_t         intf_msg;               /* messages interface data */
+    input_vlan_method_t     input_vlan_method;            /* vlan input method */    
+} program_data_t;
+
+extern program_data_t *p_program_data;
+
diff --git a/include/rsc_files.h b/include/rsc_files.h
new file mode 100644 (file)
index 0000000..78fb43b
--- /dev/null
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * rsc_files.h: resources files manipulation functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This library describe a general format used to store 'resources'. Resources
+ * can be anything, including pictures, audio streams, and so on.
+ *******************************************************************************
+ * Requires:
+ *  config.h
+ *  common.h 
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Constants
+ *******************************************************************************/
+
+/* Maximum length of a resource name (not including the final '\0') - this 
+ * constant should not be changed without extreme care */
+#define RESOURCE_MAX_NAME   32
+
+/*******************************************************************************
+ * resource_descriptor_t: resource descriptor
+ *******************************************************************************
+ * This type describe an entry in the resource table.
+ *******************************************************************************/
+typedef struct
+{
+    char    psz_name[RESOURCE_MAX_NAME + 1];                           /* name */
+    u16     i_type;                                                    /* type */
+    u64     i_offset;                                           /* data offset */
+    u64     i_size;                                               /* data size */
+} resource_descriptor_t;
+
+/* Resources types */
+#define EMPTY_RESOURCE      0                          /* empty place in table */
+#define PICTURE_RESOURCE    10                         /* native video picture */
+
+/*******************************************************************************
+ * resource_file_t: resource file descriptor
+ *******************************************************************************
+ * This type describes a resource file and store it's resources table. It can
+ * be used through the *Resource functions, or directly with the i_file field.
+ *******************************************************************************/
+typedef struct
+{
+    /* File informations */
+    int                     i_file;                         /* file descriptor */
+    int                     i_type;                               /* file type */
+    boolean_t               b_up_to_date;              /* is file up to date ? */
+    boolean_t               b_read_only;                     /* read-only mode */
+
+    /* Resources table */
+    int                     i_size;                              /* table size */
+    resource_descriptor_t * p_resource;                     /* resources table */
+} resource_file_t;
+
+/* Resources files types */
+#define VLC_RESOURCE_FILE   0                 /* VideoLAN Client resource file */
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+resource_file_t *   CreateResourceFile  ( char *psz_filename, int i_type, int i_size, int i_mode );
+resource_file_t *   OpenResourceFile    ( char *psz_filename, int i_type, int i_flags );
+int                 UpdateResourceFile  ( resource_file_t *p_file );
+int                 CloseResourceFile   ( resource_file_t *p_file );
+
+int                 SeekResource        ( resource_file_t *p_file, char *psz_name, int i_type );
+int                 ReadResource        ( resource_file_t *p_file, char *psz_name, int i_type,
+                                          size_t max_size, byte_t *p_data );
+int                 WriteResource       ( resource_file_t *p_file, char *psz_name, int i_type, 
+                                          size_t size, byte_t *p_data );
+
diff --git a/include/thread.h b/include/thread.h
new file mode 100644 (file)
index 0000000..e09d48d
--- /dev/null
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * thread.h: threads status constants
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * These constants are used by all threads in *_CreateThread() and 
+ * *_DestroyThreads() functions. Since those calls are non-blocking, an integer
+ * value is used as a shared flag to represent the status of the thread.
+ *******************************************************************************
+ * Requires:
+ *  none
+ *******************************************************************************/
+
+/* Void status - this value can be used to be sure, in an array of recorded
+ * threads, that no operation is currently in progress on the concerned thread */
+#define THREAD_NOP          0                              /* nothing happened */
+
+/* Creation status */
+#define THREAD_CREATE       10                       /* thread is initializing */
+#define THREAD_START        11                            /* thread has forked */
+#define THREAD_READY        19                              /* thread is ready */
+
+/* Destructions status */
+#define THREAD_DESTROY      20              /* destruction order has been sent */
+#define THREAD_END          21          /* destruction order has been received */
+#define THREAD_OVER         29               /* thread does not exist any more */
+
+/* Error status */
+#define THREAD_ERROR        30                             /* an error occured */
+#define THREAD_FATAL        31    /* an fatal error occured - program must end */
+
+
+
+
diff --git a/include/video.h b/include/video.h
new file mode 100644 (file)
index 0000000..ef64123
--- /dev/null
@@ -0,0 +1,188 @@
+/*******************************************************************************
+ * video.h: common video definitions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This header is required by all modules which have to handle pictures. It
+ * includes all common video types and constants.
+ *******************************************************************************
+ * Requires:
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ *******************************************************************************/
+
+/*******************************************************************************
+ * pixel_t: universal pixel value descriptor
+ *******************************************************************************
+ * This type and associated macros and functions are provided as an universal
+ * way of storing colors/pixels parameters. For pixels, it represents the
+ * actual value of the pixel. For RGB values, it is a 24 bits RGB encoded
+ * value. For masks, it is 0 or 1...
+ *******************************************************************************/
+typedef u32 pixel_t;
+
+#define RGBVALUE2RED( value )   (  (value)        & 0x0000ff )
+#define RGBVALUE2GREEN( value ) ( ((value) >> 8)  & 0x0000ff )
+#define RGBVALUE2BLUE( value )  ( ((value) >> 16) & 0x0000ff )
+
+/*******************************************************************************
+ * picture_t: video picture                                            
+ *******************************************************************************
+ * Any picture destined to be displayed by a video output thread should be 
+ * stored in this structure from it's creation to it's effective display.
+ * Two forms of pictures exists: independant pictures, which can be manipulated
+ * freely, although usage of graphic library is recommanded, and heap pictures.
+ * Extreme care should be taken when manipulating heap pictures, since any error
+ * could cause a segmentation fault in the video output thread. The rule is:
+ * once a picture is in the video heap, only it's data can be written. All other
+ * fields should only be read or modified using interface functions.
+ * Note that for all pictures, some properties should never be modified, except
+ * by the video output thread itself, once the picture has been created !
+ *******************************************************************************/
+typedef struct
+{
+    /* Type and flags - should NOT be modified except by the vout thread */
+    int             i_type;                                    /* picture type */
+    int             i_flags;                                  /* picture flags */
+    
+    /* Picture properties - those properties are fixed at initialization and 
+     * should NOT be modified */
+    int             i_width;                                  /* picture width */
+    int             i_height;                                /* picture height */
+    int             i_bpp;                            /* padded bits per pixel */
+    int             i_bytes_per_line;        /* total number of bytes per line */
+    
+    /* Picture properties - those properties can be modified is the picture is
+     * independant, or in a heap but permanent or reserved */
+    int             i_x;                 /* x position offset in output window */
+    int             i_y;                 /* y position offset in output window */
+    int             i_h_align;                         /* horizontal alignment */
+    int             i_v_align;                           /* vertical alignment */
+    int             i_h_ratio;                     /* horizontal display ratio */
+    int             i_v_ratio;                       /* vertical display ratio */
+    int             i_level;                     /* overlay hierarchical level */
+
+    /* Link reference counter - it can be modified using vout_Link and 
+     * vout_Unlink functions, or directly if the picture is independant */
+    int             i_refcount;                      /* link reference counter */
+
+    /* Video properties - those properties should not be modified once 
+     * the picture is in a heap, but can be freely modified if it is 
+     * independant */
+    int             i_stream;                               /* video stream id */
+    mtime_t         date;                                      /* display date */
+    mtime_t         duration;                 /* duration for overlay pictures */
+
+    /* Picture data - data can always be freely modified, although special care
+     * should be taken for permanent pictures to avoid flickering - p_data 
+     * itself (the pointer) should NEVER be modified */   
+    pixel_t         pixel;                   /* pixel value, for mask pictures */
+    byte_t *        p_data;                                    /* picture data */
+} picture_t;
+
+/* Pictures types */
+#define EMPTY_PICTURE           0             /* picture is waiting to be used */
+#define RGB_BLANK_PICTURE       10       /* blank picture (rgb color, no data) */
+#define PIXEL_BLANK_PICTURE     11     /* blank picture (pixel color, no data) */
+#define RGB_PICTURE             20           /* picture is 24 bits rgb encoded */
+#define PIXEL_PICTURE           30                 /* picture is pixel encoded */
+#define RGB_MASK_PICTURE        40              /* picture is a 1 bpp rgb mask */
+#define PIXEL_MASK_PICTURE      41            /* picture is a 1 bpp pixel mask */
+
+/* ?? */
+#define YUV_444_PICTURE         100                  /* chroma 444 YUV picture */
+#define YUV_422_PICTURE         101                  /* chroma 422 YUV picture */
+#define YUV_420_PICTURE         102                  /* chroma 420 YUV picture */
+
+/* Pictures properties (flags) */
+#define RESERVED_PICTURE        (1 << 0)  /* picture is not ready but reserved */
+#define PERMANENT_PICTURE       (1 << 1)               /* picture is permanent */
+#define DISPLAYED_PICTURE       (1 << 2)         /* picture has been displayed */
+#define OWNER_PICTURE           (1 << 3)              /* picture owns its data */
+#define DISPLAY_PICTURE                (1 << 4)          /* picture will be displayed */
+#define DESTROY_PICTURE                (1 << 5)          /* picture will be destroyed */
+#define TRANSPARENT_PICTURE     (1 << 8)             /* picture is transparent */
+#define OVERLAY_PICTURE                (1 << 9)       /* picture overlays another one */
+
+/* Alignments - this field describe how the position of the picture will
+ * be calculated */
+#define ALIGN_LEFT              -1                             /* left-aligned */
+#define ALIGN_TOP               -1                               /* up-aligned */
+#define ALIGN_ENTER             0                                  /* centered */
+#define ALIGN_RIGHT             1                             /* right-aligned */
+#define ALIGN_BOTTOM            1                            /* bottom-aligned */
+#define ALIGN_H_DEFAULT         ALIGN_LEFT     /* default horizontal alignment */
+#define ALIGN_V_DEFAULT            ALIGN_TOP        /* default vertical alignment */
+
+/* Display ratios - this field describe how the image will be resized before
+ * being displayed */
+#define DISPLAY_RATIO_HALF      -1                            /* 1:2 half size */
+#define DISPLAY_RATIO_NORMAL    0                           /* 1:1 normal size */
+#define DISPLAY_RATIO_DOUBLE    1                           /* 2:1 double size */
+/* ?? add other ratios (TRIPLE, THIRD), TV, automatic, ... */
+
+/*******************************************************************************
+ * video_cfg_t: video object configuration structure
+ *******************************************************************************
+ * This structure is passed as a parameter to many initialization function of
+ * the vout and vdec modules. It includes many fields describing potential 
+ * properties of a new object. The 'i_properties' field allow to set only a 
+ * subset of the required properties, asking the called function to use default 
+ * settings for the other ones.
+ *******************************************************************************/
+typedef struct video_cfg_s
+{
+    u64         i_properties;                               /* used properties */
+
+    /* Size properties */
+    int         i_width;                              /* image or window width */
+    int         i_height;                            /* image or window height */
+    int         i_size;                                           /* heap size */
+
+    /* X11 properties */
+    char *      psz_display;                                   /* display name */
+    char *      psz_title;                                     /* window title */
+    boolean_t   b_shm_ext;                        /* try to use XShm extension */
+
+    /* Pictures properties */
+    int         i_type;                                        /* picture type */
+    int         i_flags;                                      /* picture flags */
+    int         i_bpp;                                /* padded bits per pixel */
+    int         i_x;                     /* x position offset in output window */
+    int         i_y;                     /* y position offset in output window */
+    int         i_h_align;                             /* horizontal alignment */
+    int         i_v_align;                               /* vertical alignment */
+    int         i_h_ratio;                         /* horizontal display ratio */
+    int         i_v_ratio;                           /* vertical display ratio */
+    int         i_level;                         /* overlay hierarchical level */
+    int         i_refcount;                          /* link reference counter */
+    int         i_stream;                                   /* video stream id */
+    mtime_t     date;                                  /* picture display date */
+    mtime_t     duration;                     /* duration for overlay pictures */
+    pixel_t     pixel;                       /* pixel value, for mask pictures */
+    byte_t *    p_data;                                        /* picture data */
+} video_cfg_t;
+
+/* Properties flags (see picture_t and other video structures for 
+ * explanations) */
+#define VIDEO_CFG_WIDTH            (1 << 0)
+#define VIDEO_CFG_HEIGHT    (1 << 1)
+#define VIDEO_CFG_SIZE      (1 << 2)
+
+#define VIDEO_CFG_DISPLAY   (1 << 4)
+#define VIDEO_CFG_TITLE     (1 << 5)
+#define VIDEO_CFG_SHM_EXT   (1 << 6)
+
+#define VIDEO_CFG_TYPE      (1 << 8)
+#define VIDEO_CFG_FLAGS     (1 << 9)
+#define VIDEO_CFG_BPP       (1 << 10)
+#define VIDEO_CFG_POSITION  (1 << 11)                      /* both i_x and i_y */
+#define VIDEO_CFG_ALIGN     (1 << 12)          /* both i_h_align and i_v_align */
+#define VIDEO_CFG_RATIO     (1 << 13)          /* both i_h_ratio and i_y_ratio */
+#define VIDEO_CFG_LEVEL     (1 << 14)
+#define VIDEO_CFG_REFCOUNT  (1 << 15)
+#define VIDEO_CFG_STREAM    (1 << 16)
+#define VIDEO_CFG_DATE      (1 << 17)
+#define VIDEO_CFG_DURATION  (1 << 18)
+#define VIDEO_CFG_PIXEL     (1 << 19)
+#define VIDEO_CFG_DATA      (1 << 20)
diff --git a/include/video_decoder.h b/include/video_decoder.h
new file mode 100644 (file)
index 0000000..cb3bdd5
--- /dev/null
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * video_decoder.h : video decoder thread
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ *******************************************************************************
+ * Requires:
+ *  <pthread.h>
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ *  "input.h"
+ *  "video.h"
+ *  "video_output.h"
+ *  "decoder_fifo.h"
+ *******************************************************************************/
+
+/*******************************************************************************
+ * vdec_thread_t: video decoder thread descriptor
+ *******************************************************************************
+ * ??
+ *******************************************************************************/
+typedef struct vdec_thread_s
+{
+    /* Thread properties and locks */
+    boolean_t           b_die;                                   /* `die' flag */
+    boolean_t           b_run;                                   /* `run' flag */
+    boolean_t           b_error;                               /* `error' flag */
+    boolean_t           b_active;                             /* `active' flag */
+    pthread_t           thread_id;                 /* id for pthread functions */
+
+    /* Thread configuration */
+    /* ?? */
+ /*??*/
+    int *pi_status;
+    
+
+    /* Input properties */
+    input_thread_t *    p_input;                               /* input thread */
+    decoder_fifo_t      fifo;                                /* PES input fifo */
+
+    /* Output properties */
+    vout_thread_t *     p_vout;                         /* video output thread */
+    int                 i_stream;                           /* video stream id */
+    
+        
+#ifdef STATS
+    /* Statistics */
+    count_t         c_loops;                               /* number of loops */
+    count_t         c_idle_loops;                     /* number of idle loops */
+    count_t         c_pictures;                    /* number of pictures read */
+    count_t         c_i_pictures;                /* number of I pictures read */
+    count_t         c_p_pictures;                /* number of P pictures read */
+    count_t         c_b_pictures;                /* number of B pictures read */    
+    count_t         c_decoded_pictures;         /* number of pictures decoded */
+    count_t         c_decoded_i_pictures;     /* number of I pictures decoded */
+    count_t         c_decoded_p_pictures;     /* number of P pictures decoded */
+    count_t         c_decoded_b_pictures;     /* number of B pictures decoded */
+#endif
+} vdec_thread_t;
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+
+/* Thread management functions */
+vdec_thread_t * vdec_CreateThread       ( video_cfg_t *p_cfg, input_thread_t *p_input,
+                                          vout_thread_t *p_vout, int *pi_status );
+void             vdec_DestroyThread      ( vdec_thread_t *p_vdec, int *pi_status );
+
+/* Time management functions */
+/* ?? */
+
+/* Dynamic thread settings */
+/* ?? */
diff --git a/include/video_graphics.h b/include/video_graphics.h
new file mode 100644 (file)
index 0000000..e15e08b
--- /dev/null
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * video_graphics.h: pictures manipulation primitives
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Includes function to compose, convert and display pictures, and also basic
+ * functions to copy pictures data or descriptors. 
+ *******************************************************************************
+ * Requires:
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ *  "video.h"
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+
+/* Pictures management functions */
+picture_t * video_CreatePicture         ( video_cfg_t *p_cfg );
+picture_t * video_CopyPicture           ( picture_t *p_pic );
+picture_t * video_ReplicatePicture      ( picture_t *p_pic );
+void        video_DestroyPicture        ( picture_t *p_pic );
+
+/* Files functions */
+picture_t * video_ReadPicture           ( int i_file );
+
+/* Drawing functions */
+void        video_ClearPicture          ( picture_t *p_pic );
+void        video_DrawPixel             ( picture_t *p_pic, int i_x, int i_y, pixel_t value );
+void        video_DrawHLine             ( picture_t *p_pic, int i_x, int i_y, int i_width, pixel_t value );
+void        video_DrawVLine             ( picture_t *p_pic, int i_x, int i_y, int i_height, pixel_t value );
+void        video_DrawLine              ( picture_t *p_pic, int i_x1, int i_y1, 
+                                          int i_x2, int i_y2, pixel_t value );
+void        video_DrawBar               ( picture_t *p_pic, int i_x, int i_y, int i_width, 
+                                          int i_height, pixel_t value );
+void        video_DrawRectangle         ( picture_t *p_pic, int i_x, int i_y, 
+                                          int i_width, int i_height, pixel_t color );
+void        video_DrawPicture           ( picture_t *p_pic, picture_t *p_insert, int i_x, int i_y );
+void        video_DrawText              ( picture_t *p_pic, int i_x, int i_y, char *psz_text, 
+                                          int i_size, pixel_t color );
+
+/* Convertion functions */
+/* ?? rgb->pixel, pixel->rgb */
+
+/* Low-level shared functions */
+void        video_CopyPictureDescriptor ( picture_t *p_dest, picture_t *p_src );
+int         video_CreatePictureBody     ( picture_t *p_pic, video_cfg_t *p_cfg );
+
+#ifdef DEBUG
+/* Debugging functions */
+void        video_PrintPicture          ( picture_t *p_pic, char *psz_str );
+#endif
diff --git a/include/video_output.h b/include/video_output.h
new file mode 100644 (file)
index 0000000..43f1860
--- /dev/null
@@ -0,0 +1,184 @@
+/*******************************************************************************
+ * video_output.h : video output thread
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This module describes the programming interface for video output threads.
+ * It includes functions allowing to open a new thread, send pictures to a
+ * thread, and destroy a previously oppenned video output thread.
+ *******************************************************************************
+ * Requires:
+ *  <pthread.h>
+ *  "config.h"
+ *  "common.h"
+ *  "mtime.h"
+ *  "video.h"
+ *******************************************************************************/
+
+/* ?? this over-complicated API and code should be re-designed, with a simple
+ * video-stream associated to a window (each window designed to be openned in
+ * a parent one and probably without border), and have an api looking like
+ * vout_CreateWindow
+ * vout_DestroyWindow
+ * vout_AddPicture
+ * vout_RemovePicture
+ * vout_ReservePicture
+ * vout_AddReservedPicture
+ * vout_Clear
+ * vout_Refresh
+ * 
+ * the overlay/transparent, permanent and such stuff should disapear.
+ */
+
+/*******************************************************************************
+ * vout_stream_t: video stream descriptor
+ *******************************************************************************
+ * Each video stream has a set of properties, stored in this structure. It is
+ * part of vout_thread_t and is not supposed to be used anywhere else.
+ *******************************************************************************/
+typedef struct
+{
+    int                 i_status;                        /* is stream active ? */
+    picture_t *         p_next_picture;        /* next picture to be displayed */
+
+#ifdef STATS
+    /* Statistics */
+    count_t             c_pictures;          /* total number of pictures added */
+    count_t             c_rendered_pictures;    /* number of rendered pictures */
+#endif
+} vout_stream_t;
+
+/* Video stream status */
+#define VOUT_INACTIVE_STREAM    0                /* stream is inactive (empty) */
+#define VOUT_ACTIVE_STREAM      1                          /* stream is active */
+#define VOUT_ENDING_STREAM      2       /* stream will be destroyed when empty */
+#define VOUT_DESTROYED_STREAM   3                  /* stream must be destroyed */
+
+/*******************************************************************************
+ * vout_thread_t: video output thread descriptor
+ *******************************************************************************
+ * Any independant video output device, such as an X11 window, is represented
+ * by a video output thread, and described using following structure.
+ *******************************************************************************/
+typedef struct vout_thread_s
+{
+    /* Thread properties and locks */
+    boolean_t           b_die;                                   /* `die' flag */
+    boolean_t           b_error;                               /* `error' flag */
+    boolean_t           b_active;                             /* `active' flag */
+    pthread_t           thread_id;                 /* id for pthread functions */
+    pthread_mutex_t     streams_lock;             /* streams modification lock */
+    pthread_mutex_t     pictures_lock;           /* pictures modification lock */
+    int *               pi_status;                    /* temporary status flag */
+
+    /* Common display properties */
+    int                 i_width;                /* current output method width */
+    int                 i_height;              /* current output method height */
+    int                 i_screen_depth;                      /* bits per pixel */
+    int                 i_bytes_per_pixel;                /* real screen depth */
+
+    /* Output method */
+    struct vout_x11_s * p_x11;                            /* X11 output method */
+
+    /* Video heap */
+    int                 i_max_pictures;                   /* heap maximal size */
+    int                 i_pictures;                       /* current heap size */
+    picture_t *         p_picture;                                 /* pictures */
+
+    /* Streams data */
+    vout_stream_t       p_stream[VOUT_MAX_STREAMS];            /* streams data */
+
+#ifdef STATS    
+    /* Statistics */
+    count_t         c_loops;                               /* number of loops */
+    count_t         c_idle_loops;                     /* number of idle loops */
+    count_t         c_pictures;           /* number of pictures added to heap */
+    count_t         c_rendered_pictures;       /* number of pictures rendered */
+#endif
+
+    /* Rendering functions - these functions are of vout_render_blank_t and 
+     * vout_render_line_t, but are not declared here using these types since
+     * they require vout_thread_t to be defined */
+    void (* RenderRGBBlank)         ( struct vout_thread_s *p_vout, pixel_t pixel,
+                                      int i_x, int i_y, int i_width, int i_height );
+    void (* RenderPixelBlank)       ( struct vout_thread_s *p_vout, pixel_t pixel,
+                                      int i_x, int i_y, int i_width, int i_height );
+    void (* RenderRGBLine)          ( struct vout_thread_s *p_vout, picture_t *p_pic,
+                                      int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                      int i_width, int i_line_width, int i_ratio ); 
+    void (* RenderPixelLine)        ( struct vout_thread_s *p_vout, picture_t *p_pic,
+                                      int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                      int i_width, int i_line_width, int i_ratio ); 
+    void (* RenderRGBMaskLine)      ( struct vout_thread_s *p_vout, picture_t *p_pic,
+                                      int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                      int i_width, int i_line_width, int i_ratio ); 
+    void (* RenderPixelMaskLine)    ( struct vout_thread_s *p_vout, picture_t *p_pic,
+                                      int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                      int i_width, int i_line_width, int i_ratio ); 
+    /* ?? add YUV types */
+} vout_thread_t;
+
+/*******************************************************************************
+ * vout_render_blank_t: blank rendering function
+ * vout_render_line_t: rectangle rendering functions
+ *******************************************************************************
+ * All rendering functions should be of these types - for blank pictures
+ * (pictures with uniform color), blank rendering functions are called once. For
+ * other pictures, each function is called once for each picture line. Note that
+ * the part of the picture sent to the rendering functions is in the output
+ * window, since the clipping is done before. 
+ *  p_vout is the calling thread
+ *  pixel is the color or pixel value of the rectange to be drawn
+ *  p_pic is the picture to be rendered
+ *  i_x, i_y is the absolute position in output window
+ *  i_pic_x is the first pixel to be drawn in the picture
+ *  i_pic_y is the line of the picture to be drawn
+ *  i_width is the width of the area to be rendered in the picture (not in the
+ *      output window), except for blank pictures, where it is the absolute size
+ *      of the area to be rendered
+ *  i_height is the height og the area to be rendered
+ *  i_line_width is the number of time the line must be copied
+ *  i_ratio is the horizontal display ratio
+ *******************************************************************************/
+typedef void (vout_render_blank_t)( vout_thread_t *p_vout, pixel_t pixel,
+                                      int i_x, int i_y, int i_width, int i_height );
+typedef void (vout_render_line_t) ( vout_thread_t *p_vout, picture_t *p_pic,
+                                      int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                      int i_width, int i_line_width, int i_ratio );
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+vout_thread_t * vout_CreateThread               ( video_cfg_t *p_cfg, int *pi_status );
+void            vout_DestroyThread              ( vout_thread_t *p_vout, int *pi_status );
+
+picture_t *     vout_DisplayPicture             ( vout_thread_t *p_vout, picture_t *p_pic );
+picture_t *     vout_DisplayPictureCopy         ( vout_thread_t *p_vout, picture_t *p_pic );
+picture_t *     vout_DisplayPictureReplicate    ( vout_thread_t *p_vout, picture_t *p_pic );
+picture_t *     vout_DisplayReservedPicture     ( vout_thread_t *p_vout, picture_t *p_pic );
+
+picture_t *     vout_CreateReservedPicture      ( vout_thread_t *p_vout, video_cfg_t *p_cfg );
+picture_t *     vout_ReservePicture             ( vout_thread_t *p_vout, picture_t *p_pic );
+picture_t *     vout_ReservePictureCopy         ( vout_thread_t *p_vout, picture_t *p_pic );
+picture_t *     vout_ReservePictureReplicate    ( vout_thread_t *p_vout, picture_t *p_pic );
+void            vout_RemovePicture              ( vout_thread_t *p_vout, picture_t *p_pic );
+
+void            vout_RefreshPermanentPicture    ( vout_thread_t *p_vout, picture_t *p_pic, 
+                                                  mtime_t displa_date );
+
+void            vout_LinkPicture                ( vout_thread_t *p_vout, picture_t *p_pic );
+void            vout_UnlinkPicture              ( vout_thread_t *p_vout, picture_t *p_pic );
+
+int             vout_CreateStream               ( vout_thread_t *p_vout );
+void            vout_EndStream                  ( vout_thread_t *p_vout, int i_stream );
+void            vout_DestroyStream              ( vout_thread_t *p_vout, int i_stream );
+
+#ifdef DEBUG
+void            vout_PrintHeap                  ( vout_thread_t *p_vout, char *psz_str );
+#endif
+
+
+
+
+
+
+
diff --git a/include/video_x11.h b/include/video_x11.h
new file mode 100644 (file)
index 0000000..8bf7d9d
--- /dev/null
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * video_x11.h: X11 video output display method
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * The X11 method for video output thread. The functions declared here should 
+ * not be needed by any other module than video_output.c.
+ *******************************************************************************
+ * Required headers:
+ * <pthread.h>
+ * <X11/Xlib.h>
+ * <X11/Xutil.h>
+ * <X11/extensions/XShm.h>
+ * "config.h"
+ * "common.h"
+ * "mtime.h"
+ * "video.h"
+ * "video_output.h"
+ *******************************************************************************/
+
+/*******************************************************************************
+ * vout_x11_t: video output X11 method descriptor
+ *******************************************************************************
+ * This structure is part of the video output thread descriptor.
+ * It describes the X11 specific properties of an output thread. X11 video 
+ * output is performed through regular resizable windows. Windows can be
+ * dynamically resized to adapt to the size of the streams.
+ *******************************************************************************/
+typedef struct vout_x11_s
+{
+    /* User settings */
+    boolean_t           b_shm_ext;             /* shared memory extension flag */
+
+    /* Thread configuration - these properties are copied from a video_cfg_t 
+     * structure to be used in second step of initialization */
+    char *              psz_display;                           /* display name */
+    char *              psz_title;                             /* window title */
+
+    /* Internal settings and properties */
+    Display *           p_display;                          /* display pointer */
+    int                 i_screen;                             /* screen number */
+    Window              window;                     /* window instance handler */
+    GC                  gc;                /* graphic context instance handler */    
+
+    /* Window manager hints and atoms */
+    Atom                wm_protocols;                     /* WM_PROTOCOLS atom */
+    Atom                wm_delete_window;             /* WM_DELETE_WINDOW atom */
+
+    /* Color maps and translation tables - some of those tables are shifted,
+     * see x11.c for more informations. */
+    u8 *                trans_16bpp_red;           /* red (16 bpp) (SHIFTED !) */
+    u8 *                trans_16bpp_green;       /* green (16 bpp) (SHIFTED !) */
+    u8 *                trans_16bpp_blue;         /* blue (16 bpp) (SHIFTED !) */
+
+    /* ?? colormaps ? */
+    boolean_t           b_private_colormap;          /* private color map flag */
+    Colormap            private_colormap;                 /* private color map */
+
+    /* Display buffers and shared memory information */
+    int                 i_buffer_index;                        /* buffer index */
+    XImage *            p_ximage[2];                         /* XImage pointer */   
+    XShmSegmentInfo     shm_info[2];         /* shared memory zone information */
+
+    int                 i_completion_type;                               /* ?? */
+} vout_x11_t;
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+int     vout_X11AllocOutputMethod   ( vout_thread_t *p_vout, video_cfg_t *p_cfg );
+void    vout_X11FreeOutputMethod    ( vout_thread_t *p_vout );    
+int     vout_X11CreateOutputMethod  ( vout_thread_t *p_vout );
+void    vout_X11DestroyOutputMethod ( vout_thread_t *p_vout );
+int     vout_X11ManageOutputMethod  ( vout_thread_t *p_vout );
+void    vout_X11DisplayOutput       ( vout_thread_t *p_vout );
+
+
diff --git a/include/xconsole.h b/include/xconsole.h
new file mode 100644 (file)
index 0000000..e353383
--- /dev/null
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * xconsole.h: X11 console for interface
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * The X11 console is a simple way to get interactive input from the user. It
+ * does not disturbs the standard terminal output. In theory, multiple consoles
+ * could be openned on different displays.
+ *?? will probably evolve
+ *******************************************************************************/
+
+/*******************************************************************************
+ * xconsole_t: X11 console descriptor
+ *******************************************************************************
+ * The display pointer is specific to this structure since in theory, multiple
+ * console could be openned on different displays. A console is divided in two
+ * sections. The lower one is a single line edit control. Above, a multi-line
+ * output zone allow to send messages.
+ *******************************************************************************/
+typedef struct
+{
+    /* Initialization fields - those fields should be initialized before 
+     * calling intf_OpenX11Console(). */
+    char *                  psz_display;                       /* display name */
+    char *                  psz_geometry;                   /* window geometry */    
+
+    /* following fields are internal */
+    
+    /* Settings and display properties */
+    Display *               p_display;                      /* display pointer */
+    int                     i_screen;                         /* screen number */
+    XFontStruct *           p_font;                               /* used font */    
+    Window                  window;                 /* window instance handler */
+
+    /* Graphic contexts */
+    GC                      default_gc;    /* graphic context for default text */
+
+    /* Pixmaps */
+    Pixmap                  background_pixmap;            /* window background */
+
+    /* Window properties */
+    int                     i_width, i_height;            /* window dimensions */ 
+    int                     i_text_offset;  /* text zone placement from bottom */
+    int                     i_text_line_height;/* height of a single text line */
+    int                     i_edit_height;           /* total edit zone height */
+    int                     i_edit_offset;  /* edit zone placement from bottom */
+
+    /* Text array */
+    char *                  psz_text[INTF_XCONSOLE_MAX_LINES];         /* text */    
+    int                     i_text_index;                   /* last line index */
+
+    /* Edit lines properties. The line has one more character than
+     * maximum width to allow adding a terminal '\0' when it is sent to 
+     * execution or text zone. The size must stay between 0 (included) and 
+     * INTF_X11_CONSOLE_MAX_LINE_WIDTH (included). The cursor position (index)
+     * can be between 0 (included) and size (included). */
+    char                    sz_edit[INTF_XCONSOLE_MAX_LINE_WIDTH + 1]; 
+    int                     i_edit_index;                   /* cursor position */
+    int                     i_edit_size;            /* total size of edit text */
+
+    /* History. The history array (composed of asciiz strings) has a base, 
+     * marking the *next* registered line, and an index, marking the actual
+     * line browsed. When an history browse is started, the current line is
+     * stored at base (but base isn't increased), and index is modified.
+     * When a command is executed, it is registered at base and base is
+     * increased. */
+    char *                  psz_history[INTF_XCONSOLE_HISTORY_SIZE + 1]; 
+    int                     i_history_index;               /* index in history */
+    int                     i_history_base;                    /* history base */
+} xconsole_t;
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+int  intf_OpenXConsole      ( xconsole_t *p_console );
+void intf_CloseXConsole     ( xconsole_t *p_console );
+
+void intf_ManageXConsole    ( xconsole_t *p_console );
+void intf_ClearXConsole     ( xconsole_t *p_console );
+void intf_PrintXConsole     ( xconsole_t *p_console, char *psz_str );
diff --git a/include/xutils.h b/include/xutils.h
new file mode 100644 (file)
index 0000000..8418048
--- /dev/null
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * xutils.h: X11 utilities
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This library includes many usefull functions to perform simple operations
+ * using Xlib.
+ *******************************************************************************
+ * Required headers:
+ *  <X11/Xlib.h>
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Prototypes
+ *******************************************************************************/
+int     XTryLoadFont( Display *p_display, char *psz_font, XFontStruct **p_font );
+void    XEnableScreenSaver( Display *p_display );
+void    XDisableScreenSaver( Display *p_display );
diff --git a/lib/background.xpm b/lib/background.xpm
new file mode 100644 (file)
index 0000000..0ee02c6
--- /dev/null
@@ -0,0 +1,162 @@
+/* XPM */
+static char * background_xpm[] = {
+"342 47 112 2",
+"      c None",
+".     c #969694",
+"+     c #CECECC",
+"@     c #B2B2B4",
+"#     c #EAEAEC",
+"$     c #C2C2C4",
+"%     c #DEDEDC",
+"&     c #D6D6D4",
+"*     c #A6A6A4",
+"=     c #BABABC",
+"-     c #C2C2E4",
+";     c #CECEEC",
+">     c #E6E6E4",
+",     c #F6F6F4",
+"'     c #AEAEAC",
+")     c #CACACC",
+"!     c #D6D6EC",
+"~     c #BABAD4",
+"{     c #9E9E9C",
+"]     c #B2B2CC",
+"^     c #DEDEF4",
+"/     c #F2F2F4",
+"(     c #C2C2D4",
+"_     c #CACAE4",
+":     c #CECEDC",
+"<     c #A6A6B4",
+"[     c #B6B6B4",
+"}     c #EEEEEC",
+"|     c #BEBEBC",
+"1     c #D2D2EC",
+"2     c #DADAEC",
+"3     c #9E9EAC",
+"4     c #E2E2E4",
+"5     c #DADADC",
+"6     c #C6C6E4",
+"7     c #AEAEC4",
+"8     c #BEBED4",
+"9     c #9A9A9C",
+"0     c #C6C6C4",
+"a     c #E6E6F4",
+"b     c #FEFEFC",
+"c     c #B6B6CC",
+"d     c #E2E2F4",
+"e     c #C6C6D4",
+"f     c #D2D2DC",
+"g     c #AAAAB4",
+"h     c #D2D2D4",
+"i     c #B2B2C4",
+"j     c #EAEAFC",
+"k     c #DEDEEC",
+"l     c #D6D6E4",
+"m     c #AAAAAC",
+"n     c #BABACC",
+"o     c #CECEFC",
+"p     c #FAFAFC",
+"q     c #AEAEBC",
+"r     c #CACADC",
+"s     c #D6D6FC",
+"t     c #A2A2A4",
+"u     c #CACAF4",
+"v     c #B6B6C4",
+"w     c #EEEEFC",
+"x     c #BEBECC",
+"y     c #D2D2FC",
+"z     c #DADAFC",
+"A     c #A2A2AC",
+"B     c #C6C6F4",
+"C     c #BEBEE4",
+"D     c #B6B6DC",
+"E     c #96969C",
+"F     c #CECED4",
+"G     c #B2B2BC",
+"H     c #EAEAF4",
+"I     c #C2C2CC",
+"J     c #DEDEE4",
+"K     c #D6D6DC",
+"L     c #A6A6AC",
+"M     c #BABAC4",
+"N     c #C2C2EC",
+"O     c #CECEF4",
+"P     c #E6E6EC",
+"Q     c #F6F6FC",
+"R     c #AEAEB4",
+"S     c #CACAD4",
+"T     c #D6D6F4",
+"U     c #BABADC",
+"V     c #9E9EA4",
+"W     c #DEDEFC",
+"X     c #F2F2FC",
+"Y     c #C2C2DC",
+"Z     c #CACAEC",
+"`     c #CECEE4",
+" .    c #A6A6BC",
+"..    c #B6B6BC",
+"+.    c #EEEEF4",
+"@.    c #BEBEC4",
+"#.    c #D2D2F4",
+"$.    c #DADAF4",
+"%.    c #E2E2EC",
+"&.    c #DADAE4",
+"*.    c #C6C6EC",
+"=.    c #AEAECC",
+"-.    c #BEBEDC",
+";.    c #9A9AA4",
+">.    c #C6C6CC",
+",.    c #E6E6FC",
+"'.    c #B6B6D4",
+").    c #E2E2FC",
+"!.    c #C6C6DC",
+"~.    c #D2D2E4",
+"{.    c #AAAABC",
+"].    c #A2A2B4",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b w ).).W X b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b u w b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b j r  .n b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b j l M v g ;.9 ;.9 ;.9 9 ;.9 9 9 9 V G G : %.X b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b R 9 9 m b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b w u u B B B u B u o j b b b b b b b b b b b b b b b b b b b b b b b b b b b b w B B s b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b p i  . .< ! b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b 2 M V V { 9 ;.9 9 9 ;.9 ;.9 9 9 9 9 9 9 9 9 9 9 9 E 9 9 V G k X b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b f R A 9 9 9 9 9 g g H b b b b b b b b b b b b b b b b b b b b 9 . . . E 9 S b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b o B w b X u B B B B u u #./ b b b b b b b b b b b b b b b b b b b b b b b b b b ).u B u ,., b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b $. .< < < q # # / b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b w n V 9 ;.9 9 ;.V 9 ;.9 9 9 9 V g g g M @.@.x @.x I I = g g g V 9 9 9 9 9 G J b b b b b b b b b b b b b b b b b b b b b b b b I V . 9 9 . . . . . 9 9 . 9 G b b b b b b b b b b b b b b b b b b 9 E . . . . . 9 @.# p b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b o o b b b Q # O u B B B B B 6 % +.b b b b b b b b b b b b b b b b b b b b b b b b W B B B ` 5 4 , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b g < {. . .n 0 ) h # b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b r V ;.9 9 V 9 ;.;.9 9 9 m G ..= = = = = = | @.@.$ $ 0 0 0 ) + + h & & h I M g 9 E 9 L S b b b b b b b b b b b b b b b b b b b I 9 . . E m L L L L V . . . 9 . . L # Q b b b b b b b b b b b b b b 9 J b L E 9 . 9 . . $ 5 / b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b y o b b b +.# % F B B B B B B B Y + > b b b b b b b b b b b b b b b b b b b b b b b W u B u Y $ ) % p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ~ {.< g g < ..@ [ $ 5 p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b q V ;.{ V ;.9 V 9 9 V < R @ ' ' ' ' ' ' ' @ @ [ = = | $ | 0 $ 0 0 ) ) + + h & & 5 % 5 e V 9 9 L K b b b b b b b b b b b b b b I E . A >.4 Q # 4 & F ) 0 $ V . . 9 . . t K # b b b b b b b b b b b b 9 S b b , t E . . E . t | & / b b b b b b b b b b b b b b ",
+"b b b b b b b b b w B b b b / # % 5 h ) u B B B u B u M >.4 b b b b b b b b b b b b b b b b b b b b b b p B B B Y R [ + # b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b {.<  . . . .* t ' = & / b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b G V V 9 V ;.;.9 ;.V g R ' * L * * L m ' ' @ [ [ | | $ 0 ) ) + F + + h h h h h h & & 5 5 5 % S E . E E V # b b b b b b b b b b b G E t +.b p # % 5 + + ) $ | = [ m E . . E . t + 4 b b b b b b b b b b R 9 b b b > h 9 . . 9 . . m | & Q b b b b b b b b b b b b b ",
+"b b b b b b b b b B j b b +.> 4 4 > J h !.u u B B B u u @ >.4 b b b b b b b b b b b b b b b b b b b b b Q u B B 8 m R | J b b b b b b b b b b b b b b b b b b b b b b b b b b #.C z b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b Q {.g < < g < { { * [ h Q b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b G ;.;.;.V ;.9 ;.V L m m * t * t t L m ' @ [ = $ 0 ) + h & 5 % 4 4 > # > > > > > > 4 4 4 4 4 4 4 $ 9 9 9 . E @.4 , b b b b b b b b G * Q b b , # % & h + ) ) ) $ $ = [ L E . 9 . . L >.4 b b b b b b b b b . X b b / 4 h [ 9 . 9 . . ;.' 0 4 b b b b b b b b b b b b b ",
+"b b b b b b b b b s b b , # > # / / / > h N B B u B B B -.@ ) # b b b b b b b b b b b b b b b b b b b b b B B B ~ * m = & p b b b b b b b b b b b b b b b b b b b b b b b b C C C $.b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b !  . . . . .< 9 9 * [ 5 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b k V ;.V { V 9 V V m L * t { { t t * m @ [ = 0 + & 5 4 > # / p p b b b b b b b b b b b b b , , , } # >.E . . 9 9 R 0 K / b b b b b b V : b b b Q # 4 % K 5 & 5 5 5 5 & + 0 | t 9 . . . 9 @ 0 4 b b b b b b b E V b b Q } 4 5 + t . . . E . t @ + / b b b b b b b b b b b b ",
+"b b b b b b b b b b b b / # } / b b b b # : B B B B B B u G @ h / b b b b b b b b b b b b b b b b b b b b u u u G t m [ h d z W W b b b b b b b b b b b b b b b b b b b b #.C C C ^ } / b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b r < g g < < t 9 { L | % b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b i V V ;.;.;.V ;.m L * t { t t * m ' [ | 0 h 5 4 } , b b b b b b b b b b b b b b b b b b b b b b b b b ..9 9 . . 9 * @ $ % b b b b H 9 b b b b b } > 4 > > # } / / , , / } 4 & $ 9 . E . . 9 [ ) # b b b b b F E b b p } # # P % >.E 9 . E . 9 L = % b b b b b b b b b b b b ",
+"b b b b b b b b b b b b , , , b b b b b b > _ B u B B B B B m = & p b b b b b b b b b b b b b b b b b b b s B B G * * [ *.B u B u z b b b b b b b b b b b b b b b b b b b C U U -.: + h 4 p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b !.{. . . . .;.9 9 m $ # b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b < V ;.;.V V V m m t t { { t * m ' [ 0 + 5 > } p b b b b b b b b b b b b b b b b b b b b b b b b b b b b R . E 9 9 9 V * [ F / b b b V b b b b b , / / / / p b b b b b b b b b } % = 9 9 . . . t = h / b b b b 9 t b b } # } / p , 4 * . 9 9 . 9 t @ + / b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b p J N u u u B B u q ' $ > b b b b b b b b b b b b b b b b b b b W B B @ m ' @.u B u B B #.P / b b b b b b b b b b b b b b b b b C C C -.x = | h } b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b !.< < g g < 9 9 { m 0 } b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b L ;.V V ;.;.V m t t { { t * m [ | ) & 4 } b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b q 9 . E 9 { 9 t @ ) } b b G b b b b b b p p b b b b b b b b b b b b b b Q % 9 E . 9 9 9 m | % b b b F . H b / # # , b b b b & . . 9 . 9 { m | 4 b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b / ` B B B B B B B L [ + / b b b b b b b b b b b b b b b b b b X B *.@ ' ' M B B B B B >.) h # b b b b b b b b b b b b b b b b C C U U G m @ 0 J b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b !.{. . . . .{ 9 { @ + , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b : V V V { V L L V { { t * m @ | ) 5 # Q b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b @ . 9 9 9 9 9 { ' ) +.b b b b b b b b b b b b b b b b b b b b b b b b b b Q 0 9 9 . . . t @ ) # b b V V b p # > } p b b b b Q 9 . . . . 9 t [ h Q b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b P *.B u u B u B '.L | % b b b b b b b b b b b b b b b b b b Q u ` = @ [ $ Z B B u x [ = $ 5 , b b b b b b b b b b b b b b b C C C C R t m | % b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b j g < < g < . 9 { @ h p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ;.V V ;.;.L * t { { { * ' | + % } b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b L . . 9 t { { t @ h , b b b b b b b b b b b b b b b b b b b b b b b b b b b } 9 . E . . 9 * = 5 p P . >.b } > > / b b b b b b I . . . 9 . { m 0 # b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b &.B B B B B u B t ' >.# b b b b b b b b b b b b b b b b b Q B ~.0 = | 0 & K : @.[ ' @ | h / b b b b b b b b b b b b b b b U C C U g t * = 5 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b j  . . . .< 9 9 t [ & b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b : V V ;.V L * { 9 { t m [ ) 5 +.b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b E 9 9 . t 9 { * | % b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ..E . . E . t ' 0 > @ 9 +./ # 4 # , b b b b b b p . 9 . . . 9 * = 5 b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b / _ B u B B u u C * = & p b b b b b b b b b b b b b b b b Q B J + $ >.F % % & $ [ @ [ $ 5 , b b b b b b b b b b b b b b b C U C -.m t * ..5 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b j g g <  .< . 9 t ..5 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b i ;.V V g * { { { t ' = + > p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b f E . 9 V t { t m 0 # b b b b b b b b b b b b b b b b b b b b b b b b b b b b b # 9 9 9 9 . V m | 5 . L p # 4 4 } b b b b b b b b t 9 E 9 9 9 { @ + / b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b 4 B B B B B B B g m 0 4 b b b b b b b b b b b b b b b b X B # & + h % # } 4 h $ | $ h > b b b b b b b b b b b b b b b b U C U U < { * = & b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b  . . .< A 9 9 * | % b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b V V V A ' t { V t ' $ K } b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b 9 9 9 . t t { * [ F , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b . . . . . { t R @ . = # 4 % 4 , b b b b b b b b >.. . . . 9 { m 0 # b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b p ` u B B B B B u A [ h , b b b b b b b b b b b b b b b W s # 5 h 5 > , p / # 5 & % # b b b b b b b b b b b b b b b b b C C C C L { * = 5 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b < g  . .L 9 9 * $ > b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b V V V ..m t t * ' $ 5 / b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b l E . E 9 . 9 { { * | 5 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b L . E . . . { m V . h % 5 % > p b b b b b b b b b 9 . . . 9 { * = % b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b # B u B B B u B v m $ J b b b b b b b b b b b b b b b u $./ % 5 4 / b b b b , , , b b b b b b b b b b b b b b b b b b C C U U m t t [ & b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b  . .g < t 9 { m $ # b b b b b b b b b b b b b b b b b b b b b b b b b b X X X b b b b b b b b b b b b b b V ;.R ..m * * ' $ 5 , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b < . 9 9 9 . 9 . V { m | 4 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b F E . 9 E E { t . E + h & 5 # b b b b b b b b b b 9 E E . . 9 t @ + Q b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b &.u u u B B B B * @ + } b b b b b b b b b b b b b j B T # % 5 > p b b Q X Q b b b b b b b b b b b b b b b b b b b b U C C U L V * = 5 b b b b b b b b b b b b b p p b b b b b b b b b b b b b p Q Q Q b b b b b b b b b b b b b b b b b b b b b b b b b <  .{. .{ { { ' >./ b b b b b b b b b b b b b b b b b b b b Q !.v A A A 3 3 A A A A i r p b b b b b b b b V ;.+ = m m @ $ & , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b v 9 9 E ..M 9 E 9 . V { m $ > b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b , 9 . . 9 . { V 9 { $ ) h 5 / b b b b b b b b b b M 9 . . . E { m 0 # b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b +.Z B B B B B B '.* = 5 b b b b b b b b b b b b b o B W > 5 % # b b s B B s b b b b b b b b b b b b W Z N C C C *.Z C U U U A { * [ & b b b b b b b b T Y '.D ] '.] '.j b b b b b b b ).~ ] =.=.] =.=.7 =.8 1 b b b b b b b b b b b b b b b b b b b b b  .< < < V 9 t @ h / b b b b b b b b b b b b b b b b b ! !.e d w b b b b b b b b / &.I < 3 n b b b b b b b M R h = @ @ | h / b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b e 9 . E { S % ... 9 . 9 9 { * | 4 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b . . 9 . . . 9 . t [ | ) % / b b b b b b b b b b % . 9 E . 9 { m | % b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b 4 B B u B u B B g ' >.# b b b b b b b b b b b Q B u P % h 5 # b z B B B )., p b b b b b b b b s N C C N C C C U C C C C C L 9 t ..K b b b b b X - '.D '.'.'.] D ] '.'.2 , b b b W =.] =.i i 7 7 7 ] 7 7 7 7 ~ +.b b b b b b b b b b b b b b b b b b b  . . .R t t * [ & b b b b b b b b b b b b b b b b b b b b b b b b b b b b p Q , +./ / } q V k b b b b b b c f & $ = | + > b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b g E 9 9 9 ;.5 h ) @ . 9 . 9 9 { L | 4 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b L . . 9 9 . E 9 t ' [ 0 % p b b b b b b b b b b b . 9 9 . . 9 t [ & p b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b p f u B u B u u -.L [ h Q b b b b b b b b b b o u $.4 & h & P X u B B B l % 4 / b b b b b b u C C N C C C C U C C C U U U V { * [ & b b b b ; D '.'.Y ! 4 J 5 h U ] ] 8 5 # b ` ] ] ] ] =.=.=.] ] 7 7 7 7 7 7 7 J / b b b b b b b b b b b b b b b b b g < < m t { * | % b b b b b b b b b b b b b b b b b b b b b b b b b b b p , / # # > 5 ..V 3 >.J / b b b b L G 5 ) $ 0 5 , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b e V 9 9 9 9 9 @ & ) $ = g 9 E 9 . V 9 * = % b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b @ E E . . 9 . . { * @ $ 5 p b b b b b b b b b b b t . . . . 9 t R ) } b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b # 6 B B B B B B c ' $ 4 b b b b b b b b b Q B B H & ) + 5 # ,.u B B B e $ 0 5 / b b b b b N N C N C C -.~ -.U U C C U U < 9 t [ & b b b C ~ D '.2 4 h + 0 $ c '.'.] ..| F 4 !.] ] 7 ] i i 7 7 7 =.7 7 7 7 7 7 $ h # b b b b b b b b b b b b b b b ^ {. .7 m * * R 0 P b b b b b b b b b b b b b b b b b b b b b b b b b b b p , , / K v 3 V V V $ ) 5 } b b a V V L 0 ) h 4 p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b H e t E 9 9 E 9 . g ) 0 | = [ [ g 9 . 9 . ;.9 t [ K b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b f 9 . . . 9 . . 9 t ' $ % b b b b b b b b b b b b M E E . . . { m | > b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b 4 B B B B B u B m @ + +.b b b b b b b b o B ).4 h ) ) 5 / j B B N N 8 ' [ ) > b b b b W N C N - M @ * * t * L U U U U < 9 t [ h Q b ).~ D ~ ~.& $ | M c '.'.] ] q ' @ | h ] ] ~ ( @...m * t t L L g {.7 7 7 ' | h / b b b b b b b b b b b b b b !.< g | ' * m [ + / b b b b b b b b b b b b b b b b b b b b b b b b b b b X l v < V V V 3 3 g = | ) J p b d V V V V L ..>./ b b b b b b b b b b b b b b b b b b b b b b b b b b H e g 9 9 E E 9 9 9 9 t @.| = [ [ [ = | @ . 9 . 9 9 { t @ h Q b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b . . E . . E 9 9 { m $ 4 b b b b b b b b b b b b , . 9 9 E . 9 t = K b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b , f B B u B B B u m = 5 p b b b b b b z B #.} 5 ) ) + % p b B B B N ~ * m = 5 b b b b #.N C r = m * t t V t i C C C U {.. V @ + , b #.'.D '.e n c ] '.] ] '.i m t * ' = ) ] Y 5 0 @ m t t { t { V { { { {.{.L ' | J b b b b b b b b b b b b b b  . .n M ' * ' | & , b b b b b b b b b b b b b b b b b b b b b b b b d S G A t V A 3 3 V V R ' ' @ $ 5 , b b ;.V V V ;.;.;.V V g v k b b b b b b b b b b b b b b b H a e v g 9 E E 9 9 E 9 9 9 9 E R = = [ @ R R @ = $ + M . 9 . E . { { ' ) / b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b 9 9 . . 9 . . 9 { m $ > b b b b b b b b b b b b b . . 9 . 9 9 t @ + / b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b P r u B B u B B - ' $ 4 b b b b b ).B o +.% + ) ) 5 } b b B B B B N V * [ h / b b b C C *.$ @ * V { A i U U U U U U U 9 { ' + +.b $.D '.'.'.'.'.D '.c G m m * * * m [ ( =.c ) = R * * t * t * * * t g 7 7 L m = h , b b b b b b b b b b Q d i < <  .< <  .< i v 8 a w b b b b b b b b b b b b b b b b b b j r i A A 3 3 3 3 3 V V t L R m m m @ $ 5 Q b b M V V ;.V V V ;.;.V V 9 V V V V V V V V V V ;.;.;.9 ;.9 9 9 9 9 9 9 9 E 9 9 9 9 t ' = @ @ ' ' ' @ [ | 0 & % >.9 E 9 9 9 9 { m $ > b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b L . 9 E . . 9 . 9 m $ P b b b b b b b b b b b b b R . . 9 . . { * @.4 b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b J - B B B B B u - v ) > b b b s B O Q # & ) ) h > p b b u *.B N *.< t ' 0 # j b b N C - C C ~ i C C C C C '.U U U U {.V m $ J b H D D '.'.m t V t * m m m m q i v | >.] ] ] v R L m m ' @ @ G G 7 7 {.7 L * [ F } b b b b b b j _ {.{.{. .{.8 n x G R g G @.e f 8 ( c < < < c ( Y ( ( ( ( ( ( ( v A A A A A A 3 A A t A A 3 A 3 g m m * * m [ 0 4 b b b p A V V ;.9 V { ;.;.V ;.9 ;.9 9 9 9 9 9 9 9 9 9 ;.9 ;.9 9 9 ;.9 9 9 9 E 9 V g R R m m m m m ' @ | 0 & 4 / p h 9 . . . E . 9 * = J b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b F . 9 . E . 9 9 9 m 0 } b b b b b b b b b b b b b J . . . . . 9 * [ & p b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b , & - B u B B B B B Y r 2 s u B o +.P & + + h % / b b b ).N N B N *.U c Y 6 N b b *.N C C C C C -.C C U ] * 7 U U U U U i n U # H ~ '.'.'.'.q g L q i i ] '.c ] v $ S ] =.=.i ] 7 7 7 i 7 7 7 7 7 {.7 g * m [ ) # b b b $.c {. .{.{.7 ~.%.# 4 h $ [ ' @ [ 0 5 # , b b b p d l ( v < < A ].A A A ].A A A A A 3 A V 3 3 V 3 V L * t t t t m @ | & / b b b b / ..;.V V ;.;.V { ;.9 ;.9 ;.;.;.;.;.9 9 ;.9 9 9 9 9 9 ;.9 E E 9 ;.A g m m m * * * m m ' [ = 0 + 4 +.b b b l . 9 9 9 9 9 { t [ 5 b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b Q . . . 9 . . 9 { ' ) } b b b b b b b b b b b b b b 9 E . 9 E 9 . V >.> b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b } F N u B B B u B B B B B u O P 4 5 h ) h % / b b b b b O N N N B N N N N Z # } ; N C C C C C C -.~ ' L * L '.U U D U D ~ $ 5 4 r D '.'.] '.'.c ] '.'.] ] '.i [ $ + ( ] =.7 ] =.=.=.=.7 7 7 {.=.7 R * m m = h } 2 8 {.{.{.{.{.c &.> 4 % & + 0 | = [ = @.) 5 > / Q p b b b p p , , %.K ( c < A A 3 3 A A A V A A t A L L t V { t { * * @ $ & # b b b b b p % | g t V ;.;.V V ;.;.;.9 9 9 9 ;.;.{ ;.9 9 9 ;.9 9 ;.t L A * * t t t t * * m m ' [ | ) h 4 } b b b b b w . . E . . ;.9 t @ + Q b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b V 9 . 9 9 . 9 { @ + , b b b b b b b b b b b b b b = 9 . . . . . * = 5 p b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b > >.C u B u B u u u u - e h h + ) ) h 5 } b b b b b b , _ - N N N N c n 0 & % 5 !.N C C U '.{.L t * * * t g U U U ] g m = ) & K 8 D '.'.'.] ] '.] ] ] q m @ = $ ) h ) @.c 7 7 i 7 7 7 7 =.v ..@ ' m m @ | & r 7 {.{.{.n !.f & h + ) + + + ) ) $ $ 0 h 5 4 } / p p p p p , , / # > 4 % & h + >.M M G @ R R g L t t V { { { { t * m = 0 5 } b b b b b b b # + = m t t { V V V V V t t t A t t V t A t V V t { t t { t V { t t * * m ' @ = $ + K > , b b b b b b b b R 9 9 V { 9 { t @ h , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b # m . . . { 9 t [ & p b b b b b b b b b b b b b b b L { 9 9 9 { * [ + } b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b 4 $ @ {.L A A A g m @ = $ 0 ) ) h % } b b b b b b b b J $ ' R L t * ' [ $ + h F $ ..q t { { t t * m m m m R g L t { * @ $ + & + | G < L L A L L t * m ' ' = $ + h h 0 = @ m R R q G R R ' ' m ' ' @ | + 4 P ,.,.d , > & + ) $ 0 $ ) + & & 5 5 % 4 # / , b b b b b b b b , / } P % % & F ) $ | = @ @ m * * t t t t { t * * ' [ | h 4 , b b b b b b b b b > + = ' * V { { { { 9 9 9 9 9 9 9 9 9 9 { { { V { { t t t * * m m ' [ = 0 ) & % } p b b b b b b b b b b b F ..' t 9 { m = & p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b > 0 L { { t * = % b b b b b b b b b b b b b b b b # ) @ * t { L [ ) # b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b , 5 $ ' A { { t t * ' [ = $ 0 h % # b b b b b b b b b / & = ' L * * ' ..$ + K & + | ' * * t t * m ' = | | = @ m * * m @ $ h 5 5 ) = ' t V t t t * m ' ' [ = ) & 4 % & ) = @ R ' ' m m m m m ' ' @ | ) % , b b b p # 5 + 0 | $ $ ) h 5 4 # / , p b b b b b b b b b b b b b b p , +.> % & + ) $ = [ @ @ ' ' m m * * m ' @ = 0 h 4 } b b b b b b b b b b b b # & 0 = @ m * t t t V { t t t t t t t { t t t t * * m ' @ [ = $ ) + 5 4 } p b b b b b b b b b b b b b b } h [ m * m @ 0 > b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b p K | ' m m [ ) P b b b b b b b b b b b b b b b b b 4 0 @ m m ' = h } b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b Q % ) = ' * * m ' @ = $ ) & 4 / b b b b b b b b b b b } h 0 = @ @ [ | ) & 4 > % h @.[ ' ' @ [ | $ + 5 5 5 + | = [ = $ + % # } 4 h $ [ m m ' ' ' @ [ = $ ) & > / , } % + $ = [ [ @ @ @ @ [ = | 0 & > p b b b b p > 5 + ) ) + h 5 # / b b b b b b b b b b b b b b b b b b b b b b p / # 4 % & h ) $ $ | | = | | $ $ ) h 5 > / b b b b b b b b b b b b b b b , > 5 ) $ = [ @ @ @ ' ' ' ' ' ' ' @ ' ' @ [ ..= | $ 0 + h 5 % # / p b b b b b b b b b b b b b b b b b b 4 ) | [ = ) 5 p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b } & $ = = 0 5 , b b b b b b b b b b b b b b b b b , % ) | = | + 4 p b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b > & 0 $ | | $ 0 ) h 4 } p b b b b b b b b b b b b b , % h + ) + h % } p p Q # % F ) 0 >.+ & 4 } , p , # % h + + & # , b b b # 5 + 0 $ $ $ 0 0 ) h 5 > / b b b b p } % & + 0 0 $ $ 0 ) + 5 4 +.b b b b b b b / > 5 5 5 4 } , b b b b b b b b b b b b b b b b b b b b b b b b b b b b p , } # > % % % % 5 % % > # / b b b b b b b b b b b b b b b b b b b b p / > % & h + + ) ) ) + + + + ) + + + h & 5 % > # / , b b b b b b b b b b b b b b b b b b b b b b b b > 5 & 5 > p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b / % & & % / b b b b b b b b b b b b b b b b b b b p > % 5 % P , b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b p # 4 % 5 % > # / b b b b b b b b b b b b b b b b b b , } # } / p b b b b b b / } # } / p b b b b b b b , } / , b b b b b b b / > % % % % 4 # } , b b b b b b b b b , } > > 4 4 4 > } p b b b b b b b b b b b , , , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b p , / / / } +.} / / / / / / , p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b p p p b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b Q , b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b ",
+"b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b "};
diff --git a/lib/s16_0_44100.raw b/lib/s16_0_44100.raw
new file mode 100644 (file)
index 0000000..008c4f6
Binary files /dev/null and b/lib/s16_0_44100.raw differ
diff --git a/lib/s16_1_32000.raw b/lib/s16_1_32000.raw
new file mode 100644 (file)
index 0000000..75122b5
Binary files /dev/null and b/lib/s16_1_32000.raw differ
diff --git a/src/audio_decoder/audio_decoder.c b/src/audio_decoder/audio_decoder.c
new file mode 100644 (file)
index 0000000..b5fc12b
--- /dev/null
@@ -0,0 +1,1060 @@
+/******************************************************************************
+ * audio_decoder.c: MPEG1 Layer I-II audio decoder thread
+ * (c)1999 VideoLAN
+ ******************************************************************************/
+
+/*
+ * TODO :
+ * - Optimiser les NeedBits() et les GetBits() du code là où c'est possible
+ */
+
+/******************************************************************************
+ * Preamble
+ ******************************************************************************/
+#include <unistd.h>
+
+#include <pthread.h>
+#include <stdio.h>                                            /* "intf_msg.h" */
+#include <stdlib.h>                                       /* malloc(), free() */
+#include <netinet/in.h>                                            /* ntohl() */
+#include <sys/soundcard.h>                                /* "audio_output.h" */
+#include <sys/uio.h>                                             /* "input.h" */
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"
+#include "debug.h"                                       /* "input_netlist.h" */
+
+#include "intf_msg.h"                         /* intf_DbgMsg(), intf_ErrMsg() */
+
+#include "input.h"                                            /* pes_packet_t */
+#include "input_netlist.h"                          /* input_NetlistFreePES() */
+#include "decoder_fifo.h"          /* DECODER_FIFO_(ISEMPTY|START|INCSTART)() */
+
+#include "audio_output.h"
+
+#include "audio_constants.h"
+#include "audio_decoder.h"
+
+/******************************************************************************
+ * Local prototypes
+ ******************************************************************************/
+static int      InitThread              ( adec_thread_t * p_adec );
+static void     RunThread               ( adec_thread_t * p_adec );
+static void     ErrorThread             ( adec_thread_t * p_adec );
+static void     EndThread               ( adec_thread_t * p_adec );
+
+static int      adec_Layer1_Mono        ( adec_thread_t * p_adec );
+static int      adec_Layer1_Stereo      ( adec_thread_t * p_adec );
+static int      adec_Layer2_Mono        ( adec_thread_t * p_adec );
+static int      adec_Layer2_Stereo      ( adec_thread_t * p_adec );
+
+static byte_t   GetByte                 ( bit_stream_t * p_bit_stream );
+static void     NeedBits                ( bit_stream_t * p_bit_stream, int i_bits );
+static void     DumpBits                ( bit_stream_t * p_bit_stream, int i_bits );
+static int      FindHeader              ( adec_thread_t * p_adec );
+
+/******************************************************************************
+ * adec_CreateThread: creates an audio decoder thread
+ ******************************************************************************
+ * This function creates a new audio decoder thread, and returns a pointer to
+ * its description. On error, it returns NULL.
+ ******************************************************************************/
+adec_thread_t * adec_CreateThread( input_thread_t * p_input )
+{
+    adec_thread_t *     p_adec;
+
+    intf_DbgMsg("adec debug: creating audio decoder thread\n");
+
+    /* Allocate the memory needed to store the thread's structure */
+    if ( (p_adec = (adec_thread_t *)malloc( sizeof(adec_thread_t) )) == NULL )
+    {
+        intf_ErrMsg("adec error: not enough memory for adec_CreateThread() to create the new thread\n");
+        return( NULL );
+    }
+
+    /*
+     * Initialize the thread properties
+     */
+    p_adec->b_die = 0;
+    p_adec->b_error = 0;
+
+    /*
+     * Initialize the input properties
+     */
+    /* Initialize the decoder fifo's data lock and conditional variable and set
+     * its buffer as empty */
+    pthread_mutex_init( &p_adec->fifo.data_lock, NULL );
+    pthread_cond_init( &p_adec->fifo.data_wait, NULL );
+    p_adec->fifo.i_start = 0;
+    p_adec->fifo.i_end = 0;
+    /* Initialize the bit stream structure */
+    p_adec->bit_stream.p_input = p_input;
+    p_adec->bit_stream.p_decoder_fifo = &p_adec->fifo;
+    p_adec->bit_stream.fifo.buffer = 0;
+    p_adec->bit_stream.fifo.i_available = 0;
+
+    /*
+     * Initialize the decoder properties
+     */
+    p_adec->bank_0.actual = p_adec->bank_0.v1;
+    p_adec->bank_0.pos = 0;
+    p_adec->bank_1.actual = p_adec->bank_1.v1;
+    p_adec->bank_1.pos = 0;
+
+    /*
+     * Initialize the output properties
+     */
+    p_adec->p_aout = p_input->p_aout;
+    p_adec->p_aout_fifo = NULL;
+
+    /* Spawn the audio decoder thread */
+    if ( pthread_create(&p_adec->thread_id, NULL, (void *)RunThread, (void *)p_adec) )
+    {
+        intf_ErrMsg("adec error: can't spawn audio decoder thread\n");
+        free( p_adec );
+        return( NULL );
+    }
+
+    intf_DbgMsg("adec debug: audio decoder thread (%p) created\n", p_adec);
+    return( p_adec );
+}
+
+/******************************************************************************
+ * adec_DestroyThread: destroys an audio decoder thread
+ ******************************************************************************
+ * This function asks an audio decoder thread to terminate. This function has
+ * not to wait until the decoder thread has really died, because the killer (ie
+ * this function's caller) is the input thread, that's why we are sure that no
+ * other thread will try to access to this thread's descriptor after its
+ * destruction.
+ ******************************************************************************/
+void adec_DestroyThread( adec_thread_t * p_adec )
+{
+    intf_DbgMsg("adec debug: requesting termination of audio decoder thread %p\n", p_adec);
+
+    /* Ask thread to kill itself */
+    p_adec->b_die = 1;
+
+    /* Remove this as soon as the "status" flag is implemented */
+    pthread_join( p_adec->thread_id, NULL );         /* wait until it's done */
+}
+
+/* Following functions are local */
+
+/******************************************************************************
+ * GetByte : reads the next byte in the input stream
+ ******************************************************************************/
+static __inline__ byte_t GetByte( bit_stream_t * p_bit_stream )
+{
+    /* Are there some bytes left in the current TS packet ? */
+    if ( p_bit_stream->i_byte < p_bit_stream->p_ts->i_payload_end )
+    {
+        return( p_bit_stream->p_ts->buffer[ p_bit_stream->i_byte++ ] );
+    }
+    else
+    {
+        /* We are looking for the next TS packet that contains real data,
+         * and not just a PES header */
+        do
+        {
+            /* We were reading the last TS packet of this PES packet... It's
+             * time to jump to the next PES packet */
+            if ( p_bit_stream->p_ts->p_next_ts == NULL )
+            {
+                /* We are going to read/write the start and end indexes of the
+                 * decoder fifo and to use the fifo's conditional variable,
+                 * that's why we need to take the lock before */
+                pthread_mutex_lock( &p_bit_stream->p_decoder_fifo->data_lock );
+
+                /* We should increase the start index of the decoder fifo, but
+                 * if we do this now, the input thread could overwrite the
+                 * pointer to the current PES packet, and we weren't able to
+                 * give it back to the netlist. That's why we free the PES
+                 * packet first. */
+                input_NetlistFreePES( p_bit_stream->p_input, DECODER_FIFO_START(*p_bit_stream->p_decoder_fifo) );
+                DECODER_FIFO_INCSTART( *p_bit_stream->p_decoder_fifo );
+
+                /* !! b_die !! */
+                while ( DECODER_FIFO_ISEMPTY(*p_bit_stream->p_decoder_fifo) )
+                {
+                    pthread_cond_wait( &p_bit_stream->p_decoder_fifo->data_wait,
+                                       &p_bit_stream->p_decoder_fifo->data_lock );
+                }
+
+                /* The next byte could be found in the next PES packet */
+#ifdef DEBUG
+//                fprintf(stderr, "*");
+#endif
+                p_bit_stream->p_ts = DECODER_FIFO_START( *p_bit_stream->p_decoder_fifo )->p_first_ts;
+
+                /* We can release the fifo's data lock */
+                pthread_mutex_unlock( &p_bit_stream->p_decoder_fifo->data_lock );
+            }
+            /* Perhaps the next TS packet of the current PES packet contains
+             * real data (ie its payload's size is greater than 0) */
+            else
+            {
+#ifdef DEBUG
+//                fprintf(stderr, ".");
+#endif
+                p_bit_stream->p_ts = p_bit_stream->p_ts->p_next_ts;
+            }
+        } while ( p_bit_stream->p_ts->i_payload_start == p_bit_stream->p_ts->i_payload_end );
+
+        /* We've found a TS packet which contains interesting data... As we
+         * return the payload's first byte, we set i_byte to the following
+         * one */
+        p_bit_stream->i_byte = p_bit_stream->p_ts->i_payload_start;
+        return( p_bit_stream->p_ts->buffer[ p_bit_stream->i_byte++ ] );
+    }
+}
+
+/******************************************************************************
+ * NeedBits : reads i_bits new bits in the bit stream and stores them in the
+ *            bit buffer
+ ******************************************************************************
+ * - i_bits must be less or equal 32 !
+ * - There is something important to notice with that function : if the number
+ * of bits available in the bit buffer when calling NeedBits() is greater than
+ * 24 (i_available > 24) but less than the number of needed bits
+ * (i_available < i_bits), the byte returned by GetByte() will be shifted with
+ * a negative value and the number of bits available in the bit buffer will be
+ * set to more than 32 !
+ ******************************************************************************/
+static __inline__ void NeedBits( bit_stream_t * p_bit_stream, int i_bits )
+{
+    while ( p_bit_stream->fifo.i_available < i_bits )
+    {
+        p_bit_stream->fifo.buffer |= ((u32)GetByte( p_bit_stream )) << (24 - p_bit_stream->fifo.i_available);
+        p_bit_stream->fifo.i_available += 8;
+    }
+}
+
+/******************************************************************************
+ * DumpBits : removes i_bits bits from the bit buffer
+ ******************************************************************************
+ * - i_bits <= i_available
+ * - i_bits < 32 (because (u32 << 32) <=> (u32 = u32))
+ ******************************************************************************/
+static __inline__ void DumpBits( bit_stream_t * p_bit_stream, int i_bits )
+{
+    p_bit_stream->fifo.buffer <<= i_bits;
+    p_bit_stream->fifo.i_available -= i_bits;
+}
+
+/******************************************************************************
+ * FindHeader : parses an input stream until an audio frame header could be
+ *              found
+ ******************************************************************************
+ * When this function returns successfully, the header can be found in the
+ * buffer of the bit stream fifo.
+ ******************************************************************************/
+static int FindHeader( adec_thread_t * p_adec )
+{
+    while ( (!p_adec->b_die) && (!p_adec->b_error) )
+    {
+        NeedBits( &p_adec->bit_stream, 32 );
+        if ( (p_adec->bit_stream.fifo.buffer & ADEC_HEADER_SYNCWORD_MASK) == ADEC_HEADER_SYNCWORD_MASK )
+        {
+#ifdef DEBUG
+//            fprintf(stderr, "H");
+#endif
+            return( 0 );
+        }
+#ifdef DEBUG
+//        fprintf(stderr, "!");
+#endif
+        DumpBits( &p_adec->bit_stream, 8 );
+    }
+
+    return( -1 );
+}
+
+/******************************************************************************
+ * adec_Layer`L'_`M': decodes an mpeg 1, layer `L', mode `M', audio frame
+ ******************************************************************************
+ * These functions decode the audio frame which has already its header loaded
+ * in the i_header member of the audio decoder thread structure and its first
+ * byte of data described by the bit stream structure of the audio decoder
+ * thread (there is no bit available in the bit buffer yet)
+ ******************************************************************************/
+
+/******************************************************************************
+ * adec_Layer1_Mono
+ ******************************************************************************/
+static __inline__ int adec_Layer1_Mono( adec_thread_t * p_adec )
+{
+    p_adec->bit_stream.fifo.buffer = 0;
+    p_adec->bit_stream.fifo.i_available = 0;
+    return( 0 );
+}
+
+/******************************************************************************
+ * adec_Layer1_Stereo
+ ******************************************************************************/
+static __inline__ int adec_Layer1_Stereo( adec_thread_t * p_adec )
+{
+    p_adec->bit_stream.fifo.buffer = 0;
+    p_adec->bit_stream.fifo.i_available = 0;
+    return( 0 );
+}
+
+/******************************************************************************
+ * adec_Layer2_Mono
+ ******************************************************************************/
+static __inline__ int adec_Layer2_Mono( adec_thread_t * p_adec )
+{
+    p_adec->bit_stream.fifo.buffer = 0;
+    p_adec->bit_stream.fifo.i_available = 0;
+    return( 0 );
+}
+
+/******************************************************************************
+ * adec_Layer2_Stereo
+ ******************************************************************************/
+static __inline__ int adec_Layer2_Stereo( adec_thread_t * p_adec )
+{
+    typedef struct requantization_s
+    {
+        byte_t                          i_bits_per_codeword;
+        const float *                   pf_ungroup;
+        float                           f_slope;
+        float                           f_offset;
+    } requantization_t;
+
+    static const float                  pf_scalefactor[64] = ADEC_SCALE_FACTOR;
+
+    static u32                          i_header;
+    static int                          i_sampling_frequency, i_mode, i_bound;
+    static int                          pi_allocation_0[32], pi_allocation_1[32]; /* see ISO/IEC 11172-3 2.4.1.6 */
+    int                                 i_sb, i_nbal;
+    float                               f_scalefactor_0, f_scalefactor_1;
+
+    static const byte_t                 ppi_bitrate_per_channel_index[4][15] = ADEC_LAYER2_BITRATE_PER_CHANNEL_INDEX;
+    static const byte_t                 ppi_sblimit[3][11] = ADEC_LAYER2_SBLIMIT;
+    static const byte_t                 ppi_nbal[2][32] = ADEC_LAYER2_NBAL;
+
+    static const float                  pf_ungroup3[3*3*3 * 3] = ADEC_LAYER2_UNGROUP3;
+    static const float                  pf_ungroup5[5*5*5 * 3] = ADEC_LAYER2_UNGROUP5;
+    static const float                  pf_ungroup9[9*9*9 * 3] = ADEC_LAYER2_UNGROUP9;
+
+    static const requantization_t       p_requantization_cd[16] = ADEC_LAYER2_REQUANTIZATION_CD;
+    static const requantization_t       p_requantization_ab1[16] = ADEC_LAYER2_REQUANTIZATION_AB1;
+    static const requantization_t       p_requantization_ab2[16] = ADEC_LAYER2_REQUANTIZATION_AB2;
+    static const requantization_t       p_requantization_ab3[16] = ADEC_LAYER2_REQUANTIZATION_AB3;
+    static const requantization_t       p_requantization_ab4[16] = ADEC_LAYER2_REQUANTIZATION_AB4;
+    static const requantization_t *     pp_requantization_ab[30] = ADEC_LAYER2_REQUANTIZATION_AB;
+
+    static int                          i_sblimit, i_bitrate_per_channel_index;
+    static int                          pi_scfsi_0[30], pi_scfsi_1[30];
+    static const byte_t *               pi_nbal;
+    static float                        ppf_sample_0[3][32], ppf_sample_1[3][32];
+    static const requantization_t *     pp_requantization_0[30];
+    static const requantization_t *     pp_requantization_1[30];
+    static requantization_t             requantization;
+    static const float *                pf_ungroup;
+
+    static float                        pf_scalefactor_0_0[30], pf_scalefactor_0_1[30], pf_scalefactor_0_2[30];
+    static float                        pf_scalefactor_1_0[30], pf_scalefactor_1_1[30], pf_scalefactor_1_2[30];
+
+    int                                 i_2nbal, i_gr;
+    float                               f_dummy;
+
+    long                                l_end_frame;
+    s16 *                               p_s16;
+
+    int                                 i_need = 0, i_dump = 0;
+//    static const int                    pi_framesize[512] = ADEC_FRAME_SIZE;
+
+    /* Read the audio frame header and flush the bit buffer */
+    i_header = p_adec->bit_stream.fifo.buffer;
+    p_adec->bit_stream.fifo.buffer = 0;
+    p_adec->bit_stream.fifo.i_available = 0;
+    /* Read the sampling frequency (see ISO/IEC 11172-3 2.4.2.3) */
+    i_sampling_frequency = (int)((i_header & ADEC_HEADER_SAMPLING_FREQUENCY_MASK)
+        >> ADEC_HEADER_SAMPLING_FREQUENCY_SHIFT);
+    /* Read the mode (see ISO/IEC 11172-3 2.4.2.3) */
+    i_mode = (int)((i_header & ADEC_HEADER_MODE_MASK) >> ADEC_HEADER_MODE_SHIFT);
+    /* If a CRC can be found in the frame, get rid of it */
+    if ( (i_header & ADEC_HEADER_PROTECTION_BIT_MASK) == 0 )
+    {
+        GetByte( &p_adec->bit_stream );
+        GetByte( &p_adec->bit_stream );
+    }
+
+    /* Find out the bitrate per channel index */
+    i_bitrate_per_channel_index = (int)ppi_bitrate_per_channel_index[i_mode]
+        [(i_header & ADEC_HEADER_BITRATE_INDEX_MASK) >> ADEC_HEADER_BITRATE_INDEX_SHIFT];
+    /* Find out the number of subbands */
+    i_sblimit = (int)ppi_sblimit[i_sampling_frequency][i_bitrate_per_channel_index];
+    /* Check if the frame is valid or not */
+    if ( i_sblimit == 0 )
+    {
+        return( 0 );                                  /* the frame is invalid */
+    }
+    /* Find out the number of bits allocated */
+    pi_nbal = ppi_nbal[ (i_bitrate_per_channel_index <= 2) ? 0 : 1 ];
+
+    /* Find out the `bound' subband (see ISO/IEC 11172-3 2.4.2.3) */
+    if ( i_mode == 1 )
+    {
+        i_bound = (int)(((i_header & ADEC_HEADER_MODE_EXTENSION_MASK) >> (ADEC_HEADER_MODE_EXTENSION_SHIFT - 2)) + 4);
+        if ( i_bound > i_sblimit )
+        {
+            i_bound = i_sblimit;
+        }
+    }
+    else
+    {
+        i_bound = i_sblimit;
+    }
+
+    /* Read the allocation information (see ISO/IEC 11172-3 2.4.1.6) */
+    for ( i_sb = 0; i_sb < i_bound; i_sb++ )
+    {
+        i_2nbal = 2 * (i_nbal = (int)pi_nbal[ i_sb ]);
+        NeedBits( &p_adec->bit_stream, i_2nbal );
+        i_need += i_2nbal;
+        pi_allocation_0[ i_sb ] = (int)(p_adec->bit_stream.fifo.buffer >> (32 - i_nbal));
+        p_adec->bit_stream.fifo.buffer <<= i_nbal;
+        pi_allocation_1[ i_sb ] = (int)(p_adec->bit_stream.fifo.buffer >> (32 - i_nbal));
+        p_adec->bit_stream.fifo.buffer <<= i_nbal;
+        p_adec->bit_stream.fifo.i_available -= i_2nbal;
+        i_dump += i_2nbal;
+    }
+    for ( ; i_sb < i_sblimit; i_sb++ )
+    {
+        i_nbal = (int)pi_nbal[ i_sb ];
+        NeedBits( &p_adec->bit_stream, i_nbal );
+        i_need += i_nbal;
+        pi_allocation_0[ i_sb ] = (int)(p_adec->bit_stream.fifo.buffer >> (32 - i_nbal));
+        DumpBits( &p_adec->bit_stream, i_nbal );
+        i_dump += i_nbal;
+    }
+
+#define MACRO( p_requantization ) \
+    for ( i_sb = 0; i_sb < i_bound; i_sb++ ) \
+    { \
+        if ( pi_allocation_0[i_sb] ) \
+        { \
+            pp_requantization_0[i_sb] = &((p_requantization)[pi_allocation_0[i_sb]]); \
+            NeedBits( &p_adec->bit_stream, 2 ); \
+            i_need += 2; \
+            pi_scfsi_0[i_sb] = (int)(p_adec->bit_stream.fifo.buffer >> (32 - 2)); \
+            DumpBits( &p_adec->bit_stream, 2 ); \
+            i_dump += 2; \
+        } \
+        else \
+        { \
+            ppf_sample_0[0][i_sb] = .0; \
+            ppf_sample_0[1][i_sb] = .0; \
+            ppf_sample_0[2][i_sb] = .0; \
+        } \
+\
+        if ( pi_allocation_1[i_sb] ) \
+        { \
+            pp_requantization_1[i_sb] = &((p_requantization)[pi_allocation_1[i_sb]]); \
+            NeedBits( &p_adec->bit_stream, 2 ); \
+            i_need += 2; \
+            pi_scfsi_1[i_sb] = (int)(p_adec->bit_stream.fifo.buffer >> (32 - 2)); \
+            DumpBits( &p_adec->bit_stream, 2 ); \
+            i_dump += 2; \
+        } \
+        else \
+        { \
+            ppf_sample_1[0][i_sb] = .0; \
+            ppf_sample_1[1][i_sb] = .0; \
+            ppf_sample_1[2][i_sb] = .0; \
+        } \
+    } \
+\
+    for ( ; i_sb < i_sblimit; i_sb++ ) \
+    { \
+        if ( pi_allocation_0[i_sb] ) \
+        { \
+            pp_requantization_0[i_sb] = &((p_requantization)[pi_allocation_0[i_sb]]); \
+            NeedBits( &p_adec->bit_stream, 4 ); \
+            i_need += 4; \
+            pi_scfsi_0[i_sb] = (int)(p_adec->bit_stream.fifo.buffer >> (32 - 2)); \
+            p_adec->bit_stream.fifo.buffer <<= 2; \
+            pi_scfsi_1[i_sb] = (int)(p_adec->bit_stream.fifo.buffer >> (32 - 2)); \
+            p_adec->bit_stream.fifo.buffer <<= 2; \
+            p_adec->bit_stream.fifo.i_available -= 4; \
+            i_dump += 4; \
+        } \
+        else \
+        { \
+            ppf_sample_0[0][i_sb] = .0; \
+            ppf_sample_0[1][i_sb] = .0; \
+            ppf_sample_0[2][i_sb] = .0; \
+            ppf_sample_1[0][i_sb] = .0; \
+            ppf_sample_1[1][i_sb] = .0; \
+            ppf_sample_1[2][i_sb] = .0; \
+        } \
+    }
+/* #define MACRO */
+
+    if ( i_bitrate_per_channel_index <= 2 )
+    {
+        MACRO( p_requantization_cd )
+    }
+    else
+    {
+        MACRO( pp_requantization_ab[i_sb] )
+    }
+
+#define SWITCH( pi_scfsi, pf_scalefactor_0, pf_scalefactor_1, pf_scalefactor_2 ) \
+    switch ( (pi_scfsi)[i_sb] ) \
+    { \
+        case 0: \
+            NeedBits( &p_adec->bit_stream, (3*6) ); \
+            i_need += 18; \
+            (pf_scalefactor_0)[i_sb] = pf_scalefactor[p_adec->bit_stream.fifo.buffer >> (32 - 6)]; \
+            p_adec->bit_stream.fifo.buffer <<= 6; \
+            (pf_scalefactor_1)[i_sb] = pf_scalefactor[p_adec->bit_stream.fifo.buffer >> (32 - 6)]; \
+            p_adec->bit_stream.fifo.buffer <<= 6; \
+            (pf_scalefactor_2)[i_sb] = pf_scalefactor[p_adec->bit_stream.fifo.buffer >> (32 - 6)]; \
+            p_adec->bit_stream.fifo.buffer <<= 6; \
+            p_adec->bit_stream.fifo.i_available -= (3*6); \
+            i_dump += 18; \
+            break; \
+\
+        case 1: \
+            NeedBits( &p_adec->bit_stream, (2*6) ); \
+            i_need += 12; \
+            (pf_scalefactor_0)[i_sb] = \
+                (pf_scalefactor_1)[i_sb] = pf_scalefactor[p_adec->bit_stream.fifo.buffer >> (32 - 6)]; \
+            p_adec->bit_stream.fifo.buffer <<= 6; \
+            (pf_scalefactor_2)[i_sb] = pf_scalefactor[p_adec->bit_stream.fifo.buffer >> (32 - 6)]; \
+            p_adec->bit_stream.fifo.buffer <<= 6; \
+            p_adec->bit_stream.fifo.i_available -= (2*6); \
+            i_dump += 12; \
+            break; \
+\
+        case 2: \
+            NeedBits( &p_adec->bit_stream, (1*6) ); \
+            i_need += 6; \
+            (pf_scalefactor_0)[i_sb] = \
+                (pf_scalefactor_1)[i_sb] = \
+                (pf_scalefactor_2)[i_sb] = pf_scalefactor[p_adec->bit_stream.fifo.buffer >> (32 - 6)]; \
+            DumpBits( &p_adec->bit_stream, (1*6) ); \
+            i_dump += 6; \
+            break; \
+\
+        case 3: \
+            NeedBits( &p_adec->bit_stream, (2*6) ); \
+            i_need += 12; \
+            (pf_scalefactor_0)[i_sb] = pf_scalefactor[p_adec->bit_stream.fifo.buffer >> (32 - 6)]; \
+            p_adec->bit_stream.fifo.buffer <<= 6; \
+            (pf_scalefactor_1)[i_sb] = \
+                (pf_scalefactor_2)[i_sb] = pf_scalefactor[p_adec->bit_stream.fifo.buffer >> (32 - 6)]; \
+            p_adec->bit_stream.fifo.buffer <<= 6; \
+            p_adec->bit_stream.fifo.i_available -= (2*6); \
+            i_dump += 12; \
+            break; \
+    }
+/* #define SWITCH */
+
+    for ( i_sb = 0; i_sb < i_bound; i_sb++ )
+    {
+        if ( pi_allocation_0[i_sb] )
+        {
+            SWITCH( pi_scfsi_0, pf_scalefactor_0_0, pf_scalefactor_0_1, pf_scalefactor_0_2 )
+        }
+        if ( pi_allocation_1[i_sb] )
+        {
+            SWITCH( pi_scfsi_1, pf_scalefactor_1_0, pf_scalefactor_1_1, pf_scalefactor_1_2 )
+        }
+    }
+    for ( ; i_sb < i_sblimit; i_sb++ )
+    {
+        if ( pi_allocation_0[i_sb] )
+        {
+            SWITCH( pi_scfsi_0, pf_scalefactor_0_0, pf_scalefactor_0_1, pf_scalefactor_0_2 )
+            SWITCH( pi_scfsi_1, pf_scalefactor_1_0, pf_scalefactor_1_1, pf_scalefactor_1_2 )
+        }
+    }
+    for ( ; i_sb < 32; i_sb++ )
+    {
+        ppf_sample_0[0][i_sb] = .0;
+        ppf_sample_0[1][i_sb] = .0;
+        ppf_sample_0[2][i_sb] = .0;
+        ppf_sample_1[0][i_sb] = .0;
+        ppf_sample_1[1][i_sb] = .0;
+        ppf_sample_1[2][i_sb] = .0;
+    }
+
+#define NEXT_BUF \
+/* fprintf(stderr, "%p\n", p_adec->p_aout_fifo->buffer); */ \
+/* fprintf(stderr, "l_end_frame == %li, %p\n", l_end_frame, (aout_frame_t *)p_adec->p_aout_fifo->buffer + l_end_frame); */ \
+    p_s16 = ((aout_frame_t *)p_adec->p_aout_fifo->buffer)[ l_end_frame ]; \
+/* fprintf(stderr, "p_s16 == %p\n", p_s16); */ \
+    l_end_frame += 1; \
+    l_end_frame &= AOUT_FIFO_SIZE;
+/* #define NEXT_BUF */
+
+#define GROUPTEST( pp_requantization, ppf_sample, pf_sf ) \
+    requantization = *((pp_requantization)[i_sb]); \
+    if ( requantization.pf_ungroup == NULL ) \
+    { \
+        NeedBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+        i_need += requantization.i_bits_per_codeword; \
+        (ppf_sample)[0][i_sb] = (f_scalefactor_0 = (pf_sf)[i_sb]) * (requantization.f_slope * \
+            (p_adec->bit_stream.fifo.buffer >> (32 - requantization.i_bits_per_codeword)) + requantization.f_offset); \
+        DumpBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+        i_dump += requantization.i_bits_per_codeword; \
+\
+        NeedBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+        i_need += requantization.i_bits_per_codeword; \
+        (ppf_sample)[1][i_sb] = f_scalefactor_0 * (requantization.f_slope * \
+            (p_adec->bit_stream.fifo.buffer >> (32 - requantization.i_bits_per_codeword)) + requantization.f_offset); \
+        DumpBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+        i_dump += requantization.i_bits_per_codeword; \
+\
+        NeedBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+        i_need += requantization.i_bits_per_codeword; \
+        (ppf_sample)[2][i_sb] = f_scalefactor_0 * (requantization.f_slope * \
+            (p_adec->bit_stream.fifo.buffer >> (32 - requantization.i_bits_per_codeword)) + requantization.f_offset); \
+        DumpBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+        i_dump += requantization.i_bits_per_codeword; \
+    } \
+    else \
+    { \
+        NeedBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+        i_need += requantization.i_bits_per_codeword; \
+        pf_ungroup = requantization.pf_ungroup + 3 * \
+            (p_adec->bit_stream.fifo.buffer >> (32 - requantization.i_bits_per_codeword)); \
+        DumpBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+        i_dump += requantization.i_bits_per_codeword; \
+        (ppf_sample)[0][i_sb] = (f_scalefactor_0 = (pf_sf)[i_sb]) * pf_ungroup[0]; \
+        (ppf_sample)[1][i_sb] = f_scalefactor_0 * pf_ungroup[1]; \
+        (ppf_sample)[2][i_sb] = f_scalefactor_0 * pf_ungroup[2]; \
+    }
+/* #define GROUPTEST */
+
+#define READ_SAMPLE_L2S( pf_scalefactor_0, pf_scalefactor_1, i_grlimit ) \
+    for ( ; i_gr < (i_grlimit); i_gr++ ) \
+    { \
+        for ( i_sb = 0; i_sb < i_bound; i_sb++ ) \
+        { \
+            if ( pi_allocation_0[i_sb] ) \
+            { \
+                GROUPTEST( pp_requantization_0, ppf_sample_0, (pf_scalefactor_0) ) \
+            } \
+            if ( pi_allocation_1[i_sb] ) \
+            { \
+                GROUPTEST( pp_requantization_1, ppf_sample_1, (pf_scalefactor_1) ) \
+            } \
+        } \
+        for ( ; i_sb < i_sblimit; i_sb++ ) \
+        { \
+            if ( pi_allocation_0[i_sb] ) \
+            { \
+                requantization = *(pp_requantization_0[i_sb]); \
+                if ( requantization.pf_ungroup == NULL ) \
+                { \
+                    NeedBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+                    i_need += requantization.i_bits_per_codeword; \
+                    ppf_sample_0[0][i_sb] = (f_scalefactor_0 = (pf_scalefactor_0)[i_sb]) * \
+                        (requantization.f_slope * (f_dummy = \
+                        (float)(p_adec->bit_stream.fifo.buffer >> (32 - requantization.i_bits_per_codeword))) + \
+                        requantization.f_offset); \
+                    DumpBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+                    i_dump += requantization.i_bits_per_codeword; \
+                    ppf_sample_1[0][i_sb] = (f_scalefactor_1 = (pf_scalefactor_1)[i_sb]) * \
+                        (requantization.f_slope * f_dummy + requantization.f_offset); \
+\
+                    NeedBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+                    i_need += requantization.i_bits_per_codeword; \
+                    ppf_sample_0[1][i_sb] = f_scalefactor_0 * \
+                        (requantization.f_slope * (f_dummy = \
+                        (float)(p_adec->bit_stream.fifo.buffer >> (32 - requantization.i_bits_per_codeword))) + \
+                        requantization.f_offset); \
+                    DumpBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+                    i_dump += requantization.i_bits_per_codeword; \
+                    ppf_sample_1[1][i_sb] = f_scalefactor_1 * \
+                        (requantization.f_slope * f_dummy + requantization.f_offset); \
+\
+                    NeedBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+                    i_need += requantization.i_bits_per_codeword; \
+                    ppf_sample_0[2][i_sb] = f_scalefactor_0 * \
+                        (requantization.f_slope * (f_dummy = \
+                        (float)(p_adec->bit_stream.fifo.buffer >> (32 - requantization.i_bits_per_codeword))) + \
+                        requantization.f_offset); \
+                    DumpBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+                    i_dump += requantization.i_bits_per_codeword; \
+                    ppf_sample_1[2][i_sb] = f_scalefactor_1 * \
+                        (requantization.f_slope * f_dummy + requantization.f_offset); \
+                } \
+                else \
+                { \
+                    NeedBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+                    i_need += requantization.i_bits_per_codeword; \
+                    pf_ungroup = requantization.pf_ungroup + 3 * \
+                        (p_adec->bit_stream.fifo.buffer >> (32 - requantization.i_bits_per_codeword)); \
+                    DumpBits( &p_adec->bit_stream, requantization.i_bits_per_codeword ); \
+                    i_dump += requantization.i_bits_per_codeword; \
+\
+                    ppf_sample_0[0][i_sb] = (f_scalefactor_0 = (pf_scalefactor_0)[i_sb]) * pf_ungroup[0]; \
+                    ppf_sample_0[1][i_sb] = f_scalefactor_0 * pf_ungroup[1]; \
+                    ppf_sample_0[2][i_sb] = f_scalefactor_0 * pf_ungroup[2]; \
+\
+                    ppf_sample_1[0][i_sb] = (f_scalefactor_1 = (pf_scalefactor_1)[i_sb]) * pf_ungroup[0]; \
+                    ppf_sample_1[1][i_sb] = f_scalefactor_1 * pf_ungroup[1]; \
+                    ppf_sample_1[2][i_sb] = f_scalefactor_1 * pf_ungroup[2]; \
+                } \
+            } \
+        } \
+\
+/* fprintf(stderr, "%p", p_s16); */ \
+        DCT32( ppf_sample_0[0], &p_adec->bank_0 ); \
+        PCM( &p_adec->bank_0, &p_s16, 2 ); \
+/* fprintf(stderr, " %p", p_s16); */ \
+        p_s16 -= 63; \
+/* fprintf(stderr, " %p\n", p_s16); */ \
+\
+/* fprintf(stderr, "%p", p_s16); */ \
+        DCT32( ppf_sample_1[0], &p_adec->bank_1 ); \
+        PCM( &p_adec->bank_1, &p_s16, 2 ); \
+/* fprintf(stderr, " %p", p_s16); */ \
+        p_s16 -= 1; \
+/* fprintf(stderr, " %p\n", p_s16); */ \
+\
+/* fprintf(stderr, "%p", p_s16); */ \
+        DCT32( ppf_sample_0[1], &p_adec->bank_0 ); \
+        PCM( &p_adec->bank_0, &p_s16, 2 ); \
+/* fprintf(stderr, " %p", p_s16); */ \
+        p_s16 -= 63; \
+/* fprintf(stderr, " %p\n", p_s16); */ \
+\
+/* fprintf(stderr, "%p", p_s16); */ \
+        DCT32( ppf_sample_1[1], &p_adec->bank_1 ); \
+        PCM( &p_adec->bank_1, &p_s16, 2 ); \
+/* fprintf(stderr, " %p", p_s16); */ \
+        p_s16 -= 1; \
+/* fprintf(stderr, " %p\n", p_s16); */ \
+\
+/* fprintf(stderr, "%p", p_s16); */ \
+        DCT32( ppf_sample_0[2], &p_adec->bank_0 ); \
+        PCM( &p_adec->bank_0, &p_s16, 2 ); \
+/* fprintf(stderr, " %p", p_s16); */ \
+        p_s16 -= 63; \
+/* fprintf(stderr, " %p\n", p_s16); */ \
+\
+/* fprintf(stderr, "%p", p_s16); */ \
+        DCT32( ppf_sample_1[2], &p_adec->bank_1 ); \
+        PCM( &p_adec->bank_1, &p_s16, 2 ); \
+/* fprintf(stderr, " %p", p_s16); */ \
+        p_s16 -= 1; \
+/* fprintf(stderr, " %p\n", p_s16); */ \
+    }
+/* #define READ_SAMPLE_L2S */
+
+    l_end_frame = p_adec->p_aout_fifo->l_end_frame;
+    i_gr = 0;
+
+    NEXT_BUF
+    READ_SAMPLE_L2S( pf_scalefactor_0_0, pf_scalefactor_1_0, 2 )
+
+    NEXT_BUF
+    READ_SAMPLE_L2S( pf_scalefactor_0_0, pf_scalefactor_1_0, 4 )
+
+    NEXT_BUF
+    READ_SAMPLE_L2S( pf_scalefactor_0_1, pf_scalefactor_1_1, 6 )
+
+    NEXT_BUF
+    READ_SAMPLE_L2S( pf_scalefactor_0_1, pf_scalefactor_1_1, 8 )
+
+    NEXT_BUF
+    READ_SAMPLE_L2S( pf_scalefactor_0_2, pf_scalefactor_1_2, 10 )
+
+    NEXT_BUF
+    READ_SAMPLE_L2S( pf_scalefactor_0_2, pf_scalefactor_1_2, 12 )
+
+//    fprintf(stderr, "adec debug: layer == %i, padding_bit == %i, sampling_frequency == %i, bitrate_index == %i\n",
+//        (i_header & ADEC_HEADER_LAYER_MASK) >> ADEC_HEADER_LAYER_SHIFT,
+//        (i_header & ADEC_HEADER_PADDING_BIT_MASK) >> ADEC_HEADER_PADDING_BIT_SHIFT,
+//        (i_header & ADEC_HEADER_SAMPLING_FREQUENCY_MASK) >> ADEC_HEADER_SAMPLING_FREQUENCY_SHIFT,
+//        (i_header & ADEC_HEADER_BITRATE_INDEX_MASK) >> ADEC_HEADER_BITRATE_INDEX_SHIFT);
+//    fprintf(stderr, "adec debug: framesize == %i, i_need == %i, i_dump == %i\n",
+//        pi_framesize[ 128 * ((i_header & ADEC_HEADER_LAYER_MASK) >> ADEC_HEADER_LAYER_SHIFT) +
+//            64 * ((i_header & ADEC_HEADER_PADDING_BIT_MASK) >> ADEC_HEADER_PADDING_BIT_SHIFT) +
+//            16 * ((i_header & ADEC_HEADER_SAMPLING_FREQUENCY_MASK) >> ADEC_HEADER_SAMPLING_FREQUENCY_SHIFT) +
+//            1 * ((i_header & ADEC_HEADER_BITRATE_INDEX_MASK) >> ADEC_HEADER_BITRATE_INDEX_SHIFT) ],
+//        i_need,
+//        i_dump);
+    p_adec->bit_stream.fifo.buffer = 0;
+    p_adec->bit_stream.fifo.i_available = 0;
+    return( 6 );
+}
+
+/******************************************************************************
+ * InitThread : initialize an audio decoder thread
+ ******************************************************************************
+ * This function is called from RunThread and performs the second step of the
+ * initialization. It returns 0 on success.
+ ******************************************************************************/
+static int InitThread( adec_thread_t * p_adec )
+{
+    aout_fifo_t         aout_fifo;
+
+    intf_DbgMsg("adec debug: initializing audio decoder thread %p\n", p_adec);
+
+    /* Our first job is to initialize the bit stream structure with the
+     * beginning of the input stream */
+    pthread_mutex_lock( &p_adec->fifo.data_lock );
+    while ( DECODER_FIFO_ISEMPTY(p_adec->fifo) )
+    {
+        pthread_cond_wait( &p_adec->fifo.data_wait, &p_adec->fifo.data_lock );
+    }
+    p_adec->bit_stream.p_ts = DECODER_FIFO_START( p_adec->fifo )->p_first_ts;
+    p_adec->bit_stream.i_byte = p_adec->bit_stream.p_ts->i_payload_start;
+    pthread_mutex_unlock( &p_adec->fifo.data_lock );
+
+    /* Now we look for an audio frame header in the input stream */
+    if ( FindHeader(p_adec) )
+    {
+        return( -1 );                              /* b_die or b_error is set */
+    }
+
+    /*
+     * We have the header and all its informations : we must be able to create
+     * the audio output fifo.
+     */
+
+    /* Is the sound in mono mode or stereo mode ? */
+    if ( (p_adec->bit_stream.fifo.buffer & ADEC_HEADER_MODE_MASK) == ADEC_HEADER_MODE_MASK )
+    {
+        intf_DbgMsg("adec debug: mode == mono\n");
+        aout_fifo.i_type = AOUT_ADEC_MONO_FIFO;
+        aout_fifo.b_stereo = 0;
+    }
+    else
+    {
+        intf_DbgMsg("adec debug: mode == stereo\n");
+        aout_fifo.i_type = AOUT_ADEC_STEREO_FIFO;
+        aout_fifo.b_stereo = 1;
+    }
+
+    /* Checking the sampling frequency */
+    switch ( (p_adec->bit_stream.fifo.buffer & ADEC_HEADER_SAMPLING_FREQUENCY_MASK) \
+             >> ADEC_HEADER_SAMPLING_FREQUENCY_SHIFT )
+    {
+        case 0:
+            intf_DbgMsg("adec debug: sampling_frequency == 44100 Hz\n");
+            aout_fifo.l_rate = 44100;
+            break;
+
+        case 1:
+            intf_DbgMsg("adec debug: sampling_frequency == 48000 Hz\n");
+            aout_fifo.l_rate = 48000;
+            break;
+
+        case 2:
+            intf_DbgMsg("adec debug: sampling_frequency == 32000 Hz\n");
+            aout_fifo.l_rate = 32000;
+            break;
+
+        case 3:
+            intf_ErrMsg("adec error: can't create audio output fifo (sampling_frequency == `reserved')\n");
+            return( -1 );
+    }
+
+    /* Creating the audio output fifo */
+    if ( (p_adec->p_aout_fifo = aout_CreateFifo(p_adec->p_aout, &aout_fifo)) == NULL )
+    {
+        return( -1 );
+    }
+
+    intf_DbgMsg("adec debug: audio decoder thread %p initialized\n", p_adec);
+    return( 0 );
+}
+
+/******************************************************************************
+ * RunThread : audio decoder thread
+ ******************************************************************************
+ * Audio decoder thread. This function does only returns when the thread is
+ * terminated.
+ ******************************************************************************/
+static void RunThread( adec_thread_t * p_adec )
+{
+//    static const int    pi_framesize[512] = ADEC_FRAME_SIZE;
+//    int                 i_header;
+//    int                 i_framesize;
+//    int                 i_dummy;
+    mtime_t             adec_date = 0;
+
+    intf_DbgMsg("adec debug: running audio decoder thread (%p) (pid == %i)\n", p_adec, getpid());
+
+    /* Initializing the audio decoder thread */
+    if ( InitThread(p_adec) )
+    {
+        p_adec->b_error = 1;
+    }
+
+    /* Audio decoder thread's main loop */
+    while ( (!p_adec->b_die) && (!p_adec->b_error) )
+    {
+        switch ( (p_adec->bit_stream.fifo.buffer & ADEC_HEADER_LAYER_MASK) >> ADEC_HEADER_LAYER_SHIFT )
+        {
+            case 0:
+                intf_DbgMsg("adec debug: layer == 0 (reserved)\n");
+                p_adec->bit_stream.fifo.buffer = 0;
+                p_adec->bit_stream.fifo.i_available = 0;
+                break;
+
+            case 1:
+                p_adec->bit_stream.fifo.buffer = 0;
+                p_adec->bit_stream.fifo.i_available = 0;
+                break;
+
+            case 2:
+                if ( (p_adec->bit_stream.fifo.buffer & ADEC_HEADER_MODE_MASK) == ADEC_HEADER_MODE_MASK )
+                {
+                    adec_Layer2_Mono( p_adec );
+                }
+                else
+                {
+//                    i_header = p_adec->bit_stream.fifo.buffer;
+//                    i_framesize = pi_framesize[ 128*((i_header & ADEC_HEADER_LAYER_MASK) >> ADEC_HEADER_LAYER_SHIFT) +
+//                        64*((i_header & ADEC_HEADER_PADDING_BIT_MASK) >> ADEC_HEADER_PADDING_BIT_SHIFT) +
+//                        16*((i_header & ADEC_HEADER_SAMPLING_FREQUENCY_MASK) >> ADEC_HEADER_SAMPLING_FREQUENCY_SHIFT) +
+//                        1*((i_header & ADEC_HEADER_BITRATE_INDEX_MASK) >> ADEC_HEADER_BITRATE_INDEX_SHIFT) ];
+//                    for ( i_dummy = 0; i_dummy < i_framesize; i_dummy++ )
+//                    {
+//                        GetByte( &p_adec->bit_stream );
+//                    }
+//                    for ( i_dummy = 0; i_dummy < 512; i_dummy++ )
+//                    {
+//                        p_adec->bank_0.v1[ i_dummy ] = .0;
+//                        p_adec->bank_1.v1[ i_dummy ] = .0;
+//                        p_adec->bank_0.v2[ i_dummy ] = .0;
+//                        p_adec->bank_1.v2[ i_dummy ] = .0;
+//                    }
+
+                    pthread_mutex_lock( &p_adec->p_aout_fifo->data_lock );
+                    while ( (((p_adec->p_aout_fifo->l_end_frame + 6) - p_adec->p_aout_fifo->l_start_frame) & AOUT_FIFO_SIZE) < 6 ) /* !! */
+                    {
+                        pthread_cond_wait( &p_adec->p_aout_fifo->data_wait, &p_adec->p_aout_fifo->data_lock );
+                    }
+                    pthread_mutex_unlock( &p_adec->p_aout_fifo->data_lock );
+
+                    if ( adec_Layer2_Stereo(p_adec) )
+                    {
+                        pthread_mutex_lock( &p_adec->p_aout_fifo->data_lock );
+                        /* Frame 1 */
+                        p_adec->p_aout_fifo->date[p_adec->p_aout_fifo->l_end_frame] = adec_date;
+                        p_adec->p_aout_fifo->l_end_frame += 1;
+                        p_adec->p_aout_fifo->l_end_frame &= AOUT_FIFO_SIZE;
+                        /* Frame 2 */
+                        p_adec->p_aout_fifo->date[p_adec->p_aout_fifo->l_end_frame] = LAST_MDATE;
+                        p_adec->p_aout_fifo->l_end_frame += 1;
+                        p_adec->p_aout_fifo->l_end_frame &= AOUT_FIFO_SIZE;
+                        /* Frame 3 */
+                        p_adec->p_aout_fifo->date[p_adec->p_aout_fifo->l_end_frame] = LAST_MDATE;
+                        p_adec->p_aout_fifo->l_end_frame += 1;
+                        p_adec->p_aout_fifo->l_end_frame &= AOUT_FIFO_SIZE;
+                        /* Frame 4 */
+                        p_adec->p_aout_fifo->date[p_adec->p_aout_fifo->l_end_frame] = LAST_MDATE;
+                        p_adec->p_aout_fifo->l_end_frame += 1;
+                        p_adec->p_aout_fifo->l_end_frame &= AOUT_FIFO_SIZE;
+                        /* Frame 5 */
+                        p_adec->p_aout_fifo->date[p_adec->p_aout_fifo->l_end_frame] = LAST_MDATE;
+                        p_adec->p_aout_fifo->l_end_frame += 1;
+                        p_adec->p_aout_fifo->l_end_frame &= AOUT_FIFO_SIZE;
+                        /* Frame 6 */
+                        p_adec->p_aout_fifo->date[p_adec->p_aout_fifo->l_end_frame] = LAST_MDATE;
+                        p_adec->p_aout_fifo->l_end_frame += 1;
+                        p_adec->p_aout_fifo->l_end_frame &= AOUT_FIFO_SIZE;
+                        pthread_mutex_unlock( &p_adec->p_aout_fifo->data_lock );
+                        adec_date += 24000; /* !! */
+                    }
+                }
+                break;
+
+            case 3:
+                if ( (p_adec->bit_stream.fifo.buffer & ADEC_HEADER_MODE_MASK) == ADEC_HEADER_MODE_MASK )
+                {
+                    adec_Layer1_Mono( p_adec );
+                }
+                else
+                {
+                    adec_Layer1_Stereo( p_adec );
+                }
+                break;
+
+            default:
+                intf_DbgMsg("adec debug: layer == %i (unknown)\n",
+                    (p_adec->bit_stream.fifo.buffer & ADEC_HEADER_LAYER_MASK) >> ADEC_HEADER_LAYER_SHIFT);
+                p_adec->bit_stream.fifo.buffer = 0;
+                p_adec->bit_stream.fifo.i_available = 0;
+                break;
+        }
+        FindHeader( p_adec );
+    }
+
+    /* If b_error is set, the audio decoder thread enters the error loop */
+    if ( p_adec->b_error )
+    {
+        ErrorThread( p_adec );
+    }
+
+    /* End of the audio decoder thread */
+    EndThread( p_adec );
+}
+
+/******************************************************************************
+ * ErrorThread : audio decoder's RunThread() error loop
+ ******************************************************************************
+ * This function is called when an error occured during thread main's loop. The
+ * thread can still receive feed, but must be ready to terminate as soon as
+ * possible.
+ ******************************************************************************/
+static void ErrorThread( adec_thread_t *p_adec )
+{
+    /* We take the lock, because we are going to read/write the start/end
+     * indexes of the decoder fifo */
+    pthread_mutex_lock( &p_adec->fifo.data_lock );
+
+    /* Wait until a `die' order is sent */
+    while( !p_adec->b_die )
+    {
+        /* Trash all received PES packets */
+        while( !DECODER_FIFO_ISEMPTY(p_adec->fifo) )
+        {
+            input_NetlistFreePES( p_adec->bit_stream.p_input, DECODER_FIFO_START(p_adec->fifo) );
+           DECODER_FIFO_INCSTART( p_adec->fifo );
+#ifdef DEBUG
+//            fprintf(stderr, "*");
+#endif
+        }
+
+        /* Waiting for the input thread to put new PES packets in the fifo */
+        pthread_cond_wait( &p_adec->fifo.data_wait, &p_adec->fifo.data_lock );
+    }
+
+    /* We can release the lock before leaving */
+    pthread_mutex_unlock( &p_adec->fifo.data_lock );
+}
+
+/******************************************************************************
+ * EndThread : audio decoder thread destruction
+ ******************************************************************************
+ * This function is called when the thread ends after a sucessfull 
+ * initialization.
+ ******************************************************************************/
+static void EndThread( adec_thread_t *p_adec )
+{
+    intf_DbgMsg("adec debug: destroying audio decoder thread %p\n", p_adec);
+
+    /* If the audio output fifo was created, we destroy it */
+    if ( p_adec->p_aout_fifo != NULL )
+    {
+        aout_DestroyFifo( p_adec->p_aout_fifo );
+    }
+    /* Destroy descriptor */
+    free( p_adec );
+
+    intf_DbgMsg("adec debug: audio decoder thread %p destroyed\n", p_adec);
+}
diff --git a/src/audio_output/audio_dsp.c b/src/audio_output/audio_dsp.c
new file mode 100644 (file)
index 0000000..074846a
--- /dev/null
@@ -0,0 +1,176 @@
+/******************************************************************************
+ * audio_dsp.c : dsp functions library
+ * (c)1999 VideoLAN
+ ******************************************************************************/
+
+/* TODO:
+ *
+ * - an aout_dspGetFormats() function
+ * - dsp inline/static
+ * - make this library portable (see mpg123)
+ * - macroify aout_dspPlaySamples &/| aout_dspGetBufInfo ?
+ *
+ */
+
+/******************************************************************************
+ * Preamble
+ ******************************************************************************/
+#include <pthread.h>
+#include <fcntl.h>                                        /* open(), O_WRONLY */
+#include <sys/ioctl.h>                                             /* ioctl() */
+#include <unistd.h>                                       /* write(), close() */
+/* SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_STEREO, SNDCTL_DSP_SPEED, SNDCTL_DSP_GETOSPACE */
+#include <sys/soundcard.h>
+
+#include "common.h"                                      /* boolean_t, byte_t */
+#include "mtime.h"
+
+#include "audio_output.h"                                       /* aout_dsp_t */
+#include "audio_dsp.h"
+
+#include "intf_msg.h"                         /* intf_DbgMsg(), intf_ErrMsg() */
+
+/******************************************************************************
+ * aout_dspOpen: opens the audio device (the digital sound processor)
+ ******************************************************************************
+ * - This function opens the dsp as an usual non-blocking write-only file, and
+ *   modifies the p_dsp->i_fd with the file's descriptor.
+ * - p_dsp->psz_device must be set before calling this function !
+ ******************************************************************************/
+int aout_dspOpen( aout_dsp_t *p_dsp )
+{
+    if ( (p_dsp->i_fd = open( p_dsp->psz_device, O_WRONLY )) < 0 )
+    {
+        intf_ErrMsg( "aout error: can't open audio device (%s)\n", p_dsp->psz_device );
+       return( -1 );
+    }
+
+    return( 0 );
+}
+
+/******************************************************************************
+ * aout_dspReset: resets the dsp
+ ******************************************************************************/
+int aout_dspReset( aout_dsp_t *p_dsp )
+{
+    if ( ioctl( p_dsp->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 )
+    {
+        intf_ErrMsg( "aout error: can't reset audio device (%s)\n", p_dsp->psz_device );
+       return( -1 );
+    }
+
+    return( 0 );
+}
+
+/******************************************************************************
+ * aout_dspSetFormat: sets the dsp output format
+ ******************************************************************************
+ * This functions tries to initialize the dsp output format with the value
+ * contained in the dsp structure, and if this value could not be set, the
+ * default value returned by ioctl is set.
+ ******************************************************************************/
+int aout_dspSetFormat( aout_dsp_t *p_dsp )
+{
+    int i_format;
+
+    i_format = p_dsp->i_format;
+    if ( ioctl( p_dsp->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 )
+    {
+        intf_ErrMsg( "aout error: can't set audio output format (%i)\n", p_dsp->i_format );
+       return( -1 );
+    }
+
+    if ( i_format != p_dsp->i_format )
+    {
+        intf_DbgMsg( "aout debug: audio output format not supported (%i)\n", p_dsp->i_format );
+       p_dsp->i_format = i_format;
+    }
+
+    return( 0 );
+}
+
+/******************************************************************************
+ * aout_dspSetChannels: sets the dsp's stereo or mono mode
+ ******************************************************************************
+ * This function acts just like the previous one...
+ ******************************************************************************/
+int aout_dspSetChannels( aout_dsp_t *p_dsp )
+{
+    boolean_t b_stereo;
+
+    b_stereo = p_dsp->b_stereo;
+    if ( ioctl( p_dsp->i_fd, SNDCTL_DSP_STEREO, &b_stereo ) < 0 )
+    {
+        intf_ErrMsg( "aout error: can't set number of audio channels (%i)\n", p_dsp->b_stereo );
+       return( -1 );
+    }
+
+    if ( b_stereo != p_dsp->b_stereo )
+    {
+        intf_DbgMsg( "aout debug: number of audio channels not supported (%i)\n", p_dsp->b_stereo );
+       p_dsp->b_stereo = b_stereo;
+    }
+
+    return( 0 );
+}
+
+/******************************************************************************
+ * aout_dspSetRate: sets the dsp's audio output rate
+ ******************************************************************************
+ * This function tries to initialize the dsp with the rate contained in the
+ * dsp structure, but if the dsp doesn't support this value, the function uses
+ * the value returned by ioctl...
+ ******************************************************************************/
+int aout_dspSetRate( aout_dsp_t *p_dsp )
+{
+    long l_rate;
+
+    l_rate = p_dsp->l_rate;
+    if ( ioctl( p_dsp->i_fd, SNDCTL_DSP_SPEED, &l_rate ) < 0 )
+    {
+        intf_ErrMsg( "aout error: can't set audio output rate (%li)\n", p_dsp->l_rate );
+       return( -1 );
+    }
+
+    if ( l_rate != p_dsp->l_rate )
+    {
+        intf_DbgMsg( "aout debug: audio output rate not supported (%li)\n", p_dsp->l_rate );
+       p_dsp->l_rate = l_rate;
+    }
+
+    return( 0 );
+}
+
+/******************************************************************************
+ * aout_dspGetBufInfo: buffer status query
+ ******************************************************************************
+ * This function fills in the audio_buf_info structure :
+ * - int fragments : number of available fragments (partially usend ones not
+ *   counted)
+ * - int fragstotal : total number of fragments allocated
+ * - int fragsize : size of a fragment in bytes
+ * - int bytes : available space in bytes (includes partially used fragments)
+ * Note! 'bytes' could be more than fragments*fragsize
+ ******************************************************************************/
+void aout_dspGetBufInfo( aout_dsp_t *p_dsp )
+{
+    ioctl( p_dsp->i_fd, SNDCTL_DSP_GETOSPACE, &p_dsp->buf_info );
+}
+
+/******************************************************************************
+ * aout_dspPlaySamples: plays a sound samples buffer
+ ******************************************************************************
+ * This function writes a buffer of i_length bytes in the dsp
+ ******************************************************************************/
+void aout_dspPlaySamples( aout_dsp_t *p_dsp, byte_t *buffer, int i_size )
+{
+    write( p_dsp->i_fd, buffer, i_size );
+}
+
+/******************************************************************************
+ * aout_dspClose: closes the dsp audio device
+ ******************************************************************************/
+void aout_dspClose( aout_dsp_t *p_dsp )
+{
+    close( p_dsp->i_fd );
+}
diff --git a/src/audio_output/audio_output.c b/src/audio_output/audio_output.c
new file mode 100644 (file)
index 0000000..9461471
--- /dev/null
@@ -0,0 +1,806 @@
+/******************************************************************************
+ * audio_output.c : audio output thread
+ * (c)1999 VideoLAN
+ ******************************************************************************/
+
+/* TODO:
+ *
+ * - Passer un certain nombre de "fonctions" (genre add_samples) en macro ou
+ *   inline
+ * - Faire les optimisations dans les fonctions threads :
+ *   = Stocker les "petits calculs" dans des variables au lieu de les refaire
+ *     Ã  chaque boucle
+ *   = Utiliser des tables pour les gros calculs
+ * - Faire une structure différente pour intf/adec fifo
+ * - Rajouter des pthread_cond_signal ?
+ *
+ */
+
+/******************************************************************************
+ * Preamble
+ ******************************************************************************/
+#include <unistd.h>
+
+#include <pthread.h>
+#include <sys/soundcard.h>
+#include <stdio.h>                                            /* "intf_msg.h" */
+#include <stdlib.h>                             /* calloc(), malloc(), free() */
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"                              /* mtime_t, mdate(), msleep() */
+
+#include "intf_msg.h"                         /* intf_DbgMsg(), intf_ErrMsg() */
+
+#include "audio_output.h"
+#include "audio_dsp.h"
+
+/******************************************************************************
+ * Local prototypes
+ ******************************************************************************/
+
+/* Creating as much aout_Thread functions as configurations is one solution,
+ * examining the different cases in the Thread loop of an unique function is
+ * another. I chose the first solution. */
+void aout_Thread_S8_Mono        ( aout_thread_t * p_aout );
+void aout_Thread_U8_Mono        ( aout_thread_t * p_aout );
+void aout_Thread_S16_Mono       ( aout_thread_t * p_aout );
+void aout_Thread_U16_Mono       ( aout_thread_t * p_aout );
+void aout_Thread_S8_Stereo      ( aout_thread_t * p_aout );
+void aout_Thread_U8_Stereo      ( aout_thread_t * p_aout );
+void aout_Thread_S16_Stereo     ( aout_thread_t * p_aout );
+void aout_Thread_U16_Stereo     ( aout_thread_t * p_aout );
+
+static __inline__ void InitializeIncrement( aout_increment_t * p_increment, long l_numerator, long l_denominator );
+static __inline__ int NextFrame( aout_thread_t * p_aout, aout_fifo_t * p_fifo );
+
+/******************************************************************************
+ * aout_Open
+ ******************************************************************************/
+int aout_Open( aout_thread_t * p_aout )
+{
+    if ( aout_dspOpen( &p_aout->dsp ) )
+    {
+        return( -1 );
+    }
+
+    if ( aout_dspReset( &p_aout->dsp ) )
+    {
+       aout_dspClose( &p_aout->dsp );
+       return( -1 );
+    }
+
+    if ( aout_dspSetFormat( &p_aout->dsp ) )
+    {
+       aout_dspClose( &p_aout->dsp );
+       return( -1 );
+    }
+
+    if ( aout_dspSetChannels( &p_aout->dsp ) )
+    {
+       aout_dspClose( &p_aout->dsp );
+        return( -1 );
+    }
+
+    if ( aout_dspSetRate( &p_aout->dsp ) )
+    {
+       aout_dspClose( &p_aout->dsp );
+       return( -1 );
+    }
+
+    intf_DbgMsg("aout debug: audio device (%s) opened (format=%i, stereo=%i, rate=%li)\n",
+        p_aout->dsp.psz_device,
+        p_aout->dsp.i_format,
+        p_aout->dsp.b_stereo, p_aout->dsp.l_rate);
+
+    return( 0 );
+}
+
+/******************************************************************************
+ * aout_SpawnThread
+ ******************************************************************************/
+int aout_SpawnThread( aout_thread_t * p_aout )
+{
+    int         i_fifo;
+    long        l_bytes;
+    s64         s64_numerator, s64_denominator;
+    void *      aout_thread = NULL;
+
+    intf_DbgMsg("aout debug: spawning audio output thread (%p)\n", p_aout);
+
+    /* We want the audio output thread to live */
+    p_aout->b_die = 0;
+
+    /* Initialize the fifos lock */
+    pthread_mutex_init( &p_aout->fifos_lock, NULL );
+    /* Initialize audio fifos : set all fifos as empty and initialize locks */
+    for ( i_fifo = 0; i_fifo < AOUT_MAX_FIFOS; i_fifo++ )
+    {
+        p_aout->fifo[i_fifo].i_type = AOUT_EMPTY_FIFO;
+        pthread_mutex_init( &p_aout->fifo[i_fifo].data_lock, NULL );
+        pthread_cond_init( &p_aout->fifo[i_fifo].data_wait, NULL );
+    }
+
+    /* Compute the size (in audio units) of the audio output buffer. Although
+     * AOUT_BUFFER_DURATION is given in microseconds, the output rate is given
+     * in Hz, that's why we need to divide by 10^6 microseconds (1 second) */
+    p_aout->l_units = (long)( ((s64)p_aout->dsp.l_rate * AOUT_BUFFER_DURATION) / 1000000 );
+
+    /* Make aout_thread point to the right thread function, and compute the
+     * byte size of the audio output buffer */
+    switch ( p_aout->dsp.b_stereo )
+    {
+        /* Audio output is mono */
+        case 0:
+            switch ( p_aout->dsp.i_format )
+            {
+                case AFMT_U8:
+                    l_bytes = 1 * sizeof(u8) * p_aout->l_units;
+                    aout_thread = (void *)aout_Thread_U8_Mono;
+                    break;
+
+                case AFMT_S8:
+                    l_bytes = 1 * sizeof(s8) * p_aout->l_units;
+                    aout_thread = (void *)aout_Thread_S8_Mono;
+                    break;
+
+                case AFMT_U16_LE:
+                case AFMT_U16_BE:
+                    l_bytes = 1 * sizeof(u16) * p_aout->l_units;
+                    aout_thread = (void *)aout_Thread_U16_Mono;
+                    break;
+
+                case AFMT_S16_LE:
+                case AFMT_S16_BE:
+                    l_bytes = 1 * sizeof(s16) * p_aout->l_units;
+                    aout_thread = (void *)aout_Thread_S16_Mono;
+                    break;
+
+                default:
+                    intf_ErrMsg("aout error: unknown audio output format (%i)\n",
+                        p_aout->dsp.i_format);
+                   return( -1 );
+            }
+            break;
+
+        /* Audio output is stereo */
+        case 1:
+            switch ( p_aout->dsp.i_format )
+            {
+                case AFMT_U8:
+                    l_bytes = 2 * sizeof(u8) * p_aout->l_units;
+                    aout_thread = (void *)aout_Thread_U8_Stereo;
+                    break;
+
+                case AFMT_S8:
+                    l_bytes = 2 * sizeof(s8) * p_aout->l_units;
+                    aout_thread = (void *)aout_Thread_S8_Stereo;
+                    break;
+
+                case AFMT_U16_LE:
+                case AFMT_U16_BE:
+                    l_bytes = 2 * sizeof(u16) * p_aout->l_units;
+                    aout_thread = (void *)aout_Thread_U16_Stereo;
+                    break;
+
+                case AFMT_S16_LE:
+                case AFMT_S16_BE:
+                    l_bytes = 2 * sizeof(s16) * p_aout->l_units;
+                    aout_thread = (void *)aout_Thread_S16_Stereo;
+                    break;
+
+                default:
+                    intf_ErrMsg("aout error: unknown audio output format (%i)\n",
+                        p_aout->dsp.i_format);
+                    return( -1 );
+            }
+            break;
+
+        default:
+            intf_ErrMsg("aout error: unknown number of audio channels (%i)\n",
+                p_aout->dsp.b_stereo + 1);
+            return( -1 );
+    }
+
+    /* Allocate the memory needed by the audio output buffers, and set to zero
+     * the s32 buffer's memory */
+    if ( (p_aout->buffer = malloc(l_bytes)) == NULL )
+    {
+        intf_ErrMsg("aout error: not enough memory to create the output buffer\n");
+        return( -1 );
+    }
+    if ( (p_aout->s32_buffer = (s32 *)calloc(p_aout->l_units, sizeof(s32) << p_aout->dsp.b_stereo)) == NULL )
+    {
+        intf_ErrMsg("aout error: not enough memory to create the s32 output buffer\n");
+        free( p_aout->buffer );
+        return( -1 );
+    }
+
+    /* Initialize the incremental structure that is used to work out the date
+     * of the first audio unit in the output buffer */
+    s64_numerator = (s64)p_aout->l_units * 1000000;
+    s64_denominator = (s64)p_aout->dsp.l_rate;
+
+    p_aout->date_increment.l_remainder = -(long)s64_denominator;
+
+    p_aout->date_increment.l_euclidean_integer = 0;
+    while ( s64_numerator >= s64_denominator )
+    {
+        p_aout->date_increment.l_euclidean_integer++;
+        s64_numerator -= s64_denominator;
+    }
+
+    p_aout->date_increment.l_euclidean_remainder = (long)s64_numerator;
+
+    p_aout->date_increment.l_euclidean_denominator = (long)s64_denominator;
+
+    /* Before launching the thread, we try to predict the date of the first
+     * audio unit in the first output buffer */
+    p_aout->date = mdate();
+
+    /* Launch the thread */
+    if ( pthread_create( &p_aout->thread_id, NULL, aout_thread, p_aout ) )
+    {
+        intf_ErrMsg("aout error: can't spawn audio output thread (%p)\n", p_aout);
+        free( p_aout->buffer );
+        free( p_aout->s32_buffer );
+        return( -1 );
+    }
+
+    intf_DbgMsg("aout debug: audio output thread (%p) spawned\n", p_aout);
+    return( 0 );
+}
+
+/******************************************************************************
+ * aout_CancelThread
+ ******************************************************************************/
+void aout_CancelThread( aout_thread_t * p_aout )
+{
+    intf_DbgMsg("aout debug: requesting termination of audio output thread (%p)\n", p_aout);
+
+    /* Ask thread to kill itself and wait until it's done */
+    p_aout->b_die = 1;
+    pthread_join( p_aout->thread_id, NULL );
+
+    /* Free the allocated memory */
+    free( p_aout->buffer );
+    free( p_aout->s32_buffer );
+}
+
+/******************************************************************************
+ * aout_Close
+ ******************************************************************************/
+void aout_Close( aout_thread_t * p_aout )
+{
+    aout_dspClose( &p_aout->dsp );
+    intf_DbgMsg("aout debug: audio device (%s) closed\n", p_aout->dsp.psz_device);
+}
+
+/******************************************************************************
+ * aout_CreateFifo
+ ******************************************************************************/
+aout_fifo_t * aout_CreateFifo( aout_thread_t * p_aout, aout_fifo_t * p_fifo )
+{
+    int i_fifo;
+
+    /* Take the fifos lock */
+    pthread_mutex_lock( &p_aout->fifos_lock );
+
+    /* Looking for a free fifo structure */
+    for ( i_fifo = 0; i_fifo < AOUT_MAX_FIFOS; i_fifo++ )
+    {
+        if ( p_aout->fifo[i_fifo].i_type == AOUT_EMPTY_FIFO)
+       {
+            break;
+        }
+    }
+    if ( i_fifo == AOUT_MAX_FIFOS )
+    {
+        intf_ErrMsg("aout error: no empty fifo available\n");
+        pthread_mutex_unlock( &p_aout->fifos_lock );
+        return( NULL );
+    }
+
+    /* Initialize the new fifo structure */
+    switch ( p_aout->fifo[i_fifo].i_type = p_fifo->i_type )
+    {
+        case AOUT_INTF_MONO_FIFO:
+        case AOUT_INTF_STEREO_FIFO:
+           p_aout->fifo[i_fifo].b_die = 0;
+
+           p_aout->fifo[i_fifo].b_stereo = p_fifo->b_stereo;
+           p_aout->fifo[i_fifo].l_rate = p_fifo->l_rate;
+
+            p_aout->fifo[i_fifo].buffer = p_fifo->buffer;
+
+            p_aout->fifo[i_fifo].l_unit = 0;
+            InitializeIncrement( &p_aout->fifo[i_fifo].unit_increment, p_fifo->l_rate, p_aout->dsp.l_rate );
+            p_aout->fifo[i_fifo].l_units = p_fifo->l_units;
+            break;
+
+        case AOUT_ADEC_MONO_FIFO:
+        case AOUT_ADEC_STEREO_FIFO:
+            p_aout->b_die = 0;
+
+            p_aout->fifo[i_fifo].b_stereo = p_fifo->b_stereo;
+            p_aout->fifo[i_fifo].l_rate = p_fifo->l_rate;
+
+            /* Allocate the memory needed to store the audio frames. As the
+             * fifo is a rotative fifo, we must be able to find out whether the
+             * fifo is full or empty, that's why we must in fact allocate memory
+             * for (AOUT_FIFO_SIZE+1) audio frames. */
+           if ( (p_aout->fifo[i_fifo].buffer = malloc( sizeof(s16)*(AOUT_FIFO_SIZE+1)*AOUT_FRAME_SIZE )) == NULL )
+           {
+                intf_ErrMsg("aout error: not enough memory to create the frames buffer\n");
+                p_aout->fifo[i_fifo].i_type = AOUT_EMPTY_FIFO;
+               pthread_mutex_unlock( &p_aout->fifos_lock );
+               return( NULL );
+           }
+
+            /* Allocate the memory needed to store the dates of the frames */
+            if ( (p_aout->fifo[i_fifo].date = (mtime_t *)malloc( sizeof(mtime_t)*(AOUT_FIFO_SIZE+1) )) == NULL )
+            {
+                intf_ErrMsg("aout error: not enough memory to create the dates buffer\n");
+                free( p_aout->fifo[i_fifo].buffer );
+                p_aout->fifo[i_fifo].i_type = AOUT_EMPTY_FIFO;
+                pthread_mutex_unlock( &p_aout->fifos_lock );
+                return( NULL );
+            }
+
+            /* Set the fifo's buffer as empty (the first frame that is to be
+             * played is also the first frame that is not to be played) */
+            p_aout->fifo[i_fifo].l_start_frame = 0;
+            /* p_aout->fifo[i_fifo].l_next_frame = 0; */
+            p_aout->fifo[i_fifo].l_end_frame = 0;
+
+            /* Waiting for the audio decoder to compute enough frames to work
+             * out the fifo's current rate (as soon as the decoder has decoded
+             * enough frames, the members of the fifo structure that are not
+             * initialized now will be calculated) */
+            p_aout->fifo[i_fifo].l_unit = 0; /* !! */
+            p_aout->fifo[i_fifo].b_start_frame = 0;
+            p_aout->fifo[i_fifo].b_next_frame = 0;
+            break;
+
+        default:
+            intf_ErrMsg("aout error: unknown fifo type (%i)\n", p_aout->fifo[i_fifo].i_type);
+            p_aout->fifo[i_fifo].i_type = AOUT_EMPTY_FIFO;
+            pthread_mutex_unlock( &p_aout->fifos_lock );
+            return( NULL );
+    }
+
+    /* Release the fifos lock */
+    pthread_mutex_unlock( &p_aout->fifos_lock );
+
+    /* Return the pointer to the fifo structure */
+    intf_DbgMsg("aout debug: audio output fifo (%p) allocated\n", &p_aout->fifo[i_fifo]);
+    return( &p_aout->fifo[i_fifo] );
+}
+
+/******************************************************************************
+ * aout_DestroyFifo
+ ******************************************************************************/
+void aout_DestroyFifo( aout_fifo_t * p_fifo )
+{
+    intf_DbgMsg("aout debug: requesting destruction of audio output fifo (%p)\n", p_fifo);
+    p_fifo->b_die = 1;
+}
+
+/* Here are the local macros */
+
+#define S32_TO_S16( sample ) \
+    (s16)( (sample) )
+
+#define UPDATE_INCREMENT( increment, integer ) \
+    if ( ((increment).l_remainder += (increment).l_euclidean_remainder) >= 0 ) \
+    { \
+        (integer) += (increment).l_euclidean_integer + 1; \
+        (increment).l_remainder -= (increment).l_euclidean_denominator; \
+    } \
+    else \
+    { \
+        (integer) += (increment).l_euclidean_integer; \
+    }
+
+/* Following functions are local */
+
+/******************************************************************************
+ * InitializeIncrement
+ ******************************************************************************/
+static __inline__ void InitializeIncrement( aout_increment_t * p_increment, long l_numerator, long l_denominator )
+{
+    p_increment->l_remainder = -l_denominator;
+
+    p_increment->l_euclidean_integer = 0;
+    while ( l_numerator >= l_denominator )
+    {
+        p_increment->l_euclidean_integer++;
+        l_numerator -= l_denominator;
+    }
+
+    p_increment->l_euclidean_remainder = l_numerator;
+
+    p_increment->l_euclidean_denominator = l_denominator;
+}
+
+/******************************************************************************
+ * NextFrame
+ ******************************************************************************/
+static __inline__ int NextFrame( aout_thread_t * p_aout, aout_fifo_t * p_fifo )
+{
+    long l_units, l_rate;
+
+    /* We take the lock */
+    pthread_mutex_lock( &p_fifo->data_lock );
+
+    /* Are we looking for a dated start frame ? */
+    if ( !p_fifo->b_start_frame )
+    {
+        while ( p_fifo->l_start_frame != p_fifo->l_end_frame )
+        {
+            if ( p_fifo->date[p_fifo->l_start_frame] != LAST_MDATE )
+            {
+                p_fifo->b_start_frame = 1;
+                p_fifo->l_next_frame = (p_fifo->l_start_frame + 1) & AOUT_FIFO_SIZE;
+                break;
+            }
+            p_fifo->l_start_frame = (p_fifo->l_start_frame + 1) & AOUT_FIFO_SIZE;
+        }
+        if ( p_fifo->l_start_frame == p_fifo->l_end_frame )
+        {
+            pthread_mutex_unlock( &p_fifo->data_lock );
+            return( -1 );
+        }
+    }
+
+    /* We are looking for the next dated frame */
+    while ( p_fifo->l_next_frame != p_fifo->l_end_frame )
+    {
+        if ( p_fifo->date[p_fifo->l_next_frame] != LAST_MDATE )
+        {
+            p_fifo->b_next_frame = 1;
+            break;
+        }
+        p_fifo->l_next_frame = (p_fifo->l_next_frame + 1) & AOUT_FIFO_SIZE;        
+    }
+    if ( p_fifo->l_next_frame == p_fifo->l_end_frame )
+    {
+        if ( (((p_fifo->l_end_frame + 1) - p_fifo->l_start_frame) & AOUT_FIFO_SIZE) == 0 )
+        {
+            p_fifo->l_start_frame = 0;
+            p_fifo->b_start_frame = 0;
+            /* p_fifo->l_next_frame = 0; */
+            /* p_fifo->b_next_frame = 0; */
+            p_fifo->l_end_frame = 0;
+        }
+        pthread_mutex_unlock( &p_fifo->data_lock );
+        return( -1 );
+    }
+
+    l_units = ((p_fifo->l_next_frame - p_fifo->l_start_frame) & AOUT_FIFO_SIZE)
+        * (AOUT_FRAME_SIZE >> p_fifo->b_stereo);
+
+    l_rate = (long)( ((mtime_t)l_units * 1000000)
+        / (p_fifo->date[p_fifo->l_next_frame] - p_fifo->date[p_fifo->l_start_frame]) );
+
+    InitializeIncrement( &p_fifo->unit_increment, l_rate, p_aout->dsp.l_rate );
+
+    p_fifo->l_units = (((l_units - (p_fifo->l_unit -
+        (p_fifo->l_start_frame * (AOUT_FRAME_SIZE >> p_fifo->b_stereo))))
+        * p_aout->dsp.l_rate) / l_rate) + 1;
+
+    /* We release the lock before leaving */
+    pthread_mutex_unlock( &p_fifo->data_lock );
+    return( 0 );
+}
+
+void aout_Thread_S8_Mono( aout_thread_t * p_aout )
+{
+}
+
+void aout_Thread_S8_Stereo( aout_thread_t * p_aout )
+{
+}
+
+void aout_Thread_U8_Mono( aout_thread_t * p_aout )
+{
+}
+
+void aout_Thread_U8_Stereo( aout_thread_t * p_aout )
+{
+}
+
+void aout_Thread_S16_Mono( aout_thread_t * p_aout )
+{
+}
+
+void aout_Thread_S16_Stereo( aout_thread_t * p_aout )
+{
+    int i_fifo;
+    long l_units;
+    long l_buffer, l_buffer_limit;
+
+    intf_DbgMsg("adec debug: running audio output thread (%p) (pid == %i)\n", p_aout, getpid());
+
+    /* As the s32_buffer was created with calloc(), we don't have to set this
+     * memory to zero and we can immediately jump into the thread's loop */
+    while ( !p_aout->b_die )
+    {
+        pthread_mutex_lock( &p_aout->fifos_lock );
+        for ( i_fifo = 0; i_fifo < AOUT_MAX_FIFOS; i_fifo++ )
+       {
+            switch ( p_aout->fifo[i_fifo].i_type )
+           {
+               case AOUT_EMPTY_FIFO:
+                    break;
+
+               case AOUT_INTF_MONO_FIFO:
+                    if ( p_aout->fifo[i_fifo].l_units > p_aout->l_units )
+                   {
+                        l_buffer = 0;
+                        while ( l_buffer < (p_aout->l_units << 1) ) /* p_aout->dsp.b_stereo == 1 */
+                       {
+                            p_aout->s32_buffer[l_buffer++] +=
+                                (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[p_aout->fifo[i_fifo].l_unit] );
+                            p_aout->s32_buffer[l_buffer++] +=
+                                (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[p_aout->fifo[i_fifo].l_unit] );
+                            UPDATE_INCREMENT( p_aout->fifo[i_fifo].unit_increment, p_aout->fifo[i_fifo].l_unit )
+                        }
+                        p_aout->fifo[i_fifo].l_units -= p_aout->l_units;
+                    }
+                    else
+                   {
+                        l_buffer = 0;
+                        while ( l_buffer < (p_aout->fifo[i_fifo].l_units << 1) ) /* p_aout->dsp.b_stereo == 1 */
+                       {
+                            p_aout->s32_buffer[l_buffer++] +=
+                                (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[p_aout->fifo[i_fifo].l_unit] );
+                            p_aout->s32_buffer[l_buffer++] +=
+                                (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[p_aout->fifo[i_fifo].l_unit] );
+                            UPDATE_INCREMENT( p_aout->fifo[i_fifo].unit_increment, p_aout->fifo[i_fifo].l_unit )
+                        }
+                        free( p_aout->fifo[i_fifo].buffer ); /* !! */
+                        p_aout->fifo[i_fifo].i_type = AOUT_EMPTY_FIFO; /* !! */
+                        intf_DbgMsg("aout debug: audio output fifo (%p) destroyed\n", &p_aout->fifo[i_fifo]); /* !! */
+                    }
+                    break;
+
+                case AOUT_INTF_STEREO_FIFO:
+                    if ( p_aout->fifo[i_fifo].l_units > p_aout->l_units )
+                   {
+                        l_buffer = 0;
+                        while ( l_buffer < (p_aout->l_units << 1) ) /* p_aout->dsp.b_stereo == 1 */
+                       {
+                            p_aout->s32_buffer[l_buffer++] +=
+                                (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[2*p_aout->fifo[i_fifo].l_unit] );
+                            p_aout->s32_buffer[l_buffer++] +=
+                                (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[2*p_aout->fifo[i_fifo].l_unit+1] );
+                            UPDATE_INCREMENT( p_aout->fifo[i_fifo].unit_increment, p_aout->fifo[i_fifo].l_unit )
+                        }
+                        p_aout->fifo[i_fifo].l_units -= p_aout->l_units;
+                    }
+                    else
+                   {
+                        l_buffer = 0;
+                        while ( l_buffer < (p_aout->fifo[i_fifo].l_units << 1) ) /* p_aout->dsp.b_stereo */
+                       {
+                            p_aout->s32_buffer[l_buffer++] +=
+                                (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[2*p_aout->fifo[i_fifo].l_unit] );
+                            p_aout->s32_buffer[l_buffer++] +=
+                                (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[2*p_aout->fifo[i_fifo].l_unit+1] );
+                            UPDATE_INCREMENT( p_aout->fifo[i_fifo].unit_increment, p_aout->fifo[i_fifo].l_unit )
+                        }
+                        free( p_aout->fifo[i_fifo].buffer ); /* !! */
+                        p_aout->fifo[i_fifo].i_type = AOUT_EMPTY_FIFO; /* !! */
+                        intf_DbgMsg("aout debug: audio output fifo (%p) destroyed\n", &p_aout->fifo[i_fifo]); /* !! */
+                    }
+                    break;
+
+                case AOUT_ADEC_MONO_FIFO:
+                    l_units = p_aout->l_units;
+                    l_buffer = 0;
+                    while ( l_units > 0 )
+                    {
+                        if ( !p_aout->fifo[i_fifo].b_next_frame )
+                        {
+                            if ( NextFrame(p_aout, &p_aout->fifo[i_fifo]) )
+                            {
+                                break;
+                            }
+                        }
+
+                        if ( p_aout->fifo[i_fifo].l_units > l_units )
+                        {
+                            l_buffer_limit = p_aout->l_units << 1; /* p_aout->dsp.b_stereo == 1 */
+                            while ( l_buffer < l_buffer_limit )
+                            {
+                                p_aout->s32_buffer[l_buffer++] +=
+                                    (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[p_aout->fifo[i_fifo].l_unit] );
+                                p_aout->s32_buffer[l_buffer++] +=
+                                    (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[p_aout->fifo[i_fifo].l_unit] );
+
+                                UPDATE_INCREMENT( p_aout->fifo[i_fifo].unit_increment, p_aout->fifo[i_fifo].l_unit )
+                                if ( p_aout->fifo[i_fifo].l_unit >= /* p_aout->fifo[i_fifo].b_stereo == 0 */
+                                     ((AOUT_FIFO_SIZE + 1) * (AOUT_FRAME_SIZE >> 0)) )
+                                {
+                                    p_aout->fifo[i_fifo].l_unit -= /* p_aout->fifo[i_fifo].b_stereo == 0 */
+                                        ((AOUT_FIFO_SIZE + 1) * (AOUT_FRAME_SIZE >> 0));
+                                }
+                            }
+                            p_aout->fifo[i_fifo].l_units -= l_units;
+                            break;
+                        }
+                        else
+                        {
+                            l_buffer_limit = l_buffer + (p_aout->fifo[i_fifo].l_units << 1);
+                            /* p_aout->dsp.b_stereo == 1 */
+                            while ( l_buffer < l_buffer_limit )
+                            {
+                                p_aout->s32_buffer[l_buffer++] +=
+                                    (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[p_aout->fifo[i_fifo].l_unit] );
+                                p_aout->s32_buffer[l_buffer++] +=
+                                    (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[p_aout->fifo[i_fifo].l_unit] );
+
+                                UPDATE_INCREMENT( p_aout->fifo[i_fifo].unit_increment, p_aout->fifo[i_fifo].l_unit )
+                                if ( p_aout->fifo[i_fifo].l_unit >= /* p_aout->fifo[i_fifo].b_stereo == 0 */
+                                     ((AOUT_FIFO_SIZE + 1) * (AOUT_FRAME_SIZE >> 0)) )
+                                {
+                                    p_aout->fifo[i_fifo].l_unit -= /* p_aout->fifo[i_fifo].b_stereo == 0 */
+                                        ((AOUT_FIFO_SIZE + 1) * (AOUT_FRAME_SIZE >> 0));
+                                }
+                            }
+                            l_units -= p_aout->fifo[i_fifo].l_units;
+
+                            pthread_mutex_lock( &p_aout->fifo[i_fifo].data_lock );
+                            p_aout->fifo[i_fifo].l_start_frame = p_aout->fifo[i_fifo].l_next_frame;
+                            pthread_cond_signal( &p_aout->fifo[i_fifo].data_wait );
+                            pthread_mutex_unlock( &p_aout->fifo[i_fifo].data_lock );
+
+                            /* p_aout->fifo[i_fifo].b_start_frame = 1; */
+                            p_aout->fifo[i_fifo].l_next_frame += 1;
+                            p_aout->fifo[i_fifo].l_next_frame &= AOUT_FIFO_SIZE;
+                            p_aout->fifo[i_fifo].b_next_frame = 0;
+                        }
+                    }
+                    break;
+
+                case AOUT_ADEC_STEREO_FIFO:
+                    l_units = p_aout->l_units;
+                    l_buffer = 0;
+                    while ( l_units > 0 )
+                    {
+                        if ( !p_aout->fifo[i_fifo].b_next_frame )
+                        {
+                            if ( NextFrame(p_aout, &p_aout->fifo[i_fifo]) )
+                            {
+                                break;
+                            }
+                        }
+
+                        if ( p_aout->fifo[i_fifo].l_units > l_units )
+                        {
+                            l_buffer_limit = p_aout->l_units << 1; /* p_aout->dsp.b_stereo == 1 */
+                            while ( l_buffer < l_buffer_limit )
+                            {
+                                p_aout->s32_buffer[l_buffer++] +=
+                                    (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[2*p_aout->fifo[i_fifo].l_unit] );
+                                p_aout->s32_buffer[l_buffer++] +=
+                                    (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[2*p_aout->fifo[i_fifo].l_unit+1] );
+
+                                UPDATE_INCREMENT( p_aout->fifo[i_fifo].unit_increment, p_aout->fifo[i_fifo].l_unit )
+                                if ( p_aout->fifo[i_fifo].l_unit >= /* p_aout->fifo[i_fifo].b_stereo == 1 */
+                                     ((AOUT_FIFO_SIZE + 1) * (AOUT_FRAME_SIZE >> 1)) )
+                                {
+                                    p_aout->fifo[i_fifo].l_unit -= /* p_aout->fifo[i_fifo].b_stereo == 1 */
+                                        ((AOUT_FIFO_SIZE + 1) * (AOUT_FRAME_SIZE >> 1));
+                                }
+                            }
+                            p_aout->fifo[i_fifo].l_units -= l_units;
+                            break;
+                        }
+                        else
+                        {
+                            l_buffer_limit = l_buffer + (p_aout->fifo[i_fifo].l_units << 1);
+                            /* p_aout->dsp.b_stereo == 1 */
+                            while ( l_buffer < l_buffer_limit )
+                            {
+                                p_aout->s32_buffer[l_buffer++] +=
+                                    (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[2*p_aout->fifo[i_fifo].l_unit] );
+                                p_aout->s32_buffer[l_buffer++] +=
+                                    (s32)( ((s16 *)p_aout->fifo[i_fifo].buffer)[2*p_aout->fifo[i_fifo].l_unit+1] );
+
+                                UPDATE_INCREMENT( p_aout->fifo[i_fifo].unit_increment, p_aout->fifo[i_fifo].l_unit )
+                                if ( p_aout->fifo[i_fifo].l_unit >= /* p_aout->fifo[i_fifo].b_stereo == 1 */
+                                     ((AOUT_FIFO_SIZE + 1) * (AOUT_FRAME_SIZE >> 1)) )
+                                {
+                                    p_aout->fifo[i_fifo].l_unit -= /* p_aout->fifo[i_fifo].b_stereo == 1 */
+                                        ((AOUT_FIFO_SIZE + 1) * (AOUT_FRAME_SIZE >> 1));
+                                }
+                            }
+                            l_units -= p_aout->fifo[i_fifo].l_units;
+
+                            pthread_mutex_lock( &p_aout->fifo[i_fifo].data_lock );
+                            p_aout->fifo[i_fifo].l_start_frame = p_aout->fifo[i_fifo].l_next_frame;
+                            pthread_cond_signal( &p_aout->fifo[i_fifo].data_wait );
+                            pthread_mutex_unlock( &p_aout->fifo[i_fifo].data_lock );
+
+                            /* p_aout->fifo[i_fifo].b_start_frame = 1; */
+                            p_aout->fifo[i_fifo].l_next_frame += 1;
+                            p_aout->fifo[i_fifo].l_next_frame &= AOUT_FIFO_SIZE;
+                            p_aout->fifo[i_fifo].b_next_frame = 0;
+                        }
+                    }
+                    break;
+
+               default:
+                    intf_DbgMsg("aout debug: unknown fifo type (%i)\n", p_aout->fifo[i_fifo].i_type);
+                    break;
+            }
+        }
+        pthread_mutex_unlock( &p_aout->fifos_lock );
+
+        l_buffer_limit = p_aout->l_units << 1; /* p_aout->dsp.b_stereo == 1 */
+
+        for ( l_buffer = 0; l_buffer < l_buffer_limit; l_buffer++ )
+       {
+            ((s16 *)p_aout->buffer)[l_buffer] = S32_TO_S16( p_aout->s32_buffer[l_buffer] / AOUT_MAX_FIFOS );
+            p_aout->s32_buffer[l_buffer] = 0;
+        }
+
+        aout_dspGetBufInfo( &p_aout->dsp );
+        if ( p_aout->dsp.buf_info.fragments == p_aout->dsp.buf_info.fragstotal ) /* ?? */
+       {
+            aout_dspPlaySamples( &p_aout->dsp, (byte_t *)p_aout->buffer, sizeof(s16) * l_buffer_limit );
+            p_aout->date = mdate();
+        }
+        else if ( p_aout->dsp.buf_info.bytes >=
+            ((p_aout->dsp.buf_info.fragsize * p_aout->dsp.buf_info.fragstotal) -
+            (sizeof(s16) * l_buffer_limit)) )
+       {
+            aout_dspPlaySamples( &p_aout->dsp, (byte_t *)p_aout->buffer, sizeof(s16) * l_buffer_limit );
+        }
+        else
+       {
+            aout_dspPlaySamples( &p_aout->dsp, (byte_t *)p_aout->buffer, sizeof(s16) * l_buffer_limit );
+            msleep( p_aout->date_increment.l_euclidean_integer );
+        }
+        UPDATE_INCREMENT( p_aout->date_increment, p_aout->date )
+    }
+
+    pthread_mutex_lock( &p_aout->fifos_lock );
+    for ( i_fifo = 0; i_fifo < AOUT_MAX_FIFOS; i_fifo++ )
+    {
+        switch ( p_aout->fifo[i_fifo].i_type )
+        {
+            case AOUT_EMPTY_FIFO:
+                break;
+
+            case AOUT_INTF_MONO_FIFO:
+            case AOUT_INTF_STEREO_FIFO:
+                free( p_aout->fifo[i_fifo].buffer ); /* !! */
+                p_aout->fifo[i_fifo].i_type = AOUT_EMPTY_FIFO; /* !! */
+                intf_DbgMsg("aout debug: audio output fifo (%p) destroyed\n", &p_aout->fifo[i_fifo]);
+                break;
+
+            case AOUT_ADEC_MONO_FIFO:
+            case AOUT_ADEC_STEREO_FIFO:
+                free( p_aout->fifo[i_fifo].buffer );
+                free( p_aout->fifo[i_fifo].date );
+                p_aout->fifo[i_fifo].i_type = AOUT_EMPTY_FIFO; /* !! */
+                intf_DbgMsg("aout debug: audio output fifo (%p) destroyed\n", &p_aout->fifo[i_fifo]);
+                break;
+
+            default:
+                break;
+        }
+    }
+    pthread_mutex_unlock( &p_aout->fifos_lock );
+}
+
+void aout_Thread_U16_Mono( aout_thread_t * p_aout )
+{
+}
+
+void aout_Thread_U16_Stereo( aout_thread_t * p_aout )
+{
+}
diff --git a/src/generic_decoder/generic_decoder.c b/src/generic_decoder/generic_decoder.c
new file mode 100644 (file)
index 0000000..a4e99f8
--- /dev/null
@@ -0,0 +1,440 @@
+/*******************************************************************************
+ * generic_decoder.c : generic decoder thread
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This decoder provides a way to parse packets which do not belong to any 
+ * known stream type, or to redirect packets to files. It can extract PES files 
+ * from a multiplexed stream, identify automatically ES in a stream missing 
+ * stream informations (i.e. a TS stream without PSI) and update ES tables in
+ * its input thread, or just print informations about what it receives in DEBUG 
+ * mode.
+ * A single generic decoder is able to handle several ES, therefore it can be
+ * used as a 'default' decoder by the input thread.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "thread.h"
+
+#include "intf_msg.h"
+#include "debug.h"                      /* ?? temporaire, requis par netlist.h */
+
+#include "input.h"
+#include "input_netlist.h"
+#include "decoder_fifo.h"
+
+#include "generic_decoder.h"
+
+#include "video.h"
+#include "video_output.h"
+#include "video_decoder.h"
+
+/*
+ * Local prototypes
+ */
+static int      CheckConfiguration  ( gdec_cfg_t *p_cfg );
+static int      InitThread          ( gdec_thread_t *p_gdec );
+static void     RunThread           ( gdec_thread_t *p_gdec );
+static void     ErrorThread         ( gdec_thread_t *p_gdec );
+static void     EndThread           ( gdec_thread_t *p_gdec );
+
+static void     IdentifyPES         ( gdec_thread_t *p_gdec, pes_packet_t *p_pes, 
+                                      int i_stream_id );
+static void     PrintPES            ( pes_packet_t *p_pes, int i_stream_id );
+
+/*******************************************************************************
+ * gdec_CreateThread: create a generic decoder thread
+ *******************************************************************************
+ * This function creates a new generic decoder thread, and returns a pointer
+ * to its description. On error, it returns NULL.
+ * Following configuration properties are used:
+ *  GDEC_CFG_ACTIONS    (required)
+ * ??
+ *******************************************************************************/
+gdec_thread_t * gdec_CreateThread( gdec_cfg_t *p_cfg, input_thread_t *p_input,
+                                   int *pi_status )
+{
+    gdec_thread_t * p_gdec;                               /* thread descriptor */
+    int             i_status;                                 /* thread status */
+    
+    /*
+     * Check configuration 
+     */
+    if( CheckConfiguration( p_cfg ) )
+    {
+        return( NULL );
+    }
+
+    /* Allocate descriptor and initialize flags */
+    p_gdec = (gdec_thread_t *) malloc( sizeof(gdec_thread_t) );
+    if( p_gdec == NULL )                                              /* error */
+    {
+        return( NULL );
+    }
+
+    /* Copy configuration */
+    p_gdec->i_actions = p_cfg->i_actions;    
+    /* ?? */
+
+    /* Set status */
+    p_gdec->pi_status = (pi_status != NULL) ? pi_status : &i_status;
+    *p_gdec->pi_status = THREAD_CREATE;
+
+    /* Initialize flags */
+    p_gdec->b_die = 0;
+    p_gdec->b_error = 0;    
+    p_gdec->b_active = 1;
+
+    /* Create thread */
+    if( pthread_create( &p_gdec->thread_id, NULL, (void *) RunThread, (void *) p_gdec) )
+    {
+        intf_ErrMsg("gdec error: %s\n", strerror(ENOMEM));
+        intf_DbgMsg("failed\n");        
+        free( p_gdec );
+        return( NULL );
+    }   
+
+    /* If status is NULL, wait until the thread is created */
+    if( pi_status == NULL )
+    {
+        do
+        {            
+            msleep( THREAD_SLEEP );
+        }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
+                && (i_status != THREAD_FATAL) );
+        if( i_status != THREAD_READY )
+        {
+            intf_DbgMsg("failed\n");            
+            return( NULL );            
+        }        
+    }
+
+    intf_DbgMsg("succeeded -> %p\n", p_gdec);    
+    return( p_gdec );
+}
+
+/*******************************************************************************
+ * gdec_DestroyThread: destroy a generic decoder thread
+ *******************************************************************************
+ * Destroy a terminated thread. This function will return 0 if the thread could
+ * be destroyed, and non 0 else. The last case probably means that the thread
+ * was still active, and another try may succeed.
+ *******************************************************************************/
+void gdec_DestroyThread( gdec_thread_t *p_gdec, int *pi_status )
+{
+    int     i_status;                                         /* thread status */
+
+    /* Set status */
+    p_gdec->pi_status = (pi_status != NULL) ? pi_status : &i_status;
+    *p_gdec->pi_status = THREAD_DESTROY;
+     
+    /* Request thread destruction */
+    p_gdec->b_die = 1;
+
+    /* If status is NULL, wait until thread has been destroyed */
+    if( pi_status )
+    {
+        do
+        {
+            msleep( THREAD_SLEEP );
+        }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
+                && (i_status != THREAD_FATAL) );   
+    }
+    
+    intf_DbgMsg("%p -> succeeded\n", p_gdec);
+}
+
+/* following functions are local */
+
+/*******************************************************************************
+ * CheckConfiguration: check gdec_CreateThread() configuration
+ *******************************************************************************
+ * Set default parameters where required. In DEBUG mode, check if configuration
+ * is valid.
+ *******************************************************************************/
+static int CheckConfiguration( gdec_cfg_t *p_cfg )
+{
+#ifdef DEBUG
+    /* Actions (required) */
+    if( !(p_cfg->i_properties & GDEC_CFG_ACTIONS) )
+    {
+        return( 1 );        
+    }    
+#endif
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * InitThread: initialize gdec thread
+ *******************************************************************************
+ * This function is called from RunThread and performs the second step of the
+ * initialization. It returns 0 on success. Note that the thread's flag are not
+ * modified inside this function.
+ *******************************************************************************/
+static int InitThread( gdec_thread_t *p_gdec )
+{
+    /* ?? */
+
+    /* Update status */
+    *p_gdec->pi_status = THREAD_START;    
+
+    /* Initialize other properties */
+#ifdef STATS
+    p_gdec->c_loops = 0;
+    p_gdec->c_idle_loops = 0;
+    p_gdec->c_pes = 0;
+#endif
+
+    /* Mark thread as running and return */
+    *p_gdec->pi_status = THREAD_READY;    
+    intf_DbgMsg("%p -> succeeded\n", p_gdec);    
+    return(0);    
+}
+
+/*******************************************************************************
+ * RunThread: generic decoder thread
+ *******************************************************************************
+ * Generic decoder thread. This function does only returns when the thread is
+ * terminated. 
+ *******************************************************************************/
+static void RunThread( gdec_thread_t *p_gdec )
+{
+    pes_packet_t *  p_pes;                                   /* current packet */
+    int             i_stream_id;                              /* PES stream id */
+    
+    /* 
+     * Initialize thread and free configuration 
+     */
+    p_gdec->b_error = InitThread( p_gdec );
+    if( p_gdec->b_error )
+    {
+        free( p_gdec );                                  /* destroy descriptor */
+        return;
+    }
+
+    /*
+     * Main loop - it is not executed if an error occured during
+     * initialization
+     */
+    while( (!p_gdec->b_die) && (!p_gdec->b_error) )
+    {
+        /* ?? locks Ã  rajouter ? - vérifier les macros (transformer en inline ?) */
+        /* ?? on idle loop, increment c_idle_loops */
+        while( !DECODER_FIFO_ISEMPTY( p_gdec->fifo ) )
+        {            
+            p_pes = DECODER_FIFO_START( p_gdec->fifo );
+            DECODER_FIFO_INCSTART( p_gdec->fifo );
+
+            /* Extract stream id from packet if required - stream id is used
+             * by GDEC_IDENTIFY, GDEC_SAVE_DEMUX and GDEC_PRINT */
+            if( p_gdec->i_actions & (GDEC_IDENTIFY | GDEC_SAVE_DEMUX | GDEC_PRINT) )
+            {
+                i_stream_id = p_pes->p_pes_header[3];                    
+            }            
+
+            /* PES identification */
+            if( p_gdec->i_actions & GDEC_IDENTIFY )
+            {
+                IdentifyPES( p_gdec, p_pes, i_stream_id );
+            }
+
+            /* PES multiplexed stream saving */
+            if( p_gdec->i_actions & GDEC_SAVE )
+            {
+                /* ?? */
+            }
+
+            /* PES demultiplexed stream saving */
+            if( p_gdec->i_actions & GDEC_SAVE_DEMUX )
+            {
+                /* ?? */
+            }
+                       
+            /* PES information printing */
+            if( p_gdec->i_actions & GDEC_PRINT )
+            {
+                PrintPES( p_pes, i_stream_id );
+            }                        
+
+            /* Trash PES packet (give it back to fifo) */
+            input_NetlistFreePES( p_gdec->p_input, p_pes );            
+
+#ifdef STATS
+            p_gdec->c_pes++;            
+#endif
+        }
+#ifdef STATS
+        p_gdec->c_loops++;        
+#endif        
+    } 
+
+    /*
+     * Error loop
+     */
+    if( p_gdec->b_error )
+    {
+        ErrorThread( p_gdec );        
+    }
+
+    /* End of thread */
+    EndThread( p_gdec );
+}
+
+/*******************************************************************************
+ * ErrorThread: RunThread() error loop
+ *******************************************************************************
+ * This function is called when an error occured during thread main's loop. The
+ * thread can still receive feed, but must be ready to terminate as soon as
+ * possible.
+ *******************************************************************************/
+static void ErrorThread( gdec_thread_t *p_gdec )
+{
+    pes_packet_t *  p_pes;                                       /* pes packet */
+    
+    /* Wait until a `die' order */
+    while( !p_gdec->b_die )
+    {
+        /* Trash all received PES packets */
+        while( !DECODER_FIFO_ISEMPTY( p_gdec->fifo ) )
+        {            
+            p_pes = DECODER_FIFO_START( p_gdec->fifo );
+            DECODER_FIFO_INCSTART( p_gdec->fifo );
+            input_NetlistFreePES( p_gdec->p_input, p_pes );            
+        }
+
+        /* Sleep a while */
+        msleep( GDEC_IDLE_SLEEP );                
+    }
+}
+
+/*******************************************************************************
+ * EndThread: thread destruction
+ *******************************************************************************
+ * This function is called when the thread ends after a sucessfull 
+ * initialization.
+ *******************************************************************************/
+static void EndThread( gdec_thread_t *p_gdec )
+{
+    int *   pi_status;                                        /* thread status */    
+    
+    /* Store status */
+    pi_status = p_gdec->pi_status;    
+    *pi_status = THREAD_END;    
+
+#ifdef DEBUG
+    /* Check for remaining PES packets */
+    /* ?? */
+#endif
+
+    /* Destroy thread structures allocated by InitThread */
+    free( p_gdec );                                      /* destroy descriptor */
+
+    *pi_status = THREAD_OVER;    
+    intf_DbgMsg("%p\n", p_gdec);
+}
+
+/*******************************************************************************
+ * IdentifyPES: identify a PES packet
+ *******************************************************************************
+ * Update ES tables in the input thread according to the stream_id value. See
+ * ISO 13818-1, table 2-18.
+ *******************************************************************************/
+static void IdentifyPES( gdec_thread_t *p_gdec, pes_packet_t *p_pes, int i_stream_id )
+{
+    int i_id;                                         /* stream id in es table */
+    int i_type;            /* stream type according ISO/IEC 13818-1 table 2-29 */
+    
+    /* Search where the elementary stream id does come from */
+    switch( p_gdec->p_input->i_method )
+    {
+    case INPUT_METHOD_TS_FILE:                     /* TS methods: id is TS PID */
+    case INPUT_METHOD_TS_UCAST:
+    case INPUT_METHOD_TS_BCAST:
+    case INPUT_METHOD_TS_VLAN_BCAST:
+        /* ?? since PID is extracted by demux, it could be usefull to store it
+         * in a readable place, i.e. the TS packet descriptor, rather than to
+         * re-extract it now */
+        i_id = U16_AT(&p_pes->p_first_ts->buffer[1]) & 0x1fff;
+        break;        
+
+#ifdef DEBUG
+    default:                                              /* unknown id origin */
+        intf_DbgMsg("unable to identify PES using input method %d\n", 
+                    p_gdec->p_input->i_method );
+        break;        
+#endif
+    }
+    
+    /* Try to identify PES stream_id - see ISO 13818-1 table 2-18 */
+    if( (i_stream_id & 0xe0) == 0xc0 ) 
+    {
+        /* ISO/IEC 13818-3 or ISO/IEC 11172-3 audio stream - since there is no
+         * way to make the difference between the two possibilities, and since
+         * an ISO/IEC 13818-3 is capable of decoding an ISO/IEC 11172-3 stream,
+         * the first one is used */
+        i_type = MPEG2_AUDIO_ES;
+        intf_DbgMsg("PES %p identified as AUDIO\n", p_pes);        
+    }
+    else if( (i_stream_id & 0xf0) == 0xe0 )
+    {
+        /* ISO/IEC 13818-2 or ISO/IEC 11172-2 video stream - since there is no
+         * way to make the difference between the two possibilities, and since
+         * an ISO/IEC 13818-2 is capable of decoding an ISO/IEC 11172-2 stream,
+         * the first one is used */        
+        i_type = MPEG2_VIDEO_ES;
+        intf_DbgMsg("PES %p identified as VIDEO\n", p_pes);        
+    }
+    else
+    {
+        /* The stream could not be identified - just return */
+        intf_DbgMsg("PES %p could not be identified\n", p_pes);
+        return;        
+    }
+    
+    /* Update ES table */
+    /* ?? */
+}
+
+/*******************************************************************************
+ * PrintPES: print informations about a PES packet
+ *******************************************************************************
+ * This function will print information about a received PES packet. It is
+ * probably usefull only for debugging purposes, or before demultiplexing a
+ * stream. It has two different formats, depending of the presence of the DEBUG 
+ * symbol.
+ *******************************************************************************/
+static void PrintPES( pes_packet_t *p_pes, int i_stream_id )
+{
+    char psz_pes[128];                                    /* descriptor buffer */
+#ifdef DEBUG
+    /* PES informations, long (DEBUG) format - this string is maximum 70 bytes
+     * long */
+    sprintf(psz_pes, "id 0x%x, %d bytes (%d TS): %p %c%c%c%c",
+            i_stream_id, p_pes->i_pes_size, p_pes->i_ts_packets,
+            p_pes,
+            p_pes->b_data_loss ? 'l' : '-', p_pes->b_data_alignment ? 'a' : '-',
+            p_pes->b_random_access ? 'r' : '-', p_pes->b_discard_payload ? 'd' : '-' );
+#else
+    /* PES informations, short format */
+    sprintf(psz_pes, "id 0x%x, %d bytes",
+            i_stream_id, p_pes->i_pes_size );
+#endif
+    intf_Msg("gdec: PES %s\n", psz_pes );
+}
+
diff --git a/src/input/input.c b/src/input/input.c
new file mode 100644 (file)
index 0000000..b0260e8
--- /dev/null
@@ -0,0 +1,1188 @@
+/*******************************************************************************
+ * input.c: input thread 
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * Read an MPEG2 stream, demultiplex and parse it before sending it to
+ * decoders.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <sys/uio.h>                                                 /* iovec */
+#include <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+#include <sys/soundcard.h>
+
+#include <stdlib.h>                               /* atoi(), malloc(), free() */
+#include <stdio.h>
+#include <sys/ioctl.h>                                             /* ioctl() */
+#include <net/if.h>                                                  /* ifreq */
+#include <netinet/in.h>
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"
+#include "intf_msg.h"
+#include "debug.h"
+
+#include "input.h"
+#include "input_psi.h"
+#include "input_pcr.h"
+#include "input_netlist.h"
+#include "decoder_fifo.h"
+#include "input_file.h"
+#include "input_network.h"
+
+#include "audio_output.h"
+#include "audio_decoder.h"
+
+#include "video.h"
+#include "video_output.h"
+#include "video_decoder.h"
+
+/******************************************************************************
+ * Local prototypes
+ ******************************************************************************/
+static void input_Thread( input_thread_t *p_input );
+static __inline__ int input_ReadPacket( input_thread_t *p_input );
+static __inline__ void input_SortPacket( input_thread_t *p_input,
+                                         ts_packet_t *ts_packet );
+static __inline__ void input_DemuxTS( input_thread_t *p_input,
+                                      ts_packet_t *ts_packet,
+                                      es_descriptor_t *es_descriptor );
+static __inline__ void input_DemuxPES( input_thread_t *p_input,
+                                       ts_packet_t *ts_packet,
+                                       es_descriptor_t *p_es_descriptor,
+                                       boolean_t b_unit_start, boolean_t b_packet_lost );
+static __inline__ void input_DemuxPSI( input_thread_t *p_input,
+                                       ts_packet_t *ts_packet,
+                                       es_descriptor_t *p_es_descriptor,
+                                       boolean_t b_unit_start, boolean_t b_packet_lost );
+
+/*******************************************************************************
+ * input_CreateThread: initialize and spawn an input thread
+ *******************************************************************************
+ * This function initializes and spawns an input thread. It returns NULL on
+ * failure. If you want a better understanding of the input thread, don't start
+ * by reading this function :-).
+ *******************************************************************************/
+input_thread_t *input_CreateThread( input_cfg_t *p_cfg )
+{
+    input_thread_t *    p_input;
+    int i_index;
+    
+    intf_DbgMsg("input debug 1-1: creating thread (cfg : %p)\n", p_cfg );
+
+    /* Allocate input_thread_t structure. */
+    if( !( p_input = (input_thread_t *)malloc(sizeof(input_thread_t)) ) )
+    {
+        intf_ErrMsg("input error: can't allocate input thread structure (%s)\n",
+                    strerror(errno));
+        return( NULL );
+    }
+    /* Init it */
+    bzero( p_input, sizeof(input_thread_t));
+    for( i_index = 0; i_index < INPUT_MAX_ES; i_index++ )
+    {
+        p_input->p_es[i_index].i_id = EMPTY_PID;
+    }
+
+    /* Find out which method we are gonna use and retrieve pointers. */
+    if( !((p_cfg->i_properties) & INPUT_CFG_METHOD) )
+    {
+        /* i_method is not set. */
+        intf_DbgMsg("input debug: using default method (%d)\n",
+                    INPUT_DEFAULT_METHOD);
+        p_cfg->i_method = INPUT_DEFAULT_METHOD;
+        p_cfg->i_properties |= INPUT_CFG_METHOD;
+    }
+    p_input->i_method = p_cfg->i_method;
+    switch( p_cfg->i_method )
+    {
+        /* File methods */
+        case INPUT_METHOD_TS_FILE:
+            p_input->p_open = &input_FileCreateMethod;
+            p_input->p_read = &input_FileRead;
+            p_input->p_clean = &input_FileDestroyMethod;
+            break;
+
+        /* Network methods */
+        case INPUT_METHOD_TS_UCAST:
+        case INPUT_METHOD_TS_MCAST:
+        case INPUT_METHOD_TS_BCAST:
+        case INPUT_METHOD_TS_VLAN_BCAST:
+            p_input->p_open = &input_NetworkCreateMethod;
+            p_input->p_read = &input_NetworkRead;
+            p_input->p_clean = &input_NetworkDestroyMethod;
+            break;
+
+        case INPUT_METHOD_NONE:
+        default:
+#ifdef DEBUG
+            /* Internal error, which should never happen */
+            intf_DbgMsg("input debug: unknow method type %d\n",
+                            p_cfg->i_method);
+            return( NULL );
+#endif
+            break;
+    }
+
+    /* Initialize PSI decoder. */
+    intf_DbgMsg("Initializing PSI decoder\n");
+    if( input_PsiInit( p_input ) == -1 )
+    {
+        free( p_input );
+        return( NULL );
+    }
+
+    /* Initialize PCR decoder. */
+    intf_DbgMsg("Initializing PCR decoder\n");
+    if( input_PcrInit( p_input ) == -1 )
+    {
+        input_PsiClean( p_input );
+        free( p_input );
+        return( NULL );
+    }
+
+    /* Initialize netlists. */
+    if( input_NetlistOpen( p_input ) )
+    {
+        input_PsiClean( p_input );
+        input_PcrClean( p_input );
+        free( p_input );
+        return( NULL );
+    }
+
+#ifdef STATS
+    /* Initialize counters. */
+    p_input->c_bytes = 0;
+    p_input->c_payload_bytes = 0;
+    p_input->c_ts_packets_read = 0;
+    p_input->c_ts_packets_trashed = 0;
+#ifdef DEBUG
+    p_input->c_loops = 0;
+#endif
+#endif
+
+    /* Let the appropriate method open the socket. */
+    if( (*(p_input->p_open))( p_input, p_cfg ) == -1 )
+    {
+        input_NetlistClean( p_input );
+        input_PsiClean( p_input );
+        input_PcrClean( p_input );
+        free( p_input );
+        return( NULL );
+    }
+
+    intf_DbgMsg("input debug: method %d properly initialized the socket\n",
+                p_input->i_method);
+
+    /* Create thread and set locks. */
+    p_input->b_die = 0;
+    pthread_mutex_init( &p_input->netlist.lock, NULL );
+    pthread_mutex_init( &p_input->programs_lock, NULL );
+    pthread_mutex_init( &p_input->es_lock, NULL );
+#ifdef NO_THREAD
+    input_Thread( p_input );
+#else
+    if( pthread_create(&p_input->thread_id, NULL, (void *) input_Thread, 
+                       (void *) p_input) )
+    {
+        intf_ErrMsg("input error: can't spawn input thread (%s)\n", 
+                    strerror(errno) );
+        (*p_input->p_clean)( p_input );
+        input_NetlistClean( p_input );;
+        input_PsiClean( p_input );
+        input_PcrClean( p_input );
+        free( p_input );
+        return( NULL );
+    }
+#endif
+
+    /* Default setting for new decoders */
+    p_input->p_aout = p_cfg->p_aout;
+
+    return( p_input );
+}
+
+/******************************************************************************
+ * input_DestroyThread: mark an input thread as zombie
+ ******************************************************************************
+ * This function should not return until the thread is effectively cancelled.
+ ******************************************************************************/
+void input_DestroyThread( input_thread_t *p_input )
+{
+    int i_es_loop;
+
+    intf_DbgMsg("input debug: requesting termination of input thread\n");
+    p_input->b_die = 1;                          /* ask thread to kill itself */
+    pthread_join( p_input->thread_id, NULL );         /* wait until it's done */
+
+    (*p_input->p_clean)( p_input );           /* close input method */
+
+    /* Destroy all decoder threads. */
+    for( i_es_loop = 0; i_es_loop < INPUT_MAX_ES; i_es_loop++ )
+    {
+        if( p_input->pp_selected_es[i_es_loop] )
+        {
+            switch( p_input->pp_selected_es[i_es_loop]->i_type )
+            {
+                case MPEG1_VIDEO_ES:
+                case MPEG2_VIDEO_ES:
+                    vdec_DestroyThread( (vdec_thread_t*)(p_input->pp_selected_es[i_es_loop]->p_dec), NULL );
+                    break;
+                case MPEG1_AUDIO_ES:
+                case MPEG2_AUDIO_ES:
+                    adec_DestroyThread( (adec_thread_t*)(p_input->pp_selected_es[i_es_loop]->p_dec) );
+                    break;
+                default:
+#ifdef DEBUG
+                    /* This should never happen. */
+                    intf_DbgMsg("input debug: unknown stream type ! (%d, %d)\n",
+                             p_input->pp_selected_es[i_es_loop]->i_id,
+                             p_input->pp_selected_es[i_es_loop]->i_type);
+#endif
+                    break;
+            }
+        }
+        else
+        {
+            /* pp_selected_es should not contain any hole. */
+            break;
+        }
+    }
+
+    input_NetlistClean( p_input );                           /* clean netlist */
+    input_PsiClean( p_input );                       /* clean PSI information */
+    input_PcrClean( p_input );                       /* clean PCR information */
+    free( p_input );                           /* free input_thread structure */
+}
+
+#if 0
+/*******************************************************************************
+ * input_OpenAudioStream: open an audio stream
+ *******************************************************************************
+ * This function spawns an audio decoder and plugs it on the audio output
+ * thread.
+ *******************************************************************************/
+int input_OpenAudioStream( input_thread_t *p_input, int i_id )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * input_CloseAudioStream: close an audio stream
+ *******************************************************************************
+ * This function destroys an audio decoder.
+ *******************************************************************************/
+void input_CloseAudioStream( input_thread_t *p_input, int i_id )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * input_OpenVideoStream: open a video stream
+ *******************************************************************************
+ * This function spawns a video decoder and plugs it on a video output thread.
+ *******************************************************************************/
+int input_OpenVideoStream( input_thread_t *p_input, 
+                           struct vout_thread_s *p_vout, struct video_cfg_s * p_cfg )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * input_CloseVideoStream: close a video stream
+ *******************************************************************************
+ * This function destroys an video decoder.
+ *******************************************************************************/
+void input_CloseVideoStream( input_thread_t *p_input, int i_id )
+{
+    /* ?? */
+}
+#endif
+
+/* following functions are local */
+
+/*******************************************************************************
+ * input_Thread: input thread
+ *******************************************************************************
+ * Thread in charge of processing the network packets and demultiplexing.
+ *******************************************************************************/
+static void input_Thread( input_thread_t *p_input )
+{
+    intf_DbgMsg("input debug 11-1: thread %p is active\n", p_input);
+    while( !p_input->b_die )
+    {
+        /* Scatter read the UDP packet from the network or the file. */
+        if( (input_ReadPacket( p_input )) == (-1) )
+        {
+            /* ??? Normally, a thread can't kill itself, but we don't have
+             * any method in case of an error condition ... */
+            p_input->b_die = 1;
+        }
+
+#ifdef DEBUG
+        p_input->c_loops++;
+#endif
+    }
+
+    /* Ohoh, we have to die as soon as possible. */
+    intf_DbgMsg("input debug: thread %p destroyed\n", p_input);
+    pthread_exit( 0 );
+}
+
+/*******************************************************************************
+ * input_ReadPacket: reads a packet from the network or the file
+ *******************************************************************************/
+static __inline__ int input_ReadPacket( input_thread_t *p_input )
+{
+    int                 i_base_index; /* index of the first free iovec */
+    int                 i_current_index;
+    int                 i_packet_size;
+#ifdef INPUT_LIFO_TS_NETLIST
+    int                 i_meanwhile_released;
+    int                 i_currently_removed;
+#endif
+    ts_packet_t *       p_ts_packet;
+
+    /* In this function, we only care about the TS netlist. PES netlist
+     * is for the demultiplexer. */
+#ifdef INPUT_LIFO_TS_NETLIST
+    i_base_index = p_input->netlist.i_ts_index;
+
+    /* Verify that we still have packets in the TS netlist */
+    if( (INPUT_MAX_TS + INPUT_TS_READ_ONCE - 1 - p_input->netlist.i_ts_index) <= INPUT_TS_READ_ONCE )
+    {
+        intf_ErrMsg("input error: TS netlist is empty !\n");
+        return( -1 );
+    }
+
+#else /* FIFO netlist */
+    i_base_index = p_input->netlist.i_ts_start;
+    if( p_input->netlist.i_ts_start + INPUT_TS_READ_ONCE -1 > INPUT_MAX_TS )
+    {
+        /* The netlist is splitted in 2 parts. We must gather them to consolidate
+           the FIFO (we make the loop easily in having the same iovec at the far
+           end and in the beginning of netlist_free).
+           That's why the netlist is (INPUT_MAX_TS +1) + (INPUT_TS_READ_ONCE -1)
+           large. */
+        memcpy( p_input->netlist.p_ts_free + INPUT_MAX_TS + 1,
+                p_input->netlist.p_ts_free,
+                (p_input->netlist.i_ts_start + INPUT_TS_READ_ONCE - 1 - INPUT_MAX_TS)
+                  * sizeof(struct iovec) );
+    }
+
+    /* Verify that we still have packets in the TS netlist */
+    if( ((p_input->netlist.i_ts_end -1 - p_input->netlist.i_ts_start) & INPUT_MAX_TS) <= INPUT_TS_READ_ONCE )
+    {
+        intf_ErrMsg("input error: TS netlist is empty !\n");
+       return( -1 );
+    }
+#endif /* FIFO netlist */
+
+    /* Scatter read the buffer. */
+    i_packet_size = (*p_input->p_read)( p_input,
+                           &p_input->netlist.p_ts_free[i_base_index],
+                           INPUT_TS_READ_ONCE );
+    if( i_packet_size == (-1) )
+    {
+       intf_DbgMsg("Read packet %d %p %d %d\n", i_base_index,
+                       &p_input->netlist.p_ts_free[i_base_index],
+                       p_input->netlist.i_ts_start,
+                       p_input->netlist.i_ts_end);
+        intf_ErrMsg("input error: readv() failed (%s)\n", strerror(errno));
+        return( -1 );
+    }
+
+    if( i_packet_size == 0 )
+    {
+        /* No packet has been received, so stop here. */
+        return( 0 );
+    }
+     
+    /* Demultiplex the TS packets (1..INPUT_TS_READ_ONCE) received. */
+    for( i_current_index = i_base_index;
+         (i_packet_size -= TS_PACKET_SIZE) >= 0;
+         i_current_index++ )
+    {
+        /* BTW, something REALLY bad could happen if we receive packets with
+           a wrong size. */
+        p_ts_packet = (ts_packet_t*)(p_input->netlist.p_ts_free[i_current_index].iov_base);
+        /* Don't cry :-), we are allowed to do that cast, because initially,
+           our buffer was malloc'ed with sizeof(ts_packet_t) */
+
+        /* Find out if we need this packet and demultiplex. */
+        input_SortPacket( p_input /* for current PIDs and netlist */,
+                          p_ts_packet);
+    }
+
+    if( i_packet_size > 0 )
+    {
+        intf_ErrMsg("input error: wrong size\n");
+        return( -1 );
+    }
+
+    /* Remove the TS packets we have just filled from the netlist */
+#ifdef INPUT_LIFO_TS_NETLIST
+    /* We need to take a lock here while we're calculating index positions. */
+    pthread_mutex_lock( &p_input->netlist.lock );
+
+    i_meanwhile_released = i_base_index - p_input->netlist.i_ts_index;
+    if( i_meanwhile_released )
+    {
+        /* That's where it becomes funny :-). Since we didn't take locks for
+           efficiency reasons, other threads (including ourselves, with
+           input_DemuxPacket) might have released packets to the netlist.
+           So we have to copy these iovec where they should go.
+           
+           BTW, that explains why the TS netlist is
+           (INPUT_MAX_TS +1) + (TS_READ_ONCE -1) large. */
+
+        i_currently_removed = i_current_index - i_base_index;
+        if( i_meanwhile_released < i_currently_removed )
+        {
+            /* Copy all iovecs in that case */
+            memcpy( &p_input->netlist.p_ts_free[p_input->netlist.i_ts_index]
+                     + i_currently_removed,
+                    &p_input->netlist.p_ts_free[p_input->netlist.i_ts_index],
+                    i_meanwhile_released * sizeof(struct iovec) );
+        }
+        else
+        {
+            /* We have fewer places than items, so we only move
+               i_currently_removed of them. */
+            memcpy( &p_input->netlist.p_ts_free[i_base_index],
+                    &p_input->netlist.p_ts_free[p_input->netlist.i_ts_index],
+                    i_currently_removed * sizeof(struct iovec) );
+        }
+
+        /* Update i_netlist_index with the information gathered above. */
+        p_input->netlist.i_ts_index += i_currently_removed;
+    }
+    else
+    {
+        /* Nothing happened. */
+        p_input->netlist.i_ts_index = i_current_index;
+    }
+
+    pthread_mutex_unlock( &p_input->netlist.lock );
+
+#else /* FIFO netlist */
+    /* & is modulo ; that's where we make the loop. */
+    p_input->netlist.i_ts_start = i_current_index & INPUT_MAX_TS;
+#endif
+
+#ifdef STATS
+    p_input->c_ts_packets_read += i_current_index - i_base_index;
+    p_input->c_bytes += (i_current_index - i_base_index) * TS_PACKET_SIZE;
+#endif
+       return( 0 );
+}
+
+/*******************************************************************************
+ * input_SortPacket: find out whether we need that packet
+ *******************************************************************************/
+static __inline__ void input_SortPacket( input_thread_t *p_input,
+                                         ts_packet_t *p_ts_packet )
+{
+    int             i_current_pid;
+    int             i_es_loop;
+
+    /* Verify that sync_byte, error_indicator and scrambling_control are
+       what we expected. */
+    if( !(p_ts_packet->buffer[0] == 0x47) || (p_ts_packet->buffer[1] & 0x80) ||
+        (p_ts_packet->buffer[3] & 0xc0) )
+    {
+        intf_DbgMsg("input debug: invalid TS header (%p)\n", p_ts_packet);
+    }
+    else
+    {
+        /* Get the PID of the packet. Note that ntohs is needed, for endianness
+           purposes (see man page). */
+        i_current_pid = U16_AT(&p_ts_packet->buffer[1]) & 0x1fff;
+
+//     intf_DbgMsg("input debug: pid %d received (%p)\n",
+//                    i_current_pid, p_ts_packet);
+
+        /* Lock current ES state. */
+        pthread_mutex_lock( &p_input->es_lock );
+        
+       /* Verify that we actually want this PID. */
+        for( i_es_loop = 0; i_es_loop < INPUT_MAX_SELECTED_ES; i_es_loop++ )
+        {
+            if( p_input->pp_selected_es[i_es_loop] != NULL)
+            {
+                if( (*p_input->pp_selected_es[i_es_loop]).i_id
+                     == i_current_pid )
+                {
+                    /* Don't need the lock anymore, since the value pointed
+                       out by p_input->pp_selected_es[i_es_loop] can only be
+                       modified from inside the input_thread (by the PSI
+                       decoder): interface thread is only allowed to modify
+                       the pp_selected_es table */
+                    pthread_mutex_unlock( &p_input->es_lock );
+
+                    /* We're interested. Pass it to the demultiplexer. */
+                    input_DemuxTS( p_input, p_ts_packet,
+                                   p_input->pp_selected_es[i_es_loop] );
+                    return;
+               }
+            }
+            else
+            {
+                /* pp_selected_es should not contain any hole. */
+                break;
+            }
+        }
+        pthread_mutex_unlock( &p_input->es_lock );
+    }
+
+    /* We weren't interested in receiving this packet. Give it back to the
+       netlist. */
+//    intf_DbgMsg("SortPacket: freeing unwanted TS %p (pid %d)\n", p_ts_packet,
+//                     U16_AT(&p_ts_packet->buffer[1]) & 0x1fff);
+    input_NetlistFreeTS( p_input, p_ts_packet );
+#ifdef STATS
+    p_input->c_ts_packets_trashed++;
+#endif
+}
+
+/*******************************************************************************
+ * input_DemuxTS: first step of demultiplexing: the TS header
+ *******************************************************************************
+ * Stream must also only contain PES and PSI, so PID must have been filtered
+ *******************************************************************************/
+static __inline__ void input_DemuxTS( input_thread_t *p_input,
+                                      ts_packet_t *p_ts_packet,
+                                      es_descriptor_t *p_es_descriptor )
+{
+    int         i_dummy;
+    boolean_t   b_adaption;                       /* Adaption field is present */
+    boolean_t   b_payload;                           /* Packet carries payload */
+    boolean_t   b_unit_start;            /* A PSI or a PES start in the packet */
+    boolean_t   b_trash = 0;                   /* Must the packet be trashed ? */
+    boolean_t   b_lost = 0;                      /* Was there a packet lost ? */
+
+    ASSERT(p_input);
+    ASSERT(p_ts_packet);
+    ASSERT(p_es_descriptor);
+
+#define p (p_ts_packet->buffer)
+
+//    intf_DbgMsg("input debug: TS-demultiplexing packet %p, pid %d, number %d\n",
+//                p_ts_packet, U16_AT(&p[1]) & 0x1fff, p[3] & 0x0f);
+
+#ifdef STATS
+    p_es_descriptor->c_packets++;
+    p_es_descriptor->c_bytes += TS_PACKET_SIZE;
+#endif
+
+    /* Extract flags values from TS common header. */
+    b_unit_start = (p[1] & 0x40);
+    b_adaption = (p[3] & 0x20);
+    b_payload = (p[3] & 0x10);
+    
+    /* Extract adaption field informations if any */
+    if( !b_adaption )
+    {
+        /* We don't have any adaptation_field, so payload start immediately
+         after the 4 byte TS header */
+        p_ts_packet->i_payload_start = 4;
+    }
+    else
+    {
+        /* p[4] is adaptation_field_length minus one */
+        p_ts_packet->i_payload_start = 5 + p[4];
+
+        /* The adaption field can be limited to the adaptation_field_length byte,
+           so that there is nothing to do: skip this possibility */
+        if( p[4] )
+        {
+            /* If the packet has both adaptation_field and payload, adaptation_field
+               cannot be more than 182 bytes long; if there is only an adaptation_field,
+               it must fill the next 183 bytes. */
+            if( b_payload ? (p[4] > 182) : (p[4] != 183) )
+            {
+                intf_DbgMsg("input debug: invalid TS adaptation field (%p)\n",
+                            p_ts_packet);
+#ifdef STATS
+                p_es_descriptor->c_invalid_packets++;
+#endif
+                b_trash = 1;
+            }
+
+            /* No we are sure that the byte containing flags is present: read it */
+            else
+            {
+                /* discontinuity_indicator */
+                if( p[5] & 0x80 )
+                {
+                    intf_DbgMsg("discontinuity_indicator encountered by TS demux " \
+                                "(position read: %d, saved: %d)\n", p[5] & 0x80,
+                                p_es_descriptor->i_continuity_counter);
+
+                    /* If the PID carries the PCR, there will be a system time-base
+                       discontinuity. We let the PCR decoder handle that. */
+                    p_es_descriptor->b_discontinuity = 1;
+                    
+                    /* There also may be a continuity_counter discontinuity: resynchronise
+                       our counter with the one of the stream */
+                    p_es_descriptor->i_continuity_counter = (p[3] & 0x0f) - 1;
+                }
+
+                /* random_access_indicator */
+                p_es_descriptor->b_random |= p[5] & 0x40;
+
+                /* If this is a PCR_PID, and this TS packet contains a PCR, we pass it
+                   along to the PCR decoder. */
+                if( (p_es_descriptor->b_pcr) && (p[5] & 0x10) )
+                {
+                    /* There should be a PCR field in the packet, check if the adaption
+                       field is long enough to carry it */
+                    if( p[4] >= 7 )
+                    {
+                        /* Call the PCR decoder */
+                        input_PcrDecode( p_input, p_es_descriptor, &p[6] );
+                    }
+                }
+            }
+        }
+    }
+
+    /* Check the continuity of the stream. */
+    i_dummy = ((p[3] & 0x0f) - p_es_descriptor->i_continuity_counter) & 0x0f;
+    if( i_dummy == 1 )
+    {
+        /* Everything is ok, just increase our counter */
+        p_es_descriptor->i_continuity_counter++;
+    }
+    else
+    {
+        if( !b_payload && i_dummy == 0 )
+        {
+            /* This is a packet without payload, this is allowed by the draft
+               As there is nothing interessant in this packet (except PCR that
+               have already been handled), we can trash the packet. */
+            intf_DbgMsg("Packet without payload received by TS demux\n");
+            b_trash = 1;
+        }
+        else if( i_dummy <= 0 )
+        {
+            /* Duplicate packet: mark it as being to be trashed. */
+            intf_DbgMsg("Duplicate packet received by TS demux\n");
+            b_trash = 1;
+        }
+        else
+        {
+            /* This can indicate that we missed a packet or that the
+               continuity_counter wrapped and we received a dup packet: as we
+               don't know, do as if we missed a packet to be sure to recover
+               from this situation */
+            intf_DbgMsg("Packet lost by TS demux: current %d, packet %d\n",
+                        p_es_descriptor->i_continuity_counter & 0x0f,
+                        p[3] & 0x0f);
+            b_lost = 1;
+            p_es_descriptor->i_continuity_counter = p[3] & 0x0f;
+        }
+    }
+
+    /* Trash the packet if it has no payload or if it is bad */
+    if( b_trash )
+    {
+        input_NetlistFreeTS( p_input, p_ts_packet );
+#ifdef STATS
+        p_input->c_ts_packets_trashed++;
+#endif
+    }
+    else
+    {
+        if( p_es_descriptor->b_psi )
+        {
+            /* The payload contains PSI tables */
+            input_DemuxPSI( p_input, p_ts_packet, p_es_descriptor,
+                            b_unit_start, b_lost );
+        }
+        else
+        {
+            /* The payload carries a PES stream */ 
+            input_DemuxPES( p_input, p_ts_packet, p_es_descriptor,
+                            b_unit_start, b_lost );
+        }
+    }
+
+#undef p
+}
+
+
+
+
+/*******************************************************************************
+ * input_DemuxPES: 
+ *******************************************************************************
+ * Gather a PES packet and analyzes its header.
+ *******************************************************************************/
+static __inline__ void input_DemuxPES( input_thread_t *p_input,
+                                       ts_packet_t *p_ts_packet,
+                                       es_descriptor_t *p_es_descriptor,
+                                       boolean_t b_unit_start,
+                                       boolean_t b_packet_lost )
+{
+    decoder_fifo_t *            p_fifo;
+    u8                          i_pes_header_size;
+    int                         i_dummy;
+    pes_packet_t*               p_last_pes;
+    ts_packet_t *               p_ts;
+    int                         i_ts_payload_size;
+    
+
+#define p_pes (p_es_descriptor->p_pes_packet)
+
+    ASSERT(p_input);
+    ASSERT(p_ts_packet);
+    ASSERT(p_es_descriptor);
+
+//    intf_DbgMsg("PES-demultiplexing %p (%p)\n", p_ts_packet, p_pes);
+
+    /* If we lost data, discard the PES packet we are trying to reassemble
+       if any and wait for the beginning of a new one in order to synchronise
+       again */
+    if( b_packet_lost && p_pes != NULL )
+    {
+        intf_DbgMsg("PES %p trashed because of packet lost\n", p_pes);
+        input_NetlistFreePES( p_input, p_pes );
+        p_pes = NULL;
+    }
+
+    /* If the TS packet contains the begining of a new PES packet, and if we
+       were reassembling a PES packet, then the PES should be complete now,
+       so parse its header and give it to the decoders */
+    if( b_unit_start && p_pes != NULL )
+    {
+//        intf_DbgMsg("End of PES packet %p\n", p_pes);
+
+        /* Parse the header. The header has a variable length, but in order 
+           to improve the algorithm, we will read the 14 bytes we may be
+           interested in */
+        p_ts = p_pes->p_first_ts;
+        i_ts_payload_size = p_ts->i_payload_end - p_ts->i_payload_start;
+        i_dummy = 0;
+
+        if(i_ts_payload_size >= PES_HEADER_SIZE)
+        {
+            /* This part of the header entirely fits in the payload of   
+               the first TS packet */
+            p_pes->p_pes_header = &(p_ts->buffer[p_ts->i_payload_start]);
+        }
+        else
+        {
+            /* This part of the header does not fit in the current TS packet:
+               copy the part of the header we are interested in to the
+               p_pes_header_save buffer */
+            intf_DbgMsg("Code never tested encourtered, WARNING ! (benny)\n");
+            do
+            {
+                memcpy(p_pes->p_pes_header_save + i_dummy,
+                       &p_ts->buffer[p_ts->i_payload_start], i_ts_payload_size);
+                i_dummy += i_ts_payload_size;
+            
+                p_ts = p_ts->p_next_ts;
+                if(!p_ts)
+                {
+                  /* The payload of the PES packet is shorter than the 14 bytes
+                     we would read. This means that high packet lost occured
+                     so the PES won't be usefull for any decoder. Moreover,
+                     this should never happen so we can trash the packet and
+                     exit roughly without regrets */
+                  intf_DbgMsg("PES packet too short: trashed\n");
+                  input_NetlistFreePES( p_input, p_pes );
+                  p_pes = NULL;
+                  /* Stats ?? */
+                  return;
+                }
+                
+                i_ts_payload_size = p_ts->i_payload_end - p_ts->i_payload_start;
+            }
+            while(i_ts_payload_size + i_dummy < PES_HEADER_SIZE);
+
+            /* This last TS packet is partly header, partly payload, so just
+               copy the header part */
+            memcpy(p_pes->p_pes_header_save + i_dummy,
+                   &p_ts->buffer[p_ts->i_payload_start],
+                   PES_HEADER_SIZE - i_dummy);
+
+            /* The header must be read in the buffer not in any TS packet */
+           p_pes->p_pes_header = p_pes->p_pes_header_save;
+        }
+        
+        /* Now we have the part of the PES header we were interested in:
+           parse it */
+
+        /* First read the 6 header bytes common to all PES packets:
+           use them to test the PES validity */
+        if( (p_pes->p_pes_header[0] || p_pes->p_pes_header[1] ||
+            (p_pes->p_pes_header[2] != 1)) ||
+                                     /* packet_start_code_prefix != 0x000001 */
+            ((i_dummy = U16_AT(p_pes->p_pes_header + 4)) &&
+             (i_dummy + 6 != p_pes->i_pes_size)) )
+                   /* PES_packet_length is set and != total received payload */
+        {
+          /* Trash the packet and set p_pes to NULL to be sure the next PES
+             packet will have its b_data_lost flag set */
+          intf_DbgMsg("Corrupted PES packet received: trashed\n");
+          input_NetlistFreePES( p_input, p_pes );
+          p_pes = NULL;
+          /* Stats ?? */
+        }
+        else
+        {
+            /* The PES packet is valid. Check its type to test if it may
+               carry additional informations in a header extension */
+            p_pes->i_stream_id =  p_pes->p_pes_header[3];
+
+            switch( p_pes->i_stream_id )
+            {
+            case 0xBE:  /* Padding */
+            case 0xBC:  /* Program stream map */
+            case 0xBF:  /* Private stream 2 */
+            case 0xB0:  /* ECM */
+            case 0xB1:  /* EMM */
+            case 0xFF:  /* Program stream directory */
+            case 0xF2:  /* DSMCC stream */
+            case 0xF8:  /* ITU-T H.222.1 type E stream */
+                /* The payload begins immediatly after the 6 bytes header, so
+                   we have finished with the parsing */
+                i_pes_header_size = 6;
+                break;
+
+            default:
+                /* The PES header contains at least 3 more bytes: parse them */
+                p_pes->b_data_alignment = p_pes->p_pes_header[6] & 0x10;
+                p_pes->b_has_pts = p_pes->p_pes_header[7] & 0x4;
+                i_pes_header_size = 9 + p_pes->p_pes_header[8];
+                
+                /* Now parse the optional header extensions (in the limit of
+                   the 14 bytes */
+                if( p_pes->b_has_pts )
+                {
+                   /* The PTS field is split in 3 bit records. We have to add
+                      them, and thereafter we substract the 2 marker_bits */
+                    p_pes->i_pts = ( (p_pes->p_pes_header[9] << 29) +
+                                     (U16_AT(p_pes->p_pes_header + 10) << 14) +
+                                     (U16_AT(p_pes->p_pes_header + 12) >> 1) -
+                                     (1 << 14) - (1 << 29) );
+                }
+                break;
+            }
+
+            /* Now we've parsed the header, we just have to indicate in some
+               specific TS packets where the PES payload begins (renumber
+               i_payload_start), so that the decoders can find the beginning
+               of their data right out of the box. */
+            p_ts = p_pes->p_first_ts;
+            i_ts_payload_size = p_ts->i_payload_end - p_ts->i_payload_start;
+            while( i_pes_header_size > i_ts_payload_size )
+            {
+                /* These packets are entirely filled by the PES header. */
+                i_pes_header_size -= i_ts_payload_size;
+                p_ts->i_payload_start = p_ts->i_payload_end;
+                /* Go to the next TS packet: here we won't have to test it is
+                   not NULL because we trash the PES packets when packet lost
+                   occurs */
+                p_ts = p_ts->p_next_ts;
+                i_ts_payload_size = p_ts->i_payload_end - p_ts->i_payload_start;
+            }
+            /* This last packet is partly header, partly payload. */
+            p_ts->i_payload_start += i_pes_header_size;
+
+            /* Now we can eventually put the PES packet in the decoder's
+               PES fifo */
+            switch( p_es_descriptor->i_type )
+            {
+            case MPEG1_VIDEO_ES:
+            case MPEG2_VIDEO_ES:
+                p_fifo = &(((vdec_thread_t*)(p_es_descriptor->p_dec))->fifo);
+                break;
+            case MPEG1_AUDIO_ES:
+            case MPEG2_AUDIO_ES:
+                p_fifo = &(((adec_thread_t*)(p_es_descriptor->p_dec))->fifo);
+                break;
+            default:
+                /* This should never happen. */
+                intf_DbgMsg("Unknown stream type (%d, %d): PES trashed\n",
+                            p_es_descriptor->i_id, p_es_descriptor->i_type);
+                p_fifo = NULL;
+                break;
+            }
+
+            if( p_fifo != NULL )
+            {
+                pthread_mutex_lock( &p_fifo->data_lock );
+                if( DECODER_FIFO_ISFULL( *p_fifo ) )
+                {
+                    /* The FIFO is full !!! This should not happen. */
+#ifdef STATS
+                    p_input->c_ts_packets_trashed += p_pes->i_ts_packets;
+                    p_es_descriptor->c_invalid_packets += p_pes->i_ts_packets;
+#endif
+                    input_NetlistFreePES( p_input, p_pes );
+                    intf_DbgMsg("PES trashed - fifo full ! (%d, %d)\n",
+                               p_es_descriptor->i_id, p_es_descriptor->i_type);
+                }
+               else
+                {
+//                    intf_DbgMsg("Putting %p into fifo %p/%d\n",
+//                                p_pes, p_fifo, p_fifo->i_end);
+                    p_fifo->buffer[p_fifo->i_end] = p_pes;
+                    DECODER_FIFO_INCEND( *p_fifo );
+
+                    /* Warn the decoder that it's got work to do. */
+                    pthread_cond_signal( &p_fifo->data_wait );
+                }
+                pthread_mutex_unlock( &p_fifo->data_lock );
+            }
+            else
+            {
+                intf_DbgMsg("No fifo to receive PES %p: trash\n", p_pes);
+#ifdef STATS
+                p_input->c_ts_packets_trashed += p_pes->i_ts_packets;
+                p_es_descriptor->c_invalid_packets += p_pes->i_ts_packets;
+#endif
+                input_NetlistFreePES( p_input, p_pes );
+            }
+        }
+    }
+
+
+    /* If we are at the beginning of a new PES packet, we must fetch a new
+       PES buffer to begin with the reassembly of this PES packet. This is
+       also here that we can synchronise with the stream if we we lost
+       packets or if the decoder has just started */ 
+    if( b_unit_start )
+    {
+        p_last_pes = p_pes;
+
+        /* Get a new one PES from the PES netlist. */
+        if( (p_pes = input_NetlistGetPES( p_input )) == (NULL) )
+        {
+            /* PES netlist is empty ! */
+            p_input->b_error = 1;
+        }
+        else
+        {
+//           intf_DbgMsg("New PES packet %p (first TS: %p)\n", p_pes, p_ts_packet);
+
+            /* Init the PES fields so that the first TS packet could be correctly
+               added to the PES packet (see below) */
+            p_pes->p_first_ts = p_ts_packet;
+            p_pes->p_last_ts = NULL;
+
+            /* If the last pes packet was null, this means that the synchronisation
+               was lost and so warn the decoder that he will have to find a way to
+               recover */
+            if( !p_last_pes )
+                p_pes->b_data_loss = 1;
+
+            /* Read the b_random_access flag status and then reinit it */     
+            p_pes->b_random_access = p_es_descriptor->b_random;
+            p_es_descriptor->b_random = 0;
+        }
+    }
+
+
+    /* If we are synchronised with the stream, and so if we are ready to
+       receive correctly the data, add the TS packet to the current PES
+       packet */
+    if( p_pes != NULL )
+    {
+//      intf_DbgMsg("Adding TS %p to PES %p\n", p_ts_packet, p_pes);
+
+        /* Size of the payload carried in the TS packet */
+        i_ts_payload_size = p_ts_packet->i_payload_end -
+                            p_ts_packet->i_payload_start;
+
+        /* Update the relations between the TS packets */
+        p_ts_packet->p_prev_ts = p_pes->p_last_ts;
+        p_ts_packet->p_next_ts = NULL;
+        if( p_pes->i_ts_packets != 0 )
+        {
+            /* Regarder si il serait pas plus efficace de ne creer que les liens
+               precedent->suivant pour le moment, et les liens suivant->precedent
+               quand le paquet est termine */
+            /* Otherwise it is the first TS packet. */
+            p_pes->p_last_ts->p_next_ts = p_ts_packet;
+        }
+        /* Now add the TS to the PES packet */
+        p_pes->p_last_ts = p_ts_packet;
+        p_pes->i_ts_packets++;
+        p_pes->i_pes_size += i_ts_payload_size;
+
+        /* Stats */
+#ifdef STATS
+        i_dummy = p_ts_packet->i_payload_end - p_ts_packet->i_payload_start;
+        p_es_descriptor->c_payload_bytes += i_dummy;
+#endif
+    }
+    else
+    {
+        /* Since we don't use the TS packet to build a PES packet, we don't
+           need it anymore, so give it back to the netlist */
+//        intf_DbgMsg("Trashing TS %p: no PES being build\n", p_ts_packet);
+        input_NetlistFreeTS( p_input, p_ts_packet );     
+    }
+    
+#undef p_pes
+}
+
+
+
+
+/*******************************************************************************
+ * input_DemuxPSI:
+ *******************************************************************************
+ * Notice that current ES state has been locked by input_SortPacket. (No more true,
+ * changed by benny - See if it'a ok, and definitely change the code ???????? )
+ *******************************************************************************/
+static __inline__ void input_DemuxPSI( input_thread_t *p_input,
+                                       ts_packet_t *p_ts_packet,
+                                       es_descriptor_t *p_es_descriptor,
+                                       boolean_t b_unit_start, boolean_t b_packet_lost )
+{
+    int i_data_offset;      /* Offset of the interesting data in the TS packet */
+    u16 i_data_length;                                 /* Length of those data */
+    boolean_t b_first_section; /* Was there another section in the TS packet ? */
+    
+    ASSERT(p_input);
+    ASSERT(p_ts_packet);
+    ASSERT(p_es_descriptor);
+
+#define p_psi (p_es_descriptor->p_psi_section)
+
+//    intf_DbgMsg( "input debug: PSI demultiplexing %p (%p)\n", p_ts_packet, p_input);
+
+//    intf_DbgMsg( "Packet: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x (unit start: %d)\n", p_ts_packet->buffer[p_ts_packet->i_payload_start], p_ts_packet->buffer[p_ts_packet->i_payload_start+1], p_ts_packet->buffer[p_ts_packet->i_payload_start+2], p_ts_packet->buffer[p_ts_packet->i_payload_start+3], p_ts_packet->buffer[p_ts_packet->i_payload_start+4], p_ts_packet->buffer[p_ts_packet->i_payload_start+5], p_ts_packet->buffer[p_ts_packet->i_payload_start+6], p_ts_packet->buffer[p_ts_packet->i_payload_start+7], p_ts_packet->buffer[p_ts_packet->i_payload_start+8], p_ts_packet->buffer[p_ts_packet->i_payload_start+9], p_ts_packet->buffer[p_ts_packet->i_payload_start+10], p_ts_packet->buffer[p_ts_packet->i_payload_start+11], p_ts_packet->buffer[p_ts_packet->i_payload_start+12], p_ts_packet->buffer[p_ts_packet->i_payload_start+13], p_ts_packet->buffer[p_ts_packet->i_payload_start+14], p_ts_packet->buffer[p_ts_packet->i_payload_start+15], p_ts_packet->buffer[p_ts_packet->i_payload_start+16], p_ts_packet->buffer[p_ts_packet->i_payload_start+17], p_ts_packet->buffer[p_ts_packet->i_payload_start+18], p_ts_packet->buffer[p_ts_packet->i_payload_start+19], p_ts_packet->buffer[p_ts_packet->i_payload_start+20], b_unit_start);
+
+    /* The section we will deal with during the first iteration of the following
+       loop is the first one contained in the TS packet */
+    b_first_section = 1;
+
+    /* Reassemble the pieces of sections contained in the TS packet and decode
+       the sections that could have been completed */
+    do
+    {
+        /* Has the reassembly of a section already began in a previous packet ? */
+        if( p_psi->b_running_section )
+        {
+            /* Was data lost since the last TS packet ? */
+            if( b_packet_lost )
+            {
+                /* Discard the section and wait for the begining of a new one to resynch */
+                p_psi->b_running_section = 0;
+                intf_DbgMsg( "Section discarded due to packet loss\n" );
+            }
+            else
+            {
+                /* The data that complete a previously began section are always at
+                   the beginning of the TS payload... */
+                i_data_offset = p_ts_packet->i_payload_start;
+                /* ...Unless there is a pointer field, that we have to bypass */
+                if( b_unit_start )
+                    i_data_offset ++;
+//                intf_DbgMsg( "New part of the section received at offset %d\n", i_data_offset );
+            }
+        }
+        /* We are looking for the beginning of a new section */
+        else
+        {
+            if( !b_unit_start )
+            {
+                /* Cannot do anything with those data: trash both PSI section and TS packet */
+                p_psi->b_running_section = 0;
+                break;
+            }
+            else
+            {
+                /* Get the offset at which the data for that section can be found */
+                if( b_first_section )
+                {
+                    /* The offset is stored in the pointer_field since we are interested in
+                       the first section of the TS packet. Note that the +1 is to bypass
+                       the pointer field */
+                    i_data_offset = p_ts_packet->i_payload_start +
+                     p_ts_packet->buffer[p_ts_packet->i_payload_start] + 1;
+                }
+                else
+                {
+                    /* Since no gap is allowed between 2 sections in a TS packet, the
+                       offset is given by the end of the previous section. In fact, there
+                       is nothing to do, i_offset was set to the right value in the
+                       previous iteration */
+                }
+//                intf_DbgMsg( "New section beginning at offset %d in TS packet\n", i_data_offset );
+
+                /* Read the length of that section */
+                p_psi->i_length = (U16_AT(&p_ts_packet->buffer[i_data_offset+1]) & 0xFFF) + 3;
+//                intf_DbgMsg( "Section length %d\n", p_psi->i_length );
+                if( p_psi->i_length > PSI_SECTION_SIZE )
+                {
+                  /* The TS packet is corrupted, stop here to avoid possible a seg fault */
+                  intf_DbgMsg( "Section size is too big, aborting its reception\n" );
+                  break;
+                }
+
+                /* Init the reassembly of that section */
+                p_psi->b_running_section = 1;
+                p_psi->i_current_position = 0;
+            }
+        }
+
+        /* Compute the length of data related to the section in this TS packet */
+        if( p_psi->i_length - p_psi->i_current_position > TS_PACKET_SIZE - i_data_offset)
+            i_data_length = TS_PACKET_SIZE - i_data_offset;
+        else
+          i_data_length = p_psi->i_length - p_psi->i_current_position;
+
+        /* Copy those data in the section buffer */
+        memcpy( &p_psi->buffer[p_psi->i_current_position], &p_ts_packet->buffer[i_data_offset],
+                i_data_length );
+    
+        /* Interesting data are now after the ones we copied */
+        i_data_offset += i_data_length;
+
+        /* Decode the packet if it is now complete */
+        if (p_psi->i_length == p_psi->i_current_position + i_data_length)
+        {
+            /* Packet is complete, decode it */
+//            intf_DbgMsg( "SECTION COMPLETE: starting decoding of its data\n" );
+            input_PsiDecode( p_input, p_psi );
+
+            /* Prepare the buffer to receive a new section */
+            p_psi->i_current_position = 0;
+            p_psi->b_running_section = 0;
+        
+            /* The new section won't be the first anymore */
+            b_first_section = 0;
+        }
+        else
+        {
+            /* Prepare the buffer to receive the next part of the section */
+          p_psi->i_current_position += i_data_length;
+//          intf_DbgMsg( "Section not complete, waiting for the end\n" );
+        }
+    
+//        intf_DbgMsg( "Must loop ? Next data offset: %d, stuffing: %d\n",
+//                     i_data_offset, p_ts_packet->buffer[i_data_offset] );
+    }
+    /* Stop if we reached the end of the packet or stuffing bytes */
+    while( i_data_offset < TS_PACKET_SIZE && p_ts_packet->buffer[i_data_offset] != 0xFF );
+
+    /* Relase the TS packet, we don't need it anymore */
+    input_NetlistFreeTS( p_input, p_ts_packet );
+
+#undef p_psi  
+}
diff --git a/src/input/input_ctrl.c b/src/input/input_ctrl.c
new file mode 100644 (file)
index 0000000..213ade4
--- /dev/null
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * input_ctrl.c: Decodeur control
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Control the extraction and the decoding of the programs elements carried in
+ * a stream.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <sys/uio.h>                                                 /* iovec */
+#include <stdlib.h>                               /* atoi(), malloc(), free() */
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+#include <sys/soundcard.h>
+#include <netinet/in.h>                                              /* ntohs */
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"
+#include "intf_msg.h"
+#include "debug.h"
+
+#include "input.h"
+#include "input_netlist.h"
+
+#include "decoder_fifo.h"
+
+#include "audio_output.h"
+#include "audio_dsp.h"
+#include "audio_decoder.h"
+
+#include "video.h"
+#include "video_output.h"
+#include "video_decoder.h"
+
+
+/******************************************************************************
+ * input_AddPgrmElem: Start the extraction and the decoding of a program element
+ ******************************************************************************
+ * Add the element given by its PID in the list of PID to extract and spawn
+ * the decoding thread. 
+ * This function only modifies the table of selected es, but must NOT modify
+ * the table of ES itself.
+ ******************************************************************************/
+int input_AddPgrmElem( input_thread_t *p_input, int i_current_id )
+{
+    int i_es_loop, i_selected_es_loop;
+    
+    /* Since this function is intended to be called by interface, lock the
+     * elementary stream structure. */
+    pthread_mutex_lock( &p_input->es_lock );
+
+    /* Find out which PID we need. */
+    for( i_es_loop = 0; i_es_loop < INPUT_MAX_ES; i_es_loop++ )
+    {
+        if( p_input->p_es[i_es_loop].i_id == i_current_id )
+        {
+            if( p_input->p_es[i_es_loop].p_dec != NULL )
+            {
+                /* We already have a decoder for that PID. */
+                pthread_mutex_unlock( &p_input->es_lock );
+                intf_ErrMsg("input error: PID %d already selected\n",
+                            i_current_id);
+                return( -1 );
+            }
+
+            intf_DbgMsg("Requesting selection of PID %d\n",
+                        i_current_id);
+
+            /* Find a free spot in pp_selected_es. */
+            for( i_selected_es_loop = 0; p_input->pp_selected_es[i_selected_es_loop] != NULL
+                  && i_selected_es_loop < INPUT_MAX_SELECTED_ES; i_selected_es_loop++ );
+            
+            if( i_selected_es_loop == INPUT_MAX_SELECTED_ES )
+            {
+                /* array full */
+                pthread_mutex_unlock( &p_input->es_lock );
+                intf_ErrMsg("input error: MAX_SELECTED_ES reached: try increasing it in config.h\n");
+                return( -1 );
+            }
+
+            /* Don't decode PSI streams ! */
+            if( p_input->p_es[i_es_loop].b_psi )
+            {
+                intf_ErrMsg("input_error: trying to decode PID %d which is the one of a PSI\n");
+                pthread_mutex_unlock( &p_input->es_lock );
+                return( -1 );
+            }
+            else
+            {
+                /* Spawn the decoder. */
+                switch( p_input->p_es[i_es_loop].i_type )
+                {
+                    case MPEG1_AUDIO_ES:
+                    case MPEG2_AUDIO_ES:
+                        /* Spawn audio thread. */
+                        if( ((adec_thread_t*)(p_input->p_es[i_es_loop].p_dec) =
+                            adec_CreateThread( p_input )) == NULL )
+                        {
+                            intf_ErrMsg("Could not start audio decoder\n");
+                            pthread_mutex_unlock( &p_input->es_lock );
+                            return( -1 );
+                        }
+                        break;
+
+                    case MPEG1_VIDEO_ES:
+                    case MPEG2_VIDEO_ES:
+                        /* Spawn video thread. */
+/* Les 2 pointeurs NULL ne doivent pas etre NULL sinon on segfault !!!! */
+//                        if( ((vdec_thread_t*)(p_input->p_es[i_es_loop].p_dec) =
+//                            vdec_CreateThread( NULL, p_input, NULL )) == NULL )
+//                        {
+//                            intf_ErrMsg("Could not start video decoder\n");
+//                            pthread_mutex_unlock( &p_input->es_lock );
+//                            return( -1 );
+//                        }
+                        break;
+
+                    default:
+                        /* That should never happen. */
+                        intf_DbgMsg("input error: unknown stream type (%d)\n",
+                                    p_input->p_es[i_es_loop].i_type);
+                        pthread_mutex_unlock( &p_input->es_lock );
+                        return( -1 );
+                        break;
+                }
+
+                /* Initialise the demux */
+                p_input->p_es[i_es_loop].p_pes_packet = NULL;
+                p_input->p_es[i_es_loop].i_continuity_counter = 0;
+                p_input->p_es[i_es_loop].b_random = 0;
+               
+                /* Mark stream to be demultiplexed. */
+                intf_DbgMsg("Stream %d added in %d\n", i_current_id, i_selected_es_loop);
+                p_input->pp_selected_es[i_selected_es_loop] = &p_input->p_es[i_es_loop];
+                pthread_mutex_unlock( &p_input->es_lock );
+                return( 0 );
+            }
+        }
+    }
+    
+    /* We haven't found this PID in the current stream. */
+    pthread_mutex_unlock( &p_input->es_lock );
+    intf_ErrMsg("input error: can't find PID %d\n", i_current_id);
+    return( -1 );
+}
+
+/******************************************************************************
+ * input_DelPgrmElem: Stop the decoding of a program element
+ ******************************************************************************
+ * Stop the extraction of the element given by its PID and kill the associated
+ * decoder thread
+ * This function only modifies the table of selected es, but must NOT modify
+ * the table of ES itself.
+ ******************************************************************************/
+int input_DelPgrmElem( input_thread_t *p_input, int i_current_id )
+{
+    int i_selected_es_loop, i_last_selected;
+
+    /* Since this function is intended to be called by interface, lock the
+       structure. */
+    pthread_mutex_lock( &p_input->es_lock );
+
+    /* Find out which PID we need. */
+    for( i_selected_es_loop = 0; i_selected_es_loop < INPUT_MAX_SELECTED_ES;
+         i_selected_es_loop++ )
+    {
+        if( p_input->pp_selected_es[i_selected_es_loop] )
+        {
+            if( p_input->pp_selected_es[i_selected_es_loop]->i_id == i_current_id )
+            {
+                if( !(p_input->pp_selected_es[i_selected_es_loop]->p_dec) )
+                {
+                    /* We don't have a decoder for that PID. */
+                    pthread_mutex_unlock( &p_input->es_lock );
+                    intf_ErrMsg("input error: PID %d already deselected\n",
+                                i_current_id);
+                    return( -1 );
+                }
+
+                intf_DbgMsg("input debug: requesting termination of PID %d\n",
+                            i_current_id);
+
+                /* Cancel the decoder. */
+                switch( p_input->pp_selected_es[i_selected_es_loop]->i_type )
+                {
+                    case MPEG1_AUDIO_ES:
+                    case MPEG2_AUDIO_ES:
+                        adec_DestroyThread( (adec_thread_t*)(p_input->pp_selected_es[i_selected_es_loop]->p_dec) );
+                        break;
+
+                    case MPEG1_VIDEO_ES:
+                    case MPEG2_VIDEO_ES:
+                        vdec_DestroyThread( (vdec_thread_t*)(p_input->pp_selected_es[i_selected_es_loop]->p_dec), NULL );
+                        break;
+                }
+
+                /* Unmark stream. */
+                p_input->pp_selected_es[i_selected_es_loop]->p_dec = NULL;
+
+                /* Find last selected stream. */
+                for( i_last_selected = i_selected_es_loop;
+                        p_input->pp_selected_es[i_last_selected]
+                        && i_last_selected < INPUT_MAX_SELECTED_ES;
+                     i_last_selected++ );
+
+                /* Exchange streams. */
+                p_input->pp_selected_es[i_selected_es_loop] = 
+                            p_input->pp_selected_es[i_last_selected];
+                p_input->pp_selected_es[i_last_selected] = NULL;
+
+                pthread_mutex_unlock( &p_input->es_lock );
+                return( 0 );
+            }
+        }
+    }
+
+    /* We haven't found this PID in the current stream. */
+    pthread_mutex_unlock( &p_input->es_lock );
+    intf_ErrMsg("input error: can't find PID %d\n", i_current_id);
+    return( -1 );
+}
+
+
+
+/******************************************************************************
+ * input_IsElemRecv: Test if an element given by its PID is currently received
+ ******************************************************************************
+ * Cannot return the position of the es in the pp_selected_es, for it can
+ * change once we have released the lock
+ ******************************************************************************/
+boolean_t input_IsElemRecv( input_thread_t *p_input, int i_id )
+{
+  boolean_t b_is_recv = 0;
+    int i_index = 0;
+
+   /* Since this function is intended to be called by interface, lock the
+       structure. */
+    pthread_mutex_lock( &p_input->es_lock );
+
+    /* Scan the table */
+    while( i_index < INPUT_MAX_SELECTED_ES && !p_input->pp_selected_es[i_index] )
+    {
+      if( p_input->pp_selected_es[i_index]->i_id == i_id )
+      {
+        b_is_recv = 1;
+        break;
+      }
+    }
+
+    /* Unlock the structure */
+    pthread_mutex_unlock( &p_input->es_lock );
+
+    return( b_is_recv );
+}
diff --git a/src/input/input_file.c b/src/input/input_file.c
new file mode 100644 (file)
index 0000000..47405a5
--- /dev/null
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * file.c: functions to read from a file 
+ * (c)1999 VideoLAN
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <pthread.h>
+#include <sys/uio.h>
+
+#include "common.h"
+#include "config.h"
+
+#include "input.h"
+#include "input_file.h"
+
+/******************************************************************************
+ * input_FileCreateMethod : open a file descriptor
+ ******************************************************************************/
+int input_FileCreateMethod( input_thread_t *p_input ,
+                            input_cfg_t *p_cfg )
+{
+  return( -1 );
+}
+
+/******************************************************************************
+ * input_FileRead : read from a file
+ ******************************************************************************/
+int input_FileRead( input_thread_t *p_input, const struct iovec *p_vector,
+                    size_t i_count )
+{
+  return( -1 );
+}
+
+/******************************************************************************
+ * input_FileDestroyMethod : close a file descriptor
+ ******************************************************************************/
+void input_FileDestroyMethod( input_thread_t *p_input )
+{
+}
+
+
+
diff --git a/src/input/input_netlist.c b/src/input/input_netlist.c
new file mode 100644 (file)
index 0000000..044dbc7
--- /dev/null
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * netlist.c: input thread 
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * Manages the TS and PES netlists (see netlist.h).
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <pthread.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"
+#include "intf_msg.h"
+#include "debug.h"
+#include "input.h"
+#include "input_netlist.h"
+
+/******************************************************************************
+ * Local prototypes
+ ******************************************************************************/
+
+/******************************************************************************
+ * input_NetlistOpen: initialize the netlists buffers
+ ******************************************************************************/
+int input_NetlistOpen( input_thread_t *p_input )
+{
+    int                 i_base, i_packets, i_iovec;
+
+    /* Initialize running indexes. */
+#ifdef INPUT_LIFO_TS_NETLIST
+    p_input->netlist.i_ts_index = INPUT_TS_READ_ONCE;
+#else
+    p_input->netlist.i_ts_start = 0;
+    p_input->netlist.i_ts_end = 0;
+#endif
+#ifdef INPUT_LIFO_PES_NETLIST
+    p_input->netlist.i_pes_index = 1; /* We allocate one PES at a time */
+#else
+    p_input->netlist.i_pes_start = 0;
+    p_input->netlist.i_pes_end = 0;
+#endif
+
+    /* Initialize all iovec from the TS netlist with the length of a packet */
+    for( i_iovec = 0; i_iovec < INPUT_MAX_TS + INPUT_TS_READ_ONCE; i_iovec++ )
+    {
+        p_input->netlist.p_ts_free[i_iovec].iov_len = TS_PACKET_SIZE;
+    }
+
+    /* Allocate a big piece of memory to contain the INPUT_MAX_TS TS packets */
+    if( ( p_input->netlist.p_ts_packets = malloc( (INPUT_MAX_TS + 1)
+                                             * sizeof(ts_packet_t) ) ) == NULL )
+    {
+        intf_ErrMsg("input error: can't allocate TS netlist buffer (%s)\n",
+                    strerror(errno) );
+        return( -1 );
+    }
+
+    /* Allocate a big piece of memory to contain the INPUT_MAX_PES PES packets */
+    if( !( p_input->netlist.p_pes_packets = malloc( (INPUT_MAX_PES + 1)
+                                             * sizeof(pes_packet_t) ) ) )
+    {
+        intf_ErrMsg("input error: can't allocate PES netlist buffer (%s)\n",
+                    strerror(errno) );
+        free( p_input->netlist.p_ts_packets );
+        return( -1 );
+    }
+
+    /* Insert TS packets into the TS netlist */
+#ifdef INPUT_LIFO_TS_NETLIST
+    i_base = p_input->netlist.i_ts_index;
+#else
+    i_base = p_input->netlist.i_ts_start;
+#endif
+    /* i_base is now the base address to locate free packets in the netlist */
+
+    for( i_packets = 0; i_packets < INPUT_MAX_TS + 1; i_packets++ )
+    {
+        p_input->netlist.p_ts_free[i_base + i_packets].iov_base
+                              = (p_input->netlist.p_ts_packets + i_packets);
+        /* Initialize TS length. */
+        (p_input->netlist.p_ts_packets[i_packets]).i_payload_end = TS_PACKET_SIZE;
+    }
+
+    /* Insert PES packets into the netlist */
+#ifdef INPUT_LIFO_PES_NETLIST
+    i_base = p_input->netlist.i_pes_index;
+#else
+    i_base = p_input->netlist.i_pes_start;
+#endif
+    /* i_base is now the base address to locate free packets in the netlist */
+
+    for( i_packets = 0; i_packets < INPUT_MAX_PES + 1; i_packets++ )
+    {
+        p_input->netlist.p_pes_free[i_base + i_packets]
+                              = p_input->netlist.p_pes_packets + i_packets;
+    }
+
+    return( 0 );
+}
+
+/******************************************************************************
+ * input_NetlistClean: clean the netlists buffers
+ ******************************************************************************/
+void input_NetlistClean( input_thread_t *p_input )
+{
+    free( p_input->netlist.p_ts_packets );                 /* free TS netlist */
+    free( p_input->netlist.p_pes_packets );               /* free PES netlist */
+}
+
diff --git a/src/input/input_network.c b/src/input/input_network.c
new file mode 100644 (file)
index 0000000..d1cf219
--- /dev/null
@@ -0,0 +1,297 @@
+/*******************************************************************************
+ * network.c: functions to read from the network 
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Manages a socket.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <pthread.h>
+#include <sys/uio.h>
+#include <string.h>
+#include <stdio.h>
+#include <netdb.h>      /* servent, getservbyname(), hostent, gethostbyname() */
+#include <sys/socket.h>          /* socket(), setsockopt(), bind(), connect() */
+#include <unistd.h>                                                /* close() */
+#include <netinet/in.h>                      /* sockaddr_in, htons(), htonl() */
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"
+#include "netutils.h"
+
+#include "input.h"
+#include "input_network.h"
+#include "input_vlan.h"
+
+#include "intf_msg.h"
+
+/******************************************************************************
+ * Local prototypes
+ ******************************************************************************/
+
+
+/******************************************************************************
+ * input_NetworkCreateMethod: initialize a network stream
+ ******************************************************************************/
+int input_NetworkCreateMethod( input_thread_t *p_input,
+                               input_cfg_t *p_cfg )
+{
+    int                     i_socket_option, i_port, i_dummy;
+    struct sockaddr_in      sa_in;
+    char                    psz_hostname[INPUT_MAX_HOSTNAME_LENGTH];
+
+    /* First and foremost, in the VLAN method, we join the desired VLAN. */
+    if( p_input->i_method == INPUT_METHOD_TS_VLAN_BCAST )
+    {
+        /* Get a VLAN ID (VlanLib). */
+        if( ( p_input->i_vlan_id = input_VlanId( NULL, p_cfg->i_vlan ) )
+                == (-1) )
+        {
+            intf_ErrMsg("input error: VlanId() failed (%d)\n",
+                        p_input->i_vlan_id);
+            return( -1 );
+        }
+        /* Join the VLAN. */
+        if( ( i_dummy = input_VlanJoin( p_input->i_vlan_id ) ) != 0 )
+        {
+            intf_ErrMsg("input error: VlanJoin() failed (%d)\n",
+                        i_dummy);
+            return( -1 );
+        }
+    }
+
+    /* We open a SOCK_DGRAM (UDP) socket, in the AF_INET domain, automatic (0)
+     * protocol */
+    if( (p_input->i_handle = socket( AF_INET, SOCK_DGRAM, 0 )) == (-1) )
+    {
+        intf_ErrMsg("input error: socket() error (%s)\n",
+                    strerror(errno));
+        return( -1 );
+    }
+
+    intf_DbgMsg("input debug: socket %d opened (cfg: %p)\n", p_input->i_handle,
+               p_cfg);
+
+    /* we set up the options of our socket. It doesn't need to be non-blocking,
+     * on the contrary (when this thread is waiting for data, it doesn't have
+     * the lock, so decoders can work. */
+
+    /* set SO_REUSEADDR option which allows to re-bind() a busy port */
+    i_socket_option = 1;
+    if( setsockopt( p_input->i_handle,
+                    SOL_SOCKET,
+                    SO_REUSEADDR,
+                    &i_socket_option,
+                    sizeof( i_socket_option ) ) == (-1) )
+    {
+        intf_ErrMsg("input error: setsockopt(SO_REUSEADDR) error (%s)\n",
+                    strerror(errno));
+        close( p_input->i_handle );
+        return( -1 );
+    }
+
+    /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid
+     * packet loss caused by scheduling problems */
+    i_socket_option = 524288;
+    if( setsockopt( p_input->i_handle,
+                    SOL_SOCKET,
+                    SO_RCVBUF,
+                    &i_socket_option,
+                    sizeof( i_socket_option ) ) == (-1) )
+    {
+        intf_ErrMsg("input error: setsockopt(SO_RCVBUF) error (%s)\n",
+                    strerror(errno));
+        close( p_input->i_handle );
+        return( -1 );
+    }
+    
+    /* Now, we bind the socket. */
+
+    /* Find the port. */
+    if( p_cfg->i_properties & INPUT_CFG_PORT )
+    {
+        i_port = p_cfg->i_port;
+        intf_DbgMsg("input debug: using port %d\n", i_port);
+    }
+    else
+    {
+#ifdef VIDEOLAN_DEFAULT_PORT
+        /* default port */
+        i_port = VIDEOLAN_DEFAULT_PORT;
+        intf_DbgMsg("input debug: using default port (%d)\n", i_port);
+#else
+        intf_ErrMsg("input error: no default port\n");
+        return( -1 );
+#endif
+    }
+
+    /* Find the address. */
+    switch( p_input->i_method )
+    {
+        case INPUT_METHOD_TS_BCAST:
+        case INPUT_METHOD_TS_VLAN_BCAST:
+            /* In that case, we have to bind with the broadcast address.
+             * broadcast addresses are very hard to find and depends on
+             * implementation, so we thought using a #define would be much
+             * simpler. */
+#ifdef INPUT_BCAST_ADDR
+            if( BuildInetAddr( &sa_in, INPUT_BCAST_ADDR, i_port ) == (-1) )
+            {                                               /* see netutils.c */
+                close( p_input->i_handle );
+                return( -1 );
+            }
+#else
+            /* We bind with any address. Security problem ! */
+            if( BuildInetAddr( &sa_in, NULL, i_port ) == (-1) )
+            {
+                close( p_input->i_handle );
+                return( -1 ),
+            }
+#endif
+            break;
+
+        case INPUT_METHOD_TS_UCAST:
+            /* We bind with the local address. */
+            if( gethostname( psz_hostname, sizeof( psz_hostname ) ) == (-1) )
+            {
+                intf_ErrMsg("input error: gethostname failed (%s)\n",
+                            strerror(errno));
+                close( p_input->i_handle );
+                return( -1 );
+            }
+            if( BuildInetAddr( &sa_in, psz_hostname, i_port ) == (-1) )
+            {
+                close( p_input->i_handle );
+                return( -1 );
+            }
+            break;
+        case INPUT_METHOD_TS_MCAST:
+            /* We bind with 239.0.0.1. */
+            if( BuildInetAddr( &sa_in, "239.0.0.1", i_port ) == (-1) )
+            {
+                close( p_input->i_handle );
+                return( -1 );
+            }
+            break;
+    }
+
+    /* Effectively bind the socket. */
+    if( bind( p_input->i_handle,
+              (struct sockaddr *) &sa_in,
+              sizeof( sa_in ) ) < 0 )
+    {
+        intf_ErrMsg("input error: bind() failed (%s)\n",
+                    strerror(errno));
+        close( p_input->i_handle );
+        return( -1 );
+    }
+
+    /* Connect the socket to the remote server. */
+
+    /* Find which server we have to use. */
+    if( p_cfg->i_properties & INPUT_CFG_HOSTNAME )
+    {
+        if( BuildInetAddr( &sa_in, p_cfg->psz_hostname, htons(0) ) == (-1) )
+        {
+            close( p_input->i_handle );
+            return( -1 );
+        }
+    }
+    else if( p_cfg->i_properties & INPUT_CFG_IP )
+    {
+        if( BuildInetAddr( &sa_in, p_cfg->psz_ip, htons(0) ) == (-1) )
+        {
+            close( p_input->i_handle );
+            return( -1 );
+        }
+    }
+    else
+    {
+#ifdef VIDEOLAN_DEFAULT_SERVER
+        /* default server */
+        if( BuildInetAddr( &sa_in, VIDEOLAN_DEFAULT_SERVER, htons(0) ) == (-1) )
+        {
+            close( p_input->i_handle );
+            return( -1 );
+        }
+#else
+        intf_ErrMsg("input error: no default videolan server\n");
+        return( -1 );
+#endif
+    }
+
+    /* Effectively connect the socket. */
+    if( connect( p_input->i_handle,
+                 (struct sockaddr *) &sa_in,
+                 sizeof( sa_in ) ) == (-1) )
+    {
+        intf_ErrMsg("input error: connect() failed\n");
+        close( p_input->i_handle );
+        return( -1 );
+    }
+    return( 0 );
+}
+
+/******************************************************************************
+ * input_NetworkRead: read a stream from the network
+ ******************************************************************************
+ * Wait for data during up to 1 second and then abort if none is arrived. The
+ * number of bytes read is returned or -1 if an error occurs (so 0 is returned
+ * after a timeout)
+ * We don't have to make any test on presentation times, since we suppose
+ * the network server sends us data when we need it.
+ ******************************************************************************/
+int input_NetworkRead( input_thread_t *p_input, const struct iovec *p_vector,
+                       size_t i_count )
+{
+    fd_set rfds;
+    struct timeval tv;
+    int i_rc;
+
+    /* Watch the given fd to see when it has input */
+    FD_ZERO(&rfds);
+    FD_SET(p_input->i_handle, &rfds);
+  
+    /* Wait up to 1 second */
+    tv.tv_sec = 1;
+    tv.tv_usec = 0;
+
+    i_rc = select(p_input->i_handle+1, &rfds, NULL, NULL, &tv);
+
+    if( i_rc > 0 )
+    {
+        /* Data were received before timeout */
+        i_rc = readv( p_input->i_handle, p_vector, i_count );
+    }
+
+    return( i_rc );
+}
+
+/******************************************************************************
+ * input_NetworkDestroyMethod: close a network stream
+ ******************************************************************************/
+void input_NetworkDestroyMethod( input_thread_t *p_input )
+{
+    /* Close local socket. */
+    if( p_input->i_handle )
+    {
+        if( close( p_input->i_handle) == (-1) )
+        {
+            intf_ErrMsg("input error: can't close network socket (%s)\n",
+                        strerror(errno) );
+        }
+    }
+
+    /* In case of VLAN method, leave the current VLAN. */
+    if( p_input->i_method == INPUT_METHOD_TS_VLAN_BCAST )
+    {
+        input_VlanLeave( p_input->i_vlan_id );
+    }
+}
diff --git a/src/input/input_pcr.c b/src/input/input_pcr.c
new file mode 100644 (file)
index 0000000..5dcbd95
--- /dev/null
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * pcr.c: PCR management 
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Manages structures containing PCR information.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <stdio.h>
+#include <pthread.h>
+#include <sys/uio.h>                                                 /* iovec */
+#include <stdlib.h>                               /* atoi(), malloc(), free() */
+#include <netinet/in.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "debug.h"
+#include "input.h"
+#include "intf_msg.h"
+#include "input_pcr.h"
+
+/******************************************************************************
+ * input_PcrReInit : Reinitialize the pcr_descriptor
+ ******************************************************************************/
+void input_PcrReInit( input_thread_t *p_input )
+{
+    pcr_descriptor_t* p_pcr;
+    ASSERT(p_input);
+    
+    p_pcr = p_input->p_pcr;
+
+    p_pcr->c_average = 0;
+    p_pcr->c_average_jitter = 0;
+
+#ifdef STATS
+    p_pcr->c_pcr = 0;
+    p_pcr->max_jitter = 0;
+
+/* For the printf in input_PcrDecode(), this is used for debug purpose only */
+    printf("\n");
+#endif
+}
+
+/******************************************************************************
+ * input_PcrInit : Initialize PCR decoder
+ ******************************************************************************/
+int input_PcrInit( input_thread_t *p_input )
+{
+    ASSERT(p_input);
+
+    if( (p_input->p_pcr = malloc(sizeof(pcr_descriptor_t))) == NULL )
+    {
+        return( -1 );
+    }
+    pthread_mutex_init( &p_input->p_pcr->lock, NULL );
+    input_PcrReInit(p_input);
+    
+    return( 0 );
+}
+
+/******************************************************************************
+ * input_PcrDecode : Decode a PCR frame
+ ******************************************************************************/
+void input_PcrDecode( input_thread_t *p_input, es_descriptor_t *p_es,
+                      u8* p_pcr_data )
+{
+    s64 pcr_time, sys_time, delta_clock;
+    pcr_descriptor_t *p_pcr;
+        
+    ASSERT(p_pcr_data);
+    ASSERT(p_input);
+    ASSERT(p_es);
+
+    p_pcr = p_input->p_pcr;
+    if( p_es->b_discontinuity )
+    {
+        input_PcrReInit(p_input);
+        p_es->b_discontinuity = 0;
+    }
+    
+    /* Express the PCR in microseconde
+     * WARNING: do not remove the casts in the following calculation ! */
+    pcr_time = ( (( (s64)U32_AT((u32*)p_pcr_data) << 1 ) | ( p_pcr_data[4] >> 7 )) * 300 ) / 27;
+    sys_time = mdate();
+    delta_clock = sys_time - pcr_time;
+    
+    pthread_mutex_lock( &p_pcr->lock );
+    
+    if( p_pcr->c_average == PCR_MAX_AVERAGE_COUNTER )
+    {
+        p_pcr->delta_clock = (delta_clock + (p_pcr->delta_clock * (PCR_MAX_AVERAGE_COUNTER-1)))
+                             / PCR_MAX_AVERAGE_COUNTER;
+    }
+    else
+    {
+        p_pcr->delta_clock = (delta_clock + (p_pcr->delta_clock *  p_pcr->c_average))
+                             / (p_pcr->c_average + 1);
+        p_pcr->c_average++;
+    }
+
+    pthread_mutex_unlock( &p_pcr->lock );
+    
+#ifdef STATS
+    {
+        s64 jitter;
+        
+        jitter = delta_clock - p_pcr->delta_clock;
+        /* Compute the maximum jitter */
+        if( jitter < 0 )
+        {
+            if( (p_pcr->max_jitter <= 0 && p_pcr->max_jitter >= jitter) ||
+                (p_pcr->max_jitter >= 0 && p_pcr->max_jitter <= -jitter))
+                {
+                    p_pcr->max_jitter = jitter;
+                }
+        }
+        else
+        {
+            if( (p_pcr->max_jitter <= 0 && -p_pcr->max_jitter <= jitter) ||
+                (p_pcr->max_jitter >= 0 && p_pcr->max_jitter <= jitter))
+                {
+                    p_pcr->max_jitter = jitter;
+                }
+        }        
+                    
+        /* Compute the average jitter */
+        if( p_pcr->c_average_jitter == PCR_MAX_AVERAGE_COUNTER )
+        {
+            p_pcr->average_jitter = (jitter + (p_pcr->average_jitter * (PCR_MAX_AVERAGE_COUNTER-1)))
+                                    / PCR_MAX_AVERAGE_COUNTER;
+        }
+        else
+        {
+            p_pcr->average_jitter = (jitter + (p_pcr->average_jitter *  p_pcr->c_average_jitter))
+                                    / (p_pcr->c_average + 1);
+            p_pcr->c_average_jitter++;
+        }
+        
+        printf("delta: % 13Ld, max_jitter: % 9Ld, av. jitter: % 6Ld, PCR %6ld \r",
+               p_pcr->delta_clock , p_pcr->max_jitter, p_pcr->average_jitter, p_pcr->c_pcr);    
+        fflush(stdout);
+        
+        p_pcr->c_pcr++;
+    }
+#endif
+}
+
+/******************************************************************************
+ * input_PcrClean : Clean PCR structures before dying
+ ******************************************************************************/
+void input_PcrClean( input_thread_t *p_input )
+{
+    ASSERT( p_input );
+
+    free( p_input->p_pcr );
+}
diff --git a/src/input/input_psi.c b/src/input/input_psi.c
new file mode 100644 (file)
index 0000000..1150949
--- /dev/null
@@ -0,0 +1,1267 @@
+/*******************************************************************************
+ * psi.c: PSI management 
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Manages structures containing PSI information, and affiliated decoders.
+ * TO DO: Fonctions d'init des structures
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/uio.h>                                                 /* iovec */
+#include <stdlib.h>                               /* atoi(), malloc(), free() */
+#include <string.h>
+#include <netinet/in.h>                                              /* ntohs */
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"
+#include "intf_msg.h"
+#include "debug.h"
+
+#include "input.h"
+#include "input_ctrl.h"
+#include "input_psi.h"
+
+/*
+ * Precalculated 32-bits CRC table, shared by all instances of the PSI decoder
+ */
+boolean_t b_crc_initialised = 0;
+u32 i_crc_32_table[256];
+
+/*
+ * Locale type definitions
+ */
+#define PSI_VIDEO_STREAM_DESCRIPTOR                 0x02
+#define PSI_AUDIO_STREAM_DESCRIPTOR                 0x03
+#define PSI_TARGET_BACKGROUND_GRID_DESCRIPTOR       0x07
+#define PSI_VIDEO_WINDOW_DESCRIPTOR                 0x08
+
+#ifdef DVB_EXTENSIONS
+#define PSI_SERVICE_DESCRIPTOR                      0x48
+#endif
+
+/* That info must be stored in the version field since it contains
+   unused bits */
+#define PSI_UNINITIALISED                           0xFF
+
+/*
+ * Local prototypes
+ */
+static int input_AddPsiPID( input_thread_t *p_input, int i_pid );
+static int input_DelPsiPID( input_thread_t *p_input, int i_pid );
+static void DecodePgrmAssocSection( byte_t* p_pas, input_thread_t *p_input );
+static void DecodePgrmMapSection( byte_t* p_pms, input_thread_t *p_input );
+static void DecodeSrvDescrSection( byte_t* p_sdt, input_thread_t *p_input );
+static void DecodePgrmDescriptor( byte_t* p_descriptor, pgrm_descriptor_t* p_pgrm );
+static void DecodeESDescriptor( byte_t* p_descriptor, es_descriptor_t* p_es );
+
+static stream_descriptor_t* AddStreamDescr(input_thread_t* p_input,
+                                           u16 i_stream_id);
+static void DestroyStreamDescr(input_thread_t* p_input, u16 i_stream_id);
+static pgrm_descriptor_t* AddPgrmDescr(stream_descriptor_t* p_stream,
+                                       u16 i_pgrm_id);
+static void DestroyPgrmDescr(input_thread_t* p_input, stream_descriptor_t* p_stream, u16 i_pgrm_id);
+static es_descriptor_t* AddESDescr(input_thread_t* p_input,
+                                   pgrm_descriptor_t* p_pgrm, u16 i_es_pid);
+static void DestroyESDescr(input_thread_t* p_input, pgrm_descriptor_t* p_pgrm,
+                           u16 i_es_pid);
+
+static void BuildCrc32Table();
+static int CheckCRC32( u8* p_pms, int i_size);
+static boolean_t Is_known( byte_t* a_known_section, u8 i_section );
+static void Set_known( byte_t* a_known_section, u8 i_section );
+static void Unset_known( byte_t* a_known_section, u8 i_section );
+
+
+/******************************************************************************
+ * input_PsiInit: Initialize PSI decoder
+ ******************************************************************************
+ * Init the structures in which the PSI decoder will put the informations it
+ * got from PSI tables and request for the reception of the PAT.
+ ******************************************************************************/
+int input_PsiInit( input_thread_t *p_input )
+{
+  ASSERT(p_input);
+  
+  /* Precalculate the 32-bit CRC table if not already done ???
+     TO DO -> Put a lock or do that at pgrm init */
+  if( !b_crc_initialised )
+  {
+    BuildCrc32Table();
+    b_crc_initialised = 1;
+  }
+  
+  /* Init the structure that describes the stream we are receiving */
+  AddStreamDescr( p_input, PSI_UNINITIALISED );
+
+  /* Request for reception of the program association table */
+  input_AddPsiPID( p_input, 0 );
+
+#ifdef DVB_EXTENSIONS
+  /* Request for reception of the service description table */
+  input_AddPsiPID( p_input, 17 );
+#endif
+
+  return( 0 );
+}
+
+
+/******************************************************************************
+ * input_PsiClean: Clean PSI structures before dying
+ ******************************************************************************/
+int input_PsiClean( input_thread_t *p_input )
+{
+  ASSERT(p_input);
+
+  /* Stop to receive all the PSI tables associated with that program */
+  /* TO DO ??? -> Not really usefull */
+
+  /* Clean also descriptors for programs associated with that stream */
+  /* TO DO ??? -> Not really usefull and maybe buggy */
+
+  /* Destroy the stream description */
+  DestroyStreamDescr( p_input, p_input->p_stream->i_stream_id );
+
+  return( 0 );
+}
+
+
+
+/******************************************************************************
+ * input_PsiRead: Read the table of programs
+ ******************************************************************************
+ * Ugly debugging function at that time ???????
+ ******************************************************************************/
+void input_PsiRead( input_thread_t *p_input /* ??? */ )
+{
+  int i_index;
+  int i_index2;
+  pgrm_descriptor_t* p_pgrm;
+  
+  ASSERT( p_input );
+  /* Lock the tables, since this method can be called from any thread */
+  //pthread_mutex_lock()
+
+  /* Check if the table is complete or not */
+  if( !p_input->p_stream->b_is_PMT_complete )
+  {
+    intf_IntfMsg( "Warning: PMT not yet complete\n" );
+  }
+  
+  /* Read the table */
+  for( i_index = 0; i_index < p_input->p_stream->i_pgrm_number; i_index++ )
+  {
+    p_pgrm = p_input->p_stream->ap_programs[i_index];
+    intf_Msg("Printing info for program %d\n", p_pgrm->i_number );
+    intf_IntfMsg("Printing info for program %d\n", p_pgrm->i_number );
+
+    for( i_index2 = 0; i_index2 < p_pgrm->i_es_number; i_index2++ )
+    {
+      intf_Msg( " ->Pid %d: type %d, PCR: %d, PSI: %d\n", p_pgrm->ap_es[i_index2]->i_id,
+                    p_pgrm->ap_es[i_index2]->i_type, p_pgrm->ap_es[i_index2]->b_pcr,
+                    p_pgrm->ap_es[i_index2]->b_psi);
+
+      intf_IntfMsg( " ->Pid %d: type %d, PCR: %d, PSI: %d\n", p_pgrm->ap_es[i_index2]->i_id,
+                    p_pgrm->ap_es[i_index2]->i_type, p_pgrm->ap_es[i_index2]->b_pcr,
+                    p_pgrm->ap_es[i_index2]->b_psi);
+    }
+  }
+
+  /* Unock the tables */
+  //pthread_mutex_unlock()
+}
+
+
+/******************************************************************************
+ * input_PsiDecode: Decode a PSI section
+ ******************************************************************************
+ * This funtion is essentially a wrapper that will  perform basic checks on
+ * the section and then call the right function according to its type.
+ ******************************************************************************/
+void input_PsiDecode( input_thread_t *p_input, psi_section_t* p_psi_section )
+{
+  ASSERT(p_input);
+  ASSERT(p_psi_section);
+
+  /* Hexa dump of the beginning of the section (for real men) */
+  //intf_DbgMsg( "Section: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", (u8)p_psi_section->buffer[0], (u8)p_psi_section->buffer[1], (u8)p_psi_section->buffer[2], (u8)p_psi_section->buffer[3], (u8)p_psi_section->buffer[4], (u8)p_psi_section->buffer[5], (u8)p_psi_section->buffer[6], (u8)p_psi_section->buffer[7], (u8)p_psi_section->buffer[8], (u8)p_psi_section->buffer[9], (u8)p_psi_section->buffer[10], (u8)p_psi_section->buffer[11], (u8)p_psi_section->buffer[12], (u8)p_psi_section->buffer[13], (u8)p_psi_section->buffer[14], (u8)p_psi_section->buffer[15], (u8)p_psi_section->buffer[16], (u8)p_psi_section->buffer[17], (u8)p_psi_section->buffer[18], (u8)p_psi_section->buffer[19] );
+
+  /* Check the CRC validity if any CRC is carried */
+  if( p_psi_section->buffer[1] & 0x80 )
+  {
+    if( CheckCRC32 (p_psi_section->buffer, p_psi_section->i_length) )
+    {
+      intf_DbgMsg("iSize: %d, CRC: %d\n", p_psi_section->i_length,
+                  U32_AT(&p_psi_section->buffer[p_psi_section->i_length-4]));
+      intf_DbgMsg( "Invalid CRC for PSI\n" );
+      return;
+    }
+  }
+  
+  /* If the section is not immediatly applicable, trash it (DVB drafts disallow
+     transmission of such sections, so we didn't implement it) */
+  if( !p_psi_section->buffer[5] & 0x01 )
+  {
+    intf_DbgMsg( "PSI not yet applicable: trash it\n" );
+    return;
+  }
+  
+  /* Handle the packet according to it's type given it the table_id  */
+  switch ( p_psi_section->buffer[0] )
+  {
+    case 0x00:
+      //intf_DbgMsg("Program association section received\n");
+      DecodePgrmAssocSection(p_psi_section->buffer, p_input);
+      break;
+    case 0x01:
+      //intf_DbgMsg("Conditional access section received\n");
+      break;
+    case 0x02:
+      //intf_DbgMsg("Program map section received\n");
+      DecodePgrmMapSection(p_psi_section->buffer, p_input);
+      break;
+    case 0x42:
+      //intf_DbgMsg("Service description section received\n");
+      DecodeSrvDescrSection(p_psi_section->buffer, p_input);
+      break;
+    default:
+      //intf_DbgMsg("Private PSI data received (type %x), ignoring it\n",
+      //            p_psi_section->buffer[0]);
+  }
+}
+
+
+/******************************************************************************
+ * DecodeAssocSection: Decode a PAS
+ ******************************************************************************
+ * No check is made to known if the table is currently applicable or not, so
+ * that unapplicable sections must be filtered before calling this function
+ * The Program Association Table can be segmented to occupy multiple sections
+ * so that we have to know which sections we have already received (IsKnown() /
+ * SetKnown() calls)
+ ******************************************************************************/
+static void DecodePgrmAssocSection(u8* p_pas, input_thread_t *p_input )
+{
+  u8 i_stream_id;               /* Id of the stream described in that section */
+    u8 i_version;              /* Version of the table carried in the section */
+
+    u16 i_pgrm_id;                       /* Id of the current described  pgrm */
+    u16 i_pgrm_map_pid;            /* PID of the associated program map table */
+    int i_pgrm_number;         /* Number of programs described in the section */
+
+    boolean_t b_is_invalid = 0;
+    
+    u8 i_current_section;
+    u8 i_last_section;
+
+    int i_pgrm_index;
+    int i_es_index;
+
+    ASSERT(p_pas);
+    ASSERT(p_input);
+
+#define p_descr (p_input->p_stream)
+
+   /* Read stream id and version number immediately, to be sure they will be
+      initialised in all cases we will need it */
+   i_stream_id = U16_AT(&p_pas[3]);
+   i_version = (p_pas[5] >> 1) & 0x1F;
+   //intf_DbgMsg("TS Id: %d, version: %d\n", U16_AT(&p_pas[3]),(p_pas[5] >> 1) & 0x1F);
+
+    /* Test if the stream has not changed by looking at the stream_id */
+    if( p_descr->i_stream_id != i_stream_id )
+    {
+      /* This can either mean that the PSI decoder has just started or that
+         the stream has changed */
+      if( p_descr->i_PAT_version== PSI_UNINITIALISED )
+        intf_Msg("Building Program Association table\n");
+      else
+        intf_ErrMsg( "Stream Id has suddently changed ! Rebuilding PAT\n" );
+
+      /* Whatever it is, ask the PSI decoder to rebuild the table */
+      b_is_invalid = 1;
+    }
+    else
+    {
+      /* Stream has not changed, test if the PMT is up to date */
+      if( p_descr->i_PAT_version != i_version )
+      {
+        intf_Msg("PAT has been updated, rebuilding it\n");
+        /* Ask the PSI decoder to rebuild the table */
+        b_is_invalid = 1;
+      }
+    }
+    
+    /* Clear the table if needed */
+    if( b_is_invalid )
+    {
+        intf_DbgMsg("Updating PAT table\n");
+
+        /* Any program in the stream may have disapeared, or a new one
+           can have been added. The good way to handle such a case would be
+           to build a temporary table and to make a diff */
+
+        /* Stop the reception of all programs and PSI informations
+           associated with this stream, excepted the PAT on PID 0 and the SDT
+           on PID 17 */
+        for( i_es_index = 0; i_es_index < INPUT_MAX_SELECTED_ES && 
+             p_input->pp_selected_es[i_es_index]; i_es_index++ )
+        {
+          if( p_input->pp_selected_es[i_es_index]->b_psi )
+          {
+            if( p_input->pp_selected_es[i_es_index]->i_id != 0
+#ifdef DVB_EXTENSIONS
+                && p_input->pp_selected_es[i_es_index]->i_id != 17
+#endif
+            )
+              input_DelPsiPID( p_input, p_input->pp_selected_es[i_es_index]->i_id );
+          }
+          else
+            input_DelPgrmElem( p_input, p_input->pp_selected_es[i_es_index]->i_id );
+        }
+
+        /* Recreate a new stream description. Since it is virgin, the decoder
+           will on is own rebuild it entirely */
+        DestroyStreamDescr(p_input, p_descr->i_stream_id);
+        AddStreamDescr(p_input, i_stream_id);
+
+        /* Record the new version for that table */
+        p_descr->i_PAT_version = i_version;
+    }
+
+    /* Build the table if not already complete or if it was cleared */
+    if( p_descr->b_is_PAT_complete )
+    {
+      /* Nothing to do */
+      //intf_DbgMsg("Table already complete\n");
+    }
+    else
+    {
+        /* Check if we already heard about that section */
+        i_last_section = p_pas[7];
+        i_current_section = p_pas[6];
+
+//        intf_DbgMsg( "Section %d (last section %d)\n",
+//                     i_current_section, i_last_section );
+
+        if( Is_known(p_descr->a_known_PAT_sections, i_current_section) )
+        {
+          /* Nothing to do */
+//          intf_DbgMsg("Section already received, skipping\n");
+        }
+        else
+        {
+          /* Compute the number of program_map PID carried in this section */
+          i_pgrm_number = ((U16_AT(&p_pas[1]) & 0xFFF) - 9) / 4;
+          intf_DbgMsg("Number of Pgrm in that section: %d\n", i_pgrm_number);
+
+          /* Start the reception of those program map PID */
+          for( i_pgrm_index = 0; i_pgrm_index < i_pgrm_number; i_pgrm_index++ )
+          {
+            i_pgrm_id = U16_AT(&p_pas[8+4*i_pgrm_index]);
+            i_pgrm_map_pid = U16_AT(&p_pas[8+4*i_pgrm_index+2]) & 0x1FFF;
+            intf_DbgMsg("Pgrm %d described on pid %d\n", i_pgrm_id, i_pgrm_map_pid);
+
+            /* Check we are not already receiving that pid because it carries info
+               for another program */
+            for( i_es_index = 0; i_es_index < INPUT_MAX_ES; i_es_index++ )
+            {
+              if( p_input->p_es[i_es_index].i_id == i_pgrm_map_pid)
+              {
+                intf_DbgMsg("Already receiving pid %d", i_pgrm_map_pid);
+                i_es_index = INPUT_MAX_ES+1;
+                break;
+              } 
+            }
+            /* Start to receive that PID if we're not already doing it */
+            if( i_es_index <= INPUT_MAX_ES )
+              input_AddPsiPID( p_input, i_pgrm_map_pid );
+
+            /* Append an entry to the pgrm_descriptor table to later store
+               the description of this program, unless program number is 0
+               (Network information table) */
+            if( i_pgrm_id != 0 )
+            {
+              intf_Msg("Adding program %d to the Program Map Table\n", i_pgrm_id);
+              AddPgrmDescr(p_descr, i_pgrm_id);
+            }
+          }
+          
+          /* We now know the info carried in this section */
+          Set_known(p_descr->a_known_PAT_sections, i_current_section);
+        
+          /* Check if the table is now complete */
+          p_descr->i_known_PAT_sections++;
+          if( p_descr->i_known_PAT_sections >= i_last_section)
+            p_descr->b_is_PAT_complete = 1;
+        }
+    }
+
+#undef p_descr
+}
+
+
+/******************************************************************************
+ * DecodePgrmMapSection: Decode a PMS
+ ******************************************************************************
+ * No check is made to known if the table is currently applicable or not, so
+ * that unapplicable sections must be filtered before calling this function
+ * The Program Map Table can be segmented to occupy multiple sections so that
+ * we have to know which sections we have already received (IsKnown() /
+ * SetKnown() calls)
+ * Note that the processing of those sections is different from the one of the
+ * others since here a section refers to a single program, and a program cannot
+ * be segmented into multiple sections
+ ******************************************************************************/
+static void DecodePgrmMapSection( u8* p_pms, input_thread_t* p_input )
+{
+  u16 i_pgrm_number;         /* Id of the program described in that section */
+  u8 i_version;              /* Version of the description for that program */
+  
+  u16 i_offset;
+  u16 i_section_length;
+  u16 i_descr_end;
+  
+  u8 i_last_section;
+  u8 i_current_section;
+
+  u16 i_es_pid;
+
+  int i_index = 0;
+  pgrm_descriptor_t* p_pgrm;
+  es_descriptor_t* p_es;
+
+#define p_descr (p_input->p_stream)
+
+  /* Read the id of the program described in that section */
+  i_pgrm_number = U16_AT(&p_pms[3]);
+//  intf_DbgMsg( "PMT section received for program %d\n", i_pgrm_number );
+
+  /* Find where is stored the description of this program */
+  for( i_index = 0; i_index < p_descr->i_pgrm_number &&
+       i_pgrm_number != p_descr->ap_programs[i_index]->i_number; i_index++ );
+
+  if( i_index >= p_descr->i_pgrm_number )
+  {
+    /* If none entry exists, this simply means that the PAT is not complete,
+       so skip this section since it is the responsability of the PAT decoder
+       to add pgrm_descriptor slots to the table of known pgrms */
+    intf_DbgMsg( "Pgrm %d is unknown: skipping its description\n", i_pgrm_number );
+    return;
+  }
+
+  /* We now have the slot which is the program described: we can begin with
+     the decoding of its description */
+  p_pgrm = p_descr->ap_programs[i_index];
+
+  /* Which section of the description of that program did we receive ? */
+  i_last_section = p_pms[7];
+  i_current_section = p_pms[6];
+//  intf_DbgMsg("Section %d (last section %d)\n", i_current_section, i_last_section);
+
+  /* Is this an update of the description for this program ? */
+  i_version = (p_pms[5] >> 1) && 0x1F;
+    if( p_pgrm->i_version != i_version )
+    {
+        intf_Msg("Updating PMT for program %d\n", i_pgrm_number);
+
+        for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
+        {
+          /* Stop the reception of the ES if needed by calling the function
+             normally used by the interface to manage this */
+          if( input_IsElemRecv(p_input, p_pgrm->ap_es[i_index]->i_id) )
+          {
+            intf_Msg( "PID %d is no more valid: stopping its reception\n",
+                      p_pgrm->ap_es[i_index]->i_id );
+            input_DelPgrmElem( p_input, p_pgrm->ap_es[i_index]->i_id );
+          }
+
+          /* Remove the descriptor associated to the es of this programs */
+          intf_DbgMsg( "Invalidating PID %d\n", p_pgrm->ap_es[i_index]->i_id );
+          DestroyESDescr(p_input, p_pgrm, p_pgrm->ap_es[i_index]->i_id);
+        }
+        
+       /* Update version number */
+        p_pgrm->i_version = i_version;
+
+        /* Ask the decoder to update the description of that program */
+        p_descr->i_known_PMT_sections--;
+        Unset_known( p_descr->a_known_PMT_sections, i_current_section );
+        p_pgrm->b_is_ok = 0;
+    }
+
+    /* Read the info for that pgrm is the one we have is not up to date or
+       if we don't have any */
+    if( p_pgrm->b_is_ok )
+    {
+      /* Nothing to do */
+//      intf_DbgMsg("Program description OK, nothing to do\n");
+    }
+    else
+    {
+        /* Check if we already heard about that section */
+        if( Is_known(p_descr->a_known_PMT_sections, i_current_section) )
+        {
+          /* Nothing to do */
+//          intf_DbgMsg("Section already received, skipping\n");
+        }
+        else
+        {
+          /* Read the corresponding PCR */
+          p_pgrm->i_pcr_pid = U16_AT(&p_pms[8]) & 0x1fff;
+          intf_DbgMsg("PCR at PID: %d\n", p_pgrm->i_pcr_pid);
+
+          /* Compute the length of the section minus the final CRC */
+          i_section_length = (U16_AT(&p_pms[1]) & 0xFFF) + 3 - 4;
+          intf_DbgMsg("Section length (CRC not included): %d\n", i_section_length);
+
+          /* Read additional info stored in the descriptors if any */
+          intf_DbgMsg("description length for program %d: %d\n", p_pgrm->i_number,
+                   (U16_AT(&p_pms[10]) & 0x0FFF));
+          i_descr_end = (U16_AT(&p_pms[10]) & 0x0FFF) + 12;
+          intf_DbgMsg("description ends at offset: %d\n",  i_descr_end);
+
+          i_offset = 12;
+          while( i_offset < i_descr_end )
+          {
+            DecodePgrmDescriptor(&p_pms[i_offset], p_pgrm);
+            i_offset += p_pms[i_offset+1] + 2;
+          }
+
+          /* Read all the ES descriptions */
+          while( i_offset < i_section_length )
+          {
+            /* Read type of that ES */
+            intf_DbgMsg("ES Type: %d\n", p_pms[i_offset]);
+
+            /* Read PID of that ES */
+            i_es_pid = U16_AT(&p_pms[i_offset+1]) & 0x1FF;
+            intf_DbgMsg("ES PID: %d\n", i_es_pid);
+
+            /* Add the ES to the program description and reserve a slot in the
+               table of ES to store its description */
+            p_es = AddESDescr(p_input, p_pgrm, i_es_pid);
+            if (!p_es)
+            {
+              intf_ErrMsg("Warning: definition of program %d will be uncomplete\n", 
+                          p_pgrm->i_number);
+              /* The best way to handle this is to stop decoding the info for that
+                 section but do as if everything is ok. Thus we will eventually have
+                 an uncomplete ES table marked as being complete and some error msgs */
+              break;
+            }
+            else
+            {
+              /* Store the description of that PID in the slot */
+              p_es->i_type = p_pms[i_offset];
+              p_es->b_psi = 0;
+              if( i_es_pid == p_pgrm->i_pcr_pid )
+                p_es->b_pcr = 1;
+              else
+                p_es->b_pcr = 0;
+              
+              /* Read additional info given by the descriptors */
+              i_offset += 5;
+              intf_DbgMsg("description length for PID %d: %d\n", p_es->i_id,
+                       (U16_AT(&p_pms[i_offset-2]) & 0x0FFF));
+              i_descr_end = (U16_AT(&p_pms[i_offset-2]) & 0x0FFF) + i_offset;
+              intf_DbgMsg("description ends at offset: %d\n",  i_descr_end);
+              while( i_offset < i_descr_end )
+              {
+                DecodeESDescriptor(&p_pms[i_offset], p_es);
+                i_offset += p_pms[i_offset+1] + 2;
+              }
+            }
+
+            /* Jump to next ES description */
+          }
+
+          /* We now know the info carried in this section */
+          intf_Msg("Description of program %d complete\n", p_pgrm->i_number);
+          p_pgrm->b_is_ok = 1;
+          Set_known(p_descr->a_known_PMT_sections, i_current_section);
+        
+          /* Check if the PMT is now complete */
+          p_descr->i_known_PMT_sections++;
+          if( p_descr->i_known_PMT_sections >= i_last_section)
+            p_descr->b_is_PMT_complete = 1;
+        }
+    }
+
+#undef p_descr
+}
+
+
+
+/******************************************************************************
+ * DecodeSrvDescrSection
+ ******************************************************************************
+ * A finir et a refaire proprement ????
+ ******************************************************************************/
+void DecodeSrvDescrSection( byte_t* p_sdt, input_thread_t *p_input )
+{
+  u16 i_stream_id;
+  u8 i_version;
+  u16 i_length;
+  
+  int i_index;
+  int i_offset;
+  boolean_t b_must_update = 0;
+  
+  int i_descr_end;
+
+  ASSERT(p_sdt);
+  ASSERT(p_input);
+
+#define p_stream (p_input->p_stream)
+  
+   /* Read stream id and version number immediately, to be sure they will be
+      initialised in all the cases in which we will need them */
+   i_stream_id = U16_AT(&p_sdt[3]);
+   i_version = (p_sdt[5] >> 1) & 0x1F;
+   intf_DbgMsg("TS Id: %d, version: %d\n", i_stream_id, i_version);
+
+   /* Take the descriptor into account only if it describes the streams we are
+      receiving */
+   if( p_stream->i_stream_id != i_stream_id )
+   {
+     intf_DbgMsg("SDT doen't apply to our TS but to %s: aborting\n",
+                  U16_AT(&p_sdt[3]));
+   }
+   else
+   {
+     /* Section applyies to our TS, test if the SDT is up to date */
+     if( p_stream->i_SDT_version != i_version )
+     {
+       intf_Msg("SDT has been updated, NOT YET IMPLEMENTED\n");
+
+       /* Ask the PSI decoder to rebuild the table */
+        b_must_update = 1;
+     }
+   }
+
+   /* Rebuild the table if needed */
+   if( b_must_update )
+   {
+     intf_DbgMsg("Updating SDT table\n");
+
+     i_length = p_sdt[1] & 0x0FFF;
+     i_offset = 11;
+
+     while(i_offset < i_length)
+     {
+       
+       /* Find the program to which the description applies */
+       for( i_index = 0; i_index < p_stream->i_pgrm_number; i_index++ )
+       {
+         if( p_stream->ap_programs[i_index]->i_number == U16_AT(&p_sdt[i_offset]) )
+         {
+           /* Here we are */
+           intf_Msg("FOUND: %d\n", p_stream->ap_programs[i_index]->i_number);
+           break;
+         }
+       }
+
+       /* Copy the info to the description of that program */
+       i_offset += 5;
+       intf_DbgMsg("description length for SDT: %d\n",
+                   (U16_AT(&p_sdt[i_offset-2]) & 0x0FFF));
+       i_descr_end = (U16_AT(&p_sdt[i_offset-2]) & 0x0FFF) + i_offset;
+       intf_DbgMsg("description ends at offset: %d\n",  i_descr_end);
+       while( i_offset < i_descr_end )
+       {
+         DecodePgrmDescriptor(&p_sdt[i_offset], p_stream->ap_programs[i_index]);
+         i_offset += p_sdt[i_offset+1] + 2;
+       }
+     }
+   }
+#undef p_stream
+};
+
+
+
+
+
+
+
+/******************************************************************************
+ * DecodePgrmDescr
+ ******************************************************************************
+ * Decode any descriptor applying to the definition of a program
+ ******************************************************************************/
+static void DecodePgrmDescriptor( byte_t* p_descriptor, pgrm_descriptor_t* p_pgrm )
+{
+    u8 i_type;                                     /* Type of the descriptor */
+    u8 i_length;                                 /* Length of the descriptor */
+#ifdef DVB_EXTENSIONS
+    int i_offset;                      /* Current position in the descriptor */
+#endif
+
+    ASSERT(p_descriptor);
+    ASSERT(p_pgrm);
+
+    /* Read type and length of the descriptor */
+    i_type = p_descriptor[0];
+    i_length = p_descriptor[1];
+
+    /* Handle specific descriptor info */
+    switch(i_type)
+    {
+#ifdef DVB_EXTENSIONS        
+    case PSI_SERVICE_DESCRIPTOR:
+    {
+        /* Store service type */
+        p_pgrm->i_srv_type = p_descriptor[2];
+
+        /* Jump to the beginning of the service name */
+        i_offset = p_descriptor[3] + 5;
+
+        /* Check that the charset used is the normal one (latin) by testing the
+           first byte of the name */
+        if( p_descriptor[i_offset] >= 0x20 )
+        {
+            /* The charset is the one of our computer: just manually dup the string */
+            p_pgrm->psz_srv_name = malloc(i_length - i_offset +1);
+            memcpy(p_pgrm->psz_srv_name, &p_descriptor[i_offset], i_length - i_offset);
+            p_pgrm->psz_srv_name[i_length - i_offset + 1] = '\0';
+        }
+        else
+        {
+            /* Indicate that the name couldn't be printed */
+            p_pgrm->psz_srv_name = "Ununderstandable :)";
+        }
+        break;
+    }
+#endif
+    default:
+//        intf_DbgMsg("Unhandled program descriptor received (type: %d)\n", i_type);
+//        intf_Msg("Unhandled ES descriptor received (type: %d)\n", i_type);
+    }
+}
+
+
+
+
+/******************************************************************************
+ * DecodeESDescriptor
+ ******************************************************************************
+ * Decode any descriptor applying to the definition of an ES
+ ******************************************************************************/
+static void DecodeESDescriptor( byte_t* p_descriptor, es_descriptor_t* p_es )
+{
+    u8 i_type;                                     /* Type of the descriptor */
+    u8 i_length;                                 /* Length of the descriptor */
+//    int i_offset;                      /* Current position in the descriptor */
+
+    ASSERT(p_descriptor);
+    ASSERT(p_es);
+
+    /* Read type and length of the descriptor */
+    i_type = p_descriptor[0];
+    i_length = p_descriptor[1];
+
+    switch( i_type )
+    {
+    case PSI_VIDEO_STREAM_DESCRIPTOR:
+    {
+        intf_DbgMsg("Video stream descriptor received\n");
+        break;
+    }
+    case PSI_AUDIO_STREAM_DESCRIPTOR:
+    {
+        intf_DbgMsg("Audio stream descriptor received\n");
+        break;
+    }
+    case PSI_TARGET_BACKGROUND_GRID_DESCRIPTOR:
+    {
+        intf_DbgMsg("Target background descriptor received\n");
+        break;
+    }   
+    case PSI_VIDEO_WINDOW_DESCRIPTOR:
+    {
+        intf_DbgMsg("Video window descriptor received\n");
+        break;
+    }
+    default:
+//        intf_DbgMsg("Unhandled ES descriptor received (type: %d)\n", i_type);
+//        intf_Msg("Unhandled ES descriptor received (type: %d)\n", i_type);
+    }
+}
+
+
+
+/******************************************************************************
+ * input_AddPsiPID: Start to receive the PSI info contained in a PID
+ ******************************************************************************
+ * Add a descriptor to the table of es descriptor for that es and mark the es
+ * as being to be received by the input (since all PSI must be received to
+ * build the description of the program)
+ ******************************************************************************/
+static int input_AddPsiPID( input_thread_t *p_input, int i_pid )
+{
+  int i_index;
+  es_descriptor_t* p_psi_es;
+  int i_rc = 0;  
+
+  /* Store the description of this stream in the input thread */
+  p_psi_es = AddESDescr(p_input, NULL, i_pid);
+  
+  if(p_psi_es)
+  {
+    /* Precise this ES carries PSI */
+    p_psi_es->b_psi = 1;
+
+    /* Create the buffer needed by the PSI decoder */
+    p_psi_es->p_psi_section = malloc( sizeof( psi_section_t) );
+    if( !p_psi_es->p_psi_section )
+    {
+      intf_ErrMsg( "Malloc error\n" );
+      i_rc = -1;
+    }
+    else
+    {
+      /* Init the reception for that PID */
+      p_psi_es->p_psi_section->b_running_section = 0;
+//      p_psi_es->p_psi_section->b_discard_payload = 0;
+
+      /* Ask the input thread to demultiplex it: since the interface
+         can also access the table of selected es, lock the elementary
+         stream structure */
+      pthread_mutex_lock( &p_input->es_lock );
+      for( i_index = 0; i_index < INPUT_MAX_SELECTED_ES; i_index++ )
+      {
+        if( !p_input->pp_selected_es[i_index] )
+        {
+          intf_DbgMsg( "Free Selected ES slot found at offset %d: Will use it for PID %d\n",
+                       i_index, i_pid );
+          p_input->pp_selected_es[i_index] = p_psi_es;
+          break;
+        }
+      }
+      pthread_mutex_unlock( &p_input->es_lock );
+
+      if( i_index >= INPUT_MAX_SELECTED_ES )
+      {
+        intf_ErrMsg( "Stream carries to many PID for our decoder\n" );
+        i_rc = -1;
+      }
+    }
+  }
+
+  return( i_rc );
+}
+
+
+/******************************************************************************
+ * input_DelPsiPID: Stop to receive the PSI info contained in a PID
+ ******************************************************************************
+ * Remove the PID from the list of ES descriptors and from the list of ES that
+ * the input must receive.
+ * Known PID for PSI should always be received, so that their description
+ * should be pointed out by a member of pp_selected_es. But as INPUT_MAX_ES
+ * can be different of INPUT_MAX_SELECTED_ES, this may happen, so that we must
+ * do 2 loops.
+ ******************************************************************************/
+static int input_DelPsiPID( input_thread_t *p_input, int i_pid )
+{
+  int i_es_index, i_last_sel;  
+
+  intf_DbgMsg( "Deleting PSI PID %d\n", i_pid );
+
+  /* Stop to receive the ES. Since the interface can also access the table
+     of selected es, lock the elementary stream structure */
+  pthread_mutex_lock( &p_input->es_lock );
+  
+  for( i_es_index = 0; i_es_index < INPUT_MAX_SELECTED_ES; i_es_index++ )
+  {
+    if( p_input->pp_selected_es[i_es_index] &&
+        p_input->pp_selected_es[i_es_index]->i_id == i_pid )
+    {
+      /* Unmark the stream */
+      p_input->pp_selected_es[i_es_index] = NULL;
+
+      /* There must not be any gap in the pp_selected_es, so move the last
+         selected stream to this slot */
+      for( i_last_sel = i_es_index; p_input->pp_selected_es[i_last_sel] &&
+            i_last_sel < INPUT_MAX_SELECTED_ES; i_last_sel++ );
+      p_input->pp_selected_es[i_es_index] = p_input->pp_selected_es[i_last_sel];
+      p_input->pp_selected_es[i_last_sel] = NULL;
+      break;
+    }
+  }
+
+  pthread_mutex_unlock( &p_input->es_lock );
+
+#ifdef DEBUG
+  /* Check if the pp_selected_es table may be corrupted */
+  if( i_es_index >= INPUT_MAX_PROGRAM_ES )
+  {
+    intf_ErrMsg( "DelPsiPID error: PID %d is not currently received\n", i_pid );
+  }
+#endif
+
+  /* Remove the desription of that ES from the table of ES */
+  DestroyESDescr(p_input, NULL, i_pid);
+
+  return( 0 );
+}
+
+
+
+/******************************************************************************
+ * Precalculate the 32-bit CRC table
+ ******************************************************************************
+ * This table is a global variable shared by all decoders, so it has to be
+ * initialised only once
+ ******************************************************************************/
+void BuildCrc32Table( )
+{
+    u32 i, j, k;
+    for( i = 0 ; i < 256 ; i++ )
+    {
+        k = 0;
+        for (j = (i << 24) | 0x800000 ; j != 0x80000000 ; j <<= 1)
+            k = (k << 1) ^ (((k ^ j) & 0x80000000) ? 0x04c11db7 : 0);
+        i_crc_32_table[i] = k;
+    }
+}
+
+
+/******************************************************************************
+ * Test the validity of a checksum
+ ******************************************************************************
+ * The checksum must be stored at the end of the data, and the given size must
+ * include the 32 bits of the CRC.
+ * Return 0 if the checksum is OK, any other value if the data are corrupted
+ ******************************************************************************/
+int CheckCRC32(byte_t* p_data, int i_data_size)
+{  
+  int i;
+  u32 i_crc = 0xffffffff;
+
+  ASSERT(p_data);
+
+  for (i = 0; i < i_data_size; i++)
+    i_crc = (i_crc << 8) ^ i_crc_32_table[(i_crc >> 24) ^ p_data[i]];
+
+  return i_crc;
+}
+
+
+
+/******************************************************************************
+ * Is_known: check if a given section has already been received
+ ******************************************************************************
+ * As a table cannot be segmented into more than 256 sections, we store a 256
+ * bits long table, each bit set to one indicating that the corresponding
+ * saction has been received
+ ******************************************************************************/
+boolean_t Is_known( byte_t* a_known_section, u8 i_section )
+{
+  byte_t mask;
+  boolean_t b_is_known;
+  
+  /* Where to get the information ? */
+  int i_bit_in_byte = i_section % 8;
+  int i_byte_in_table = (i_section - i_bit_in_byte) / 8;
+
+  /* Build mask to read the Is_known flag */
+  mask = 0x01 << i_bit_in_byte;
+  
+  /* Read the flag */
+  b_is_known = a_known_section[i_byte_in_table] & mask;
+
+  return b_is_known;
+}
+
+
+/******************************************************************************
+ * Set_known: mark a given section has having been received
+ ******************************************************************************
+ *
+ ******************************************************************************/
+static void Set_known( byte_t* a_known_section, u8 i_section )
+{
+  byte_t mask;
+  
+  /* Where to get the information ? */
+  int i_bit_in_byte = i_section % 8;
+  int i_byte_in_table = (i_section - i_bit_in_byte) / 8;
+
+  /* Build mask to read the Is_known flag */
+  mask = 0x01 << i_bit_in_byte;
+  
+  /* Set the flag */
+  a_known_section[i_byte_in_table] |= mask;
+}
+
+
+/******************************************************************************
+ * Unset_known: remove the 'received' mark for a given section
+ ******************************************************************************
+ * 
+ ******************************************************************************/
+static void Unset_known( byte_t* a_known_section, u8 i_section )
+{
+  byte_t mask;
+  
+  /* Where to get the information ? */
+  int i_bit_in_byte = i_section % 8;
+  int i_byte_in_table = (i_section - i_bit_in_byte) / 8;
+
+  /* Build mask to read the Is_known flag */
+  mask = 0x01 << i_bit_in_byte;
+  mask = ~mask;
+  
+  /* Unset the flag */
+  a_known_section[i_byte_in_table] &= mask;
+}
+
+
+/******************************************************************************
+ * AddStreamDescr: add and init the stream descriptor of the given input
+ ******************************************************************************
+ * 
+ ******************************************************************************/
+static stream_descriptor_t* AddStreamDescr(input_thread_t* p_input, u16 i_stream_id)
+{
+  ASSERT(p_input);
+  
+  intf_DbgMsg("Adding description for stream %d\n", i_stream_id);
+
+  p_input->p_stream = malloc( sizeof(stream_descriptor_t) );
+
+  p_input->p_stream->i_stream_id = i_stream_id;
+
+  p_input->p_stream->i_PAT_version = PSI_UNINITIALISED;
+  p_input->p_stream->i_known_PAT_sections = 0;
+  bzero( p_input->p_stream->a_known_PAT_sections,
+         sizeof(*p_input->p_stream->a_known_PAT_sections) );
+  p_input->p_stream->b_is_PAT_complete = 0;
+
+  p_input->p_stream->i_known_PMT_sections = 0;
+  bzero( p_input->p_stream->a_known_PMT_sections,
+         sizeof(*p_input->p_stream->a_known_PMT_sections) );
+  p_input->p_stream->b_is_PMT_complete = 0;
+
+#ifdef DVB_EXTENSIONS
+  p_input->p_stream->i_SDT_version = PSI_UNINITIALISED;
+  p_input->p_stream->i_known_SDT_sections = 0;
+  bzero( p_input->p_stream->a_known_SDT_sections,
+         sizeof(*p_input->p_stream->a_known_SDT_sections) );
+  p_input->p_stream->b_is_SDT_complete = 0;
+#endif
+
+  p_input->p_stream->i_pgrm_number = 0;
+  p_input->p_stream->ap_programs = NULL;
+
+  return p_input->p_stream;
+}
+
+
+/******************************************************************************
+ * DestroyStreamDescr: destroy the stream desciptor of the given input
+ ******************************************************************************
+ * 
+ ******************************************************************************/
+static void DestroyStreamDescr(input_thread_t* p_input, u16 i_stream_id)
+{
+  int i_index;
+
+  ASSERT(p_input);
+
+  /* Free the structures that describes the programs of that stream */
+  for( i_index = 0; i_index < p_input->p_stream->i_pgrm_number; i_index++ )
+  {
+    DestroyPgrmDescr( p_input, p_input->p_stream,
+                      p_input->p_stream->ap_programs[i_index]->i_number );
+  }
+  
+  /* Free the table of pgrm descriptors */
+  free( p_input->p_stream->ap_programs );
+
+  /* Free the structure that describes the stream itself */
+  free( p_input->p_stream );
+
+  /* Input thread has no more stream descriptor */
+  p_input->p_stream = NULL;
+}
+
+
+/******************************************************************************
+ * AddPgrmDescr: add and init a program descriptor
+ ******************************************************************************
+ * This program descriptor will be referenced in the given stream descriptor
+ ******************************************************************************/
+static pgrm_descriptor_t* AddPgrmDescr(stream_descriptor_t* p_stream, u16 i_pgrm_id)
+{
+  ASSERT(p_stream);
+
+  intf_DbgMsg("Adding description for pgrm %d\n", i_pgrm_id);
+
+  /* Add an entry to the list of program associated with the stream */
+  p_stream->i_pgrm_number++;
+  p_stream->ap_programs = realloc( p_stream->ap_programs, 
+                                   p_stream->i_pgrm_number*sizeof(pgrm_descriptor_t*) );
+
+  /* Allocate the structure to store this description */
+  p_stream->ap_programs[p_stream->i_pgrm_number-1] = malloc(sizeof(pgrm_descriptor_t));
+
+  /* Init this entry */
+  p_stream->ap_programs[p_stream->i_pgrm_number-1]->i_number = i_pgrm_id;
+  p_stream->ap_programs[p_stream->i_pgrm_number-1]->i_version = PSI_UNINITIALISED;
+  p_stream->ap_programs[p_stream->i_pgrm_number-1]->b_is_ok = 0;
+
+  p_stream->ap_programs[p_stream->i_pgrm_number-1]->i_es_number = 0;
+  p_stream->ap_programs[p_stream->i_pgrm_number-1]->ap_es = NULL;
+
+  /* descriptors ???? */
+
+  return p_stream->ap_programs[p_stream->i_pgrm_number-1];
+}
+
+
+/******************************************************************************
+ * AddPgrmDescr: destroy a program descriptor
+ ******************************************************************************
+ * All ES descriptions referenced in the descriptor will be deleted.
+ ******************************************************************************/
+static void DestroyPgrmDescr(input_thread_t* p_input, stream_descriptor_t* p_stream, u16 i_pgrm_id)
+{
+  int i_index;
+  pgrm_descriptor_t* p_pgrm;
+  
+  ASSERT(p_stream);
+
+  /* Find where is described this program */
+  for(i_index = 0; i_index < p_stream->i_pgrm_number; i_index++)
+  {
+    if( p_stream->ap_programs[i_index]->i_number == i_pgrm_id )
+    {
+      /* Here we are */
+      p_pgrm = p_stream->ap_programs[i_index];
+      break;
+    }
+  }
+
+  /* Free the structures that describes the es that belongs to that program */
+  for( i_index = 0; i_index < p_pgrm->i_es_number; i_index++ )
+    DestroyESDescr(p_input, p_pgrm, p_pgrm->ap_es[i_index]->i_id);
+
+  /* Free the table of es descriptors */
+  free( p_pgrm->ap_es );
+
+  /* Free the description of this stream */
+  free( p_pgrm );
+
+  /* Remove this program from he list of programs of the stream */
+  p_stream->i_pgrm_number--;
+  p_stream->ap_programs[i_index] = p_stream->ap_programs[p_stream->i_pgrm_number];
+  p_stream->ap_programs = realloc(p_stream->ap_programs, p_stream->i_pgrm_number);
+}
+
+
+/******************************************************************************
+ * AddESDescr:
+ ******************************************************************************
+ * Reserve a slot in the table of ES descritors for the ES and add it to the
+ * list of ES of p_pgrm. If p_pgrm if NULL, then the ES is considered as stand
+ * alone (PSI ?)
+ ******************************************************************************/
+static es_descriptor_t* AddESDescr(input_thread_t* p_input,
+                                   pgrm_descriptor_t* p_pgrm, u16 i_es_pid)
+{
+  int i_index;
+  es_descriptor_t* p_es = NULL;
+  
+  ASSERT(p_input);
+  
+  intf_DbgMsg("Adding description for ES %d\n", i_es_pid);
+
+  /* Find an empty slot to store the description of that es */
+  for( i_index = 0; i_index < INPUT_MAX_ES &&
+        p_input->p_es[i_index].i_id != EMPTY_PID; i_index++ );
+
+  if( i_index >= INPUT_MAX_ES )
+  {
+    /* No slot is empty */
+    intf_ErrMsg("Stream carries to many PID for our decoder\n");
+  }
+  else
+  {
+    /* Reserve the slot for that ES */
+    p_es = &p_input->p_es[i_index];
+    p_es->i_id = i_es_pid;
+    intf_DbgMsg("Slot %d in p_es table reserved for ES %d\n", i_index, i_es_pid);
+    
+    /* Init its values */
+    p_es->i_type = 0;  /* ??? */
+    p_es->b_psi = 0;
+    p_es->b_pcr = 0;
+    
+    p_es->p_pes_packet = NULL;
+//    p_es->p_next_pes_packet = NULL;
+    p_es->p_dec = NULL;
+
+    /* Add this ES to the program definition if one is given */
+    if( p_pgrm )
+    {
+      p_pgrm->i_es_number++;
+      p_pgrm->ap_es = realloc( p_pgrm->ap_es, p_pgrm->i_es_number*sizeof(es_descriptor_t *) );
+      p_pgrm->ap_es[p_pgrm->i_es_number-1] = p_es;
+      intf_DbgMsg("Added ES %d to definition of pgrm %d\n", i_es_pid, p_pgrm->i_number);
+    }
+    else
+      intf_DbgMsg("Added ES %d not added to the definition of any pgrm\n", i_es_pid);
+  }
+  
+  return p_es;
+}
+
+
+
+/******************************************************************************
+ * DestroyESDescr:
+ ******************************************************************************
+ * 
+ ******************************************************************************/
+static void DestroyESDescr(input_thread_t* p_input, pgrm_descriptor_t* p_pgrm, u16 i_pid)
+{
+  int i_index;
+  
+  ASSERT(p_pgrm);
+
+  /* Look for the description of the ES */
+  for(i_index = 0; i_index < INPUT_MAX_ES; i_index++)
+  {
+    if( p_input->p_es[i_index].i_id == i_pid )
+    {
+      /* The table of stream descriptors is static, so don't free memory
+         but just mark the slot as unused */
+      p_input->p_es[i_index].i_id = EMPTY_PID;
+      break;
+    }
+  }
+
+  /* Remove this ES from the description of the program if it is associated to
+   one */
+  if( p_pgrm )
+  {
+    for( i_index = 0; i_index < INPUT_MAX_ES; i_index++ )
+    {
+      if( p_input->p_es[i_index].i_id == i_pid )
+      {
+        p_pgrm->i_es_number--;
+        p_pgrm->ap_es[i_index] = p_pgrm->ap_es[p_pgrm->i_es_number];
+        p_pgrm->ap_es = realloc(p_pgrm->ap_es, p_pgrm->i_es_number);
+        break;
+      }
+    }
+  }
+}
diff --git a/src/input/input_vlan.c b/src/input/input_vlan.c
new file mode 100644 (file)
index 0000000..d5d911b
--- /dev/null
@@ -0,0 +1,761 @@
+/*******************************************************************************
+ * input_vlan.c: vlan input method
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * ?? 
+ *******************************************************************************/
+
+/* ???????????????????????????????????????????????????????????????????????????
+ * This works (well, it should :), but should have a good place in horror museum:
+ * - the vlan-capable interfaces are retrieved from a names list, instead
+ *   of being read from the system
+ * - the vlan server sucks, and therefore the vlan clients sucks:
+ *      - it is unable to process several operations between a login and a logout
+ *        A lot of requests could be grouped if it could.
+ *      - it is incoherent concerning it's messages (and what it needs to perform
+ *        an operation
+ *      - it is totally unable to handle several mac adresses on a single switch
+ *        port (and therefore bridged/hubbed machines)
+ * - however, the API is ok, should be able to handle all futures evolutions, 
+ *   including vlan-conscient cards.
+ *
+ * So there is a lot to do in this file, but not before having reprogrammed the
+ * vlan server !
+ * What would be a good interface to the vlan server ? Here are some ideas:
+ *      ( later ! )
+ * ??????????????????????????????????????????????????????????????????????????? */
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "netutils.h"
+
+#include "input.h"
+#include "input_vlan.h"
+
+#include "audio_output.h"
+
+#include "video.h"
+#include "video_output.h"
+
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+
+#include "pgm_data.h"
+
+/*
+ * Constants
+ */
+
+/* List of vlan-capable interfaces names */
+static const char *psz_ifaces_names[] = { "eth0", "eth1", "eth2", "eth3", "eth4", "eth5", NULL };
+   
+/*
+ * Local prototypes
+ */
+static int  IfaceInfo               ( input_vlan_iface_t *p_iface );
+static int  IfaceDependance         ( input_vlan_method_t *p_method, int i_iface );
+static int  ServerLogin             ( input_vlan_server_t *p_server );
+static void ServerLogout            ( input_vlan_server_t *p_server );
+static int  ServerRequestChange     ( input_vlan_server_t *p_server,
+                                      input_vlan_iface_t *p_iface, int i_vlan );
+static int  ServerRequestInfo       ( input_vlan_server_t *p_server,
+                                      input_vlan_iface_t *p_iface );
+
+/*******************************************************************************
+ * input_VlanMethodInit: initialize global vlan method data
+ *******************************************************************************
+ * Initialize vlan input method global data. This function should be called
+ * once before any input thread is created or any call to other input_Vlan*()
+ * function is attempted.
+ *******************************************************************************/
+int input_VlanMethodInit( input_vlan_method_t *p_method, char *psz_server, int i_port )
+{
+    int                     i_index;                /* interface/servers index */
+    input_vlan_iface_t *    p_iface;                             /* interfaces */
+
+    /* Build vlan server descriptor */
+    if( BuildInetAddr( &p_method->server.sa_in, psz_server, i_port ) )
+    {
+        return( -1 );        
+    }    
+
+    /* Allocate interfaces array */
+    for( i_index = 0; psz_ifaces_names[i_index] != NULL; i_index++ )
+    {
+        ;        
+    }    
+    p_iface = malloc( sizeof(input_vlan_iface_t) * i_index );
+    if( p_iface == NULL )
+    {        
+        return( ENOMEM );
+    }   
+
+    /* Initialize interfaces array */
+    for( i_index = p_method->i_ifaces = 0; psz_ifaces_names[i_index] != NULL; i_index++ )
+    {
+        /* Retrieve interface name */
+        p_iface[p_method->i_ifaces].psz_name = (char *) psz_ifaces_names[i_index];
+
+        /* Test if interface is vlan-capable */
+        if( !IfaceInfo( &p_iface[p_method->i_ifaces] ) )
+        {
+            /* If interface passed first step, login to vlan server */
+            if( !ServerLogin( &p_method->server ) )
+            {
+                /* Request informations from server about the interface - if the interface
+                 * pass this last test, it is vlan-capable and can be added to the list of
+                 * interfaces. */
+                if( !ServerRequestInfo( &p_method->server, &p_iface[p_method->i_ifaces]) )
+                {
+                    /* Check if interface is dependant */
+                    if( !IfaceDependance( p_method, p_method->i_ifaces ) )
+                    {                        
+                        /* Interface is master: initialize properties */
+                        p_iface[p_method->i_ifaces].i_default_vlan = p_iface[p_method->i_ifaces].i_vlan;
+                        p_iface[p_method->i_ifaces].i_refcount = 0;
+                        intf_DbgMsg("input debug: added vlan-capable interface %s (%s)\n", 
+                                    p_iface[p_method->i_ifaces].psz_name, 
+                                    p_iface[p_method->i_ifaces].psz_mac);
+                    }          
+#ifdef DEBUG
+                    else
+                    {
+                        /* Interface is slave */
+                        intf_DbgMsg("input debug: added vlan-capable interface %s (%s), depends from %s\n",
+                                    p_iface[p_method->i_ifaces].psz_name, 
+                                    p_iface[p_method->i_ifaces].psz_mac,
+                                    p_iface[p_iface[p_method->i_ifaces].i_master].psz_name );
+                    }
+#endif
+                    /* Increment size counter */            
+                    p_method->i_ifaces++;
+                }
+                /* Logout from server */
+                ServerLogout( &p_method->server );    
+            }
+        }
+    }
+
+    /* If number of vlan-capable interfaces is null, then desactivate vlans */
+    if( p_method->i_ifaces == 0 )
+    {
+        free( p_iface );        
+        return( -1 );        
+    }
+    
+    /* Reallocate interfaces array to save memory */
+    p_method->p_iface = realloc( p_iface, sizeof(input_vlan_iface_t) * p_method->i_ifaces );
+    if( p_method->p_iface == NULL )
+    {        
+        /* Realloc failed, but the previous pointer is still valid */
+        p_method->p_iface = p_iface;
+    }      
+
+    /* Initialize lock */
+    pthread_mutex_init( &p_method->lock, NULL );
+
+    intf_Msg("input: vlans input method installed\n", p_method->i_ifaces);
+    return( 0 );    
+}
+
+/*******************************************************************************
+ * input_VlanMethodFree: free global vlan method data
+ *******************************************************************************
+ * Free resources allocated by input_VlanMethodInit. This function should be
+ * called at the end of the program.
+ *******************************************************************************/
+void input_VlanMethodFree( input_vlan_method_t *p_method )
+{
+    int i_index;                                     /* server/interface index */
+
+    /* Leave all remaining vlans */
+    for( i_index = 0; i_index < p_method->i_ifaces; i_index++ )
+    {
+#ifdef DEBUG
+        /* Check if interface is still locked */
+        if( p_method->p_iface[i_index].i_refcount )
+        {
+            intf_DbgMsg("input debug: interface %s is still vlan-locked\n", 
+                        p_method->p_iface[i_index].psz_name);
+            p_method->p_iface[i_index].i_refcount = 0;
+        }        
+#endif
+        /* Join default (starting) vlan */
+        input_VlanJoin( VLAN_ID( i_index, p_method->p_iface[i_index].i_default_vlan ) );        
+    }    
+
+    /* Free interfaces array */
+    free( p_method->p_iface );    
+
+    intf_DbgMsg("input debug: vlan method terminated\n");
+}
+
+/*******************************************************************************
+ * input_VlanId: get a vlan_id for a given interface
+ *******************************************************************************
+ * Get a vlan_id given a network interface and a vlan number. If psz_iface is
+ * NULL, then the default network interface will be used. A negative value
+ * will be returned in case of error.
+ *******************************************************************************/
+int input_VlanId( char *psz_iface, int i_vlan )
+{
+    input_vlan_method_t *   p_method;                    /* method global data */
+    int                     i_index;                        /* interface index */
+
+    p_method = &p_program_data->input_vlan_method;
+
+    /* If psz_iface is NULL, use first (default) interface (if there is one) */
+    if( psz_iface == NULL )
+    {           
+        return( p_method->i_ifaces ? VLAN_ID( 0, i_vlan ) : -1 );    
+    }
+        
+    /* Browse all interfaces */
+    for( i_index = 0; i_index < p_program_data->input_vlan_method.i_ifaces ; i_index++ )
+    {
+        /* If interface has been found, return */
+        if( !strcmp( p_program_data->input_vlan_method.p_iface[i_index].psz_name, psz_iface ) )
+        {
+            return( VLAN_ID( i_index, i_vlan ) );
+        }        
+    }    
+    
+    return( -1 );
+}
+
+/*******************************************************************************
+ * input_VlanJoin: join a vlan
+ *******************************************************************************
+ * This function will try to join a vlan. If the relevant interface is already
+ * on the good vlan, nothing will be done. Else, and if possible (if the 
+ * interface is not locked), the vlan server will be contacted and a change will
+ * be requested. The function will block until the change is effective. Note
+ * that once a vlan is no more used, it's interface should be unlocked using
+ * input_VlanLeave().
+ * Non 0 will be returned in case of error.
+ *******************************************************************************/
+int input_VlanJoin( int i_vlan_id )
+{    
+    input_vlan_method_t *   p_method;                    /* method global data */
+    input_vlan_iface_t *    p_iface;                   /* interface (shortcut) */
+    int                     i_err;                          /* error indicator */
+
+    /* Initialize shortcuts, and use master if interface is dependant */
+    i_err = 0;    
+    p_method = &p_program_data->input_vlan_method;
+    p_iface = &p_method->p_iface[ VLAN_ID_IFACE( i_vlan_id ) ];
+    if( p_iface->i_master >= 0 )
+    {
+        p_iface = &p_method->p_iface[ p_iface->i_master ];     
+    }
+    
+    /* Get lock */
+    pthread_mutex_lock( &p_method->lock );
+    
+    /* If the interface is in the wished vlan, increase lock counter */
+    if( p_iface->i_vlan != VLAN_ID_VLAN( i_vlan_id ) )
+    {
+        p_iface->i_refcount++;         
+    }
+    /* If not, if it is not locked, the vlan can be changed */
+    else if( !p_iface->i_refcount )
+    {        
+        /* Login to server */
+        if( (i_err = !ServerLogin( &p_method->server )) )
+        {
+
+            /* Request vlan change */
+            if( (i_err = !ServerRequestChange( &p_method->server, 
+                                               p_iface, VLAN_ID_VLAN( i_vlan_id ) ) ) )
+            {
+                p_iface->i_refcount++;   
+            }            
+            /* Logout */
+            ServerLogout( &p_method->server );
+        }
+    }
+    /* Else, the vlan is locked and can't be changed */
+    else
+    {
+        i_err = 1;
+    }                    
+
+    /* Release lock (if this point is reached, the function succeeded) */
+    pthread_mutex_unlock( &p_method->lock );       
+    return( i_err );    
+}
+
+/*******************************************************************************
+ * input_VlanLeave: leave a vlan
+ *******************************************************************************
+ * This function tells the vlan library that the designed interface is no more
+ * locked and than vlan changes can occur.
+ *******************************************************************************/
+void input_VlanLeave( int i_vlan_id )
+{
+    input_vlan_method_t *   p_method;                    /* method global data */
+    input_vlan_iface_t *    p_iface;                   /* interface (shortcut) */
+    int                     i_err;                          /* error indicator */
+
+    /* Initialize shortcuts, and use master if interface is dependant */
+    i_err = 0;    
+    p_method = &p_program_data->input_vlan_method;
+    p_iface = &p_method->p_iface[ VLAN_ID_IFACE( i_vlan_id ) ];
+    if( p_iface->i_master >= 0 )
+    {
+        p_iface = &p_method->p_iface[ p_iface->i_master ];     
+    }
+    
+    /* Get lock */
+    pthread_mutex_lock( &p_method->lock );
+
+    /* Decrease reference counter */
+    p_method->p_iface[ VLAN_ID_IFACE( i_vlan_id ) ].i_refcount--;    
+
+    /* Release lock */
+    pthread_mutex_unlock( &p_method->lock );   
+}
+
+/*******************************************************************************
+ * input_VlanRequest: request vlan number for a given interface
+ *******************************************************************************
+ * Request the vlan number (and not id) of a given network interface. A 
+ * connection to the server can eventually occur, event if it not the case in
+ * current implementation. A negative number can be returned on error.
+ *******************************************************************************/
+int input_VlanRequest( char *psz_iface )
+{
+    input_vlan_method_t *   p_method;                    /* method global data */
+    int                     i_index;                        /* interface index */
+    
+    p_method = &p_program_data->input_vlan_method;
+
+    /* If psz_iface is NULL, use first (default) interface (if there is one) - 
+     * note that interface 0 can't be dependant, so dependance does not need
+     * to be tested */
+    if( psz_iface == NULL )
+    {           
+        return( p_method->i_ifaces ? p_method->p_iface[0].i_vlan : -1 );    
+    }
+        
+    /* Browse all interfaces */
+    for( i_index = 0; i_index < p_method->i_ifaces ; i_index++ )
+    {
+        /* If interface has been found, return vlan */
+        if( !strcmp( p_method->p_iface[i_index].psz_name, psz_iface ) )
+        {
+            /* If interface is dependant, use master, else return own vlan */
+            return( (p_method->p_iface[i_index].i_master >= 0) ?
+                    p_method->p_iface[p_method->p_iface[i_index].i_master].i_vlan :
+                    p_method->p_iface[i_index].i_vlan );
+        }        
+    }    
+
+    /* If not found, return an error */
+    return( -1 );    
+}
+
+/*******************************************************************************
+ * input_VlanSynchronize: resynchronize with vlan server
+ *******************************************************************************
+ * Resynchronize with the vlan server. Vlans for all interfaces are requested
+ * and changed if required. This call may take a lot of time, and is intended
+ * for emergency situations.
+ *******************************************************************************/
+int input_VlanSynchronize( void )
+{
+    input_vlan_method_t *   p_method;                    /* method global data */
+    input_vlan_iface_t *    p_iface;                   /* interface (shortcut) */
+    int                     i_index;                        /* interface index */
+    int                     i_vlan;              /* vlan for current interface */
+    
+    /* Get lock */
+    p_method = &p_program_data->input_vlan_method;
+    pthread_mutex_lock( &p_method->lock );
+
+    for( i_index = 0; i_index < p_method->i_ifaces; i_index++ )
+    {        
+        p_iface = &p_method->p_iface[ i_index ];
+        
+        /* Ignore dependant interfaces and interfaces for wich login failed */
+        if( (p_iface->i_master < 0) && !ServerLogin( &p_method->server ) )
+        {            
+            /* Request interface informations */
+            i_vlan = p_iface->i_vlan;
+            if( !ServerRequestInfo( &p_method->server, p_iface ) )
+            {
+                /* If synchronization has been lost, then try to resynchronize -
+                 * this part is ugly because of the vlan server bug requiring a 
+                 * logout between two requests */
+                if( p_iface->i_vlan != i_vlan )
+                {
+                    intf_Msg("input: lost vlan synchronization for interface %s\n", 
+                             p_iface->psz_name );                    
+                    ServerLogout( &p_method->server );
+                    if( !ServerLogin( &p_method->server ) )
+                    {
+                        if( !ServerRequestChange( &p_method->server, p_iface, i_vlan ) )
+                        {
+                            intf_Msg("input: retrieved vlan synchronization for interface %s\n", 
+                                     p_iface->psz_name );          
+                        }                        
+                    }
+                    /* Note that when this login fails, then the next logout will
+                     * also fail... but I don't want to spend time finding a 
+                     * clean way to avoid this if the vlan server bug is fixed */
+                }                                
+            }            
+            /* Logout */
+            ServerLogout( &p_method->server );            
+        }        
+    }    
+
+    /* Release lock */
+    pthread_mutex_unlock( &p_method->lock );   
+    return( 0 );    
+}
+
+/* following functions are local */
+
+/*******************************************************************************
+ * IfaceInfo: read info about an interface
+ *******************************************************************************
+ * This function reads informations about a network interface. It should return
+ * 0 and updated interface informations for vlan capable interfaces, and non 0
+ * if interface is not vlan-capable or informations request failed.
+ *******************************************************************************/
+static int IfaceInfo( input_vlan_iface_t *p_iface )
+{
+    int             i_socket;
+    struct ifreq    device;
+
+    /* Copy interface name */
+    strcpy(device.ifr_name, p_iface->psz_name);
+
+    /* Open a datagram socket */
+    i_socket = socket(AF_INET, SOCK_DGRAM, 0);
+    if(i_socket < 0)
+    {
+        intf_ErrMsg("input error: unable to open socket on %s: %s\n", 
+                    p_iface->psz_name, strerror(errno));
+        return( -1 );        
+    }
+
+    /* Read IP address */
+    if(ioctl(i_socket, SIOCGIFDSTADDR, &device) < 0)
+    {
+        intf_ErrMsg("input error: can not read IP address for %s: %s\n", 
+                    p_iface->psz_name, strerror(errno));
+        return( -1 );
+    }
+    memcpy( &p_iface->sa_in, &device.ifr_hwaddr, sizeof(struct sockaddr_in));
+
+    /* Read MAC address */
+    if(ioctl(i_socket, SIOCGIFHWADDR, &device) < 0)
+    {
+        intf_ErrMsg("input error: can not read MAC address for %s: %s\n",
+                    p_iface->psz_name, strerror(errno));
+        return( -1 );
+    }
+
+    /* Translate MAC address to ASCII standard */
+    sprintf(p_iface->psz_mac, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+            device.ifr_hwaddr.sa_data[0]&0xff,
+            device.ifr_hwaddr.sa_data[1]&0xff,
+            device.ifr_hwaddr.sa_data[2]&0xff,
+            device.ifr_hwaddr.sa_data[3]&0xff,
+            device.ifr_hwaddr.sa_data[4]&0xff,
+            device.ifr_hwaddr.sa_data[5]&0xff);
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * IfaceDependance: check interface dependance
+ *******************************************************************************
+ * Check if an interface designed by it's index is dependant from another one.
+ * All the interfaces from 0 to i_index excluded are tested. If a 'master'  
+ * interface is found, then the 'i_master' field is set to a positive value.
+ * Non 0 is returned if the interface is dependant.
+ *******************************************************************************/
+static int IfaceDependance( input_vlan_method_t *p_method, int i_iface )
+{
+    int i_index;                                            /* interface index */
+    
+    for( i_index = 0; i_index < i_iface; i_index++ )
+    {
+        /* Two interface are dependant if they are on the same switch and
+         * port */
+        if( ( p_method->p_iface[i_index].i_switch == p_method->p_iface[i_iface].i_switch )
+            && ( p_method->p_iface[i_index].i_port == p_method->p_iface[i_iface].i_port ) )
+        {
+            /* Interface is slave */
+            p_method->p_iface[i_iface].i_master = i_index;
+            return( 1 );            
+        }        
+    }
+
+    /* Interface is master */
+    p_method->p_iface[i_iface].i_master = -1;    
+    return( 0 );    
+}
+
+/*******************************************************************************
+ * ServerLogin: login to a vlan server
+ *******************************************************************************
+ * Initiate login sequence to a vlan server: open a socket, bind it and send
+ * login sequence. If the login fails for any reason, non 0 is returned.
+ *******************************************************************************/
+static int ServerLogin( input_vlan_server_t *p_server )
+{
+    struct sockaddr_in  sa_client;                           /* client address */
+    char                psz_msg[VLAN_SERVER_MSG_LENGTH  + 1];/* server message */
+    int                 i_bytes;                       /* number of bytes read */    
+
+    psz_msg[VLAN_SERVER_MSG_LENGTH] = '\0';       /* make sure the string ends */
+
+    /* Initialize local socket */
+    BuildInetAddr( &sa_client, NULL, 0 );
+    p_server->i_socket = socket(AF_INET, SOCK_STREAM, 0);
+    if( p_server->i_socket < 0 )
+    {
+        /* Error: return an error */
+        intf_ErrMsg("input error: can not open socket (%s)\n", strerror(errno));
+        return( errno );
+    }
+           
+    /* Bind the server socket to client */
+    if( bind( p_server->i_socket, (struct sockaddr *) &sa_client, sizeof(sa_client)) < 0)
+    {
+        /* Error: close socket and return an error */
+        intf_ErrMsg("input error: can not bind socket (%s)\n", strerror(errno));        
+        close( p_server->i_socket );
+        return( errno );
+    }
+
+    /* Try to connect to the VLANserver */
+    if( connect( p_server->i_socket, (struct sockaddr *) &p_server->sa_in, 
+                 sizeof(p_server->sa_in)) < 0)
+    {
+        /* Error: close socket and return an error */
+        intf_ErrMsg("input error: unable to connect to the VLAN server (%s)\n", 
+                    strerror(errno));
+        close( p_server->i_socket );
+        return( errno );        
+    }
+
+    /* Send login message */
+    snprintf(psz_msg, VLAN_SERVER_MSG_LENGTH, "%d %s %s %s\n", 
+             VLAN_LOGIN_REQUEST, VLAN_CLIENT_VERSION, 
+             p_server->psz_login, p_server->psz_passwd );
+    if( send(p_server->i_socket, psz_msg, sizeof(char) * strlen( psz_msg ), 0) < 0)
+    {
+        intf_ErrMsg("input error: unable to login to the VLANserver: %s", 
+                    strerror(errno));
+        close( p_server->i_socket );
+        return( errno );        
+    }
+
+    /* Listen to response */
+    i_bytes = recv(p_server->i_socket, psz_msg, VLAN_SERVER_MSG_LENGTH, 0);
+    if( i_bytes < 0 )
+    {        
+        intf_ErrMsg("input error: no response from VLANserver: %s\n",
+                    strerror(errno));
+        ServerLogout( p_server );
+        return( -1 );
+    }
+
+    /* Parse answer to login request */
+    psz_msg[ i_bytes ] = '\0';                         /* terminate string */    
+    if( atoi(psz_msg) == VLAN_LOGIN_REJECTED )
+    {
+        intf_ErrMsg("input error: login rejected by VLANserver: %s\n", psz_msg);
+        ServerLogout( p_server );
+        return( -1 );        
+    }
+    else if( atoi(psz_msg) != VLAN_LOGIN_ANSWER )
+    {
+        intf_ErrMsg("input error: unexpected answer from VLAN server: %s\n", psz_msg);
+        ServerLogout( p_server );
+        return( -1 );        
+    }
+    
+    intf_DbgMsg("input debug: VLANserver login ok.\n");    
+    return 0;
+}
+
+/*******************************************************************************
+ * ServerLogout: logout from a vlan server
+ *******************************************************************************
+ * Logout from a vlan server. This function sends the logout message to the
+ * server and close the socket.
+ *******************************************************************************/
+static void ServerLogout( input_vlan_server_t *p_server )
+{
+    char    psz_msg[VLAN_SERVER_MSG_LENGTH  + 1];            /* server message */
+
+    psz_msg[VLAN_SERVER_MSG_LENGTH] = '\0';       /* make sure the string ends */
+
+    /* Send logout */
+    snprintf(psz_msg, VLAN_SERVER_MSG_LENGTH, "%d\n", VLAN_LOGOUT);
+    if( send(p_server->i_socket, psz_msg, sizeof(char) * strlen(psz_msg), 0) < 0)
+    {
+        intf_ErrMsg("input error: can't send logout message to VLANserver: %s\n", 
+                    strerror(errno));
+    }
+  
+    /* Close socket */
+    if( close(p_server->i_socket) < 0)
+    {
+        intf_ErrMsg("input error: unable to close socket: %s\n", strerror(errno));
+    }
+
+    intf_DbgMsg("input debug: VLANserver logout ok\n");    
+}
+
+/*******************************************************************************
+ * ServerRequestChange: request vlan change from a server
+ *******************************************************************************
+ * Request vlan change from a vlan server. The client must be logged in. If the
+ * change succeeded, the interface structure is updated. Note that only masters
+ * should be sent to this function.
+ *******************************************************************************/
+static int ServerRequestChange( input_vlan_server_t *p_server, 
+                                input_vlan_iface_t *p_iface, int i_vlan )
+{
+    char    psz_msg[VLAN_SERVER_MSG_LENGTH  + 1];            /* server message */
+    int     i_bytes;                                   /* number of bytes read */
+          
+    psz_msg[VLAN_SERVER_MSG_LENGTH] = '\0';       /* make sure the string ends */
+
+    /* Send request */
+    snprintf(psz_msg, VLAN_SERVER_MSG_LENGTH, "%d %s %s %d %d", 
+             VLAN_CHANGE_REQUEST, p_iface->psz_mac, 
+             inet_ntoa(p_iface->sa_in.sin_addr), i_vlan, p_iface->i_vlan);
+    if( send( p_server->i_socket, psz_msg, sizeof(char) * strlen(psz_msg), 0) < 0)
+    {
+        intf_ErrMsg("input error: unable to send request to VLANserver: %s\n", 
+                    strerror(errno));        
+        return( -1 );
+    }
+
+    /* Listen to response */
+    i_bytes = recv(p_server->i_socket, psz_msg, VLAN_SERVER_MSG_LENGTH, 0);
+    if( i_bytes < 0 )
+    {        
+        intf_ErrMsg("input error: no response from VLANserver: %s",
+                    strerror(errno));
+        return( -1 );
+    }
+
+    /* Parse answer to vlan request */
+    psz_msg[ i_bytes ] = '\0';                         /* terminate string */    
+    if( atoi( psz_msg ) == VLAN_CHANGE_REJECTED )
+    {
+        intf_ErrMsg("input error: change request rejected by VLANserver: %s\n", psz_msg );
+        return( -1 );      
+    }
+    else if( atoi( psz_msg ) != VLAN_CHANGE_ANSWER )
+    {
+        intf_ErrMsg("input error: unexpected answer from VLAN server: %s\n", psz_msg);
+        return( -1 );                
+    }
+
+    /* ?? send packet for the switch to learn mac again */
+
+    /* Update interface and return */
+    intf_DbgMsg("input debug: interface %s moved to vlan %d\n", 
+                p_iface->psz_name, i_vlan );   
+    p_iface->i_vlan = i_vlan;    
+    return( 0 ); 
+}
+
+/*******************************************************************************
+ * ServerRequestInfo: ask current vlan to server
+ *******************************************************************************
+ * Request current vlan from a vlan server. The client must be logged in. This
+ * function updates the p_iface structure or returns non 0. Note that only
+ * masters should be sent to this function.
+ *******************************************************************************/
+static int ServerRequestInfo( input_vlan_server_t *p_server, 
+                              input_vlan_iface_t *p_iface )
+{
+    char    psz_msg[VLAN_SERVER_MSG_LENGTH  + 1];            /* server message */
+    int     i_bytes;                                   /* number of bytes read */
+    int     i_switch;                                         /* switch number */
+    int     i_port;                                             /* port number */
+    int     i_vlan;                                             /* vlan number */
+    int     i_sharers;                 /* number of mac addresses on this port */    
+          
+    psz_msg[VLAN_SERVER_MSG_LENGTH] = '\0';       /* make sure the string ends */
+
+    /* Send request */
+    snprintf(psz_msg, VLAN_SERVER_MSG_LENGTH, "%d", VLAN_INFO_REQUEST);
+    if( send( p_server->i_socket, psz_msg, sizeof(char) * strlen(psz_msg), 0) < 0)
+    {
+        intf_ErrMsg("input error: unable to send request to VLANserver: %s\n", 
+                    strerror(errno));        
+        return( -1 );
+    }
+
+    /* Listen to response */
+    i_bytes = recv(p_server->i_socket, psz_msg, VLAN_SERVER_MSG_LENGTH, 0);
+    if( i_bytes < 0 )
+    {        
+        intf_ErrMsg("input error: no response from VLANserver: %s",
+                    strerror(errno));
+        return( -1 );
+    }
+
+    /* Parse answer to vlan request */
+    psz_msg[ i_bytes ] = '\0';                         /* terminate string */    
+    if( atoi( psz_msg ) == VLAN_INFO_REJECTED )
+    {
+        intf_ErrMsg("input error: info request rejected by VLANserver: %s\n", psz_msg );
+        return( -1 );      
+    }
+    else if( atoi( psz_msg ) != VLAN_INFO_ANSWER )
+    {
+        intf_ErrMsg("input error: unexpected answer from VLAN server: %s\n", psz_msg);
+        return( -1 );                
+    }
+    else if( sscanf(psz_msg, "%*d %d %d %d %d", &i_switch, &i_port, &i_vlan, &i_sharers) != 4 )
+    {
+        intf_ErrMsg("input error: invalid answer from VLAN server: %s\n", psz_msg);
+        return( -1 );                        
+    }
+
+    /* Update interface and return */
+    intf_DbgMsg("input debug: interface %s is on switch %d, port %d, vlan %d, %d sharers\n", 
+                p_iface->psz_name, i_switch, i_port, i_vlan, i_sharers);    
+    p_iface->i_switch = i_switch;    
+    p_iface->i_port = i_port;    
+    p_iface->i_vlan = i_vlan;    
+    p_iface->i_sharers = i_sharers;    
+    return( 0 );    
+}
+
+
+
+
diff --git a/src/interface/control.c b/src/interface/control.c
new file mode 100644 (file)
index 0000000..de19a23
--- /dev/null
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * control.c: user control functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Library of functions common to all threads, allowing access to various
+ * structures and settings. Interfaces should only use those functions
+ * to read or write informations from other threads.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <pthread.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h> 
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+
+#include "input.h"
+#include "input_vlan.h"
+
+#include "audio_output.h"
+
+#include "video.h"
+#include "video_output.h"
+
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+#include "control.h"
+
+#include "pgm_data.h"
+
+/*******************************************************************************
+ * intf_CreateVoutThread: create video output thread in interface
+ *******************************************************************************
+ * This function creates - if possible - a new video output thread in the
+ * interface registery, using interface default settings. It returns the
+ * thread number for the interface, or a negative number.
+ * If video is desactivated, nothing will be done. If psz_title is not NULL, it
+ * will be used as window's title, and width and height will also be used if
+ * they are positive.
+ *******************************************************************************/
+int intf_CreateVoutThread( intf_thread_t *p_intf, char *psz_title, int i_width, int i_height )
+{
+    int             i_thread;                                  /* thread index */
+    video_cfg_t     cfg;                               /* thread configuration */    
+
+    /* Verify that video is enabled */
+    if( !p_program_data->cfg.b_video )
+    {
+        return( -1 );
+    }
+
+    /* Set configuration */
+    memcpy( &cfg, &p_program_data->vout_cfg, sizeof( cfg ) );
+    if( psz_title != NULL )
+    {
+        cfg.i_properties |= VIDEO_CFG_TITLE;
+        cfg.psz_title = psz_title;        
+    }
+    if( i_width > 0 )
+    {
+        cfg.i_properties |= VIDEO_CFG_WIDTH;
+        cfg.i_width = i_width;        
+    }
+    if( i_height > 0 )
+    {
+        cfg.i_properties |= VIDEO_CFG_HEIGHT;
+        cfg.i_height = i_height;
+    }
+
+    /* Find an empty place */
+    for( i_thread = 0; i_thread < VOUT_MAX_THREADS; i_thread++ )
+    {
+        if( p_intf->pp_vout[i_thread] == NULL )
+        {
+            /* The current place is empty: create a thread */
+            p_intf->pp_vout[i_thread] = vout_CreateThread( &cfg, NULL );
+            if( p_intf->pp_vout[i_thread] == NULL )                   /* error */
+            {                    
+                return( -1 );
+            }
+        }
+    }
+
+    /* No empty place has been found */
+    return( -1 );
+}
+
+
+/*******************************************************************************
+ * intf_DestroyVoutThread: destroy video output thread in interface
+ *******************************************************************************
+ * This function destroy a video output thread created with
+ * intf_CreateVoutThread().
+ *******************************************************************************/
+void intf_DestroyVoutThread( intf_thread_t *p_intf, int i_thread )
+{
+#ifdef DEBUG
+    /* Check if thread still exists */
+    if( p_intf->pp_vout[i_thread] == NULL )
+    {
+        intf_DbgMsg("intf error: destruction of an inexistant vout thread\n");
+        return;
+    }
+#endif
+
+    /* Destroy thread and marks its place as empty */
+    vout_DestroyThread( p_intf->pp_vout[i_thread], NULL );
+    p_intf->pp_vout[i_thread] = NULL;
+}
+
+
+/*******************************************************************************
+ * intf_CreateInputThread: create input thread in interface
+ *******************************************************************************
+ * This function creates - if possible - a new input thread in the
+ * interface registery, using interface default settings. It returns the
+ * thread number for the interface, or a negative number.
+ *******************************************************************************/
+int intf_CreateInputThread( intf_thread_t *p_intf, input_cfg_t* p_cfg )
+{
+    int             i_thread;                                  /* thread index */
+
+    /* Find an empty place */
+    for( i_thread = 0; i_thread < INPUT_MAX_THREADS; i_thread++ )
+    {
+        if( p_intf->pp_input[i_thread] == NULL )
+        {
+            /* The current place is empty: create a thread and return */
+            p_intf->pp_input[i_thread] = input_CreateThread( p_cfg );
+            return( (p_intf->pp_input[i_thread] != NULL) ? i_thread : -1 );
+        }
+    }
+
+    /* No empty place has been found */
+    return( -1 );    
+}
+
+/*******************************************************************************
+ * intf_DestroyInputThread: destroy input thread in interface
+ *******************************************************************************
+ * This function destroy aa input thread created with
+ * intf_CreateInputThread().
+ *******************************************************************************/
+void intf_DestroyInputThread( intf_thread_t *p_intf, int i_thread )
+{
+#ifdef DEBUG
+    /* Check if thread still exists */
+    if( p_intf->pp_input[i_thread] == NULL )
+    {
+        intf_DbgMsg("intf error: destruction of an inexistant input thread\n");
+        return;        
+    }
+#endif
+
+    /* Destroy thread and marks its place as empty */
+    input_DestroyThread( p_intf->pp_input[i_thread] );
+    p_intf->pp_input[i_thread] = NULL;
+}
diff --git a/src/interface/interface.c b/src/interface/interface.c
new file mode 100644 (file)
index 0000000..74c4d69
--- /dev/null
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * interface.c: interface access for other threads
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * This library provides basic functions for threads to interact with user
+ * interface, such as command line.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <pthread.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "thread.h"
+
+#include "input.h"
+#include "input_vlan.h"
+#include "decoder_fifo.h"
+
+#include "audio_output.h"
+#include "audio_decoder.h"
+
+#include "video.h"
+#include "video_output.h"
+#include "video_decoder.h"
+
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+
+#include "pgm_data.h"
+/* ?? remove useless headers */
+
+/*
+ * Local prototypes
+ */
+static int  StartInterface  ( intf_thread_t *p_intf );
+static void EndInterface    ( intf_thread_t *p_intf );
+
+/*******************************************************************************
+ * intf_Run
+ *******************************************************************************
+ * what it does:
+ *     - Create an X11 console
+ *     - wait for a command and try to execute it
+ *     - interpret the order returned after the command execution
+ *     - print the messages of the message queue (intf_FlushMsg)
+ * return value: 0 if successful, < 0 otherwise
+ *******************************************************************************/
+int intf_Run( intf_thread_t *p_intf )
+{
+    /* When it is started, interface won't die immediatly */
+    p_intf->b_die = 0;
+    if( StartInterface( p_intf ) )                                    /* error */
+    {
+        return( 1 );
+    }
+    
+    /* Main loop */
+    while(!p_intf->b_die)
+    {
+        /* Flush waiting messages */
+        intf_FlushMsg();
+
+        /* Manage specific interfaces */
+        intf_ManageXConsole( &p_intf->xconsole );               /* X11 console */
+
+        /* Sleep to avoid using all CPU - since some interfaces needs to access 
+         * keyboard events, a 100ms delay is a good compromise */
+        msleep( INTF_IDLE_SLEEP );
+    }
+
+    /* End of interface thread - the main() function will close all remaining
+     * output threads */
+    EndInterface( p_intf );
+    return ( 0 );
+}
+
+/* following functions are local */
+
+/*******************************************************************************
+ * StartInterface: prepare interface before main loop
+ *******************************************************************************
+ * This function opens output devices and create specific interfaces. It send
+ * it's own error messages.
+ *******************************************************************************/
+static int StartInterface( intf_thread_t *p_intf )
+{
+    int i_thread;                                              /* thread index */
+    
+    /* Empty all threads array */
+    for( i_thread = 0; i_thread < VOUT_MAX_THREADS; i_thread++ )
+    {
+        p_intf->pp_vout[i_thread] = NULL;        
+    }
+    for( i_thread = 0; i_thread < INPUT_MAX_THREADS; i_thread++ )
+    {
+        p_intf->pp_input[i_thread] = NULL;        
+    }    
+
+    /* Start X11 Console*/
+    if( intf_OpenXConsole( &p_intf->xconsole ) )
+    {
+        intf_ErrMsg("intf error: can't open X11 console\n");
+        return( 1 );
+    }
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * EndInterface: clean interface after main loop
+ *******************************************************************************
+ * This function destroys specific interfaces and close output devices.
+ *******************************************************************************/
+static void EndInterface( intf_thread_t *p_intf )
+{
+    int         i_thread;                                      /* thread index */
+    boolean_t   b_thread;                          /* flag for remaing threads */
+    int         pi_vout_status[VOUT_MAX_THREADS];       /* vout threads status */
+    
+    
+    
+    /* Close X11 console */
+    intf_CloseXConsole( &p_intf->xconsole );        
+
+    /* Destroy all remaining input threads */
+    for( i_thread = 0; i_thread < INPUT_MAX_THREADS; i_thread++ )
+    {
+        if( p_intf->pp_input[i_thread] != NULL )
+        {
+            input_DestroyThread( p_intf->pp_input[i_thread] );
+        }        
+    }
+
+    /* Destroy all remaining video output threads - all destruction orders are send,
+     * then all THREAD_OVER status are received */
+    for( i_thread = 0, b_thread = 0; i_thread < VOUT_MAX_THREADS; i_thread++ )
+    {
+        if( p_intf->pp_vout[i_thread] != NULL )
+        {
+            vout_DestroyThread( p_intf->pp_vout[i_thread], &pi_vout_status[i_thread] );
+            b_thread = 1;            
+        }
+    }
+    while( b_thread )
+    {
+        msleep( INTF_IDLE_SLEEP );        
+        b_thread = 0;        
+        for( i_thread = 0; i_thread < VOUT_MAX_THREADS; i_thread++ )
+        {
+            if( (p_intf->pp_vout[i_thread] != NULL) 
+                && (pi_vout_status[i_thread] != THREAD_OVER) )
+            {
+                b_thread = 1;
+            }     
+        }
+    }
+    
+
+}
diff --git a/src/interface/intf_cmd.c b/src/interface/intf_cmd.c
new file mode 100644 (file)
index 0000000..9cff69d
--- /dev/null
@@ -0,0 +1,550 @@
+/*******************************************************************************
+ * intf_cmd.c: interface commands parsing and executions functions
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * This file implements the interface commands execution functions. It is used
+ * by command-line oriented interfaces and scripts. The commands themselves are
+ * implemented in intf_ctrl.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+
+#include "input.h"
+#include "input_vlan.h"
+
+#include "audio_output.h"
+
+#include "video.h"
+#include "video_output.h"
+
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+#include "intf_cmd.h"
+#include "intf_ctrl.h"
+
+#include "pgm_data.h"
+
+/*
+ * Local prototypes
+ */
+static int  ParseCommandArguments   ( char *psz_argv[INTF_MAX_ARGS], char *psz_cmd );
+static int  CheckCommandArguments   ( intf_arg_t argv[INTF_MAX_ARGS], int i_argc, 
+                                      char *psz_argv[INTF_MAX_ARGS], char *psz_format );
+static void ParseFormatString       ( intf_arg_t format[INTF_MAX_ARGS], char *psz_format );
+static int  ConvertArgument         ( intf_arg_t *p_arg, int i_flags, char *psz_str );
+
+/*******************************************************************************
+ * intf_ExecCommand: parse and execute a command               
+ *******************************************************************************
+ * This function is called when a command needs to be executed. It parse the
+ * command line, build an argument array, find the command in control commands
+ * array and run the command. It returns the return value of the command, or
+ * EINVAL if no command could be executed. Command line is modified by this 
+ * function.
+ * Note that this function may terminate abruptly the program or signify it's
+ * end to the interface thread.
+ *******************************************************************************/
+int intf_ExecCommand( char *psz_cmd )
+{
+    char *          psz_argv[INTF_MAX_ARGS];             /* arguments pointers */
+    intf_arg_t      argv[INTF_MAX_ARGS];                /* converted arguments */
+    int             i_argc;                             /* number of arguments */
+    int             i_index;                           /* multi-purposes index */
+    int             i_return;                          /* command return value */
+
+    intf_DbgMsg("intf debug: command `%s'\n", psz_cmd);
+
+    /* Parse command line (separate arguments). If nothing has been found, 
+     * the function returns without error */
+    i_argc = ParseCommandArguments( psz_argv, psz_cmd );
+    if( !i_argc )
+    {
+        return( 0 );
+    }
+
+    /* Find command. Command is always the first token on the line */
+    for( i_index = 0; 
+         control_command[i_index].psz_name && strcmp( psz_argv[0], control_command[i_index].psz_name ); 
+         i_index++ )
+    {
+        ;
+    }
+    if( !control_command[i_index].psz_name )                /* unknown command */
+    {
+        /* Print error message */
+        intf_IntfMsg( "error: unknown command `%s'. Try `help'", psz_argv[0] );
+        return( INTF_USAGE_ERROR );
+    }
+
+    /* Check arguments validity */
+    if( CheckCommandArguments( argv, i_argc, psz_argv, control_command[i_index].psz_format ) )
+    {
+        /* The given arguments does not match the format string. An error message has 
+         * already been displayed, so only the usage string is printed */
+        intf_IntfMsg( "usage: %s", control_command[i_index].psz_usage );
+        return( INTF_USAGE_ERROR );
+    }
+
+    /* Execute command */    
+    i_return = control_command[i_index].function( i_argc, argv );
+
+    /* Manage special error codes */
+    switch( i_return )
+    {
+    case INTF_FATAL_ERROR:                                      /* fatal error */
+        /* Print message and terminates the interface thread */
+        intf_ErrMsg( "intf fatal: in command `%s'\n", psz_argv[0] );
+        p_program_data->intf_thread.b_die = 1;
+        break;
+
+    case INTF_CRITICAL_ERROR:                                /* critical error */
+        /* Print message, flush messages queue and exit. Note that this
+         * error should be very rare since it does not even try to cancel other
+         * threads... */
+        intf_ErrMsg("intf critical: in command `%s'. Please report this error !\n", psz_argv[0] );
+        intf_FlushMsg();
+        exit( INTF_CRITICAL_ERROR );
+        break;
+
+    case INTF_USAGE_ERROR:                                      /* usage error */
+        /* Print error message and usage */
+        intf_IntfMsg( "usage: %s", control_command[i_index].psz_usage );
+        break;
+    }
+
+    /* Return error code */
+    return( i_return );
+}
+
+/*******************************************************************************
+ * intf_ExecScript: parse and execute a command script
+ *******************************************************************************
+ * This function, based on ExecCommand read a file and tries to execute each
+ * of its line as a command. It returns 0 if everything succeeded, a negative
+ * number if the script could not be executed and a positive one if an error
+ * occured during execution.
+ *******************************************************************************/
+int intf_ExecScript( char *psz_filename )
+{
+    FILE *  p_file;                                                    /* file */
+    char    psz_line[INTF_MAX_CMD_SIZE];                               /* line */
+    char *  psz_index;                                      /* index in string */
+    int     i_err;                                          /* error indicator */    
+    
+    /* Open file */
+    i_err = 0;    
+    p_file = fopen( psz_filename, "r" );
+    if( p_file == NULL )
+    {
+        intf_ErrMsg("intf error: %s: %s\n", psz_filename, strerror(errno));
+        return( -1 );        
+    }
+    
+    /* For each line: read and execute */    
+    while( fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL )
+    {
+        /* If line begins with a '#', it is a comment and shoule be ignored,
+         * else, execute it */
+        if( psz_line[0] != '#' )
+        {            
+            /* The final '\n' needs to be removed before execution */
+            for( psz_index = psz_line; *psz_index && (*psz_index != '\n'); psz_index++ )
+            {                
+                ;                
+            }
+            if( *psz_index == '\n' )
+            {
+                *psz_index = '\0';                
+            }            
+
+            /* Execute command */
+            i_err |= intf_ExecCommand( psz_line );    
+        }                
+    }
+    if( !feof( p_file ) )
+    {
+        intf_ErrMsg("intf error: %s: %s\n", psz_filename, strerror(errno));
+        return( -1 );        
+    }
+    
+    /* Close file */
+    fclose( p_file );
+    return( i_err != 0 );
+}
+
+/* following functions are local */
+
+/*******************************************************************************
+ * ParseCommandArguments: isolate arguments in a command line
+ *******************************************************************************
+ * This function modify the original command line, adding '\0' and completes
+ * an array of pointers to beginning of arguments. It return the number of
+ * arguments.
+ *******************************************************************************/
+static int ParseCommandArguments( char *psz_argv[INTF_MAX_ARGS], char *psz_cmd )
+{
+    int         i_argc;                                 /* number of arguments */
+    char *      psz_index;                                            /* index */
+    boolean_t   b_block;                         /* block (argument) indicator */
+
+    /* Initialize parser state */
+    b_block = 0;     /* we start outside a block to remove spaces at beginning */
+    i_argc = 0;
+
+    /* Go through command until end has been reached or maximal number of
+     * arguments has been reached */
+    for( psz_index = psz_cmd; *psz_index && (i_argc < INTF_MAX_ARGS); psz_index++ )
+    {
+        /* Inside a block, end of blocks are marked by spaces */
+        if( b_block )
+        {
+            if( *psz_index == ' ' )
+            {
+                *psz_index = '\0';                 /* mark the end of argument */
+                b_block = 0;                                 /* exit the block */
+            }
+            
+        }
+        /* Outside a block, beginning of blocks are marked by any character
+         * different from space */
+        else
+        {
+            if( *psz_index != ' ' )
+            {
+                psz_argv[i_argc++] = psz_index;              /* store argument */
+                b_block = 1;                                /* enter the block */            
+            }
+        }
+    }
+
+    /* Return number of arguments found */
+    return( i_argc );
+}
+
+/*******************************************************************************
+ * CheckCommandArguments: check arguments agains format
+ *******************************************************************************
+ * This function parse each argument and tries to find a match in the format
+ * string. It fills the argv array.
+ * If all arguments have been sucessfuly identified and converted, it returns
+ * 0, else, an error message is issued and non 0 is returned.
+ * Note that no memory is allocated by this function, but that the arguments
+ * can be modified.
+ *******************************************************************************/
+static int CheckCommandArguments( intf_arg_t argv[INTF_MAX_ARGS], int i_argc, 
+                                  char *psz_argv[INTF_MAX_ARGS], char *psz_format )
+{
+    intf_arg_t  format[INTF_MAX_ARGS];             /* parsed format indicators */
+    int         i_arg;                                       /* argument index */
+    int         i_format;                                      /* format index */    
+    char *      psz_index;                                     /* string index */
+    char *      psz_cmp_index;                     /* string comparaison index */
+    int         i_index;                                      /* generic index */    
+    boolean_t   b_found;                              /* `argument found' flag */
+
+
+    /* Build format array */
+    ParseFormatString( format, psz_format );
+
+    /* Initialize parser: i_format must be the first non named formatter */
+    for( i_format = 0; ( i_format < INTF_MAX_ARGS ) 
+             && (format[i_format].i_flags & INTF_NAMED_ARG);
+         i_format++ )
+    {
+        ;
+    }
+
+    /* Scan all arguments */
+    for( i_arg = 1; i_arg < i_argc; i_arg++ )
+    {
+        b_found = 0;
+
+        /* Test if argument can be taken as a named argument: try to find a
+         * '=' in the string */
+        for( psz_index = psz_argv[i_arg]; *psz_index && ( *psz_index != '=' ); psz_index++ ) 
+        {
+            ;
+        }
+        if( *psz_index == '=' )                                   /* '=' found */
+        {
+            /* Browse all named arguments to check if there is one matching */
+            for( i_index = 0; (i_index < INTF_MAX_ARGS) 
+                     && ( format[i_index].i_flags & INTF_NAMED_ARG )
+                     && !b_found;
+                 i_index++ )
+            {
+                /* Current format string is named... compare start of two 
+                 * names. A local inline ntation of a strcmp is used since
+                 * string isn't ended by '\0' but by '=' */
+                for( psz_index = psz_argv[i_arg], psz_cmp_index = format[i_index].ps_name; 
+                     (*psz_index == *psz_cmp_index) && (*psz_index != '=') && (*psz_cmp_index != '=');
+                     psz_index++, psz_cmp_index++ )
+                {
+                    ;
+                }
+                if( *psz_index == *psz_cmp_index )          /* the names match */
+                {
+                    /* The argument is a named argument which name match the
+                     * named argument i_index. To be valid, the argument should
+                     * not have been already encountered and the type must 
+                     * match. Before going further, the '=' is replaced by
+                     * a '\0'. */
+                    *psz_index = '\0';
+
+                    /* Check unicity. If the argument has already been encountered,
+                     * print an error message and return. */
+                    if( format[i_index].i_flags & INTF_PRESENT_ARG )/* present */
+                    {
+                        intf_IntfMsg("error: `%s' has already been encountered", psz_argv[i_arg] );
+                        return( 1 );
+                    }
+
+                     /* Register argument and prepare exit */
+                    b_found = 1;
+                    format[i_index].i_flags |= INTF_PRESENT_ARG;
+                    argv[i_arg].i_flags = INTF_NAMED_ARG;
+                    argv[i_arg].i_index = i_index;
+                    argv[i_arg].ps_name = psz_argv[i_arg];                    
+
+                    /* Check type and store value */
+                    psz_index++;
+                    if( ConvertArgument( &argv[i_arg], format[i_index].i_flags, psz_index ) )
+                    {
+                        /* An error occured during conversion */
+                        intf_IntfMsg( "error: invalid type for `%s'", psz_index );
+                    }
+                }
+            }
+        }  
+
+        /* If argument is not a named argument, the format string will
+         * be browsed starting from last position until the argument is
+         * found or an error occurs. */
+        if( !b_found )
+        {
+            /* Reset type indicator */
+            argv[i_arg].i_flags = 0;
+
+            /* If argument is not a named argument, the format string will
+             * be browsed starting from last position until the argument is
+             * found, an error occurs or the last format argument is 
+             * reached */
+            while( !b_found && (i_format < INTF_MAX_ARGS) && format[i_format].i_flags )
+            {
+                /* Try to convert argument */
+                if( !ConvertArgument( &argv[i_arg], format[i_format].i_flags, psz_argv[i_arg] ) ) 
+                {
+                    /* Matching format has been found */
+                    b_found = 1;
+                    format[i_format].i_flags |= INTF_PRESENT_ARG;
+                    argv[i_arg].i_index = i_format;
+
+                    /* If argument is repeatable, dot not increase format counter */
+                    if( !(format[i_format].i_flags & INTF_REP_ARG) )
+                    {
+                        i_format++;
+                    }
+                }
+                else
+                {
+                    /* Argument does not match format. This can be an error, or
+                     * just a missing optionnal parameter, or the end of a 
+                     * repeated argument */                       
+                    if( (format[i_format].i_flags & INTF_OPT_ARG) 
+                        || (format[i_format].i_flags & INTF_PRESENT_ARG) )
+                    {
+                        /* This is not an error */
+                        i_format++;
+                    }
+                    else
+                    {
+                        /* The present format argument is mandatory and does
+                         * not match the argument */
+                        intf_IntfMsg("error: missing argument before `%s'", psz_argv[i_arg] );
+                        return( 1 );
+                    }
+                }
+            }
+        }
+
+        /* If argument is not a named argument and hasn't been found in
+         * format string, then it is an usage error and the function can
+         * return */
+        if( !b_found )
+        {
+            intf_IntfMsg("error: `%s' does not match any argument", psz_argv[i_arg] );
+            return( 1 );
+        }
+
+        intf_DbgMsg("intf debug: argument flags=0x%x (index=%d) name=%s str=%s int=%d float=%f\n",
+                    argv[i_arg].i_flags,
+                    argv[i_arg].i_index,
+                    (argv[i_arg].i_flags & INTF_NAMED_ARG) ? argv[i_arg].ps_name : "NA",
+                    (argv[i_arg].i_flags & INTF_STR_ARG) ? argv[i_arg].psz_str : "NA",
+                    (argv[i_arg].i_flags & INTF_INT_ARG) ? argv[i_arg].i_num : 0,
+                    (argv[i_arg].i_flags & INTF_FLOAT_ARG) ? argv[i_arg].f_num : 0);
+    }
+
+    /* Parse all remaining format specifier to verify they are all optionnal */
+    for( ;  (i_format < INTF_MAX_ARGS) && format[i_format].i_flags ; i_format++ )
+    {
+        if( !(( format[i_format].i_flags & INTF_OPT_ARG)
+              || ( format[i_format].i_flags & INTF_PRESENT_ARG)) )
+        {
+            /* Format has not been used and is neither optionnal nor multiple
+             * and present */
+            intf_IntfMsg("error: missing argument(s)\n");
+            return( 1 );
+        }
+    }
+    
+    /* If an error occured, the function already exited, so if this point is
+     * reached, everything is fine */
+    return( 0 );
+}
+
+/*******************************************************************************
+ * ConvertArgument: try to convert an argument to a given type
+ *******************************************************************************
+ * This function tries to convert the string argument given in psz_str to
+ * a type specified in i_flags. It updates p_arg and returns O on success,
+ * or 1 on error. No error message is issued.
+ *******************************************************************************/
+static int ConvertArgument( intf_arg_t *p_arg, int i_flags, char *psz_str )
+{
+    char *psz_end;                     /* end pointer for conversion functions */
+
+    if( i_flags & INTF_STR_ARG )                                     /* string */
+    {
+        /* A conversion from a string to a string will always succeed... */
+        p_arg->psz_str = psz_str;
+        p_arg->i_flags |= INTF_STR_ARG;
+    }
+    else if( i_flags & INTF_INT_ARG )                               /* integer */
+    {        
+        p_arg->i_num = strtol( psz_str, &psz_end, 0 );       /* convert string */
+        /* If the conversion failed, return 1 and do not modify argument
+         * flags. Else, add 'int' flag and continue. */
+        if( !*psz_str || *psz_end )
+        {
+            return( 1 );
+        }
+        p_arg->i_flags |= INTF_INT_ARG;
+    }
+    else if( i_flags & INTF_FLOAT_ARG )                               /* float */
+    {
+        p_arg->f_num = strtod( psz_str, &psz_end );          /* convert string */
+        /* If the conversion failed, return 1 and do not modify argument
+         * flags. Else, add 'float' flag and continue. */
+        if( !*psz_str || *psz_end )
+        {
+            return( 1 );
+        }
+        p_arg->i_flags |= INTF_FLOAT_ARG;
+    }
+#ifdef DEBUG
+    else                                      /* error: missing type specifier */
+    {
+        intf_ErrMsg("intf error: missing type specifier for `%s' (0x%x)\n", psz_str, i_flags);
+        return( 1 );
+    }
+#endif
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * ParseFormatString: parse a format string                              (ok ?)
+ *******************************************************************************
+ * This function read a format string, as specified in the control_command
+ * array, and fill a format array, to allow easier argument identification.
+ * Note that no memory is allocated by this function, but that, in a named
+ * argument, the name field does not end with a '\0' but with an '='.
+ * See command.h for format string specifications.
+ * Note that this function is designed to be efficient, not to check everything
+ * in a format string, which should be entered by a developper and therefore
+ * should be correct (TRUST !).
+ *******************************************************************************/
+static void ParseFormatString( intf_arg_t format[INTF_MAX_ARGS], char *psz_format )
+{
+    char *  psz_index;                                  /* format string index */
+    char *  psz_start;                                /* argument format start */
+    char *  psz_item;                                            /* item index */
+    int     i_index;                                           /* format index */
+
+    /* Initialize parser */
+    i_index = 0;
+    psz_start = psz_format;      
+
+    /* Reset first format indicator */
+    format[ 0 ].i_flags = 0;
+
+    /* Parse format string */
+    for( psz_index = psz_format; *psz_index && (i_index < INTF_MAX_ARGS) ; psz_index++ )
+    {
+        /* A space is always an item terminator */
+        if( *psz_index == ' ' )
+        {        
+            /* Parse format item. Items are parsed from end to beginning or to
+             * first '=' */
+            for( psz_item = psz_index - 1; 
+                 (psz_item >= psz_start) && !( format[i_index].i_flags & INTF_NAMED_ARG); 
+                 psz_item-- )
+            {
+                switch( *psz_item )
+                {
+                case 's':                                            /* string */
+                    format[i_index].i_flags |= INTF_STR_ARG;
+                    break;
+                case 'i':                                           /* integer */
+                    format[i_index].i_flags |= INTF_INT_ARG;
+                    break;
+                case 'f':                                             /* float */
+                    format[i_index].i_flags |= INTF_FLOAT_ARG;
+                    break;
+                case '*':                                   /* can be repeated */
+                    format[i_index].i_flags |= INTF_REP_ARG;
+                    break;
+                case '?':                                /* optionnal argument */
+                    format[i_index].i_flags |= INTF_OPT_ARG;
+                    break;
+                case '=':                                     /* name argument */
+                    format[i_index].i_flags |= INTF_NAMED_ARG;
+                    format[i_index].ps_name = psz_start;    
+                    break;
+#ifdef DEBUG
+                default:  /* error which should never happen: incorrect format */
+                    intf_DbgMsg("intf error: incorrect format string `%s'\n", psz_format);
+                    break;
+#endif
+                }
+            }
+
+            /* Mark next item start, increase items counter and reset next 
+             * format indicator, if it wasn't the last one. */
+            i_index++;
+            psz_start = psz_index + 1;    
+            if( i_index != INTF_MAX_ARGS )         /* end of array not reached */
+            {
+                format[ i_index ].i_flags = 0;
+            }
+        }
+    }
+}
diff --git a/src/interface/intf_ctrl.c b/src/interface/intf_ctrl.c
new file mode 100644 (file)
index 0000000..72d8170
--- /dev/null
@@ -0,0 +1,639 @@
+/*******************************************************************************
+ * intf_ctrl.c: interface commands access to control functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Library of functions common to all interfaces, allowing access to various
+ * structures and settings. Interfaces should only use those functions
+ * to read or write informations from other threads.
+ * A control function must be declared in the `local prototypes' section (it's
+ * type is fixed), and copied into the control_command array. Functions should
+ * be listed in alphabetical order, so when `help' is called they are also
+ * displayed in this order.
+ * A control function can use any function of the program, but should respect
+ * two points: first, it should not block, since if it does so, the whole
+ * interface thread will hang and in particular miscelannous interface events
+ * won't be handled. Secondly, it should send it's output messages exclusively
+ * with intf_IntfMsg() function, except particularly critical messages which
+ * can use over intf_*Msg() functions.
+ * Control functions should return 0 (INTF_NO_ERROR) on success, or one of the
+ * error codes defined in command.h. Custom error codes are allowed, but should
+ * be positive.
+ * More informations about parameters stand in `list of commands' section.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/soundcard.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+
+#include "input.h"
+#include "input_ctrl.h"
+#include "input_vlan.h"
+#include "input_psi.h"
+#include "decoder_fifo.h"
+
+#include "audio_output.h"
+#include "audio_decoder.h"
+
+#include "video.h"
+#include "video_output.h"
+#include "video_graphics.h"
+#include "video_decoder.h"
+
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+#include "intf_cmd.h"
+#include "control.h"
+#include "intf_ctrl.h"
+
+#include "pgm_data.h"
+
+/*
+ * Local prototypes
+ */
+static int Demo                 ( int i_argc, intf_arg_t *p_argv );
+static int DisplayImage         ( int i_argc, intf_arg_t *p_argv );
+static int Exec                 ( int i_argc, intf_arg_t *p_argv );
+static int Help                 ( int i_argc, intf_arg_t *p_argv );
+static int PlayAudio            ( int i_argc, intf_arg_t *p_argv );
+static int PlayVideo            ( int i_argc, intf_arg_t *p_argv );
+static int Quit                 ( int i_argc, intf_arg_t *p_argv );
+static int SelectPID            ( int i_argc, intf_arg_t *p_argv );
+static int SpawnInput           ( int i_argc, intf_arg_t *p_argv );
+#ifdef DEBUG
+static int Test                 ( int i_argc, intf_arg_t *p_argv );
+#endif
+static int Vlan                 ( int i_argc, intf_arg_t *p_argv );
+static int Psi                  ( int i_argc, intf_arg_t *p_argv );
+
+/*
+ * List of commands.
+ * This list is used by intf_ExecCommand function to find functions to
+ * execute and prepare its arguments. It is terminated by an element  which name 
+ * is a null pointer. intf_command_t is defined in command.h.
+ *
+ * Here is a description of a command description elements:
+ *  name is the name of the function, as it should be typed on command line,
+ *  function is a pointer to the control function,
+ *  format is an argument descriptor (see below),
+ *  summary is a text string displayed in regard of the command name when `help'
+ *      is called without parameters, and whith usage on syntax error,
+ *  usage is a short syntax indicator displayed with summary when the command 
+ *      causes a syntax error,
+ *  help is a complete help about the command, displayed when `help' is called with
+ *      the command name as parameter.
+ *
+ * Format string is a list of ' ' separated strings, which have following 
+ * meanings:
+ *  s       string argument
+ *  i       integer argument
+ *  f       float argument
+ *  ?       optionnal argument
+ *  *       argument can be repeated
+ *  name=   named argument
+ * Example: "channel=i? s*? i " means that any number of string arguments, 
+ * followed by a single mandatory integer argument are waited. A named argument,
+ * which name is `channel' and must have an integer value can be optionnaly
+ * specified at beginning. The last space is mandatory if there is at least one
+ * element, since it acts as an item terminator.
+ * Named arguments MUST be at the beginning of the format string, and in
+ * alphabetic order, but their order on command line has no importance. 
+ * The format string can't have more than INTF_MAX_ARGS elements.
+ */
+const intf_command_t control_command[] =
+{
+  { "demo", Demo,                                                      /* demo */
+    /* format: */   "", 
+    /* summary: */  "program demonstration", 
+    /* usage: */    "demo", 
+    /* help: */     "Start program capabilities demonstration." },
+  { "display", DisplayImage,                                        /* display */
+    /* format: */   "s ",
+    /* summary: */  "load and display an image",
+    /* usage: */    "display <file>",
+    /* help: */     "Load and display an image. Image format is automatically " \
+    "identified from file name extension." },
+  { "exec", Exec,                                                      /* exec */
+    /* format: */   "s ",
+    /* summary: */  "execute a script file",
+    /* usage: */    "exec <file>",
+    /* help: */     "Load an execute a script." },      
+  { "exit", Quit,                                         /* exit (quit alias) */
+    /* format: */   "",
+    /* summary: */  "quit program",
+    /* usage: */    "exit",
+    /* help: */     "see `quit'." },
+  { "help", Help,                                                      /* help */
+    /* format: */   "s? ", 
+    /* summary: */  "list all functions or print help about a specific function", 
+    /* usage: */    "help [command]", 
+    /* help: */     "If called without argument, list all available " \
+    " functions.\nIf a command name is provided as argument, displays a short " \
+    "inline help about the command.\n" },
+  { "play-audio", PlayAudio,                                     /* play-audio */
+    /* format: */   "stereo=i? rate=i? s ", 
+    /* summary: */  "play an audio file",
+    /* usage: */    "play-audio [stereo=1/0] [rate=r] <file>",
+    /* help: */     "Load and play an audio file." },
+  { "play-video", PlayVideo,                                      /* play-video */
+    /* format: */   "s ", 
+    /* summary: */  "play a video (.vlp) file",
+    /* usage: */    "play-video <file>",
+    /* help: */     "Load and play a video file." },
+  { "quit", Quit,                                                      /* quit */
+    /* format: */   "",
+    /* summary: */  "quit program",
+    /* usage: */    "quit",
+    /* help: */     "Terminates the program execution... There is not much to" \
+    " say about it !" },
+  { "select-pid", SelectPID,                                     /* select-pid */
+    /* format: */   "i i ",
+    /* summary: */  "spawn a decoder thread for a specified PID",
+    /* summary: */  "select-pid <input> <pid>",
+    /* help: */     "Spawn a decoder thread for <pid>. The stream will be" \
+    " received by <input>." },
+  { "spawn-input", SpawnInput,                                  /* spawn-input */
+    /* format: */   "method=i? filename=s? hostname=s? ip=s? port=i? vlan=i?",
+    /* summary: */  "spawn an input thread",
+    /* summary: */  "spawn-input [method=<method>]\n" \
+    "[filename=<file>|hostname=<hostname>|ip=<ip>]\n" \
+    "[port=<port>] [vlan=<vlan>]",
+    /* help: */     "Spawn an input thread. Method is 10, 20, 21, 22, 32, "\
+    "hostname is the fully-qualified domain name, ip is a dotted-decimal address." },
+#ifdef DEBUG
+  { "test", Test,                                                      /* test */
+    /* format: */   "i? ",
+    /* summary: */  "crazy developper's test",
+    /* usage: */    "depends on the last coder :-)",
+    /* help: */     "`test' works only in DEBUG mode, and is provide for " \
+    "developpers as an easy way to test part of their code. If you don't know " \
+    "what it should do, just try !" },
+#endif
+  { "vlan", Vlan,
+    /* format: */   "intf=s? s i? ",
+    /* summary: */  "vlan operations",
+    /* usage: */    "vlan synchro\n" \
+    "vlan [intf=<interface>] request\n" \
+    "vlan [intf=<interface>] join <vlan>\n" \
+    "vlan [intf=<interface>] leave"
+    /* help: */     "Perform various operations on vlans. 'synchro' resynchronize " \
+    "with the server. 'request' ask which is the current vlan (for the default " \
+    "interface or for a given one). 'join' and 'leave' try to change vlan." },
+  { "psi", Psi,
+    /* format: */   "i ",
+    /* summary: */  "Dump PSI tables",
+    /* usage: */    "psi <input thread index>",
+    /* help: */     "Display the PSI tables on the console. Warning: this is debug" \
+    "command, it can leads to pb since locks are not taken yet" },
+  { 0, 0, 0, 0, 0 }                                        /* array terminator */
+};
+
+/* following functions are local */
+
+/*******************************************************************************
+ * Demo: demo
+ *******************************************************************************
+ * This function is provided to display a demo of program possibilities. 
+ *******************************************************************************/
+static int Demo( int i_argc, intf_arg_t *p_argv )
+{
+    intf_IntfMsg( COPYRIGHT_MESSAGE );
+
+    return( INTF_NO_ERROR );
+}
+
+/*******************************************************************************
+ * Exec: execute a script
+ *******************************************************************************
+ * This function load and execute a script.
+ *******************************************************************************/
+static int Exec( int i_argc, intf_arg_t *p_argv )
+{
+    int i_err;                                                   /* error code */
+    
+    i_err = intf_ExecScript( p_argv[1].psz_str );
+    return( i_err ? INTF_OTHER_ERROR : INTF_NO_ERROR );     
+}
+
+/*******************************************************************************
+ * DisplayImage: load and display an image                               (ok ?)
+ *******************************************************************************
+ * Try to load an image identified by it's filename and displays it as a still
+ * image using interface video heap.
+ *******************************************************************************/
+static int DisplayImage( int i_argc, intf_arg_t *p_argv )
+{
+    /* ?? */
+    return( INTF_NO_ERROR );
+}
+
+/*******************************************************************************
+ * Help: list all available commands                                     (ok ?)
+ *******************************************************************************
+ * This function print a list of available commands
+ *******************************************************************************/
+static int Help( int i_argc, intf_arg_t *p_argv )
+{
+    int     i_index;                                          /* command index */
+
+    /* If called with an argument: look for the command and display it's help */
+    if( i_argc == 2 )
+    {
+        for( i_index = 0; control_command[i_index].psz_name 
+                 && strcmp( control_command[i_index].psz_name, p_argv[1].psz_str );
+             i_index++ )
+        {
+            ;
+        }
+        /* Command has been found in list */
+        if( control_command[i_index].psz_name )
+        {
+            intf_IntfMsg( control_command[i_index].psz_usage );
+            intf_IntfMsg( control_command[i_index].psz_help );
+        }
+        /* Command is unknown */
+        else
+        {
+            intf_IntfMsg("help: don't know command `%s'", p_argv[1].psz_str);
+            return( INTF_OTHER_ERROR );
+        }
+    }
+    /* If called without argument: print all commands help field */
+    else
+    {
+        for( i_index = 0; control_command[i_index].psz_name; i_index++ )
+        {
+            intf_IntfMsg( "%s: %s",  control_command[i_index].psz_name, 
+                          control_command[i_index].psz_summary );
+        }
+    }
+
+    return( INTF_NO_ERROR );
+}
+
+/******************************************************************************
+ * PlayAudio: play an audio file                                         (ok ?)
+ ******************************************************************************
+ * Play a raw audio file from a file, at a given rate.
+ ******************************************************************************/
+static int PlayAudio( int i_argc, intf_arg_t *p_argv )
+{
+    char *psz_file;                       /* name of the audio raw file (s16) */
+    int i_fd;       /* file descriptor of the audio file that is to be loaded */
+    aout_fifo_t fifo;          /* fifo stores the informations about the file */
+    struct stat stat_buffer;       /* needed to find out the size of psz_file */
+    int i_arg;                                              /* argument index */
+
+    if ( !p_program_data->cfg.b_audio )                  /* audio is disabled */
+    {
+        intf_IntfMsg("play-audio error: audio is disabled");
+        return( INTF_NO_ERROR );
+    }
+
+    /* Set default configuration */
+    fifo.b_stereo = AOUT_DEFAULT_STEREO;
+    fifo.l_rate = AOUT_DEFAULT_RATE;
+
+    /* The stereo and rate parameters are essential ! */
+    /* Parse parameters - see command list above */
+    for ( i_arg = 1; i_arg < i_argc; i_arg++ )
+    {
+        switch( p_argv[i_arg].i_index )
+        {
+        case 0:                                                     /* stereo */
+            fifo.b_stereo = p_argv[i_arg].i_num;
+            break;
+        case 1:                                                       /* rate */
+            fifo.l_rate = p_argv[i_arg].i_num;
+            break;
+        case 2:                                                   /* filename */
+            psz_file = p_argv[i_arg].psz_str;
+            break;
+        }
+    }
+
+    /* Setting up the type of the fifo */
+    switch ( fifo.b_stereo )
+    {
+        case 0:
+            fifo.i_type = AOUT_INTF_MONO_FIFO;
+            break;
+
+        case 1:
+            fifo.i_type = AOUT_INTF_STEREO_FIFO;
+            break;
+
+        default:
+            intf_IntfMsg("play-audio error: stereo must be 0 or 1");
+            return( INTF_OTHER_ERROR );
+    }
+
+    /* Open file */
+    i_fd =  open( psz_file, O_RDONLY );
+    if ( i_fd < 0 )                                                  /* error */
+    {
+        intf_IntfMsg("play-audio error: can't open `%s'", psz_file);
+        return( INTF_OTHER_ERROR );
+    }
+
+    /* Get file size to calculate number of audio units */
+    fstat( i_fd, &stat_buffer );
+    fifo.l_units = ( long )( stat_buffer.st_size / (sizeof(s16) << fifo.b_stereo) );
+
+    /* Allocate memory, read file and close it */
+    if ( (fifo.buffer = malloc(sizeof(s16)*(fifo.l_units << fifo.b_stereo))) == NULL ) /* !! */
+    {
+        intf_IntfMsg("play-audio error: not enough memory to read `%s'", psz_file );
+        close( i_fd );                                          /* close file */
+        return( INTF_OTHER_ERROR );
+    }
+    if ( read(i_fd, fifo.buffer, sizeof(s16)*(fifo.l_units << fifo.b_stereo))
+        != sizeof(s16)*(fifo.l_units << fifo.b_stereo) )
+    {
+        intf_IntfMsg("play-audio error: can't read %s", psz_file);
+        free( fifo.buffer );
+        close( i_fd );
+        return( INTF_OTHER_ERROR );
+    }
+    close( i_fd );
+
+    /* Now we can work out how many output units we can compute with the fifo */
+    fifo.l_units = (long)(((s64)fifo.l_units*(s64)p_program_data->aout_thread.dsp.l_rate)/(s64)fifo.l_rate);
+
+    /* Create the fifo */
+    if ( aout_CreateFifo(&p_program_data->aout_thread, &fifo) == NULL )
+    {
+        intf_IntfMsg("play-audio error: can't create audio fifo");
+        free( fifo.buffer );
+        return( INTF_OTHER_ERROR );
+    }
+
+    return( INTF_NO_ERROR );
+}
+
+/*******************************************************************************
+ * PlayVideo: play a video sequence from a file
+ *******************************************************************************
+ * ??
+ *******************************************************************************/
+static int PlayVideo( int i_argc, intf_arg_t *p_argv )
+{
+    /* ?? */
+    return( INTF_NO_ERROR );    
+}
+
+/*******************************************************************************
+ * Quit: quit program                                                    (ok ?)
+ *******************************************************************************
+ * This function set `die' flag of interface, asking the program to terminate.
+ *******************************************************************************/
+static int Quit( int i_argc, intf_arg_t *p_argv )
+{
+    p_program_data->intf_thread.b_die = 1;
+    return( INTF_NO_ERROR );
+}
+
+
+/******************************************************************************
+ *
+ ******************************************************************************
+ *
+ ******************************************************************************/
+static int SelectPID( int i_argc, intf_arg_t *p_argv )
+{
+    int i_input, i_pid;
+    int i_arg;
+  
+    /* Parse parameters - see command list above */
+    for ( i_arg = 1; i_arg < i_argc; i_arg++ )
+    {
+      switch( p_argv[i_arg].i_index )
+      {
+      case 0:
+          i_input = p_argv[i_arg].i_num;
+          break;
+      case 1:
+         i_pid = p_argv[i_arg].i_num;
+        break;
+      }
+    }
+
+
+    /* Find to which input this command is destinated */
+    if(i_input < INPUT_MAX_THREADS )
+    {
+        if( p_program_data->intf_thread.pp_input[i_input] )
+        {
+            intf_IntfMsg( "Adding PID %d to input %d\n", i_pid, i_input );
+            input_AddPgrmElem( p_program_data->intf_thread.pp_input[i_input],
+                               i_pid );
+            return( INTF_NO_ERROR );
+        }
+    }      
+
+    /* No such input was created */
+    intf_IntfMsg("No such input thread is currently running: %d\n", i_input);
+    return(  INTF_OTHER_ERROR );
+}
+
+
+/******************************************************************************
+ * SpawnInput: spawn an input thread                                     (ok ?)
+ ******************************************************************************
+ * Spawn an input thread with the correct p_cfg parameters.
+ ******************************************************************************/
+static int SpawnInput( int i_argc, intf_arg_t *p_argv )
+{
+    input_cfg_t         cfg;
+    int                 i_arg;
+    
+    /* Erase p_cfg. */
+    bzero( &cfg, sizeof( cfg ) );
+
+    /* Parse parameters - see command list above */
+    for ( i_arg = 1; i_arg < i_argc; i_arg++ )
+    {
+        switch( p_argv[i_arg].i_index )
+        {
+        case 0:                                                     /* method */
+            cfg.i_method = p_argv[i_arg].i_num;
+            break;
+        case 1:                                                   /* filename */
+            cfg.psz_filename = p_argv[i_arg].psz_str;
+            break;
+        case 2:                                                   /* hostname */
+            cfg.psz_hostname = p_argv[i_arg].psz_str;
+            break;
+        case 3:                                                         /* ip */
+            cfg.psz_ip = p_argv[i_arg].psz_str;
+            break;
+        case 4:                                                       /* port */
+            cfg.i_port = p_argv[i_arg].i_num;
+            break;
+        case 5:                                                       /* VLAN */
+            cfg.i_vlan = p_argv[i_arg].i_num;
+            break;
+        }
+    }
+
+    /* Setting i_properties to indicate which parameters are set. */
+    if( cfg.i_method )
+    {
+        cfg.i_properties |= INPUT_CFG_METHOD;
+    }
+    if( cfg.psz_filename )
+    {
+        cfg.i_properties |= INPUT_CFG_FILENAME;
+    }
+    if( cfg.psz_hostname )
+    {
+        cfg.i_properties |= INPUT_CFG_HOSTNAME;
+    }
+    if( cfg.psz_ip )
+    {
+        cfg.i_properties |= INPUT_CFG_IP;
+    }
+    if( cfg.i_port )
+    {
+        cfg.i_properties |= INPUT_CFG_PORT;
+    }
+    if( cfg.i_vlan )
+    {
+        cfg.i_properties |= INPUT_CFG_VLAN;
+    }
+
+    /* Default settings for the decoder threads */
+    cfg.p_aout = p_program_data->intf_thread.p_aout;
+
+    /* Create the input thread */
+    if( intf_CreateInputThread( &p_program_data->intf_thread, &cfg ) == -1)
+    {
+        return( INTF_OTHER_ERROR );
+    }
+
+    return( INTF_NO_ERROR );
+}
+
+/*******************************************************************************
+ * Test: test function
+ *******************************************************************************
+ * This function is provided to test new functions in the program. Fell free
+ * to modify ! 
+ * This function is only defined in DEBUG mode.
+ *******************************************************************************/
+#ifdef DEBUG
+static int Test( int i_argc, intf_arg_t *p_argv )
+{
+    int i_thread;    
+
+    if( i_argc == 1 )
+    {
+        i_thread = intf_CreateVoutThread( &p_program_data->intf_thread, NULL, -1, -1);
+        intf_IntfMsg("return value: %d", i_thread );        
+    }
+    else
+    {
+        i_thread = p_argv[1].i_num;        
+        intf_DestroyVoutThread( &p_program_data->intf_thread, i_thread );        
+    }    
+
+    return( INTF_NO_ERROR );
+}
+#endif
+
+/*******************************************************************************
+ * Vlan: vlan operations
+ *******************************************************************************
+ * This function performs various vlan operations.
+ *******************************************************************************/
+static int Vlan( int i_argc, intf_arg_t *p_argv  )
+{
+    int i_command;                                  /* command argument number */
+
+    /* Do not try anything if vlans are desactivated */
+    if( !p_program_data->cfg.b_vlans )
+    {
+        intf_IntfMsg("vlans are desactivated");
+        return( INTF_OTHER_ERROR );
+    }
+    
+    /* Look for command in list of arguments - this argument is mandatory and
+     * imposed by the calling function */
+    for( i_command = 1; p_argv[i_command].i_index == 1; i_command++ )
+    {
+        ;        
+    }
+    
+    /* Command is 'synchro' */
+    if( !strcmp(p_argv[i_command].psz_str, "synchro") )
+    {
+        input_VlanSynchronize();
+    }
+    /* Command is 'request' */
+    else if( !strcmp(p_argv[i_command].psz_str, "request") )
+    {
+        /* ?? */
+    }
+    /* Command is 'join' */
+    else if( !strcmp(p_argv[i_command].psz_str, "join") )
+    {
+        /* ?? */
+    }    
+    /* Command is 'leave' */
+    else if( !strcmp(p_argv[i_command].psz_str, "leave") )
+    {
+        /* ?? */
+    }
+    /* Command is unknown */
+    else
+    {
+        intf_IntfMsg("vlan error: unknown command %s", p_argv[i_command].psz_str );
+        return( INTF_USAGE_ERROR );        
+    }
+         
+    return( INTF_NO_ERROR );
+}
+
+
+/*******************************************************************************
+ * Psi
+ *******************************************************************************
+ * This function is provided to display PSI tables. 
+ *******************************************************************************/
+static int Psi( int i_argc, intf_arg_t *p_argv )
+{
+    int i_index = p_argv[1].i_num;
+  
+    if(i_index < INPUT_MAX_THREADS )
+    {
+      if(p_program_data->intf_thread.pp_input[i_index])
+      {
+        /* Read the Psi table for that thread */
+        intf_IntfMsg("Reading PSI table for input %d\n", i_index);
+        input_PsiRead(p_program_data->intf_thread.pp_input[i_index]);
+        return( INTF_NO_ERROR );
+      }
+    }      
+
+    /* No such input was created */
+    intf_IntfMsg("No such input thread is currently running: %d\n", i_index);
+    
+    return(  INTF_OTHER_ERROR );
+}
diff --git a/src/interface/intf_msg.c b/src/interface/intf_msg.c
new file mode 100644 (file)
index 0000000..bc9d9aa
--- /dev/null
@@ -0,0 +1,506 @@
+/*******************************************************************************
+ * intf_msg.c: messages interface
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * This library provides basic functions for threads to interact with user
+ * interface, such as message output. If INTF_MSG_QUEUE is defined (which is the
+ * defaul), messages are not printed directly by threads, to bypass console 
+ * limitations and slow printf() calls, but sent to a queue and printed later by
+ * interface thread. 
+ * If INTF_MSG_QUEUE is not defined, output is directly performed on stderr.
+ * Exported symbols are declared in intf_msg.h.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "debug.h"
+
+#include "input.h"
+#include "input_vlan.h"
+
+#include "audio_output.h"
+
+#include "video.h"
+#include "video_output.h"
+
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+
+#include "pgm_data.h"
+
+/*
+ * Local prototypes
+ */
+
+static void QueueMsg        ( interface_msg_t *p_intf_msg, int i_type,
+                              char *psz_format, va_list ap );
+static void PrintMsg        ( interface_msg_message_t *p_msg );
+#ifdef DEBUG
+static void QueueDbgMsg     ( interface_msg_t *p_intf_msg, char *psz_file, 
+                              char *psz_function, int i_line, 
+                              char *psz_format, va_list ap );
+#endif
+#ifdef INTF_MSG_QUEUE
+static void FlushLockedMsg  ( interface_msg_t *p_intf_msg );
+#endif
+
+/*******************************************************************************
+ * intf_InitMsg: initialize messages interface                            (ok ?)
+ *******************************************************************************
+ * This functions has to be called before any call to other intf_*Msg functions.
+ * It set up the locks and the message queue if it is used. On error, 
+ * it returns errno (without printing its own error messages) and free all 
+ * alocated resources.
+ *******************************************************************************/
+int intf_InitMsg( interface_msg_t *p_intf_msg )
+{
+#ifdef INTF_MSG_QUEUE
+    /* Message queue initialization */
+    pthread_mutex_init( &p_intf_msg->lock, NULL );           /* intialize lock */
+    p_intf_msg->i_count = 0;                                 /* queue is empty */
+#endif
+
+#ifdef DEBUG_LOG
+    /* Log file initialization */
+    p_intf_msg->p_log_file = fopen( DEBUG_LOG, "w+" );
+    if ( !p_intf_msg->p_log_file )
+    {        
+        return( errno );
+    }    
+#endif
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * intf_TerminateMsg: free resources allocated by intf_InitMsg            (ok ?)
+ *******************************************************************************
+ * This functions prints all messages remaining in queue, then free all the 
+ * resources allocated by intf_InitMsg.
+ * No other messages interface functions should be called after this one.
+ *******************************************************************************/
+void intf_TerminateMsg( interface_msg_t *p_intf_msg )
+{
+    intf_FlushMsg();                           /* print all remaining messages */
+
+#ifdef DEBUG_LOG
+    /* Close log file */
+    fclose( p_intf_msg->p_log_file );
+#endif
+}
+
+/*******************************************************************************
+ * intf_Msg: print a message                                              (ok ?)
+ *******************************************************************************
+ * This function queue a message for later printing, or print it immediately
+ * if the queue isn't used.
+ *******************************************************************************/
+void intf_Msg( char *psz_format, ... )
+{
+    va_list ap;
+
+    va_start( ap, psz_format );
+    QueueMsg( &p_program_data->intf_msg, INTF_MSG_STD, psz_format, ap );
+    va_end( ap );
+}
+/*******************************************************************************
+ * intf_ErrMsg : print an error message                                   (ok ?)
+ *******************************************************************************
+ * This function is the same as intf_Msg, except that it prints its messages
+ * on stderr.
+ *******************************************************************************/ 
+void intf_ErrMsg(char *psz_format, ...)
+{
+    va_list ap;
+
+    va_start( ap, psz_format );
+    QueueMsg( &p_program_data->intf_msg, INTF_MSG_ERR, psz_format, ap );
+    va_end( ap );
+}
+
+/*******************************************************************************
+ * intf_IntfMsg : print an interface message                              (ok ?)
+ *******************************************************************************
+ * In opposition to all other intf_*Msg function, this function does not print
+ * it's message on default terminal (stdout or stderr), but send it to 
+ * interface (in fact to the X11 console). This means that the interface MUST
+ * be initialized and a X11 console openned before this function is used, and
+ * that once the console is closed, this call is vorbidden.
+ * Practically, only the interface thread itself should call this function, and 
+ * flush all messages before intf_CloseX11Console() is called.
+ *******************************************************************************/
+void intf_IntfMsg(char *psz_format, ...)
+{
+    va_list ap;
+
+    va_start( ap, psz_format );
+    QueueMsg( &p_program_data->intf_msg, INTF_MSG_INTF, psz_format, ap );
+    va_end( ap );
+}
+
+/*******************************************************************************
+ * _intf_DbgMsg: print a debugging message                                (ok ?)
+ *******************************************************************************
+ * This function prints a debugging message. Compared to other intf_*Msg 
+ * functions, it is only defined if DEBUG is defined and require a file name,
+ * a function name and a line number as additionnal debugging informations. It
+ * also prints a debugging header for each received line.
+ *******************************************************************************/ 
+#ifdef DEBUG
+void _intf_DbgMsg( char *psz_file, char *psz_function, int i_line, 
+                   char *psz_format, ...)
+{
+    va_list ap;
+
+    va_start( ap, psz_format );
+    QueueDbgMsg( &p_program_data->intf_msg, psz_file, psz_function, i_line, 
+                 psz_format, ap );
+    va_end( ap );
+}
+#endif
+
+/*******************************************************************************
+ * intf_ErrMsgImm: print a message                                        (ok ?)
+ *******************************************************************************
+ * This function prints a message immediately. If the queue is used, all 
+ * waiting messages are also printed.
+ *******************************************************************************/
+void intf_MsgImm( char *psz_format, ... )
+{
+    va_list ap;
+
+    va_start( ap, psz_format );
+    QueueMsg( &p_program_data->intf_msg, INTF_MSG_STD, psz_format, ap );
+    va_end( ap );
+    intf_FlushMsg();
+}
+/*******************************************************************************
+ * intf_ErrMsgImm: print an error message immediately                     (ok ?)
+ *******************************************************************************
+ * This function is the same as intf_MsgImm, except that it prints its message
+ * on stderr.
+ *******************************************************************************/
+void intf_ErrMsgImm(char *psz_format, ...)
+{
+    va_list ap;
+
+    va_start( ap, psz_format );
+    QueueMsg( &p_program_data->intf_msg, INTF_MSG_ERR, psz_format, ap );
+    va_end( ap );
+    intf_FlushMsg();
+}
+
+/*******************************************************************************
+ * _intf_DbgMsgImm: print a debugging message immediately                 (ok ?)
+ *******************************************************************************
+ * This function is the same as intf_DbgMsgImm, except that it prints its
+ * message immediately. It should only be called through the macro 
+ * intf_DbgMsgImm().
+ *******************************************************************************/
+#ifdef DEBUG
+void _intf_DbgMsgImm( char *psz_file, char *psz_function, int i_line, 
+                      char *psz_format, ...)
+{
+    va_list ap;
+
+    va_start( ap, psz_format );
+    QueueDbgMsg( &p_program_data->intf_msg, psz_file, psz_function, i_line, 
+                 psz_format, ap );
+    va_end( ap );
+    intf_FlushMsg();
+}
+#endif
+
+/*******************************************************************************
+ * intf_FlushMsg                                                          (ok ?)
+ *******************************************************************************
+ * Print all messages remaining in queue: get lock and call FlushLockedMsg.
+ * This function does nothing if the message queue isn't used.
+ * This function is only implemented if message queue is used. If not, it is an
+ * empty macro.
+ *******************************************************************************/
+#ifdef INTF_MSG_QUEUE
+void intf_FlushMsg( void )
+{
+    pthread_mutex_lock( &p_program_data->intf_msg.lock );          /* get lock */
+    FlushLockedMsg( &p_program_data->intf_msg );             /* flush messages */
+    pthread_mutex_unlock( &p_program_data->intf_msg.lock );  /* give lock back */
+}
+#endif
+
+/* following functions are local */
+
+/*******************************************************************************
+ * QueueMsg: add a message to a queue                                    (ok ?)
+ *******************************************************************************
+ * This function provide basic functionnalities to other intf_*Msg functions.
+ * It add a message to a queue (after having printed all stored messages if it
+ * is full. If the message can't be converted to string in memory, it exit the  
+ * program. If the queue is not used, it prints the message immediately.
+ *******************************************************************************/
+static void QueueMsg(interface_msg_t *p_intf_msg, int i_type, char *psz_format, va_list ap)
+{
+    char *                          psz_str;       /* formatted message string */
+#ifndef INTF_MSG_QUEUE
+    interface_msg_message_t         msg;                            /* message */
+#endif
+
+    /* Convert message to string */   
+    vasprintf( &psz_str, psz_format, ap );
+    if( psz_str == NULL )
+    {
+        fprintf(stderr, "intf error: *** can not store message (%s) ***\n", 
+                strerror(errno) );
+        vfprintf(stderr, psz_format, ap );
+        exit( errno );
+    }
+
+#ifdef INTF_MSG_QUEUE
+
+    /* 
+     * Queue mode: the queue is flushed if it is full, then the message is
+     * queued. A lock is required on queue to avoid indexes corruption 
+     */
+    pthread_mutex_lock( &p_intf_msg->lock );                        /* get lock */
+    
+    if( p_intf_msg->i_count == INTF_MSG_QSIZE )        /* flush queue if needed */
+    {  
+#ifdef DEBUG                   /* in debug mode, queue overflow causes a waring */
+        fprintf(stderr, "intf warning: *** message queue overflow ***\n" );
+#endif
+        FlushLockedMsg( p_intf_msg );
+    }
+    
+    /* Queue message - if DEBUG if defined, the message is dated */
+    p_intf_msg->msg[ p_intf_msg->i_count ].i_type =     i_type;
+    p_intf_msg->msg[ p_intf_msg->i_count++ ].psz_msg =  psz_str;    
+#ifdef DEBUG
+    p_intf_msg->msg[ p_intf_msg->i_count ].date =       mdate();
+#endif
+
+    pthread_mutex_unlock( &p_intf_msg->lock );               /* give lock back */
+
+#else
+
+    /* 
+     * Instant mode: the message is converted and printed immediately 
+     */
+    msg.i_type = i_type; 
+    msg.psz_msg = psz_str;
+#ifdef DEBUG
+    msg.date = mdate();
+#endif
+    PrintMsg( &msg );                                         /* print message */
+    free( psz_str );                                      /* free message data */    
+
+#endif
+}
+
+/*******************************************************************************
+ * QueueDbgMsg: add a message to a queue with debugging informations
+ *******************************************************************************
+ * This function is the same as QueueMsg, except that it is only defined when
+ * DEBUG is define, and require additionnal debugging informations.
+ *******************************************************************************/
+#ifdef DEBUG
+static void QueueDbgMsg(interface_msg_t *p_intf_msg, char *psz_file, char *psz_function,
+                     int i_line, char *psz_format, va_list ap)
+{
+    char *                          psz_str;       /* formatted message string */
+#ifndef INTF_MSG_QUEUE
+    interface_msg_message_t         msg;                            /* message */
+#endif
+
+    /* Convert message to string */   
+    vasprintf( &psz_str, psz_format, ap );    
+    if( psz_str == NULL )
+    {                    /* critical error: not enough memory to store message */
+        fprintf(stderr, "intf error: *** can not store message (%s) ***\n", strerror(errno) );
+        fprintf(stderr, INTF_MSG_DBG_FORMAT, psz_file, psz_function, i_line );
+        vfprintf(stderr, psz_format, ap );
+        exit( errno );
+    }
+
+#ifdef INTF_MSG_QUEUE
+
+    /* 
+     * Queue mode: the queue is flushed if it is full, then the message is
+     * queued. A lock is required on queue to avoid indexes corruption 
+     */
+    pthread_mutex_lock( &p_intf_msg->lock );                       /* get lock */
+    
+    if( p_intf_msg->i_count == INTF_MSG_QSIZE )       /* flush queue if needed */
+    {  
+        fprintf(stderr, "intf warning: *** message queue overflow ***\n" );
+        FlushLockedMsg( p_intf_msg );
+    }
+    
+    /* Queue message */
+    p_intf_msg->msg[ p_intf_msg->i_count ].i_type =         INTF_MSG_DBG;    
+    p_intf_msg->msg[ p_intf_msg->i_count ].date =           mdate();
+    p_intf_msg->msg[ p_intf_msg->i_count ].psz_file =       psz_file;
+    p_intf_msg->msg[ p_intf_msg->i_count ].psz_function =   psz_function;
+    p_intf_msg->msg[ p_intf_msg->i_count ].i_line =         i_line;
+    p_intf_msg->msg[ p_intf_msg->i_count++ ].psz_msg =      psz_str;    
+
+    pthread_mutex_unlock( &p_intf_msg->lock );               /* give lock back */
+
+#else
+
+    /* 
+     * Instant mode: the message is converted and printed immediately 
+     */
+    msg.i_type =        INTF_MSG_DBG; 
+    msg.psz_file =      psz_file;
+    msg.psz_function =  psz_function;
+    msg.i_line =        i_line;
+    msg.date =          mdate();
+    msg.psz_msg =       psz_str;
+    PrintMsg( &msg );                                         /* print message */
+    free( psz_str );                                      /* free message data */    
+
+#endif
+}
+#endif
+
+/*******************************************************************************
+ * FlushLockedMsg                                                        (ok ?)
+ *******************************************************************************
+ * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
+ * this function does not check the lock. This function is only defined if
+ * INTF_MSG_QUEUE is defined.
+ *******************************************************************************/
+#ifdef INTF_MSG_QUEUE
+static void FlushLockedMsg ( interface_msg_t *p_intf_msg )
+{
+    int i_index;
+
+    for( i_index = 0; i_index < p_intf_msg->i_count; i_index++ )
+    {
+        /* Print message and free message data */
+        PrintMsg( &p_intf_msg->msg[i_index] );      
+        free( p_intf_msg->msg[i_index].psz_msg );
+    }
+    
+    p_intf_msg->i_count = 0;
+}
+#endif
+
+/*******************************************************************************
+ * PrintMsg: print a message                                              (ok ?)
+ *******************************************************************************
+ * Print a single message. The message data is not freed. This function exists
+ * in two version. The DEBUG version prints a date with each message, and is
+ * able to log messages (if DEBUG_LOG is defined).
+ * The normal one just prints messages to the screen.
+ *******************************************************************************/
+#ifdef DEBUG
+
+static void PrintMsg( interface_msg_message_t *p_msg )
+{
+    char    psz_date[MSTRTIME_MAX_SIZE];              /* formatted time buffer */
+    char *  psz_msg;                                         /* message buffer */
+    
+
+    /* Computes date */
+    mstrtime( psz_date, p_msg->date );    
+
+    /* Format message - the message is formatted here because in case the log
+     * file is used, it avoids another format string parsing */
+    switch( p_msg->i_type )
+    {
+    case INTF_MSG_STD:                                     /* regular messages */
+    case INTF_MSG_ERR:
+        asprintf( &psz_msg, "(%s) %s", psz_date, p_msg->psz_msg );
+        break;
+
+    case INTF_MSG_INTF:                                  /* interface messages */
+        asprintf( &psz_msg, p_msg->psz_msg );
+        break;
+        
+    case INTF_MSG_DBG:                                       /* debug messages */
+        asprintf( &psz_msg, "(%s) " INTF_MSG_DBG_FORMAT "%s", 
+                  psz_date, p_msg->psz_file, p_msg->psz_function, p_msg->i_line, 
+                  p_msg->psz_msg );            
+        break;                
+    }
+    
+    /* Check if formatting function suceeded */
+    if( psz_msg == NULL )
+    {
+        fprintf( stderr, "intf error: *** can not format message (%s): %s ***\n", 
+                 strerror( errno ), p_msg->psz_msg );        
+        return;        
+    }
+
+    /*
+     * Print messages
+     */
+    switch( p_msg->i_type )
+    {
+    case INTF_MSG_STD:                                    /* standard messages */
+        fprintf( stdout, psz_msg );
+        break;
+    case INTF_MSG_ERR:                                       /* error messages */
+#ifndef DEBUG_LOG_ONLY
+    case INTF_MSG_DBG:                                   /* debugging messages */
+#endif
+        fprintf( stderr, psz_msg );
+        break;
+    case INTF_MSG_INTF:                                  /* interface messages */
+        intf_PrintXConsole( &p_program_data->intf_thread.xconsole, psz_msg );
+        break;
+    }
+    
+#ifdef DEBUG_LOG
+    /* Append all messages to log file */
+    fprintf( p_program_data->intf_msg.p_log_file, psz_msg );
+#endif
+
+    /* Free formatted message */
+    free( psz_msg );    
+}
+
+#else
+
+static void PrintMsg( interface_msg_message_t *p_msg )
+{
+    /*
+     * Print messages on screen 
+     */
+    switch( p_msg->i_type )
+    {
+    case INTF_MSG_STD:                                    /* standard messages */
+    case INTF_MSG_DBG:                                       /* debug messages */
+        fprintf( stdout, p_msg->psz_msg );
+        break;
+    case INTF_MSG_ERR:                                       /* error messages */
+        fprintf( stderr, p_msg->psz_msg );
+        break;
+    case INTF_MSG_INTF:                                  /* interface messages */
+        intf_PrintXConsole( &p_program_data->intf_thread.xconsole, 
+                            p_msg->psz_msg );
+        break;
+    } 
+}
+
+#endif
diff --git a/src/interface/main.c b/src/interface/main.c
new file mode 100644 (file)
index 0000000..f3e3e3f
--- /dev/null
@@ -0,0 +1,460 @@
+/*******************************************************************************
+ * main.c: main vlc source
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * Includes the main() function for vlc. Parses command line, start interface
+ * and spawn threads.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "netutils.h"
+
+#include "input.h"
+#include "input_vlan.h"
+#include "decoder_fifo.h"
+
+#include "audio_output.h"
+#include "audio_decoder.h"
+
+#include "video.h"
+#include "video_output.h"
+#include "video_decoder.h"
+
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+
+#include "pgm_data.h"
+
+
+
+/*
+ * Command line options constants. If something is changed here, be sure that
+ * GetConfiguration and Usage are also changed.
+ */
+
+/* Long options return values - note that values corresponding to short options
+ * chars, and in general any regular char, should be avoided */
+#define OPT_DISPLAY             130
+
+#define OPT_CONSOLE_DISPLAY     140
+#define OPT_CONSOLE_GEOMETRY    141
+
+#define OPT_NOAUDIO             150
+#define OPT_STEREO              151
+#define OPT_MONO                152
+#define OPT_RATE                153
+
+#define OPT_NOVIDEO             160
+#define OPT_XSHM                161
+#define OPT_NOXSHM              162
+
+#define OPT_NOVLANS             170
+#define OPT_VLAN_SERVER         171
+/* Long options */
+static const struct option longopts[] =
+{   
+    /*  name,               has_arg,    flag,   val */     
+
+    /* General/common options */
+    {   "help",             0,          0,      'h' },          
+    {   "display",          1,          0,      OPT_DISPLAY },
+
+    /* Interface options */
+    {   "console-display",  1,          0,      OPT_CONSOLE_DISPLAY }, 
+    {   "console-geometry", 1,          0,      OPT_CONSOLE_GEOMETRY },
+
+    /* Audio options */
+    {   "noaudio",          0,          0,      OPT_NOAUDIO },       
+    {   "stereo",           0,          0,      OPT_STEREO },
+    {   "mono",             0,          0,      OPT_MONO },      
+    {   "rate",             0,          0,      OPT_RATE },
+
+    /* Video options */
+    {   "novideo",          0,          0,      OPT_NOVIDEO },           
+    {   "xshm",             0,          0,      OPT_XSHM },
+    {   "noxshm",           0,          0,      OPT_NOXSHM },    
+
+    /* VLAN management options */
+    {   "novlans",          0,          0,      OPT_NOVLANS },
+    {   "vlanserver",       1,          0,      OPT_VLAN_SERVER },                    
+
+    {   0,                  0,          0,      0 }
+};
+
+/* Short options */
+static const char *psz_shortopts = "h";
+
+/*
+ * Global variable program_data
+ */
+program_data_t *p_program_data;                              /* see pgm_data.h */
+
+/*
+ * Local prototypes
+ */
+static void SetDefaultConfiguration ( program_data_t *p_data );
+static int  GetConfiguration        ( program_data_t *p_config, int i_argc, char *ppsz_argv[],
+                                      char *ppsz_env[] );
+static void Usage                   ( void );
+static long GetIntParam             ( const char *psz_param, long i_min, long i_max, int *pi_err );
+static void InitSignalHandler       ( void );
+static void SignalHandler           ( int i_signal );
+
+/*******************************************************************************
+ * main: parse command line, start interface and spawn threads
+ *******************************************************************************
+ * Steps during program execution are:
+ *      -configuration parsing and messages interface initialization
+ *      -openning of audio output device
+ *      -execution of interface, which exit on error or on user request
+ *      -closing of audio output device
+ * On error, the spawned threads are cancelled, and the openned devices closed.
+ *******************************************************************************
+ * ?? Signal handlers should be restored to default once the interface has
+ * been closed, since they will cause a crash if the message interface is no
+ * more active.
+ *******************************************************************************/
+int main( int i_argc, char *ppsz_argv[], char *ppsz_env[] )
+{
+    program_data_t  program_data;         /* root of all data - see pgm_data.h */
+    
+    /*
+     * Read configuration, initialize messages interface and set up program
+     */
+    p_program_data = &program_data;              /* set up the global variable */
+    if( intf_InitMsg( &program_data.intf_msg ) )   /* start messages interface */
+    {
+        fprintf(stderr, "intf critical: can't initialize messages interface (%s)\n",
+                strerror(errno));
+        return(errno);
+    }
+    if( GetConfiguration( &program_data,                 /* parse command line */
+                          i_argc, ppsz_argv, ppsz_env ) )
+    {
+        intf_TerminateMsg( &program_data.intf_msg );
+        return(errno);
+    }
+    intf_MsgImm( COPYRIGHT_MESSAGE );                 /* print welcome message */
+    InitSignalHandler();                   /* prepare signals for interception */    
+
+    /*
+     * Initialize shared resources and libraries
+     */
+    if( program_data.cfg.b_vlans 
+         && input_VlanMethodInit( &program_data.input_vlan_method, 
+                                  program_data.cfg.psz_input_vlan_server,
+                                  program_data.cfg.i_input_vlan_server_port) )
+    {
+        /* On error during vlans initialization, switch of vlans */
+        intf_Msg("intf: switching off vlans\n");
+        program_data.cfg.b_vlans = 0;
+    }
+    
+    /*
+     * Open audio device
+     */
+    if( program_data.cfg.b_audio )
+    {                                                
+        if( aout_Open( &program_data.aout_thread ) )
+        {
+            /* On error during audio initialization, switch of audio */
+            intf_Msg("intf: switching off audio\n");
+            program_data.cfg.b_audio = 0;
+        }
+        else if( aout_SpawnThread( &program_data.aout_thread ) )
+        {
+            aout_Close( &program_data.aout_thread );
+            input_VlanMethodFree( &program_data.input_vlan_method );            
+            intf_TerminateMsg( &program_data.intf_msg );
+            return( -1 );
+        }
+        program_data.intf_thread.p_aout = &program_data.aout_thread;
+    }
+    
+    /*
+     * Run interface
+     */
+    intf_DbgMsg("intf debug: starting interface\n");
+    intf_Run( &program_data.intf_thread );
+    intf_DbgMsg("intf debug: interface terminated\n");
+
+    /* 
+     * Close audio device
+     */
+    if( program_data.cfg.b_audio )                 
+    {
+        aout_CancelThread( &program_data.aout_thread );
+        aout_Close( &program_data.aout_thread );
+    }    
+
+    /*
+     * Free shared resources and libraries
+     */
+    if( program_data.cfg.b_vlans )
+    {        
+        input_VlanMethodFree( &program_data.input_vlan_method );    
+    }    
+
+    /*
+     * Terminate messages interface and program
+     */
+    intf_Msg( "program terminated.\n" );
+    intf_TerminateMsg( &program_data.intf_msg );
+    return( 0 );
+}
+
+/* following functions are local */
+
+/*******************************************************************************
+ * SetDefaultConfiguration: set default options
+ *******************************************************************************
+ * This function is called by GetConfiguration before command line is parsed.
+ * It sets all the default values required later by the program. Note that
+ * all properties must be initialized, wether they are command-line dependant
+ * or not.
+ *******************************************************************************/
+static void SetDefaultConfiguration( program_data_t *p_data )
+{
+    /*
+     * Audio output thread configuration 
+     */
+    p_data->cfg.b_audio = 1;                 /* audio is activated by default */
+    p_data->aout_thread.dsp.psz_device = AOUT_DEFAULT_DEVICE;
+    /* je rajouterai la détection des formats supportés quand le reste
+     * marchera */
+    p_data->aout_thread.dsp.i_format = AOUT_DEFAULT_FORMAT;
+    p_data->aout_thread.dsp.b_stereo = AOUT_DEFAULT_STEREO;
+    p_data->aout_thread.dsp.l_rate = AOUT_DEFAULT_RATE;
+
+    /*
+     * Interface thread configuration
+     */
+    /* X11 console */
+    p_data->intf_thread.xconsole.psz_display = NULL;
+    p_data->intf_thread.xconsole.psz_geometry =        INTF_XCONSOLE_GEOMETRY;
+
+    /* --- ?? following are ok */
+
+    /*
+     * Video output thread configuration
+     */
+    p_data->cfg.b_video =                   1;    
+    p_data->vout_cfg.i_properties =         0;
+
+    /* VLAN management */
+    p_data->cfg.b_vlans =                   1;    
+    p_data->cfg.psz_input_vlan_server =     VLAN_DEFAULT_SERVER;
+    p_data->cfg.i_input_vlan_server_port =  VLAN_DEFAULT_SERVER_PORT;    
+}
+
+/*******************************************************************************
+ * GetConfiguration: parse command line
+ *******************************************************************************
+ * Parse command line and configuration file for configuration. If the inline
+ * help is requested, the function Usage() is called and the function returns
+ * -1 (causing main() to exit). Note than messages interface is initialized at
+ * this stage.
+ *******************************************************************************/
+static int GetConfiguration( program_data_t *p_data, int i_argc, 
+                             char *ppsz_argv[], char *ppsz_env[] )
+{
+    int c, i_err;
+
+    /* Set default configuration and copy arguments */
+    p_data->i_argc = i_argc;
+    p_data->ppsz_argv = ppsz_argv;
+    p_data->ppsz_env = ppsz_env;    
+    SetDefaultConfiguration( p_data );
+
+    /* Parse command line */
+    opterr = 0;
+    while( ( c = getopt_long( i_argc, ppsz_argv, psz_shortopts, longopts, 0 ) ) != EOF )
+    {
+        switch( c )
+        {
+        /* General/common options */   
+        case 'h':                                                /* -h, --help */
+            Usage();
+            return( -1 );
+            break;
+        case OPT_DISPLAY:                                         /* --display */
+            p_data->vout_cfg.psz_display = optarg;
+            p_data->vout_cfg.i_properties |= VIDEO_CFG_DISPLAY;
+            p_data->intf_thread.xconsole.psz_display = optarg;
+            break;
+
+        /* Interface options */
+        case OPT_CONSOLE_DISPLAY:                         /* --console-display */
+            p_data->intf_thread.xconsole.psz_display = optarg;
+            break;
+        case OPT_CONSOLE_GEOMETRY:                       /* --console-geometry */
+            p_data->intf_thread.xconsole.psz_geometry = optarg;
+            break;
+
+        /* Audio options */
+        case OPT_NOAUDIO:                                        /* --noaudio */
+           p_data->cfg.b_audio = 0;
+            break;
+        case OPT_STEREO:                                          /* --stereo */
+           p_data->aout_thread.dsp.b_stereo = 1;
+            break;
+        case OPT_MONO:                                              /* --mono */
+           p_data->aout_thread.dsp.b_stereo = 0;
+            break;
+        case OPT_RATE:                                              /* --rate */
+           p_data->aout_thread.dsp.l_rate = GetIntParam(optarg, AOUT_MIN_RATE, AOUT_MAX_RATE, &i_err );
+            if( i_err )
+            {
+                return( EINVAL );
+           }
+            break;
+
+        /* Video options */
+        case OPT_NOVIDEO:                                         /* --novideo */
+            p_data->cfg.b_video = 0;
+            break;       
+        case OPT_XSHM:                                               /* --xshm */
+            p_data->vout_cfg.b_shm_ext = 1;
+            p_data->vout_cfg.i_properties |= VIDEO_CFG_SHM_EXT;
+            break;
+        case OPT_NOXSHM:                                           /* --noxshm */
+            p_data->vout_cfg.b_shm_ext = 0;
+            p_data->vout_cfg.i_properties |= VIDEO_CFG_SHM_EXT;
+            break;
+
+        /* VLAN management options */
+        case OPT_NOVLANS:                                         /* --novlans */
+            p_data->cfg.b_vlans = 0;
+            break;
+        case  OPT_VLAN_SERVER:                                 /* --vlanserver */
+            p_data->cfg.i_input_vlan_server_port = ServerPort( optarg );
+            p_data->cfg.psz_input_vlan_server = optarg;
+            break;
+           
+        /* Internal error: unknown option */
+        case '?':                          
+        default:
+            intf_ErrMsg("intf error: unknown option '%s'\n", ppsz_argv[optind - 1]);
+            return( EINVAL );
+            break;
+        }
+    }
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * Usage: print program usage
+ *******************************************************************************
+ * Print a short inline help. Message interface is initialized at this stage.
+ *******************************************************************************/
+static void Usage( void )
+{
+    intf_Msg(COPYRIGHT_MESSAGE);
+    /* General options */
+    intf_Msg("usage: vlc [options...]\n" \
+             "  -h, --help                      print usage\n" \
+             );
+    /* Audio options */
+    intf_Msg("  --noaudio                       disable audio\n" \
+             "  --stereo                        enable stereo\n" \
+             "  --mono                          disable stereo\n"
+             "  --rate <rate>                   audio output rate (kHz)\n" \
+             );
+    /* Video options */
+    intf_Msg("  --novideo                       disable video\n" \
+             "  --xshm, --noxshm                enable/disable use of XShm extension\n" \
+             "  -d, --display <display>         set display name\n" \
+             );
+
+    /* VLAN management options */
+    intf_Msg("  --novlans                              disable vlans\n" \
+             "  --vlanserver <server[:port]>   set vlan server address\n" \
+             );
+}
+
+/*******************************************************************************
+ * GetIntParam: convert a string to an integer
+ *******************************************************************************
+ * This function convert a string to an integer, check range of the value
+ * and that there is no error during convertion. pi_err is a pointer to an
+ * error flag, which contains non 0 on error. This function prints its own
+ * error messages.
+ *******************************************************************************/
+static long GetIntParam( const char *psz_param, long i_min, long i_max, int *pi_err )
+{
+    char *psz_endptr;
+    long int i_value;
+
+    i_value = strtol( psz_param, &psz_endptr, 0 );
+    if( (psz_param[0] == '\0') && (*psz_endptr != '\0') )  /* conversion error */
+    {
+        intf_ErrMsg("intf error: conversion error ('%s' should be an integer between %ld and %ld)\n",
+                    psz_param, i_min, i_max );
+        *pi_err = EINVAL;
+        return( 0 );
+    }
+    if( (i_value < i_min) || (i_value > i_max) )                /* range error */
+    {
+        intf_ErrMsg("intf error: range error ('%s' should be an integer between %ld and %ld)\n",
+                    psz_param, i_min, i_max );
+        *pi_err = EINVAL;
+        return( 0 );
+    }
+    *pi_err = 0;
+    return( i_value );
+}
+/*******************************************************************************
+ * InitSignalHandler: system signal handler initialization
+ *******************************************************************************
+ * Set the signal handlers. SIGTERM is not intercepted, because we need at
+ * at least a method to kill the program when all other methods failed, and 
+ * when we don't want to use SIGKILL.
+ *******************************************************************************/
+static void InitSignalHandler( void )
+{
+  /* Termination signals */
+  signal( SIGHUP, SignalHandler );
+  signal( SIGINT, SignalHandler );
+  signal( SIGQUIT, SignalHandler );
+}
+
+/*******************************************************************************
+ * SignalHandler: system signal handler
+ *******************************************************************************
+ * This function is called when a signal is received by the program. It ignores
+ * it or tries to terminate cleanly.
+ *******************************************************************************/
+static void SignalHandler( int i_signal )
+{
+  /* Once a signal has been trapped, the signal handler needs to be re-armed on
+   * linux systems */
+  signal( i_signal, SignalHandler );
+
+  /* Acknowledge the signal received */
+  intf_ErrMsgImm("intf: signal %d received\n", i_signal );
+
+  /* Try to terminate everything */
+  /* ?? this probably needs to be changed */
+ /*  p_program_data->intf_thread.b_die = 1; */
+}
diff --git a/src/interface/xconsole.c b/src/interface/xconsole.c
new file mode 100644 (file)
index 0000000..914206a
--- /dev/null
@@ -0,0 +1,785 @@
+/*******************************************************************************
+ * xconsole.c: X11 console for interface
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * The X11 console is a simple way to get interactive input from the user. It
+ * does not disturbs the standard terminal output. In theory, multiple consoles
+ * could be opened on different displays.
+ * The console has 2 parts: a multi-line display on top, able to display
+ * messages, and an input line at bottom. 
+ *******************************************************************************/
+
+/* ?? todo: background color, color lines, pixmaps in front of line, 
+ * multiple fonts, control-like implementation, mouse management (copy/paste)... 
+ * add a scroller to text zone
+ * It will probably be better to use something esier to use than plain xlib.
+ *
+ * There is no prompt yet since a pixmap would be sexier as a text prompt :) 
+ * issue beeps (using audio_output, of course) on 'errors' (eol, end of history
+ * browse, ...) 
+ * remove dups from history */
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <keysym.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/xpm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "xutils.h"
+
+#include "xconsole.h"
+#include "intf_msg.h"
+#include "intf_cmd.h"
+
+/*
+ * Local prototypes
+ */
+static int  CreateXConsoleWindow        ( xconsole_t *p_console );
+static void DestroyXConsoleWindow       ( xconsole_t *p_console );
+static void PrintXConsoleLine           ( xconsole_t *p_console, int i_line,
+                                          char *psz_str );
+static void RedrawXConsoleText          ( xconsole_t *p_console );
+static void RedrawXConsoleEdit          ( xconsole_t *p_console );
+static void RegisterXConsoleHistory     ( xconsole_t *p_console );
+static void RecallXConsoleHistory       ( xconsole_t *p_console, int i_index );
+
+/*******************************************************************************
+ * intf_OpenXConsole: open console
+ *******************************************************************************
+ * This function try to open an X11 display and create a simple text window.
+ * It returns 0 on success.
+ *******************************************************************************/
+int intf_OpenXConsole( xconsole_t *p_console )
+{
+    int i_index;                                         /* all purposes index */
+
+    /* Open display */
+    p_console->psz_display = XDisplayName( p_console->psz_display );
+    p_console->p_display = XOpenDisplay( p_console->psz_display ); 
+    if( !p_console->p_display )                                       /* error */
+    {
+        intf_ErrMsg("intf error: can't open display %s\n", 
+                    p_console->psz_display );
+        return( -1 );               
+    }
+
+    /* Get the screen number */
+    p_console->i_screen = DefaultScreen( p_console->p_display );    
+   
+    /* Create window and set console attributes */
+    if( CreateXConsoleWindow( p_console ) )                           /* error */
+    {
+        XFreeFont( p_console->p_display, p_console->p_font );     /* free font */
+        XCloseDisplay( p_console->p_display );                /* close display */
+        return( 1 );                                      /* return error code */
+    }
+
+    /* Set all text as empty */
+    for( i_index = 0; i_index < INTF_XCONSOLE_MAX_LINES; i_index++ )
+    {
+        p_console->psz_text[i_index] = 0;                    /* clear the line */
+    }
+    p_console->i_text_index = 0;
+
+    /* Clear edit line and history */
+    for( i_index = 0; i_index < INTF_XCONSOLE_HISTORY_SIZE; i_index++ )
+    {
+        p_console->psz_history[i_index] = 0;                  /* clear the line */
+    }
+    p_console->i_history_index = 0;
+    p_console->i_history_base = 0;
+    p_console->i_edit_index = 0;
+    p_console->i_edit_size = 0;
+
+    /* Print welcome message */
+    intf_PrintXConsole( p_console, INTF_XCONSOLE_WELCOME_MSG );
+
+    intf_DbgMsg("intf debug: X11 console %p opened\n", p_console );
+    return( 0 );
+}
+
+/*******************************************************************************
+ * intf_CloseXConsole: close console                                     (ok ?)
+ *******************************************************************************
+ * Destroy a console window and close the display. This function should be
+ * called when the interface thread ends, and any further access to this console
+ * is vorbidden.
+ *******************************************************************************/
+void intf_CloseXConsole( xconsole_t *p_console )
+{
+    int i_index;                                         /* all purposes index */   
+
+    /* Destroy history */
+    for( i_index = 0; i_index < INTF_XCONSOLE_HISTORY_SIZE; i_index++ )
+    {
+        if( p_console->psz_history[i_index] )      /* history line isn't empty */
+        {
+            free( p_console->psz_history[i_index] );          /* free the line */
+        }
+    }
+
+    /* Destroy text */
+    for( i_index = 0; i_index < INTF_XCONSOLE_MAX_LINES; i_index++ )
+    {
+        if( p_console->psz_text[i_index] )                 /* line isn't empty */
+        {
+            free( p_console->psz_text[i_index] );             /* free the line */
+        }
+    }
+
+    /* Destroy window and resources */
+    DestroyXConsoleWindow( p_console ); 
+
+    /* Close display */
+    XSetErrorHandler( NULL );              /* ?? reset non-fatal error handler */
+    XSetIOErrorHandler( NULL );                   /* ?? reset IO error handler */
+    XCloseDisplay( p_console->p_display );                    /* close display */
+
+    intf_DbgMsg("intf debug: X11 console %p closed\n", p_console );    
+}
+
+/*******************************************************************************
+ * intf_ManageXConsole: manage X11 console events
+ *******************************************************************************
+ * This function manage events received in X11 console such as key pressed,
+ * messages received, and so on... It also run commands typed in the console.
+ *******************************************************************************/
+void intf_ManageXConsole( xconsole_t *p_console )
+{
+    XEvent  x_event;                                          /* generic event */
+    char    x_char;                                /* keyboard event character */
+    KeySym  keysym;                                                  /* keysym */
+    int     i_index;                                    /* multi purpose index */
+    int         i_next_index;                            /* next history index */   
+
+    /* If window has been resized, update attributes and redraw */
+    while( XCheckTypedEvent( p_console->p_display, ConfigureNotify, &x_event ) )
+    {
+        /* ?? check if move, expose or resize !!! */
+        p_console->i_width = ((XConfigureEvent *) &x_event)->width;
+        p_console->i_height = ((XConfigureEvent *) &x_event)->height;
+        RedrawXConsoleText( p_console );        
+        RedrawXConsoleEdit( p_console );
+        XFlush( p_console->p_display );
+    }
+
+    /* Check keyboard events */
+    while( XCheckTypedEvent( p_console->p_display, KeyPress, &x_event ) )
+    {
+        /* If XLookupString returns a single character, the key is probably an
+         * ascii character and is added to the edit line, except if it is a
+         * control character (ascii < 32) or DEL (ascii = 127) */
+        if( (XLookupString( (XKeyEvent *) &x_event, &x_char, 1, &keysym, 0 ) == 1) 
+            && ( x_char >= 32 ) && ( x_char != 127 ) )
+        {
+            /* Increase line size, except if maximal size has been reached */
+            if( ++p_console->i_edit_size > INTF_XCONSOLE_MAX_LINE_WIDTH )
+            {
+                /* Maximal size reached */
+                p_console->i_edit_size = INTF_XCONSOLE_MAX_LINE_WIDTH;
+            }
+
+            /* Shift right line from cursor position to the end of line */
+            for( i_index = p_console->i_edit_size - 1; i_index > p_console->i_edit_index; i_index-- )
+            {
+                p_console->sz_edit[i_index] = p_console->sz_edit[i_index - 1];                
+            }
+
+            /* Insert character at the cursor position and increase cursor position, 
+             * except if the cursor reached the right of the line */
+            if( p_console->i_edit_index < INTF_XCONSOLE_MAX_LINE_WIDTH )
+            {
+                p_console->sz_edit[p_console->i_edit_index++] = x_char;
+            }
+        }
+        /* Else, the key is a control key, and keysym is used to process it. */
+        else
+        {
+            switch( keysym )
+            {
+            /* Cursor position modification */
+            case XK_Delete:                                          /* Delete */ 
+                /* Delete has effect only if cursor is not at the end of the line */
+                if( p_console->i_edit_index < p_console->i_edit_size )
+                {
+                    /* Shift left line from cursor position (excluded) to the end of line */
+                    for( i_index = p_console->i_edit_index + 1; i_index < p_console->i_edit_size; i_index++ )
+                    {
+                        p_console->sz_edit[i_index - 1] = p_console->sz_edit[i_index];
+                    }
+                    /* Decrease line size */
+                    p_console->i_edit_size--;
+                }
+                break;             
+            case XK_BackSpace:                                    /* BackSpace */
+                 /* BackSpace has effect only if cursor is not at the beginning of the line */
+                if( p_console->i_edit_index > 0 )
+                {
+                    /* Shift left line from cursor position (included) to the end of line */
+                    for( i_index = p_console->i_edit_index; i_index < p_console->i_edit_size; i_index++ )
+                    {
+                        p_console->sz_edit[i_index - 1] = p_console->sz_edit[i_index];
+                    }
+                    /* Decrease line size and cursor position */
+                    p_console->i_edit_size--;
+                    p_console->i_edit_index--;
+                }
+                break;
+            case XK_Left:                                              /* Left */
+                /* Move left */
+                if( --p_console->i_edit_index < 0 )
+                {
+                    p_console->i_edit_index = 0;
+                }
+                break;  
+            case XK_Right:                                            /* Right */
+                /* Move right */
+                if( ++p_console->i_edit_index > p_console->i_edit_size )
+                {
+                    p_console->i_edit_index = p_console->i_edit_size;
+                }
+                break;
+            case XK_Home:                                              /* Home */
+                /* Go to beginning of line */
+                p_console->i_edit_index = 0;
+                break;
+            case XK_End:                                                /* End */
+                /* Go to end of line */
+                p_console->i_edit_index = p_console->i_edit_size;
+                break;
+
+            /* Clear */
+            case XK_Escape:
+                /* Clear line and reset history index and record */
+                p_console->i_edit_index = 0;
+                p_console->i_edit_size = 0;
+                p_console->i_history_index = p_console->i_history_base;
+                break;
+
+            /* History */
+            case XK_Up:
+                /* If history index is history base, this marks the beginning of
+                 * an history browse, and the current edited line needs to be
+                 * registered */
+                if( p_console->i_history_index == p_console->i_history_base )
+                {
+                    RegisterXConsoleHistory( p_console );
+                }
+
+                /* Calculate next index */
+                if( p_console->i_history_index )
+                {
+                    i_next_index = p_console->i_history_index - 1;
+                }
+                else                        
+                {
+                    i_next_index = INTF_XCONSOLE_HISTORY_SIZE - 1;
+                }
+
+                /* Go to previous record, if it not blank and a loop hasn't been made */
+                if( (i_next_index != p_console->i_history_base) && p_console->psz_history[i_next_index] )
+                {
+                    RecallXConsoleHistory( p_console, i_next_index );
+                }
+                break;                
+            case XK_Down:
+                /* If history index is not history base, Calculate next index */
+                if( p_console->i_history_index != p_console->i_history_base )
+                {
+                    /* Calculate next index */
+                    if( p_console->i_history_index == INTF_XCONSOLE_HISTORY_SIZE - 1 )
+                    {
+                        i_next_index = 0;
+                    }
+                    else
+                    {
+                        i_next_index = p_console->i_history_index + 1;
+                    }
+
+                    /* Go to previous record */
+                    RecallXConsoleHistory( p_console, i_next_index ); 
+                }
+                break;            
+
+            /* Command execution */
+            case XK_Return:
+                /* Command is executed only if line is not empty */
+                if( p_console->i_edit_size )
+                {                    
+                    /* Add current line to history and increase history base. The
+                     * called function also add a terminal '\0' to sz_edit, which
+                     * allow to send it without furhter modification to execution. */
+                    RegisterXConsoleHistory( p_console );
+                    if( ++p_console->i_history_base > INTF_XCONSOLE_HISTORY_SIZE )
+                    {
+                        p_console->i_history_base = 0;
+                    }
+
+                    /* Execute command */
+                    intf_ExecCommand( p_console->sz_edit );
+
+                    /* Clear line and reset history index */
+                    p_console->i_edit_index = 0;
+                    p_console->i_edit_size = 0;
+                    p_console->i_history_index = p_console->i_history_base;
+                }
+                break;
+            }
+        }
+
+        /* Redraw edit zone */
+        RedrawXConsoleEdit( p_console );
+        XFlush( p_console->p_display );
+    }
+}
+
+/*******************************************************************************
+ * intf_ClearXConsole: clear all lines in an X11 console               (ok ?)
+ *******************************************************************************
+ * This function clears a X11 console, destroying all its text. The input
+ * line is not reset.
+ *******************************************************************************/
+void intf_ClearXConsole( xconsole_t *p_console )
+{
+    int i_index;                                         /* all purposes index */   
+
+    /* Clear all lines - once a line has been cleared, it's data pointer
+     * must be set to 0 to avoid clearing it again. */
+    for( i_index = 0; i_index < INTF_XCONSOLE_MAX_LINES; i_index++ )
+    {
+        if( p_console->psz_text[i_index] )          
+        {
+            free( p_console->psz_text[i_index] );
+            p_console->psz_text[i_index] = 0;
+        }
+    }
+
+    /* Redraw the window */
+    RedrawXConsoleText( p_console );
+    XFlush( p_console->p_display );
+}
+
+/*******************************************************************************
+ * intf_PrintXConsole: print a message in a X11 console                (ok ?)
+ *******************************************************************************
+ * This function print a message in an X11 console window. On error, it prints
+ * an error message and exit. The message is processed and copied and can be 
+ * freed once the function has been called. During processing, the string can
+ * be splitted in several lines if '\n' are encountered or if a line is larger
+ * than INTF_XCONSOLE_MAX_LINE_WIDTH. A trailing '\n' will be ignored.
+ *******************************************************************************/
+void intf_PrintXConsole( xconsole_t *p_console, char *psz_str )
+{
+    char        sz_line_buffer[INTF_XCONSOLE_MAX_LINE_WIDTH + 1];    /* buffer */
+    char *      psz_index;                                     /* string index */
+    int         i_char_index;                            /* char index in line */
+    boolean_t   b_eol;                                     /* end of line flag */  
+
+    /* Split the string in different lines. A line */
+    i_char_index = 0;                                           /* character 0 */
+    b_eol = 0;
+    for( psz_index = psz_str; *psz_index; psz_index++ )
+    {
+        /* Check if we reached an end of line: if the current character is
+         * really an end of line or if the next one marks the end of the
+         * string. An end of line at then end of a string will be only
+         * processed once. */
+        if( (*psz_index == '\n') || ( psz_index[1] == '\0' ) )
+        {
+            b_eol = 1;            
+        }
+        /* Else, the current character is added at the end of the line buffer */
+        {
+            sz_line_buffer[ i_char_index++ ] = *psz_index;
+            /* If we reached the maximal size for the line buffer, we also need
+             * to treat the end of line */
+            if( i_char_index == INTF_XCONSOLE_MAX_LINE_WIDTH )
+            {
+                b_eol = 1;
+            }
+        }
+
+        /* Check if an end of line has been detected */
+        if( b_eol )
+        {
+            /* Add a '\0' character at the end of the line buffer - note that
+             * i_char_index is now the line size + 1. */
+            sz_line_buffer[ i_char_index++ ] = '\0';
+
+            /* Increase line index */
+            if( p_console->i_text_index++                              /* loop */
+                == INTF_XCONSOLE_MAX_LINES )
+            {
+                p_console->i_text_index = 0;
+            }
+            /* If there was a line in the current location, it needs to be 
+             * erased. It will disappear from the console messages history. */
+            if( p_console->psz_text[ p_console->i_text_index] )  
+            {
+                free( p_console->psz_text[ p_console->i_text_index] );
+            }
+
+            /* Allocate memory in lines array, and copy string - a memcpy is used
+             * since the size of the string is already known. On error during the 
+             * allocation (ENOMEM), the line is set as blank and an error message 
+             * is issued. */
+            p_console->psz_text[ p_console->i_text_index ]
+                = (char *) malloc( sizeof(char) * i_char_index );
+            if( !p_console->psz_text[ p_console->i_text_index] )      /* error */
+            {
+                intf_ErrMsg("intf error: can't copy `%s' (%s)\n", 
+                            sz_line_buffer, strerror(ENOMEM) );
+                p_console->psz_text[ p_console->i_text_index] = 0;
+            }
+            else                                                    /* success */
+            {
+                memcpy( p_console->psz_text[ p_console->i_text_index ], 
+                        sz_line_buffer, i_char_index );
+            }
+
+            /* Reset end of line indicator and char index */
+            b_eol = 0;
+            i_char_index = 0;
+        }
+    }
+
+    /* Display lines */
+    /* ?? now: redraw... would be better to scroll and draw only the good one */
+    RedrawXConsoleText( p_console );
+    XFlush( p_console->p_display );
+}
+
+/* following functions are local */
+
+/*******************************************************************************
+ * RegisterXConsoleHistory: send current edit line to history          (ok ?)
+ *******************************************************************************
+ * This function add a '\0' termination character at the end of the editing
+ * buffer, and copy it to the actual history base. If there is already a
+ * command registered at this place, it is removed.
+ * Note that the base index isn't updated.
+ * If there is not enough memory to allocate history record, the previous
+ * record is still cleared, and the new one is left blank. Note that this will
+ * probably cause problems when the history will be browsed, since the line
+ * won't be able to be recovered.
+ *******************************************************************************/
+static void RegisterXConsoleHistory( xconsole_t *p_console )
+{
+    /* Add a null character to mark the end of the edit line */
+    p_console->sz_edit[p_console->i_edit_size] = '\0';
+
+    /* Free current history record if required */
+    if(  p_console->psz_history[p_console->i_history_base] )
+    {
+        free( p_console->psz_history[p_console->i_history_base] );
+    }
+
+    /* Allocate memory */
+    p_console->psz_history[p_console->i_history_base] 
+        = (char *) malloc( sizeof(char) * ( p_console->i_edit_size + 1 ) );
+
+    /* On error, an error message is issued and the line is left blank */
+    if(  !p_console->psz_history[p_console->i_history_base] )         /* error */
+    {
+        intf_ErrMsg("intf error: can't register `%s' in history\n", p_console->sz_edit );
+        p_console->psz_history[p_console->i_history_base] = 0;            
+    }
+    /* If the allocation succeeded, the line is copied. Memcpy is used rather than
+     * strcpy because the line size is already known. */
+    else
+    {
+        memcpy( p_console->psz_history[p_console->i_history_base], 
+                p_console->sz_edit, p_console->i_edit_size + 1 );                
+    }
+}
+
+/*******************************************************************************
+ * RecallXConsoleHistory: copy an historic record to edit line         (ok ?)
+ *******************************************************************************
+ * This function copy an historic record to edit line and update historic 
+ * index.
+ *******************************************************************************/
+static void RecallXConsoleHistory( xconsole_t *p_console, int i_index )
+{
+    /* Calculate line size and copy it to edit line */
+    for( p_console->i_edit_size = 0; 
+         p_console->psz_history[i_index][p_console->i_edit_size];
+         p_console->i_edit_size++ )
+    {
+        p_console->sz_edit[p_console->i_edit_size] = p_console->psz_history[i_index][p_console->i_edit_size];
+    }
+
+    /* Update indexes */
+    p_console->i_history_index = i_index;
+    p_console->i_edit_index = p_console->i_edit_size;
+}
+
+
+/*******************************************************************************
+ * CreateXConsoleWindow: open and set-up X11 console window            (ok ?)
+ *******************************************************************************
+ * This function tries to create and set up a console window. This window is
+ * resizeable.
+  *******************************************************************************/
+static int CreateXConsoleWindow( xconsole_t *p_console )
+{
+    XSizeHints              xsize_hints;
+    XSetWindowAttributes    xwindow_attributes;
+    XGCValues               xgcvalues;
+    int                     i_dummy;                    /* dummy for geometry */ 
+
+    /* Load first (edit) font - some of its properties are required to adapt
+     * window properties */
+    if( XTryLoadFont( p_console->p_display, INTF_XCONSOLE_FONT, &p_console->p_font ) )
+    {
+        return( 1 );
+    }  
+
+    /* Set size related console properties */
+    p_console->i_edit_height =      p_console->p_font->ascent + p_console->p_font->descent + 5;
+    p_console->i_edit_offset =      p_console->p_font->descent + 2;
+    p_console->i_text_offset =      p_console->i_edit_height + p_console->p_font->descent;
+    p_console->i_text_line_height = p_console->p_font->ascent + p_console->p_font->descent;
+
+    /* Read window geometry and prepare size hints */
+    XParseGeometry( p_console->psz_geometry,
+                    &i_dummy, &i_dummy, &p_console->i_width, &p_console->i_height );
+    xsize_hints.base_width = p_console->i_height;
+    xsize_hints.base_height = p_console->i_width;
+    xsize_hints.min_width =  p_console->i_text_line_height;
+    xsize_hints.min_height =  p_console->i_text_line_height;
+    xsize_hints.flags = PSize | PMinSize;
+
+    /* Prepare the window attributes */
+    xwindow_attributes.backing_store =      Always;
+    xwindow_attributes.background_pixel =   WhitePixel( p_console->p_display, p_console->i_screen );
+    xwindow_attributes.event_mask =         KeyPressMask | StructureNotifyMask;
+
+    /* Create the window */
+    p_console->window = XCreateWindow( p_console->p_display,
+                                       DefaultRootWindow( p_console->p_display ),
+                                       0, 0, p_console->i_width, p_console->i_height, 0,
+                                       CopyFromParent, InputOutput, CopyFromParent,
+                                       CWBackingStore | CWBackPixel | CWEventMask,
+                                       &xwindow_attributes );
+    XSetWMNormalHints( p_console->p_display, p_console->window, &xsize_hints );
+    XStoreName(p_console->p_display, p_console->window, INTF_XCONSOLE_TITLE);
+    
+    /* Creation of a graphic contexts */
+    xgcvalues.background = WhitePixel( p_console->p_display, p_console->i_screen );
+    xgcvalues.foreground = BlackPixel( p_console->p_display, p_console->i_screen );
+    p_console->default_gc = XCreateGC( p_console->p_display, p_console->window,
+                                       GCForeground | GCBackground, 
+                                       &xgcvalues);
+
+/* ?? pixmap is always freed, etc... -> to be fixed ! */
+#ifdef INTF_XCONSOLE_BACKGROUND_PIXMAP
+    /* Load background pixmap */
+    if( XpmReadFileToPixmap( p_console->p_display,                    /* error */
+                             p_console->window, INTF_XCONSOLE_BACKGROUND_PIXMAP,
+                             &p_console->background_pixmap, NULL, NULL) )
+    {
+        intf_ErrMsg("intf error: can't load background pixmap `%s'\n", 
+                    INTF_XCONSOLE_BACKGROUND_PIXMAP );
+    }
+    else                                                            /* success */
+    {
+        /* Set window background */
+        XSetWindowBackgroundPixmap(p_console->p_display, p_console->window, 
+                                   p_console->background_pixmap );  
+    }
+#endif
+
+    /* Map the window */
+    XMapWindow( p_console->p_display, p_console->window );              /* map */
+    XFlush( p_console->p_display );
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * DestroyXConsoleWindow: destroy console window                       (ok ?)
+ *******************************************************************************
+ * Destroy a console window opened by CreateXConsoleWindow.
+ *******************************************************************************/
+static void DestroyXConsoleWindow( xconsole_t *p_console )
+{
+    /* Unmap window */
+    XUnmapWindow( p_console->p_display, p_console->window );    
+
+    /* Destroy graphic contexts */
+    XFreeGC( p_console->p_display, p_console->default_gc );
+
+    /* Free pixmaps */
+    XFreePixmap( p_console->p_display, p_console->background_pixmap );
+
+    /* Free font */
+    XFreeFont( p_console->p_display, p_console->p_font );
+
+    /* Destroy window */
+    XDestroyWindow( p_console->p_display, p_console->window );
+}
+
+/*******************************************************************************
+ * PrintXConsoleLine: print a message in console                       (ok ?)
+ *******************************************************************************
+ * Print a message in console. Line 0 is just above the edit line, and line
+ * number increase while going up. The string is scanned for control
+ * characters.
+ *******************************************************************************/
+static void PrintXConsoleLine( xconsole_t *p_console, int i_line, char *psz_str )
+{
+    XTextItem   textitem[INTF_XCONSOLE_MAX_LINE_WIDTH];                /* text */
+    int         i_nb_textitems;                   /* total number of textitems */
+
+    int         i_x;                                    /* horizontal position */
+    int         i_y;                                 /* base vertical position */
+   
+    /* If string is a null pointer, return */
+    if( !psz_str )
+    {
+        return;
+    }
+
+    /* Prepare array of textitems */
+    for( i_nb_textitems = 0; 
+         *psz_str && (i_nb_textitems < INTF_XCONSOLE_MAX_LINE_WIDTH);  
+         i_nb_textitems++ )
+    {
+        /* Check if first character is a control character - if a control
+         * character has been successfully processed, process next one */
+        while( *psz_str == '\033' )
+        {
+            psz_str++;
+            switch( *psz_str )
+            {
+            case 'g':                                                  /* bell */
+                /* ?? ring bell */
+                psz_str++;
+                break; 
+
+            case '\0':                                 /* error: end of string */
+                /* Print error message */
+                intf_ErrMsg("intf error: unauthorized control character `\\0'\n");
+                break;
+
+            default:                       /* error: unknown control character */
+                /* Print error message */
+                intf_ErrMsg("intf error: unknown control character `%c'\n", *psz_str );
+                break;
+            } 
+        }
+
+        /* Prepare textitem */
+        textitem[i_nb_textitems].chars = psz_str;
+        textitem[i_nb_textitems].nchars = 0;
+        textitem[i_nb_textitems].delta = 0;
+        textitem[i_nb_textitems].font = p_console->p_font->fid;
+
+        /* Reach next control character or end of string */
+        do
+        {
+            psz_str++;
+            textitem[i_nb_textitems].nchars++; 
+        }while( *psz_str && (*psz_str != '\033') );
+    }
+
+    /* Calulcate base position */
+    i_x = 0;
+    i_y = p_console->i_height - p_console->i_text_offset - i_line * p_console->i_text_line_height;
+
+    /* Print text */
+    XDrawText( p_console->p_display, p_console->window, p_console->default_gc, i_x, i_y,
+               textitem, i_nb_textitems );   
+}
+
+/*******************************************************************************
+ * RedrawXConsoleEdit: redraw console edit zone                        (ok ?)
+ *******************************************************************************
+ * This function redraw just the edit zone of the console. It has to be called
+ * when the window is resized.
+ *******************************************************************************/
+static void RedrawXConsoleEdit( xconsole_t *p_console )
+{
+    XTextItem   textitem;                                              /* text */
+    int         i_x;                                        /* cursor position */
+
+    /* Clear window edit zone */
+    XClearArea( p_console->p_display, p_console->window, 0,
+                p_console->i_height - p_console->i_edit_height, 
+                p_console->i_width, p_console->i_edit_height, False );
+
+    /* Draw separation line */
+    XDrawLine( p_console->p_display, p_console->window, p_console->default_gc,
+               0, p_console->i_height - p_console->i_edit_height,
+               p_console->i_width, p_console->i_height - p_console->i_edit_height );
+
+    /* Prepare text to be printed */
+    textitem.chars = p_console->sz_edit;
+    textitem.nchars = p_console->i_edit_size;
+    textitem.delta = 0;
+    textitem.font = p_console->p_font->fid;
+
+    /* Print text */
+    XDrawText( p_console->p_display, p_console->window, p_console->default_gc, 
+               0, p_console->i_height - p_console->i_edit_offset,
+               &textitem, 1 );
+
+    /* Get cursor position */
+    i_x = XTextWidth( p_console->p_font, p_console->sz_edit, p_console->i_edit_index );
+
+    /* Print cursor. Cursor is placed at the beginning of the edited character,
+     * and it's size is calculated from maximal character size in the font. */
+    XDrawLine( p_console->p_display, p_console->window, p_console->default_gc,
+               i_x, p_console->i_height - p_console->i_edit_offset - p_console->p_font->ascent,
+               i_x, p_console->i_height - p_console->i_edit_offset + p_console->p_font->descent );
+}
+
+/*******************************************************************************
+ * RedrawXConsoleText: redraw console text zone                        (ok ?)
+ *******************************************************************************
+ * This function redraw all lines in the console. It has to be called when
+ * the window is resized.
+ *******************************************************************************/
+static void RedrawXConsoleText( xconsole_t *p_console )
+{
+    int i_line;                                                /* current line */
+    int i_index;                                       /* multi-purposes index */
+    int i_maxline;                                      /* maximum line number */
+        
+    /* Clear window text zone */
+    XClearArea( p_console->p_display, p_console->window, 0, 0, p_console->i_width, 
+                p_console->i_height - p_console->i_edit_height, False );
+
+    /* Get maximum line number from window height */
+    i_maxline = (p_console->i_height - p_console->i_text_offset) 
+        / p_console->i_text_line_height;
+    
+    /* Re-print messages zone: start from first message, line 0 and print
+     * messages until top of window is reached or all lines have been printed,
+     * which happens when i_index is the start index again */
+    i_line = 0;
+    i_index = p_console->i_text_index;
+    do
+    {
+        /* Print line */
+        PrintXConsoleLine( p_console, i_line, p_console->psz_text[i_index] );
+
+        /* Decrease i_index. Note that lines must be printed in reverse order
+         * since they are added in order. */
+        if( !i_index-- )                                               /* loop */
+        {
+            i_index = INTF_XCONSOLE_MAX_LINES - 1;
+        }
+    }
+    while( (i_line++ < i_maxline) && (i_index != p_console->i_text_index) );
+}
diff --git a/src/misc/mtime.c b/src/misc/mtime.c
new file mode 100644 (file)
index 0000000..93c1b8b
--- /dev/null
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * mtime.c: high rezolution time management functions
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * Functions are prototyped in mtime.h.
+ *******************************************************************************
+ * to-do list:
+ *  see if using Linux real-time extensions is possible and profitable
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "common.h"
+#include "mtime.h"
+
+/*******************************************************************************
+ * mstrtime: return a date in a readable format
+ *******************************************************************************
+ * This functions is provided for any interface function which need to print a
+ * date. psz_buffer should be a buffer long enough to store the formatted
+ * date. 
+ *******************************************************************************/
+char *mstrtime( char *psz_buffer, mtime_t date )
+{
+    sprintf( psz_buffer, "%02d:%02d:%02d-%03d.%03d", 
+             (int) (date / (1000UL * 1000UL * 60UL * 60UL) % 24UL), 
+             (int) (date / (1000UL * 1000UL * 60UL) % 60UL),
+             (int) (date / (1000UL * 1000UL) % 60UL),
+             (int) (date / 1000UL % 1000UL),
+             (int) (date % 1000UL) );
+    return( psz_buffer );
+}
+
+/*******************************************************************************
+ * mdate: return high precision date (inline function)
+ *******************************************************************************
+ * Uses the gettimeofday() function when possible (1 MHz resolution) or the
+ * ftime() function (1 kHz resolution).
+ *******************************************************************************
+ * to-do list: ??
+ *  implement the function when gettimeofday is not available
+ *  this function should be decalred as inline
+ *******************************************************************************/
+mtime_t mdate( void ) 
+{
+    struct timeval tv_date;
+
+    /* gettimeofday() could return an error, and should be tested. However, the
+     * only possible error, according to 'man', is EFAULT, which can not happen
+     * here, since tv is a local variable. */
+    gettimeofday( &tv_date, NULL );
+    return( (mtime_t) tv_date.tv_sec * 1000000 + (mtime_t) tv_date.tv_usec );
+}
+
+/*******************************************************************************
+ * mwait: wait for a date (inline function)                            
+ *******************************************************************************
+ * This function uses select() and an system date function to wake up at a
+ * precise date. It should be used for process synchronization. If current date
+ * is posterior to wished date, the function returns immediately.
+ *******************************************************************************
+ * to-do list:
+ *  implement the function when gettimeofday is not available
+ *  optimize delay calculation
+ *  ?? declare as inline
+ *******************************************************************************/
+void mwait( mtime_t date )
+{
+    struct timeval tv_date, tv_delay;
+    s64            delay;            /* delay in msec, signed to detect errors */
+
+    /* see mdate() about gettimeofday() possible errors */
+    gettimeofday( &tv_date, NULL );
+
+    /* calculate delay and check if current date is before wished date */
+    delay = date - (mtime_t) tv_date.tv_sec * 1000000 - (mtime_t) tv_date.tv_usec;
+    if( delay < 0 )                       /* wished date is already passed */
+    {
+        return;
+    }
+    tv_delay.tv_sec = delay / 1000000;
+    tv_delay.tv_usec = delay % 1000000; 
+    
+    /* see msleep() about select() errors */
+    select( 0, NULL, NULL, NULL, &tv_delay );
+}
+
+/*******************************************************************************
+ * msleep: more precise sleep() (inline function)                        (ok ?)
+ *******************************************************************************
+ * This function uses select() in a classical way to implement a sleep() call
+ * with a microsecond precision. 
+ * For synchronization purposes, mwait() should be prefered.
+ *******************************************************************************
+ * ?? decalre as inline
+ *******************************************************************************/
+void msleep( mtime_t delay )
+{
+    struct timeval tv_delay;
+
+    tv_delay.tv_sec = delay / 1000000;
+    tv_delay.tv_usec = delay % 1000000;
+    /* select() return value should be tested, since several possible errors
+     * can occur. However, they should only happen in very particular occasions
+     * (i.e. when a signal is sent to the thread, or when memory is full), and
+     * can be ingnored. */
+    select( 0, NULL, NULL, NULL, &tv_delay );
+}
diff --git a/src/misc/netutils.c b/src/misc/netutils.c
new file mode 100644 (file)
index 0000000..d104808
--- /dev/null
@@ -0,0 +1,289 @@
+/*******************************************************************************
+ * netutils.c: various network functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * ?? 
+ *******************************************************************************
+ * Required headers:
+ * <netinet/in.h>
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <netdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+
+#include "intf_msg.h"
+#include "debug.h"
+
+#include "netutils.h"
+
+/*******************************************************************************
+ * BuildInetAddr: build an Internet address descriptor
+ *******************************************************************************
+ * Build an internet socket descriptor from a host name, or an ip, and a port.
+ * If the address is NULL, then INADDR_ANY will be used, allowing to receive
+ * on any address for a local socket. Usually, in this case, 'port' is also null
+ * and the function always succeeds.
+ *******************************************************************************/
+int BuildInetAddr( struct sockaddr_in *p_sa_in, char *psz_in_addr, int i_port )
+{
+    struct hostent *p_hostent;                              /* host descriptor */
+
+    bzero( p_sa_in, sizeof( struct sockaddr_in ) );
+    p_sa_in->sin_family = AF_INET;                                   /* family */
+    p_sa_in->sin_port = htons( i_port );                               /* port */
+
+    /* Use INADDR_ANY if psz_in_addr is NULL */
+    if( psz_in_addr == NULL )
+    {        
+        p_sa_in->sin_addr.s_addr = htonl(INADDR_ANY);
+    }    
+    /* Try to convert address directly from in_addr - this will work if 
+     * psz_in_addr is dotted decimal. */    
+    else if( !inet_aton( psz_in_addr, &p_sa_in->sin_addr) )
+    {
+        /* The convertion failed: the address is an host name, which needs
+         * to be resolved */
+        intf_DbgMsg("debug: resolving internet address %s...\n", psz_in_addr);        
+        if ( (p_hostent = gethostbyname(psz_in_addr)) == NULL)
+        {
+            intf_ErrMsg("error: unknown host %s\n", psz_in_addr);
+            return( -1 );            
+        }
+
+        /* Copy the first address of the host in the socket address */
+        bcopy( p_hostent->h_addr_list[0], &p_sa_in->sin_addr, p_hostent->h_length);
+    }
+    return( 0 );    
+}
+
+
+/*******************************************************************************
+ * ServerPort: extract port from a "server:port" adress
+ *******************************************************************************
+ * Returns the port number in a "server:port" address and replace the ":" by
+ * a NUL character, or returns -1.
+ *******************************************************************************/
+int ServerPort( char *psz_addr )
+{
+    char *psz_index;
+
+    /* Scan string for ':' */
+    for( psz_index = psz_addr; *psz_index && (*psz_index != ':'); psz_index++ )
+    {
+        ;
+    }        
+    
+    /* If a port number has been found, convert it and return it */
+    if( *psz_index == ':' )
+    {
+        *psz_index = '\0';
+        return( atoi( psz_index + 1 ) );
+    }
+
+    return( - 1 );    
+}
+
+
+/*******************************************************************************
+ * ReadIfConf: Read the configuration of an interface
+ *******************************************************************************
+ * i_sockfd must reference a socket open as follow: AF_INET, DOCK_DGRAM, 0
+ *******************************************************************************/
+int ReadIfConf(int i_sockfd, if_descr_t* p_ifdescr, char* psz_name)
+{             
+    struct ifreq ifr_config;
+    int i_rc = 0;
+  
+    ASSERT(p_ifdescr);
+    ASSERT(psz_name);
+  
+    /* Which interface are we interested in ? */
+    strcpy(ifr_config.ifr_name, psz_name);
+
+    /* Read the flags for that interface */
+    i_rc = ioctl(i_sockfd, SIOCGIFFLAGS, (byte_t *)&ifr_config);
+    if( !i_rc )
+    {
+        p_ifdescr->i_flags = ifr_config.ifr_flags;
+        intf_DbgMsg("%s flags: 0x%x\n", psz_name, p_ifdescr->i_flags);
+    }
+    else
+    {
+        intf_ErrMsg("Cannot read flags for interface %s: %s\n", psz_name,
+                    strerror(errno));
+        return -1;
+    }
+
+    /* Read physical address of the interface and store it in our description */
+    i_rc = ioctl(i_sockfd, SIOCGIFHWADDR, (byte_t *)&ifr_config);
+    if( !i_rc )
+    {
+        memcpy(&p_ifdescr->sa_phys_addr, &ifr_config.ifr_addr, sizeof(struct sockaddr));
+        intf_DbgMsg("%s MAC address: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", psz_name,
+                    p_ifdescr->sa_phys_addr.sa_data[0]&0xff,
+                    p_ifdescr->sa_phys_addr.sa_data[1]&0xff,
+                    p_ifdescr->sa_phys_addr.sa_data[2]&0xff,
+                    p_ifdescr->sa_phys_addr.sa_data[3]&0xff,
+                    p_ifdescr->sa_phys_addr.sa_data[4]&0xff,
+                    p_ifdescr->sa_phys_addr.sa_data[5]&0xff);
+    }
+    else
+    {
+        intf_ErrMsg("Cannot read hardware address for interface %s: %s\n",
+                    psz_name, strerror(errno));
+        return -1;
+    }
+  
+    /* Read IP address of the interface and store it in our description */
+    i_rc = ioctl(i_sockfd, SIOCGIFADDR, (byte_t *)&ifr_config);
+    if( !i_rc )
+    {
+        memcpy(&p_ifdescr->sa_net_addr, &ifr_config.ifr_addr, sizeof(struct sockaddr));
+        intf_DbgMsg("%s IP address: %s\n", psz_name,
+                    inet_ntoa(p_ifdescr->sa_net_addr.sin_addr));
+    }
+    else
+    {
+        intf_ErrMsg("Cannot read network address for interface %s: %s\n",
+                    psz_name, strerror(errno));
+        return -1;
+    }
+  
+    /* Read broadcast address of the interface and store it in our description */
+    if(p_ifdescr->i_flags & IFF_POINTOPOINT)
+    {
+        intf_DbgMsg("%s doen't not support broadcast\n", psz_name);
+        i_rc = ioctl(i_sockfd, SIOCGIFDSTADDR, (byte_t *)&ifr_config);
+    }
+    else
+    {
+        intf_DbgMsg("%s supports broadcast\n", psz_name);
+        i_rc = ioctl(i_sockfd, SIOCGIFBRDADDR, (byte_t *)&ifr_config);
+    }
+    if( !i_rc )
+    {
+        memcpy(&p_ifdescr->sa_bcast_addr, &ifr_config.ifr_addr, sizeof(struct sockaddr));
+        intf_DbgMsg("%s broadcast address: %s\n", psz_name,
+                    inet_ntoa(p_ifdescr->sa_bcast_addr.sin_addr));
+    }
+    else
+    {
+        intf_ErrMsg("Cannot read broadcast address for interface %s: %s\n",
+                    psz_name, strerror(errno));
+        return -1;
+    }
+  
+    return i_rc;
+}
+
+
+
+/*******************************************************************************
+ * ReadNetConf: Retrieve the network configuration of the host
+ *******************************************************************************
+ * Only IP interfaces are listed, and only if they are up
+ * i_sockfd must reference a socket open as follow: AF_INET, DOCK_DGRAM, 0
+ *******************************************************************************/
+int ReadNetConf(int i_sockfd, net_descr_t* p_net_descr)
+{
+    struct ifreq* a_ifr_ifconf = NULL;
+    struct ifreq* p_ifr_current_if;
+    struct ifconf ifc_netconf;
+  
+    int i_if_number;
+    int i_remaining;
+    int i_rc = 0;
+
+    ASSERT(p_net_descr);
+
+    /* Start by assuming we have few than 3 interfaces (i_if_number will
+       be incremented by 1 when entering the loop) */
+    i_if_number = 2;
+
+    /* Retrieve network configuration for that host */
+    do
+    {
+        i_if_number++;
+        a_ifr_ifconf = realloc(a_ifr_ifconf, i_if_number*sizeof(struct ifreq));
+        ifc_netconf.ifc_len = i_if_number*sizeof(struct ifreq);
+        ifc_netconf.ifc_req = a_ifr_ifconf;
+
+        i_rc = ioctl(i_sockfd, SIOCGIFCONF, (byte_t*)&ifc_netconf);
+        if( i_rc )
+        {
+            intf_ErrMsg("Cannot read network configuration: %s\n",
+                        strerror(errno));
+            break;
+        }
+    }
+    /* If we detected ifc_len interfaces, this may mean that others have
+       been missed because the a_ifr_ifconf was to little, so increase
+       it's size and retry */
+    while( ifc_netconf.ifc_len >= i_if_number*sizeof(struct ifreq) );
+
+    /* No see what we detected */
+    if( !i_rc )
+    {
+        /* Init the given net_descr_t struct */
+        p_net_descr->i_if_number = 0;
+        p_net_descr->a_if = NULL;
+
+        /* Iterate through the entries of the a_ifr_ifconf table */
+        p_ifr_current_if = ifc_netconf.ifc_req;
+        for( i_remaining = ifc_netconf.ifc_len / sizeof (struct ifreq);
+             i_remaining-- > 0; p_ifr_current_if++ )
+        {
+            intf_DbgMsg("Found interface %s\n", p_ifr_current_if->ifr_name);
+
+            /* Don't use an interface devoted to an address family other than IP */
+            if(p_ifr_current_if->ifr_addr.sa_family != AF_INET)
+                continue;
+
+            /* Read the status of this interface */
+            if( ioctl(i_sockfd, SIOCGIFFLAGS, (byte_t *)p_ifr_current_if) < 0 )
+            {
+                intf_ErrMsg("Cannot access interface %s: %s\n",
+                            p_ifr_current_if->ifr_name, strerror(errno));
+                i_rc = -1;
+                break;
+            }
+            else
+            {
+                /* Skip this interface if it is not up or if this is a loopback one */
+                if( !p_ifr_current_if->ifr_flags & IFF_UP ||
+                    p_ifr_current_if->ifr_flags & IFF_LOOPBACK )
+                  continue;
+
+                /* Add an entry to the net_descr struct to store the description of
+                   that interface */
+                p_net_descr->i_if_number++;
+                p_net_descr->a_if = realloc(p_net_descr->a_if,
+                                            p_net_descr->i_if_number*sizeof(if_descr_t));
+                /* Read the info ??? */
+                i_rc = ReadIfConf(i_sockfd, &p_net_descr->a_if[p_net_descr->i_if_number-1],
+                                  p_ifr_current_if->ifr_name);
+            }
+        }
+    }
+  
+    /* Don't need the a_ifr_ifconf anymore */
+    free( a_ifr_ifconf );
+    return i_rc;
+}
+
+
diff --git a/src/misc/rsc_files.c b/src/misc/rsc_files.c
new file mode 100644 (file)
index 0000000..7139f60
--- /dev/null
@@ -0,0 +1,411 @@
+/*******************************************************************************
+ * rsc_files.c: resources files manipulation functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This library describe a general format used to store 'resources'. Resources
+ * can be anything, including pictures, audio streams, and so on.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Format of a resource file:
+ *  offset      type        meaning
+ *  0           char[2]     "RF" (magic number)
+ *  2           char[2]     "VL" (minor magic number, ignored)
+ *  4           u16         i_type: resource file type. This is to allow
+ *                          different versions of the resources types constants.
+ *  6           u16         i_size: number of entries in the resources table.
+ * {
+ *  +0          char[32]    resource name (ASCIIZ or ASCII[32])
+ *  +32         u16         resource type
+ *  +34         u64         data offset in bytes, from beginning of file
+ *  +42         u64         data size in bytes
+ * } * i_size
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+
+#include "rsc_files.h"
+
+#include "intf_msg.h"
+
+/*******************************************************************************
+ * CreateResourceFile: create a new resource file
+ *******************************************************************************
+ * Creates a new resource file. The file is opened read/write and erased if
+ * it already exists.
+ *******************************************************************************
+ * Messages type: rsc, major code 101
+ *******************************************************************************/
+resource_file_t *CreateResourceFile( char *psz_filename, int i_type, int i_size, 
+                                     int i_mode )
+{
+    resource_file_t *   p_file;                              /* new descriptor */
+    int                 i_index;                             /* resource index */
+
+    /* Create descriptor and tables */
+    p_file = malloc( sizeof(resource_file_t) );
+    if( p_file == NULL )
+    {
+        intf_ErrMsg("rsc error 101-1: %s\n", strerror(errno));
+        return( NULL );
+    }
+    p_file->p_resource = malloc( sizeof(resource_descriptor_t) * i_size );
+    if( p_file->p_resource == NULL )
+    {
+        intf_ErrMsg("rsc error 101-2: %s\n", strerror(errno));
+        free( p_file );
+        return( NULL );
+    } 
+                
+    /* Open file */
+    p_file->i_file = open( psz_filename, O_CREAT | O_RDWR, i_mode ); 
+    if( p_file->i_file == -1 )                                        /* error */
+    {
+        intf_ErrMsg("rsc error 101-3: %s: %s\n", psz_filename, strerror(errno) );
+        free( p_file->p_resource );
+        free( p_file );        
+    }
+
+    /* Initialize tables */
+    p_file->i_type = i_type;
+    p_file->i_size = i_size;
+    p_file->b_read_only = 0;
+    for( i_index = 0; i_index < i_size; i_index++ )
+    {
+        p_file->p_resource[i_index].i_type = EMPTY_RESOURCE;
+    }
+
+    return( p_file );
+}
+
+/*******************************************************************************
+ * OpenResourceFile: open an existing resource file read-only
+ *******************************************************************************
+ * Open an existing resource file. i_flags should be O_RDONLY or O_RDWR. An
+ * error will occurs if the file does not exists.
+ *******************************************************************************
+ * Messages type: rsc, major code 102
+ *******************************************************************************/
+resource_file_t *OpenResourceFile( char *psz_filename, int i_type, int i_flags )
+{
+    resource_file_t *   p_file;                              /* new descriptor */
+    int                 i_index;                             /* resource index */
+    byte_t              p_buffer[50];                                /* buffer */
+
+    /* Create descriptor and tables */
+    p_file = malloc( sizeof(resource_file_t) );
+    if( p_file == NULL )
+    {
+        intf_ErrMsg("rsc error 102-1: %s\n", strerror(errno));
+        return( NULL );
+    }   
+                
+    /* Open file */
+    p_file->i_file = open( psz_filename, i_flags ); 
+    if( p_file->i_file == -1 )                                        /* error */
+    {
+        intf_ErrMsg("rsc error 102-2: %s: %s\n", psz_filename, strerror(errno) );
+        free( p_file );         
+        return( NULL );
+    }   
+    
+    /* Read header */
+    if( read( p_file->i_file, p_buffer, 8 ) != 8)
+    {
+        intf_ErrMsg("rsc error 102-3: %s: unexpected end of file (not a resource file ?)\n");
+        close( p_file->i_file );
+        free( p_file);
+        return( NULL );
+    }
+    if( (p_buffer[0] != 'R') || (p_buffer[0] != 'F') || (*(u16 *)(p_buffer + 4) != i_type) )
+    {
+        intf_ErrMsg("rsc error 102-4: %s is not a valid resource file or has incorrect type\n", psz_filename);
+        close( p_file->i_file );
+        free( p_file );
+        return( NULL );
+    }
+    p_file->i_type = i_type;
+    p_file->i_size = *(u16 *)(p_buffer + 6);
+    intf_DbgMsg("rsc debug 102-1: %s opened, %d resources\n", psz_filename, p_file->i_size);
+
+    /* Allocate tables */
+    p_file->p_resource = malloc( sizeof(resource_descriptor_t) * p_file->i_size );
+    if( p_file->p_resource == NULL )
+    {
+        intf_ErrMsg("rsc error 102-5: %s\n", strerror(errno));
+        close( p_file->i_file );
+        free( p_file );
+        return( NULL );
+    } 
+    
+    /* Initialize table */
+    p_file->b_up_to_date = 1;
+    p_file->b_read_only = ( i_flags & O_RDWR ) ? 0 : 1;
+    for( i_index = 0; i_index < p_file->i_size; i_index++ )
+    {
+        if( read( p_file->i_file, p_buffer, 50 ) != 50 )
+        {
+            intf_ErrMsg("rsc error 102-6: %s: unexpected end of file\n", psz_filename);
+            close( p_file->i_file );
+            free( p_file->p_resource );
+            free( p_file );
+            return( NULL );
+        }
+        memcpy( p_file->p_resource[i_index].psz_name, p_buffer, 32 );
+        p_file->p_resource[i_index].psz_name[RESOURCE_MAX_NAME] = '\0';
+        p_file->p_resource[i_index].i_type =    *(u16 *)(p_buffer + 32 );
+        p_file->p_resource[i_index].i_offset =  *(u64 *)(p_buffer + 34 );
+        p_file->p_resource[i_index].i_size =    *(u64 *)(p_buffer + 42 );
+    }
+
+    return( p_file );
+}
+
+/*******************************************************************************
+ * UpdateResourceFile: write the resources table in a resource file
+ *******************************************************************************
+ * This function writes resources table in the resource file. This is 
+ * automatically done when the file is closed, but can also be done manually.
+ *******************************************************************************
+ * Messages type: rsc, major code 103
+ *******************************************************************************/
+int UpdateResourceFile( resource_file_t *p_file )
+{
+    byte_t      p_buffer[50];                                        /* buffer */
+    int         i_index;                                     /* resource index */
+                             
+#ifdef DEBUG
+    if( p_file->b_read_only )
+    {
+        intf_DbgMsg("rsc debug 103-1: can't update a read-only file\n");
+        return( -1 );
+    }
+#endif
+
+    /* Seek beginning of file */
+    if( lseek( p_file->i_file, 0, SEEK_SET ) )
+    {
+        intf_ErrMsg("rsc error 103-1: %s\n", strerror(errno));
+        return( -2 );
+    }
+
+    /* Write header */
+    p_buffer[0] =               'R';
+    p_buffer[1] =               'F';
+    p_buffer[2] =               'V';
+    p_buffer[3] =               'L';
+    *(u16 *)(p_buffer + 4) =    p_file->i_type;
+    *(u16 *)(p_buffer + 6) =    p_file->i_size;
+    if( write( p_file->i_file, p_buffer, 8 ) != 8 )
+    {
+        intf_ErrMsg("rsc error 103-2: %s\n", strerror(errno)); 
+        return( -3 );
+    }
+
+    /* Write resources table */
+    for( i_index = 0; i_index < p_file->i_size; i_index++ )
+    {
+        memcpy( p_buffer, p_file->p_resource[i_index].psz_name, 32 );
+        *(u16 *)(p_buffer + 32) =   p_file->p_resource[i_index].i_type;
+        *(u64 *)(p_buffer + 34) =   p_file->p_resource[i_index].i_offset;
+        *(u64 *)(p_buffer + 42) =   p_file->p_resource[i_index].i_size;            
+        if( write( p_file->i_file, p_buffer, 8 ) != 8 )
+        {
+            intf_ErrMsg("rsc error 103-3: %s\n", strerror(errno)); 
+            return( -4 );
+        }           
+    }
+
+    /* Mark file as up to date */
+    p_file->b_up_to_date = 1;
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * CloseResourceFile: close a resource file
+ *******************************************************************************
+ * Updates the resources table if required, and close the file. It returns non
+ * 0 in case of error.
+ *******************************************************************************
+ * Messages type: rsc, major code 104
+ *******************************************************************************/
+int CloseResourceFile( resource_file_t *p_file )
+{
+    /* Update table */
+    if( !p_file->b_up_to_date && ( UpdateResourceFile( p_file ) < 0 ) )
+    {
+        return( -1 );
+    }
+
+    /* Close and destroy descriptor */
+    if( close( p_file->i_file ) )
+    {
+        intf_ErrMsg("rsc error 104-1: %s\n", strerror(errno));
+        return( -2 );
+    }
+    free( p_file->p_resource );
+    free( p_file );
+    return( 0 );
+}
+
+/*******************************************************************************
+ * SeekResource: seek a resource in a resource file
+ *******************************************************************************
+ * Look for a resource in file and place the "reading head" at the beginning of
+ * the resource data.
+ * In case of success, the resource number is returned. Else, a negative number
+ * is returned.
+ *******************************************************************************
+ * Messages type: rsc, major code 105
+ *******************************************************************************/
+int SeekResource( resource_file_t *p_file, char *psz_name, int i_type )
+{
+    int     i_index;                                         /* resource index */
+
+    /* Look for resource in table */
+    for( i_index = 0; 
+         (i_index < p_file->i_size) 
+             && ((i_type != p_file->p_resource[i_index].i_type )
+                 || strcmp(psz_name, p_file->p_resource[i_index].psz_name));
+         i_index++ )
+    {
+        ;
+    }
+    if( i_index == p_file->i_size )
+    {
+        intf_ErrMsg("rsc error 105-1: unknown resource %s.%d\n", psz_name, i_type);
+        return( -1 );
+    }
+
+    /* Seek */
+    if( lseek( p_file->i_file, p_file->p_resource[i_index].i_offset, SEEK_SET ) )
+    {
+        intf_ErrMsg("rsc error 105-2: can not reach %s.%d: %s\n", psz_name, 
+                    i_type, strerror(errno));
+        return( -2 );
+    }
+
+    return( i_index );
+}
+
+/*******************************************************************************
+ * ReadResource: read a resource
+ *******************************************************************************
+ * Read a resource from a file. The function returns a negative value in case
+ * of error, and 0 in case of success.
+ *******************************************************************************
+ * Messages type: rsc, major code 106
+ *******************************************************************************/
+int ReadResource( resource_file_t *p_file, char *psz_name, int i_type,
+                  size_t max_size, byte_t *p_data )
+{
+    int i_index;                                             /* resource index */
+
+    /* Seek resource */
+    i_index = SeekResource( p_file, psz_name, i_type );
+    if( i_index < 0 )
+    {
+        return( -1 );
+    }
+
+    /* Check if buffer is large enough */
+    if( max_size < p_file->p_resource[i_index].i_size )
+    {
+        intf_ErrMsg("rsc error 106-1: buffer is too small\n");
+        return( -2 );
+    }
+
+    /* Read data */
+    if( read( p_file->i_file, p_data, p_file->p_resource[i_index].i_size ) 
+        != p_file->p_resource[i_index].i_size )
+    {
+        intf_ErrMsg("rsc error 106-2: can not read %s.%d: %s\n",
+                    p_file->p_resource[i_index].psz_name, 
+                    p_file->p_resource[i_index].i_type,
+                    strerror(errno));
+        return( -3 );
+    }
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * WriteResource: write a resource
+ *******************************************************************************
+ * Append a resource at the end of the file. It returns non 0 on error.
+ *******************************************************************************
+ * Messages type: rsc, major code 107
+ *******************************************************************************/
+int WriteResource( resource_file_t *p_file, char *psz_name, int i_type, 
+                   size_t size, byte_t *p_data )
+{
+    int i_index;                                             /* resource index */
+    int i_tmp_index;                               /* temporary resource index */
+    u64 i_offset;                                                    /* offset */
+
+#ifdef DEBUG
+    if( p_file->b_read_only )
+    {
+        intf_DbgMsg("rsc debug 107-1: can not write to a read-only resource file\n");
+        return( -1 );
+    }
+#endif
+
+    /* Look for an empty place in the resources table */
+    i_index = -1;
+    i_offset = p_file->i_size * 50 + 8;
+    for( i_tmp_index = 0; i_tmp_index < p_file->i_size; i_tmp_index++ )
+    {
+        if( p_file->p_resource[i_tmp_index].i_type != EMPTY_RESOURCE)
+        {
+            i_offset = MAX( i_offset, p_file->p_resource[i_tmp_index].i_offset 
+                            + p_file->p_resource[i_tmp_index].i_size );
+        }
+        else if( i_index == -1 )
+        {
+            i_index = i_tmp_index;
+        }
+    }
+    if( i_index == -1 )
+    {
+        intf_ErrMsg("rsc error 107-1: resources table is full\n");
+        return( -1 );
+    }
+
+    /* Seek end of file */
+    if( lseek( p_file->i_file, i_offset, SEEK_SET ) )
+    {
+        intf_ErrMsg("rsc error 107-2: %s\n", strerror(errno));
+        return( -2 );
+    }
+
+    /* Write data */
+    if( write( p_file->i_file, p_data, size ) != size )
+    {
+        intf_ErrMsg("rsc error 107-3: %s\n", strerror(errno));
+        return( -3 );
+    }
+
+    /* Update table */
+    strncpy( p_file->p_resource[i_index].psz_name, psz_name, RESOURCE_MAX_NAME );
+    p_file->p_resource[i_index].psz_name[RESOURCE_MAX_NAME] = '\0';        
+    p_file->p_resource[i_index].i_type = i_type;
+    p_file->p_resource[i_index].i_offset = i_offset;
+    p_file->p_resource[i_index].i_size = size;
+    p_file->b_up_to_date = 0;
+
+    return( 0 );
+}
diff --git a/src/misc/xutils.c b/src/misc/xutils.c
new file mode 100644 (file)
index 0000000..926b938
--- /dev/null
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * xutils.c: X11 utilities
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * This library includes many usefull functions to perform simple operations
+ * using Xlib.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "intf_msg.h"
+
+
+
+/*******************************************************************************
+ * XTryLoadFont: try to load a font and a fallback font
+ *******************************************************************************
+ * This function try to load a font, and, on failure, a default fallback font
+ * (usually 'fixed'). This function returns 0 on success, >0 if it failed but
+ * the default font 
+ *******************************************************************************/
+int XTryLoadFont( Display *p_display, char *psz_font, XFontStruct **p_font )
+{
+    *p_font = XLoadQueryFont( p_display, psz_font );
+    if( *p_font )                                                   /* success */
+    {
+        return( 0 );
+    }
+
+    intf_DbgMsg("X11 error: can't load font `%s', using `%s' instead\n", p_font, X11_DEFAULT_FONT );
+    *p_font = XLoadQueryFont( p_display, X11_DEFAULT_FONT );
+    if( *p_font )                                                   /* success */
+    {
+        return( 1 );
+    }
+
+    /* Fatal error */
+    intf_ErrMsg("X11 error: can't load fallback font `%s'\n", X11_DEFAULT_FONT );
+    return( -1 );
+}
+
+/*******************************************************************************
+ * XEnableScreenSaver: enable screen saver
+ *******************************************************************************
+ * This function enable the screen saver on a display after it had been
+ * disabled by XDisableScreenSaver. Both functions use a counter mechanism to 
+ * know wether the screen saver can be activated or not: if n successive calls
+ * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
+ * will be required before the screen saver could effectively be activated.
+ *******************************************************************************/
+int XEnableScreenSaver( Display *p_display )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * XDisableScreenSaver: disable screen saver
+ *******************************************************************************
+ * See XEnableScreenSaver
+ *******************************************************************************/
+int XDisableScreenSaver( Display *p_display )
+{
+    /* ?? */
+}
+
diff --git a/src/video_decoder/video_decoder.c b/src/video_decoder/video_decoder.c
new file mode 100644 (file)
index 0000000..048fc52
--- /dev/null
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * video_decoder.c : video decoder thread
+ * (c)1999 VideoLAN
+ *******************************************************************************/
+
+/* ?? passer en terminate/destroy avec les signaux supplémentaires */
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+
+#include "intf_msg.h"
+#include "debug.h"                      /* ?? temporaire, requis par netlist.h */
+
+#include "input.h"
+#include "input_netlist.h"
+#include "decoder_fifo.h"
+#include "video.h"
+#include "video_output.h"
+#include "video_decoder.h"
+
+/*
+ * Local prototypes
+ */
+static int      CheckConfiguration  ( video_cfg_t *p_cfg );
+static int      InitThread          ( vdec_thread_t *p_vdec );
+static void     RunThread           ( vdec_thread_t *p_vdec );
+static void     ErrorThread         ( vdec_thread_t *p_vdec );
+static void     EndThread           ( vdec_thread_t *p_vdec );
+
+/*******************************************************************************
+ * vdec_CreateThread: create a generic decoder thread
+ *******************************************************************************
+ * This function creates a new video decoder thread, and returns a pointer
+ * to its description. On error, it returns NULL.
+ * Following configuration properties are used:
+ * ??
+ *******************************************************************************/
+vdec_thread_t * vdec_CreateThread( video_cfg_t *p_cfg, input_thread_t *p_input,
+                                   vout_thread_t *p_vout, int *pi_status )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * vdec_DestroyThread: destroy a generic decoder thread
+ *******************************************************************************
+ * Destroy a terminated thread. This function will return 0 if the thread could
+ * be destroyed, and non 0 else. The last case probably means that the thread
+ * was still active, and another try may succeed.
+ *******************************************************************************/
+void vdec_DestroyThread( vdec_thread_t *p_vdec, int *pi_status )
+{
+    /* ?? */
+}
+
+/* following functions are local */
+
+/*******************************************************************************
+ * CheckConfiguration: check vdec_CreateThread() configuration
+ *******************************************************************************
+ * Set default parameters where required. In DEBUG mode, check if configuration
+ * is valid.
+ *******************************************************************************/
+static int CheckConfiguration( video_cfg_t *p_cfg )
+{
+    /* ?? */
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * InitThread: initialize vdec output thread
+ *******************************************************************************
+ * This function is called from RunThread and performs the second step of the
+ * initialization. It returns 0 on success. Note that the thread's flag are not
+ * modified inside this function.
+ *******************************************************************************/
+static int InitThread( vdec_thread_t *p_vdec )
+{
+    /* ?? */
+    /* Create video stream */
+    p_vdec->i_stream =  vout_CreateStream( p_vdec->p_vout );
+    if( p_vdec->i_stream < 0 )                                        /* error */
+    {
+        return( 1 );        
+    }
+    
+    /* Initialize decoding data */    
+    /* ?? */
+
+    /* Initialize other properties */
+#ifdef STATS
+    p_vdec->c_loops = 0;    
+    p_vdec->c_idle_loops = 0;
+    p_vdec->c_pictures = 0;
+    p_vdec->c_i_pictures = 0;
+    p_vdec->c_p_pictures = 0;
+    p_vdec->c_b_pictures = 0;
+    p_vdec->c_decoded_pictures = 0;
+    p_vdec->c_decoded_i_pictures = 0;
+    p_vdec->c_decoded_p_pictures = 0;
+    p_vdec->c_decoded_b_pictures = 0;
+#endif
+
+    /* Mark thread as running and return */
+    intf_DbgMsg("vdec debug: InitThread(%p) succeeded\n", p_vdec);    
+    return( 0 );    
+}
+
+/*******************************************************************************
+ * RunThread: generic decoder thread
+ *******************************************************************************
+ * Generic decoder thread. This function does only returns when the thread is
+ * terminated. 
+ *******************************************************************************/
+static void RunThread( vdec_thread_t *p_vdec )
+{
+    /* 
+     * Initialize thread and free configuration 
+     */
+    p_vdec->b_error = InitThread( p_vdec );
+    if( p_vdec->b_error )
+    {
+        return;
+    }
+    p_vdec->b_run = 1;
+
+    /*
+     * Main loop - it is not executed if an error occured during
+     * initialization
+     */
+    while( (!p_vdec->b_die) && (!p_vdec->b_error) )
+    {
+        /* ?? */
+    } 
+
+    /*
+     * Error loop
+     */
+    if( p_vdec->b_error )
+    {
+        ErrorThread( p_vdec );        
+    }
+
+    /* End of thread */
+    EndThread( p_vdec );
+    p_vdec->b_run = 0;
+}
+
+/*******************************************************************************
+ * ErrorThread: RunThread() error loop
+ *******************************************************************************
+ * This function is called when an error occured during thread main's loop. The
+ * thread can still receive feed, but must be ready to terminate as soon as
+ * possible.
+ *******************************************************************************/
+static void ErrorThread( vdec_thread_t *p_vdec )
+{
+    /* Wait until a `die' order */
+    while( !p_vdec->b_die )
+    {
+        /* ?? trash all trashable PES packets */
+
+        /* Sleep a while */
+        msleep( VDEC_IDLE_SLEEP );                
+    }
+}
+
+/*******************************************************************************
+ * EndThread: thread destruction
+ *******************************************************************************
+ * This function is called when the thread ends after a sucessfull 
+ * initialization.
+ *******************************************************************************/
+static void EndThread( vdec_thread_t *p_vdec )
+{
+#ifdef DEBUG
+    /* Check for remaining PES packets */
+    /* ?? */
+#endif
+
+    /* Destroy thread structures allocated by InitThread */
+    vout_DestroyStream( p_vdec->p_vout, p_vdec->i_stream );
+    /* ?? */
+
+    intf_DbgMsg("vdec debug: EndThread(%p)\n", p_vdec);
+}
+
+
diff --git a/src/video_output/video_graphics.c b/src/video_output/video_graphics.c
new file mode 100644 (file)
index 0000000..555186a
--- /dev/null
@@ -0,0 +1,713 @@
+/*******************************************************************************
+ * video_graphics.c: pictures manipulation functions
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * Includes function to compose, convert and display pictures, and also basic
+ * functions to copy/convert pictures data or descriptors and read pictures
+ * from a file.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"
+
+#include "video.h"
+#include "video_graphics.h"
+
+#include "intf_msg.h"
+
+/*
+ * Local prototypes
+ */
+static int  ReadPictureConfiguration    ( int i_file, video_cfg_t *p_cfg );
+
+/*******************************************************************************
+ * video_CreatePicture: create an empty picture
+ *******************************************************************************
+ * This function create an empty image. A null pointer is returned if the
+ * function fails. 
+ * Following configuration properties are used:
+ *  VIDEO_CFG_WIDTH     picture width (required)
+ *  VIDEO_CFG_HEIGHT    picture height (required)
+ *  VIDEO_CFG_TYPE      picture type (required)
+ *  VIDEO_CFG_FLAGS     flags
+ *  VIDEO_CFG_BPP       padded bpp (required for pixel pictures)
+ *  VIDEO_CFG_POSITION  position in output window
+ *  VIDEO_CFG_ALIGN     base position in output window
+ *  VIDEO_CFG_RATIO     display ratio
+ *  VIDEO_CFG_LEVEL     overlay hierarchical level  
+ *  VIDEO_CFG_REFCOUNT  links reference counter
+ *  VIDEO_CFG_STREAM    video stream id
+ *  VIDEO_CFG_DATE      display date
+ *  VIDEO_CFG_DURATION  duration for overlay pictures
+ *  VIDEO_CFG_PIXEL     pixel value for mask pictures
+ *  VIDEO_CFG_DATA      picture data (required if not owner and non blank)
+ *******************************************************************************/
+picture_t *video_CreatePicture( video_cfg_t *p_cfg )
+{
+    picture_t *p_newpic;                             /* new picture descriptor */
+
+#ifdef DEBUG
+    /* Check base configuration */
+    if( (p_cfg->i_properties & (VIDEO_CFG_WIDTH | VIDEO_CFG_HEIGHT | VIDEO_CFG_TYPE))
+        != (VIDEO_CFG_WIDTH | VIDEO_CFG_HEIGHT | VIDEO_CFG_TYPE) )
+    {
+        intf_DbgMsg("invalid picture configuration\n");
+        return( NULL );
+    }
+    /* Check flags validity */
+    if( (p_cfg->i_properties & VIDEO_CFG_FLAGS) 
+        && (p_cfg->i_flags & ( RESERVED_PICTURE | DISPLAYED_PICTURE 
+                               | DISPLAY_PICTURE | DESTROY_PICTURE )) )
+    {
+        intf_DbgMsg("invalid picture flags\n");
+        return( NULL );
+    }
+#endif
+
+    /* Allocate descriptor */
+    p_newpic = malloc( sizeof(picture_t) );
+    if( !p_newpic )                                  /* error: malloc() failed */
+    {
+        return( NULL );
+    }
+    
+    /* Create picture */
+    if( video_CreatePictureBody( p_newpic, p_cfg ) )                  /* error */
+    {
+        free( p_newpic );
+        return( NULL );
+    }
+
+    /* Log and return */
+#ifdef VOUT_DEBUG /* ?? -> video_debug ? */
+    video_PrintPicture( p_newpic, "video: created picture " );
+#else
+    intf_DbgMsg("created picture %dx%d at %p\n", p_newpic->i_width, p_newpic->i_height, p_newpic);
+#endif
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * video_CopyPicture: create a copy of a picture
+ *******************************************************************************
+ * This function creates a copy of a picture. It returns NULL on error.
+ *******************************************************************************
+ * Messages type: video, major code: 102
+ *******************************************************************************/
+picture_t * video_CopyPicture( picture_t *p_pic )
+{
+    picture_t * p_newpic;                            /* new picture descriptor */
+
+    p_newpic = malloc( sizeof(picture_t) );
+    if( p_newpic != NULL )
+    {        
+        video_CopyPictureDescriptor( p_newpic, p_pic );
+        /* If source picture owns its data, make a copy */
+        if( p_pic->i_flags & OWNER_PICTURE )
+        {              
+            p_newpic->p_data = malloc( p_pic->i_height * p_pic->i_bytes_per_line );
+            if( p_newpic->p_data == NULL )                            /* error */
+            {
+                free( p_newpic );
+                return( NULL );
+            }
+            memcpy( p_newpic->p_data, p_pic->p_data,
+                    p_pic->i_height * p_pic->i_bytes_per_line );
+        }
+        /* If source picture does not owns its data, copy the reference */
+        else              
+        {
+            p_newpic->p_data = p_pic->p_data;
+        }
+    }
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * video_ReplicatePicture: creates a replica of a picture
+ *******************************************************************************
+ * This function creates a replica of the original picture. It returns NULL on
+ * error.
+ *******************************************************************************
+ * Messages type: video, major code: 103
+ *******************************************************************************/
+picture_t * video_ReplicatePicture( picture_t *p_pic )
+{
+    picture_t * p_newpic;                            /* new picture descrpitor */
+
+    p_newpic = malloc( sizeof(picture_t) );
+    if( p_newpic != NULL )
+    {        
+        video_CopyPictureDescriptor( p_newpic, p_pic );
+        p_newpic->i_flags &= ~OWNER_PICTURE;
+        p_newpic->p_data = p_pic->p_data;
+    }
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * video_DestroyPicture: destroys a picture
+ *******************************************************************************
+ * This function destroy a picture created with any of the video_*Picture 
+ * functions.
+ *******************************************************************************
+ * Messages type: video, major code: 104
+ *******************************************************************************/
+void video_DestroyPicture( picture_t *p_pic )
+{    
+    intf_DbgMsg("video debug 104-1: destroying picture %p\n", p_pic );
+    
+    /* Destroy data if picture owns it */
+    if( p_pic->i_flags & OWNER_PICTURE )
+    {
+        free( p_pic->p_data );                                  
+    }
+    /* Destroy descriptor */
+    free( p_pic );
+}
+
+/*******************************************************************************
+ * video_ClearPicture: clear a picture
+ *******************************************************************************
+ * Set all pixel to 0 (black).
+ *******************************************************************************
+ * Messages type: video, major code: 105
+ *******************************************************************************/
+void video_ClearPicture( picture_t *p_pic )
+{
+    switch( p_pic->i_type )
+    {
+    case RGB_BLANK_PICTURE:
+    case PIXEL_BLANK_PICTURE:
+        /* Blank pictures will have their pixel value set to 0 */
+        p_pic->pixel = 0;        
+        break;
+    default:
+        /* All pictures types except blank ones have a meaningfull i_bytes_per_line
+         * field, and blank ones don't need to be cleared. */
+        memset( p_pic->p_data, 0, p_pic->i_height * p_pic->i_bytes_per_line );
+        break;        
+    }    
+}
+
+/*******************************************************************************
+ * video_DrawPixel: set a pixel in a picture
+ *******************************************************************************
+ * This function set a pixel's value in an picture. It is an easy, but slow way
+ * to render an image, and should be avoided. Exact meaning of value depends of
+ * the picture type.
+ *******************************************************************************
+ * Messages type: video, major code: 106
+ *******************************************************************************/
+void video_DrawPixel( picture_t *p_pic, int i_x, int i_y, pixel_t value )
+{
+    switch( p_pic->i_type )
+    {
+    case RGB_PICTURE:                          /* 24 bits encoded RGB pictures */
+        p_pic->p_data[i_y * p_pic->i_bytes_per_line + i_x * 3 ] =    RGBVALUE2RED( value );
+        p_pic->p_data[i_y * p_pic->i_bytes_per_line + i_x * 3 + 1] = RGBVALUE2GREEN( value );
+        p_pic->p_data[i_y * p_pic->i_bytes_per_line + i_x * 3 + 2] = RGBVALUE2BLUE( value );
+        break;
+
+    case PIXEL_PICTURE:                              /* pixel encoded pictures */
+        /* ?? */
+        break;
+
+    case RGB_MASK_PICTURE:                          /* 1 bpp rgb mask pictures */
+    case PIXEL_MASK_PICTURE:                      /* 1 bpp pixel mask pictures */
+        /* ?? */
+        break;
+#ifdef DEBUG
+    default:
+        intf_DbgMsg("video debug 106-1: invalid picture %p type %d\n", 
+                    p_pic, p_pic->i_type);
+        break;
+#endif
+    }
+}
+
+
+
+/*******************************************************************************
+ * video_DrawHLine: draw an horizontal line in a picture
+ *******************************************************************************
+ *
+ *******************************************************************************
+ * Messages type: video, major code: 107
+ *******************************************************************************/
+void video_DrawHLine( picture_t *p_pic, int i_x, int i_y, int i_width, pixel_t value )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * video_DrawVLine: draw a vertical line in a picture
+ *******************************************************************************
+ *
+ *******************************************************************************
+ * Messages type: video, major code: 108
+ *******************************************************************************/
+void video_DrawVLine( picture_t *p_pic, int i_x, int i_y, int i_width, pixel_t value )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * video_DrawLine: draw a line in a picture
+ *******************************************************************************
+ * video_DrawHLine() and video_DrawVLine() functions should be prefered if
+ * possible.
+ *******************************************************************************
+ * Messages type: video, major code: 109
+ *******************************************************************************/
+void video_DrawLine( picture_t *p_pic, int i_x1, int i_y1, int i_x2, int i_y2, pixel_t value )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * video_DrawBar: draw a bar in a picture
+ *******************************************************************************
+ * Draw a bar (filled rectangle) in a picture/
+ *******************************************************************************
+ * Messages type: video, major code: 110
+ *******************************************************************************/
+void video_DrawBar( picture_t *p_pic, int i_x, int i_y, int i_width, int i_height, 
+                   pixel_t value )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * video_DrawRectangle: draw a rectangle in a picture
+ *******************************************************************************
+ * Draw a rectangle (empty) in an image
+ *******************************************************************************
+ * Messages type: video, major code: 111
+ *******************************************************************************/
+void video_DrawRectangle( picture_t *p_pic, int i_x, int i_y, 
+                         int i_width, int i_height, pixel_t value )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * video_DrawImage: insert a picture in another one
+ *******************************************************************************
+ *  ??
+ *******************************************************************************
+ * Messages type: video, major code: 112
+ *******************************************************************************/
+void video_DrawPicture( picture_t *p_pic, picture_t *p_insert, int i_x, int i_y )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * video_DrawText: insert text in a picture
+ *******************************************************************************
+ * This function prints a simple text in an image. It does not support 
+ * different fonts, or complex text settings, but is designed to provide a
+ * simple way to display messages, counters and indications.
+ *******************************************************************************
+ * Messages type: video, major code: 113
+ *******************************************************************************/
+void video_DrawText( picture_t *p_picture, int i_x, int i_y, char *psz_text, 
+                    int i_size, pixel_t value )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * video_CopyPictureDescriptor: copy a picture descriptor
+ *******************************************************************************
+ * This function is used when a picture is added to the video heap. It does not
+ * copy the p_data field. Some post-treatment must obviously be done, especially
+ * concerning the OWNER_PICTURE flag, the i_refcount and p_data fields.
+ * Although it is exported, since it is used in several of the vout_* modules,
+ * it should not be used directly outside the video output module.
+ *******************************************************************************
+ * Messages type: video, major code 114
+ *******************************************************************************/
+void video_CopyPictureDescriptor( picture_t *p_dest, picture_t *p_src )
+{
+    p_dest->i_type =            p_src->i_type;
+    p_dest->i_flags =           p_src->i_flags;
+
+    p_dest->i_width =           p_src->i_width;
+    p_dest->i_height =          p_src->i_height;
+    p_dest->i_bpp =             p_src->i_bpp;
+    p_dest->i_bytes_per_line =  p_src->i_bytes_per_line;
+
+    p_dest->i_x =               p_src->i_x;
+    p_dest->i_y =               p_src->i_y;
+    p_dest->i_h_align =         p_src->i_h_align;
+    p_dest->i_v_align =         p_src->i_v_align;
+    p_dest->i_h_ratio =         p_src->i_h_ratio;
+    p_dest->i_v_ratio =         p_src->i_v_ratio;
+    p_dest->i_level =           p_src->i_level;
+
+    p_dest->i_refcount =        p_src->i_refcount;
+
+    p_dest->i_stream =          p_src->i_stream;
+    p_dest->date =              p_src->date;
+    p_dest->duration =          p_src->duration;
+
+    p_dest->pixel =             p_src->pixel;
+}
+
+/*******************************************************************************
+ * video_CreatePictureBody: create a picture in an descriptor
+ *******************************************************************************
+ * This function is used vy vout_CreateReservedPicture and vout_CreatePicture.
+ * It should not be called directly outside the video output module.
+ * It returns non 0 if the creation failed.
+ *******************************************************************************
+ * Messages type: video, major code 115
+ *******************************************************************************/
+int video_CreatePictureBody( picture_t *p_pic, video_cfg_t *p_cfg )
+{
+#ifdef DEBUG
+    /* Check base configuration */
+    if( (p_cfg->i_properties & (VIDEO_CFG_WIDTH | VIDEO_CFG_HEIGHT | VIDEO_CFG_TYPE))
+        != (VIDEO_CFG_WIDTH | VIDEO_CFG_HEIGHT | VIDEO_CFG_TYPE) )
+    {
+        intf_DbgMsg("video debug 115-1: invalid picture configuration\n");
+        return( 1 );
+    }
+    /* Check flags validity */
+    if( (p_cfg->i_properties & VIDEO_CFG_FLAGS) 
+        && (p_cfg->i_flags & ( RESERVED_PICTURE | DISPLAYED_PICTURE 
+                               | DISPLAY_PICTURE | DESTROY_PICTURE )) )
+    {
+        intf_DbgMsg("video debug 115-2: invalid picture flags\n");
+        return( 1 );
+    }
+#endif
+
+    /* Initialize types, flags and dimensions */
+    p_pic->i_type =     p_cfg->i_type;
+    p_pic->i_flags =    ( p_cfg->i_properties & VIDEO_CFG_FLAGS ) ? p_cfg->i_flags : 0;
+    p_pic->i_width =    p_cfg->i_width;
+    p_pic->i_height =   p_cfg->i_height;
+
+    /* Initialize other pictures properties */
+    if( p_cfg->i_properties & VIDEO_CFG_POSITION )
+    {
+        p_pic->i_x = p_cfg->i_x;
+        p_pic->i_y = p_cfg->i_y;
+    }
+    else
+    {
+        p_pic->i_x = 0;
+        p_pic->i_y = 0;
+    }
+    if( p_cfg->i_properties & VIDEO_CFG_ALIGN )
+    {
+        p_pic->i_h_align = p_cfg->i_h_align;
+        p_pic->i_v_align = p_cfg->i_v_align;
+    }
+    else
+    {
+        p_pic->i_h_align = ALIGN_H_DEFAULT;
+        p_pic->i_v_align = ALIGN_V_DEFAULT;
+    }
+    if( p_cfg->i_properties & VIDEO_CFG_RATIO )
+    {
+        p_pic->i_h_ratio = p_cfg->i_h_ratio;
+        p_pic->i_v_ratio = p_cfg->i_v_ratio;
+    }
+    else
+    {
+        p_pic->i_h_ratio = DISPLAY_RATIO_NORMAL;
+        p_pic->i_v_ratio = DISPLAY_RATIO_NORMAL;
+    }
+    p_pic->i_level =    ( p_cfg->i_properties & VIDEO_CFG_LEVEL ) 
+        ? p_cfg->i_level : ( (p_pic->i_flags & OVERLAY_PICTURE) ? 1 : 0 );
+    p_pic->i_refcount = ( p_cfg->i_properties & VIDEO_CFG_REFCOUNT ) ? p_cfg->i_refcount : 0;
+    p_pic->i_stream =   ( p_cfg->i_properties & VIDEO_CFG_STREAM ) ? p_cfg->i_stream : 0;
+    p_pic->date =       ( p_cfg->i_properties & VIDEO_CFG_DATE ) ? p_cfg->date : 0;
+    p_pic->duration =   ( p_cfg->i_properties & VIDEO_CFG_DURATION ) ? p_cfg->duration : 0;
+
+    /* Initialize type-dependant properties */
+    switch( p_pic->i_type )
+    {
+    case RGB_BLANK_PICTURE:                               /* rgb blank picture */
+    case PIXEL_BLANK_PICTURE:                           /* pixel blank picture */
+#ifdef DEBUG
+        /* Check validity: blank pictures can't be transparent or owner */
+        if( p_pic->i_flags & ( OWNER_PICTURE | TRANSPARENT_PICTURE ) )
+        {            
+            intf_DbgMsg("video debug 115-3: invalid blank picture flags\n");   
+            return( 1 );
+        }
+        /* Set to 0 unused fields */
+        p_pic->i_bpp = 0;
+        p_pic->i_bytes_per_line = 0;
+        p_pic->p_data = NULL;
+#endif
+        /* Set color */
+        p_pic->pixel = ( p_cfg->i_properties & VIDEO_CFG_PIXEL ) ? p_cfg->pixel : 0;
+        break;
+
+    case RGB_PICTURE:                                            /* 24 bpp RGB */
+#ifdef DEBUG
+        /* Set to 0 unused fields */
+        p_pic->pixel = 0;
+#endif
+        /* Set fields */
+        p_pic->i_bpp = 24;
+        p_pic->i_bytes_per_line = PAD( p_pic->i_width * 3, sizeof(int) );
+        break;
+
+    case PIXEL_PICTURE:                               /* pixel encoded picture */
+#ifdef DEBUG
+        /* Check validity: pixel pictures must have the bpp property defined, 
+         * and it must be a multiple of 8 */
+        if( ! (p_cfg->i_properties & VIDEO_CFG_BPP) )
+        {            
+            intf_DbgMsg("video debug 115-4: missing bpp for pixel picture\n");   
+            return( 1 );
+        }
+        else if( p_cfg->i_bpp % 8 )
+        {
+            intf_DbgMsg("video debug 115-5: invalid bpp for pixel picture\n");   
+            return( 1 );       
+        }
+
+        /* Set to 0 unused fields */
+        p_pic->pixel = 0;
+#endif
+        /* Set fields */
+        p_pic->i_bpp = p_cfg->i_bpp;
+        p_pic->i_bytes_per_line = PAD( p_pic->i_width * (p_cfg->i_bpp / 8), sizeof(int) );
+        break;
+
+    case RGB_MASK_PICTURE:                                   /* 1 bpp rgb mask */
+    case PIXEL_MASK_PICTURE:                               /* 1 bpp pixel mask */        
+        /* Set fields */
+        p_pic->i_bpp = 1;
+        p_pic->i_bytes_per_line = PAD( p_pic->i_width / 8, sizeof(int) );
+        p_pic->pixel = ( p_cfg->i_properties & VIDEO_CFG_PIXEL ) ? p_cfg->pixel : 0;
+        break;
+
+#ifdef DEBUG
+    default:                                    /* error: unknown picture type */
+        intf_DbgMsg("video debug 115-6: unknown picture type\n");
+        return( 1 );
+        break;
+#endif
+    }
+
+    /* If picture is not blank, the p_data has some meaning */
+    if( (p_pic->i_type != RGB_BLANK_PICTURE) 
+        && (p_pic->i_type != PIXEL_BLANK_PICTURE) )
+    {
+        /* If picture owns its data, allocate p_data */
+        if( p_pic->i_flags & OWNER_PICTURE )
+        {
+            p_pic->p_data = malloc( p_pic->i_height * p_pic->i_bytes_per_line );
+            if( p_pic->p_data == NULL )                               /* error */
+            {
+                return( 1 );
+            }
+        }
+        /* If picture is a reference, copy pointer */
+        else
+        {
+#ifdef DEBUG
+            /* Check configuration */
+            if( !(p_cfg->i_properties & VIDEO_CFG_DATA) )
+            {
+                intf_DbgMsg("video debug 115-7: missing picture data\n");
+                return( 1 );
+            }
+#endif
+            p_pic->p_data = p_cfg->p_data;
+        }
+    }
+   
+    return( 0 );
+}
+
+/*******************************************************************************
+ * video_ReadPicture: read a picture from a file
+ *******************************************************************************
+ * This function reads a picture from an openned file. The picture must be
+ * stored in native format, ie: header, according to ReadPictureConfiguration
+ * function specifications, then raw data.
+ *******************************************************************************
+ * Messages type: video, major code 116
+ *******************************************************************************/
+picture_t * video_ReadPicture( int i_file )
+{
+    video_cfg_t     p_cfg;                        /* new picture configuration */    
+    picture_t *     p_newpic;                        /* new picture descriptor */
+    
+    /* Read picture configuration */
+    if( ReadPictureConfiguration( i_file, &p_cfg ) )
+    {
+        return( NULL );
+    }
+    
+    /* Create picture and allocate data */
+    p_newpic = video_CreatePicture( &p_cfg );    
+    if( p_newpic == NULL ) 
+    {
+        return( NULL );        
+    }
+    
+    /* Read data if required */
+    if( (p_newpic->i_type != RGB_BLANK_PICTURE) && (p_newpic->i_type != RGB_BLANK_PICTURE) )
+    {
+        if( read( i_file, p_newpic->p_data, p_newpic->i_height * p_newpic->i_bytes_per_line ) 
+            != p_newpic->i_height * p_newpic->i_bytes_per_line )
+        {
+            intf_ErrMsg("video error 116-2: %s\n", strerror(errno));
+            video_DestroyPicture( p_newpic );
+            return( NULL );            
+        }
+        
+    }
+
+    /* Log and return */
+#ifdef VOUT_DEBUG /* ?? -> video_debug ? */
+    video_PrintPicture( p_newpic, "video debug 116-1: read picture " );
+#endif
+    return( p_newpic );
+}
+
+/* following functions are debugging functions */
+
+/*******************************************************************************
+ * video_PrintPicture: display picture state (debugging function)
+ *******************************************************************************
+ * This function, which is only defined if DEBUG is defined, can be used for
+ * debugging purposes. It prints on debug stream the main characteristics of
+ * a picture. The second parameter is printed in front of data. Note that no
+ * header is printed by default.
+ *******************************************************************************
+ * Messages type: video, major code 141
+ *******************************************************************************/
+#ifdef DEBUG
+void video_PrintPicture( picture_t *p_pic, char *psz_str )
+{
+    char *      psz_type;                                       /* type string */
+    char        sz_flags[9];                                   /* flags string */
+    char        sz_date[MSTRTIME_MAX_SIZE];                     /* date buffer */
+    char        sz_duration[MSTRTIME_MAX_SIZE];             /* duration buffer */
+
+    /* Non empty picture */
+    if( p_pic->i_type != EMPTY_PICTURE )
+    {
+        /* Build picture type information string */
+        switch( p_pic->i_type )
+        {
+        case RGB_BLANK_PICTURE:
+            psz_type = "rgb-blank";
+            break;
+        case PIXEL_BLANK_PICTURE:
+            psz_type = "pixel-blank";
+            break;
+        case RGB_PICTURE:
+            psz_type = "rgb";
+            break;
+        case PIXEL_PICTURE:
+            psz_type = "pixel";
+            break;
+        case RGB_MASK_PICTURE:
+            psz_type = "rgb mask";
+            break;
+        case PIXEL_MASK_PICTURE:
+            psz_type = "pixel mask";
+            break;            
+        default:
+            psz_type = "?";
+            break;                
+        }
+
+        /* Build flags information strings */
+        sz_flags[0] = ( p_pic->i_flags & RESERVED_PICTURE ) ?       'r' : '-';
+        sz_flags[1] = ( p_pic->i_flags & PERMANENT_PICTURE ) ?      'p' : '-';
+        sz_flags[2] = ( p_pic->i_flags & DISPLAYED_PICTURE ) ?      'D' : '-';
+        sz_flags[3] = ( p_pic->i_flags & OWNER_PICTURE ) ?          'O' : '-';
+        sz_flags[4] = ( p_pic->i_flags & DISPLAY_PICTURE ) ?        'd' : '-';
+        sz_flags[5] = ( p_pic->i_flags & DESTROY_PICTURE ) ?        'x' : '-';
+        sz_flags[6] = ( p_pic->i_flags & TRANSPARENT_PICTURE ) ?    't' : '-';
+        sz_flags[7] = ( p_pic->i_flags & OVERLAY_PICTURE ) ?        'o' : '-';
+        sz_flags[8] = '\0';
+
+        /* Print information strings */
+        intf_DbgMsg("%s%p: %s (%s) %dx%d-%d.%d s=%d rc=%d d=%s t=%s\n",
+                    psz_str, p_pic,
+                    psz_type, sz_flags, 
+                    p_pic->i_width, p_pic->i_height, p_pic->i_bpp, p_pic->i_bytes_per_line, 
+                    p_pic->i_stream,
+                    p_pic->i_refcount, 
+                    mstrtime( sz_date, p_pic->date ), mstrtime( sz_duration, p_pic->duration) );
+    }
+    /* Empty picture */
+    else
+    {
+        intf_DbgMsg("%sempty\n", psz_str);
+    }
+}   
+#endif
+
+/* following functions are local */
+
+/*******************************************************************************
+ * ReadPictureConfiguration: read a vout_cfg_t structure from a file
+ *******************************************************************************
+ * This function reads a picture configuration and properties from a file. Note
+ * that some fields of the vout_cft_t, which do not concern pictures, are
+ * ignored. Note that the file is in MSB (little-endian).
+ *******************************************************************************
+ * Messages type: video, major code 151
+ *******************************************************************************/
+static int ReadPictureConfiguration( int i_file, video_cfg_t *p_cfg )
+{
+    byte_t  p_buffer[42];         /* buffer used to store the packed structure */
+
+    /* Read buffer */
+    if( read( i_file, p_buffer, 42 ) != 42 )
+    {
+        intf_ErrMsg("video error 151-1: %s\n", strerror(errno) );        
+        return( errno );
+    }
+
+    /* Parse buffer */
+    p_cfg->i_properties =   ntoh32( *(u32 *)( p_buffer ) );
+    p_cfg->i_type =         *(u8 *)( p_buffer + 4 );
+    p_cfg->i_bpp=           *(u8 *)( p_buffer + 5 );
+    p_cfg->i_width =        ntoh16( *(u16 *)( p_buffer + 6 ) );
+    p_cfg->i_height =       ntoh16( *(u16 *)( p_buffer + 8 ) );
+    p_cfg->i_flags =        ntoh16( *(u16 *)( p_buffer + 10 ) );
+    p_cfg->i_x =            ntoh16( *(s16 *)( p_buffer + 12 ) );
+    p_cfg->i_y =            ntoh16( *(s16 *)( p_buffer + 14 ) );
+    p_cfg->i_h_align =      *(s8 *)( p_buffer + 16 );
+    p_cfg->i_v_align =      *(s8 *)( p_buffer + 17 );
+    p_cfg->i_h_ratio =      *(s8 *)( p_buffer + 19 );
+    p_cfg->i_v_ratio =      *(s8 *)( p_buffer + 19 );
+    p_cfg->i_level  =       *(s8 *)( p_buffer + 20 );
+    p_cfg->i_stream =       *(u8 *)( p_buffer + 21 );
+    p_cfg->date =           ntoh64( *(u64 *)( p_buffer + 22 ) );
+    p_cfg->duration =       ntoh64( *(u64 *)( p_buffer + 30 ) );
+    p_cfg->pixel =          ntoh32( *(u32 *)( p_buffer + 38 ) );
+   
+    return( 0 );
+}
+
+
diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c
new file mode 100644 (file)
index 0000000..276655d
--- /dev/null
@@ -0,0 +1,1636 @@
+/*******************************************************************************
+ * video_output.c : video output thread
+ * (c)1999 VideoLAN
+ *******************************************************************************
+ * This module describes the programming interface for video output threads.
+ * It includes functions allowing to open a new thread, send pictures to a
+ * thread, and destroy a previously oppenned video output thread.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h> 
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+
+#include "common.h"
+#include "config.h"
+#include "mtime.h"
+#include "thread.h"
+
+#include "video.h"
+#include "video_graphics.h"
+#include "video_output.h"
+#include "video_x11.h"
+
+#include "intf_msg.h"
+
+/*
+ * Local prototypes
+ */
+static int      CheckConfiguration  ( video_cfg_t *p_cfg );
+static int      InitThread          ( vout_thread_t *p_vout );
+static void     RunThread           ( vout_thread_t *p_vout );
+static void     ErrorThread         ( vout_thread_t *p_vout );
+static void     EndThread           ( vout_thread_t *p_vout );
+
+static picture_t *  FindPicture     ( vout_thread_t *p_vout );
+static void         EmptyPicture    ( vout_thread_t *p_vout, picture_t *p_pic );
+static mtime_t      FirstPass       ( vout_thread_t *p_vout, mtime_t current_date );
+static void         SecondPass      ( vout_thread_t *p_vout, mtime_t display_date );
+static void         SortPictures    ( vout_thread_t *p_vout, picture_t **pp_picture );
+static void         RenderPicture   ( vout_thread_t *p_vout, picture_t *p_pic );
+static void         ClipPicture     ( int *pi_ofs, int i_size, int *pi_pic_ofs, int *pi_pic_size,
+                                      int i_ratio, int i_placement );
+
+/*******************************************************************************
+ * vout_CreateThread: creates a new video output thread
+ *******************************************************************************
+ * This function creates a new video output thread, and returns a pointer
+ * to its description. On error, it returns NULL.
+ * Following configuration properties are used:
+ *  VIDEO_CFG_SIZE      video heap maximal size
+ *  VIDEO_CFG_WIDTH     window width
+ *  VIDEO_CFG_HEIGHT    window height 
+ * Using X11 display method (the only one supported yet):
+ *  VIDEO_CFG_DISPLAY   display used
+ *  VIDEO_CFG_TITLE     window title
+ *  VIDEO_CFG_SHM_EXT   try to use XShm extension
+ * If pi_status is NULL, then the function will block until the thread is ready.
+ * If not, pi_error will be updated using one of the THREAD_* constants.
+ *******************************************************************************/
+vout_thread_t *vout_CreateThread( video_cfg_t *p_cfg, int *pi_status )
+{
+    vout_thread_t * p_vout;                               /* thread descriptor */
+    int             i_status;                                 /* thread status */
+
+    /*
+     * Check configuration 
+     */
+    if( CheckConfiguration( p_cfg ) )
+    {
+        return( NULL );
+    }
+
+    /* Allocate descriptor and initialize flags */
+    p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
+    if( p_vout == NULL )                                              /* error */
+    {
+        return( NULL );
+    }
+    if( vout_X11AllocOutputMethod( p_vout, p_cfg ) )
+    {
+        free( p_vout );
+        return( NULL );        
+    }
+
+    /* Copy configuration */
+    p_vout->i_max_pictures = p_cfg->i_size;    
+    p_vout->i_width = p_cfg->i_width;
+    p_vout->i_height = p_cfg->i_height;
+   
+    /* Set status */
+    p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
+    *p_vout->pi_status = THREAD_CREATE;
+    
+    /* Initialize flags */
+    p_vout->b_die = 0;
+    p_vout->b_error = 0;    
+    p_vout->b_active = 0;
+
+    /* Create thread and set locks */
+    pthread_mutex_init( &p_vout->streams_lock, NULL );
+    pthread_mutex_init( &p_vout->pictures_lock, NULL );
+    if( pthread_create( &p_vout->thread_id, NULL, (void *) RunThread, (void *) p_vout) )
+    {
+        intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
+        intf_DbgMsg("failed\n");        
+        vout_X11FreeOutputMethod( p_vout );        
+        free( p_vout );
+        return( NULL );
+    }   
+
+    /* If status is NULL, wait until the thread is created */
+    if( pi_status == NULL )
+    {
+        do
+        {            
+            msleep( THREAD_SLEEP );
+        }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR) 
+                && (i_status != THREAD_FATAL) );
+        if( i_status != THREAD_READY )
+        {
+            intf_DbgMsg("failed\n");            
+            return( NULL );            
+        }        
+    }
+
+    intf_DbgMsg("succeeded -> %p\n", p_vout);    
+    return( p_vout );
+}
+
+/*******************************************************************************
+ * vout_DestroyThread: destroys a previously created thread
+ *******************************************************************************
+ * Destroy a terminated thread. 
+ * The function will request a destruction of the specified thread. If pi_error
+ * is NULL, it will return once the thread is destroyed. Else, it will be 
+ * update using one of the THREAD_* constants.
+ *******************************************************************************/
+void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
+{  
+    int     i_status;                                         /* thread status */
+
+    /* Set status */
+    p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
+    *p_vout->pi_status = THREAD_DESTROY;
+     
+    /* Request thread destruction */
+    p_vout->b_die = 1;
+
+    /* If status is NULL, wait until thread has been destroyed */
+    if( pi_status )
+    {
+        do
+        {
+            msleep( THREAD_SLEEP );
+        }while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR) 
+                && (i_status != THREAD_FATAL) );   
+    }
+    
+    intf_DbgMsg("%p -> succeeded\n", p_vout);
+}
+
+/*******************************************************************************
+ * vout_DisplayPicture: add a picture to a thread
+ *******************************************************************************
+ * The picture will be placed in an empty place of the video heap of the thread.
+ * If this is impossible, NULL will be returned. Else, the original picture
+ * is destroyed.
+ *******************************************************************************/
+picture_t * vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    picture_t * p_newpic;                                 /* new picture index */
+
+    p_newpic = FindPicture( p_vout );
+    if( p_newpic != NULL )
+    {        
+        /* Copy picture information */
+        video_CopyPictureDescriptor( p_newpic, p_pic );
+        p_newpic->p_data = p_pic->p_data;
+        free( p_pic );
+
+        /* Update heap size and stats */
+        p_vout->i_pictures++;
+#ifdef STATS
+        p_vout->c_pictures++;
+        p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
+#endif
+    }
+
+    /* Release lock and return */
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * vout_DisplayPictureCopy: copy a picture to a thread
+ *******************************************************************************
+ * The picture will be copied in an empty place of the video heap of the thread.
+ * If this is impossible, NULL will be returned. The picture used is the copy
+ * of the picture passed as argument, which remains usable.
+ * The picture data are copied only if the original picture owns its own data.
+ * The link reference counter is set to 0.
+ *******************************************************************************/
+picture_t * vout_DisplayPictureCopy( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    picture_t * p_newpic;                                 /* new picture index */
+
+    p_newpic = FindPicture( p_vout );
+    if( p_newpic != NULL )
+    {        
+        /* Copy picture information */
+        video_CopyPictureDescriptor( p_newpic, p_pic );
+
+        /* If source picture owns its data, make a copy */
+        if( p_pic->i_flags & OWNER_PICTURE )
+        {      
+            p_newpic->p_data = malloc( p_pic->i_height * p_pic->i_bytes_per_line );
+            if( p_newpic->p_data == NULL )                            /* error */
+            {
+                intf_ErrMsg("vout error: %s\n", strerror(ENOMEM) );
+
+                /* Restore type and flags */
+                p_newpic->i_type = EMPTY_PICTURE;
+                p_newpic->i_flags = 0;
+                pthread_mutex_unlock( &p_vout->pictures_lock );            
+                return( NULL );
+            }
+            memcpy( p_newpic->p_data, p_pic->p_data,
+                    p_pic->i_height * p_pic->i_bytes_per_line );
+        }
+        /* If source picture does not owns its data, copy the reference */
+        else              
+        {
+            p_newpic->p_data = p_pic->p_data;
+        }
+        p_newpic->i_refcount = 0;
+            
+        /* Update heap size and stats */
+        p_vout->i_pictures++;
+#ifdef STATS
+        p_vout->c_pictures++;
+        p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
+#endif
+    }
+
+    /* Release lock and return */
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * vout_DisplayPictureReplicate: copy a picture to a thread
+ *******************************************************************************
+ * The picture will be copied in an empty place of the video heap of the thread.
+ * If this is impossible, NULL will be returned. The picture used is the copy
+ * of the picture passed as argument, which remains usable.
+ * The picture data are a reference to original picture (the picture in the heap
+ * does not owns its data). Link reference counter is set to 0.
+ *******************************************************************************/
+picture_t * vout_DisplayPictureReplicate( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    picture_t * p_newpic;                            /* new picture descrpitor */
+
+    p_newpic = FindPicture( p_vout );
+    if( p_newpic != NULL )
+    {        
+        /* Copy picture information */
+        video_CopyPictureDescriptor( p_newpic, p_pic );   
+        p_newpic->i_flags &= ~OWNER_PICTURE;
+        p_newpic->p_data = p_pic->p_data;
+        p_newpic->i_refcount = 0;
+            
+        /* Update heap size and stats */
+        p_vout->i_pictures++;
+#ifdef STATS
+        p_vout->c_pictures++;
+        p_vout->p_stream[ p_newpic->i_stream ].c_pictures++;
+#endif
+    }
+
+   /* Release lock and return */
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * vout_DisplayReservedPicture: add a reserved picture to a thread
+ *******************************************************************************
+ * The picture will be placed in an empty place of the video heap of the thread.
+ * If the picture has been declared on reservation as permanent, it remains
+ * usable. Else, it will be destroyed by this call.
+ * This function can't fail, since it is the purpose of picture reservation to
+ * provide such a robust mechanism.
+ *******************************************************************************/
+picture_t * vout_DisplayReservedPicture( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    pthread_mutex_lock( &p_vout->pictures_lock );
+
+    /* Remove reservation flag */
+    p_pic->i_flags &= ~RESERVED_PICTURE;
+
+#ifdef STATS
+    /* Update stats */
+    p_vout->c_pictures++;
+    p_vout->p_stream[ p_pic->i_stream ].c_pictures++;
+#endif
+
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+    return( p_pic );
+}
+
+/*******************************************************************************
+ * vout_CreateReservedPicture: reserve a place for a new picture in heap
+ *******************************************************************************
+ * This function create a reserved image in the video output heap. 
+ * A null pointer is returned if the function fails. 
+ * Following configuration properties are used:
+ *  VIDEO_CFG_WIDTH     picture width (required)
+ *  VIDEO_CFG_HEIGHT    picture height (required)
+ *  VIDEO_CFG_TYPE      picture type (required)
+ *  VIDEO_CFG_FLAGS     flags
+ *  VIDEO_CFG_BPP       padded bpp (required for pixel pictures)
+ *  VIDEO_CFG_POSITION  position in output window
+ *  VIDEO_CFG_ALIGN     base position in output window
+ *  VIDEO_CFG_RATIO     display ratio
+ *  VIDEO_CFG_LEVEL     overlay hierarchical level  
+ *  VIDEO_CFG_REFCOUNT  links reference counter
+ *  VIDEO_CFG_STREAM    video stream id
+ *  VIDEO_CFG_DATE      display date
+ *  VIDEO_CFG_DURATION  duration for overlay pictures
+ *  VIDEO_CFG_PIXEL     pixel value for mask pictures
+ *  VIDEO_CFG_DATA      picture data (required if not owner and non blank)
+ * Pictures created using this function are always reserved, even if they did
+ * not had the RESERVED_PICTURE flag set.
+ * This function is very similar to the video_CreatePicture function.
+ *******************************************************************************/
+picture_t *vout_CreateReservedPicture( vout_thread_t *p_vout, video_cfg_t *p_cfg )
+{
+    picture_t * p_newpic;                                 /* new picture index */
+
+    p_newpic = FindPicture( p_vout );
+    if( p_newpic != NULL )
+    {        
+        /* Create new picture */
+        if( video_CreatePictureBody( p_newpic, p_cfg ) )
+        {   
+            intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));             
+            /* Error: restore type and flags */
+            p_newpic->i_type = EMPTY_PICTURE;
+            p_newpic->i_flags = 0;
+            pthread_mutex_unlock( &p_vout->pictures_lock );
+            return( NULL );
+        }
+        p_newpic->i_flags |= RESERVED_PICTURE;
+
+        /* Update heap size, release lock and return */
+        p_vout->i_pictures++;
+    }
+
+    /* Release lock and return */
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * vout_ReservePicture: reserve a place for a picture in heap
+ *******************************************************************************
+ * This function transforms an existing picture in a reserved picture in a
+ * video heap. The picture will be placed in an empty place of the video heap 
+ * of the thread. If this is impossible, NULL will be returned. Else, the 
+ * original picture is destroyed.
+ *******************************************************************************/
+picture_t *vout_ReservePicture( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    picture_t * p_newpic;                                 /* new picture index */
+
+    p_newpic = FindPicture( p_vout );
+    if( p_newpic != NULL )
+    {        
+        /* Copy picture information */
+        video_CopyPictureDescriptor( p_newpic, p_pic );
+        p_newpic->p_data = p_pic->p_data;
+        p_newpic->i_flags |= RESERVED_PICTURE;
+        free( p_pic );           
+
+        /* Update heap size */
+        p_vout->i_pictures++;
+    }
+
+    /* Release lock and return */
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * vout_ReservePictureCopy: reserve a place for a picture in heap
+ *******************************************************************************
+ * This function returns a pointer to a picture which can be processed. The
+ * returned picture is a copy of the one passed as parameter.
+ * This function returns NULL if the reservation fails.
+ *******************************************************************************/
+picture_t *vout_ReservePictureCopy( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    picture_t * p_newpic;                                 /* new picture index */
+
+    p_newpic = FindPicture( p_vout );
+    if( p_newpic != NULL )
+    {        
+        /* Copy picture information */
+        video_CopyPictureDescriptor( p_newpic, p_pic );
+
+        /* If source picture owns its data, make a copy */
+        if( p_pic->i_flags & OWNER_PICTURE )
+        {              
+            p_newpic->p_data = malloc( p_pic->i_height * p_pic->i_bytes_per_line );
+            if( p_newpic->p_data == NULL )                            /* error */
+            {
+                intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));                    
+                /* Restore type and flags */
+                p_newpic->i_type = EMPTY_PICTURE;
+                p_newpic->i_flags = 0;
+                pthread_mutex_unlock( &p_vout->pictures_lock );   
+                return( NULL );
+            }
+            memcpy( p_newpic->p_data, p_pic->p_data,
+                    p_pic->i_height * p_pic->i_bytes_per_line );
+        }   
+        /* If source picture does not owns its data, copy the reference */
+        else              
+        {
+            p_newpic->p_data = p_pic->p_data;
+        }
+        p_newpic->i_refcount = 0;
+        p_newpic->i_flags |= RESERVED_PICTURE;
+
+        /* Update heap size */
+        p_vout->i_pictures++;
+    }
+
+    /* Release lock and return */
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * vout_ReservePictureReplicate: reserve a place for a picture in heap
+ *******************************************************************************
+ * This function returns a pointer to a picture which can be processed. The
+ * returned picture is a copy of the one passed as parameter.
+ * This function returns NULL if the reservation fails.
+ *******************************************************************************/
+picture_t *vout_ReservePictureReplicate( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    picture_t * p_newpic;                                 /* new picture index */
+
+    p_newpic = FindPicture( p_vout );
+    if( p_newpic != NULL )
+    {        
+        /* Copy picture information */
+        video_CopyPictureDescriptor( p_newpic, p_pic );
+        p_newpic->p_data = p_pic->p_data;
+        p_newpic->i_refcount = 0;
+        p_newpic->i_flags &= ~OWNER_PICTURE;
+        p_newpic->i_flags |= RESERVED_PICTURE;
+
+        /* Update heap size */
+        p_vout->i_pictures++;
+    }
+
+    /* Release lock and return */
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+    return( p_newpic );
+}
+
+/*******************************************************************************
+ * vout_RemovePicture: remove a permanent or reserved picture from the heap
+ *******************************************************************************
+ * This function frees a previously reserved picture or a permanent
+ * picture. The picture data is destroyed if required. 
+ *******************************************************************************/
+void vout_RemovePicture( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    pthread_mutex_lock( &p_vout->pictures_lock );
+
+    /* Mark picture for destruction */
+    p_pic->i_flags |= DESTROY_PICTURE;
+    /* Since permanent pictures can normally not be destroyed by the vout thread,   
+     * the permanent flag needs to be removed */
+    p_pic->i_flags &= ~PERMANENT_PICTURE;
+    intf_DbgMsg("%p -> picture %p removing requested\n", p_vout, p_pic );
+
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+}
+
+/*******************************************************************************
+ * vout_RefreshPermanentPicture: 
+ *******************************************************************************
+ * Set the DISPLAYED_PICTURE flag of a permanent picture to 0, allowing to
+ * display it again if it not overlaid. The display date is also updated.
+ *******************************************************************************/
+void vout_RefreshPermanentPicture( vout_thread_t *p_vout, picture_t *p_pic, 
+                                   mtime_t display_date )
+{
+    pthread_mutex_lock( &p_vout->pictures_lock );
+    p_pic->i_flags &= ~DISPLAYED_PICTURE;
+    p_pic->date = display_date;
+    pthread_mutex_lock( &p_vout->pictures_lock );
+}
+
+/*******************************************************************************
+ * vout_LinkPicture: increment reference counter of a picture
+ *******************************************************************************
+ * This function increment the reference counter of a picture in the video
+ * heap.
+ *******************************************************************************/
+void vout_LinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    pthread_mutex_lock( &p_vout->pictures_lock );
+    p_pic->i_refcount++;
+    pthread_mutex_unlock( &p_vout->pictures_lock );
+}
+
+/*******************************************************************************
+ * vout_UnlinkPicture: decrement reference counter of a picture
+ *******************************************************************************
+ * This function decrement the reference counter of a picture in the video heap.
+ *******************************************************************************/
+void vout_UnlinkPicture( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    pthread_mutex_lock( &p_vout->pictures_lock );
+    p_pic->i_refcount--;
+#ifdef DEBUG
+    if( p_pic->i_refcount < 0 )
+    {
+        intf_DbgMsg("%p -> picture %p i_refcount < 0\n", p_vout, p_pic);
+    }
+#endif
+    pthread_mutex_unlock( &p_vout->pictures_lock );    
+}
+
+/*******************************************************************************
+ * vout_CreateStream: get a new stream id
+ *******************************************************************************
+ * Stream ids are used to create list of pictures in a video output thread.
+ * The function returns an unused stream id, or a negative number on error.
+ * Stream id 0 is never returned, since it is not a stream descriptor but a set 
+ * of independant images.
+ *******************************************************************************/
+int vout_CreateStream( vout_thread_t *p_vout )
+{
+    int     i_index;                                           /* stream index */
+
+    /* Find free (inactive) stream id */
+    pthread_mutex_lock( &p_vout->streams_lock );                   /* get lock */
+    for( i_index = 1; i_index < VOUT_MAX_STREAMS; i_index++ )  /* find free id */
+    {
+        if( p_vout->p_stream[i_index].i_status == VOUT_INACTIVE_STREAM )
+        {
+            /* Initialize stream */
+            p_vout->p_stream[i_index].i_status = VOUT_ACTIVE_STREAM; 
+#ifdef STATS
+            p_vout->p_stream[i_index].c_pictures = 0;
+            p_vout->p_stream[i_index].c_rendered_pictures = 0;
+#endif
+            
+            /* Return stream id */
+            intf_DbgMsg("%p -> stream %i created\n", p_vout, i_index);
+            pthread_mutex_unlock( &p_vout->streams_lock );     /* release lock */
+            return( i_index );                                    /* return id */
+        }
+    }
+   
+    /* Failure: all streams id are already active */
+    intf_DbgMsg("%p -> failed\n", p_vout);    
+    pthread_mutex_unlock( &p_vout->streams_lock );    
+    return( -1 );
+}
+
+/*******************************************************************************
+ * vout_EndStream: free a previously allocated stream
+ *******************************************************************************
+ * This function must be called once a stream is no more used. It can be called
+ * even if there are remaining pictures in the video heap, since the stream will
+ * only be destroyed once all pictures of the stream have been displayed.
+ *******************************************************************************/
+void vout_EndStream( vout_thread_t *p_vout, int i_stream )
+{
+    pthread_mutex_lock( &p_vout->streams_lock );                   /* get lock */
+    p_vout->p_stream[i_stream].i_status = VOUT_ENDING_STREAM;   /* mark stream */
+    pthread_mutex_unlock( &p_vout->streams_lock );             /* release lock */
+    intf_DbgMsg("%p -> stream %d\n", p_vout, i_stream);
+}
+
+/*******************************************************************************
+ * vout_DestroyStream: free a previously allocated stream
+ *******************************************************************************
+ * This function must be called once a stream is no more used. It can be called
+ * even if there are remaining pictures in the video heap, since all pictures
+ * will be removed before the stream is destroyed.
+ *******************************************************************************/
+void vout_DestroyStream( vout_thread_t *p_vout, int i_stream )
+{
+    pthread_mutex_lock( &p_vout->streams_lock );                   /* get lock */
+    p_vout->p_stream[i_stream].i_status = VOUT_DESTROYED_STREAM;/* mark stream */
+    pthread_mutex_unlock( &p_vout->streams_lock );             /* release lock */
+    intf_DbgMsg("%p -> stream %d\n", p_vout, i_stream);
+}
+
+/* following functions are debugging functions */
+
+/*******************************************************************************
+ * vout_PrintHeap: display heap state (debugging function)
+ *******************************************************************************
+ * This functions, which is only defined if DEBUG is defined, can be used 
+ * for debugging purposes. It prints on debug stream the current state of the   
+ * heap. The second parameter is used to identify the output.
+ *******************************************************************************/
+#ifdef DEBUG
+void vout_PrintHeap( vout_thread_t *p_vout, char *psz_str )
+{
+    int         i_picture;                                    /* picture index */
+
+    intf_Msg("vout: --- thread %p heap status (%s) ---\n", p_vout, psz_str );
+
+    /* Browse all pictures in heap */
+    for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
+    {
+        if( p_vout->p_picture[i_picture].i_type != EMPTY_PICTURE )
+        {
+            video_PrintPicture( &p_vout->p_picture[i_picture], "vout: ..." );
+        }
+    }   
+
+    intf_Msg("vout: --- end ---\n");
+}
+#endif
+
+/* following functions are local */
+
+/*******************************************************************************
+ * CheckConfiguration: check vout_CreateThread() configuration
+ *******************************************************************************
+ * Set default parameters where required. In DEBUG mode, check if configuration
+ * is valid.
+ *******************************************************************************/
+static int CheckConfiguration( video_cfg_t *p_cfg )
+{
+    /* Heap size */
+    if( !( p_cfg->i_properties & VIDEO_CFG_SIZE ) )
+    {
+        p_cfg->i_size = VOUT_HEAP_SIZE;
+    }
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * InitThread: initialize video output thread
+ *******************************************************************************
+ * This function is called from RunThread and performs the second step of the
+ * initialization. It returns 0 on success. Note that the thread's flag are not
+ * modified inside this function.
+ *******************************************************************************/
+static int InitThread( vout_thread_t *p_vout )
+{
+    int     i_index;                                          /* generic index */    
+
+    /* Update status */
+    *p_vout->pi_status = THREAD_START;    
+    
+    /* Allocate video heap */
+    p_vout->p_picture = 
+        (picture_t *) malloc( sizeof(picture_t) * p_vout->i_max_pictures );
+    if( !p_vout->p_picture )                                          /* error */
+    {        
+        intf_ErrMsg("vout error: %s\n", strerror(ENOMEM));
+        intf_DbgMsg("%p -> failed\n", p_vout);
+        *p_vout->pi_status = THREAD_ERROR;        
+        return( 1 );
+    }
+
+    /* Initialize pictures */    
+    for( i_index = 0; i_index < p_vout->i_max_pictures; i_index++)
+    {
+        p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
+        p_vout->p_picture[i_index].i_flags = 0;
+    }
+
+    /* Initialize video streams - note that stream 0 is always active */
+    p_vout->p_stream[0].i_status = VOUT_ACTIVE_STREAM; 
+#ifdef STATS
+    p_vout->p_stream[0].c_pictures = 0;
+    p_vout->p_stream[0].c_rendered_pictures = 0;
+#endif
+    for( i_index = 1; i_index < VOUT_MAX_STREAMS; i_index++ )
+    {
+        p_vout->p_stream[i_index].i_status = VOUT_INACTIVE_STREAM;
+    }
+    /* Initialize other properties */
+    p_vout->i_pictures = 0;
+#ifdef STATS
+    p_vout->c_loops = 0;
+    p_vout->c_idle_loops = 0;
+    p_vout->c_pictures = 0;
+    p_vout->c_rendered_pictures = 0;
+#endif
+
+    /* Initialize output method - width, height, screen depth and bytes per 
+     * pixel are initialized by this call. */
+    if( vout_X11CreateOutputMethod( p_vout ) )                        /* error */
+    {
+        free( p_vout->p_picture );
+        *p_vout->pi_status = THREAD_ERROR;        
+        return( 1 );
+    } 
+
+    /* Mark thread as running and return */
+    *p_vout->pi_status = THREAD_READY;    
+    intf_DbgMsg("%p -> succeeded\n", p_vout);    
+    return(0);    
+}
+
+/*******************************************************************************
+ * RunThread: video output thread
+ *******************************************************************************
+ * Video output thread. This function does only returns when the thread is
+ * terminated. It handles the pictures arriving in the video heap and the
+ * display device events.
+ *******************************************************************************/
+static void RunThread( vout_thread_t *p_vout)
+{
+    int             i_stream;                                  /* stream index */
+    int             i_picture;                                /* picture index */
+    int             i_active_streams;              /* number of active streams */
+    int             i_err;                                       /* error code */
+    mtime_t         display_date;                              /* display date */    
+    mtime_t         current_date;                              /* current date */
+    picture_t *     pp_sorted_picture[VOUT_MAX_PICTURES];   /* sorted pictures */
+#ifdef VOUT_DEBUG
+    char            sz_date[MSTRTIME_MAX_SIZE];                 /* date buffer */
+#endif
+
+    /* 
+     * Initialize thread and free configuration 
+     */
+    p_vout->b_error = InitThread( p_vout );
+    if( p_vout->b_error )
+    {
+        free( p_vout );                                  /* destroy descriptor */
+        return;        
+    }    
+
+    /*
+     * Main loop - it is not executed if an error occured during
+     * initialization
+     */
+    while( (!p_vout->b_die) && (!p_vout->b_error) )
+    {
+        /* Get locks on pictures and streams */
+        pthread_mutex_lock( &p_vout->streams_lock );
+        pthread_mutex_lock( &p_vout->pictures_lock );
+        
+        /* Initialise streams: clear images from all streams */
+        for( i_stream = 0; i_stream < VOUT_MAX_STREAMS; i_stream++ )
+        {
+            p_vout->p_stream[i_stream].p_next_picture = NULL;
+        }
+
+        /* First pass: a set of pictures is selected - all unselected pictures
+         * which should be removed are removed, and a display date is computed.
+         * If a stream has no next_picture after this step, then the heap does
+         * not include any picture from that stream. */
+        current_date = mdate();
+        display_date = FirstPass( p_vout, current_date );
+
+        /* Stream management: streams which are in ENDING or DESTROYED state and
+         * which have no more pictures are destroyed */
+        for( i_active_streams = i_stream = 1; 
+             i_stream < VOUT_MAX_STREAMS; 
+             i_stream++ )
+        {    
+            switch( p_vout->p_stream[i_stream].i_status )
+            {
+            case VOUT_ACTIVE_STREAM:                          /* active stream */
+                i_active_streams++;
+                break;
+
+            case VOUT_ENDING_STREAM:            /* ending or destroyed streams */
+            case VOUT_DESTROYED_STREAM:
+                /* Those streams can be destroyed if there are no more picture
+                 * remaining. This should always be the case for destroyed
+                 * streams, except if it includes permanent pictures */
+                if( p_vout->p_stream[i_stream].p_next_picture == NULL )
+                {
+                    p_vout->p_stream[i_stream].i_status = VOUT_INACTIVE_STREAM;
+#ifdef STATS                   
+                    intf_DbgMsg("%p -> stream %d destroyed %d pictures, %d rendered pictures\n", 
+                                p_vout, i_stream, p_vout->p_stream[i_stream].c_pictures, 
+                                p_vout->p_stream[i_stream].c_rendered_pictures );
+#else
+                    intf_DbgMsg("%p -> stream %d destroyed\n", p_vout, i_stream );
+#endif
+                }
+                /* If the stream can't be destroyed, it means it is still 
+                 * active - in that case, the next image pointer needs to be
+                 * cleard */
+                else
+                {
+                    i_active_streams++;
+                    p_vout->p_stream[i_stream].p_next_picture = NULL;
+                }
+                break;
+            }
+        }
+      
+        /* From now until next loop, only next_picture field in streams
+         * will be used, and selected pictures structures won't modified.
+         * Therefore, locks can be released */
+        pthread_mutex_unlock( &p_vout->pictures_lock );
+        pthread_mutex_unlock( &p_vout->streams_lock );
+
+        /* If there are some pictures to display, then continue */
+        if( display_date != LAST_MDATE )
+        {
+            /* Increase display_date if it is too low */
+            if( display_date < current_date + VOUT_DISPLAY_DELAY )
+            {
+                intf_Msg("vout: late picture(s) detected\n");
+                display_date = current_date + VOUT_DISPLAY_DELAY;
+            }
+#ifdef VOUT_DEBUG
+            intf_DbgMsg("%p -> display_date is %s\n", p_vout, 
+                        mstrtime( sz_date, display_date ));
+#endif
+
+            /* Second pass: a maximum of one picture per stream is selected
+             * (except for stream 0), and some previously selected pictures may
+             * be removed */
+            SecondPass( p_vout, display_date );
+#ifdef VOUT_DEBUG
+            vout_PrintHeap( p_vout, "ready for rendering" );
+#endif
+            /* Rendering: sort pictures and render them */
+            SortPictures( p_vout, pp_sorted_picture );
+            for( i_picture = 0; pp_sorted_picture[i_picture] != NULL; i_picture++ )
+            {
+                /* Render picture */
+                RenderPicture( p_vout, pp_sorted_picture[i_picture] );             
+            }
+            
+            /* Handle output method events - this function can return a negative
+             * value, which means a fatal error and the end of the display loop 
+             * (i.e. the X11 window has been destroyed), a positive one, meaning
+             * a modification occured in the pictures and they do have need to 
+             * be displayed, or 0 */
+            i_err = vout_X11ManageOutputMethod( p_vout );
+            if( !i_err )
+            {
+                /* Sleep and display */
+                mwait( display_date );
+                vout_X11DisplayOutput( p_vout );
+            }
+            else if( i_err < 0 )
+            {                
+                /* An error occured, and the thread must terminate immediately,
+                 * without displaying anything - setting b_error to 1 cause the
+                 * immediate end of the main while() loop. */
+                p_vout->b_error = 1;
+            }
+        }        
+        /* If there is nothing to display, just handle events and sleep a while, 
+         * hopping there will be something to do later */
+        else    
+        {
+            if( vout_X11ManageOutputMethod( p_vout ) < 0)
+            {
+                p_vout->b_error = 1;
+                
+            }
+            else
+            {   
+                msleep( VOUT_IDLE_SLEEP );
+            }            
+#ifdef STATS
+            /* Update counters */
+            p_vout->c_idle_loops++;
+#endif
+        }        
+
+#ifdef STATS
+        /* Update counters */
+        p_vout->c_loops++;
+#endif
+    } 
+
+    /*
+     * Error loop
+     */
+    if( p_vout->b_error )
+    {
+        ErrorThread( p_vout );        
+    }
+
+    /* End of thread */
+    EndThread( p_vout );
+}
+
+/*******************************************************************************
+ * ErrorThread: RunThread() error loop
+ *******************************************************************************
+ * This function is called when an error occured during thread main's loop. The
+ * thread can still receive feed, but must be ready to terminate as soon as
+ * possible.
+ *******************************************************************************/
+static void ErrorThread( vout_thread_t *p_vout )
+{
+    int i_picture;                                            /* picture index */    
+    
+    /* Wait until a `die' order */
+    while( !p_vout->b_die )
+    {
+        /* Get lock on pictures */
+        pthread_mutex_lock( &p_vout->pictures_lock );     
+
+        /* Try to remove all pictures - only removable pictures will be
+         * removed */
+        for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
+        {   
+            if( p_vout->p_picture[i_picture].i_type != EMPTY_PICTURE )
+            {
+                EmptyPicture( p_vout, &p_vout->p_picture[i_picture] );
+            }
+        }
+        
+        /* Release locks on pictures */
+        pthread_mutex_unlock( &p_vout->pictures_lock );
+
+        /* Sleep a while */
+        msleep( VOUT_IDLE_SLEEP );                
+    }
+}
+
+/*******************************************************************************
+ * EndThread: thread destruction
+ *******************************************************************************
+ * This function is called when the thread ends after a sucessfull 
+ * initialization.
+ *******************************************************************************/
+static void EndThread( vout_thread_t *p_vout )
+{
+    int *   pi_status;                                        /* thread status */
+#ifdef DEBUG
+    int     i_stream;                                    /* video stream index */
+#endif
+        
+    /* Store status */
+    pi_status = p_vout->pi_status;    
+    *pi_status = THREAD_END;    
+    
+#ifdef DEBUG
+    /* Check for remaining pictures or video streams */
+    if( p_vout->i_pictures )
+    {
+        intf_DbgMsg("%p -> remaining pictures\n", p_vout);        
+    }   
+    for( i_stream = 1; 
+         (i_stream < VOUT_MAX_STREAMS) && (p_vout->p_stream[i_stream].i_status == VOUT_INACTIVE_STREAM);
+         i_stream++ )
+    {
+        ;        
+    }    
+    if( i_stream != VOUT_MAX_STREAMS )
+    {
+        intf_DbgMsg("%p -> remaining video streams\n", p_vout);        
+    }    
+#endif
+
+    /* Destroy thread structures allocated by InitThread */
+    vout_X11DestroyOutputMethod( p_vout );            /* destroy output method */
+    free( p_vout->p_picture );                                    /* free heap */
+    free( p_vout );
+
+    /* Update status */
+    *pi_status = THREAD_OVER;    
+    intf_DbgMsg("%p\n", p_vout);
+}
+
+/*******************************************************************************
+ * FindPicture: find an empty picture in a video heap
+ *******************************************************************************
+ * This function is used by most of the vout_*Picture*() functions. It locks the
+ * video heap, look for an empty picture in it and return a pointer to this
+ * picture, or NULL if the heap is full.
+ * Not that the heap is not unlocked when this function returns, and it needs 
+ * to be donne by the calling function.
+ *******************************************************************************/
+static picture_t *FindPicture( vout_thread_t *p_vout )
+{
+    int         i_picture;                                    /* picture index */
+
+    /* Get lock */
+    pthread_mutex_lock( &p_vout->pictures_lock );
+
+    /* Look for an empty place */
+    for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
+    {
+        /* An empty place has been found: return */
+        if( p_vout->p_picture[i_picture].i_type == EMPTY_PICTURE )
+        {
+            return( &p_vout->p_picture[i_picture] );
+        }
+    }
+
+    /* Nothing has been found */
+    return( NULL );    
+}
+
+/*******************************************************************************
+ * EmptyPicture: empty a picture without destroying its descriptor
+ *******************************************************************************
+ * When a picture is no more used in video heap or must be destroyed, this
+ * function is called to destroy associated data and set picture type to empty.
+ * Only non permanent and unlinked pictures can be destroyed. 
+ * Only non-empty pictures should be sent to this function.
+ * All picture flags are cleared, and the heap size is decreased.
+ *******************************************************************************
+ * Messages type: vout, major code 15
+ *******************************************************************************/
+static void EmptyPicture( vout_thread_t *p_vout, picture_t *p_pic )
+{    
+#ifdef DEBUG
+    /* Check if picture is non-empty */
+    if( p_pic->i_type == EMPTY_PICTURE )
+    {
+        intf_DbgMsg("%p -> trying to remove empty picture %p\n",
+                    p_vout, p_pic);
+        return;
+    }
+#endif
+
+    /* Mark as ready for destruction */
+    p_pic->i_flags |= DESTROY_PICTURE;
+    p_pic->i_flags &= ~DISPLAY_PICTURE;
+
+    /* If picture is no more referenced and is not permanent, destroy */
+    if( (p_pic->i_refcount == 0) && !(p_pic->i_flags & PERMANENT_PICTURE) )
+    {
+        /* If picture owns its data, free them */
+        if( p_pic->i_flags & OWNER_PICTURE )
+        {
+            free( p_pic->p_data );
+        }           
+        p_pic->i_type = EMPTY_PICTURE;
+        p_pic->i_flags = 0;  
+        p_vout->i_pictures--;
+        intf_DbgMsg("%p -> picture %p removed\n", p_vout, p_pic);
+    }
+}
+
+/*******************************************************************************
+ * FirstPass: first pass of the vout thread on pictures
+ *******************************************************************************
+ * A set of pictures is selected according to the current date and their display
+ * date. Some unselected and already displayed pictures are removed, and the
+ * next display date is computed. 
+ * The next_picture fields of all the streams are updated. Note that streams
+ * are initialized before this function is called.
+ *******************************************************************************
+ * Messages type: vout, major code 18
+ *******************************************************************************/
+static mtime_t FirstPass( vout_thread_t *p_vout, mtime_t current_date )
+{
+    mtime_t         display_date;                              /* display date */
+    int             i_picture;                                /* picture index */
+    picture_t *     p_picture;                   /* picture pointer (shortcut) */
+    /* Set up date */
+    display_date = LAST_MDATE;
+        
+    /* Browse all pictures */
+    for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
+    {
+        p_picture = &p_vout->p_picture[i_picture];
+
+        /* Empty pictures are never selected */
+        if( p_picture->i_type == EMPTY_PICTURE )
+        {
+            ;
+        }               
+        /* Destroyed pictures are never selected, they can be emptied if needed */
+        else if( p_picture->i_flags & DESTROY_PICTURE )
+        {
+            EmptyPicture( p_vout, p_picture );
+        }  
+#ifdef DEBUG
+        /* Pictures in an inactive stream are submitted to a deletion attempt,
+         * and nether selected - this case should never happen */
+        else if( p_vout->p_stream[ p_picture->i_stream ].i_status == VOUT_INACTIVE_STREAM )
+        {   
+            intf_DbgMsg("%p -> picture %p belongs to an inactive stream\n", 
+                        p_vout, p_picture);
+            EmptyPicture( p_vout, p_picture );
+        }  
+#endif
+        /* Pictures in a destroyed stream are submitted to a deletion attempt,
+         * and nether selected */
+        else if( p_vout->p_stream[ p_picture->i_stream ].i_status == VOUT_DESTROYED_STREAM )
+        {   
+            EmptyPicture( p_vout, p_picture );
+        }
+        /* Reserved pictures are never selected */
+        else if( p_picture->i_flags & RESERVED_PICTURE )
+        {
+            p_picture->i_flags &= ~DISPLAY_PICTURE;
+        }
+        /* Overlay pictures */
+        else if( p_picture->i_flags & OVERLAY_PICTURE )
+        {
+            /* A picture is outdated if it is not permanent and if it's maximal
+             * date is passed */
+            if( !(p_picture->i_flags & PERMANENT_PICTURE ) 
+                && (p_picture->date + p_picture->duration < current_date) )
+            {
+                p_picture->i_flags &= ~DISPLAY_PICTURE;      
+                EmptyPicture( p_vout, p_picture );
+            }
+            /* Else if picture is in stream 0, it will always be selected */
+            else if( p_picture->i_stream == 0 )
+            {
+                p_picture->i_flags |= DISPLAY_PICTURE;   
+                /* Update display_date if picture has never been displayed */
+                if( !(p_picture->i_flags & DISPLAYED_PICTURE) && (p_picture->date < display_date) )
+                {
+                    display_date = p_picture->date;
+                }    
+            }
+            /* The picture can be considered as a regular picture, because it
+             * has never been displayed */
+            else if( !(p_picture->i_flags & DISPLAYED_PICTURE) )
+            {                
+                /* Select picture if can be updated */
+                if( (p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL)
+                    || (p_vout->p_stream[p_picture->i_stream].p_next_picture->date > p_picture->date))
+                {
+                    p_picture->i_flags |= DISPLAY_PICTURE;   
+                    p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
+                    /* Update display_date */
+                    if( p_picture->date < display_date )
+                    {
+                        display_date = p_picture->date;
+                    }
+                }
+            }
+            /* In other cases (overlay pictures which have already been displayed), 
+             * the picture is always selected */
+            else
+            {
+                p_picture->i_flags |= DISPLAY_PICTURE;
+                /* The stream is only updated if there is no picture in that stream */
+                if( p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL )
+                {
+                    p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
+                }
+            }
+        }
+        /* Non overlay pictures are deleted if they have been displayed, and
+         * selected if there date is valid */
+        else
+        {
+            /* Remove already displayed pictures */
+            if( p_picture->i_flags & DISPLAYED_PICTURE )
+            {
+                EmptyPicture( p_vout, p_picture );
+            }
+            /* Remove passed pictures, The picture is marked as displayed in case it 
+             * could not be removed */
+            else if( p_picture->date < current_date )
+            {
+                intf_DbgMsg("%p -> picture %p detected late\n", 
+                            p_vout, p_picture );
+                p_picture->i_flags |= DISPLAYED_PICTURE;
+                EmptyPicture( p_vout, p_picture );
+            }
+            /* Update streams for other pictures */
+            else
+            {
+                p_picture->i_flags |= DISPLAY_PICTURE;
+                /* Update 'next picture' field in stream descriptor */
+                if( (p_vout->p_stream[ p_picture->i_stream].p_next_picture == NULL)
+                    || (p_vout->p_stream[p_picture->i_stream].p_next_picture->date > p_picture->date))
+                {
+                   p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
+                   /* Update display_date */
+                   if( p_picture->date < display_date )
+                   {
+                       display_date = p_picture->date;
+                   }
+                }
+            }
+        }
+    }
+
+    return( display_date );
+}
+
+/*******************************************************************************
+ * SecondPass: second pass of the vout thread on pictures
+ *******************************************************************************
+ * Select only one picture per stream other than stream 0, and remove pictures
+ * which should have been displayed.... but arrived too late. Note that nothing
+ * is locked when this function is called, and therefore pictures should not
+ * be removed here.
+ * Only previously selected pictures are processed.
+ *******************************************************************************
+ * Messages type: vout, major code 19
+ *******************************************************************************/
+static void SecondPass( vout_thread_t *p_vout, mtime_t display_date )
+{
+    int         i_picture;                                    /* picture index */
+    picture_t * p_picture;                       /* picture pointer (shortcut) */
+
+    for( i_picture = 0; i_picture < p_vout->i_max_pictures; i_picture++ )
+    {
+        p_picture = &p_vout->p_picture[i_picture];
+        
+        /* Process only previously selected pictures */
+        if( p_picture->i_flags & DISPLAY_PICTURE )
+        {
+            /* Deselect picture if it is too youg */
+            if( p_picture->date > display_date + VOUT_DISPLAY_TOLERANCE )
+            {
+                p_picture->i_flags &= ~DISPLAY_PICTURE;
+            }
+            /* Pictures in stream 0 are selected depending only of their date */
+            else if( p_picture->i_stream == 0 )
+            {
+                /* Overlay pictures are not selected if they are 
+                 * outdated */
+                if( p_picture->i_flags & OVERLAY_PICTURE )
+                {
+                    if( !(p_picture->i_flags & PERMANENT_PICTURE)
+                    && (display_date > p_picture->date + p_picture->duration) )
+                    {
+                        p_picture->i_flags |= DESTROY_PICTURE;
+                        p_picture->i_flags &= ~DISPLAY_PICTURE;
+                    }
+                }
+                /* Regular pictures are selected if they are not too late */
+                else if( p_picture->date < display_date - VOUT_DISPLAY_TOLERANCE )
+                {
+                    intf_DbgMsg("%p -> picture %p detected late\n",
+                                p_vout, p_picture );
+                    p_picture->i_flags |= DISPLAYED_PICTURE | DESTROY_PICTURE;
+                    p_picture->i_flags &= ~DISPLAY_PICTURE;
+                }
+            }
+            /* Overlay pictures which have been displayed have special 
+             * processing */
+            else if( (p_picture->i_flags & OVERLAY_PICTURE) 
+                     && (p_picture->i_flags & DISPLAYED_PICTURE) )
+            {
+                /* If the stream is not empty, or the picture has been 
+                 * outdated, de select */
+                if( (p_vout->p_stream[p_picture->i_stream].p_next_picture != NULL)
+                    || (!(p_picture->i_flags & PERMANENT_PICTURE)
+                        && (display_date > p_picture->date + p_picture->duration) ) )
+                {
+                    p_picture->i_flags |= DESTROY_PICTURE;
+                    p_picture->i_flags &= ~DISPLAY_PICTURE;
+                }
+            }
+            /* Other pictures are 'regular' pictures */
+            else 
+            {
+                /* If the stream is empty, always select */
+                if( p_vout->p_stream[p_picture->i_stream].p_next_picture == NULL)
+                {
+                    p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;
+                }
+                /* Else, remove and mark as displayed (skip) if the picture is too old */
+                else if( p_picture->date < display_date - VOUT_DISPLAY_TOLERANCE )
+                {
+                    intf_DbgMsg("%p -> picture %p detected late\n",
+                                p_vout, p_picture );
+                    p_picture->i_flags |= DISPLAYED_PICTURE | DESTROY_PICTURE;
+                    p_picture->i_flags &= ~DISPLAY_PICTURE;
+                }
+                /* Else, select if the picture is younger than the current selected one */
+                else if( p_picture->date 
+                          < p_vout->p_stream[p_picture->i_stream].p_next_picture->date )
+                {
+                    /* Deselect the previous picture */
+                    p_vout->p_stream[p_picture->i_stream].p_next_picture->i_flags 
+                        &= ~DISPLAY_PICTURE; 
+                    /* Select the current one */
+                    p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;     
+                }
+                /* Else, select if the current selected one is an already displayed overlay */
+                else if( (p_vout->p_stream[p_picture->i_stream].p_next_picture->i_flags 
+                          & ( OVERLAY_PICTURE | DISPLAYED_PICTURE )) 
+                         == (OVERLAY_PICTURE | DISPLAYED_PICTURE) ) 
+
+                {
+                    /* Deselect and remove the previous picture */
+                    p_picture->i_flags |= DESTROY_PICTURE;
+                    p_picture->i_flags &= ~DISPLAY_PICTURE;
+                    /* Select the current one */
+                    p_vout->p_stream[p_picture->i_stream].p_next_picture = p_picture;     
+                }
+                /* Else, deselect the picture */
+                else
+                {
+                    p_picture->i_flags &= ~DISPLAY_PICTURE;
+                }
+            }
+        }
+    }
+}
+
+/*******************************************************************************
+ * SortPictures: sort pictures ready for display by hierarchical level
+ *******************************************************************************
+ * Build an array of pictures in rendering order, starting from minimal to
+ * level to maximal level. A NULL pointer always ends the array, which size is
+ * limited to VOUT_MAX_PICTURES.
+ *******************************************************************************
+ * Messages type: vout, major code 26
+ *******************************************************************************/
+static void SortPictures( vout_thread_t *p_vout, picture_t **pp_picture )
+{
+    int             i_picture;                /* picture index in sorted array */
+    int             i_heap_picture;                   /* picture index in heap */
+    picture_t *     p_previous_picture;                /* first shift register */
+    picture_t *     p_current_picture;                /* second shift register */
+
+    /* Initialize array */
+    pp_picture[0] = NULL;
+
+    /* Copy and sort pictures */
+    for( i_heap_picture = 0; i_heap_picture < p_vout->i_max_pictures ; i_heap_picture++ )
+    {
+        /* Sort only selected pictures */
+        if( p_vout->p_picture[ i_heap_picture ].i_flags & DISPLAY_PICTURE )
+        {            
+            /* Find picture position */
+            for( i_picture = 0; (pp_picture[i_picture] != NULL) 
+                     && (pp_picture[i_picture]->i_level 
+                         <= p_vout->p_picture[ i_heap_picture ].i_level);
+                 i_picture++ )
+            {
+                ;
+            }
+            p_current_picture = &p_vout->p_picture[ i_heap_picture ];
+
+            /* Insert picture and shift end of array */
+            for( ; p_previous_picture != NULL; i_picture++ )
+            {
+                p_previous_picture = p_current_picture;
+                p_current_picture = pp_picture[i_picture];
+                if( i_picture == VOUT_MAX_PICTURES - 1 )
+                {
+                    p_previous_picture = NULL;
+                }
+                pp_picture[i_picture] = p_previous_picture; 
+            }
+        }
+    }
+
+}
+
+/*******************************************************************************
+ * RenderPicture: render a picture
+ *******************************************************************************
+ * This function convert a picture from a video heap to a pixel-encoded image
+ * and copy it to the current rendering buffer. No lock is required, since the
+ * rendered picture has been determined as existant, and will only be destroyed
+ * by the vout_Thread() later.
+ *******************************************************************************
+ * Messages types: vout, major code 27
+ *******************************************************************************/
+static void RenderPicture( vout_thread_t *p_vout, picture_t *p_pic )
+{
+    int     i_x, i_y;                                   /* position in display */
+    int     i_pic_x;                            /* x offset in clipped picture */
+    int     i_pic_y;                            /* y offset in clipped picture */
+    int     i_height, i_width;                   /* clipped picture dimensions */
+    int     i_line_width;                                        /* line width */
+    void    (*RenderLine)(vout_thread_t *p_vout, picture_t *p_pic, /* renderer */
+                          int i_x, int i_y, int i_pic_x, int i_pic_y, int i_width,
+                          int i_line_width, int i_ratio );
+
+    /* Update counters - this is done now since for blank pictures, the function 
+     * may never reach it's regular end */
+    p_pic->i_flags |= DISPLAYED_PICTURE;
+#ifdef STATS
+    p_vout->c_rendered_pictures++;
+    p_vout->p_stream[ p_pic->i_stream ].c_rendered_pictures++;
+#endif                
+    
+    /* Computes position and size of the picture zone to render */
+    i_x = p_pic->i_x;
+    i_y = p_pic->i_y;
+    i_width = p_pic->i_width;
+    i_height = p_pic->i_height;
+    ClipPicture( &i_x, p_vout->i_width, &i_pic_x, &i_width, p_pic->i_h_ratio, p_pic->i_h_align );
+    ClipPicture( &i_y, p_vout->i_height, &i_pic_y, &i_height, p_pic->i_v_ratio, p_pic->i_v_align );
+#ifdef VOUT_DEBUG
+   intf_DbgMsg("%p -> picture %p, (%d-%d:%d-%d, %dx%d)\n", 
+               p_vout, p_pic, i_x, i_pic_x, i_y, i_pic_y, i_width, i_height );
+
+#endif
+
+    /* If there is noting to display, returns immediately */
+    if( (i_pic_x >= i_width) || (i_pic_y >= i_height) )
+    {
+        return;
+    }
+    
+    /* Determine method used to render line. This function is choosed here to
+     * avoid multiple conditions tests later */
+    switch( p_pic->i_type )
+    {
+    case RGB_BLANK_PICTURE:         /* picture is blank, RGB encoded (no data) */
+    case PIXEL_BLANK_PICTURE:     /* picture is blank, pixel encoded (no data) */
+        /* Blank pictures are displayed immediately - dimensions are real ones,
+         * and should be recalculated */
+        switch( p_pic->i_h_ratio )                   
+        {
+        case DISPLAY_RATIO_HALF:                              /* 1:2 half size */
+            i_width = (i_width - i_pic_x) / 2;
+            break;     
+        case DISPLAY_RATIO_NORMAL:                          /* 1:1 normal size */
+            i_width -= i_pic_x;
+            break;
+        case DISPLAY_RATIO_DOUBLE:                          /* 2:1 double size */   
+            i_width = (i_width - i_pic_x) * 2;
+            break;
+            /* ?? add others display ratio */
+        }
+        switch( p_pic->i_v_ratio )                   
+        {
+        case DISPLAY_RATIO_HALF:                              /* 1:2 half size */
+            i_height = (i_height - i_pic_y) / 2;
+            break;     
+        case DISPLAY_RATIO_NORMAL:                          /* 1:1 normal size */
+            i_height -= i_pic_y;
+            break;
+        case DISPLAY_RATIO_DOUBLE:                          /* 2:1 double size */   
+            i_height = (i_height - i_pic_y) * 2;
+            break;
+            /* ?? add others display ratio */
+        }
+        if( p_pic->i_type == RGB_BLANK_PICTURE )          /* RGB blank picture */
+        {            
+            p_vout->RenderRGBBlank( p_vout, p_pic->pixel, i_x, i_y, i_width, i_height );
+        }
+        else                                            /* pixel blank picture */
+        {
+            p_vout->RenderPixelBlank( p_vout, p_pic->pixel, i_x, i_y, i_width, i_height );
+        }        
+        return;
+        break;
+    case RGB_PICTURE:                        /* picture is 24 bits rgb encoded */
+        RenderLine = p_vout->RenderRGBLine;
+        break;
+    case PIXEL_PICTURE:                            /* picture is pixel encoded */
+        RenderLine = p_vout->RenderPixelLine;
+        break;
+    case RGB_MASK_PICTURE:                      /* picture is a 1 rgb bpp mask */
+        RenderLine = p_vout->RenderRGBMaskLine;
+        break;
+    case PIXEL_MASK_PICTURE:                  /* picture is a 1 pixel bpp mask */
+        RenderLine = p_vout->RenderPixelMaskLine;
+        break;
+        /* ?? add YUV types */
+#ifdef DEBUG
+    default:                      /* internal error, which should never happen */
+        intf_DbgMsg("%p -> unknown type for picture %p\n", p_vout, p_pic);
+        break;  
+#endif  
+    }
+
+    /* For non blank pictures, loop on lines */
+    for( ; i_pic_y < i_height; i_pic_y++ )
+    {
+        /* First step: check if line has to be rendered. This is not obvious since
+         * if display ratio is less than 1:1, some of the lines don't need to
+         * be displayed, and therefore do not need to be rendered. */
+        switch( p_pic->i_v_ratio )
+        {
+        case DISPLAY_RATIO_HALF:                              /* 1:2 half size */
+            /* Only even lines are rendered, and copied once */
+            i_line_width = i_pic_y % 2;
+            break;
+
+        case DISPLAY_RATIO_NORMAL:                          /* 1:1 normal size */
+            /* All lines are rendered and copied once */
+            i_line_width = 1;
+            break;
+
+        case DISPLAY_RATIO_DOUBLE:                          /* 2:1 double size */
+            /* All lines are rendered and copied twice */
+            i_line_width = 2;
+            break;
+        }
+
+        if( i_line_width )
+        {
+            /* Computed line width can be reduced if it would cause to render 
+             * outside the display */
+            if( i_y + i_line_width > p_vout->i_height )
+            {
+                i_line_width = p_vout->i_height - i_y;
+            }
+
+            /* Second step: render line. Since direct access to the rendering 
+             * buffer is required, functions in charge of rendering the line
+             * are part of the display driver. Because this step is critical
+             * and require high optimization, different methods are used 
+             * depending of the horizontal display ratio, the image type and 
+             * the screen depth. */
+            RenderLine( p_vout, p_pic, i_x, i_y, i_pic_x, i_pic_y,
+                        i_width, i_line_width, p_pic->i_h_ratio );
+            
+            /* Increment display line index */
+            i_y += i_line_width;
+        }
+    }
+}
+
+/*******************************************************************************
+ * ClipPicture: clip a picture in display window
+ *******************************************************************************
+ * This function computes picture placement in display window and rendering
+ * zone, according to wished picture placement and display ratio. It must be
+ * called twice, once and with the x coordinates and once with the y
+ * coordinates. 
+ * The pi_pic_ofs parameter is only written, but pi_ofs and pi_pic_size must
+ * be valid arguments. Note that *pi_pic_size is still the rendering zone size,
+ * starting from picture offset 0 and not the size starting from *pi_pic_ofs
+ * (same thing for i_size).
+ *******************************************************************************
+ * Messages types: vout, major code 28
+ *******************************************************************************/
+static void ClipPicture( int *pi_ofs, int i_size, int *pi_pic_ofs, int *pi_pic_size,
+                         int i_ratio, int i_placement )
+{
+    int i_ofs;                                   /* temporary picture position */
+
+    /* Computes base picture position */
+    switch( i_placement )
+    {
+    case -1:                                               /* left/top aligned */
+        i_ofs = *pi_ofs;
+        break;
+    case 0:                                                        /* centered */
+        i_ofs = *pi_ofs + (i_size - *pi_pic_size) / 2;
+        break;
+    case 1:                                            /* right/bottom aligned */
+        i_ofs = *pi_ofs + i_size - *pi_pic_size;
+        break;
+#ifdef DEBUG
+    default:                      /* internal error, which should never happen */
+        intf_DbgMsg("invalid placement\n");
+        break;
+#endif
+    }
+
+    /* Computes base rendering position and update picture position - i_ofs is
+     * the picture position, and can be negative */
+    if( i_ofs < 0 )                       /* picture starts outside the screen */
+    {
+        switch( i_ratio )                   
+        {
+        case DISPLAY_RATIO_HALF:                              /* 1:2 half size */
+            *pi_pic_ofs = - *pi_ofs * 2;
+            break;     
+        case DISPLAY_RATIO_NORMAL:                          /* 1:1 normal size */
+            *pi_pic_ofs = -*pi_ofs;
+            break;
+        case DISPLAY_RATIO_DOUBLE:                          /* 2:1 double size */
+            *pi_pic_ofs = -CEIL(*pi_ofs, 2);
+            break;
+#ifdef DEBUG
+        default:                  /* internal error, which should never happen */
+            intf_DbgMsg("unsupported ratio\n");
+            break;  
+#endif  
+        }
+        *pi_ofs = 0;
+    }
+    else                                   /* picture starts inside the screen */
+    {
+        *pi_pic_ofs = 0;
+        *pi_ofs = i_ofs;
+    }
+
+    /* Computes rendering size - i_ofs is the picture position, and can be 
+     * negative, *pi_ofs is the real picture position, and is always positive */
+    switch( i_ratio )                   
+    {
+    case DISPLAY_RATIO_HALF:                                  /* 1:2 half size */
+        if( i_ofs + CEIL(*pi_pic_size, 2) > i_size )
+        {
+            *pi_pic_size = ( i_size - i_ofs ) * 2;
+        }
+        break;     
+    case DISPLAY_RATIO_NORMAL:                              /* 1:1 normal size */
+        if( i_ofs + *pi_pic_size > i_size )
+        {
+            *pi_pic_size = i_size - i_ofs;
+        }
+        break;
+    case DISPLAY_RATIO_DOUBLE:                              /* 2:1 double size */
+        if( *pi_ofs + *pi_pic_size * 2 > i_size )
+        {
+            *pi_pic_size = ( i_size - i_ofs ) / 2;
+        }
+        break;
+#ifdef DEBUG
+    default:                  /* internal error, which should never happen */
+        intf_DbgMsg("unsupported ratio\n");
+        break;  
+#endif  
+    }     
+}
+
diff --git a/src/video_output/video_x11.c b/src/video_output/video_x11.c
new file mode 100644 (file)
index 0000000..d929443
--- /dev/null
@@ -0,0 +1,1128 @@
+/*******************************************************************************
+ * vout_x11.c: X11 video output display method
+ * (c)1998 VideoLAN
+ *******************************************************************************
+ * The X11 method for video output thread. It's properties (and the vout_x11_t
+ * type) are defined in vout.h. The functions declared here should not be
+ * needed by any other module than vout.c.
+ *******************************************************************************/
+
+/*******************************************************************************
+ * Preamble
+ *******************************************************************************/
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/shm.h>
+#include <sys/soundcard.h>
+#include <sys/uio.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/XShm.h>
+
+#include "config.h"
+#include "common.h"
+#include "mtime.h"
+#include "xutils.h"
+
+#include "input.h"
+#include "input_vlan.h"
+
+#include "audio_output.h"
+
+#include "video.h"
+#include "video_output.h"
+#include "video_x11.h"
+
+#include "xconsole.h"
+#include "interface.h"
+#include "intf_msg.h"
+
+#include "pgm_data.h"
+
+/*
+ * Local prototypes
+ */
+static int  X11CheckConfiguration   ( video_cfg_t *p_cfg );
+
+static int  X11OpenDisplay          ( vout_thread_t *p_vout );
+static int  X11CreateWindow         ( vout_thread_t *p_vout );
+static int  X11CreateImages         ( vout_thread_t *p_vout );
+static void X11DestroyImages        ( vout_thread_t *p_vout );
+static void X11DestroyWindow        ( vout_thread_t *p_vout );
+static void X11CloseDisplay         ( vout_thread_t *p_vout );
+static int  X11CreateImage          ( vout_thread_t *p_vout, XImage **pp_ximage );
+static void X11DestroyImage         ( XImage *p_ximage );
+static int  X11CreateShmImage       ( vout_thread_t *p_vout, XImage **pp_ximage, 
+                                      XShmSegmentInfo *p_shm_info );
+static void X11DestroyShmImage      ( vout_thread_t *p_vout, XImage *p_ximage, 
+                                      XShmSegmentInfo *p_shm_info );
+
+static vout_render_blank_t  X11RenderRGBBlank;
+static vout_render_blank_t  X11RenderPixelBlank8bpp;
+static vout_render_blank_t  X11RenderPixelBlank16bpp;
+static vout_render_blank_t  X11RenderPixelBlank24bpp;
+static vout_render_blank_t  X11RenderPixelBlank32bpp;
+static vout_render_line_t   X11RenderRGBLine8bpp;
+static vout_render_line_t   X11RenderRGBLine16bpp;
+static vout_render_line_t   X11RenderRGBLine24bpp;
+static vout_render_line_t   X11RenderRGBLine32bpp;
+static vout_render_line_t   X11RenderPixelLine8bpp;
+static vout_render_line_t   X11RenderPixelLine16bpp;
+static vout_render_line_t   X11RenderPixelLine24bpp;
+static vout_render_line_t   X11RenderPixelLine32bpp;
+static vout_render_line_t   X11RenderRGBMaskLine;
+static vout_render_line_t   X11RenderPixelMaskLine8bpp;
+static vout_render_line_t   X11RenderPixelMaskLine16bpp;
+static vout_render_line_t   X11RenderPixelMaskLine24bpp;
+static vout_render_line_t   X11RenderPixelMaskLine32bpp;
+/* ?? YUV types */
+
+/*******************************************************************************
+ * vout_X11AllocOutputMethod: allocate X11 video thread output method
+ *******************************************************************************
+ * This function creates a X11 output method descriptor in the vout_thread_t
+ * desriptor and initialize it.
+ * Following configuration properties are used:
+ *  VIDEO_CFG_DISPLAY    display used
+ *  VIDEO_CFG_TITLE      window title
+ *  VIDEO_CFG_SHM_EXT    try to use XShm extension
+ *******************************************************************************/
+int vout_X11AllocOutputMethod( vout_thread_t *p_vout, video_cfg_t *p_cfg )
+{
+    /* Check configuration */
+    if( X11CheckConfiguration(p_cfg) )
+    {
+        return( 1 );
+    }
+
+    /* Allocate descriptor */
+    p_vout->p_x11 = (vout_x11_t *) malloc( sizeof( vout_x11_t ) );
+    if( p_vout->p_x11 == NULL )
+    {
+        intf_ErrMsg("vout error 101-1: cannot allocate X11 method descriptor: %s\n",
+                    strerror(errno));        
+        return( 1 );        
+    }   
+
+    /* Initialize fields - string passed in configuration structure are copied
+     * since they can be destroyed at any moment - remember that NULL is a valid
+     * value for psz_display */
+    p_vout->p_x11->psz_title = (char *) malloc( strlen(p_cfg->psz_title) + 1);
+    if( p_vout->p_x11->psz_title == NULL )
+    {
+        free( p_vout->p_x11 );            
+        return( 1 );
+    }        
+    strcpy( p_vout->p_x11->psz_title, p_cfg->psz_title );    
+    if( p_cfg->psz_display != NULL ) 
+    {
+        p_vout->p_x11->psz_display = (char *) malloc( strlen(p_cfg->psz_display) + 1);
+        if( p_vout->p_x11->psz_display == NULL )
+        {
+            free( p_vout->p_x11->psz_title );            
+            free( p_vout->p_x11 );            
+            return( 1 );
+        }        
+        strcpy( p_vout->p_x11->psz_display, p_cfg->psz_display );    
+    }
+    else
+    {
+        p_vout->p_x11->psz_display = NULL;        
+    }
+    p_vout->p_x11->b_shm_ext = p_cfg->b_shm_ext;
+    return( 0 );    
+}
+
+/*******************************************************************************
+ * vout_X11FreeOutputMethod: free X11 video thread output method
+ *******************************************************************************
+ * Free an X11 video thread output method allocated by 
+ * vout_X11AllocOutputMethod()
+ *******************************************************************************/
+void vout_X11FreeOutputMethod( vout_thread_t *p_vout )
+{
+    if( p_vout->p_x11->psz_display != NULL )
+    {
+        free( p_vout->p_x11->psz_display );        
+    }
+    free( p_vout->p_x11->psz_title );        
+    free( p_vout->p_x11 );    
+}
+
+/*******************************************************************************
+ * vout_X11CreateOutputMethod: create X11 video thread output method
+ *******************************************************************************
+ * This function opens a display and create a X11 window according to the user
+ * configuration.
+ *******************************************************************************/
+int vout_X11CreateOutputMethod( vout_thread_t *p_vout )
+{
+    if( X11OpenDisplay( p_vout ) )                             /* open display */
+    {
+        free( p_vout->p_x11 );        
+        return( 1 );
+    }
+    if( X11CreateWindow( p_vout ) )                           /* create window */
+    {
+        X11CloseDisplay( p_vout );
+        free( p_vout->p_x11 );        
+        return( 1 );
+    }
+    if( X11CreateImages( p_vout ) )                           /* create images */
+    {
+        X11DestroyWindow( p_vout );
+        X11CloseDisplay( p_vout );
+        free( p_vout->p_x11 );
+        return( 1 );        
+    }
+    intf_DbgMsg("%p -> success, depth=%d bpp, XShm=%d\n", 
+                p_vout, p_vout->i_screen_depth, p_vout->p_x11->b_shm_ext);
+    return( 0 );
+}
+
+/*******************************************************************************
+ * vout_X11DestroyOutputMethod: destroy X11 video thread output method
+ *******************************************************************************
+ * Destroys an output method created by vout_X11CreateOutputMethod
+ *******************************************************************************
+ * Messages type: vout, major code: 102
+ *******************************************************************************/
+void vout_X11DestroyOutputMethod( vout_thread_t *p_vout )
+{
+    X11DestroyImages( p_vout );
+    X11DestroyWindow( p_vout );
+       X11CloseDisplay( p_vout );
+    intf_DbgMsg("%p\n", p_vout );
+}
+
+/*******************************************************************************
+ * vout_X11ManageOutputMethod: handle X11 events
+ *******************************************************************************
+ * This function should be called regularly by video output thread. It manages
+ * X11 events and allows window resizing. It returns a negative value if 
+ * something happened which does not allow the thread to continue, and a 
+ * positive one if the thread can go on, but the images have been modified and
+ * therefore it is useless to display them.
+ *******************************************************************************
+ * Messages type: vout, major code: 103
+ *******************************************************************************/
+int vout_X11ManageOutputMethod( vout_thread_t *p_vout )
+{
+    XEvent      xevent;                                           /* X11 event */
+    boolean_t   b_resized;                          /* window has been resized */
+    
+    /* Handle X11 events: ConfigureNotify events are parsed to know if the
+     * output window's size changed, MapNotify and UnmapNotify to know if the
+     * window is mapped (and if the display is usefull), and ClientMessages
+     * to intercept window destruction requests */
+    b_resized = 0;
+    while( XCheckWindowEvent( p_vout->p_x11->p_display, p_vout->p_x11->window,
+                              StructureNotifyMask, &xevent ) == True )
+    {      
+        /* ConfigureNotify event: prepare  */
+        if( (xevent.type == ConfigureNotify)  
+            && ((xevent.xconfigure.width != p_vout->i_width)
+                || (xevent.xconfigure.height != p_vout->i_height)) )
+        {
+            /* Update dimensions */
+            b_resized = 1;
+            p_vout->i_width = xevent.xconfigure.width;
+            p_vout->i_height = xevent.xconfigure.height;
+        }
+        /* MapNotify event: change window status and disable screen saver */
+        else if( (xevent.type == MapNotify) && !p_vout->b_active )
+        {
+            XDisableScreenSaver( p_vout->p_x11->p_display );       
+            p_vout->b_active = 1;
+        }
+        /* UnmapNotify event: change window status and enable screen saver */
+        else if( (xevent.type == UnmapNotify) && p_vout->b_active )
+        {
+            XEnableScreenSaver( p_vout->p_x11->p_display );       
+            p_vout->b_active = 0;            
+        }        
+        /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
+         * are handled - according to the man pages, the format is always 32 
+         * in this case */
+        else if( (xevent.type == ClientMessage)
+/*                 && (xevent.xclient.message_type == p_vout->p_x11->wm_protocols) 
+                 && (xevent.xclient.data.l[0] == p_vout->p_x11->wm_delete_window )*/ )
+        {
+            intf_DbgMsg("******* ClientMessage ******\n");
+            
+            /* ?? this never happens :( */
+            return( -1 );
+        }
+#ifdef DEBUG
+        /* Other event */
+        else
+        {            
+            intf_DbgMsg("%p -> unhandled event type %d received\n", p_vout, xevent.type );
+        }        
+#endif
+    }
+
+    /* If window has been resized, re-create images */
+    if( b_resized )
+    {
+        intf_DbgMsg("%p -> resizing window\n", p_vout);
+        X11DestroyImages( p_vout );
+        if( X11CreateImages( p_vout ) )
+        {
+            /* A fatal error occured: images could not be re-created. Note
+             * that in this case, the images pointers will be NULL, so the
+             * image destructor will know it does not need to destroy them. */
+            return( -1 );
+        }
+        return( 1 );        
+    }
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * vout_X11DisplayOutput: displays previously rendered output
+ *******************************************************************************
+ * This function send the currently rendered image to X11 server, wait until
+ * it is displayed and switch the two rendering buffer, preparing next frame.
+ *******************************************************************************
+ * Messages type: vout, major code: 105
+ *******************************************************************************/
+void vout_X11DisplayOutput( vout_thread_t *p_vout )
+{
+    if( p_vout->p_x11->b_shm_ext)                                 /* XShm is used */
+    {
+        /* Display rendered image using shared memory extension */
+        XShmPutImage(p_vout->p_x11->p_display, p_vout->p_x11->window, p_vout->p_x11->gc, 
+                     p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ], 
+                     0, 0, 0, 0,  
+                     p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->width,  
+                     p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->height, True);
+
+        /* Send the order to the X server */
+        XFlush(p_vout->p_x11->p_display);
+        
+        /* ?? wait until effective display ? */
+/*        do XNextEvent(Display_Ptr, &xev);
+        while(xev.type!=CompletionType);*/
+    }
+    else                                  /* regular X11 capabilities are used */
+    {
+        XPutImage(p_vout->p_x11->p_display, p_vout->p_x11->window, p_vout->p_x11->gc, 
+                  p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ], 
+                  0, 0, 0, 0,  
+                  p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->width,  
+                  p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->height);
+        /* Send the order to the X server */
+        XFlush(p_vout->p_x11->p_display);   /* ?? not needed ? */
+    }
+
+    /* Swap buffers */
+    p_vout->p_x11->i_buffer_index = ++p_vout->p_x11->i_buffer_index & 1;
+}
+
+/* following functions are local */
+
+/*******************************************************************************
+ * X11CheckConfiguration: check configuration.
+ *******************************************************************************
+ * Set default parameters where required. In DEBUG mode, check if configuration
+ * is valid.
+ *******************************************************************************
+ * Messages type: vout, major code: 116
+ *******************************************************************************/
+static int  X11CheckConfiguration( video_cfg_t *p_cfg )
+{
+    /* Window dimensions */
+    if( !( p_cfg->i_properties & VIDEO_CFG_WIDTH ) )
+    {
+        p_cfg->i_width = VOUT_WIDTH;
+    }
+    if( !( p_cfg->i_properties & VIDEO_CFG_HEIGHT ) )
+    {
+        p_cfg->i_height = VOUT_HEIGHT;
+    }
+    /* Display */
+    if( !( p_cfg->i_properties & VIDEO_CFG_DISPLAY ) )
+    {
+        p_cfg->psz_display = NULL;
+    }
+
+    /* Window title */
+    if( !( p_cfg->i_properties & VIDEO_CFG_TITLE ) )
+    {
+        p_cfg->psz_title = VOUT_TITLE;
+    }
+
+    /* Use of XShm extension */
+    if( !( p_cfg->i_properties & VIDEO_CFG_SHM_EXT ) )
+    {
+        p_cfg->b_shm_ext = VOUT_SHM_EXT;
+    }
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * X11OpenDisplay: open X11 display
+ *******************************************************************************
+ * Opens an X11 display and set up pictures rendering functions according to
+ * screen depth.
+ * Following configuration properties are used:
+ *  VIDEO_CFG_DISPLAY    display used
+ *  VIDEO_CFG_SHM_EXT    try to use XShm extension
+ *******************************************************************************
+ * Messages type: vout, major code: 106
+ *******************************************************************************/
+static int X11OpenDisplay( vout_thread_t *p_vout )
+{
+    char *psz_display;
+
+    /* Open display, using display name provided or default one */
+    psz_display = XDisplayName( p_vout->p_x11->psz_display );
+    p_vout->p_x11->p_display = XOpenDisplay( psz_display );
+    if( !p_vout->p_x11->p_display )                                      /* error */
+    {
+        intf_ErrMsg("vout error 106-1: can't open display %s\n", psz_display );
+       return( 1 );               
+    }
+
+    /* Check if XShm extension is wished and supported */
+    /* ?? in old client, we checked if display was local or not - is it really
+     * needed ? */
+    if( p_vout->p_x11->b_shm_ext )
+    {
+        p_vout->p_x11->b_shm_ext = (XShmQueryExtension(p_vout->p_x11->p_display) == True);
+    }
+    else
+    {
+        p_vout->p_x11->b_shm_ext = 0;
+    }
+
+    /* Get the screen number and depth (bpp) - select functions depending 
+     * of this value. i_bytes_per_pixel is required since on some hardware,
+     * depth as 15bpp are used, which can cause problems with later memory
+     * allocation. */
+    p_vout->p_x11->i_screen = DefaultScreen( p_vout->p_x11->p_display );
+    p_vout->i_screen_depth = DefaultDepth( p_vout->p_x11->p_display, 
+                                           p_vout->p_x11->i_screen );
+    switch( p_vout->i_screen_depth )
+    {
+    case 8:                                              /* 8 bpp (256 colors) */        
+        p_vout->i_bytes_per_pixel = 1;
+        p_vout->RenderRGBBlank = X11RenderRGBBlank;
+        p_vout->RenderPixelBlank = X11RenderPixelBlank8bpp;
+        p_vout->RenderRGBLine = X11RenderRGBLine8bpp;
+        p_vout->RenderPixelLine = X11RenderPixelLine8bpp;
+        p_vout->RenderRGBMaskLine = X11RenderRGBMaskLine;
+        p_vout->RenderPixelMaskLine = X11RenderPixelMaskLine8bpp;                
+        /*
+        Process_Frame=Dither_Frame;
+        Process_Top_Field=Dither_Top_Field;
+        Process_Bottom_Field=Dither_Bottom_Field;
+        Process_Top_Field420=Dither_Top_Field420;
+        Process_Bottom_Field420=Dither_Bottom_Field420; ?? */
+        break;
+
+    case 15:                        /* 15 bpp (16bpp with a missing green bit) */
+        p_vout->i_bytes_per_pixel = 2;              
+        /*
+         ?? */
+        p_vout->RenderRGBBlank = X11RenderRGBBlank;
+        p_vout->RenderPixelBlank = X11RenderPixelBlank16bpp;
+        p_vout->RenderRGBLine = X11RenderRGBLine16bpp;
+        p_vout->RenderPixelLine = X11RenderPixelLine16bpp;
+        p_vout->RenderRGBMaskLine = X11RenderRGBMaskLine;
+        p_vout->RenderPixelMaskLine = X11RenderPixelMaskLine16bpp;        
+        /* ?? probably a 16bpp with differenr convertion functions - just map
+         * functions, then switch to 16bpp */
+        break;
+
+    case 16:                                          /* 16 bpp (65536 colors) */
+        p_vout->i_bytes_per_pixel = 2;
+        p_vout->RenderRGBBlank = X11RenderRGBBlank;
+        p_vout->RenderPixelBlank = X11RenderPixelBlank16bpp;
+        p_vout->RenderRGBLine = X11RenderRGBLine16bpp;
+        p_vout->RenderPixelLine = X11RenderPixelLine16bpp;
+        p_vout->RenderRGBMaskLine = X11RenderRGBMaskLine;
+        p_vout->RenderPixelMaskLine = X11RenderPixelMaskLine16bpp;               
+       /*
+        Process_Frame=Translate_Frame;
+        Process_Top_Field=Translate_Top_Field;
+        Process_Bottom_Field=Translate_Bottom_Field;
+        Process_Top_Field420=Translate_Top_Field420;
+        Process_Bottom_Field420=Translate_Bottom_Field420; ?? */
+        break;
+
+    case 24:                                    /* 24 bpp (millions of colors) */
+        p_vout->i_bytes_per_pixel = 3;
+        p_vout->RenderRGBBlank = X11RenderRGBBlank;
+        p_vout->RenderPixelBlank = X11RenderPixelBlank24bpp;
+        p_vout->RenderRGBLine = X11RenderRGBLine24bpp;
+        p_vout->RenderPixelLine = X11RenderPixelLine24bpp;
+        p_vout->RenderRGBMaskLine = X11RenderRGBMaskLine;
+        p_vout->RenderPixelMaskLine = X11RenderPixelMaskLine24bpp;        
+        /*
+        Process_Frame=Translate_Frame;
+        Process_Top_Field=Translate_Top_Field;
+        Process_Bottom_Field=Translate_Bottom_Field;
+        Process_Top_Field420=Translate_Top_Field420;
+        Process_Bottom_Field420=Translate_Bottom_Field420; ?? */
+        break;
+
+    case 32:                                    /* 32 bpp (millions of colors) */
+        p_vout->i_bytes_per_pixel = 4;
+        p_vout->RenderRGBBlank = X11RenderRGBBlank;
+        p_vout->RenderPixelBlank = X11RenderPixelBlank32bpp;
+        p_vout->RenderRGBLine = X11RenderRGBLine32bpp;
+        p_vout->RenderPixelLine = X11RenderPixelLine32bpp;
+        p_vout->RenderRGBMaskLine = X11RenderRGBMaskLine;
+        p_vout->RenderPixelMaskLine = X11RenderPixelMaskLine32bpp;
+        /*
+        Process_Frame=Translate_Frame;
+        Process_Top_Field=Translate_Top_Field;
+        Process_Bottom_Field=Translate_Bottom_Field;
+        Process_Top_Field420=Translate_Top_Field420;
+        Process_Bottom_Field420=Translate_Bottom_Field420; ?? */
+        break;
+
+    default:                                       /* unsupported screen depth */
+        intf_ErrMsg("vout error 106-2: screen depth %i is not supported\n", 
+                    p_vout->i_screen_depth);
+        XCloseDisplay( p_vout->p_x11->p_display );
+        return( 1  );
+        break;
+    }
+    return( 0 ); 
+}
+
+/*******************************************************************************
+ * X11CreateWindow: create X11 window
+ *******************************************************************************
+ * Create and set-up the output window.
+ * Following configuration properties are used:
+ *  VIDEO_CFG_WIDTH      window width
+ *  VIDEO_CFG_HEIGHT     window height 
+ *  VIDEO_CFG_TITLE      window title
+ *******************************************************************************
+ * Messages type: vout, major code: 107
+ *******************************************************************************/
+static int X11CreateWindow( vout_thread_t *p_vout )
+{
+    XSizeHints              xsize_hints;
+    XSetWindowAttributes    xwindow_attributes;
+    XGCValues               xgcvalues;
+    XEvent                  xevent;
+    boolean_t               b_expose;
+    boolean_t               b_configure_notify;
+    boolean_t               b_map_notify;    
+
+    /* Prepare window manager hints and properties */
+    xsize_hints.base_width =            p_vout->i_width;
+    xsize_hints.base_height =           p_vout->i_height;
+    xsize_hints.flags =                 PSize;
+    p_vout->p_x11->wm_protocols =       XInternAtom( p_vout->p_x11->p_display, "WM_PROTOCOLS", True );    
+    p_vout->p_x11->wm_delete_window =   XInternAtom( p_vout->p_x11->p_display, "WM_DELETE_WINDOW", True );
+    /* ?? add icons and placement hints ? */
+
+    /* Prepare window attributes */
+    xwindow_attributes.backing_store = Always;         /* save the hidden part */  
+    /* Create the window and set hints - the window must receive ConfigureNotify
+     * events, and, until it is displayed, Expose and MapNotify events. */
+    p_vout->p_x11->window = XCreateSimpleWindow( p_vout->p_x11->p_display,
+                                              DefaultRootWindow( p_vout->p_x11->p_display ),
+                                              0, 0, 
+                                              p_vout->i_width, p_vout->i_height,
+                                              0, 0, 0);
+    XSelectInput( p_vout->p_x11->p_display, p_vout->p_x11->window, 
+                  ExposureMask | StructureNotifyMask );
+    XChangeWindowAttributes( p_vout->p_x11->p_display, p_vout->p_x11->window, 
+                             CWBackingStore, &xwindow_attributes);
+
+    /* Set window manager hints and properties: size hints, command, window's name,
+     * and accepted protocols */
+    XSetWMNormalHints( p_vout->p_x11->p_display, p_vout->p_x11->window, &xsize_hints );
+    XSetCommand( p_vout->p_x11->p_display, p_vout->p_x11->window, 
+                 p_program_data->ppsz_argv, p_program_data->i_argc );    
+    XStoreName( p_vout->p_x11->p_display, p_vout->p_x11->window, p_vout->p_x11->psz_title );
+    if( (p_vout->p_x11->wm_protocols == None)          /* use WM_DELETE_WINDOW */
+        || (p_vout->p_x11->wm_delete_window == None)
+        || !XSetWMProtocols( p_vout->p_x11->p_display, p_vout->p_x11->window, 
+                             &p_vout->p_x11->wm_delete_window, 1 ) )
+    {
+        /* WM_DELETE_WINDOW is not supported by window manager */
+        intf_Msg("vout: missing or bad window manager - please exit program kindly.\n");
+    }
+    
+    /* Creation of a graphic context that doesn't generate a GraphicsExpose event
+       when using functions like XCopyArea */
+    xgcvalues.graphics_exposures = False;    
+    p_vout->p_x11->gc =  XCreateGC( p_vout->p_x11->p_display, p_vout->p_x11->window,
+                                    GCGraphicsExposures, &xgcvalues);
+
+    /* Create color system */
+    /*?? if( CreateX11Colors( p_vout ) )
+    {
+        intf_ErrMsg("vout error 107-1: can't initialize color system\n");
+        XCloseDisplay( p_vout->p_x11->p_display );
+        return( - 1 );
+    }*/
+   
+    /* Send orders to server, and wait until window is displayed - three events
+     * must be received: a MapNotify event, an Expose event allowing drawing in the
+     * window, and a ConfigureNotify to get the window dimensions. Once those events
+     * have been received, only ConfigureNotify events need to be received. */   
+    b_expose = 0;
+    b_configure_notify = 0;
+    b_map_notify = 0;
+    XMapWindow( p_vout->p_x11->p_display, p_vout->p_x11->window);
+    do
+    {
+        XNextEvent( p_vout->p_x11->p_display, &xevent);
+        if( (xevent.type == Expose) 
+            && (xevent.xexpose.window == p_vout->p_x11->window) )
+        {
+            b_expose = 1;
+        }
+        else if( (xevent.type == MapNotify) 
+                 && (xevent.xmap.window == p_vout->p_x11->window) )
+        {
+            b_map_notify = 1;
+        }
+        else if( (xevent.type == ConfigureNotify) 
+                 && (xevent.xconfigure.window == p_vout->p_x11->window) )
+        {
+            b_configure_notify = 1;
+            p_vout->i_width = xevent.xconfigure.width;
+            p_vout->i_height = xevent.xconfigure.height;       
+        }
+    }
+    while( !( b_expose && b_configure_notify && b_map_notify ) );
+    XSelectInput( p_vout->p_x11->p_display, p_vout->p_x11->window, StructureNotifyMask );
+
+    /* At this stage, the window is openned, displayed, and ready to receive data */
+    p_vout->b_active = 1;    
+    return( 0 );
+}
+
+/*******************************************************************************
+ * X11CreateImages: create X11 rendering buffers
+ *******************************************************************************
+ * Create two XImages which will be used as rendering buffers. On error, non 0
+ * will be returned and the images pointer will be set to NULL (see 
+ * vout_X11ManageOutputMethod()).
+ *******************************************************************************
+ * Messages type: vout, major code: 108
+ *******************************************************************************/
+static int X11CreateImages( vout_thread_t *p_vout )
+{
+    int i_err;
+
+    /* Create XImages using XShm extension - on failure, fall back to regular 
+     * way (and destroy the first image if it was created successfully) */
+    if( p_vout->p_x11->b_shm_ext )
+    {
+        /* Create first image */
+        i_err = X11CreateShmImage( p_vout, &p_vout->p_x11->p_ximage[0], 
+                                   &p_vout->p_x11->shm_info[0] );
+        if( !i_err )                           /* first image has been created */
+        {
+            /* Create second image */
+            if( X11CreateShmImage( p_vout, &p_vout->p_x11->p_ximage[1], 
+                                   &p_vout->p_x11->shm_info[1] ) )
+            {                               /* error creating the second image */
+                X11DestroyShmImage( p_vout, p_vout->p_x11->p_ximage[0], 
+                                    &p_vout->p_x11->shm_info[0] );
+                i_err = 1;
+            }
+        }
+        if( i_err )                                        /* an error occured */
+        {                        
+            intf_Msg("vout: XShm extension desactivated\n" );
+            p_vout->p_x11->b_shm_ext = 0;
+        }
+    }
+
+    /* Create XImages without XShm extension */
+    if( !p_vout->p_x11->b_shm_ext )
+    {
+        if( X11CreateImage( p_vout, &p_vout->p_x11->p_ximage[0] ) )
+        {
+            intf_Msg("vout error 108-1: can't create images\n");
+            p_vout->p_x11->p_ximage[0] = NULL;
+            p_vout->p_x11->p_ximage[1] = NULL;
+            return( -1 );
+        }
+        if( X11CreateImage( p_vout, &p_vout->p_x11->p_ximage[1] ) )
+        {
+            intf_Msg("vout error 108-2: can't create images\n");
+            X11DestroyImage( p_vout->p_x11->p_ximage[0] );
+            p_vout->p_x11->p_ximage[0] = NULL;
+            p_vout->p_x11->p_ximage[1] = NULL;
+            return( -1 );
+        }
+    }
+
+    /* Set buffer index to 0 */
+    p_vout->p_x11->i_buffer_index = 0;
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * X11DestroyImages: destroy X11 rendering buffers
+ *******************************************************************************
+ * Destroy buffers created by vout_X11CreateImages().
+ *******************************************************************************
+ * Messages type: vout, major code: 109
+ *******************************************************************************/
+static void X11DestroyImages( vout_thread_t *p_vout )
+{
+    if( p_vout->p_x11->b_shm_ext )                              /* Shm XImages... */
+    {
+        X11DestroyShmImage( p_vout, p_vout->p_x11->p_ximage[0], 
+                            &p_vout->p_x11->shm_info[0] );
+               X11DestroyShmImage( p_vout, p_vout->p_x11->p_ximage[1], 
+                            &p_vout->p_x11->shm_info[1] );
+       }
+       else                                              /* ...or regular XImages */
+       {
+               X11DestroyImage( p_vout->p_x11->p_ximage[0] );
+               X11DestroyImage( p_vout->p_x11->p_ximage[1] );
+       }
+}
+
+/*******************************************************************************
+ * X11DestroyWindow: destroy X11 window
+ *******************************************************************************
+ * Destroy an X11 window created by vout_X11CreateWindow
+ *******************************************************************************
+ * Messages type: vout, major code: 110
+ *******************************************************************************/
+static void X11DestroyWindow( vout_thread_t *p_vout )
+{
+    XUnmapWindow( p_vout->p_x11->p_display, p_vout->p_x11->window );
+/*  ??  DestroyX11Colors( p_vout );  */         
+    /* ?? no more valid: colormap */
+/*    if( p_vout->p_x11->b_private_colormap )
+    {   
+        XFreeColormap( p_vout->p_x11->p_display, p_vout->p_x11->private_colormap);
+    }    */
+       XFreeGC( p_vout->p_x11->p_display, p_vout->p_x11->gc );
+    XDestroyWindow( p_vout->p_x11->p_display, p_vout->p_x11->window );
+}
+
+/*******************************************************************************
+ * X11CloseDisplay: close X11 display
+ *******************************************************************************
+ * Close an X11 display openned by vout_X11OpenDisplay().
+ *******************************************************************************
+ * Messages type: vout, major code: 111
+ *******************************************************************************/
+static void X11CloseDisplay( vout_thread_t *p_vout )
+{
+    XCloseDisplay( p_vout->p_x11->p_display );                   /* close display */
+}
+
+/*******************************************************************************
+ * X11CreateImage: create an XImage                                      
+ *******************************************************************************
+ * Messages type: vout, major code 112
+ *******************************************************************************/
+static int X11CreateImage( vout_thread_t *p_vout, XImage **pp_ximage )
+{
+    byte_t *    pb_data;                            /* image data storage zone */
+    int         i_quantum;                       /* XImage quantum (see below) */
+  
+    /* Allocate memory for image */
+    pb_data = (byte_t *) malloc( p_vout->i_bytes_per_pixel
+                                 * p_vout->i_width 
+                                 * p_vout->i_height );
+    if( !pb_data )                                                    /* error */
+    {
+        intf_ErrMsg("vout error 112-1: %s\n", strerror(ENOMEM));
+        return( -1 );   
+    }
+
+    /* Optimize the quantum of a scanline regarding its size - the quantum is
+       a diviser of the number of bits between the start of two scanlines. */
+    if( !(( p_vout->i_width * p_vout->i_bytes_per_pixel ) % 32) )
+    {
+        i_quantum = 32;
+    }
+    else    
+    {
+        if( !(( p_vout->i_width * p_vout->i_bytes_per_pixel ) % 16) )
+        {
+            i_quantum = 16;
+        }
+        else
+        {
+            i_quantum = 8;
+        }
+    }
+    
+    /* Create XImage */
+    *pp_ximage = XCreateImage( p_vout->p_x11->p_display, 
+                               DefaultVisual(p_vout->p_x11->p_display, p_vout->p_x11->i_screen),
+                               p_vout->i_screen_depth, ZPixmap, 0, pb_data, 
+                               p_vout->i_width, p_vout->i_height, i_quantum, 0);
+    if(! *pp_ximage )                                                 /* error */
+    {
+        intf_ErrMsg( "vout error 112-2: XCreateImage() failed\n" );
+        free( pb_data );
+        return( -1 );
+    }
+
+    return 0;
+}
+
+/*******************************************************************************
+ * X11CreateShmImage: create an XImage using shared memory extension
+ *******************************************************************************
+ * Prepare an XImage for DisplayX11ShmImage function.
+ * The order of the operations respects the recommandations of the mit-shm 
+ * document by J.Corbet and K.Packard. Most of the parameters were copied from 
+ * there.
+ * ?? error on failure:
+ * X Error of failed request:  BadAccess (attempt to access private resource denied)
+ *  Major opcode of failed request:  129 (MIT-SHM)
+ *  Minor opcode of failed request:  1 (X_ShmAttach)
+ *  Serial number of failed request:  17
+ *  Current serial number in output stream:  18         
+ *******************************************************************************
+ * Messages type: vout, major code 113
+ *******************************************************************************/
+static int X11CreateShmImage( vout_thread_t *p_vout, XImage **pp_ximage, 
+                              XShmSegmentInfo *p_shm_info)
+{
+    /* Create XImage */
+    *pp_ximage = XShmCreateImage( p_vout->p_x11->p_display, 
+                                  DefaultVisual(p_vout->p_x11->p_display, p_vout->p_x11->i_screen),
+                                  p_vout->i_screen_depth, ZPixmap, 0, 
+                                  p_shm_info, p_vout->i_width, p_vout->i_height );
+    if(! *pp_ximage )                                                 /* error */
+    {
+        intf_ErrMsg("vout error 113-1: XShmCreateImage() failed\n");
+        return( -1 );
+    }
+
+    /* Allocate shared memory segment - 0777 set the access permission
+     * rights (like umask), they are not yet supported by X servers */
+    p_shm_info->shmid = shmget( IPC_PRIVATE, 
+                                (*pp_ximage)->bytes_per_line * (*pp_ximage)->height, 
+                                IPC_CREAT | 0777);
+    if( p_shm_info->shmid < 0)                                        /* error */
+    {
+        intf_ErrMsg("vout error 113-2: can't allocate shared image data (%s)\n",
+                    strerror(errno));
+        XDestroyImage( *pp_ximage );
+        return( -1 );
+    }
+
+    /* Attach shared memory segment to process (read/write) */
+    p_shm_info->shmaddr = (*pp_ximage)->data = shmat(p_shm_info->shmid, 0, 0);
+    if(! p_shm_info->shmaddr )
+    {                                                                 /* error */
+        intf_ErrMsg("vout error 113-3: can't attach shared memory (%s)\n",
+                    strerror(errno));
+        shmctl( p_shm_info->shmid, IPC_RMID, 0 );        /* free shared memory */
+        XDestroyImage( *pp_ximage );
+        return( -1 );
+    }
+
+    /* Mark the shm segment to be removed when there will be no more
+     * attachements, so it is automatic on process exit or after shmdt */
+    shmctl( p_shm_info->shmid, IPC_RMID, 0 );
+
+    /* Attach shared memory segment to X server (read only) */
+    p_shm_info->readOnly = True;
+    if( XShmAttach( p_vout->p_x11->p_display, p_shm_info ) == False )    /* error */
+    {
+        intf_ErrMsg("vout error 113-4: can't attach shared memory to server\n");
+        shmdt( p_shm_info->shmaddr );     /* detach shared memory from process
+                                           * and automatic free                */
+        XDestroyImage( *pp_ximage );
+        return( -1 );
+    }
+
+    /* ?? don't know what it is. Function XShmGetEventBase prototype is defined
+     * in mit-shm document, but does not appears in any header. */
+    p_vout->p_x11->i_completion_type = XShmGetEventBase(p_vout->p_x11->p_display) + ShmCompletion;
+
+    return( 0 );
+}
+
+/*******************************************************************************
+ * X11DestroyImage: destroy an XImage                                  
+ *******************************************************************************
+ * Destroy XImage AND associated data. If pointer is NULL, the image won't be
+ * destroyed (see vout_X11ManageOutputMethod())
+ *******************************************************************************
+ * Messages type: vout, major code 114
+ *******************************************************************************/
+static void X11DestroyImage( XImage *p_ximage )
+{
+    if( p_ximage != NULL )
+    {
+        XDestroyImage( p_ximage );                       /* no free() required */
+    }
+}
+
+/*******************************************************************************
+ * X11DestroyShmImage                                                    
+ *******************************************************************************
+ * Destroy XImage AND associated data. Detach shared memory segment from
+ * server and process, then free it. If pointer is NULL, the image won't be
+ * destroyed (see vout_X11ManageOutputMethod()) 
+ *******************************************************************************
+ * Messages type: vout, major code 115
+ *******************************************************************************/
+static void X11DestroyShmImage( vout_thread_t *p_vout, XImage *p_ximage, 
+                                XShmSegmentInfo *p_shm_info )
+{
+    /* If pointer is NULL, do nothing */
+    if( p_ximage == NULL )
+    {
+        return;
+    }
+
+    XShmDetach( p_vout->p_x11->p_display, p_shm_info );     /* detach from server */
+    XDestroyImage( p_ximage );
+    if( shmdt( p_shm_info->shmaddr ) )    /* detach shared memory from process */
+    {                                     /* also automatic freeing...         */
+        intf_ErrMsg("vout error 115-1: can't detach shared memory (%s)\n", 
+                    strerror(errno));
+    }
+}
+
+/* following functions are local rendering functions */
+
+/*******************************************************************************
+ * X11RenderRGBBlank: RGB blank picture rendering function
+ *******************************************************************************
+ * Render a blank picture. Opposed to other rendering function, this one is
+ * picture-based and not line-based. Dimensions sent as parameters are effective
+ * dimensions of the rectangle to draw.
+ *******************************************************************************
+ * Messages type: vout, major code: ???
+ *******************************************************************************/
+static void X11RenderRGBBlank( vout_thread_t *p_vout, pixel_t pixel, 
+                               int i_x, int i_y, int i_width, int i_height )
+{
+    /* ?? convert rgb->pixel */
+    /* ?? call p_vout->RenderPixelBlank */
+}
+
+/*******************************************************************************
+ * X11RenderPixelBlank*: pixel blank picture rendering functions
+ *******************************************************************************
+ * Render a blank picture. Opposed to other rendering function, this one is
+ * picture-based and not line-based. Dimensions sent as parameters are effective
+ * dimensions of the rectangle to draw.
+ *******************************************************************************
+ * Messages type: vout, major code: 117
+ *******************************************************************************/
+static void X11RenderPixelBlank8bpp( vout_thread_t *p_vout, pixel_t pixel, 
+                                     int i_x, int i_y, int i_width, int i_height )
+{
+    int         i_line;                                        /* current line */
+    int         i_bytes_per_line;                     /* XImage bytes per line */
+    byte_t *    p_data;                                         /* XImage data */
+    
+    i_bytes_per_line = p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->bytes_per_line;
+    p_data = p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->data + i_bytes_per_line * i_y;
+    
+    for( i_line = i_y; i_line < i_y + i_height; i_line++, p_data += i_bytes_per_line )
+    {
+        memset( p_data + i_x, pixel, i_width - i_x );
+    }
+}
+
+static void X11RenderPixelBlank16bpp( vout_thread_t *p_vout, pixel_t pixel, 
+                                      int i_x, int i_y, int i_width, int i_height )
+{
+    int         i_line;                                        /* current line */
+    int         i_pixel;                                       /* pixel offset */
+    int         i_bytes_per_line;                     /* XImage bytes per line */
+    byte_t *    p_data;                                         /* XImage data */
+    
+    i_bytes_per_line = p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->bytes_per_line;
+    p_data = p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->data + i_bytes_per_line * i_y;
+    
+    for( i_line = i_y; i_line < i_y + i_height; i_line++, p_data += i_bytes_per_line )
+    {
+        for( i_pixel = 0; i_pixel < i_width; i_pixel++ )
+        {
+            ((u16 *)p_data)[ i_x + i_pixel ] = pixel;
+        }
+        break;
+    }
+}
+
+static void X11RenderPixelBlank24bpp( vout_thread_t *p_vout, pixel_t pixel, 
+                                      int i_x, int i_y, int i_width, int i_height )
+{
+    int         i_line;                                        /* current line */
+    int         i_pixel;                                       /* pixel offset */
+    int         i_bytes_per_line;                     /* XImage bytes per line */
+    byte_t *    p_data;                                         /* XImage data */
+    
+    i_bytes_per_line = p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->bytes_per_line;
+    p_data = p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->data + i_bytes_per_line * i_y;
+    
+    for( i_line = i_y; i_line < i_y + i_height; i_line++, p_data += i_bytes_per_line )
+    {
+        for( i_pixel = 0; i_pixel < i_width; i_pixel++ )
+        {
+            *(u32 *)(p_data + (i_x + i_pixel) * 3) |= pixel;
+        }
+    }    
+}
+
+static void X11RenderPixelBlank32bpp( vout_thread_t *p_vout, pixel_t pixel, 
+                                      int i_x, int i_y, int i_width, int i_height )
+{
+    int         i_line;                                        /* current line */
+    int         i_pixel;                                       /* pixel offset */
+    int         i_bytes_per_line;                     /* XImage bytes per line */
+    byte_t *    p_data;                                         /* XImage data */
+    
+    i_bytes_per_line = p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->bytes_per_line;
+    p_data = p_vout->p_x11->p_ximage[ p_vout->p_x11->i_buffer_index ]->data + i_bytes_per_line * i_y;
+    
+    for( i_line = i_y; i_line < i_y + i_height; i_line++, p_data += i_bytes_per_line )
+    {
+        for( i_pixel = 0; i_pixel < i_width; i_pixel++ )
+        {
+            ((u32 *)p_data)[ i_x + i_pixel ] = pixel;
+        }
+        break;
+    }
+}
+
+/*******************************************************************************
+ * X11RenderRGBLine*: RGB picture rendering functions
+ *******************************************************************************
+ * Render a 24bpp RGB-encoded picture line.
+ *******************************************************************************
+ * Messages type: vout, major code: 118
+ *******************************************************************************/
+static void X11RenderRGBLine8bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                  int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                  int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderRGBLine16bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                   int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                   int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderRGBLine24bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                   int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                   int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderRGBLine32bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                   int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                   int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * X11RenderPixelLine*: pixel picture rendering functions
+ *******************************************************************************
+ * Render a pixel-encoded picture line.
+ *******************************************************************************
+ * Messages type: vout, major code: 119
+ *******************************************************************************/
+static void X11RenderPixelLine8bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                    int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                    int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderPixelLine16bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                     int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                     int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderPixelLine24bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                     int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                     int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderPixelLine32bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                     int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                     int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * X11RenderRGBMaskLine: mask picture rendering function
+ *******************************************************************************
+ * Render a 1bpp RGB mask-encoded picture line.
+ *******************************************************************************
+ * Messages type: vout, major code: 120
+ *******************************************************************************/
+static void X11RenderRGBMaskLine( vout_thread_t *p_vout, picture_t *p_pic,
+                                  int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                  int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+/*******************************************************************************
+ * X11RenderPixelMaskLine: mask picture rendering functions
+ *******************************************************************************
+ * Render a 1bpp pixel mask-encoded picture line.
+ *******************************************************************************
+ * Messages type: vout, major code: 121
+ *******************************************************************************/
+static void X11RenderPixelMaskLine8bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                        int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                        int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderPixelMaskLine16bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                         int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                         int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderPixelMaskLine24bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                         int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                         int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}
+
+static void X11RenderPixelMaskLine32bpp( vout_thread_t *p_vout, picture_t *p_pic,
+                                         int i_x, int i_y, int i_pic_x, int i_pic_y, 
+                                         int i_width, int i_line_width, int i_ratio )
+{
+    /* ?? */
+}