diff options
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() @@ -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). @@ -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: ------------------------- @@ -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 @@ -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 @@ -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 @@ -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(§or, 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) { @@ -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); @@ -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 @@ -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 @@ -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?", @@ -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: @@ -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); }; @@ -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" @@ -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 @@ -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()??? @@ -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; @@ -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); } @@ -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) |