summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2001-08-06 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2001-08-06 18:00:00 +0200
commitf1d1c9849c8e27cccb46cf9c0d0ccb59da3f91f9 (patch)
treeb5a5f73f7b7595c7371cab1fc11f2ea60aa2b392
parent8f9cc68f76c4fd0960f919a77fb16a6455922deb (diff)
downloadvdr-patch-lnbsharing-f1d1c9849c8e27cccb46cf9c0d0ccb59da3f91f9.tar.gz
vdr-patch-lnbsharing-f1d1c9849c8e27cccb46cf9c0d0ccb59da3f91f9.tar.bz2
Version 0.90vdr-0.90
- Modified the display of the channel group separators (thanks to Markus Lang for this suggestion). - Added support for replaying DVDs (thanks to Andreas Schultz). See INSTALL for instructions on how to compile VDR with DVD support. - Fixed replay progress display in case replay is paused while watching an ongoing recording. - Ringbuffer uses semaphores to signal empty/full conditions. - Fixed calculating the timeout value in cFile::FileReady() (thanks to Wolfgang Henselmann-Weiss).
-rw-r--r--CONTRIBUTORS10
-rw-r--r--HISTORY12
-rw-r--r--INSTALL24
-rw-r--r--MANUAL2
-rw-r--r--Makefile51
-rw-r--r--Tools/master-timer/LIESMICH106
-rw-r--r--Tools/master-timer/README52
-rw-r--r--Tools/master-timer/Todo10
-rwxr-xr-xTools/master-timer/master-timer.pl1169
-rwxr-xr-xTools/master-timer/process_summary.pl79
-rw-r--r--Tools/master-timer/sample/channels-to-scan8
-rw-r--r--Tools/master-timer/sample/config14
-rw-r--r--Tools/master-timer/sample/deepblack79
-rw-r--r--Tools/master-timer/sample/done1
-rw-r--r--Tools/master-timer/sample/subtitle-movie41
-rw-r--r--Tools/master-timer/sample/torecord32
-rwxr-xr-xTools/master-timer/scan-channels8
-rwxr-xr-xTools/schnitt/cut2.pl39
-rwxr-xr-xTools/schnitt/cut3.pl40
-rwxr-xr-xTools/schnitt/cutall3.pl31
-rw-r--r--Tools/schnitt/dump.c65
-rwxr-xr-xTools/schnitt/play10
-rw-r--r--ac3dec/Makefile22
-rw-r--r--ac3dec/ac3.h60
-rw-r--r--ac3dec/ac3_internal.h344
-rw-r--r--ac3dec/bit_allocate.c494
-rw-r--r--ac3dec/bit_allocate.h24
-rw-r--r--ac3dec/bitstream.c76
-rw-r--r--ac3dec/bitstream.h76
-rw-r--r--ac3dec/coeff.c353
-rw-r--r--ac3dec/coeff.h24
-rw-r--r--ac3dec/crc.c96
-rw-r--r--ac3dec/crc.h27
-rw-r--r--ac3dec/debug.c58
-rw-r--r--ac3dec/debug.h37
-rw-r--r--ac3dec/decode.c269
-rw-r--r--ac3dec/decode.h22
-rw-r--r--ac3dec/dither.c115
-rw-r--r--ac3dec/dither.h37
-rw-r--r--ac3dec/downmix.c428
-rw-r--r--ac3dec/downmix.h28
-rw-r--r--ac3dec/exponent.c135
-rw-r--r--ac3dec/exponent.h28
-rw-r--r--ac3dec/imdct.c468
-rw-r--r--ac3dec/imdct.h26
-rw-r--r--ac3dec/parse.c597
-rw-r--r--ac3dec/parse.h28
-rw-r--r--ac3dec/rematrix.c83
-rw-r--r--ac3dec/rematrix.h25
-rw-r--r--ac3dec/sanity_check.c131
-rw-r--r--ac3dec/sanity_check.h27
-rw-r--r--ac3dec/stats.c178
-rw-r--r--ac3dec/stats.h27
-rw-r--r--channels.conf2
-rw-r--r--config.h4
-rw-r--r--dvbapi.c1451
-rw-r--r--dvbapi.h19
-rw-r--r--dvd.c148
-rw-r--r--dvd.h53
-rw-r--r--i18n.c22
-rw-r--r--menu.c201
-rw-r--r--menu.h28
-rw-r--r--osd.c3
-rw-r--r--osd.h9
-rw-r--r--ringbuffer.c253
-rw-r--r--ringbuffer.h84
-rw-r--r--thread.c39
-rw-r--r--thread.h17
-rw-r--r--tools.c6
-rw-r--r--vdr.c46
70 files changed, 8206 insertions, 405 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 676cf89..46a8410 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -106,3 +106,13 @@ Ulrich Röder <dynamite@efr-net.de>
Helmut Schächner <schaechner@yahoo.com>
for his support in keeping the Premiere World channels up to date in 'channels.conf'
+
+Andreas Schultz <aschultz@warp10.net>
+ for adding support for replaying DVDs (much of this was derived from
+ dvdplayer-0.5 by Matjaz Thaler <matjaz.thaler@guest.arnes.si>)
+
+Aaron Holtzman
+ for writing 'ac3dec'
+
+Wolfgang Henselmann-Weiss <Wolfgang_Henselmann@betaresearch.de>
+ for fixing calculating the timeout value in cFile::FileReady()
diff --git a/HISTORY b/HISTORY
index d625b26..041e442 100644
--- a/HISTORY
+++ b/HISTORY
@@ -609,3 +609,15 @@ Video Disk Recorder Revision History
- Increased timeout until reporting "video data stream broken" when recording.
- Explicitly switching back to the previously active channel after ending a
replay session (to have it shown correctly in case it was in 'Transfer Mode').
+
+2001-08-06: Version 0.90
+
+- Modified the display of the channel group separators (thanks to Markus Lang
+ for this suggestion).
+- Added support for replaying DVDs (thanks to Andreas Schultz). See INSTALL for
+ instructions on how to compile VDR with DVD support.
+- Fixed replay progress display in case replay is paused while watching an
+ ongoing recording.
+- Ringbuffer uses semaphores to signal empty/full conditions.
+- Fixed calculating the timeout value in cFile::FileReady() (thanks to
+ Wolfgang Henselmann-Weiss).
diff --git a/INSTALL b/INSTALL
index 28037eb..86b4a1e 100644
--- a/INSTALL
+++ b/INSTALL
@@ -15,7 +15,16 @@ If you have the DVB driver source in a different location
you will have to change the definition of DVBDIR in the
Makefile.
-This program requires the card driver version 0.9.0 or higher
+If you want to use your DVD drive you will need to compile VDR with
+
+ make DVD=1
+
+to activate DVD support. VDR then also needs the package 'libdvdread'
+in order to replay DVDs. This package is expected to be located in the
+directory ../DVD (seen from the VDR directory). Adjust the definition
+of DVDDIR in the Makefile if necessary.
+
+VDR requires the card driver version 0.9.0 or higher
to work properly. You need to load the dvb.o module *without* option
'outstream=0' (previous versions of VDR required this option to have
the driver supply the data in AV_PES format; as of version 0.70 VDR
@@ -100,6 +109,19 @@ This program must be given to VDR with the '-a' option, as in
vdr -a ac3play
+Accessing the DVD drive:
+------------------------
+
+By default VDR expects the DVD drive to be located at /dev/dvd (which
+typically is a symbolic link to the actual device, for instance /dev/hdc).
+You can use the '-V' option to overwrite this, as in
+
+ vdr -V /media/dvd
+
+Note that the user id under which VDR runs needs to have write access to
+the DVD device in order to replay CSS protected DVDs (which also requires
+the presence of the 'libcss' library).
+
The video data directory:
-------------------------
diff --git a/MANUAL b/MANUAL
index c59f3fe..59ff57c 100644
--- a/MANUAL
+++ b/MANUAL
@@ -19,7 +19,7 @@ Video Disk Recorder User's Manual
Back - Menu off Main menu Main menu Discard Main menu Recordings menu
Red - Record Edit Edit - Play -
Green - Language New New - Rewind Skip -60s
- Yellow - - Delete Delete - Delete Skip +60s
+ Yellow - Eject DVD Delete Delete - Delete Skip +60s
Blue - Resume Mark Mark - Summary Stop
0..9 Ch select - - - Numeric inp. - Editing
diff --git a/Makefile b/Makefile
index 507de21..8918717 100644
--- a/Makefile
+++ b/Makefile
@@ -4,12 +4,24 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.22 2001/06/02 09:15:39 kls Exp $
+# $Id: Makefile 1.24 2001/08/06 16:13:42 kls Exp $
DVBDIR = ../DVB
+DVDDIR = ../DVD
+AC3DIR = ./ac3dec
INCLUDES = -I$(DVBDIR)/ost/include
-OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\
+
+ifdef DVD
+INCLUDES += -I$(DVDDIR)/libdvdread
+LIBDIRS += -L$(DVDDIR)/libdvdread/dvdread/.libs
+DEFINES += -DDVDSUPPORT
+DEFINES += -D_LARGEFILE64_SOURCE # needed by libdvdread
+AC3LIB = $(AC3DIR)/libac3.a
+DVDLIB = -ldvdread
+endif
+
+OBJS = config.o dvbapi.o dvbosd.o dvd.o eit.o font.o i18n.o interface.o menu.o osd.o\
recording.o remote.o remux.o ringbuffer.o svdrp.o thread.o tools.o vdr.o\
videodir.o
@@ -42,29 +54,30 @@ font: genfontfile fontfix.c fontosd.c
# Dependencies:
-config.o : config.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
-dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h eit.h font.h recording.h remux.h ringbuffer.h thread.h tools.h videodir.h
+config.o : config.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
+dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h recording.h remux.h ringbuffer.h thread.h tools.h videodir.h
dvbosd.o : dvbosd.c dvbosd.h font.h tools.h
-eit.o : eit.c config.h dvbapi.h dvbosd.h eit.h font.h thread.h tools.h videodir.h
+dvd.o : dvd.c dvd.h
+eit.o : eit.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h thread.h tools.h videodir.h
font.o : font.c font.h fontfix.c fontosd.c tools.h
-i18n.o : i18n.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h thread.h tools.h
-interface.o : interface.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
-menu.o : menu.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h
-osd.o : osd.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h
-recording.o : recording.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
-remote.o : remote.c config.h dvbapi.h dvbosd.h eit.h font.h remote.h thread.h tools.h
+i18n.o : i18n.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h i18n.h thread.h tools.h
+interface.o : interface.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h i18n.h interface.h remote.h svdrp.h thread.h tools.h
+menu.o : menu.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h
+osd.o : osd.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h i18n.h interface.h osd.h remote.h svdrp.h thread.h tools.h
+recording.o : recording.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h interface.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
+remote.o : remote.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h remote.h thread.h tools.h
remux.o : remux.c remux.h thread.h tools.h
ringbuffer.o: ringbuffer.c ringbuffer.h thread.h tools.h
-svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h
+svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h
thread.o : thread.c thread.h tools.h
tools.o : tools.c tools.h
-vdr.o : vdr.c config.h dvbapi.h dvbosd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
+vdr.o : vdr.c config.h dvbapi.h dvbosd.h dvd.h eit.h font.h i18n.h interface.h menu.h osd.h recording.h remote.h svdrp.h thread.h tools.h videodir.h
videodir.o : videodir.c tools.h videodir.h
# The main program:
-vdr: $(OBJS)
- g++ -g -O2 $(OBJS) -lncurses -ljpeg -lpthread -o vdr
+vdr: $(OBJS) $(AC3LIB)
+ g++ -g -O2 $(OBJS) -lncurses -ljpeg -lpthread $(LIBDIRS) $(DVDLIB) $(AC3LIB) -o vdr
# The font files:
@@ -80,9 +93,15 @@ genfontfile.o: genfontfile.c
genfontfile: genfontfile.o
gcc -o $@ -L/usr/X11R6/lib $< -lX11
+# The ac3dec library:
+
+$(AC3LIB):
+ make -C $(AC3DIR) all
+
# Housekeeping:
clean:
- -rm -f $(OBJS) vdr genfontfile genfontfile.o core
+ make -C $(AC3DIR) clean
+ -rm -f $(OBJS) vdr genfontfile genfontfile.o core *~
CLEAN: clean
-rm -f fontfix.c fontosd.c
diff --git a/Tools/master-timer/LIESMICH b/Tools/master-timer/LIESMICH
new file mode 100644
index 0000000..9694875
--- /dev/null
+++ b/Tools/master-timer/LIESMICH
@@ -0,0 +1,106 @@
+ Master-Timer
+ ============
+
+
+1. Einleitung
+-------------
+
+Master-Timer ist ein System zum automatischen Aufnehmen von Serien und Filmen.
+
+2. Voraussetzungen
+------------------
+
+VDR liefert die "epg.data".
+
+3. Konfigurationsdateien
+------------------------
+
+Alle Konfigurationsdateien liegen unter "<HOME>.master-timer"
+
+config: Eine Ansammlung von Key-Value Paaren. Alle sind "optional" und
+ erhalten dann die angegebenen Default-Werte
+
+(# = Kommentarzeilen)
+marginstart (Default 600)
+ Anzahl der "Sicherheits" Sekunden die ein Timer frueher beginnen soll
+
+marginstop (Default 600)
+ Anzahl der "Sicherheits" Sekunden die ein Timer laenger dauern soll
+
+defaultprio (Default 50)
+ Die Prioritaet die fuer Timer verwendet wird wo keine Prioritaet
+ angegeben ist
+
+DVBCards (Default 1)
+ Anzahl der vorhandenen DVB-Karten (Derzeit nicht verwendet)
+
+Dest-Host (Default "localhost")
+ Host-name oder IP des Rechners auf dem VDR laeuft
+
+Dest-Port (Default "2001")
+ Port der VDR verwendet
+
+jointimers (Default 0)
+ Sollen aufeinanderfolgende Timer auf den gleichen Kanal zusammengefasst
+ werden (0 = "Nein", alles andere "Ja")
+
+debug (Default 0)
+ Debug-Level, die einzelnen Debug-Werte muessen aus folgenden Werten
+ zusammengezaehlt werden
+ 1 : Dump "torecord"
+ 2 : Dump all timers
+ 4 : Show when a timer will be deleted
+ 8 : Dump the "Done" REs
+ 16 : Verbose Config-Reading
+
+deepblack: Eine Liste von Titeln die man NIEMALS NIMMER sehen will
+ Jede Zeile = 1 Titel
+
+subtitle-movies: Eine Liste der "Subtitel" die ein Zeichen fuer einen Film sind
+ (Soweit die von den Sendern richtig ausgefuellt sind.)
+ Jede Zeile = 1 Subtitel
+
+torecord: Die Sachen die man Aufnehmen will
+ Jede Zeile = 1 Timer
+# Format: (Every field is "optional".
+# [Title RE|Subtitle RE|Description RE|Channel-Name|Timeframe|Prio|Timer-Title]
+#
+# To record something at least one of the "Title", "Subtitle" or "Description"
+# Fields has to be provided. This 3 fields are "include" and the rest are
+# "exclude" fields!
+#
+# More than one channel definition can be provided. The delimiter is ";"
+# Additionaly you can make a "blacklist" of Channels when you prepent a "!" to the first Channel Definition
+# The "!" is only tested for the FIRST Channel definition.
+# You can only have a white or a blacklist (Mixing doesn't make sense!)
+#
+# ex. Record the series "Deep Space Nine" on Sci-Fantasy in the timeframe 09:00 - 14:00
+# Deep Space Nine|||Sci-Fantasy|0900-1400|99|DS9
+#
+# Record all "Actionfilm"s with "Schwarzenegger"
+# |Actionfilm|Schwarzenegger
+#
+
+
+done: The titles/subtitles which are already recorded/should not be recorded
+ (Programmed Timers which got inserted into "done" will be deleted
+ automaticaly)
+
+4. Notices
+----------
+
+- Recordings "overlapping" on the same channel, will be joined into one Timer
+- Title/Subtitle/Descriptions are "fixed" for Channel that don't fill them
+ out "correctly" (Currently the "Bugs" from Pro-7/VOX/VIVA)
+ Pro7: Remove the Title from the Subtitle '<Title> / <Subtitle>'
+ VOX/VIVA: Subtitle is enclosed into "" and after ". " is the description
+ VIVA: When the Subtitle beginns with space the subtitle is moved to
+ description
+ All (except the second VIVA one) fixes are tried onto ALL Subtitles.
+
+5. Known-Bugs
+-------------
+
+- It isn't checked if there are enough DVB-Cards
+- Overlapping Timers, on the same channel, are always joined
+- JOINed timers which are "done" don't get deleted automaticaly
diff --git a/Tools/master-timer/README b/Tools/master-timer/README
new file mode 100644
index 0000000..c71e6e1
--- /dev/null
+++ b/Tools/master-timer/README
@@ -0,0 +1,52 @@
+ Master-Timer (w) by Matthias Schniedermeyer (ms@citd.de)
+ ============
+
+
+1. Introduction
+---------------
+
+Master-Timer ist a system for recording Films/Series automaticaly
+
+2. Requierements
+----------------
+
+epg.data
+
+3. Config-Files
+---------------
+
+For all files: One Entry per Line. Each line is a "Regular Expresion"
+ So you can use all Perl-Style REs you want.
+ The RE are matched with "i" so they are case insensitive!
+ (Except for the "done"-list, these must match excatly!)
+
+deepblack: Blacklist of "Titles" you NEVER EVER want to get to you eyes
+
+subtitle-movies: A list of "Subtitles" which indicate a movie.
+ (For Channels that correctly fill out the Subtitle.
+ e.g. it won't work for *eRTL*)
+
+torecord: The titles/subtitles/Description you want to record
+
+done: The titles/subtitles which are already recorded/should not be recorded
+ (Programmed Timers which got inserted into "done" will be deleted
+ automaticaly)
+
+4. Notices
+----------
+
+- Recordings "overlapping" on the same channel, will be joined into one Timer
+- Title/Subtitle/Descriptions are "fixed" for Channel that don't fill them
+ out "correctly" (Currently the "Bugs" from Pro-7/VOX/VIVA)
+ Pro7: Remove the Title from the Subtitle '<Title> / <Subtitle>'
+ VOX/VIVA: Subtitle is enclosed into "" and after ". " is the description
+ VIVA: When the Subtitle beginns with space the subtitle is moved to
+ description
+ All (except the second VIVA one) fixes are tried onto ALL Subtitles.
+
+5. Known-Bugs
+-------------
+
+- It isn't checked if there are enough DVB-Cards
+- Overlapping Timers, on the same channel, are always joined
+- JOINed timers which are "done" don't get deleted automaticaly
diff --git a/Tools/master-timer/Todo b/Tools/master-timer/Todo
new file mode 100644
index 0000000..722ee96
--- /dev/null
+++ b/Tools/master-timer/Todo
@@ -0,0 +1,10 @@
+
+- "Intelligenter" Kanal-Scanner (z.B. nur 1 Kanal fuer ein
+ Sender-"Gruppe")
+- Filtern nach Serie/Film
+- "Komfortable" Anzeige, mit Black & Whitelisten, fuer Genres/Titeln usw.
+- Unterstueztung von 1xVDR pro Karte
+- Abspielen (mit automatischen "killen" des "Frontend"-VDRs) von
+ Aufzeichnungen
+- "View"-Timer d.h. Timer der nicht Aufnimmt sondern nur den Kanal aendert
+- "unwichtige" Timer "verdraengen" wenn andere Aufnahmen anstehen.
diff --git a/Tools/master-timer/master-timer.pl b/Tools/master-timer/master-timer.pl
new file mode 100755
index 0000000..3b98acc
--- /dev/null
+++ b/Tools/master-timer/master-timer.pl
@@ -0,0 +1,1169 @@
+#!/usr/bin/perl -w
+
+use strict;
+# For the TCP-Connection to VDR
+use Socket;
+# For converting the Timers, read from VDR, back to Unix-Timestamps
+use Time::Local;
+
+# Debugmode
+# You have to add the following numbers to build the debug-var
+# 1 : Dump the "torecord"
+# 2 : Dump all timers
+# 4 : Show when a timer will be deleted
+# 8 : Dump the "Done" REs
+# 16 : Verbose Config-Reading
+my $debug = 0;
+
+# The Supervariable Program
+# %Program{$title}{$channel}{$time}{duration}
+# {subtitle}
+# {description}
+
+# The Supervariable Timer
+# %Timer{$time}{$channel}{$title}{duration}
+# {subtitle}
+# {prio}
+# {real_title}
+# {VDR} (Already programmed)
+# The Value of VDR is ">0" for the position in the Timer-List or "R" for a "Repeating" Timer.
+# A Value of >1.000.000 is a Master Timer-Timer which is already programmed into VDR
+
+# Variable-Definition
+my (%Program, @channels, %channels, %Timer);
+
+# Which Subtitles are Movies
+my ($subtitle_movie);
+
+# Blacklist
+my ($title_deepblack);
+
+# What is already recorded/Should not be recorded
+my ($title_done, $subtitle_done);
+
+# What to record
+my ($title_torecord, $subtitle_torecord, $description_torecord, @title_torecord, @subtitle_torecord, @description_torecord, @channel_torecord, @timeframe_torecord, @prio_torecord, @timer_title_torecord, $num_torecord, @marginstart_torecord, @marginstop_torecord, @machine_torecord);
+
+# Default Priority for Timers (Config: defaultprio)
+my $default_prio = 50;
+
+# How many DVB-S cards are there (Config: DVBCards)
+my $DVB_cards = 1;
+
+# How many seconds to substract from the time and to add to the duration
+my $marginstart = 60*10; # Config: Marginstart
+my $marginstop = 60*10; # Config: Marginstop
+
+# Shall Timers, on the same channel, be joined if they overlap
+my $jointimers = 0;
+
+# Hostname/IP of DVB-Computer and the Port of VDR
+my @Dest = ("localhost:2001"); # Config: Dest
+
+# Which VDR-Instance shall be used
+my $currentVDR = 1;
+
+# Working-Variables
+my ($title, $duration, $subtitle, $channel, $time, $description, $hit);
+my (@time, @date);
+
+sub sub_die
+ {
+ my ($error) = @_;
+ &closesocket();
+ die "$error";
+ }
+
+
+if ($ARGV[0])
+ {
+ $currentVDR = $ARGV[0];
+ }
+
+&init();
+&dumpdone() if ($debug & 8);
+&dumptorecord() if ($debug & 1);
+&fetchVDRTimers();
+&process_torecord();
+print "Timers before joining\n" if ($debug & 2 && $jointimers);
+&dumptimers() if ($debug & 2);
+if ($jointimers)
+ {
+ &jointimers();
+ print "Timers after joining\n" if ($debug & 2);
+ &dumptimers() if ($debug & 2);
+ }
+
+&printtimers();
+&transfertimers();
+&closesocket();
+
+#
+# Subfunctions
+#
+
+sub dumpdone()
+ {
+ print "Start Done-dump\n";
+ print "Titledone: \"$title_done\"\n";
+ print "Subtitledone \"$subtitle_done\"\n";
+ print "End Done-dump\n";
+ }
+
+sub dumptorecord()
+ {
+ print "Start Torecord-dump\n";
+ print "Regex-Title: $title_torecord\n";
+ print "Regex-Subtitle: $subtitle_torecord\n";
+ print "Regex-Description: $description_torecord\n";
+ foreach my $num (0 .. $num_torecord)
+ {
+ print "Timer Number $num: ";
+
+ print "Title: \"$title_torecord[$num]\" " if ($title_torecord[$num]);
+ print "Title: \"\" " unless ($title_torecord[$num]);
+
+ print "Subtitle: \"$subtitle_torecord[$num]\" "if ($subtitle_torecord[$num]);
+ print "Subtitle: \"\" " unless ($subtitle_torecord[$num]);
+
+ print "Description: \"$description_torecord[$num]\" " if ($description_torecord[$num]);
+ print "Description: \"\" " unless ($description_torecord[$num]);
+
+ print "Timeframe: \"$timeframe_torecord[$num]\" " if ($timeframe_torecord[$num]);
+ print "Timeframe: \"\" " unless ($timeframe_torecord[$num]);
+
+ print "Channel: \"". join (";",@{$channel_torecord[$num]})."\" " if ($channel_torecord[$num]);
+ print "Channel: \"\" " unless ($channel_torecord[$num]);
+
+ print "Prio: \"$prio_torecord[$num]\" " if ($prio_torecord[$num]);
+ print "Prio: \"\" " unless ($prio_torecord[$num]);
+
+ print "Timertitle: \"$timer_title_torecord[$num]\" " if ($timer_title_torecord[$num]);
+ print "Timertitle: \"\" " unless ($timer_title_torecord[$num]);
+
+ print "Marginstart: \"$marginstart_torecord[$num]\" " if ($marginstart_torecord[$num]);
+ print "Marginstart: \"\" " unless ($marginstart_torecord[$num]);
+
+ print "Marginstop: \"$marginstop_torecord[$num]\" " if ($marginstop_torecord[$num]);
+ print "Marginstop: \"\" " unless ($marginstop_torecord[$num]);
+
+ print "Machine: \"$machine_torecord[$num]\" " if ($machine_torecord[$num]);
+ print "Machine: \"\" " unless ($machine_torecord[$num]);
+
+ print "\n";
+ }
+ print "End Torecord-dump\n";
+ }
+
+sub dumptimers()
+ {
+ print "Start Timers-dump\n";
+ foreach $time (sort {$a <=> $b} keys %Timer)
+ {
+ foreach $channel (sort keys %{%Timer->{$time}})
+ {
+ foreach $title (sort keys %{%Timer->{$time}->{$channel}})
+ {
+ my ($prio, @time, @date, @time2);
+ my ($realtitle);
+ @time = &GetTime ($time);
+ @date = &GetDay ($time);
+ @time2 = &GetTime ($time + $Timer{$time}{$channel}{$title}{duration});
+ $subtitle = $Timer{$time}{$channel}{$title}{subtitle};
+ $prio = $Timer{$time}{$channel}{$title}{prio};
+ $realtitle = $Timer{$time}{$channel}{$title}{real_title};
+ print "2:$channels{$channel}{number}:$date[1]:$time[0]$time[1]:$time2[0]$time2[1]:$prio:99:$title:Title: \"$realtitle\"||Subtitle: \"$subtitle\":$Timer{$time}{$channel}{$title}{VDR}\n";
+ }
+ }
+ }
+ print "End Timers-dump\n";
+ }
+
+sub printtimers()
+ {
+ foreach $time (sort {$a <=> $b} keys %Timer)
+ {
+ foreach $channel (sort keys %{%Timer->{$time}})
+ {
+ foreach $title (sort keys %{%Timer->{$time}->{$channel}})
+ {
+ my ($prio, @time, @date, @time2);
+ if ($Timer{$time}{$channel}{$title}{VDR} eq 0)
+ {
+ my ($realtitle);
+ @time = &GetTime ($time);
+ @date = &GetDay ($time);
+ @time2 = &GetTime ($time + $Timer{$time}{$channel}{$title}{duration});
+ $subtitle = $Timer{$time}{$channel}{$title}{subtitle};
+ $prio = $Timer{$time}{$channel}{$title}{prio};
+ $realtitle = $Timer{$time}{$channel}{$title}{real_title};
+
+ print "2:$channels{$channel}{number}:$date[1]:$time[0]$time[1]:$time2[0]$time2[1]:$prio:99:$title:Title: \"$realtitle\"||Subtitle: \"$subtitle\"\n";
+ }
+ }
+ }
+ }
+ }
+
+sub transfertimers()
+ {
+ foreach $time (sort {$a <=> $b} keys %Timer)
+ {
+ foreach $channel (sort keys %{%Timer->{$time}})
+ {
+ foreach $title (sort keys %{%Timer->{$time}->{$channel}})
+ {
+ my ($prio, @time, @date, @time2, $realtitle, $result);
+ if ($Timer{$time}{$channel}{$title}{VDR} eq 0)
+ {
+ @time = &GetTime ($time);
+ @date = &GetDay ($time);
+ @time2 = &GetTime ($time + $Timer{$time}{$channel}{$title}{duration});
+ $subtitle = $Timer{$time}{$channel}{$title}{subtitle};
+ $prio = $Timer{$time}{$channel}{$title}{prio};
+ $realtitle = $Timer{$time}{$channel}{$title}{real_title};
+
+ ($result) = GetSend ("newt 2:$channels{$channel}{number}:$date[1]:$time[0]$time[1]:$time2[0]$time2[1]:$prio:99:$title:Title: \"$realtitle\"||Subtitle: \"$subtitle\"");
+ print "Timer: $result" if ($debug & 2);
+ }
+ }
+ }
+ }
+ }
+
+# Convert the Unix-Time-Stamp into "month" and "Day of month"
+sub GetDay
+ {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(shift);
+ $mon++;
+ $mon = sprintf ("%02i",$mon);
+ $mday = sprintf ("%02i",$mday);
+ return ($mon, $mday);
+ }
+
+# Convert the Unix-Time-Stramp into "hour" and "minute"
+sub GetTime
+ {
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(shift);
+ $hour = sprintf ("%02i",$hour);
+ $min = sprintf ("%02i",$min);
+ return ($hour, $min);
+ }
+
+# Workaround some EPG-Bugs
+sub correct_epg_data
+ {
+ if ($subtitle)
+ {
+ # For Pro-7. Remove $title from $subtitle
+ $subtitle =~ s/$title\s\/\s//;
+
+ # For VOX & VIVA. The Format it '"<Subtitle>". <Description>'
+ if ($subtitle =~ /^\"(.*?)\"\.\s(.*)/)
+ {
+ # Lets see if there are Channels that where the VOX/VIVA scheme matches, but also have a description
+ if ($description)
+ {
+ sub_die ("Subtitle: \"$subtitle\"\nDescription\"$description\"\n");
+ }
+ $subtitle = $1;
+ $description = $2;
+ }
+ elsif ($channel eq "VIVA")
+ {
+ if ($subtitle =~ /^\s(.*)/)
+ {
+ $subtitle = "";
+ $description = $1;
+ }
+ }
+ }
+
+ # Workaround for the broken PRO-7/Kabel-1 EPG-Date. If Time is between 00.00 and 05.00 the time is shifted forward by a day
+ if ($channel eq "Pro-7" || $channel eq "Kabel-1")
+ {
+ my (@time);
+ @time = GetTime ($time);
+ if ($time[0] >= 0 && ($time[0] <= 4 || ($time[0] == 5 && $time[1] == 0)))
+ {
+ $time += 24*60*60;
+ }
+ }
+}
+
+# Add a Recording into the "to record"-List
+sub addtimer
+ {
+ my ($hit, $title, $realtitle, $subtitle, $channel, $time, $duration, $prio, $VDR, $time2, $title2, $channel2, $marginstart, $marginstop);
+ ($title, $realtitle, $subtitle, $channel, $time, $duration, $prio, $VDR, $marginstart, $marginstop) = @_;
+# print "Title: \"$title\" Realtitle: \"$realtitle\" Subtitle: \"$subtitle\" Channel: \"$channel\" Time: \"$time\" Duration: \"$duration\" Prio: \"$prio\" VDR: \"$VDR\"\n";
+
+ $hit = 1;
+
+ foreach $time2 (sort keys %Timer)
+ {
+ foreach $title2 (sort keys %{%Timer->{$time2}->{$channel}})
+ {
+ my ($ctime, $ctime2);
+ $ctime = $time2;
+ $ctime2 = $time2 + $Timer{$time2}{$channel}{$title2}{duration};
+
+ if (($time >= $ctime) && ($time <= $ctime2))
+ {
+ undef $hit;
+ }
+ }
+ }
+
+
+ if ($hit)
+ {
+ $time -= $marginstart;
+ $duration += $marginstart + $marginstop;
+ $Timer{$time}{$channel}{$title}{duration}=$duration;
+ $Timer{$time}{$channel}{$title}{subtitle}=$subtitle;
+ $Timer{$time}{$channel}{$title}{prio}=$prio;
+ $Timer{$time}{$channel}{$title}{VDR}=$VDR;
+ $Timer{$time}{$channel}{$title}{real_title}=$realtitle;
+ }
+ }
+
+sub deltimer()
+ {
+ my ($time, $channel, $title, $delete_from_VDR);
+ ($time, $channel, $title, $delete_from_VDR) = @_;
+
+# if ($delete_from_VDR)
+# {
+# if ($Timer{$time}{$channel}{$title}{VDR})
+# {
+# if ($Timer{$time}{$channel}{$title}{VDR} =~ s/ ^R/)
+# {
+# print "Error: A Repeating-Timer can't be deleted from VDR: \"$title\"\n";
+# }
+# elsif ($Timer{$time}{$channel}{$title}{VDR} < 1000000)
+# {
+# print "A User-Programmed Timer has been deleted from VDR: \"$title\"\n";
+# }
+# else
+# {
+#
+# }
+# }
+# }
+
+ delete $Timer{$time}{$channel}{$title}{duration};
+ delete $Timer{$time}{$channel}{$title}{subtitle};
+ delete $Timer{$time}{$channel}{$title}{prio};
+ delete $Timer{$time}{$channel}{$title}{VDR};
+ delete $Timer{$time}{$channel}{$title}{real_title};
+ delete $Timer{$time}{$channel}{$title};
+ delete $Timer{$time}{$channel} if (keys %{ $Timer{$time}{$channel} } == 1);
+ delete $Timer{$time} if (keys %{ $Timer{$time} } == 1);
+ }
+
+sub jointimers
+ {
+ #
+ # FIXME: 2 Timers on the same channel will always be joined.
+ # It should be checked if there is another DVB-Card available.
+ #
+ # FIXME2: When one timer is already programmed in VDR, delete that timer in VDR.
+ my ($running, $counter, @times, $channel, $title, $channel2, $title2);
+ $running = 1;
+ outer: while ($running)
+ {
+ $counter = 0;
+ @times = sort {$a <=> $b} keys %Timer;
+
+ # We only need to check till the second last timer. The last one can't have a overlapping one.
+ while ($counter < $#times)
+ {
+ foreach $channel (sort keys %{%Timer->{$times[$counter]}})
+ {
+ foreach $title (sort keys %{%Timer->{$times[$counter]}->{$channel}})
+ {
+ if ($times[$counter + 1] < ($times[$counter] + $Timer{$times[$counter]}{$channel}{$title}{duration}))
+ {
+ foreach $channel2 (sort keys %{%Timer->{$times[$counter + 1]}})
+ {
+ foreach $title2 (sort keys %{%Timer->{$times[$counter + 1]}->{$channel}})
+ {
+ if ($channel eq $channel2)
+ {
+ my ($duration, $subtitle, $prio, $realtitle, $duration2, $subtitle2, $prio2, $realtitle2);
+ # Values from Lower-Timer
+ $duration = $Timer{$times[$counter]}{$channel}{$title}{duration};
+ $subtitle = $Timer{$times[$counter]}{$channel}{$title}{subtitle};
+ $prio = $Timer{$times[$counter]}{$channel}{$title}{prio};
+ $realtitle = $Timer{$times[$counter]}{$channel}{$title}{real_title};
+
+ # Values from Higher-Timer
+ $duration2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{duration};
+ $subtitle2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{subtitle};
+ $prio2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{prio};
+ $realtitle2 = $Timer{$times[$counter + 1]}{$channel2}{$title2}{real_title};
+
+ # Use the Higher Priority for the new Timer
+ $prio = ($prio > $prio2) ? $prio : $prio2;
+
+ # Delete the two "Obsolet" Timers
+ &deltimer ($times[$counter], $channel, $title);
+ &deltimer ($times[$counter + 1], $channel2, $title2);
+
+ # And set the new one
+ &addtimer ("$title + $title2", "$realtitle\~$realtitle2", "$subtitle\~$subtitle2", $channel, $times[$counter], $duration2 + ($times[$counter + 1 ] - $times[$counter]),$prio,0,0,0);
+
+ # Now a Value is "missing", so we will redo the whole thing. (This will do three-times JOIN correct)
+ redo outer;
+ }
+ }
+ }
+ }
+ }
+ }
+ $counter++;
+ }
+ undef $running;
+ }
+ }
+
+sub process_torecord
+ {
+ my ($first_hit, $prio, $timer_title);
+ foreach $title (sort keys %Program)
+ {
+ foreach $channel (sort keys %{%Program->{$title}})
+ {
+ foreach $time (sort {$a <=> $b} keys %{%Program->{$title}->{$channel}})
+ {
+ undef $hit;
+
+ # First look if any of the Title/Subtitle/Description REs match
+ if ($title =~ /$title_torecord/i)
+ {
+ $hit = 1;
+ }
+ elsif ($Program{$title}{$channel}{$time}{subtitle} && $Program{$title}{$channel}{$time}{subtitle} =~ /$subtitle_torecord/i)
+ {
+ $hit = 1;
+ }
+ elsif ($Program{$title}{$channel}{$time}{description} && $Program{$title}{$channel}{$time}{description} =~ /$description_torecord/i)
+ {
+ $hit = 1;
+ }
+
+ # Now look if we have a "exact" hit
+ if ($hit)
+ {
+ my ($counter);
+ undef $hit;
+ foreach $counter (0 .. $num_torecord)
+ {
+
+ if ($title_torecord[$counter])
+ {
+ if (!($title =~ /$title_torecord[$counter]/i))
+ {
+ next;
+ }
+ }
+
+ if ($subtitle_torecord[$counter])
+ {
+ if (!($Program{$title}{$channel}{$time}{subtitle} =~ /$subtitle_torecord[$counter]/i))
+ {
+ next;
+ }
+ elsif (!$title_torecord[$counter] && !$description_torecord[$counter])
+ {
+ next;
+ }
+ }
+
+ if ($description_torecord[$counter])
+ {
+ if ($Program{$title}{$channel}{$time}{description})
+ {
+ if (!($Program{$title}{$channel}{$time}{description} =~ /$description_torecord[$counter]/i))
+ {
+ next;
+ }
+ }
+ elsif (!$title_torecord[$counter] && !$subtitle_torecord[$counter])
+ {
+ next;
+ }
+ }
+
+ if ($channel_torecord[$counter])
+ {
+ my ($hit);
+ # Blacklist-Mode
+ if ($channel_torecord[$counter][0] =~ /^!/)
+ {
+ $hit = 1;
+ foreach (0 .. $#{$channel_torecord[$counter]})
+ {
+ # Strip a possibel "!" Charactar
+ $channel_torecord[$counter][$_] =~ /^!?(.*)/;
+ if ($channel =~ /^$1$/)
+ {
+ undef $hit;
+ last;
+ }
+ }
+ }
+ # Whitelist-Mode
+ else
+ {
+ undef $hit;
+ foreach (0 .. $#{$channel_torecord[$counter]})
+ {
+ # Strip a possibel "!" Charactar
+ $channel_torecord[$counter][$_] =~ /^!?(.*)/;
+ if ($channel =~ /^$1$/)
+ {
+ $hit = 1;
+ last ;
+ }
+ }
+ }
+ if (!$hit)
+ {
+ next;
+ }
+ }
+
+ if ($timeframe_torecord[$counter])
+ {
+ my (@time, $time2, $ctime, $ctime2);
+ @time = GetTime($time);
+ $time2 = "$time[0]$time[1]";
+
+ ($ctime, $ctime2) = split (/\-/,$timeframe_torecord[$counter]);
+
+ if (!$ctime)
+ {
+ $ctime = "0";
+ }
+ if (!$ctime2)
+ {
+ $ctime2 = "2400";
+ }
+
+ if ($ctime < $ctime2)
+ {
+ if (!($time2 >= $ctime && $time2 <= $ctime2))
+ {
+ next;
+ }
+ }
+ else
+ {
+ if (!(($time2 >= $ctime && $time2 <= "2400") || ($time2 >= "0" && $time2 <= $ctime2)))
+ {
+ next;
+ }
+ }
+ }
+
+ if ($prio_torecord[$counter])
+ {
+ $prio = $prio_torecord[$counter];
+ }
+ else
+ {
+ $prio = 50;
+ }
+
+ # What Title to use for the timer
+ if ($timer_title_torecord[$counter])
+ {
+ $timer_title = $timer_title_torecord[$counter]
+ }
+ elsif ($title_torecord[$counter])
+ {
+ $timer_title = $title_torecord[$counter]
+ }
+ else
+ {
+ $timer_title = $title;
+ }
+
+ my ($subtitle);
+ if ($Program{$title}{$channel}{$time}{subtitle})
+ {
+ $subtitle = $Program{$title}{$channel}{$time}{subtitle};
+ }
+ else
+ {
+ $subtitle = "";
+ }
+
+ &addtimer ($timer_title,$title,$subtitle,$channel,$time,$Program{$title}{$channel}{$time}{duration},$prio,0,$marginstart_torecord[$counter],$marginstop_torecord[$counter]);
+ last;
+ }
+ }
+ }
+ }
+ }
+ }
+
+# Open the connection to VDR
+sub initsocket
+ {
+ my ($Dest, $Port) = split (/\:/,$Dest[$currentVDR - 1],2);
+ my $iaddr = inet_aton($Dest);
+ my $paddr = sockaddr_in($Port, $iaddr);
+
+ socket(SOCKET, PF_INET, SOCK_STREAM, getprotobyname('tcp'));
+ connect(SOCKET, $paddr) or sub_die ("Can't connect to VDR\n");
+ select(SOCKET); $| = 1;
+ select(STDOUT);
+
+ while (<SOCKET>) {
+ last if substr($_, 3, 1) ne "-";
+ }
+ }
+
+# Send a command to VDR and read back the result
+sub GetSend
+ {
+ my ($command, @retval);
+
+ while ($command = shift)
+ {
+ print SOCKET "$command\r\n";
+ while (<SOCKET>) {
+ (@retval) = (@retval, $_);
+ last if substr($_, 3, 1) ne "-";
+ }
+ }
+
+ foreach my $retval (@retval)
+ {
+ $retval =~ s/\x0d//g;
+ }
+ return (@retval);
+ }
+
+# Close the socket to VDR
+sub closesocket
+ {
+ print SOCKET "Quit\r\n";
+ close(SOCKET);
+ }
+
+
+# Fetch the timers-List from VDR via SVDR and process it.
+sub fetchVDRTimers
+ {
+ my (@timers, $timer, $position, $active, $channel, $day, $start, $end, $prio, $ttl, $title, $subtitle, $minute, $duration);
+ my ($utime, $utime2);
+
+ # First fetch the timers-list from VDR
+ @timers = GetSend ("lstt");
+
+ foreach $timer (@timers)
+ {
+# $timer =~ s/\x0d//g;
+ chomp $timer;
+ # a Valid Timer-line beginns with "250"
+ if ($timer =~ s/250-|250\s//)
+ {
+ # Extract the Position in front of the line
+ ($position, $timer) = split (/\s/,$timer,2);
+
+# print "Position: \"$position\" Timer: \"$timer\"\n";
+ # Split the : seperated values
+ ($active, $channel, $day, $start, $end, $prio, $ttl, $title, $subtitle) = split (/\:/,$timer,9);
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+
+ # If the string is exactly 7 char wide, then its a "repeating"-timer
+ if ($active >= 1)
+ {
+ if ($day =~ /(.)(.)(.)(.)(.)(.)(.)/)
+ {
+ my (@days);
+ @days = ($1, $2, $3, $4, $5, $6, $7);
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+
+ $start =~ /(\d\d)(\d\d)/;
+ $hour = $1;
+ $minute = $2;
+ $utime = timelocal 0, $minute, $hour, $mday, $mon, $year;
+ $end =~ /(\d\d)(\d\d)/;
+ $hour = $1;
+ $minute = $2;
+ $utime2 = timelocal 0, $minute, $hour, $mday, $mon, $year;
+ if ($end < $start)
+ {
+ $utime2 += 24*60*60;
+ }
+ $duration = $utime2 - $utime;
+
+ # "Normalize" the timestamp to monday
+ $utime = $utime - ($wday * 24 * 60 *60);
+
+ foreach my $num (0 .. $#days)
+ {
+ if ($days[$num] ne "-")
+ {
+ my $utime3;
+ # Todays before today will be shifted in the next week
+ if (($num + 1) < $wday)
+ {
+ $utime3 = $utime + (($num + 7 + 1) * 24 * 60 * 60);
+ }
+ else
+ {
+ $utime3 = $utime + (($num + 1) * 24 * 60 * 60);
+ }
+ &addtimer ($title,$title,$subtitle,$channels[$channel],$utime3,$duration,$prio,"R$position",0,0);
+ }
+ }
+ }
+
+ # When the Day-Value is between 1 and 31, then its a "One time" Timer
+ elsif (($day >= 1) && ($day <= 31))
+ {
+ if ($active == "2")
+ {
+ $position += 1000000;
+ }
+ # When the Day is before the Current-Day, then the Timer is for the next month
+ if ($day < $mday)
+ {
+ $mon++;
+ if ($mon == 12)
+ {
+ $mon = 0;
+ $year ++;
+ }
+ }
+ $start =~ /(\d\d)(\d\d)/;
+ $hour = $1;
+ $minute = $2;
+ $utime = timelocal 0, $minute, $hour, $day, $mon, $year;
+ $end =~ /(\d\d)(\d\d)/;
+ $hour = $1;
+ $minute = $2;
+ $utime2 = timelocal 0, $minute, $hour, $day, $mon, $year;
+ if ($end < $start)
+ {
+ $utime2 += 24*60*60;
+ }
+ $duration = $utime2 - $utime;
+
+ &addtimer ($title,$title,$subtitle,$channels[$channel],$utime,$duration,$prio,$position,0,0);
+ }
+ }
+ }
+ }
+ }
+
+# Parse file "epg.data"
+sub initepgdata
+ {
+ open (FI,"epg.data") or sub_die ("Can't open file \"epg.data\"\n");
+
+ while (<FI>)
+ {
+ # Begin Channel
+ if (/^C\s(\d+)\s+(.+)/)
+ {
+ $channel = $2;
+ while (<FI>)
+ {
+ # End Channel
+ if (/^c$/)
+ {
+ last;
+ }
+ # Begin Timer
+ elsif (/^E\s(\d+)\s+(\d+)\s+(\d+)$/)
+ {
+ # Undef this Variables because it is possibel that not every timer uses this values
+ undef $duration;
+ undef $subtitle;
+ undef $description;
+
+ $time=$2;
+ $duration=$3;
+ }
+ # Title
+ elsif (/^T\s(.*)/)
+ {
+ $title = $1;
+ }
+ # Subtitle
+ elsif (/^S\s(.*)/)
+ {
+ $subtitle=$1;
+ }
+ # Description
+ elsif (/^D\s(.*)/)
+ {
+ $description=$1;
+ }
+ # End Timer
+ elsif (/^e$/)
+ {
+ # Only accept timers that are in the future
+ if ($time < time)
+ {
+ next;
+ }
+
+ # Work around the diffrent Bugs in the data
+ &correct_epg_data();
+
+ # Check if the title is in the DEEP-Blacklist
+ if ($title =~ /$title_deepblack/i)
+ {
+ next;
+ }
+
+ # Check if the Title & Subtitle is in the Done-List
+ if ($title =~ /$title_done/)
+ {
+ if ($subtitle)
+ {
+ if ($subtitle =~ /$subtitle_done/)
+ {
+ next;
+ }
+ }
+ }
+
+ $Program{$title}{$channel}{$time}{duration}=$duration;
+ if ($subtitle)
+ {
+ $Program{$title}{$channel}{$time}{subtitle}=$subtitle;
+ }
+ if ($description)
+ {
+ $Program{$title}{$channel}{$time}{description}=$description;
+ }
+ }
+ }
+ }
+ }
+ close (FI);
+ }
+
+# What is a Movie (When correctly stored into Subtitle)
+sub initmovie
+ {
+ my (@list,$list);
+ open (FI,"$ENV{HOME}/.master-timer/subtitle-movie") or return;
+ @list = <FI>;
+ close(FI);
+
+ foreach $list (@list)
+ {
+ chomp $list;
+ }
+ $subtitle_movie = join ('|',@list);
+ }
+
+# What should be blacklistet
+sub initblacklist
+ {
+ my (@list,$list);
+ if (open (FI,"$ENV{HOME}/.master-timer/deepblack"))
+ {
+ @list = <FI>;
+ close(FI);
+
+ foreach $list (@list)
+ {
+ chomp $list;
+ }
+ $title_deepblack = join ('|',@list);
+ }
+ else
+ {
+ $title_deepblack = "^\$";
+ }
+ }
+
+# What is already recorded/Should not be recorded
+sub initdone
+ {
+ my (@list,$list, %title_done, %subtitle_done, $title_temp, $subtitle_temp);
+ if (open (FI,"$ENV{HOME}/.master-timer/done"))
+ {
+ @list = <FI>;
+ close (FI);
+
+ foreach $list (@list)
+ {
+ chomp $list;
+ ($title_temp,$subtitle_temp) = split (/\|/,$list);
+ if ($title_temp)
+ {
+ $title_done{"^$title_temp\$"} = 1;
+ }
+ if ($subtitle_temp)
+ {
+ $subtitle_done{"^$subtitle_temp\$"} = 1;
+ }
+ }
+ $title_done = join ('|',sort keys %title_done);
+ $subtitle_done = join ('|',sort keys %subtitle_done);
+
+ # Ein paar Zeichen Escapen
+ $title_done =~ s/\?/\\\?/g;
+ $title_done =~ s/\+/\\\+/g;
+ $subtitle_done =~ s/\?/\\\?/g;
+ $subtitle_done =~ s/\+/\\\+/g;
+
+ # Now delete Timers in VDR that are already in the done-List
+ my ($position, $timer, $active, $g, $title, $subtitle, $counter, @todel);
+ $counter = 0;
+ @list = GetSend ("LSTT");
+
+ foreach $timer (@list)
+ {
+# $timer =~ s/0x0d//g;
+ chomp $timer;
+ if ($timer =~ s/250-|250\s//)
+ {
+ ($position, $timer) = split (/\s/,$timer,2);
+ # Split the : seperated values
+ ($active, $g, $g, $g, $g, $g, $g, $title, $subtitle) = split (/\:/,$timer,9);
+ if ($active == 2)
+ {
+ # Title: "Shakespeare in Love"||Subtitle: "Romanze"
+ my ($ctitle, $csubtitle);
+ if ($subtitle && $subtitle =~ /^Title\:\s\"(.*)\"\|\|Subtitle\:\s\"(.*)\"/)
+ {
+ $title = $1;
+ $subtitle = $2;
+ if ($subtitle)
+ {
+ my (@titles, @subtitles, $num, $hit);
+ undef $hit;
+ @titles = split (/\~/,$title);
+ @subtitles = split (/\~/,$subtitle);
+ foreach $num (0 .. $#titles)
+ {
+ if ($titles[$num] =~ /$title_done/ && $subtitles[$num] =~ /$subtitle_done/)
+ {
+ $hit = 1;
+ }
+ else
+ {
+ undef $hit;
+ last;
+ }
+ }
+
+ if ($hit)
+ {
+ my ($result);
+ print "Delete Timer: $title $subtitle\n" if ($debug & 4);
+ $position -= $counter;
+ ($result) = GetSend ("DELT $position");
+ print "Result: $result" if ($debug & 4);
+ if ($result =~ /^250/)
+ {
+ $counter++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+# What should be recorded
+sub inittorecord
+ {
+ my (@list, $list, $title, $subtitle, $description, $channel, $timeframe, $prio, $timer_title, $margin, $machine, @title_list, @subtitle_list, @description_list);
+ my $counter = 0;
+ open (FI,"$ENV{HOME}/.master-timer/torecord") or sub_die ("Can't open file \"torecord\"\n");
+ @list = <FI>;
+ close(FI);
+
+ foreach $list (0 .. $#list)
+ {
+ chomp $list[$list];
+ if ($list[$list] && !($list[$list] =~ /^\#/))
+ {
+ ($title, $subtitle, $description, $channel, $timeframe, $prio, $timer_title, $margin, $machine) = split (/\|/,$list[$list]);
+
+ # Accept torecord only if it is for the current machine
+ if ((!$machine && $currentVDR == 1) || $machine == $currentVDR)
+ {
+ if ($title)
+ {
+ $title_torecord[$counter] = $title;
+ $title_list[$#title_list + 1] = $title;
+ }
+ if ($subtitle)
+ {
+ $subtitle_torecord[$counter] = $subtitle;
+ $subtitle_list[$#subtitle_list + 1] = $subtitle;
+ }
+ if ($description)
+ {
+ $description_torecord[$counter] = $description;
+ $description_list[$#description_list + 1] = $description;
+ }
+ if ($channel)
+ {
+ my (@temp);
+ @temp = split (/\;/,$channel);
+ foreach (0 .. $#temp)
+ {
+ $channel_torecord[$counter][$_] = $temp[$_];
+ }
+ }
+ if ($timeframe)
+ {
+ $timeframe_torecord[$counter] = $timeframe;
+ }
+ if ($prio)
+ {
+ $prio_torecord[$counter] = $prio;
+ }
+ else
+ {
+ $prio_torecord[$counter] = $default_prio;
+ }
+ if ($timer_title)
+ {
+ $timer_title_torecord[$counter] = $timer_title;
+ }
+ if ($margin)
+ {
+ my ($start, $stop);
+ ($start, $stop) = split (/;/,$margin, 2);
+ $marginstart_torecord[$counter] = $start if ($start);
+ $marginstop_torecord[$counter] = $stop if ($stop);
+ }
+ # Set Default-Margins if not margins defined
+ $marginstart_torecord[$counter] = $marginstart if (!$marginstart_torecord[$counter]);
+ $marginstop_torecord[$counter] = $marginstop if (!$marginstop_torecord[$counter]);
+ $counter++;
+ }
+ }
+ }
+
+ $num_torecord = $counter - 1;
+
+ $title_torecord = join ('|',@title_list);
+ $subtitle_torecord = join ('|',@subtitle_list);
+ $description_torecord = join ('|',@description_list);
+
+ if (!$title_torecord)
+ {
+ $title_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$";
+ }
+ if (!$subtitle_torecord)
+ {
+ $subtitle_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$";
+ }
+ if (!$description_torecord)
+ {
+ $description_torecord = "^Dieseshierwirdgarantiertnieundnimmeraufirgendetwassinnvollesmatchen\$";
+ }
+ }
+
+# Parse the "channels.conf" of VDR
+sub initchannellist
+ {
+ my ($counter, $chan, $garbage, $card, @temp_channels, $temp, $i);
+
+ @temp_channels = GetSend ("LSTC");
+
+ foreach $i (0 .. $#temp_channels)
+ {
+ $temp = $temp_channels[$i];
+# $temp =~ s/\x0d//g;
+ chomp $temp;
+
+ if ($temp =~ s/250-|250\s//)
+ {
+ ($counter, $temp) = split (/\s/,$temp,2);
+ ($chan, $garbage,$garbage, $garbage, $garbage, $garbage, $garbage, $card, $garbage) = split (/\:/,$temp);
+ $channels[$counter] = $chan;
+ $channels{$chan}{number} = $counter;
+ $channels{$chan}{card} = $card;
+ $counter++;
+ }
+ }
+ }
+
+sub initconfigfile
+ {
+ open (FI,"$ENV{HOME}/.master-timer/config") or return;
+ while (<FI>)
+ {
+ s/\#.*//;
+ chomp;
+ if ($_)
+ {
+ my ($key, $value);
+ ($key, $value) = split (/\s+=\s+/);
+ if ($key =~ /^debug$/i)
+ {
+ $debug = $value;
+ print "Debug-Level = $value\n" if ($debug & 16);
+ }
+ elsif ($key =~ /^marginstart$/i)
+ {
+ print "Marginstart = $value\n" if ($debug & 16);
+ $marginstart = $value;
+ }
+ elsif ($key =~ /^marginstop$/i)
+ {
+ print "Marginstop = $value\n" if ($debug & 16);
+ $marginstop = $value;
+ }
+ elsif ($key =~ /^DVBCards$/i)
+ {
+ print "DVB_Cards = $value\n" if ($debug & 16);
+ $DVB_cards = $value;
+ }
+ elsif ($key =~ /^defaultprio$/i)
+ {
+ print "Default Priority = $value\n" if ($debug & 16);
+ $default_prio = $value;
+ }
+ elsif ($key =~ /^Dest$/i)
+ {
+ print "Destination Host/IP:Port = $value\n" if ($debug & 16);
+ @Dest = split (/\s+/,$value);
+ }
+ elsif ($key =~ /^jointimers$/i)
+ {
+ print "Join Timers = $value\n" if ($debug & 16);
+ $jointimers = $value;
+ }
+ else
+ {
+ print "Unkown Key: \"$key\" with Value: \"$value\"\n";
+ }
+ }
+ }
+ print "End Config\n" if ($debug & 16);
+ }
+
+sub init
+ {
+ &initconfigfile();
+ &initsocket();
+ &initmovie();
+ &initblacklist();
+ &initdone();
+ &initchannellist();
+ &initepgdata();
+ &inittorecord();
+ }
diff --git a/Tools/master-timer/process_summary.pl b/Tools/master-timer/process_summary.pl
new file mode 100755
index 0000000..ebe6300
--- /dev/null
+++ b/Tools/master-timer/process_summary.pl
@@ -0,0 +1,79 @@
+#!/usr/bin/perl -w
+
+$dir = "/home/ms/.master-timer";
+
+open (FI,"$dir/done") or die "Can't open \"done\"\n";
+while (<FI>)
+ {
+ chomp;
+ if ($_)
+ {
+ ($title,$subtitle) = split (/\|/,$_,2);
+ $Done{$title}{$subtitle}=1;
+ }
+ }
+close (FI);
+
+&traverse('/video');
+
+if ($hit)
+ {
+ rename ("$dir/done","$dir/done.bak");
+ open (FO,">$dir/done");
+ foreach $title (sort keys %Done)
+ {
+ foreach $subtitle (sort keys %{%Done->{$title}})
+ {
+ print FO "$title\|$subtitle\n";
+ }
+ }
+ }
+
+sub traverse
+ {
+ local($dir) = shift;
+ local($path);
+ unless (opendir(DIR, $dir))
+ {
+ warn "Can't open $dir\n";
+ closedir(DIR);
+ return;
+ }
+ foreach (readdir(DIR))
+ {
+ next if $_ eq '.' || $_ eq '..';
+ $path = "$dir/$_";
+ if (-d $path) # a directory
+ {
+ &traverse($path);
+ }
+ if ($_ eq "summary.vdr")
+ {
+ open (FI,"$path") or die "Can't open \"$path\"\n";
+ @lines = <FI>;
+ close (FI);
+ if ($lines[0] =~ /^Title\:\s\"(.*)\"/)
+ {
+ @titles = split (/\~/,$1);
+ if ($lines[2] && $lines[2] =~ /^Subtitle\:\s\"(.*)\"/)
+ {
+ @subtitles = split (/\~/,$1);
+ foreach $num (0 .. $#titles)
+ {
+ if ($titles[$num] && $subtitles[$num])
+ {
+ if (!$Done{$titles[$num]}{$subtitles[$num]})
+ {
+ $Done{$titles[$num]}{$subtitles[$num]}=1;
+ $hit = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ closedir(DIR);
+ }
+
+
diff --git a/Tools/master-timer/sample/channels-to-scan b/Tools/master-timer/sample/channels-to-scan
new file mode 100644
index 0000000..6acf157
--- /dev/null
+++ b/Tools/master-timer/sample/channels-to-scan
@@ -0,0 +1,8 @@
+1
+2
+3
+4
+5
+13
+18
+21
diff --git a/Tools/master-timer/sample/config b/Tools/master-timer/sample/config
new file mode 100644
index 0000000..d01c8a8
--- /dev/null
+++ b/Tools/master-timer/sample/config
@@ -0,0 +1,14 @@
+# How Many Seconds "too early" should the timer begin
+marginstart = 600
+# How Many Seocnds "too long" should the timer end
+marginstop = 600
+# When the Prio isn't provied in the config-File use this value
+defaultprio = 50
+# How many DVB-Cards are installed in the Computer (Not used yet)
+DVBCards = 3
+# IP/Hostname:Port of the Destinations (Space is used for delimiter)
+Dest-Host = localhost:2001
+# Should Timers on the same channels be joined when they overlapp (0 = off)
+jointimers = 1
+# Debug-Level
+debug = 0
diff --git a/Tools/master-timer/sample/deepblack b/Tools/master-timer/sample/deepblack
new file mode 100644
index 0000000..63b4f9e
--- /dev/null
+++ b/Tools/master-timer/sample/deepblack
@@ -0,0 +1,79 @@
+Für alle Fälle Stefanie
+'MAX' - Das ganze Leben!
+10 vor 11
+17:30 live
+18:30
+24 Stunden
+Andreas Türck
+Arabella
+^BIZZ$
+Big Brother
+Britt - Der Talk um Eins
+Bärbel Schäfer
+Call TV
+Chicago Hope - Endstation Hoffnung
+Chicago Hope
+DIE REDAKTION
+Dauerwerbesendungen
+Die Harald Schmidt Show
+Die Oliver Geissen Show
+Die Quiz Show
+Doppelter Einsatz
+Dr. Stefan Frank - Der Arzt, dem die Frauen vertrauen
+EXCLUSIV
+EXTRA
+Ehekriege
+Ein Bayer auf Rügen
+Emergency Room
+Explosiv - Das Magazin
+GIRLSCAMP
+Glücksrad
+Gute Zeiten, schlechte Zeiten
+Hallo, Onkel Doc!
+Hans Meiser
+Hercules
+Hinter Gittern - Der Frauenknast
+Infomercials
+Jeder gegen Jeden
+K1 DIE REPORTAGE
+K1 Das Magazin
+K1 Nachrichten
+Kickers
+Kochduell
+Nachrichten
+Nicole - Entscheidung am Nachmittag
+OP ruft Dr. Bruckner
+PREMIERE WORLD - Das Programm
+PROSIEBEN REPORTAGE
+Peter Imhof
+Programm ab
+Programm von
+Punkt 12
+Punkt 6
+Punkt 9
+RTL II News
+RTL SHOP
+RTL aktuell
+RTL-Nachtjournal
+SAT.1-FRÜHSTÜCKSFERNSEHEN
+Spiegel TV-Reportage
+UEFA Champions
+fussball
+fßball
+Vera am Mittag
+Wolffs Revier
+Zapping
+alphateam
+peep!
+s.a.m.
+taff.
+^blitz$
+SK Kölsch
+^Becker$
+Kommissar Rex
+Fit For Fun TV
+Nur die Liebe zählt
+Unsere kleine Farm
+Die Waltons
+^Die Zwei$
+^Sieben$
diff --git a/Tools/master-timer/sample/done b/Tools/master-timer/sample/done
new file mode 100644
index 0000000..76819c7
--- /dev/null
+++ b/Tools/master-timer/sample/done
@@ -0,0 +1 @@
+Alles Routine|Komödie
diff --git a/Tools/master-timer/sample/subtitle-movie b/Tools/master-timer/sample/subtitle-movie
new file mode 100644
index 0000000..3b5a0ab
--- /dev/null
+++ b/Tools/master-timer/sample/subtitle-movie
@@ -0,0 +1,41 @@
+^Abenteuerfilm$
+^Actionfilm$
+^Actionkomödie$
+^Actionthriller$
+^Agentenfilm$
+^Biografie$
+^Biographie$
+^Computeranimation$
+^Drama$
+^Episodenfilm$
+^Erotikfilm$
+^Familiendrama$
+^Fantasy$
+^Fantasykomödie$
+^Gangsterfilm$
+^Gerichtsfilm$
+^Gesellschaftsdrama$
+^Horrorfilm$
+^Horrorkomödie$
+^Kinderfilm$
+^Komödie$
+^Kriegsfilm$
+^Krimikomödie$
+^Kriminalfilm$
+^Liebesfilm$
+^Melodram$
+^Melodrama$
+^Musical$
+^Politthriller$
+^Psychothriller$
+^Road Movie$
+^Romanze$
+^Satire$
+^Science-Fiction$
+^Spielfilm$
+^TV Movie$
+^TV-Drama$
+^Thriller$
+^Western$
+^Zeichentrick$
+^Zeichentrickkomödie$
diff --git a/Tools/master-timer/sample/torecord b/Tools/master-timer/sample/torecord
new file mode 100644
index 0000000..0306830
--- /dev/null
+++ b/Tools/master-timer/sample/torecord
@@ -0,0 +1,32 @@
+# Format: (Every field is "optional".
+# [Title RE|Subtitle RE|Description RE|Channel-Name|Timeframe|Prio|Timer-Title|Marginstart;Marginstop|VDR-Instance]
+#
+# To record something at least one of the "Title", "Subtitle" or "Description"
+# Fields has to be provided. This 3 fields are "include" and the rest are
+# "exclude" fields!
+#
+# More than one channel definition can be provided. The delimiter is ";"
+# Additionaly you can make a "blacklist" of Channels when you prepent a "!" to the first Channel Definition
+# The "!" is only tested for the FIRST Channel definition.
+# You can only have a white or a blacklist (Mixing doesn't make sense!)
+#
+# ex. Record the series "Deep Space Nine" on Sci-Fantasy in the timeframe 09:00 - 14:00 with 60 Seconds Marginstart and -60 Seconds Marginstop
+# Deep Space Nine|||Sci-Fantasy|0900-1400|99|DS9|60;-60
+#
+# Record all "Actionfilm"s with "Schwarzenegger"
+# |Actionfilm|Schwarzenegger
+#
+Babylon 5|||!Pro-7||99|60;-60|1
+Deep Space Nine|||||99|DS9|60;-60|2
+Seven Days|||||99|
+Stargate|||||99|
+Futurama||||2100-2300|50|
+Ally McBeal|||||99|
+Snoops|||||50|
+^Friends$|||||99|Friends|
+Pensacola|||||50|
+seaQuest|||||50|
+||Paltrow|Sci Fantasy;13th Street;Star Kino;Cine Action;Cine Comedy;Romantic Movies;Studio Universal;Premiere||99|
+||Aniston|||99|
+Matrix
+
diff --git a/Tools/master-timer/scan-channels b/Tools/master-timer/scan-channels
new file mode 100755
index 0000000..324181b
--- /dev/null
+++ b/Tools/master-timer/scan-channels
@@ -0,0 +1,8 @@
+#!/bin/sh
+old=`svdrpsend.pl chan | grep 250 | cut -d " " -f2`
+for dat in `cat $HOME/.master-timer/channels-to-scan`
+do
+ svdrpsend.pl "chan $dat"
+ sleep 30s
+done
+svdrpsend.pl "chan $old"
diff --git a/Tools/schnitt/cut2.pl b/Tools/schnitt/cut2.pl
new file mode 100755
index 0000000..6131e88
--- /dev/null
+++ b/Tools/schnitt/cut2.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+
+$titel = $ARGV[0];
+
+chdir ("/x2/temp");
+
+@files=<teil*.mpg>;
+$cd = 1;
+mkdir "/x2/temp/$cd";
+open (FF,">/x2/temp/$cd/$titel\ CD\ $cd");
+close (FF);
+
+foreach $file (@files)
+ {
+ $size = -s $file;
+ $total += $size;
+ if ($total <= 660*1024*1024)
+ {
+ print "Moving $file\n";
+ system ("mv /x2/temp/$file /x2/temp/$cd/$file");
+ }
+ else
+ {
+ print "Splitting $file\n";
+ $file =~ s/\.mpg$//;
+ $total -= $size;
+ $size = (660*1024*1024) - $total;
+ $cd = `cut3.pl /x2/temp $cd $file $size \'$titel\' < $file.mpg`;
+ chomp $cd;
+ $total = 0;
+ @files2=</x2/temp/$cd/teil*>;
+ foreach $file2 (@files2)
+ {
+ $total += -s $file2;
+ }
+ print "CD: $cd Total $total\n";
+ unlink "$file.mpg";
+ }
+ }
diff --git a/Tools/schnitt/cut3.pl b/Tools/schnitt/cut3.pl
new file mode 100755
index 0000000..ddaf0d6
--- /dev/null
+++ b/Tools/schnitt/cut3.pl
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+$read = $size = 1024*1024;
+
+$dir = $ARGV[0];
+$subdir = $ARGV[1];
+$teil = $ARGV[2];
+$count1 = $ARGV[3];
+$title = $ARGV[4];
+
+$filenum = "1";
+$count = 0;
+
+open (FI,">$dir/$subdir/$teil.$filenum.mpg");
+
+while ($read == $size)
+ {
+ if (($filenum == 1 && $count < $count1) || ($filenum > 1 && $count < 660*1024*1024))
+ {
+ $read = read (STDIN,$data,$size);
+ print FI $data;
+ $count += $size;
+ $a = $count /1024/1024;
+ }
+ else
+ {
+ close (FI);
+ $filenum++;
+ $subdir++;
+ mkdir ("$dir/$subdir");
+ open (FF,">$dir/$subdir/$title\ CD\ $subdir");
+ close (FF);
+ open (FI,">$dir/$subdir/$teil.$filenum.mpg");
+ $count = 0;
+ }
+ }
+
+close FI;
+
+print "$subdir\n";
diff --git a/Tools/schnitt/cutall3.pl b/Tools/schnitt/cutall3.pl
new file mode 100755
index 0000000..6961666
--- /dev/null
+++ b/Tools/schnitt/cutall3.pl
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+open (FI,"cut") or die "Kann Cut-Datei nicht oeffnen\n";
+
+outer: while (<FI>)
+ {
+ chomp;
+ if (! ($_ > 1 || $_ eq "0"))
+ {
+ open (FO,">cut2");
+ print FO "$_\n";
+ while (<FI>)
+ {
+ chomp;
+ if ($_ > 1 || $_ eq "0")
+ {
+ print FO "$_\n";
+ }
+ else
+ {
+ system ("cutt");
+ redo outer;
+ }
+ }
+ }
+ }
+if ( -f "cut2")
+ {
+ system ("cutt");
+ unlink "cut2";
+ }
diff --git a/Tools/schnitt/dump.c b/Tools/schnitt/dump.c
new file mode 100644
index 0000000..a362f68
--- /dev/null
+++ b/Tools/schnitt/dump.c
@@ -0,0 +1,65 @@
+#include "libmpeg3.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ mpeg3_t *file;
+ int x,y,ii,i,j,result,out;
+ int howmany;
+ unsigned char *output, **output_rows;
+ char filename[100];
+ char header[100];
+ char temp;
+
+ howmany = atoi (argv[2]);
+
+ if ((file = mpeg3_open(argv[1])) == NULL)
+ {
+ printf ("Open failed\n");
+ return 1;
+ }
+ mpeg3_set_cpus(file,1);
+ mpeg3_set_mmx(file,0);
+ if (mpeg3_has_video == 0)
+ {
+ printf ("Stream has no Video\n");
+ return 1;
+ }
+ x = mpeg3_video_width(file,0);
+ y = mpeg3_video_height(file, 0);
+ output = malloc (x*y*3 + 4);
+ output_rows = malloc (sizeof(unsigned char*) * y);
+ for(i = 0; i < y; i++)
+ output_rows[i] = &output[i * x * 3];
+
+ for (ii = 0; ii < howmany; ii++)
+ {
+ result = mpeg3_read_frame(file,output_rows,0,0,x,y,x,y,0,0);
+
+ sprintf (filename,"/x2/temp/output%03i.ppm",ii);
+ sprintf (header,"P6\n%i %i\n255\n\r",x,y);
+
+/* printf ("Opening %s\n",filename); */
+
+ if ((out = open (filename,O_CREAT|O_WRONLY|O_TRUNC,0755)) == -1)
+ {
+ printf ("Can't open %s\n",filename);
+ return 1;
+ }
+
+ write (out,header,strlen(header));
+
+ for (i = 0; i < y; i++)
+ for (j = 0; j < x; j++)
+ {
+ temp = output [(i*x+j)*3 + 1];
+ output[(i*x+j)*3 + 1] = output [(i*x+j)*3 + 0];
+ output[(i*x+j)*3 + 0] = temp;
+ }
+ write (out, output, x*y*3);
+ close (out);
+ }
+}
diff --git a/Tools/schnitt/play b/Tools/schnitt/play
new file mode 100755
index 0000000..0c43bff
--- /dev/null
+++ b/Tools/schnitt/play
@@ -0,0 +1,10 @@
+#!/bin/sh
+if [ "$1" != "" ]; then
+ vmount "$1"
+fi
+ssh dvb2 /usr/local/bin/my/novdr
+sleep 1s
+cat /mnt/*/* | buffer -b 1000 -S 1024 | ssh dvb2 play2
+ssh dvb2 rm /tmp/novdr
+umount /mnt/*
+
diff --git a/ac3dec/Makefile b/ac3dec/Makefile
new file mode 100644
index 0000000..a0547e9
--- /dev/null
+++ b/ac3dec/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for 'ac3dec'
+#
+# $Id: Makefile 1.1 2001/08/03 12:58:06 kls Exp $
+
+OBJS = coeff.o decode.o exponent.o rematrix.o bit_allocate.o crc.o dither.o\
+ imdct.o sanity_check.o bitstream.o debug.o downmix.o parse.o stats.o
+
+DEFINES += -DDOLBY_SURROUND
+
+all: libac3.a
+
+libac3.a: $(OBJS)
+ ar -rc libac3.a $(OBJS)
+
+# Implicit rules:
+
+%.o: %.c
+ gcc -g -O2 -Wall -m486 -c $(DEFINES) $<
+
+clean:
+ rm -f *~ libac3.a $(OBJS)
diff --git a/ac3dec/ac3.h b/ac3dec/ac3.h
new file mode 100644
index 0000000..4919fc5
--- /dev/null
+++ b/ac3dec/ac3.h
@@ -0,0 +1,60 @@
+/*
+ * ac3.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#ifndef AARONS_TYPES
+#define AARONS_TYPES
+typedef unsigned long long uint_64;
+typedef unsigned int uint_32;
+typedef unsigned short uint_16;
+typedef unsigned char uint_8;
+
+typedef signed long long sint_64;
+typedef signed int sint_32;
+typedef signed short sint_16;
+typedef signed char sint_8;
+#endif
+
+#define AC3_DOLBY_SURR_ENABLE 0x1
+#define AC3_3DNOW_ENABLE 0x2
+#define AC3_MMX_ENABLE 0x4
+#define AC3_ALTIVEC_ENABLE 0x8
+
+typedef struct ac3_config_s
+{
+ //Bit flags that enable various things
+ uint_32 flags;
+ //Callback that points the decoder to new stream data
+ void (*fill_buffer_callback)(uint_8 **, uint_8 **);
+ //Number of discrete channels in final output (for downmixing)
+ uint_16 num_output_ch;
+ //Which channel of a dual mono stream to select
+ uint_16 dual_mono_ch_sel;
+} ac3_config_t;
+
+void ac3_init(ac3_config_t *);
+uint_32 ac3_decode_data(uint_8 *data_start,uint_8 *data_end, int ac3reset, int *input_pointer, int *output_pointer, char *ac3_data);
+
+
+
+
diff --git a/ac3dec/ac3_internal.h b/ac3dec/ac3_internal.h
new file mode 100644
index 0000000..235189e
--- /dev/null
+++ b/ac3dec/ac3_internal.h
@@ -0,0 +1,344 @@
+/*
+ * ac3_internal.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __GNUC__
+#define inline
+#endif
+
+/* Exponent strategy constants */
+#define EXP_REUSE (0)
+#define EXP_D15 (1)
+#define EXP_D25 (2)
+#define EXP_D45 (3)
+
+/* Delta bit allocation constants */
+#define DELTA_BIT_REUSE (0)
+#define DELTA_BIT_NEW (1)
+#define DELTA_BIT_NONE (2)
+#define DELTA_BIT_RESERVED (3)
+
+/* samples work structure */
+typedef float stream_samples_t[6][256];
+
+/* global config structure */
+extern ac3_config_t ac3_config;
+/* global error flag */
+extern uint_32 error_flag;
+
+/* Everything you wanted to know about band structure */
+/*
+ * The entire frequency domain is represented by 256 real
+ * floating point fourier coefficients. Only the lower 253
+ * coefficients are actually utilized however. We use arrays
+ * of 256 to be efficient in some cases.
+ *
+ * The 5 full bandwidth channels (fbw) can have their higher
+ * frequencies coupled together. These coupled channels then
+ * share their high frequency components.
+ *
+ * This coupling band is broken up into 18 sub-bands starting
+ * at mantissa number 37. Each sub-band is 12 bins wide.
+ *
+ * There are 50 bit allocation sub-bands which cover the entire
+ * frequency range. The sub-bands are of non-uniform width, and
+ * approximate a 1/6 octave scale.
+ */
+
+/* The following structures are filled in by their corresponding parse_*
+ * functions. See http://www.atsc.org/Standards/A52/a_52.pdf for
+ * full details on each field. Indented fields are used to denote
+ * conditional fields.
+ */
+
+typedef struct syncinfo_s
+{
+ uint_32 magic;
+ /* Sync word == 0x0B77 */
+ uint_16 syncword;
+ /* crc for the first 5/8 of the sync block */
+ /* uint_16 crc1; */
+ /* Stream Sampling Rate (kHz) 0 = 48, 1 = 44.1, 2 = 32, 3 = reserved */
+ uint_16 fscod;
+ /* Frame size code */
+ uint_16 frmsizecod;
+
+ /* Information not in the AC-3 bitstream, but derived */
+ /* Frame size in 16 bit words */
+ uint_16 frame_size;
+ /* Bit rate in kilobits */
+ uint_16 bit_rate;
+ /* sampling rate in hertz */
+ uint_32 sampling_rate;
+
+} syncinfo_t;
+
+typedef struct bsi_s
+{
+ uint_32 magic;
+ /* Bit stream identification == 0x8 */
+ uint_16 bsid;
+ /* Bit stream mode */
+ uint_16 bsmod;
+ /* Audio coding mode */
+ uint_16 acmod;
+ /* If we're using the centre channel then */
+ /* centre mix level */
+ uint_16 cmixlev;
+ /* If we're using the surround channel then */
+ /* surround mix level */
+ uint_16 surmixlev;
+ /* If we're in 2/0 mode then */
+ /* Dolby surround mix level - NOT USED - */
+ uint_16 dsurmod;
+ /* Low frequency effects on */
+ uint_16 lfeon;
+ /* Dialogue Normalization level */
+ uint_16 dialnorm;
+ /* Compression exists */
+ uint_16 compre;
+ /* Compression level */
+ uint_16 compr;
+ /* Language code exists */
+ uint_16 langcode;
+ /* Language code */
+ uint_16 langcod;
+ /* Audio production info exists*/
+ uint_16 audprodie;
+ uint_16 mixlevel;
+ uint_16 roomtyp;
+ /* If we're in dual mono mode (acmod == 0) then extra stuff */
+ uint_16 dialnorm2;
+ uint_16 compr2e;
+ uint_16 compr2;
+ uint_16 langcod2e;
+ uint_16 langcod2;
+ uint_16 audprodi2e;
+ uint_16 mixlevel2;
+ uint_16 roomtyp2;
+ /* Copyright bit */
+ uint_16 copyrightb;
+ /* Original bit */
+ uint_16 origbs;
+ /* Timecode 1 exists */
+ uint_16 timecod1e;
+ /* Timecode 1 */
+ uint_16 timecod1;
+ /* Timecode 2 exists */
+ uint_16 timecod2e;
+ /* Timecode 2 */
+ uint_16 timecod2;
+ /* Additional bit stream info exists */
+ uint_16 addbsie;
+ /* Additional bit stream length - 1 (in bytes) */
+ uint_16 addbsil;
+ /* Additional bit stream information (max 64 bytes) */
+ uint_8 addbsi[64];
+
+ /* Information not in the AC-3 bitstream, but derived */
+ /* Number of channels (excluding LFE)
+ * Derived from acmod */
+ uint_16 nfchans;
+} bsi_t;
+
+
+/* more pain */
+typedef struct audblk_s
+{
+ uint_32 magic1;
+ /* block switch bit indexed by channel num */
+ uint_16 blksw[5];
+ /* dither enable bit indexed by channel num */
+ uint_16 dithflag[5];
+ /* dynamic range gain exists */
+ uint_16 dynrnge;
+ /* dynamic range gain */
+ uint_16 dynrng;
+ /* if acmod==0 then */
+ /* dynamic range 2 gain exists */
+ uint_16 dynrng2e;
+ /* dynamic range 2 gain */
+ uint_16 dynrng2;
+ /* coupling strategy exists */
+ uint_16 cplstre;
+ /* coupling in use */
+ uint_16 cplinu;
+ /* channel coupled */
+ uint_16 chincpl[5];
+ /* if acmod==2 then */
+ /* Phase flags in use */
+ uint_16 phsflginu;
+ /* coupling begin frequency code */
+ uint_16 cplbegf;
+ /* coupling end frequency code */
+ uint_16 cplendf;
+ /* coupling band structure bits */
+ uint_16 cplbndstrc[18];
+ /* Do coupling co-ords exist for this channel? */
+ uint_16 cplcoe[5];
+ /* Master coupling co-ordinate */
+ uint_16 mstrcplco[5];
+ /* Per coupling band coupling co-ordinates */
+ uint_16 cplcoexp[5][18];
+ uint_16 cplcomant[5][18];
+ /* Phase flags for dual mono */
+ uint_16 phsflg[18];
+ /* Is there a rematrixing strategy */
+ uint_16 rematstr;
+ /* Rematrixing bits */
+ uint_16 rematflg[4];
+ /* Coupling exponent strategy */
+ uint_16 cplexpstr;
+ /* Exponent strategy for full bandwidth channels */
+ uint_16 chexpstr[5];
+ /* Exponent strategy for lfe channel */
+ uint_16 lfeexpstr;
+ /* Channel bandwidth for independent channels */
+ uint_16 chbwcod[5];
+ /* The absolute coupling exponent */
+ uint_16 cplabsexp;
+ /* Coupling channel exponents (D15 mode gives 18 * 12 /3 encoded exponents */
+ uint_16 cplexps[18 * 12 / 3];
+ /* Sanity checking constant */
+ uint_32 magic2;
+ /* fbw channel exponents */
+ uint_16 exps[5][252 / 3];
+ /* channel gain range */
+ uint_16 gainrng[5];
+ /* low frequency exponents */
+ uint_16 lfeexps[3];
+
+ /* Bit allocation info */
+ uint_16 baie;
+ /* Slow decay code */
+ uint_16 sdcycod;
+ /* Fast decay code */
+ uint_16 fdcycod;
+ /* Slow gain code */
+ uint_16 sgaincod;
+ /* dB per bit code */
+ uint_16 dbpbcod;
+ /* masking floor code */
+ uint_16 floorcod;
+
+ /* SNR offset info */
+ uint_16 snroffste;
+ /* coarse SNR offset */
+ uint_16 csnroffst;
+ /* coupling fine SNR offset */
+ uint_16 cplfsnroffst;
+ /* coupling fast gain code */
+ uint_16 cplfgaincod;
+ /* fbw fine SNR offset */
+ uint_16 fsnroffst[5];
+ /* fbw fast gain code */
+ uint_16 fgaincod[5];
+ /* lfe fine SNR offset */
+ uint_16 lfefsnroffst;
+ /* lfe fast gain code */
+ uint_16 lfefgaincod;
+
+ /* Coupling leak info */
+ uint_16 cplleake;
+ /* coupling fast leak initialization */
+ uint_16 cplfleak;
+ /* coupling slow leak initialization */
+ uint_16 cplsleak;
+
+ /* delta bit allocation info */
+ uint_16 deltbaie;
+ /* coupling delta bit allocation exists */
+ uint_16 cpldeltbae;
+ /* fbw delta bit allocation exists */
+ uint_16 deltbae[5];
+ /* number of cpl delta bit segments */
+ uint_16 cpldeltnseg;
+ /* coupling delta bit allocation offset */
+ uint_16 cpldeltoffst[8];
+ /* coupling delta bit allocation length */
+ uint_16 cpldeltlen[8];
+ /* coupling delta bit allocation length */
+ uint_16 cpldeltba[8];
+ /* number of delta bit segments */
+ uint_16 deltnseg[5];
+ /* fbw delta bit allocation offset */
+ uint_16 deltoffst[5][8];
+ /* fbw delta bit allocation length */
+ uint_16 deltlen[5][8];
+ /* fbw delta bit allocation length */
+ uint_16 deltba[5][8];
+
+ /* skip length exists */
+ uint_16 skiple;
+ /* skip length */
+ uint_16 skipl;
+
+ //Removed Feb 2000 -ah
+ /* channel mantissas */
+ //uint_16 chmant[5][256];
+
+ /* coupling mantissas */
+ uint_16 cplmant[256];
+
+ //Removed Feb 2000 -ah
+ /* coupling mantissas */
+ //uint_16 lfemant[7];
+
+
+ /* -- Information not in the bitstream, but derived thereof -- */
+
+ /* Number of coupling sub-bands */
+ uint_16 ncplsubnd;
+
+ /* Number of combined coupling sub-bands
+ * Derived from ncplsubnd and cplbndstrc */
+ uint_16 ncplbnd;
+
+ /* Number of exponent groups by channel
+ * Derived from strmant, endmant */
+ uint_16 nchgrps[5];
+
+ /* Number of coupling exponent groups
+ * Derived from cplbegf, cplendf, cplexpstr */
+ uint_16 ncplgrps;
+
+ /* End mantissa numbers of fbw channels */
+ uint_16 endmant[5];
+
+ /* Start and end mantissa numbers for the coupling channel */
+ uint_16 cplstrtmant;
+ uint_16 cplendmant;
+
+ /* Decoded exponent info */
+ uint_16 fbw_exp[5][256];
+ uint_16 cpl_exp[256];
+ uint_16 lfe_exp[7];
+
+ /* Bit allocation pointer results */
+ uint_16 fbw_bap[5][256];
+ uint_16 cpl_bap[256];
+ uint_16 lfe_bap[7];
+
+ uint_32 magic3;
+} audblk_t;
+
+
diff --git a/ac3dec/bit_allocate.c b/ac3dec/bit_allocate.c
new file mode 100644
index 0000000..053e09c
--- /dev/null
+++ b/ac3dec/bit_allocate.c
@@ -0,0 +1,494 @@
+/*
+ * bit_allocate.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+
+static inline sint_16 logadd(sint_16 a,sint_16 b);
+static sint_16 calc_lowcomp(sint_16 a,sint_16 b0,sint_16 b1,sint_16 bin);
+static inline uint_16 min(sint_16 a,sint_16 b);
+static inline uint_16 max(sint_16 a,sint_16 b);
+static void ba_compute_psd(sint_16 start, sint_16 end, sint_16 exps[],
+ sint_16 psd[], sint_16 bndpsd[]);
+
+static void ba_compute_excitation(sint_16 start, sint_16 end,sint_16 fgain,
+ sint_16 fastleak, sint_16 slowleak, sint_16 is_lfe, sint_16 bndpsd[],
+ sint_16 excite[]);
+static void ba_compute_mask(sint_16 start, sint_16 end, uint_16 fscod,
+ uint_16 deltbae, uint_16 deltnseg, uint_16 deltoffst[], uint_16 deltba[],
+ uint_16 deltlen[], sint_16 excite[], sint_16 mask[]);
+static void ba_compute_bap(sint_16 start, sint_16 end, sint_16 snroffset,
+ sint_16 psd[], sint_16 mask[], sint_16 bap[]);
+
+/* Misc LUTs for bit allocation process */
+
+static sint_16 slowdec[] = { 0x0f, 0x11, 0x13, 0x15 };
+static sint_16 fastdec[] = { 0x3f, 0x53, 0x67, 0x7b };
+static sint_16 slowgain[] = { 0x540, 0x4d8, 0x478, 0x410 };
+static sint_16 dbpbtab[] = { 0x000, 0x700, 0x900, 0xb00 };
+
+static uint_16 floortab[] = { 0x2f0, 0x2b0, 0x270, 0x230, 0x1f0, 0x170, 0x0f0, 0xf800 };
+static sint_16 fastgain[] = { 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, 0x400 };
+
+
+static sint_16 bndtab[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 31,
+ 34, 37, 40, 43, 46, 49, 55, 61, 67, 73,
+ 79, 85, 97, 109, 121, 133, 157, 181, 205, 229 };
+
+static sint_16 bndsz[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 3,
+ 3, 3, 3, 3, 3, 6, 6, 6, 6, 6,
+ 6, 12, 12, 12, 12, 24, 24, 24, 24, 24 };
+
+static sint_16 masktab[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 28, 28, 29,
+ 29, 29, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34,
+ 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 37, 37, 37,
+ 37, 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 40,
+ 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
+ 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 0, 0, 0 };
+
+
+static sint_16 latab[] = { 0x0040, 0x003f, 0x003e, 0x003d, 0x003c, 0x003b, 0x003a, 0x0039,
+ 0x0038, 0x0037, 0x0036, 0x0035, 0x0034, 0x0034, 0x0033, 0x0032,
+ 0x0031, 0x0030, 0x002f, 0x002f, 0x002e, 0x002d, 0x002c, 0x002c,
+ 0x002b, 0x002a, 0x0029, 0x0029, 0x0028, 0x0027, 0x0026, 0x0026,
+ 0x0025, 0x0024, 0x0024, 0x0023, 0x0023, 0x0022, 0x0021, 0x0021,
+ 0x0020, 0x0020, 0x001f, 0x001e, 0x001e, 0x001d, 0x001d, 0x001c,
+ 0x001c, 0x001b, 0x001b, 0x001a, 0x001a, 0x0019, 0x0019, 0x0018,
+ 0x0018, 0x0017, 0x0017, 0x0016, 0x0016, 0x0015, 0x0015, 0x0015,
+ 0x0014, 0x0014, 0x0013, 0x0013, 0x0013, 0x0012, 0x0012, 0x0012,
+ 0x0011, 0x0011, 0x0011, 0x0010, 0x0010, 0x0010, 0x000f, 0x000f,
+ 0x000f, 0x000e, 0x000e, 0x000e, 0x000d, 0x000d, 0x000d, 0x000d,
+ 0x000c, 0x000c, 0x000c, 0x000c, 0x000b, 0x000b, 0x000b, 0x000b,
+ 0x000a, 0x000a, 0x000a, 0x000a, 0x000a, 0x0009, 0x0009, 0x0009,
+ 0x0009, 0x0009, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008,
+ 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0006, 0x0006,
+ 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0005, 0x0005,
+ 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0005, 0x0004, 0x0004,
+ 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004, 0x0004,
+ 0x0004, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003,
+ 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0002,
+ 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002,
+ 0x0002, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+ 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000};
+
+static sint_16 hth[][50] = {{ 0x04d0, 0x04d0, 0x0440, 0x0400, 0x03e0, 0x03c0, 0x03b0, 0x03b0,
+ 0x03a0, 0x03a0, 0x03a0, 0x03a0, 0x03a0, 0x0390, 0x0390, 0x0390,
+ 0x0380, 0x0380, 0x0370, 0x0370, 0x0360, 0x0360, 0x0350, 0x0350,
+ 0x0340, 0x0340, 0x0330, 0x0320, 0x0310, 0x0300, 0x02f0, 0x02f0,
+ 0x02f0, 0x02f0, 0x0300, 0x0310, 0x0340, 0x0390, 0x03e0, 0x0420,
+ 0x0460, 0x0490, 0x04a0, 0x0460, 0x0440, 0x0440, 0x0520, 0x0800,
+ 0x0840, 0x0840 },
+
+ { 0x04f0, 0x04f0, 0x0460, 0x0410, 0x03e0, 0x03d0, 0x03c0, 0x03b0,
+ 0x03b0, 0x03a0, 0x03a0, 0x03a0, 0x03a0, 0x03a0, 0x0390, 0x0390,
+ 0x0390, 0x0380, 0x0380, 0x0380, 0x0370, 0x0370, 0x0360, 0x0360,
+ 0x0350, 0x0350, 0x0340, 0x0340, 0x0320, 0x0310, 0x0300, 0x02f0,
+ 0x02f0, 0x02f0, 0x02f0, 0x0300, 0x0320, 0x0350, 0x0390, 0x03e0,
+ 0x0420, 0x0450, 0x04a0, 0x0490, 0x0460, 0x0440, 0x0480, 0x0630,
+ 0x0840, 0x0840 },
+
+ { 0x0580, 0x0580, 0x04b0, 0x0450, 0x0420, 0x03f0, 0x03e0, 0x03d0,
+ 0x03c0, 0x03b0, 0x03b0, 0x03b0, 0x03a0, 0x03a0, 0x03a0, 0x03a0,
+ 0x03a0, 0x03a0, 0x03a0, 0x03a0, 0x0390, 0x0390, 0x0390, 0x0390,
+ 0x0380, 0x0380, 0x0380, 0x0370, 0x0360, 0x0350, 0x0340, 0x0330,
+ 0x0320, 0x0310, 0x0300, 0x02f0, 0x02f0, 0x02f0, 0x0300, 0x0310,
+ 0x0330, 0x0350, 0x03c0, 0x0410, 0x0470, 0x04a0, 0x0460, 0x0440,
+ 0x0450, 0x04e0 }};
+
+
+static sint_16 baptab[] = { 0, 1, 1, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6,
+ 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10,
+ 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14,
+ 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15 };
+
+static sint_16 sdecay;
+static sint_16 fdecay;
+static sint_16 sgain;
+static sint_16 dbknee;
+static sint_16 floor;
+static sint_16 psd[256];
+static sint_16 bndpsd[256];
+static sint_16 excite[256];
+static sint_16 mask[256];
+
+static inline uint_16
+max(sint_16 a,sint_16 b)
+{
+ return (a > b ? a : b);
+}
+
+static inline uint_16
+min(sint_16 a,sint_16 b)
+{
+ return (a < b ? a : b);
+}
+
+static inline sint_16
+logadd(sint_16 a,sint_16 b)
+{
+ sint_16 c;
+ sint_16 address;
+
+ c = a - b;
+ address = min((abs(c) >> 1), 255);
+
+ if (c >= 0)
+ return(a + latab[address]);
+ else
+ return(b + latab[address]);
+}
+
+
+void bit_allocate(uint_16 fscod, bsi_t *bsi, audblk_t *audblk)
+{
+ uint_16 i;
+ sint_16 fgain;
+ sint_16 snroffset;
+ sint_16 start;
+ sint_16 end;
+ sint_16 fastleak;
+ sint_16 slowleak;
+
+ /* Only perform bit_allocation if the exponents have changed or we
+ * have new sideband information */
+ if (audblk->chexpstr[0] == 0 && audblk->chexpstr[1] == 0 &&
+ audblk->chexpstr[2] == 0 && audblk->chexpstr[3] == 0 &&
+ audblk->chexpstr[4] == 0 && audblk->cplexpstr == 0 &&
+ audblk->lfeexpstr == 0 && audblk->baie == 0 &&
+ audblk->snroffste == 0 && audblk->deltbaie == 0)
+ return;
+
+ /* Do some setup before we do the bit alloc */
+ sdecay = slowdec[audblk->sdcycod];
+ fdecay = fastdec[audblk->fdcycod];
+ sgain = slowgain[audblk->sgaincod];
+ dbknee = dbpbtab[audblk->dbpbcod];
+ floor = floortab[audblk->floorcod];
+
+ /* if all the SNR offset constants are zero then the whole block is zero */
+ if(!audblk->csnroffst && !audblk->fsnroffst[0] &&
+ !audblk->fsnroffst[1] && !audblk->fsnroffst[2] &&
+ !audblk->fsnroffst[3] && !audblk->fsnroffst[4] &&
+ !audblk->cplfsnroffst && !audblk->lfefsnroffst)
+ {
+ memset(audblk->fbw_bap,0,sizeof(uint_16) * 256 * 5);
+ memset(audblk->cpl_bap,0,sizeof(uint_16) * 256);
+ memset(audblk->lfe_bap,0,sizeof(uint_16) * 7);
+ return;
+ }
+
+
+ for(i = 0; i < bsi->nfchans; i++)
+ {
+ start = 0;
+ end = audblk->endmant[i] ;
+ fgain = fastgain[audblk->fgaincod[i]];
+ snroffset = (((audblk->csnroffst - 15) << 4) + audblk->fsnroffst[i]) << 2 ;
+ fastleak = 0;
+ slowleak = 0;
+
+ ba_compute_psd(start, end, audblk->fbw_exp[i], psd, bndpsd);
+
+ ba_compute_excitation(start, end , fgain, fastleak, slowleak, 0, bndpsd, excite);
+
+ ba_compute_mask(start, end, fscod, audblk->deltbae[i], audblk->deltnseg[i],
+ audblk->deltoffst[i], audblk->deltba[i], audblk->deltlen[i], excite, mask);
+
+ ba_compute_bap(start, end, snroffset, psd, mask, audblk->fbw_bap[i]);
+ }
+
+ if(audblk->cplinu)
+ {
+ start = audblk->cplstrtmant;
+ end = audblk->cplendmant;
+ fgain = fastgain[audblk->cplfgaincod];
+ snroffset = (((audblk->csnroffst - 15) << 4) + audblk->cplfsnroffst) << 2 ;
+ fastleak = (audblk->cplfleak << 8) + 768;
+ slowleak = (audblk->cplsleak << 8) + 768;
+
+ ba_compute_psd(start, end, audblk->cpl_exp, psd, bndpsd);
+
+ ba_compute_excitation(start, end , fgain, fastleak, slowleak, 0, bndpsd, excite);
+
+ ba_compute_mask(start, end, fscod, audblk->cpldeltbae, audblk->cpldeltnseg,
+ audblk->cpldeltoffst, audblk->cpldeltba, audblk->cpldeltlen, excite, mask);
+
+ ba_compute_bap(start, end, snroffset, psd, mask, audblk->cpl_bap);
+ }
+
+ if(bsi->lfeon)
+ {
+ start = 0;
+ end = 7;
+ fgain = fastgain[audblk->lfefgaincod];
+ snroffset = (((audblk->csnroffst - 15) << 4) + audblk->lfefsnroffst) << 2 ;
+ fastleak = 0;
+ slowleak = 0;
+
+ ba_compute_psd(start, end, audblk->lfe_exp, psd, bndpsd);
+
+ ba_compute_excitation(start, end , fgain, fastleak, slowleak, 1, bndpsd, excite);
+
+ /* Perform no delta bit allocation for lfe */
+ ba_compute_mask(start, end, fscod, 2, 0, 0, 0, 0, excite, mask);
+
+ ba_compute_bap(start, end, snroffset, psd, mask, audblk->lfe_bap);
+ }
+}
+
+
+static void ba_compute_psd(sint_16 start, sint_16 end, sint_16 exps[],
+ sint_16 psd[], sint_16 bndpsd[])
+{
+ int bin,i,j,k;
+ sint_16 lastbin = 0;
+
+ /* Map the exponents into dBs */
+ for (bin=start; bin<end; bin++)
+ {
+ psd[bin] = (3072 - (exps[bin] << 7));
+ }
+
+ /* Integrate the psd function over each bit allocation band */
+ j = start;
+ k = masktab[start];
+
+ do
+ {
+ lastbin = min(bndtab[k] + bndsz[k], end);
+ bndpsd[k] = psd[j];
+ j++;
+
+ for (i = j; i < lastbin; i++)
+ {
+ bndpsd[k] = logadd(bndpsd[k], psd[j]);
+ j++;
+ }
+
+ k++;
+ } while (end > lastbin);
+}
+
+static void ba_compute_excitation(sint_16 start, sint_16 end,sint_16 fgain,
+ sint_16 fastleak, sint_16 slowleak, sint_16 is_lfe, sint_16 bndpsd[],
+ sint_16 excite[])
+{
+ int bin;
+ sint_16 bndstrt;
+ sint_16 bndend;
+ sint_16 lowcomp = 0;
+ sint_16 begin = 0;
+
+ /* Compute excitation function */
+ bndstrt = masktab[start];
+ bndend = masktab[end - 1] + 1;
+
+ if (bndstrt == 0) /* For fbw and lfe channels */
+ {
+ lowcomp = calc_lowcomp(lowcomp, bndpsd[0], bndpsd[1], 0);
+ excite[0] = bndpsd[0] - fgain - lowcomp;
+ lowcomp = calc_lowcomp(lowcomp, bndpsd[1], bndpsd[2], 1);
+ excite[1] = bndpsd[1] - fgain - lowcomp;
+ begin = 7 ;
+
+ /* Note: Do not call calc_lowcomp() for the last band of the lfe channel, (bin = 6) */
+ for (bin = 2; bin < 7; bin++)
+ {
+ if (!(is_lfe && (bin == 6)))
+ lowcomp = calc_lowcomp(lowcomp, bndpsd[bin], bndpsd[bin+1], bin);
+ fastleak = bndpsd[bin] - fgain;
+ slowleak = bndpsd[bin] - sgain;
+ excite[bin] = fastleak - lowcomp;
+
+ if (!(is_lfe && (bin == 6)))
+ {
+ if (bndpsd[bin] <= bndpsd[bin+1])
+ {
+ begin = bin + 1 ;
+ break;
+ }
+ }
+ }
+
+ for (bin = begin; bin < min(bndend, 22); bin++)
+ {
+ if (!(is_lfe && (bin == 6)))
+ lowcomp = calc_lowcomp(lowcomp, bndpsd[bin], bndpsd[bin+1], bin);
+ fastleak -= fdecay ;
+ fastleak = max(fastleak, bndpsd[bin] - fgain);
+ slowleak -= sdecay ;
+ slowleak = max(slowleak, bndpsd[bin] - sgain);
+ excite[bin] = max(fastleak - lowcomp, slowleak);
+ }
+ begin = 22;
+ }
+ else /* For coupling channel */
+ {
+ begin = bndstrt;
+ }
+
+ for (bin = begin; bin < bndend; bin++)
+ {
+ fastleak -= fdecay;
+ fastleak = max(fastleak, bndpsd[bin] - fgain);
+ slowleak -= sdecay;
+ slowleak = max(slowleak, bndpsd[bin] - sgain);
+ excite[bin] = max(fastleak, slowleak) ;
+ }
+}
+
+static void ba_compute_mask(sint_16 start, sint_16 end, uint_16 fscod,
+ uint_16 deltbae, uint_16 deltnseg, uint_16 deltoffst[], uint_16 deltba[],
+ uint_16 deltlen[], sint_16 excite[], sint_16 mask[])
+{
+ int bin,k;
+ sint_16 bndstrt;
+ sint_16 bndend;
+ sint_16 delta;
+
+ bndstrt = masktab[start];
+ bndend = masktab[end - 1] + 1;
+
+ /* Compute the masking curve */
+
+ for (bin = bndstrt; bin < bndend; bin++)
+ {
+ if (bndpsd[bin] < dbknee)
+ {
+ excite[bin] += ((dbknee - bndpsd[bin]) >> 2);
+ }
+ mask[bin] = max(excite[bin], hth[fscod][bin]);
+ }
+
+ /* Perform delta bit modulation if necessary */
+ if ((deltbae == DELTA_BIT_REUSE) || (deltbae == DELTA_BIT_NEW))
+ {
+ sint_16 band = 0;
+ sint_16 seg = 0;
+
+ for (seg = 0; seg < deltnseg+1; seg++)
+ {
+ band += deltoffst[seg];
+ if (deltba[seg] >= 4)
+ {
+ delta = (deltba[seg] - 3) << 7;
+ }
+ else
+ {
+ delta = (deltba[seg] - 4) << 7;
+ }
+
+ for (k = 0; k < deltlen[seg]; k++)
+ {
+ mask[band] += delta;
+ band++;
+ }
+ }
+ }
+}
+
+static void ba_compute_bap(sint_16 start, sint_16 end, sint_16 snroffset,
+ sint_16 psd[], sint_16 mask[], sint_16 bap[])
+{
+ int i,j,k;
+ sint_16 lastbin = 0;
+ sint_16 address = 0;
+
+ /* Compute the bit allocation pointer for each bin */
+ i = start;
+ j = masktab[start];
+
+ do
+ {
+ lastbin = min(bndtab[j] + bndsz[j], end);
+ mask[j] -= snroffset;
+ mask[j] -= floor;
+
+ if (mask[j] < 0)
+ mask[j] = 0;
+
+ mask[j] &= 0x1fe0;
+ mask[j] += floor;
+ for (k = i; k < lastbin; k++)
+ {
+ address = (psd[i] - mask[j]) >> 5;
+ address = min(63, max(0, address));
+ bap[i] = baptab[address];
+ i++;
+ }
+ j++;
+ } while (end > lastbin);
+}
+
+static sint_16
+calc_lowcomp(sint_16 a,sint_16 b0,sint_16 b1,sint_16 bin)
+{
+
+ if (bin < 7)
+ {
+ if ((b0 + 256) == b1)
+ a = 384;
+ else if (b0 > b1)
+ a = max(0, a - 64);
+ }
+ else if (bin < 20)
+ {
+ if ((b0 + 256) == b1)
+ a = 320;
+ else if (b0 > b1)
+ a = max(0, a - 64) ;
+ }
+ else
+ a = max(0, a - 128);
+
+ return(a);
+}
+
diff --git a/ac3dec/bit_allocate.h b/ac3dec/bit_allocate.h
new file mode 100644
index 0000000..e48b0b2
--- /dev/null
+++ b/ac3dec/bit_allocate.h
@@ -0,0 +1,24 @@
+/*
+ * bit_allocate.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+void bit_allocate(uint_16 fscod, bsi_t *bsi, audblk_t *audblk);
diff --git a/ac3dec/bitstream.c b/ac3dec/bitstream.c
new file mode 100644
index 0000000..296d5ee
--- /dev/null
+++ b/ac3dec/bitstream.c
@@ -0,0 +1,76 @@
+/*
+ * bitstream.c
+ *
+ * Copyright (C) Aaron Holtzman - Dec 1999
+ *
+ * This file is part of ac3dec, a free AC-3 audio decoder
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "ac3.h"
+#include "ac3_internal.h"
+#include "bitstream.h"
+
+
+uint_8 *buffer_start = 0;
+uint_32 bits_left = 0;
+uint_32 current_word;
+
+static inline void
+bitstream_fill_current()
+{
+ current_word = *((uint_32*)buffer_start)++;
+ current_word = swab32(current_word);
+}
+
+//
+// The fast paths for _get is in the
+// bitstream.h header file so it can be inlined.
+//
+// The "bottom half" of this routine is suffixed _bh
+//
+// -ah
+//
+
+uint_32
+bitstream_get_bh(uint_32 num_bits)
+{
+ uint_32 result;
+
+ num_bits -= bits_left;
+ result = (current_word << (32 - bits_left)) >> (32 - bits_left);
+
+ bitstream_fill_current();
+
+ if(num_bits != 0)
+ result = (result << num_bits) | (current_word >> (32 - num_bits));
+
+ bits_left = 32 - num_bits;
+
+ return result;
+}
+
+void
+bitstream_init(uint_8 *start)
+{
+ //initialize the start of the buffer
+ buffer_start = start;
+ bits_left = 0;
+}
diff --git a/ac3dec/bitstream.h b/ac3dec/bitstream.h
new file mode 100644
index 0000000..3351930
--- /dev/null
+++ b/ac3dec/bitstream.h
@@ -0,0 +1,76 @@
+/*
+ * bitstream.h
+ *
+ * Copyright (C) Aaron Holtzman - Dec 1999
+ *
+ * This file is part of ac3dec, a free AC-3 audio decoder
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+//My new and improved vego-matic endian swapping routine
+//(stolen from the kernel)
+#ifdef WORDS_BIGENDIAN
+
+# define swab32(x) (x)
+
+#else
+
+# if defined (__i386__)
+
+# define swab32(x) __i386_swab32(x)
+ static inline const uint_32 __i386_swab32(uint_32 x)
+ {
+ __asm__("bswap %0" : "=r" (x) : "0" (x));
+ return x;
+ }
+
+# else
+
+# define swab32(x)\
+((((uint_8*)&x)[0] << 24) | (((uint_8*)&x)[1] << 16) | \
+ (((uint_8*)&x)[2] << 8) | (((uint_8*)&x)[3]))
+
+# endif
+#endif
+
+extern uint_32 bits_left;
+extern uint_32 current_word;
+
+void bitstream_init(uint_8 *start);
+
+uint_8 bitstream_get_byte(void);
+
+uint_8 *bitstream_get_buffer_start(void);
+void bitstream_buffer_frame(uint_32 frame_size);
+
+uint_32 bitstream_get_bh(uint_32 num_bits);
+
+static inline uint_32
+bitstream_get(uint_32 num_bits)
+{
+ uint_32 result;
+
+ if(num_bits < bits_left)
+ {
+ result = (current_word << (32 - bits_left)) >> (32 - num_bits);
+ bits_left -= num_bits;
+ return result;
+ }
+
+ return bitstream_get_bh(num_bits);
+}
+
diff --git a/ac3dec/coeff.c b/ac3dec/coeff.c
new file mode 100644
index 0000000..b9f03ff
--- /dev/null
+++ b/ac3dec/coeff.c
@@ -0,0 +1,353 @@
+/*
+ * coeff.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+#include "decode.h"
+#include "bitstream.h"
+#include "dither.h"
+#include "coeff.h"
+
+//
+//Lookup tables of 0.15 two's complement quantization values
+//
+static const uint_16 q_1[3] =
+{
+ ( -2 << 15)/3, 0,( 2 << 15)/3
+};
+
+static const uint_16 q_2[5] =
+{
+ ( -4 << 15)/5,( -2 << 15)/5, 0,
+ ( 2 << 15)/5,( 4 << 15)/5
+};
+
+static const uint_16 q_3[7] =
+{
+ ( -6 << 15)/7,( -4 << 15)/7,( -2 << 15)/7, 0,
+ ( 2 << 15)/7,( 4 << 15)/7,( 6 << 15)/7
+};
+
+static const uint_16 q_4[11] =
+{
+ (-10 << 15)/11,(-8 << 15)/11,(-6 << 15)/11, ( -4 << 15)/11,(-2 << 15)/11, 0,
+ ( 2 << 15)/11,( 4 << 15)/11,( 6 << 15)/11, ( 8 << 15)/11,(10 << 15)/11
+};
+
+static const uint_16 q_5[15] =
+{
+ (-14 << 15)/15,(-12 << 15)/15,(-10 << 15)/15,
+ ( -8 << 15)/15,( -6 << 15)/15,( -4 << 15)/15,
+ ( -2 << 15)/15, 0 ,( 2 << 15)/15,
+ ( 4 << 15)/15,( 6 << 15)/15,( 8 << 15)/15,
+ ( 10 << 15)/15,( 12 << 15)/15,( 14 << 15)/15
+};
+
+//
+// Scale factors for convert_to_float
+//
+
+static const uint_32 u32_scale_factors[25] =
+{
+ 0x38000000, //2 ^ -(0 + 15)
+ 0x37800000, //2 ^ -(1 + 15)
+ 0x37000000, //2 ^ -(2 + 15)
+ 0x36800000, //2 ^ -(3 + 15)
+ 0x36000000, //2 ^ -(4 + 15)
+ 0x35800000, //2 ^ -(5 + 15)
+ 0x35000000, //2 ^ -(6 + 15)
+ 0x34800000, //2 ^ -(7 + 15)
+ 0x34000000, //2 ^ -(8 + 15)
+ 0x33800000, //2 ^ -(9 + 15)
+ 0x33000000, //2 ^ -(10 + 15)
+ 0x32800000, //2 ^ -(11 + 15)
+ 0x32000000, //2 ^ -(12 + 15)
+ 0x31800000, //2 ^ -(13 + 15)
+ 0x31000000, //2 ^ -(14 + 15)
+ 0x30800000, //2 ^ -(15 + 15)
+ 0x30000000, //2 ^ -(16 + 15)
+ 0x2f800000, //2 ^ -(17 + 15)
+ 0x2f000000, //2 ^ -(18 + 15)
+ 0x2e800000, //2 ^ -(19 + 15)
+ 0x2e000000, //2 ^ -(20 + 15)
+ 0x2d800000, //2 ^ -(21 + 15)
+ 0x2d000000, //2 ^ -(22 + 15)
+ 0x2c800000, //2 ^ -(23 + 15)
+ 0x2c000000 //2 ^ -(24 + 15)
+};
+
+static float *scale_factor = (float*)u32_scale_factors;
+
+//These store the persistent state of the packed mantissas
+static uint_16 m_1[3];
+static uint_16 m_2[3];
+static uint_16 m_4[2];
+static uint_16 m_1_pointer;
+static uint_16 m_2_pointer;
+static uint_16 m_4_pointer;
+
+//Conversion from bap to number of bits in the mantissas
+//zeros account for cases 0,1,2,4 which are special cased
+static uint_16 qnttztab[16] = { 0, 0, 0, 3, 0 , 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16};
+
+static void coeff_reset(void);
+static sint_16 coeff_get_mantissa(uint_16 bap, uint_16 dithflag);
+static void coeff_uncouple_ch(float samples[],bsi_t *bsi,audblk_t *audblk,uint_32 ch);
+
+//
+// Convert a 0.15 fixed point number into IEEE single
+// precision floating point and scale by 2^-exp
+//
+static inline float
+convert_to_float(uint_16 exp, sint_16 mantissa)
+{
+ float x;
+
+ //the scale by 2^-15 is built into the scale factor table
+ x = mantissa * scale_factor[exp];
+
+ return x;
+}
+
+void
+coeff_unpack(bsi_t *bsi, audblk_t *audblk, stream_samples_t samples)
+{
+ uint_16 i,j;
+ uint_32 done_cpl = 0;
+ sint_16 mantissa;
+
+ coeff_reset();
+
+ for(i=0; i< bsi->nfchans; i++)
+ {
+ for(j=0; j < audblk->endmant[i]; j++)
+ {
+ mantissa = coeff_get_mantissa(audblk->fbw_bap[i][j],audblk->dithflag[i]);
+ samples[i][j] = convert_to_float(audblk->fbw_exp[i][j],mantissa);
+ }
+
+ if(audblk->cplinu && audblk->chincpl[i] && !(done_cpl))
+ {
+ // ncplmant is equal to 12 * ncplsubnd
+ // Don't dither coupling channel until channel separation so that
+ // interchannel noise is uncorrelated
+ for(j=audblk->cplstrtmant; j < audblk->cplendmant; j++)
+ audblk->cplmant[j] = coeff_get_mantissa(audblk->cpl_bap[j],0);
+ done_cpl = 1;
+ }
+ }
+
+ //uncouple the channel if necessary
+ if(audblk->cplinu)
+ {
+ for(i=0; i< bsi->nfchans; i++)
+ {
+ if(audblk->chincpl[i])
+ coeff_uncouple_ch(samples[i],bsi,audblk,i);
+ }
+
+ }
+
+ if(bsi->lfeon)
+ {
+ // There are always 7 mantissas for lfe, no dither for lfe
+ for(j=0; j < 7 ; j++)
+ {
+ mantissa = coeff_get_mantissa(audblk->lfe_bap[j],0);
+ samples[5][j] = convert_to_float(audblk->lfe_exp[j],mantissa);
+ }
+ }
+}
+
+//
+//Fetch a mantissa from the bitstream
+//
+//The mantissa returned is a signed 0.15 fixed point number
+//
+static sint_16
+coeff_get_mantissa(uint_16 bap, uint_16 dithflag)
+{
+ uint_16 mantissa;
+ uint_16 group_code;
+
+ //If the bap is 0-5 then we have special cases to take care of
+ switch(bap)
+ {
+ case 0:
+ if(dithflag)
+ mantissa = dither_gen();
+ else
+ mantissa = 0;
+ break;
+
+ case 1:
+ if(m_1_pointer > 2)
+ {
+ group_code = bitstream_get(5);
+
+ if(group_code > 26)
+ goto error;
+
+ m_1[0] = group_code / 9;
+ m_1[1] = (group_code % 9) / 3;
+ m_1[2] = (group_code % 9) % 3;
+ m_1_pointer = 0;
+ }
+ mantissa = m_1[m_1_pointer++];
+ mantissa = q_1[mantissa];
+ break;
+ case 2:
+
+ if(m_2_pointer > 2)
+ {
+ group_code = bitstream_get(7);
+
+ if(group_code > 124)
+ goto error;
+
+ m_2[0] = group_code / 25;
+ m_2[1] = (group_code % 25) / 5 ;
+ m_2[2] = (group_code % 25) % 5 ;
+ m_2_pointer = 0;
+ }
+ mantissa = m_2[m_2_pointer++];
+ mantissa = q_2[mantissa];
+ break;
+
+ case 3:
+ mantissa = bitstream_get(3);
+
+ if(mantissa > 6)
+ goto error;
+
+ mantissa = q_3[mantissa];
+ break;
+
+ case 4:
+ if(m_4_pointer > 1)
+ {
+ group_code = bitstream_get(7);
+
+ if(group_code > 120)
+ goto error;
+
+ m_4[0] = group_code / 11;
+ m_4[1] = group_code % 11;
+ m_4_pointer = 0;
+ }
+ mantissa = m_4[m_4_pointer++];
+ mantissa = q_4[mantissa];
+ break;
+
+ case 5:
+ mantissa = bitstream_get(4);
+
+ if(mantissa > 14)
+ goto error;
+
+ mantissa = q_5[mantissa];
+ break;
+
+ default:
+ mantissa = bitstream_get(qnttztab[bap]);
+ mantissa <<= 16 - qnttztab[bap];
+ }
+
+ return mantissa;
+
+
+
+error:
+ if(!error_flag)
+ fprintf(stderr,"** Invalid mantissa - skipping frame **\n");
+ error_flag = 1;
+
+ return 0;
+}
+
+//
+// Reset the mantissa state
+//
+static void
+coeff_reset(void)
+{
+ m_1[2] = m_1[1] = m_1[0] = 0;
+ m_2[2] = m_2[1] = m_2[0] = 0;
+ m_4[1] = m_4[0] = 0;
+ m_1_pointer = m_2_pointer = m_4_pointer = 3;
+}
+
+//
+// Uncouple the coupling channel into a fbw channel
+//
+static void
+coeff_uncouple_ch(float samples[],bsi_t *bsi,audblk_t *audblk,uint_32 ch)
+{
+ uint_32 bnd = 0;
+ uint_32 sub_bnd = 0;
+ uint_32 i,j;
+ float cpl_coord = 1.0;
+ uint_32 cpl_exp_tmp;
+ uint_32 cpl_mant_tmp;
+ sint_16 mantissa;
+
+
+ for(i=audblk->cplstrtmant;i<audblk->cplendmant;)
+ {
+ if(!audblk->cplbndstrc[sub_bnd++])
+ {
+ cpl_exp_tmp = audblk->cplcoexp[ch][bnd] + 3 * audblk->mstrcplco[ch];
+ if(audblk->cplcoexp[ch][bnd] == 15)
+ cpl_mant_tmp = (audblk->cplcomant[ch][bnd]) << 11;
+ else
+ cpl_mant_tmp = ((0x10) | audblk->cplcomant[ch][bnd]) << 10;
+
+ cpl_coord = convert_to_float(cpl_exp_tmp,cpl_mant_tmp) * 8.0f;
+
+ //Invert the phase for the right channel if necessary
+ if(bsi->acmod == 0x2 && audblk->phsflginu && ch == 1 && audblk->phsflg[bnd])
+ cpl_coord *= -1;
+
+ bnd++;
+ }
+
+ for(j=0;j < 12; j++)
+ {
+ //Get new dither values for each channel if necessary, so
+ //the channels are uncorrelated
+ if(audblk->dithflag[ch] && audblk->cpl_bap[i] == 0)
+ mantissa = dither_gen();
+ else
+ mantissa = audblk->cplmant[i];
+
+ samples[i] = cpl_coord * convert_to_float(audblk->cpl_exp[i],mantissa);
+
+ i++;
+ }
+ }
+}
diff --git a/ac3dec/coeff.h b/ac3dec/coeff.h
new file mode 100644
index 0000000..dc822a9
--- /dev/null
+++ b/ac3dec/coeff.h
@@ -0,0 +1,24 @@
+/*
+ * coeff.h
+ *
+ * Copyright (C) Aaron Holtzman - Feb 2000
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+void coeff_unpack(bsi_t *bsi, audblk_t *audblk,stream_samples_t samples);
diff --git a/ac3dec/crc.c b/ac3dec/crc.c
new file mode 100644
index 0000000..3210ce7
--- /dev/null
+++ b/ac3dec/crc.c
@@ -0,0 +1,96 @@
+/*
+ * crc.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+#include <sys/time.h>
+
+#include "crc.h"
+
+static const uint_16 crc_lut[256] =
+{
+ 0x0000,0x8005,0x800f,0x000a,0x801b,0x001e,0x0014,0x8011,
+ 0x8033,0x0036,0x003c,0x8039,0x0028,0x802d,0x8027,0x0022,
+ 0x8063,0x0066,0x006c,0x8069,0x0078,0x807d,0x8077,0x0072,
+ 0x0050,0x8055,0x805f,0x005a,0x804b,0x004e,0x0044,0x8041,
+ 0x80c3,0x00c6,0x00cc,0x80c9,0x00d8,0x80dd,0x80d7,0x00d2,
+ 0x00f0,0x80f5,0x80ff,0x00fa,0x80eb,0x00ee,0x00e4,0x80e1,
+ 0x00a0,0x80a5,0x80af,0x00aa,0x80bb,0x00be,0x00b4,0x80b1,
+ 0x8093,0x0096,0x009c,0x8099,0x0088,0x808d,0x8087,0x0082,
+ 0x8183,0x0186,0x018c,0x8189,0x0198,0x819d,0x8197,0x0192,
+ 0x01b0,0x81b5,0x81bf,0x01ba,0x81ab,0x01ae,0x01a4,0x81a1,
+ 0x01e0,0x81e5,0x81ef,0x01ea,0x81fb,0x01fe,0x01f4,0x81f1,
+ 0x81d3,0x01d6,0x01dc,0x81d9,0x01c8,0x81cd,0x81c7,0x01c2,
+ 0x0140,0x8145,0x814f,0x014a,0x815b,0x015e,0x0154,0x8151,
+ 0x8173,0x0176,0x017c,0x8179,0x0168,0x816d,0x8167,0x0162,
+ 0x8123,0x0126,0x012c,0x8129,0x0138,0x813d,0x8137,0x0132,
+ 0x0110,0x8115,0x811f,0x011a,0x810b,0x010e,0x0104,0x8101,
+ 0x8303,0x0306,0x030c,0x8309,0x0318,0x831d,0x8317,0x0312,
+ 0x0330,0x8335,0x833f,0x033a,0x832b,0x032e,0x0324,0x8321,
+ 0x0360,0x8365,0x836f,0x036a,0x837b,0x037e,0x0374,0x8371,
+ 0x8353,0x0356,0x035c,0x8359,0x0348,0x834d,0x8347,0x0342,
+ 0x03c0,0x83c5,0x83cf,0x03ca,0x83db,0x03de,0x03d4,0x83d1,
+ 0x83f3,0x03f6,0x03fc,0x83f9,0x03e8,0x83ed,0x83e7,0x03e2,
+ 0x83a3,0x03a6,0x03ac,0x83a9,0x03b8,0x83bd,0x83b7,0x03b2,
+ 0x0390,0x8395,0x839f,0x039a,0x838b,0x038e,0x0384,0x8381,
+ 0x0280,0x8285,0x828f,0x028a,0x829b,0x029e,0x0294,0x8291,
+ 0x82b3,0x02b6,0x02bc,0x82b9,0x02a8,0x82ad,0x82a7,0x02a2,
+ 0x82e3,0x02e6,0x02ec,0x82e9,0x02f8,0x82fd,0x82f7,0x02f2,
+ 0x02d0,0x82d5,0x82df,0x02da,0x82cb,0x02ce,0x02c4,0x82c1,
+ 0x8243,0x0246,0x024c,0x8249,0x0258,0x825d,0x8257,0x0252,
+ 0x0270,0x8275,0x827f,0x027a,0x826b,0x026e,0x0264,0x8261,
+ 0x0220,0x8225,0x822f,0x022a,0x823b,0x023e,0x0234,0x8231,
+ 0x8213,0x0216,0x021c,0x8219,0x0208,0x820d,0x8207,0x0202
+};
+
+static uint_16 state;
+
+void
+crc_init(void)
+{
+ state = 0;
+}
+
+
+inline void crc_process_byte(uint_8 data)
+{
+ state = crc_lut[data ^ (state>>8)] ^ (state<<8);
+}
+
+void
+crc_process_frame(uint_8 *data,uint_32 num_bytes)
+{
+ uint_32 i;
+
+ for(i=0;i<num_bytes;i++)
+ crc_process_byte(data[i]);
+}
+
+int
+crc_validate(void)
+{
+ return(state == 0);
+}
diff --git a/ac3dec/crc.h b/ac3dec/crc.h
new file mode 100644
index 0000000..07d57b1
--- /dev/null
+++ b/ac3dec/crc.h
@@ -0,0 +1,27 @@
+/*
+ * crc.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+int crc_validate(void);
+void crc_init(void);
+void crc_process_byte(uint_8 data);
+void crc_process_frame(uint_8 *data,uint_32 num_bytes);
diff --git a/ac3dec/debug.c b/ac3dec/debug.c
new file mode 100644
index 0000000..b7d6a3b
--- /dev/null
+++ b/ac3dec/debug.c
@@ -0,0 +1,58 @@
+/*
+ *
+ * debug.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include "debug.h"
+
+static int debug_level = -1;
+
+// Determine is debug output is required.
+// We could potentially have multiple levels of debug info
+int debug_is_on(void)
+{
+ char *env_var;
+
+ if(debug_level < 0)
+ {
+ env_var = getenv("AC3_DEBUG");
+
+ if (env_var)
+ {
+ debug_level = 1;
+ }
+ else
+ debug_level = 0;
+ }
+
+ return debug_level;
+}
+
+//If you don't have gcc, then ya don't get debug output
+#ifndef __GNUC__
+void dprintf(char fmt[],...)
+{
+ int foo = 0;
+}
+#endif
+
diff --git a/ac3dec/debug.h b/ac3dec/debug.h
new file mode 100644
index 0000000..f45cb5b
--- /dev/null
+++ b/ac3dec/debug.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * debug.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+int debug_is_on(void);
+
+#ifdef __GNUC__
+#define dprintf(format,args...)\
+{\
+ if (debug_is_on())\
+ {\
+ fprintf(stderr,format,## args);\
+ }\
+}
+#else
+void dprintf(char fmt[],...);
+#endif
diff --git a/ac3dec/decode.c b/ac3dec/decode.c
new file mode 100644
index 0000000..5e7f034
--- /dev/null
+++ b/ac3dec/decode.c
@@ -0,0 +1,269 @@
+/*
+ * decode.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * Added support for DVB-s PCI card by:
+ * Matjaz Thaler <matjaz.thaler@rd.iskraemeco.si> - November 2000
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "ac3.h"
+#include "ac3_internal.h"
+#include "bitstream.h"
+#include "imdct.h"
+#include "exponent.h"
+#include "coeff.h"
+#include "bit_allocate.h"
+#include "parse.h"
+#include "crc.h"
+#include "stats.h"
+#include "rematrix.h"
+#include "sanity_check.h"
+#include "downmix.h"
+#include "debug.h"
+
+#define AC3_BUFFER_SIZE (6*1024*16)
+
+//our global config structure
+ac3_config_t ac3_config;
+uint_32 error_flag = 0;
+
+static audblk_t audblk;
+static bsi_t bsi;
+static syncinfo_t syncinfo;
+static uint_32 frame_count = 0;
+//static uint_32 is_output_initialized = 0;
+
+//the floating point samples for one audblk
+static stream_samples_t samples;
+
+//the integer samples for the entire frame (with enough space for 2 ch out)
+//if this size change, be sure to change the size when muting
+static sint_16 s16_samples[2 * 6 * 256];
+
+
+//Storage for the syncframe
+#define SYNC_BUFFER_MAX_SIZE 4096
+static uint_8 sync_buffer[SYNC_BUFFER_MAX_SIZE];
+static uint_32 sync_buffer_size = 0;;
+
+uint_32
+decode_sync_buffer_syncframe(syncinfo_t *syncinfo, uint_8 **start,uint_8 *end)
+{
+ uint_8 *cur = *start;
+ uint_16 syncword = syncinfo->syncword;
+ uint_32 ret = 0;
+
+ //
+ // Find an ac3 sync frame.
+ //
+resync:
+
+ while(syncword != 0x0b77)
+ {
+ if(cur >= end)
+ goto done;
+ syncword = (syncword << 8) + *cur++;
+ }
+
+ //need the next 3 bytes to decide how big the frame is
+ while(sync_buffer_size < 3)
+ {
+ if(cur >= end)
+ goto done;
+
+ sync_buffer[sync_buffer_size++] = *cur++;
+ }
+
+ parse_syncinfo(syncinfo,sync_buffer);
+ stats_print_syncinfo(syncinfo);
+
+ while(sync_buffer_size < syncinfo->frame_size * 2 - 2)
+ {
+ if(cur >= end)
+ goto done;
+
+ sync_buffer[sync_buffer_size++] = *cur++;
+ }
+
+ // Check the crc over the entire frame
+ crc_init();
+ crc_process_frame(sync_buffer,syncinfo->frame_size * 2 - 2);
+
+ if(!crc_validate())
+ {
+ fprintf(stderr,"** CRC failed - skipping frame **\n");
+ syncword = 0xffff;
+ sync_buffer_size = 0;
+ goto resync;
+ }
+
+ //
+ //if we got to this point, we found a valid ac3 frame to decode
+ //
+
+ bitstream_init(sync_buffer);
+ //get rid of the syncinfo struct as we already parsed it
+ bitstream_get(24);
+
+ //reset the syncword for next time
+ syncword = 0xffff;
+ sync_buffer_size = 0;
+ ret = 1;
+
+done:
+ syncinfo->syncword = syncword;
+ *start = cur;
+ return ret;
+}
+
+void
+decode_mute(void)
+{
+ fprintf(stderr,"muting frame\n");
+ //mute the frame
+ memset(s16_samples,0,sizeof(sint_16) * 256 * 2 * 6);
+ error_flag = 0;
+}
+
+
+void
+ac3_init(ac3_config_t *config)
+{
+ memcpy(&ac3_config,config,sizeof(ac3_config_t));
+
+ imdct_init();
+ sanity_check_init(&syncinfo,&bsi,&audblk);
+
+ // ac3_output = *foo;
+}
+
+uint_32 ac3_decode_data(uint_8 *data_start,uint_8 *data_end, int ac3reset, int *input_pointer, int *output_pointer, char *ac3_data)
+{
+ uint_32 i;
+ int datasize;
+ char *data;
+
+ if(ac3reset != 0){
+ syncinfo.syncword = 0xffff;
+ sync_buffer_size = 0;
+ }
+
+ while(decode_sync_buffer_syncframe(&syncinfo,&data_start,data_end))
+ {
+ dprintf("(decode) begin frame %d\n",frame_count++);
+
+ if(error_flag)
+ {
+ decode_mute();
+ continue;
+ }
+
+ parse_bsi(&bsi);
+
+ for(i=0; i < 6; i++)
+ {
+ //Initialize freq/time sample storage
+ memset(samples,0,sizeof(float) * 256 * (bsi.nfchans + bsi.lfeon));
+
+ // Extract most of the audblk info from the bitstream
+ // (minus the mantissas
+ parse_audblk(&bsi,&audblk);
+
+ // Take the differential exponent data and turn it into
+ // absolute exponents
+ exponent_unpack(&bsi,&audblk);
+ if(error_flag)
+ goto error;
+
+ // Figure out how many bits per mantissa
+ bit_allocate(syncinfo.fscod,&bsi,&audblk);
+
+ // Extract the mantissas from the stream and
+ // generate floating point frequency coefficients
+ coeff_unpack(&bsi,&audblk,samples);
+ if(error_flag)
+ goto error;
+
+ if(bsi.acmod == 0x2)
+ rematrix(&audblk,samples);
+
+ // Convert the frequency samples into time samples
+ imdct(&bsi,&audblk,samples);
+
+ // Downmix into the requested number of channels
+ // and convert floating point to sint_16
+ downmix(&bsi,samples,&s16_samples[i * 2 * 256]);
+
+ sanity_check(&syncinfo,&bsi,&audblk);
+ if(error_flag)
+ goto error;
+
+ continue;
+ }
+
+ parse_auxdata(&syncinfo);
+
+ /*
+ if(!is_output_initialized)
+ {
+ ac3_output.open(16,syncinfo.sampling_rate,2);
+ is_output_initialized = 1;
+ }
+ */
+ data = (char *)s16_samples;
+ datasize = 0;
+ while(datasize < 6144){
+ if(((*input_pointer+1) % AC3_BUFFER_SIZE) != *output_pointer){ // There is room in the sync_buffer
+ ac3_data[*input_pointer]=data[datasize];
+ datasize++;
+ *input_pointer = (*input_pointer+1) % AC3_BUFFER_SIZE;
+ }
+ else{
+ *input_pointer = *output_pointer = 0;
+ break;
+ }
+ }
+
+
+ //write(1, s16_samples, 256 * 6 * 2* 2);
+ //ac3_output.play(s16_samples, 256 * 6 * 2);
+error:
+ ;
+ //find a new frame
+ }
+
+ return 0;
+}
diff --git a/ac3dec/decode.h b/ac3dec/decode.h
new file mode 100644
index 0000000..bb84a11
--- /dev/null
+++ b/ac3dec/decode.h
@@ -0,0 +1,22 @@
+/*
+ * decode.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
diff --git a/ac3dec/dither.c b/ac3dec/dither.c
new file mode 100644
index 0000000..31e74f6
--- /dev/null
+++ b/ac3dec/dither.c
@@ -0,0 +1,115 @@
+/*
+ * dither.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+#include "dither.h"
+
+
+const uint_16 dither_lut[256] =
+{
+ 0x0000, 0xa011, 0xe033, 0x4022, 0x6077, 0xc066, 0x8044, 0x2055,
+ 0xc0ee, 0x60ff, 0x20dd, 0x80cc, 0xa099, 0x0088, 0x40aa, 0xe0bb,
+ 0x21cd, 0x81dc, 0xc1fe, 0x61ef, 0x41ba, 0xe1ab, 0xa189, 0x0198,
+ 0xe123, 0x4132, 0x0110, 0xa101, 0x8154, 0x2145, 0x6167, 0xc176,
+ 0x439a, 0xe38b, 0xa3a9, 0x03b8, 0x23ed, 0x83fc, 0xc3de, 0x63cf,
+ 0x8374, 0x2365, 0x6347, 0xc356, 0xe303, 0x4312, 0x0330, 0xa321,
+ 0x6257, 0xc246, 0x8264, 0x2275, 0x0220, 0xa231, 0xe213, 0x4202,
+ 0xa2b9, 0x02a8, 0x428a, 0xe29b, 0xc2ce, 0x62df, 0x22fd, 0x82ec,
+ 0x8734, 0x2725, 0x6707, 0xc716, 0xe743, 0x4752, 0x0770, 0xa761,
+ 0x47da, 0xe7cb, 0xa7e9, 0x07f8, 0x27ad, 0x87bc, 0xc79e, 0x678f,
+ 0xa6f9, 0x06e8, 0x46ca, 0xe6db, 0xc68e, 0x669f, 0x26bd, 0x86ac,
+ 0x6617, 0xc606, 0x8624, 0x2635, 0x0660, 0xa671, 0xe653, 0x4642,
+ 0xc4ae, 0x64bf, 0x249d, 0x848c, 0xa4d9, 0x04c8, 0x44ea, 0xe4fb,
+ 0x0440, 0xa451, 0xe473, 0x4462, 0x6437, 0xc426, 0x8404, 0x2415,
+ 0xe563, 0x4572, 0x0550, 0xa541, 0x8514, 0x2505, 0x6527, 0xc536,
+ 0x258d, 0x859c, 0xc5be, 0x65af, 0x45fa, 0xe5eb, 0xa5c9, 0x05d8,
+ 0xae79, 0x0e68, 0x4e4a, 0xee5b, 0xce0e, 0x6e1f, 0x2e3d, 0x8e2c,
+ 0x6e97, 0xce86, 0x8ea4, 0x2eb5, 0x0ee0, 0xaef1, 0xeed3, 0x4ec2,
+ 0x8fb4, 0x2fa5, 0x6f87, 0xcf96, 0xefc3, 0x4fd2, 0x0ff0, 0xafe1,
+ 0x4f5a, 0xef4b, 0xaf69, 0x0f78, 0x2f2d, 0x8f3c, 0xcf1e, 0x6f0f,
+ 0xede3, 0x4df2, 0x0dd0, 0xadc1, 0x8d94, 0x2d85, 0x6da7, 0xcdb6,
+ 0x2d0d, 0x8d1c, 0xcd3e, 0x6d2f, 0x4d7a, 0xed6b, 0xad49, 0x0d58,
+ 0xcc2e, 0x6c3f, 0x2c1d, 0x8c0c, 0xac59, 0x0c48, 0x4c6a, 0xec7b,
+ 0x0cc0, 0xacd1, 0xecf3, 0x4ce2, 0x6cb7, 0xcca6, 0x8c84, 0x2c95,
+ 0x294d, 0x895c, 0xc97e, 0x696f, 0x493a, 0xe92b, 0xa909, 0x0918,
+ 0xe9a3, 0x49b2, 0x0990, 0xa981, 0x89d4, 0x29c5, 0x69e7, 0xc9f6,
+ 0x0880, 0xa891, 0xe8b3, 0x48a2, 0x68f7, 0xc8e6, 0x88c4, 0x28d5,
+ 0xc86e, 0x687f, 0x285d, 0x884c, 0xa819, 0x0808, 0x482a, 0xe83b,
+ 0x6ad7, 0xcac6, 0x8ae4, 0x2af5, 0x0aa0, 0xaab1, 0xea93, 0x4a82,
+ 0xaa39, 0x0a28, 0x4a0a, 0xea1b, 0xca4e, 0x6a5f, 0x2a7d, 0x8a6c,
+ 0x4b1a, 0xeb0b, 0xab29, 0x0b38, 0x2b6d, 0x8b7c, 0xcb5e, 0x6b4f,
+ 0x8bf4, 0x2be5, 0x6bc7, 0xcbd6, 0xeb83, 0x4b92, 0x0bb0, 0xaba1
+};
+
+uint_16 lfsr_state = 1;
+
+//
+// see dither_gen (inline-able) in dither.h
+//
+
+#if 0
+
+//
+// this is the old dither_gen with is much slower than the new inlined
+// lut version and is still here because it's easier to understand.
+//
+
+/*
+ * Generate eight bits of pseudo-entropy using a 16 bit linear
+ * feedback shift register (LFSR). The primitive polynomial used
+ * is 1 + x^4 + x^14 + x^16.
+ *
+ * The distribution is uniform, over the range [-0.707,0.707]
+ *
+ */
+
+uint_16 dither_gen(void)
+{
+ int i;
+ uint_32 state;
+
+ //explicitly bring the state into a local var as gcc > 3.0?
+ //doesn't know how to optimize out the stores
+ state = lfsr_state;
+
+ //Generate eight pseudo random bits
+ for(i=0;i<8;i++)
+ {
+ state <<= 1;
+
+ if(state & 0x10000)
+ state ^= 0xa011;
+ }
+
+ lfsr_state = state;
+
+ return (((((sint_32)state<<8)>>8) * (sint_32) (0.707106 * 256.0))>>16);
+}
+
+#endif
diff --git a/ac3dec/dither.h b/ac3dec/dither.h
new file mode 100644
index 0000000..6d68e1b
--- /dev/null
+++ b/ac3dec/dither.h
@@ -0,0 +1,37 @@
+/*
+ * dither.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+extern uint_16 lfsr_state;
+extern const uint_16 dither_lut[256];
+
+static inline uint_16 dither_gen(void)
+{
+ sint_16 state;
+
+ state = dither_lut[lfsr_state >> 8] ^ (lfsr_state << 8);
+
+ lfsr_state = (uint_16) state;
+
+ return ((state * (sint_32) (0.707106 * 256.0))>>8);
+}
diff --git a/ac3dec/downmix.c b/ac3dec/downmix.c
new file mode 100644
index 0000000..94bc51a
--- /dev/null
+++ b/ac3dec/downmix.c
@@ -0,0 +1,428 @@
+/*
+ *
+ * downmix.c
+ *
+ * Copyright (C) Aaron Holtzman - Sept 1999
+ *
+ * Originally based on code by Yuqing Deng.
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+#include "decode.h"
+#include "downmix.h"
+#include "debug.h"
+
+
+//Pre-scaled downmix coefficients
+static float cmixlev_lut[4] = { 0.2928, 0.2468, 0.2071, 0.2468 };
+static float smixlev_lut[4] = { 0.2928, 0.2071, 0.0 , 0.2071 };
+
+static void
+downmix_3f_2r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
+{
+ uint_32 j;
+ float right_tmp;
+ float left_tmp;
+ float clev,slev;
+ float *centre = 0, *left = 0, *right = 0, *left_sur = 0, *right_sur = 0;
+
+ left = samples[0];
+ centre = samples[1];
+ right = samples[2];
+ left_sur = samples[3];
+ right_sur = samples[4];
+
+ clev = cmixlev_lut[bsi->cmixlev];
+ slev = smixlev_lut[bsi->surmixlev];
+
+#if defined DOLBY_SURROUND
+ for (j = 0; j < 256; j++)
+ {
+ right_tmp = *left_sur++ + *right_sur++;
+ left_tmp = 1.4142f * *left++ + *centre - right_tmp;
+ right_tmp += 1.4142f * *right++ + *centre++;
+
+ s16_samples[j * 2 ] = (sint_16) (left_tmp * 16000.0f);
+ s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 16000.0f);
+ }
+#else
+ for (j = 0; j < 256; j++)
+ {
+ left_tmp = 0.4142f * *left++ + clev * *centre + slev * *left_sur++;
+ right_tmp= 0.4142f * *right++ + clev * *centre++ + slev * *right_sur++;
+
+ s16_samples[j * 2 ] = (sint_16) (left_tmp * 32767.0f);
+ s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
+ }
+#endif
+}
+
+static void
+downmix_2f_2r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
+{
+ uint_32 j;
+ float right_tmp;
+ float left_tmp;
+ float slev;
+ float *left = 0, *right = 0, *left_sur = 0, *right_sur = 0;
+
+ left = samples[0];
+ right = samples[1];
+ left_sur = samples[2];
+ right_sur = samples[3];
+
+ slev = smixlev_lut[bsi->surmixlev];
+
+ for (j = 0; j < 256; j++)
+ {
+ left_tmp = 0.4142f * *left++ + slev * *left_sur++;
+ right_tmp= 0.4142f * *right++ + slev * *right_sur++;
+
+ s16_samples[j * 2 ] = (sint_16) (left_tmp * 32767.0f);
+ s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
+ }
+}
+
+static void
+downmix_3f_1r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
+{
+ uint_32 j;
+ float right_tmp;
+ float left_tmp;
+ float clev,slev;
+ float *centre = 0, *left = 0, *right = 0, *sur = 0;
+
+ left = samples[0];
+ centre = samples[1];
+ right = samples[2];
+ //Mono surround
+ sur = samples[3];
+
+ clev = cmixlev_lut[bsi->cmixlev];
+ slev = smixlev_lut[bsi->surmixlev];
+
+ for (j = 0; j < 256; j++)
+ {
+ left_tmp = 0.4142f * *left++ + clev * *centre++ + slev * *sur;
+ right_tmp= 0.4142f * *right++ + clev * *centre + slev * *sur++;
+
+ s16_samples[j * 2 ] = (sint_16) (left_tmp * 32767.0f);
+ s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
+ }
+}
+
+
+static void
+downmix_2f_1r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
+{
+ uint_32 j;
+ float right_tmp;
+ float left_tmp;
+ float slev;
+ float *left = 0, *right = 0, *sur = 0;
+
+ left = samples[0];
+ right = samples[1];
+ //Mono surround
+ sur = samples[2];
+
+ slev = smixlev_lut[bsi->surmixlev];
+
+ for (j = 0; j < 256; j++)
+ {
+ left_tmp = 0.4142f * *left++ + slev * *sur;
+ right_tmp= 0.4142f * *right++ + slev * *sur++;
+
+ s16_samples[j * 2 ] = (sint_16) (left_tmp * 32767.0f);
+ s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
+ }
+}
+
+static void
+downmix_3f_0r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
+{
+ uint_32 j;
+ float right_tmp;
+ float left_tmp;
+ float clev;
+ float *centre = 0, *left = 0, *right = 0;
+
+ left = samples[0];
+ centre = samples[1];
+ right = samples[2];
+
+ clev = cmixlev_lut[bsi->cmixlev];
+
+ for (j = 0; j < 256; j++)
+ {
+ left_tmp = 0.4142f * *left++ + clev * *centre;
+ right_tmp= 0.4142f * *right++ + clev * *centre++;
+
+ s16_samples[j * 2 ] = (sint_16) (left_tmp * 32767.0f);
+ s16_samples[j * 2 + 1] = (sint_16) (right_tmp * 32767.0f);
+ }
+}
+
+static void
+downmix_2f_0r_to_2ch(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
+{
+ uint_32 j;
+ float *left = 0, *right = 0;
+
+ left = samples[0];
+ right = samples[1];
+
+ for (j = 0; j < 256; j++)
+ {
+ s16_samples[j * 2 ] = (sint_16) (*left++ * 32767.0f);
+ s16_samples[j * 2 + 1] = (sint_16) (*right++ * 32767.0f);
+ }
+}
+
+static void
+downmix_1f_0r_to_2ch(float *centre,sint_16 *s16_samples)
+{
+ uint_32 j;
+ float tmp;
+
+ //Mono program!
+
+ for (j = 0; j < 256; j++)
+ {
+ tmp = 32767.0f * 0.7071f * *centre++;
+
+ s16_samples[j * 2 ] = s16_samples[j * 2 + 1] = (sint_16) tmp;
+ }
+}
+
+//
+// Downmix into 2 or 4 channels (4 ch isn't in quite yet)
+//
+// The downmix function names have the following format
+//
+// downmix_Xf_Yr_to_[2|4]ch[_dolby]
+//
+// where X = number of front channels
+// Y = number of rear channels
+// [2|4] = number of output channels
+// [_dolby] = with or without dolby surround mix
+//
+
+void downmix(bsi_t* bsi, stream_samples_t samples,sint_16 *s16_samples)
+{
+ if(bsi->acmod > 7)
+ dprintf("(downmix) invalid acmod number\n");
+
+ //
+ //There are two main cases, with or without Dolby Surround
+ //
+ if(ac3_config.flags & AC3_DOLBY_SURR_ENABLE)
+ {
+ fprintf(stderr,"Dolby Surround Mixes not currently enabled\n");
+ exit(1);
+ }
+
+ //Non-Dolby surround downmixes
+ switch(bsi->acmod)
+ {
+ // 3/2
+ case 7:
+ downmix_3f_2r_to_2ch(bsi,samples,s16_samples);
+ break;
+
+ // 2/2
+ case 6:
+ downmix_2f_2r_to_2ch(bsi,samples,s16_samples);
+ break;
+
+ // 3/1
+ case 5:
+ downmix_3f_1r_to_2ch(bsi,samples,s16_samples);
+ break;
+
+ // 2/1
+ case 4:
+ downmix_2f_1r_to_2ch(bsi,samples,s16_samples);
+ break;
+
+ // 3/0
+ case 3:
+ downmix_3f_0r_to_2ch(bsi,samples,s16_samples);
+ break;
+
+ case 2:
+ downmix_2f_0r_to_2ch(bsi,samples,s16_samples);
+ break;
+
+ // 1/0
+ case 1:
+ downmix_1f_0r_to_2ch(samples[0],s16_samples);
+ break;
+
+ // 1+1
+ case 0:
+ downmix_1f_0r_to_2ch(samples[ac3_config.dual_mono_ch_sel],s16_samples);
+ break;
+ }
+}
+
+
+
+#if 0
+
+ //the dolby mixes lay here for the time being
+ switch(bsi->acmod)
+ {
+ // 3/2
+ case 7:
+ left = samples[0];
+ centre = samples[1];
+ right = samples[2];
+ left_sur = samples[3];
+ right_sur = samples[4];
+
+ for (j = 0; j < 256; j++)
+ {
+ right_tmp = 0.2265f * *left_sur++ + 0.2265f * *right_sur++;
+ left_tmp = -1 * right_tmp;
+ right_tmp += 0.3204f * *right++ + 0.2265f * *centre;
+ left_tmp += 0.3204f * *left++ + 0.2265f * *centre++;
+
+ samples[1][j] = right_tmp;
+ samples[0][j] = left_tmp;
+ }
+
+ break;
+
+ // 2/2
+ case 6:
+ left = samples[0];
+ right = samples[1];
+ left_sur = samples[2];
+ right_sur = samples[3];
+
+ for (j = 0; j < 256; j++)
+ {
+ right_tmp = 0.2265f * *left_sur++ + 0.2265f * *right_sur++;
+ left_tmp = -1 * right_tmp;
+ right_tmp += 0.3204f * *right++;
+ left_tmp += 0.3204f * *left++ ;
+
+ samples[1][j] = right_tmp;
+ samples[0][j] = left_tmp;
+ }
+ break;
+
+ // 3/1
+ case 5:
+ left = samples[0];
+ centre = samples[1];
+ right = samples[2];
+ //Mono surround
+ right_sur = samples[3];
+
+ for (j = 0; j < 256; j++)
+ {
+ right_tmp = 0.2265f * *right_sur++;
+ left_tmp = -1 * right_tmp;
+ right_tmp += 0.3204f * *right++ + 0.2265f * *centre;
+ left_tmp += 0.3204f * *left++ + 0.2265f * *centre++;
+
+ samples[1][j] = right_tmp;
+ samples[0][j] = left_tmp;
+ }
+ break;
+
+ // 2/1
+ case 4:
+ left = samples[0];
+ right = samples[1];
+ //Mono surround
+ right_sur = samples[2];
+
+ for (j = 0; j < 256; j++)
+ {
+ right_tmp = 0.2265f * *right_sur++;
+ left_tmp = -1 * right_tmp;
+ right_tmp += 0.3204f * *right++;
+ left_tmp += 0.3204f * *left++;
+
+ samples[1][j] = right_tmp;
+ samples[0][j] = left_tmp;
+ }
+ break;
+
+ // 3/0
+ case 3:
+ left = samples[0];
+ centre = samples[1];
+ right = samples[2];
+
+ for (j = 0; j < 256; j++)
+ {
+ right_tmp = 0.3204f * *right++ + 0.2265f * *centre;
+ left_tmp = 0.3204f * *left++ + 0.2265f * *centre++;
+
+ samples[1][j] = right_tmp;
+ samples[0][j] = left_tmp;
+ }
+ break;
+
+ // 2/0
+ case 2:
+ //Do nothing!
+ break;
+
+ // 1/0
+ case 1:
+ //Mono program!
+ right = samples[0];
+
+ for (j = 0; j < 256; j++)
+ {
+ right_tmp = 0.7071f * *right++;
+
+ samples[1][j] = right_tmp;
+ samples[0][j] = right_tmp;
+ }
+
+ break;
+
+ // 1+1
+ case 0:
+ //Dual mono, output selected by user
+ right = samples[ac3_config.dual_mono_ch_sel];
+
+ for (j = 0; j < 256; j++)
+ {
+ right_tmp = 0.7071f * *right++;
+
+ samples[1][j] = right_tmp;
+ samples[0][j] = right_tmp;
+ }
+ break;
+#endif
diff --git a/ac3dec/downmix.h b/ac3dec/downmix.h
new file mode 100644
index 0000000..7537c62
--- /dev/null
+++ b/ac3dec/downmix.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * downmix.h
+ *
+ * Copyright (C) Aaron Holtzman - Sept 1999
+ *
+ * Originally based on code by Yeqing Deng.
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+void downmix(bsi_t* bsi, stream_samples_t stream_samples,sint_16 *s16_samples);
diff --git a/ac3dec/exponent.c b/ac3dec/exponent.c
new file mode 100644
index 0000000..98111a5
--- /dev/null
+++ b/ac3dec/exponent.c
@@ -0,0 +1,135 @@
+/*
+ * exponent.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+#include "decode.h"
+#include "exponent.h"
+
+
+static void exp_unpack_ch(uint_16 type,uint_16 expstr,uint_16 ngrps,uint_16 initial_exp,
+ uint_16 exps[], uint_16 *dest);
+
+void
+exponent_unpack( bsi_t *bsi, audblk_t *audblk)
+{
+ uint_16 i;
+
+ for(i=0; i< bsi->nfchans; i++)
+ exp_unpack_ch(UNPACK_FBW, audblk->chexpstr[i], audblk->nchgrps[i], audblk->exps[i][0],
+ &audblk->exps[i][1], audblk->fbw_exp[i]);
+
+ if(audblk->cplinu)
+ exp_unpack_ch(UNPACK_CPL, audblk->cplexpstr, audblk->ncplgrps, audblk->cplabsexp << 1,
+ audblk->cplexps, &audblk->cpl_exp[audblk->cplstrtmant]);
+
+ if(bsi->lfeon)
+ exp_unpack_ch(UNPACK_LFE, audblk->lfeexpstr, 2, audblk->lfeexps[0],
+ &audblk->lfeexps[1], audblk->lfe_exp);
+}
+
+
+static void
+exp_unpack_ch(uint_16 type,uint_16 expstr,uint_16 ngrps,uint_16 initial_exp,
+ uint_16 exps[], uint_16 *dest)
+{
+ uint_16 i,j;
+ sint_16 exp_acc;
+ sint_16 exp_1,exp_2,exp_3;
+
+ if(expstr == EXP_REUSE)
+ return;
+
+ /* Handle the initial absolute exponent */
+ exp_acc = initial_exp;
+ j = 0;
+
+ /* In the case of a fbw channel then the initial absolute values is
+ * also an exponent */
+ if(type != UNPACK_CPL)
+ dest[j++] = exp_acc;
+
+ /* Loop through the groups and fill the dest array appropriately */
+ for(i=0; i< ngrps; i++)
+ {
+ if(exps[i] > 124)
+ goto error;
+
+ exp_1 = exps[i] / 25;
+ exp_2 = (exps[i] - (exp_1 * 25)) / 5;
+ exp_3 = exps[i] - (exp_1 * 25) - (exp_2 * 5) ;
+
+ exp_acc += (exp_1 - 2);
+
+ switch(expstr)
+ {
+ case EXP_D45:
+ dest[j++] = exp_acc;
+ dest[j++] = exp_acc;
+ case EXP_D25:
+ dest[j++] = exp_acc;
+ case EXP_D15:
+ dest[j++] = exp_acc;
+ }
+
+ exp_acc += (exp_2 - 2);
+
+ switch(expstr)
+ {
+ case EXP_D45:
+ dest[j++] = exp_acc;
+ dest[j++] = exp_acc;
+ case EXP_D25:
+ dest[j++] = exp_acc;
+ case EXP_D15:
+ dest[j++] = exp_acc;
+ }
+
+ exp_acc += (exp_3 - 2);
+
+ switch(expstr)
+ {
+ case EXP_D45:
+ dest[j++] = exp_acc;
+ dest[j++] = exp_acc;
+ case EXP_D25:
+ dest[j++] = exp_acc;
+ case EXP_D15:
+ dest[j++] = exp_acc;
+ }
+ }
+
+ return;
+
+ goto error;
+error:
+ if(!error_flag)
+ fprintf(stderr,"** Invalid exponent - skipping frame **\n");
+ error_flag = 1;
+}
+
diff --git a/ac3dec/exponent.h b/ac3dec/exponent.h
new file mode 100644
index 0000000..06c59db
--- /dev/null
+++ b/ac3dec/exponent.h
@@ -0,0 +1,28 @@
+/*
+ * exponent.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define UNPACK_FBW 1
+#define UNPACK_CPL 2
+#define UNPACK_LFE 4
+
+void exponent_unpack( bsi_t *bsi, audblk_t *audblk);
diff --git a/ac3dec/imdct.c b/ac3dec/imdct.c
new file mode 100644
index 0000000..ae2794e
--- /dev/null
+++ b/ac3dec/imdct.c
@@ -0,0 +1,468 @@
+/*
+ * imdct.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+#include "decode.h"
+#include "imdct.h"
+
+void imdct_do_256(float data[],float delay[]);
+void imdct_do_512(float data[],float delay[]);
+
+typedef struct complex_s
+{
+ float real;
+ float imag;
+} complex_t;
+
+
+#define N 512
+
+
+/* 128 point bit-reverse LUT */
+static uint_8 bit_reverse_512[128] = {
+ 0x00, 0x40, 0x20, 0x60, 0x10, 0x50, 0x30, 0x70,
+ 0x08, 0x48, 0x28, 0x68, 0x18, 0x58, 0x38, 0x78,
+ 0x04, 0x44, 0x24, 0x64, 0x14, 0x54, 0x34, 0x74,
+ 0x0c, 0x4c, 0x2c, 0x6c, 0x1c, 0x5c, 0x3c, 0x7c,
+ 0x02, 0x42, 0x22, 0x62, 0x12, 0x52, 0x32, 0x72,
+ 0x0a, 0x4a, 0x2a, 0x6a, 0x1a, 0x5a, 0x3a, 0x7a,
+ 0x06, 0x46, 0x26, 0x66, 0x16, 0x56, 0x36, 0x76,
+ 0x0e, 0x4e, 0x2e, 0x6e, 0x1e, 0x5e, 0x3e, 0x7e,
+ 0x01, 0x41, 0x21, 0x61, 0x11, 0x51, 0x31, 0x71,
+ 0x09, 0x49, 0x29, 0x69, 0x19, 0x59, 0x39, 0x79,
+ 0x05, 0x45, 0x25, 0x65, 0x15, 0x55, 0x35, 0x75,
+ 0x0d, 0x4d, 0x2d, 0x6d, 0x1d, 0x5d, 0x3d, 0x7d,
+ 0x03, 0x43, 0x23, 0x63, 0x13, 0x53, 0x33, 0x73,
+ 0x0b, 0x4b, 0x2b, 0x6b, 0x1b, 0x5b, 0x3b, 0x7b,
+ 0x07, 0x47, 0x27, 0x67, 0x17, 0x57, 0x37, 0x77,
+ 0x0f, 0x4f, 0x2f, 0x6f, 0x1f, 0x5f, 0x3f, 0x7f};
+
+static uint_8 bit_reverse_256[64] = {
+ 0x00, 0x20, 0x10, 0x30, 0x08, 0x28, 0x18, 0x38,
+ 0x04, 0x24, 0x14, 0x34, 0x0c, 0x2c, 0x1c, 0x3c,
+ 0x02, 0x22, 0x12, 0x32, 0x0a, 0x2a, 0x1a, 0x3a,
+ 0x06, 0x26, 0x16, 0x36, 0x0e, 0x2e, 0x1e, 0x3e,
+ 0x01, 0x21, 0x11, 0x31, 0x09, 0x29, 0x19, 0x39,
+ 0x05, 0x25, 0x15, 0x35, 0x0d, 0x2d, 0x1d, 0x3d,
+ 0x03, 0x23, 0x13, 0x33, 0x0b, 0x2b, 0x1b, 0x3b,
+ 0x07, 0x27, 0x17, 0x37, 0x0f, 0x2f, 0x1f, 0x3f};
+
+static complex_t buf[128];
+
+/* Twiddle factor LUT */
+static complex_t *w[7];
+static complex_t w_1[1];
+static complex_t w_2[2];
+static complex_t w_4[4];
+static complex_t w_8[8];
+static complex_t w_16[16];
+static complex_t w_32[32];
+static complex_t w_64[64];
+
+/* Twiddle factors for IMDCT */
+static float xcos1[128];
+static float xsin1[128];
+static float xcos2[64];
+static float xsin2[64];
+
+/* Delay buffer for time domain interleaving */
+static float delay[6][256];
+
+/* Windowing function for Modified DCT - Thank you acroread */
+static float window[] = {
+ 0.00014, 0.00024, 0.00037, 0.00051, 0.00067, 0.00086, 0.00107, 0.00130,
+ 0.00157, 0.00187, 0.00220, 0.00256, 0.00297, 0.00341, 0.00390, 0.00443,
+ 0.00501, 0.00564, 0.00632, 0.00706, 0.00785, 0.00871, 0.00962, 0.01061,
+ 0.01166, 0.01279, 0.01399, 0.01526, 0.01662, 0.01806, 0.01959, 0.02121,
+ 0.02292, 0.02472, 0.02662, 0.02863, 0.03073, 0.03294, 0.03527, 0.03770,
+ 0.04025, 0.04292, 0.04571, 0.04862, 0.05165, 0.05481, 0.05810, 0.06153,
+ 0.06508, 0.06878, 0.07261, 0.07658, 0.08069, 0.08495, 0.08935, 0.09389,
+ 0.09859, 0.10343, 0.10842, 0.11356, 0.11885, 0.12429, 0.12988, 0.13563,
+ 0.14152, 0.14757, 0.15376, 0.16011, 0.16661, 0.17325, 0.18005, 0.18699,
+ 0.19407, 0.20130, 0.20867, 0.21618, 0.22382, 0.23161, 0.23952, 0.24757,
+ 0.25574, 0.26404, 0.27246, 0.28100, 0.28965, 0.29841, 0.30729, 0.31626,
+ 0.32533, 0.33450, 0.34376, 0.35311, 0.36253, 0.37204, 0.38161, 0.39126,
+ 0.40096, 0.41072, 0.42054, 0.43040, 0.44030, 0.45023, 0.46020, 0.47019,
+ 0.48020, 0.49022, 0.50025, 0.51028, 0.52031, 0.53033, 0.54033, 0.55031,
+ 0.56026, 0.57019, 0.58007, 0.58991, 0.59970, 0.60944, 0.61912, 0.62873,
+ 0.63827, 0.64774, 0.65713, 0.66643, 0.67564, 0.68476, 0.69377, 0.70269,
+ 0.71150, 0.72019, 0.72877, 0.73723, 0.74557, 0.75378, 0.76186, 0.76981,
+ 0.77762, 0.78530, 0.79283, 0.80022, 0.80747, 0.81457, 0.82151, 0.82831,
+ 0.83496, 0.84145, 0.84779, 0.85398, 0.86001, 0.86588, 0.87160, 0.87716,
+ 0.88257, 0.88782, 0.89291, 0.89785, 0.90264, 0.90728, 0.91176, 0.91610,
+ 0.92028, 0.92432, 0.92822, 0.93197, 0.93558, 0.93906, 0.94240, 0.94560,
+ 0.94867, 0.95162, 0.95444, 0.95713, 0.95971, 0.96217, 0.96451, 0.96674,
+ 0.96887, 0.97089, 0.97281, 0.97463, 0.97635, 0.97799, 0.97953, 0.98099,
+ 0.98236, 0.98366, 0.98488, 0.98602, 0.98710, 0.98811, 0.98905, 0.98994,
+ 0.99076, 0.99153, 0.99225, 0.99291, 0.99353, 0.99411, 0.99464, 0.99513,
+ 0.99558, 0.99600, 0.99639, 0.99674, 0.99706, 0.99736, 0.99763, 0.99788,
+ 0.99811, 0.99831, 0.99850, 0.99867, 0.99882, 0.99895, 0.99908, 0.99919,
+ 0.99929, 0.99938, 0.99946, 0.99953, 0.99959, 0.99965, 0.99969, 0.99974,
+ 0.99978, 0.99981, 0.99984, 0.99986, 0.99988, 0.99990, 0.99992, 0.99993,
+ 0.99994, 0.99995, 0.99996, 0.99997, 0.99998, 0.99998, 0.99998, 0.99999,
+ 0.99999, 0.99999, 0.99999, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000,
+ 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 };
+
+
+static inline void swap_cmplx(complex_t *a, complex_t *b)
+{
+ complex_t tmp;
+
+ tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+
+
+static inline complex_t cmplx_mult(complex_t a, complex_t b)
+{
+ complex_t ret;
+
+ ret.real = a.real * b.real - a.imag * b.imag;
+ ret.imag = a.real * b.imag + a.imag * b.real;
+
+ return ret;
+}
+
+void imdct_init(void)
+{
+ int i,k;
+ complex_t angle_step;
+ complex_t current_angle;
+
+ /* Twiddle factors to turn IFFT into IMDCT */
+ for( i=0; i < 128; i++)
+ {
+ xcos1[i] = -cos(2.0f * M_PI * (8*i+1)/(8*N)) ;
+ xsin1[i] = -sin(2.0f * M_PI * (8*i+1)/(8*N)) ;
+ }
+
+ /* More twiddle factors to turn IFFT into IMDCT */
+ for( i=0; i < 64; i++)
+ {
+ xcos2[i] = -cos(2.0f * M_PI * (8*i+1)/(4*N)) ;
+ xsin2[i] = -sin(2.0f * M_PI * (8*i+1)/(4*N)) ;
+ }
+
+ /* Canonical twiddle factors for FFT */
+ w[0] = w_1;
+ w[1] = w_2;
+ w[2] = w_4;
+ w[3] = w_8;
+ w[4] = w_16;
+ w[5] = w_32;
+ w[6] = w_64;
+
+ for( i = 0; i < 7; i++)
+ {
+ angle_step.real = cos(-2.0 * M_PI / (1 << (i+1)));
+ angle_step.imag = sin(-2.0 * M_PI / (1 << (i+1)));
+
+ current_angle.real = 1.0;
+ current_angle.imag = 0.0;
+
+ for (k = 0; k < 1 << i; k++)
+ {
+ w[i][k] = current_angle;
+ current_angle = cmplx_mult(current_angle,angle_step);
+ }
+ }
+}
+
+void
+imdct_do_512(float data[],float delay[])
+{
+ int i,k;
+ int p,q;
+ int m;
+ int two_m;
+ int two_m_plus_one;
+
+ float tmp_a_i;
+ float tmp_a_r;
+ float tmp_b_i;
+ float tmp_b_r;
+
+ float *data_ptr;
+ float *delay_ptr;
+ float *window_ptr;
+
+ //
+ // 512 IMDCT with source and dest data in 'data'
+ //
+
+ // Pre IFFT complex multiply plus IFFT cmplx conjugate and bit reverse
+ // permutation
+ for( i=0; i < 128; i++)
+ {
+ k = bit_reverse_512[i];
+
+ /* z[i] = (X[256-2*i-1] + j * X[2*i]) * (xcos1[i] + j * xsin1[i]) ; */
+ buf[k].real = (data[256-2*i-1] * xcos1[i]) - (data[2*i] * xsin1[i]);
+ buf[k].imag = -1.0 * ((data[2*i] * xcos1[i]) + (data[256-2*i-1] * xsin1[i]));
+ }
+
+ // FFT Merge
+ for (m=0; m < 7; m++)
+ {
+ if(m)
+ two_m = (1 << m);
+ else
+ two_m = 1;
+
+ two_m_plus_one = (1 << (m+1));
+
+ for(k = 0; k < two_m; k++)
+ {
+ for(i = 0; i < 128; i += two_m_plus_one)
+ {
+ p = k + i;
+ q = p + two_m;
+ tmp_a_r = buf[p].real;
+ tmp_a_i = buf[p].imag;
+ tmp_b_r = buf[q].real * w[m][k].real - buf[q].imag * w[m][k].imag;
+ tmp_b_i = buf[q].imag * w[m][k].real + buf[q].real * w[m][k].imag;
+ buf[p].real = tmp_a_r + tmp_b_r;
+ buf[p].imag = tmp_a_i + tmp_b_i;
+ buf[q].real = tmp_a_r - tmp_b_r;
+ buf[q].imag = tmp_a_i - tmp_b_i;
+
+ }
+ }
+ }
+
+ /* Post IFFT complex multiply plus IFFT complex conjugate*/
+ for( i=0; i < 128; i++)
+ {
+ /* y[n] = z[n] * (xcos1[n] + j * xsin1[n]) ; */
+ tmp_a_r = buf[i].real;
+ tmp_a_i = buf[i].imag;
+ //Note that I flipped the signs on the imaginary ops to do the complex conj
+ buf[i].real =(tmp_a_r * xcos1[i]) + (tmp_a_i * xsin1[i]);
+ buf[i].imag =(tmp_a_r * xsin1[i]) - (tmp_a_i * xcos1[i]);
+ }
+
+ data_ptr = data;
+ delay_ptr = delay;
+ window_ptr = window;
+
+ /* Window and convert to real valued signal */
+ for(i=0; i< 64; i++)
+ {
+ *data_ptr++ = 2.0f * (-buf[64+i].imag * *window_ptr++ + *delay_ptr++);
+ *data_ptr++ = 2.0f * ( buf[64-i-1].real * *window_ptr++ + *delay_ptr++);
+ }
+
+ for(i=0; i< 64; i++)
+ {
+ *data_ptr++ = 2.0f * (-buf[i].real * *window_ptr++ + *delay_ptr++);
+ *data_ptr++ = 2.0f * ( buf[128-i-1].imag * *window_ptr++ + *delay_ptr++);
+ }
+
+ /* The trailing edge of the window goes into the delay line */
+ delay_ptr = delay;
+
+ for(i=0; i< 64; i++)
+ {
+ *delay_ptr++ = -buf[64+i].real * *--window_ptr;
+ *delay_ptr++ = buf[64-i-1].imag * *--window_ptr;
+ }
+
+ for(i=0; i<64; i++)
+ {
+ *delay_ptr++ = buf[i].imag * *--window_ptr;
+ *delay_ptr++ = -buf[128-i-1].real * *--window_ptr;
+ }
+}
+
+void
+imdct_do_256(float data[],float delay[])
+{
+ int i,k;
+ int p,q;
+ int m;
+ int two_m;
+ int two_m_plus_one;
+
+ float tmp_a_i;
+ float tmp_a_r;
+ float tmp_b_i;
+ float tmp_b_r;
+
+ float *data_ptr;
+ float *delay_ptr;
+ float *window_ptr;
+
+ complex_t *buf_1, *buf_2;
+
+ buf_1 = &buf[0];
+ buf_2 = &buf[64];
+
+ // Pre IFFT complex multiply plus IFFT cmplx conjugate and bit reverse
+ // permutation
+ for(i=0; i<64; i++)
+ {
+ /* X1[i] = X[2*i] */
+ /* X2[i] = X[2*i+1] */
+
+ k = bit_reverse_256[i];
+
+ p = 2 * (128-2*i-1);
+ q = 2 * (2 * i);
+
+ /* Z1[i] = (X1[128-2*i-1] + j * X1[2*i]) * (xcos2[i] + j * xsin2[i]); */
+ buf_1[k].real = data[p] * xcos2[i] - data[q] * xsin2[i];
+ buf_1[k].imag = -1.0f * (data[q] * xcos2[i] + data[p] * xsin2[i]);
+ /* Z2[i] = (X2[128-2*i-1] + j * X2[2*i]) * (xcos2[i] + j * xsin2[i]); */
+ buf_2[k].real = data[p + 1] * xcos2[i] - data[q + 1] * xsin2[i];
+ buf_2[k].imag = -1.0f * ( data[q + 1] * xcos2[i] + data[p + 1] * xsin2[i]);
+ }
+
+ // FFT Merge
+ for (m=0; m < 6; m++)
+ {
+ two_m = (1 << m);
+ two_m_plus_one = (1 << (m+1));
+
+ if(m)
+ two_m = (1 << m);
+ else
+ two_m = 1;
+
+ for(k = 0; k < two_m; k++)
+ {
+ for(i = 0; i < 64; i += two_m_plus_one)
+ {
+ p = k + i;
+ q = p + two_m;
+ //Do block 1
+ tmp_a_r = buf_1[p].real;
+ tmp_a_i = buf_1[p].imag;
+ tmp_b_r = buf_1[q].real * w[m][k].real - buf_1[q].imag * w[m][k].imag;
+ tmp_b_i = buf_1[q].imag * w[m][k].real + buf_1[q].real * w[m][k].imag;
+ buf_1[p].real = tmp_a_r + tmp_b_r;
+ buf_1[p].imag = tmp_a_i + tmp_b_i;
+ buf_1[q].real = tmp_a_r - tmp_b_r;
+ buf_1[q].imag = tmp_a_i - tmp_b_i;
+
+ //Do block 2
+ tmp_a_r = buf_2[p].real;
+ tmp_a_i = buf_2[p].imag;
+ tmp_b_r = buf_2[q].real * w[m][k].real - buf_2[q].imag * w[m][k].imag;
+ tmp_b_i = buf_2[q].imag * w[m][k].real + buf_2[q].real * w[m][k].imag;
+ buf_2[p].real = tmp_a_r + tmp_b_r;
+ buf_2[p].imag = tmp_a_i + tmp_b_i;
+ buf_2[q].real = tmp_a_r - tmp_b_r;
+ buf_2[q].imag = tmp_a_i - tmp_b_i;
+
+ }
+ }
+ }
+
+ // Post IFFT complex multiply
+ for( i=0; i < 64; i++)
+ {
+ //Note that I flipped the signs on the imaginary ops to do the complex conj
+
+ /* y1[n] = z1[n] * (xcos2[n] + j * xs in2[n]) ; */
+ tmp_a_r = buf_1[i].real;
+ tmp_a_i = buf_1[i].imag;
+ buf_1[i].real =(tmp_a_r * xcos2[i]) + (tmp_a_i * xsin2[i]);
+ buf_1[i].imag =(tmp_a_r * xsin2[i]) - (tmp_a_i * xcos2[i]);
+ /* y2[n] = z2[n] * (xcos2[n] + j * xsin2[n]) ; */
+ tmp_a_r = buf_2[i].real;
+ tmp_a_i = buf_2[i].imag;
+ buf_2[i].real =(tmp_a_r * xcos2[i]) + (tmp_a_i * xsin2[i]);
+ buf_2[i].imag =(tmp_a_r * xsin2[i]) - (tmp_a_i * xcos2[i]);
+ }
+
+ data_ptr = data;
+ delay_ptr = delay;
+ window_ptr = window;
+
+ /* Window and convert to real valued signal */
+ for(i=0; i< 64; i++)
+ {
+ *data_ptr++ = 2.0f * (-buf_1[i].imag * *window_ptr++ + *delay_ptr++);
+ *data_ptr++ = 2.0f * ( buf_1[64-i-1].real * *window_ptr++ + *delay_ptr++);
+ }
+
+ for(i=0; i< 64; i++)
+ {
+ *data_ptr++ = 2.0f * (-buf_1[i].real * *window_ptr++ + *delay_ptr++);
+ *data_ptr++ = 2.0f * ( buf_1[64-i-1].imag * *window_ptr++ + *delay_ptr++);
+ }
+
+ delay_ptr = delay;
+
+ for(i=0; i< 64; i++)
+ {
+ *delay_ptr++ = -buf_2[i].real * *--window_ptr;
+ *delay_ptr++ = buf_2[64-i-1].imag * *--window_ptr;
+ }
+
+ for(i=0; i< 64; i++)
+ {
+ *delay_ptr++ = buf_2[i].imag * *--window_ptr;
+ *delay_ptr++ = -buf_2[64-i-1].real * *--window_ptr;
+ }
+}
+
+//FIXME remove - for timing code
+///#include <sys/time.h>
+//FIXME remove
+
+void
+imdct(bsi_t *bsi,audblk_t *audblk, stream_samples_t samples) {
+ int i;
+
+ //handy timing code
+ //struct timeval start,end;
+
+ //gettimeofday(&start,0);
+
+ for(i=0; i<bsi->nfchans;i++)
+ {
+ if(audblk->blksw[i])
+ imdct_do_256(samples[i],delay[i]);
+ else
+ imdct_do_512(samples[i],delay[i]);
+ }
+ //gettimeofday(&end,0);
+ //printf("imdct %ld us\n",(end.tv_sec - start.tv_sec) * 1000000 +
+ //end.tv_usec - start.tv_usec);
+
+ //XXX We don't bother with the IMDCT for the LFE as it's currently
+ //unused.
+ //if (bsi->lfeon)
+ // imdct_do_512(coeffs->lfe,samples->channel[5],delay[5]);
+ //
+}
diff --git a/ac3dec/imdct.h b/ac3dec/imdct.h
new file mode 100644
index 0000000..750aa87
--- /dev/null
+++ b/ac3dec/imdct.h
@@ -0,0 +1,26 @@
+/*
+ * imdct.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+void imdct(bsi_t *bsi,audblk_t *audblk, stream_samples_t samples);
+void imdct_init(void);
diff --git a/ac3dec/parse.c b/ac3dec/parse.c
new file mode 100644
index 0000000..3560bc5
--- /dev/null
+++ b/ac3dec/parse.c
@@ -0,0 +1,597 @@
+/*
+ * parse.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+#include "bitstream.h"
+#include "stats.h"
+#include "debug.h"
+#include "parse.h"
+
+/* Misc LUT */
+static const uint_16 nfchans[8] = {2,1,2,3,3,4,4,5};
+
+struct frmsize_s
+{
+ uint_16 bit_rate;
+ uint_16 frm_size[3];
+};
+
+static const struct frmsize_s frmsizecod_tbl[64] =
+{
+ { 32 ,{64 ,69 ,96 } },
+ { 32 ,{64 ,70 ,96 } },
+ { 40 ,{80 ,87 ,120 } },
+ { 40 ,{80 ,88 ,120 } },
+ { 48 ,{96 ,104 ,144 } },
+ { 48 ,{96 ,105 ,144 } },
+ { 56 ,{112 ,121 ,168 } },
+ { 56 ,{112 ,122 ,168 } },
+ { 64 ,{128 ,139 ,192 } },
+ { 64 ,{128 ,140 ,192 } },
+ { 80 ,{160 ,174 ,240 } },
+ { 80 ,{160 ,175 ,240 } },
+ { 96 ,{192 ,208 ,288 } },
+ { 96 ,{192 ,209 ,288 } },
+ { 112 ,{224 ,243 ,336 } },
+ { 112 ,{224 ,244 ,336 } },
+ { 128 ,{256 ,278 ,384 } },
+ { 128 ,{256 ,279 ,384 } },
+ { 160 ,{320 ,348 ,480 } },
+ { 160 ,{320 ,349 ,480 } },
+ { 192 ,{384 ,417 ,576 } },
+ { 192 ,{384 ,418 ,576 } },
+ { 224 ,{448 ,487 ,672 } },
+ { 224 ,{448 ,488 ,672 } },
+ { 256 ,{512 ,557 ,768 } },
+ { 256 ,{512 ,558 ,768 } },
+ { 320 ,{640 ,696 ,960 } },
+ { 320 ,{640 ,697 ,960 } },
+ { 384 ,{768 ,835 ,1152 } },
+ { 384 ,{768 ,836 ,1152 } },
+ { 448 ,{896 ,975 ,1344 } },
+ { 448 ,{896 ,976 ,1344 } },
+ { 512 ,{1024 ,1114 ,1536 } },
+ { 512 ,{1024 ,1115 ,1536 } },
+ { 576 ,{1152 ,1253 ,1728 } },
+ { 576 ,{1152 ,1254 ,1728 } },
+ { 640 ,{1280 ,1393 ,1920 } },
+ { 640 ,{1280 ,1394 ,1920 } }
+};
+
+/* Parse a syncinfo structure, minus the sync word */
+void
+parse_syncinfo(syncinfo_t *syncinfo,uint_8 *data)
+{
+ //
+ // We need to read in the entire syncinfo struct (0x0b77 + 24 bits)
+ // in order to determine how big the frame is
+ //
+
+ // Get the sampling rate
+ syncinfo->fscod = (data[2] >> 6) & 0x3;
+
+ if(syncinfo->fscod == 3)
+ {
+ //invalid sampling rate code
+ error_flag = 1;
+ return;
+ }
+ else if(syncinfo->fscod == 2)
+ syncinfo->sampling_rate = 32000;
+ else if(syncinfo->fscod == 1)
+ syncinfo->sampling_rate = 44100;
+ else
+ syncinfo->sampling_rate = 48000;
+
+ // Get the frame size code
+ syncinfo->frmsizecod = data[2] & 0x3f;
+
+ // Calculate the frame size and bitrate
+ syncinfo->frame_size =
+ frmsizecod_tbl[syncinfo->frmsizecod].frm_size[syncinfo->fscod];
+ syncinfo->bit_rate = frmsizecod_tbl[syncinfo->frmsizecod].bit_rate;
+
+}
+
+/*
+ * This routine fills a bsi struct from the AC3 stream
+ */
+
+void
+parse_bsi(bsi_t *bsi)
+{
+ uint_32 i;
+
+ /* Check the AC-3 version number */
+ bsi->bsid = bitstream_get(5);
+
+ /* Get the audio service provided by the steram */
+ bsi->bsmod = bitstream_get(3);
+
+ /* Get the audio coding mode (ie how many channels)*/
+ bsi->acmod = bitstream_get(3);
+ /* Predecode the number of full bandwidth channels as we use this
+ * number a lot */
+ bsi->nfchans = nfchans[bsi->acmod];
+
+ /* If it is in use, get the centre channel mix level */
+ if ((bsi->acmod & 0x1) && (bsi->acmod != 0x1))
+ bsi->cmixlev = bitstream_get(2);
+
+ /* If it is in use, get the surround channel mix level */
+ if (bsi->acmod & 0x4)
+ bsi->surmixlev = bitstream_get(2);
+
+ /* Get the dolby surround mode if in 2/0 mode */
+ if(bsi->acmod == 0x2)
+ bsi->dsurmod= bitstream_get(2);
+
+ /* Is the low frequency effects channel on? */
+ bsi->lfeon = bitstream_get(1);
+
+ /* Get the dialogue normalization level */
+ bsi->dialnorm = bitstream_get(5);
+
+ /* Does compression gain exist? */
+ bsi->compre = bitstream_get(1);
+ if (bsi->compre)
+ {
+ /* Get compression gain */
+ bsi->compr = bitstream_get(8);
+ }
+
+ /* Does language code exist? */
+ bsi->langcode = bitstream_get(1);
+ if (bsi->langcode)
+ {
+ /* Get langauge code */
+ bsi->langcod = bitstream_get(8);
+ }
+
+ /* Does audio production info exist? */
+ bsi->audprodie = bitstream_get(1);
+ if (bsi->audprodie)
+ {
+ /* Get mix level */
+ bsi->mixlevel = bitstream_get(5);
+
+ /* Get room type */
+ bsi->roomtyp = bitstream_get(2);
+ }
+
+ /* If we're in dual mono mode then get some extra info */
+ if (bsi->acmod ==0)
+ {
+ /* Get the dialogue normalization level two */
+ bsi->dialnorm2 = bitstream_get(5);
+
+ /* Does compression gain two exist? */
+ bsi->compr2e = bitstream_get(1);
+ if (bsi->compr2e)
+ {
+ /* Get compression gain two */
+ bsi->compr2 = bitstream_get(8);
+ }
+
+ /* Does language code two exist? */
+ bsi->langcod2e = bitstream_get(1);
+ if (bsi->langcod2e)
+ {
+ /* Get langauge code two */
+ bsi->langcod2 = bitstream_get(8);
+ }
+
+ /* Does audio production info two exist? */
+ bsi->audprodi2e = bitstream_get(1);
+ if (bsi->audprodi2e)
+ {
+ /* Get mix level two */
+ bsi->mixlevel2 = bitstream_get(5);
+
+ /* Get room type two */
+ bsi->roomtyp2 = bitstream_get(2);
+ }
+ }
+
+ /* Get the copyright bit */
+ bsi->copyrightb = bitstream_get(1);
+
+ /* Get the original bit */
+ bsi->origbs = bitstream_get(1);
+
+ /* Does timecode one exist? */
+ bsi->timecod1e = bitstream_get(1);
+
+ if(bsi->timecod1e)
+ bsi->timecod1 = bitstream_get(14);
+
+ /* Does timecode two exist? */
+ bsi->timecod2e = bitstream_get(1);
+
+ if(bsi->timecod2e)
+ bsi->timecod2 = bitstream_get(14);
+
+ /* Does addition info exist? */
+ bsi->addbsie = bitstream_get(1);
+
+ if(bsi->addbsie)
+ {
+ /* Get how much info is there */
+ bsi->addbsil = bitstream_get(6);
+
+ /* Get the additional info */
+ for(i=0;i<(bsi->addbsil + 1);i++)
+ bsi->addbsi[i] = bitstream_get(8);
+ }
+
+ stats_print_bsi(bsi);
+}
+
+/* More pain inducing parsing */
+void
+parse_audblk(bsi_t *bsi,audblk_t *audblk)
+{
+ int i,j;
+
+ for (i=0;i < bsi->nfchans; i++)
+ {
+ /* Is this channel an interleaved 256 + 256 block ? */
+ audblk->blksw[i] = bitstream_get(1);
+ }
+
+ for (i=0;i < bsi->nfchans; i++)
+ {
+ /* Should we dither this channel? */
+ audblk->dithflag[i] = bitstream_get(1);
+ }
+
+ /* Does dynamic range control exist? */
+ audblk->dynrnge = bitstream_get(1);
+ if (audblk->dynrnge)
+ {
+ /* Get dynamic range info */
+ audblk->dynrng = bitstream_get(8);
+ }
+
+ /* If we're in dual mono mode then get the second channel DR info */
+ if (bsi->acmod == 0)
+ {
+ /* Does dynamic range control two exist? */
+ audblk->dynrng2e = bitstream_get(1);
+ if (audblk->dynrng2e)
+ {
+ /* Get dynamic range info */
+ audblk->dynrng2 = bitstream_get(8);
+ }
+ }
+
+ /* Does coupling strategy exist? */
+ audblk->cplstre = bitstream_get(1);
+ if (audblk->cplstre)
+ {
+ /* Is coupling turned on? */
+ audblk->cplinu = bitstream_get(1);
+ if(audblk->cplinu)
+ {
+ for(i=0;i < bsi->nfchans; i++)
+ audblk->chincpl[i] = bitstream_get(1);
+ if(bsi->acmod == 0x2)
+ audblk->phsflginu = bitstream_get(1);
+ audblk->cplbegf = bitstream_get(4);
+ audblk->cplendf = bitstream_get(4);
+ audblk->ncplsubnd = (audblk->cplendf + 2) - audblk->cplbegf + 1;
+
+ /* Calculate the start and end bins of the coupling channel */
+ audblk->cplstrtmant = (audblk->cplbegf * 12) + 37 ;
+ audblk->cplendmant = ((audblk->cplendf + 3) * 12) + 37;
+
+ /* The number of combined subbands is ncplsubnd minus each combined
+ * band */
+ audblk->ncplbnd = audblk->ncplsubnd;
+
+ for(i=1; i< audblk->ncplsubnd; i++)
+ {
+ audblk->cplbndstrc[i] = bitstream_get(1);
+ audblk->ncplbnd -= audblk->cplbndstrc[i];
+ }
+ }
+ }
+
+ if(audblk->cplinu)
+ {
+ /* Loop through all the channels and get their coupling co-ords */
+ for(i=0;i < bsi->nfchans;i++)
+ {
+ if(!audblk->chincpl[i])
+ continue;
+
+ /* Is there new coupling co-ordinate info? */
+ audblk->cplcoe[i] = bitstream_get(1);
+
+ if(audblk->cplcoe[i])
+ {
+ audblk->mstrcplco[i] = bitstream_get(2);
+ for(j=0;j < audblk->ncplbnd; j++)
+ {
+ audblk->cplcoexp[i][j] = bitstream_get(4);
+ audblk->cplcomant[i][j] = bitstream_get(4);
+ }
+ }
+ }
+
+ /* If we're in dual mono mode, there's going to be some phase info */
+ if( (bsi->acmod == 0x2) && audblk->phsflginu &&
+ (audblk->cplcoe[0] || audblk->cplcoe[1]))
+ {
+ for(j=0;j < audblk->ncplbnd; j++)
+ audblk->phsflg[j] = bitstream_get(1);
+
+ }
+ }
+
+ /* If we're in dual mono mode, there may be a rematrix strategy */
+ if(bsi->acmod == 0x2)
+ {
+ audblk->rematstr = bitstream_get(1);
+ if(audblk->rematstr)
+ {
+ if (audblk->cplinu == 0)
+ {
+ for(i = 0; i < 4; i++)
+ audblk->rematflg[i] = bitstream_get(1);
+ }
+ if((audblk->cplbegf > 2) && audblk->cplinu)
+ {
+ for(i = 0; i < 4; i++)
+ audblk->rematflg[i] = bitstream_get(1);
+ }
+ if((audblk->cplbegf <= 2) && audblk->cplinu)
+ {
+ for(i = 0; i < 3; i++)
+ audblk->rematflg[i] = bitstream_get(1);
+ }
+ if((audblk->cplbegf == 0) && audblk->cplinu)
+ for(i = 0; i < 2; i++)
+ audblk->rematflg[i] = bitstream_get(1);
+
+ }
+ }
+
+ if (audblk->cplinu)
+ {
+ /* Get the coupling channel exponent strategy */
+ audblk->cplexpstr = bitstream_get(2);
+ audblk->ncplgrps = (audblk->cplendmant - audblk->cplstrtmant) /
+ (3 << (audblk->cplexpstr-1));
+ }
+
+ for(i = 0; i < bsi->nfchans; i++)
+ audblk->chexpstr[i] = bitstream_get(2);
+
+ /* Get the exponent strategy for lfe channel */
+ if(bsi->lfeon)
+ audblk->lfeexpstr = bitstream_get(1);
+
+ /* Determine the bandwidths of all the fbw channels */
+ for(i = 0; i < bsi->nfchans; i++)
+ {
+ uint_16 grp_size;
+
+ if(audblk->chexpstr[i] != EXP_REUSE)
+ {
+ if (audblk->cplinu && audblk->chincpl[i])
+ {
+ audblk->endmant[i] = audblk->cplstrtmant;
+ }
+ else
+ {
+ audblk->chbwcod[i] = bitstream_get(6);
+ audblk->endmant[i] = ((audblk->chbwcod[i] + 12) * 3) + 37;
+ }
+
+ /* Calculate the number of exponent groups to fetch */
+ grp_size = 3 * (1 << (audblk->chexpstr[i] - 1));
+ audblk->nchgrps[i] = (audblk->endmant[i] - 1 + (grp_size - 3)) / grp_size;
+ }
+ }
+
+ /* Get the coupling exponents if they exist */
+ if(audblk->cplinu && (audblk->cplexpstr != EXP_REUSE))
+ {
+ audblk->cplabsexp = bitstream_get(4);
+ for(i=0;i< audblk->ncplgrps;i++)
+ audblk->cplexps[i] = bitstream_get(7);
+ }
+
+ /* Get the fwb channel exponents */
+ for(i=0;i < bsi->nfchans; i++)
+ {
+ if(audblk->chexpstr[i] != EXP_REUSE)
+ {
+ audblk->exps[i][0] = bitstream_get(4);
+ for(j=1;j<=audblk->nchgrps[i];j++)
+ audblk->exps[i][j] = bitstream_get(7);
+ audblk->gainrng[i] = bitstream_get(2);
+ }
+ }
+
+ /* Get the lfe channel exponents */
+ if(bsi->lfeon && (audblk->lfeexpstr != EXP_REUSE))
+ {
+ audblk->lfeexps[0] = bitstream_get(4);
+ audblk->lfeexps[1] = bitstream_get(7);
+ audblk->lfeexps[2] = bitstream_get(7);
+ }
+
+ /* Get the parametric bit allocation parameters */
+ audblk->baie = bitstream_get(1);
+
+ if(audblk->baie)
+ {
+ audblk->sdcycod = bitstream_get(2);
+ audblk->fdcycod = bitstream_get(2);
+ audblk->sgaincod = bitstream_get(2);
+ audblk->dbpbcod = bitstream_get(2);
+ audblk->floorcod = bitstream_get(3);
+ }
+
+ /* Get the SNR off set info if it exists */
+ audblk->snroffste = bitstream_get(1);
+
+ if(audblk->snroffste)
+ {
+ audblk->csnroffst = bitstream_get(6);
+
+ if(audblk->cplinu)
+ {
+ audblk->cplfsnroffst = bitstream_get(4);
+ audblk->cplfgaincod = bitstream_get(3);
+ }
+
+ for(i = 0;i < bsi->nfchans; i++)
+ {
+ audblk->fsnroffst[i] = bitstream_get(4);
+ audblk->fgaincod[i] = bitstream_get(3);
+ }
+ if(bsi->lfeon)
+ {
+
+ audblk->lfefsnroffst = bitstream_get(4);
+ audblk->lfefgaincod = bitstream_get(3);
+ }
+ }
+
+ /* Get coupling leakage info if it exists */
+ if(audblk->cplinu)
+ {
+ audblk->cplleake = bitstream_get(1);
+
+ if(audblk->cplleake)
+ {
+ audblk->cplfleak = bitstream_get(3);
+ audblk->cplsleak = bitstream_get(3);
+ }
+ }
+
+ /* Get the delta bit alloaction info */
+ audblk->deltbaie = bitstream_get(1);
+
+ if(audblk->deltbaie)
+ {
+ if(audblk->cplinu)
+ audblk->cpldeltbae = bitstream_get(2);
+
+ for(i = 0;i < bsi->nfchans; i++)
+ audblk->deltbae[i] = bitstream_get(2);
+
+ if (audblk->cplinu && (audblk->cpldeltbae == DELTA_BIT_NEW))
+ {
+ audblk->cpldeltnseg = bitstream_get(3);
+ for(i = 0;i < audblk->cpldeltnseg + 1; i++)
+ {
+ audblk->cpldeltoffst[i] = bitstream_get(5);
+ audblk->cpldeltlen[i] = bitstream_get(4);
+ audblk->cpldeltba[i] = bitstream_get(3);
+ }
+ }
+
+ for(i = 0;i < bsi->nfchans; i++)
+ {
+ if (audblk->deltbae[i] == DELTA_BIT_NEW)
+ {
+ audblk->deltnseg[i] = bitstream_get(3);
+ for(j = 0; j < audblk->deltnseg[i] + 1; j++)
+ {
+ audblk->deltoffst[i][j] = bitstream_get(5);
+ audblk->deltlen[i][j] = bitstream_get(4);
+ audblk->deltba[i][j] = bitstream_get(3);
+ }
+ }
+ }
+ }
+
+ /* Check to see if there's any dummy info to get */
+ if((audblk->skiple = bitstream_get(1)))
+ {
+ uint_16 skip_data;
+
+ audblk->skipl = bitstream_get(9);
+ //XXX remove
+ //fprintf(stderr,"(parse) skipping %d bytes\n",audblk->skipl);
+
+ for(i = 0; i < audblk->skipl ; i++)
+ {
+ skip_data = bitstream_get(8);
+ //XXX remove
+ //fprintf(stderr,"skipped data %2x\n",skip_data);
+ //if(skip_data != 0)
+ //{
+ //dprintf("(parse) Invalid skipped data %2x\n",skip_data);
+ //exit(1);
+ //}
+ }
+ }
+
+ stats_print_audblk(bsi,audblk);
+}
+
+void
+parse_auxdata(syncinfo_t *syncinfo)
+{
+ //FIXME keep this now that we don't really need it?
+#if 0
+ int i;
+ int skip_length;
+ uint_16 crc;
+ uint_16 auxdatae;
+
+ skip_length = (syncinfo->frame_size * 16) - bitstream_get_total_bits() - 17 - 1;
+
+ //XXX remove
+ //dprintf("(auxdata) skipping %d auxbits\n",skip_length);
+
+ for(i=0; i < skip_length; i++)
+ //printf("Skipped bit %i\n",(uint_16)bitstream_get(1));
+ bitstream_get(1);
+
+ //get the auxdata exists bit
+ auxdatae = bitstream_get(1);
+
+ //XXX remove
+ //dprintf("auxdatae = %i\n",auxdatae);
+
+ //Skip the CRC reserved bit
+ bitstream_get(1);
+
+ //Get the crc
+ crc = bitstream_get(16);
+#endif
+}
+
+
diff --git a/ac3dec/parse.h b/ac3dec/parse.h
new file mode 100644
index 0000000..cdaee66
--- /dev/null
+++ b/ac3dec/parse.h
@@ -0,0 +1,28 @@
+/*
+ * parse.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+void parse_syncinfo(syncinfo_t *syncinfo,uint_8 *data);
+void parse_audblk(bsi_t *bsi,audblk_t *audblk);
+void parse_bsi(bsi_t *bsi);
+void parse_auxdata(syncinfo_t *syncinfo);
+
diff --git a/ac3dec/rematrix.c b/ac3dec/rematrix.c
new file mode 100644
index 0000000..caa7094
--- /dev/null
+++ b/ac3dec/rematrix.c
@@ -0,0 +1,83 @@
+/*
+ * rematrix.c
+ *
+ * Copyright (C) Aaron Holtzman - July 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+#include "decode.h"
+#include "rematrix.h"
+
+struct rematrix_band_s
+{
+ uint_32 start;
+ uint_32 end;
+};
+
+struct rematrix_band_s rematrix_band[] = { {13,24}, {25,36}, {37 ,60}, {61,252}};
+
+static inline uint_32 min(uint_32 a,uint_32 b);
+
+static inline uint_32
+min(uint_32 a,uint_32 b)
+{
+ return (a < b ? a : b);
+}
+
+/* This routine simply does stereo rematixing for the 2 channel
+ * stereo mode */
+void rematrix(audblk_t *audblk, stream_samples_t samples)
+{
+ uint_32 num_bands;
+ uint_32 start;
+ uint_32 end;
+ uint_32 i,j;
+ float left,right;
+
+ if(!audblk->cplinu || audblk->cplbegf > 2)
+ num_bands = 4;
+ else if (audblk->cplbegf > 0)
+ num_bands = 3;
+ else
+ num_bands = 2;
+
+ for(i=0;i < num_bands; i++)
+ {
+ if(!audblk->rematflg[i])
+ continue;
+
+ start = rematrix_band[i].start;
+ end = min(rematrix_band[i].end ,12 * audblk->cplbegf + 36);
+
+ for(j=start;j < end; j++)
+ {
+ left = samples[0][j] + samples[1][j];
+ right = samples[0][j] - samples[1][j];
+ samples[0][j] = left;
+ samples[1][j] = right;
+ }
+ }
+}
diff --git a/ac3dec/rematrix.h b/ac3dec/rematrix.h
new file mode 100644
index 0000000..0be6528
--- /dev/null
+++ b/ac3dec/rematrix.h
@@ -0,0 +1,25 @@
+/*
+ * rematrix.h
+ *
+ * Copyright (C) Aaron Holtzman - July 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+void rematrix(audblk_t *audblk, stream_samples_t samples);
diff --git a/ac3dec/sanity_check.c b/ac3dec/sanity_check.c
new file mode 100644
index 0000000..461f20e
--- /dev/null
+++ b/ac3dec/sanity_check.c
@@ -0,0 +1,131 @@
+/*
+ * sanity_check.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "ac3.h"
+#include "ac3_internal.h"
+#include "sanity_check.h"
+
+
+void
+sanity_check_init(syncinfo_t *syncinfo, bsi_t *bsi, audblk_t *audblk)
+{
+ syncinfo->magic = AC3_MAGIC_NUMBER;
+ bsi->magic = AC3_MAGIC_NUMBER;
+ audblk->magic1 = AC3_MAGIC_NUMBER;
+ audblk->magic2 = AC3_MAGIC_NUMBER;
+ audblk->magic3 = AC3_MAGIC_NUMBER;
+}
+
+void
+sanity_check(syncinfo_t *syncinfo, bsi_t *bsi, audblk_t *audblk)
+{
+ int i;
+
+ if(syncinfo->magic != AC3_MAGIC_NUMBER)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- syncinfo magic number **");
+ error_flag = 1;
+ }
+
+ if(bsi->magic != AC3_MAGIC_NUMBER)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- bsi magic number **");
+ error_flag = 1;
+ }
+
+ if(audblk->magic1 != AC3_MAGIC_NUMBER)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- audblk magic number 1 **");
+ error_flag = 1;
+ }
+
+ if(audblk->magic2 != AC3_MAGIC_NUMBER)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- audblk magic number 2 **");
+ error_flag = 1;
+ }
+
+ if(audblk->magic3 != AC3_MAGIC_NUMBER)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- audblk magic number 3 **");
+ error_flag = 1;
+ }
+
+ for(i = 0;i < 5 ; i++)
+ {
+ if (audblk->fbw_exp[i][255] !=0 || audblk->fbw_exp[i][254] !=0 ||
+ audblk->fbw_exp[i][253] !=0)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- fbw_exp out of bounds **");
+ error_flag = 1;
+ }
+
+ if (audblk->fbw_bap[i][255] !=0 || audblk->fbw_bap[i][254] !=0 ||
+ audblk->fbw_bap[i][253] !=0)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- fbw_bap out of bounds **");
+ error_flag = 1;
+ }
+
+ }
+
+ if (audblk->cpl_exp[255] !=0 || audblk->cpl_exp[254] !=0 ||
+ audblk->cpl_exp[253] !=0)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- cpl_exp out of bounds **");
+ error_flag = 1;
+ }
+
+ if (audblk->cpl_bap[255] !=0 || audblk->cpl_bap[254] !=0 ||
+ audblk->cpl_bap[253] !=0)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- cpl_bap out of bounds **");
+ error_flag = 1;
+ }
+
+ if (audblk->cplmant[255] !=0 || audblk->cplmant[254] !=0 ||
+ audblk->cplmant[253] !=0)
+ {
+ fprintf(stderr,"\n** Sanity check failed -- cpl_mant out of bounds **");
+ error_flag = 1;
+ }
+
+ if ((audblk->cplinu == 1) && (audblk->cplbegf > (audblk->cplendf+2)))
+ {
+ fprintf(stderr,"\n** Sanity check failed -- cpl params inconsistent **");
+ error_flag = 1;
+ }
+
+ for(i=0; i < bsi->nfchans; i++)
+ {
+ if((audblk->chincpl[i] == 0) && (audblk->chbwcod[i] > 60))
+ {
+ fprintf(stderr,"\n** Sanity check failed -- chbwcod too big **");
+ error_flag = 1;
+ }
+ }
+
+ return;
+}
diff --git a/ac3dec/sanity_check.h b/ac3dec/sanity_check.h
new file mode 100644
index 0000000..c4ca63a
--- /dev/null
+++ b/ac3dec/sanity_check.h
@@ -0,0 +1,27 @@
+/*
+ * sanity_check.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define AC3_MAGIC_NUMBER 0xdeadbeef
+
+void sanity_check_init(syncinfo_t *syncinfo, bsi_t *bsi, audblk_t *audblk);
+void sanity_check(syncinfo_t *syncinfo, bsi_t *bsi, audblk_t *audblk);
diff --git a/ac3dec/stats.c b/ac3dec/stats.c
new file mode 100644
index 0000000..cfeb453
--- /dev/null
+++ b/ac3dec/stats.c
@@ -0,0 +1,178 @@
+/*
+ * stats.c
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+//#include "config.h"
+#include "ac3.h"
+#include "ac3_internal.h"
+
+
+#include "decode.h"
+#include "stats.h"
+#include "debug.h"
+
+
+static const char *service_ids[8] =
+{
+ "CM","ME","VI","HI",
+ "D", "C","E", "VO"
+};
+
+struct mixlev_s
+{
+ float clev;
+ char *desc;
+};
+
+static const struct mixlev_s cmixlev_tbl[4] =
+{
+ {0.707, "(-3.0 dB)"}, {0.595, "(-4.5 dB)"},
+ {0.500, "(-6.0 dB)"}, {1.0, "Invalid"}
+};
+
+static const struct mixlev_s smixlev_tbl[4] =
+{
+ {0.707, "(-3.0 dB)"}, {0.500, "(-6.0 dB)"},
+ { 0.0, "off "}, { 1.0, "Invalid"}
+};
+
+static const char *language[128] =
+{
+ "unknown", "Albanian", "Breton", "Catalan", "Croatian", "Welsh", "Czech", "Danish",
+ "German", "English", "Spanish", "Esperanto", "Estonian", "Basque", "Faroese", "French",
+ "Frisian", "Irish", "Gaelic", "Galician", "Icelandic", "Italian", "Lappish", "Latin",
+ "Latvian", "Luxembourgian", "Lithuanian", "Hungarian", "Maltese", "Dutch", "Norwegian", "Occitan",
+ "Polish", "Portugese", "Romanian", "Romansh", "Serbian", "Slovak", "Slovene", "Finnish",
+ "Swedish", "Turkish", "Flemish", "Walloon", "0x2c", "0x2d", "0x2e", "0x2f",
+ "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
+ "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "0x3f",
+ "background", "0x41", "0x42", "0x43", "0x44", "Zulu", "Vietnamese", "Uzbek",
+ "Urdu", "Ukrainian", "Thai", "Telugu", "Tatar", "Tamil", "Tadzhik", "Swahili",
+ "Sranan Tongo", "Somali", "Sinhalese", "Shona", "Serbo-Croat", "Ruthenian", "Russian", "Quechua",
+ "Pustu", "Punjabi", "Persian", "Papamiento", "Oriya", "Nepali", "Ndebele", "Marathi",
+ "Moldavian", "Malaysian", "Malagasay", "Macedonian", "Laotian", "Korean", "Khmer", "Kazakh",
+ "Kannada", "Japanese", "Indonesian", "Hindi", "Hebrew", "Hausa", "Gurani", "Gujurati",
+ "Greek", "Georgian", "Fulani", "Dari", "Churash", "Chinese", "Burmese", "Bulgarian",
+ "Bengali", "Belorussian", "Bambora", "Azerbijani", "Assamese", "Armenian", "Arabic", "Amharic"
+};
+
+void stats_print_banner(syncinfo_t *syncinfo,bsi_t *bsi)
+{
+ fprintf(stdout,"ac3dec-0.6.2-cvs (C) 2000 Aaron Holtzman (aholtzma@ess.engr.uvic.ca)\n");
+
+ fprintf(stdout,"%d.%d Mode ",bsi->nfchans,bsi->lfeon);
+ fprintf(stdout,"%2.1f KHz",syncinfo->sampling_rate * 1e-3);
+ fprintf(stdout,"%4d kbps ",syncinfo->bit_rate);
+ if (bsi->langcode && (bsi->langcod < 128))
+ fprintf(stdout,"%s ", language[bsi->langcod]);
+
+ switch(bsi->bsmod)
+ {
+ case 0:
+ fprintf(stdout,"Complete Main Audio Service");
+ break;
+ case 1:
+ fprintf(stdout,"Music and Effects Audio Service");
+ case 2:
+ fprintf(stdout,"Visually Impaired Audio Service");
+ break;
+ case 3:
+ fprintf(stdout,"Hearing Impaired Audio Service");
+ break;
+ case 4:
+ fprintf(stdout,"Dialogue Audio Service");
+ break;
+ case 5:
+ fprintf(stdout,"Commentary Audio Service");
+ break;
+ case 6:
+ fprintf(stdout,"Emergency Audio Service");
+ break;
+ case 7:
+ fprintf(stdout,"Voice Over Audio Service");
+ break;
+ }
+ fprintf(stdout,"\n");
+}
+
+void stats_print_syncinfo(syncinfo_t *syncinfo)
+{
+ dprintf("(syncinfo) ");
+
+ switch (syncinfo->fscod)
+ {
+ case 2:
+ dprintf("32 KHz ");
+ break;
+ case 1:
+ dprintf("44.1 KHz ");
+ break;
+ case 0:
+ dprintf("48 KHz ");
+ break;
+ default:
+ dprintf("Invalid sampling rate ");
+ break;
+ }
+
+ dprintf("%4d kbps %4d words per frame\n",syncinfo->bit_rate,
+ syncinfo->frame_size);
+
+}
+
+void stats_print_bsi(bsi_t *bsi)
+{
+ dprintf("(bsi) ");
+ dprintf("%s",service_ids[bsi->bsmod]);
+ dprintf(" %d.%d Mode ",bsi->nfchans,bsi->lfeon);
+ if ((bsi->acmod & 0x1) && (bsi->acmod != 0x1))
+ dprintf(" Centre Mix Level %s ",cmixlev_tbl[bsi->cmixlev].desc);
+ if (bsi->acmod & 0x4)
+ dprintf(" Sur Mix Level %s ",smixlev_tbl[bsi->cmixlev].desc);
+ dprintf("\n");
+
+}
+
+char *exp_strat_tbl[4] = {"R ","D15 ","D25 ","D45 "};
+
+void stats_print_audblk(bsi_t *bsi,audblk_t *audblk)
+{
+ uint_32 i;
+
+ dprintf("(audblk) ");
+ dprintf("%s ",audblk->cplinu ? "cpl on " : "cpl off");
+ dprintf("%s ",audblk->baie? "bai " : " ");
+ dprintf("%s ",audblk->snroffste? "snroffst " : " ");
+ dprintf("%s ",audblk->deltbaie? "deltba " : " ");
+ dprintf("%s ",audblk->phsflginu? "phsflg " : " ");
+ dprintf("(%s %s %s %s %s) ",exp_strat_tbl[audblk->chexpstr[0]],
+ exp_strat_tbl[audblk->chexpstr[1]],exp_strat_tbl[audblk->chexpstr[2]],
+ exp_strat_tbl[audblk->chexpstr[3]],exp_strat_tbl[audblk->chexpstr[4]]);
+ dprintf("[");
+ for(i=0;i<bsi->nfchans;i++)
+ dprintf("%1d",audblk->blksw[i]);
+ dprintf("]");
+
+ dprintf("\n");
+}
diff --git a/ac3dec/stats.h b/ac3dec/stats.h
new file mode 100644
index 0000000..8a9ecb6
--- /dev/null
+++ b/ac3dec/stats.h
@@ -0,0 +1,27 @@
+/*
+ * stats.h
+ *
+ * Copyright (C) Aaron Holtzman - May 1999
+ *
+ * This file is part of ac3dec, a free Dolby AC-3 stream decoder.
+ *
+ * ac3dec is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * ac3dec is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Make; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+void stats_print_syncinfo(syncinfo_t *syncinfo);
+void stats_print_bsi(bsi_t *bsi);
+void stats_print_audblk(bsi_t *bsi,audblk_t *audblk);
+void stats_print_banner(syncinfo_t *syncinfo,bsi_t *bsi);
diff --git a/channels.conf b/channels.conf
index 8fc1a98..1f62f70 100644
--- a/channels.conf
+++ b/channels.conf
@@ -94,7 +94,7 @@ Cinedom 5B:11720:h:0:27500:1791:1792:0:3:177
Cinedom 5C:12070:h:0:27500:1023:1024:0:3:186
:Beta Digital
CNBC:12148:h:0:27500:255:256:0:3:35
-Liberty TV.com:12610:V:0:22000:941:943:0:0:12199
+Liberty TV.com:12610:V:0:22000:941:943,942:0:0:12199
:PW Erotic
Beate-Uhse.TV:11758:h:0:27500:3839:3840:0:3:21
Blue Movie 1:11758:h:0:27500:1791:1792:0:3:513
diff --git a/config.h b/config.h
index 58d0382..bbf5d65 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.55 2001/07/27 13:32:53 kls Exp $
+ * $Id: config.h 1.57 2001/08/06 16:44:38 kls Exp $
*/
#ifndef __CONFIG_H
@@ -19,7 +19,7 @@
#include "eit.h"
#include "tools.h"
-#define VDRVERSION "0.85"
+#define VDRVERSION "0.90"
#define MaxBuffer 10000
diff --git a/dvbapi.c b/dvbapi.c
index 07b6f61..0524f00 100644
--- a/dvbapi.c
+++ b/dvbapi.c
@@ -4,9 +4,14 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.c 1.96 2001/07/29 10:32:50 kls Exp $
+ * DVD support initially written by Andreas Schultz <aschultz@warp10.net>
+ * based on dvdplayer-0.5 by Matjaz Thaler <matjaz.thaler@guest.arnes.si>
+ *
+ * $Id: dvbapi.c 1.101 2001/08/06 16:24:13 kls Exp $
*/
+//#define DVDDEBUG 1
+
#include "dvbapi.h"
#include <dirent.h>
#include <errno.h>
@@ -21,6 +26,13 @@ extern "C" {
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
+
+#ifdef DVDSUPPORT
+extern "C" {
+#include "ac3dec/ac3.h"
+}
+#endif //DVDSUPPORT
+
#include "config.h"
#include "recording.h"
#include "remux.h"
@@ -40,7 +52,8 @@ extern "C" {
// The size of the array used to buffer video data:
// (must be larger than MINVIDEODATA - see remux.h)
-#define VIDEOBUFSIZE (1024*1024)
+#define VIDEOBUFSIZE (1024*1024)
+#define AC3_BUFFER_SIZE (6*1024*16)
// The maximum size of a single frame:
#define MAXFRAMESIZE (192*1024)
@@ -437,7 +450,7 @@ int cFileName::NextFile(void)
// --- cRecordBuffer ---------------------------------------------------------
-class cRecordBuffer : public cRingBuffer {
+class cRecordBuffer : public cRingBufferLinear {
private:
cDvbApi *dvbApi;
cFileName fileName;
@@ -460,7 +473,7 @@ public:
};
cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2)
-:cRingBuffer(VIDEOBUFSIZE, true)
+:cRingBufferLinear(VIDEOBUFSIZE, true)
,fileName(FileName, true)
,remux(VPid, APid1, APid2, DPid1, DPid2, true)
{
@@ -615,70 +628,228 @@ int ReadFrame(int f, uchar *b, int Length, int Max)
return r;
}
-// --- cReplayBuffer ---------------------------------------------------------
+// --- cPlayBuffer ---------------------------------------------------------
-class cReplayBuffer : public cRingBuffer {
-private:
+class cPlayBuffer : public cRingBufferFrame {
+protected:
cDvbApi *dvbApi;
- cIndexFile *index;
- cFileName fileName;
- int fileOffset;
int videoDev, audioDev;
FILE *dolbyDev;
- int replayFile;
- bool eof;
int blockInput, blockOutput;
- bool paused, fastForward, fastRewind;
- int lastIndex, stillIndex, playIndex;
+ bool still, paused, fastForward, fastRewind;
+ int readIndex, writeIndex;
+ bool canDoTrickMode;
bool canToggleAudioTrack;
uchar audioTrack;
+ virtual void Empty(bool Block = false);
+ virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00) {}
+ virtual void Output(void);
+public:
+ cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev);
+ virtual ~cPlayBuffer();
+ virtual void Pause(void);
+ virtual void Play(void);
+ virtual void Forward(void);
+ virtual void Backward(void);
+ virtual int SkipFrames(int Frames) { return -1; }
+ virtual void SkipSeconds(int Seconds) {}
+ virtual void Goto(int Position, bool Still = false) {}
+ virtual void GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { Current = Total = -1; }
+ bool CanToggleAudioTrack(void) { return canToggleAudioTrack; };
+ virtual void ToggleAudioTrack(void);
+ };
+
+cPlayBuffer::cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev)
+:cRingBufferFrame(VIDEOBUFSIZE)
+{
+ dvbApi = DvbApi;
+ videoDev = VideoDev;
+ audioDev = AudioDev;
+ dolbyDev = NULL;
+ blockInput = blockOutput = false;
+ still = paused = fastForward = fastRewind = false;
+ readIndex = writeIndex = -1;
+ canDoTrickMode = false;
+ canToggleAudioTrack = false;
+ audioTrack = 0xC0;
+ if (cDvbApi::AudioCommand()) {
+ dolbyDev = popen(cDvbApi::AudioCommand(), "w");
+ if (!dolbyDev)
+ esyslog(LOG_ERR, "ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand());
+ }
+}
+
+cPlayBuffer::~cPlayBuffer()
+{
+ if (dolbyDev)
+ pclose(dolbyDev);
+}
+
+void cPlayBuffer::Output(void)
+{
+ dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
+
+ while (Busy()) {
+ if (blockOutput) {
+ if (blockOutput > 1)
+ blockOutput = 1;
+ continue;
+ }
+ const cFrame *frame = Get();
+ if (frame) {
+ StripAudioPackets((uchar *)frame->Data(), frame->Count(), (fastForward || fastRewind) ? 0x00 : audioTrack);//XXX
+ for (int i = 0; i < ((paused && fastRewind) ? 24 : 1); i++) { // show every I_FRAME 24 times in slow rewind mode to achieve roughly the same speed as in slow forward mode
+ const uchar *p = frame->Data();
+ int r = frame->Count();
+ while (r > 0 && Busy() && !blockOutput) {
+ cFile::FileReadyForWriting(videoDev, 100);
+ int w = write(videoDev, p, r);
+ if (w > 0) {
+ p += w;
+ r -= w;
+ }
+ else if (w < 0 && errno != EAGAIN) {
+ LOG_ERROR;
+ Stop();
+ return;
+ }
+ }
+ writeIndex = frame->Index();
+ }
+ Drop(frame);
+ }
+ }
+
+ dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
+}
+
+void cPlayBuffer::Empty(bool Block)
+{
+ if (!(blockInput || blockOutput)) {
+ blockInput = blockOutput = 2;
+ EnablePut();
+ EnableGet();
+ time_t t0 = time(NULL);
+ while ((blockInput > 1 || blockOutput > 1) && time(NULL) - t0 < 2)
+ usleep(1);
+ Lock();
+ readIndex = writeIndex;
+ cRingBufferFrame::Clear();
+ CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
+ CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
+ }
+ if (!Block) {
+ blockInput = blockOutput = 0;
+ Unlock();
+ }
+}
+
+void cPlayBuffer::Pause(void)
+{
+ paused = !paused;
+ bool empty = fastForward || fastRewind;
+ if (empty)
+ Empty(true);
+ fastForward = fastRewind = false;
+ CHECK(ioctl(videoDev, paused ? VIDEO_FREEZE : VIDEO_CONTINUE));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, paused));
+ still = false;
+ if (empty)
+ Empty(false);
+}
+
+void cPlayBuffer::Play(void)
+{
+ if (fastForward || fastRewind || paused) {
+ bool empty = !paused || fastRewind;
+ if (empty)
+ Empty(true);
+ still = false;
+ CHECK(ioctl(videoDev, paused ? VIDEO_CONTINUE : VIDEO_PLAY));
+ CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, false));
+ if (empty)
+ Empty(false);
+ fastForward = fastRewind = paused = false;
+ }
+}
+
+void cPlayBuffer::Forward(void)
+{
+ if (canDoTrickMode || paused) {
+ bool empty = !paused || fastRewind;
+ if (empty) {
+ Empty(true);
+ if (fastForward)
+ readIndex -= 150; // this about compensates for the buffered data, so that we don't get too far ahead
+ }
+ still = false;
+ fastForward = !fastForward;
+ fastRewind = false;
+ if (paused)
+ CHECK(ioctl(videoDev, fastForward ? VIDEO_SLOWMOTION : VIDEO_FREEZE, 2));
+ CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastForward));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastForward || paused));
+ if (empty)
+ Empty(false);
+ }
+}
+
+void cPlayBuffer::Backward(void)
+{
+ if (canDoTrickMode) {
+ Empty(true);
+ still = false;
+ fastRewind = !fastRewind;
+ fastForward = false;
+ if (paused)
+ CHECK(ioctl(videoDev, fastRewind ? VIDEO_CONTINUE : VIDEO_FREEZE));
+ CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastRewind));
+ CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastRewind || paused));
+ Empty(false);
+ }
+}
+
+void cPlayBuffer::ToggleAudioTrack(void)
+{
+ if (CanToggleAudioTrack()) {
+ audioTrack = (audioTrack == 0xC0) ? 0xC1 : 0xC0;
+ Empty();
+ }
+}
+
+// --- cReplayBuffer ---------------------------------------------------------
+
+class cReplayBuffer : public cPlayBuffer {
+private:
+ cIndexFile *index;
+ cFileName fileName;
+ int replayFile;
+ bool eof;
bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
- void Clear(bool Block = false);
void Close(void);
- void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
+ virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
void DisplayFrame(uchar *b, int Length);
int Resume(void);
bool Save(void);
protected:
virtual void Input(void);
- virtual void Output(void);
public:
cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName);
virtual ~cReplayBuffer();
- void Pause(void);
- void Play(void);
- void Forward(void);
- void Backward(void);
- int SkipFrames(int Frames);
- void SkipSeconds(int Seconds);
- void Goto(int Position, bool Still = false);
- void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
- bool CanToggleAudioTrack(void) { return canToggleAudioTrack; }
- void ToggleAudioTrack(void);
+ virtual int SkipFrames(int Frames);
+ virtual void SkipSeconds(int Seconds);
+ virtual void Goto(int Position, bool Still = false);
+ virtual void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
};
cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName)
-:cRingBuffer(VIDEOBUFSIZE)
+:cPlayBuffer(DvbApi, VideoDev, AudioDev)
,fileName(FileName, false)
{
- dvbApi = DvbApi;
index = NULL;
- fileOffset = 0;
- videoDev = VideoDev;
- audioDev = AudioDev;
- dolbyDev = NULL;
- if (cDvbApi::AudioCommand()) {
- dolbyDev = popen(cDvbApi::AudioCommand(), "w");
- if (!dolbyDev)
- esyslog(LOG_ERR, "ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand());
- }
replayFile = fileName.Open();
eof = false;
- blockInput = blockOutput = false;
- paused = fastForward = fastRewind = false;
- lastIndex = stillIndex = playIndex = -1;
- canToggleAudioTrack = false;
- audioTrack = 0xC0;
if (!fileName.Name())
return;
// Create the index file:
@@ -690,6 +861,7 @@ cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const
delete index;
index = NULL;
}
+ canDoTrickMode = index != NULL;
dvbApi->SetModeReplay();
Start();
}
@@ -699,8 +871,6 @@ cReplayBuffer::~cReplayBuffer()
Stop();
Save();
Close();
- if (dolbyDev)
- pclose(dolbyDev);
dvbApi->SetModeNormal(false);
delete index;
}
@@ -709,22 +879,23 @@ void cReplayBuffer::Input(void)
{
dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid());
- int ResumeIndex = Resume();
- if (ResumeIndex >= 0)
- isyslog(LOG_INFO, "resuming replay at index %d (%s)", ResumeIndex, IndexToHMSF(ResumeIndex, true));
+ readIndex = Resume();
+ if (readIndex >= 0)
+ isyslog(LOG_INFO, "resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
- int lastIndex = -1;
- int brakeCounter = 0;
uchar b[MAXFRAMESIZE];
while (Busy() && (blockInput || NextFile())) {
- if (!blockInput && stillIndex < 0) {
+ if (blockInput) {
+ if (blockInput > 1)
+ blockInput = 1;
+ continue;
+ }
+ if (!still) {
int r = 0;
if (fastForward && !paused || fastRewind) {
- int Index = (lastIndex >= 0) ? lastIndex : index->Get(fileName.Number(), fileOffset);
uchar FileNumber;
int FileOffset, Length;
- if (!paused || (brakeCounter++ % 24) == 0) // show every I_FRAME 24 times in rmSlowRewind mode to achieve roughly the same speed as in slow forward mode
- Index = index->GetNextIFrame(Index, fastForward, &FileNumber, &FileOffset, &Length);
+ int Index = index->GetNextIFrame(readIndex, fastForward, &FileNumber, &FileOffset, &Length);
if (Index >= 0) {
if (!NextFile(FileNumber, FileOffset))
break;
@@ -734,126 +905,85 @@ void cReplayBuffer::Input(void)
Play();
continue;
}
- lastIndex = Index;
- playIndex = -1;
+ readIndex = Index;
r = ReadFrame(replayFile, b, Length, sizeof(b));
- StripAudioPackets(b, r);
}
else if (index) {
- lastIndex = -1;
- playIndex = (playIndex >= 0) ? playIndex + 1 : index->Get(fileName.Number(), fileOffset);
uchar FileNumber;
int FileOffset, Length;
- if (!(index->Get(playIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
+ readIndex++;
+ if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
break;
r = ReadFrame(replayFile, b, Length, sizeof(b));
- StripAudioPackets(b, r, audioTrack);
}
else // allows replay even if the index file is missing
r = read(replayFile, b, sizeof(b));
if (r > 0) {
- uchar *p = b;
- while (r > 0 && Busy() && !blockInput) {
- int w = Put(p, r);
- p += w;
- r -= w;
- usleep(1); // this keeps the CPU load low
- }
+ cFrame *frame = new cFrame(b, r, readIndex);
+ while (Busy() && !blockInput && !Put(frame))
+ ;
}
- else if (r ==0)
+ else if (r == 0)
eof = true;
else if (r < 0 && errno != EAGAIN) {
LOG_ERROR;
break;
}
}
- else
+ else//XXX
usleep(1); // this keeps the CPU load low
- if (blockInput > 1)
- blockInput = 1;
}
dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
}
-void cReplayBuffer::Output(void)
-{
- dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- while (Busy()) {
- int r = blockOutput ? 0 : Get(b, sizeof(b));
- if (r > 0) {
- uchar *p = b;
- while (r > 0 && Busy() && !blockOutput) {
- cFile::FileReadyForWriting(videoDev, 100);
- int w = write(videoDev, p, r);
- if (w > 0) {
- p += w;
- r -= w;
- fileOffset += w;
- }
- else if (w < 0 && errno != EAGAIN) {
- LOG_ERROR;
- Stop();
- return;
- }
- }
- }
- else
- usleep(1); // this keeps the CPU load low
- if (blockOutput > 1)
- blockOutput = 1;
- }
-
- dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid());
-}
-
void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except)
{
- for (int i = 0; i < Length - 6; i++) {
- if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
- uchar c = b[i + 3];
- int l = b[i + 4] * 256 + b[i + 5] + 6;
- switch (c) {
- case 0xBD: // dolby
- if (Except && dolbyDev) {
- int written = b[i + 8] + 9; // skips the PES header
- int n = l - written;
- while (n > 0) {
- int w = fwrite(&b[i + written], 1, n, dolbyDev);
- if (w < 0) {
- LOG_ERROR;
- break;
+ if (canDoTrickMode) {
+ for (int i = 0; i < Length - 6; i++) {
+ if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
+ uchar c = b[i + 3];
+ int l = b[i + 4] * 256 + b[i + 5] + 6;
+ switch (c) {
+ case 0xBD: // dolby
+ if (Except && dolbyDev) {
+ int written = b[i + 8] + 9; // skips the PES header
+ int n = l - written;
+ while (n > 0) {
+ int w = fwrite(&b[i + written], 1, n, dolbyDev);
+ if (w < 0) {
+ LOG_ERROR;
+ break;
+ }
+ n -= w;
+ written += w;
}
- n -= w;
- written += w;
- }
- }
- // continue with deleting the data - otherwise it disturbs DVB replay
- case 0xC0 ... 0xC1: // audio
- if (c == 0xC1)
- canToggleAudioTrack = true;
- if (!Except || c != Except) {
- int n = l;
- for (int j = i; j < Length && n--; j++)
- b[j] = 0x00;
- }
- break;
- case 0xE0 ... 0xEF: // video
- break;
- default:
- //esyslog(LOG_ERR, "ERROR: unexpected packet id %02X", c);
- l = 0;
- }
- if (l)
- i += l - 1; // the loop increments, too!
+ }
+ // continue with deleting the data - otherwise it disturbs DVB replay
+ case 0xC0 ... 0xC1: // audio
+ if (c == 0xC1)
+ canToggleAudioTrack = true;
+ if (!Except || c != Except) {
+ int n = l;
+ for (int j = i; j < Length && n--; j++)
+ b[j] = 0x00;
+ }
+ break;
+ case 0xE0 ... 0xEF: // video
+ break;
+ default:
+ //esyslog(LOG_ERR, "ERROR: unexpected packet id %02X", c);
+ l = 0;
+ }
+ if (l)
+ i += l - 1; // the loop increments, too!
+ }
+ /*XXX
+ else
+ esyslog(LOG_ERR, "ERROR: broken packet header");
+ XXX*/
}
- /*XXX
- else
- esyslog(LOG_ERR, "ERROR: broken packet header");
- XXX*/
- }
+ }
}
void cReplayBuffer::DisplayFrame(uchar *b, int Length)
@@ -865,87 +995,11 @@ void cReplayBuffer::DisplayFrame(uchar *b, int Length)
CHECK(ioctl(videoDev, VIDEO_STILLPICTURE, &sp));
}
-void cReplayBuffer::Clear(bool Block)
-{
- if (!(blockInput || blockOutput)) {
- blockInput = blockOutput = 2;
- time_t t0 = time(NULL);
- while ((blockInput > 1 || blockOutput > 1) && time(NULL) - t0 < 2)
- usleep(1);
- Lock();
- cRingBuffer::Clear();
- playIndex = -1;
- CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
- CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
- }
- if (!Block) {
- blockInput = blockOutput = 0;
- Unlock();
- }
-}
-
-void cReplayBuffer::Pause(void)
-{
- paused = !paused;
- CHECK(ioctl(videoDev, paused ? VIDEO_FREEZE : VIDEO_CONTINUE));
- if (fastForward || fastRewind) {
- if (paused)
- Clear();
- fastForward = fastRewind = false;
- }
- CHECK(ioctl(audioDev, AUDIO_SET_MUTE, paused));
- stillIndex = -1;
-}
-
-void cReplayBuffer::Play(void)
-{
- if (fastForward || fastRewind || paused) {
- if (!paused)
- Clear();
- stillIndex = -1;
- CHECK(ioctl(videoDev, paused ? VIDEO_CONTINUE : VIDEO_PLAY));
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(audioDev, AUDIO_SET_MUTE, false));
- fastForward = fastRewind = paused = false;
- }
-}
-
-void cReplayBuffer::Forward(void)
-{
- if (index || paused) {
- if (!paused)
- Clear(true);
- stillIndex = -1;
- fastForward = !fastForward;
- fastRewind = false;
- if (paused)
- CHECK(ioctl(videoDev, fastForward ? VIDEO_SLOWMOTION : VIDEO_FREEZE, 2));
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastForward));
- CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastForward || paused));
- if (!paused)
- Clear(false);
- }
-}
-
-void cReplayBuffer::Backward(void)
-{
- if (index) {
- Clear(true);
- stillIndex = -1;
- fastRewind = !fastRewind;
- fastForward = false;
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, !fastRewind));
- CHECK(ioctl(audioDev, AUDIO_SET_MUTE, fastRewind || paused));
- Clear(false);
- }
-}
-
void cReplayBuffer::Close(void)
{
if (replayFile >= 0) {
fileName.Close();
replayFile = -1;
- fileOffset = 0;
}
}
@@ -966,7 +1020,7 @@ int cReplayBuffer::Resume(void)
bool cReplayBuffer::Save(void)
{
if (index) {
- int Index = index->Get(fileName.Number(), fileOffset);
+ int Index = writeIndex;
if (Index >= 0) {
Index -= RESUMEBACKUP;
if (Index > 0)
@@ -995,8 +1049,8 @@ int cReplayBuffer::SkipFrames(int Frames)
void cReplayBuffer::SkipSeconds(int Seconds)
{
if (index && Seconds) {
- Clear(true);
- int Index = index->Get(fileName.Number(), fileOffset);
+ Empty(true);
+ int Index = writeIndex;
if (Index >= 0) {
if (Seconds < 0) {
int sec = index->Last() / FRAMESPERSEC;
@@ -1008,10 +1062,9 @@ void cReplayBuffer::SkipSeconds(int Seconds)
Index = 1; // not '0', to allow GetNextIFrame() below to work!
uchar FileNumber;
int FileOffset;
- if (index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) >= 0)
- NextFile(FileNumber, FileOffset);
+ readIndex = writeIndex = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset) - 1; // Input() will first increment it!
}
- Clear(false);
+ Empty(false);
Play();
}
}
@@ -1019,7 +1072,7 @@ void cReplayBuffer::SkipSeconds(int Seconds)
void cReplayBuffer::Goto(int Index, bool Still)
{
if (index) {
- Clear(true);
+ Empty(true);
if (paused)
CHECK(ioctl(videoDev, VIDEO_CONTINUE));
if (++Index <= 0)
@@ -1028,28 +1081,27 @@ void cReplayBuffer::Goto(int Index, bool Still)
int FileOffset, Length;
Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
- stillIndex = Index;
- playIndex = -1;
+ still = true;
uchar b[MAXFRAMESIZE];
int r = ReadFrame(replayFile, b, Length, sizeof(b));
if (r > 0)
DisplayFrame(b, r);
- fileOffset += Length;
paused = true;
}
else
- stillIndex = playIndex = -1;
- Clear(false);
+ still = false;
+ readIndex = writeIndex = Index;
+ Empty(false);
}
}
void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{
if (index) {
- if (stillIndex >= 0)
- Current = stillIndex;
+ if (still)
+ Current = readIndex;
else {
- Current = index->Get(fileName.Number(), fileOffset);
+ Current = writeIndex;
if (SnapToIFrame) {
int i1 = index->GetNextIFrame(Current + 1, false);
int i2 = index->GetNextIFrame(Current, true);
@@ -1064,10 +1116,8 @@ void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
{
- if (FileNumber > 0) {
- fileOffset = FileOffset;
+ if (FileNumber > 0)
replayFile = fileName.SetOffset(FileNumber, FileOffset);
- }
else if (replayFile >= 0 && eof) {
Close();
replayFile = fileName.NextFile();
@@ -1076,17 +1126,892 @@ bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
return replayFile >= 0;
}
-void cReplayBuffer::ToggleAudioTrack(void)
+#ifdef DVDSUPPORT
+// --- cDVDplayBuffer --------------------------------------------------------
+
+class cDVDplayBuffer : public cPlayBuffer {
+private:
+ uchar audioTrack;
+
+ cDVD *dvd;//XXX necessary???
+
+ int titleid;
+ int chapid;
+ int angle;
+ dvd_file_t *title;
+ ifo_handle_t *vmg_file;
+ ifo_handle_t *vts_file;
+
+ int doplay;
+ int cyclestate;
+ int prevcycle;
+ int brakeCounter;
+ int skipCnt;
+
+ tt_srpt_t *tt_srpt;
+ vts_ptt_srpt_t *vts_ptt_srpt;
+ pgc_t *cur_pgc;
+ dsi_t dsi_pack;
+ unsigned int next_vobu;
+ unsigned int prev_vobu;
+ unsigned int next_ilvu_start;
+ unsigned int cur_output_size;
+ unsigned int min_output_size;
+ unsigned int pktcnt;
+ int pgc_id;
+ int start_cell;
+ int next_cell;
+ int prev_cell;
+ int cur_cell;
+ unsigned int cur_pack;
+ int ttn;
+ int pgn;
+
+ uchar *data;
+
+ int logAudioTrack;
+ int maxAudioTrack;
+
+ ac3_config_t ac3_config;
+ enum { AC3_STOP, AC3_START, AC3_PLAY } ac3stat;
+ uchar *ac3data;
+ int ac3inp;
+ int ac3outp;
+ int lpcm_count;
+ int is_nav_pack(unsigned char *buffer);
+ void Close(void);
+ virtual void Empty(bool Block = false);
+ int decode_packet(unsigned char *sector, int iframe);
+ int ScanVideoPacket(const uchar *Data, int Count, uchar *PictureType);
+ bool PacketStart(uchar **Data, int len);
+ int GetPacketType(const uchar *Data);
+ int GetStuffingLen(const uchar *Data);
+ int GetPacketLength(const uchar *Data);
+ int GetPESHeaderLength(const uchar *Data);
+ int SendPCM(int size);
+ void playDecodedAC3(void);
+ void handleAC3(unsigned char *sector, int length);
+ void putFrame(unsigned char *sector, int length);
+ unsigned int getAudioStream(unsigned int StreamId);
+ void setChapid(void);
+ void NextState(int State) { prevcycle = cyclestate; cyclestate = State; }
+protected:
+ virtual void Input(void);
+public:
+ cDVDplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, cDVD *DvD, int title);
+ virtual ~cDVDplayBuffer();
+ virtual int SkipFrames(int Frames);
+ virtual void SkipSeconds(int Seconds);
+ virtual void Goto(int Position, bool Still = false);
+ virtual void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
+ virtual void ToggleAudioTrack(void);
+ };
+
+#define cOPENDVD 0
+#define cOPENTITLE 1
+#define cOPENCHAPTER 2
+#define cOUTCELL 3
+#define cREADFRAME 4
+#define cOUTPACK 5
+#define cOUTFRAMES 6
+
+#define aAC3 0x80
+#define aLPCM 0xA0
+
+cDVDplayBuffer::cDVDplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, cDVD *DvD, int title)
+:cPlayBuffer(DvbApi, VideoDev, AudioDev)
+{
+ dvd = DvD;
+ titleid = title;
+ chapid = 0;
+ angle = 0;
+ cyclestate = cOPENDVD;
+ prevcycle = 0;
+ brakeCounter = 0;
+ skipCnt = 0;
+ logAudioTrack = 0;
+ canToggleAudioTrack = true;//XXX determine from cDVD!
+ ac3_config.num_output_ch = 2;
+ // ac3_config.flags = /* mm_accel() | */ MM_ACCEL_MLIB;
+ ac3_config.flags = 0;
+ ac3_init(&ac3_config);
+ data = new uchar[1024 * DVD_VIDEO_LB_LEN];
+ ac3data = new uchar[AC3_BUFFER_SIZE];
+ ac3inp = ac3outp = 0;
+ ac3stat = AC3_START;
+ canDoTrickMode = true;
+ dvbApi->SetModeReplay();
+ Start();
+}
+
+cDVDplayBuffer::~cDVDplayBuffer()
{
- if (CanToggleAudioTrack()) {
- audioTrack = (audioTrack == 0xC0) ? 0xC1 : 0xC0;
- Clear();
+ Stop();
+ Close();
+ dvbApi->SetModeNormal(false);
+ delete ac3data;
+ delete data;
+}
+
+unsigned int cDVDplayBuffer::getAudioStream(unsigned int StreamId)
+{
+ unsigned int trackID;
+
+ if ((cyclestate < cOPENCHAPTER) || (StreamId > 7))
+ return 0;
+ if (!(cur_pgc->audio_control[StreamId] & 0x8000))
+ return 0;
+ int track = (cur_pgc->audio_control[StreamId] >> 8) & 0x07;
+ switch (vts_file->vtsi_mat->vts_audio_attr[track].audio_format) {
+ case 0: // ac3
+ trackID = aAC3;
+ break;
+ case 2: // mpeg1
+ case 3: // mpeg2ext
+ case 4: // lpcm
+ case 6: // dts
+ trackID = aLPCM;
+ break;
+ default: esyslog(LOG_ERR, "ERROR: unknown Audio stream info");
+ return 0;
+ }
+ trackID |= track;
+ return trackID;
+}
+
+void cDVDplayBuffer::ToggleAudioTrack(void)
+{
+ unsigned int newTrack;
+
+ if (CanToggleAudioTrack() && maxAudioTrack != 0) {
+ logAudioTrack = (logAudioTrack + 1) % maxAudioTrack;
+ if ((newTrack = getAudioStream(logAudioTrack)) != 0)
+ audioTrack = newTrack;
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVB: Audio Stream ID changed to: %x", audioTrack);
+#endif
+ ac3stat = AC3_START;
+ ac3outp = ac3inp;
+ }
+}
+
+/**
+ * Returns true if the pack is a NAV pack. This check is clearly insufficient,
+ * and sometimes we incorrectly think that valid other packs are NAV packs. I
+ * need to make this stronger.
+ */
+inline int cDVDplayBuffer::is_nav_pack(unsigned char *buffer)
+{
+ return buffer[41] == 0xbf && buffer[1027] == 0xbf;
+}
+
+void cDVDplayBuffer::Input(void)
+{
+ dsyslog(LOG_INFO, "input thread started (pid=%d)", getpid());
+
+ doplay = true;
+ while (Busy() && doplay) {
+ if (blockInput) {
+ if (blockInput > 1)
+ blockInput = 1;
+ continue;
+ }
+
+ //BEGIN: ripped from play_title
+
+ /**
+ * Playback by cell in this pgc, starting at the cell for our chapter.
+ */
+
+ //dsyslog(LOG_INFO, "DVD: cyclestate: %d", cyclestate);
+ switch (cyclestate) {
+
+ case cOPENDVD: // open the DVD and get all the basic information
+ {
+ if (!dvd->isValid()) {
+ doplay = false;
+ break;
+ }
+
+ /**
+ * Load the video manager to find out the information about the titles on
+ * this disc.
+ */
+ vmg_file = dvd->openVMG();
+ if (!vmg_file) {
+ esyslog(LOG_ERR, "ERROR: can't open VMG info");
+ doplay = false;
+ break;
+ }
+ tt_srpt = vmg_file->tt_srpt;
+
+ NextState(cOPENTITLE);
+ break;
+ }
+
+ case cOPENTITLE: // open the selected title
+ {
+ /**
+ * Make sure our title number is valid.
+ */
+ isyslog(LOG_INFO, "DVD: there are %d titles on this DVD", tt_srpt->nr_of_srpts);
+ if (titleid < 0 || titleid >= tt_srpt->nr_of_srpts) {
+ esyslog(LOG_ERR, "ERROR: invalid title %d", titleid + 1);
+ doplay = false;
+ break;
+ }
+
+ /**
+ * Load the VTS information for the title set our title is in.
+ */
+ vts_file = dvd->openVTS(tt_srpt->title[titleid].title_set_nr);
+ if (!vts_file) {
+ esyslog(LOG_ERR, "ERROR: can't open the title %d info file", tt_srpt->title[titleid].title_set_nr);
+ doplay = false;
+ break;
+ }
+
+ NextState(cOPENCHAPTER);
+ break;
+ }
+
+ case cOPENCHAPTER:
+ {
+ /**
+ * Make sure the chapter number is valid for this title.
+ */
+ isyslog(LOG_INFO, "DVD: there are %d chapters in this title", tt_srpt->title[titleid].nr_of_ptts);
+ if (chapid < 0 || chapid >= tt_srpt->title[titleid].nr_of_ptts) {
+ esyslog(LOG_ERR, "ERROR: invalid chapter %d", chapid + 1);
+ doplay = false;
+ break;
+ }
+
+ /**
+ * Determine which program chain we want to watch. This is based on the
+ * chapter number.
+ */
+ ttn = tt_srpt->title[titleid].vts_ttn;
+ vts_ptt_srpt = vts_file->vts_ptt_srpt;
+ pgc_id = vts_ptt_srpt->title[ttn - 1].ptt[chapid].pgcn;
+ pgn = vts_ptt_srpt->title[ttn - 1].ptt[chapid].pgn;
+ cur_pgc = vts_file->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
+ start_cell = cur_pgc->program_map[pgn - 1] - 1;
+
+ /**
+ * setup Audio information
+ **/
+ for (maxAudioTrack = 0; maxAudioTrack < 8; maxAudioTrack++) {
+ if (!(cur_pgc->audio_control[maxAudioTrack] & 0x8000))
+ break;
+ }
+ canToggleAudioTrack = (maxAudioTrack > 0);
+ // init the AudioInformation
+ audioTrack = getAudioStream(logAudioTrack);
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: max: %d, track: %x", maxAudioTrack, audioTrack);
+#endif
+
+ /**
+ * We've got enough info, time to open the title set data.
+ */
+ title = dvd->openTitle(tt_srpt->title[titleid].title_set_nr, DVD_READ_TITLE_VOBS);
+ if (!title) {
+ esyslog(LOG_ERR, "ERROR: can't open title VOBS (VTS_%02d_1.VOB).", tt_srpt->title[titleid].title_set_nr);
+ doplay = false;
+ break;
+ }
+
+ /**
+ * Playback by cell in this pgc, starting at the cell for our chapter.
+ */
+ next_cell = start_cell;
+ prev_cell = start_cell;
+ cur_cell = start_cell;
+
+ NextState(cOUTCELL);
+ break;
+ }
+
+ case cOUTCELL:
+ {
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: new cell: %d", cur_cell);
+ dsyslog(LOG_INFO, "DVD: vob_id: %x, cell_nr: %x", cur_pgc->cell_position[cur_cell].vob_id_nr, cur_pgc->cell_position[cur_cell].cell_nr);
+#endif
+
+ if (cur_cell < 0) {
+ cur_cell = 0;
+ Backward();
+ }
+ doplay = (cur_cell < cur_pgc->nr_of_cells);
+ if (!doplay)
+ break;
+
+ /* Check if we're entering an angle block. */
+ if (cur_pgc->cell_playback[cur_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK) {
+ cur_cell += angle;
+ for (int i = 0; ; ++i) {
+ if (cur_pgc->cell_playback[cur_cell + i].block_mode == BLOCK_MODE_LAST_CELL) {
+ next_cell = cur_cell + i + 1;
+ break;
+ }
+ }
+ }
+ else {
+ next_cell = cur_cell + 1;
+ prev_cell = cur_cell - 1;
+ }
+
+ // init settings for next state
+ if (!fastRewind)
+ cur_pack = cur_pgc->cell_playback[cur_cell].first_sector;
+ else
+ cur_pack = cur_pgc->cell_playback[cur_cell].last_vobu_start_sector;
+
+ NextState(cOUTPACK);
+ break;
+ }
+
+ case cOUTPACK:
+ {
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: new pack: %d", cur_pack);
+#endif
+ /**
+ * We loop until we're out of this cell.
+ */
+
+ if (!fastRewind) {
+ if (cur_pack >= cur_pgc->cell_playback[cur_cell].last_sector) {
+ cur_cell = next_cell;
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: end of pack");
+#endif
+ NextState(cOUTCELL);
+ break;
+ }
+ }
+ else {
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: prev: %d, curr: %x, next: %x, prev: %x", prevcycle, cur_pack, next_vobu, prev_vobu);
+#endif
+ if ((cur_pack & 0x80000000) != 0) {
+ cur_cell = prev_cell;
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: start of pack");
+#endif
+ NextState(cOUTCELL);
+ break;
+ }
+ }
+
+ /**
+ * Read NAV packet.
+ */
+ int len = DVDReadBlocks(title, cur_pack, 1, data);
+ if (len == 0) {
+ esyslog(LOG_ERR, "ERROR: read failed for block %d", cur_pack);
+ doplay = false;
+ break;
+ }
+ if (!is_nav_pack(data)) {
+ esyslog(LOG_ERR, "ERROR: no nav_pack");
+ return;
+ }
+
+ /**
+ * Parse the contained dsi packet.
+ */
+ navRead_DSI(&dsi_pack, &(data[DSI_START_BYTE]), sizeof(dsi_t));
+ if (cur_pack != dsi_pack.dsi_gi.nv_pck_lbn) {
+ esyslog(LOG_ERR, "ERROR: cur_pack != dsi_pack.dsi_gi.nv_pck_lbn");
+ return;
+ }
+ // navPrint_DSI(&dsi_pack);
+
+ /**
+ * Determine where we go next. These values are the ones we mostly
+ * care about.
+ */
+ next_ilvu_start = cur_pack + dsi_pack.sml_agli.data[angle].address;
+ cur_output_size = dsi_pack.dsi_gi.vobu_ea;
+ min_output_size = dsi_pack.dsi_gi.vobu_1stref_ea;
+
+ /**
+ * If we're not at the end of this cell, we can determine the next
+ * VOBU to display using the VOBU_SRI information section of the
+ * DSI. Using this value correctly follows the current angle,
+ * avoiding the doubled scenes in The Matrix, and makes our life
+ * really happy.
+ *
+ * Otherwise, we set our next address past the end of this cell to
+ * force the code above to go to the next cell in the program.
+ */
+ if (dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL)
+ next_vobu = cur_pack + (dsi_pack.vobu_sri.next_vobu & 0x7fffffff);
+ else
+ next_vobu = cur_pack + cur_output_size + 1;
+
+ if (dsi_pack.vobu_sri.prev_vobu != SRI_END_OF_CELL)
+ prev_vobu = cur_pack - (dsi_pack.vobu_sri.prev_vobu & 0x7fffffff);
+ else {
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: cur: %x, prev: %x", cur_pack, dsi_pack.vobu_sri.prev_vobu);
+#endif
+ prev_vobu = 0x80000000;
+ }
+
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: curr: %x, next: %x, prev: %x", cur_pack, next_vobu, prev_vobu);
+#endif
+ if (cur_output_size >= 1024) {
+ esyslog(LOG_ERR, "ERROR: cur_output_size >= 1024");
+ return;
+ }
+ cur_pack++;
+
+ NextState(cREADFRAME);
+ break;
+ }
+
+ case cREADFRAME:
+ {
+ int trickMode = (fastForward && !paused || fastRewind);
+
+ /* FIXME:
+ * the entire trickMode code relies on the assumtion
+ * that there is only one I-FRAME per PACK
+ *
+ * I have no clue wether that is correct or not !!!
+ */
+ if (trickMode && (skipCnt++ % 4 != 0)) {
+ cur_pack = (!fastRewind) ? next_vobu : prev_vobu;
+ NextState(cOUTPACK);
+ break;
+ }
+
+ if (trickMode)
+ cur_output_size = min_output_size;
+
+ /**
+ * Read in cursize packs.
+ */
+#ifdef DVDDEBUG
+ dsyslog(LOG_INFO, "DVD: read pack: %d", cur_pack);
+#endif
+ int len = DVDReadBlocks(title, cur_pack, cur_output_size, data);
+ if (len != (int)cur_output_size * DVD_VIDEO_LB_LEN) {
+ esyslog(LOG_ERR, "ERROR: read failed for %d blocks at %d", cur_output_size, cur_pack);
+ doplay = false;
+ break;
+ }
+ pktcnt = 0;
+ NextState(cOUTFRAMES);
+ break;
+ }
+
+ case cOUTFRAMES:
+ {
+ int trickMode = (fastForward && !paused || fastRewind);
+
+ /**
+ * Output cursize packs.
+ */
+ if (pktcnt >= cur_output_size) {
+ cur_pack = next_vobu;
+ NextState(cOUTPACK);
+ break;
+ }
+ //dsyslog(LOG_INFO, "DVD: pack: %d, frame: %d", cur_pack, pktcnt);
+
+ if (decode_packet(&data[pktcnt * DVD_VIDEO_LB_LEN], trickMode) != 1) { //we've got a video packet
+ if (trickMode) {
+ //dsyslog(LOG_INFO, "DVD: did pack: %d", pktcnt);
+ cur_pack = (!fastRewind) ? next_vobu : prev_vobu;
+ NextState(cOUTPACK);
+ break;
+ }
+ }
+
+ pktcnt++;
+
+ if (pktcnt >= cur_output_size) {
+ cur_pack = next_vobu;
+ NextState(cOUTPACK);
+ break;
+ }
+ break;
+ }
+
+ default:
+ {
+ esyslog(LOG_ERR, "ERROR: cyclestate %d not known", cyclestate);
+ return;
+ }
+ }
+
+ // dsyslog(LOG_INF, "DVD: new cyclestate: %d, pktcnt: %d, cur: %d", cyclestate, pktcnt, cur_output_size);
+ }
+
+ dsyslog(LOG_INFO, "input thread ended (pid=%d)", getpid());
+}
+
+#define NO_PICTURE 0
+#define SC_PICTURE 0x00
+
+inline bool cDVDplayBuffer::PacketStart(uchar **Data, int len)
+{
+ while (len > 6 && !((*Data)[0] == 0x00 && (*Data)[1] == 0x00 && (*Data)[2] == 0x01))
+ (*Data)++;
+ return ((*Data)[0] == 0x00 && (*Data)[1] == 0x00 && (*Data)[2] == 0x01);
+}
+
+inline int cDVDplayBuffer::GetPacketType(const uchar *Data)
+{
+ return Data[3];
+}
+
+inline int cDVDplayBuffer::GetStuffingLen(const uchar *Data)
+{
+ return Data[13] & 0x07;
+}
+
+inline int cDVDplayBuffer::GetPacketLength(const uchar *Data)
+{
+ return (Data[4] << 8) + Data[5] + 6;
+}
+
+inline int cDVDplayBuffer::GetPESHeaderLength(const uchar *Data)
+{
+ return (Data[8]);
+}
+
+int cDVDplayBuffer::ScanVideoPacket(const uchar *Data, int Count, uchar *PictureType)
+{
+ // Scans the video packet starting at Offset and returns its length.
+ // If the return value is -1 the packet was not completely in the buffer.
+
+ int Length = GetPacketLength(Data);
+ if (Length > 0 && Length <= Count) {
+ int i = 8; // the minimum length of the video packet header
+ i += Data[i] + 1; // possible additional header bytes
+ for (; i < Length; i++) {
+ if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
+ switch (Data[i + 3]) {
+ case SC_PICTURE: *PictureType = (uchar)(Data[i + 5] >> 3) & 0x07;
+ return Length;
+ }
+ }
+ }
+ PictureType = NO_PICTURE;
+ return Length;
+ }
+ return -1;
+}
+
+#define SYSTEM_HEADER 0xBB
+#define PROG_STREAM_MAP 0xBC
+#ifndef PRIVATE_STREAM1
+#define PRIVATE_STREAM1 0xBD
+#endif
+#define PADDING_STREAM 0xBE
+#ifndef PRIVATE_STREAM2
+#define PRIVATE_STREAM2 0xBF
+#endif
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+// data=PCM samples, 16 bit, LSB first, 48kHz, stereo
+int cDVDplayBuffer::SendPCM(int size)
+{
+
+#define MAXSIZE 2032
+
+ uchar buffer[MAXSIZE + 16];
+ int length = 0;
+ int p_size;
+
+ if (ac3inp == ac3outp)
+ return 1;
+
+ while (size > 0) {
+ if (size >= MAXSIZE)
+ p_size = MAXSIZE;
+ else
+ p_size = size;
+ length = 10;
+
+ while (p_size) {
+ if (ac3outp != ac3inp) { // data in the buffer
+ buffer[(length + 6) ^ 1] = ac3data[ac3outp]; // swab because ac3dec delivers wrong byteorder
+ // XXX there is no 'swab' here??? (kls)
+ p_size--;
+ length++;
+ ac3outp = (ac3outp + 1) % AC3_BUFFER_SIZE;
+ }
+ else
+ break;
+ }
+
+ buffer[0] = 0x00;
+ buffer[1] = 0x00;
+ buffer[2] = 0x01;
+ buffer[3] = PRIVATE_STREAM1;
+
+ buffer[4] = (length >> 8) & 0xff;
+ buffer[5] = length & 0xff;
+
+ buffer[6] = 0x80;
+ buffer[7] = 0x00;
+ buffer[8] = 0x00;
+
+ buffer[9] = aLPCM; // substream ID
+ buffer[10] = 0x00; // other stuff (see DVD specs), ignored by driver
+ buffer[11] = 0x00;
+ buffer[12] = 0x00;
+ buffer[13] = 0x00;
+ buffer[14] = 0x00;
+ buffer[15] = 0x00;
+
+ length += 6;
+
+ putFrame(buffer, length);
+ size -= MAXSIZE;
+ }
+ return 0;
+}
+
+void cDVDplayBuffer::playDecodedAC3(void)
+{
+ int ac3_datasize = (AC3_BUFFER_SIZE + ac3inp - ac3outp) % AC3_BUFFER_SIZE;
+
+ if (ac3_datasize) {
+ if (ac3_datasize > 1024 * 48)
+ SendPCM(3096);
+ else if (ac3_datasize > 1024 * 32)
+ SendPCM(1536);
+ else if (ac3_datasize > 1024 * 16 && !(lpcm_count % 2))
+ SendPCM(1536);
+ else if (ac3_datasize && !(lpcm_count % 4))
+ SendPCM(1536);
+ lpcm_count++;
+ }
+ else
+ lpcm_count=0;
+}
+
+void cDVDplayBuffer::handleAC3(unsigned char *sector, int length)
+{
+ if (dolbyDev) {
+ while (length > 0) {
+ int w = fwrite(sector, 1, length , dolbyDev);
+ if (w < 0) {
+ LOG_ERROR;
+ break;
+ }
+ length -= w;
+ sector += w;
+ }
+ }
+ else {
+ if (ac3stat == AC3_PLAY)
+ ac3_decode_data(sector, sector+length, 0, &ac3inp, &ac3outp, (char *)ac3data);
+ else if (ac3stat == AC3_START) {
+ ac3_decode_data(sector, sector+length, 1, &ac3inp, &ac3outp, (char *)ac3data);
+ ac3stat = AC3_PLAY;
+ }
}
+ //playDecodedAC3();
}
+void cDVDplayBuffer::putFrame(unsigned char *sector, int length)
+{
+ cFrame *frame = new cFrame(sector, length);
+ while (Busy() && !blockInput && !Put(frame))
+ ;
+}
+
+int cDVDplayBuffer::decode_packet(unsigned char *sector, int trickMode)
+{
+ uchar pt = 1;
+#if 0
+ uchar *osect = sector;
+#endif
+
+ //make sure we got a PS packet header
+ if (!PacketStart(&sector, DVD_VIDEO_LB_LEN) && GetPacketType(sector) != 0xBA) {
+ esyslog(LOG_ERR, "ERROR: got unexpected packet: %x %x %x %x", sector[0], sector[1], sector[2], sector[3]);
+ return -1;
+ }
+
+ int offset = 14 + GetStuffingLen(sector);
+ sector += offset;
+ int r = DVD_VIDEO_LB_LEN - offset;
+ int datalen = r;
+
+ sector[6] &= 0x8f;
+ uchar *data = sector;
+
+ switch (GetPacketType(sector)) {
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ {
+ ScanVideoPacket(sector, r, &pt);
+ if (trickMode && pt != 1)
+ return pt;
+ break;
+ }
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E: {
+ // no sound in trick mode
+ if (trickMode)
+ return 1;
+ if (audioTrack != GetPacketType(sector))
+ return 5;
+ break;
+ }
+ case PRIVATE_STREAM1:
+ {
+ datalen = GetPacketLength(sector);
+ //skip optional Header bytes
+ datalen -= GetPESHeaderLength(sector);
+ data += GetPESHeaderLength(sector);
+ //skip mandatory header bytes
+ data += 3;
+ //fallthrough is intended
+ }
+ case PRIVATE_STREAM2:
+ {
+ //FIXME: Stream1 + Stream2 is ok, but is Stream2 alone also?
+
+ // no sound in trick mode
+ if (trickMode)
+ return 1;
+
+ // skip PS header bytes
+ data += 6;
+ // data now points to the beginning of the payload
+
+ if (audioTrack == *data) {
+ switch (audioTrack & 0xF8) {
+ case aAC3:
+ data += 4;
+ // correct a3 data lenght - FIXME: why 13 ???
+ datalen -= 13;
+ handleAC3(data, datalen);
+ break;
+ case aLPCM:
+ // write(audio, sector+14 , sector[19]+(sector[18]<<8)+6);
+ putFrame(sector, GetPacketLength(sector));
+ break;
+ default:
+ break;
+ }
+ }
+ return pt;
+ }
+ default:
+ case SYSTEM_HEADER:
+ case PROG_STREAM_MAP:
+ {
+ esyslog(LOG_ERR, "ERROR: don't know what to do - packetType: %x", GetPacketType(sector));
+ // just skip them for now,l but try to debug it
+ dsyslog(LOG_INFO, "DVD: curr cell: %8x, Nr of cells: %8x", cur_cell, cur_pgc->nr_of_cells);
+ dsyslog(LOG_INFO, "DVD: curr pack: %8x, last sector: %8x", cur_pack, cur_pgc->cell_playback[cur_cell].last_sector);
+ dsyslog(LOG_INFO, "DVD: curr pkt: %8x, output size: %8x", pktcnt, cur_output_size);
+#if 0
+ // looks like my DVD is/was brocken .......
+ for (int n = 0; n <= 255; n++) {
+ dsyslog(LOG_INFO, "%4x %2x %2x %2x %2x %2x %2x %2x %2x", n * 8,
+ osect[n * 8 + 0], osect[n * 8 + 1], osect[n * 8 + 2], osect[n * 8 + 3],
+ osect[n * 8 + 4], osect[n * 8 + 5], osect[n * 8 + 6], osect[n * 8 + 7]);
+ }
+ return 0;
+#endif
+ return pt;
+ }
+ }
+ putFrame(sector, r);
+ if ((audioTrack & 0xF8) == aAC3)
+ playDecodedAC3();
+ return pt;
+}
+
+void cDVDplayBuffer::Empty(bool Block)
+{
+ if (!(blockInput || blockOutput)) {
+ cPlayBuffer::Empty(true);
+ ac3stat = AC3_START;
+ ac3outp = ac3inp;
+ }
+ if (!Block)
+ cPlayBuffer::Empty(false);
+}
+
+void cDVDplayBuffer::Close(void)
+{
+ dvd->Close();
+}
+
+int cDVDplayBuffer::SkipFrames(int Frames)
+{
+ return -1;
+}
+
+/* Figure out the correct pgN from the cell and update state. */
+void cDVDplayBuffer::setChapid(void)
+{
+ int new_pgN = 0;
+
+ while (new_pgN < cur_pgc->nr_of_programs && cur_cell >= cur_pgc->program_map[new_pgN])
+ new_pgN++;
+
+ if (new_pgN == cur_pgc->nr_of_programs) { /* We are at the last program */
+ if (cur_cell > cur_pgc->nr_of_cells)
+ chapid = 1; /* We are past the last cell */
+ }
+
+ chapid = new_pgN;
+}
+
+void cDVDplayBuffer::SkipSeconds(int Seconds)
+{
+ if (Seconds) {
+ setChapid();
+ int newchapid = Seconds > 0 ? chapid + 1 : chapid - 1;
+
+ if (newchapid >= 0 && newchapid < tt_srpt->title[titleid].nr_of_ptts) {
+ Empty(true);
+ chapid = newchapid;
+ NextState(cOPENCHAPTER);
+ if (ac3stat != AC3_STOP)
+ ac3stat = AC3_START;
+ ac3outp = ac3inp;
+ Empty(false);
+ Play();
+ }
+ }
+}
+
+void cDVDplayBuffer::Goto(int Index, bool Still)
+{
+}
+
+void cDVDplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
+{
+ Current = Total = -1;
+}
+#endif //DVDSUPPORT
+
// --- cTransferBuffer -------------------------------------------------------
-class cTransferBuffer : public cRingBuffer {
+class cTransferBuffer : public cRingBufferLinear {
private:
cDvbApi *dvbApi;
int fromDevice, toDevice;
@@ -1102,7 +2027,7 @@ public:
};
cTransferBuffer::cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid)
-:cRingBuffer(VIDEOBUFSIZE, true)
+:cRingBufferLinear(VIDEOBUFSIZE, true)
,remux(VPid, APid, 0, 0, 0)
{
dvbApi = DvbApi;
@@ -2212,7 +3137,7 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
esyslog(LOG_ERR, "ERROR %d in qpsk get event", res);
}
else
- esyslog(LOG_ERR, "ERROR: timeout while tuning\n");
+ esyslog(LOG_ERR, "ERROR: timeout while tuning");
}
else if (fd_qamfe >= 0) { // DVB-C
@@ -2239,7 +3164,7 @@ bool cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char Polarization,
esyslog(LOG_ERR, "ERROR %d in qam get event", res);
}
else
- esyslog(LOG_ERR, "ERROR: timeout while tuning\n");
+ esyslog(LOG_ERR, "ERROR: timeout while tuning");
}
else {
esyslog(LOG_ERR, "ERROR: attempt to set channel without DVB-S or DVB-C device");
@@ -2411,6 +3336,36 @@ bool cDvbApi::StartReplay(const char *FileName)
return false;
}
+#ifdef DVDSUPPORT
+bool cDvbApi::StartDVDplay(cDVD *dvd, int TitleID)
+{
+ if (Recording()) {
+ esyslog(LOG_ERR, "ERROR: StartDVDplay() called while recording - ignored!");
+ return false;
+ }
+ StopTransfer();
+ StopReplay();
+ if (fd_video >= 0 && fd_audio >= 0) {
+
+ // Check DeviceName:
+
+ if (!dvd) {
+ esyslog(LOG_ERR, "ERROR: StartDVDplay: DVD device is (null)");
+ return false;
+ }
+
+ // Create replay buffer:
+
+ replayBuffer = new cDVDplayBuffer(this, fd_video, fd_audio, dvd, TitleID);
+ if (replayBuffer)
+ return true;
+ else
+ esyslog(LOG_ERR, "ERROR: can't allocate replaying buffer");
+ }
+ return false;
+}
+#endif //DVDSUPPORT
+
void cDvbApi::StopReplay(void)
{
if (replayBuffer) {
diff --git a/dvbapi.h b/dvbapi.h
index 2203d79..41193ae 100644
--- a/dvbapi.h
+++ b/dvbapi.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbapi.h 1.42 2001/07/27 11:40:38 kls Exp $
+ * $Id: dvbapi.h 1.44 2001/08/05 15:57:45 kls Exp $
*/
#ifndef __DVBAPI_H
@@ -26,7 +26,11 @@
#include <ost/audio.h>
#include <ost/osd.h>
#include <stdio.h>
+
#include "dvbosd.h"
+#ifdef DVDSUPPORT
+#include "dvd.h"
+#endif //DVDSUPPORT
#include "eit.h"
#include "thread.h"
@@ -44,7 +48,11 @@ int HMSFToIndex(const char *HMSF);
class cChannel;
class cRecordBuffer;
+class cPlayBuffer;
class cReplayBuffer;
+#ifdef DVDSUPPORT
+class cDVDplayBuffer;
+#endif //DVDSUPPORT
class cTransferBuffer;
class cCuttingBuffer;
@@ -60,6 +68,9 @@ public:
class cDvbApi {
friend class cRecordBuffer;
friend class cReplayBuffer;
+#ifdef DVDSUPPORT
+ friend class cDVDplayBuffer;
+#endif //DVDSUPPORT
friend class cTransferBuffer;
private:
int videoDev;
@@ -202,7 +213,7 @@ private:
private:
cRecordBuffer *recordBuffer;
- cReplayBuffer *replayBuffer;
+ cPlayBuffer *replayBuffer;
int ca;
int priority;
int Ca(void) { return ca; }
@@ -238,6 +249,10 @@ public:
// Starts replaying the given file.
// If there is already a replay session active, it will be stopped
// and the new file will be played back.
+#ifdef DVDSUPPORT
+ bool StartDVDplay(cDVD *dvd, int TitleID);//XXX dvd parameter necessary???
+ // Starts replaying the given TitleID on the DVD.
+#endif //DVDSUPPORT
void StopReplay(void);
// Stops the current replay session (if any).
void Pause(void);
diff --git a/dvd.c b/dvd.c
new file mode 100644
index 0000000..841e998
--- /dev/null
+++ b/dvd.c
@@ -0,0 +1,148 @@
+/*
+ * dvd.c: Functions for handling DVDs
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * Initially written by Andreas Schultz <aschultz@warp10.net>
+ *
+ * $Id: dvd.c 1.3 2001/08/06 16:07:44 kls Exp $
+ */
+
+#ifdef DVDSUPPORT
+
+//#define DVDSUPPORTDEBUG 1
+//#define DEBUG_BUFFER 1
+
+#include <fcntl.h>
+#include <linux/cdrom.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "dvd.h"
+
+// --- cDVD ----------------------------------------------------------------------------
+
+const char *cDVD::deviceName = "/dev/dvd";
+cDVD *cDVD::dvdInstance = NULL;
+
+cDVD *cDVD::getDVD(void)
+{
+ if (!dvdInstance)
+ new cDVD;
+ return dvdInstance;
+}
+
+cDVD::cDVD(void)
+{
+ dvd = NULL;
+ title = NULL;
+ vmg_file = NULL;
+ vts_file = NULL;
+ dvdInstance = this;
+}
+
+cDVD::~cDVD()
+{
+ Close();
+}
+
+int cDVD::Command(int Cmd)
+{
+ int result = -1;
+ int f;
+ if ((f = open(deviceName, O_RDONLY | O_NONBLOCK)) > 0) {
+ result = ioctl(f, Cmd, 0);
+ close(f);
+ }
+ return result;
+}
+
+void cDVD::SetDeviceName(const char *DeviceName)
+{
+ deviceName = strdup(DeviceName);
+}
+
+const char *cDVD::DeviceName(void)
+{
+ return deviceName;
+}
+
+bool cDVD::DriveExists(void)
+{
+ return access(deviceName, F_OK) == 0;
+}
+
+bool cDVD::DiscOk(void)
+{
+ return Command(CDROM_DRIVE_STATUS) == CDS_DISC_OK;
+}
+
+void cDVD::Eject(void)
+{
+ if (dvdInstance)
+ dvdInstance->Close();
+ Command(CDROMEJECT);
+}
+
+void cDVD::Open(void)
+{
+ if (!dvd)
+ dvd = DVDOpen(deviceName);
+}
+
+void cDVD::Close(void)
+{
+#ifdef DVDSUPPORTDEBUG
+ dsyslog(LOG_INFO, "DVD: cDVD::Close(%p): vts: %p, vmg: %p, title: %p, dvd: %p", this, vts_file, vmg_file, title, dvd);
+#endif
+ if (vts_file)
+ ifoClose(vts_file);
+ if (vmg_file)
+ ifoClose(vmg_file);
+ if (title)
+ DVDCloseFile(title);
+ if (dvd)
+ DVDClose(dvd);
+ vts_file = NULL;
+ vmg_file = NULL;
+ title = NULL;
+ dvd = NULL;
+}
+
+ifo_handle_t *cDVD::openVMG(void)
+{
+ if (!isValid())
+ return NULL;
+ if (!vmg_file)
+ vmg_file = ifoOpen(dvd, 0);
+ return vmg_file;
+}
+
+ifo_handle_t *cDVD::openVTS(int TitleSet)
+{
+ if (!isValid())
+ return NULL;
+ if (vts_file && (titleset != TitleSet)) {
+ ifoClose(vts_file);
+ vts_file = NULL;
+ }
+ if (!vts_file) {
+ titleset = TitleSet;
+ vts_file = ifoOpen(dvd, TitleSet);
+ }
+ return vts_file;
+}
+
+dvd_file_t *cDVD::openTitle(int Title, dvd_read_domain_t domain)
+{
+ if (!isValid())
+ return NULL;
+ if (title)
+ DVDCloseFile(title);
+ title = DVDOpenFile(dvd, Title, domain);
+ return title;
+}
+
+#endif //DVDSUPPORT
diff --git a/dvd.h b/dvd.h
new file mode 100644
index 0000000..68fc1d3
--- /dev/null
+++ b/dvd.h
@@ -0,0 +1,53 @@
+/*
+ * dvd.h: Functions for handling DVDs
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * Initially written by Andreas Schultz <aschultz@warp10.net>
+ *
+ * $Id: dvd.h 1.3 2001/08/05 16:00:57 kls Exp $
+ */
+
+#ifndef __DVD_H
+#define __DVD_H
+
+#ifdef DVDSUPPORT
+
+#include <dvdread/dvd_reader.h>
+#include <dvdread/ifo_types.h>
+#include <dvdread/ifo_read.h>
+#include <dvdread/dvd_udf.h>
+#include <dvdread/nav_read.h>
+#include <dvdread/nav_print.h>
+
+class cDVD {
+private:
+ static cDVD *dvdInstance;
+ static const char *deviceName;
+ dvd_reader_t *dvd;
+ dvd_file_t *title;
+ ifo_handle_t *vmg_file;
+ ifo_handle_t *vts_file;
+ int titleset;
+ static int Command(int Cmd);
+public:
+ cDVD(void);
+ ~cDVD();
+ static void SetDeviceName(const char *DeviceName);
+ static const char *DeviceName(void);
+ static bool DriveExists(void);
+ static bool DiscOk(void);
+ static void Eject(void);
+ void Open(void);
+ void Close(void);
+ bool isValid(void) { return (dvd != NULL); }
+ ifo_handle_t *openVMG(void);
+ ifo_handle_t *openVTS(int TitleSet);
+ dvd_file_t *openTitle(int Title, dvd_read_domain_t domain);
+ static cDVD *getDVD(void);
+ };
+
+#endif //DVDSUPPORT
+
+#endif //__DVD_H
diff --git a/i18n.c b/i18n.c
index 571eea9..ebc366d 100644
--- a/i18n.c
+++ b/i18n.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: i18n.c 1.26 2001/07/27 13:32:43 kls Exp $
+ * $Id: i18n.c 1.27 2001/08/02 14:40:16 kls Exp $
*
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net>
* Italian translations provided by Alberto Carraro <bertocar@tin.it>
@@ -113,6 +113,15 @@ const tPhrase Phrases[] = {
"Enregistrements",
"Opptak",
},
+ { "DVD",
+ "DVD",
+ "DVD",
+ "DVD",
+ "DVD",
+ "DVD",
+ "DVD",
+ "DVD",
+ },
{ "Setup",
"Einstellungen",
"Nastavitve",
@@ -194,7 +203,7 @@ const tPhrase Phrases[] = {
"Prochains programmes",
"Hvilket program er neste?",
},
- // Button texts (must not be more than 10 characters!):
+ // Button texts (should not be more than 10 characters!):
{ "Edit",
"Editieren",
"Uredi",
@@ -321,6 +330,15 @@ const tPhrase Phrases[] = {
"Langue",
"Språk",
},
+ { "Eject DVD",
+ "DVD auswerfen",
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ "", // TODO
+ },
// Confirmations:
{ "Delete channel?",
"Kanal löschen?",
diff --git a/menu.c b/menu.c
index 30532a7..e25b230 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.88 2001/07/28 16:17:28 kls Exp $
+ * $Id: menu.c 1.95 2001/08/05 16:09:41 kls Exp $
*/
#include "menu.h"
@@ -590,7 +590,7 @@ cMenuChannelItem::cMenuChannelItem(int Index, cChannel *Channel)
index = Index;
channel = Channel;
if (channel->groupSep)
- SetColor(clrWhite, clrCyan);
+ SetColor(clrCyan, clrBackground);
Set();
}
@@ -600,7 +600,7 @@ void cMenuChannelItem::Set(void)
if (!channel->groupSep)
asprintf(&buffer, "%d\t%s", channel->number, channel->name );
else
- asprintf(&buffer, "\t%s", channel->name);
+ asprintf(&buffer, "---\t%s ----------------------------------------------------------------", channel->name);
SetText(buffer, false);
}
@@ -1590,6 +1590,80 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
return state;
}
+#ifdef DVDSUPPORT
+// --- cMenuDVDItem ----------------------------------------------------------
+
+class cMenuDVDItem : public cOsdItem {
+ private:
+ int title;
+ int chapters;
+ virtual void Set(void);
+public:
+ cMenuDVDItem(int Title, int Chapters);
+ int Title(void) { return title; }
+ };
+
+cMenuDVDItem::cMenuDVDItem(int Title, int Chapters)
+{
+ title = Title;
+ chapters = Chapters;
+ Set();
+}
+
+void cMenuDVDItem::Set(void)
+{
+ char *buffer = NULL;
+ asprintf(&buffer, " %2d.\tTitle - \t%2d\tChapters", title + 1, chapters);
+ SetText(buffer, false);
+}
+
+// --- cMenuDVD --------------------------------------------------------------
+
+cMenuDVD::cMenuDVD(void)
+:cOsdMenu(tr("DVD"), 5, 8, 3)
+{
+ if ((dvd = cDVD::getDVD())) {
+ dvd->Open();
+ ifo_handle_t *vmg = dvd->openVMG();
+ if (vmg) {
+ dsyslog(LOG_INFO, "DVD: vmg: %p", vmg);//XXX
+ tt_srpt_t *tt_srpt = vmg->tt_srpt;
+ dsyslog(LOG_INFO, "DVD: tt_srpt: %p", tt_srpt);//XXX
+ for (int i = 0; i < tt_srpt->nr_of_srpts; i++)
+ Add(new cMenuDVDItem(i, tt_srpt->title[i].nr_of_ptts));
+ }
+ }
+ SetHelp(tr("Play"), NULL, NULL, NULL);
+ Display();
+}
+
+eOSState cMenuDVD::Play(void)
+{
+ cMenuDVDItem *ri = (cMenuDVDItem *)Get(Current());
+ if (ri) {
+ cReplayControl::SetDVD(dvd, ri->Title());
+ isyslog(LOG_INFO, "DVD: playing title %d", ri->Title());
+ return osReplay;
+ }
+ return osContinue;
+}
+
+eOSState cMenuDVD::ProcessKey(eKeys Key)
+{
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk:
+ case kRed: return Play();
+ case kMenu: return osEnd;
+ default: break;
+ }
+ }
+ return state;
+}
+#endif //DVDSUPPORT
+
// --- cMenuSetup ------------------------------------------------------------
class cMenuSetup : public cOsdMenu {
@@ -1714,23 +1788,21 @@ eOSState cMenuCommands::ProcessKey(eKeys Key)
#define STOP_RECORDING tr(" Stop recording ")
-static const char *hk(int n, const char *s)
-{
- static char buffer[32];
- snprintf(buffer, sizeof(buffer), " %d %s", n, s);
- return buffer;
-}
-
cMenuMain::cMenuMain(bool Replaying)
:cOsdMenu(tr("Main"))
{
- Add(new cOsdItem(hk(1, tr("Schedule")), osSchedule));
- Add(new cOsdItem(hk(2, tr("Channels")), osChannels));
- Add(new cOsdItem(hk(3, tr("Timers")), osTimers));
- Add(new cOsdItem(hk(4, tr("Recordings")), osRecordings));
- Add(new cOsdItem(hk(5, tr("Setup")), osSetup));
+ digit = 0;
+ Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
+ Add(new cOsdItem(hk(tr("Channels")), osChannels));
+ Add(new cOsdItem(hk(tr("Timers")), osTimers));
+ Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
+#ifdef DVDSUPPORT
+ if (cDVD::DriveExists())
+ Add(new cOsdItem(hk(tr("DVD")), osDVD));
+#endif //DVDSUPPORT
+ Add(new cOsdItem(hk(tr("Setup")), osSetup));
if (Commands.Count())
- Add(new cOsdItem(hk(6, tr("Commands")), osCommands));
+ Add(new cOsdItem(hk(tr("Commands")), osCommands));
if (Replaying)
Add(new cOsdItem(tr(" Stop replaying"), osStopReplay));
const char *s = NULL;
@@ -1741,13 +1813,30 @@ cMenuMain::cMenuMain(bool Replaying)
delete buffer;
}
if (cVideoCutter::Active())
- Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit));
- SetHelp(tr("Record"), cDvbApi::PrimaryDvbApi->CanToggleAudioTrack() ? tr("Language") : NULL, NULL, cReplayControl::LastReplayed() ? tr("Resume") : NULL);
+ Add(new cOsdItem(hk(tr(" Cancel editing")), osCancelEdit));
+ const char *DVDbutton =
+#ifdef DVDSUPPORT
+ cDVD::DiscOk() ? tr("Eject DVD") : NULL;
+#else
+ NULL;
+#endif //DVDSUPPORT
+ SetHelp(tr("Record"), cDvbApi::PrimaryDvbApi->CanToggleAudioTrack() ? tr("Language") : NULL, DVDbutton, cReplayControl::LastReplayed() ? tr("Resume") : NULL);
Display();
lastActivity = time(NULL);
SetHasHotkeys();
}
+const char *cMenuMain::hk(const char *s)
+{
+ static char buffer[32];
+ if (digit < 9) {
+ snprintf(buffer, sizeof(buffer), " %d %s", ++digit, s);
+ return buffer;
+ }
+ else
+ return s;
+}
+
eOSState cMenuMain::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key);
@@ -1757,6 +1846,9 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
case osChannels: return AddSubMenu(new cMenuChannels);
case osTimers: return AddSubMenu(new cMenuTimers);
case osRecordings: return AddSubMenu(new cMenuRecordings);
+#ifdef DVDSUPPORT
+ case osDVD: return AddSubMenu(new cMenuDVD);
+#endif //DVDSUPPORT
case osSetup: return AddSubMenu(new cMenuSetup);
case osCommands: return AddSubMenu(new cMenuCommands);
case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
@@ -1773,22 +1865,31 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
}
break;
default: switch (Key) {
- case kMenu: state = osEnd; break;
- case kRed: if (!HasSubMenu())
- state = osRecord;
- break;
- case kGreen: if (!HasSubMenu()) {
- if (cDvbApi::PrimaryDvbApi->CanToggleAudioTrack()) {
- Interface->Clear();
- cDvbApi::PrimaryDvbApi->ToggleAudioTrack();
- state = osEnd;
- }
- }
- break;
- case kBlue: if (!HasSubMenu())
- state = osReplay;
- break;
- default: break;
+ case kMenu: state = osEnd; break;
+ case kRed: if (!HasSubMenu())
+ state = osRecord;
+ break;
+ case kGreen: if (!HasSubMenu()) {
+ if (cDvbApi::PrimaryDvbApi->CanToggleAudioTrack()) {
+ Interface->Clear();
+ cDvbApi::PrimaryDvbApi->ToggleAudioTrack();
+ state = osEnd;
+ }
+ }
+ break;
+#ifdef DVDSUPPORT
+ case kYellow: if (!HasSubMenu()) {
+ if (cDVD::DiscOk()) {
+ cDVD::Eject();
+ state = osEnd;
+ }
+ }
+ break;
+#endif //DVDSUPPORT
+ case kBlue: if (!HasSubMenu())
+ state = osReplay;
+ break;
+ default: break;
}
}
if (Key != kNone)
@@ -2144,6 +2245,10 @@ void cProgressBar::Mark(int x, bool Start, bool Current)
char *cReplayControl::fileName = NULL;
char *cReplayControl::title = NULL;
+#ifdef DVDSUPPORT
+cDVD *cReplayControl::dvd = NULL;//XXX
+int cReplayControl::titleid = 0;//XXX
+#endif //DVDSUPPORT
cReplayControl::cReplayControl(void)
{
@@ -2155,6 +2260,10 @@ cReplayControl::cReplayControl(void)
marks.Load(fileName);
dvbApi->StartReplay(fileName);
}
+#ifdef DVDSUPPORT
+ else if (dvd)
+ dvbApi->StartDVDplay(dvd, titleid);//XXX
+#endif //DVDSUPPORT
}
cReplayControl::~cReplayControl()
@@ -2171,6 +2280,15 @@ void cReplayControl::SetRecording(const char *FileName, const char *Title)
title = Title ? strdup(Title) : NULL;
}
+#ifdef DVDSUPPORT
+void cReplayControl::SetDVD(cDVD *DVD, int Title)//XXX
+{
+ SetRecording(NULL, NULL);
+ dvd = DVD;
+ titleid = Title;
+}
+#endif //DVDSUPPORT
+
const char *cReplayControl::LastReplayed(void)
{
return fileName;
@@ -2216,10 +2334,10 @@ bool cReplayControl::ShowProgress(bool Initial)
}
if (Total != lastTotal) {
Interface->Write(-7, 2, IndexToHMSF(Total));
- Interface->Flush();
- lastTotal = Total;
+ if (!Initial)
+ Interface->Flush();
}
- if (Current != lastCurrent) {
+ if (Current != lastCurrent || Total != lastTotal) {
#ifdef DEBUG_OSD
int p = Width() * Current / Total;
Interface->Fill(0, 1, p, 1, clrGreen);
@@ -2227,12 +2345,14 @@ bool cReplayControl::ShowProgress(bool Initial)
#else
cProgressBar ProgressBar(Width() * dvbApi->CellWidth(), dvbApi->LineHeight(), Current, Total, marks);
Interface->SetBitmap(0, dvbApi->LineHeight(), ProgressBar);
- Interface->Flush();
+ if (!Initial)
+ Interface->Flush();
#endif
Interface->Write(0, 2, IndexToHMSF(Current, displayFrames));
Interface->Flush();
lastCurrent = Current;
}
+ lastTotal = Total;
return true;
}
return false;
@@ -2338,11 +2458,10 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
// Positioning:
case kUp: dvbApi->Play(); break;
case kDown: dvbApi->Pause(); break;
- case kLeft: dvbApi->Backward(); break;
- case kRight: dvbApi->Forward(); break;
case kLeft|k_Release:
+ case kLeft: dvbApi->Backward(); break;
case kRight|k_Release:
- dvbApi->Play(); break;
+ case kRight: dvbApi->Forward(); break;
case kGreen|k_Repeat:
case kGreen: dvbApi->SkipSeconds(-60); break;
case kYellow|k_Repeat:
diff --git a/menu.h b/menu.h
index adc10b7..c47e5cb 100644
--- a/menu.h
+++ b/menu.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.h 1.20 2001/07/28 13:59:29 kls Exp $
+ * $Id: menu.h 1.22 2001/08/05 16:04:58 kls Exp $
*/
#ifndef _MENU_H
@@ -13,17 +13,22 @@
#define _GNU_SOURCE
#include "dvbapi.h"
+#ifdef DVDSUPPORT
+#include "dvd.h"
+#endif //DVDSUPPORT
#include "osd.h"
#include "recording.h"
class cMenuMain : public cOsdMenu {
private:
time_t lastActivity;
+ int digit;
+ const char *hk(const char *s);
public:
cMenuMain(bool Replaying);
virtual eOSState ProcessKey(eKeys Key);
};
-
+
class cDisplayChannel : public cOsdBase {
private:
bool withInfo, group;
@@ -39,6 +44,18 @@ public:
virtual eOSState ProcessKey(eKeys Key);
};
+#ifdef DVDSUPPORT
+class cMenuDVD : public cOsdMenu {
+private:
+ cDVD *dvd;//XXX member really necessary???
+ eOSState Play(void);
+ eOSState Eject(void);
+public:
+ cMenuDVD(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+#endif //DVDSUPPORT
+
class cMenuRecordings : public cOsdMenu {
private:
cRecordings Recordings;
@@ -88,6 +105,10 @@ private:
void Show(int Seconds = 0);
void Hide(void);
static char *fileName;
+#ifdef DVDSUPPORT
+ static cDVD *dvd;//XXX member really necessary???
+ static int titleid;//XXX
+#endif //DVDSUPPORT
static char *title;
bool ShowProgress(bool Initial);
void MarkToggle(void);
@@ -101,6 +122,9 @@ public:
virtual eOSState ProcessKey(eKeys Key);
bool Visible(void) { return visible; }
static void SetRecording(const char *FileName, const char *Title);
+#ifdef DVDSUPPORT
+ static void SetDVD(cDVD *DVD, int Title);//XXX
+#endif //DVDSUPPORT
static const char *LastReplayed(void);
static void ClearLastReplayed(const char *FileName);
};
diff --git a/osd.c b/osd.c
index c29c983..daedc1e 100644
--- a/osd.c
+++ b/osd.c
@@ -4,11 +4,10 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.c 1.16 2001/02/24 16:26:11 kls Exp $
+ * $Id: osd.c 1.17 2001/08/02 14:18:17 kls Exp $
*/
#include "osd.h"
-#include <assert.h>
#include <string.h>
#include "i18n.h"
diff --git a/osd.h b/osd.h
index a1977be..ec1b115 100644
--- a/osd.h
+++ b/osd.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.h 1.22 2001/07/27 11:33:30 kls Exp $
+ * $Id: osd.h 1.23 2001/08/02 13:48:34 kls Exp $
*/
#ifndef __OSD_H
@@ -27,6 +27,7 @@ enum eOSState { osUnknown,
osCommands,
osRecord,
osReplay,
+ osDVD,
osStopRecord,
osStopReplay,
osCancelEdit,
@@ -43,7 +44,7 @@ private:
protected:
bool fresh;
bool userColor;
- eDvbColor fgColor, bgColor;
+ eDvbColor fgColor, bgColor;
public:
cOsdItem(eOSState State = osUnknown);
cOsdItem(const char *Text, eOSState State = osUnknown);
@@ -55,7 +56,7 @@ public:
virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
virtual void Set(void) {}
virtual eOSState ProcessKey(eKeys Key);
- };
+};
class cOsdBase {
protected:
@@ -67,7 +68,7 @@ public:
int Height(void) { return Interface->Height(); }
bool NeedsFastResponse(void) { return needsFastResponse; }
virtual eOSState ProcessKey(eKeys Key) = 0;
- };
+};
class cOsdMenu : public cOsdBase, public cList<cOsdItem> {
private:
diff --git a/ringbuffer.c b/ringbuffer.c
index 8511a1c..7e09b2a 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -7,7 +7,7 @@
* Parts of this file were inspired by the 'ringbuffy.c' from the
* LinuxDVB driver (see linuxtv.org).
*
- * $Id: ringbuffer.c 1.2 2001/05/20 11:58:08 kls Exp $
+ * $Id: ringbuffer.c 1.4 2001/08/05 12:17:45 kls Exp $
*/
#include "ringbuffer.h"
@@ -41,61 +41,128 @@ cRingBuffer::cRingBuffer(int Size, bool Statistics)
{
size = Size;
statistics = Statistics;
- buffer = NULL;
inputThread = NULL;
outputThread = NULL;
+ busy = false;
maxFill = 0;
+}
+
+cRingBuffer::~cRingBuffer()
+{
+ delete inputThread;
+ delete outputThread;
+ if (statistics)
+ dsyslog(LOG_INFO, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
+}
+
+void cRingBuffer::WaitForPut(void)
+{
+ putMutex.Lock();
+ readyForPut.Wait(putMutex);
+ putMutex.Unlock();
+}
+
+void cRingBuffer::WaitForGet(void)
+{
+ getMutex.Lock();
+ readyForGet.Wait(getMutex);
+ getMutex.Unlock();
+}
+
+void cRingBuffer::EnablePut(void)
+{
+ readyForPut.Broadcast();
+}
+
+void cRingBuffer::EnableGet(void)
+{
+ readyForGet.Broadcast();
+}
+
+bool cRingBuffer::Start(void)
+{
+ if (!busy) {
+ busy = true;
+ outputThread = new cRingBufferOutputThread(this);
+ if (!outputThread->Start())
+ DELETENULL(outputThread);
+ inputThread = new cRingBufferInputThread(this);
+ if (!inputThread->Start()) {
+ DELETENULL(inputThread);
+ DELETENULL(outputThread);
+ }
+ busy = outputThread && inputThread;
+ }
+ return busy;
+}
+
+bool cRingBuffer::Active(void)
+{
+ return outputThread && outputThread->Active() && inputThread && inputThread->Active();
+}
+
+void cRingBuffer::Stop(void)
+{
busy = false;
- if (size > 1) { // 'size - 1' must not be 0!
- buffer = new uchar[size];
+ for (time_t t0 = time(NULL) + 3; time(NULL) < t0; ) {
+ if (!((outputThread && outputThread->Active()) || (inputThread && inputThread->Active())))
+ break;
+ }
+ DELETENULL(inputThread);
+ DELETENULL(outputThread);
+}
+
+// --- cRingBufferLinear ----------------------------------------------------
+
+cRingBufferLinear::cRingBufferLinear(int Size, bool Statistics)
+:cRingBuffer(Size, Statistics)
+{
+ buffer = NULL;
+ if (Size > 1) { // 'Size - 1' must not be 0!
+ buffer = new uchar[Size];
if (!buffer)
- esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", size);
+ esyslog(LOG_ERR, "ERROR: can't allocate ring buffer (size=%d)", Size);
Clear();
}
else
- esyslog(LOG_ERR, "ERROR: illegal size for ring buffer (%d)", size);
+ esyslog(LOG_ERR, "ERROR: illegal size for ring buffer (%d)", Size);
}
-cRingBuffer::~cRingBuffer()
+cRingBufferLinear::~cRingBufferLinear()
{
- delete inputThread;
- delete outputThread;
delete buffer;
- if (statistics)
- dsyslog(LOG_INFO, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
}
-int cRingBuffer::Available(void)
+int cRingBufferLinear::Available(void)
{
- mutex.Lock();
+ Lock();
int diff = head - tail;
- int cont = (diff >= 0) ? diff : size + diff;
- mutex.Unlock();
- return cont;
+ Unlock();
+ return (diff >= 0) ? diff : Size() + diff;
}
-void cRingBuffer::Clear(void)
+void cRingBufferLinear::Clear(void)
{
- mutex.Lock();
+ Lock();
head = tail = 0;
- mutex.Unlock();
+ Unlock();
}
-int cRingBuffer::Put(const uchar *Data, int Count)
+int cRingBufferLinear::Put(const uchar *Data, int Count)
{
if (Count > 0) {
- mutex.Lock();
- int rest = size - head;
+ Lock();
+ int rest = Size() - head;
int diff = tail - head;
- mutex.Unlock();
- int free = (diff > 0) ? diff - 1 : size + diff - 1;
+ Unlock();
+ int free = (diff > 0) ? diff - 1 : Size() + diff - 1;
if (statistics) {
- int fill = size - free - 1 + Count;
- if (fill >= size)
- fill = size - 1;
+ int fill = Size() - free - 1 + Count;
+ if (fill >= Size())
+ fill = Size() - 1;
if (fill > maxFill) {
maxFill = fill;
- int percent = maxFill * 100 / (size - 1);
+ int percent = maxFill * 100 / (Size() - 1);
if (percent > 75)
dsyslog(LOG_INFO, "buffer usage: %d%%", percent);
}
@@ -120,14 +187,14 @@ int cRingBuffer::Put(const uchar *Data, int Count)
return Count;
}
-int cRingBuffer::Get(uchar *Data, int Count)
+int cRingBufferLinear::Get(uchar *Data, int Count)
{
if (Count > 0) {
- mutex.Lock();
- int rest = size - tail;
+ Lock();
+ int rest = Size() - tail;
int diff = head - tail;
- mutex.Unlock();
- int cont = (diff >= 0) ? diff : size + diff;
+ Unlock();
+ int cont = (diff >= 0) ? diff : Size() + diff;
if (rest <= 0)
return 0;
if (cont < Count)
@@ -146,36 +213,112 @@ int cRingBuffer::Get(uchar *Data, int Count)
return Count;
}
-bool cRingBuffer::Start(void)
+// --- cFrame ----------------------------------------------------------------
+
+cFrame::cFrame(const uchar *Data, int Count, int Index)
{
- if (!busy) {
- busy = true;
- outputThread = new cRingBufferOutputThread(this);
- if (!outputThread->Start())
- DELETENULL(outputThread);
- inputThread = new cRingBufferInputThread(this);
- if (!inputThread->Start()) {
- DELETENULL(inputThread);
- DELETENULL(outputThread);
+ count = Count;
+ index = Index;
+ data = new uchar[count];
+ if (data)
+ memcpy(data, Data, count);
+ else
+ esyslog(LOG_ERR, "ERROR: can't allocate frame buffer (count=%d)", count);
+ next = NULL;
+}
+
+cFrame::~cFrame()
+{
+ delete data;
+}
+
+// --- cRingBufferFrame ------------------------------------------------------
+
+cRingBufferFrame::cRingBufferFrame(int Size, bool Statistics = false)
+:cRingBuffer(Size, Statistics)
+{
+ head = NULL;
+ currentFill = 0;
+}
+
+cRingBufferFrame::~cRingBufferFrame()
+{
+ Clear();
+}
+
+void cRingBufferFrame::Clear(void)
+{
+ Lock();
+ const cFrame *p;
+ while ((p = Get(false)) != NULL)
+ Drop(p);
+ Unlock();
+ EnablePut();
+ EnableGet();
+}
+
+bool cRingBufferFrame::Put(cFrame *Frame)
+{
+ if (Frame->Count() <= Free()) {
+ Lock();
+ if (head) {
+ Frame->next = head->next;
+ head->next = Frame;
+ head = Frame;
}
- busy = outputThread && inputThread;
+ else {
+ head = Frame->next = Frame;
+ }
+ currentFill += Frame->Count();
+ Unlock();
+ EnableGet();
+ return true;
}
- return busy;
+ WaitForPut();
+ return false;
}
-bool cRingBuffer::Active(void)
+const cFrame *cRingBufferFrame::Get(bool Wait)
{
- return outputThread && outputThread->Active() && inputThread && inputThread->Active();
+ Lock();
+ cFrame *p = head ? head->next : NULL;
+ Unlock();
+ if (!p && Wait)
+ WaitForGet();
+ return p;
}
-void cRingBuffer::Stop(void)
+void cRingBufferFrame::Delete(const cFrame *Frame)
{
- busy = false;
- for (time_t t0 = time(NULL) + 3; time(NULL) < t0; ) {
- if (!((outputThread && outputThread->Active()) || (inputThread && inputThread->Active())))
- break;
- }
- DELETENULL(inputThread);
- DELETENULL(outputThread);
+ currentFill -= Frame->Count();
+ delete Frame;
+}
+
+void cRingBufferFrame::Drop(const cFrame *Frame)
+{
+ Lock();
+ if (head) {
+ if (Frame == head->next) {
+ if (head->next != head) {
+ head->next = Frame->next;
+ Delete(Frame);
+ }
+ else {
+ Delete(head);
+ head = NULL;
+ }
+ }
+ else
+ esyslog(LOG_ERR, "ERROR: attempt to drop wrong frame from ring buffer!");
+ }
+ Unlock();
+ EnablePut();
}
+int cRingBufferFrame::Available(void)
+{
+ Lock();
+ int av = currentFill;
+ Unlock();
+ return av;
+}
diff --git a/ringbuffer.h b/ringbuffer.h
index 49be769..f61d9e0 100644
--- a/ringbuffer.h
+++ b/ringbuffer.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: ringbuffer.h 1.2 2001/05/20 11:56:44 kls Exp $
+ * $Id: ringbuffer.h 1.4 2001/08/05 11:12:06 kls Exp $
*/
#ifndef __RINGBUFFER_H
@@ -24,24 +24,24 @@ private:
cRingBufferInputThread *inputThread;
cRingBufferOutputThread *outputThread;
cMutex mutex;
- int size, head, tail;
- uchar *buffer;
- int maxFill;
+ cCondVar readyForPut, readyForGet;
+ cMutex putMutex, getMutex;
+ int size;
bool busy;
- bool statistics;
protected:
+ int maxFill;//XXX
+ bool statistics;//XXX
+ void WaitForPut(void);
+ void WaitForGet(void);
+ void EnablePut(void);
+ void EnableGet(void);
+ virtual void Clear(void) = 0;
+ virtual int Available(void) = 0;
+ int Free(void) { return size - Available() - 1; }
void Lock(void) { mutex.Lock(); }
void Unlock(void) { mutex.Unlock(); }
- int Available(void);
+ int Size(void) { return size; }
bool Busy(void) { return busy; }
- void Clear(void);
- // Immediately clears the ring buffer.
- int Put(const uchar *Data, int Count);
- // Puts at most Count bytes of Data into the ring buffer.
- // Returns the number of bytes actually stored.
- int Get(uchar *Data, int Count);
- // Gets at most Count bytes of Data from the ring buffer.
- // Returns the number of bytes actually retrieved.
virtual void Input(void) = 0;
// Runs as a separate thread and shall continuously read data from
// a source and call Put() to store the data in the ring buffer.
@@ -56,4 +56,60 @@ public:
void Stop(void);
};
+class cRingBufferLinear : public cRingBuffer {
+private:
+ int head, tail;
+ uchar *buffer;
+protected:
+ virtual int Available(void);
+ virtual void Clear(void);
+ // Immediately clears the ring buffer.
+ int Put(const uchar *Data, int Count);
+ // Puts at most Count bytes of Data into the ring buffer.
+ // Returns the number of bytes actually stored.
+ int Get(uchar *Data, int Count);
+ // Gets at most Count bytes of Data from the ring buffer.
+ // Returns the number of bytes actually retrieved.
+public:
+ cRingBufferLinear(int Size, bool Statistics = false);
+ virtual ~cRingBufferLinear();
+ };
+
+class cFrame {
+ friend class cRingBufferFrame;
+private:
+ cFrame *next;
+ uchar *data;
+ int count;
+ int index;
+public:
+ cFrame(const uchar *Data, int Count, int Index = -1);
+ ~cFrame();
+ const uchar *Data(void) const { return data; }
+ int Count(void) const { return count; }
+ int Index(void) const { return index; }
+ };
+
+class cRingBufferFrame : public cRingBuffer {
+private:
+ cFrame *head;
+ int currentFill;
+ void Delete(const cFrame *Frame);
+protected:
+ virtual int Available(void);
+ virtual void Clear(void);
+ // Immediately clears the ring buffer.
+ bool Put(cFrame *Frame);
+ // Puts the Frame into the ring buffer.
+ // Returns true if this was possible.
+ const cFrame *Get(bool Wait = true);
+ // Gets the next frame from the ring buffer.
+ // The actual data still remains in the buffer until Drop() is called.
+ void Drop(const cFrame *Frame);
+ // Drops the Frame that has just been fetched with Get().
+public:
+ cRingBufferFrame(int Size, bool Statistics = false);
+ virtual ~cRingBufferFrame();
+ };
+
#endif // __RINGBUFFER_H
diff --git a/thread.c b/thread.c
index e253ea0..bfc8aab 100644
--- a/thread.c
+++ b/thread.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.c 1.9 2001/06/27 11:34:41 kls Exp $
+ * $Id: thread.c 1.11 2001/08/05 10:36:52 kls Exp $
*/
#include "thread.h"
@@ -14,6 +14,42 @@
#include <unistd.h>
#include "tools.h"
+// --- cCondVar --------------------------------------------------------------
+
+cCondVar::cCondVar(void)
+{
+ pthread_cond_init(&cond, 0);
+}
+
+cCondVar::~cCondVar()
+{
+ pthread_cond_destroy(&cond);
+}
+
+bool cCondVar::Wait(cMutex &Mutex)
+{
+ return pthread_cond_wait(&cond, &Mutex.mutex);
+}
+
+/*
+bool cCondVar::TimedWait(cMutex &Mutex, unsigned long tmout)
+{
+ return pthread_cond_timedwait(&cond, &Mutex.mutex, tmout);
+}
+*/
+
+void cCondVar::Broadcast(void)
+{
+ pthread_cond_broadcast(&cond);
+}
+
+/*
+void cCondVar::Signal(void)
+{
+ pthread_cond_signal(&cond);
+}
+*/
+
// --- cMutex ----------------------------------------------------------------
cMutex::cMutex(void)
@@ -85,6 +121,7 @@ bool cThread::Start(void)
running = true;
parentPid = getpid();
pthread_create(&thread, NULL, (void *(*) (void *))&StartThread, (void *)this);
+ pthread_setschedparam(thread, SCHED_RR, 0);
usleep(10000); // otherwise calling Active() immediately after Start() causes a "pure virtual method called" error
}
return true; //XXX return value of pthread_create()???
diff --git a/thread.h b/thread.h
index bf9804d..e8e796e 100644
--- a/thread.h
+++ b/thread.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.h 1.6 2001/06/27 11:22:04 kls Exp $
+ * $Id: thread.h 1.8 2001/08/05 10:36:47 kls Exp $
*/
#ifndef __THREAD_H
@@ -13,7 +13,22 @@
#include <pthread.h>
#include <sys/types.h>
+class cMutex;
+
+class cCondVar {
+private:
+ pthread_cond_t cond;
+public:
+ cCondVar(void);
+ ~cCondVar();
+ bool Wait(cMutex &Mutex);
+ //bool TimedWait(cMutex &Mutex, unsigned long tmout);
+ void Broadcast(void);
+ //void Signal(void);
+ };
+
class cMutex {
+ friend class cCondVar;
private:
pthread_mutex_t mutex;
pid_t lockingPid;
diff --git a/tools.c b/tools.c
index de1a52c..c75efbb 100644
--- a/tools.c
+++ b/tools.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.c 1.34 2001/05/20 08:30:54 kls Exp $
+ * $Id: tools.c 1.35 2001/08/05 12:38:06 kls Exp $
*/
#define _GNU_SOURCE
@@ -406,8 +406,8 @@ bool cFile::FileReady(int FileDes, int TimeoutMs)
FD_SET(FileDes, &set);
if (TimeoutMs < 100)
TimeoutMs = 100;
- timeout.tv_sec = 0;
- timeout.tv_usec = TimeoutMs * 1000;
+ timeout.tv_sec = TimeoutMs / 1000;
+ timeout.tv_usec = (TimeoutMs % 1000) * 1000;
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
}
diff --git a/vdr.c b/vdr.c
index 12b1a53..15a69de 100644
--- a/vdr.c
+++ b/vdr.c
@@ -2,27 +2,27 @@
* vdr.c: Video Disk Recorder main program
*
* Copyright (C) 2000 Klaus Schmidinger
- *
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
+ *
* The author can be reached at kls@cadsoft.de
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
- * $Id: vdr.c 1.58 2001/06/23 12:29:41 kls Exp $
+ * $Id: vdr.c 1.61 2001/08/05 16:15:51 kls Exp $
*/
#include <getopt.h>
@@ -31,6 +31,9 @@
#include <unistd.h>
#include "config.h"
#include "dvbapi.h"
+#ifdef DVDSUPPORT
+#include "dvd.h"
+#endif //DVDSUPPORT
#include "i18n.h"
#include "interface.h"
#include "menu.h"
@@ -85,14 +88,15 @@ int main(int argc, char *argv[])
{ "log", required_argument, NULL, 'l' },
{ "port", required_argument, NULL, 'p' },
{ "video", required_argument, NULL, 'v' },
+ { "dvd", required_argument, NULL, 'V' },
{ "watchdog", required_argument, NULL, 'w' },
{ "terminal", required_argument, NULL, 't' },
{ 0 }
};
-
+
int c;
int option_index = 0;
- while ((c = getopt_long(argc, argv, "a:c:dD:hl:p:v:w:t:", long_options, &option_index)) != -1) {
+ while ((c = getopt_long(argc, argv, "a:c:dD:hl:p:v:V:w:t:", long_options, &option_index)) != -1) {
switch (c) {
case 'a': cDvbApi::SetAudioCommand(optarg);
break;
@@ -124,6 +128,7 @@ int main(int argc, char *argv[])
" -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
" 0 turns off SVDRP\n"
" -v DIR, --video=DIR use DIR as video directory (default: %s)\n"
+ " -V DEV, --dvd=DEV use DEV as the DVD device (default: %s)\n"
" -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
" seconds (default: %d); '0' disables the watchdog\n"
" -t TTY, --terminal=TTY controlling tty\n"
@@ -131,6 +136,11 @@ int main(int argc, char *argv[])
"Report bugs to <vdr-bugs@cadsoft.de>\n",
DEFAULTSVDRPPORT,
VideoDirectory,
+#ifdef DVDSUPPORT
+ cDVD::DeviceName(),
+#else
+ "no DVD support",
+#endif //DVDSUPPORT
DEFAULTWATCHDOG
);
return 0;
@@ -158,6 +168,18 @@ int main(int argc, char *argv[])
while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
optarg[strlen(optarg) - 1] = 0;
break;
+ case 'V':
+#ifdef DVDSUPPORT
+ cDVD::SetDeviceName(optarg);
+ if (!cDVD::DriveExists()) {
+ fprintf(stderr, "vdr: DVD drive not found: %s\n", optarg);
+ return 2;
+ }
+#else
+ fprintf(stderr, "vdr: DVD support has not been compiled in!");
+ return 2;
+#endif //DVDSUPPORT
+ break;
case 'w': if (isnumber(optarg)) {
int t = atoi(optarg);
if (t >= 0) {
@@ -173,7 +195,7 @@ int main(int argc, char *argv[])
}
// Log file:
-
+
if (SysLogLevel > 0)
openlog("vdr", LOG_PID | LOG_CONS, LOG_USER);
@@ -324,6 +346,12 @@ int main(int argc, char *argv[])
DELETENULL(ReplayControl);
ReplayControl = new cReplayControl;
break;
+#ifdef DVDSUPPORT
+ case osDVD: DELETENULL(Menu);
+ DELETENULL(ReplayControl);
+ Menu = new cMenuDVD;
+ break;
+#endif //DVDSUPPORT
case osStopReplay:
DELETENULL(*Interact);
DELETENULL(ReplayControl);
@@ -358,7 +386,7 @@ int main(int argc, char *argv[])
case kRight: if (!Interface->Recording()) {
int SaveGroup = CurrentGroup;
if (NORMALKEY(key) == kRight)
- CurrentGroup = Channels.GetNextGroup(CurrentGroup) ;
+ CurrentGroup = Channels.GetNextGroup(CurrentGroup) ;
else
CurrentGroup = Channels.GetPrevGroup(CurrentGroup < 1 ? 1 : CurrentGroup);
if (CurrentGroup < 0)