summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2002-06-16 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2002-06-16 18:00:00 +0200
commitf06d2c27fca449148d9d8fac19d81c668744f170 (patch)
tree8f21a604339719672bf7ab8b6e9d69bfa1817ac5
parentd07e3829f7c44d713b2677ef73f864af80b3dea3 (diff)
downloadvdr-patch-lnbsharing-f06d2c27fca449148d9d8fac19d81c668744f170.tar.gz
vdr-patch-lnbsharing-f06d2c27fca449148d9d8fac19d81c668744f170.tar.bz2
Version 1.1.3vdr-1.1.3
- Improved the VDR Makefile to avoid a warning if the '.dependencies' file does not exist, and also using $(MAKE) to call recursive makes. - Changed the name of the 'package' target in the plugin Makefiles to 'dist' (following the suggestions in the "GNU Make" manual). If you already have started a plugin project, you may want to change this in your Makefile accordingly. - Improved the plugin Makefile to avoid a warning if the '.dependencies' file does not exist, and also using $(shell...) to get the version numbers. If you already have started a plugin project, you may want to change this in your Makefile accordingly. - Fixed some function headers to make them compile with gcc 3.x (thanks to Gregoire Favre). - Fixed the cutting mechanism to make it re-sync in case a frame is larger than the buffer (thanks to Sven Grothklags). - Added an error message if the directory specified in the '-L' option can't be accessed (suggested by Stefan Huelswitt). - Rearranged OSD class names to make 'cOsd' available for the main OSD interface. - Completely moved OSD handling out of the cDvbApi class, into the new cOsd. - Implemented cStatus to allow plugins to set up a status monitor. See PLUGINS.html for details. - Moved the cEITScanner out of dvbapi.h/.c, into the new eitscan.h/.c. - Added Swedish language texts (thanks to Tomas Prybil). - Fixed parsing 'E' records in epg2html.pl (thanks to Matthias Fechner for pointing out this one). - Removed compiler option '-m486' to make it work on non-Intel platforms. If you have already started a plugin project, you may want to make sure you remove this option from your existing Makefile. - Completely rearranged the recording and replay functions to make them available to plugins. - Replay is now done in a single thread (no more syncing between input and output thread necessary). - It is now possible to record several channels on the same transponder with "budget cards". VDR automatically attaches a recording timer to a card that already records on the appropriate transponder. How many parallel recordings can actually be done depends on the computer's performance. Currently any number of recordings gets attached to a card, so you should carefully plan your timers to not exceed the limit. On a K6-II/450 it was possible to record three channels from transponder 12480 with a single WinTV NOVA-S. - Timers that record two successive shows on the same channel may now overlap and will use the same DVB card. During the time where both timers record the data is simply saved to both files. - The following limitations apply to this version: + Transfer mode doesn't work yet. + The '-a' option (for Dolby Digital audio) doesn't work yet. + Switching between different language tracks doesn't work yet. + Cutting doesn't work yet.
-rw-r--r--CONTRIBUTORS24
-rw-r--r--HISTORY71
-rw-r--r--Makefile21
-rw-r--r--PLUGINS.html125
-rw-r--r--PLUGINS/SRC/hello/HISTORY4
-rw-r--r--PLUGINS/SRC/hello/Makefile12
-rw-r--r--PLUGINS/SRC/hello/hello.c4
-rw-r--r--PLUGINS/SRC/status/COPYING340
-rw-r--r--PLUGINS/SRC/status/HISTORY6
-rw-r--r--PLUGINS/SRC/status/Makefile79
-rw-r--r--PLUGINS/SRC/status/README11
-rw-r--r--PLUGINS/SRC/status/status.c179
-rw-r--r--audio.c11
-rw-r--r--audio.h13
-rw-r--r--config.c29
-rw-r--r--config.h12
-rw-r--r--device.c1017
-rw-r--r--device.h199
-rw-r--r--dmxdev.c.diff12
-rw-r--r--dvbapi.c2846
-rw-r--r--dvbapi.h335
-rw-r--r--dvbosd.c16
-rw-r--r--dvbosd.h8
-rw-r--r--dvbplayer.c733
-rw-r--r--dvbplayer.h60
-rw-r--r--eit.c3
-rw-r--r--eitscan.c84
-rw-r--r--eitscan.h33
-rwxr-xr-xepg2html.pl4
-rw-r--r--i18n.c361
-rw-r--r--i18n.h4
-rw-r--r--interface.c40
-rw-r--r--interface.h4
-rw-r--r--menu.c225
-rw-r--r--menu.h25
-rw-r--r--menuitems.c18
-rwxr-xr-xnewplugin12
-rw-r--r--osd.c243
-rw-r--r--osd.h47
-rw-r--r--osdbase.c38
-rw-r--r--osdbase.h8
-rw-r--r--player.c55
-rw-r--r--player.h51
-rw-r--r--receiver.c55
-rw-r--r--receiver.h48
-rw-r--r--recorder.c149
-rw-r--r--recorder.h43
-rw-r--r--recording.c350
-rw-r--r--recording.h61
-rw-r--r--remote.c6
-rw-r--r--ringbuffer.c95
-rw-r--r--ringbuffer.h42
-rw-r--r--status.c96
-rw-r--r--status.h75
-rw-r--r--svdrp.c26
-rw-r--r--thread.c3
-rw-r--r--thread.h4
-rw-r--r--tmp.dif118
-rw-r--r--tools.c7
-rw-r--r--tools.h11
-rw-r--r--vdr.c70
61 files changed, 5097 insertions, 3584 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 22e72a7..22bbc70 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -137,6 +137,8 @@ Stefan Huelswitt <huels@iname.com>
for suggesting to make 'package' target in the plugin's Makefile produce a package that
expands to a directory with just the plugin name and version number
for suggesting to make the config directory available to plugins
+ for suggesting to add an error message if the directory specified in the '-L'
+ option can't be accessed
Ulrich Röder <roeder@efr-net.de>
for pointing out that there are channels that have a symbol rate higher than
@@ -338,3 +340,25 @@ Oliver Lorei <oliverlorei@cityweb.de>
Andreas Böttger <fboettger@t-online.de>
for reporting a bug in skipping forward in time shift mode near the end of the recording
+
+Onno Kreuzinger <ok@solutas.net>
+ for reporting leftover references to the file FORMATS in MANUAL and svdrp.c
+
+Rudi Hofer (Rudi.Hofer@cadsoft.de)
+ for his help in keeping 'channels.conf' up to date
+
+Gregoire Favre <greg@ulima.unil.ch>
+ for fixing some function headers to make them compile with gcc 3.x
+
+Sven Grothklags <sven@uni-paderborn.de>
+ for fixing the cutting mechanism to make it re-sync in case a frame is larger
+ than the buffer
+
+Tomas Prybil <tomas.prybil@copper.se>
+ for translating OSD texts to the Swedish language
+
+Matthias Fechner <matthiasfechner@web.de>
+ for pointing out a bug in parsing 'E' records in epg2html.pl
+
+Paul Lacatus <paul@campina.iiruc.ro>
+ for translating OSD texts to the Romanian language
diff --git a/HISTORY b/HISTORY
index 6af3acb..b15541c 100644
--- a/HISTORY
+++ b/HISTORY
@@ -951,7 +951,7 @@ Video Disk Recorder Revision History
- Changed the [dei]syslog macros in tools.h to use a variable number of args,
thus making it safe to use them in nested 'if/else' statements.
- Fixed error handling in establishing an SVDRP connection (thanks to Davide
- Achilli) for pointing this out).
+ Achilli for pointing out this one).
- The new configuration file 'svdrphosts.conf' is now used to define which
hosts may access the SVDRP port (by default only 'localhost' has access).
See FORMATS for details.
@@ -1144,7 +1144,7 @@ Video Disk Recorder Revision History
- New command command line option '-V' to display the VDR version.
- Adjusting column width for channel numbers in case there are more than 999
channels.
-- Checking the return value of '...FileRady...' calls in dvbapi.c for better
+- Checking the return value of '...FileReady...' calls in dvbapi.c for better
performance under heavy system load.
- New 'make' target 'install', which copies the manual pages and executables
to their appropriate system locations and creates the /video directory if
@@ -1188,7 +1188,7 @@ Video Disk Recorder Revision History
- Now the EPG scan skips channels that have their 'Ca' parameter explicitly set
to an other DVB card (suggested by Sergei Haller).
- Fixed a possible hangup when reading a broken epg.data file (thanks to Henning
- Holtschneider for pointing this one out).
+ Holtschneider for pointing out this one).
- Fixed a bug in the editing process in case a previously edited file with the
same name was manually deleted on a system with more than one video directory
(thanks to Dirk Wiebel for reporting this one).
@@ -1261,7 +1261,8 @@ Video Disk Recorder Revision History
interface. 'osdbase.c/.h' now implements the abstract OSD, while 'dvbosd.c/.h'
is the actual implementation for the DVB hardware. This is in preparation for
allowing additional kinds of OSD hardware implementations.
-- Fixed leftover references to the file FORMATS in MANUAL and svdrp.c.
+- Fixed leftover references to the file FORMATS in MANUAL and svdrp.c (thanks to
+ Onno Kreuzinger for reporting this one).
- Avoiding ambiguities in the cList template class in case one defines a "list of
lists" (thanks to Stefan Huelswitt).
- Simplified the basic cMenuSetupPage class for easier use in plugins.
@@ -1284,3 +1285,65 @@ Video Disk Recorder Revision History
See PLUGINS.html, section "Configuration files" for details.
- Improved the [eid]syslog() macros, so that the LOG_... macros don't need to be
given any more.
+
+2002-05-26: Version 1.0.3
+
+- Updated 'Premiere Sport 2' in channels.conf (thanks to Rudi Hofer).
+- Fixed some function headers to make them compile with gcc 3.x (thanks to
+ Gregoire Favre).
+- Fixed the cutting mechanism to make it re-sync in case a frame is larger than the
+ buffer (thanks to Sven Grothklags).
+- Added Swedish language texts (thanks to Tomas Prybil).
+
+2002-06-10: Version 1.0.4
+
+- Added Romanian language texts (thanks to Paul Lacatus).
+- Removed compiler option '-m486' to make it work on non-Intel platforms (thanks
+ to Alastair McKinstry for pointing this out).
+
+2002-06-16: Version 1.1.3
+
+- Improved the VDR Makefile to avoid a warning if the '.dependencies' file does
+ not exist, and also using $(MAKE) to call recursive makes.
+- Changed the name of the 'package' target in the plugin Makefiles to 'dist'
+ (following the suggestions in the "GNU Make" manual). If you already have started
+ a plugin project, you may want to change this in your Makefile accordingly.
+- Improved the plugin Makefile to avoid a warning if the '.dependencies' file does
+ not exist, and also using $(shell...) to get the version numbers. If you already have
+ started a plugin project, you may want to change this in your Makefile accordingly.
+- Fixed some function headers to make them compile with gcc 3.x (thanks to
+ Gregoire Favre).
+- Fixed the cutting mechanism to make it re-sync in case a frame is larger than the
+ buffer (thanks to Sven Grothklags).
+- Added an error message if the directory specified in the '-L' option can't be
+ accessed (suggested by Stefan Huelswitt).
+- Rearranged OSD class names to make 'cOsd' available for the main OSD interface.
+- Completely moved OSD handling out of the cDvbApi class, into the new cOsd.
+- Implemented cStatus to allow plugins to set up a status monitor.
+ See PLUGINS.html for details.
+- Moved the cEITScanner out of dvbapi.h/.c, into the new eitscan.h/.c.
+- Added Swedish language texts (thanks to Tomas Prybil).
+- Fixed parsing 'E' records in epg2html.pl (thanks to Matthias Fechner for pointing
+ out this one).
+- Removed compiler option '-m486' to make it work on non-Intel platforms. If you
+ have already started a plugin project, you may want to make sure you remove this
+ option from your existing Makefile.
+- Completely rearranged the recording and replay functions to make them available
+ to plugins.
+- Replay is now done in a single thread (no more syncing between input and output
+ thread necessary).
+- It is now possible to record several channels on the same transponder with "budget
+ cards". VDR automatically attaches a recording timer to a card that already
+ records on the appropriate transponder. How many parallel recordings can actually
+ be done depends on the computer's performance. Currently any number of recordings
+ gets attached to a card, so you should carefully plan your timers to not exceed
+ the limit. On a K6-II/450 it was possible to record three channels from transponder
+ 12480 with a single WinTV NOVA-S.
+- Timers that record two successive shows on the same channel may now overlap and
+ will use the same DVB card. During the time where both timers record the data
+ is simply saved to both files.
+- The following limitations apply to this version:
+ + Transfer mode doesn't work yet.
+ + The '-a' option (for Dolby Digital audio) doesn't work yet.
+ + Switching between different language tracks doesn't work yet.
+ + Cutting doesn't work yet.
diff --git a/Makefile b/Makefile
index d6f7887..3cc655f 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: Makefile 1.35 2002/05/10 10:24:46 kls Exp $
+# $Id: Makefile 1.40 2002/06/10 16:31:34 kls Exp $
.DELETE_ON_ERROR:
@@ -21,9 +21,10 @@ INCLUDES = -I$(DVBDIR)/ost/include
DTVLIB = $(DTVDIR)/libdtv.a
-OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o\
- menuitems.o osdbase.o osd.o plugin.o recording.o remote.o remux.o ringbuffer.o\
- svdrp.o thread.o tools.o vdr.o videodir.o
+OBJS = audio.o config.o device.o dvbplayer.o dvbosd.o eit.o eitscan.o font.o i18n.o\
+ interface.o menu.o menuitems.o osdbase.o osd.o player.o plugin.o receiver.o\
+ recorder.o recording.o remote.o remux.o ringbuffer.o status.o svdrp.o thread.o\
+ tools.o vdr.o videodir.o
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
@@ -57,7 +58,7 @@ font: genfontfile fontfix.c fontosd.c
# Implicit rules:
%.o: %.c
- g++ -g -O2 -Wall -Woverloaded-virtual -m486 -c $(DEFINES) $(INCLUDES) $<
+ g++ -g -O2 -Wall -Woverloaded-virtual -c $(DEFINES) $(INCLUDES) $<
# Dependencies:
@@ -66,7 +67,7 @@ DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
-include $(DEPFILE)
+-include $(DEPFILE)
# The main program:
@@ -88,7 +89,7 @@ genfontfile: genfontfile.c
# The libdtv library:
$(DTVLIB) $(DTVDIR)/libdtv.h:
- make -C $(DTVDIR) all
+ $(MAKE) -C $(DTVDIR) all
# The 'include' directory (for plugins):
@@ -99,10 +100,10 @@ include-dir:
# Plugins:
plugins: include-dir
- @for i in `ls $(PLUGINDIR)/SRC | grep -v '[^a-z0-9]'`; do make -C "$(PLUGINDIR)/SRC/$$i" all; done
+ @for i in `ls $(PLUGINDIR)/SRC | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/SRC/$$i" all; done
plugins-clean:
- @for i in `ls $(PLUGINDIR)/SRC | grep -v '[^a-z0-9]'`; do make -C "$(PLUGINDIR)/SRC/$$i" clean; done
+ @for i in `ls $(PLUGINDIR)/SRC | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/SRC/$$i" clean; done
@-rm -f $(PLUGINDIR)/lib/*
# Install the files:
@@ -119,7 +120,7 @@ install:
# Housekeeping:
clean:
- make -C $(DTVDIR) clean
+ $(MAKE) -C $(DTVDIR) clean
-rm -f $(OBJS) $(DEPFILE) vdr genfontfile genfontfile.o core* *~
-rm -rf include
fontclean:
diff --git a/PLUGINS.html b/PLUGINS.html
index 4cc27da..9b5a496 100644
--- a/PLUGINS.html
+++ b/PLUGINS.html
@@ -4,7 +4,7 @@
</head>
<body bgcolor="white">
-<h1>The VDR Plugin System</h1>
+<center><h1>The VDR Plugin System</h1></center>
VDR provides an easy to use plugin interface that allows additional functionality
to be added to the program by implementing a dynamically loadable library file.
@@ -12,18 +12,30 @@ This interface allows programmers to develop additional functionality for VDR co
separate from the core VDR source, without the need of patching the original
VDR code (and all the problems of correlating various patches).
<p>
-This document describes the "outside" interface of the plugin system.
-It handles everything necessary for a plugin to get hooked into the core
+<!--X1.1.3--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+This document is divided into two parts, the first one describing the
+<a href="#Part I - The Outside Interface"><i>outside</i> interface</a>
+of the plugin system, and the second one describing the
+<a href="#Part II - The Inside Interface"><i>inside</i> interface</a>.
+The <i>outside</i> interface handles everything necessary for a plugin to get hooked into the core
VDR program and present itself to the user.
+The <i>inside</i> interface provides the plugin code access to VDR's internal data
+structures and allows it to hook itself into specific areas to perform special actions.
+<!--X1.1.3--></td></tr></table>
<p>
-<!--X1.1.1--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
+<!--X1.1.1--><table width=100%><tr><td bgcolor=lime>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.1 are marked like this.
<!--X1.1.1--></td></tr></table>
-<!--X1.1.2--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+<!--X1.1.2--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.2 are marked like this.
<!--X1.1.2--></td></tr></table>
+<!--X1.1.3--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+Important modifications introduced in version 1.1.3 are marked like this.
+<!--X1.1.3--></td></tr></table>
<!--<p>TODO: Link to the document about VDR base classes to use when implementing actual functionality (yet to be written).-->
+<a name="Part I - The Outside Interface"><hr><center><h1>Part I - The Outside Interface</h1></center>
+
<hr><h2>Quick start</h2>
<center><i><b>Can't wait, can't wait!</b></i></center><p>
@@ -117,7 +129,7 @@ from the web, it will typically have a name like
<p>
and will unpack into a directory named
<p>
-<!--X1.1.2--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+<!--X1.1.2--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
<tt>hello-0.0.1</tt>
<!--X1.1.2--></td></tr></table>
<p>
@@ -125,7 +137,7 @@ To use the <tt>plugins</tt> and <tt>plugins-clean</tt> targets from the VDR <tt>
you need to unpack such an archive into the <tt>VDR/PLUGINS/SRC</tt> directory and
create a symbolic link with the basic plugin name, as in
-<!--X1.1.2--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+<!--X1.1.2--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
ln -s hello-0.0.1 hello
</pre></td></tr></table><p>
@@ -191,7 +203,7 @@ its memory. You don't need to worry about the details behind all this.
If your plugin requires additional source files, simply add them to your plugin's
source directory and adjust the <tt>Makefile</tt> accordingly.
<p>
-<!--X1.1.1--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
+<!--X1.1.1--><table width=100%><tr><td bgcolor=lime>&nbsp;</td><td width=100%>
Header files usually contain preprocessor statements that prevent the same
file (or rather its contents, to be precise) from being included more than once, like
@@ -410,7 +422,7 @@ If a plugin implements a function that runs in the background (presumably in a
thread of its own), or wants to make use of <a href="#Internationalization">internationalization</a>,
it needs to implement the function
-<!--X1.1.2--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+<!--X1.1.2--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool Start(void);
</pre></td></tr></table><p>
@@ -422,7 +434,7 @@ its task. This may, for instance, be a thread that collects data from the DVB
stream, which is later presented to the user via a function that is available
from the main menu.
<p>
-<!--X1.1.2--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+<!--X1.1.2--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
A return value of <i>false</i> indicates that something has gone wrong and the
plugin will not be able to perform its task. In that case, the plugin should
write a proper error message to the log file. The first plugin that returns
@@ -486,7 +498,7 @@ interaction is possible. If a specific action takes longer than a few seconds,
the plugin should launch a separate thread to do this.
</b>
-<!--X1.1.2--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+<!--X1.1.2--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
<hr><h2>Housekeeping</h2>
<center><i><b>Chores, chores...</b></i></center><p>
@@ -533,7 +545,7 @@ previously stored in the global setup data (see below). It shall return
<i>true</i> if the parameter was parsed correctly, <i>false</i> in case of
an error. If <i>false</i> is returned, an error message will be written to
the log file (and program execution will continue).
-<!--X1.1.1--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
+<!--X1.1.1--><table width=100%><tr><td bgcolor=lime>&nbsp;</td><td width=100%>
A possible implementation of <tt>SetupParse()</tt> could look like this:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
@@ -588,7 +600,7 @@ needs setup parameters that are not directly user adjustable. It can use
<tt>SetupStore()</tt> and <tt>SetupParse()</tt> without presenting these
parameters to the user.
-<!--X1.1.1--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
+<!--X1.1.1--><table width=100%><tr><td bgcolor=lime>&nbsp;</td><td width=100%>
<a name="The Setup menu"><hr><h2>The Setup menu</h2>
<center><i><b>Have it your way!</b></i></center><p>
@@ -648,7 +660,7 @@ your setup parameters and use that one to copy all parameters with one single st
(like VDR does with its cSetup class).
<!--X1.1.1--></td></tr></table>
-<!--X1.1.2--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+<!--X1.1.2--><table width=100%><tr><td bgcolor=cyan>&nbsp;</td><td width=100%>
<hr><h2>Configuration files</h2>
<center><i><b>I want my own stuff!</b></i></center><p>
@@ -815,16 +827,18 @@ and display their help and/or version information in addition to its own output.
If you want to make your plugin available to other VDR users, you'll need to
make a package that can be easily distributed.
+<!--X1.1.3--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
The <tt>Makefile</tt> that has been created by the call to
<a href="#Initializing a new plugin directory"><tt>newplugin</tt></a>
-provides the target <tt>package</tt>, which does this for you.
+provides the target <tt>dist</tt>, which does this for you.
<p>
-Simply change into your source directory and execute <tt>make package</tt>:
+Simply change into your source directory and execute <tt>make dist</tt>:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
cd VDR/PLUGINS/SRC/hello
-make package
+make dist
</pre></td></tr></table><p>
+<!--X1.1.3--></td></tr></table>
After this you should find a file named like
@@ -835,5 +849,82 @@ vdr-hello-0.0.1.tgz
in your source directory, where <tt>hello</tt> will be replaced with your actual
plugin's name, and <tt>0.0.1</tt> will be your plugin's current version number.
+<!--X1.1.3--><table width=100%><tr><td bgcolor=red>&nbsp;</td><td width=100%>
+<a name="Part II - The Inside Interface"><hr><center><h1>Part II - The Inside Interface</h1></center>
+
+<hr><h2>Status monitor</h2>
+
+<center><i><b>A piece of the action</b></i></center><p>
+
+If a plugin wants to get informed on various events in VDR, it can derive a class from
+<tt>cStatus</tt>, as in
+
+<p><table><tr><td bgcolor=#F0F0F0><pre><br>
+#include &lt;vdr/status.h&gt;
+
+class cMyStatusMonitor : public cStatus {
+protected:
+ virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
+ };
+
+void cMyStatusMonitor::ChannelSwitch(const cDevice *Device, int ChannelNumber)
+{
+ if (ChannelNumber)
+ dsyslog("channel switched to %d on DVB %d", ChannelNumber, Device-&gt;CardIndex());
+ else
+ dsyslog("about to switch channel on DVB %d", Device-&gt;CardIndex());
+}
+</pre></td></tr></table><p>
+
+An object of this class will be informed whenever the channel is switched on one of
+the DVB devices. It could be used in a plugin like this:
+
+<p><table><tr><td bgcolor=#F0F0F0><pre><br>
+#include &lt;vdr/plugin.h&gt;
+
+class cPluginStatus : public cPlugin {
+private:
+ cMyStatusMonitor *statusMonitor;
+public:
+ cPluginStatus(void);
+ virtual ~cPluginStatus();
+ ...
+ virtual bool Start(void);
+ ...
+ };
+
+cPluginStatus::cPluginStatus(void)
+{
+ statusMonitor = NULL;
+}
+
+cPluginStatus::~cPluginStatus()
+{
+ delete statusMonitor;
+}
+
+bool cPluginStatus::Start(void)
+{
+ statusMonitor = new cMyStatusMonitor;
+ return true;
+}
+</pre></td></tr></table><p>
+
+Note that the actual object is created in the <tt>Start()</tt> function, not in the
+constructor! It is also important to delete the object in the destructor, in order to
+avoid memory leaks.
+<p>
+A Plugin can implement any number of <tt>cStatus</tt> derived objects, and once
+the plugin has been started it may create and delete them as necessary.
+No further action apart from creating an object derived from <tt>cStatus</tt>
+is necessary. VDR will automatically hook it into a list of status monitors, with
+their individual virtual member functions being called in the same sequence as the
+objects were created.
+<p>
+See the file <tt>status.h</tt> for detailed information on which status monitor
+member functions are available in <tt>cStatus</tt>. You only need to implement
+the functions you actually want to use.
+<!--X1.1.3--></td></tr></table>
+
</body>
</html>
diff --git a/PLUGINS/SRC/hello/HISTORY b/PLUGINS/SRC/hello/HISTORY
index 4df316f..f4cc743 100644
--- a/PLUGINS/SRC/hello/HISTORY
+++ b/PLUGINS/SRC/hello/HISTORY
@@ -14,3 +14,7 @@ VDR Plugin 'hello' Revision History
- Changed return type of cPluginHello::Start().
- Added cPluginHello::Housekeeping().
- Modified package generation.
+
+2002-05-17: Version 0.0.4
+
+- Makefile improvements.
diff --git a/PLUGINS/SRC/hello/Makefile b/PLUGINS/SRC/hello/Makefile
index fbd7450..b9d911a 100644
--- a/PLUGINS/SRC/hello/Makefile
+++ b/PLUGINS/SRC/hello/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for a Video Disk Recorder plugin
#
-# $Id: Makefile 1.2 2002/05/12 15:09:07 kls Exp $
+# $Id: Makefile 1.4 2002/06/10 16:24:06 kls Exp $
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
@@ -11,7 +11,7 @@ PLUGIN = hello
### The version number of this plugin (taken from the main source file):
-VERSION = `grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g'`
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
### The directory environment:
@@ -23,7 +23,7 @@ TMPDIR = /tmp
### The version number of VDR (taken from VDR's "config.h"):
-VDRVERSION = `grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g'`
+VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
### The name of the distribution archive:
@@ -43,7 +43,7 @@ OBJS = $(PLUGIN).o i18n.o
### The C++ compiler and options:
CXX = g++
-CXXFLAGS = -O2 -Wall -Woverloaded-virtual -m486
+CXXFLAGS = -O2 -Wall -Woverloaded-virtual
### Implicit rules:
@@ -57,7 +57,7 @@ DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
-include $(DEPFILE)
+-include $(DEPFILE)
### Targets:
@@ -67,7 +67,7 @@ libvdr-$(PLUGIN).so: $(OBJS)
$(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@
@cp $@ $(LIBDIR)/$@.$(VDRVERSION)
-package: clean
+dist: clean
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@mkdir $(TMPDIR)/$(ARCHIVE)
@cp -a * $(TMPDIR)/$(ARCHIVE)
diff --git a/PLUGINS/SRC/hello/hello.c b/PLUGINS/SRC/hello/hello.c
index 9a6c6e3..50aeeb3 100644
--- a/PLUGINS/SRC/hello/hello.c
+++ b/PLUGINS/SRC/hello/hello.c
@@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: hello.c 1.4 2002/05/12 10:18:59 kls Exp $
+ * $Id: hello.c 1.5 2002/05/14 21:23:25 kls Exp $
*/
#include <getopt.h>
@@ -11,7 +11,7 @@
#include <vdr/plugin.h>
#include "i18n.h"
-static const char *VERSION = "0.0.3";
+static const char *VERSION = "0.0.4";
static const char *DESCRIPTION = "A friendly greeting";
static const char *MAINMENUENTRY = "Hello";
diff --git a/PLUGINS/SRC/status/COPYING b/PLUGINS/SRC/status/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/PLUGINS/SRC/status/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/PLUGINS/SRC/status/HISTORY b/PLUGINS/SRC/status/HISTORY
new file mode 100644
index 0000000..2792936
--- /dev/null
+++ b/PLUGINS/SRC/status/HISTORY
@@ -0,0 +1,6 @@
+VDR Plugin 'status' Revision History
+------------------------------------
+
+2002-05-18: Version 0.0.1
+
+- Initial revision.
diff --git a/PLUGINS/SRC/status/Makefile b/PLUGINS/SRC/status/Makefile
new file mode 100644
index 0000000..a0dbbc0
--- /dev/null
+++ b/PLUGINS/SRC/status/Makefile
@@ -0,0 +1,79 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = status
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The directory environment:
+
+DVBDIR = ../../../../DVB/ost/include
+VDRDIR = ../../..
+VDRINC = $(VDRDIR)/include
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES = -I$(VDRINC) -I$(DVBDIR)
+
+DEFINES = -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o
+
+### The C++ compiler and options:
+
+CXX = g++
+CXXFLAGS = -g -O2 -Wall -Woverloaded-virtual
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: libvdr-$(PLUGIN).so
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@
+ @cp $@ $(LIBDIR)/$@.$(VDRVERSION)
+
+package: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
diff --git a/PLUGINS/SRC/status/README b/PLUGINS/SRC/status/README
new file mode 100644
index 0000000..356f1f8
--- /dev/null
+++ b/PLUGINS/SRC/status/README
@@ -0,0 +1,11 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Klaus Schmidinger <kls@cadsoft.de>
+
+Project's homepage: www.cadsoft.de/people/kls/vdr
+
+Latest version available at: www.cadsoft.de/people/kls/vdr/software.htm
+
+See the file COPYING for license information.
+
+Description: This is an example that shows the use of cStatus.
diff --git a/PLUGINS/SRC/status/status.c b/PLUGINS/SRC/status/status.c
new file mode 100644
index 0000000..c9c494a
--- /dev/null
+++ b/PLUGINS/SRC/status/status.c
@@ -0,0 +1,179 @@
+/*
+ * status.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include <vdr/plugin.h>
+#include <vdr/status.h>
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "Status monitor test";
+static const char *MAINMENUENTRY = NULL;
+
+// ---
+
+class cStatusTest : public cStatus {
+protected:
+ virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
+ virtual void Recording(const cDevice *Device, const char *Name);
+ virtual void Replaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name);
+ virtual void SetVolume(int Volume, bool Absolute);
+ virtual void OsdClear(void);
+ virtual void OsdTitle(const char *Title);
+ virtual void OsdStatusMessage(const char *Message);
+ virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
+ virtual void OsdCurrentItem(const char *Text);
+ virtual void OsdTextItem(const char *Text, bool Scroll);
+ virtual void OsdChannel(const char *Text);
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
+ };
+
+void cStatusTest::ChannelSwitch(const cDevice *Device, int ChannelNumber)
+{
+ dsyslog("status: cStatusTest::ChannelSwitch %d %d", Device->CardIndex(), ChannelNumber);
+}
+
+void cStatusTest::Recording(const cDevice *Device, const char *Name)
+{
+ dsyslog("status: cStatusTest::Recording %d %s", Device->CardIndex(), Name);
+}
+
+void cStatusTest::Replaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name)
+{
+ dsyslog("status: cStatusTest::Replaying %s", Name);
+}
+
+void cStatusTest::SetVolume(int Volume, bool Absolute)
+{
+ dsyslog("status: cStatusTest::SetVolume %d %d", Volume, Absolute);
+}
+
+void cStatusTest::OsdClear(void)
+{
+ dsyslog("status: cStatusTest::OsdClear");
+}
+
+void cStatusTest::OsdTitle(const char *Title)
+{
+ dsyslog("status: cStatusTest::OsdTitle '%s'", Title);
+}
+
+void cStatusTest::OsdStatusMessage(const char *Message)
+{
+ dsyslog("status: cStatusTest::OsdStatusMessage '%s'", Message);
+}
+
+void cStatusTest::OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+{
+ dsyslog("status: cStatusTest::OsdHelpKeys %s - %s - %s - %s", Red, Green, Yellow, Blue);
+}
+
+void cStatusTest::OsdCurrentItem(const char *Text)
+{
+ dsyslog("status: cStatusTest::OsdCurrentItem %s", Text);
+}
+
+void cStatusTest::OsdTextItem(const char *Text, bool Scroll)
+{
+ dsyslog("status: cStatusTest::OsdTextItem %s %d", Text, Scroll);
+}
+
+void cStatusTest::OsdChannel(const char *Text)
+{
+ dsyslog("status: cStatusTest::OsdChannel %s", Text);
+}
+
+void cStatusTest::OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle)
+{
+ char buffer[25];
+ struct tm tm_r;
+ dsyslog("status: cStatusTest::OsdProgramme");
+ strftime(buffer, sizeof(buffer), "%R", localtime_r(&PresentTime, &tm_r));
+ dsyslog("%5s %s", buffer, PresentTitle);
+ dsyslog("%5s %s", "", PresentSubtitle);
+ strftime(buffer, sizeof(buffer), "%R", localtime_r(&FollowingTime, &tm_r));
+ dsyslog("%5s %s", buffer, FollowingTitle);
+ dsyslog("%5s %s", "", FollowingSubtitle);
+}
+
+// ---
+
+class cPluginStatus : public cPlugin {
+private:
+ // Add any member variables or functions you may need here.
+ cStatusTest *statusTest;
+public:
+ cPluginStatus(void);
+ virtual ~cPluginStatus();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return DESCRIPTION; }
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Start(void);
+ virtual void Housekeeping(void);
+ virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
+ virtual cOsdMenu *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+ };
+
+cPluginStatus::cPluginStatus(void)
+{
+ // Initialize any member varaiables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+ statusTest = NULL;
+}
+
+cPluginStatus::~cPluginStatus()
+{
+ // Clean up after yourself!
+ delete statusTest;
+}
+
+const char *cPluginStatus::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return NULL;
+}
+
+bool cPluginStatus::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ return true;
+}
+
+bool cPluginStatus::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ statusTest = new cStatusTest;
+ return true;
+}
+
+void cPluginStatus::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+cOsdMenu *cPluginStatus::MainMenuAction(void)
+{
+ // Perform the action when selected from the main VDR menu.
+ return NULL;
+}
+
+cMenuSetupPage *cPluginStatus::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return NULL;
+}
+
+bool cPluginStatus::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ return false;
+}
+
+VDRPLUGINCREATOR(cPluginStatus); // Don't touch this!
diff --git a/audio.c b/audio.c
new file mode 100644
index 0000000..226505b
--- /dev/null
+++ b/audio.c
@@ -0,0 +1,11 @@
+/*
+ * audio.c: The basic audio interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: audio.c 1.1 2002/05/30 11:08:54 kls Exp $
+ */
+
+#include "audio.h"
+
diff --git a/audio.h b/audio.h
new file mode 100644
index 0000000..545a0af
--- /dev/null
+++ b/audio.h
@@ -0,0 +1,13 @@
+/*
+ * audio.h: The basic audio interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: audio.h 1.1 2002/05/30 11:07:28 kls Exp $
+ */
+
+#ifndef __AUDIO_H
+#define __AUDIO_H
+
+#endif //__AUDIO_H
diff --git a/config.c b/config.c
index f28d1a5..6753ce8 100644
--- a/config.c
+++ b/config.c
@@ -4,16 +4,16 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.c 1.101 2002/05/13 16:28:12 kls Exp $
+ * $Id: config.c 1.102 2002/06/16 12:57:31 kls Exp $
*/
#include "config.h"
#include <ctype.h>
#include <stdlib.h>
-#include "dvbapi.h"
#include "i18n.h"
#include "interface.h"
#include "plugin.h"
+#include "recording.h"
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
// format characters in order to allow any number of blanks after a numeric
@@ -293,15 +293,15 @@ bool cChannel::Save(FILE *f)
return fprintf(f, ToText()) > 0;
}
-bool cChannel::Switch(cDvbApi *DvbApi, bool Log)
+bool cChannel::Switch(cDevice *Device, bool Log)
{
- if (!DvbApi)
- DvbApi = cDvbApi::PrimaryDvbApi;
- if (!DvbApi->Recording() && !groupSep) {
+ if (!Device)
+ Device = cDevice::PrimaryDevice();
+ if (!(Device->IsPrimaryDevice() && Device->Receiving()) && !groupSep) {
if (Log)
isyslog("switching to channel %d", number);
for (int i = 3; i--;) {
- switch (DvbApi->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, apid2, dpid1, dpid2, tpid, ca, pnr)) {
+ switch (Device->SetChannel(number, frequency, polarization, diseqc, srate, vpid, apid1, tpid, ca, pnr)) {
case scrOk: return true;
case scrNoTransfer: if (Interface)
Interface->Error(tr("Can't start Transfer Mode!"));
@@ -312,7 +312,7 @@ bool cChannel::Switch(cDvbApi *DvbApi, bool Log)
}
return false;
}
- if (DvbApi->Recording())
+ if (Device->IsPrimaryDevice() && Device->Receiving())
Interface->Error(tr("Channel locked (recording)!"));
return false;
}
@@ -326,7 +326,7 @@ cTimer::cTimer(bool Instant)
startTime = stopTime = 0;
recording = pending = false;
active = Instant ? taActInst : taInactive;
- cChannel *ch = Channels.GetByNumber(cDvbApi::CurrentChannel());
+ cChannel *ch = Channels.GetByNumber(cDevice::CurrentChannel());
channel = ch ? ch->number : 0;
time_t t = time(NULL);
struct tm tm_r;
@@ -836,10 +836,10 @@ cChannel *cChannels::GetByServiceID(unsigned short ServiceId)
return NULL;
}
-bool cChannels::SwitchTo(int Number, cDvbApi *DvbApi)
+bool cChannels::SwitchTo(int Number, cDevice *Device)
{
cChannel *channel = GetByNumber(Number);
- return channel && channel->Switch(DvbApi);
+ return channel && channel->Switch(Device);
}
const char *cChannels::GetChannelNameByNumber(int Number)
@@ -957,6 +957,7 @@ bool cSetupLine::operator< (const cListObject &ListObject)
bool cSetupLine::Parse(char *s)
{
+ //dsyslog("cSetupLine::Parse '%s'", s);//XXX-
char *p = strchr(s, '=');
if (p) {
*p = 0;
@@ -974,6 +975,7 @@ bool cSetupLine::Parse(char *s)
}
name = strdup(Name);
value = strdup(Value);
+ //dsyslog("cSetupLine::Parse '%s' = '%s'", name, value);//XXX-
return true;
}
}
@@ -982,6 +984,7 @@ bool cSetupLine::Parse(char *s)
bool cSetupLine::Save(FILE *f)
{
+ //dsyslog("cSetupLine::Save '%s' = '%s'", name, value);//XXX-
return fprintf(f, "%s%s%s = %s\n", plugin ? plugin : "", plugin ? "." : "", name, value) > 0;
}
@@ -1095,7 +1098,7 @@ bool cSetup::Load(const char *FileName)
void cSetup::StoreCaCaps(const char *Name)
{
- for (int d = 0; d < MAXDVBAPI; d++) {
+ for (int d = 0; d < MAXDEVICES; d++) {
char buffer[MAXPARSEBUFFER];
char *q = buffer;
*buffer = 0;
@@ -1115,7 +1118,7 @@ bool cSetup::ParseCaCaps(const char *Value)
{
char *p;
int d = strtol(Value, &p, 10);
- if (d > 0 && d <= MAXDVBAPI) {
+ if (d > 0 && d <= MAXDEVICES) {
d--;
int i = 0;
while (p != Value && p && *p) {
diff --git a/config.h b/config.h
index cb21745..577d02d 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.116 2002/05/13 16:28:16 kls Exp $
+ * $Id: config.h 1.118 2002/05/31 10:20:56 kls Exp $
*/
#ifndef __CONFIG_H
@@ -15,11 +15,11 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
-#include "dvbapi.h"
+#include "device.h"
#include "eit.h"
#include "tools.h"
-#define VDRVERSION "1.1.2"
+#define VDRVERSION "1.1.3"
#define MAXPRIORITY 99
#define MAXLIFETIME 99
@@ -118,7 +118,7 @@ public:
const char *ToText(void);
bool Parse(const char *s);
bool Save(FILE *f);
- bool Switch(cDvbApi *DvbApi = NULL, bool Log = true);
+ bool Switch(cDevice *Device = NULL, bool Log = true);
};
enum eTimerActive { taInactive = 0,
@@ -294,7 +294,7 @@ public:
cChannel *GetByNumber(int Number);
cChannel *GetByServiceID(unsigned short ServiceId);
const char *GetChannelNameByNumber(int Number);
- bool SwitchTo(int Number, cDvbApi *DvbApi = NULL);
+ bool SwitchTo(int Number, cDevice *Device = NULL);
int MaxNumber(void) { return maxNumber; }
};
@@ -385,7 +385,7 @@ public:
int MinEventTimeout, MinUserInactivity;
int MultiSpeedMode;
int ShowReplayMode;
- int CaCaps[MAXDVBAPI][MAXCACAPS];
+ int CaCaps[MAXDEVICES][MAXCACAPS];
int CurrentChannel;
int CurrentVolume;
int __EndData__;
diff --git a/device.c b/device.c
new file mode 100644
index 0000000..fe75659
--- /dev/null
+++ b/device.c
@@ -0,0 +1,1017 @@
+/*
+ * device.c: The basic device interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: device.c 1.2 2002/06/16 13:23:31 kls Exp $
+ */
+
+#include "device.h"
+#include <errno.h>
+extern "C" {
+#define HAVE_BOOLEAN
+#include <jpeglib.h>
+}
+#include <linux/videodev.h>
+#include <ost/sec.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include "player.h"
+#include "receiver.h"
+#include "status.h"
+
+#define DEV_VIDEO "/dev/video"
+#define DEV_OST_OSD "/dev/ost/osd"
+#define DEV_OST_FRONTEND "/dev/ost/frontend"
+#define DEV_OST_SEC "/dev/ost/sec"
+#define DEV_OST_DVR "/dev/ost/dvr"
+#define DEV_OST_DEMUX "/dev/ost/demux"
+#define DEV_OST_VIDEO "/dev/ost/video"
+#define DEV_OST_AUDIO "/dev/ost/audio"
+
+// The default priority for non-primary DVB cards:
+#define DEFAULTPRIORITY -2
+
+#define TS_SIZE 188
+#define TS_SYNC_BYTE 0x47
+#define PID_MASK_HI 0x1F
+
+// The maximum time we wait before assuming that a recorded video data stream
+// is broken:
+#define MAXBROKENTIMEOUT 30 // seconds
+
+static const char *OstName(const char *Name, int n)
+{
+ static char buffer[_POSIX_PATH_MAX];
+ snprintf(buffer, sizeof(buffer), "%s%d", Name, n);
+ return buffer;
+}
+
+static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false)
+{
+ const char *FileName = OstName(Name, n);
+ int fd = open(FileName, Mode);
+ if (fd < 0 && ReportError)
+ LOG_ERROR_STR(FileName);
+ return fd;
+}
+
+int cDevice::numDevices = 0;
+int cDevice::useDevice = 0;
+cDevice *cDevice::device[MAXDEVICES] = { NULL };
+cDevice *cDevice::primaryDevice = NULL;
+
+cDevice::cDevice(int n)
+{
+ frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
+ siProcessor = NULL;
+ cardIndex = n;
+
+ // Devices that are present on all card types:
+
+ fd_frontend = OstOpen(DEV_OST_FRONTEND, n, O_RDWR);
+
+ // Devices that are only present on DVB-S cards:
+
+ fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR);
+
+ // Devices that are only present on cards with decoders:
+
+ fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR);
+ fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK);
+ fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK);
+
+ // Video format:
+
+ SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
+
+ // We only check the devices that must be present - the others will be checked before accessing them://XXX
+
+ if (fd_frontend >= 0) {
+ siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n));
+ FrontendInfo feinfo;
+ if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0)
+ frontendType = feinfo.type;
+ else
+ LOG_ERROR;
+ }
+ else
+ esyslog("ERROR: can't open video device %d", n);
+
+ dvrFileName = strdup(OstName(DEV_OST_DVR, CardIndex()));
+ active = false;
+
+ currentChannel = 0;
+ frequency = 0;
+
+ mute = false;
+ volume = Setup.CurrentVolume;
+
+ player = NULL;
+
+ for (int i = 0; i < MAXRECEIVERS; i++)
+ receiver[i] = NULL;
+ ca = -1;
+}
+
+cDevice::~cDevice()
+{
+ delete dvrFileName;
+ delete siProcessor;
+ Detach(player);
+ for (int i = 0; i < MAXRECEIVERS; i++)
+ Detach(receiver[i]);
+ // We're not explicitly closing any device files here, since this sometimes
+ // caused segfaults. Besides, the program is about to terminate anyway...
+}
+
+void cDevice::SetUseDevice(int n)
+{
+ if (n < MAXDEVICES)
+ useDevice |= (1 << n);
+}
+
+bool cDevice::SetPrimaryDevice(int n)
+{
+ n--;
+ if (0 <= n && n < numDevices && device[n]) {
+ isyslog("setting primary device to %d", n + 1);
+ primaryDevice = device[n];
+ return true;
+ }
+ esyslog("invalid devive number: %d", n + 1);
+ return false;
+}
+
+cDevice *cDevice::GetDevice(int Ca, int Priority, int Frequency, int Vpid, bool *ReUse)
+{
+ if (ReUse)
+ *ReUse = false;
+ cDevice *d = NULL;
+ int Provides[MAXDEVICES];
+ // Check which devices provide Ca:
+ for (int i = 0; i < numDevices; i++) {
+ if ((Provides[i] = device[i]->ProvidesCa(Ca)) != 0) { // this device is basicly able to do the job
+ //XXX+ dsyslog("GetDevice: %d %d %d %5d %5d", i, device[i]->HasDecoder(), device[i]->Receiving(), Frequency, device[i]->frequency);//XXX
+ if ( (!device[i]->HasDecoder() // it's a "budget card" which can receive multiple channels...
+ && device[i]->frequency == Frequency // ...and it is tuned to the requested frequency...
+ && device[i]->Receiving() // ...and is already receiving
+ // need not check priority - if a budget card is already receiving on the requested
+ // frequency, we can attach another receiver regardless of priority
+ )
+ || (device[i]->HasDecoder() // it's a "full featured card" which can receive only one channel...
+ && device[i]->frequency == Frequency // ...and it is tuned to the requested frequency...
+ && device[i]->pidHandles[ptVideo].pid == Vpid // ...and the requested video PID...
+ && device[i]->Receiving() // ...and is already receiving
+ // need not check priority - if a full featured card is already receiving the requested
+ // frequency and video PID, we can attach another receiver regardless of priority
+ )
+ ) {
+ d = device[i];
+ if (ReUse)
+ *ReUse = true;
+ break;
+ }
+ if (Priority > device[i]->Priority() // Priority is high enough to use this device
+ && (!d // we don't have a device yet, or...
+ || device[i]->Priority() < d->Priority() // ...this one has an even lower Priority
+ || (device[i]->Priority() == d->Priority() // ...same Priority...
+ && Provides[i] < Provides[d->CardIndex()] // ...but this one provides fewer Ca values
+ )
+ )
+ )
+ d = device[i];
+ }
+ }
+ /*XXX+ too complex with multiple recordings per device
+ if (!d && Ca > MAXDEVICES) {
+ // We didn't find one the easy way, so now we have to try harder:
+ int ShiftLevel = -1;
+ for (int i = 0; i < numDevices; i++) {
+ if (Provides[i]) { // this device is basicly able to do the job, but for some reason we didn't get it above
+ int sl = device[i]->CanShift(Ca, Priority); // asks this device to shift its job to another device
+ if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) {
+ d = device[i]; // found one that can be shifted with the fewest number of subsequent shifts
+ ShiftLevel = sl;
+ }
+ }
+ }
+ }
+ XXX*/
+ return d;
+}
+
+void cDevice::SetCaCaps(void)
+{
+ for (int d = 0; d < numDevices; d++) {
+ for (int i = 0; i < MAXCACAPS; i++)
+ device[d]->caCaps[i] = Setup.CaCaps[device[d]->CardIndex()][i];
+ }
+}
+
+bool cDevice::Probe(const char *FileName)
+{
+ if (access(FileName, F_OK) == 0) {
+ dsyslog("probing %s", FileName);
+ int f = open(FileName, O_RDONLY);
+ if (f >= 0) {
+ close(f);
+ return true;
+ }
+ else if (errno != ENODEV && errno != EINVAL)
+ LOG_ERROR_STR(FileName);
+ }
+ else if (errno != ENOENT)
+ LOG_ERROR_STR(FileName);
+ return false;
+}
+
+bool cDevice::Initialize(void)
+{
+ numDevices = 0;
+ for (int i = 0; i < MAXDEVICES; i++) {
+ if (useDevice == 0 || (useDevice & (1 << i)) != 0) {
+ if (Probe(OstName(DEV_OST_FRONTEND, i)))
+ device[numDevices++] = new cDevice(i);
+ else
+ break;
+ }
+ }
+ primaryDevice = device[0];
+ if (numDevices > 0) {
+ isyslog("found %d video device%s", numDevices, numDevices > 1 ? "s" : "");
+ SetCaCaps();
+ }
+ else
+ esyslog("ERROR: no video device found, giving up!");
+ return numDevices > 0;
+}
+
+void cDevice::Shutdown(void)
+{
+ for (int i = 0; i < numDevices; i++) {
+ delete device[i];
+ device[i] = NULL;
+ }
+ primaryDevice = NULL;
+}
+
+bool cDevice::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
+{
+ int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR, true);
+ if (videoDev >= 0) {
+ int result = 0;
+ struct video_mbuf mbuf;
+ result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf);
+ if (result == 0) {
+ int msize = mbuf.size;
+ unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
+ if (mem && mem != (unsigned char *)-1) {
+ // set up the size and RGB
+ struct video_capability vc;
+ result |= ioctl(videoDev, VIDIOCGCAP, &vc);
+ struct video_mmap vm;
+ vm.frame = 0;
+ if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
+ (SizeY > 0) && (SizeY <= vc.maxheight)) {
+ vm.width = SizeX;
+ vm.height = SizeY;
+ }
+ else {
+ vm.width = vc.maxwidth;
+ vm.height = vc.maxheight;
+ }
+ vm.format = VIDEO_PALETTE_RGB24;
+ result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm);
+ result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame);
+ // make RGB out of BGR:
+ int memsize = vm.width * vm.height;
+ unsigned char *mem1 = mem;
+ for (int i = 0; i < memsize; i++) {
+ unsigned char tmp = mem1[2];
+ mem1[2] = mem1[0];
+ mem1[0] = tmp;
+ mem1 += 3;
+ }
+
+ if (Quality < 0)
+ Quality = 255; //XXX is this 'best'???
+
+ isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
+ FILE *f = fopen(FileName, "wb");
+ if (f) {
+ if (Jpeg) {
+ // write JPEG file:
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+ jpeg_stdio_dest(&cinfo, f);
+ cinfo.image_width = vm.width;
+ cinfo.image_height = vm.height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, Quality, true);
+ jpeg_start_compress(&cinfo, true);
+
+ int rs = vm.width * 3;
+ JSAMPROW rp[vm.height];
+ for (int k = 0; k < vm.height; k++)
+ rp[k] = &mem[rs * k];
+ jpeg_write_scanlines(&cinfo, rp, vm.height);
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+ }
+ else {
+ // write PNM file:
+ if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 ||
+ fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) {
+ LOG_ERROR_STR(FileName);
+ result |= 1;
+ }
+ }
+ fclose(f);
+ }
+ else {
+ LOG_ERROR_STR(FileName);
+ result |= 1;
+ }
+ munmap(mem, msize);
+ }
+ else
+ result |= 1;
+ }
+ close(videoDev);
+ return result == 0;
+ }
+ return false;
+}
+
+void cDevice::SetVideoFormat(videoFormat_t Format)
+{
+ if (HasDecoder())
+ CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, Format));
+}
+
+// ptVideo ptAudio ptTeletext ptDolby ptOther
+dmxPesType_t PesTypes[] = { DMX_PES_VIDEO, DMX_PES_AUDIO, DMX_PES_TELETEXT, DMX_PES_OTHER, DMX_PES_OTHER };
+
+//#define PRINTPIDS(s) { char b[500]; char *q = b; q += sprintf(q, "%d %s ", CardIndex(), s); for (int i = 0; i < MAXPIDHANDLES; i++) q += sprintf(q, " %s%4d %d", i == ptOther ? "* " : "", pidHandles[i].pid, pidHandles[i].used); dsyslog(b); } //XXX+
+#define PRINTPIDS(s)
+
+bool cDevice::AddPid(int Pid, ePidType PidType)
+{
+ if (Pid) {
+ int n = -1;
+ int a = -1;
+ for (int i = 0; i < MAXPIDHANDLES; i++) {
+ if (pidHandles[i].pid == Pid)
+ n = i;
+ else if (a < 0 && i >= ptOther && !pidHandles[i].used)
+ a = i;
+ }
+ dmxPesType_t PesType = PesTypes[ptOther];
+ if (n >= 0) {
+ // The Pid is already in use
+ if (++pidHandles[n].used == 2 && n <= ptTeletext) {
+ // It's a special PID that has to be switched into "tap" mode
+ PRINTPIDS("A");//XXX+
+ return SetPid(pidHandles[n].fd, PesTypes[n], Pid, DMX_OUT_TS_TAP);
+ }
+ PRINTPIDS("a");//XXX+
+ return true;
+ }
+ else if (PidType < ptOther) {
+ // The Pid is not yet in use and it is a special one
+ n = PidType;
+ PesType = PesTypes[PidType];
+ PRINTPIDS("B");//XXX+
+ }
+ else if (a >= 0) {
+ // The Pid is not yet in use and we have a free slot
+ n = a;
+ }
+ else
+ esyslog("ERROR: no free slot for PID %d", Pid);
+ if (n >= 0) {
+ pidHandles[n].pid = Pid;
+ pidHandles[n].fd = OstOpen(DEV_OST_DEMUX, CardIndex(), O_RDWR | O_NONBLOCK, true);
+ pidHandles[n].used = 1;
+ PRINTPIDS("C");//XXX+
+ return SetPid(pidHandles[n].fd, PesType, Pid, PidType <= ptTeletext ? DMX_OUT_DECODER : DMX_OUT_TS_TAP);
+ }
+ }
+ return true;
+}
+
+bool cDevice::DelPid(int Pid)
+{
+ if (Pid) {
+ for (int i = 0; i < MAXPIDHANDLES; i++) {
+ if (pidHandles[i].pid == Pid) {
+ switch (--pidHandles[i].used) {
+ case 0: CHECK(ioctl(pidHandles[i].fd, DMX_STOP));//XXX+ is this necessary???
+ close(pidHandles[i].fd);
+ pidHandles[i].fd = -1;
+ pidHandles[i].pid = 0;
+ break;
+ case 1: if (i <= ptTeletext)
+ SetPid(pidHandles[i].fd, PesTypes[i], Pid, DMX_OUT_DECODER);
+ break;
+ }
+ PRINTPIDS("D");//XXX+
+ return pidHandles[i].used;
+ }
+ }
+ }
+ return false;
+}
+
+bool cDevice::SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output)
+{
+ if (Pid) {
+ CHECK(ioctl(fd, DMX_STOP));
+ if (Pid != 0x1FFF) {
+ dmxPesFilterParams pesFilterParams;
+ pesFilterParams.pid = Pid;
+ pesFilterParams.input = DMX_IN_FRONTEND;
+ pesFilterParams.output = Output;
+ pesFilterParams.pesType = PesType;
+ pesFilterParams.flags = DMX_IMMEDIATE_START;
+ //XXX+ pesFilterParams.flags = DMX_CHECK_CRC;//XXX
+ if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
+ LOG_ERROR;
+ return false;
+ }
+ //XXX+ CHECK(ioctl(fd, DMX_SET_BUFFER_SIZE, KILOBYTE(32)));//XXX
+ //XXX+ CHECK(ioctl(fd, DMX_START));//XXX
+ }
+ }
+ return true;
+}
+
+eSetChannelResult cDevice::SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr)
+{
+ //XXX+StopTransfer();
+ //XXX+StopReplay();
+
+ cStatus::MsgChannelSwitch(this, 0);
+
+ // Must set this anyway to avoid getting stuck when switching through
+ // channels with 'Up' and 'Down' keys:
+ currentChannel = ChannelNumber;
+
+ // Avoid noise while switching:
+
+ if (HasDecoder()) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
+ CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
+ }
+
+ // Stop setting system time:
+
+ if (siProcessor)
+ siProcessor->SetCurrentTransponder(0);
+
+ // If this card can't receive this channel, we must not actually switch
+ // the channel here, because that would irritate the driver when we
+ // start replaying in Transfer Mode immediately after switching the channel:
+ bool NeedsTransferMode = (IsPrimaryDevice() && !ProvidesCa(Ca));
+
+ if (!NeedsTransferMode) {
+
+ // Turn off current PIDs:
+
+ if (HasDecoder()) {
+ DelPid(pidHandles[ptVideo].pid);
+ DelPid(pidHandles[ptAudio].pid);
+ DelPid(pidHandles[ptTeletext].pid);
+ DelPid(pidHandles[ptDolby].pid);
+ }
+
+ FrontendParameters Frontend;
+
+ switch (frontendType) {
+ case FE_QPSK: { // DVB-S
+
+ // Frequency offsets:
+
+ unsigned int freq = Frequency;
+ int tone = SEC_TONE_OFF;
+
+ if (freq < (unsigned int)Setup.LnbSLOF) {
+ freq -= Setup.LnbFrequLo;
+ tone = SEC_TONE_OFF;
+ }
+ else {
+ freq -= Setup.LnbFrequHi;
+ tone = SEC_TONE_ON;
+ }
+
+ Frontend.Frequency = freq * 1000UL;
+ Frontend.Inversion = INVERSION_AUTO;
+ Frontend.u.qpsk.SymbolRate = Srate * 1000UL;
+ Frontend.u.qpsk.FEC_inner = FEC_AUTO;
+
+ int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
+
+ // DiseqC:
+
+ secCommand scmd;
+ scmd.type = 0;
+ scmd.u.diseqc.addr = 0x10;
+ scmd.u.diseqc.cmd = 0x38;
+ scmd.u.diseqc.numParams = 1;
+ scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0);
+
+ secCmdSequence scmds;
+ scmds.voltage = volt;
+ scmds.miniCommand = SEC_MINI_NONE;
+ scmds.continuousTone = tone;
+ scmds.numCommands = Setup.DiSEqC ? 1 : 0;
+ scmds.commands = &scmd;
+
+ CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
+ }
+ break;
+ case FE_QAM: { // DVB-C
+
+ // Frequency and symbol rate:
+
+ Frontend.Frequency = Frequency * 1000000UL;
+ Frontend.Inversion = INVERSION_AUTO;
+ Frontend.u.qam.SymbolRate = Srate * 1000UL;
+ Frontend.u.qam.FEC_inner = FEC_AUTO;
+ Frontend.u.qam.QAM = QAM_64;
+ }
+ break;
+ case FE_OFDM: { // DVB-T
+
+ // Frequency and OFDM paramaters:
+
+ Frontend.Frequency = Frequency * 1000UL;
+ Frontend.Inversion = INVERSION_AUTO;
+ Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ;
+ Frontend.u.ofdm.HP_CodeRate=FEC_2_3;
+ Frontend.u.ofdm.LP_CodeRate=FEC_1_2;
+ Frontend.u.ofdm.Constellation=QAM_64;
+ Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K;
+ Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32;
+ Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE;
+ }
+ break;
+ default:
+ esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
+ return scrFailed;
+ }
+
+ // Tuning:
+
+ CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
+
+ // Wait for channel sync:
+
+ if (cFile::FileReady(fd_frontend, 5000)) {
+ FrontendEvent event;
+ int res = ioctl(fd_frontend, FE_GET_EVENT, &event);
+ if (res >= 0) {
+ if (event.type != FE_COMPLETION_EV) {
+ esyslog("ERROR: channel %d not sync'ed on DVB card %d!", ChannelNumber, CardIndex() + 1);
+ if (IsPrimaryDevice())
+ cThread::RaisePanic();
+ return scrFailed;
+ }
+ }
+ else
+ esyslog("ERROR %d in frontend get event (channel %d, card %d)", res, ChannelNumber, CardIndex() + 1);
+ }
+ else
+ esyslog("ERROR: timeout while tuning");
+
+ frequency = Frequency;
+
+ // PID settings:
+
+ if (HasDecoder()) {
+ if (!(AddPid(Vpid, ptVideo) && AddPid(Apid, ptAudio))) {//XXX+ dolby Dpid1!!! (if audio plugins are attached)
+ esyslog("ERROR: failed to set PIDs for channel %d", ChannelNumber);
+ return scrFailed;
+ }
+ if (IsPrimaryDevice())
+ AddPid(Tpid, ptTeletext);
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ }
+ }
+
+ if (IsPrimaryDevice() && siProcessor)
+ siProcessor->SetCurrentServiceID(Pnr);
+
+ eSetChannelResult Result = scrOk;
+
+ // If this DVB card can't receive this channel, let's see if we can
+ // use the card that actually can receive it and transfer data from there:
+
+ if (NeedsTransferMode) {
+ cDevice *CaDevice = GetDevice(Ca, 0);
+ if (CaDevice && !CaDevice->Receiving()) {
+ if ((Result = CaDevice->SetChannel(ChannelNumber, Frequency, Polarization, Diseqc, Srate, Vpid, Apid, Tpid, Ca, Pnr)) == scrOk) {
+ //XXX+SetModeReplay();
+ //XXX+transferringFromDevice = CaDevice->StartTransfer(fd_video);
+ }
+ }
+ else
+ Result = scrNoTransfer;
+ }
+
+ if (HasDecoder()) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
+ }
+
+ // Start setting system time:
+
+ if (Result == scrOk && siProcessor)
+ siProcessor->SetCurrentTransponder(Frequency);
+
+ cStatus::MsgChannelSwitch(this, ChannelNumber);
+
+ return Result;
+}
+
+bool cDevice::ToggleMute(void)
+{
+ int OldVolume = volume;
+ mute = !mute;
+ SetVolume(0, mute);
+ volume = OldVolume;
+ return mute;
+}
+
+void cDevice::SetVolume(int Volume, bool Absolute)
+{
+ if (HasDecoder()) {
+ volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME);
+ audioMixer_t am;
+ am.volume_left = am.volume_right = volume;
+ CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
+ cStatus::MsgSetVolume(volume, Absolute);
+ if (volume > 0)
+ mute = false;
+ }
+}
+
+void cDevice::TrickSpeed(int Speed)
+{
+ if (fd_video >= 0)
+ CHECK(ioctl(fd_video, VIDEO_SLOWMOTION, Speed));
+}
+
+void cDevice::Clear(void)
+{
+ if (fd_video >= 0)
+ CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
+ if (fd_audio >= 0)
+ CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+}
+
+void cDevice::Play(void)
+{
+ if (fd_audio >= 0)
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ if (fd_video >= 0)
+ CHECK(ioctl(fd_video, VIDEO_CONTINUE));
+}
+
+void cDevice::Freeze(void)
+{
+ if (fd_audio >= 0)
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
+ if (fd_video >= 0)
+ CHECK(ioctl(fd_video, VIDEO_FREEZE));
+}
+
+void cDevice::Mute(void)
+{
+ if (fd_audio >= 0) {
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, false));
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
+ }
+}
+
+void cDevice::StillPicture(const uchar *Data, int Length)
+{
+ Mute();
+/* Using the VIDEO_STILLPICTURE ioctl call would be the
+ correct way to display a still frame, but unfortunately this
+ doesn't work with frames from VDR. So let's do pretty much the
+ same here as in DVB/driver/dvb.c's play_iframe() - I have absolutely
+ no idea why it works this way, but doesn't work with VIDEO_STILLPICTURE.
+ If anybody ever finds out what could be changed so that VIDEO_STILLPICTURE
+ could be used, please let me know!
+ kls 2002-03-23
+*/
+//#define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
+#ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
+ videoDisplayStillPicture sp = { (char *)Data, Length };
+ CHECK(ioctl(fd_video, VIDEO_STILLPICTURE, &sp));
+#else
+#define MIN_IFRAME 400000
+ for (int i = MIN_IFRAME / Length + 1; i > 0; i--) {
+ safe_write(fd_video, Data, Length);
+ usleep(1); // allows the buffer to be displayed in case the progress display is active
+ }
+#endif
+}
+
+bool cDevice::Replaying(void)
+{
+ /*XXX+
+ if (replayBuffer && !replayBuffer->Active())
+ StopReplay();
+ return replayBuffer != NULL;
+ XXX*/
+ return player != NULL;
+}
+
+bool cDevice::Attach(cPlayer *Player)
+{
+ if (Receiving()) {
+ esyslog("ERROR: attempt to attach a cPlayer while receiving on device %d - ignored", CardIndex() + 1);
+ return false;
+ }
+ if (HasDecoder()) {
+ if (player)
+ Detach(player);
+
+ if (siProcessor)
+ siProcessor->SetStatus(false);
+ CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
+ CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ CHECK(ioctl(fd_audio, AUDIO_PLAY));
+ CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
+ CHECK(ioctl(fd_video, VIDEO_PLAY));
+
+ player = Player;
+ player->device = this;
+ player->deviceFileHandle = fd_video;
+ player->Activate(true);
+ return true;
+ }
+ return false;
+}
+
+void cDevice::Detach(cPlayer *Player)
+{
+ if (Player && player == Player) {
+ player->Activate(false);
+ player->deviceFileHandle = -1;
+ player->device = NULL;
+ player = NULL;
+
+ CHECK(ioctl(fd_video, VIDEO_STOP, true));
+ CHECK(ioctl(fd_audio, AUDIO_STOP, true));
+ CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
+ CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
+ CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
+ CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
+ CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
+ if (siProcessor)
+ siProcessor->SetStatus(true);
+ }
+}
+
+void cDevice::StopReplay(void)
+{
+ if (player) {
+ Detach(player);
+ /*XXX+
+ if (IsPrimaryDevice()) {
+ // let's explicitly switch the channel back in case it was in Transfer Mode:
+ cChannel *Channel = Channels.GetByNumber(currentChannel);
+ if (Channel) {
+ Channel->Switch(this, false);
+ usleep(100000); // allow driver to sync in case a new replay will start immediately
+ }
+ }
+ XXX*/
+ }
+}
+
+int cDevice::PlayVideo(const uchar *Data, int Length)
+{
+ if (fd_video >= 0)
+ return write(fd_video, Data, Length);
+ return -1;
+}
+
+int cDevice::PlayAudio(const uchar *Data, int Length)
+{
+ //XXX+
+ return -1;
+}
+
+int cDevice::Priority(void)
+{
+ if (IsPrimaryDevice() && !Receiving())
+ return Setup.PrimaryLimit - 1;
+ int priority = DEFAULTPRIORITY;
+ for (int i = 0; i < MAXRECEIVERS; i++) {
+ if (receiver[i])
+ priority = max(receiver[i]->priority, priority);
+ }
+ return priority;
+}
+
+int cDevice::CanShift(int Ca, int Priority, int UsedCards)
+{
+ return -1;//XXX+ too complex with multiple recordings per device
+ // Test whether a receiving on this DVB device can be shifted to another one
+ // in order to perform a new receiving with the given Ca and Priority on this device:
+ int ShiftLevel = -1; // default means this device can't be shifted
+ if (UsedCards & (1 << CardIndex()) != 0)
+ return ShiftLevel; // otherwise we would get into a loop
+ if (Receiving()) {
+ if (ProvidesCa(Ca) // this device provides the requested Ca
+ && (Ca != this->Ca() // the requested Ca is different from the one currently used...
+ || Priority > this->Priority())) { // ...or the request comes from a higher priority
+ cDevice *d = NULL;
+ int Provides[MAXDEVICES];
+ UsedCards |= (1 << CardIndex());
+ for (int i = 0; i < numDevices; i++) {
+ if ((Provides[i] = device[i]->ProvidesCa(this->Ca())) != 0) { // this device is basicly able to do the job
+ if (device[i] != this) { // it is not _this_ device
+ int sl = device[i]->CanShift(this->Ca(), Priority, UsedCards); // this is the original Priority!
+ if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) {
+ d = device[i];
+ ShiftLevel = sl;
+ }
+ }
+ }
+ }
+ if (ShiftLevel >= 0)
+ ShiftLevel++; // adds the device's own shift
+ }
+ }
+ else if (Priority > this->Priority())
+ ShiftLevel = 0; // no shifting necessary, this device can do the job
+ return ShiftLevel;
+}
+
+int cDevice::ProvidesCa(int Ca)
+{
+ if (Ca == CardIndex() + 1)
+ return 1; // exactly _this_ card was requested
+ if (Ca && Ca <= MAXDEVICES)
+ return 0; // a specific card was requested, but not _this_ one
+ int result = Ca ? 0 : 1; // by default every card can provide FTA
+ int others = Ca ? 1 : 0;
+ for (int i = 0; i < MAXCACAPS; i++) {
+ if (caCaps[i]) {
+ if (caCaps[i] == Ca)
+ result = 1;
+ else
+ others++;
+ }
+ }
+ return result ? result + others : 0;
+}
+
+bool cDevice::Receiving(void)
+{
+ for (int i = 0; i < MAXRECEIVERS; i++) {
+ if (receiver[i])
+ return true;
+ }
+ return false;
+}
+
+void cDevice::Action(void)
+{
+ dsyslog("receiver thread started on device %d (pid=%d)", CardIndex() + 1, getpid());
+
+ int fd_dvr = open(dvrFileName, O_RDONLY | O_NONBLOCK);
+ if (fd_dvr >= 0) {
+ pollfd pfd;
+ pfd.fd = fd_dvr;
+ pfd.events = pfd.revents = POLLIN;
+ uchar b[TS_SIZE];
+ time_t t = time(NULL);
+ active = true;
+ for (; active;) {
+
+ // Read data from the DVR device:
+
+ if (pfd.revents & POLLIN != 0) {
+ int r = read(fd_dvr, b, sizeof(b));
+ if (r == TS_SIZE) {
+ if (*b == TS_SYNC_BYTE) {
+ // We're locked on to a TS packet
+ int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2];
+ // Distribute the packet to all attached receivers:
+ Lock();
+ for (int i = 0; i < MAXRECEIVERS; i++) {
+ if (receiver[i] && receiver[i]->WantsPid(Pid))
+ receiver[i]->Receive(b, TS_SIZE);
+ }
+ Unlock();
+ }
+ t = time(NULL);
+ }
+ else if (r > 0)
+ esyslog("ERROR: got incomplete TS packet (%d bytes)", r);//XXX+ TODO do we have to read the rest???
+ else if (r < 0) {
+ if (FATALERRNO) {
+ if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
+ esyslog("ERROR: DVB driver buffer overflow on device %d", CardIndex() + 1);
+ else {
+ LOG_ERROR;
+ break;
+ }
+ }
+ }
+ }
+
+ // Wait for more data to become available:
+
+ poll(&pfd, 1, 100);
+
+ //XXX+ put this into the recorder??? or give the receiver a flag whether it wants this?
+ if (time(NULL) - t > MAXBROKENTIMEOUT) {
+ esyslog("ERROR: video data stream broken on device %d", CardIndex() + 1);
+ cThread::EmergencyExit(true);
+ t = time(NULL);
+ }
+ }
+ close(fd_dvr);
+ }
+ else
+ LOG_ERROR_STR(dvrFileName);
+
+ dsyslog("receiver thread ended on device %d (pid=%d)", CardIndex() + 1, getpid());
+}
+
+bool cDevice::Attach(cReceiver *Receiver)
+{
+ //XXX+ check for same transponder???
+ if (!Receiver)
+ return false;
+ if (Receiver->device == this)
+ return true;
+ StopReplay();
+ for (int i = 0; i < MAXRECEIVERS; i++) {
+ if (!receiver[i]) {
+ //siProcessor->SetStatus(false);//XXX+
+ for (int n = 0; n < MAXRECEIVEPIDS; n++) {
+ if (Receiver->pids[n])
+ AddPid(Receiver->pids[n]);//XXX+ retval!
+ else
+ break;
+ }
+ Receiver->Activate(true);
+ Lock();
+ Receiver->device = this;
+ receiver[i] = Receiver;
+ Unlock();
+ Start();
+ return true;
+ }
+ }
+ esyslog("ERROR: no free receiver slot!");
+ return false;
+}
+
+void cDevice::Detach(cReceiver *Receiver)
+{
+ if (!Receiver || Receiver->device != this)
+ return;
+ bool receiversLeft = false;
+ for (int i = 0; i < MAXRECEIVERS; i++) {
+ if (receiver[i] == Receiver) {
+ Receiver->Activate(false);
+ Lock();
+ receiver[i] = NULL;
+ Receiver->device = NULL;
+ Unlock();
+ for (int n = 0; n < MAXRECEIVEPIDS; n++) {
+ if (Receiver->pids[n])
+ DelPid(Receiver->pids[n]);
+ else
+ break;
+ }
+ }
+ else if (receiver[i])
+ receiversLeft = true;
+ }
+ if (!receiversLeft) {
+ active = false;
+ Cancel(3);
+ }
+}
diff --git a/device.h b/device.h
new file mode 100644
index 0000000..a25ff94
--- /dev/null
+++ b/device.h
@@ -0,0 +1,199 @@
+/*
+ * device.h: The basic device interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: device.h 1.1 2002/06/09 15:48:32 kls Exp $
+ */
+
+#ifndef __DEVICE_H
+#define __DEVICE_H
+
+#include <stdlib.h> // FIXME: this is apparently necessary for the ost/... header files
+ // FIXME: shouldn't every header file include ALL the other header
+ // FIXME: files it depends on? The sequence in which header files
+ // FIXME: are included here should not matter - and it should NOT
+ // FIXME: be necessary to include <stdlib.h> here!
+#include <ost/dmx.h>
+#include <ost/frontend.h>
+#include <ost/audio.h>
+#include <ost/video.h>
+#include "eit.h"
+#include "thread.h"
+
+enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed };
+
+#define MAXDEVICES 4 // the maximum number of devices in the system
+#define MAXCACAPS 16 // the maximum number of different CA values per DVB device
+#define MAXPIDHANDLES 16 // the maximum number of different PIDs per DVB device
+#define MAXRECEIVERS 16 // the maximum number of receivers per DVB device
+#define MAXVOLUME 255
+#define VOLUMEDELTA 5 // used to increase/decrease the volume
+
+class cPlayer;
+class cReceiver;
+
+class cDevice : cThread {
+ friend class cOsd;//XXX
+private:
+ static int numDevices;
+ static int useDevice;
+ static cDevice *device[MAXDEVICES];
+ static cDevice *primaryDevice;
+public:
+ static int NumDevices(void) { return numDevices; }
+ // Returns the total number of DVB devices.
+ static void SetUseDevice(int n);
+ // Sets the 'useDevice' flag of the given DVB device.
+ // If this function is not called before Initialize(), all DVB devices
+ // will be used.
+ static bool SetPrimaryDevice(int n);
+ // Sets the primary DVB device to 'n' (which must be in the range
+ // 1...numDevices) and returns true if this was possible.
+ static cDevice *PrimaryDevice(void) { return primaryDevice; }
+ // Returns the primary DVB device.
+ static cDevice *GetDevice(int Ca, int Priority, int Frequency = 0, int Vpid = 0, bool *ReUse = NULL);
+ // Selects a free DVB device, avoiding the primaryDevice if possible.
+ // If Ca is not 0, the device with the given number will be returned
+ // in case Ca is <= MAXDEVICES, or the device that provides the given
+ // value in its caCaps.
+ // If there is a device that is already tuned to the given Frequency,
+ // and that device is able to receive multiple channels ("budget" cards),
+ // that device will be returned. Else if a ("full featured") device is
+ // tuned to Frequency and Vpid, that one will be returned.
+ // If all DVB devices are currently receiving, the one receiving the
+ // lowest priority timer (if any) that is lower than the given Priority
+ // will be returned.
+ // If ReUse is given, the caller will be informed whether the device can be re-used
+ // for a new recording. If ReUse returns 'true', the caller must NOT switch the channel
+ // (the device is already properly tuned). Otherwise the caller MUST switch the channel.
+ static void SetCaCaps(void);
+ // Sets the CaCaps of all DVB devices according to the Setup data.
+ static bool Probe(const char *FileName);
+ // Probes for existing DVB devices.
+ static bool Initialize(void);
+ // Initializes the DVB devices.
+ // Must be called before accessing any DVB functions.
+ static void Shutdown(void);
+ // Closes down all DVB devices.
+ // Must be called at the end of the program.
+private:
+ int cardIndex;
+ int caCaps[MAXCACAPS];
+ FrontendType frontendType;
+ char *dvrFileName;
+ bool active;
+ int fd_osd, fd_frontend, fd_sec, fd_audio, fd_video;
+ int OsdDeviceHandle(void) { return fd_osd; }
+public:
+ cDevice(int n);
+ virtual ~cDevice();
+ bool IsPrimaryDevice(void) { return this == primaryDevice; }
+ int CardIndex(void) const { return cardIndex; }
+ // Returns the card index of this device (0 ... MAXDEVICES - 1).
+ int ProvidesCa(int Ca);
+ // Checks whether this DVB device provides the given value in its
+ // caCaps. Returns 0 if the value is not provided, 1 if only this
+ // value is provided, and > 1 if this and other values are provided.
+ // If the given value is equal to the number of this DVB device,
+ // 1 is returned. If it is 0 (FTA), 1 plus the number of other values
+ // in caCaps is returned.
+ bool HasDecoder(void) const { return fd_video >= 0 && fd_audio >= 0; }
+
+// Channel facilities
+
+private:
+ int currentChannel;
+ int frequency;
+public:
+ eSetChannelResult SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid, int Tpid, int Ca, int Pnr);
+ static int CurrentChannel(void) { return primaryDevice ? primaryDevice->currentChannel : 0; }
+ int Channel(void) { return currentChannel; }
+
+// PID handle facilities
+
+private:
+ enum ePidType { ptVideo, ptAudio, ptTeletext, ptDolby, ptOther };
+ class cPidHandle {
+ public:
+ int pid;
+ int fd;
+ int used;
+ cPidHandle(void) { pid = used = 0; fd = -1; }
+ };
+ cPidHandle pidHandles[MAXPIDHANDLES];
+ bool AddPid(int Pid, ePidType PidType = ptOther);
+ bool DelPid(int Pid);
+ bool SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output);
+ virtual void Action(void);
+
+// Image Grab facilities
+
+public:
+ bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
+
+// Video format facilities
+
+public:
+ virtual void SetVideoFormat(videoFormat_t Format);
+
+// Volume facilities
+
+private:
+ bool mute;
+ int volume;
+public:
+ bool IsMute(void) { return mute; }
+ bool ToggleMute(void);
+ // Turns the volume off or on and returns the new mute state.
+ void SetVolume(int Volume, bool Absolute = false);
+ // Sets the volume to the given value, either absolutely or relative to
+ // the current volume.
+ static int CurrentVolume(void) { return primaryDevice ? primaryDevice->volume : 0; }//XXX???
+
+ // EIT facilities
+
+private:
+ cSIProcessor *siProcessor;
+
+// Player facilities
+
+private:
+ cPlayer *player;
+public:
+ void TrickSpeed(int Speed);
+ void Clear(void);
+ void Play(void);
+ void Freeze(void);
+ void Mute(void);
+ void StillPicture(const uchar *Data, int Length);
+ bool Replaying(void);
+ // Returns true if we are currently replaying.
+ void StopReplay(void);
+ // Stops the current replay session (if any).
+ bool Attach(cPlayer *Player);
+ void Detach(cPlayer *Player);
+ virtual int PlayVideo(const uchar *Data, int Length);
+ virtual int PlayAudio(const uchar *Data, int Length);
+
+// Receiver facilities
+
+private:
+ cReceiver *receiver[MAXRECEIVERS];
+ int ca;
+ int Priority(void);
+ // Returns the priority of the current receiving session (0..MAXPRIORITY),
+ // or -1 if no receiver is currently active. The primary DVB device will
+ // always return at least Setup.PrimaryLimit-1.
+ int CanShift(int Ca, int Priority, int UsedCards = 0);
+public:
+ int Ca(void) { return ca; }
+ // Returns the ca of the current receiving session.
+ bool Receiving(void);
+ // Returns true if we are currently receiving.
+ bool Attach(cReceiver *Receiver);
+ void Detach(cReceiver *Receiver);
+ };
+
+#endif //__DEVICE_H
diff --git a/dmxdev.c.diff b/dmxdev.c.diff
new file mode 100644
index 0000000..85672c0
--- /dev/null
+++ b/dmxdev.c.diff
@@ -0,0 +1,12 @@
+--- dmxdev.c Mon Apr 1 10:59:46 2002
++++ dmxdev.c Sun Jun 16 11:02:38 2002
+@@ -1048,6 +1048,9 @@
+ if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite)
+ return (POLLIN | POLLRDNORM | POLLPRI);
+
++ if (dmxdev->dvr_buffer.error)
++ return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
++
+ return 0;
+ } else
+ return (POLLOUT | POLLWRNORM | POLLPRI);
diff --git a/dvbapi.c b/dvbapi.c
deleted file mode 100644
index 0b14a87..0000000
--- a/dvbapi.c
+++ /dev/null
@@ -1,2846 +0,0 @@
-/*
- * dvbapi.c: Interface to the DVB driver
- *
- * See the main source file 'vdr.c' for copyright information and
- * how to reach the author.
- *
- * $Id: dvbapi.c 1.175 2002/05/13 16:29:17 kls Exp $
- */
-
-#include "dvbapi.h"
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-extern "C" {
-#define HAVE_BOOLEAN
-#include <jpeglib.h>
-}
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include "config.h"
-#include "recording.h"
-#include "remux.h"
-#include "ringbuffer.h"
-#include "tools.h"
-#include "videodir.h"
-
-#define DEV_VIDEO "/dev/video"
-#define DEV_OST_OSD "/dev/ost/osd"
-#define DEV_OST_FRONTEND "/dev/ost/frontend"
-#define DEV_OST_SEC "/dev/ost/sec"
-#define DEV_OST_DVR "/dev/ost/dvr"
-#define DEV_OST_DEMUX "/dev/ost/demux"
-#define DEV_OST_VIDEO "/dev/ost/video"
-#define DEV_OST_AUDIO "/dev/ost/audio"
-
-// The size of the array used to buffer video data:
-// (must be larger than MINVIDEODATA - see remux.h)
-#define VIDEOBUFSIZE MEGABYTE(1)
-
-// The maximum size of a single frame:
-#define MAXFRAMESIZE KILOBYTE(192)
-
-#define MAXFILESPERRECORDING 255
-
-#define MINFREEDISKSPACE (512) // MB
-#define DISKCHECKINTERVAL 100 // seconds
-
-#define INDEXFILESUFFIX "/index.vdr"
-#define RECORDFILESUFFIX "/%03d.vdr"
-#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
-
-// The number of frames to back up when resuming an interrupted replay session:
-#define RESUMEBACKUP (10 * FRAMESPERSEC)
-
-// The maximum time we wait before assuming that a recorded video data stream
-// is broken:
-#define MAXBROKENTIMEOUT 30 // seconds
-
-// The maximum time to wait before giving up while catching up on an index file:
-#define MAXINDEXCATCHUP 2 // seconds
-
-// The default priority for non-primary DVB cards:
-#define DEFAULTPRIORITY -2
-
-#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
-
-#define FATALERRNO (errno != EAGAIN && errno != EINTR)
-
-typedef unsigned char uchar;
-
-const char *IndexToHMSF(int Index, bool WithFrame)
-{
- static char buffer[16];
- int f = (Index % FRAMESPERSEC) + 1;
- int s = (Index / FRAMESPERSEC);
- int m = s / 60 % 60;
- int h = s / 3600;
- s %= 60;
- snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
- return buffer;
-}
-
-int HMSFToIndex(const char *HMSF)
-{
- int h, m, s, f = 0;
- if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
- return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
- return 0;
-}
-
-// --- cIndexFile ------------------------------------------------------------
-
-class cIndexFile {
-private:
- struct tIndex { int offset; uchar type; uchar number; short reserved; };
- int f;
- char *fileName;
- int size, last;
- tIndex *index;
- cResumeFile resumeFile;
- bool CatchUp(int Index = -1);
-public:
- cIndexFile(const char *FileName, bool Record);
- ~cIndexFile();
- bool Ok(void) { return index != NULL; }
- bool Write(uchar PictureType, uchar FileNumber, int FileOffset);
- bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL);
- int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false);
- int Get(uchar FileNumber, int FileOffset);
- int Last(void) { CatchUp(); return last; }
- int GetResume(void) { return resumeFile.Read(); }
- bool StoreResume(int Index) { return resumeFile.Save(Index); }
- };
-
-cIndexFile::cIndexFile(const char *FileName, bool Record)
-:resumeFile(FileName)
-{
- f = -1;
- fileName = NULL;
- size = 0;
- last = -1;
- index = NULL;
- if (FileName) {
- fileName = new char[strlen(FileName) + strlen(INDEXFILESUFFIX) + 1];
- if (fileName) {
- strcpy(fileName, FileName);
- char *pFileExt = fileName + strlen(fileName);
- strcpy(pFileExt, INDEXFILESUFFIX);
- int delta = 0;
- if (access(fileName, R_OK) == 0) {
- struct stat buf;
- if (stat(fileName, &buf) == 0) {
- delta = buf.st_size % sizeof(tIndex);
- if (delta) {
- delta = sizeof(tIndex) - delta;
- esyslog("ERROR: invalid file size (%ld) in '%s'", buf.st_size, fileName);
- }
- last = (buf.st_size + delta) / sizeof(tIndex) - 1;
- if (!Record && last >= 0) {
- size = last + 1;
- index = new tIndex[size];
- if (index) {
- f = open(fileName, O_RDONLY);
- if (f >= 0) {
- if ((int)safe_read(f, index, buf.st_size) != buf.st_size) {
- esyslog("ERROR: can't read from file '%s'", fileName);
- delete index;
- index = NULL;
- close(f);
- f = -1;
- }
- // we don't close f here, see CatchUp()!
- }
- else
- LOG_ERROR_STR(fileName);
- }
- else
- esyslog("ERROR: can't allocate %d bytes for index '%s'", size * sizeof(tIndex), fileName);
- }
- }
- else
- LOG_ERROR;
- }
- else if (!Record)
- isyslog("missing index file %s", fileName);
- if (Record) {
- if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) {
- if (delta) {
- esyslog("ERROR: padding index file with %d '0' bytes", delta);
- while (delta--)
- writechar(f, 0);
- }
- }
- else
- LOG_ERROR_STR(fileName);
- }
- }
- else
- esyslog("ERROR: can't copy file name '%s'", FileName);
- }
-}
-
-cIndexFile::~cIndexFile()
-{
- if (f >= 0)
- close(f);
- delete fileName;
- delete index;
-}
-
-bool cIndexFile::CatchUp(int Index)
-{
- if (index && f >= 0) {
- for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >= last); i++) {
- struct stat buf;
- if (fstat(f, &buf) == 0) {
- int newLast = buf.st_size / sizeof(tIndex) - 1;
- if (newLast > last) {
- if (size <= newLast) {
- size *= 2;
- if (size <= newLast)
- size = newLast + 1;
- }
- index = (tIndex *)realloc(index, size * sizeof(tIndex));
- if (index) {
- int offset = (last + 1) * sizeof(tIndex);
- int delta = (newLast - last) * sizeof(tIndex);
- if (lseek(f, offset, SEEK_SET) == offset) {
- if (safe_read(f, &index[last + 1], delta) != delta) {
- esyslog("ERROR: can't read from index");
- delete index;
- index = NULL;
- close(f);
- f = -1;
- break;
- }
- last = newLast;
- }
- else
- LOG_ERROR_STR(fileName);
- }
- else
- esyslog("ERROR: can't realloc() index");
- }
- }
- else
- LOG_ERROR_STR(fileName);
- if (Index >= last)
- sleep(1);
- else
- return true;
- }
- }
- return false;
-}
-
-bool cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset)
-{
- if (f >= 0) {
- tIndex i = { FileOffset, PictureType, FileNumber, 0 };
- if (safe_write(f, &i, sizeof(i)) < 0) {
- LOG_ERROR_STR(fileName);
- close(f);
- f = -1;
- return false;
- }
- last++;
- }
- return f >= 0;
-}
-
-bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length)
-{
- if (index) {
- CatchUp(Index);
- if (Index >= 0 && Index <= last) {
- *FileNumber = index[Index].number;
- *FileOffset = index[Index].offset;
- if (PictureType)
- *PictureType = index[Index].type;
- if (Length) {
- int fn = index[Index + 1].number;
- int fo = index[Index + 1].offset;
- if (fn == *FileNumber)
- *Length = fo - *FileOffset;
- else
- *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
- }
- return true;
- }
- }
- return false;
-}
-
-int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length, bool StayOffEnd)
-{
- if (index) {
- CatchUp();
- int d = Forward ? 1 : -1;
- for (;;) {
- Index += d;
- if (Index >= 0 && Index < last - ((Forward && StayOffEnd) ? 100 : 0)) {
- if (index[Index].type == I_FRAME) {
- if (FileNumber)
- *FileNumber = index[Index].number;
- else
- FileNumber = &index[Index].number;
- if (FileOffset)
- *FileOffset = index[Index].offset;
- else
- FileOffset = &index[Index].offset;
- if (Length) {
- // all recordings end with a non-I_FRAME, so the following should be safe:
- int fn = index[Index + 1].number;
- int fo = index[Index + 1].offset;
- if (fn == *FileNumber)
- *Length = fo - *FileOffset;
- else {
- esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
- *Length = -1;
- }
- }
- return Index;
- }
- }
- else
- break;
- }
- }
- return -1;
-}
-
-int cIndexFile::Get(uchar FileNumber, int FileOffset)
-{
- if (index) {
- CatchUp();
- //TODO implement binary search!
- int i;
- for (i = 0; i < last; i++) {
- if (index[i].number > FileNumber || (index[i].number == FileNumber) && index[i].offset >= FileOffset)
- break;
- }
- return i;
- }
- return -1;
-}
-
-// --- cFileName -------------------------------------------------------------
-
-class cFileName {
-private:
- int file;
- int fileNumber;
- char *fileName, *pFileNumber;
- bool record;
- bool blocking;
-public:
- cFileName(const char *FileName, bool Record, bool Blocking = false);
- ~cFileName();
- const char *Name(void) { return fileName; }
- int Number(void) { return fileNumber; }
- int Open(void);
- void Close(void);
- int SetOffset(int Number, int Offset = 0);
- int NextFile(void);
- };
-
-cFileName::cFileName(const char *FileName, bool Record, bool Blocking)
-{
- file = -1;
- fileNumber = 0;
- record = Record;
- blocking = Blocking;
- // Prepare the file name:
- fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
- if (!fileName) {
- esyslog("ERROR: can't copy file name '%s'", fileName);
- return;
- }
- strcpy(fileName, FileName);
- pFileNumber = fileName + strlen(fileName);
- SetOffset(1);
-}
-
-cFileName::~cFileName()
-{
- Close();
- delete fileName;
-}
-
-int cFileName::Open(void)
-{
- if (file < 0) {
- int BlockingFlag = blocking ? 0 : O_NONBLOCK;
- if (record) {
- dsyslog("recording to '%s'", fileName);
- file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag);
- if (file < 0)
- LOG_ERROR_STR(fileName);
- }
- else {
- if (access(fileName, R_OK) == 0) {
- dsyslog("playing '%s'", fileName);
- file = open(fileName, O_RDONLY | BlockingFlag);
- if (file < 0)
- LOG_ERROR_STR(fileName);
- }
- else if (errno != ENOENT)
- LOG_ERROR_STR(fileName);
- }
- }
- return file;
-}
-
-void cFileName::Close(void)
-{
- if (file >= 0) {
- if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0))
- LOG_ERROR_STR(fileName);
- file = -1;
- }
-}
-
-int cFileName::SetOffset(int Number, int Offset)
-{
- if (fileNumber != Number)
- Close();
- if (0 < Number && Number <= MAXFILESPERRECORDING) {
- fileNumber = Number;
- sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
- if (record) {
- if (access(fileName, F_OK) == 0) // file exists, let's try next suffix
- return SetOffset(Number + 1);
- else if (errno != ENOENT) { // something serious has happened
- LOG_ERROR_STR(fileName);
- return -1;
- }
- // found a non existing file suffix
- }
- if (Open() >= 0) {
- if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) {
- LOG_ERROR_STR(fileName);
- return -1;
- }
- }
- return file;
- }
- esyslog("ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
- return -1;
-}
-
-int cFileName::NextFile(void)
-{
- return SetOffset(fileNumber + 1);
-}
-
-// --- cRecordBuffer ---------------------------------------------------------
-
-class cRecordBuffer : public cRingBufferLinear {
-private:
- cDvbApi *dvbApi;
- cFileName fileName;
- cIndexFile *index;
- cRemux remux;
- uchar pictureType;
- int fileSize;
- int videoDev;
- int recordFile;
- bool recording;
- time_t lastDiskSpaceCheck;
- bool RunningLowOnDiskSpace(void);
- bool NextFile(void);
-protected:
- virtual void Input(void);
- virtual void Output(void);
-public:
- cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2);
- virtual ~cRecordBuffer();
- };
-
-cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2)
-:cRingBufferLinear(VIDEOBUFSIZE, true)
-,fileName(FileName, true)
-,remux(VPid, APid1, APid2, DPid1, DPid2, true)
-{
- dvbApi = DvbApi;
- index = NULL;
- pictureType = NO_PICTURE;
- fileSize = 0;
- recordFile = fileName.Open();
- recording = false;
- lastDiskSpaceCheck = time(NULL);
- if (!fileName.Name())
- return;
- // Create the index file:
- index = new cIndexFile(FileName, true);
- if (!index)
- esyslog("ERROR: can't allocate index");
- // let's continue without index, so we'll at least have the recording
- videoDev = dvbApi->SetModeRecord();
- Start();
-}
-
-cRecordBuffer::~cRecordBuffer()
-{
- Stop();
- dvbApi->SetModeNormal(true);
- delete index;
-}
-
-bool cRecordBuffer::RunningLowOnDiskSpace(void)
-{
- if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
- int Free = FreeDiskSpaceMB(fileName.Name());
- lastDiskSpaceCheck = time(NULL);
- if (Free < MINFREEDISKSPACE) {
- dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
- return true;
- }
- }
- return false;
-}
-
-bool cRecordBuffer::NextFile(void)
-{
- if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
- if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
- recordFile = fileName.NextFile();
- fileSize = 0;
- }
- }
- return recordFile >= 0;
-}
-
-void cRecordBuffer::Input(void)
-{
- dsyslog("input thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- time_t t = time(NULL);
- recording = true;
- for (;;) {
- if (cFile::FileReady(videoDev, 100)) {
- int r = read(videoDev, b, sizeof(b));
- if (r > 0) {
- uchar *p = b;
- while (r > 0) {
- int w = Put(p, r);
- p += w;
- r -= w;
- if (r > 0)
- usleep(1); // this keeps the CPU load low
- }
- t = time(NULL);
- }
- else if (r < 0) {
- if (FATALERRNO) {
- if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
- esyslog("ERROR (%s,%d): DVB driver buffer overflow", __FILE__, __LINE__);
- else {
- LOG_ERROR;
- break;
- }
- }
- }
- }
- if (time(NULL) - t > MAXBROKENTIMEOUT) {
- esyslog("ERROR: video data stream broken");
- cThread::EmergencyExit(true);
- t = time(NULL);
- }
- if (!recording)
- break;
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cRecordBuffer::Output(void)
-{
- dsyslog("output thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- int r = 0;
- for (;;) {
- int g = Get(b + r, sizeof(b) - r);
- if (g > 0)
- r += g;
- if (r > 0) {
- int Count = r, Result;
- const uchar *p = remux.Process(b, Count, Result, &pictureType);
- if (p) {
- if (!Busy() && pictureType == I_FRAME) // finish the recording before the next 'I' frame
- break;
- if (NextFile()) {
- if (index && pictureType != NO_PICTURE)
- index->Write(pictureType, fileName.Number(), fileSize);
- if (safe_write(recordFile, p, Result) < 0) {
- LOG_ERROR_STR(fileName.Name());
- recording = false;
- return;
- }
- fileSize += Result;
- }
- else
- break;
- }
- if (Count > 0) {
- r -= Count;
- memmove(b, b + Count, r);
- }
- if (!recording)
- break;
- }
- else
- usleep(1); // this keeps the CPU load low
- }
- recording = false;
-
- dsyslog("output thread ended (pid=%d)", getpid());
-}
-
-// --- ReadFrame -------------------------------------------------------------
-
-int ReadFrame(int f, uchar *b, int Length, int Max)
-{
- if (Length == -1)
- Length = Max; // this means we read up to EOF (see cIndex)
- else if (Length > Max) {
- esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max);
- Length = Max;
- }
- int r = safe_read(f, b, Length);
- if (r < 0)
- LOG_ERROR;
- return r;
-}
-
-// --- cBackTrace ----------------------------------------------------------
-
-#define AVG_FRAME_SIZE 15000 // an assumption about the average frame size
-#define DVB_BUF_SIZE (256 * 1024) // an assumption about the dvb firmware buffer size
-#define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20) // how many entries are needed to backtrace buffer contents
-
-class cBackTrace {
-private:
- int index[BACKTRACE_ENTRIES];
- int length[BACKTRACE_ENTRIES];
- int pos, num;
-public:
- cBackTrace(void);
- void Clear(void);
- void Add(int Index, int Length);
- int Get(bool Forward);
- };
-
-cBackTrace::cBackTrace(void)
-{
- Clear();
-}
-
-void cBackTrace::Clear(void)
-{
- pos = num = 0;
-}
-
-void cBackTrace::Add(int Index, int Length)
-{
- index[pos] = Index;
- length[pos] = Length;
- if (++pos >= BACKTRACE_ENTRIES)
- pos = 0;
- if (num < BACKTRACE_ENTRIES)
- num++;
-}
-
-int cBackTrace::Get(bool Forward)
-{
- int p = pos;
- int n = num;
- int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024); //XXX (256 * 1024) == DVB_BUF_SIZE ???
- int i = -1;
-
- while (n && l > 0) {
- if (--p < 0)
- p = BACKTRACE_ENTRIES - 1;
- i = index[p] - 1;
- l -= length[p];
- n--;
- }
- return i;
-}
-
-// --- cPlayBuffer ---------------------------------------------------------
-
-#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
-
-class cPlayBuffer : public cRingBufferFrame {
-private:
- cBackTrace backTrace;
-protected:
- enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
- enum ePlayDirs { pdForward, pdBackward };
- static int Speeds[];
- cDvbApi *dvbApi;
- int videoDev, audioDev;
- cPipe dolbyDev;
- int blockInput, blockOutput;
- ePlayModes playMode;
- ePlayDirs playDir;
- int trickSpeed;
- int readIndex, writeIndex;
- bool canDoTrickMode;
- bool canToggleAudioTrack;
- bool skipAC3bytes;
- uchar audioTrack;
- void TrickSpeed(int Increment);
- virtual void Empty(bool Block = false);
- virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00) {}
- virtual void PlayExternalDolby(const uchar *b, int MaxLength);
- virtual void Output(void);
- void putFrame(cFrame *Frame);
- void putFrame(unsigned char *Data, int Length, eFrameType Type = ftUnknown);
-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 GetReplayMode(bool &Play, bool &Forward, int &Speed);
- bool CanToggleAudioTrack(void) { return canToggleAudioTrack; };
- virtual void ToggleAudioTrack(void);
- };
-
-#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
-#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
-#define SPEED_MULT 12 // the speed multiplier
-int cPlayBuffer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
-
-cPlayBuffer::cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev)
-:cRingBufferFrame(VIDEOBUFSIZE)
-{
- dvbApi = DvbApi;
- videoDev = VideoDev;
- audioDev = AudioDev;
- blockInput = blockOutput = false;
- playMode = pmPlay;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- readIndex = writeIndex = -1;
- canDoTrickMode = false;
- canToggleAudioTrack = false;
- skipAC3bytes = false;
- audioTrack = 0xC0;
-}
-
-cPlayBuffer::~cPlayBuffer()
-{
-}
-
-void cPlayBuffer::PlayExternalDolby(const uchar *b, int MaxLength)
-{
- if (cDvbApi::AudioCommand()) {
- if (!dolbyDev && !dolbyDev.Open(cDvbApi::AudioCommand(), "w")) {
- esyslog("ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand());
- return;
- }
- if (b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01) {
- if (b[3] == 0xBD) { // dolby
- int l = b[4] * 256 + b[5] + 6;
- int written = b[8] + (skipAC3bytes ? 13 : 9); // skips the PES header
- int n = min(l - written, MaxLength);
- while (n > 0) {
- int w = fwrite(&b[written], 1, n, dolbyDev);
- if (w < 0) {
- LOG_ERROR;
- break;
- }
- n -= w;
- written += w;
- }
- }
- }
- }
-}
-
-void cPlayBuffer::Output(void)
-{
- dsyslog("output thread started (pid=%d)", getpid());
-
- while (Busy()) {
- if (blockOutput) {
- if (blockOutput > 1)
- blockOutput = 1;
- continue;
- }
- const cFrame *frame = Get();
- if (frame) {
- if (frame->Type() == ftDolby)
- PlayExternalDolby(frame->Data(), frame->Count());
- else {
- StripAudioPackets((uchar *)frame->Data(), frame->Count(), (playMode == pmFast || playMode == pmSlow) ? 0x00 : audioTrack);//XXX
- const uchar *p = frame->Data();
- int r = frame->Count();
- while (r > 0 && Busy() && !blockOutput) {
- if (cFile::FileReadyForWriting(videoDev, 100)) {
- int w = write(videoDev, p, r);
- if (w > 0) {
- p += w;
- r -= w;
- }
- else if (w < 0 && FATALERRNO) {
- LOG_ERROR;
- Stop();
- return;
- }
- }
- }
- writeIndex = frame->Index();
- backTrace.Add(frame->Index(), frame->Count());
- }
- Drop(frame);
- }
- }
-
- dsyslog("output thread ended (pid=%d)", getpid());
-}
-
-void cPlayBuffer::putFrame(cFrame *Frame)
-{
- while (Busy() && !blockInput) {
- if (Put(Frame))
- return;
- }
- delete Frame; // caller relies on frame being put, otherwise this would be a memory leak!
-}
-
-void cPlayBuffer::putFrame(unsigned char *Data, int Length, eFrameType Type)
-{
- putFrame(new cFrame(Data, Length, Type));
-}
-
-void cPlayBuffer::TrickSpeed(int Increment)
-{
- int nts = trickSpeed + Increment;
- if (Speeds[nts] == 1) {
- trickSpeed = nts;
- if (playMode == pmFast)
- Play();
- else
- Pause();
- }
- else if (Speeds[nts]) {
- trickSpeed = nts;
- int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
- int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
- if (sp > MAX_VIDEO_SLOWMOTION)
- sp = MAX_VIDEO_SLOWMOTION;
- CHECK(ioctl(videoDev, VIDEO_SLOWMOTION, sp));
- }
-}
-
-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();
- if ((readIndex = backTrace.Get(playDir == pdForward)) < 0)
- readIndex = writeIndex;
- cRingBufferFrame::Clear();
- CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
- CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
- }
- if (!Block) {
- blockInput = blockOutput = 0;
- backTrace.Clear();
- Unlock();
- }
-}
-
-void cPlayBuffer::Pause(void)
-{
- if (playMode == pmPause || playMode == pmStill)
- Play();
- else {
- bool empty = (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward));
- if (empty)
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- CHECK(ioctl(videoDev, VIDEO_FREEZE));
- playMode = pmPause;
- if (empty)
- Empty(false);
- }
-}
-
-void cPlayBuffer::Play(void)
-{
- if (playMode != pmPlay) {
- bool empty = (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward));
- if (empty)
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- playMode = pmPlay;
- playDir = pdForward;
- if (empty)
- Empty(false);
- }
-}
-
-void cPlayBuffer::Forward(void)
-{
- if (canDoTrickMode) {
- switch (playMode) {
- case pmFast:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdForward ? 1 : -1);
- break;
- }
- else if (playDir == pdForward) {
- Play();
- break;
- }
- // run into pmPlay
- case pmPlay:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmFast;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
- Empty(false);
- break;
- case pmSlow:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdForward ? -1 : 1);
- break;
- }
- else if (playDir == pdForward) {
- Pause();
- break;
- }
- // run into pmPause
- case pmStill:
- case pmPause:
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmSlow;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
- break;
- }
- }
-}
-
-void cPlayBuffer::Backward(void)
-{
- if (canDoTrickMode) {
- switch (playMode) {
- case pmFast:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdBackward ? 1 : -1);
- break;
- }
- else if (playDir == pdBackward) {
- Play();
- break;
- }
- // run into pmPlay
- case pmPlay:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmFast;
- playDir = pdBackward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
- Empty(false);
- break;
- case pmSlow:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdBackward ? -1 : 1);
- break;
- }
- else if (playDir == pdBackward) {
- Pause();
- break;
- }
- // run into pmPause
- case pmStill:
- case pmPause:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmSlow;
- playDir = pdBackward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
- Empty(false);
- break;
- }
- }
-}
-
-bool cPlayBuffer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
-{
- Play = (playMode == pmPlay || playMode == pmFast);
- Forward = (playDir == pdForward);
- if (playMode == pmFast || playMode == pmSlow)
- Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
- else
- Speed = -1;
- return true;
-}
-
-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 Close(void);
- 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);
-public:
- cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName);
- virtual ~cReplayBuffer();
- 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)
-:cPlayBuffer(DvbApi, VideoDev, AudioDev)
-,fileName(FileName, false)
-{
- index = NULL;
- replayFile = fileName.Open();
- eof = false;
- if (!fileName.Name())
- return;
- // Create the index file:
- index = new cIndexFile(FileName, false);
- if (!index)
- esyslog("ERROR: can't allocate index");
- else if (!index->Ok()) {
- delete index;
- index = NULL;
- }
- canDoTrickMode = index != NULL;
- dvbApi->SetModeReplay();
- Start();
-}
-
-cReplayBuffer::~cReplayBuffer()
-{
- Stop();
- Save();
- Close();
- dvbApi->SetModeNormal(false);
- delete index;
-}
-
-void cReplayBuffer::Input(void)
-{
- dsyslog("input thread started (pid=%d)", getpid());
-
- readIndex = Resume();
- if (readIndex >= 0)
- isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
-
- uchar b[MAXFRAMESIZE];
- while (Busy() && (blockInput || NextFile())) {
- if (blockInput) {
- if (blockInput > 1)
- blockInput = 1;
- continue;
- }
- if (playMode != pmStill) {
- int r = 0;
- if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
- uchar FileNumber;
- int FileOffset, Length;
- int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
- if (Index >= 0) {
- if (!NextFile(FileNumber, FileOffset))
- break;
- }
- else {
- // can't call Play() here, because those functions may only be
- // called from the foreground thread - and we also don't need
- // to empty the buffer here
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- playMode = pmPlay;
- playDir = pdForward;
- continue;
- }
- readIndex = Index;
- r = ReadFrame(replayFile, b, Length, sizeof(b));
- // must call StripAudioPackets() here because the buffer is not emptied
- // when falling back from "fast forward" to "play" (see above)
- StripAudioPackets(b, r);
- }
- else if (index) {
- uchar FileNumber;
- int FileOffset, Length;
- readIndex++;
- if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
- break;
- r = ReadFrame(replayFile, b, Length, sizeof(b));
- }
- else // allows replay even if the index file is missing
- r = read(replayFile, b, sizeof(b));
- if (r > 0)
- putFrame(new cFrame(b, r, ftUnknown, readIndex));
- else if (r == 0)
- eof = true;
- else if (r < 0 && FATALERRNO) {
- LOG_ERROR;
- break;
- }
- }
- else//XXX
- usleep(1); // this keeps the CPU load low
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except)
-{
- 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)
- PlayExternalDolby(&b[i], Length - i);
- // 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("ERROR: unexpected packet id %02X", c);
- l = 0;
- }
- if (l)
- i += l - 1; // the loop increments, too!
- }
- /*XXX
- else
- esyslog("ERROR: broken packet header");
- XXX*/
- }
- }
-}
-
-void cReplayBuffer::DisplayFrame(uchar *b, int Length)
-{
- StripAudioPackets(b, Length);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- CHECK(ioctl(audioDev, AUDIO_SET_MUTE, true));
-/* Using the VIDEO_STILLPICTURE ioctl call would be the
- correct way to display a still frame, but unfortunately this
- doesn't work with frames from VDR. So let's do pretty much the
- same here as in DVB/driver/dvb.c's play_iframe() - I have absolutely
- no idea why it works this way, but doesn't work with VIDEO_STILLPICTURE.
- If anybody ever finds out what could be changed so that VIDEO_STILLPICTURE
- could be used, please let me know!
- kls 2002-03-23
-*/
-//#define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
-#ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
- videoDisplayStillPicture sp = { (char *)b, Length };
- CHECK(ioctl(videoDev, VIDEO_STILLPICTURE, &sp));
-#else
-#define MIN_IFRAME 400000
- for (int i = MIN_IFRAME / Length + 1; i > 0; i--) {
- safe_write(videoDev, b, Length);
- usleep(1); // allows the buffer to be displayed in case the progress display is active
- }
-#endif
-}
-
-void cReplayBuffer::Close(void)
-{
- if (replayFile >= 0) {
- fileName.Close();
- replayFile = -1;
- }
-}
-
-int cReplayBuffer::Resume(void)
-{
- if (index) {
- int Index = index->GetResume();
- if (Index >= 0) {
- uchar FileNumber;
- int FileOffset;
- if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
- return Index;
- }
- }
- return -1;
-}
-
-bool cReplayBuffer::Save(void)
-{
- if (index) {
- int Index = writeIndex;
- if (Index >= 0) {
- Index -= RESUMEBACKUP;
- if (Index > 0)
- Index = index->GetNextIFrame(Index, false);
- else
- Index = 0;
- if (Index >= 0)
- return index->StoreResume(Index);
- }
- }
- return false;
-}
-
-int cReplayBuffer::SkipFrames(int Frames)
-{
- if (index && Frames) {
- int Current, Total;
- GetIndex(Current, Total, true);
- int OldCurrent = Current;
- Current = index->GetNextIFrame(Current + Frames, Frames > 0);
- return Current >= 0 ? Current : OldCurrent;
- }
- return -1;
-}
-
-void cReplayBuffer::SkipSeconds(int Seconds)
-{
- if (index && Seconds) {
- Empty(true);
- int Index = writeIndex;
- if (Index >= 0) {
- Index = max(Index + Seconds * FRAMESPERSEC, 0);
- if (Index > 0)
- Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
- if (Index >= 0)
- readIndex = writeIndex = Index - 1; // Input() will first increment it!
- }
- Empty(false);
- Play();
- }
-}
-
-void cReplayBuffer::Goto(int Index, bool Still)
-{
- if (index) {
- Empty(true);
- if (++Index <= 0)
- Index = 1; // not '0', to allow GetNextIFrame() below to work!
- uchar FileNumber;
- int FileOffset, Length;
- Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
- if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
- uchar b[MAXFRAMESIZE];
- int r = ReadFrame(replayFile, b, Length, sizeof(b));
- if (r > 0) {
- if (playMode == pmPause)
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- DisplayFrame(b, r);
- }
- playMode = pmStill;
- }
- readIndex = writeIndex = Index;
- Empty(false);
- }
-}
-
-void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
-{
- if (index) {
- if (playMode == pmStill)
- Current = max(readIndex, 0);
- else {
- Current = max(writeIndex, 0);
- if (SnapToIFrame) {
- int i1 = index->GetNextIFrame(Current + 1, false);
- int i2 = index->GetNextIFrame(Current, true);
- Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
- }
- }
- Total = index->Last();
- }
- else
- Current = Total = -1;
-}
-
-bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
-{
- if (FileNumber > 0)
- replayFile = fileName.SetOffset(FileNumber, FileOffset);
- else if (replayFile >= 0 && eof) {
- Close();
- replayFile = fileName.NextFile();
- }
- eof = false;
- return replayFile >= 0;
-}
-
-// --- cTransferBuffer -------------------------------------------------------
-
-class cTransferBuffer : public cRingBufferLinear {
-private:
- cDvbApi *dvbApi;
- int fromDevice, toDevice;
- bool gotBufferReserve;
- cRemux remux;
-protected:
- virtual void Input(void);
- virtual void Output(void);
-public:
- cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid);
- virtual ~cTransferBuffer();
- void SetAudioPid(int APid);
- };
-
-cTransferBuffer::cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid)
-:cRingBufferLinear(VIDEOBUFSIZE, true)
-,remux(VPid, APid, 0, 0, 0)
-{
- dvbApi = DvbApi;
- fromDevice = dvbApi->SetModeRecord();
- toDevice = ToDevice;
- gotBufferReserve = false;
- Start();
-}
-
-cTransferBuffer::~cTransferBuffer()
-{
- Stop();
- dvbApi->SetModeNormal(true);
-}
-
-void cTransferBuffer::SetAudioPid(int APid)
-{
- Clear();
- //XXX we may need to have access to the audio device, too, in order to clear it
- CHECK(ioctl(toDevice, VIDEO_CLEAR_BUFFER));
- gotBufferReserve = false;
- remux.SetAudioPid(APid);
-}
-
-void cTransferBuffer::Input(void)
-{
- dsyslog("input thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- int n = 0;
- while (Busy()) {
- if (cFile::FileReady(fromDevice, 100)) {
- int r = read(fromDevice, b + n, sizeof(b) - n);
- if (r > 0) {
- n += r;
- int Count = n, Result;
- const uchar *p = remux.Process(b, Count, Result);
- if (p) {
- while (Result > 0 && Busy()) {
- int w = Put(p, Result);
- p += w;
- Result -= w;
- }
- }
- if (Count > 0) {
- n -= Count;
- memmove(b, b + Count, n);
- }
- }
- else if (r < 0) {
- if (FATALERRNO) {
- if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
- esyslog("ERROR (%s,%d): DVB driver buffer overflow", __FILE__, __LINE__);
- else {
- LOG_ERROR;
- break;
- }
- }
- }
- }
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cTransferBuffer::Output(void)
-{
- dsyslog("output thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- while (Busy()) {
- if (!gotBufferReserve) {
- if (Available() < MAXFRAMESIZE) {
- usleep(100000); // allow the buffer to collect some reserve
- continue;
- }
- else
- gotBufferReserve = true;
- }
- int r = Get(b, sizeof(b));
- if (r > 0) {
- uchar *p = b;
- while (r > 0 && Busy() && cFile::FileReadyForWriting(toDevice, 100)) {
- int w = write(toDevice, p, r);
- if (w > 0) {
- p += w;
- r -= w;
- }
- else if (w < 0 && FATALERRNO) {
- LOG_ERROR;
- Stop();
- return;
- }
- }
- }
- else
- usleep(1); // this keeps the CPU load low
- }
-
- dsyslog("output thread ended (pid=%d)", getpid());
-}
-
-// --- cCuttingBuffer --------------------------------------------------------
-
-class cCuttingBuffer : public cThread {
-private:
- const char *error;
- bool active;
- int fromFile, toFile;
- cFileName *fromFileName, *toFileName;
- cIndexFile *fromIndex, *toIndex;
- cMarks fromMarks, toMarks;
-protected:
- virtual void Action(void);
-public:
- cCuttingBuffer(const char *FromFileName, const char *ToFileName);
- virtual ~cCuttingBuffer();
- const char *Error(void) { return error; }
- };
-
-cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
-{
- error = NULL;
- active = false;
- fromFile = toFile = -1;
- fromFileName = toFileName = NULL;
- fromIndex = toIndex = NULL;
- if (fromMarks.Load(FromFileName) && fromMarks.Count()) {
- fromFileName = new cFileName(FromFileName, false, true);
- toFileName = new cFileName(ToFileName, true, true);
- fromIndex = new cIndexFile(FromFileName, false);
- toIndex = new cIndexFile(ToFileName, true);
- toMarks.Load(ToFileName); // doesn't actually load marks, just sets the file name
- Start();
- }
- else
- esyslog("no editing marks found for %s", FromFileName);
-}
-
-cCuttingBuffer::~cCuttingBuffer()
-{
- active = false;
- Cancel(3);
- delete fromFileName;
- delete toFileName;
- delete fromIndex;
- delete toIndex;
-}
-
-void cCuttingBuffer::Action(void)
-{
- dsyslog("video cutting thread started (pid=%d)", getpid());
-
- cMark *Mark = fromMarks.First();
- if (Mark) {
- fromFile = fromFileName->Open();
- toFile = toFileName->Open();
- active = fromFile >= 0 && toFile >= 0;
- int Index = Mark->position;
- Mark = fromMarks.Next(Mark);
- int FileSize = 0;
- int CurrentFileNumber = 0;
- int LastIFrame = 0;
- toMarks.Add(0);
- toMarks.Save();
- uchar buffer[MAXFRAMESIZE];
- while (active) {
- uchar FileNumber;
- int FileOffset, Length;
- uchar PictureType;
-
- // Make sure there is enough disk space:
-
- AssertFreeDiskSpace();
-
- // Read one frame:
-
- if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
- if (FileNumber != CurrentFileNumber) {
- fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
- CurrentFileNumber = FileNumber;
- }
- if (fromFile >= 0) {
- Length = ReadFrame(fromFile, buffer, Length, sizeof(buffer));
- if (Length < 0) {
- error = "ReadFrame";
- break;
- }
- }
- else {
- error = "fromFile";
- break;
- }
- }
- else
- break;
-
- // Write one frame:
-
- if (PictureType == I_FRAME) { // every file shall start with an I_FRAME
- if (!Mark) // edited version shall end before next I-frame
- break;
- if (FileSize > MEGABYTE(Setup.MaxVideoFileSize)) {
- toFile = toFileName->NextFile();
- if (toFile < 0) {
- error = "toFile 1";
- break;
- }
- FileSize = 0;
- }
- LastIFrame = 0;
- }
- if (safe_write(toFile, buffer, Length) < 0) {
- error = "safe_write";
- break;
- }
- if (!toIndex->Write(PictureType, toFileName->Number(), FileSize)) {
- error = "toIndex";
- break;
- }
- FileSize += Length;
- if (!LastIFrame)
- LastIFrame = toIndex->Last();
-
- // Check editing marks:
-
- if (Mark && Index >= Mark->position) {
- Mark = fromMarks.Next(Mark);
- toMarks.Add(LastIFrame);
- if (Mark)
- toMarks.Add(toIndex->Last() + 1);
- toMarks.Save();
- if (Mark) {
- Index = Mark->position;
- Mark = fromMarks.Next(Mark);
- CurrentFileNumber = 0; // triggers SetOffset before reading next frame
- if (Setup.SplitEditedFiles) {
- toFile = toFileName->NextFile();
- if (toFile < 0) {
- error = "toFile 2";
- break;
- }
- FileSize = 0;
- }
- }
- // the 'else' case (i.e. 'final end mark reached') is handled above
- // in 'Write one frame', so that the edited version will end right
- // before the next I-frame.
- }
- }
- }
- else
- esyslog("no editing marks found!");
- dsyslog("end video cutting thread");
-}
-
-// --- cVideoCutter ----------------------------------------------------------
-
-char *cVideoCutter::editedVersionName = NULL;
-cCuttingBuffer *cVideoCutter::cuttingBuffer = NULL;
-bool cVideoCutter::error = false;
-bool cVideoCutter::ended = false;
-
-bool cVideoCutter::Start(const char *FileName)
-{
- if (!cuttingBuffer) {
- error = false;
- ended = false;
- cRecording Recording(FileName);
- const char *evn = Recording.PrefixFileName('%');
- if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
- // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
- // remove a possible deleted recording with the same name to avoid symlink mixups:
- char *s = strdup(evn);
- char *e = strrchr(s, '.');
- if (e) {
- if (strcmp(e, ".rec") == 0) {
- strcpy(e, ".del");
- RemoveVideoFile(s);
- }
- }
- delete s;
- // XXX
- editedVersionName = strdup(evn);
- Recording.WriteSummary();
- cuttingBuffer = new cCuttingBuffer(FileName, editedVersionName);
- return true;
- }
- }
- return false;
-}
-
-void cVideoCutter::Stop(void)
-{
- bool Interrupted = cuttingBuffer && cuttingBuffer->Active();
- const char *Error = cuttingBuffer ? cuttingBuffer->Error() : NULL;
- delete cuttingBuffer;
- cuttingBuffer = NULL;
- if ((Interrupted || Error) && editedVersionName) {
- if (Interrupted)
- isyslog("editing process has been interrupted");
- if (Error)
- esyslog("ERROR: '%s' during editing process", Error);
- RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
- }
-}
-
-bool cVideoCutter::Active(void)
-{
- if (cuttingBuffer) {
- if (cuttingBuffer->Active())
- return true;
- error = cuttingBuffer->Error();
- Stop();
- if (!error)
- cRecordingUserCommand::InvokeCommand(RUC_EDITEDRECORDING, editedVersionName);
- delete editedVersionName;
- editedVersionName = NULL;
- ended = true;
- }
- return false;
-}
-
-bool cVideoCutter::Error(void)
-{
- bool result = error;
- error = false;
- return result;
-}
-
-bool cVideoCutter::Ended(void)
-{
- bool result = ended;
- ended = false;
- return result;
-}
-
-// --- cDvbApi ---------------------------------------------------------------
-
-static const char *OstName(const char *Name, int n)
-{
- static char buffer[_POSIX_PATH_MAX];
- snprintf(buffer, sizeof(buffer), "%s%d", Name, n);
- return buffer;
-}
-
-static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false)
-{
- const char *FileName = OstName(Name, n);
- int fd = open(FileName, Mode);
- if (fd < 0 && ReportError)
- LOG_ERROR_STR(FileName);
- return fd;
-}
-
-int cDvbApi::NumDvbApis = 0;
-int cDvbApi::useDvbApi = 0;
-cDvbApi *cDvbApi::dvbApi[MAXDVBAPI] = { NULL };
-cDvbApi *cDvbApi::PrimaryDvbApi = NULL;
-char *cDvbApi::audioCommand = NULL;
-
-cDvbApi::cDvbApi(int n)
-{
- frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
- vPid = aPid1 = aPid2 = dPid1 = dPid2 = 0;
- siProcessor = NULL;
- recordBuffer = NULL;
- replayBuffer = NULL;
- transferBuffer = NULL;
- transferringFromDvbApi = NULL;
- ca = -1;
- priority = DEFAULTPRIORITY;
- cardIndex = n;
-
- // Devices that are only present on DVB-C or DVB-S cards:
-
- fd_frontend = OstOpen(DEV_OST_FRONTEND, n, O_RDWR);
- fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR);
-
- // Devices that all DVB cards must have:
-
- fd_demuxv = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxa1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxa2 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxd1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxd2 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxt = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
-
- // Devices not present on "budget" cards:
-
- fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR);
- fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK);
- fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK);
-
- // Devices that will be dynamically opened and closed when necessary:
-
- fd_dvr = -1;
-
- // Video format:
-
- SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
-
- // We only check the devices that must be present - the others will be checked before accessing them:
-
- if (fd_frontend >= 0 && fd_demuxv >= 0 && fd_demuxa1 >= 0 && fd_demuxa2 >= 0 && fd_demuxd1 >= 0 && fd_demuxd2 >= 0 && fd_demuxt >= 0) {
- siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n));
- FrontendInfo feinfo;
- CHECK(ioctl(fd_frontend, FE_GET_INFO, &feinfo));
- frontendType = feinfo.type;
- }
- else
- esyslog("ERROR: can't open video device %d", n);
- cols = rows = 0;
-
-#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
- initscr();
- keypad(stdscr, true);
- nonl();
- cbreak();
- noecho();
- timeout(10);
-#endif
-#if defined(DEBUG_OSD)
- memset(&colorPairs, 0, sizeof(colorPairs));
- start_color();
- leaveok(stdscr, true);
- window = NULL;
-#else
- osd = NULL;
-#endif
- currentChannel = 1;
- mute = false;
- volume = Setup.CurrentVolume;
-}
-
-cDvbApi::~cDvbApi()
-{
- delete siProcessor;
- Close();
- StopReplay();
- StopRecord();
- StopTransfer();
- // We're not explicitly closing any device files here, since this sometimes
- // caused segfaults. Besides, the program is about to terminate anyway...
-#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
- endwin();
-#endif
-}
-
-void cDvbApi::SetUseDvbApi(int n)
-{
- if (n < MAXDVBAPI)
- useDvbApi |= (1 << n);
-}
-
-bool cDvbApi::SetPrimaryDvbApi(int n)
-{
- n--;
- if (0 <= n && n < NumDvbApis && dvbApi[n]) {
- isyslog("setting primary DVB to %d", n + 1);
- PrimaryDvbApi = dvbApi[n];
- return true;
- }
- esyslog("invalid DVB interface: %d", n + 1);
- return false;
-}
-
-int cDvbApi::CanShift(int Ca, int Priority, int UsedCards)
-{
- // Test whether a recording on this DVB device can be shifted to another one
- // in order to perform a new recording with the given Ca and Priority on this device:
- int ShiftLevel = -1; // default means this device can't be shifted
- if (UsedCards & (1 << CardIndex()) != 0)
- return ShiftLevel; // otherwise we would get into a loop
- if (Recording()) {
- if (ProvidesCa(Ca) // this device provides the requested Ca
- && (Ca != this->Ca() // the requested Ca is different from the one currently used...
- || Priority > this->Priority())) { // ...or the request comes from a higher priority
- cDvbApi *d = NULL;
- int Provides[MAXDVBAPI];
- UsedCards |= (1 << CardIndex());
- for (int i = 0; i < NumDvbApis; i++) {
- if ((Provides[i] = dvbApi[i]->ProvidesCa(this->Ca())) != 0) { // this device is basicly able to do the job
- if (dvbApi[i] != this) { // it is not _this_ device
- int sl = dvbApi[i]->CanShift(this->Ca(), Priority, UsedCards); // this is the original Priority!
- if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) {
- d = dvbApi[i];
- ShiftLevel = sl;
- }
- }
- }
- }
- if (ShiftLevel >= 0)
- ShiftLevel++; // adds the device's own shift
- }
- }
- else if (Priority > this->Priority())
- ShiftLevel = 0; // no shifting necessary, this device can do the job
- return ShiftLevel;
-}
-
-cDvbApi *cDvbApi::GetDvbApi(int Ca, int Priority)
-{
- cDvbApi *d = NULL;
- int Provides[MAXDVBAPI];
- // Check which devices provide Ca:
- for (int i = 0; i < NumDvbApis; i++) {
- if ((Provides[i] = dvbApi[i]->ProvidesCa(Ca)) != 0) { // this device is basicly able to do the job
- if (Priority > dvbApi[i]->Priority() // Priority is high enough to use this device
- && (!d // we don't have a device yet, or...
- || dvbApi[i]->Priority() < d->Priority() // ...this one has an even lower Priority
- || (dvbApi[i]->Priority() == d->Priority() // ...same Priority...
- && Provides[i] < Provides[d->CardIndex()]))) // ...but this one provides fewer Ca values
- d = dvbApi[i];
- }
- }
- if (!d && Ca > MAXDVBAPI) {
- // We didn't find one the easy way, so now we have to try harder:
- int ShiftLevel = -1;
- for (int i = 0; i < NumDvbApis; i++) {
- if (Provides[i]) { // this device is basicly able to do the job, but for some reason we didn't get it above
- int sl = dvbApi[i]->CanShift(Ca, Priority); // asks this device to shift its job to another device
- if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) {
- d = dvbApi[i]; // found one that can be shifted with the fewest number of subsequent shifts
- ShiftLevel = sl;
- }
- }
- }
- }
- return d;
-}
-
-void cDvbApi::SetCaCaps(void)
-{
- for (int d = 0; d < NumDvbApis; d++) {
- for (int i = 0; i < MAXCACAPS; i++)
- dvbApi[d]->caCaps[i] = Setup.CaCaps[dvbApi[d]->CardIndex()][i];
- }
-}
-
-int cDvbApi::ProvidesCa(int Ca)
-{
- if (Ca == CardIndex() + 1)
- return 1; // exactly _this_ card was requested
- if (Ca && Ca <= MAXDVBAPI)
- return 0; // a specific card was requested, but not _this_ one
- int result = Ca ? 0 : 1; // by default every card can provide FTA
- int others = Ca ? 1 : 0;
- for (int i = 0; i < MAXCACAPS; i++) {
- if (caCaps[i]) {
- if (caCaps[i] == Ca)
- result = 1;
- else
- others++;
- }
- }
- return result ? result + others : 0;
-}
-
-bool cDvbApi::Probe(const char *FileName)
-{
- if (access(FileName, F_OK) == 0) {
- dsyslog("probing %s", FileName);
- int f = open(FileName, O_RDONLY);
- if (f >= 0) {
- close(f);
- return true;
- }
- else if (errno != ENODEV && errno != EINVAL)
- LOG_ERROR_STR(FileName);
- }
- else if (errno != ENOENT)
- LOG_ERROR_STR(FileName);
- return false;
-}
-
-bool cDvbApi::Init(void)
-{
- NumDvbApis = 0;
- for (int i = 0; i < MAXDVBAPI; i++) {
- if (useDvbApi == 0 || (useDvbApi & (1 << i)) != 0) {
- if (Probe(OstName(DEV_OST_FRONTEND, i)))
- dvbApi[NumDvbApis++] = new cDvbApi(i);
- else
- break;
- }
- }
- PrimaryDvbApi = dvbApi[0];
- if (NumDvbApis > 0)
- isyslog("found %d video device%s", NumDvbApis, NumDvbApis > 1 ? "s" : "");
- else
- esyslog("ERROR: no video device found, giving up!");
- SetCaCaps();
- return NumDvbApis > 0;
-}
-
-void cDvbApi::Cleanup(void)
-{
- for (int i = 0; i < NumDvbApis; i++) {
- delete dvbApi[i];
- dvbApi[i] = NULL;
- }
- PrimaryDvbApi = NULL;
-}
-
-bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
-{
- int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR, true);
- if (videoDev >= 0) {
- int result = 0;
- struct video_mbuf mbuf;
- result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf);
- if (result == 0) {
- int msize = mbuf.size;
- unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
- if (mem && mem != (unsigned char *)-1) {
- // set up the size and RGB
- struct video_capability vc;
- result |= ioctl(videoDev, VIDIOCGCAP, &vc);
- struct video_mmap vm;
- vm.frame = 0;
- if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
- (SizeY > 0) && (SizeY <= vc.maxheight)) {
- vm.width = SizeX;
- vm.height = SizeY;
- }
- else {
- vm.width = vc.maxwidth;
- vm.height = vc.maxheight;
- }
- vm.format = VIDEO_PALETTE_RGB24;
- result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm);
- result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame);
- // make RGB out of BGR:
- int memsize = vm.width * vm.height;
- unsigned char *mem1 = mem;
- for (int i = 0; i < memsize; i++) {
- unsigned char tmp = mem1[2];
- mem1[2] = mem1[0];
- mem1[0] = tmp;
- mem1 += 3;
- }
-
- if (Quality < 0)
- Quality = 255; //XXX is this 'best'???
-
- isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
- FILE *f = fopen(FileName, "wb");
- if (f) {
- if (Jpeg) {
- // write JPEG file:
- struct jpeg_compress_struct cinfo;
- struct jpeg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_compress(&cinfo);
- jpeg_stdio_dest(&cinfo, f);
- cinfo.image_width = vm.width;
- cinfo.image_height = vm.height;
- cinfo.input_components = 3;
- cinfo.in_color_space = JCS_RGB;
-
- jpeg_set_defaults(&cinfo);
- jpeg_set_quality(&cinfo, Quality, true);
- jpeg_start_compress(&cinfo, true);
-
- int rs = vm.width * 3;
- JSAMPROW rp[vm.height];
- for (int k = 0; k < vm.height; k++)
- rp[k] = &mem[rs * k];
- jpeg_write_scanlines(&cinfo, rp, vm.height);
- jpeg_finish_compress(&cinfo);
- jpeg_destroy_compress(&cinfo);
- }
- else {
- // write PNM file:
- if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 ||
- fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) {
- LOG_ERROR_STR(FileName);
- result |= 1;
- }
- }
- fclose(f);
- }
- else {
- LOG_ERROR_STR(FileName);
- result |= 1;
- }
- munmap(mem, msize);
- }
- else
- result |= 1;
- }
- close(videoDev);
- return result == 0;
- }
- return false;
-}
-
-#ifdef DEBUG_OSD
-void cDvbApi::SetColor(eDvbColor colorFg, eDvbColor colorBg)
-{
- int color = (colorBg << 16) | colorFg | 0x80000000;
- for (int i = 0; i < MaxColorPairs; i++) {
- if (!colorPairs[i]) {
- colorPairs[i] = color;
- init_pair(i + 1, colorFg, colorBg);
- wattrset(window, COLOR_PAIR(i + 1));
- break;
- }
- else if (color == colorPairs[i]) {
- wattrset(window, COLOR_PAIR(i + 1));
- break;
- }
- }
-}
-#endif
-
-void cDvbApi::Open(int w, int h)
-{
- int d = (h < 0) ? Setup.OSDheight + h : 0;
- h = abs(h);
- cols = w;
- rows = h;
-#ifdef DEBUG_OSD
- window = subwin(stdscr, h, w, d, (Setup.OSDwidth - w) / 2);
- syncok(window, true);
- #define B2C(b) (((b) * 1000) / 255)
- #define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b))
- //XXX
- SETCOLOR(clrBackground, 0x00, 0x00, 0x00, 127); // background 50% gray
- SETCOLOR(clrBlack, 0x00, 0x00, 0x00, 255);
- SETCOLOR(clrRed, 0xFC, 0x14, 0x14, 255);
- SETCOLOR(clrGreen, 0x24, 0xFC, 0x24, 255);
- SETCOLOR(clrYellow, 0xFC, 0xC0, 0x24, 255);
- SETCOLOR(clrBlue, 0x00, 0x00, 0xFC, 255);
- SETCOLOR(clrCyan, 0x00, 0xFC, 0xFC, 255);
- SETCOLOR(clrMagenta, 0xB0, 0x00, 0xFC, 255);
- SETCOLOR(clrWhite, 0xFC, 0xFC, 0xFC, 255);
-#else
- w *= charWidth;
- h *= lineHeight;
- d *= lineHeight;
- int x = (720 - w + charWidth) / 2; //TODO PAL vs. NTSC???
- int y = (576 - Setup.OSDheight * lineHeight) / 2 + d;
- //XXX
- osd = new cDvbOsd(fd_osd, x, y);
- //XXX TODO this should be transferred to the places where the individual windows are requested (there's too much detailed knowledge here!)
- if (h / lineHeight == 5) { //XXX channel display
- osd->Create(0, 0, w, h, 4);
- }
- else if (h / lineHeight == 1) { //XXX info display
- osd->Create(0, 0, w, h, 4);
- }
- else if (d == 0) { //XXX full menu
- osd->Create(0, 0, w, lineHeight, 2);
- osd->Create(0, lineHeight, w, (Setup.OSDheight - 3) * lineHeight, 2);
- osd->AddColor(clrBackground);
- osd->AddColor(clrCyan);
- osd->AddColor(clrWhite);
- osd->AddColor(clrBlack);
- osd->Create(0, (Setup.OSDheight - 2) * lineHeight, w, 2 * lineHeight, 4);
- }
- else { //XXX progress display
- /*XXX
- osd->Create(0, 0, w, lineHeight, 1);
- osd->Create(0, lineHeight, w, lineHeight, 2, false);
- osd->Create(0, 2 * lineHeight, w, lineHeight, 1);
- XXX*///XXX some pixels are not drawn correctly with lower bpp values
- osd->Create(0, 0, w, 3*lineHeight, 4);
- }
-#endif
-}
-
-void cDvbApi::Close(void)
-{
-#ifdef DEBUG_OSD
- if (window) {
- delwin(window);
- window = 0;
- }
-#else
- delete osd;
- osd = NULL;
-#endif
-}
-
-void cDvbApi::Clear(void)
-{
-#ifdef DEBUG_OSD
- SetColor(clrBackground, clrBackground);
- Fill(0, 0, cols, rows, clrBackground);
-#else
- osd->Clear();
-#endif
-}
-
-void cDvbApi::Fill(int x, int y, int w, int h, eDvbColor color)
-{
- if (x < 0) x = cols + x;
- if (y < 0) y = rows + y;
-#ifdef DEBUG_OSD
- SetColor(color, color);
- for (int r = 0; r < h; r++) {
- wmove(window, y + r, x); // ncurses wants 'y' before 'x'!
- whline(window, ' ', w);
- }
- wsyncup(window); // shouldn't be necessary because of 'syncok()', but w/o it doesn't work
-#else
- osd->Fill(x * charWidth, y * lineHeight, (x + w) * charWidth - 1, (y + h) * lineHeight - 1, color);
-#endif
-}
-
-void cDvbApi::SetBitmap(int x, int y, const cBitmap &Bitmap)
-{
-#ifndef DEBUG_OSD
- osd->SetBitmap(x, y, Bitmap);
-#endif
-}
-
-void cDvbApi::ClrEol(int x, int y, eDvbColor color)
-{
- Fill(x, y, cols - x, 1, color);
-}
-
-int cDvbApi::CellWidth(void)
-{
-#ifdef DEBUG_OSD
- return 1;
-#else
- return charWidth;
-#endif
-}
-
-int cDvbApi::LineHeight(void)
-{
-#ifdef DEBUG_OSD
- return 1;
-#else
- return lineHeight;
-#endif
-}
-
-int cDvbApi::Width(unsigned char c)
-{
-#ifdef DEBUG_OSD
- return 1;
-#else
- return osd->Width(c);
-#endif
-}
-
-int cDvbApi::WidthInCells(const char *s)
-{
-#ifdef DEBUG_OSD
- return strlen(s);
-#else
- return (osd->Width(s) + charWidth - 1) / charWidth;
-#endif
-}
-
-eDvbFont cDvbApi::SetFont(eDvbFont Font)
-{
-#ifdef DEBUG_OSD
- return Font;
-#else
- return osd->SetFont(Font);
-#endif
-}
-
-void cDvbApi::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor colorBg)
-{
- if (x < 0) x = cols + x;
- if (y < 0) y = rows + y;
-#ifdef DEBUG_OSD
- SetColor(colorFg, colorBg);
- wmove(window, y, x); // ncurses wants 'y' before 'x'!
- waddnstr(window, s, cols - x);
-#else
- osd->Text(x * charWidth, y * lineHeight, s, colorFg, colorBg);
-#endif
-}
-
-void cDvbApi::Flush(void)
-{
-#ifdef DEBUG_OSD
- refresh();
-#else
- if (osd)
- osd->Flush();
-#endif
-}
-
-int cDvbApi::Priority(void)
-{
- return (this == PrimaryDvbApi && !Recording()) ? Setup.PrimaryLimit - 1 : priority;
-}
-
-int cDvbApi::SetModeRecord(void)
-{
- // Sets up the DVB device for recording
-
- SetPids(true);
- if (fd_dvr >= 0)
- close(fd_dvr);
- fd_dvr = OstOpen(DEV_OST_DVR, CardIndex(), O_RDONLY | O_NONBLOCK);
- if (fd_dvr < 0)
- LOG_ERROR;
- return fd_dvr;
-}
-
-void cDvbApi::SetModeReplay(void)
-{
- // Sets up the DVB device for replay
-
- if (fd_video >= 0 && fd_audio >= 0) {
- if (siProcessor)
- siProcessor->SetStatus(false);
- CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
- CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
- CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(fd_audio, AUDIO_PLAY));
- CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
- CHECK(ioctl(fd_video, VIDEO_PLAY));
- }
-}
-
-void cDvbApi::SetModeNormal(bool FromRecording)
-{
- // Puts the DVB device back into "normal" viewing mode (after replay or recording)
-
- if (FromRecording) {
- close(fd_dvr);
- fd_dvr = -1;
- SetPids(false);
- }
- else {
- if (fd_video >= 0 && fd_audio >= 0) {
- CHECK(ioctl(fd_video, VIDEO_STOP, true));
- CHECK(ioctl(fd_audio, AUDIO_STOP, true));
- CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
- CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
- CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
- CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
- CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
- if (siProcessor)
- siProcessor->SetStatus(true);
- }
- }
-}
-
-void cDvbApi::SetVideoFormat(videoFormat_t Format)
-{
- if (fd_video >= 0)
- CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, Format));
-}
-
-bool cDvbApi::SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output)
-{
- if (Pid) {
- CHECK(ioctl(fd, DMX_STOP));
- if (Pid != 0x1FFF) {
- dmxPesFilterParams pesFilterParams;
- pesFilterParams.pid = Pid;
- pesFilterParams.input = DMX_IN_FRONTEND;
- pesFilterParams.output = Output;
- pesFilterParams.pesType = PesType;
- pesFilterParams.flags = DMX_IMMEDIATE_START;
- if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
- LOG_ERROR;
- return false;
- }
- }
- }
- return true;
-}
-
-bool cDvbApi::SetPids(bool ForRecording)
-{
- return SetVpid(vPid, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
- SetApid1(aPid1, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
- SetApid2(ForRecording ? aPid2 : 0, DMX_OUT_TS_TAP) && (!Setup.RecordDolbyDigital ||
- SetDpid1(ForRecording ? dPid1 : 0, DMX_OUT_TS_TAP) &&
- SetDpid2(ForRecording ? dPid2 : 0, DMX_OUT_TS_TAP));
-}
-
-eSetChannelResult cDvbApi::SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid, int Ca, int Pnr)
-{
- StopTransfer();
- StopReplay();
-
- // Must set this anyway to avoid getting stuck when switching through
- // channels with 'Up' and 'Down' keys:
- currentChannel = ChannelNumber;
- vPid = Vpid;
- aPid1 = Apid1;
- aPid2 = Apid2;
- dPid1 = Dpid1;
- dPid2 = Dpid2;
-
- // Avoid noise while switching:
-
- if (fd_video >= 0 && fd_audio >= 0) {
- CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
- CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
- CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
- CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
- }
-
- // Stop setting system time:
-
- if (siProcessor)
- siProcessor->SetCurrentTransponder(0);
-
- // If this card can't receive this channel, we must not actually switch
- // the channel here, because that would irritate the driver when we
- // start replaying in Transfer Mode immediately after switching the channel:
- bool NeedsTransferMode = (this == PrimaryDvbApi && !ProvidesCa(Ca));
-
- if (!NeedsTransferMode) {
-
- // Turn off current PIDs:
-
- SetVpid( 0x1FFF, DMX_OUT_DECODER);
- SetApid1(0x1FFF, DMX_OUT_DECODER);
- SetApid2(0x1FFF, DMX_OUT_DECODER);
- SetDpid1(0x1FFF, DMX_OUT_DECODER);
- SetDpid2(0x1FFF, DMX_OUT_DECODER);
- SetTpid( 0x1FFF, DMX_OUT_DECODER);
-
- FrontendParameters Frontend;
-
- switch (frontendType) {
- case FE_QPSK: { // DVB-S
-
- // Frequency offsets:
-
- unsigned int freq = Frequency;
- int tone = SEC_TONE_OFF;
-
- if (freq < (unsigned int)Setup.LnbSLOF) {
- freq -= Setup.LnbFrequLo;
- tone = SEC_TONE_OFF;
- }
- else {
- freq -= Setup.LnbFrequHi;
- tone = SEC_TONE_ON;
- }
-
- Frontend.Frequency = freq * 1000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.qpsk.SymbolRate = Srate * 1000UL;
- Frontend.u.qpsk.FEC_inner = FEC_AUTO;
-
- int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
-
- // DiseqC:
-
- secCommand scmd;
- scmd.type = 0;
- scmd.u.diseqc.addr = 0x10;
- scmd.u.diseqc.cmd = 0x38;
- scmd.u.diseqc.numParams = 1;
- scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0);
-
- secCmdSequence scmds;
- scmds.voltage = volt;
- scmds.miniCommand = SEC_MINI_NONE;
- scmds.continuousTone = tone;
- scmds.numCommands = Setup.DiSEqC ? 1 : 0;
- scmds.commands = &scmd;
-
- CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
- }
- break;
- case FE_QAM: { // DVB-C
-
- // Frequency and symbol rate:
-
- Frontend.Frequency = Frequency * 1000000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.qam.SymbolRate = Srate * 1000UL;
- Frontend.u.qam.FEC_inner = FEC_AUTO;
- Frontend.u.qam.QAM = QAM_64;
- }
- break;
- case FE_OFDM: { // DVB-T
-
- // Frequency and OFDM paramaters:
-
- Frontend.Frequency = Frequency * 1000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ;
- Frontend.u.ofdm.HP_CodeRate=FEC_2_3;
- Frontend.u.ofdm.LP_CodeRate=FEC_1_2;
- Frontend.u.ofdm.Constellation=QAM_64;
- Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K;
- Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32;
- Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE;
- }
- break;
- default:
- esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
- return scrFailed;
- }
-
- // Tuning:
-
- CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
-
- // Wait for channel sync:
-
- if (cFile::FileReady(fd_frontend, 5000)) {
- FrontendEvent event;
- int res = ioctl(fd_frontend, FE_GET_EVENT, &event);
- if (res >= 0) {
- if (event.type != FE_COMPLETION_EV) {
- esyslog("ERROR: channel %d not sync'ed on DVB card %d!", ChannelNumber, CardIndex() + 1);
- if (this == PrimaryDvbApi)
- cThread::RaisePanic();
- return scrFailed;
- }
- }
- else
- esyslog("ERROR %d in frontend get event (channel %d, card %d)", res, ChannelNumber, CardIndex() + 1);
- }
- else
- esyslog("ERROR: timeout while tuning");
-
- // PID settings:
-
- if (!SetPids(false)) {
- esyslog("ERROR: failed to set PIDs for channel %d", ChannelNumber);
- return scrFailed;
- }
- SetTpid(Tpid, DMX_OUT_DECODER);
- if (fd_audio >= 0)
- CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
- }
-
- if (this == PrimaryDvbApi && siProcessor)
- siProcessor->SetCurrentServiceID(Pnr);
-
- eSetChannelResult Result = scrOk;
-
- // If this DVB card can't receive this channel, let's see if we can
- // use the card that actually can receive it and transfer data from there:
-
- if (NeedsTransferMode) {
- cDvbApi *CaDvbApi = GetDvbApi(Ca, 0);
- if (CaDvbApi && !CaDvbApi->Recording()) {
- if ((Result = CaDvbApi->SetChannel(ChannelNumber, Frequency, Polarization, Diseqc, Srate, Vpid, Apid1, Apid2, Dpid1, Dpid2, Tpid, Ca, Pnr)) == scrOk) {
- SetModeReplay();
- transferringFromDvbApi = CaDvbApi->StartTransfer(fd_video);
- }
- }
- else
- Result = scrNoTransfer;
- }
-
- if (fd_video >= 0 && fd_audio >= 0) {
- CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
- CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
- }
-
- // Start setting system time:
-
- if (Result == scrOk && siProcessor)
- siProcessor->SetCurrentTransponder(Frequency);
-
- return Result;
-}
-
-bool cDvbApi::Transferring(void)
-{
- return transferBuffer;
-}
-
-cDvbApi *cDvbApi::StartTransfer(int TransferToVideoDev)
-{
- StopTransfer();
- transferBuffer = new cTransferBuffer(this, TransferToVideoDev, vPid, aPid1);
- return this;
-}
-
-void cDvbApi::StopTransfer(void)
-{
- if (transferBuffer) {
- delete transferBuffer;
- transferBuffer = NULL;
- }
- if (transferringFromDvbApi) {
- transferringFromDvbApi->StopTransfer();
- transferringFromDvbApi = NULL;
- }
-}
-
-int cDvbApi::SecondsToFrames(int Seconds)
-{
- return Seconds * FRAMESPERSEC;
-}
-
-bool cDvbApi::Recording(void)
-{
- if (recordBuffer && !recordBuffer->Active())
- StopRecord();
- return recordBuffer != NULL;
-}
-
-bool cDvbApi::Replaying(void)
-{
- if (replayBuffer && !replayBuffer->Active())
- StopReplay();
- return replayBuffer != NULL;
-}
-
-bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
-{
- if (Recording()) {
- esyslog("ERROR: StartRecord() called while recording - ignored!");
- return false;
- }
-
- StopTransfer();
-
- StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
-
- // Check FileName:
-
- if (!FileName) {
- esyslog("ERROR: StartRecord: file name is (null)");
- return false;
- }
- isyslog("record %s", FileName);
-
- // Create directories if necessary:
-
- if (!MakeDirs(FileName, true))
- return false;
-
- // Make sure the disk is up and running:
-
- SpinUpDisk(FileName);
-
- // Create recording buffer:
-
- recordBuffer = new cRecordBuffer(this, FileName, vPid, aPid1, aPid2, dPid1, dPid2);
-
- if (recordBuffer) {
- ca = Ca;
- priority = Priority;
- return true;
- }
- else
- esyslog("ERROR: can't allocate recording buffer");
-
- return false;
-}
-
-void cDvbApi::StopRecord(void)
-{
- if (recordBuffer) {
- delete recordBuffer;
- recordBuffer = NULL;
- ca = -1;
- priority = DEFAULTPRIORITY;
- }
-}
-
-bool cDvbApi::StartReplay(const char *FileName)
-{
- if (Recording()) {
- esyslog("ERROR: StartReplay() called while recording - ignored!");
- return false;
- }
- StopTransfer();
- StopReplay();
- if (fd_video >= 0 && fd_audio >= 0) {
-
- // Check FileName:
-
- if (!FileName) {
- esyslog("ERROR: StartReplay: file name is (null)");
- return false;
- }
- isyslog("replay %s", FileName);
-
- // Create replay buffer:
-
- replayBuffer = new cReplayBuffer(this, fd_video, fd_audio, FileName);
- if (replayBuffer)
- return true;
- else
- esyslog("ERROR: can't allocate replaying buffer");
- }
- return false;
-}
-
-void cDvbApi::StopReplay(void)
-{
- if (replayBuffer) {
- delete replayBuffer;
- replayBuffer = NULL;
- if (this == PrimaryDvbApi) {
- // let's explicitly switch the channel back in case it was in Transfer Mode:
- cChannel *Channel = Channels.GetByNumber(currentChannel);
- if (Channel) {
- Channel->Switch(this, false);
- usleep(100000); // allow driver to sync in case a new replay will start immediately
- }
- }
- }
-}
-
-void cDvbApi::Pause(void)
-{
- if (replayBuffer)
- replayBuffer->Pause();
-}
-
-void cDvbApi::Play(void)
-{
- if (replayBuffer)
- replayBuffer->Play();
-}
-
-void cDvbApi::Forward(void)
-{
- if (replayBuffer)
- replayBuffer->Forward();
-}
-
-void cDvbApi::Backward(void)
-{
- if (replayBuffer)
- replayBuffer->Backward();
-}
-
-void cDvbApi::SkipSeconds(int Seconds)
-{
- if (replayBuffer)
- replayBuffer->SkipSeconds(Seconds);
-}
-
-int cDvbApi::SkipFrames(int Frames)
-{
- if (replayBuffer)
- return replayBuffer->SkipFrames(Frames);
- return -1;
-}
-
-bool cDvbApi::GetIndex(int &Current, int &Total, bool SnapToIFrame)
-{
- if (replayBuffer) {
- replayBuffer->GetIndex(Current, Total, SnapToIFrame);
- return true;
- }
- return false;
-}
-
-bool cDvbApi::GetReplayMode(bool &Play, bool &Forward, int &Speed)
-{
- return replayBuffer && replayBuffer->GetReplayMode(Play, Forward, Speed);
-}
-
-void cDvbApi::Goto(int Position, bool Still)
-{
- if (replayBuffer)
- replayBuffer->Goto(Position, Still);
-}
-
-bool cDvbApi::CanToggleAudioTrack(void)
-{
- return replayBuffer ? replayBuffer->CanToggleAudioTrack() : (aPid1 && aPid2 && aPid1 != aPid2);
-}
-
-bool cDvbApi::ToggleAudioTrack(void)
-{
- if (replayBuffer) {
- replayBuffer->ToggleAudioTrack();
- return true;
- }
- else {
- int a = aPid2;
- aPid2 = aPid1;
- aPid1 = a;
- if (transferringFromDvbApi)
- return transferringFromDvbApi->ToggleAudioTrack();
- else {
- if (transferBuffer)
- transferBuffer->SetAudioPid(aPid1);
- return SetPids(transferBuffer != NULL);
- }
- }
- return false;
-}
-
-bool cDvbApi::ToggleMute(void)
-{
- int OldVolume = volume;
- mute = !mute;
- SetVolume(0, mute);
- volume = OldVolume;
- return mute;
-}
-
-void cDvbApi::SetVolume(int Volume, bool Absolute)
-{
- if (fd_audio >= 0) {
- volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME);
- audioMixer_t am;
- am.volume_left = am.volume_right = volume;
- CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
- if (volume > 0)
- mute = false;
- }
-}
-
-void cDvbApi::SetAudioCommand(const char *Command)
-{
- delete audioCommand;
- audioCommand = strdup(Command);
-}
-
-// --- cEITScanner -----------------------------------------------------------
-
-cEITScanner::cEITScanner(void)
-{
- lastScan = lastActivity = time(NULL);
- currentChannel = 0;
- lastChannel = 0;
- numTransponders = 0;
- transponders = NULL;
-}
-
-cEITScanner::~cEITScanner()
-{
- delete transponders;
-}
-
-bool cEITScanner::TransponderScanned(cChannel *Channel)
-{
- for (int i = 0; i < numTransponders; i++) {
- if (transponders[i] == Channel->frequency)
- return true;
- }
- transponders = (int *)realloc(transponders, ++numTransponders * sizeof(int));
- transponders[numTransponders - 1] = Channel->frequency;
- return false;
-}
-
-void cEITScanner::Activity(void)
-{
- if (currentChannel) {
- Channels.SwitchTo(currentChannel);
- currentChannel = 0;
- }
- lastActivity = time(NULL);
-}
-
-void cEITScanner::Process(void)
-{
- if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
- time_t now = time(NULL);
- if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
- for (int i = 0; i < MAXDVBAPI; i++) {
- cDvbApi *DvbApi = cDvbApi::GetDvbApi(i + 1, MAXPRIORITY + 1);
- if (DvbApi) {
- if (DvbApi != cDvbApi::PrimaryDvbApi || (cDvbApi::NumDvbApis == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
- if (!(DvbApi->Recording() || DvbApi->Replaying() || DvbApi->Transferring())) {
- int oldCh = lastChannel;
- int ch = oldCh + 1;
- while (ch != oldCh) {
- if (ch > Channels.MaxNumber()) {
- ch = 1;
- numTransponders = 0;
- }
- cChannel *Channel = Channels.GetByNumber(ch);
- if (Channel) {
- if (Channel->ca <= MAXDVBAPI && !DvbApi->ProvidesCa(Channel->ca))
- break; // the channel says it explicitly needs a different card
- if (Channel->pnr && !TransponderScanned(Channel)) {
- if (DvbApi == cDvbApi::PrimaryDvbApi && !currentChannel)
- currentChannel = DvbApi->Channel();
- Channel->Switch(DvbApi, false);
- lastChannel = ch;
- break;
- }
- }
- ch++;
- }
- }
- }
- }
- }
- lastScan = time(NULL);
- }
- }
-}
-
diff --git a/dvbapi.h b/dvbapi.h
deleted file mode 100644
index 7e63878..0000000
--- a/dvbapi.h
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * dvbapi.h: Interface to the DVB driver
- *
- * See the main source file 'vdr.c' for copyright information and
- * how to reach the author.
- *
- * $Id: dvbapi.h 1.69 2002/04/21 09:49:22 kls Exp $
- */
-
-#ifndef __DVBAPI_H
-#define __DVBAPI_H
-
-#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
-#include <ncurses.h>
-#undef ERR //XXX ncurses defines this - but this clashes with newer system header files
-#endif
-#include <stdlib.h> // FIXME: this is apparently necessary for the ost/... header files
- // FIXME: shouldn't every header file include ALL the other header
- // FIXME: files it depends on? The sequence in which header files
- // FIXME: are included here should not matter - and it should NOT
- // FIXME: be necessary to include <stdlib.h> here!
-#include <linux/videodev.h>
-#include <ost/dmx.h>
-#include <ost/sec.h>
-#include <ost/frontend.h>
-#include <ost/video.h>
-#include <ost/audio.h>
-#include <ost/osd.h>
-#include <stdio.h>
-
-#include "dvbosd.h"
-#include "eit.h"
-#include "thread.h"
-
-#define FRAMESPERSEC 25
-
-// The maximum file size is limited by the range that can be covered
-// with 'int'. 4GB might be possible (if the range is considered
-// 'unsigned'), 2GB should be possible (even if the range is considered
-// 'signed'), so let's use 2000MB for absolute safety (the actual file size
-// may be slightly higher because we stop recording only before the next
-// 'I' frame, to have a complete Group Of Pictures):
-#define MAXVIDEOFILESIZE 2000 // MB
-#define MINVIDEOFILESIZE 100 // MB
-
-#define MAXVOLUME 255
-#define VOLUMEDELTA 5 // used to increase/decrease the volume
-
-const char *IndexToHMSF(int Index, bool WithFrame = false);
- // Converts the given index to a string, optionally containing the frame number.
-int HMSFToIndex(const char *HMSF);
- // Converts the given string (format: "hh:mm:ss.ff") to an index.
-
-enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed };
-
-class cChannel;
-
-class cRecordBuffer;
-class cPlayBuffer;
-class cReplayBuffer;
-class cTransferBuffer;
-class cCuttingBuffer;
-
-class cVideoCutter {
-private:
- static char *editedVersionName;
- static cCuttingBuffer *cuttingBuffer;
- static bool error;
- static bool ended;
-public:
- static bool Start(const char *FileName);
- static void Stop(void);
- static bool Active(void);
- static bool Error(void);
- static bool Ended(void);
- };
-
-class cDvbApi {
- friend class cRecordBuffer;
- friend class cReplayBuffer;
- friend class cTransferBuffer;
-private:
- FrontendType frontendType;
- int fd_osd, fd_frontend, fd_sec, fd_dvr, fd_audio, fd_video, fd_demuxa1, fd_demuxa2, fd_demuxd1, fd_demuxd2, fd_demuxv, fd_demuxt;
- int vPid, aPid1, aPid2, dPid1, dPid2;
- bool SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output);
- bool SetVpid(int Vpid, dmxOutput_t Output) { return SetPid(fd_demuxv, DMX_PES_VIDEO, Vpid, Output); }
- bool SetApid1(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa1, DMX_PES_AUDIO, Apid, Output); }
- bool SetApid2(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa2, DMX_PES_OTHER, Apid, Output); }
- bool SetDpid1(int Dpid, dmxOutput_t Output) { return SetPid(fd_demuxd1, DMX_PES_OTHER, Dpid, Output); }
- bool SetDpid2(int Dpid, dmxOutput_t Output) { return SetPid(fd_demuxd2, DMX_PES_OTHER, Dpid, Output); }
- bool SetTpid(int Tpid, dmxOutput_t Output) { return SetPid(fd_demuxt, DMX_PES_TELETEXT, Tpid, Output); }
- bool SetPids(bool ForRecording);
- cDvbApi(int n);
-public:
- ~cDvbApi();
-
-#define MAXDVBAPI 4 // the maximum number of DVB cards in the system
-#define MAXCACAPS 16 // the maximum number of different CA values per DVB card
-
- static int NumDvbApis;
-private:
- static cDvbApi *dvbApi[MAXDVBAPI];
- static int useDvbApi;
- int cardIndex;
- int caCaps[MAXCACAPS];
- int CanShift(int Ca, int Priority, int UsedCards = 0);
-public:
- static cDvbApi *PrimaryDvbApi;
- static void SetUseDvbApi(int n);
- // Sets the 'useDvbApi' flag of the given DVB device.
- // If this function is not called before Init(), all DVB devices
- // will be used.
- static bool SetPrimaryDvbApi(int n);
- // Sets the primary DVB device to 'n' (which must be in the range
- // 1...NumDvbApis) and returns true if this was possible.
- static cDvbApi *GetDvbApi(int Ca, int Priority);
- // Selects a free DVB device, avoiding the PrimaryDvbApi if possible.
- // If Ca is not 0, the device with the given number will be returned
- // in case Ca is <= MAXDVBAPI, or the device that provides the given
- // value in its caCaps.
- // If all DVB devices are currently recording, the one recording the
- // lowest priority timer (if any) that is lower than the given Priority
- // will be returned.
- // The caller must check whether the returned DVB device is actually
- // recording and stop recording if necessary.
- int CardIndex(void) { return cardIndex; }
- // Returns the card index of this DvbApi (0 ... MAXDVBAPI - 1).
- static void SetCaCaps(void);
- // Sets the CaCaps of all DVB devices according to the Setup data.
- int ProvidesCa(int Ca);
- // Checks whether this DVB device provides the given value in its
- // caCaps. Returns 0 if the value is not provided, 1 if only this
- // value is provided, and > 1 if this and other values are provided.
- // If the given value is equal to the number of this DVB device,
- // 1 is returned. If it is 0 (FTA), 1 plus the number of other values
- // in caCaps is returned.
- static bool Probe(const char *FileName);
- // Probes for existing DVB devices.
- static bool Init(void);
- // Initializes the DVB API.
- // Must be called before accessing any DVB functions.
- static void Cleanup(void);
- // Closes down all DVB devices.
- // Must be called at the end of the program.
-
- // EIT facilities
-
-private:
- cSIProcessor *siProcessor;
-public:
- // Image Grab facilities
-
- bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
-
- // On Screen Display facilities
-
-private:
- enum { charWidth = 12, // average character width
- lineHeight = 27 // smallest text height
- };
-#ifdef DEBUG_OSD
- WINDOW *window;
- enum { MaxColorPairs = 16 };
- int colorPairs[MaxColorPairs];
- void SetColor(eDvbColor colorFg, eDvbColor colorBg = clrBackground);
-#else
- cDvbOsd *osd;
-#endif
- int cols, rows;
-public:
- void Open(int w, int h);
- void Close(void);
- void Clear(void);
- void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground);
- void SetBitmap(int x, int y, const cBitmap &Bitmap);
- void ClrEol(int x, int y, eDvbColor color = clrBackground);
- int CellWidth(void);
- int LineHeight(void);
- int Width(unsigned char c);
- int WidthInCells(const char *s);
- eDvbFont SetFont(eDvbFont Font);
- void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground);
- void Flush(void);
-
- // Video format facilities:
-
- void SetVideoFormat(videoFormat_t Format);
-
- // Channel facilities
-
-private:
- int currentChannel;
-public:
- eSetChannelResult SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid, int Ca, int Pnr);
- static int CurrentChannel(void) { return PrimaryDvbApi ? PrimaryDvbApi->currentChannel : 0; }
- int Channel(void) { return currentChannel; }
-
- // Transfer facilities
-
-private:
- cTransferBuffer *transferBuffer;
- cDvbApi *transferringFromDvbApi;
-public:
- bool Transferring(void);
- // Returns true if we are currently transferring video data.
-private:
- cDvbApi *StartTransfer(int TransferToVideoDev);
- // Starts transferring video data from this DVB device to TransferToVideoDev.
- void StopTransfer(void);
- // Stops transferring video data (in case a transfer is currently active).
-
- // Record/Replay facilities
-
-private:
- cRecordBuffer *recordBuffer;
- cPlayBuffer *replayBuffer;
- int ca;
- int priority;
- int Priority(void);
- // Returns the priority of the current recording session (0..MAXPRIORITY),
- // or -1 if no recording is currently active. The primary DVB device will
- // always return at least Setup.PrimaryLimit-1.
- int SetModeRecord(void);
- // Initiates recording mode and returns the file handle to read from.
- void SetModeReplay(void);
- void SetModeNormal(bool FromRecording);
-public:
- int Ca(void) { return ca; }
- // Returns the ca of the current recording session.
- int SecondsToFrames(int Seconds);
- // Returns the number of frames corresponding to the given number of seconds.
- bool Recording(void);
- // Returns true if we are currently recording.
- bool Replaying(void);
- // Returns true if we are currently replaying.
- bool StartRecord(const char *FileName, int Ca, int Priority);
- // Starts recording the current channel into the given file, with
- // the given ca and priority.
- // In order to be able to record longer movies,
- // a numerical suffix will be appended to the file name. The inital
- // value of that suffix will be larger than any existing file under
- // the given name, thus allowing an interrupted recording to continue
- // gracefully.
- // Returns true if recording was started successfully.
- // If there is already a recording session active, false will be
- // returned.
- void StopRecord(void);
- // Stops the current recording session (if any).
- bool StartReplay(const char *FileName);
- // 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.
- void StopReplay(void);
- // Stops the current replay session (if any).
- void Pause(void);
- // Pauses the current replay session, or resumes a paused session.
- void Play(void);
- // Resumes normal replay mode.
- void Forward(void);
- // Runs the current replay session forward at a higher speed.
- void Backward(void);
- // Runs the current replay session backwards at a higher speed.
- void SkipSeconds(int Seconds);
- // Skips the given number of seconds in the current replay session.
- // The sign of 'Seconds' determines the direction in which to skip.
- // Use a very large negative value to go all the way back to the
- // beginning of the recording.
- int SkipFrames(int Frames);
- // Returns the new index into the current replay session after skipping
- // the given number of frames (no actual repositioning is done!).
- // The sign of 'Frames' determines the direction in which to skip.
- bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
- // Returns the current and total frame index, optionally snapped to the
- // nearest I-frame.
- bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
- // Returns the current replay mode (if applicable).
- // 'Play' tells whether we are playing or pausing, 'Forward' tells whether
- // we are going forward or backward and 'Speed' is -1 if this is normal
- // play/pause mode, 0 if it is single speed fast/slow forward/back mode
- // and >0 if this is multi speed mode.
- void Goto(int Index, bool Still = false);
- // Positions to the given index and displays that frame as a still picture
- // if Still is true.
-
- // Audio track facilities
-
-public:
- bool CanToggleAudioTrack(void);
- // Returns true if we are currently replaying and this recording has two
- // audio tracks, or if the current channel has two audio PIDs.
- bool ToggleAudioTrack(void);
- // Toggles the audio track if possible.
-
- // Dolby Digital audio facilities
-
-private:
- static char *audioCommand;
-public:
- static void SetAudioCommand(const char *Command);
- static const char *AudioCommand(void) { return audioCommand; }
-
- // Volume facilities:
-
-private:
- bool mute;
- int volume;
-public:
- bool IsMute(void) { return mute; }
- bool ToggleMute(void);
- // Turns the volume off or on and returns the new mute state.
- void SetVolume(int Volume, bool Absolute = false);
- // Sets the volume to the given value, either absolutely or relative to
- // the current volume.
- static int CurrentVolume(void) { return PrimaryDvbApi ? PrimaryDvbApi->volume : 0; }
- };
-
-class cEITScanner {
-private:
- enum { ActivityTimeout = 60,
- ScanTimeout = 20
- };
- time_t lastScan, lastActivity;
- int currentChannel, lastChannel;
- int numTransponders, *transponders;
- bool TransponderScanned(cChannel *Channel);
-public:
- cEITScanner(void);
- ~cEITScanner();
- bool Active(void) { return currentChannel; }
- void Activity(void);
- void Process(void);
- };
-
-#endif //__DVBAPI_H
diff --git a/dvbosd.c b/dvbosd.c
index 294ee90..ad51966 100644
--- a/dvbosd.c
+++ b/dvbosd.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbosd.c 1.15 2002/05/13 16:29:20 kls Exp $
+ * $Id: dvbosd.c 1.17 2002/05/18 13:39:02 kls Exp $
*/
#include "dvbosd.h"
@@ -13,12 +13,12 @@
#include <sys/unistd.h>
#include "tools.h"
-cDvbOsd::cDvbOsd(int VideoDev, int x, int y)
-:cOsd(x, y)
+cDvbOsd::cDvbOsd(int OsdDev, int x, int y)
+:cOsdBase(x, y)
{
- videoDev = VideoDev;
- if (videoDev < 0)
- esyslog("ERROR: illegal video device handle (%d)!", videoDev);
+ osdDev = OsdDev;
+ if (osdDev < 0)
+ esyslog("ERROR: illegal OSD device handle (%d)!", osdDev);
}
cDvbOsd::~cDvbOsd()
@@ -44,7 +44,7 @@ bool cDvbOsd::SetWindow(cWindow *Window)
void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data)
{
- if (videoDev >= 0) {
+ if (osdDev >= 0) {
osd_cmd_t dc;
dc.cmd = cmd;
dc.color = color;
@@ -58,7 +58,7 @@ void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, co
sigfillset(&set);
sigdelset(&set, SIGALRM);
sigprocmask(SIG_BLOCK, &set, &oldset);
- ioctl(videoDev, OSD_SEND_CMD, &dc);
+ ioctl(osdDev, OSD_SEND_CMD, &dc);
if (cmd == OSD_SetBlock) // XXX this is the only command that takes longer
usleep(5000); // XXX Workaround for a driver bug (cInterface::DisplayChannel() displayed texts at wrong places
// XXX and sometimes the OSD was no longer displayed).
diff --git a/dvbosd.h b/dvbosd.h
index ff26bcf..0bc6079 100644
--- a/dvbosd.h
+++ b/dvbosd.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbosd.h 1.11 2002/05/10 14:22:07 kls Exp $
+ * $Id: dvbosd.h 1.13 2002/05/18 13:38:09 kls Exp $
*/
#ifndef __DVBOSD_H
@@ -13,9 +13,9 @@
#include <ost/osd.h>
#include "osdbase.h"
-class cDvbOsd : public cOsd {
+class cDvbOsd : public cOsdBase {
private:
- int videoDev;
+ int osdDev;
bool SetWindow(cWindow *Window);
void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL);
protected:
@@ -26,7 +26,7 @@ protected:
virtual void MoveWindow(cWindow *Window, int x, int y);
virtual void CloseWindow(cWindow *Window);
public:
- cDvbOsd(int VideoDev, int x, int y);
+ cDvbOsd(int OsdDev, int x, int y);
virtual ~cDvbOsd();
};
diff --git a/dvbplayer.c b/dvbplayer.c
new file mode 100644
index 0000000..10fdf4b
--- /dev/null
+++ b/dvbplayer.c
@@ -0,0 +1,733 @@
+/*
+ * dvbplayer.c: The DVB player
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: dvbplayer.c 1.1 2002/06/16 10:59:45 kls Exp $
+ */
+
+#include "dvbplayer.h"
+#include <poll.h>
+#include "recording.h"
+#include "ringbuffer.h"
+#include "thread.h"
+
+// --- ReadFrame -------------------------------------------------------------
+
+int ReadFrame(int f, uchar *b, int Length, int Max)
+{
+ if (Length == -1)
+ Length = Max; // this means we read up to EOF (see cIndex)
+ else if (Length > Max) {
+ esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max);
+ Length = Max;
+ }
+ int r = safe_read(f, b, Length);
+ if (r < 0)
+ LOG_ERROR;
+ return r;
+}
+
+// --- cBackTrace ----------------------------------------------------------
+
+#define AVG_FRAME_SIZE 15000 // an assumption about the average frame size
+#define DVB_BUF_SIZE (256 * 1024) // an assumption about the dvb firmware buffer size
+#define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20) // how many entries are needed to backtrace buffer contents
+
+class cBackTrace {
+private:
+ int index[BACKTRACE_ENTRIES];
+ int length[BACKTRACE_ENTRIES];
+ int pos, num;
+public:
+ cBackTrace(void);
+ void Clear(void);
+ void Add(int Index, int Length);
+ int Get(bool Forward);
+ };
+
+cBackTrace::cBackTrace(void)
+{
+ Clear();
+}
+
+void cBackTrace::Clear(void)
+{
+ pos = num = 0;
+}
+
+void cBackTrace::Add(int Index, int Length)
+{
+ index[pos] = Index;
+ length[pos] = Length;
+ if (++pos >= BACKTRACE_ENTRIES)
+ pos = 0;
+ if (num < BACKTRACE_ENTRIES)
+ num++;
+}
+
+int cBackTrace::Get(bool Forward)
+{
+ int p = pos;
+ int n = num;
+ int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024); //XXX (256 * 1024) == DVB_BUF_SIZE ???
+ int i = -1;
+
+ while (n && l > 0) {
+ if (--p < 0)
+ p = BACKTRACE_ENTRIES - 1;
+ i = index[p] - 1;
+ l -= length[p];
+ n--;
+ }
+ return i;
+}
+
+// --- cDvbPlayer ------------------------------------------------------------
+
+//XXX+ also used in recorder.c - find a better place???
+// The size of the array used to buffer video data:
+// (must be larger than MINVIDEODATA - see remux.h)
+#define VIDEOBUFSIZE MEGABYTE(1)
+
+// The maximum size of a single frame:
+#define MAXFRAMESIZE KILOBYTE(192)
+
+// The number of frames to back up when resuming an interrupted replay session:
+#define RESUMEBACKUP (10 * FRAMESPERSEC)
+
+class cDvbPlayer : public cPlayer, cThread {
+private:
+ enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
+ enum ePlayDirs { pdForward, pdBackward };
+ static int Speeds[];
+ cRingBufferFrame *ringBuffer;
+ cBackTrace *backTrace;
+ cFileName *fileName;
+ cIndexFile *index;
+ int replayFile;
+ bool eof;
+ bool active;
+ ePlayModes playMode;
+ ePlayDirs playDir;
+ int trickSpeed;
+ int readIndex, writeIndex;
+ cFrame *readFrame;
+ const cFrame *playFrame;
+ void TrickSpeed(int Increment);
+ void Empty(void);
+ void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
+ bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
+ int Resume(void);
+ bool Save(void);
+protected:
+ virtual void Activate(bool On);
+ virtual void Action(void);
+public:
+ cDvbPlayer(const char *FileName);
+ virtual ~cDvbPlayer();
+ bool Active(void) { return active; }
+ 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 GetReplayMode(bool &Play, bool &Forward, int &Speed);
+ };
+
+#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
+#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
+#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
+#define SPEED_MULT 12 // the speed multiplier
+int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
+
+cDvbPlayer::cDvbPlayer(const char *FileName)
+{
+ ringBuffer = NULL;
+ backTrace = NULL;
+ index = NULL;
+ eof = false;
+ active = false;
+ playMode = pmPlay;
+ playDir = pdForward;
+ trickSpeed = NORMAL_SPEED;
+ readIndex = writeIndex = -1;
+ readFrame = NULL;
+ playFrame = NULL;
+ isyslog("replay %s", FileName);
+ fileName = new cFileName(FileName, false);
+ replayFile = fileName->Open();
+ if (replayFile < 0)
+ return;
+ ringBuffer = new cRingBufferFrame(VIDEOBUFSIZE);
+ // Create the index file:
+ index = new cIndexFile(FileName, false);
+ if (!index)
+ esyslog("ERROR: can't allocate index");
+ else if (!index->Ok()) {
+ delete index;
+ index = NULL;
+ }
+ backTrace = new cBackTrace;
+}
+
+cDvbPlayer::~cDvbPlayer()
+{
+ Detach();
+ Save();
+ delete index;
+ delete fileName;
+ delete backTrace;
+ delete ringBuffer;
+}
+
+void cDvbPlayer::TrickSpeed(int Increment)
+{
+ int nts = trickSpeed + Increment;
+ if (Speeds[nts] == 1) {
+ trickSpeed = nts;
+ if (playMode == pmFast)
+ Play();
+ else
+ Pause();
+ }
+ else if (Speeds[nts]) {
+ trickSpeed = nts;
+ int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
+ int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
+ if (sp > MAX_VIDEO_SLOWMOTION)
+ sp = MAX_VIDEO_SLOWMOTION;
+ DeviceTrickSpeed(sp);
+ }
+}
+
+void cDvbPlayer::Empty(void)
+{
+ Lock();
+ if ((readIndex = backTrace->Get(playDir == pdForward)) < 0)
+ readIndex = writeIndex;
+ readFrame = NULL;
+ playFrame = NULL;
+ ringBuffer->Clear();
+ backTrace->Clear();
+ DeviceClear();
+ Unlock();
+}
+
+void cDvbPlayer::StripAudioPackets(uchar *b, int Length, uchar Except)
+{
+ if (index) {
+ 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)
+ ;//XXX+ PlayExternalDolby(&b[i], Length - i);
+ // continue with deleting the data - otherwise it disturbs DVB replay
+ case 0xC0 ... 0xC1: // audio
+ if (c == 0xC1)
+ ;//XXX+ 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("ERROR: unexpected packet id %02X", c);
+ l = 0;
+ }
+ if (l)
+ i += l - 1; // the loop increments, too!
+ }
+ /*XXX
+ else
+ esyslog("ERROR: broken packet header");
+ XXX*/
+ }
+ }
+}
+
+bool cDvbPlayer::NextFile(uchar FileNumber, int FileOffset)
+{
+ if (FileNumber > 0)
+ replayFile = fileName->SetOffset(FileNumber, FileOffset);
+ else if (replayFile >= 0 && eof)
+ replayFile = fileName->NextFile();
+ eof = false;
+ return replayFile >= 0;
+}
+
+int cDvbPlayer::Resume(void)
+{
+ if (index) {
+ int Index = index->GetResume();
+ if (Index >= 0) {
+ uchar FileNumber;
+ int FileOffset;
+ if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
+ return Index;
+ }
+ }
+ return -1;
+}
+
+bool cDvbPlayer::Save(void)
+{
+ if (index) {
+ int Index = writeIndex;
+ if (Index >= 0) {
+ Index -= RESUMEBACKUP;
+ if (Index > 0)
+ Index = index->GetNextIFrame(Index, false);
+ else
+ Index = 0;
+ if (Index >= 0)
+ return index->StoreResume(Index);
+ }
+ }
+ return false;
+}
+
+void cDvbPlayer::Activate(bool On)
+{
+ if (On) {
+ if (replayFile >= 0)
+ Start();
+ }
+ else if (active) {
+ active = false;
+ Cancel(3);
+ }
+}
+
+void cDvbPlayer::Action(void)
+{
+ active = true;
+ dsyslog("dvbplayer thread started (pid=%d)", getpid());
+
+ uchar b[MAXFRAMESIZE];
+ const uchar *p = NULL;
+ int pc = 0;
+
+ pollfd pfd[2];
+ pfd[0].fd = DeviceFileHandle();
+ pfd[0].events = pfd[0].revents = POLLOUT;
+ pfd[1].fd = replayFile;
+ pfd[1].events = pfd[1].revents = POLLIN;
+
+ readIndex = Resume();
+ if (readIndex >= 0)
+ isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
+
+ while (active && NextFile()) {
+ {
+ LOCK_THREAD;
+
+ // Read the next frame from the file:
+
+ if (!readFrame && (pfd[1].revents & POLLIN)) {
+ if (playMode != pmStill) {
+ int r = 0;
+ if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
+ uchar FileNumber;
+ int FileOffset, Length;
+ int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
+ if (Index >= 0) {
+ if (!NextFile(FileNumber, FileOffset))
+ break;
+ }
+ else {
+ // can't call Play() here, because those functions may only be
+ // called from the foreground thread - and we also don't need
+ // to empty the buffer here
+ DevicePlay();
+ playMode = pmPlay;
+ playDir = pdForward;
+ continue;
+ }
+ readIndex = Index;
+ r = ReadFrame(replayFile, b, Length, sizeof(b));
+ // must call StripAudioPackets() here because the buffer is not emptied
+ // when falling back from "fast forward" to "play" (see above)
+ StripAudioPackets(b, r);
+ }
+ else if (index) {
+ uchar FileNumber;
+ int FileOffset, Length;
+ readIndex++;
+ if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
+ break;
+ r = ReadFrame(replayFile, b, Length, sizeof(b));
+ }
+ else // allows replay even if the index file is missing
+ r = read(replayFile, b, sizeof(b));
+ if (r > 0)
+ readFrame = new cFrame(b, r, ftUnknown, readIndex);
+ else if (r == 0)
+ eof = true;
+ else if (r < 0 && FATALERRNO) {
+ LOG_ERROR;
+ break;
+ }
+ }
+ else//XXX
+ usleep(1); // this keeps the CPU load low
+ }
+
+ // Store the frame in the buffer:
+
+ if (readFrame) {
+ if (ringBuffer->Put(readFrame))
+ readFrame = NULL;
+ }
+
+ // Get the next frame from the buffer:
+
+ if (!playFrame) {
+ playFrame = ringBuffer->Get();
+ p = NULL;
+ pc = 0;
+ }
+
+ // Play the frame:
+
+ if (playFrame && (pfd[0].revents & POLLOUT)) {
+ if (!p) {
+ p = playFrame->Data();
+ pc = playFrame->Count();
+ }
+ if (p) {
+ int w = PlayVideo(p, pc);
+ if (w > 0) {
+ p += w;
+ pc -= w;
+ }
+ else if (w < 0 && FATALERRNO) {
+ LOG_ERROR;
+ break;
+ }
+ }
+ if (pc == 0) {
+ writeIndex = playFrame->Index();
+ backTrace->Add(playFrame->Index(), playFrame->Count());
+ ringBuffer->Drop(playFrame);
+ playFrame = NULL;
+ p = 0;
+ }
+ }
+ }
+
+ // Wait for input or output to become ready:
+
+ if (poll(pfd, readFrame ? 1 : 2, 10) < 0 && FATALERRNO) {
+ LOG_ERROR;
+ break;
+ }
+ }
+
+ dsyslog("dvbplayer thread ended (pid=%d)", getpid());
+ active = false;
+}
+
+void cDvbPlayer::Pause(void)
+{
+ if (playMode == pmPause || playMode == pmStill)
+ Play();
+ else {
+ LOCK_THREAD;
+ if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
+ Empty();
+ DeviceFreeze();
+ playMode = pmPause;
+ }
+}
+
+void cDvbPlayer::Play(void)
+{
+ if (playMode != pmPlay) {
+ LOCK_THREAD;
+ if (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))
+ Empty();
+ DevicePlay();
+ playMode = pmPlay;
+ playDir = pdForward;
+ }
+}
+
+void cDvbPlayer::Forward(void)
+{
+ if (index) {
+ switch (playMode) {
+ case pmFast:
+ if (Setup.MultiSpeedMode) {
+ TrickSpeed(playDir == pdForward ? 1 : -1);
+ break;
+ }
+ else if (playDir == pdForward) {
+ Play();
+ break;
+ }
+ // run into pmPlay
+ case pmPlay: {
+ LOCK_THREAD;
+ Empty();
+ DeviceMute();
+ playMode = pmFast;
+ playDir = pdForward;
+ trickSpeed = NORMAL_SPEED;
+ TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
+ }
+ break;
+ case pmSlow:
+ if (Setup.MultiSpeedMode) {
+ TrickSpeed(playDir == pdForward ? -1 : 1);
+ break;
+ }
+ else if (playDir == pdForward) {
+ Pause();
+ break;
+ }
+ // run into pmPause
+ case pmStill:
+ case pmPause:
+ DeviceMute();
+ playMode = pmSlow;
+ playDir = pdForward;
+ trickSpeed = NORMAL_SPEED;
+ TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
+ break;
+ }
+ }
+}
+
+void cDvbPlayer::Backward(void)
+{
+ if (index) {
+ switch (playMode) {
+ case pmFast:
+ if (Setup.MultiSpeedMode) {
+ TrickSpeed(playDir == pdBackward ? 1 : -1);
+ break;
+ }
+ else if (playDir == pdBackward) {
+ Play();
+ break;
+ }
+ // run into pmPlay
+ case pmPlay: {
+ LOCK_THREAD;
+ Empty();
+ DeviceMute();
+ playMode = pmFast;
+ playDir = pdBackward;
+ trickSpeed = NORMAL_SPEED;
+ TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
+ }
+ break;
+ case pmSlow:
+ if (Setup.MultiSpeedMode) {
+ TrickSpeed(playDir == pdBackward ? -1 : 1);
+ break;
+ }
+ else if (playDir == pdBackward) {
+ Pause();
+ break;
+ }
+ // run into pmPause
+ case pmStill:
+ case pmPause: {
+ LOCK_THREAD;
+ Empty();
+ DeviceMute();
+ playMode = pmSlow;
+ playDir = pdBackward;
+ trickSpeed = NORMAL_SPEED;
+ TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
+ }
+ break;
+ }
+ }
+}
+
+int cDvbPlayer::SkipFrames(int Frames)
+{
+ if (index && Frames) {
+ int Current, Total;
+ GetIndex(Current, Total, true);
+ int OldCurrent = Current;
+ Current = index->GetNextIFrame(Current + Frames, Frames > 0);
+ return Current >= 0 ? Current : OldCurrent;
+ }
+ return -1;
+}
+
+void cDvbPlayer::SkipSeconds(int Seconds)
+{
+ if (index && Seconds) {
+ LOCK_THREAD;
+ Empty();
+ int Index = writeIndex;
+ if (Index >= 0) {
+ Index = max(Index + Seconds * FRAMESPERSEC, 0);
+ if (Index > 0)
+ Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
+ if (Index >= 0)
+ readIndex = writeIndex = Index - 1; // Input() will first increment it!
+ }
+ Play();
+ }
+}
+
+void cDvbPlayer::Goto(int Index, bool Still)
+{
+ if (index) {
+ LOCK_THREAD;
+ Empty();
+ if (++Index <= 0)
+ Index = 1; // not '0', to allow GetNextIFrame() below to work!
+ uchar FileNumber;
+ int FileOffset, Length;
+ Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
+ if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
+ uchar b[MAXFRAMESIZE];
+ int r = ReadFrame(replayFile, b, Length, sizeof(b));
+ if (r > 0) {
+ if (playMode == pmPause)
+ DevicePlay();
+ StripAudioPackets(b, r);
+ DeviceStillPicture(b, r);
+ }
+ playMode = pmStill;
+ }
+ readIndex = writeIndex = Index;
+ }
+}
+
+void cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
+{
+ if (index) {
+ if (playMode == pmStill)
+ Current = max(readIndex, 0);
+ else {
+ Current = max(writeIndex, 0);
+ if (SnapToIFrame) {
+ int i1 = index->GetNextIFrame(Current + 1, false);
+ int i2 = index->GetNextIFrame(Current, true);
+ Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
+ }
+ }
+ Total = index->Last();
+ }
+ else
+ Current = Total = -1;
+}
+
+bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
+{
+ Play = (playMode == pmPlay || playMode == pmFast);
+ Forward = (playDir == pdForward);
+ if (playMode == pmFast || playMode == pmSlow)
+ Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
+ else
+ Speed = -1;
+ return true;
+}
+
+// --- cDvbPlayerControl -----------------------------------------------------
+
+cDvbPlayerControl::cDvbPlayerControl(void)
+{
+ player = NULL;
+}
+
+cDvbPlayerControl::~cDvbPlayerControl()
+{
+ Stop();
+}
+
+bool cDvbPlayerControl::Active(void)
+{
+ return player && player->Active();
+}
+
+bool cDvbPlayerControl::Start(const char *FileName)
+{
+ delete player;
+ player = new cDvbPlayer(FileName);
+ if (cDevice::PrimaryDevice()->Attach(player))
+ return true;
+ Stop();
+ return false;
+}
+
+void cDvbPlayerControl::Stop(void)
+{
+ delete player;
+ player = NULL;
+}
+
+void cDvbPlayerControl::Pause(void)
+{
+ if (player)
+ player->Pause();
+}
+
+void cDvbPlayerControl::Play(void)
+{
+ if (player)
+ player->Play();
+}
+
+void cDvbPlayerControl::Forward(void)
+{
+ if (player)
+ player->Forward();
+}
+
+void cDvbPlayerControl::Backward(void)
+{
+ if (player)
+ player->Backward();
+}
+
+void cDvbPlayerControl::SkipSeconds(int Seconds)
+{
+ if (player)
+ player->SkipSeconds(Seconds);
+}
+
+int cDvbPlayerControl::SkipFrames(int Frames)
+{
+ if (player)
+ return player->SkipFrames(Frames);
+ return -1;
+}
+
+bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
+{
+ if (player) {
+ player->GetIndex(Current, Total, SnapToIFrame);
+ return true;
+ }
+ return false;
+}
+
+bool cDvbPlayerControl::GetReplayMode(bool &Play, bool &Forward, int &Speed)
+{
+ return player && player->GetReplayMode(Play, Forward, Speed);
+}
+
+void cDvbPlayerControl::Goto(int Position, bool Still)
+{
+ if (player)
+ player->Goto(Position, Still);
+}
diff --git a/dvbplayer.h b/dvbplayer.h
new file mode 100644
index 0000000..b05bcdf
--- /dev/null
+++ b/dvbplayer.h
@@ -0,0 +1,60 @@
+/*
+ * dvbplayer.h: The DVB player
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: dvbplayer.h 1.1 2002/06/16 10:59:14 kls Exp $
+ */
+
+#ifndef __DVBPLAYER_H
+#define __DVBPLAYER_H
+
+#include "player.h"
+#include "thread.h"
+
+class cDvbPlayer;
+
+class cDvbPlayerControl : public cControl {
+private:
+ cDvbPlayer *player;
+public:
+ cDvbPlayerControl(void);
+ virtual ~cDvbPlayerControl();
+ bool Active(void);
+ bool Start(const char *FileName);
+ // Starts replaying the given file.
+ void Stop(void);
+ // Stops the current replay session (if any).
+ void Pause(void);
+ // Pauses the current replay session, or resumes a paused session.
+ void Play(void);
+ // Resumes normal replay mode.
+ void Forward(void);
+ // Runs the current replay session forward at a higher speed.
+ void Backward(void);
+ // Runs the current replay session backwards at a higher speed.
+ int SkipFrames(int Frames);
+ // Returns the new index into the current replay session after skipping
+ // the given number of frames (no actual repositioning is done!).
+ // The sign of 'Frames' determines the direction in which to skip.
+ void SkipSeconds(int Seconds);
+ // Skips the given number of seconds in the current replay session.
+ // The sign of 'Seconds' determines the direction in which to skip.
+ // Use a very large negative value to go all the way back to the
+ // beginning of the recording.
+ bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
+ // Returns the current and total frame index, optionally snapped to the
+ // nearest I-frame.
+ bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
+ // Returns the current replay mode (if applicable).
+ // 'Play' tells whether we are playing or pausing, 'Forward' tells whether
+ // we are going forward or backward and 'Speed' is -1 if this is normal
+ // play/pause mode, 0 if it is single speed fast/slow forward/back mode
+ // and >0 if this is multi speed mode.
+ void Goto(int Index, bool Still = false);
+ // Positions to the given index and displays that frame as a still picture
+ // if Still is true.
+ };
+
+#endif //__DVBPLAYER_H
diff --git a/eit.c b/eit.c
index 7f1bacf..f5d98f5 100644
--- a/eit.c
+++ b/eit.c
@@ -16,7 +16,7 @@
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
- * $Id: eit.c 1.45 2002/05/13 16:35:49 kls Exp $
+ * $Id: eit.c 1.46 2002/05/31 10:26:56 kls Exp $
***************************************************************************/
#include "eit.h"
@@ -26,6 +26,7 @@
#include <iomanip.h>
#include <iostream.h>
#include <limits.h>
+#include <ost/dmx.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/eitscan.c b/eitscan.c
new file mode 100644
index 0000000..f620b09
--- /dev/null
+++ b/eitscan.c
@@ -0,0 +1,84 @@
+/*
+ * eitscan.c: EIT scanner
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: eitscan.c 1.2 2002/06/02 09:02:05 kls Exp $
+ */
+
+#include "eitscan.h"
+
+cEITScanner::cEITScanner(void)
+{
+ lastScan = lastActivity = time(NULL);
+ currentChannel = 0;
+ lastChannel = 0;
+ numTransponders = 0;
+ transponders = NULL;
+}
+
+cEITScanner::~cEITScanner()
+{
+ delete transponders;
+}
+
+bool cEITScanner::TransponderScanned(cChannel *Channel)
+{
+ for (int i = 0; i < numTransponders; i++) {
+ if (transponders[i] == Channel->frequency)
+ return true;
+ }
+ transponders = (int *)realloc(transponders, ++numTransponders * sizeof(int));
+ transponders[numTransponders - 1] = Channel->frequency;
+ return false;
+}
+
+void cEITScanner::Activity(void)
+{
+ if (currentChannel) {
+ Channels.SwitchTo(currentChannel);
+ currentChannel = 0;
+ }
+ lastActivity = time(NULL);
+}
+
+void cEITScanner::Process(void)
+{
+ if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
+ time_t now = time(NULL);
+ if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
+ for (int i = 0; i < MAXDEVICES; i++) {
+ cDevice *Device = cDevice::GetDevice(i + 1, MAXPRIORITY + 1);
+ if (Device) {
+ if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
+ if (!(Device->Receiving() || Device->Replaying()/*XXX+ || Device->Transferring()XXX*/)) {
+ int oldCh = lastChannel;
+ int ch = oldCh + 1;
+ while (ch != oldCh) {
+ if (ch > Channels.MaxNumber()) {
+ ch = 1;
+ numTransponders = 0;
+ }
+ cChannel *Channel = Channels.GetByNumber(ch);
+ if (Channel) {
+ if (Channel->ca <= MAXDEVICES && !Device->ProvidesCa(Channel->ca))
+ break; // the channel says it explicitly needs a different card
+ if (Channel->pnr && !TransponderScanned(Channel)) {
+ if (Device == cDevice::PrimaryDevice() && !currentChannel)
+ currentChannel = Device->Channel();
+ Channel->Switch(Device, false);
+ lastChannel = ch;
+ break;
+ }
+ }
+ ch++;
+ }
+ }
+ }
+ }
+ }
+ lastScan = time(NULL);
+ }
+ }
+}
diff --git a/eitscan.h b/eitscan.h
new file mode 100644
index 0000000..7dacb82
--- /dev/null
+++ b/eitscan.h
@@ -0,0 +1,33 @@
+/*
+ * eitscan.h: EIT scanner
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: eitscan.h 1.1 2002/05/20 11:00:05 kls Exp $
+ */
+
+#ifndef __EITSCAN_H
+#define __EITSCAN_H
+
+#include <time.h>
+#include "config.h"
+
+class cEITScanner {
+private:
+ enum { ActivityTimeout = 60,
+ ScanTimeout = 20
+ };
+ time_t lastScan, lastActivity;
+ int currentChannel, lastChannel;
+ int numTransponders, *transponders;
+ bool TransponderScanned(cChannel *Channel);
+public:
+ cEITScanner(void);
+ ~cEITScanner();
+ bool Active(void) { return currentChannel; }
+ void Activity(void);
+ void Process(void);
+ };
+
+#endif //__EITSCAN_H
diff --git a/epg2html.pl b/epg2html.pl
index cdeb638..6b7b8c9 100755
--- a/epg2html.pl
+++ b/epg2html.pl
@@ -12,7 +12,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: epg2html.pl 1.3 2002/02/26 22:10:47 kls Exp $
+# $Id: epg2html.pl 1.4 2002/05/30 09:46:46 kls Exp $
@Index = ();
@@ -45,7 +45,7 @@ while (<>) {
push(@Index, qq{<a href="$Page">$Channel</a><br>\n});
my %Events = ();
while (<>) {
- if (/^E (.*?) (.*?) (.*?)/) {
+ if (/^E (.*?) (.*?) ([^ ]*)/) {
(my $Time, $Duration) = ($2, $3);
my $Title = "", $Subtitle = "", $Description = "";
while (<>) {
diff --git a/i18n.c b/i18n.c
index 664b8ec..84164a3 100644
--- a/i18n.c
+++ b/i18n.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: i18n.c 1.89 2002/05/13 16:30:00 kls Exp $
+ * $Id: i18n.c 1.91 2002/06/10 16:16:08 kls Exp $
*
* Translations provided by:
*
@@ -18,6 +18,8 @@
* Polish Michael Rakowski <mrak@gmx.de>
* Spanish Ruben Nunez Francisco <ruben.nunez@tang-it.com>
* Greek Dimitrios Dimitrakos <mail@dimitrios.de>
+ * Swedish Tomas Prybil <tomas@prybil.se>
+ * Romanian Paul Lacatus <paul@campina.iiruc.ro>
*
*/
@@ -79,6 +81,8 @@ const tI18nPhrase Phrases[] = {
"Polski",
"Español",
"Ellinika",
+ "Svenska",
+ "Romaneste",
},
// Menu titles:
{ "VDR",
@@ -93,6 +97,8 @@ const tI18nPhrase Phrases[] = {
"VDR",
"VDR",
"VDR",
+ "VDR",
+ "VDR",
},
{ "Schedule",
"Programm",
@@ -106,6 +112,8 @@ const tI18nPhrase Phrases[] = {
"Program",
"Programa",
"Programma",
+ "Program",
+ "Program",
},
{ "Channels",
"Kanäle",
@@ -119,6 +127,8 @@ const tI18nPhrase Phrases[] = {
"Kanaly",
"Canales",
"Kanalia",
+ "Kanaler",
+ "Canale",
},
{ "Timers",
"Timer",
@@ -132,6 +142,8 @@ const tI18nPhrase Phrases[] = {
"Timery",
"Timer",
"Programmatismos",
+ "Timers",
+ "Timere",
},
{ "Recordings",
"Aufzeichnungen",
@@ -145,6 +157,8 @@ const tI18nPhrase Phrases[] = {
"Nagrania",
"Grabaciones",
"Egrafes",
+ "Inspelningar",
+ "Inregistrari",
},
{ "Setup",
"Einstellungen",
@@ -158,6 +172,8 @@ const tI18nPhrase Phrases[] = {
"Nastawy",
"Configuración",
"Rithmisis",
+ "Inställningar",
+ "Setari",
},
{ "Commands",
"Befehle",
@@ -171,6 +187,8 @@ const tI18nPhrase Phrases[] = {
"Rozkazy",
"Órdenes",
"Entoles",
+ "Kommandon",
+ "Comenzi",
},
{ "Edit channel",
"Kanal editieren",
@@ -184,6 +202,8 @@ const tI18nPhrase Phrases[] = {
"Ustawienie kanalu",
"Modificar canal",
"Prosarmoges kanaliou",
+ "Ändra kanal",
+ "Editare canale",
},
{ "Edit timer",
"Timer editieren",
@@ -197,6 +217,8 @@ const tI18nPhrase Phrases[] = {
"Ustawienie timerow",
"Modificar timer",
"Prosarmoges programmatismou",
+ "Ändra timer",
+ "Editare timere",
},
{ "Event",
"Sendung",
@@ -210,6 +232,8 @@ const tI18nPhrase Phrases[] = {
"Audycja",
"Evento",
"Ekpompi",
+ "Sändning",
+ "Evenimente",
},
{ "Summary",
"Inhalt",
@@ -223,6 +247,8 @@ const tI18nPhrase Phrases[] = {
"Zawartosc",
"Resúmen",
"Periexomeno",
+ "Sammandrag",
+ "Cuprins",
},
{ "Schedule - %s",
"Programm - %s",
@@ -236,6 +262,8 @@ const tI18nPhrase Phrases[] = {
"Program - %s",
"Programa - %s",
"Programma - %s",
+ "Program - %s",
+ "Program - %s",
},
{ "What's on now?",
"Was läuft jetzt?",
@@ -249,6 +277,8 @@ const tI18nPhrase Phrases[] = {
"Program biezacy",
"¿Qué hay ahora?",
"Ti pezi tora",
+ "Vilket program sänds nu?",
+ "Programul actual?",
},
{ "What's on next?",
"Was läuft als nächstes?",
@@ -262,6 +292,8 @@ const tI18nPhrase Phrases[] = {
"Program nastepny",
"¿Qué hay proximo?",
"Ti tha peksi meta",
+ "Vilket är nästa program?",
+ "Programul urmator?",
},
// Button texts (should not be more than 10 characters!):
{ "Edit",
@@ -276,6 +308,8 @@ const tI18nPhrase Phrases[] = {
"Edycja",
"Modificar",
"Prosarmogi",
+ "Ändra",
+ "Modificare",
},
{ "New",
"Neu",
@@ -289,6 +323,8 @@ const tI18nPhrase Phrases[] = {
"Nowy",
"Nuevo",
"Neo",
+ "Ny",
+ "Nou",
},
{ "Delete",
"Löschen",
@@ -302,6 +338,8 @@ const tI18nPhrase Phrases[] = {
"Usunac",
"Borrar",
"Swisimo",
+ "Ta bort",
+ "Sterge",
},
{ "Mark",
"Markieren",
@@ -315,6 +353,8 @@ const tI18nPhrase Phrases[] = {
"Zaznaczyc",
"Marcar",
"Markarisma",
+ "Märk",
+ "Marcheaza",
},
{ "On/Off",
"Ein/Aus",
@@ -328,6 +368,8 @@ const tI18nPhrase Phrases[] = {
"Zal./ Wyl.",
"On/Off",
"Energo/Klisto",
+ "På/Av",
+ "Pornit/Oprit",
},
{ "Record",
"Aufnehmen",
@@ -341,6 +383,8 @@ const tI18nPhrase Phrases[] = {
"Nagrywac",
"Grabar",
"Egrafi",
+ "Inspelning",
+ "Inregistrare",
},
{ "Play",
"Wiedergabe",
@@ -354,6 +398,8 @@ const tI18nPhrase Phrases[] = {
"Odtwarzac",
"Play",
"Anametadosi",
+ "Spela upp",
+ "Redare",
},
{ "Rewind",
"Anfang",
@@ -367,6 +413,8 @@ const tI18nPhrase Phrases[] = {
"Poczatek",
"Rebobinar",
"Arxi",
+ "Återspolning",
+ "Inapoi",
},
{ "Button$Stop",
"Beenden",
@@ -380,6 +428,8 @@ const tI18nPhrase Phrases[] = {
"Zakonczyc",
"Parar",
"Terma",
+ "Stopp",
+ "Stop",
},
{ "Resume",
"Weiter",
@@ -393,6 +443,8 @@ const tI18nPhrase Phrases[] = {
"Dalej",
"Continuar",
"Sinexia",
+ "Fortsätt",
+ "Reia",
},
{ "Summary",
"Inhalt",
@@ -406,6 +458,8 @@ const tI18nPhrase Phrases[] = {
"Zawartosc",
"Resumen",
"Periexomeno",
+ "Sammandrag",
+ "Cuprins",
},
{ "Open",
"Öffnen",
@@ -419,6 +473,8 @@ const tI18nPhrase Phrases[] = {
"Otworzyc",
"Abrir",
"Anigma",
+ "Öppna",
+ "Deschide",
},
{ "Switch",
"Umschalten",
@@ -432,6 +488,8 @@ const tI18nPhrase Phrases[] = {
"Przelaczyc",
"Cambiar",
"Alagi",
+ "Byt",
+ "Schimba",
},
{ "Now",
"Jetzt",
@@ -445,6 +503,8 @@ const tI18nPhrase Phrases[] = {
"Teraz",
"Ahora",
"Tora",
+ "Nu",
+ "Acum",
},
{ "Next",
"Nächste",
@@ -458,6 +518,8 @@ const tI18nPhrase Phrases[] = {
"Nastepny",
"Siguiente",
"Epomeno",
+ "Nästa",
+ "Urmator",
},
{ "Button$Schedule",
"Programm",
@@ -471,6 +533,8 @@ const tI18nPhrase Phrases[] = {
"Program",
"Programa",
"Programma",
+ "Program",
+ "Program",
},
{ "Language",
"Sprache",
@@ -484,6 +548,8 @@ const tI18nPhrase Phrases[] = {
"Jezyk",
"Lengua",
"Glosa",
+ "Språk",
+ "Limba",
},
{ "Eject",
"Auswerfen",
@@ -497,6 +563,8 @@ const tI18nPhrase Phrases[] = {
"Wyrzucenie",
"Eyectar",
"Apovoli",
+ "Mata ut",
+ "Ejecteaza",
},
{ "ABC/abc",
"ABC/abc",
@@ -510,6 +578,8 @@ const tI18nPhrase Phrases[] = {
"ABC/abc",
"ABC/abc",
"ABC/abc",
+ "ABC/abc",
+ "ABC/abc",
},
{ "Insert",
"Einfügen",
@@ -523,6 +593,8 @@ const tI18nPhrase Phrases[] = {
"",// TODO
"",// TODO
"Isodos",
+ "Infoga",
+ "Insereaza",
},
{ "Overwrite",
"Überschreiben",
@@ -536,6 +608,8 @@ const tI18nPhrase Phrases[] = {
"",// TODO
"",// TODO
"Epanagrafi",
+ "Skriv över",
+ "Suprascrie",
},
// Confirmations:
{ "Delete channel?",
@@ -550,6 +624,8 @@ const tI18nPhrase Phrases[] = {
"Usunac kanal?",
"¿Eliminar canal?",
"Na sviso to kanali?",
+ "Ta bort kanalen?",
+ "Sterg canalul?",
},
{ "Delete timer?",
"Timer löschen?",
@@ -563,6 +639,8 @@ const tI18nPhrase Phrases[] = {
"Usunac timer?",
"¿Eliminar timer?",
"Svisimo tou programmitismou?",
+ "Ta bort timern?",
+ "Sterg timer-ul?",
},
{ "Delete recording?",
"Aufzeichnung löschen?",
@@ -576,6 +654,8 @@ const tI18nPhrase Phrases[] = {
"Usunac nagranie?",
"¿Eliminar grabacion?",
"Svisimo tis egrafis?",
+ "Ta bort inspelningen?",
+ "Sterg inregistrarea?",
},
{ "Timer still recording - really delete?",
"Timer zeichnet auf - trotzdem löschen?",
@@ -589,6 +669,8 @@ const tI18nPhrase Phrases[] = {
"Nagrywanie w trakcie - napewno usunac?",
"¿Timer activo - de verdad eliminarlo?",
"Ginete akoma programmatismeni egrafi - na svisti sigoura?",
+ "Timerstyrd inspelning pågår - Avbryta ändå?",
+ "Timer-ul in inregistrare - sterg?",
},
{ "Stop recording?",
"Aufzeichnung beenden?",
@@ -602,6 +684,8 @@ const tI18nPhrase Phrases[] = {
"Zakonczyc nagranie?",
"¿Parar grabación?",
"Akirosi egrafis?",
+ "Stanna inspelning?",
+ "Opresc inregistrarea?",
},
{ "on primary interface",
"auf dem primären Interface",
@@ -615,6 +699,8 @@ const tI18nPhrase Phrases[] = {
"na pierwszym interfejsie",
"en interface primario",
"stin protevon karta",
+ "från det första enheten?",
+ "pe prima interfata",
},
{ "Cancel editing?",
"Schneiden abbrechen?",
@@ -628,6 +714,8 @@ const tI18nPhrase Phrases[] = {
"Zakonczyc montaz?",
"¿Cancelar modificación?",
"Akirosi alagon?",
+ "Avbryta editeringen?",
+ "Opresc editarea?",
},
{ "Really restart?",
"Wirklich neu starten?",
@@ -641,6 +729,8 @@ const tI18nPhrase Phrases[] = {
"Rzeczywiscie nowy start?",
"¿De verdad reiniciar?",
"Na gini sigoura epanekinisi?",
+ "Vill du verkligen starta om?",
+ "Esti sigur de repornire?",
},
{ "Recording - restart anyway?",
"Aufnahme läuft - trotzdem neu starten?",
@@ -654,6 +744,8 @@ const tI18nPhrase Phrases[] = {
"Nagrywanie w trakcie - rzeczywiscie nowy start?",
"¿Grabando - reiniciar?",
"Ginete egrafi - na gini epanekinisi sigoura?",
+ "Inspelning pågår, vill du starta om i alla fall?",
+ "In inregistrare - repornesc?",
},
{ "Recording - shut down anyway?",
"Aufnahme läuft - trotzdem ausschalten?",
@@ -667,6 +759,8 @@ const tI18nPhrase Phrases[] = {
"Nagrywanie w trakcie - mimo to wylaczyc?",
"¿Grabando - apagar?",
"Ginete egrafi - na stamatisi i litourgia sigoura?",
+ "Inspelning pågår, vill du avbryta i alla fall?",
+ "In inregistrare - opresc?",
},
{ "Recording in %d minutes, shut down anyway?",
"Aufnahme in %d Minuten - trotzdem ausschalten?",
@@ -680,6 +774,8 @@ const tI18nPhrase Phrases[] = {
"Nagrywanie za %d minut - mimo to wylaczyc?",
"¿Grabando en %d minutos, de verdad cortar?",
"Anamenete egrafi se %d lepta - na stamatisi i litourgia sigoura?",
+ "Inspelning startar om %d minuter, vill du avsluta?",
+ "Inregistrez in %d minute, opresc?",
},
{ "Press any key to cancel shutdown",
"Taste drücken um Shutdown abzubrechen",
@@ -693,6 +789,8 @@ const tI18nPhrase Phrases[] = {
"Dowolny przycisk zatrzyma wylaczanie",
"Pulse una tecla para interrumpir corte",
"Piese ena pliktro na stamatisi to katevasma",
+ "Tryck valfri knapp för att avbryta nedstängning",
+ "Apasa orice tasta pentru a anula inchiderea",
},
// Channel parameters:
{ "Name",
@@ -707,6 +805,8 @@ const tI18nPhrase Phrases[] = {
"Nazwa",
"Nombre",
"Onoma",
+ "Namn",
+ "Nume",
},
{ "Frequency",
"Frequenz",
@@ -720,6 +820,8 @@ const tI18nPhrase Phrases[] = {
"Czestotliwosc",
"Frecuencia",
"Sixnotita",
+ "Frekvens",
+ "Frecventa",
},
{ "Polarization",
"Polarisation",
@@ -733,6 +835,8 @@ const tI18nPhrase Phrases[] = {
"Polaryzacja",
"Polarización",
"Polosi",
+ "Polarisation",
+ "Polarizare",
},
{ "DiSEqC",
"DiSEqC",
@@ -746,6 +850,8 @@ const tI18nPhrase Phrases[] = {
"DiSEqC",
"DiSEqC",
"DiSEqC",
+ "DiSEqC",
+ "DiSEqC",
},
{ "Srate",
"Srate",
@@ -759,6 +865,8 @@ const tI18nPhrase Phrases[] = {
"Srate",
"Srate",
"Srate",
+ "Srate",
+ "Rata simboluri",
},
{ "Vpid",
"Vpid",
@@ -772,6 +880,8 @@ const tI18nPhrase Phrases[] = {
"Vpid",
"Vpid",
"Vpid",
+ "Vpid",
+ "PID Video",
},
{ "Apid1",
"Apid1",
@@ -785,6 +895,8 @@ const tI18nPhrase Phrases[] = {
"Apid1",
"Apid1",
"Apid1",
+ "Apid1",
+ "PID Audio (1)",
},
{ "Apid2",
"Apid2",
@@ -798,6 +910,8 @@ const tI18nPhrase Phrases[] = {
"Apid2",
"Apid2",
"Apid2",
+ "Apid2",
+ "PID Audio (2)",
},
{ "Dpid1",
"Dpid1",
@@ -811,6 +925,8 @@ const tI18nPhrase Phrases[] = {
"Dpid1",
"Dpid1",
"Dpid1",
+ "Dpid1",
+ "PID AC3 (1)",
},
{ "Dpid2",
"Dpid2",
@@ -824,6 +940,8 @@ const tI18nPhrase Phrases[] = {
"Dpid2",
"Dpid2",
"Dpid2",
+ "Dpid2",
+ "PID AC3 (2)",
},
{ "Tpid",
"Tpid",
@@ -837,6 +955,8 @@ const tI18nPhrase Phrases[] = {
"Tpid",
"Tpid",
"Tpid",
+ "Tpid",
+ "PID Teletext",
},
{ "CA",
"CA",
@@ -850,6 +970,8 @@ const tI18nPhrase Phrases[] = {
"CA",
"CA",
"CA",
+ "CA",
+ "Criptare",
},
{ "Pnr",
"Pnr",
@@ -863,6 +985,8 @@ const tI18nPhrase Phrases[] = {
"Pnr",
"Pnr",
"Pnr",
+ "Pnr",
+ "Nr. Prog.",
},
// Timer parameters:
{ "Active",
@@ -877,6 +1001,8 @@ const tI18nPhrase Phrases[] = {
"Aktywny",
"Activo",
"Energo",
+ "Aktiv",
+ "Activ",
},
{ "Channel",
"Kanal",
@@ -890,6 +1016,8 @@ const tI18nPhrase Phrases[] = {
"Kanal",
"Canal",
"Kanali",
+ "Kanal",
+ "Canal",
},
{ "Day",
"Tag",
@@ -903,6 +1031,8 @@ const tI18nPhrase Phrases[] = {
"Dzien",
"Día",
"Imera",
+ "Dag",
+ "Ziua",
},
{ "Start",
"Anfang",
@@ -916,6 +1046,8 @@ const tI18nPhrase Phrases[] = {
"Poczatek",
"Comienzo",
"Arxi",
+ "Börjar",
+ "Start",
},
{ "Stop",
"Ende",
@@ -929,6 +1061,8 @@ const tI18nPhrase Phrases[] = {
"Koniec",
"Fin",
"Telos",
+ "Slutar",
+ "Stop",
},
{ "Priority",
"Priorität",
@@ -942,6 +1076,8 @@ const tI18nPhrase Phrases[] = {
"Priorytet",
"Prioridad",
"Protereotita",
+ "Prioritet",
+ "Prioritate",
},
{ "Lifetime",
"Lebensdauer",
@@ -955,6 +1091,8 @@ const tI18nPhrase Phrases[] = {
"Trwalosc dni",
"Durabilidad",
"Xronos Zois",
+ "Speltid",
+ "Durata",
},
{ "File",
"Datei",
@@ -968,6 +1106,8 @@ const tI18nPhrase Phrases[] = {
"Plik",
"Fichero",
"Arxeio",
+ "Filnamn",
+ "Fisier",
},
{ "First day",
"Erster Tag",
@@ -981,6 +1121,8 @@ const tI18nPhrase Phrases[] = {
"Pierwszy dzien",
"Primer día",
"Proti mera",
+ "Första dag",
+ "Prima zi",
},
// Error messages:
{ "Channel is being used by a timer!",
@@ -995,6 +1137,8 @@ const tI18nPhrase Phrases[] = {
"Kanal jest zajety przez timer nagran",
"¡Canal está ocupado por un timer!",
"To kanali xrisimopiite apo programmatismeni thesi",
+ "Kanalen används av en timer!",
+ "Canalul este utilizat de un timer!",
},
{ "Can't switch channel!",
"Kanal kann nicht umgeschaltet werden!",
@@ -1008,6 +1152,8 @@ const tI18nPhrase Phrases[] = {
"Kanal nie moze byc teraz przelaczony!",
"¡No puedo cambiar canal!",
"Den mporo na pao sto kanali!",
+ "Omöjligt att byta kanal!",
+ "Nu pot comuta canalul!",
},
{ "Timer is recording!",
"Timer zeichnet gerade auf!",
@@ -1021,6 +1167,8 @@ const tI18nPhrase Phrases[] = {
"Timer nagrywa!",
"¡Timer esta grabando!",
"Ginete progrmamatismeni egrafi!",
+ "Timerstyrd inspelning pågår!",
+ "Timer-ul este in inregistrare!",
},
{ "Error while accessing recording!",
"Fehler beim Ansprechen der Aufzeichnung!",
@@ -1034,6 +1182,8 @@ const tI18nPhrase Phrases[] = {
"Blad - brak dostepu do nagrania!",
"¡Error al accesar la grabación!",
"Lathos stin evresi tis egrafis!",
+ "Det går inte att läsa inspelningen",
+ "Eroare in timpul accesarii inregistrarii",
},
{ "Error while deleting recording!",
"Fehler beim Löschen der Aufzeichnung!",
@@ -1047,6 +1197,8 @@ const tI18nPhrase Phrases[] = {
"Blad przy usuwaniu nagrania!",
"¡Error al borrar la grabación!",
"Lathos stin prospathia na svisti i egrafi!",
+ "Det går inte att ta bort inspelningen",
+ "Eroare in timpul stergerii inregistrarii!",
},
{ "*** Invalid Channel ***",
"*** Ungültiger Kanal ***",
@@ -1060,6 +1212,8 @@ const tI18nPhrase Phrases[] = {
"*** Niewazny kanal ***",
"*** Canal inválido ***",
"*** Kanali akiro ***",
+ "*** Felaktig kanal ***",
+ "*** Canal invalid ***",
},
{ "No free DVB device to record!",
"Keine freie DVB-Karte zum Aufnehmen!",
@@ -1073,6 +1227,8 @@ const tI18nPhrase Phrases[] = {
"Brak wolnej karty DVB do nagrywania!",
"¡No hay dispositivo DVB disponible para grabar!",
"Den iparxi elevteri DVB Karta gia egrafi!",
+ "Det finns ingen ledig DVB enhet för inspelning!",
+ "Nu mai sunt dispozitive DVB pentru inregistrare!",
},
{ "Channel locked (recording)!",
"Kanal blockiert (zeichnet auf)!",
@@ -1086,6 +1242,8 @@ const tI18nPhrase Phrases[] = {
"Kanal zablokowany (nagrywanie w toku)!",
"¡Canal bloqueado (grabando)!",
"To kanali ine mplokarismeno (Ginete egrafi)!",
+ "Kanalen är låst (inspelning pågår)!",
+ "Canal blocat (inregistrare)!",
},
{ "Can't start Transfer Mode!",
"Transfer-Mode kann nicht gestartet werden!",
@@ -1099,6 +1257,8 @@ const tI18nPhrase Phrases[] = {
"Tryb transferowy jest niemozliwy!",
"¡No puedo iniciar modo de transferencia!",
"Den mpori na arxisi to Transfer-Mode!",
+ "Kan inte starta Transfer Mode!",
+ "Nu pot porni Modul de Transfer!",
},
{ "Can't start editing process!",
"Schnitt kann nicht gestartet werden!",
@@ -1112,6 +1272,8 @@ const tI18nPhrase Phrases[] = {
"Uruchamianie montazu jest niemozliwe!",
"¡No puedo iniciar proceso de modificación!",
"Den mpori na arxisi to kopsimo tis tenias!",
+ "Kan inte starta editering!",
+ "Nu pot porni procesul de editare!",
},
{ "Editing process already active!",
"Schnitt bereits aktiv!",
@@ -1125,6 +1287,8 @@ const tI18nPhrase Phrases[] = {
"Montaz w toku!",
"¡Proceso de modificación ya fue iniciado!",
"To kopsimo ti tenias ini idi se litourgia!",
+ "Editering är redan aktiv!",
+ "Procesul de editare este activ!",
},
{ "Can't shutdown - option '-s' not given!",
"Shutdown unmöglich - Option '-s' fehlt!",
@@ -1138,6 +1302,8 @@ const tI18nPhrase Phrases[] = {
"Wylaczenie niemozliwe - brak opcji '-s'!",
"¡No puedo cortar - opción '-s' absente!",
"Den mporo na kliso ton ipologisti. Lipi i parametros '-s'!",
+ "Kan inte avsluta, måste använda flagga '-s'",
+ "Nu pot opri calculatorul - vezi optiunea '-s'",
},
{ "Low disk space!",
"Platte beinahe voll!",
@@ -1151,6 +1317,8 @@ const tI18nPhrase Phrases[] = {
"Dysk wkrotce pelny!",
"¡Disco casi lleno",
"O Skliros kontevi na gemisi!",
+ "Lågt diskutrymme!",
+ "Spatiu scazut pe disc!",
},
// Setup pages:
{ "OSD",
@@ -1165,6 +1333,8 @@ const tI18nPhrase Phrases[] = {
"OSD",
"OSD",
"OSD",
+ "OSD",
+ "OSD",
},
{ "EPG",
"EPG",
@@ -1178,6 +1348,8 @@ const tI18nPhrase Phrases[] = {
"EPG",
"EPG",
"EPG",
+ "Elektronisk programguide",
+ "EPG",
},
{ "DVB",
"DVB",
@@ -1191,6 +1363,8 @@ const tI18nPhrase Phrases[] = {
"DVB",
"DVB",
"DVB",
+ "DVB",
+ "Placa DVB",
},
{ "LNB",
"LNB",
@@ -1204,6 +1378,8 @@ const tI18nPhrase Phrases[] = {
"LNB",
"LNB",
"LNB",
+ "LNB",
+ "LNB",
},
{ "CICAM",
"CICAM",
@@ -1217,6 +1393,8 @@ const tI18nPhrase Phrases[] = {
"CICAM",
"CICAM",
"CICAM",
+ "CICAM",
+ "Acces conditionat",
},
{ "Recording",
"Aufnahme",
@@ -1230,6 +1408,8 @@ const tI18nPhrase Phrases[] = {
"Nagranie",
"Grabación",
"Egrafi",
+ "Inspelning",
+ "Inregistrare",
},
{ "Replay",
"Wiedergabe",
@@ -1243,6 +1423,8 @@ const tI18nPhrase Phrases[] = {
"Odtwarzanie",
"Poner",
"Anametadosi",
+ "Repris",
+ "Redare",
},
{ "Miscellaneous",
"Sonstiges",
@@ -1256,6 +1438,8 @@ const tI18nPhrase Phrases[] = {
"Pozostale",
"Varios",
"Diafora",
+ "Diverse",
+ "Diverse",
},
{ "Plugins",
"Plugins",
@@ -1295,6 +1479,8 @@ const tI18nPhrase Phrases[] = {
"Zastartowac",
"Reiniciar",
"Epanekinisi",
+ "Omstart",
+ "Restart",
},
// Setup parameters:
{ "Setup.OSD$Language",
@@ -1309,6 +1495,8 @@ const tI18nPhrase Phrases[] = {
"Jezyk",
"Lengua",
"Glosa",
+ "Språk",
+ "Limba OSD",
},
{ "Setup.OSD$Width",
"Breite",
@@ -1322,6 +1510,8 @@ const tI18nPhrase Phrases[] = {
"Szerokosc",
"Anchura",
"Makros",
+ "Bredd",
+ "Latime OSD",
},
{ "Setup.OSD$Height",
"Höhe",
@@ -1335,6 +1525,8 @@ const tI18nPhrase Phrases[] = {
"Wysokosc",
"Altura",
"Ipsos",
+ "Höjd",
+ "Inaltime OSD",
},
{ "Setup.OSD$Message time (s)",
"Anzeigedauer für Nachrichten (s)",
@@ -1348,6 +1540,8 @@ const tI18nPhrase Phrases[] = {
"Czas wyswietlania wiadomosci (s)",
"Duración muestra mensajes (s)",
"Xronos endiksis minimaton (d)",
+ "Tid för meddelanden (sek)",
+ "Timp afisare mesaj (sec)",
},
{ "Setup.OSD$Channel info position",
"Kanal-Info Position",
@@ -1361,6 +1555,8 @@ const tI18nPhrase Phrases[] = {
"Lokalizacja informacji o kanale",
"Posición para información canal",
"Thesi Pliroforias kanalion",
+ "Placering av kanalinformation",
+ "Pozitie info canal",
},
{ "Setup.OSD$Info on channel switch",
"Info beim Kanalwechsel",
@@ -1374,6 +1570,8 @@ const tI18nPhrase Phrases[] = {
"Informacja przy zmianie kanalu",
"Información para cambio de canal",
"Plirofories stin alagi kanaliou",
+ "Information vid kanalbyte",
+ "Info despre comutare canal",
},
{ "Setup.OSD$Scroll pages",
"Seitenweise scrollen",
@@ -1387,6 +1585,8 @@ const tI18nPhrase Phrases[] = {
"Przesuwac stronami",
"Desplazar página entera",
"Scroll selidas",
+ "Bläddra sidor",
+ "Deruleaza pagini",
},
{ "Setup.OSD$Sort timers",
"Timer sortieren",
@@ -1400,6 +1600,8 @@ const tI18nPhrase Phrases[] = {
"Sortowanie timerow",
"Ordenar timer",
"Organosi programmatismenon",
+ "Sortera timers",
+ "Sortare timere",
},
{ "Setup.OSD$Recording directories",
"Aufnahmeverzeichnisse",
@@ -1413,6 +1615,8 @@ const tI18nPhrase Phrases[] = {
"Wykaz nagran",
"Directorios para grabación",
"Fakeli egrafon",
+ "Kataloger för inspelningar",
+ "Directoare inregistrari",
},
{ "Setup.EPG$EPG scan timeout (h)",
"Zeit bis EPG Scan (h)",
@@ -1426,6 +1630,8 @@ const tI18nPhrase Phrases[] = {
"Czas do skanu EPG (h)",
"Tiempo hasta exploración EPG (h)",
"Xronos mexri sarosi EPG se Ores",
+ "EPG sökning timeout",
+ "Timeout EPG",
},
{ "Setup.EPG$EPG bugfix level",
"EPG Fehlerbereinigung",
@@ -1439,6 +1645,8 @@ const tI18nPhrase Phrases[] = {
"Poziom bledow EPG",
"Nivel para arreglar EPG",
"EPG Bugfix Vathmos",
+ "Nivå för EPG bugfix",
+ "Nivel corectie EPG",
},
{ "Setup.EPG$Set system time",
"Systemzeit stellen",
@@ -1452,6 +1660,8 @@ const tI18nPhrase Phrases[] = {
"Ustawianie czasu",
"Ajustar reloj de sistema",
"Sintonismos Oras ipologosti",
+ "Ställ in systemtid",
+ "Seteaza ceasul sistem",
},
{ "Setup.EPG$Use time from transponder",
"Transponder für Systemzeit",
@@ -1465,6 +1675,8 @@ const tI18nPhrase Phrases[] = {
"Transponder do ustawiania czasu",
"Transponder para reloj de sistema",
"Transponder gia sintonismo tis oras",
+ "Använd klockan från fransponder",
+ "Preia ceasul din transponder",
},
{ "Setup.DVB$Primary DVB interface",
"Primäres DVB Interface",
@@ -1478,6 +1690,8 @@ const tI18nPhrase Phrases[] = {
"Pierwotny interfejs DVB",
"Primer interface DVB",
"Protevon DVB karta",
+ "Primär DVB enhet",
+ "Placa DVB primara",
},
{ "Setup.DVB$Video format",
"Video Format",
@@ -1491,6 +1705,8 @@ const tI18nPhrase Phrases[] = {
"Format telewizyjny",
"Formato Vídeo",
"Video Format",
+ "Video format",
+ "Format Video",
},
{ "Setup.LNB$SLOF (MHz)",
"SLOF (MHz)",
@@ -1504,6 +1720,8 @@ const tI18nPhrase Phrases[] = {
"SLOF (MHz)",
"SLOF (MHz)",
"SLOF (MHz)",
+ "SLOF (MHz)",
+ "SLOF (MHz)",
},
{ "Setup.LNB$Low LNB frequency (MHz)",
"Untere LNB-Frequenz (MHz)",
@@ -1517,6 +1735,8 @@ const tI18nPhrase Phrases[] = {
"Dolna czestotliwosc LNB (MHz)",
"Frecuencia baja LNB (MHz)",
"Kato LNB-Sixnotita (MHz)",
+ "Undre LNB frekvens (MHz)",
+ "Frecvnta LO LNB (Mhz)",
},
{ "Setup.LNB$High LNB frequency (MHz)",
"Obere LNB-Frequenz (MHz)",
@@ -1530,6 +1750,8 @@ const tI18nPhrase Phrases[] = {
"Gorna czestotliwosc LNB (MHz)",
"Frecuencia alta LNB (MHz)",
"Ano LNB-Sixnotita (MHz)",
+ "Övre LNB frekvens (MHz)",
+ "Feecventa HI LNB (MHz)",
},
{ "Setup.LNB$Use DiSEqC",
"DiSEqC benutzen",
@@ -1543,6 +1765,8 @@ const tI18nPhrase Phrases[] = {
"Uzywac DiSEqC",
"Utilizar DiSEqC",
"Energopiisi DiSEqC",
+ "Använd DiSEqC",
+ "Utilizez DiSEqC",
},
{ "Setup.CICAM$CICAM DVB",
"CICAM DVB",
@@ -1556,6 +1780,8 @@ const tI18nPhrase Phrases[] = {
"CICAM DVB",
"CICAM DVB",
"CICAM DVB",
+ "CICAM DVB",
+ "Setare acces conditional",
},
{ "Setup.Recording$Margin at start (min)",
"Zeitpuffer bei Anfang (min)",
@@ -1569,6 +1795,8 @@ const tI18nPhrase Phrases[] = {
"Poczatkowy czas buforowy (min)",
"Comenzar grabación antes (min)",
"Prosthetos xronos prin arxi (lepta)",
+ "Marginal för start (min)",
+ "Margine la pornire (min)",
},
{ "Setup.Recording$Margin at stop (min)",
"Zeitpuffer bei Ende (min)",
@@ -1582,6 +1810,8 @@ const tI18nPhrase Phrases[] = {
"Koncowy czas buforowy (min)",
"Cortar grabación después (min)",
"Prosthetos xronos sto telos (lepta)",
+ "Marginal för stopp (min)",
+ "Margine la oprire (min)",
},
{ "Setup.Recording$Primary limit",
"Primär-Limit",
@@ -1595,6 +1825,8 @@ const tI18nPhrase Phrases[] = {
"Pierwotny limit",
"L'mite primario",
"Protevon limit",
+ "Primär gräns",
+ "Limita Primara",
},
{ "Setup.Recording$Default priority",
"Default Priorität",
@@ -1608,6 +1840,8 @@ const tI18nPhrase Phrases[] = {
"Priorytet pierwotny",
"Prioridad predefinida",
"Protereotita",
+ "Normal prioritet",
+ "Prioritate implicita",
},
{ "Setup.Recording$Default lifetime (d)",
"Default Lebensdauer (d)",
@@ -1621,6 +1855,8 @@ const tI18nPhrase Phrases[] = {
"Pierwotna trwalosc (d)",
"Duración predefinida",
"Xronos zois",
+ "Normal livstid",
+ "Durata predefinita",
},
{ "Setup.Recording$Use episode name",
"Episodenname verwenden",
@@ -1634,6 +1870,8 @@ const tI18nPhrase Phrases[] = {
"Czy uzywac nazwe epizodu",
"Utilizar nombre de episodo",
"Xrisimopiisi onomatos episodiou",
+ "Använd episodnamn",
+ "Utilizeaza numele episodului",
},
{ "Setup.Recording$Mark instant recording",
"Direktaufzeichnung markieren",
@@ -1647,6 +1885,8 @@ const tI18nPhrase Phrases[] = {
"Zaznaczyc natychm. nagranie",
"Marcar grabaciones instantáneas",
"Markarisma apevthias egrafis",
+ "Märk direktinspelning",
+ "Inregistrare imediata",
},
{ "Setup.Recording$Name instant recording",
"Direktaufzeichnung benennen",
@@ -1660,6 +1900,8 @@ const tI18nPhrase Phrases[] = {
"Nazwac natychm. nagranie",
"Nombrar grabaciones instantáneas",
"eponomasi apevthias egrafis",
+ "Namnge direktinspelning",
+ "Nume inregistrare imediata",
},
{ "Setup.Recording$Instant rec. time (min)",
"Dauer der Direktaufzeichnung (min)",
@@ -1673,6 +1915,8 @@ const tI18nPhrase Phrases[] = {
"",//TODO
"",//TODO
"",//TODO
+ "Direktinspelning längd (min)",
+ "Timpul de inregistarea imediata (min)",
},
{ "Setup.Recording$Record Dolby Digital",
"Dolby Digital Ton aufzeichnen",
@@ -1686,6 +1930,8 @@ const tI18nPhrase Phrases[] = {
"Nagrywac Dolby Digital",
"Grabar sonido Dolby Digital",
"Egrafi tou Dolby Digital ixou",
+ "Spela in ljud med Dolby Digital",
+ "Inregistreaza Dolby Digital",
},
{ "Setup.Recording$Max. video file size (MB)",
"Max. Video Dateigröße (MB)",
@@ -1699,6 +1945,8 @@ const tI18nPhrase Phrases[] = {
"Maks. wielkosc pliku (MB)",
"Tamaño máx. ficheros (MB)",
"Megisto megethos arxeiou (MB)",
+ "Maximal filstorlek för inspelning (MB)",
+ "Dimensiune maxima a fisierului video (MB)",
},
{ "Setup.Recording$Split edited files",
"Editierte Dateien aufteilen",
@@ -1712,6 +1960,8 @@ const tI18nPhrase Phrases[] = {
"Dzielic montowane pliki",
"Quebrar ficheros",
"Diamelisma epeksergasm. arxeion",
+ "Dela upp editerade filer",
+ "Separare fisiere editate",
},
{ "Setup.Replay$Multi speed mode",
"MultiSpeed Modus",
@@ -1725,6 +1975,8 @@ const tI18nPhrase Phrases[] = {
"Tryb wielopredkosciowy",
"Modo multi-velocidad",
"Multispeed modus",
+ "Multispeed mode",
+ "Mod multi-rata",
},
{ "Setup.Replay$Show replay mode",
"Wiedergabestatus anzeigen",
@@ -1738,6 +1990,8 @@ const tI18nPhrase Phrases[] = {
"Wyswietlac status odtwarzania",
"Mostrar modo de replay",
"Endiksi status anametadosis",
+ "Visa uppspelnings mode",
+ "Afiseaza modul de redare",
},
{ "Setup.Miscellaneous$Min. event timeout (min)",
"Mindest Event Pause (min)",
@@ -1751,6 +2005,8 @@ const tI18nPhrase Phrases[] = {
"Min. czas do nast. akcji (Event) (min)",
"Tiempo mínimo pausa (min)",
"Elaxistos Xronos paremvolis (lepta)",
+ "Minsta händelse-pause (min)",
+ "MinEventTimeout (min)",
},
{ "Setup.Miscellaneous$Min. user inactivity (min)",
"Mindest Benutzer-Inaktivität (min)",
@@ -1764,6 +2020,8 @@ const tI18nPhrase Phrases[] = {
"Min. brak aktywnosci uzytkownika (min)",
"Tiempo mínimo inactividad (min)",
"Elaxistos xronos mi xrisis (lepta)",
+ "Minsta anändar-inaktivitet (min)",
+ "Durata minima de inactivitate (min)",
},
{ "Setup.Miscellaneous$SVDRP timeout (s)",
"SVDRP Timeout (s)",
@@ -1777,6 +2035,8 @@ const tI18nPhrase Phrases[] = {
"Min. brak aktywnosci SVDRP (s)",
"SVDRP interrupción (s)",
"SVDRP Timeout (d)",
+ "SVDRP Timeout (d)",
+ "Timeout SVDRP (sec)",
},
// The days of the week:
{ "MTWTFSS",
@@ -1791,6 +2051,8 @@ const tI18nPhrase Phrases[] = {
"PWSCPSN",
"LMMJVSD",
"DTTPPSK",
+ "MTOTFLS",
+ "LMMJVSD",
},
{ "MonTueWedThuFriSatSun", // must all be 3 letters!
"MonDieMitDonFreSamSon",
@@ -1804,6 +2066,8 @@ const tI18nPhrase Phrases[] = {
"PonWtoSroCzwPiaSobNie",
"LunMarMieJueVieSabDom",
"DevTriTetPemParSavKir",
+ "MånTisOnsTorFreLörSön",
+ "LunMarMieJoiVinSimDum",
},
// The allowed characters in strings:
{ " abcdefghijklmnopqrstuvwxyz0123456789-.#~",
@@ -1818,6 +2082,8 @@ const tI18nPhrase Phrases[] = {
"",// TODO
" aábcdeéfghiíjklmnñoópqrstuúvwxyz0123456789-.#~",
"",// TODO
+ " abcdefghijklmnopqrstuvxyzåäö0123456789-.#~",
+ " abcdefghijklmnopqrstuvwxyz0123456789-.#~",
},
// Learning keys:
{ "Learning Remote Control Keys",
@@ -1832,6 +2098,8 @@ const tI18nPhrase Phrases[] = {
"Nauka kodu pilota",
"Aprendiendo teclas del telemando",
"Ekmathisi Remote Control",
+ "Inlärning av fjärrkontrollsknappar",
+ "Invatare taste telecomanda",
},
{ "Phase 1: Detecting RC code type",
"Phase 1: FB Code feststellen",
@@ -1845,6 +2113,8 @@ const tI18nPhrase Phrases[] = {
"Faza 1: Detekcja typu kodu",
"Fase 1: Detectando tipo de receptor",
"Phasi 1: Dilosi RC Code",
+ "Steg1: identifiering av RC kod",
+ "Faza 1: Detectie tip telecomanda",
},
{ "Press any key on the RC unit",
"Eine Taste auf der FB drücken",
@@ -1858,6 +2128,8 @@ const tI18nPhrase Phrases[] = {
"Nacisnac klawisz pilota",
"Pulse una tecla en el telemando",
"Pata ena pliktro sto RC",
+ "Tryck valfri tangent på fjärrkontrollen",
+ "Apasati o tasta pe telecomanda",
},
{ "RC code detected!",
"FB Code erkannt!",
@@ -1871,6 +2143,8 @@ const tI18nPhrase Phrases[] = {
"Kod pilota zostal poznany!",
"¡Código detectado!",
"Evresi RC Code!",
+ "RC koden detekterad!",
+ "S-a detectat tipul telecomenzii!",
},
{ "Do not press any key...",
"Keine Taste drücken...",
@@ -1884,6 +2158,8 @@ const tI18nPhrase Phrases[] = {
"Nie naciskac klawiszy...",
"No pulse tecla...",
"Min patas Pliktra...",
+ "Tryck inte på någon knapp...",
+ "Nu apasati nicio tasta...",
},
{ "Phase 2: Learning specific key codes",
"Phase 2: Einzelne Tastencodes lernen",
@@ -1897,6 +2173,8 @@ const tI18nPhrase Phrases[] = {
"Faza 2: Nauka pojedynczych klawiszy",
"Fase 2: Aprendiendo códigos específicos",
"Fasi 2: Ekmathisi memonomenon kodikon pliktron",
+ "Fas 2: Inlärning av specifika knapp koder",
+ "Faza 2: Invatarea codurilor specifice tastelor",
},
{ "Press key for '%s'",
"Taste für '%s' drücken",
@@ -1910,6 +2188,8 @@ const tI18nPhrase Phrases[] = {
"Nacisnac klawisz dla '%s'",
"Pulsar tecla para '%s'",
"Pata to pliktro gia '%s'",
+ "Tryck på knappen för '%s'",
+ "Apasati tasta pentru '%s'",
},
{ "Press 'Up' to confirm",
"'Auf' drücken zum Bestätigen",
@@ -1923,6 +2203,8 @@ const tI18nPhrase Phrases[] = {
"Nacisnac 'Gora' do potwierdzenia",
"Pulse 'Arriba' para confirmar",
"Pata 'pano' gia apodoxi",
+ "Tryck 'Upp' för att bekräfta",
+ "Apsati 'Sus' pentru confirmare",
},
{ "Press 'Down' to continue",
"'Ab' drücken zum Weitermachen",
@@ -1936,6 +2218,8 @@ const tI18nPhrase Phrases[] = {
"Nacisnac 'Dol' zeby kontynuowac",
"Pulse 'Abajo' para confirmar",
"Pata 'kato' gia sinexia",
+ "Tryck 'Ner' för att bekräfta",
+ "Apasati jos pentru continuare",
},
{ "(press 'Up' to go back)",
"('Auf' drücken um zurückzugehen)",
@@ -1949,6 +2233,8 @@ const tI18nPhrase Phrases[] = {
"(Nacisnac 'Gora' cofa)",
"(Pulse 'Arriba' para retornar)",
"(Pata 'pano' gia na pas piso)",
+ "(Tryck 'Upp' för att backa)",
+ "(Apsati 'Sus' pentru revenire)",
},
{ "(press 'Down' to end key definition)",
"('Ab' drücken zum Beenden)",
@@ -1962,6 +2248,8 @@ const tI18nPhrase Phrases[] = {
"(Nacisnac 'Dol' by zakonczyc)",
"(Pulse 'Abajo' para terminar programación teclas)",
"(Pata 'Kato' gia termatismo)",
+ "(Tryck 'Ner' för att avsluta knapp definition)",
+ "(Apasati 'Jos' pentru terminare)",
},
{ "Phase 3: Saving key codes",
"Phase 3: Codes abspeichern",
@@ -1975,6 +2263,8 @@ const tI18nPhrase Phrases[] = {
"Faza 3: Zapamietac Kod",
"Fase 3: Guardar códigos de teclas",
"Fasi 3: Apothikevsi kodikon",
+ "Fas 3: Spara knappkoder",
+ "Faza 3: Salvarea codurilor de taste",
},
{ "Press 'Up' to save, 'Down' to cancel",
"'Auf' speichert, 'Ab' bricht ab",
@@ -1988,6 +2278,8 @@ const tI18nPhrase Phrases[] = {
"'Gora' zapamietuje, 'Dol' przerywa",
"Pulse 'Arriba' para guarder, 'Abajo' para anular",
"'kato' apothikevsi, 'Pano' akirosi",
+ "Tryck 'Upp' för att spara, 'Ner' för att avsluta",
+ "Apsati 'Sus' pentru salvare, 'Jos' pentru anulare",
},
// Key names:
{ "Up",
@@ -2002,6 +2294,8 @@ const tI18nPhrase Phrases[] = {
"Gora",
"Arriba",
"Pano",
+ "Upp",
+ "Sus",
},
{ "Down",
"Ab",
@@ -2015,6 +2309,8 @@ const tI18nPhrase Phrases[] = {
"Dol",
"Abajo",
"Kato",
+ "Ner",
+ "Jos",
},
{ "Menu",
"Menü",
@@ -2028,6 +2324,8 @@ const tI18nPhrase Phrases[] = {
"Menu",
"Menu",
"Menou",
+ "Meny",
+ "Meniu",
},
{ "Ok",
"Ok",
@@ -2041,6 +2339,8 @@ const tI18nPhrase Phrases[] = {
"Ok",
"Ok",
"Ok",
+ "Ok",
+ "Ok",
},
{ "Back",
"Zurück",
@@ -2054,6 +2354,8 @@ const tI18nPhrase Phrases[] = {
"Wstecz",
"Retornar",
"Piso",
+ "Tillbaka",
+ "Inapoi",
},
{ "Left",
"Links",
@@ -2067,6 +2369,8 @@ const tI18nPhrase Phrases[] = {
"Lewo",
"Izquierda",
"Aristera",
+ "Vänster",
+ "Stinga",
},
{ "Right",
"Rechts",
@@ -2080,6 +2384,8 @@ const tI18nPhrase Phrases[] = {
"Prawo",
"Derecha",
"Deksia",
+ "Höger",
+ "Dreapta",
},
{ "Red",
"Rot",
@@ -2093,6 +2399,8 @@ const tI18nPhrase Phrases[] = {
"Czerwony",
"Rojo",
"Kokino",
+ "Röd",
+ "Rosu",
},
{ "Green",
"Grün",
@@ -2106,6 +2414,8 @@ const tI18nPhrase Phrases[] = {
"Zielony",
"Verde",
"Prasino",
+ "Grön",
+ "Verde",
},
{ "Yellow",
"Gelb",
@@ -2119,6 +2429,8 @@ const tI18nPhrase Phrases[] = {
"Zolty",
"Amarillo",
"Kitrino",
+ "Gul",
+ "Galben",
},
{ "Blue",
"Blau",
@@ -2132,6 +2444,8 @@ const tI18nPhrase Phrases[] = {
"Niebieski",
"Azul",
"Mple",
+ "Blå",
+ "Albastru",
},
{ "Power",
"Ausschalten",
@@ -2145,6 +2459,8 @@ const tI18nPhrase Phrases[] = {
"Wylaczyc",
"Corriente",
"Klisimo",
+ "På/Av",
+ "Pornit",
},
{ "Volume+",
"Lautstärke+",
@@ -2158,6 +2474,8 @@ const tI18nPhrase Phrases[] = {
"Glosnej",
"Volumen+",
"Entasi+",
+ "Volym+",
+ "Volum+",
},
{ "Volume-",
"Lautstärke-",
@@ -2171,6 +2489,8 @@ const tI18nPhrase Phrases[] = {
"Ciszej",
"Volumen-",
"Entasi-",
+ "Volym-",
+ "Volum-",
},
{ "Mute",
"Stumm",
@@ -2184,6 +2504,8 @@ const tI18nPhrase Phrases[] = {
"Cisza",
"Mudo",
"Mougko",
+ "Ljud Av",
+ "Mut(e)",
},
// Miscellaneous:
{ "yes",
@@ -2198,6 +2520,8 @@ const tI18nPhrase Phrases[] = {
"tak",
"sí",
"nai",
+ "ja",
+ "da",
},
{ "no",
"nein",
@@ -2211,6 +2535,8 @@ const tI18nPhrase Phrases[] = {
"nie",
"no",
"oxi",
+ "nej",
+ "nu",
},
{ "top",
"oben",
@@ -2224,6 +2550,8 @@ const tI18nPhrase Phrases[] = {
"gora",
"parte sup.",
"pano",
+ "övre",
+ "sus",
},
{ "bottom",
"unten",
@@ -2237,6 +2565,8 @@ const tI18nPhrase Phrases[] = {
"dol",
"fondo",
"kato",
+ "nedre",
+ "jos",
},
{ "Disk",
"Disk",
@@ -2250,6 +2580,8 @@ const tI18nPhrase Phrases[] = {
"Disk",
"Disco",
"Disk",
+ "Disk",
+ "Disc",
},
{ "free",
"frei",
@@ -2263,6 +2595,8 @@ const tI18nPhrase Phrases[] = {
"pozostalo",
"libre",
"akoma",
+ "ledigt",
+ "liber",
},
{ "Jump: ", // note the trailing blank
"Springen: ",
@@ -2276,6 +2610,8 @@ const tI18nPhrase Phrases[] = {
"Skok: ",
"Saltar: ",
"Pidima: ",
+ "Hopp: ",
+ "Salt: ",
},
{ "Volume ", // note the trailing blank
"Lautstärke ",
@@ -2289,6 +2625,8 @@ const tI18nPhrase Phrases[] = {
"Glosnosc ",
"Volumen ",
"Entasi ",
+ "Volym ",
+ "Volum ",
},
{ " Stop replaying", // note the leading blank!
" Wiedergabe beenden",
@@ -2302,6 +2640,8 @@ const tI18nPhrase Phrases[] = {
" Zatrzymac odtwarzanie",
" Parar reprodución",
" Telos anametadosis",
+ " Avsluta uppspelning",
+ " Opreste redare",
},
{ " Stop recording ", // note the leading and trailing blanks!
" Aufzeichnung beenden ",
@@ -2315,6 +2655,8 @@ const tI18nPhrase Phrases[] = {
" Zatrzymac nagrywanie ",
" Parar grabación ",
" Telos egrafis ",
+ " Avsluta inspelning ",
+ " Opreste inregistrarea ",
},
{ " Cancel editing", // note the leading blank!
" Schneiden abbrechen",
@@ -2328,6 +2670,8 @@ const tI18nPhrase Phrases[] = {
" Przerwac montaz",
" Anular modificación",
" Diakopi kopsimatos",
+ " Avbryt editering",
+ " Opreste editare",
},
{ "Switching primary DVB...",
"Primäres Interface wird umgeschaltet...",
@@ -2341,6 +2685,8 @@ const tI18nPhrase Phrases[] = {
"Pierwszy interfejs DVB przelacza...",
"Cambio interface primario...",
"I protevon DVB Karta alazi...",
+ "Byter primär DVB enhet...",
+ "Comuta interfata primara DVB...",
},
{ "Up/Dn for new location - OK to move",
"Auf/Ab für neue Position - dann OK",
@@ -2354,6 +2700,8 @@ const tI18nPhrase Phrases[] = {
"Gora/Dol na nowa pozycje - Ok zmienia",
"Arriba/Abajo para nuevo lugar - OK para mover",
"Pano/Kato gia nea thesi. meta OK",
+ "Upp/Ner för ny plats - OK för att flytta",
+ "Sus/Jos pentru noua locatie - OK pentru a muta",
},
{ "Editing process started",
"Schnitt gestartet",
@@ -2367,6 +2715,8 @@ const tI18nPhrase Phrases[] = {
"Uruchomiony proces montazu",
"Proceso modificación iniciado",
"Arxi kopsimatos",
+ "Editering startad",
+ "Procesul de editare a inceput",
},
{ "Editing process finished",
"Schnitt beendet",
@@ -2380,6 +2730,8 @@ const tI18nPhrase Phrases[] = {
"Proces montazu zakonczony",
"Proceso modificacion terminado",
"To kopsimo termatistike",
+ "Editering avslutad",
+ "Procesul de editare s-a terminat",
},
{ "Editing process failed!",
"Schnitt gescheitert!",
@@ -2393,6 +2745,8 @@ const tI18nPhrase Phrases[] = {
"Bledny proces montazu!",
"Modificación ha fallado!",
"Kopsimo apetixe!",
+ "Editeringsprocessen misslyckades",
+ "Proces de editare nereusit",
},
{ "scanning recordings...",
"Aufzeichnungen werden durchsucht...",
@@ -2406,6 +2760,8 @@ const tI18nPhrase Phrases[] = {
"Skan nagran...",
"buscando grabaciones...",
"Ginete sarosi egrafon...",
+ "Söker igenom inspelningarna...",
+ "Caut inregistrari...",
},
{ "This plugin has no setup parameters!",
"Dieses Plugin hat keine Setup-Parameter!",
@@ -2419,7 +2775,8 @@ const tI18nPhrase Phrases[] = {
"",// TODO
"",// TODO
"",// TODO
- },
+ "",// TODO
+ },
{ NULL }
};
diff --git a/i18n.h b/i18n.h
index 68fe736..2b0ae50 100644
--- a/i18n.h
+++ b/i18n.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: i18n.h 1.2 2002/05/05 13:42:31 kls Exp $
+ * $Id: i18n.h 1.4 2002/06/10 16:14:02 kls Exp $
*/
#ifndef __I18N_H
@@ -12,7 +12,7 @@
#include <stdio.h>
-const int I18nNumLanguages = 12;
+const int I18nNumLanguages = 14;
typedef const char *tI18nPhrase[I18nNumLanguages];
diff --git a/interface.c b/interface.c
index 0731f0d..fa36b4c 100644
--- a/interface.c
+++ b/interface.c
@@ -4,13 +4,15 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: interface.c 1.48 2002/05/13 16:30:22 kls Exp $
+ * $Id: interface.c 1.52 2002/06/16 13:23:40 kls Exp $
*/
#include "interface.h"
#include <ctype.h>
#include <unistd.h>
#include "i18n.h"
+#include "osd.h"
+#include "status.h"
cInterface *Interface = NULL;
@@ -50,7 +52,7 @@ void cInterface::Open(int NumCols, int NumLines)
NumCols = Setup.OSDwidth;
if (NumLines == 0)
NumLines = Setup.OSDheight;
- cDvbApi::PrimaryDvbApi->Open(width = NumCols, height = NumLines);
+ cOsd::Open(width = NumCols, height = NumLines);
}
}
@@ -59,7 +61,7 @@ void cInterface::Close(void)
if (open == 1)
Clear();
if (!--open) {
- cDvbApi::PrimaryDvbApi->Close();
+ cOsd::Close();
width = height = 0;
}
}
@@ -125,31 +127,32 @@ eKeys cInterface::Wait(int Seconds, bool KeepChar)
void cInterface::Clear(void)
{
if (open)
- cDvbApi::PrimaryDvbApi->Clear();
+ cOsd::Clear();
+ cStatus::MsgOsdClear();
}
void cInterface::ClearEol(int x, int y, eDvbColor Color)
{
if (open)
- cDvbApi::PrimaryDvbApi->ClrEol(x, y, Color);
+ cOsd::ClrEol(x, y, Color);
}
void cInterface::Fill(int x, int y, int w, int h, eDvbColor Color)
{
if (open)
- cDvbApi::PrimaryDvbApi->Fill(x, y, w, h, Color);
+ cOsd::Fill(x, y, w, h, Color);
}
void cInterface::SetBitmap(int x, int y, const cBitmap &Bitmap)
{
if (open)
- cDvbApi::PrimaryDvbApi->SetBitmap(x, y, Bitmap);
+ cOsd::SetBitmap(x, y, Bitmap);
}
void cInterface::Flush(void)
{
if (open)
- cDvbApi::PrimaryDvbApi->Flush();
+ cOsd::Flush();
}
void cInterface::SetCols(int *c)
@@ -163,7 +166,7 @@ void cInterface::SetCols(int *c)
eDvbFont cInterface::SetFont(eDvbFont Font)
{
- return cDvbApi::PrimaryDvbApi->SetFont(Font);
+ return cOsd::SetFont(Font);
}
char *cInterface::WrapText(const char *Text, int Width, int *Height)
@@ -183,7 +186,7 @@ char *cInterface::WrapText(const char *Text, int Width, int *Height)
char *Delim = NULL;
int w = 0;
- Width *= cDvbApi::PrimaryDvbApi->CellWidth();
+ Width *= cOsd::CellWidth();
while (*t && t[strlen(t) - 1] == '\n')
t[strlen(t) - 1] = 0; // skips trailing newlines
@@ -198,7 +201,7 @@ char *cInterface::WrapText(const char *Text, int Width, int *Height)
}
else if (isspace(*p))
Blank = p;
- int cw = cDvbApi::PrimaryDvbApi->Width(*p);
+ int cw = cOsd::Width(*p);
if (w + cw > Width) {
if (Blank) {
*Blank = '\n';
@@ -237,7 +240,7 @@ char *cInterface::WrapText(const char *Text, int Width, int *Height)
void cInterface::Write(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor)
{
if (open)
- cDvbApi::PrimaryDvbApi->Text(x, y, s, FgColor, BgColor);
+ cOsd::Text(x, y, s, FgColor, BgColor);
}
void cInterface::WriteText(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor)
@@ -278,7 +281,7 @@ void cInterface::Title(const char *s)
strn0cpy(buffer, s, n + 1);
Write(1, 0, buffer, clrBlack, clrCyan);
t++;
- Write(-(cDvbApi::PrimaryDvbApi->WidthInCells(t) + 1), 0, t, clrBlack, clrCyan);
+ Write(-(cOsd::WidthInCells(t) + 1), 0, t, clrBlack, clrCyan);
}
else {
int x = (Width() - strlen(s)) / 2;
@@ -286,6 +289,7 @@ void cInterface::Title(const char *s)
x = 0;
Write(x, 0, s, clrBlack, clrCyan);
}
+ cStatus::MsgOsdTitle(s);
}
void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor)
@@ -298,6 +302,7 @@ void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor)
x = 0;
Write(x, Line, s, FgColor, BgColor);
}
+ cStatus::MsgOsdStatusMessage(s);
}
void cInterface::Info(const char *s)
@@ -337,12 +342,12 @@ void cInterface::HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvb
{
if (open) {
const int w = Width() / 4;
- cDvbApi::PrimaryDvbApi->Fill(Index * w, -1, w, 1, Text ? BgColor : clrBackground);
+ cOsd::Fill(Index * w, -1, w, 1, Text ? BgColor : clrBackground);
if (Text) {
int l = (w - int(strlen(Text))) / 2;
if (l < 0)
l = 0;
- cDvbApi::PrimaryDvbApi->Text(Index * w + l, -1, Text, FgColor, BgColor);
+ cOsd::Text(Index * w + l, -1, Text, FgColor, BgColor);
}
}
}
@@ -353,6 +358,7 @@ void cInterface::Help(const char *Red, const char *Green, const char *Yellow, co
HelpButton(1, Green, clrBlack, clrGreen);
HelpButton(2, Yellow, clrBlack, clrYellow);
HelpButton(3, Blue, clrWhite, clrBlue);
+ cStatus::MsgOsdHelpKeys(Red, Green, Yellow, Blue);
}
void cInterface::QueryKeys(void)
@@ -478,6 +484,6 @@ void cInterface::DisplayRecording(int Index, bool On)
bool cInterface::Recording(void)
{
- // This is located here because the Interface has to do with the "PrimaryDvbApi" anyway
- return cDvbApi::PrimaryDvbApi->Recording();
+ // This is located here because the Interface has to do with the "PrimaryDevice" anyway
+ return cDevice::PrimaryDevice()->Receiving();
}
diff --git a/interface.h b/interface.h
index 914a383..240db63 100644
--- a/interface.h
+++ b/interface.h
@@ -4,14 +4,14 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: interface.h 1.25 2002/04/19 13:17:15 kls Exp $
+ * $Id: interface.h 1.26 2002/05/18 13:43:20 kls Exp $
*/
#ifndef __INTERFACE_H
#define __INTERFACE_H
#include "config.h"
-#include "dvbapi.h"
+#include "osdbase.h"
#include "remote.h"
#include "svdrp.h"
diff --git a/menu.c b/menu.c
index e149a72..ff63fbc 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.192 2002/05/13 16:30:50 kls Exp $
+ * $Id: menu.c 1.197 2002/06/16 13:23:51 kls Exp $
*/
#include "menu.h"
@@ -18,12 +18,15 @@
#include "i18n.h"
#include "menuitems.h"
#include "plugin.h"
+#include "recording.h"
+#include "status.h"
#include "videodir.h"
#define MENUTIMEOUT 120 // seconds
#define MAXWAIT4EPGINFO 10 // seconds
#define MODETIMEOUT 3 // seconds
+#define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)
#define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours
#define CHNUMWIDTH (Channels.Count() > 999 ? 5 : 4) // there are people with more than 999 channels...
@@ -375,7 +378,7 @@ eOSState cMenuEditCaItem::ProcessKey(eKeys Key)
}
}
else if (NORMALKEY(Key) == kRight) {
- if (ca && ca->Next() && (allowCardNr || ((cCaDefinition *)ca->Next())->Number() > MAXDVBAPI)) {
+ if (ca && ca->Next() && (allowCardNr || ((cCaDefinition *)ca->Next())->Number() > MAXDEVICES)) {
ca = (cCaDefinition *)ca->Next();
*value = ca->Number();
}
@@ -493,7 +496,7 @@ cMenuChannels::cMenuChannels(void)
//TODO
int i = 0;
cChannel *channel;
- int curr = ((channel = Channels.GetByNumber(cDvbApi::CurrentChannel())) != NULL) ? channel->Index() : -1;
+ int curr = ((channel = Channels.GetByNumber(cDevice::CurrentChannel())) != NULL) ? channel->Index() : -1;
while ((channel = Channels.Get(i)) != NULL) {
Add(new cMenuChannelItem(i, channel), i == curr);
@@ -1144,7 +1147,7 @@ cMenuSchedule::cMenuSchedule(void)
{
now = next = false;
otherChannel = 0;
- cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel());
+ cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (channel) {
cMenuWhatsOn::SetCurrentChannel(channel->number);
schedules = cSIProcessor::Schedules(mutexLock);
@@ -1263,7 +1266,7 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
cChannel *channel = Channels.GetByServiceID(ei->GetServiceID());
if (channel) {
PrepareSchedule(channel);
- if (channel->number != cDvbApi::CurrentChannel()) {
+ if (channel->number != cDevice::CurrentChannel()) {
otherChannel = channel->number;
SetHelp(tr("Record"), tr("Now"), tr("Next"), tr("Switch"));
}
@@ -1440,7 +1443,7 @@ eOSState cMenuRecordings::Rewind(void)
{
cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
if (ri && !ri->IsDirectory()) {
- cDvbApi::PrimaryDvbApi->StopReplay(); // must do this first to be able to rewind the currently replayed recording
+ cDevice::PrimaryDevice()->StopReplay(); // must do this first to be able to rewind the currently replayed recording
cResumeFile ResumeFile(ri->FileName());
ResumeFile.Delete();
return Play();
@@ -1613,7 +1616,7 @@ public:
cMenuSetupDVB::cMenuSetupDVB(void)
{
SetSection(tr("DVB"));
- Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDvbApi::NumDvbApis));
+ Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"), &data.VideoFormat, "4:3", "16:9"));
}
@@ -1625,7 +1628,7 @@ eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
if (state == osBack && Key == kOk) {
if (Setup.PrimaryDVB != oldPrimaryDVB) {
state = osSwitchDvb;
- cDvbApi::PrimaryDvbApi->SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
+ cDevice::PrimaryDevice()->SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
}
}
return state;
@@ -1658,7 +1661,7 @@ public:
cMenuSetupCICAM::cMenuSetupCICAM(void)
{
SetSection(tr("CICAM"));
- for (int d = 0; d < cDvbApi::NumDvbApis; d++) {
+ for (int d = 0; d < cDevice::NumDevices(); d++) {
for (int i = 0; i < 2; i++) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%s%d %d", tr("Setup.CICAM$CICAM DVB"), d + 1, i + 1);
@@ -1672,7 +1675,7 @@ eOSState cMenuSetupCICAM::ProcessKey(eKeys Key)
eOSState state = cMenuSetupBase::ProcessKey(Key);
if (state == osBack && Key == kOk)
- cDvbApi::SetCaCaps();
+ cDevice::SetCaCaps();
return state;
}
@@ -2023,12 +2026,14 @@ void cMenuMain::Set(void)
// Editing control:
+ /*XXX+
if (cVideoCutter::Active())
Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit));
+ XXX*/
// Color buttons:
- SetHelp(tr("Record"), cDvbApi::PrimaryDvbApi->CanToggleAudioTrack() ? tr("Language") : NULL, NULL, replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL);
+ SetHelp(tr("Record"), /*XXX+ cDevice::PrimaryDevice()->CanToggleAudioTrack() ? tr("Language") :XXX*/ NULL, NULL, replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL);
Display();
lastActivity = time(NULL);
}
@@ -2058,7 +2063,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
}
break;
case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
- cVideoCutter::Stop();
+ //XXX+cVideoCutter::Stop();
return osEnd;
}
break;
@@ -2081,11 +2086,13 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
state = osRecord;
break;
case kGreen: if (!HasSubMenu()) {
- if (cDvbApi::PrimaryDvbApi->CanToggleAudioTrack()) {
+ /*XXX+
+ if (cDevice::PrimaryDevice()->CanToggleAudioTrack()) {
Interface->Clear();
- cDvbApi::PrimaryDvbApi->ToggleAudioTrack();
+ cDevice::PrimaryDevice()->ToggleAudioTrack();
state = osEnd;
}
+ XXX*/
}
break;
case kBlue: if (!HasSubMenu())
@@ -2113,7 +2120,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
#define INFOTIMEOUT 5000 //ms
cDisplayChannel::cDisplayChannel(int Number, bool Switched)
-:cOsdBase(true)
+:cOsdObject(true)
{
group = -1;
withInfo = !Switched || Setup.ShowInfoOnChSwitch;
@@ -2130,10 +2137,10 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched)
}
cDisplayChannel::cDisplayChannel(eKeys FirstKey)
-:cOsdBase(true)
+:cOsdObject(true)
{
group = -1;
- oldNumber = cDvbApi::CurrentChannel();
+ oldNumber = cDevice::CurrentChannel();
number = 0;
lastTime = time_ms();
int EpgLines = Setup.ShowInfoOnChSwitch ? 5 : 1;
@@ -2162,6 +2169,7 @@ void cDisplayChannel::DisplayChannel(const cChannel *Channel)
Interface->Write(0, 0, buffer);
const char *date = DayDateTime();
Interface->Write(-strlen(date), 0, date);
+ cStatus::MsgOsdChannel(buffer);
}
void cDisplayChannel::DisplayInfo(void)
@@ -2215,6 +2223,7 @@ void cDisplayChannel::DisplayInfo(void)
Interface->Flush();
lines = Lines;
lastTime = time_ms();
+ cStatus::MsgOsdProgramme(Present ? Present->GetTime() : 0, PresentTitle, PresentSubtitle, Following ? Following->GetTime() : 0, FollowingTitle, FollowingSubtitle);
}
}
}
@@ -2250,7 +2259,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
case kRight:
withInfo = false;
if (group < 0) {
- cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel());
+ cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (channel)
group = channel->Index();
}
@@ -2322,10 +2331,10 @@ cVolumeBar::cVolumeBar(int Width, int Height, int Current, int Total, const char
cDisplayVolume *cDisplayVolume::displayVolume = NULL;
cDisplayVolume::cDisplayVolume(void)
-:cOsdBase(true)
+:cOsdObject(true)
{
displayVolume = this;
- timeout = time_ms() + (cDvbApi::PrimaryDvbApi->IsMute() ? MUTETIMEOUT : VOLUMETIMEOUT);
+ timeout = time_ms() + (cDevice::PrimaryDevice()->IsMute() ? MUTETIMEOUT : VOLUMETIMEOUT);
Interface->Open(Setup.OSDwidth, -1);
Show();
}
@@ -2338,13 +2347,13 @@ cDisplayVolume::~cDisplayVolume()
void cDisplayVolume::Show(void)
{
- cDvbApi *dvbApi = cDvbApi::PrimaryDvbApi;
- if (dvbApi->IsMute()) {
+ cDevice *device = cDevice::PrimaryDevice();
+ if (device->IsMute()) {
Interface->Fill(0, 0, Width(), 1, clrTransparent);
Interface->Write(0, 0, tr("Mute"), clrGreen);
}
else {
- int Current = cDvbApi::CurrentVolume();
+ int Current = cDevice::CurrentVolume();
int Total = MAXVOLUME;
const char *Prompt = tr("Volume ");
#ifdef DEBUG_OSD
@@ -2354,7 +2363,7 @@ void cDisplayVolume::Show(void)
Interface->Fill(l, 0, p, 1, clrGreen);
Interface->Fill(l + p, 0, Width() - l - p, 1, clrWhite);
#else
- cVolumeBar VolumeBar(Width() * dvbApi->CellWidth(), dvbApi->LineHeight(), Current, Total, Prompt);
+ cVolumeBar VolumeBar(Width() * cOsd::CellWidth(), cOsd::LineHeight(), Current, Total, Prompt);
Interface->SetBitmap(0, 0, VolumeBar);
#endif
}
@@ -2384,7 +2393,7 @@ eOSState cDisplayVolume::ProcessKey(eKeys Key)
timeout = time_ms() + VOLUMETIMEOUT;
break;
case kMute:
- if (cDvbApi::PrimaryDvbApi->IsMute()) {
+ if (cDevice::PrimaryDevice()->IsMute()) {
Show();
timeout = time_ms() + MUTETIMEOUT;
}
@@ -2402,41 +2411,45 @@ eOSState cDisplayVolume::ProcessKey(eKeys Key)
// --- cRecordControl --------------------------------------------------------
-cRecordControl::cRecordControl(cDvbApi *DvbApi, cTimer *Timer)
+cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
{
eventInfo = NULL;
instantId = NULL;
fileName = NULL;
- dvbApi = DvbApi;
- if (!dvbApi) dvbApi = cDvbApi::PrimaryDvbApi;//XXX
+ recorder = NULL;
+ device = Device;
+ if (!device) device = cDevice::PrimaryDevice();//XXX
timer = Timer;
if (!timer) {
timer = new cTimer(true);
Timers.Add(timer);
Timers.Save();
- asprintf(&instantId, cDvbApi::NumDvbApis > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), dvbApi->CardIndex() + 1);
+ asprintf(&instantId, cDevice::NumDevices() > 1 ? "%s - %d" : "%s", Channels.GetChannelNameByNumber(timer->channel), device->CardIndex() + 1);
}
timer->SetPending(true);
timer->SetRecording(true);
- if (Channels.SwitchTo(timer->channel, dvbApi)) {
- const char *Title = NULL;
- const char *Subtitle = NULL;
- const char *Summary = NULL;
- if (GetEventInfo()) {
- Title = eventInfo->GetTitle();
- Subtitle = eventInfo->GetSubtitle();
- Summary = eventInfo->GetExtendedDescription();
- dsyslog("Title: '%s' Subtitle: '%s'", Title, Subtitle);
- }
- cRecording Recording(timer, Title, Subtitle, Summary);
- fileName = strdup(Recording.FileName());
- cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
- if (dvbApi->StartRecord(fileName, Channels.GetByNumber(timer->channel)->ca, timer->priority))
- Recording.WriteSummary();
- Interface->DisplayRecording(dvbApi->CardIndex(), true);
+
+ const char *Title = NULL;
+ const char *Subtitle = NULL;
+ const char *Summary = NULL;
+ if (GetEventInfo()) {
+ Title = eventInfo->GetTitle();
+ Subtitle = eventInfo->GetSubtitle();
+ Summary = eventInfo->GetExtendedDescription();
+ dsyslog("Title: '%s' Subtitle: '%s'", Title, Subtitle);
+ }
+ cRecording Recording(timer, Title, Subtitle, Summary);
+ fileName = strdup(Recording.FileName());
+ cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
+ cChannel *ch = Channels.GetByNumber(timer->channel);
+ recorder = new cRecorder(fileName, ch->ca, timer->priority, ch->vpid, ch->apid1, ch->apid2, ch->dpid1, ch->dpid2);
+ if (device->Attach(recorder)) {
+ Recording.WriteSummary();
+ cStatus::MsgRecording(device, fileName);
+ Interface->DisplayRecording(device->CardIndex(), true);
}
else
- cThread::EmergencyExit(true);
+ DELETENULL(recorder);
}
cRecordControl::~cRecordControl()
@@ -2479,7 +2492,8 @@ bool cRecordControl::GetEventInfo(void)
void cRecordControl::Stop(bool KeepInstant)
{
if (timer) {
- dvbApi->StopRecord();
+ cStatus::MsgRecording(device, NULL);
+ DELETENULL(recorder);
timer->SetRecording(false);
if ((IsInstant() && !KeepInstant) || (timer->IsSingleEvent() && !timer->Matches())) {
// checking timer->Matches() to make sure we don't delete the timer
@@ -2489,14 +2503,14 @@ void cRecordControl::Stop(bool KeepInstant)
Timers.Save();
}
timer = NULL;
- Interface->DisplayRecording(dvbApi->CardIndex(), false);
+ Interface->DisplayRecording(device->CardIndex(), false);
cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
}
}
bool cRecordControl::Process(time_t t)
{
- if (!timer || !timer->Matches(t))
+ if (!recorder || !timer || !timer->Matches(t))
return false;
AssertFreeDiskSpace(timer->priority);
return true;
@@ -2504,20 +2518,27 @@ bool cRecordControl::Process(time_t t)
// --- cRecordControls -------------------------------------------------------
-cRecordControl *cRecordControls::RecordControls[MAXDVBAPI] = { NULL };
+cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
bool cRecordControls::Start(cTimer *Timer)
{
- int ch = Timer ? Timer->channel : cDvbApi::CurrentChannel();
+ int ch = Timer ? Timer->channel : cDevice::CurrentChannel();
cChannel *channel = Channels.GetByNumber(ch);
if (channel) {
- cDvbApi *dvbApi = cDvbApi::GetDvbApi(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority);
- if (dvbApi) {
- Stop(dvbApi);
- for (int i = 0; i < MAXDVBAPI; i++) {
+ bool ReUse = false;
+ cDevice *device = cDevice::GetDevice(channel->ca, Timer ? Timer->priority : Setup.DefaultPriority, channel->frequency, channel->vpid, &ReUse);
+ if (device) {
+ if (!ReUse) {
+ Stop(device);
+ if (!channel->Switch(device)) {
+ cThread::EmergencyExit(true);
+ return false;
+ }
+ }
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (!RecordControls[i]) {
- RecordControls[i] = new cRecordControl(dvbApi, Timer);
+ RecordControls[i] = new cRecordControl(device, Timer);
return true;
}
}
@@ -2532,7 +2553,7 @@ bool cRecordControls::Start(cTimer *Timer)
void cRecordControls::Stop(const char *InstantId)
{
- for (int i = 0; i < MAXDVBAPI; i++) {
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
const char *id = RecordControls[i]->InstantId();
if (id && strcmp(id, InstantId) == 0)
@@ -2541,12 +2562,12 @@ void cRecordControls::Stop(const char *InstantId)
}
}
-void cRecordControls::Stop(cDvbApi *DvbApi)
+void cRecordControls::Stop(cDevice *Device)
{
- for (int i = 0; i < MAXDVBAPI; i++) {
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
- if (RecordControls[i]->Uses(DvbApi)) {
- isyslog("stopping recording on DVB device %d due to higher priority", DvbApi->CardIndex() + 1);
+ if (RecordControls[i]->Uses(Device)) {
+ isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);
RecordControls[i]->Stop(true);
}
}
@@ -2555,11 +2576,11 @@ void cRecordControls::Stop(cDvbApi *DvbApi)
bool cRecordControls::StopPrimary(bool DoIt)
{
- if (cDvbApi::PrimaryDvbApi->Recording()) {
- cDvbApi *dvbApi = cDvbApi::GetDvbApi(cDvbApi::PrimaryDvbApi->Ca(), 0);
- if (dvbApi) {
+ if (cDevice::PrimaryDevice()->Receiving()) {
+ cDevice *device = cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0);
+ if (device) {
if (DoIt)
- Stop(cDvbApi::PrimaryDvbApi);
+ Stop(cDevice::PrimaryDevice());
return true;
}
}
@@ -2568,7 +2589,7 @@ bool cRecordControls::StopPrimary(bool DoIt)
const char *cRecordControls::GetInstantId(const char *LastInstantId)
{
- for (int i = 0; i < MAXDVBAPI; i++) {
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
if (!LastInstantId && RecordControls[i]->InstantId())
return RecordControls[i]->InstantId();
@@ -2581,7 +2602,7 @@ const char *cRecordControls::GetInstantId(const char *LastInstantId)
cRecordControl *cRecordControls::GetRecordControl(const char *FileName)
{
- for (int i = 0; i < MAXDVBAPI; i++) {
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i] && strcmp(RecordControls[i]->FileName(), FileName) == 0)
return RecordControls[i];
}
@@ -2590,7 +2611,7 @@ cRecordControl *cRecordControls::GetRecordControl(const char *FileName)
void cRecordControls::Process(time_t t)
{
- for (int i = 0; i < MAXDVBAPI; i++) {
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i]) {
if (!RecordControls[i]->Process(t))
DELETENULL(RecordControls[i]);
@@ -2600,13 +2621,19 @@ void cRecordControls::Process(time_t t)
bool cRecordControls::Active(void)
{
- for (int i = 0; i < MAXDVBAPI; i++) {
+ for (int i = 0; i < MAXRECORDCONTROLS; i++) {
if (RecordControls[i])
return true;
}
return false;
}
+void cRecordControls::Shutdown(void)
+{
+ for (int i = 0; i < MAXRECORDCONTROLS; i++)
+ DELETENULL(RecordControls[i]);
+}
+
// --- cProgressBar ----------------------------------------------------------
class cProgressBar : public cBitmap {
@@ -2658,22 +2685,24 @@ char *cReplayControl::title = NULL;
cReplayControl::cReplayControl(void)
{
- dvbApi = cDvbApi::PrimaryDvbApi;
visible = modeOnly = shown = displayFrames = false;
lastCurrent = lastTotal = -1;
timeoutShow = 0;
timeSearchActive = false;
if (fileName) {
marks.Load(fileName);
- if (!dvbApi->StartReplay(fileName))
- Interface->Error(tr("Channel locked (recording)!"));
+ if (!Start(fileName))
+ Interface->Error(tr("Channel locked (recording)!"));//XXX+
+ else
+ cStatus::MsgReplaying(this, fileName);
}
}
cReplayControl::~cReplayControl()
{
Hide();
- dvbApi->StopReplay();
+ cStatus::MsgReplaying(this, NULL);
+ Stop();
}
void cReplayControl::SetRecording(const char *FileName, const char *Title)
@@ -2719,7 +2748,7 @@ void cReplayControl::Hide(void)
void cReplayControl::DisplayAtBottom(const char *s)
{
if (s) {
- int w = dvbApi->WidthInCells(s);
+ int w = cOsd::WidthInCells(s);
int d = max(Width() - w, 0) / 2;
if (modeOnly) //XXX remove when displaying replay mode differently
Interface->Fill(0, -1, Interface->Width(), 1, clrTransparent); //XXX remove when displaying replay mode differently
@@ -2735,7 +2764,7 @@ void cReplayControl::ShowMode(void)
if (Setup.ShowReplayMode && !timeSearchActive) {
bool Play, Forward;
int Speed;
- if (dvbApi->GetReplayMode(Play, Forward, Speed)) {
+ if (GetReplayMode(Play, Forward, Speed)) {
bool NormalPlay = (Play && Speed == -1);
if (!visible) {
@@ -2773,7 +2802,7 @@ bool cReplayControl::ShowProgress(bool Initial)
{
int Current, Total;
- if (dvbApi->GetIndex(Current, Total) && Total > 0) {
+ if (GetIndex(Current, Total) && Total > 0) {
if (!visible) {
Interface->Open(Setup.OSDwidth, -3);
needsFastResponse = visible = true;
@@ -2795,8 +2824,8 @@ bool cReplayControl::ShowProgress(bool Initial)
Interface->Fill(0, 1, p, 1, clrGreen);
Interface->Fill(p, 1, Width() - p, 1, clrWhite);
#else
- cProgressBar ProgressBar(Width() * dvbApi->CellWidth(), dvbApi->LineHeight(), Current, Total, marks);
- Interface->SetBitmap(0, dvbApi->LineHeight(), ProgressBar);
+ cProgressBar ProgressBar(Width() * cOsd::CellWidth(), cOsd::LineHeight(), Current, Total, marks);
+ Interface->SetBitmap(0, cOsd::LineHeight(), ProgressBar);
if (!Initial)
Interface->Flush();
#endif
@@ -2848,14 +2877,14 @@ void cReplayControl::TimeSearchProcess(eKeys Key)
int dir = (Key == kRight ? 1 : -1);
if (dir > 0)
Seconds = min(Total - Current - STAY_SECONDS_OFF_END, Seconds);
- dvbApi->SkipSeconds(Seconds * dir);
+ SkipSeconds(Seconds * dir);
timeSearchActive = false;
}
break;
case kUp:
case kDown:
Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
- dvbApi->Goto(Seconds * FRAMESPERSEC, Key == kDown);
+ Goto(Seconds * FRAMESPERSEC, Key == kDown);
timeSearchActive = false;
break;
default:
@@ -2893,7 +2922,7 @@ void cReplayControl::TimeSearch(void)
void cReplayControl::MarkToggle(void)
{
int Current, Total;
- if (dvbApi->GetIndex(Current, Total, true)) {
+ if (GetIndex(Current, Total, true)) {
cMark *m = marks.Get(Current);
lastCurrent = -1; // triggers redisplay
if (m)
@@ -2910,10 +2939,10 @@ void cReplayControl::MarkJump(bool Forward)
{
if (marks.Count()) {
int Current, Total;
- if (dvbApi->GetIndex(Current, Total)) {
+ if (GetIndex(Current, Total)) {
cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
if (m)
- dvbApi->Goto(m->position, true);
+ Goto(m->position, true);
}
displayFrames = true;
}
@@ -2922,11 +2951,11 @@ void cReplayControl::MarkJump(bool Forward)
void cReplayControl::MarkMove(bool Forward)
{
int Current, Total;
- if (dvbApi->GetIndex(Current, Total)) {
+ if (GetIndex(Current, Total)) {
cMark *m = marks.Get(Current);
if (m) {
displayFrames = true;
- int p = dvbApi->SkipFrames(Forward ? 1 : -1);
+ int p = SkipFrames(Forward ? 1 : -1);
cMark *m2;
if (Forward) {
if ((m2 = marks.Next(m)) != NULL && m2->position <= p)
@@ -2936,7 +2965,7 @@ void cReplayControl::MarkMove(bool Forward)
if ((m2 = marks.Prev(m)) != NULL && m2->position >= p)
return;
}
- dvbApi->Goto(m->position = p, true);
+ Goto(m->position = p, true);
marks.Save();
}
}
@@ -2944,6 +2973,7 @@ void cReplayControl::MarkMove(bool Forward)
void cReplayControl::EditCut(void)
{
+ /*XXX+
if (fileName) {
Hide();
if (!cVideoCutter::Active()) {
@@ -2956,12 +2986,13 @@ void cReplayControl::EditCut(void)
Interface->Error(tr("Editing process already active!"));
ShowMode();
}
+ XXX*/
}
void cReplayControl::EditTest(void)
{
int Current, Total;
- if (dvbApi->GetIndex(Current, Total)) {
+ if (GetIndex(Current, Total)) {
cMark *m = marks.Get(Current);
if (!m)
m = marks.GetNext(Current);
@@ -2969,8 +3000,8 @@ void cReplayControl::EditTest(void)
if ((m->Index() & 0x01) != 0)
m = marks.Next(m);
if (m) {
- dvbApi->Goto(m->position - dvbApi->SecondsToFrames(3));
- dvbApi->Play();
+ Goto(m->position - SecondsToFrames(3));
+ Play();
}
}
}
@@ -2978,7 +3009,7 @@ void cReplayControl::EditTest(void)
eOSState cReplayControl::ProcessKey(eKeys Key)
{
- if (!dvbApi->Replaying())
+ if (!Active())
return osEnd;
if (visible) {
if (timeoutShow && time(NULL) > timeoutShow) {
@@ -3000,21 +3031,21 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
bool DoShowMode = true;
switch (Key) {
// Positioning:
- case kUp: dvbApi->Play(); break;
- case kDown: dvbApi->Pause(); break;
+ case kUp: Play(); break;
+ case kDown: Pause(); break;
case kLeft|k_Release:
if (Setup.MultiSpeedMode) break;
- case kLeft: dvbApi->Backward(); break;
+ case kLeft: Backward(); break;
case kRight|k_Release:
if (Setup.MultiSpeedMode) break;
- case kRight: dvbApi->Forward(); break;
+ case kRight: Forward(); break;
case kRed: TimeSearch(); break;
case kGreen|k_Repeat:
- case kGreen: dvbApi->SkipSeconds(-60); break;
+ case kGreen: SkipSeconds(-60); break;
case kYellow|k_Repeat:
- case kYellow: dvbApi->SkipSeconds( 60); break;
+ case kYellow: SkipSeconds( 60); break;
case kBlue: Hide();
- dvbApi->StopReplay();
+ Stop();
return osEnd;
default: {
DoShowMode = false;
diff --git a/menu.h b/menu.h
index a0e0851..9dfe1a4 100644
--- a/menu.h
+++ b/menu.h
@@ -4,14 +4,16 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.h 1.42 2002/04/13 15:31:41 kls Exp $
+ * $Id: menu.h 1.44 2002/06/14 12:33:35 kls Exp $
*/
#ifndef __MENU_H
#define __MENU_H
-#include "dvbapi.h"
+#include "device.h"
#include "osd.h"
+#include "dvbplayer.h"
+#include "recorder.h"
#include "recording.h"
class cMenuMain : public cOsdMenu {
@@ -24,7 +26,7 @@ public:
virtual eOSState ProcessKey(eKeys Key);
};
-class cDisplayChannel : public cOsdBase {
+class cDisplayChannel : public cOsdObject {
private:
int group;
bool withInfo;
@@ -40,7 +42,7 @@ public:
virtual eOSState ProcessKey(eKeys Key);
};
-class cDisplayVolume : public cOsdBase {
+class cDisplayVolume : public cOsdObject {
private:
int timeout;
static cDisplayVolume *displayVolume;
@@ -76,17 +78,18 @@ public:
class cRecordControl {
private:
- cDvbApi *dvbApi;
+ cDevice *device;
cTimer *timer;
+ cRecorder *recorder;
const cEventInfo *eventInfo;
char *instantId;
char *fileName;
bool GetEventInfo(void);
public:
- cRecordControl(cDvbApi *DvbApi, cTimer *Timer = NULL);
+ cRecordControl(cDevice *Device, cTimer *Timer = NULL);
virtual ~cRecordControl();
bool Process(time_t t);
- bool Uses(cDvbApi *DvbApi) { return DvbApi == dvbApi; }
+ bool Uses(cDevice *Device) { return Device == device; }
void Stop(bool KeepInstant = false);
bool IsInstant(void) { return instantId; }
const char *InstantId(void) { return instantId; }
@@ -96,21 +99,21 @@ public:
class cRecordControls {
private:
- static cRecordControl *RecordControls[MAXDVBAPI];
+ static cRecordControl *RecordControls[];
public:
static bool Start(cTimer *Timer = NULL);
static void Stop(const char *InstantId);
- static void Stop(cDvbApi *DvbApi);
+ static void Stop(cDevice *Device);
static bool StopPrimary(bool DoIt = false);
static const char *GetInstantId(const char *LastInstantId);
static cRecordControl *GetRecordControl(const char *FileName);
static void Process(time_t t);
static bool Active(void);
+ static void Shutdown(void);
};
-class cReplayControl : public cOsdBase {
+class cReplayControl : public cDvbPlayerControl {
private:
- cDvbApi *dvbApi;
cMarks marks;
bool visible, modeOnly, shown, displayFrames;
int lastCurrent, lastTotal;
diff --git a/menuitems.c b/menuitems.c
index 3a6f94e..6a54a86 100644
--- a/menuitems.c
+++ b/menuitems.c
@@ -4,13 +4,14 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menuitems.c 1.2 2002/05/11 10:49:45 kls Exp $
+ * $Id: menuitems.c 1.6 2002/06/16 13:23:56 kls Exp $
*/
#include "menuitems.h"
#include <ctype.h>
#include "i18n.h"
#include "plugin.h"
+#include "status.h"
// --- cMenuEditItem ---------------------------------------------------------
@@ -186,18 +187,18 @@ void cMenuEditStrItem::Set(void)
strncpy(buf, value, pos);
snprintf(buf + pos, sizeof(buf) - pos - 2, fmt, *(value + pos), value + pos + 1);
int width = Interface->Width() - Interface->GetCols()[0];
- if (cDvbApi::PrimaryDvbApi->WidthInCells(buf) <= width) {
+ if (cOsd::WidthInCells(buf) <= width) {
// the whole buffer fits on the screen
SetValue(buf);
return;
}
- width *= cDvbApi::PrimaryDvbApi->CellWidth();
- width -= cDvbApi::PrimaryDvbApi->Width('>'); // assuming '<' and '>' have the same with
+ width *= cOsd::CellWidth();
+ width -= cOsd::Width('>'); // assuming '<' and '>' have the same with
int w = 0;
int i = 0;
int l = strlen(buf);
while (i < l && w <= width)
- w += cDvbApi::PrimaryDvbApi->Width(buf[i++]);
+ w += cOsd::Width(buf[i++]);
if (i >= pos + 4) {
// the cursor fits on the screen
buf[i - 1] = '>';
@@ -214,7 +215,7 @@ void cMenuEditStrItem::Set(void)
else
i--;
while (i >= 0 && w <= width)
- w += cDvbApi::PrimaryDvbApi->Width(buf[i--]);
+ w += cOsd::Width(buf[i--]);
buf[++i] = '<';
SetValue(buf + i);
}
@@ -397,6 +398,7 @@ void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
// scroll indicators use inverted color scheme!
if (CanScrollUp()) Interface->Write(x + w - 1, y, "^", bgColor, fgColor);
if (CanScrollDown()) Interface->Write(x + w - 1, y + h - 1, "v", bgColor, fgColor);
+ cStatus::MsgOsdTextItem(text);
}
void cMenuTextItem::ScrollUp(bool Page)
@@ -406,6 +408,7 @@ void cMenuTextItem::ScrollUp(bool Page)
offset = max(offset - (Page ? h : 1), 0);
Display();
}
+ cStatus::MsgOsdTextItem(NULL, true);
}
void cMenuTextItem::ScrollDown(bool Page)
@@ -415,6 +418,7 @@ void cMenuTextItem::ScrollDown(bool Page)
offset = min(offset + (Page ? h : 1), lines - h);
Display();
}
+ cStatus::MsgOsdTextItem(NULL, false);
}
eOSState cMenuTextItem::ProcessKey(eKeys Key)
@@ -471,7 +475,7 @@ void cMenuSetupPage::SetPlugin(cPlugin *Plugin)
SetSection(buf);
}
-void cMenuSetupPage::SetupStore(const char *Name, const char *Value = NULL)
+void cMenuSetupPage::SetupStore(const char *Name, const char *Value)
{
if (plugin)
plugin->SetupStore(Name, Value);
diff --git a/newplugin b/newplugin
index c25efb1..2612c85 100755
--- a/newplugin
+++ b/newplugin
@@ -12,7 +12,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: newplugin 1.4 2002/05/12 15:02:13 kls Exp $
+# $Id: newplugin 1.6 2002/06/10 16:24:34 kls Exp $
$PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";
@@ -66,7 +66,7 @@ PLUGIN = $PLUGIN_NAME
### The version number of this plugin (taken from the main source file):
-VERSION = `grep 'static const char \\*VERSION *=' \$(PLUGIN).c | awk '{ print \$\$6 }' | sed -e 's/[";]//g'`
+VERSION = \$(shell grep 'static const char \\*VERSION *=' \$(PLUGIN).c | awk '{ print \$\$6 }' | sed -e 's/[";]//g')
### The directory environment:
@@ -78,7 +78,7 @@ TMPDIR = /tmp
### The version number of VDR (taken from VDR's "config.h"):
-VDRVERSION = `grep 'define VDRVERSION ' \$(VDRDIR)/config.h | awk '{ print \$\$3 }' | sed -e 's/"//g'`
+VDRVERSION = \$(shell grep 'define VDRVERSION ' \$(VDRDIR)/config.h | awk '{ print \$\$3 }' | sed -e 's/"//g')
### The name of the distribution archive:
@@ -98,7 +98,7 @@ OBJS = \$(PLUGIN).o
### The C++ compiler and options:
CXX = g++
-CXXFLAGS = -O2 -Wall -Woverloaded-virtual -m486
+CXXFLAGS = -O2 -Wall -Woverloaded-virtual
### Implicit rules:
@@ -112,7 +112,7 @@ DEPFILE = .dependencies
\$(DEPFILE): Makefile
\@\$(MAKEDEP) \$(DEFINES) \$(INCLUDES) \$(OBJS:%.o=%.c) > \$\@
-include \$(DEPFILE)
+-include \$(DEPFILE)
### Targets:
@@ -251,7 +251,7 @@ The next steps you should perform now are:
sub CreateFile
{
my ($Name, $Content) = @_;
- open(FILE, ">$PLUGINDIR/$Name") || die "$Name: $!\n";
+ open(FILE, ">$PLUGINDIR/$Name") || die "$Name: V113 $!\n";
print FILE $Content;
close(FILE);
}
diff --git a/osd.c b/osd.c
index 6feeaa8..1092187 100644
--- a/osd.c
+++ b/osd.c
@@ -4,12 +4,243 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.c 1.25 2002/05/12 11:38:39 kls Exp $
+ * $Id: osd.c 1.29 2002/06/16 13:24:00 kls Exp $
*/
#include "osd.h"
#include <string.h>
+#include "device.h"
#include "i18n.h"
+#include "status.h"
+
+// --- cOsd ------------------------------------------------------------------
+
+#ifdef DEBUG_OSD
+ WINDOW *cOsd::window = NULL;
+ int cOsd::colorPairs[MaxColorPairs] = { 0 };
+#else
+ cDvbOsd *cOsd::osd = NULL;
+#endif
+ int cOsd::cols = 0;
+ int cOsd::rows = 0;
+
+void cOsd::Initialize(void)
+{
+#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
+ initscr();
+ keypad(stdscr, true);
+ nonl();
+ cbreak();
+ noecho();
+ timeout(10);
+#endif
+#if defined(DEBUG_OSD)
+ start_color();
+ leaveok(stdscr, true);
+#endif
+}
+
+void cOsd::Shutdown(void)
+{
+ Close();
+#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
+ endwin();
+#endif
+}
+
+#ifdef DEBUG_OSD
+void cOsd::SetColor(eDvbColor colorFg, eDvbColor colorBg)
+{
+ int color = (colorBg << 16) | colorFg | 0x80000000;
+ for (int i = 0; i < MaxColorPairs; i++) {
+ if (!colorPairs[i]) {
+ colorPairs[i] = color;
+ init_pair(i + 1, colorFg, colorBg);
+ wattrset(window, COLOR_PAIR(i + 1));
+ break;
+ }
+ else if (color == colorPairs[i]) {
+ wattrset(window, COLOR_PAIR(i + 1));
+ break;
+ }
+ }
+}
+#endif
+
+void cOsd::Open(int w, int h)
+{
+ int d = (h < 0) ? Setup.OSDheight + h : 0;
+ h = abs(h);
+ cols = w;
+ rows = h;
+#ifdef DEBUG_OSD
+ window = subwin(stdscr, h, w, d, (Setup.OSDwidth - w) / 2);
+ syncok(window, true);
+ #define B2C(b) (((b) * 1000) / 255)
+ #define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b))
+ //XXX
+ SETCOLOR(clrBackground, 0x00, 0x00, 0x00, 127); // background 50% gray
+ SETCOLOR(clrBlack, 0x00, 0x00, 0x00, 255);
+ SETCOLOR(clrRed, 0xFC, 0x14, 0x14, 255);
+ SETCOLOR(clrGreen, 0x24, 0xFC, 0x24, 255);
+ SETCOLOR(clrYellow, 0xFC, 0xC0, 0x24, 255);
+ SETCOLOR(clrBlue, 0x00, 0x00, 0xFC, 255);
+ SETCOLOR(clrCyan, 0x00, 0xFC, 0xFC, 255);
+ SETCOLOR(clrMagenta, 0xB0, 0x00, 0xFC, 255);
+ SETCOLOR(clrWhite, 0xFC, 0xFC, 0xFC, 255);
+#else
+ w *= charWidth;
+ h *= lineHeight;
+ d *= lineHeight;
+ int x = (720 - w + charWidth) / 2; //TODO PAL vs. NTSC???
+ int y = (576 - Setup.OSDheight * lineHeight) / 2 + d;
+ //XXX
+ osd = new cDvbOsd(cDevice::PrimaryDevice()->OsdDeviceHandle(), x, y);
+ //XXX TODO this should be transferred to the places where the individual windows are requested (there's too much detailed knowledge here!)
+ if (h / lineHeight == 5) { //XXX channel display
+ osd->Create(0, 0, w, h, 4);
+ }
+ else if (h / lineHeight == 1) { //XXX info display
+ osd->Create(0, 0, w, h, 4);
+ }
+ else if (d == 0) { //XXX full menu
+ osd->Create(0, 0, w, lineHeight, 2);
+ osd->Create(0, lineHeight, w, (Setup.OSDheight - 3) * lineHeight, 2);
+ osd->AddColor(clrBackground);
+ osd->AddColor(clrCyan);
+ osd->AddColor(clrWhite);
+ osd->AddColor(clrBlack);
+ osd->Create(0, (Setup.OSDheight - 2) * lineHeight, w, 2 * lineHeight, 4);
+ }
+ else { //XXX progress display
+ /*XXX
+ osd->Create(0, 0, w, lineHeight, 1);
+ osd->Create(0, lineHeight, w, lineHeight, 2, false);
+ osd->Create(0, 2 * lineHeight, w, lineHeight, 1);
+ XXX*///XXX some pixels are not drawn correctly with lower bpp values
+ osd->Create(0, 0, w, 3*lineHeight, 4);
+ }
+#endif
+}
+
+void cOsd::Close(void)
+{
+#ifdef DEBUG_OSD
+ if (window) {
+ delwin(window);
+ window = 0;
+ }
+#else
+ delete osd;
+ osd = NULL;
+#endif
+}
+
+void cOsd::Clear(void)
+{
+#ifdef DEBUG_OSD
+ SetColor(clrBackground, clrBackground);
+ Fill(0, 0, cols, rows, clrBackground);
+#else
+ osd->Clear();
+#endif
+}
+
+void cOsd::Fill(int x, int y, int w, int h, eDvbColor color)
+{
+ if (x < 0) x = cols + x;
+ if (y < 0) y = rows + y;
+#ifdef DEBUG_OSD
+ SetColor(color, color);
+ for (int r = 0; r < h; r++) {
+ wmove(window, y + r, x); // ncurses wants 'y' before 'x'!
+ whline(window, ' ', w);
+ }
+ wsyncup(window); // shouldn't be necessary because of 'syncok()', but w/o it doesn't work
+#else
+ osd->Fill(x * charWidth, y * lineHeight, (x + w) * charWidth - 1, (y + h) * lineHeight - 1, color);
+#endif
+}
+
+void cOsd::SetBitmap(int x, int y, const cBitmap &Bitmap)
+{
+#ifndef DEBUG_OSD
+ osd->SetBitmap(x, y, Bitmap);
+#endif
+}
+
+void cOsd::ClrEol(int x, int y, eDvbColor color)
+{
+ Fill(x, y, cols - x, 1, color);
+}
+
+int cOsd::CellWidth(void)
+{
+#ifdef DEBUG_OSD
+ return 1;
+#else
+ return charWidth;
+#endif
+}
+
+int cOsd::LineHeight(void)
+{
+#ifdef DEBUG_OSD
+ return 1;
+#else
+ return lineHeight;
+#endif
+}
+
+int cOsd::Width(unsigned char c)
+{
+#ifdef DEBUG_OSD
+ return 1;
+#else
+ return osd->Width(c);
+#endif
+}
+
+int cOsd::WidthInCells(const char *s)
+{
+#ifdef DEBUG_OSD
+ return strlen(s);
+#else
+ return (osd->Width(s) + charWidth - 1) / charWidth;
+#endif
+}
+
+eDvbFont cOsd::SetFont(eDvbFont Font)
+{
+#ifdef DEBUG_OSD
+ return Font;
+#else
+ return osd->SetFont(Font);
+#endif
+}
+
+void cOsd::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor colorBg)
+{
+ if (x < 0) x = cols + x;
+ if (y < 0) y = rows + y;
+#ifdef DEBUG_OSD
+ SetColor(colorFg, colorBg);
+ wmove(window, y, x); // ncurses wants 'y' before 'x'!
+ waddnstr(window, s, cols - x);
+#else
+ osd->Text(x * charWidth, y * lineHeight, s, colorFg, colorBg);
+#endif
+}
+
+void cOsd::Flush(void)
+{
+#ifdef DEBUG_OSD
+ refresh();
+#else
+ if (osd)
+ osd->Flush();
+#endif
+}
// --- cOsdItem --------------------------------------------------------------
@@ -200,8 +431,11 @@ void cOsdMenu::Display(void)
}
for (int i = first; i < count; i++) {
cOsdItem *item = Get(i);
- if (item)
+ if (item) {
item->Display(i - first, i == current ? clrBlack : clrWhite, i == current ? clrCyan : clrBackground);
+ if (i == current)
+ cStatus::MsgOsdCurrentItem(item->Text());
+ }
if (++n == MAXOSDITEMS) //TODO get this from Interface!!!
break;
}
@@ -225,8 +459,11 @@ void cOsdMenu::RefreshCurrent(void)
void cOsdMenu::DisplayCurrent(bool Current)
{
cOsdItem *item = Get(current);
- if (item)
+ if (item) {
item->Display(current - first, Current ? clrBlack : clrWhite, Current ? clrCyan : clrBackground);
+ if (Current)
+ cStatus::MsgOsdCurrentItem(item->Text());
+ }
}
void cOsdMenu::Clear(void)
diff --git a/osd.h b/osd.h
index afd4485..88c986c 100644
--- a/osd.h
+++ b/osd.h
@@ -4,14 +4,19 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.h 1.29 2002/05/12 11:19:22 kls Exp $
+ * $Id: osd.h 1.31 2002/05/18 14:00:15 kls Exp $
*/
#ifndef __OSD_H
#define __OSD_H
+#if defined(DEBUG_OSD) || defined(REMOTE_KBD)
+#include <ncurses.h>
+#endif
#include "config.h"
+#include "dvbosd.h"
#include "interface.h"
+#include "osdbase.h"
#include "tools.h"
#define MAXOSDITEMS (Setup.OSDheight - 4)
@@ -47,6 +52,38 @@ enum eOSState { osUnknown,
osUser10,
};
+class cOsd {
+private:
+ enum { charWidth = 12, // average character width
+ lineHeight = 27 // smallest text height
+ };
+#ifdef DEBUG_OSD
+ static WINDOW *window;
+ enum { MaxColorPairs = 16 };
+ static int colorPairs[MaxColorPairs];
+ static void SetColor(eDvbColor colorFg, eDvbColor colorBg = clrBackground);
+#else
+ static cDvbOsd *osd;
+#endif
+ static int cols, rows;
+public:
+ static void Initialize(void);
+ static void Shutdown(void);
+ static void Open(int w, int h);
+ static void Close(void);
+ static void Clear(void);
+ static void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground);
+ static void SetBitmap(int x, int y, const cBitmap &Bitmap);
+ static void ClrEol(int x, int y, eDvbColor color = clrBackground);
+ static int CellWidth(void);
+ static int LineHeight(void);
+ static int Width(unsigned char c);
+ static int WidthInCells(const char *s);
+ static eDvbFont SetFont(eDvbFont Font);
+ static void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground);
+ static void Flush(void);
+ };
+
class cOsdItem : public cListObject {
private:
const char *text;
@@ -69,19 +106,19 @@ public:
virtual eOSState ProcessKey(eKeys Key);
};
-class cOsdBase {
+class cOsdObject {
protected:
bool needsFastResponse;
public:
- cOsdBase(bool FastResponse = false) { needsFastResponse = FastResponse; }
- virtual ~cOsdBase() {}
+ cOsdObject(bool FastResponse = false) { needsFastResponse = FastResponse; }
+ virtual ~cOsdObject() {}
int Width(void) { return Interface->Width(); }
int Height(void) { return Interface->Height(); }
bool NeedsFastResponse(void) { return needsFastResponse; }
virtual eOSState ProcessKey(eKeys Key) = 0;
};
-class cOsdMenu : public cOsdBase, public cList<cOsdItem> {
+class cOsdMenu : public cOsdObject, public cList<cOsdItem> {
private:
char *title;
int cols[cInterface::MaxCols];
diff --git a/osdbase.c b/osdbase.c
index 1e51940..e7486a7 100644
--- a/osdbase.c
+++ b/osdbase.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osdbase.c 1.2 2002/05/13 16:30:59 kls Exp $
+ * $Id: osdbase.c 1.4 2002/05/18 12:39:16 kls Exp $
*/
#include "osdbase.h"
@@ -316,22 +316,22 @@ const char *cWindow::Data(int x, int y)
return cBitmap::Data(x, y);
}
-// --- cOsd ------------------------------------------------------------------
+// --- cOsdBase --------------------------------------------------------------
-cOsd::cOsd(int x, int y)
+cOsdBase::cOsdBase(int x, int y)
{
numWindows = 0;
x0 = x;
y0 = y;
}
-cOsd::~cOsd()
+cOsdBase::~cOsdBase()
{
for (int i = 0; i < numWindows; i++)
delete window[i];
}
-tWindowHandle cOsd::Create(int x, int y, int w, int h, int Bpp, bool ClearWithBackground, bool Tiled)
+tWindowHandle cOsdBase::Create(int x, int y, int w, int h, int Bpp, bool ClearWithBackground, bool Tiled)
{
if (numWindows < MAXNUMWINDOWS) {
if (x >= 0 && y >= 0 && w > 0 && h > 0 && (Bpp == 1 || Bpp == 2 || Bpp == 4 || Bpp == 8)) {
@@ -356,7 +356,7 @@ tWindowHandle cOsd::Create(int x, int y, int w, int h, int Bpp, bool ClearWithBa
return -1;
}
-void cOsd::AddColor(eDvbColor Color, tWindowHandle Window)
+void cOsdBase::AddColor(eDvbColor Color, tWindowHandle Window)
{
cWindow *w = GetWindow(Window);
if (w) {
@@ -365,7 +365,7 @@ void cOsd::AddColor(eDvbColor Color, tWindowHandle Window)
}
}
-cWindow *cOsd::GetWindow(int x, int y)
+cWindow *cOsdBase::GetWindow(int x, int y)
{
for (int i = 0; i < numWindows; i++) {
if (window[i]->Tiled() && window[i]->Contains(x, y))
@@ -374,7 +374,7 @@ cWindow *cOsd::GetWindow(int x, int y)
return NULL;
}
-cWindow *cOsd::GetWindow(tWindowHandle Window)
+cWindow *cOsdBase::GetWindow(tWindowHandle Window)
{
if (0 <= Window && Window < numWindows)
return window[Window];
@@ -383,7 +383,7 @@ cWindow *cOsd::GetWindow(tWindowHandle Window)
return NULL;
}
-void cOsd::Flush(void)
+void cOsdBase::Flush(void)
{
for (int i = 0; i < numWindows; i++) {
CommitWindow(window[i]);
@@ -396,7 +396,7 @@ void cOsd::Flush(void)
}
}
-void cOsd::Clear(tWindowHandle Window)
+void cOsdBase::Clear(tWindowHandle Window)
{
if (Window == ALL_TILED_WINDOWS || Window == ALL_WINDOWS) {
for (int i = 0; i < numWindows; i++)
@@ -410,31 +410,31 @@ void cOsd::Clear(tWindowHandle Window)
}
}
-void cOsd::Fill(int x1, int y1, int x2, int y2, eDvbColor Color, tWindowHandle Window)
+void cOsdBase::Fill(int x1, int y1, int x2, int y2, eDvbColor Color, tWindowHandle Window)
{
cWindow *w = (Window == ALL_TILED_WINDOWS) ? GetWindow(x1, y1) : GetWindow(Window);
if (w)
w->Fill(x1, y1, x2, y2, Color);
}
-void cOsd::SetBitmap(int x, int y, const cBitmap &Bitmap, tWindowHandle Window)
+void cOsdBase::SetBitmap(int x, int y, const cBitmap &Bitmap, tWindowHandle Window)
{
cWindow *w = (Window == ALL_TILED_WINDOWS) ? GetWindow(x, y) : GetWindow(Window);
if (w)
w->SetBitmap(x, y, Bitmap);
}
-int cOsd::Width(unsigned char c)
+int cOsdBase::Width(unsigned char c)
{
return numWindows ? window[0]->Width(c) : 0;
}
-int cOsd::Width(const char *s)
+int cOsdBase::Width(const char *s)
{
return numWindows ? window[0]->Width(s) : 0;
}
-eDvbFont cOsd::SetFont(eDvbFont Font)
+eDvbFont cOsdBase::SetFont(eDvbFont Font)
{
eDvbFont oldFont = Font;
for (int i = 0; i < numWindows; i++)
@@ -442,14 +442,14 @@ eDvbFont cOsd::SetFont(eDvbFont Font)
return oldFont;
}
-void cOsd::Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground, tWindowHandle Window)
+void cOsdBase::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor ColorBg, tWindowHandle Window)
{
cWindow *w = (Window == ALL_TILED_WINDOWS) ? GetWindow(x, y) : GetWindow(Window);
if (w)
w->Text(x, y, s, ColorFg, ColorBg);
}
-void cOsd::Relocate(tWindowHandle Window, int x, int y, int NewWidth, int NewHeight)
+void cOsdBase::Relocate(tWindowHandle Window, int x, int y, int NewWidth, int NewHeight)
{
cWindow *w = GetWindow(Window);
if (w) {
@@ -471,12 +471,12 @@ void cOsd::Relocate(tWindowHandle Window, int x, int y, int NewWidth, int NewHei
}
}
-void cOsd::Hide(tWindowHandle Window)
+void cOsdBase::Hide(tWindowHandle Window)
{
HideWindow(GetWindow(Window), true);
}
-void cOsd::Show(tWindowHandle Window)
+void cOsdBase::Show(tWindowHandle Window)
{
HideWindow(GetWindow(Window), false);
}
diff --git a/osdbase.h b/osdbase.h
index 859939b..a20dfc4 100644
--- a/osdbase.h
+++ b/osdbase.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osdbase.h 1.1 2002/05/10 14:26:09 kls Exp $
+ * $Id: osdbase.h 1.2 2002/05/18 12:38:17 kls Exp $
*/
#ifndef __OSDBASE_H
@@ -119,7 +119,7 @@ typedef int tWindowHandle;
#define ALL_TILED_WINDOWS (-3)
#define LAST_CREATED_WINDOW (-4)
-class cOsd {
+class cOsdBase {
private:
int numWindows;
int x0, y0;
@@ -149,9 +149,9 @@ protected:
virtual void CloseWindow(cWindow *Window) = 0;
// Close the window and release any OSD hardware resources allocated for it.
public:
- cOsd(int x, int y);
+ cOsdBase(int x, int y);
// Initializes the OSD, starting at screen coordinates (x, y).
- virtual ~cOsd();
+ virtual ~cOsdBase();
// Destroys all windows and shuts down the OSD.
tWindowHandle Create(int x, int y, int w, int h, int Bpp, bool ClearWithBackground = true, bool Tiled = true);
// Creates a window at coordinates (x, y), which are relative to the OSD's
diff --git a/player.c b/player.c
new file mode 100644
index 0000000..fe0a277
--- /dev/null
+++ b/player.c
@@ -0,0 +1,55 @@
+/*
+ * player.c: The basic player interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: player.c 1.1 2002/06/16 10:34:50 kls Exp $
+ */
+
+#include "player.h"
+
+// --- cPlayer ---------------------------------------------------------------
+
+cPlayer::cPlayer(void)
+{
+ device = NULL;
+ deviceFileHandle = -1;
+}
+
+cPlayer::~cPlayer()
+{
+ Detach();
+}
+
+int cPlayer::PlayVideo(const uchar *Data, int Length)
+{
+ if (device)
+ return device->PlayVideo(Data, Length);
+ esyslog("ERROR: attempt to use cPlayer::PlayVideo() without attaching to a cDevice!");
+ return -1;
+}
+
+int cPlayer::PlayAudio(const uchar *Data, int Length)
+{
+ if (device)
+ return device->PlayAudio(Data, Length);
+ esyslog("ERROR: attempt to use cPlayer::PlayAudio() without attaching to a cDevice!");
+ return -1;
+}
+
+void cPlayer::Detach(void)
+{
+ if (device)
+ device->Detach(this);
+}
+
+// --- cControl --------------------------------------------------------------
+
+cControl::cControl(void)
+{
+}
+
+cControl::~cControl()
+{
+}
diff --git a/player.h b/player.h
new file mode 100644
index 0000000..1221c24
--- /dev/null
+++ b/player.h
@@ -0,0 +1,51 @@
+/*
+ * player.h: The basic player interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: player.h 1.1 2002/06/16 11:52:45 kls Exp $
+ */
+
+#ifndef __PLAYER_H
+#define __PLAYER_H
+
+#include "device.h"
+#include "osd.h"
+
+class cPlayer {
+ friend class cDevice;
+private:
+ cDevice *device;
+ int deviceFileHandle;
+protected:
+ int DeviceFileHandle(void) { return deviceFileHandle; } //XXX+ needed for polling
+ void DeviceTrickSpeed(int Speed) { if (device) device->TrickSpeed(Speed); }
+ void DeviceClear(void) { if (device) device->Clear(); }
+ void DevicePlay(void) { if (device) device->Play(); }
+ void DeviceFreeze(void) { if (device) device->Freeze(); }
+ void DeviceMute(void) { if (device) device->Mute(); }
+ void DeviceStillPicture(const uchar *Data, int Length) { if (device) device->StillPicture(Data, Length); }
+ void Detach(void);
+ virtual void Activate(bool On) {}
+ // This function is called right after the cPlayer has been attached to
+ // (On == true) or before it gets detached from (On == false) a cDevice.
+ // It can be used to do things like starting/stopping a thread.
+ int PlayVideo(const uchar *Data, int Length);
+ // Sends the given Data to the video device and returns the number of
+ // bytes that have actually been accepted by the video device (or a
+ // negative value in case of an error).
+ int PlayAudio(const uchar *Data, int Length);
+ // XXX+ TODO
+public:
+ cPlayer(void);
+ virtual ~cPlayer();
+ };
+
+class cControl : public cOsdObject {
+public:
+ cControl(void);
+ virtual ~cControl();
+ };
+
+#endif //__PLAYER_H
diff --git a/receiver.c b/receiver.c
new file mode 100644
index 0000000..04f9847
--- /dev/null
+++ b/receiver.c
@@ -0,0 +1,55 @@
+/*
+ * receiver.c: The basic receiver interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: receiver.c 1.1 2002/06/08 09:58:29 kls Exp $
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "receiver.h"
+
+cReceiver::cReceiver(int Ca, int Priority, int NumPids, ...)
+{
+ device = NULL;
+ ca = Ca;
+ priority = Priority;
+ if (NumPids) {
+ va_list ap;
+ va_start(ap, NumPids);
+ int n = 0;
+ while (n < MAXRECEIVEPIDS && NumPids--) {
+ if ((pids[n] = va_arg(ap, int)) != 0)
+ n++;
+ }
+ va_end(ap);
+ }
+ else
+ esyslog("ERROR: cReceiver called without a PID!");
+}
+
+cReceiver::~cReceiver()
+{
+ Detach();
+}
+
+bool cReceiver::WantsPid(int Pid)
+{
+ if (Pid) {
+ for (int i = 0; i < MAXRECEIVEPIDS; i++) {
+ if (pids[i] == Pid)
+ return true;
+ if (!pids[i])
+ break;
+ }
+ }
+ return false;
+}
+
+void cReceiver::Detach(void)
+{
+ if (device)
+ device->Detach(this);
+}
diff --git a/receiver.h b/receiver.h
new file mode 100644
index 0000000..aa3b9d1
--- /dev/null
+++ b/receiver.h
@@ -0,0 +1,48 @@
+/*
+ * receiver.h: The basic receiver interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: receiver.h 1.1 2002/06/08 15:32:51 kls Exp $
+ */
+
+#ifndef __RECEIVER_H
+#define __RECEIVER_H
+
+#include "device.h"
+
+#define MAXRECEIVEPIDS 16 // the maximum number of PIDs per receiver
+
+class cReceiver {
+ friend class cDevice;
+private:
+ cDevice *device;
+ int ca;
+ int priority;
+ int pids[MAXRECEIVEPIDS];
+ bool WantsPid(int Pid);
+protected:
+ void Detach(void);
+ virtual void Activate(bool On) {}
+ // This function is called just before the cReceiver gets attached to
+ // (On == true) or detached from (On == false) a cDevice. It can be used
+ // to do things like starting/stopping a thread.
+ // It is guaranteed that Receive() will not be called before Activate(true).
+ virtual void Receive(uchar *Data, int Length) = 0;
+ // This function is called from the cDevice we are attached to, and
+ // delivers one TS packet from the set of PIDs the cReceiver has requested.
+ // The data packet must be accepted immediately, and the call must return
+ // as soon as possible, without any unnecessary delay. Each TS packet
+ // will be delivered only ONCE, so the cReceiver must make sure that
+ // it will be able to buffer the data if necessary.
+public:
+ cReceiver(int Ca, int Priority, int NumPids, ...);
+ // Creates a new receiver that requires conditional access Ca and has
+ // the given Priority. NumPids defines the number of PIDs that follow
+ // this parameter. If any of these PIDs are 0, they will be silently ignored.
+ // The total number of non-zero PIDs must not exceed MAXRECEIVEPIDS.
+ virtual ~cReceiver();
+ };
+
+#endif //__RECEIVER_H
diff --git a/recorder.c b/recorder.c
new file mode 100644
index 0000000..3a0941b
--- /dev/null
+++ b/recorder.c
@@ -0,0 +1,149 @@
+/*
+ * recorder.h: The actual DVB recorder
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: recorder.c 1.1 2002/06/16 10:03:25 kls Exp $
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "recorder.h"
+
+// The size of the array used to buffer video data:
+// (must be larger than MINVIDEODATA - see remux.h)
+#define VIDEOBUFSIZE MEGABYTE(1)
+
+#define MINFREEDISKSPACE (512) // MB
+#define DISKCHECKINTERVAL 100 // seconds
+
+cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2)
+:cReceiver(Ca, Priority, 5, VPid, APid1, APid2, DPid1, DPid2)
+{
+ ringBuffer = NULL;
+ remux = NULL;
+ fileName = NULL;
+ index = NULL;
+ pictureType = NO_PICTURE;
+ fileSize = 0;
+ active = false;
+ lastDiskSpaceCheck = time(NULL);
+ isyslog("record %s", FileName);
+
+ // Create directories if necessary:
+
+ if (!MakeDirs(FileName, true))
+ return;
+
+ // Make sure the disk is up and running:
+
+ SpinUpDisk(FileName);
+
+ ringBuffer = new cRingBufferLinear(VIDEOBUFSIZE, true);
+ remux = new cRemux(VPid, APid1, APid2, DPid1, DPid2, true);
+ fileName = new cFileName(FileName, true);
+ recordFile = fileName->Open();
+ if (recordFile < 0)
+ return;
+ // Create the index file:
+ index = new cIndexFile(FileName, true);
+ if (!index)
+ esyslog("ERROR: can't allocate index");
+ // let's continue without index, so we'll at least have the recording
+}
+
+cRecorder::~cRecorder()
+{
+ Detach();
+ delete index;
+ delete fileName;
+ delete remux;
+ delete ringBuffer;
+}
+
+void cRecorder::Activate(bool On)
+{
+ if (On) {
+ if (recordFile >= 0)
+ Start();
+ }
+ else if (active) {
+ active = false;
+ Cancel(3);
+ }
+}
+
+bool cRecorder::RunningLowOnDiskSpace(void)
+{
+ if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
+ int Free = FreeDiskSpaceMB(fileName->Name());
+ lastDiskSpaceCheck = time(NULL);
+ if (Free < MINFREEDISKSPACE) {
+ dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool cRecorder::NextFile(void)
+{
+ if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
+ if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
+ recordFile = fileName->NextFile();
+ fileSize = 0;
+ }
+ }
+ return recordFile >= 0;
+}
+
+void cRecorder::Receive(uchar *Data, int Length)
+{
+ int p = ringBuffer->Put(Data, Length);
+ if (p != Length && active)
+ esyslog("ERROR: ring buffer overflow (%d bytes dropped)", Length - p);
+}
+
+void cRecorder::Action(void)
+{
+ dsyslog("recording thread started (pid=%d)", getpid());
+
+ uchar b[MINVIDEODATA];
+ int r = 0;
+ active = true;
+ while (active) {
+ int g = ringBuffer->Get(b + r, sizeof(b) - r);
+ if (g > 0)
+ r += g;
+ if (r > 0) {
+ int Count = r, Result;
+ const uchar *p = remux->Process(b, Count, Result, &pictureType);
+ if (p) {
+ //XXX+ active??? see old version (Busy)
+ if (!active && pictureType == I_FRAME) // finish the recording before the next 'I' frame
+ break;
+ if (NextFile()) {
+ if (index && pictureType != NO_PICTURE)
+ index->Write(pictureType, fileName->Number(), fileSize);
+ if (safe_write(recordFile, p, Result) < 0) {
+ LOG_ERROR_STR(fileName->Name());
+ break;
+ }
+ fileSize += Result;
+ }
+ else
+ break;
+ }
+ if (Count > 0) {
+ r -= Count;
+ memmove(b, b + Count, r);
+ }
+ }
+ else
+ usleep(1); // this keeps the CPU load low
+ }
+
+ dsyslog("recording thread ended (pid=%d)", getpid());
+}
diff --git a/recorder.h b/recorder.h
new file mode 100644
index 0000000..e19148a
--- /dev/null
+++ b/recorder.h
@@ -0,0 +1,43 @@
+/*
+ * recorder.h: The actual DVB recorder
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: recorder.h 1.1 2002/06/08 09:35:03 kls Exp $
+ */
+
+#ifndef __RECORDER_H
+#define __RECORDER_H
+
+#include "receiver.h"
+#include "recording.h"
+#include "remux.h"
+#include "ringbuffer.h"
+#include "thread.h"
+
+class cRecorder : public cReceiver, cThread {
+private:
+ cRingBufferLinear *ringBuffer;
+ cRemux *remux;
+ cFileName *fileName;
+ cIndexFile *index;
+ uchar pictureType;
+ int fileSize;
+ int recordFile;
+ bool active;
+ time_t lastDiskSpaceCheck;
+ bool RunningLowOnDiskSpace(void);
+ bool NextFile(void);
+protected:
+ virtual void Activate(bool On);
+ virtual void Receive(uchar *Data, int Length);
+ virtual void Action(void);
+public:
+ cRecorder(const char *FileName, int Ca, int Priority, int VPid, int APid1, int APid2, int DPid1, int DPid2);
+ // Creates a new recorder that requires conditional access Ca, has
+ // the given Priority and will record the given PIDs into the file FileName.
+ virtual ~cRecorder();
+ };
+
+#endif //__RECORDER_H
diff --git a/recording.c b/recording.c
index c65aaaf..7bc896c 100644
--- a/recording.c
+++ b/recording.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.c 1.62 2002/05/13 16:31:21 kls Exp $
+ * $Id: recording.c 1.63 2002/06/16 11:29:27 kls Exp $
*/
#include "recording.h"
@@ -16,6 +16,7 @@
#include <unistd.h>
#include "i18n.h"
#include "interface.h"
+#include "remux.h" //XXX+ I_FRAME
#include "tools.h"
#include "videodir.h"
@@ -732,3 +733,350 @@ void cRecordingUserCommand::InvokeCommand(const char *State, const char *Recordi
delete cmd;
}
}
+
+// --- XXX+
+
+//XXX+ somewhere else???
+// --- cIndexFile ------------------------------------------------------------
+
+#define INDEXFILESUFFIX "/index.vdr"
+
+// The maximum time to wait before giving up while catching up on an index file:
+#define MAXINDEXCATCHUP 2 // seconds
+
+cIndexFile::cIndexFile(const char *FileName, bool Record)
+:resumeFile(FileName)
+{
+ f = -1;
+ fileName = NULL;
+ size = 0;
+ last = -1;
+ index = NULL;
+ if (FileName) {
+ fileName = new char[strlen(FileName) + strlen(INDEXFILESUFFIX) + 1];
+ if (fileName) {
+ strcpy(fileName, FileName);
+ char *pFileExt = fileName + strlen(fileName);
+ strcpy(pFileExt, INDEXFILESUFFIX);
+ int delta = 0;
+ if (access(fileName, R_OK) == 0) {
+ struct stat buf;
+ if (stat(fileName, &buf) == 0) {
+ delta = buf.st_size % sizeof(tIndex);
+ if (delta) {
+ delta = sizeof(tIndex) - delta;
+ esyslog("ERROR: invalid file size (%ld) in '%s'", buf.st_size, fileName);
+ }
+ last = (buf.st_size + delta) / sizeof(tIndex) - 1;
+ if (!Record && last >= 0) {
+ size = last + 1;
+ index = new tIndex[size];
+ if (index) {
+ f = open(fileName, O_RDONLY);
+ if (f >= 0) {
+ if ((int)safe_read(f, index, buf.st_size) != buf.st_size) {
+ esyslog("ERROR: can't read from file '%s'", fileName);
+ delete index;
+ index = NULL;
+ close(f);
+ f = -1;
+ }
+ // we don't close f here, see CatchUp()!
+ }
+ else
+ LOG_ERROR_STR(fileName);
+ }
+ else
+ esyslog("ERROR: can't allocate %d bytes for index '%s'", size * sizeof(tIndex), fileName);
+ }
+ }
+ else
+ LOG_ERROR;
+ }
+ else if (!Record)
+ isyslog("missing index file %s", fileName);
+ if (Record) {
+ if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) {
+ if (delta) {
+ esyslog("ERROR: padding index file with %d '0' bytes", delta);
+ while (delta--)
+ writechar(f, 0);
+ }
+ }
+ else
+ LOG_ERROR_STR(fileName);
+ }
+ }
+ else
+ esyslog("ERROR: can't copy file name '%s'", FileName);
+ }
+}
+
+cIndexFile::~cIndexFile()
+{
+ if (f >= 0)
+ close(f);
+ delete fileName;
+ delete index;
+}
+
+bool cIndexFile::CatchUp(int Index)
+{
+ if (index && f >= 0) {
+ for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >= last); i++) {
+ struct stat buf;
+ if (fstat(f, &buf) == 0) {
+ int newLast = buf.st_size / sizeof(tIndex) - 1;
+ if (newLast > last) {
+ if (size <= newLast) {
+ size *= 2;
+ if (size <= newLast)
+ size = newLast + 1;
+ }
+ index = (tIndex *)realloc(index, size * sizeof(tIndex));
+ if (index) {
+ int offset = (last + 1) * sizeof(tIndex);
+ int delta = (newLast - last) * sizeof(tIndex);
+ if (lseek(f, offset, SEEK_SET) == offset) {
+ if (safe_read(f, &index[last + 1], delta) != delta) {
+ esyslog("ERROR: can't read from index");
+ delete index;
+ index = NULL;
+ close(f);
+ f = -1;
+ break;
+ }
+ last = newLast;
+ }
+ else
+ LOG_ERROR_STR(fileName);
+ }
+ else
+ esyslog("ERROR: can't realloc() index");
+ }
+ }
+ else
+ LOG_ERROR_STR(fileName);
+ if (Index >= last)
+ sleep(1);
+ else
+ return true;
+ }
+ }
+ return false;
+}
+
+bool cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset)
+{
+ if (f >= 0) {
+ tIndex i = { FileOffset, PictureType, FileNumber, 0 };
+ if (safe_write(f, &i, sizeof(i)) < 0) {
+ LOG_ERROR_STR(fileName);
+ close(f);
+ f = -1;
+ return false;
+ }
+ last++;
+ }
+ return f >= 0;
+}
+
+bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length)
+{
+ if (index) {
+ CatchUp(Index);
+ if (Index >= 0 && Index <= last) {
+ *FileNumber = index[Index].number;
+ *FileOffset = index[Index].offset;
+ if (PictureType)
+ *PictureType = index[Index].type;
+ if (Length) {
+ int fn = index[Index + 1].number;
+ int fo = index[Index + 1].offset;
+ if (fn == *FileNumber)
+ *Length = fo - *FileOffset;
+ else
+ *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length, bool StayOffEnd)
+{
+ if (index) {
+ CatchUp();
+ int d = Forward ? 1 : -1;
+ for (;;) {
+ Index += d;
+ if (Index >= 0 && Index < last - ((Forward && StayOffEnd) ? 100 : 0)) {
+ if (index[Index].type == I_FRAME) {
+ if (FileNumber)
+ *FileNumber = index[Index].number;
+ else
+ FileNumber = &index[Index].number;
+ if (FileOffset)
+ *FileOffset = index[Index].offset;
+ else
+ FileOffset = &index[Index].offset;
+ if (Length) {
+ // all recordings end with a non-I_FRAME, so the following should be safe:
+ int fn = index[Index + 1].number;
+ int fo = index[Index + 1].offset;
+ if (fn == *FileNumber)
+ *Length = fo - *FileOffset;
+ else {
+ esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
+ *Length = -1;
+ }
+ }
+ return Index;
+ }
+ }
+ else
+ break;
+ }
+ }
+ return -1;
+}
+
+int cIndexFile::Get(uchar FileNumber, int FileOffset)
+{
+ if (index) {
+ CatchUp();
+ //TODO implement binary search!
+ int i;
+ for (i = 0; i < last; i++) {
+ if (index[i].number > FileNumber || (index[i].number == FileNumber) && index[i].offset >= FileOffset)
+ break;
+ }
+ return i;
+ }
+ return -1;
+}
+
+// --- cFileName -------------------------------------------------------------
+
+#include <errno.h>
+#include <unistd.h>
+#include "videodir.h"
+
+#define MAXFILESPERRECORDING 255
+#define RECORDFILESUFFIX "/%03d.vdr"
+#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
+
+cFileName::cFileName(const char *FileName, bool Record, bool Blocking)
+{
+ file = -1;
+ fileNumber = 0;
+ record = Record;
+ blocking = Blocking;
+ // Prepare the file name:
+ fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
+ if (!fileName) {
+ esyslog("ERROR: can't copy file name '%s'", fileName);
+ return;
+ }
+ strcpy(fileName, FileName);
+ pFileNumber = fileName + strlen(fileName);
+ SetOffset(1);
+}
+
+cFileName::~cFileName()
+{
+ Close();
+ delete fileName;
+}
+
+int cFileName::Open(void)
+{
+ if (file < 0) {
+ int BlockingFlag = blocking ? 0 : O_NONBLOCK;
+ if (record) {
+ dsyslog("recording to '%s'", fileName);
+ file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag);
+ if (file < 0)
+ LOG_ERROR_STR(fileName);
+ }
+ else {
+ if (access(fileName, R_OK) == 0) {
+ dsyslog("playing '%s'", fileName);
+ file = open(fileName, O_RDONLY | BlockingFlag);
+ if (file < 0)
+ LOG_ERROR_STR(fileName);
+ }
+ else if (errno != ENOENT)
+ LOG_ERROR_STR(fileName);
+ }
+ }
+ return file;
+}
+
+void cFileName::Close(void)
+{
+ if (file >= 0) {
+ if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0))
+ LOG_ERROR_STR(fileName);
+ file = -1;
+ }
+}
+
+int cFileName::SetOffset(int Number, int Offset)
+{
+ if (fileNumber != Number)
+ Close();
+ if (0 < Number && Number <= MAXFILESPERRECORDING) {
+ fileNumber = Number;
+ sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
+ if (record) {
+ if (access(fileName, F_OK) == 0) // file exists, let's try next suffix
+ return SetOffset(Number + 1);
+ else if (errno != ENOENT) { // something serious has happened
+ LOG_ERROR_STR(fileName);
+ return -1;
+ }
+ // found a non existing file suffix
+ }
+ if (Open() >= 0) {
+ if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) {
+ LOG_ERROR_STR(fileName);
+ return -1;
+ }
+ }
+ return file;
+ }
+ esyslog("ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
+ return -1;
+}
+
+int cFileName::NextFile(void)
+{
+ return SetOffset(fileNumber + 1);
+}
+
+const char *IndexToHMSF(int Index, bool WithFrame)
+{
+ static char buffer[16];
+ int f = (Index % FRAMESPERSEC) + 1;
+ int s = (Index / FRAMESPERSEC);
+ int m = s / 60 % 60;
+ int h = s / 3600;
+ s %= 60;
+ snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
+ return buffer;
+}
+
+int HMSFToIndex(const char *HMSF)
+{
+ int h, m, s, f = 0;
+ if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
+ return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+ return 0;
+}
+
+int SecondsToFrames(int Seconds)
+{
+ return Seconds * FRAMESPERSEC;
+}
diff --git a/recording.h b/recording.h
index 38626f8..16c6506 100644
--- a/recording.h
+++ b/recording.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recording.h 1.22 2002/02/03 11:59:49 kls Exp $
+ * $Id: recording.h 1.23 2002/06/16 11:29:47 kls Exp $
*/
#ifndef __RECORDING_H
@@ -104,4 +104,63 @@ public:
static void InvokeCommand(const char *State, const char *RecordingFileName);
};
+//XXX+
+#define FRAMESPERSEC 25
+
+// The maximum file size is limited by the range that can be covered
+// with 'int'. 4GB might be possible (if the range is considered
+// 'unsigned'), 2GB should be possible (even if the range is considered
+// 'signed'), so let's use 2000MB for absolute safety (the actual file size
+// may be slightly higher because we stop recording only before the next
+// 'I' frame, to have a complete Group Of Pictures):
+#define MAXVIDEOFILESIZE 2000 // MB
+#define MINVIDEOFILESIZE 100 // MB
+
+class cIndexFile {
+private:
+ struct tIndex { int offset; uchar type; uchar number; short reserved; };
+ int f;
+ char *fileName;
+ int size, last;
+ tIndex *index;
+ cResumeFile resumeFile;
+ bool CatchUp(int Index = -1);
+public:
+ cIndexFile(const char *FileName, bool Record);
+ ~cIndexFile();
+ bool Ok(void) { return index != NULL; }
+ bool Write(uchar PictureType, uchar FileNumber, int FileOffset);
+ bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL);
+ int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false);
+ int Get(uchar FileNumber, int FileOffset);
+ int Last(void) { CatchUp(); return last; }
+ int GetResume(void) { return resumeFile.Read(); }
+ bool StoreResume(int Index) { return resumeFile.Save(Index); }
+ };
+
+class cFileName {
+private:
+ int file;
+ int fileNumber;
+ char *fileName, *pFileNumber;
+ bool record;
+ bool blocking;
+public:
+ cFileName(const char *FileName, bool Record, bool Blocking = false);
+ ~cFileName();
+ const char *Name(void) { return fileName; }
+ int Number(void) { return fileNumber; }
+ int Open(void);
+ void Close(void);
+ int SetOffset(int Number, int Offset = 0);
+ int NextFile(void);
+ };
+
+const char *IndexToHMSF(int Index, bool WithFrame = false);
+ // Converts the given index to a string, optionally containing the frame number.
+int HMSFToIndex(const char *HMSF);
+ // Converts the given string (format: "hh:mm:ss.ff") to an index.
+int SecondsToFrames(int Seconds); //XXX+ ->player???
+ // Returns the number of frames corresponding to the given number of seconds.
+
#endif //__RECORDING_H
diff --git a/remote.c b/remote.c
index a9fee37..0ee42a7 100644
--- a/remote.c
+++ b/remote.c
@@ -6,7 +6,7 @@
*
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
*
- * $Id: remote.c 1.26 2002/05/13 16:31:27 kls Exp $
+ * $Id: remote.c 1.27 2002/05/18 12:55:39 kls Exp $
*/
#include "remote.h"
@@ -24,6 +24,10 @@
#include <sys/un.h>
#endif
+#if defined REMOTE_KBD
+#include <ncurses.h>
+#endif
+
#include "config.h"
#include "tools.h"
diff --git a/ringbuffer.c b/ringbuffer.c
index 3918297..421f19f 100644
--- a/ringbuffer.c
+++ b/ringbuffer.c
@@ -1,5 +1,5 @@
/*
- * ringbuffer.c: A threaded ring buffer
+ * ringbuffer.c: A ring buffer
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
@@ -7,50 +7,25 @@
* Parts of this file were inspired by the 'ringbuffy.c' from the
* LinuxDVB driver (see linuxtv.org).
*
- * $Id: ringbuffer.c 1.7 2002/05/13 16:31:46 kls Exp $
+ * $Id: ringbuffer.c 1.9 2002/06/16 11:24:40 kls Exp $
*/
#include "ringbuffer.h"
+#include <unistd.h>
#include "tools.h"
-// --- cRingBufferInputThread -------------------------------------------------
-
-class cRingBufferInputThread : public cThread {
-private:
- cRingBuffer *ringBuffer;
-protected:
- virtual void Action(void) { ringBuffer->Input(); }
-public:
- cRingBufferInputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; }
- };
-
-// --- cRingBufferOutputThread ------------------------------------------------
-
-class cRingBufferOutputThread : public cThread {
-private:
- cRingBuffer *ringBuffer;
-protected:
- virtual void Action(void) { ringBuffer->Output(); }
-public:
- cRingBufferOutputThread(cRingBuffer *RingBuffer) { ringBuffer = RingBuffer; }
- };
-
-// --- cRingBuffer ------------------------------------------------------------
+// --- cRingBuffer -----------------------------------------------------------
cRingBuffer::cRingBuffer(int Size, bool Statistics)
{
size = Size;
statistics = Statistics;
- inputThread = NULL;
- outputThread = NULL;
- busy = false;
maxFill = 0;
+ lastPercent = 0;
}
cRingBuffer::~cRingBuffer()
{
- delete inputThread;
- delete outputThread;
if (statistics)
dsyslog("buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
}
@@ -79,45 +54,13 @@ 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;
- 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::cRingBufferLinear(int Size, bool Statistics)
:cRingBuffer(Size, Statistics)
{
buffer = NULL;
+ getThreadPid = -1;
if (Size > 1) { // 'Size - 1' must not be 0!
buffer = new uchar[Size];
if (!buffer)
@@ -146,6 +89,8 @@ void cRingBufferLinear::Clear(void)
Lock();
head = tail = 0;
Unlock();
+ EnablePut();
+ EnableGet();
}
int cRingBufferLinear::Put(const uchar *Data, int Count)
@@ -159,11 +104,13 @@ int cRingBufferLinear::Put(const uchar *Data, int Count)
int fill = Size() - free - 1 + Count;
if (fill >= Size())
fill = Size() - 1;
- if (fill > maxFill) {
+ if (fill > maxFill)
maxFill = fill;
- int percent = maxFill * 100 / (Size() - 1);
+ int percent = maxFill * 100 / (Size() - 1) / 5 * 5;
+ if (abs(lastPercent - percent) >= 5) {
if (percent > 75)
- dsyslog("buffer usage: %d%%", percent);
+ dsyslog("buffer usage: %d%% (pid=%d)", percent, getThreadPid);
+ lastPercent = percent;
}
}
if (free > 0) {
@@ -185,6 +132,7 @@ int cRingBufferLinear::Put(const uchar *Data, int Count)
else
Count = 0;
Unlock();
+ EnableGet();
}
return Count;
}
@@ -193,6 +141,8 @@ int cRingBufferLinear::Get(uchar *Data, int Count)
{
if (Count > 0) {
Lock();
+ if (getThreadPid < 0)
+ getThreadPid = getpid();
int rest = Size() - tail;
int diff = head - tail;
int cont = (diff >= 0) ? diff : Size() + diff;
@@ -213,6 +163,8 @@ int cRingBufferLinear::Get(uchar *Data, int Count)
else
Count = 0;
Unlock();
+ if (Count == 0)
+ WaitForGet();
}
return Count;
}
@@ -239,7 +191,7 @@ cFrame::~cFrame()
// --- cRingBufferFrame ------------------------------------------------------
-cRingBufferFrame::cRingBufferFrame(int Size, bool Statistics = false)
+cRingBufferFrame::cRingBufferFrame(int Size, bool Statistics)
:cRingBuffer(Size, Statistics)
{
head = NULL;
@@ -255,7 +207,7 @@ void cRingBufferFrame::Clear(void)
{
Lock();
const cFrame *p;
- while ((p = Get(false)) != NULL)
+ while ((p = Get()) != NULL)
Drop(p);
Unlock();
EnablePut();
@@ -279,17 +231,14 @@ bool cRingBufferFrame::Put(cFrame *Frame)
EnableGet();
return true;
}
- WaitForPut();
return false;
}
-const cFrame *cRingBufferFrame::Get(bool Wait)
+const cFrame *cRingBufferFrame::Get(void)
{
Lock();
cFrame *p = head ? head->next : NULL;
Unlock();
- if (!p && Wait)
- WaitForGet();
return p;
}
diff --git a/ringbuffer.h b/ringbuffer.h
index 7e1025b..43176bd 100644
--- a/ringbuffer.h
+++ b/ringbuffer.h
@@ -1,10 +1,10 @@
/*
- * ringbuffer.h: A threaded ring buffer
+ * ringbuffer.h: A ring buffer
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: ringbuffer.h 1.5 2001/11/03 10:41:33 kls Exp $
+ * $Id: ringbuffer.h 1.6 2002/06/16 11:30:07 kls Exp $
*/
#ifndef __RINGBUFFER_H
@@ -12,24 +12,17 @@
#include "thread.h"
-typedef unsigned char uchar;
-
-class cRingBufferInputThread;
-class cRingBufferOutputThread;
+typedef unsigned char uchar;//XXX+
class cRingBuffer {
- friend class cRingBufferInputThread;
- friend class cRingBufferOutputThread;
private:
- cRingBufferInputThread *inputThread;
- cRingBufferOutputThread *outputThread;
cMutex mutex;
cCondVar readyForPut, readyForGet;
cMutex putMutex, getMutex;
int size;
- bool busy;
protected:
int maxFill;//XXX
+ int lastPercent;
bool statistics;//XXX
void WaitForPut(void);
void WaitForGet(void);
@@ -41,26 +34,19 @@ protected:
void Lock(void) { mutex.Lock(); }
void Unlock(void) { mutex.Unlock(); }
int Size(void) { return size; }
- bool Busy(void) { return busy; }
- 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.
- virtual void Output(void) = 0;
- // Runs as a separate thread and shall continuously call Get() to
- // retrieve data from the ring buffer and write it to a destination.
public:
cRingBuffer(int Size, bool Statistics = false);
virtual ~cRingBuffer();
- bool Start(void);
- bool Active(void);
- void Stop(void);
};
class cRingBufferLinear : public cRingBuffer {
private:
int head, tail;
uchar *buffer;
-protected:
+ pid_t getThreadPid;
+public:
+ cRingBufferLinear(int Size, bool Statistics = false);
+ virtual ~cRingBufferLinear();
virtual int Available(void);
virtual void Clear(void);
// Immediately clears the ring buffer.
@@ -70,9 +56,6 @@ protected:
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();
};
enum eFrameType { ftUnknown, ftVideo, ftAudio, ftDolby };
@@ -99,21 +82,20 @@ private:
cFrame *head;
int currentFill;
void Delete(const cFrame *Frame);
-protected:
+public:
+ cRingBufferFrame(int Size, bool Statistics = false);
+ virtual ~cRingBufferFrame();
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);
+ const cFrame *Get(void);
// Gets the next frame from the ring buffer.
// The actual data still remains in the buffer until Drop() is called.
void Drop(const cFrame *Frame);
// Drops the Frame that has just been fetched with Get().
-public:
- cRingBufferFrame(int Size, bool Statistics = false);
- virtual ~cRingBufferFrame();
};
#endif // __RINGBUFFER_H
diff --git a/status.c b/status.c
new file mode 100644
index 0000000..76dc840
--- /dev/null
+++ b/status.c
@@ -0,0 +1,96 @@
+/*
+ * status.c: Status monitoring
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: status.c 1.3 2002/06/16 13:24:36 kls Exp $
+ */
+
+#include "status.h"
+
+// --- cStatus ---------------------------------------------------------------
+
+cList<cStatus> cStatus::statusMonitors;
+
+cStatus::cStatus(void)
+{
+ statusMonitors.Add(this);
+}
+
+cStatus::~cStatus()
+{
+ statusMonitors.Del(this, false);
+}
+
+void cStatus::MsgChannelSwitch(const cDevice *Device, int ChannelNumber)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->ChannelSwitch(Device, ChannelNumber);
+}
+
+void cStatus::MsgRecording(const cDevice *Device, const char *Name)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->Recording(Device, Name);
+}
+
+void cStatus::MsgReplaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->Replaying(DvbPlayerControl, Name);
+}
+
+void cStatus::MsgSetVolume(int Volume, bool Absolute)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->SetVolume(Volume, Absolute);
+}
+
+void cStatus::MsgOsdClear(void)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdClear();
+}
+
+void cStatus::MsgOsdTitle(const char *Title)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdTitle(Title);
+}
+
+void cStatus::MsgOsdStatusMessage(const char *Message)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdStatusMessage(Message);
+}
+
+void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdHelpKeys(Red, Green, Yellow, Blue);
+}
+
+void cStatus::MsgOsdCurrentItem(const char *Text)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdCurrentItem(Text);
+}
+
+void cStatus::MsgOsdTextItem(const char *Text, bool Scroll)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdTextItem(Text, Scroll);
+}
+
+void cStatus::MsgOsdChannel(const char *Text)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdChannel(Text);
+}
+
+void cStatus::MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle)
+{
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+}
diff --git a/status.h b/status.h
new file mode 100644
index 0000000..d68f23a
--- /dev/null
+++ b/status.h
@@ -0,0 +1,75 @@
+/*
+ * status.h: Status monitoring
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: status.h 1.3 2002/06/16 13:24:50 kls Exp $
+ */
+
+#ifndef __STATUS_H
+#define __STATUS_H
+
+#include "config.h"
+#include "device.h"
+#include "dvbplayer.h"
+#include "tools.h"
+
+class cStatus : public cListObject {
+private:
+ static cList<cStatus> statusMonitors;
+protected:
+ // These functions can be implemented by derived classes to receive status information:
+ virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber) {}
+ // Indicates a channel switch on the given DVB device.
+ // If ChannelNumber is 0, this is before the channel is being switched,
+ // otherwise ChannelNumber is the number of the channel that has been switched to.
+ virtual void Recording(const cDevice *Device, const char *Name) {}
+ // The given DVB device has started recording Name. Name is the full directory
+ // name of the recording. If Name is NULL, the recording has ended.
+ virtual void Replaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name) {}
+ // The given player control has started replaying Name. Name is the full directory
+ // name of the recording. If Name is NULL, the replay has ended.
+ virtual void SetVolume(int Volume, bool Absolute) {}
+ // The volume has been set to the given value, either
+ // absolutely or relative to the current volume.
+ virtual void OsdClear(void) {}
+ // The OSD has been cleared.
+ virtual void OsdTitle(const char *Title) {}
+ // Title has been displayed in the title line of the menu.
+ virtual void OsdStatusMessage(const char *Message) {}
+ // Message has been displayed in the status line of the menu.
+ // If Message is NULL, the status line has been cleared.
+ virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) {}
+ // The help keys have been set to the given values (may be NULL).
+ virtual void OsdCurrentItem(const char *Text) {}
+ // The OSD displays the given single line Text as the current menu item.
+ virtual void OsdTextItem(const char *Text, bool Scroll) {}
+ // The OSD displays the given multi line text. If Text points to an
+ // actual string, that text shall be displayed and Scroll has no
+ // meaning. If Text is NULL, Scroll defines whether the previously
+ // received text shall be scrolled up (true) or down (false) and
+ // the text shall be redisplayed with the new offset.
+ virtual void OsdChannel(const char *Text) {}
+ // The OSD displays the single line Text with the current channel information.
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+ // The OSD displays the given programme information.
+public:
+ cStatus(void);
+ virtual ~cStatus();
+ // These functions are called whenever the related status information changes:
+ static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber);
+ static void MsgRecording(const cDevice *Device, const char *Name);
+ static void MsgReplaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name);
+ static void MsgSetVolume(int Volume, bool Absolute);
+ static void MsgOsdClear(void);
+ static void MsgOsdTitle(const char *Title);
+ static void MsgOsdStatusMessage(const char *Message);
+ static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
+ static void MsgOsdCurrentItem(const char *Text);
+ static void MsgOsdTextItem(const char *Text, bool Scroll = false);
+ static void MsgOsdChannel(const char *Text);
+ static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
+ };
+
+#endif //__STATUS_H
diff --git a/svdrp.c b/svdrp.c
index ff3a775..9d70732 100644
--- a/svdrp.c
+++ b/svdrp.c
@@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
- * $Id: svdrp.c 1.37 2002/05/13 16:32:05 kls Exp $
+ * $Id: svdrp.c 1.38 2002/06/09 15:56:54 kls Exp $
*/
#include "svdrp.h"
@@ -27,7 +27,7 @@
#include <sys/time.h>
#include <unistd.h>
#include "config.h"
-#include "dvbapi.h"
+#include "device.h"
#include "interface.h"
#include "tools.h"
@@ -390,12 +390,12 @@ void cSVDRP::CmdCHAN(const char *Option)
n = o;
}
else if (strcmp(Option, "-") == 0) {
- n = cDvbApi::CurrentChannel();
+ n = cDevice::CurrentChannel();
if (n > 1)
n--;
}
else if (strcmp(Option, "+") == 0) {
- n = cDvbApi::CurrentChannel();
+ n = cDevice::CurrentChannel();
if (n < Channels.MaxNumber())
n++;
}
@@ -430,11 +430,11 @@ void cSVDRP::CmdCHAN(const char *Option)
return;
}
}
- cChannel *channel = Channels.GetByNumber(cDvbApi::CurrentChannel());
+ cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
if (channel)
Reply(250, "%d %s", channel->number, channel->name);
else
- Reply(550, "Unable to find channel \"%d\"", cDvbApi::CurrentChannel());
+ Reply(550, "Unable to find channel \"%d\"", cDevice::CurrentChannel());
}
void cSVDRP::CmdDELC(const char *Option)
@@ -541,7 +541,7 @@ void cSVDRP::CmdGRAB(const char *Option)
Reply(501, "Unexpected parameter \"%s\"", p);
return;
}
- if (cDvbApi::PrimaryDvbApi->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY))
+ if (cDevice::PrimaryDevice()->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY))
Reply(250, "Grabbed image %s", Option);
else
Reply(451, "Grab image failed");
@@ -926,22 +926,22 @@ void cSVDRP::CmdVOLU(const char *Option)
{
if (*Option) {
if (isnumber(Option))
- cDvbApi::PrimaryDvbApi->SetVolume(strtol(Option, NULL, 10), true);
+ cDevice::PrimaryDevice()->SetVolume(strtol(Option, NULL, 10), true);
else if (strcmp(Option, "+") == 0)
- cDvbApi::PrimaryDvbApi->SetVolume(VOLUMEDELTA);
+ cDevice::PrimaryDevice()->SetVolume(VOLUMEDELTA);
else if (strcmp(Option, "-") == 0)
- cDvbApi::PrimaryDvbApi->SetVolume(-VOLUMEDELTA);
+ cDevice::PrimaryDevice()->SetVolume(-VOLUMEDELTA);
else if (strcasecmp(Option, "MUTE") == 0)
- cDvbApi::PrimaryDvbApi->ToggleMute();
+ cDevice::PrimaryDevice()->ToggleMute();
else {
Reply(501, "Unknown option: \"%s\"", Option);
return;
}
}
- if (cDvbApi::PrimaryDvbApi->IsMute())
+ if (cDevice::PrimaryDevice()->IsMute())
Reply(250, "Audio is mute");
else
- Reply(250, "Audio volume is %d", cDvbApi::CurrentVolume());
+ Reply(250, "Audio volume is %d", cDevice::CurrentVolume());
}
#define CMD(c) (strcasecmp(Cmd, c) == 0)
diff --git a/thread.c b/thread.c
index 925fc4d..1448cd8 100644
--- a/thread.c
+++ b/thread.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.c 1.20 2002/05/13 16:32:09 kls Exp $
+ * $Id: thread.c 1.21 2002/06/01 15:28:46 kls Exp $
*/
#include "thread.h"
@@ -147,6 +147,7 @@ bool cThread::Active(void)
void cThread::Cancel(int WaitSeconds)
{
+ running = false;
if (WaitSeconds > 0) {
for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
if (!Active())
diff --git a/thread.h b/thread.h
index 3ac398a..d23814e 100644
--- a/thread.h
+++ b/thread.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.h 1.12 2002/02/23 13:53:38 kls Exp $
+ * $Id: thread.h 1.13 2002/06/01 14:55:31 kls Exp $
*/
#ifndef __THREAD_H
@@ -54,9 +54,9 @@ private:
static bool signalHandlerInstalled;
static void SignalHandler(int signum);
static void *StartThread(cThread *Thread);
+protected:
void Lock(void) { mutex.Lock(); }
void Unlock(void) { mutex.Unlock(); }
-protected:
void WakeUp(void);
virtual void Action(void) = 0;
void Cancel(int WaitSeconds = 0);
diff --git a/tmp.dif b/tmp.dif
new file mode 100644
index 0000000..87c57b7
--- /dev/null
+++ b/tmp.dif
@@ -0,0 +1,118 @@
+--- dvb.c
++++ dvb.c Wed May 29 17:41:53 2002
+@@ -444,6 +444,8 @@
+ break;
+ if (!dvb->arm_ready)
+ continue;
++ if (down_interruptible(&dvb->dcomlock))
++ continue;
+ newloops=rdebi(dvb, DEBINOSWAP, STATUS_LOOPS, 0, 2);
+ if (newloops==dvb->arm_loops) {
+ printk("dvb%d: ARM crashed!\n", dvb->num);
+@@ -451,6 +453,7 @@
+ newloops=rdebi(dvb, DEBINOSWAP, STATUS_LOOPS, 0, 2)-1;
+ }
+ dvb->arm_loops=newloops;
++ up(&dvb->dcomlock);
+ }
+ dvb->arm_thread = NULL;
+ return 0;
+@@ -1158,6 +1161,31 @@
+ return blen;
+ }
+
++/* Timer to avoid broken data transfer in gpioirq() */
++
++#define GPIO_RXTIME_FRAME ((32*HZ)/1000)
++
++static void gpio_receive_timeout(unsigned long data)
++{
++ struct dvb_struct *dvb = (struct dvb_struct *)data;
++ dvb->debitimer.expires = 0;
++}
++
++static void gpio_receive_timer(struct dvb_struct *dvb, unsigned long ticks)
++{
++ dvb_demux_filter_t *dvbdmxfilter = dvb->handle2filter[((dvb->debitype)>>8)&0x1f];
++
++ if (!dvbdmxfilter || dvbdmxfilter->type != DMX_TYPE_TS)
++ return;
++ mod_timer(&dvb->debitimer, jiffies + ticks);
++}
++
++static inline int gpio_rx_pending(struct dvb_struct *dvb)
++{
++ return !timer_pending(&dvb->debitimer) && \
++ dvb->debitimer.expires == 0;
++}
++
+ static void
+ gpioirq(struct saa7146* saa, void *data)
+ {
+@@ -1313,6 +1341,7 @@
+ iwdebi(dvb, DEBINOSWAP, RX_BUFF, 0, 2);
+ break;
+ } /* yes, fall through */
++ gpio_receive_timer(dvb, GPIO_RXTIME_FRAME);
+ case DATA_TS_RECORD:
+ case DATA_PES_RECORD:
+ saa7146_write(dvb->saa_mem, IER,
+@@ -1843,7 +1872,7 @@
+ bpp=dvb->osdbpp[dvb->osdwin]+1;
+ bpl=((w*bpp+7)&~7)/8;
+ size=h*bpl;
+- lpb=(32*1024)/bpl;
++ lpb=(16*1024)/bpl;
+ bnum=size/(lpb*bpl);
+ brest=size-bnum*lpb*bpl;
+
+@@ -1851,6 +1880,10 @@
+ LoadBitmap(dvb, bpp2bit[dvb->osdbpp[dvb->osdwin]], w, lpb, inc, data);
+ BlitBitmap(dvb, dvb->osdwin, x0, y0+i*lpb, 0);
+ data+=lpb*inc;
++ if (gpio_rx_pending(dvb)) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(GPIO_RXTIME_FRAME);
++ }
+ }
+ if (brest) {
+ LoadBitmap(dvb, bpp2bit[dvb->osdbpp[dvb->osdwin]], w, brest/bpl, inc, data);
+@@ -3819,6 +3852,7 @@
+ return TTBStop(dvb);
+
+ if (dvbdmxfeed->type == DMX_TYPE_TS) {
++ del_timer(&dvb->debitimer);
+ if (dvbdmxfeed->ts_type & TS_DECODER) {
+ if (dvbdmxfeed->pes_type>=DMX_TS_PES_OTHER ||
+ !dvbdmx->pesfilter[dvbdmxfeed->pes_type])
+@@ -5472,8 +5506,12 @@
+ spin_lock_init (&dvb->debilock);
+ sema_init(&dvb->dcomlock, 1);
+ init_waitqueue_head(&dvb->debiq);
++ init_timer(&dvb->debitimer);
+ dvb->debilock=SPIN_LOCK_UNLOCKED;
+ dvb->debitype=-1;
++ dvb->debitimer.function = gpio_receive_timeout;
++ dvb->debitimer.data = (unsigned long) dvb;
++ dvb->debitimer.expires = jiffies;
+
+ dvb->num=num;
+ dvb->i2cbus=adap;
+@@ -5741,6 +5779,7 @@
+
+ /* release the saa7146s */
+ for( i = 0; i < num_dvb; i++) {
++ del_timer_sync(&(dvbs[i].debitimer));
+ dvbs[i].arm_rmmod=1;
+ wake_up_interruptible(&dvbs[i].arm_wait);
+ while (dvbs[i].arm_thread)
+--- dvb.h
++++ dvb.h Wed May 29 17:41:08 2002
+@@ -566,6 +566,7 @@
+ spinlock_t debilock;
+ struct semaphore dcomlock;
+ WAIT_QUEUE debiq;
++ struct timer_list debitimer;
+ int debitype;
+ int debilen;
+ int debibuf;
diff --git a/tools.c b/tools.c
index 440ddd1..08aa8ae 100644
--- a/tools.c
+++ b/tools.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.c 1.66 2002/05/13 17:56:17 kls Exp $
+ * $Id: tools.c 1.67 2002/05/18 15:10:45 kls Exp $
*/
#include "tools.h"
@@ -793,14 +793,15 @@ void cListBase::Ins(cListObject *Object, cListObject *Before)
}
}
-void cListBase::Del(cListObject *Object)
+void cListBase::Del(cListObject *Object, bool DeleteObject)
{
if (Object == objects)
objects = Object->Next();
if (Object == lastObject)
lastObject = Object->Prev();
Object->Unlink();
- delete Object;
+ if (DeleteObject)
+ delete Object;
}
void cListBase::Move(int From, int To)
diff --git a/tools.h b/tools.h
index d98dfb6..5f09edb 100644
--- a/tools.h
+++ b/tools.h
@@ -4,13 +4,13 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.h 1.45 2002/05/13 16:21:55 kls Exp $
+ * $Id: tools.h 1.47 2002/06/09 11:09:10 kls Exp $
*/
#ifndef __TOOLS_H
#define __TOOLS_H
-//#include <errno.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
@@ -18,6 +18,8 @@
#include <sys/stat.h>
#include <sys/types.h>
+typedef unsigned char uchar;
+
extern int SysLogLevel;
#define esyslog(a...) void( (SysLogLevel > 0) ? syslog(LOG_ERR, a) : void() )
@@ -36,6 +38,9 @@ extern int SysLogLevel;
#define DELETENULL(p) (delete (p), p = NULL)
+#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
+#define FATALERRNO (errno != EAGAIN && errno != EINTR)
+
template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
@@ -135,7 +140,7 @@ public:
virtual ~cListBase();
void Add(cListObject *Object, cListObject *After = NULL);
void Ins(cListObject *Object, cListObject *Before = NULL);
- void Del(cListObject *Object);
+ void Del(cListObject *Object, bool DeleteObject = true);
virtual void Move(int From, int To);
void Move(cListObject *From, cListObject *To);
virtual void Clear(void);
diff --git a/vdr.c b/vdr.c
index 7fffa6f..bef3919 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
- * $Id: vdr.c 1.109 2002/05/13 16:32:49 kls Exp $
+ * $Id: vdr.c 1.114 2002/06/16 11:30:28 kls Exp $
*/
#include <getopt.h>
@@ -31,11 +31,12 @@
#include <stdlib.h>
#include <unistd.h>
#include "config.h"
-#include "dvbapi.h"
-#include "eit.h"
+#include "device.h"
+#include "eitscan.h"
#include "i18n.h"
#include "interface.h"
#include "menu.h"
+#include "osd.h"
#include "plugin.h"
#include "recording.h"
#include "tools.h"
@@ -117,15 +118,17 @@ int main(int argc, char *argv[])
int c;
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {
switch (c) {
- case 'a': cDvbApi::SetAudioCommand(optarg);
+ /*XXX+
+ case 'a': cDevice::SetAudioCommand(optarg);
break;
+ XXX*/
case 'c': ConfigDirectory = optarg;
break;
case 'd': DaemonMode = true; break;
case 'D': if (isnumber(optarg)) {
int n = atoi(optarg);
- if (0 <= n && n < MAXDVBAPI) {
- cDvbApi::SetUseDvbApi(n);
+ if (0 <= n && n < MAXDEVICES) {
+ cDevice::SetUseDevice(n);
break;
}
}
@@ -146,7 +149,12 @@ int main(int argc, char *argv[])
fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
return 2;
break;
- case 'L': PluginManager.SetDirectory(optarg);
+ case 'L': if (access(optarg, R_OK | X_OK) == 0)
+ PluginManager.SetDirectory(optarg);
+ else {
+ fprintf(stderr, "vdr: can't access plugin directory: %s\n", optarg);
+ return 2;
+ }
break;
case 'm': MuteAudio = true;
break;
@@ -317,10 +325,10 @@ int main(int argc, char *argv[])
// DVB interfaces:
- if (!cDvbApi::Init())
+ if (!cDevice::Initialize())
return 2;
- cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB);
+ cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
cSIProcessor::Read();
@@ -329,13 +337,17 @@ int main(int argc, char *argv[])
if (!PluginManager.StartPlugins())
return 2;
+ // OSD:
+
+ cOsd::Initialize();
+
// Channel:
Channels.SwitchTo(Setup.CurrentChannel);
if (MuteAudio)
- cDvbApi::PrimaryDvbApi->ToggleMute();
+ cDevice::PrimaryDevice()->ToggleMute();
else
- cDvbApi::PrimaryDvbApi->SetVolume(Setup.CurrentVolume, true);
+ cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true);
cEITScanner EITScanner;
@@ -358,10 +370,10 @@ int main(int argc, char *argv[])
// Main program loop:
- cOsdBase *Menu = NULL;
+ cOsdObject *Menu = NULL;
cReplayControl *ReplayControl = NULL;
int LastChannel = -1;
- int PreviousChannel = cDvbApi::CurrentChannel();
+ int PreviousChannel = cDevice::CurrentChannel();
time_t LastActivity = 0;
int MaxLatencyTime = 0;
bool ForceShutdown = false;
@@ -386,12 +398,12 @@ int main(int argc, char *argv[])
}
}
// Channel display:
- if (!EITScanner.Active() && cDvbApi::CurrentChannel() != LastChannel) {
+ if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {
if (!Menu)
- Menu = new cDisplayChannel(cDvbApi::CurrentChannel(), LastChannel > 0);
+ Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel > 0);
if (LastChannel > 0)
PreviousChannel = LastChannel;
- LastChannel = cDvbApi::CurrentChannel();
+ LastChannel = cDevice::CurrentChannel();
}
// Timers and Recordings:
if (!Menu) {
@@ -404,7 +416,7 @@ int main(int argc, char *argv[])
}
}
// User Input:
- cOsdBase **Interact = Menu ? &Menu : (cOsdBase **)&ReplayControl;
+ cOsdObject **Interact = Menu ? &Menu : (cOsdObject **)&ReplayControl;
eKeys key = Interface->GetKey(!*Interact || !(*Interact)->NeedsFastResponse());
if (NORMALKEY(key) != kNone) {
EITScanner.Activity();
@@ -419,11 +431,11 @@ int main(int argc, char *argv[])
case kVolDn:
case kMute:
if (key == kMute) {
- if (!cDvbApi::PrimaryDvbApi->ToggleMute() && !Menu)
+ if (!cDevice::PrimaryDevice()->ToggleMute() && !Menu)
break; // no need to display "mute off"
}
else
- cDvbApi::PrimaryDvbApi->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA);
+ cDevice::PrimaryDevice()->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA);
if (!Menu && (!ReplayControl || !ReplayControl->Visible()))
Menu = cDisplayVolume::Create();
cDisplayVolume::Process(key);
@@ -467,7 +479,7 @@ int main(int argc, char *argv[])
case osSwitchDvb:
DELETENULL(*Interact);
Interface->Info(tr("Switching primary DVB..."));
- cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB);
+ cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
break;
case osBack:
case osEnd: DELETENULL(*Interact);
@@ -480,7 +492,7 @@ int main(int argc, char *argv[])
switch (key) {
// Toggle channels:
case k0: {
- int CurrentChannel = cDvbApi::CurrentChannel();
+ int CurrentChannel = cDevice::CurrentChannel();
Channels.SwitchTo(PreviousChannel);
PreviousChannel = CurrentChannel;
break;
@@ -501,7 +513,7 @@ int main(int argc, char *argv[])
case kUp:
case kDown|k_Repeat:
case kDown: {
- int n = cDvbApi::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1);
+ int n = cDevice::CurrentChannel() + (NORMALKEY(key) == kUp ? 1 : -1);
cChannel *channel = Channels.GetByNumber(n);
if (channel)
channel->Switch();
@@ -517,14 +529,16 @@ int main(int argc, char *argv[])
}
if (!Menu) {
EITScanner.Process();
+ /*XXX+
if (!cVideoCutter::Active() && cVideoCutter::Ended()) {
if (cVideoCutter::Error())
Interface->Error(tr("Editing process failed!"));
else
Interface->Info(tr("Editing process finished"));
}
+ XXX*/
}
- if (!*Interact && ((!cRecordControls::Active() && !cVideoCutter::Active()) || ForceShutdown)) {
+ if (!*Interact && ((!cRecordControls::Active() /*XXX+&& !cVideoCutter::Active()XXX*/) || ForceShutdown)) {
time_t Now = time(NULL);
if (Now - LastActivity > ACTIVITYTIMEOUT) {
// Shutdown:
@@ -583,15 +597,17 @@ int main(int argc, char *argv[])
}
if (Interrupted)
isyslog("caught signal %d", Interrupted);
- cVideoCutter::Stop();
+ cRecordControls::Shutdown();
+ //XXX+cVideoCutter::Stop();
delete Menu;
delete ReplayControl;
delete Interface;
+ cOsd::Shutdown();
PluginManager.Shutdown(true);
- Setup.CurrentChannel = cDvbApi::CurrentChannel();
- Setup.CurrentVolume = cDvbApi::CurrentVolume();
+ Setup.CurrentChannel = cDevice::CurrentChannel();
+ Setup.CurrentVolume = cDevice::CurrentVolume();
Setup.Save();
- cDvbApi::Cleanup();
+ cDevice::Shutdown();
if (WatchdogTimeout > 0)
dsyslog("max. latency time %d seconds", MaxLatencyTime);
isyslog("exiting");