--- /dev/null
+################################################################################
+# 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.
--- /dev/null
+################################################################################
+# 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 $@'
+
+
--- /dev/null
+* 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)
--- /dev/null
+%
+% 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}}
--- /dev/null
+- 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) !
+
--- /dev/null
+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
+
+
+
+
+
+
+
--- /dev/null
+%
+% 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}
--- /dev/null
+#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
--- /dev/null
+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 ?)
--- /dev/null
+%
+% 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}
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+-----------------------------------------------------------------------------
+* 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).
+
+
--- /dev/null
+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
+
+
+
--- /dev/null
+/*******************************************************************************
+ * 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"
+
+
+
--- /dev/null
+/******************************************************************************
+ * 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 */ \
+}
--- /dev/null
+/******************************************************************************
+ * 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);
+}
--- /dev/null
+/******************************************************************************
+ * 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 );
--- /dev/null
+/******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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) ) ) )
--- /dev/null
+/*******************************************************************************
+ * 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"
--- /dev/null
+/*******************************************************************************
+ * 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 );
+
+
+
--- /dev/null
+/*******************************************************************************
+ * 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
--- /dev/null
+/******************************************************************************
+ * 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;
--- /dev/null
+/*******************************************************************************
+ * 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 */
+/* ?? */
--- /dev/null
+/*******************************************************************************
+ * 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 */
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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 );
+
+
+
--- /dev/null
+/******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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[];
+
--- /dev/null
+/*******************************************************************************
+ * 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, ... );
+
+
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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 );
+
--- /dev/null
+/*******************************************************************************
+ * 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;
+
--- /dev/null
+/*******************************************************************************
+ * 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 );
+
--- /dev/null
+/*******************************************************************************
+ * 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 */
+
+
+
+
--- /dev/null
+/*******************************************************************************
+ * 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)
--- /dev/null
+/*******************************************************************************
+ * 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 */
+/* ?? */
--- /dev/null
+/*******************************************************************************
+ * 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
--- /dev/null
+/*******************************************************************************
+ * 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
+
+
+
+
+
+
+
--- /dev/null
+/*******************************************************************************
+ * 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 );
+
+
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/*******************************************************************************
+ * 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 );
--- /dev/null
+/* 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 "};
--- /dev/null
+/******************************************************************************
+ * 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);
+}
--- /dev/null
+/******************************************************************************
+ * 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 );
+}
--- /dev/null
+/******************************************************************************
+ * 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 )
+{
+}
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
+
--- /dev/null
+/*******************************************************************************
+ * 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
+}
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
--- /dev/null
+/*******************************************************************************
+ * 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 )
+{
+}
+
+
+
--- /dev/null
+/*******************************************************************************
+ * 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 */
+}
+
--- /dev/null
+/*******************************************************************************
+ * 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 );
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
+
+
+
+
--- /dev/null
+/*******************************************************************************
+ * 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;
+}
--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+ }
+ }
+
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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;
+ }
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
--- /dev/null
+/*******************************************************************************
+ * 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
--- /dev/null
+/*******************************************************************************
+ * 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; */
+}
--- /dev/null
+/*******************************************************************************
+ * 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) );
+}
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
--- /dev/null
+/*******************************************************************************
+ * 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;
+}
+
+
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
--- /dev/null
+/*******************************************************************************
+ * 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 )
+{
+ /* ?? */
+}
+
--- /dev/null
+/*******************************************************************************
+ * 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);
+}
+
+
--- /dev/null
+/*******************************************************************************
+ * 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 );
+}
+
+
--- /dev/null
+/*******************************************************************************
+ * 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
+ }
+}
+
--- /dev/null
+/*******************************************************************************
+ * 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 )
+{
+ /* ?? */
+}